0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-13 18:48:37 -05:00

🐛 Fixed issues with masks when coloring

This commit is contained in:
alonso.torres 2020-12-08 15:33:53 +01:00 committed by Hirunatan
parent 07d77c1320
commit 4b22615f97
7 changed files with 304 additions and 282 deletions

View file

@ -144,38 +144,36 @@
}
}
.element-list li.masked {
.element-children {
li:first-child {
position: relative;
.element-list li.masked > .element-children > li {
&:first-child {
position: relative;
&::before {
content: " ";
border-right: 1px solid $color-gray-40;
border-top: 1px solid $color-gray-40;
position: absolute;
width: 6px;
height: 6px;
transform: rotate(-45deg);
top: -1px;
left: -4px;
}
&::before {
content: " ";
border-right: 1px solid $color-gray-40;
border-top: 1px solid $color-gray-40;
position: absolute;
width: 6px;
height: 6px;
transform: rotate(-45deg);
top: -1px;
left: -4px;
}
}
li:last-child {
border-left: none;
position: relative;
&:last-child {
border-left: none;
position: relative;
&::after {
content: " ";
border-left: 1px solid $color-gray-40;
border-bottom: 1px solid $color-gray-40;
height: 1rem;
width: 0.3rem;
position: absolute;
top: 0;
left: 0;
}
&::after {
content: " ";
border-left: 1px solid $color-gray-40;
border-bottom: 1px solid $color-gray-40;
height: 1rem;
width: 0.3rem;
position: absolute;
top: 0;
left: 0;
}
}
}

View file

@ -32,6 +32,7 @@
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.texts :as dwtxt]
[app.main.data.workspace.transforms :as dwt]
[app.main.data.workspace.groups :as dwg]
[app.main.data.workspace.drawing :as dwd]
[app.main.data.workspace.drawing.path :as dwdp]
[app.main.repo :as rp]
@ -1011,11 +1012,12 @@
(ptk/reify ::set-shape-proportion-lock
ptk/WatchEvent
(watch [_ state stream]
(rx/of (dwc/update-shapes [id] (fn [shape]
(if-not lock
(assoc shape :proportion-lock false)
(-> (assoc shape :proportion-lock true)
(gpr/assign-proportions)))))))))
(letfn [(assign-proportions [shape]
(if-not lock
(assoc shape :proportion-lock false)
(-> (assoc shape :proportion-lock true)
(gpr/assign-proportions))))]
(rx/of (dwc/update-shapes [id] assign-proportions))))))
;; --- Update Shape Position
@ -1371,135 +1373,6 @@
(with-meta params
{:on-success image-uploaded})))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; GROUPS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def group-selected
(ptk/reify ::group-selected
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
selected (get-in state [:workspace-local :selected])
shapes (dws/shapes-for-grouping objects selected)]
(when-not (empty? shapes)
(let [[group rchanges uchanges] (dws/prepare-create-group page-id shapes "Group-" false)]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
(dwc/select-shapes (d/ordered-set (:id group))))))))))
(def ungroup-selected
(ptk/reify ::ungroup-selected
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
selected (get-in state [:workspace-local :selected])
group-id (first selected)
group (get objects group-id)]
(when (and (= 1 (count selected))
(= (:type group) :group))
(let [[rchanges uchanges]
(dws/prepare-remove-group page-id group objects)]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))))
(def mask-group
(ptk/reify ::mask-group
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
selected (get-in state [:workspace-local :selected])
shapes (dws/shapes-for-grouping objects selected)]
(when-not (empty? shapes)
(let [;; If the selected shape is a group, we can use it. If not,
;; create a new group and set it as masked.
[group rchanges uchanges]
(if (and (= (count shapes) 1)
(= (:type (first shapes)) :group))
[(first shapes) [] []]
(dws/prepare-create-group page-id shapes "Group-" true))
rchanges (d/concat rchanges
[{:type :mod-obj
:page-id page-id
:id (:id group)
:operations [{:type :set
:attr :masked-group?
:val true}]}
{:type :reg-objects
:page-id page-id
:shapes [(:id group)]}])
uchanges (conj uchanges
{:type :mod-obj
:page-id page-id
:id (:id group)
:operations [{:type :set
:attr :masked-group?
:val nil}]})
;; If the mask has the default color, change it automatically
;; to white, to have an opaque mask by default (user may change
;; it later to have different degrees of transparency).
mask (first shapes)
rchanges (if (not= (:fill-color mask) cp/default-color)
rchanges
(conj rchanges
{:type :mod-obj
:page-id page-id
:id (:id mask)
:operations [{:type :set
:attr :fill-color
:val "#ffffff"}]}))
uchanges (if (not= (:fill-color mask) cp/default-color)
uchanges
(conj uchanges
{:type :mod-obj
:page-id page-id
:id (:id mask)
:operations [{:type :set
:attr :fill-color
:val (:fill-color mask)}]}))]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
(dwc/select-shapes (d/ordered-set (:id group))))))))))
(def unmask-group
(ptk/reify ::unmask-group
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
selected (get-in state [:workspace-local :selected])]
(when (= (count selected) 1)
(let [group (get objects (first selected))
rchanges [{:type :mod-obj
:page-id page-id
:id (:id group)
:operations [{:type :set
:attr :masked-group?
:val nil}]}
{:type :reg-objects
:page-id page-id
:shapes [(:id group)]}]
uchanges [{:type :mod-obj
:page-id page-id
:id (:id group)
:operations [{:type :set
:attr :masked-group?
:val (:masked-group? group)}]}
{:type :reg-objects
:page-id page-id
:shapes [(:id group)]}]]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
(dwc/select-shapes (d/ordered-set (:id group))))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Interactions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -1631,6 +1504,13 @@
(d/export dwc/start-edition-mode)
(d/export dwdp/start-path-edit)
;; Groups
(d/export dwg/mask-group)
(d/export dwg/unmask-group)
(d/export dwg/group-selected)
(d/export dwg/ungroup-selected)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Shortcuts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -0,0 +1,199 @@
(ns app.main.data.workspace.groups
(:require
[app.common.data :as d]
[app.common.geom.shapes :as gsh]
[app.common.pages :as cp]
[app.common.pages-helpers :as cph]
[app.main.data.workspace.common :as dwc]
[app.main.data.workspace.selection :as dws]
[beicon.core :as rx]
[potok.core :as ptk]))
(defn shapes-for-grouping
[objects selected]
(->> selected
(map #(get objects %))
(filter #(not= :frame (:type %)))
(map #(assoc % ::index (cph/position-on-parent (:id %) objects)))
(sort-by ::index)))
(defn- make-group
[shapes prefix keep-name]
(let [selrect (gsh/selection-rect shapes)
frame-id (-> shapes first :frame-id)
parent-id (-> shapes first :parent-id)
group-name (if (and keep-name
(= (count shapes) 1)
(= (:type (first shapes)) :group))
(:name (first shapes))
(name (gensym prefix)))]
(-> (cp/make-minimal-group frame-id selrect group-name)
(gsh/setup selrect)
(assoc :shapes (mapv :id shapes)))))
(defn prepare-create-group
[page-id shapes prefix keep-name]
(let [group (make-group shapes prefix keep-name)
rchanges [{:type :add-obj
:id (:id group)
:page-id page-id
:frame-id (:frame-id (first shapes))
:parent-id (:parent-id (first shapes))
:obj group
:index (::index (first shapes))}
{:type :mov-objects
:page-id page-id
:parent-id (:id group)
:shapes (mapv :id shapes)}]
uchanges (conj
(mapv (fn [obj] {:type :mov-objects
:page-id page-id
:parent-id (:parent-id obj)
:index (::index obj)
:shapes [(:id obj)]})
shapes)
{:type :del-obj
:id (:id group)
:page-id page-id})]
[group rchanges uchanges]))
(defn prepare-remove-group
[page-id group objects]
(let [shapes (:shapes group)
parent-id (cph/get-parent (:id group) objects)
parent (get objects parent-id)
index-in-parent (->> (:shapes parent)
(map-indexed vector)
(filter #(#{(:id group)} (second %)))
(ffirst))
rchanges [{:type :mov-objects
:page-id page-id
:parent-id parent-id
:shapes shapes
:index index-in-parent}
{:type :del-obj
:page-id page-id
:id (:id group)}]
uchanges [{:type :add-obj
:page-id page-id
:id (:id group)
:frame-id (:frame-id group)
:obj (assoc group :shapes [])}
{:type :mov-objects
:page-id page-id
:parent-id (:id group)
:shapes shapes}
{:type :mov-objects
:page-id page-id
:parent-id parent-id
:shapes [(:id group)]
:index index-in-parent}]]
[rchanges uchanges]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; GROUPS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def group-selected
(ptk/reify ::group-selected
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
selected (get-in state [:workspace-local :selected])
shapes (shapes-for-grouping objects selected)]
(when-not (empty? shapes)
(let [[group rchanges uchanges] (prepare-create-group page-id shapes "Group-" false)]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
(dwc/select-shapes (d/ordered-set (:id group))))))))))
(def ungroup-selected
(ptk/reify ::ungroup-selected
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
selected (get-in state [:workspace-local :selected])
group-id (first selected)
group (get objects group-id)]
(when (and (= 1 (count selected))
(= (:type group) :group))
(let [[rchanges uchanges]
(prepare-remove-group page-id group objects)]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))))
(def mask-group
(ptk/reify ::mask-group
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
selected (get-in state [:workspace-local :selected])
shapes (shapes-for-grouping objects selected)]
(when-not (empty? shapes)
(let [;; If the selected shape is a group, we can use it. If not,
;; create a new group and set it as masked.
[group rchanges uchanges]
(if (and (= (count shapes) 1)
(= (:type (first shapes)) :group))
[(first shapes) [] []]
(prepare-create-group page-id shapes "Group-" true))
rchanges (d/concat rchanges
[{:type :mod-obj
:page-id page-id
:id (:id group)
:operations [{:type :set
:attr :masked-group?
:val true}]}
{:type :reg-objects
:page-id page-id
:shapes [(:id group)]}])
uchanges (conj uchanges
{:type :mod-obj
:page-id page-id
:id (:id group)
:operations [{:type :set
:attr :masked-group?
:val nil}]})]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
(dwc/select-shapes (d/ordered-set (:id group))))))))))
(def unmask-group
(ptk/reify ::unmask-group
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
selected (get-in state [:workspace-local :selected])]
(when (= (count selected) 1)
(let [group (get objects (first selected))
rchanges [{:type :mod-obj
:page-id page-id
:id (:id group)
:operations [{:type :set
:attr :masked-group?
:val nil}]}
{:type :reg-objects
:page-id page-id
:shapes [(:id group)]}]
uchanges [{:type :mod-obj
:page-id page-id
:id (:id group)
:operations [{:type :set
:attr :masked-group?
:val (:masked-group? group)}]}
{:type :reg-objects
:page-id page-id
:shapes [(:id group)]}]]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
(dwc/select-shapes (d/ordered-set (:id group))))))))))

View file

@ -17,7 +17,7 @@
[app.common.geom.shapes :as geom]
[app.main.data.messages :as dm]
[app.main.data.workspace.common :as dwc]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.groups :as dwg]
[app.main.data.workspace.libraries-helpers :as dwlh]
[app.common.pages :as cp]
[app.main.repo :as rp]
@ -182,7 +182,7 @@
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
selected (get-in state [:workspace-local :selected])
shapes (dws/shapes-for-grouping objects selected)]
shapes (dwg/shapes-for-grouping objects selected)]
(when-not (empty? shapes)
(let [;; If the selected shape is a group, we can use it. If not,
;; we need to create a group before creating the component.
@ -190,7 +190,7 @@
(if (and (= (count shapes) 1)
(= (:type (first shapes)) :group))
[(first shapes) [] []]
(dws/prepare-create-group page-id shapes "Component-" true))
(dwg/prepare-create-group page-id shapes "Component-" true))
[new-shape new-shapes updated-shapes]
(dwlh/make-component-shape group objects)

View file

@ -197,91 +197,6 @@
(rx/of (deselect-all) (select-shape (:id selected))))))))
;; --- Group shapes
(defn shapes-for-grouping
[objects selected]
(->> selected
(map #(get objects %))
(filter #(not= :frame (:type %)))
(map #(assoc % ::index (cph/position-on-parent (:id %) objects)))
(sort-by ::index)))
(defn- make-group
[shapes prefix keep-name]
(let [selrect (geom/selection-rect shapes)
frame-id (-> shapes first :frame-id)
parent-id (-> shapes first :parent-id)
group-name (if (and keep-name
(= (count shapes) 1)
(= (:type (first shapes)) :group))
(:name (first shapes))
(name (gensym prefix)))]
(-> (cp/make-minimal-group frame-id selrect group-name)
(geom/setup selrect)
(assoc :shapes (mapv :id shapes)))))
(defn prepare-create-group
[page-id shapes prefix keep-name]
(let [group (make-group shapes prefix keep-name)
rchanges [{:type :add-obj
:id (:id group)
:page-id page-id
:frame-id (:frame-id (first shapes))
:parent-id (:parent-id (first shapes))
:obj group
:index (::index (first shapes))}
{:type :mov-objects
:page-id page-id
:parent-id (:id group)
:shapes (mapv :id shapes)}]
uchanges (conj
(mapv (fn [obj] {:type :mov-objects
:page-id page-id
:parent-id (:parent-id obj)
:index (::index obj)
:shapes [(:id obj)]})
shapes)
{:type :del-obj
:id (:id group)
:page-id page-id})]
[group rchanges uchanges]))
(defn prepare-remove-group
[page-id group objects]
(let [shapes (:shapes group)
parent-id (cph/get-parent (:id group) objects)
parent (get objects parent-id)
index-in-parent (->> (:shapes parent)
(map-indexed vector)
(filter #(#{(:id group)} (second %)))
(ffirst))
rchanges [{:type :mov-objects
:page-id page-id
:parent-id parent-id
:shapes shapes
:index index-in-parent}
{:type :del-obj
:page-id page-id
:id (:id group)}]
uchanges [{:type :add-obj
:page-id page-id
:id (:id group)
:frame-id (:frame-id group)
:obj (assoc group :shapes [])}
{:type :mov-objects
:page-id page-id
:parent-id (:id group)
:shapes shapes}
{:type :mov-objects
:page-id page-id
:parent-id parent-id
:shapes [(:id group)]
:index index-in-parent}]]
[rchanges uchanges]))
;; --- Duplicate Shapes
(declare prepare-duplicate-change)
(declare prepare-duplicate-frame-change)

View file

@ -10,42 +10,37 @@
(ns app.main.ui.shapes.group
(:require
[rumext.alpha :as mf]
[cuerdas.core :as str]
[app.main.ui.shapes.attrs :as attrs]
[app.common.geom.shapes :as geom]))
[app.main.ui.shapes.mask :refer [mask-str mask-factory]]))
(defn group-shape
[shape-wrapper]
(mf/fnc group-shape
{::mf/wrap-props false}
[props]
(let [frame (unchecked-get props "frame")
shape (unchecked-get props "shape")
childs (unchecked-get props "childs")
expand-mask (unchecked-get props "expand-mask")
pointer-events (unchecked-get props "pointer-events")
mask (if (and (:masked-group? shape) (not expand-mask))
(first childs)
nil)
childs (if (and (:masked-group? shape) (not expand-mask))
(rest childs)
childs)
{:keys [id x y width height]} shape
transform (geom/transform-matrix shape)]
[:g.group {:pointer-events pointer-events
:mask (when (and mask (not expand-mask))
(str/fmt "url(#%s)" (:id mask)))}
(when mask
[:defs
[:mask {:id (:id mask)
:width width
:height height}
(let [render-mask (mask-factory shape-wrapper)]
(mf/fnc group-shape
{::mf/wrap-props false}
[props]
(let [frame (unchecked-get props "frame")
shape (unchecked-get props "shape")
childs (unchecked-get props "childs")
expand-mask (unchecked-get props "expand-mask")
pointer-events (unchecked-get props "pointer-events")
{:keys [id x y width height]} shape
show-mask? (and (:masked-group? shape) (not expand-mask))
mask (when show-mask? (first childs))
childs (if show-mask? (rest childs) childs)]
[:g.group
{:pointer-events pointer-events
:mask (when (and mask (not expand-mask)) (mask-str mask))}
(when mask
[:> render-mask #js {:frame frame :mask mask}])
(for [item childs]
[:& shape-wrapper {:frame frame
:shape mask}]]])
(for [item childs]
[:& shape-wrapper {:frame frame
:shape item
:key (:id item)}])])))
:shape item
:key (:id item)}])]))))

View file

@ -0,0 +1,35 @@
;; 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/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns app.main.ui.shapes.mask
(:require
[rumext.alpha :as mf]
[cuerdas.core :as str]))
(defn mask-str [mask]
(str/fmt "url(#%s)" (str (:id mask) "-mask")))
(defn mask-factory
[shape-wrapper]
(mf/fnc mask-shape
{::mf/wrap-props false}
[props]
(let [frame (unchecked-get props "frame")
mask (unchecked-get props "mask")]
[:defs
[:filter {:id (str (:id mask) "-filter")}
[:feFlood {:flood-color "white"}]
[:feComposite {:in "BackgroundImage"
:in2 "SourceGraphic"
:operator "in"
:result "comp"}]]
[:mask {:id (str (:id mask) "-mask")}
[:g {:filter (str/fmt "url(#%s)" (str (:id mask) "-filter"))}
[:& shape-wrapper {:frame frame :shape mask}]]]])))