mirror of
https://github.com/penpot/penpot.git
synced 2025-06-13 16:12:17 +02:00
🎉 Cap stop amount in UI for wasm (#6438)
* 🎉 Cap in the colorpicker the amount of stops a gradient can have * 🎉 Cap the stops amount in gradient handlers * 🎉 Disable add stop in gradient handlers (viewport + colorpicker) * ✨ Add integration test for gradient limits * 💄 Address PR suggestion
This commit is contained in:
parent
69cc4fb4c2
commit
91fbe8f8ef
10 changed files with 438 additions and 102 deletions
|
@ -13,8 +13,9 @@
|
|||
[app.common.schema :as sm]
|
||||
[app.common.text :as txt]
|
||||
[app.common.types.color :as ctc]
|
||||
[app.common.types.shape :refer [check-stroke]]
|
||||
[app.common.types.shape :as shp]
|
||||
[app.common.types.shape.shadow :refer [check-shadow]]
|
||||
[app.config :as cfg]
|
||||
[app.main.broadcast :as mbc]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.helpers :as dsh]
|
||||
|
@ -24,6 +25,7 @@
|
|||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.features :as features]
|
||||
[app.util.storage :as storage]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -421,7 +423,7 @@
|
|||
[ids stroke]
|
||||
|
||||
(assert
|
||||
(check-stroke stroke)
|
||||
(shp/check-stroke stroke)
|
||||
"expected a valid stroke struct")
|
||||
|
||||
(assert
|
||||
|
@ -821,39 +823,43 @@
|
|||
(update [_ state]
|
||||
(update state :colorpicker
|
||||
(fn [{:keys [stops editing-stop] :as state}]
|
||||
(if (cc/uniform-spread? stops)
|
||||
;; Add to uniform
|
||||
(let [stops (->> (cc/uniform-spread (first stops) (last stops) (inc (count stops)))
|
||||
(mapv split-color-components))]
|
||||
(-> state
|
||||
(assoc :current-color (get stops editing-stop))
|
||||
(assoc :stops stops)))
|
||||
(let [cap-stops? (or (features/active-feature? state "render-wasm/v1") (contains? cfg/flags :binary-fills))
|
||||
can-add-stop? (or (not cap-stops?) (< (count stops) shp/MAX-GRADIENT-STOPS))]
|
||||
(if can-add-stop?
|
||||
(if (cc/uniform-spread? stops)
|
||||
;; Add to uniform
|
||||
(let [stops (->> (cc/uniform-spread (first stops) (last stops) (inc (count stops)))
|
||||
(mapv split-color-components))]
|
||||
(-> state
|
||||
(assoc :current-color (get stops editing-stop))
|
||||
(assoc :stops stops)))
|
||||
|
||||
;; We add the stop to the middle point between the selected
|
||||
;; and the next one.
|
||||
;; If the last stop is selected then it's added between the
|
||||
;; last two stops.
|
||||
(let [index
|
||||
(if (= editing-stop (dec (count stops)))
|
||||
(dec editing-stop)
|
||||
editing-stop)
|
||||
;; We add the stop to the middle point between the selected
|
||||
;; and the next one.
|
||||
;; If the last stop is selected then it's added between the
|
||||
;; last two stops.
|
||||
(let [index
|
||||
(if (= editing-stop (dec (count stops)))
|
||||
(dec editing-stop)
|
||||
editing-stop)
|
||||
|
||||
{from-offset :offset} (get stops index)
|
||||
{to-offset :offset} (get stops (inc index))
|
||||
{from-offset :offset} (get stops index)
|
||||
{to-offset :offset} (get stops (inc index))
|
||||
|
||||
half-point-offset
|
||||
(+ from-offset (/ (- to-offset from-offset) 2))
|
||||
half-point-offset
|
||||
(+ from-offset (/ (- to-offset from-offset) 2))
|
||||
|
||||
new-stop (-> (cc/interpolate-gradient stops half-point-offset)
|
||||
(split-color-components))
|
||||
new-stop (-> (cc/interpolate-gradient stops half-point-offset)
|
||||
(split-color-components))
|
||||
|
||||
stops (conj stops new-stop)
|
||||
stops (into [] (sort-by :offset stops))
|
||||
editing-stop (d/index-of-pred stops #(= new-stop %))]
|
||||
(-> state
|
||||
(assoc :editing-stop editing-stop)
|
||||
(assoc :current-color (get stops editing-stop))
|
||||
(assoc :stops stops)))))))))
|
||||
stops (conj stops new-stop)
|
||||
stops (into [] (sort-by :offset stops))
|
||||
editing-stop (d/index-of-pred stops #(= new-stop %))]
|
||||
(-> state
|
||||
(assoc :editing-stop editing-stop)
|
||||
(assoc :current-color (get stops editing-stop))
|
||||
(assoc :stops stops))))
|
||||
state)))))))
|
||||
|
||||
(defn update-colorpicker-add-stop
|
||||
[offset]
|
||||
|
@ -863,15 +869,18 @@
|
|||
(update state :colorpicker
|
||||
(fn [state]
|
||||
(let [stops (:stops state)
|
||||
new-stop (-> (cc/interpolate-gradient stops offset)
|
||||
(split-color-components))
|
||||
stops (conj stops new-stop)
|
||||
stops (into [] (sort-by :offset stops))
|
||||
editing-stop (d/index-of-pred stops #(= new-stop %))]
|
||||
(-> state
|
||||
(assoc :editing-stop editing-stop)
|
||||
(assoc :current-color (get stops editing-stop))
|
||||
(assoc :stops stops))))))))
|
||||
cap-stops? (or (features/active-feature? state "render-wasm/v1") (contains? cfg/flags :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))
|
||||
stops (conj stops new-stop)
|
||||
stops (into [] (sort-by :offset stops))
|
||||
editing-stop (d/index-of-pred stops #(= new-stop %))]
|
||||
(-> state
|
||||
(assoc :editing-stop editing-stop)
|
||||
(assoc :current-color (get stops editing-stop))
|
||||
(assoc :stops stops)))
|
||||
state)))))))
|
||||
|
||||
(defn update-colorpicker-stops
|
||||
[stops]
|
||||
|
@ -881,7 +890,8 @@
|
|||
(update state :colorpicker
|
||||
(fn [state]
|
||||
(let [stop (or (:editing-stop state) 0)
|
||||
stops (mapv split-color-components stops)]
|
||||
cap-stops? (or (features/active-feature? state "render-wasm/v1") (contains? cfg/flags :binary-fills))
|
||||
stops (mapv split-color-components (if cap-stops? (take shp/MAX-GRADIENT-STOPS stops) stops))]
|
||||
(-> state
|
||||
(assoc :current-color (get stops stop))
|
||||
(assoc :stops stops))))))))
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.types.shape :as shp]
|
||||
[app.config :as cfg]
|
||||
[app.main.data.event :as-alias ev]
|
||||
[app.main.data.modal :as modal]
|
||||
|
@ -20,6 +21,7 @@
|
|||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.media :as dwm]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||
|
@ -336,6 +338,8 @@
|
|||
(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))
|
||||
|
||||
tabs
|
||||
#js [#js {:aria-label (tr "workspace.libraries.colors.rgba")
|
||||
:icon ic/rgba
|
||||
|
@ -435,7 +439,7 @@
|
|||
(when (= selected-mode :gradient)
|
||||
[:> gradients*
|
||||
{:type (:type state)
|
||||
:stops (:stops state)
|
||||
:stops (if cap-stops? (vec (take shp/MAX-GRADIENT-STOPS (:stops state))) (:stops state))
|
||||
:editing-stop (:editing-stop state)
|
||||
:on-stop-edit-start handle-stop-edit-start
|
||||
:on-stop-edit-finish handle-stop-edit-finish
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.math :as mth]
|
||||
[app.common.types.shape :as shp]
|
||||
[app.config :as cfg]
|
||||
[app.main.features :as features]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input*]]
|
||||
[app.main.ui.components.reorder-handler :refer [reorder-handler]]
|
||||
[app.main.ui.components.select :refer [select]]
|
||||
|
@ -283,7 +286,9 @@
|
|||
(mf/deps on-reverse-stops)
|
||||
(fn []
|
||||
(when on-reverse-stops
|
||||
(on-reverse-stops))))]
|
||||
(on-reverse-stops))))
|
||||
cap-stops? (or (features/use-feature "render-wasm/v1") (contains? cfg/flags :binary-fills))
|
||||
add-stop-disabled? (when cap-stops? (>= (count stops) shp/MAX-GRADIENT-STOPS))]
|
||||
|
||||
[:div {:class (stl/css :gradient-panel)}
|
||||
[:div {:class (stl/css :gradient-preview)}
|
||||
|
@ -294,9 +299,10 @@
|
|||
:on-pointer-leave handle-preview-leave
|
||||
:on-pointer-move handle-preview-move
|
||||
:on-pointer-down handle-preview-down}
|
||||
[:div {:class (stl/css :gradient-preview-stop-preview)
|
||||
:style {:display (if (:hover? @preview-state) "block" "none")
|
||||
"--preview-position" (dm/str (* 100 (:offset @preview-state)) "%")}}]]
|
||||
(when (not add-stop-disabled?)
|
||||
[:div {:class (stl/css :gradient-preview-stop-preview)
|
||||
:style {:display (if (:hover? @preview-state) "block" "none")
|
||||
"--preview-position" (dm/str (* 100 (:offset @preview-state)) "%")}}])]
|
||||
|
||||
[:div {:class (stl/css :gradient-preview-stop-wrapper)}
|
||||
(for [[index {:keys [color offset r g b alpha]}] (d/enumerate stops)]
|
||||
|
@ -339,6 +345,7 @@
|
|||
:icon "switch"}]
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label "Add stop"
|
||||
:disabled add-stop-disabled?
|
||||
:on-click handle-add-stop
|
||||
:icon "add"}]]]
|
||||
|
||||
|
|
|
@ -15,7 +15,10 @@
|
|||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.points :as gsp]
|
||||
[app.common.math :as mth]
|
||||
[app.common.types.shape :as shp]
|
||||
[app.config :as cfg]
|
||||
[app.main.data.workspace.colors :as dc]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.workspace.viewport.viewport-ref :as uwvv]
|
||||
|
@ -131,6 +134,9 @@
|
|||
|
||||
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))
|
||||
can-add-stop? (if cap-stops? (< (count stops) shp/MAX-GRADIENT-STOPS) true)
|
||||
|
||||
endpoint-on-pointer-down
|
||||
(fn [position event]
|
||||
(dom/stop-propagation event)
|
||||
|
@ -164,7 +170,8 @@
|
|||
points-on-pointer-enter
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(swap! handler-state assoc :display? true)))
|
||||
(when can-add-stop?
|
||||
(swap! handler-state assoc :display? true))))
|
||||
|
||||
points-on-pointer-leave
|
||||
(mf/use-fn
|
||||
|
@ -177,17 +184,17 @@
|
|||
(fn [e]
|
||||
(dom/prevent-default e)
|
||||
(dom/stop-propagation e)
|
||||
|
||||
(let [raw-pt (dom/get-client-position e)
|
||||
position (uwvv/point->viewport raw-pt)
|
||||
lv (-> (gpt/to-vec from-p to-p) (gpt/unit))
|
||||
nv (gpt/normal-left lv)
|
||||
offset (-> (gsp/project-t position [from-p to-p] nv)
|
||||
(mth/precision 2))
|
||||
new-stop (cc/interpolate-gradient stops offset)
|
||||
stops (conj stops new-stop)
|
||||
stops (->> stops (sort-by :offset) (into []))]
|
||||
(st/emit! (dc/update-colorpicker-stops stops)))))
|
||||
(when can-add-stop?
|
||||
(let [raw-pt (dom/get-client-position e)
|
||||
position (uwvv/point->viewport raw-pt)
|
||||
lv (-> (gpt/to-vec from-p to-p) (gpt/unit))
|
||||
nv (gpt/normal-left lv)
|
||||
offset (-> (gsp/project-t position [from-p to-p] nv)
|
||||
(mth/precision 2))
|
||||
new-stop (cc/interpolate-gradient stops offset)
|
||||
stops (conj stops new-stop)
|
||||
stops (->> stops (sort-by :offset) (into []))]
|
||||
(st/emit! (dc/update-colorpicker-stops stops))))))
|
||||
|
||||
points-on-pointer-move
|
||||
(mf/use-fn
|
||||
|
@ -354,7 +361,7 @@
|
|||
:cx (:x width-p)
|
||||
:cy (:y width-p)
|
||||
:r (/ gradient-width-handler-radius-handler zoom)
|
||||
:fill "transpgarent"
|
||||
:fill "transparent"
|
||||
:on-pointer-down (partial endpoint-on-pointer-down :width-p)
|
||||
:on-pointer-enter (partial endpoint-on-pointer-enter :width-p)
|
||||
:on-pointer-leave (partial endpoint-on-pointer-leave :width-p)
|
||||
|
@ -518,7 +525,10 @@
|
|||
shape (mf/deref shape-ref)
|
||||
state (mf/deref refs/colorpicker)
|
||||
gradient (:gradient state)
|
||||
stops (:stops state)
|
||||
cap-stops? (or (features/use-feature "render-wasm/v1") (contains? cfg/flags :binary-fills))
|
||||
stops (if cap-stops?
|
||||
(vec (take shp/MAX-GRADIENT-STOPS (:stops state)))
|
||||
(:stops state))
|
||||
editing-stop (:editing-stop state)]
|
||||
|
||||
(when (and (some? gradient) (= id (:shape-id gradient)))
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
(ns app.render-wasm.serializers.fills
|
||||
(:require
|
||||
[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 ^:private GRADIENT-BASE-SIZE 28)
|
||||
;; TODO: Define in shape model
|
||||
(def ^:private MAX-GRADIENT-STOPS 16)
|
||||
|
||||
(def GRADIENT-BYTE-SIZE
|
||||
(+ GRADIENT-BASE-SIZE (* MAX-GRADIENT-STOPS GRADIENT-STOP-SIZE)))
|
||||
|
||||
(def GRADIENT-BYTE-SIZE 156)
|
||||
(def SOLID-BYTE-SIZE 4)
|
||||
(def IMAGE-BYTE-SIZE 28)
|
||||
|
||||
|
@ -48,7 +45,7 @@
|
|||
end-x (:end-x gradient)
|
||||
end-y (:end-y gradient)
|
||||
width (or (:width gradient) 0)
|
||||
stops (take MAX-GRADIENT-STOPS (:stops gradient))
|
||||
stops (take shp/MAX-GRADIENT-STOPS (:stops gradient))
|
||||
type (if (= (:type gradient) :linear) 0x01 0x02)]
|
||||
(.setUint8 dview offset type true)
|
||||
(.setFloat32 dview (+ offset 4) start-x true)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue