🎉 Add background blur

This commit is contained in:
Eva Marco 2026-06-03 08:46:50 +02:00
parent 726a0cf89f
commit f2d58e970a
42 changed files with 630 additions and 308 deletions

View File

@ -38,13 +38,13 @@
(d/concat-vec
[{:id "BackgroundImageFix" :type :image-fix}]
;; Background blur won't work in current SVG specification
;; We can revisit this in the future
#_(->> shape :blur (into []) (blur-filters :background-blur))
(->> shape :shadow (apply-filters :style :drop-shadow))
[{:id "shape" :type :blend-filters}]
(->> shape :shadow (apply-filters :style :inner-shadow))
;; Background blur won't work in current SVG specification
;; We can revisit this in the future
#_(->> shape :background-blur list (apply-filters :type :background-blur))
(->> shape :blur list (apply-filters :type :layer-blur))))
(defn- calculate-filter-bounds
@ -100,17 +100,13 @@
;; shapes because selrect stores the unrotated rectangle, not the screen-space bbox.
(and (empty? (-> shape :shadow))
(or (nil? (:blur shape))
(not= :layer-blur (-> shape :blur :type))
(zero? (-> shape :blur :value (or 0)))))
(-> (dm/get-prop shape :points)
(grc/points->rect))
:else
(let [filters (shape->filters shape)
blur-value (case (-> shape :blur :type)
:layer-blur (or (-> shape :blur :value) 0)
:background-blur 0
0)
blur-value (or (-> shape :blur :value) 0)
srect (-> (dm/get-prop shape :points)
(grc/points->rect))]
(get-rect-filter-bounds srect filters blur-value ignore-shadow-margin?)))))
@ -242,10 +238,7 @@
(not (cfh/frame-shape? shape)) (or (:children-bounds shape)))
filters (shape->filters shape)
blur-value (case (-> shape :blur :type)
:layer-blur (or (-> shape :blur :value) 0)
:background-blur 0
0)]
blur-value (or (-> shape :blur :value) 0)]
(get-rect-filter-bounds children-bounds filters blur-value ignore-shadow-margin?))))

View File

@ -24,3 +24,7 @@
[shape scale]
(update-in shape [:blur :value] * scale))
(defn update-background-blur-scale
[shape scale]
(update-in shape [:background-blur :value] * scale))

View File

@ -91,6 +91,7 @@
:blend-mode :layer-effects-group
:shadow :shadow-group
:blur :blur-group
:background-blur :blur-group
:masked-group :mask-group
:constraints-h :constraints-group
:constraints-v :constraints-group

View File

@ -692,6 +692,9 @@
(some? (:blur shape))
(gse/update-blur-scale value)
(some? (:background-blur shape))
(gse/update-background-blur-scale value)
(ctl/flex-layout? shape)
(ctl/update-flex-scale value)

View File

@ -25,7 +25,7 @@
fills)))
(def group-style-properties
#{:shadow :blur})
#{:shadow :blur :background-blur})
;; FIXME: revisit
(def style-properties

View File

@ -24,6 +24,7 @@
[app.common.types.path :as path]
[app.common.types.plugins :as ctpg]
[app.common.types.shape.attrs :refer [default-color]]
[app.common.types.shape.background-blur :as ctsbb]
[app.common.types.shape.blur :as ctsb]
[app.common.types.shape.export :as ctse]
[app.common.types.shape.interactions :as ctsi]
@ -222,6 +223,7 @@
[:shadow {:optional true}
[:vector {:gen/max 1} ctss/schema:shadow]]
[:blur {:optional true} ctsb/schema:blur]
[:background-blur {:optional true} ctsbb/schema:background-blur]
[:grow-type {:optional true}
[::sm/one-of grow-types]]
[:applied-tokens {:optional true} cto/schema:applied-tokens]
@ -415,7 +417,7 @@
:remote-synced :shape-ref :touched :blocked :collapsed :locked
:hidden :masked-group :fills :proportion :proportion-lock :constraints-h
:constraints-v :fixed-scroll :r1 :r2 :r3 :r4 :rotation :opacity :grids :exports
:strokes :blend-mode :interactions :shadow :blur :grow-type :applied-tokens
:strokes :blend-mode :interactions :shadow :blur :background-blur :grow-type :applied-tokens
:plugin-data})
(def ^:private allowed-shape-geom-attrs #{:x :y :width :height})
@ -657,6 +659,7 @@
;; - Contraints
;; - Shadow
;; - Blur
;; - Background blur
;; - Border radius
(def ^:private basic-extract-props
#{:fills
@ -681,6 +684,7 @@
:shadow
:blur
:background-blur
;; Radius
:r1

View File

@ -32,6 +32,7 @@
:shadow
:blur
:background-blur
:fills
:fill-color
@ -108,6 +109,7 @@
:shadow
:blur
:background-blur
:exports
@ -166,6 +168,7 @@
:shadow
:blur
:background-blur
:exports
@ -223,6 +226,7 @@
:shadow
:blur
:background-blur
:exports
@ -280,6 +284,7 @@
:shadow
:blur
:background-blur
:exports
@ -336,6 +341,7 @@
:shadow
:blur
:background-blur
:typography-ref-id
:typography-ref-file
@ -398,6 +404,7 @@
:shadow
:blur
:background-blur
:exports
@ -456,6 +463,7 @@
:shadow
:blur
:background-blur
:exports
@ -513,6 +521,7 @@
:shadow
:blur
:background-blur
:exports

View File

@ -0,0 +1,16 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC Sucursal en España SL
(ns app.common.types.shape.background-blur
(:require
[app.common.schema :as sm]))
(def schema:background-blur
[:map {:title "BackgroundBlur"}
[:id ::sm/uuid]
[:type [:enum :background-blur]]
[:value ::sm/safe-number]
[:hidden :boolean]])

View File

@ -8,12 +8,9 @@
(:require
[app.common.schema :as sm]))
(def schema:blur-type
[:enum :layer-blur :background-blur])
(def schema:blur
[:map {:title "Blur"}
[:id ::sm/uuid]
[:type schema:blur-type]
[:type [:enum :layer-blur]]
[:value ::sm/safe-number]
[:hidden :boolean]])

View File

@ -73,6 +73,7 @@
:r4
:shadow
:blur
:background-blur
:strokes
:width
:height

View File

@ -281,7 +281,7 @@
(grc/fix-aspect-ratio aspect-ratio))
;; Bounds without shadows/blur will be the bounds of the thumbnail
bounds2 (gsb/get-object-bounds objects (dissoc frame :shadow :blur))
bounds2 (gsb/get-object-bounds objects (dissoc frame :shadow :blur :background-blur))
delta-bounds (gpt/point (:x bounds) (:y bounds))
vector (gpt/negate delta-bounds)

View File

@ -14,33 +14,32 @@
(mf/defc title-bar*
[{:keys [class collapsable collapsed title children
btn-icon btn-title add-icon-gap
title-class on-collapsed on-btn-click]}]
[:div {:class [(stl/css :title-bar)
class]}
title-class on-collapsed on-btn-click] :rest props}]
(let [props (mf/spread-props props {:class (stl/css :icon-text-btn)
:on-click on-collapsed})]
(if ^boolean collapsable
[:div {:class [(stl/css :title-wrapper) title-class]}
[:div {:class [(stl/css :title-bar) class]}
(if ^boolean collapsable
[:div {:class [(stl/css :title-wrapper) title-class]}
(let [icon-id (if collapsed "arrow-right" "arrow-down")]
[:> :button props
[:> icon* {:icon-id icon-id
:size "s"
:class (stl/css :icon)}]
[:div {:class (stl/css :title)} title]])]
(let [icon-id (if collapsed "arrow-right" "arrow-down")]
[:button {:class (stl/css :icon-text-btn)
:on-click on-collapsed}
[:> icon* {:icon-id icon-id
:size "s"
:class (stl/css :icon)}]
[:div {:class (stl/css :title)} title]])]
[:div {:class [(stl/css-case :title-only true
:title-only-icon-gap add-icon-gap)
title-class]}
title])
[:div {:class [(stl/css-case :title-only true
:title-only-icon-gap add-icon-gap)
title-class]}
title])
children
children
(when (some? on-btn-click)
[:> icon-button* {:variant "ghost"
:aria-label btn-title
:on-click on-btn-click
:icon btn-icon}])])
(when (some? on-btn-click)
[:> icon-button* {:variant "ghost"
:aria-label btn-title
:on-click on-btn-click
:icon btn-icon}])]))
(mf/defc inspect-title-bar*

View File

@ -25,6 +25,7 @@
display: grid;
align-items: center;
justify-content: flex-start;
text-align: start;
grid-auto-flow: column;
height: 100%;
min-height: deprecated.$s-32;

View File

@ -8,6 +8,8 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data.macros :as dm]
[app.config :as cf]
[app.main.features :as features]
[app.main.ui.components.copy-button :refer [copy-button*]]
[app.main.ui.components.title-bar :refer [inspect-title-bar*]]
[app.util.code-gen.style-css :as css]
@ -15,35 +17,53 @@
[rumext.v2 :as mf]))
(defn- has-blur? [shape]
(:blur shape))
(defn- blur-css-property [shape]
(if (= :background-blur (get-in shape [:blur :type]))
:backdrop-filter
:filter))
(or (:blur shape)
(:background-blur shape)))
(mf/defc blur-panel
[{:keys [objects shapes]}]
(let [shapes (->> shapes (filter has-blur?))]
(let [render-wasm? (features/use-feature "render-wasm/v1")
bg-blur? (and render-wasm?
(contains? cf/flags :background-blur))
shapes (->> shapes (filter #(has-blur? %)))
title (if bg-blur?
(tr "labels.blur-effects")
(tr "labels.blur"))]
(when (seq shapes)
[:div {:class (stl/css :attributes-block)}
[:> inspect-title-bar*
{:title (tr "inspect.attributes.blur")
{:title title
:class (stl/css :title-wrapper)
:title-class (stl/css :blur-attr-title)}
(when (= (count shapes) 1)
(let [prop (blur-css-property (first shapes))]
[:> copy-button* {:data (css/get-css-property objects (first shapes) prop)
:class (stl/css :copy-btn-title)}]))]
(let [background-blur (:background-blur (first shapes))
layer-blur (:blur (first shapes))]
(when background-blur
[:> copy-button* {:data (css/get-css-property objects (first shapes) :backdrop-filter)
:class (stl/css :copy-btn-title)}])
(when layer-blur
[:> copy-button* {:data (css/get-css-property objects (first shapes) :filter)
:class (stl/css :copy-btn-title)}])))]
[:div {:class (stl/css :attributes-content)}
(for [shape shapes]
(let [prop (blur-css-property shape)]
(let [background-blur (:background-blur (first shapes))
layer-blur (:blur (first shapes))]
[:div {:class (stl/css :blur-row)
:key (dm/str "block-" (:id shape) "-blur")}
[:div {:class (stl/css :global/attr-label)}
(if (= prop :backdrop-filter) "Backdrop Filter" "Filter")]
[:div {:class (stl/css :global/attr-value)}
[:> copy-button* {:data (css/get-css-property objects shape prop)}
[:div {:class (stl/css :button-children)}
(css/get-css-value objects shape prop)]]]]))]])))
(when background-blur
[:*
[:div {:class (stl/css :global/attr-label)}
"Backdrop Filter"]
[:div {:class (stl/css :global/attr-value)}
[:> copy-button* {:data (css/get-css-property objects shape :backdrop-filter)}
[:div {:class (stl/css :button-children)}
(css/get-css-value objects shape :backdrop-filter)]]]])
(when layer-blur
[:*
[:div {:class (stl/css :global/attr-label)}
"Filter"]
[:div {:class (stl/css :global/attr-value)}
[:> copy-button* {:data (css/get-css-property objects shape :filter)}
[:div {:class (stl/css :button-children)}
(css/get-css-value objects shape :filter)]]]])]))]])))

View File

@ -51,7 +51,6 @@
:grid-column
:grid-row])
(def type->panel-group
{:multiple [:fill :stroke :text :shadow :blur :layout-element]
:frame [:visibility :geometry :fill :stroke :shadow :blur :layout :layout-element]
@ -72,7 +71,8 @@
(seq (:strokes shape)))
(defn- has-blur? [shape]
(:blur shape))
(or (:blur shape)
(:background-blur shape)))
(defn- has-text? [shape]
(:content shape))

View File

@ -18,12 +18,24 @@
[:div {:class (stl/css :blur-panel)}
(for [shape shapes]
[:div {:key (:id shape) :class (stl/css :blur-shape)}
(let [property :filter
value (css/get-css-value objects shape property)
property-name (cmm/get-css-rule-humanized property)
property-value (css/get-css-property objects shape property)]
[:> properties-row* {:key (dm/str "blur-property-" property)
:term property-name
:detail (dm/str value)
:property property-value
:copiable true}])])])
(let [blur-property :filter
blur-value (css/get-css-value objects shape blur-property)
background-blur-property :backdrop-filter
blur-property-name (cmm/get-css-rule-humanized blur-property)
blur-property-value (css/get-css-property objects shape blur-property)
background-blur-value (css/get-css-value objects shape background-blur-property)
background-blur-property-name (cmm/get-css-rule-humanized background-blur-property)
background-blur-property-value (css/get-css-property objects shape background-blur-property)]
[:*
(when blur-property-value
[:> properties-row* {:key (dm/str "blur-property-" blur-property)
:term blur-property-name
:detail (dm/str blur-value)
:property blur-property-value
:copiable true}])
(when background-blur-property-value
[:> properties-row* {:key (dm/str "blur-property-" background-blur-property)
:term background-blur-property-name
:detail (dm/str background-blur-value)
:property background-blur-property-value
:copiable true}])])])])

View File

@ -8,6 +8,8 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.config :as cf]
[app.main.features :as features]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
[app.util.clipboard :as clipboard]
@ -16,22 +18,27 @@
(defn- panel->title
[type]
(case type
:variant (tr "inspect.tabs.styles.variants-panel")
:token (tr "inspect.tabs.styles.token-panel")
:geometry (tr "inspect.attributes.size")
:fill (tr "labels.fill")
:stroke (tr "labels.stroke")
:text (tr "labels.text")
:blur (tr "labels.blur")
:shadow (tr "labels.shadow")
:layout (tr "labels.layout")
:flex-element "Flex Element"
:grid-element "Grid Element"
:layout-element "Layout Element"
:visibility (tr "labels.visibility")
:svg (tr "labels.svg")
nil))
(let [render-wasm? (features/use-feature "render-wasm/v1")
bg-blur? (and render-wasm?
(contains? cf/flags :background-blur))]
(case type
:variant (tr "inspect.tabs.styles.variants-panel")
:token (tr "inspect.tabs.styles.token-panel")
:geometry (tr "inspect.attributes.size")
:fill (tr "labels.fill")
:stroke (tr "labels.stroke")
:text (tr "labels.text")
:blur (if bg-blur?
(tr "labels.blur-effects")
(tr "labels.blur"))
:shadow (tr "labels.shadow")
:layout (tr "labels.layout")
:flex-element "Flex Element"
:grid-element "Grid Element"
:layout-element "Layout Element"
:visibility (tr "labels.visibility")
:svg (tr "labels.svg")
nil)))
(mf/defc style-box*
[{:keys [panel shorthand children]}]

View File

@ -183,6 +183,7 @@
([props shape position render-id]
(let [shape-fills (get shape :fills)
shape-shadow (get shape :shadow)
shape-blur (get shape :blur)
svg-attrs (get-svg-props shape render-id)

View File

@ -480,7 +480,8 @@
stroke-id (dm/str (dm/fmt "strokes-%-%" prefix shape-id))
shape-blur (get shape :blur)
shape-blur (get shape :blur)
shape-fills (get shape :fills)
shape-shadow (get shape :shadow)
shape-strokes (not-empty strokes)
@ -498,6 +499,7 @@
open-path? (and ^boolean (cfh/path-shape? shape)
^boolean (path/shape-with-open-path? shape))]
(when-not ^boolean (cfh/frame-shape? shape)
(when (and (some? shape-blur)
(not ^boolean (:hidden shape-blur)))

View File

@ -217,8 +217,8 @@
:penpot:spread (str spread)}])))
(defn- export-blur-data [{:keys [blur]}]
(when-let [{:keys [type hidden value]} blur]
(mf/html
(when-let [{:keys [type hidden value]} blur]
(mf/html
[:> "penpot:blur"
#js {:penpot:blur-type (d/name type)
:penpot:hidden (str hidden)

View File

@ -67,6 +67,7 @@
filter-id-blur (dm/fmt "filter-blur-%" render-id)
filter-id-shadows (dm/fmt "filter-shadow-%" render-id)
filter-str-blur (filters/filter-str filter-id-blur (dissoc shape :shadow))
filter-str-shadows (filters/filter-str filter-id-shadows (dissoc shape :blur))

View File

@ -14,146 +14,279 @@
[app.main.data.workspace.shapes :as dwsh]
[app.main.features :as features]
[app.main.store :as st]
[app.main.ui.components.numeric-input :refer [numeric-input*]]
[app.main.ui.components.select :refer [select]]
[app.main.ui.components.title-bar :refer [title-bar*]]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.controls.numeric-input :refer [numeric-input*]]
[app.main.ui.ds.controls.select :refer [select*]]
[app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.icons :as deprecated-icon]
[app.main.ui.ds.tooltip.tooltip :refer [tooltip*]]
[app.util.i18n :as i18n :refer [tr]]
[rumext.v2 :as mf]))
(def blur-attrs [:blur])
(def blur-attrs [:blur :background-blur])
(defn create-blur []
(defn create-blur [type]
(let [id (uuid/next)]
{:id id
:type :layer-blur
:type type
:value 4
:hidden false}))
(mf/defc blur-menu* [{:keys [ids type values]}]
(let [blur (:blur values)
has-value? (not (nil? blur))
render-wasm? (features/use-feature "render-wasm/v1")
bg-blur? (and render-wasm?
(contains? cf/flags :background-blur))
(mf/defc blur-menu-content*
[{:keys [blur-key value change-fn blur-values]}]
(let [render-wasm? (features/use-feature "render-wasm/v1")
bg-blur? (and render-wasm?
(contains? cf/flags :background-blur))
is-hidden (get value :hidden)
show-more-options* (mf/use-state false)
show-more-options (deref show-more-options*)
toggle-more-options (mf/use-fn #(swap! show-more-options* not))
state* (mf/use-state {:show-content true
:show-more-options false})
handle-delete
(mf/use-fn
(mf/deps change-fn blur-key)
(fn []
(change-fn #(dissoc % blur-key))))
handle-toggle-visibility
(mf/use-fn
(mf/deps change-fn blur-key)
(fn []
(change-fn #(update-in % [blur-key :hidden] not))))
handle-change
(mf/use-fn
(mf/deps change-fn blur-key)
(fn [value]
(change-fn #(assoc-in % [blur-key :value] value))))
handle-type-change
(mf/use-fn
(mf/deps change-fn value blur-key)
(fn [type]
(let [type-kw (keyword type)
target-key (if (= type-kw :layer-blur) :blur :background-blur)]
(change-fn
(fn [shape]
(cond
;; mismo tipo
(= blur-key target-key)
shape
;; ya existe un blur del tipo destino
(contains? shape target-key)
shape
;; blur origen no existe
(not (contains? shape blur-key))
shape
:else
(let [blur (get shape blur-key)]
(-> shape
(dissoc blur-key)
(assoc target-key
(assoc blur :type type-kw))))))))))
bb-disabled? (and (= 2 (count blur-values))
(not= blur-key :background-blur))
lb-disabled? (and (= 2 (count blur-values))
(not= blur-key :blur))
label-ref (mf/use-ref nil)
type-options
[{:value "layer-blur" :disabled lb-disabled? :id "layer-blur" :label (tr "workspace.options.blur-options.layer-blur")}
{:value "background-blur" :disabled bb-disabled? :id "background-blur" :label (tr "workspace.options.blur-options.background-blur")}]]
[:*
[:div {:class (stl/css-case :first-row true
:hidden is-hidden)}
[:div {:class (stl/css :blur-info)
:data-testid "blur-info"}
[:> icon-button* {:class (stl/css-case :show-more true
:selected show-more-options)
:on-click toggle-more-options
:selected show-more-options
:variant "ghost"
:disabled (or
is-hidden
(and (= blur-key :background-blur)
(= false bg-blur?)))
:aria-label (tr "workspace.options.blur-options.toggle-more-options")
:icon i/menu}]
(if bg-blur?
[:> select* {:class (stl/css :blur-type-select)
:default-selected (d/name (:type value))
:options type-options
:disabled is-hidden
:on-change handle-type-change}]
[:> tooltip* {:trigger-ref label-ref
:id "background-blur-disabled-label"
:class (stl/css :disabled-label-tooltip)
:content (tr "workspace.options.blur-options.disabled-blur-label")}
[:span {:aria-labelledby "background-blur-disabled-label"
:ref label-ref
:class (stl/css-case :label true
:disabled-label (and (= blur-key :background-blur)
(= false bg-blur?)))}
(d/name (:type value))]])]
[:div {:class (stl/css :actions)}
[:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.options.blur-options.toggle-blur")
:on-click handle-toggle-visibility
:disabled (and (= blur-key :background-blur)
(= false bg-blur?))
:tooltip-placement "top-left"
:icon (if (or is-hidden
(and (= blur-key :background-blur)
(= false bg-blur?))) i/hide i/shown)}]
[:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.options.blur-options.remove-blur")
:on-click handle-delete
:tooltip-placement "top-left"
:icon i/remove}]]]
(when show-more-options
[:div {:class (stl/css :second-row)}
[:> numeric-input*
{:class (stl/css :numeric-input)
:placeholder "--"
:min 0
:text-icon "value"
:on-change handle-change
:name "blur-value"
:value (:value value)}]])]))
(defn get-blurs [values]
(cond-> []
(:blur values)
(conj {:key :blur
:value (:blur values)})
(:background-blur values)
(conj {:key :background-blur
:value (:background-blur values)})))
(defn- check-props
"A blur-menu specific memoize check function that only checks if
specific values are changed on provided props. This allows pass the
whole shape as values without adding additional rerenders when other
shape properties changes."
[n-props o-props]
(and (identical? (unchecked-get n-props "ids")
(unchecked-get o-props "ids"))
(let [o-vals (unchecked-get o-props "values")
n-vals (unchecked-get n-props "values")
o-blur-values (get o-vals :blur)
n-blur-values (get n-vals :blur)
o-background-blur-values (get o-vals :background-blur)
n-background-blur-values (get n-vals :background-blur)]
(and (identical? o-blur-values n-blur-values)
(identical? o-background-blur-values n-background-blur-values)))))
(mf/defc blur-menu*
{::mf/wrap [#(mf/memo' % check-props)]}
[{:keys [ids type values]}]
(let [render-wasm? (features/use-feature "render-wasm/v1")
bg-blur? (and render-wasm?
(contains? cf/flags :background-blur))
blur-values (get-blurs values)
mixed-state (and (or (= :group type)
(= :multiple type))
(boolean
(some #(= :multiple (:value %)) blur-values)))
state* (mf/use-state {:show-content true})
state (deref state*)
open? (:show-content state)
more-options? (:show-more-options state)
toggle-content (mf/use-fn #(swap! state* update :show-content not))
toggle-more-options (mf/use-fn #(swap! state* update :show-more-options not))
hidden? (:hidden blur)
change!
(mf/use-fn
(mf/deps ids)
(fn [update-fn]
(st/emit! (dwsh/update-shapes ids update-fn))))
(st/emit! (dwsh/update-shapes ids update-fn)
(udw/trigger-bounding-box-cloaking ids))))
handle-delete-all
(mf/use-fn
(mf/deps change!)
(fn []
(change! #(dissoc % :blur :background-blur))))
handle-add
(mf/use-fn
(mf/deps change! ids)
(mf/deps change! blur-values)
(fn []
(st/emit! (udw/trigger-bounding-box-cloaking ids))
(change! #(assoc % :blur (create-blur)))))
(cond
(= 1 (count blur-values))
(let [existing-key (:key (first blur-values))
new-key (if (= existing-key :blur)
:background-blur
:blur)]
(change! #(assoc % new-key (create-blur (if (= :blur new-key)
:layer-blur
:background-blur)))))
(= 0 (count blur-values))
(change! #(assoc % :blur (create-blur :layer-blur))))
:else
blur-values))]
handle-delete
(mf/use-fn
(mf/deps change! ids)
(fn []
(st/emit! (udw/trigger-bounding-box-cloaking ids))
(change! #(dissoc % :blur))))
handle-change
(mf/use-fn
(mf/deps change! ids)
(fn [value]
(st/emit! (udw/trigger-bounding-box-cloaking ids))
(change! #(cond-> %
(not (contains? % :blur))
(assoc :blur (create-blur))
:always
(assoc-in [:blur :value] value)))))
handle-type-change
(mf/use-fn
(mf/deps change! ids)
(fn [value]
(st/emit! (udw/trigger-bounding-box-cloaking ids))
(change! #(assoc-in % [:blur :type] (keyword value)))))
handle-toggle-visibility
(mf/use-fn
(mf/deps change! ids)
(fn []
(st/emit! (udw/trigger-bounding-box-cloaking ids))
(change! #(update-in % [:blur :hidden] not))))
type-options
(mf/with-memo [bg-blur?]
(cond-> [{:value "layer-blur" :label (tr "workspace.options.blur-options.layer-blur")}]
bg-blur?
(conj {:value "background-blur" :label (tr "workspace.options.blur-options.background-blur")})))]
[:div {:class (stl/css :element-set)}
[:section {:class (stl/css :element-set)
:hidden (not open?)
:aria-label (if bg-blur?
(tr "labels.blur-effects")
(tr "labels.blur"))}
[:div {:class (stl/css :element-title)}
[:> title-bar* {:collapsable has-value?
[:> title-bar* {:collapsable (seq blur-values)
:collapsed (not open?)
:on-collapsed toggle-content
:title (case type
:multiple (tr "workspace.options.blur-options.title.multiple")
:group (tr "workspace.options.blur-options.title.group")
(tr "workspace.options.blur-options.title"))
:class (stl/css-case :title-spacing-blur (not has-value?))}
(when-not has-value?
[:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.options.blur-options.add-blur")
:on-click handle-add
:icon i/add
:data-testid "add-blur"}])]]
(when (and open? has-value?)
[:div {:class (stl/css :element-set-content)}
[:div {:class (stl/css-case :first-row true
:hidden hidden?)}
[:div {:class (stl/css :blur-info)
:data-testid "blur-info"}
[:button {:class (stl/css-case :show-more true
:selected more-options?)
:on-click toggle-more-options}
deprecated-icon/menu]
(if bg-blur?
[:& select {:class (stl/css :blur-type-select)
:default-value (d/name (:type blur))
:options type-options
:disabled hidden?
:on-change handle-type-change}]
[:span {:class (stl/css :label)}
(tr "workspace.options.blur-options.title")])]
[:div {:class (stl/css :actions)}
[:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.options.blur-options.toggle-blur")
:on-click handle-toggle-visibility
:icon (if hidden? i/hide i/shown)}]
[:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.options.blur-options.remove-blur")
:on-click handle-delete
:icon i/remove}]]]
(when more-options?
[:div {:class (stl/css :second-row)}
[:label {:class (stl/css :label)
:for "blur-input-sidebar"}
(tr "inspect.attributes.blur.value")]
[:> numeric-input*
{:className (stl/css :numeric-input)
:placeholder "--"
:id "blur-input-sidebar"
:min "0"
:on-change handle-change
:value (:value blur)}]])])]))
:aria-expanded open?
:aria-controls "blur-content"
:title (if bg-blur?
(cond
(= type :multiple) (tr "workspace.options.blur-effects-options.title.multiple")
(= type :group) (tr "workspace.options.blur-effects-options.title.group")
:else (tr "labels.blur-effects"))
(cond
(= type :multiple) (tr "workspace.options.blur-options.title.multiple")
(= type :group) (tr "workspace.options.blur-options.title.group")
:else (tr "labels.blur")))
:class (stl/css-case :title-spacing-blur (not (seq blur-values))
:long-title true)}
(when (and
(not mixed-state)
(< (count blur-values)
(if bg-blur? 2 1)))
[:> icon-button*
{:variant "ghost"
:aria-label (tr "workspace.options.blur-options.add-blur")
:on-click handle-add
:icon i/add
:tooltip-placement "top-left"
:data-testid "add-blur"}])]]
(when (and open? (seq blur-values))
[:div {:class (stl/css :element-set-content)
:hidden (not open?)
:id "blur-content"}
(if mixed-state
[:div {:class (stl/css :first-row)}
[:span {:class (stl/css :mixed-label)}
(tr "labels.mixed-values")]
[:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.options.blur-options.remove-blur")
:on-click handle-delete-all
:tooltip-placement "top-left"
:icon i/remove}]]
(for [{:keys [key value]} blur-values]
[:> blur-menu-content*
{:key key
:blur-key key
:value value
:blur-values blur-values
:change-fn change!}]))])]))

View File

@ -4,114 +4,130 @@
//
// Copyright (c) KALEIDOS INC Sucursal en España SL
@use "refactor/common-refactor.scss" as deprecated;
@use "ds/_borders.scss" as *;
@use "ds/typography.scss" as t;
@use "ds/_sizes.scss" as *;
@use "ds/_utils.scss" as *;
@use "../../../sidebar/common/sidebar.scss" as sidebar;
.element-set {
@include sidebar.option-grid-structure;
}
.element-title {
grid-column: span 8;
}
.title-spacing-blur {
padding-left: deprecated.$s-2;
padding-left: var(--sp-xxs);
margin: 0;
}
.element-set-content {
@include deprecated.flex-column;
.long-title {
height: auto;
}
margin-bottom: deprecated.$s-8;
.element-set-content {
display: flex;
flex-direction: column;
gap: var(--sp-xs);
margin-bottom: var(--sp-s);
}
.first-row {
@include sidebar.option-grid-structure;
}
.blur-info {
grid-column: span 6;
display: flex;
align-items: center;
gap: deprecated.$s-1;
flex-grow: 1;
border-radius: deprecated.$br-8;
background-color: var(--input-details-color);
.show-more {
background-color: var(--color-background-tertiary);
border: $b-1 solid var(--color-background-tertiary);
border-radius: $br-8 0 0 $br-8;
.show-more {
@extend %button-secondary;
height: deprecated.$s-32;
width: deprecated.$s-28;
border-radius: deprecated.$br-8 0 0 deprecated.$br-8;
box-sizing: border-box;
border: deprecated.$s-1 solid var(--button-secondary-background-color-rest);
svg {
@extend %button-icon;
}
&.selected {
background-color: var(--button-radio-background-color-active);
svg {
stroke: var(--button-radio-foreground-color-active);
}
}
}
.label {
@include deprecated.body-small-typography;
flex-grow: 1;
display: flex;
align-items: center;
height: deprecated.$s-32;
padding: 0 deprecated.$s-8;
border-radius: 0 deprecated.$br-8 deprecated.$br-8 0;
background-color: var(--input-background-color);
color: var(--menu-foreground-color);
box-sizing: border-box;
border: deprecated.$s-1 solid var(--input-border-color);
}
.blur-type-select {
flex-grow: 1;
border-radius: 0 deprecated.$br-8 deprecated.$br-8 0;
}
&:hover {
background-color: var(--color-background-quaternary);
border: $b-1 solid var(--color-background-quaternary);
}
.actions {
@include deprecated.flex-row;
&:disabled {
background-color: var(--color-background-primary);
border-block-start: $b-1 solid var(--color-background-quaternary);
border-block-end: $b-1 solid var(--color-background-quaternary);
border-inline-start: $b-1 solid var(--color-background-quaternary);
}
}
&.hidden {
.blur-info {
@include deprecated.hidden-element;
.blur-info {
grid-column: span 6;
display: flex;
align-items: center;
flex-grow: 1;
gap: 1px;
border-radius: $br-8;
background-color: var(--color-background-primary);
}
.show-more {
@include deprecated.hidden-element;
.mixed-label {
@include t.use-typography("body-small");
border: deprecated.$s-1 solid var(--input-border-color-disabled);
}
grid-column: span 7;
gap: 1px;
border-radius: $br-8;
display: flex;
align-items: center;
flex-grow: 1;
height: $sz-32;
padding: 0 var(--sp-s);
background-color: var(--color-background-tertiary);
border: $b-1 solid var(--color-background-tertiary);
color: var(--color-foreground-primary);
box-sizing: border-box;
}
.label {
@include deprecated.hidden-element;
.disabled-label-tooltip {
flex-grow:1;
}
border: deprecated.$s-1 solid var(--input-border-color-disabled);
}
}
}
.label {
@include t.use-typography("body-small");
display: flex;
align-items: center;
flex-grow: 1;
height: $sz-32;
padding: 0 var(--sp-s);
border-radius: 0 $br-8 $br-8 0;
background-color: var(--color-background-tertiary);
border: $b-1 solid var(--color-background-tertiary);
color: var(--color-foreground-primary);
box-sizing: border-box;
}
.blur-type-select {
flex-grow: 1;
border-radius: 0 $br-8 $br-8 0;
}
.actions {
display: flex;
align-items: center;
gap: var(--sp-xs);
}
.hidden .blur-info,
.hidden .blur-info .label {
cursor: default;
pointer-events: none;
box-sizing: border-box;
color: var(--color-foreground-secondary);
stroke: var(--color-foreground-secondary);
background-color: transparent;
}
.hidden .blur-info .label {
border: $b-1 solid var(--color-background-quaternary);
}
.second-row {
@extend %input-element;
@include deprecated.body-small-typography;
width: deprecated.$s-92;
.label {
padding-left: deprecated.$s-8;
width: deprecated.$s-60;
}
width: var(--three-columns-width);
}
.disabled-label {
color: var(--color-foreground-secondary);
background-color: var(--color-background-primary);
border-top: $b-1 solid var(--color-background-quaternary);
border-bottom: $b-1 solid var(--color-background-quaternary);
border-right: $b-1 solid var(--color-background-quaternary);
border-left: $b-1 solid transparent;
}

View File

@ -451,5 +451,5 @@
[:> icon-button* {:variant "ghost"
:aria-label (tr "settings.select-this-color")
:on-click handle-select
:tooltip-position "top-left"
:tooltip-placement "top-left"
:icon i/move}])]))

View File

@ -135,7 +135,7 @@
[:> shadow-menu* {:ids ids :values (get shape :shadow)}]
[:> blur-menu* {:ids ids
:values (select-keys shape [:blur])}]
:values (select-keys shape [:blur :background-blur])}]
[:> exports-menu* {:type type
:ids ids

View File

@ -132,7 +132,7 @@
:applied-tokens applied-tokens}]
[:> shadow-menu* {:ids ids :values (get shape :shadow)}]
[:> blur-menu* {:ids ids
:values (select-keys shape [:blur])}]
:values (select-keys shape [:blur :background-blur])}]
[:> svg-attrs-menu* {:ids ids
:values (select-keys shape [:svg-attrs])}]
[:> exports-menu* {:type type

View File

@ -159,7 +159,7 @@
:libraries libraries}]
[:> shadow-menu* {:ids ids :values (get shape :shadow)}]
[:> blur-menu* {:ids ids
:values (select-keys shape [:blur])}]
:values (select-keys shape [:blur :background-blur])}]
[:> frame-grid* {:shape shape}]
[:> exports-menu* {:type shape-type
:ids ids

View File

@ -48,6 +48,7 @@
:fill :shape
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :shape
:text :children
:exports :shape
@ -61,6 +62,7 @@
:fill :children
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :children
:text :children
:exports :shape
@ -74,6 +76,7 @@
:fill :shape
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :shape
:text :ignore
:exports :shape
@ -87,6 +90,7 @@
:fill :text
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :shape
:text :text
:exports :shape
@ -100,6 +104,7 @@
:fill :ignore
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :ignore
:text :ignore
:exports :shape
@ -113,6 +118,7 @@
:fill :shape
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :shape
:text :ignore
:exports :shape
@ -126,6 +132,7 @@
:fill :shape
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :shape
:text :ignore
:exports :shape
@ -139,6 +146,7 @@
:fill :shape
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :shape
:text :ignore
:exports :shape
@ -152,6 +160,7 @@
:fill :shape
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :shape
:text :ignore
:exports :shape

View File

@ -133,7 +133,7 @@
:applied-tokens applied-tokens}]
[:> shadow-menu* {:ids ids :values (get shape :shadow)}]
[:> blur-menu* {:ids ids
:values (select-keys shape [:blur])}]
:values (select-keys shape [:blur :background-blur])}]
[:> svg-attrs-menu* {:ids ids
:values (select-keys shape [:svg-attrs])}]
[:> exports-menu* {:type type

View File

@ -134,7 +134,7 @@
[:> shadow-menu* {:ids ids :values (get shape :shadow)}]
[:> blur-menu* {:ids ids
:values (select-keys shape [:blur])}]
:values (select-keys shape [:blur :background-blur])}]
[:> svg-attrs-menu* {:ids ids
:values (select-keys shape [:svg-attrs])}]

View File

@ -201,7 +201,7 @@
[:> shadow-menu* {:ids ids :values (get shape :shadow)}]
[:> blur-menu* {:ids ids
:values (select-keys shape [:blur])}]
:values (select-keys shape [:blur :background-blur])}]
[:> svg-attrs-menu* {:ids ids
:values (select-keys shape [:svg-attrs])}]

View File

@ -211,7 +211,7 @@
[:> blur-menu*
{:ids ids
:values (select-keys shape [:blur])}]
:values (select-keys shape [:blur :background-blur])}]
[:> exports-menu* {:type type
:ids ids

View File

@ -232,7 +232,7 @@
;; export interface Blur {
;; id?: string;
;; type?: 'layer-blur';
;; type?: 'layer-blur' | 'background-blur';
;; value?: number;
;; hidden?: boolean;
;; }

View File

@ -24,6 +24,7 @@
[app.common.types.grid :as ctg]
[app.common.types.path :as path]
[app.common.types.shape :as cts]
[app.common.types.shape.background-blur :as ctsbb]
[app.common.types.shape.blur :as ctsb]
[app.common.types.shape.export :as ctse]
[app.common.types.shape.interactions :as ctsi]
@ -176,6 +177,15 @@
:hidden false}
blur))
(defn- background-blur-defaults
[blur]
(d/patch-object
{:id (uuid/next)
:type :background-blur
:value 4
:hidden false}
blur))
(defn commit-fills!
[plugin-id ^js self value]
(let [shape (u/proxy->shape self)
@ -510,6 +520,25 @@
:else
(st/emit! (dwsh/update-shapes [id] #(assoc % :blur value)))))))}
:background-blur
{:this true
:get #(-> % u/proxy->shape :background-blur format/format-blur)
:set
(fn [self value]
(if (nil? value)
(st/emit! (dwsh/update-shapes [id] #(dissoc % :background-blur)))
(let [id (obj/get self "$id")
value (background-blur-defaults (parser/parse-blur value))]
(cond
(not (sm/validate ctsbb/schema:background-blur value))
(u/not-valid plugin-id :background-blur value)
(not (r/check-permission plugin-id "content:write"))
(u/not-valid plugin-id :background-blur "Plugin doesn't have 'content:write' permission")
:else
(st/emit! (dwsh/update-shapes [id] #(assoc % :background-blur value)))))))}
:exports
{:this true
:get #(-> % u/proxy->shape :exports format/format-exports)

View File

@ -945,12 +945,21 @@
(defn set-shape-blur
[blur]
(if (some? blur)
(let [type (-> blur :type sr/translate-blur-type)
(let [type (sr/translate-blur-type :layer-blur)
hidden (:hidden blur)
value (:value blur)]
(h/call wasm/internal-module "_set_shape_blur" type hidden value))
(h/call wasm/internal-module "_clear_shape_blur")))
(defn set-shape-background-blur
[background-blur]
(if (some? background-blur)
(let [type (sr/translate-blur-type :background-blur)
hidden (:hidden background-blur)
value (:value background-blur)]
(h/call wasm/internal-module "_set_shape_blur" type hidden value))
(h/call wasm/internal-module "_clear_shape_blur")))
(defn set-shape-corners
[corners]
(let [[r1 r2 r3 r4] (map #(d/nilv % 0) corners)]
@ -1331,6 +1340,7 @@
bool-type (get shape :bool-type)
grow-type (get shape :grow-type)
blur (get shape :blur)
background-blur (get shape :background-blur)
svg-attrs (get shape :svg-attrs)
shadows (get shape :shadow)]
@ -1339,6 +1349,7 @@
;; Remaining properties that need separate calls (variable-length or conditional)
(set-shape-children children)
(set-shape-blur blur)
(set-shape-background-blur background-blur)
(when (= type :group)
(set-masked (boolean masked)))
(when (= type :bool)

View File

@ -130,6 +130,7 @@
(defn- set-wasm-attr!
[shape k]
(when wasm/context-initialized?
;;TODO_BLUR: ask about this,
(let [shape (case k
:svg-attrs (svg-filters/apply-svg-derived (assoc shape :svg-attrs (get shape :svg-attrs)))
(:fills :blur :shadow) (svg-filters/apply-svg-derived shape)
@ -187,6 +188,9 @@
:blur
(api/set-shape-blur v)
:background-blur
(api/set-shape-background-blur v)
:shadow
(api/set-shape-shadows v)
@ -230,6 +234,7 @@
;; Always update fills/blur/shadow to clear previous state if filters disappear
(api/set-shape-fills id (:fills shape) false)
(api/set-shape-blur (:blur shape))
(api/set-shape-background-blur (:background-blur shape))
(api/set-shape-shadows (:shadow shape)))
:masked-group

View File

@ -44,7 +44,7 @@
(set! context-initialized? false)
(reset! context-lost? false))
;; TODO_BLUR: ask for blur-type??
(defonce serializers
#js {:blur-type shared/RawBlurType
:blend-mode shared/RawBlendMode

View File

@ -38,7 +38,7 @@
:border-color :border-color
:box-shadow :shadows
:filter :blur
:backdrop-filter :blur
:backdrop-filter :background-blur
:gap :size-array
:row-gap :size-array
:column-gap :size-array
@ -202,5 +202,6 @@
:tracks (format-tracks value)
:shadows (format-shadow value options)
:blur (format-blur value)
:background-blur (format-blur value)
:matrix (format-matrix value)
(if (keyword? value) (d/name value) value))))

View File

@ -256,14 +256,12 @@
(defn- get-filter
[shape]
(when-not (cgc/svg-markup? shape)
(when (= :layer-blur (get-in shape [:blur :type]))
(get-in shape [:blur :value]))))
(get-in shape [:blur :value])))
(defn- get-backdrop-filter
[shape]
(when-not (cgc/svg-markup? shape)
(when (= :background-blur (get-in shape [:blur :type]))
(get-in shape [:blur :value]))))
(get-in shape [:background-blur :value])))
(defn- get-display
[shape]

View File

@ -2755,6 +2755,10 @@ msgstr "Bad Gateway"
msgid "labels.blur"
msgstr "Blur"
#: src/app/main/ui/inspect/styles/style_box.cljs
msgid "labels.blur-effects"
msgstr "Blur effects"
#: src/app/main/data/common.cljs:119, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:543, src/app/main/ui/dashboard/team.cljs:866, src/app/main/ui/dashboard/team.cljs:1345, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:167, src/app/main/ui/settings/integrations.cljs:228, src/app/main/ui/viewer/share_link.cljs:204, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:242
msgid "labels.cancel"
msgstr "Cancel"
@ -7159,10 +7163,30 @@ msgstr "Layer blur"
msgid "workspace.options.blur-options.remove-blur"
msgstr "Remove blur"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-options.toggle-more-options"
msgstr "Show/hide more options"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-options.disabled-blur-label"
msgstr "Background blur is only supported in the new render. Switch to the new render in the Preferences menu to use this effect"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:113, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:138
msgid "workspace.options.blur-options.title"
msgstr "Blur"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-effects-options.title"
msgstr "Blur effects"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-effects-options.title.group"
msgstr "Group blur effects"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-effects-options.title.multiple"
msgstr "Selection blur effects"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:112
msgid "workspace.options.blur-options.title.group"
msgstr "Group blur"

View File

@ -2685,6 +2685,10 @@ msgstr "Error del servidor (Bad Gateway)"
msgid "labels.blur"
msgstr "Desenfoque"
#: src/app/main/ui/inspect/styles/style_box.cljs
msgid "labels.blur-effects"
msgstr "Efectos de desenfoque"
#: src/app/main/data/common.cljs:119, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:543, src/app/main/ui/dashboard/team.cljs:866, src/app/main/ui/dashboard/team.cljs:1345, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:167, src/app/main/ui/settings/integrations.cljs:228, src/app/main/ui/viewer/share_link.cljs:204, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:242
msgid "labels.cancel"
msgstr "Cancelar"
@ -6986,10 +6990,30 @@ msgstr "Desenfoque de capa"
msgid "workspace.options.blur-options.remove-blur"
msgstr "Eliminar desenfoque"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-options.toggle-more-options"
msgstr "Mostrar/ocultar más opciones"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-options.disabled-blur-label"
msgstr "El desenfoque de fondo (Background Blur) solo está disponible en el nuevo motor de renderizado. Actívalo desde el menú de Preferencias para utilizar este efecto."
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:113, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:138
msgid "workspace.options.blur-options.title"
msgstr "Desenfoque"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-effects-options.title"
msgstr "Efectos de desenfoque"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-effects-options.title.group"
msgstr "Efectos de desenfoque del grupo"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-effects-options.title.multiple"
msgstr "Efectos de desenfoque de la selección"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:112
msgid "workspace.options.blur-options.title.group"
msgstr "Desenfoque del grupo"

View File

@ -24,6 +24,7 @@ use std::collections::{HashMap, HashSet};
use options::RenderOptions;
pub use surfaces::{SurfaceId, Surfaces};
// TODO_BLUR: should we add here BackgroundBlur
use crate::error::{Error, Result};
use crate::math;
use crate::shapes::{
@ -370,7 +371,7 @@ pub(crate) struct RenderState {
// migration to remove group-level fills is completed, this code should be removed.
// Frames contained in groups must reset this nested_fills stack pushing a new empty vector.
pub nested_fills: Vec<Vec<Fill>>,
pub nested_blurs: Vec<Option<Blur>>, // FIXME: why is this an option?
pub nested_blurs: Vec<Option<Blur>>, // FIXME: why is this an option?, sholud be an option now? TODO_BLUR
pub nested_shadows: Vec<Vec<Shadow>>,
pub show_grid: Option<Uuid>,
pub rulers: RulerState,
@ -587,7 +588,7 @@ impl RenderState {
backbuffer_crop_cache: HashMap::default(),
})
}
/// TODO_BLUR: ?
/// Combines every visible layer blur currently active (ancestors + shape)
/// into a single equivalent blur. Layer blur radii compound by adding their
/// variances (σ² = radius²), so we: