Merge pull request #6578 from penpot/ladybenko-11105-cap-fills

🎉 Disable adding fills in UI when limit has been reached
This commit is contained in:
Andrey Antukh 2025-05-30 10:11:05 +02:00 committed by GitHub
commit 50cc70201d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 415 additions and 51 deletions

View file

@ -222,7 +222,6 @@
(update :fills #(into [attrs] %))))
undo-id
(js/Symbol)]
(rx/concat
(rx/of (dwu/start-undo-transaction undo-id))
(transform-fill state ids color change-fn options)
@ -823,7 +822,7 @@
(update [_ state]
(update state :colorpicker
(fn [{:keys [stops editing-stop] :as state}]
(let [cap-stops? (or (features/active-feature? state "render-wasm/v1") (contains? cfg/flags :binary-fills))
(let [cap-stops? (or (features/active-feature? state "render-wasm/v1") (contains? cfg/flags :frontend-binary-fills))
can-add-stop? (or (not cap-stops?) (< (count stops) shp/MAX-GRADIENT-STOPS))]
(if can-add-stop?
(if (cc/uniform-spread? stops)
@ -869,7 +868,7 @@
(update state :colorpicker
(fn [state]
(let [stops (:stops state)
cap-stops? (or (features/active-feature? state "render-wasm/v1") (contains? cfg/flags :binary-fills))
cap-stops? (or (features/active-feature? state "render-wasm/v1") (contains? cfg/flags :frontend-binary-fills))
can-add-stop? (or (not cap-stops?) (< (count stops) shp/MAX-GRADIENT-STOPS))]
(if can-add-stop? (let [new-stop (-> (cc/interpolate-gradient stops offset)
(split-color-components))
@ -890,7 +889,7 @@
(update state :colorpicker
(fn [state]
(let [stop (or (:editing-stop state) 0)
cap-stops? (or (features/active-feature? state "render-wasm/v1") (contains? cfg/flags :binary-fills))
cap-stops? (or (features/active-feature? state "render-wasm/v1") (contains? cfg/flags :frontend-binary-fills))
stops (mapv split-color-components (if cap-stops? (take shp/MAX-GRADIENT-STOPS stops) stops))]
(-> state
(assoc :current-color (get stops stop))

View file

@ -338,7 +338,7 @@
(fn [value]
(st/emit! (dc/update-colorpicker-gradient-opacity (/ value 100)))))
cap-stops? (or (features/use-feature "render-wasm/v1") (contains? cfg/flags :binary-fills))
cap-stops? (or (features/use-feature "render-wasm/v1") (contains? cfg/flags :frontend-binary-fills))
tabs
#js [#js {:aria-label (tr "workspace.libraries.colors.rgba")

View file

@ -287,7 +287,7 @@
(fn []
(when on-reverse-stops
(on-reverse-stops))))
cap-stops? (or (features/use-feature "render-wasm/v1") (contains? cfg/flags :binary-fills))
cap-stops? (or (features/use-feature "render-wasm/v1") (contains? cfg/flags :frontend-binary-fills))
add-stop-disabled? (when cap-stops? (>= (count stops) shp/MAX-GRADIENT-STOPS))]
[:div {:class (stl/css :gradient-panel)}

View file

@ -10,7 +10,9 @@
[app.common.colors :as clr]
[app.common.data :as d]
[app.common.types.color :as ctc]
[app.common.types.shape :as shp]
[app.common.types.shape.attrs :refer [default-color]]
[app.config :as cfg]
[app.main.data.workspace.colors :as dc]
[app.main.store :as st]
[app.main.ui.components.title-bar :refer [title-bar]]
@ -44,7 +46,7 @@
(mf/defc fill-menu
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values"]))]}
[{:keys [ids type values disable-remove?] :as props}]
[{:keys [ids type values] :as props}]
(let [label (case type
:multiple (tr "workspace.options.selection-fill")
:group (tr "workspace.options.group-fill")
@ -52,9 +54,14 @@
;; Excluding nil values
values (d/without-nils values)
fills (:fills values)
fills (if (contains? cfg/flags :frontend-binary-fills)
(take shp/MAX-FILLS (d/nilv (:fills values) []))
(:fills values))
has-fills? (or (= :multiple fills) (some? (seq fills)))
can-add-fills? (if (contains? cfg/flags :frontend-binary-fills)
(and (not (= :multiple fills))
(< (count fills) shp/MAX-FILLS))
(not (= :multiple fills)))
state* (mf/use-state has-fills?)
open? (deref state*)
@ -73,12 +80,12 @@
(mf/use-fn
(mf/deps ids fills)
(fn [_]
(st/emit! (dc/add-fill ids {:color default-color
:opacity 1}))
(when (or (= :multiple fills)
(not (some? (seq fills))))
(open-content))))
(when can-add-fills?
(st/emit! (dc/add-fill ids {:color default-color
:opacity 1}))
(when (or (= :multiple fills)
(not (some? (seq fills))))
(open-content)))))
on-change
(fn [index]
@ -146,11 +153,12 @@
:title label
:class (stl/css-case :title-spacing-fill (not has-fills?))}
(when (and (not disable-remove?) (not (= :multiple fills)))
(when (not (= :multiple fills))
[:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.options.fill.add-fill")
:on-click on-add
:data-testid "add-fill"
:disabled (not can-add-fills?)
:icon "add"}])]]
(when open?
@ -167,7 +175,7 @@
(seq fills)
[:& h/sortable-container {}
(for [[index value] (d/enumerate (:fills values []))]
(for [[index value] (d/enumerate fills)]
[:> color-row* {:color (ctc/fill->shape-color value)
:key index
:index index

View file

@ -134,7 +134,7 @@
handler-state (mf/use-state {:display? false :offset 0 :hover nil})
cap-stops? (or (features/use-feature "render-wasm/v1") (contains? cfg/flags :binary-fills))
cap-stops? (or (features/use-feature "render-wasm/v1") (contains? cfg/flags :frontend-binary-fills))
can-add-stop? (if cap-stops? (< (count stops) shp/MAX-GRADIENT-STOPS) true)
endpoint-on-pointer-down
@ -525,7 +525,7 @@
shape (mf/deref shape-ref)
state (mf/deref refs/colorpicker)
gradient (:gradient state)
cap-stops? (or (features/use-feature "render-wasm/v1") (contains? cfg/flags :binary-fills))
cap-stops? (or (features/use-feature "render-wasm/v1") (contains? cfg/flags :frontend-binary-fills))
stops (if cap-stops?
(vec (take shp/MAX-GRADIENT-STOPS (:stops state)))
(:stops state))

View file

@ -13,6 +13,7 @@
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.types.path :as path]
[app.common.types.shape :as shp]
[app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid]
[app.config :as cf]
@ -241,33 +242,24 @@
[fills]
(h/call wasm/internal-module "_clear_shape_fills")
(keep (fn [fill]
(let [opacity (or (:fill-opacity fill) 1.0)
color (:fill-color fill)
gradient (:fill-color-gradient fill)
image (:fill-image fill)
offset (mem/alloc-bytes sr-fills/FILL-BYTE-SIZE)
(let [offset (mem/alloc-bytes sr-fills/FILL-BYTE-SIZE)
heap (mem/get-heap-u8)
dview (js/DataView. (.-buffer heap))]
(cond
(some? color)
(do
(sr-fills/write-solid-fill! offset dview (sr-clr/hex->u32argb color opacity))
(h/call wasm/internal-module "_add_shape_fill"))
(some? gradient)
(do
(sr-fills/write-gradient-fill! offset dview gradient opacity)
(h/call wasm/internal-module "_add_shape_fill"))
(some? image)
(let [id (dm/get-prop image :id)
buffer (uuid/get-u32 id)
cached-image? (h/call wasm/internal-module "_is_image_cached" (aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))]
(sr-fills/write-image-fill! offset dview id opacity (dm/get-prop image :width) (dm/get-prop image :height))
(h/call wasm/internal-module "_add_shape_fill")
(when (== cached-image? 0)
dview (js/DataView. (.-buffer heap))
image (:fill-image fill)]
(sr-fills/write-fill! offset dview fill)
(h/call wasm/internal-module "_add_shape_fill")
;; store image for image fills if not cached
(when (some? image)
(let [id (dm/get-prop image :id)
buffer (uuid/get-u32 id)
cached-image? (h/call wasm/internal-module "_is_image_cached"
(aget buffer 0)
(aget buffer 1)
(aget buffer 2)
(aget buffer 3))]
(when (zero? cached-image?)
(store-image id))))))
fills))
(take shp/MAX-FILLS fills)))
(defn set-shape-strokes
[strokes]

View file

@ -1,12 +1,12 @@
(ns app.render-wasm.serializers.fills
(:require
[app.common.data.macros :as dm]
[app.common.types.shape :as shp]
[app.common.uuid :as uuid]
[app.render-wasm.serializers.color :as clr]))
(def ^:private GRADIENT-STOP-SIZE 8)
(def GRADIENT-BYTE-SIZE 156)
(def SOLID-BYTE-SIZE 4)
(def IMAGE-BYTE-SIZE 28)
@ -14,6 +14,7 @@
;; FIXME: get it from the wasm module
(def FILL-BYTE-SIZE (+ 4 (max GRADIENT-BYTE-SIZE IMAGE-BYTE-SIZE SOLID-BYTE-SIZE)))
(defn write-solid-fill!
[offset dview argb]
(.setUint8 dview offset 0x00 true)
@ -60,4 +61,21 @@
stop-offset (:offset stop)]
(.setUint32 dview loop-offset argb true)
(.setFloat32 dview (+ loop-offset 4) stop-offset true)
(recur (rest stops) (+ loop-offset GRADIENT-STOP-SIZE)))))))
(recur (rest stops) (+ loop-offset GRADIENT-STOP-SIZE)))))))
(defn write-fill!
[offset dview fill]
(let [opacity (or (:fill-opacity fill) 1.0)
color (:fill-color fill)
gradient (:fill-color-gradient fill)
image (:fill-image fill)]
(cond
(some? color)
(write-solid-fill! offset dview (clr/hex->u32argb color opacity))
(some? gradient)
(write-gradient-fill! offset dview gradient opacity)
(some? image)
(let [id (dm/get-prop image :id)]
(write-image-fill! offset dview id opacity (dm/get-prop image :width) (dm/get-prop image :height))))))