mirror of
https://github.com/penpot/penpot.git
synced 2025-06-06 18:01:37 +02:00
Merge pull request #4029 from penpot/alotor-bugfixes-9
Alotor bugfixes 9
This commit is contained in:
commit
1ef8da0414
33 changed files with 1149 additions and 806 deletions
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
- Select through stroke only rectangle [Taiga #5484](https://tree.taiga.io/project/penpot/issue/5484)
|
- Select through stroke only rectangle [Taiga #5484](https://tree.taiga.io/project/penpot/issue/5484)
|
||||||
- Override browser Ctrl+ and Ctrl- zoom with Penpot Zoom [Taiga #3200](https://tree.taiga.io/project/penpot/us/3200)
|
- Override browser Ctrl+ and Ctrl- zoom with Penpot Zoom [Taiga #3200](https://tree.taiga.io/project/penpot/us/3200)
|
||||||
|
- Reduce handlers for the flex layout gaps and paddings
|
||||||
|
|
||||||
### :bug: Bugs fixed
|
### :bug: Bugs fixed
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
[it shapes objects page-id file-id components-v2 prepare-create-group prepare-create-board]
|
[it shapes objects page-id file-id components-v2 prepare-create-group prepare-create-board]
|
||||||
(let [changes (pcb/empty-changes it page-id)
|
(let [changes (pcb/empty-changes it page-id)
|
||||||
|
|
||||||
|
from-singe-frame? (and (= 1 (count shapes)) (-> shapes first cfh/frame-shape?))
|
||||||
[root changes old-root-ids]
|
[root changes old-root-ids]
|
||||||
(if (and (= (count shapes) 1)
|
(if (and (= (count shapes) 1)
|
||||||
(or (and (= (:type (first shapes)) :group) (not components-v2))
|
(or (and (= (:type (first shapes)) :group) (not components-v2))
|
||||||
|
@ -72,6 +73,15 @@
|
||||||
|
|
||||||
[root changes (map :id shapes)]))
|
[root changes (map :id shapes)]))
|
||||||
|
|
||||||
|
changes
|
||||||
|
(cond-> changes
|
||||||
|
(not from-singe-frame?)
|
||||||
|
(pcb/update-shapes
|
||||||
|
(:shapes root)
|
||||||
|
(fn [shape]
|
||||||
|
(-> shape
|
||||||
|
(assoc :constraints-h :scale :constraints-v :scale)))))
|
||||||
|
|
||||||
objects' (assoc objects (:id root) root)
|
objects' (assoc objects (:id root) root)
|
||||||
|
|
||||||
[root-shape changes] (generate-add-component-changes changes root objects' file-id page-id components-v2)
|
[root-shape changes] (generate-add-component-changes changes root objects' file-id page-id components-v2)
|
||||||
|
|
|
@ -71,6 +71,23 @@
|
||||||
parent-id (or parent-id (dm/get-in objects [selected-id :parent-id]))
|
parent-id (or parent-id (dm/get-in objects [selected-id :parent-id]))
|
||||||
base-parent (get objects parent-id)
|
base-parent (get objects parent-id)
|
||||||
|
|
||||||
|
layout-props
|
||||||
|
(when (and (= 1 (count selected))
|
||||||
|
(ctl/any-layout? base-parent))
|
||||||
|
(let [shape (get objects selected-id)]
|
||||||
|
(select-keys shape ctl/layout-item-props)))
|
||||||
|
|
||||||
|
target-cell-id
|
||||||
|
(if (and (nil? target-cell-id)
|
||||||
|
(ctl/grid-layout? objects parent-id))
|
||||||
|
;; Find the top-left grid cell of the selected elements
|
||||||
|
(let [ncols (count (:layout-grid-columns base-parent))]
|
||||||
|
(->> selected
|
||||||
|
(map #(ctl/get-cell-by-shape-id base-parent %))
|
||||||
|
(apply min-key (fn [{:keys [row column]}] (+ (* ncols row) column)))
|
||||||
|
:id))
|
||||||
|
target-cell-id)
|
||||||
|
|
||||||
attrs {:type :frame
|
attrs {:type :frame
|
||||||
:x (:x srect)
|
:x (:x srect)
|
||||||
:y (:y srect)
|
:y (:y srect)
|
||||||
|
@ -90,12 +107,14 @@
|
||||||
:parent-id parent-id
|
:parent-id parent-id
|
||||||
:shapes (into [] selected))
|
:shapes (into [] selected))
|
||||||
|
|
||||||
:always
|
(some? layout-props)
|
||||||
(with-meta {:index new-index})
|
(d/patch-object layout-props)
|
||||||
|
|
||||||
(or (not= frame-id uuid/zero) without-fill?)
|
(or (not= frame-id uuid/zero) without-fill?)
|
||||||
(assoc :fills [] :hide-in-viewer true)))
|
(assoc :fills [] :hide-in-viewer true)))
|
||||||
|
|
||||||
|
shape (with-meta shape {:index new-index})
|
||||||
|
|
||||||
[shape changes]
|
[shape changes]
|
||||||
(prepare-add-shape changes shape objects)
|
(prepare-add-shape changes shape objects)
|
||||||
|
|
||||||
|
@ -105,15 +124,23 @@
|
||||||
changes
|
changes
|
||||||
(cond-> changes
|
(cond-> changes
|
||||||
(ctl/grid-layout? objects (:parent-id shape))
|
(ctl/grid-layout? objects (:parent-id shape))
|
||||||
(-> (cond-> (some? target-cell-id)
|
(-> (pcb/update-shapes
|
||||||
(pcb/update-shapes
|
[(:parent-id shape)]
|
||||||
[(:parent-id shape)]
|
(fn [parent objects]
|
||||||
(fn [parent]
|
;; This restores the grid layout before adding and moving the shapes
|
||||||
(-> parent
|
;; this is done because the add+move could have altered the layout and we
|
||||||
(assoc :layout-grid-cells (:layout-grid-cells base-parent))
|
;; want to do it after both operations are completed. Also here we could
|
||||||
(assoc-in [:layout-grid-cells target-cell-id :shapes] [id])
|
;; asign the new element to a target-cell
|
||||||
(assoc :position :auto)))))
|
(-> parent
|
||||||
(pcb/update-shapes [(:parent-id shape)] ctl/assign-cells {:with-objects? true})
|
(assoc :layout-grid-cells (:layout-grid-cells base-parent))
|
||||||
|
(assoc :layout-grid-rows (:layout-grid-rows base-parent))
|
||||||
|
(assoc :layout-grid-columns (:layout-grid-columns base-parent))
|
||||||
|
|
||||||
|
(cond-> (some? target-cell-id)
|
||||||
|
(assoc-in [:layout-grid-cells target-cell-id :shapes] [(:id shape)]))
|
||||||
|
(ctl/assign-cells objects)))
|
||||||
|
{:with-objects? true})
|
||||||
|
|
||||||
(pcb/reorder-grid-children [(:parent-id shape)])))]
|
(pcb/reorder-grid-children [(:parent-id shape)])))]
|
||||||
|
|
||||||
[shape changes])))))
|
[shape changes])))))
|
||||||
|
|
|
@ -414,7 +414,11 @@
|
||||||
(gco/transform-points shape-center (:transform group (gmt/matrix))))
|
(gco/transform-points shape-center (:transform group (gmt/matrix))))
|
||||||
|
|
||||||
;; Calculate the new selrect
|
;; Calculate the new selrect
|
||||||
new-selrect (grc/points->rect base-points)]
|
sr-transform (gmt/transform-in (gco/points->center new-points) (:transform-inverse group (gmt/matrix)))
|
||||||
|
new-selrect
|
||||||
|
(-> new-points
|
||||||
|
(gco/transform-points sr-transform)
|
||||||
|
(grc/points->rect))]
|
||||||
|
|
||||||
;; Updates the shape and the applytransform-rect will update the other properties
|
;; Updates the shape and the applytransform-rect will update the other properties
|
||||||
(-> group
|
(-> group
|
||||||
|
|
|
@ -83,18 +83,19 @@
|
||||||
:layout-grid-dir :layout-container
|
:layout-grid-dir :layout-container
|
||||||
:layout-grid-rows :layout-container
|
:layout-grid-rows :layout-container
|
||||||
:layout-grid-columns :layout-container
|
:layout-grid-columns :layout-container
|
||||||
:layout-grid-cells :layout-container
|
:layout-grid-cells :layout-container})
|
||||||
|
|
||||||
:layout-item-margin :layout-item
|
|
||||||
:layout-item-margin-type :layout-item
|
|
||||||
:layout-item-h-sizing :layout-item
|
|
||||||
:layout-item-v-sizing :layout-item
|
|
||||||
:layout-item-max-h :layout-item
|
|
||||||
:layout-item-min-h :layout-item
|
|
||||||
:layout-item-max-w :layout-item
|
|
||||||
:layout-item-min-w :layout-item
|
|
||||||
:layout-item-align-self :layout-item})
|
|
||||||
|
|
||||||
|
(def swap-keep-attrs
|
||||||
|
[:layout-item-margin
|
||||||
|
:layout-item-margin-type
|
||||||
|
:layout-item-h-sizing
|
||||||
|
:layout-item-v-sizing
|
||||||
|
:layout-item-max-h
|
||||||
|
:layout-item-min-h
|
||||||
|
:layout-item-max-w
|
||||||
|
:layout-item-min-w
|
||||||
|
:layout-item-absolute
|
||||||
|
:layout-item-z-index])
|
||||||
|
|
||||||
(defn instance-root?
|
(defn instance-root?
|
||||||
"Check if this shape is the head of a top instance."
|
"Check if this shape is the head of a top instance."
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
(ns app.common.types.container
|
(ns app.common.types.container
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.files.helpers :as cfh]
|
[app.common.files.helpers :as cfh]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
|
@ -287,7 +288,8 @@
|
||||||
component-shape (if components-v2
|
component-shape (if components-v2
|
||||||
(-> (get-shape component-page (:main-instance-id component))
|
(-> (get-shape component-page (:main-instance-id component))
|
||||||
(assoc :parent-id nil) ;; On v2 we force parent-id to nil in order to behave like v1
|
(assoc :parent-id nil) ;; On v2 we force parent-id to nil in order to behave like v1
|
||||||
(assoc :frame-id uuid/zero))
|
(assoc :frame-id uuid/zero)
|
||||||
|
(d/without-keys ctk/swap-keep-attrs))
|
||||||
(get-shape component (:id component)))
|
(get-shape component (:id component)))
|
||||||
|
|
||||||
orig-pos (gpt/point (:x component-shape) (:y component-shape))
|
orig-pos (gpt/point (:x component-shape) (:y component-shape))
|
||||||
|
|
|
@ -73,6 +73,18 @@
|
||||||
(def justify-items-types
|
(def justify-items-types
|
||||||
#{:start :end :center :stretch})
|
#{:start :end :center :stretch})
|
||||||
|
|
||||||
|
(def layout-item-props
|
||||||
|
[:layout-item-margin
|
||||||
|
:layout-item-margin-type
|
||||||
|
:layout-item-h-sizing
|
||||||
|
:layout-item-v-sizing
|
||||||
|
:layout-item-max-h
|
||||||
|
:layout-item-min-h
|
||||||
|
:layout-item-max-w
|
||||||
|
:layout-item-min-w
|
||||||
|
:layout-item-absolute
|
||||||
|
:layout-item-z-index])
|
||||||
|
|
||||||
(sm/def! ::layout-attrs
|
(sm/def! ::layout-attrs
|
||||||
[:map {:title "LayoutAttrs"}
|
[:map {:title "LayoutAttrs"}
|
||||||
[:layout {:optional true} [::sm/one-of layout-types]]
|
[:layout {:optional true} [::sm/one-of layout-types]]
|
||||||
|
|
|
@ -117,17 +117,17 @@
|
||||||
(st/emit! (initialize)))
|
(st/emit! (initialize)))
|
||||||
|
|
||||||
(defn ^:export reinit
|
(defn ^:export reinit
|
||||||
[]
|
([]
|
||||||
;; NOTE: in cases of some strange behavior after hot-reload,
|
(reinit false))
|
||||||
;; uncomment this lines; they make a hard-rerender instead
|
([hard?]
|
||||||
;; soft-rerender.
|
;; The hard flag will force to unmount the whole UI and will redraw every component
|
||||||
;;
|
(when hard?
|
||||||
;; (mf/unmount! app-root)
|
(mf/unmount! app-root)
|
||||||
;; (mf/unmount! modal-root)
|
(mf/unmount! modal-root)
|
||||||
;; (set! app-root (mf/create-root (dom/get-element "app")))
|
(set! app-root (mf/create-root (dom/get-element "app")))
|
||||||
;; (set! modal-root (mf/create-root (dom/get-element "modal")))
|
(set! modal-root (mf/create-root (dom/get-element "modal"))))
|
||||||
(st/emit! (ev/initialize))
|
(st/emit! (ev/initialize))
|
||||||
(init-ui))
|
(init-ui)))
|
||||||
|
|
||||||
(defn ^:dev/after-load after-load
|
(defn ^:dev/after-load after-load
|
||||||
[]
|
[]
|
||||||
|
|
|
@ -829,7 +829,7 @@
|
||||||
0)))))
|
0)))))
|
||||||
|
|
||||||
(defn- add-component-for-swap
|
(defn- add-component-for-swap
|
||||||
[shape file-id id-new-component index target-cell]
|
[shape file-id id-new-component index target-cell keep-props-values]
|
||||||
(dm/assert! (uuid? id-new-component))
|
(dm/assert! (uuid? id-new-component))
|
||||||
(dm/assert! (uuid? file-id))
|
(dm/assert! (uuid? file-id))
|
||||||
(ptk/reify ::add-component-for-swap
|
(ptk/reify ::add-component-for-swap
|
||||||
|
@ -856,8 +856,13 @@
|
||||||
(:parent-id shape)
|
(:parent-id shape)
|
||||||
(:frame-id shape))
|
(:frame-id shape))
|
||||||
|
|
||||||
;; We need to set the same index as the original shape
|
changes
|
||||||
changes (pcb/change-parent changes (:parent-id shape) [new-shape] index {:component-swap true})]
|
(-> changes
|
||||||
|
;; Restore the properties
|
||||||
|
(pcb/update-shapes [(:id new-shape)] #(d/patch-object % keep-props-values))
|
||||||
|
|
||||||
|
;; We need to set the same index as the original shape
|
||||||
|
(pcb/change-parent (:parent-id shape) [new-shape] index {:component-swap true}))]
|
||||||
|
|
||||||
;; First delete so we don't break the grid layout cells
|
;; First delete so we don't break the grid layout cells
|
||||||
(rx/of (dch/commit-changes changes)
|
(rx/of (dch/commit-changes changes)
|
||||||
|
@ -880,9 +885,12 @@
|
||||||
target-cell (when (ctl/grid-layout? parent)
|
target-cell (when (ctl/grid-layout? parent)
|
||||||
(ctl/get-cell-by-shape-id parent (:id shape)))
|
(ctl/get-cell-by-shape-id parent (:id shape)))
|
||||||
|
|
||||||
index (find-shape-index objects (:parent-id shape) (:id shape))]
|
index (find-shape-index objects (:parent-id shape) (:id shape))
|
||||||
|
|
||||||
|
;; Store the properties that need to be maintained when the component is swapped
|
||||||
|
keep-props-values (select-keys shape ctk/swap-keep-attrs)]
|
||||||
(rx/of (dwsh/delete-shapes nil (d/ordered-set (:id shape)) {:component-swap true})
|
(rx/of (dwsh/delete-shapes nil (d/ordered-set (:id shape)) {:component-swap true})
|
||||||
(add-component-for-swap shape file-id id-new-component index target-cell)
|
(add-component-for-swap shape file-id id-new-component index target-cell keep-props-values)
|
||||||
(ptk/data-event :layout/update [(:parent-id shape)]))))))
|
(ptk/data-event :layout/update [(:parent-id shape)]))))))
|
||||||
|
|
||||||
(defn component-multi-swap
|
(defn component-multi-swap
|
||||||
|
@ -1002,12 +1010,16 @@
|
||||||
(rx/of (dch/commit-changes (assoc changes ;; TODO a ver qué pasa con esto
|
(rx/of (dch/commit-changes (assoc changes ;; TODO a ver qué pasa con esto
|
||||||
:file-id file-id))))
|
:file-id file-id))))
|
||||||
(when-not (empty? updated-frames)
|
(when-not (empty? updated-frames)
|
||||||
(->> (rx/from updated-frames)
|
(rx/merge
|
||||||
(rx/mapcat (fn [shape]
|
(rx/of (ptk/data-event :layout/update (map :id updated-frames)))
|
||||||
(rx/of
|
(->> (rx/from updated-frames)
|
||||||
(dwt/clear-thumbnail file-id (:page-id shape) (:id shape) "frame")
|
(rx/mapcat
|
||||||
(when-not (= (:frame-id shape) uuid/zero)
|
(fn [shape]
|
||||||
(dwt/clear-thumbnail file-id (:page-id shape) (:frame-id shape) "frame")))))))
|
(rx/of
|
||||||
|
(dwt/clear-thumbnail file-id (:page-id shape) (:id shape) "frame")
|
||||||
|
(when-not (= (:frame-id shape) uuid/zero)
|
||||||
|
(dwt/clear-thumbnail file-id (:page-id shape) (:frame-id shape) "frame"))))))))
|
||||||
|
|
||||||
(when (not= file-id library-id)
|
(when (not= file-id library-id)
|
||||||
;; When we have just updated the library file, give some time for the
|
;; When we have just updated the library file, give some time for the
|
||||||
;; update to finish, before marking this file as synced.
|
;; update to finish, before marking this file as synced.
|
||||||
|
|
|
@ -52,6 +52,14 @@
|
||||||
(when-let [editor (:workspace-editor state)]
|
(when-let [editor (:workspace-editor state)]
|
||||||
(ts/schedule #(.focus ^js editor))))))
|
(ts/schedule #(.focus ^js editor))))))
|
||||||
|
|
||||||
|
(defn gen-name
|
||||||
|
[editor]
|
||||||
|
(when (some? editor)
|
||||||
|
(let [result
|
||||||
|
(-> (ted/get-editor-current-plain-text editor)
|
||||||
|
(txt/generate-shape-name))]
|
||||||
|
(when (not= result "") result))))
|
||||||
|
|
||||||
(defn update-editor-state
|
(defn update-editor-state
|
||||||
[{:keys [id] :as shape} editor-state]
|
[{:keys [id] :as shape} editor-state]
|
||||||
(ptk/reify ::update-editor-state
|
(ptk/reify ::update-editor-state
|
||||||
|
@ -62,7 +70,7 @@
|
||||||
(update state :workspace-editor-state dissoc id)))))
|
(update state :workspace-editor-state dissoc id)))))
|
||||||
|
|
||||||
(defn finalize-editor-state
|
(defn finalize-editor-state
|
||||||
[id]
|
[id update-name?]
|
||||||
(ptk/reify ::finalize-editor-state
|
(ptk/reify ::finalize-editor-state
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
|
@ -72,8 +80,8 @@
|
||||||
editor-state (get-in state [:workspace-editor-state id])
|
editor-state (get-in state [:workspace-editor-state id])
|
||||||
content (-> editor-state
|
content (-> editor-state
|
||||||
(ted/get-editor-current-content))
|
(ted/get-editor-current-content))
|
||||||
text (-> (ted/get-editor-current-plain-text editor-state)
|
name (gen-name editor-state)
|
||||||
(txt/generate-shape-name))
|
|
||||||
new-shape? (nil? (:content shape))]
|
new-shape? (nil? (:content shape))]
|
||||||
(if (ted/content-has-text? content)
|
(if (ted/content-has-text? content)
|
||||||
(let [content (d/merge (ted/export-content content)
|
(let [content (d/merge (ted/export-content content)
|
||||||
|
@ -93,8 +101,8 @@
|
||||||
(assoc :content content)
|
(assoc :content content)
|
||||||
(cond-> position-data
|
(cond-> position-data
|
||||||
(assoc :position-data position-data))
|
(assoc :position-data position-data))
|
||||||
(cond-> new-shape?
|
(cond-> (and update-name? (some? name))
|
||||||
(assoc :name text))
|
(assoc :name name))
|
||||||
(cond-> (or (some? width) (some? height))
|
(cond-> (or (some? width) (some? height))
|
||||||
(gsh/transform-shape (ctm/change-size shape width height))))))
|
(gsh/transform-shape (ctm/change-size shape width height))))))
|
||||||
{:undo-group (when new-shape? id)})))))
|
{:undo-group (when new-shape? id)})))))
|
||||||
|
@ -104,29 +112,31 @@
|
||||||
(dwsh/delete-shapes #{id})))))))))
|
(dwsh/delete-shapes #{id})))))))))
|
||||||
|
|
||||||
(defn initialize-editor-state
|
(defn initialize-editor-state
|
||||||
[{:keys [id content] :as shape} decorator]
|
[{:keys [id name content] :as shape} decorator]
|
||||||
(ptk/reify ::initialize-editor-state
|
(ptk/reify ::initialize-editor-state
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [text-state (some->> content ted/import-content)
|
(let [text-state (some->> content ted/import-content)
|
||||||
attrs (d/merge txt/default-text-attrs
|
attrs (d/merge txt/default-text-attrs
|
||||||
(get-in state [:workspace-global :default-font]))
|
(get-in state [:workspace-global :default-font]))
|
||||||
editor (cond-> (ted/create-editor-state text-state decorator)
|
editor (cond-> (ted/create-editor-state text-state decorator)
|
||||||
(and (nil? content) (some? attrs))
|
(and (nil? content) (some? attrs))
|
||||||
(ted/update-editor-current-block-data attrs))]
|
(ted/update-editor-current-block-data attrs))]
|
||||||
(-> state
|
(-> state
|
||||||
(assoc-in [:workspace-editor-state id] editor))))
|
(assoc-in [:workspace-editor-state id] editor))))
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ _ stream]
|
(watch [_ state stream]
|
||||||
;; We need to finalize editor on two main events: (1) when user
|
;; We need to finalize editor on two main events: (1) when user
|
||||||
;; explicitly navigates to other section or page; (2) when user
|
;; explicitly navigates to other section or page; (2) when user
|
||||||
;; leaves the editor.
|
;; leaves the editor.
|
||||||
(->> (rx/merge
|
(let [editor (dm/get-in state [:workspace-editor-state id])
|
||||||
(rx/filter (ptk/type? ::rt/navigate) stream)
|
update-name? (or (nil? content) (= name (gen-name editor)))]
|
||||||
(rx/filter #(= ::finalize-editor-state %) stream))
|
(->> (rx/merge
|
||||||
(rx/take 1)
|
(rx/filter (ptk/type? ::rt/navigate) stream)
|
||||||
(rx/map #(finalize-editor-state id))))))
|
(rx/filter #(= ::finalize-editor-state %) stream))
|
||||||
|
(rx/take 1)
|
||||||
|
(rx/map #(finalize-editor-state id update-name?)))))))
|
||||||
|
|
||||||
(defn select-all
|
(defn select-all
|
||||||
"Select all content of the current editor. When not editor found this
|
"Select all content of the current editor. When not editor found this
|
||||||
|
|
16
frontend/src/app/main/ui/flex_controls.cljs
Normal file
16
frontend/src/app/main/ui/flex_controls.cljs
Normal 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
|
||||||
|
|
||||||
|
(ns app.main.ui.flex-controls
|
||||||
|
(:require
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.main.ui.flex-controls.gap :as fcg]
|
||||||
|
[app.main.ui.flex-controls.margin :as fcm]
|
||||||
|
[app.main.ui.flex-controls.padding :as fcp]))
|
||||||
|
|
||||||
|
(dm/export fcg/gap-control)
|
||||||
|
(dm/export fcm/margin-control)
|
||||||
|
(dm/export fcp/padding-control)
|
35
frontend/src/app/main/ui/flex_controls/common.cljs
Normal file
35
frontend/src/app/main/ui/flex_controls/common.cljs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
(ns app.main.ui.flex-controls.common
|
||||||
|
(:require
|
||||||
|
[app.main.ui.formats :as fmt]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
;; ------------------------------------------------
|
||||||
|
;; CONSTANTS
|
||||||
|
;; ------------------------------------------------
|
||||||
|
|
||||||
|
(def font-size 11)
|
||||||
|
(def distance-color "var(--color-distance)")
|
||||||
|
(def distance-text-color "var(--color-white)")
|
||||||
|
(def warning-color "var(--color-warning)")
|
||||||
|
(def flex-display-pill-width 40)
|
||||||
|
(def flex-display-pill-height 20)
|
||||||
|
(def flex-display-pill-border-radius 4)
|
||||||
|
|
||||||
|
(mf/defc flex-display-pill
|
||||||
|
[{:keys [x y width height font-size border-radius value color]}]
|
||||||
|
[:g.distance-pill
|
||||||
|
[:rect {:x x
|
||||||
|
:y y
|
||||||
|
:width width
|
||||||
|
:height height
|
||||||
|
:rx border-radius
|
||||||
|
:ry border-radius
|
||||||
|
:style {:fill color}}]
|
||||||
|
|
||||||
|
[:text {:x (+ x (/ width 2))
|
||||||
|
:y (+ y (/ height 2))
|
||||||
|
:text-anchor "middle"
|
||||||
|
:dominant-baseline "central"
|
||||||
|
:style {:fill distance-text-color
|
||||||
|
:font-size font-size}}
|
||||||
|
(fmt/format-number (or value 0))]])
|
311
frontend/src/app/main/ui/flex_controls/gap.cljs
Normal file
311
frontend/src/app/main/ui/flex_controls/gap.cljs
Normal file
|
@ -0,0 +1,311 @@
|
||||||
|
;; 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
|
||||||
|
|
||||||
|
(ns app.main.ui.flex-controls.gap
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.files.helpers :as cfh]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.common.geom.shapes.flex-layout :as gsl]
|
||||||
|
[app.common.geom.shapes.points :as gpo]
|
||||||
|
[app.common.types.modifiers :as ctm]
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
|
[app.main.data.workspace.modifiers :as dwm]
|
||||||
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.ui.css-cursors :as cur]
|
||||||
|
[app.main.ui.flex-controls.common :as fcc]
|
||||||
|
[app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(mf/defc gap-display
|
||||||
|
[{:keys [frame-id zoom gap-type gap on-pointer-enter on-pointer-leave
|
||||||
|
rect-data hover? selected? mouse-pos hover-value
|
||||||
|
on-move-selected on-context-menu]}]
|
||||||
|
(let [resizing (mf/use-var nil)
|
||||||
|
start (mf/use-var nil)
|
||||||
|
original-value (mf/use-var 0)
|
||||||
|
negate? (:resize-negate? rect-data)
|
||||||
|
axis (:resize-axis rect-data)
|
||||||
|
|
||||||
|
on-pointer-down
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps frame-id gap-type gap)
|
||||||
|
(fn [event]
|
||||||
|
(dom/capture-pointer event)
|
||||||
|
(reset! resizing gap-type)
|
||||||
|
(reset! start (dom/get-client-position event))
|
||||||
|
(reset! original-value (:initial-value rect-data))))
|
||||||
|
|
||||||
|
on-lost-pointer-capture
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps frame-id gap-type gap)
|
||||||
|
(fn [event]
|
||||||
|
(dom/release-pointer event)
|
||||||
|
(reset! resizing nil)
|
||||||
|
(reset! start nil)
|
||||||
|
(reset! original-value 0)
|
||||||
|
(st/emit! (dwm/apply-modifiers))))
|
||||||
|
|
||||||
|
on-pointer-move
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps frame-id gap-type gap)
|
||||||
|
(fn [event]
|
||||||
|
(let [pos (dom/get-client-position event)]
|
||||||
|
(reset! mouse-pos (point->viewport pos))
|
||||||
|
(when (= @resizing gap-type)
|
||||||
|
(let [delta (-> (gpt/to-vec @start pos)
|
||||||
|
(cond-> negate? gpt/negate)
|
||||||
|
(get axis))
|
||||||
|
val (int (max (+ @original-value (/ delta zoom)) 0))
|
||||||
|
layout-gap (assoc gap gap-type val)
|
||||||
|
modifiers (dwm/create-modif-tree [frame-id] (ctm/change-property (ctm/empty) :layout-gap layout-gap))]
|
||||||
|
|
||||||
|
(reset! hover-value val)
|
||||||
|
(st/emit! (dwm/set-modifiers modifiers)))))))]
|
||||||
|
|
||||||
|
[:g.gap-rect
|
||||||
|
[:rect.info-area
|
||||||
|
{:x (:x rect-data)
|
||||||
|
:y (:y rect-data)
|
||||||
|
:width (:width rect-data)
|
||||||
|
:height (:height rect-data)
|
||||||
|
:on-pointer-enter on-pointer-enter
|
||||||
|
:on-pointer-leave on-pointer-leave
|
||||||
|
:on-pointer-move on-pointer-move
|
||||||
|
:on-pointer-down on-move-selected
|
||||||
|
:on-context-menu on-context-menu
|
||||||
|
|
||||||
|
:style {:fill (if (or hover? selected?) fcc/distance-color "none")
|
||||||
|
:opacity (if selected? 0.5 0.25)}}]
|
||||||
|
|
||||||
|
(let [handle-width
|
||||||
|
(if (= axis :x)
|
||||||
|
(/ 2 zoom)
|
||||||
|
(min (* (:width rect-data) 0.5) (/ 20 zoom)))
|
||||||
|
|
||||||
|
handle-height
|
||||||
|
(if (= axis :y)
|
||||||
|
(/ 2 zoom)
|
||||||
|
(min (* (:height rect-data) 0.5) (/ 30 zoom)))]
|
||||||
|
[:rect.handle
|
||||||
|
{:x (+ (:x rect-data) (/ (- (:width rect-data) handle-width) 2))
|
||||||
|
:y (+ (:y rect-data) (/ (- (:height rect-data) handle-height) 2))
|
||||||
|
:width handle-width
|
||||||
|
:height handle-height
|
||||||
|
:on-pointer-enter on-pointer-enter
|
||||||
|
:on-pointer-leave on-pointer-leave
|
||||||
|
:on-pointer-down on-pointer-down
|
||||||
|
:on-lost-pointer-capture on-lost-pointer-capture
|
||||||
|
:on-pointer-move on-pointer-move
|
||||||
|
:on-context-menu on-context-menu
|
||||||
|
:class (when (or hover? selected?)
|
||||||
|
(if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90)))
|
||||||
|
:style {:fill (if (or hover? selected?) fcc/distance-color "none")
|
||||||
|
:opacity (if selected? 0 1)}}])]))
|
||||||
|
|
||||||
|
(mf/defc gap-rects
|
||||||
|
[{:keys [frame zoom on-move-selected on-context-menu]}]
|
||||||
|
(let [frame-id (:id frame)
|
||||||
|
saved-dir (:layout-flex-dir frame)
|
||||||
|
is-col? (or (= :column saved-dir) (= :column-reverse saved-dir))
|
||||||
|
flip-x (:flip-x frame)
|
||||||
|
flip-y (:flip-y frame)
|
||||||
|
pill-width (/ fcc/flex-display-pill-width zoom)
|
||||||
|
pill-height (/ fcc/flex-display-pill-height zoom)
|
||||||
|
workspace-modifiers (mf/deref refs/workspace-modifiers)
|
||||||
|
gap-selected (mf/deref refs/workspace-gap-selected)
|
||||||
|
hover (mf/use-var nil)
|
||||||
|
hover-value (mf/use-var 0)
|
||||||
|
mouse-pos (mf/use-var nil)
|
||||||
|
padding (:layout-padding frame)
|
||||||
|
gap (:layout-gap frame)
|
||||||
|
{:keys [width height x1 y1]} (:selrect frame)
|
||||||
|
on-pointer-enter (fn [hover-type val]
|
||||||
|
(reset! hover hover-type)
|
||||||
|
(reset! hover-value val))
|
||||||
|
|
||||||
|
on-pointer-leave #(reset! hover nil)
|
||||||
|
negate {:column-gap (if flip-x true false)
|
||||||
|
:row-gap (if flip-y true false)}
|
||||||
|
|
||||||
|
objects (wsh/lookup-page-objects @st/state)
|
||||||
|
children (->> (cfh/get-immediate-children objects frame-id)
|
||||||
|
(remove ctl/position-absolute?))
|
||||||
|
|
||||||
|
children-to-display (if (or (= :row-reverse saved-dir)
|
||||||
|
(= :column-reverse saved-dir))
|
||||||
|
(drop-last children)
|
||||||
|
(rest children))
|
||||||
|
children-to-display (->> children-to-display
|
||||||
|
(map #(gsh/transform-shape % (get-in workspace-modifiers [(:id %) :modifiers]))))
|
||||||
|
|
||||||
|
wrap-blocks
|
||||||
|
(let [block-children (->> children
|
||||||
|
(map #(vector (gpo/parent-coords-bounds (:points %) (:points frame)) %)))
|
||||||
|
bounds (d/lazy-map (keys objects) #(gsh/shape->points (get objects %)))
|
||||||
|
|
||||||
|
layout-data (gsl/calc-layout-data frame (:points frame) block-children bounds objects)
|
||||||
|
layout-bounds (:layout-bounds layout-data)
|
||||||
|
xv #(gpo/start-hv layout-bounds %)
|
||||||
|
yv #(gpo/start-vv layout-bounds %)]
|
||||||
|
(for [{:keys [start-p line-width line-height layout-gap-row layout-gap-col num-children]} (:layout-lines layout-data)]
|
||||||
|
(let [line-width (if is-col? line-width (+ line-width (* (dec num-children) layout-gap-row)))
|
||||||
|
line-height (if is-col? (+ line-height (* (dec num-children) layout-gap-col)) line-height)
|
||||||
|
end-p (-> start-p (gpt/add (xv line-width)) (gpt/add (yv line-height)))]
|
||||||
|
{:x1 (min (:x start-p) (:x end-p))
|
||||||
|
:y1 (min (:y start-p) (:y end-p))
|
||||||
|
:x2 (max (:x start-p) (:x end-p))
|
||||||
|
:y2 (max (:y start-p) (:y end-p))})))
|
||||||
|
|
||||||
|
block-contains
|
||||||
|
(fn [x y block]
|
||||||
|
(if is-col?
|
||||||
|
(<= (:x1 block) x (:x2 block))
|
||||||
|
(<= (:y1 block) y (:y2 block))))
|
||||||
|
|
||||||
|
get-container-block
|
||||||
|
(fn [shape]
|
||||||
|
(let [selrect (:selrect shape)
|
||||||
|
x (/ (+ (:x1 selrect) (:x2 selrect)) 2)
|
||||||
|
y (/ (+ (:y1 selrect) (:y2 selrect)) 2)]
|
||||||
|
(->> wrap-blocks
|
||||||
|
(filter #(block-contains x y %))
|
||||||
|
first)))
|
||||||
|
|
||||||
|
create-cgdd
|
||||||
|
(fn [shape]
|
||||||
|
(let [block (get-container-block shape)
|
||||||
|
x (if flip-x
|
||||||
|
(- (:x1 (:selrect shape))
|
||||||
|
(get-in shape [:layout-item-margin :m2])
|
||||||
|
(:column-gap gap))
|
||||||
|
(+ (:x2 (:selrect shape)) (get-in shape [:layout-item-margin :m2])))
|
||||||
|
y (:y1 block)
|
||||||
|
h (- (:y2 block) (:y1 block))]
|
||||||
|
{:x x
|
||||||
|
:y y
|
||||||
|
:height h
|
||||||
|
:width (:column-gap gap)
|
||||||
|
:initial-value (:column-gap gap)
|
||||||
|
:resize-type :left
|
||||||
|
:resize-axis :x
|
||||||
|
:resize-negate? (:column-gap negate)
|
||||||
|
:gap-type (if is-col? :row-gap :column-gap)}))
|
||||||
|
|
||||||
|
create-cgdd-block
|
||||||
|
(fn [block]
|
||||||
|
(let [x (if flip-x
|
||||||
|
(- (:x1 block) (:column-gap gap))
|
||||||
|
(:x2 block))
|
||||||
|
y (if flip-y
|
||||||
|
(+ y1 (:p3 padding))
|
||||||
|
(+ y1 (:p1 padding)))
|
||||||
|
h (- height (+ (:p1 padding) (:p3 padding)))]
|
||||||
|
{:x x
|
||||||
|
:y y
|
||||||
|
:width (:column-gap gap)
|
||||||
|
:height h
|
||||||
|
:initial-value (:column-gap gap)
|
||||||
|
:resize-type :left
|
||||||
|
:resize-axis :x
|
||||||
|
:resize-negate? (:column-gap negate)
|
||||||
|
:gap-type (if is-col? :column-gap :row-gap)}))
|
||||||
|
|
||||||
|
create-rgdd
|
||||||
|
(fn [shape]
|
||||||
|
(let [block (get-container-block shape)
|
||||||
|
x (:x1 block)
|
||||||
|
y (if flip-y
|
||||||
|
(- (:y1 (:selrect shape))
|
||||||
|
(get-in shape [:layout-item-margin :m3])
|
||||||
|
(:row-gap gap))
|
||||||
|
(+ (:y2 (:selrect shape)) (get-in shape [:layout-item-margin :m3])))
|
||||||
|
w (- (:x2 block) (:x1 block))]
|
||||||
|
{:x x
|
||||||
|
:y y
|
||||||
|
:width w
|
||||||
|
:height (:row-gap gap)
|
||||||
|
:initial-value (:row-gap gap)
|
||||||
|
:resize-type :bottom
|
||||||
|
:resize-axis :y
|
||||||
|
:resize-negate? (:row-gap negate)
|
||||||
|
:gap-type (if is-col? :row-gap :column-gap)}))
|
||||||
|
|
||||||
|
create-rgdd-block
|
||||||
|
(fn [block]
|
||||||
|
(let [x (if flip-x
|
||||||
|
(+ x1 (:p2 padding))
|
||||||
|
(+ x1 (:p4 padding)))
|
||||||
|
y (if flip-y
|
||||||
|
(- (:y1 block) (:row-gap gap))
|
||||||
|
(:y2 block))
|
||||||
|
w (- width (+ (:p2 padding) (:p4 padding)))]
|
||||||
|
{:x x
|
||||||
|
:y y
|
||||||
|
:width w
|
||||||
|
:height (:row-gap gap)
|
||||||
|
:initial-value (:row-gap gap)
|
||||||
|
:resize-type :bottom
|
||||||
|
:resize-axis :y
|
||||||
|
:resize-negate? (:row-gap negate)
|
||||||
|
:gap-type (if is-col? :column-gap :row-gap)}))
|
||||||
|
|
||||||
|
display-blocks (if is-col?
|
||||||
|
(->> (drop-last wrap-blocks)
|
||||||
|
(map create-cgdd-block))
|
||||||
|
(->> (drop-last wrap-blocks)
|
||||||
|
(map create-rgdd-block)))
|
||||||
|
|
||||||
|
display-children (if is-col?
|
||||||
|
(->> children-to-display
|
||||||
|
(map create-rgdd))
|
||||||
|
(->> children-to-display
|
||||||
|
(map create-cgdd)))]
|
||||||
|
|
||||||
|
[:g.gaps {:pointer-events "visible"}
|
||||||
|
(for [[index display-item] (d/enumerate (concat display-blocks display-children))]
|
||||||
|
(let [gap-type (:gap-type display-item)]
|
||||||
|
[:& gap-display {:key (str frame-id index)
|
||||||
|
:frame-id frame-id
|
||||||
|
:zoom zoom
|
||||||
|
:gap-type gap-type
|
||||||
|
:gap gap
|
||||||
|
:on-pointer-enter (partial on-pointer-enter gap-type (get gap gap-type))
|
||||||
|
:on-pointer-leave on-pointer-leave
|
||||||
|
:on-move-selected on-move-selected
|
||||||
|
:on-context-menu on-context-menu
|
||||||
|
:rect-data display-item
|
||||||
|
:hover? (= @hover gap-type)
|
||||||
|
:selected? (= gap-selected gap-type)
|
||||||
|
:mouse-pos mouse-pos
|
||||||
|
:hover-value hover-value}]))
|
||||||
|
|
||||||
|
(when @hover
|
||||||
|
[:& fcc/flex-display-pill
|
||||||
|
{:height pill-height
|
||||||
|
:width pill-width
|
||||||
|
:font-size (/ fcc/font-size zoom)
|
||||||
|
:border-radius (/ fcc/flex-display-pill-border-radius zoom)
|
||||||
|
:color fcc/distance-color
|
||||||
|
:x (:x @mouse-pos)
|
||||||
|
:y (- (:y @mouse-pos) pill-width)
|
||||||
|
:value @hover-value}])]))
|
||||||
|
|
||||||
|
|
||||||
|
(mf/defc gap-control
|
||||||
|
[{:keys [frame zoom on-move-selected on-context-menu]}]
|
||||||
|
(when frame
|
||||||
|
[:g.measurement-gaps {:pointer-events "none"}
|
||||||
|
[:g.hover-shapes
|
||||||
|
[:& gap-rects
|
||||||
|
{:frame frame
|
||||||
|
:zoom zoom
|
||||||
|
:on-move-selected on-move-selected
|
||||||
|
:on-context-menu on-context-menu}]]]))
|
185
frontend/src/app/main/ui/flex_controls/margin.cljs
Normal file
185
frontend/src/app/main/ui/flex_controls/margin.cljs
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
;; 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
|
||||||
|
|
||||||
|
(ns app.main.ui.flex-controls.margin
|
||||||
|
(:require
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.types.modifiers :as ctm]
|
||||||
|
[app.main.data.workspace.modifiers :as dwm]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.ui.css-cursors :as cur]
|
||||||
|
[app.main.ui.flex-controls.common :as fcc]
|
||||||
|
[app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(mf/defc margin-display [{:keys [shape-id zoom hover-all? hover-v? hover-h? margin-num margin on-pointer-enter on-pointer-leave
|
||||||
|
rect-data hover? selected? mouse-pos hover-value]}]
|
||||||
|
(let [resizing? (mf/use-var false)
|
||||||
|
start (mf/use-var nil)
|
||||||
|
original-value (mf/use-var 0)
|
||||||
|
negate? (true? (:resize-negate? rect-data))
|
||||||
|
axis (:resize-axis rect-data)
|
||||||
|
|
||||||
|
on-pointer-down
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps shape-id margin-num margin)
|
||||||
|
(fn [event]
|
||||||
|
(dom/capture-pointer event)
|
||||||
|
(reset! resizing? true)
|
||||||
|
(reset! start (dom/get-client-position event))
|
||||||
|
(reset! original-value (:initial-value rect-data))))
|
||||||
|
|
||||||
|
on-lost-pointer-capture
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps shape-id margin-num margin)
|
||||||
|
(fn [event]
|
||||||
|
(dom/release-pointer event)
|
||||||
|
(reset! resizing? false)
|
||||||
|
(reset! start nil)
|
||||||
|
(reset! original-value 0)
|
||||||
|
(st/emit! (dwm/apply-modifiers))))
|
||||||
|
|
||||||
|
on-pointer-move
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps shape-id margin-num margin hover-all? hover-v? hover-h?)
|
||||||
|
(fn [event]
|
||||||
|
(let [pos (dom/get-client-position event)]
|
||||||
|
(reset! mouse-pos (point->viewport pos))
|
||||||
|
(when @resizing?
|
||||||
|
(let [delta (-> (gpt/to-vec @start pos)
|
||||||
|
(cond-> negate? gpt/negate)
|
||||||
|
(get axis))
|
||||||
|
val (int (max (+ @original-value (/ delta zoom)) 0))
|
||||||
|
layout-item-margin (cond
|
||||||
|
hover-all? (assoc margin :m1 val :m2 val :m3 val :m4 val)
|
||||||
|
hover-v? (assoc margin :m1 val :m3 val)
|
||||||
|
hover-h? (assoc margin :m2 val :m4 val)
|
||||||
|
:else (assoc margin margin-num val))
|
||||||
|
layout-item-margin-type (if (= (:m1 margin) (:m2 margin) (:m3 margin) (:m4 margin)) :simple :multiple)
|
||||||
|
modifiers (dwm/create-modif-tree [shape-id]
|
||||||
|
(-> (ctm/empty)
|
||||||
|
(ctm/change-property :layout-item-margin layout-item-margin)
|
||||||
|
(ctm/change-property :layout-item-margin-type layout-item-margin-type)))]
|
||||||
|
(reset! hover-value val)
|
||||||
|
(st/emit! (dwm/set-modifiers modifiers)))))))]
|
||||||
|
|
||||||
|
[:rect.margin-rect
|
||||||
|
{:x (:x rect-data)
|
||||||
|
:y (:y rect-data)
|
||||||
|
:width (:width rect-data)
|
||||||
|
:height (:height rect-data)
|
||||||
|
:on-pointer-enter on-pointer-enter
|
||||||
|
:on-pointer-leave on-pointer-leave
|
||||||
|
:on-pointer-down on-pointer-down
|
||||||
|
:on-lost-pointer-capture on-lost-pointer-capture
|
||||||
|
:on-pointer-move on-pointer-move
|
||||||
|
:class (when (or hover? selected?)
|
||||||
|
(if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90)))
|
||||||
|
:style {:fill (if (or hover? selected?) fcc/warning-color "none")
|
||||||
|
:opacity (if selected? 0.5 0.25)}}]))
|
||||||
|
|
||||||
|
|
||||||
|
(mf/defc margin-rects [{:keys [shape frame zoom alt? shift?]}]
|
||||||
|
(let [shape-id (:id shape)
|
||||||
|
pill-width (/ fcc/flex-display-pill-width zoom)
|
||||||
|
pill-height (/ fcc/flex-display-pill-height zoom)
|
||||||
|
margins-selected (mf/deref refs/workspace-margins-selected)
|
||||||
|
hover-value (mf/use-var 0)
|
||||||
|
mouse-pos (mf/use-var nil)
|
||||||
|
hover (mf/use-var nil)
|
||||||
|
hover-all? (and (not (nil? @hover)) alt?)
|
||||||
|
hover-v? (and (or (= @hover :m1) (= @hover :m3)) shift?)
|
||||||
|
hover-h? (and (or (= @hover :m2) (= @hover :m4)) shift?)
|
||||||
|
margin (:layout-item-margin shape)
|
||||||
|
{:keys [width height x1 x2 y1 y2]} (:selrect shape)
|
||||||
|
on-pointer-enter (fn [hover-type val]
|
||||||
|
(reset! hover hover-type)
|
||||||
|
(reset! hover-value val))
|
||||||
|
on-pointer-leave #(reset! hover nil)
|
||||||
|
hover? #(or hover-all?
|
||||||
|
(and (or (= % :m1) (= % :m3)) hover-v?)
|
||||||
|
(and (or (= % :m2) (= % :m4)) hover-h?)
|
||||||
|
(= @hover %))
|
||||||
|
margin-display-data {:m1 {:key (str shape-id "-m1")
|
||||||
|
:x x1
|
||||||
|
:y (if (:flip-y frame) y2 (- y1 (:m1 margin)))
|
||||||
|
:width width
|
||||||
|
:height (:m1 margin)
|
||||||
|
:initial-value (:m1 margin)
|
||||||
|
:resize-type :top
|
||||||
|
:resize-axis :y
|
||||||
|
:resize-negate? (:flip-y frame)}
|
||||||
|
:m2 {:key (str shape-id "-m2")
|
||||||
|
:x (if (:flip-x frame) (- x1 (:m2 margin)) x2)
|
||||||
|
:y y1
|
||||||
|
:width (:m2 margin)
|
||||||
|
:height height
|
||||||
|
:initial-value (:m2 margin)
|
||||||
|
:resize-type :left
|
||||||
|
:resize-axis :x
|
||||||
|
:resize-negate? (:flip-x frame)}
|
||||||
|
:m3 {:key (str shape-id "-m3")
|
||||||
|
:x x1
|
||||||
|
:y (if (:flip-y frame) (- y1 (:m3 margin)) y2)
|
||||||
|
:width width
|
||||||
|
:height (:m3 margin)
|
||||||
|
:initial-value (:m3 margin)
|
||||||
|
:resize-type :top
|
||||||
|
:resize-axis :y
|
||||||
|
:resize-negate? (:flip-y frame)}
|
||||||
|
:m4 {:key (str shape-id "-m4")
|
||||||
|
:x (if (:flip-x frame) x2 (- x1 (:m4 margin)))
|
||||||
|
:y y1
|
||||||
|
:width (:m4 margin)
|
||||||
|
:height height
|
||||||
|
:initial-value (:m4 margin)
|
||||||
|
:resize-type :left
|
||||||
|
:resize-axis :x
|
||||||
|
:resize-negate? (:flip-x frame)}}]
|
||||||
|
|
||||||
|
[:g.margins {:pointer-events "visible"}
|
||||||
|
(for [[margin-num rect-data] margin-display-data]
|
||||||
|
[:& margin-display
|
||||||
|
{:key (:key rect-data)
|
||||||
|
:shape-id shape-id
|
||||||
|
:zoom zoom
|
||||||
|
:hover-all? hover-all?
|
||||||
|
:hover-v? hover-v?
|
||||||
|
:hover-h? hover-h?
|
||||||
|
:margin-num margin-num
|
||||||
|
:margin margin
|
||||||
|
:on-pointer-enter (partial on-pointer-enter margin-num (get margin margin-num))
|
||||||
|
:on-pointer-leave on-pointer-leave
|
||||||
|
:rect-data rect-data
|
||||||
|
:hover? (hover? margin-num)
|
||||||
|
:selected? (get margins-selected margin-num)
|
||||||
|
:mouse-pos mouse-pos
|
||||||
|
:hover-value hover-value}])
|
||||||
|
|
||||||
|
(when @hover
|
||||||
|
[:& fcc/flex-display-pill
|
||||||
|
{:height pill-height
|
||||||
|
:width pill-width
|
||||||
|
:font-size (/ fcc/font-size zoom)
|
||||||
|
:border-radius (/ fcc/flex-display-pill-border-radius zoom)
|
||||||
|
:color fcc/warning-color
|
||||||
|
:x (:x @mouse-pos)
|
||||||
|
:y (- (:y @mouse-pos) pill-width)
|
||||||
|
:value @hover-value}])]))
|
||||||
|
|
||||||
|
(mf/defc margin-control
|
||||||
|
[{:keys [shape parent zoom alt? shift?]}]
|
||||||
|
(when shape
|
||||||
|
[:g.measurement-gaps {:pointer-events "none"}
|
||||||
|
[:g.hover-shapes
|
||||||
|
[:& margin-rects
|
||||||
|
{:shape shape
|
||||||
|
:frame parent
|
||||||
|
:zoom zoom
|
||||||
|
:alt? alt?
|
||||||
|
:shift? shift?}]]]))
|
223
frontend/src/app/main/ui/flex_controls/padding.cljs
Normal file
223
frontend/src/app/main/ui/flex_controls/padding.cljs
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
;; 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
|
||||||
|
|
||||||
|
(ns app.main.ui.flex-controls.padding
|
||||||
|
(:require
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.types.modifiers :as ctm]
|
||||||
|
[app.main.data.workspace.modifiers :as dwm]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.ui.css-cursors :as cur]
|
||||||
|
[app.main.ui.flex-controls.common :as fcc]
|
||||||
|
[app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(mf/defc padding-display
|
||||||
|
[{:keys [frame-id zoom hover-all? hover-v? hover-h? padding-num padding on-pointer-enter on-pointer-leave
|
||||||
|
rect-data hover? selected? mouse-pos hover-value on-move-selected on-context-menu]}]
|
||||||
|
(let [resizing? (mf/use-var false)
|
||||||
|
start (mf/use-var nil)
|
||||||
|
original-value (mf/use-var 0)
|
||||||
|
negate? (true? (:resize-negate? rect-data))
|
||||||
|
axis (:resize-axis rect-data)
|
||||||
|
|
||||||
|
on-pointer-down
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps frame-id rect-data padding-num)
|
||||||
|
(fn [event]
|
||||||
|
(dom/capture-pointer event)
|
||||||
|
(reset! resizing? true)
|
||||||
|
(reset! start (dom/get-client-position event))
|
||||||
|
(reset! original-value (:initial-value rect-data))))
|
||||||
|
|
||||||
|
on-lost-pointer-capture
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps frame-id padding-num padding)
|
||||||
|
(fn [event]
|
||||||
|
(dom/release-pointer event)
|
||||||
|
(reset! resizing? false)
|
||||||
|
(reset! start nil)
|
||||||
|
(reset! original-value 0)
|
||||||
|
(st/emit! (dwm/apply-modifiers))))
|
||||||
|
|
||||||
|
on-pointer-move
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps frame-id padding-num padding hover-all? hover-v? hover-h?)
|
||||||
|
(fn [event]
|
||||||
|
(let [pos (dom/get-client-position event)]
|
||||||
|
(reset! mouse-pos (point->viewport pos))
|
||||||
|
(when @resizing?
|
||||||
|
(let [delta (-> (gpt/to-vec @start pos)
|
||||||
|
(cond-> negate? gpt/negate)
|
||||||
|
(get axis))
|
||||||
|
val (int (max (+ @original-value (/ delta zoom)) 0))
|
||||||
|
layout-padding (cond
|
||||||
|
hover-all? (assoc padding :p1 val :p2 val :p3 val :p4 val)
|
||||||
|
hover-v? (assoc padding :p1 val :p3 val)
|
||||||
|
hover-h? (assoc padding :p2 val :p4 val)
|
||||||
|
:else (assoc padding padding-num val))
|
||||||
|
|
||||||
|
|
||||||
|
layout-padding-type (if (= (:p1 padding) (:p2 padding) (:p3 padding) (:p4 padding)) :simple :multiple)
|
||||||
|
modifiers (dwm/create-modif-tree [frame-id]
|
||||||
|
(-> (ctm/empty)
|
||||||
|
(ctm/change-property :layout-padding layout-padding)
|
||||||
|
(ctm/change-property :layout-padding-type layout-padding-type)))]
|
||||||
|
(reset! hover-value val)
|
||||||
|
(st/emit! (dwm/set-modifiers modifiers)))))))]
|
||||||
|
|
||||||
|
[:g.padding-rect
|
||||||
|
[:rect.info-area
|
||||||
|
{:x (:x rect-data)
|
||||||
|
:y (:y rect-data)
|
||||||
|
:width (max 0 (:width rect-data))
|
||||||
|
:height (max 0 (:height rect-data))
|
||||||
|
|
||||||
|
:on-pointer-enter on-pointer-enter
|
||||||
|
:on-pointer-leave on-pointer-leave
|
||||||
|
:on-pointer-move on-pointer-move
|
||||||
|
:on-pointer-down on-move-selected
|
||||||
|
:on-context-menu on-context-menu
|
||||||
|
:style {:fill (if (or hover? selected?) fcc/distance-color "none")
|
||||||
|
:opacity (if selected? 0.5 0.25)}}]
|
||||||
|
|
||||||
|
(let [handle-width
|
||||||
|
(if (= axis :x)
|
||||||
|
(/ 2 zoom)
|
||||||
|
(min (* (:width rect-data) 0.5) (/ 20 zoom)))
|
||||||
|
|
||||||
|
handle-height
|
||||||
|
(if (= axis :y)
|
||||||
|
(/ 2 zoom)
|
||||||
|
(min (* (:height rect-data) 0.5) (/ 30 zoom)))]
|
||||||
|
[:rect.handle
|
||||||
|
{:x (+ (:x rect-data) (/ (- (:width rect-data) handle-width) 2))
|
||||||
|
:y (+ (:y rect-data) (/ (- (:height rect-data) handle-height) 2))
|
||||||
|
:width handle-width
|
||||||
|
:height handle-height
|
||||||
|
:on-pointer-enter on-pointer-enter
|
||||||
|
:on-pointer-leave on-pointer-leave
|
||||||
|
:on-pointer-down on-pointer-down
|
||||||
|
:on-lost-pointer-capture on-lost-pointer-capture
|
||||||
|
:on-pointer-move on-pointer-move
|
||||||
|
:on-context-menu on-context-menu
|
||||||
|
:class (when (or hover? selected?)
|
||||||
|
(if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90)))
|
||||||
|
:style {:fill (if (or hover? selected?) fcc/distance-color "none")
|
||||||
|
:opacity (if selected? 0 1)}}])]))
|
||||||
|
|
||||||
|
(mf/defc padding-rects
|
||||||
|
[{:keys [frame zoom alt? shift? on-move-selected on-context-menu]}]
|
||||||
|
(let [frame-id (:id frame)
|
||||||
|
paddings-selected (mf/deref refs/workspace-paddings-selected)
|
||||||
|
hover-value (mf/use-var 0)
|
||||||
|
mouse-pos (mf/use-var nil)
|
||||||
|
hover (mf/use-var nil)
|
||||||
|
hover-all? (and (not (nil? @hover)) alt?)
|
||||||
|
hover-v? (and (or (= @hover :p1) (= @hover :p3)) shift?)
|
||||||
|
hover-h? (and (or (= @hover :p2) (= @hover :p4)) shift?)
|
||||||
|
padding (:layout-padding frame)
|
||||||
|
{:keys [width height x1 x2 y1 y2]} (:selrect frame)
|
||||||
|
on-pointer-enter (fn [hover-type val]
|
||||||
|
(reset! hover hover-type)
|
||||||
|
(reset! hover-value val))
|
||||||
|
on-pointer-leave #(reset! hover nil)
|
||||||
|
pill-width (/ fcc/flex-display-pill-width zoom)
|
||||||
|
pill-height (/ fcc/flex-display-pill-height zoom)
|
||||||
|
hover? #(or hover-all?
|
||||||
|
(and (or (= % :p1) (= % :p3)) hover-v?)
|
||||||
|
(and (or (= % :p2) (= % :p4)) hover-h?)
|
||||||
|
(= @hover %))
|
||||||
|
negate {:p1 (if (:flip-y frame) true false)
|
||||||
|
:p2 (if (:flip-x frame) true false)
|
||||||
|
:p3 (if (:flip-y frame) true false)
|
||||||
|
:p4 (if (:flip-x frame) true false)}
|
||||||
|
negate (cond-> negate
|
||||||
|
(not= :auto (:layout-item-h-sizing frame)) (assoc :p2 (not (:p2 negate)))
|
||||||
|
(not= :auto (:layout-item-v-sizing frame)) (assoc :p3 (not (:p3 negate))))
|
||||||
|
|
||||||
|
padding-rect-data {:p1 {:key (str frame-id "-p1")
|
||||||
|
:x x1
|
||||||
|
:y (if (:flip-y frame) (- y2 (:p1 padding)) y1)
|
||||||
|
:width width
|
||||||
|
:height (:p1 padding)
|
||||||
|
:initial-value (:p1 padding)
|
||||||
|
:resize-type (if (:flip-y frame) :bottom :top)
|
||||||
|
:resize-axis :y
|
||||||
|
:resize-negate? (:p1 negate)}
|
||||||
|
:p2 {:key (str frame-id "-p2")
|
||||||
|
:x (if (:flip-x frame) x1 (- x2 (:p2 padding)))
|
||||||
|
:y y1
|
||||||
|
:width (:p2 padding)
|
||||||
|
:height height
|
||||||
|
:initial-value (:p2 padding)
|
||||||
|
:resize-type :left
|
||||||
|
:resize-axis :x
|
||||||
|
:resize-negate? (:p2 negate)}
|
||||||
|
:p3 {:key (str frame-id "-p3")
|
||||||
|
:x x1
|
||||||
|
:y (if (:flip-y frame) y1 (- y2 (:p3 padding)))
|
||||||
|
:width width
|
||||||
|
:height (:p3 padding)
|
||||||
|
:initial-value (:p3 padding)
|
||||||
|
:resize-type :bottom
|
||||||
|
:resize-axis :y
|
||||||
|
:resize-negate? (:p3 negate)}
|
||||||
|
:p4 {:key (str frame-id "-p4")
|
||||||
|
:x (if (:flip-x frame) (- x2 (:p4 padding)) x1)
|
||||||
|
:y y1
|
||||||
|
:width (:p4 padding)
|
||||||
|
:height height
|
||||||
|
:initial-value (:p4 padding)
|
||||||
|
:resize-type (if (:flip-x frame) :right :left)
|
||||||
|
:resize-axis :x
|
||||||
|
:resize-negate? (:p4 negate)}}]
|
||||||
|
|
||||||
|
[:g.paddings {:pointer-events "visible"}
|
||||||
|
(for [[padding-num rect-data] padding-rect-data]
|
||||||
|
[:& padding-display
|
||||||
|
{:key (:key rect-data)
|
||||||
|
:frame-id frame-id
|
||||||
|
:zoom zoom
|
||||||
|
:hover-all? hover-all?
|
||||||
|
:hover-v? hover-v?
|
||||||
|
:hover-h? hover-h?
|
||||||
|
:padding padding
|
||||||
|
:mouse-pos mouse-pos
|
||||||
|
:hover-value hover-value
|
||||||
|
:padding-num padding-num
|
||||||
|
:on-pointer-enter (partial on-pointer-enter padding-num (get padding padding-num))
|
||||||
|
:on-pointer-leave on-pointer-leave
|
||||||
|
:on-move-selected on-move-selected
|
||||||
|
:on-context-menu on-context-menu
|
||||||
|
:hover? (hover? padding-num)
|
||||||
|
:selected? (get paddings-selected padding-num)
|
||||||
|
:rect-data rect-data}])
|
||||||
|
(when @hover
|
||||||
|
[:& fcc/flex-display-pill
|
||||||
|
{:height pill-height
|
||||||
|
:width pill-width
|
||||||
|
:font-size (/ fcc/font-size zoom)
|
||||||
|
:border-radius (/ fcc/flex-display-pill-border-radius zoom)
|
||||||
|
:color fcc/distance-color
|
||||||
|
:x (:x @mouse-pos)
|
||||||
|
:y (- (:y @mouse-pos) pill-width)
|
||||||
|
:value @hover-value}])]))
|
||||||
|
|
||||||
|
(mf/defc padding-control
|
||||||
|
[{:keys [frame zoom alt? shift? on-move-selected on-context-menu]}]
|
||||||
|
(when frame
|
||||||
|
[:g.measurement-gaps {:pointer-events "none"}
|
||||||
|
[:g.hover-shapes
|
||||||
|
[:& padding-rects
|
||||||
|
{:frame frame
|
||||||
|
:zoom zoom
|
||||||
|
:alt? alt?
|
||||||
|
:shift? shift?
|
||||||
|
:on-move-selected on-move-selected
|
||||||
|
:on-context-menu on-context-menu}]]]))
|
|
@ -8,24 +8,12 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.files.helpers :as cfh]
|
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.rect :as grc]
|
[app.common.geom.rect :as grc]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.geom.shapes.flex-layout :as gsl]
|
|
||||||
[app.common.geom.shapes.points :as gpo]
|
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.types.modifiers :as ctm]
|
|
||||||
[app.common.types.shape.layout :as ctl]
|
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace.modifiers :as dwm]
|
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
|
||||||
[app.main.refs :as refs]
|
|
||||||
[app.main.store :as st]
|
|
||||||
[app.main.ui.css-cursors :as cur]
|
|
||||||
[app.main.ui.formats :as fmt]
|
[app.main.ui.formats :as fmt]
|
||||||
[app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]]
|
|
||||||
[app.util.dom :as dom]
|
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
;; ------------------------------------------------
|
;; ------------------------------------------------
|
||||||
|
@ -57,9 +45,6 @@
|
||||||
(def distance-pill-height 16)
|
(def distance-pill-height 16)
|
||||||
(def distance-line-stroke 1)
|
(def distance-line-stroke 1)
|
||||||
(def warning-color "var(--color-warning)")
|
(def warning-color "var(--color-warning)")
|
||||||
(def flex-display-pill-width 40)
|
|
||||||
(def flex-display-pill-height 20)
|
|
||||||
(def flex-display-pill-border-radius 4)
|
|
||||||
|
|
||||||
;; ------------------------------------------------
|
;; ------------------------------------------------
|
||||||
;; HELPERS
|
;; HELPERS
|
||||||
|
@ -277,600 +262,3 @@
|
||||||
[:& size-display {:selrect hover-selrect :zoom zoom}]
|
[:& size-display {:selrect hover-selrect :zoom zoom}]
|
||||||
[:& distance-display {:from hover-selrect :to selected-selrect :zoom zoom :bounds bounds-selrect}]])])))
|
[:& distance-display {:from hover-selrect :to selected-selrect :zoom zoom :bounds bounds-selrect}]])])))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(mf/defc flex-display-pill [{:keys [x y width height font-size border-radius value color]}]
|
|
||||||
[:g.distance-pill
|
|
||||||
[:rect {:x x
|
|
||||||
:y y
|
|
||||||
:width width
|
|
||||||
:height height
|
|
||||||
:rx border-radius
|
|
||||||
:ry border-radius
|
|
||||||
:style {:fill color}}]
|
|
||||||
|
|
||||||
[:text {:x (+ x (/ width 2))
|
|
||||||
:y (+ y (/ height 2))
|
|
||||||
:text-anchor "middle"
|
|
||||||
:dominant-baseline "central"
|
|
||||||
:style {:fill distance-text-color
|
|
||||||
:font-size font-size}}
|
|
||||||
(fmt/format-number (or value 0))]])
|
|
||||||
|
|
||||||
|
|
||||||
(mf/defc padding-display [{:keys [frame-id zoom hover-all? hover-v? hover-h? padding-num padding on-pointer-enter on-pointer-leave
|
|
||||||
rect-data hover? selected? mouse-pos hover-value]}]
|
|
||||||
(let [resizing? (mf/use-var false)
|
|
||||||
start (mf/use-var nil)
|
|
||||||
original-value (mf/use-var 0)
|
|
||||||
negate? (true? (:resize-negate? rect-data))
|
|
||||||
axis (:resize-axis rect-data)
|
|
||||||
|
|
||||||
on-pointer-down
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps frame-id rect-data padding-num)
|
|
||||||
(fn [event]
|
|
||||||
(dom/capture-pointer event)
|
|
||||||
(reset! resizing? true)
|
|
||||||
(reset! start (dom/get-client-position event))
|
|
||||||
(reset! original-value (:initial-value rect-data))))
|
|
||||||
|
|
||||||
on-lost-pointer-capture
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps frame-id padding-num padding)
|
|
||||||
(fn [event]
|
|
||||||
(dom/release-pointer event)
|
|
||||||
(reset! resizing? false)
|
|
||||||
(reset! start nil)
|
|
||||||
(reset! original-value 0)
|
|
||||||
(st/emit! (dwm/apply-modifiers))))
|
|
||||||
|
|
||||||
on-pointer-move
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps frame-id padding-num padding hover-all? hover-v? hover-h?)
|
|
||||||
(fn [event]
|
|
||||||
(let [pos (dom/get-client-position event)]
|
|
||||||
(reset! mouse-pos (point->viewport pos))
|
|
||||||
(when @resizing?
|
|
||||||
(let [delta (-> (gpt/to-vec @start pos)
|
|
||||||
(cond-> negate? gpt/negate)
|
|
||||||
(get axis))
|
|
||||||
val (int (max (+ @original-value (/ delta zoom)) 0))
|
|
||||||
layout-padding (cond
|
|
||||||
hover-all? (assoc padding :p1 val :p2 val :p3 val :p4 val)
|
|
||||||
hover-v? (assoc padding :p1 val :p3 val)
|
|
||||||
hover-h? (assoc padding :p2 val :p4 val)
|
|
||||||
:else (assoc padding padding-num val))
|
|
||||||
|
|
||||||
|
|
||||||
layout-padding-type (if (= (:p1 padding) (:p2 padding) (:p3 padding) (:p4 padding)) :simple :multiple)
|
|
||||||
modifiers (dwm/create-modif-tree [frame-id]
|
|
||||||
(-> (ctm/empty)
|
|
||||||
(ctm/change-property :layout-padding layout-padding)
|
|
||||||
(ctm/change-property :layout-padding-type layout-padding-type)))]
|
|
||||||
(reset! hover-value val)
|
|
||||||
(st/emit! (dwm/set-modifiers modifiers)))))))]
|
|
||||||
|
|
||||||
[:rect.padding-rect {:x (:x rect-data)
|
|
||||||
:y (:y rect-data)
|
|
||||||
:width (max 0 (:width rect-data))
|
|
||||||
:height (max 0 (:height rect-data))
|
|
||||||
:on-pointer-enter on-pointer-enter
|
|
||||||
:on-pointer-leave on-pointer-leave
|
|
||||||
:on-pointer-down on-pointer-down
|
|
||||||
:on-lost-pointer-capture on-lost-pointer-capture
|
|
||||||
:on-pointer-move on-pointer-move
|
|
||||||
:class (when (or hover? selected?)
|
|
||||||
(if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90)))
|
|
||||||
:style {:fill (if (or hover? selected?) distance-color "none")
|
|
||||||
:opacity (if selected? 0.5 0.25)}}]))
|
|
||||||
|
|
||||||
(mf/defc padding-rects [{:keys [frame zoom alt? shift?]}]
|
|
||||||
(let [frame-id (:id frame)
|
|
||||||
paddings-selected (mf/deref refs/workspace-paddings-selected)
|
|
||||||
hover-value (mf/use-var 0)
|
|
||||||
mouse-pos (mf/use-var nil)
|
|
||||||
hover (mf/use-var nil)
|
|
||||||
hover-all? (and (not (nil? @hover)) alt?)
|
|
||||||
hover-v? (and (or (= @hover :p1) (= @hover :p3)) shift?)
|
|
||||||
hover-h? (and (or (= @hover :p2) (= @hover :p4)) shift?)
|
|
||||||
padding (:layout-padding frame)
|
|
||||||
{:keys [width height x1 x2 y1 y2]} (:selrect frame)
|
|
||||||
on-pointer-enter (fn [hover-type val]
|
|
||||||
(reset! hover hover-type)
|
|
||||||
(reset! hover-value val))
|
|
||||||
on-pointer-leave #(reset! hover nil)
|
|
||||||
pill-width (/ flex-display-pill-width zoom)
|
|
||||||
pill-height (/ flex-display-pill-height zoom)
|
|
||||||
hover? #(or hover-all?
|
|
||||||
(and (or (= % :p1) (= % :p3)) hover-v?)
|
|
||||||
(and (or (= % :p2) (= % :p4)) hover-h?)
|
|
||||||
(= @hover %))
|
|
||||||
negate {:p1 (if (:flip-y frame) true false)
|
|
||||||
:p2 (if (:flip-x frame) true false)
|
|
||||||
:p3 (if (:flip-y frame) true false)
|
|
||||||
:p4 (if (:flip-x frame) true false)}
|
|
||||||
negate (cond-> negate
|
|
||||||
(not= :auto (:layout-item-h-sizing frame)) (assoc :p2 (not (:p2 negate)))
|
|
||||||
(not= :auto (:layout-item-v-sizing frame)) (assoc :p3 (not (:p3 negate))))
|
|
||||||
|
|
||||||
padding-rect-data {:p1 {:key (str frame-id "-p1")
|
|
||||||
:x x1
|
|
||||||
:y (if (:flip-y frame) (- y2 (:p1 padding)) y1)
|
|
||||||
:width width
|
|
||||||
:height (:p1 padding)
|
|
||||||
:initial-value (:p1 padding)
|
|
||||||
:resize-type (if (:flip-y frame) :bottom :top)
|
|
||||||
:resize-axis :y
|
|
||||||
:resize-negate? (:p1 negate)}
|
|
||||||
:p2 {:key (str frame-id "-p2")
|
|
||||||
:x (if (:flip-x frame) x1 (- x2 (:p2 padding)))
|
|
||||||
:y y1
|
|
||||||
:width (:p2 padding)
|
|
||||||
:height height
|
|
||||||
:initial-value (:p2 padding)
|
|
||||||
:resize-type :left
|
|
||||||
:resize-axis :x
|
|
||||||
:resize-negate? (:p2 negate)}
|
|
||||||
:p3 {:key (str frame-id "-p3")
|
|
||||||
:x x1
|
|
||||||
:y (if (:flip-y frame) y1 (- y2 (:p3 padding)))
|
|
||||||
:width width
|
|
||||||
:height (:p3 padding)
|
|
||||||
:initial-value (:p3 padding)
|
|
||||||
:resize-type :bottom
|
|
||||||
:resize-axis :y
|
|
||||||
:resize-negate? (:p3 negate)}
|
|
||||||
:p4 {:key (str frame-id "-p4")
|
|
||||||
:x (if (:flip-x frame) (- x2 (:p4 padding)) x1)
|
|
||||||
:y y1
|
|
||||||
:width (:p4 padding)
|
|
||||||
:height height
|
|
||||||
:initial-value (:p4 padding)
|
|
||||||
:resize-type (if (:flip-x frame) :right :left)
|
|
||||||
:resize-axis :x
|
|
||||||
:resize-negate? (:p4 negate)}}]
|
|
||||||
|
|
||||||
[:g.paddings {:pointer-events "visible"}
|
|
||||||
(for [[padding-num rect-data] padding-rect-data]
|
|
||||||
[:& padding-display {:key (:key rect-data)
|
|
||||||
:frame-id frame-id
|
|
||||||
:zoom zoom
|
|
||||||
:hover-all? hover-all?
|
|
||||||
:hover-v? hover-v?
|
|
||||||
:hover-h? hover-h?
|
|
||||||
:padding padding
|
|
||||||
:mouse-pos mouse-pos
|
|
||||||
:hover-value hover-value
|
|
||||||
:padding-num padding-num
|
|
||||||
:on-pointer-enter (partial on-pointer-enter padding-num (get padding padding-num))
|
|
||||||
:on-pointer-leave on-pointer-leave
|
|
||||||
:hover? (hover? padding-num)
|
|
||||||
:selected? (get paddings-selected padding-num)
|
|
||||||
:rect-data rect-data}])
|
|
||||||
(when @hover
|
|
||||||
[:& flex-display-pill {:height pill-height
|
|
||||||
:width pill-width
|
|
||||||
:font-size (/ font-size zoom)
|
|
||||||
:border-radius (/ flex-display-pill-border-radius zoom)
|
|
||||||
:color distance-color
|
|
||||||
:x (:x @mouse-pos)
|
|
||||||
:y (- (:y @mouse-pos) pill-width)
|
|
||||||
:value @hover-value}])]))
|
|
||||||
|
|
||||||
(mf/defc margin-display [{:keys [shape-id zoom hover-all? hover-v? hover-h? margin-num margin on-pointer-enter on-pointer-leave
|
|
||||||
rect-data hover? selected? mouse-pos hover-value]}]
|
|
||||||
(let [resizing? (mf/use-var false)
|
|
||||||
start (mf/use-var nil)
|
|
||||||
original-value (mf/use-var 0)
|
|
||||||
negate? (true? (:resize-negate? rect-data))
|
|
||||||
axis (:resize-axis rect-data)
|
|
||||||
|
|
||||||
on-pointer-down
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps shape-id margin-num margin)
|
|
||||||
(fn [event]
|
|
||||||
(dom/capture-pointer event)
|
|
||||||
(reset! resizing? true)
|
|
||||||
(reset! start (dom/get-client-position event))
|
|
||||||
(reset! original-value (:initial-value rect-data))))
|
|
||||||
|
|
||||||
on-lost-pointer-capture
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps shape-id margin-num margin)
|
|
||||||
(fn [event]
|
|
||||||
(dom/release-pointer event)
|
|
||||||
(reset! resizing? false)
|
|
||||||
(reset! start nil)
|
|
||||||
(reset! original-value 0)
|
|
||||||
(st/emit! (dwm/apply-modifiers))))
|
|
||||||
|
|
||||||
on-pointer-move
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps shape-id margin-num margin hover-all? hover-v? hover-h?)
|
|
||||||
(fn [event]
|
|
||||||
(let [pos (dom/get-client-position event)]
|
|
||||||
(reset! mouse-pos (point->viewport pos))
|
|
||||||
(when @resizing?
|
|
||||||
(let [delta (-> (gpt/to-vec @start pos)
|
|
||||||
(cond-> negate? gpt/negate)
|
|
||||||
(get axis))
|
|
||||||
val (int (max (+ @original-value (/ delta zoom)) 0))
|
|
||||||
layout-item-margin (cond
|
|
||||||
hover-all? (assoc margin :m1 val :m2 val :m3 val :m4 val)
|
|
||||||
hover-v? (assoc margin :m1 val :m3 val)
|
|
||||||
hover-h? (assoc margin :m2 val :m4 val)
|
|
||||||
:else (assoc margin margin-num val))
|
|
||||||
layout-item-margin-type (if (= (:m1 margin) (:m2 margin) (:m3 margin) (:m4 margin)) :simple :multiple)
|
|
||||||
modifiers (dwm/create-modif-tree [shape-id]
|
|
||||||
(-> (ctm/empty)
|
|
||||||
(ctm/change-property :layout-item-margin layout-item-margin)
|
|
||||||
(ctm/change-property :layout-item-margin-type layout-item-margin-type)))]
|
|
||||||
(reset! hover-value val)
|
|
||||||
(st/emit! (dwm/set-modifiers modifiers)))))))]
|
|
||||||
|
|
||||||
[:rect.margin-rect {:x (:x rect-data)
|
|
||||||
:y (:y rect-data)
|
|
||||||
:width (:width rect-data)
|
|
||||||
:height (:height rect-data)
|
|
||||||
:on-pointer-enter on-pointer-enter
|
|
||||||
:on-pointer-leave on-pointer-leave
|
|
||||||
:on-pointer-down on-pointer-down
|
|
||||||
:on-lost-pointer-capture on-lost-pointer-capture
|
|
||||||
:on-pointer-move on-pointer-move
|
|
||||||
:class (when (or hover? selected?)
|
|
||||||
(if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90)))
|
|
||||||
:style {:fill (if (or hover? selected?) warning-color "none")
|
|
||||||
:opacity (if selected? 0.5 0.25)}}]))
|
|
||||||
|
|
||||||
(mf/defc margin-rects [{:keys [shape frame zoom alt? shift?]}]
|
|
||||||
(let [shape-id (:id shape)
|
|
||||||
pill-width (/ flex-display-pill-width zoom)
|
|
||||||
pill-height (/ flex-display-pill-height zoom)
|
|
||||||
margins-selected (mf/deref refs/workspace-margins-selected)
|
|
||||||
hover-value (mf/use-var 0)
|
|
||||||
mouse-pos (mf/use-var nil)
|
|
||||||
hover (mf/use-var nil)
|
|
||||||
hover-all? (and (not (nil? @hover)) alt?)
|
|
||||||
hover-v? (and (or (= @hover :m1) (= @hover :m3)) shift?)
|
|
||||||
hover-h? (and (or (= @hover :m2) (= @hover :m4)) shift?)
|
|
||||||
margin (:layout-item-margin shape)
|
|
||||||
{:keys [width height x1 x2 y1 y2]} (:selrect shape)
|
|
||||||
on-pointer-enter (fn [hover-type val]
|
|
||||||
(reset! hover hover-type)
|
|
||||||
(reset! hover-value val))
|
|
||||||
on-pointer-leave #(reset! hover nil)
|
|
||||||
hover? #(or hover-all?
|
|
||||||
(and (or (= % :m1) (= % :m3)) hover-v?)
|
|
||||||
(and (or (= % :m2) (= % :m4)) hover-h?)
|
|
||||||
(= @hover %))
|
|
||||||
margin-display-data {:m1 {:key (str shape-id "-m1")
|
|
||||||
:x x1
|
|
||||||
:y (if (:flip-y frame) y2 (- y1 (:m1 margin)))
|
|
||||||
:width width
|
|
||||||
:height (:m1 margin)
|
|
||||||
:initial-value (:m1 margin)
|
|
||||||
:resize-type :top
|
|
||||||
:resize-axis :y
|
|
||||||
:resize-negate? (:flip-y frame)}
|
|
||||||
:m2 {:key (str shape-id "-m2")
|
|
||||||
:x (if (:flip-x frame) (- x1 (:m2 margin)) x2)
|
|
||||||
:y y1
|
|
||||||
:width (:m2 margin)
|
|
||||||
:height height
|
|
||||||
:initial-value (:m2 margin)
|
|
||||||
:resize-type :left
|
|
||||||
:resize-axis :x
|
|
||||||
:resize-negate? (:flip-x frame)}
|
|
||||||
:m3 {:key (str shape-id "-m3")
|
|
||||||
:x x1
|
|
||||||
:y (if (:flip-y frame) (- y1 (:m3 margin)) y2)
|
|
||||||
:width width
|
|
||||||
:height (:m3 margin)
|
|
||||||
:initial-value (:m3 margin)
|
|
||||||
:resize-type :top
|
|
||||||
:resize-axis :y
|
|
||||||
:resize-negate? (:flip-y frame)}
|
|
||||||
:m4 {:key (str shape-id "-m4")
|
|
||||||
:x (if (:flip-x frame) x2 (- x1 (:m4 margin)))
|
|
||||||
:y y1
|
|
||||||
:width (:m4 margin)
|
|
||||||
:height height
|
|
||||||
:initial-value (:m4 margin)
|
|
||||||
:resize-type :left
|
|
||||||
:resize-axis :x
|
|
||||||
:resize-negate? (:flip-x frame)}}]
|
|
||||||
|
|
||||||
[:g.margins {:pointer-events "visible"}
|
|
||||||
(for [[margin-num rect-data] margin-display-data]
|
|
||||||
[:& margin-display
|
|
||||||
{:key (:key rect-data)
|
|
||||||
:shape-id shape-id
|
|
||||||
:zoom zoom
|
|
||||||
:hover-all? hover-all?
|
|
||||||
:hover-v? hover-v?
|
|
||||||
:hover-h? hover-h?
|
|
||||||
:margin-num margin-num
|
|
||||||
:margin margin
|
|
||||||
:on-pointer-enter (partial on-pointer-enter margin-num (get margin margin-num))
|
|
||||||
:on-pointer-leave on-pointer-leave
|
|
||||||
:rect-data rect-data
|
|
||||||
:hover? (hover? margin-num)
|
|
||||||
:selected? (get margins-selected margin-num)
|
|
||||||
:mouse-pos mouse-pos
|
|
||||||
:hover-value hover-value}])
|
|
||||||
|
|
||||||
(when @hover
|
|
||||||
[:& flex-display-pill {:height pill-height
|
|
||||||
:width pill-width
|
|
||||||
:font-size (/ font-size zoom)
|
|
||||||
:border-radius (/ flex-display-pill-border-radius zoom)
|
|
||||||
:color warning-color
|
|
||||||
:x (:x @mouse-pos)
|
|
||||||
:y (- (:y @mouse-pos) pill-width)
|
|
||||||
:value @hover-value}])]))
|
|
||||||
|
|
||||||
(mf/defc gap-display [{:keys [frame-id zoom gap-type gap on-pointer-enter on-pointer-leave
|
|
||||||
rect-data hover? selected? mouse-pos hover-value]}]
|
|
||||||
(let [resizing (mf/use-var nil)
|
|
||||||
start (mf/use-var nil)
|
|
||||||
original-value (mf/use-var 0)
|
|
||||||
negate? (:resize-negate? rect-data)
|
|
||||||
axis (:resize-axis rect-data)
|
|
||||||
|
|
||||||
on-pointer-down
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps frame-id gap-type gap)
|
|
||||||
(fn [event]
|
|
||||||
(dom/capture-pointer event)
|
|
||||||
(reset! resizing gap-type)
|
|
||||||
(reset! start (dom/get-client-position event))
|
|
||||||
(reset! original-value (:initial-value rect-data))))
|
|
||||||
|
|
||||||
on-lost-pointer-capture
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps frame-id gap-type gap)
|
|
||||||
(fn [event]
|
|
||||||
(dom/release-pointer event)
|
|
||||||
(reset! resizing nil)
|
|
||||||
(reset! start nil)
|
|
||||||
(reset! original-value 0)
|
|
||||||
(st/emit! (dwm/apply-modifiers))))
|
|
||||||
|
|
||||||
on-pointer-move
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps frame-id gap-type gap)
|
|
||||||
(fn [event]
|
|
||||||
(let [pos (dom/get-client-position event)]
|
|
||||||
(reset! mouse-pos (point->viewport pos))
|
|
||||||
(when (= @resizing gap-type)
|
|
||||||
(let [delta (-> (gpt/to-vec @start pos)
|
|
||||||
(cond-> negate? gpt/negate)
|
|
||||||
(get axis))
|
|
||||||
val (int (max (+ @original-value (/ delta zoom)) 0))
|
|
||||||
layout-gap (assoc gap gap-type val)
|
|
||||||
modifiers (dwm/create-modif-tree [frame-id] (ctm/change-property (ctm/empty) :layout-gap layout-gap))]
|
|
||||||
|
|
||||||
(reset! hover-value val)
|
|
||||||
(st/emit! (dwm/set-modifiers modifiers)))))))]
|
|
||||||
|
|
||||||
[:rect.gap-rect {:x (:x rect-data)
|
|
||||||
:y (:y rect-data)
|
|
||||||
:width (:width rect-data)
|
|
||||||
:height (:height rect-data)
|
|
||||||
:on-pointer-enter on-pointer-enter
|
|
||||||
:on-pointer-leave on-pointer-leave
|
|
||||||
:on-pointer-down on-pointer-down
|
|
||||||
:on-lost-pointer-capture on-lost-pointer-capture
|
|
||||||
:on-pointer-move on-pointer-move
|
|
||||||
:class (when (or hover? selected?)
|
|
||||||
(if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90)))
|
|
||||||
:style {:fill (if (or hover? selected?) distance-color "none")
|
|
||||||
:opacity (if selected? 0.5 0.25)}}]))
|
|
||||||
|
|
||||||
(mf/defc gap-rects [{:keys [frame zoom]}]
|
|
||||||
(let [frame-id (:id frame)
|
|
||||||
saved-dir (:layout-flex-dir frame)
|
|
||||||
is-col? (or (= :column saved-dir) (= :column-reverse saved-dir))
|
|
||||||
flip-x (:flip-x frame)
|
|
||||||
flip-y (:flip-y frame)
|
|
||||||
pill-width (/ flex-display-pill-width zoom)
|
|
||||||
pill-height (/ flex-display-pill-height zoom)
|
|
||||||
workspace-modifiers (mf/deref refs/workspace-modifiers)
|
|
||||||
gap-selected (mf/deref refs/workspace-gap-selected)
|
|
||||||
hover (mf/use-var nil)
|
|
||||||
hover-value (mf/use-var 0)
|
|
||||||
mouse-pos (mf/use-var nil)
|
|
||||||
padding (:layout-padding frame)
|
|
||||||
gap (:layout-gap frame)
|
|
||||||
{:keys [width height x1 y1]} (:selrect frame)
|
|
||||||
on-pointer-enter (fn [hover-type val]
|
|
||||||
(reset! hover hover-type)
|
|
||||||
(reset! hover-value val))
|
|
||||||
|
|
||||||
on-pointer-leave #(reset! hover nil)
|
|
||||||
negate {:column-gap (if flip-x true false)
|
|
||||||
:row-gap (if flip-y true false)}
|
|
||||||
|
|
||||||
objects (wsh/lookup-page-objects @st/state)
|
|
||||||
children (->> (cfh/get-immediate-children objects frame-id)
|
|
||||||
(remove ctl/position-absolute?))
|
|
||||||
|
|
||||||
children-to-display (if (or (= :row-reverse saved-dir)
|
|
||||||
(= :column-reverse saved-dir))
|
|
||||||
(drop-last children)
|
|
||||||
(rest children))
|
|
||||||
children-to-display (->> children-to-display
|
|
||||||
(map #(gsh/transform-shape % (get-in workspace-modifiers [(:id %) :modifiers]))))
|
|
||||||
|
|
||||||
wrap-blocks
|
|
||||||
(let [block-children (->> children
|
|
||||||
(map #(vector (gpo/parent-coords-bounds (:points %) (:points frame)) %)))
|
|
||||||
bounds (d/lazy-map (keys objects) #(gsh/shape->points (get objects %)))
|
|
||||||
|
|
||||||
layout-data (gsl/calc-layout-data frame (:points frame) block-children bounds objects)
|
|
||||||
layout-bounds (:layout-bounds layout-data)
|
|
||||||
xv #(gpo/start-hv layout-bounds %)
|
|
||||||
yv #(gpo/start-vv layout-bounds %)]
|
|
||||||
(for [{:keys [start-p line-width line-height layout-gap-row layout-gap-col num-children]} (:layout-lines layout-data)]
|
|
||||||
(let [line-width (if is-col? line-width (+ line-width (* (dec num-children) layout-gap-row)))
|
|
||||||
line-height (if is-col? (+ line-height (* (dec num-children) layout-gap-col)) line-height)
|
|
||||||
end-p (-> start-p (gpt/add (xv line-width)) (gpt/add (yv line-height)))]
|
|
||||||
{:x1 (min (:x start-p) (:x end-p))
|
|
||||||
:y1 (min (:y start-p) (:y end-p))
|
|
||||||
:x2 (max (:x start-p) (:x end-p))
|
|
||||||
:y2 (max (:y start-p) (:y end-p))})))
|
|
||||||
|
|
||||||
block-contains
|
|
||||||
(fn [x y block]
|
|
||||||
(if is-col?
|
|
||||||
(<= (:x1 block) x (:x2 block))
|
|
||||||
(<= (:y1 block) y (:y2 block))))
|
|
||||||
|
|
||||||
get-container-block
|
|
||||||
(fn [shape]
|
|
||||||
(let [selrect (:selrect shape)
|
|
||||||
x (/ (+ (:x1 selrect) (:x2 selrect)) 2)
|
|
||||||
y (/ (+ (:y1 selrect) (:y2 selrect)) 2)]
|
|
||||||
(->> wrap-blocks
|
|
||||||
(filter #(block-contains x y %))
|
|
||||||
first)))
|
|
||||||
|
|
||||||
create-cgdd
|
|
||||||
(fn [shape]
|
|
||||||
(let [block (get-container-block shape)
|
|
||||||
x (if flip-x
|
|
||||||
(- (:x1 (:selrect shape))
|
|
||||||
(get-in shape [:layout-item-margin :m2])
|
|
||||||
(:column-gap gap))
|
|
||||||
(+ (:x2 (:selrect shape)) (get-in shape [:layout-item-margin :m2])))
|
|
||||||
y (:y1 block)
|
|
||||||
h (- (:y2 block) (:y1 block))]
|
|
||||||
{:x x
|
|
||||||
:y y
|
|
||||||
:height h
|
|
||||||
:width (:column-gap gap)
|
|
||||||
:initial-value (:column-gap gap)
|
|
||||||
:resize-type :left
|
|
||||||
:resize-axis :x
|
|
||||||
:resize-negate? (:column-gap negate)
|
|
||||||
:gap-type (if is-col? :row-gap :column-gap)}))
|
|
||||||
|
|
||||||
create-cgdd-block
|
|
||||||
(fn [block]
|
|
||||||
(let [x (if flip-x
|
|
||||||
(- (:x1 block) (:column-gap gap))
|
|
||||||
(:x2 block))
|
|
||||||
y (if flip-y
|
|
||||||
(+ y1 (:p3 padding))
|
|
||||||
(+ y1 (:p1 padding)))
|
|
||||||
h (- height (+ (:p1 padding) (:p3 padding)))]
|
|
||||||
{:x x
|
|
||||||
:y y
|
|
||||||
:width (:column-gap gap)
|
|
||||||
:height h
|
|
||||||
:initial-value (:column-gap gap)
|
|
||||||
:resize-type :left
|
|
||||||
:resize-axis :x
|
|
||||||
:resize-negate? (:column-gap negate)
|
|
||||||
:gap-type (if is-col? :column-gap :row-gap)}))
|
|
||||||
|
|
||||||
create-rgdd
|
|
||||||
(fn [shape]
|
|
||||||
(let [block (get-container-block shape)
|
|
||||||
x (:x1 block)
|
|
||||||
y (if flip-y
|
|
||||||
(- (:y1 (:selrect shape))
|
|
||||||
(get-in shape [:layout-item-margin :m3])
|
|
||||||
(:row-gap gap))
|
|
||||||
(+ (:y2 (:selrect shape)) (get-in shape [:layout-item-margin :m3])))
|
|
||||||
w (- (:x2 block) (:x1 block))]
|
|
||||||
{:x x
|
|
||||||
:y y
|
|
||||||
:width w
|
|
||||||
:height (:row-gap gap)
|
|
||||||
:initial-value (:row-gap gap)
|
|
||||||
:resize-type :bottom
|
|
||||||
:resize-axis :y
|
|
||||||
:resize-negate? (:row-gap negate)
|
|
||||||
:gap-type (if is-col? :row-gap :column-gap)}))
|
|
||||||
|
|
||||||
create-rgdd-block
|
|
||||||
(fn [block]
|
|
||||||
(let [x (if flip-x
|
|
||||||
(+ x1 (:p2 padding))
|
|
||||||
(+ x1 (:p4 padding)))
|
|
||||||
y (if flip-y
|
|
||||||
(- (:y1 block) (:row-gap gap))
|
|
||||||
(:y2 block))
|
|
||||||
w (- width (+ (:p2 padding) (:p4 padding)))]
|
|
||||||
{:x x
|
|
||||||
:y y
|
|
||||||
:width w
|
|
||||||
:height (:row-gap gap)
|
|
||||||
:initial-value (:row-gap gap)
|
|
||||||
:resize-type :bottom
|
|
||||||
:resize-axis :y
|
|
||||||
:resize-negate? (:row-gap negate)
|
|
||||||
:gap-type (if is-col? :column-gap :row-gap)}))
|
|
||||||
|
|
||||||
display-blocks (if is-col?
|
|
||||||
(->> (drop-last wrap-blocks)
|
|
||||||
(map create-cgdd-block))
|
|
||||||
(->> (drop-last wrap-blocks)
|
|
||||||
(map create-rgdd-block)))
|
|
||||||
|
|
||||||
display-children (if is-col?
|
|
||||||
(->> children-to-display
|
|
||||||
(map create-rgdd))
|
|
||||||
(->> children-to-display
|
|
||||||
(map create-cgdd)))]
|
|
||||||
|
|
||||||
[:g.gaps {:pointer-events "visible"}
|
|
||||||
(for [[index display-item] (d/enumerate (concat display-blocks display-children))]
|
|
||||||
(let [gap-type (:gap-type display-item)]
|
|
||||||
[:& gap-display {:key (str frame-id index)
|
|
||||||
:frame-id frame-id
|
|
||||||
:zoom zoom
|
|
||||||
:gap-type gap-type
|
|
||||||
:gap gap
|
|
||||||
:on-pointer-enter (partial on-pointer-enter gap-type (get gap gap-type))
|
|
||||||
:on-pointer-leave on-pointer-leave
|
|
||||||
:rect-data display-item
|
|
||||||
:hover? (= @hover gap-type)
|
|
||||||
:selected? (= gap-selected gap-type)
|
|
||||||
:mouse-pos mouse-pos
|
|
||||||
:hover-value hover-value}]))
|
|
||||||
|
|
||||||
(when @hover
|
|
||||||
[:& flex-display-pill {:height pill-height
|
|
||||||
:width pill-width
|
|
||||||
:font-size (/ font-size zoom)
|
|
||||||
:border-radius (/ flex-display-pill-border-radius zoom)
|
|
||||||
:color distance-color
|
|
||||||
:x (:x @mouse-pos)
|
|
||||||
:y (- (:y @mouse-pos) pill-width)
|
|
||||||
:value @hover-value}])]))
|
|
||||||
|
|
||||||
(mf/defc padding
|
|
||||||
[{:keys [frame zoom alt? shift?]}]
|
|
||||||
(when frame
|
|
||||||
[:g.measurement-gaps {:pointer-events "none"}
|
|
||||||
[:g.hover-shapes
|
|
||||||
[:& padding-rects {:frame frame :zoom zoom :alt? alt? :shift? shift?}]]]))
|
|
||||||
|
|
||||||
(mf/defc gap
|
|
||||||
[{:keys [frame zoom]}]
|
|
||||||
(when frame
|
|
||||||
[:g.measurement-gaps {:pointer-events "none"}
|
|
||||||
[:g.hover-shapes
|
|
||||||
[:& gap-rects {:frame frame :zoom zoom}]]]))
|
|
||||||
|
|
||||||
(mf/defc margin
|
|
||||||
[{:keys [shape parent zoom alt? shift?]}]
|
|
||||||
(when shape
|
|
||||||
[:g.measurement-gaps {:pointer-events "none"}
|
|
||||||
[:g.hover-shapes
|
|
||||||
[:& margin-rects {:shape shape :frame parent :zoom zoom :alt? alt? :shift? shift?}]]]))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -113,24 +113,27 @@
|
||||||
[:& (mf/provider shapes/base-frame-ctx) {:value base}
|
[:& (mf/provider shapes/base-frame-ctx) {:value base}
|
||||||
[:& (mf/provider shapes/frame-offset-ctx) {:value offset}
|
[:& (mf/provider shapes/frame-offset-ctx) {:value offset}
|
||||||
;; We have two different svgs for fixed and not fixed elements so we can emulate the sticky css attribute in svg
|
;; We have two different svgs for fixed and not fixed elements so we can emulate the sticky css attribute in svg
|
||||||
[:svg.not-fixed {:view-box vbox
|
[:svg {:class (stl/css :fixed)
|
||||||
:width (:width size)
|
:view-box vbox
|
||||||
:height (:height size)
|
:width (:width size)
|
||||||
:version "1.1"
|
:height (:height size)
|
||||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
:version "1.1"
|
||||||
:xmlns "http://www.w3.org/2000/svg"
|
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||||
:fill "none"}
|
:xmlns "http://www.w3.org/2000/svg"
|
||||||
[:& wrapper-not-fixed {:shape frame :view-box vbox}]]
|
:fill "none"
|
||||||
[:svg.fixed {:view-box vbox
|
:style {:width (:width size)
|
||||||
:width (:width size)
|
:height (:height size)}}
|
||||||
:height (:height size)
|
[:& wrapper-fixed {:shape fixed-frame :view-box vbox}]]
|
||||||
:version "1.1"
|
|
||||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
[:svg {:class (stl/css :not-fixed)
|
||||||
:xmlns "http://www.w3.org/2000/svg"
|
:view-box vbox
|
||||||
:fill "none"
|
:width (:width size)
|
||||||
:style {:width (:width size)
|
:height (:height size)
|
||||||
:height (:height size)}}
|
:version "1.1"
|
||||||
[:& wrapper-fixed {:shape fixed-frame :view-box vbox}]]]]))
|
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||||
|
:xmlns "http://www.w3.org/2000/svg"
|
||||||
|
:fill "none"}
|
||||||
|
[:& wrapper-not-fixed {:shape frame :view-box vbox}]]]]))
|
||||||
|
|
||||||
(mf/defc viewport
|
(mf/defc viewport
|
||||||
{::mf/wrap [mf/memo]
|
{::mf/wrap [mf/memo]
|
||||||
|
|
|
@ -78,3 +78,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// breakpoint 1013px
|
// breakpoint 1013px
|
||||||
|
|
||||||
|
.fixed {
|
||||||
|
position: fixed;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
:global(.frame-children) g {
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -106,8 +106,9 @@
|
||||||
[:span {:class (stl/css :title)} title]
|
[:span {:class (stl/css :title)} title]
|
||||||
(when shortcut
|
(when shortcut
|
||||||
[:span {:class (stl/css :shortcut)}
|
[:span {:class (stl/css :shortcut)}
|
||||||
(for [sc (scd/split-sc shortcut)]
|
(for [[idx sc] (d/enumerate (scd/split-sc shortcut))]
|
||||||
[:span {:class (stl/css :shortcut-key)} sc])])
|
[:span {:key (dm/str shortcut "-" idx)
|
||||||
|
:class (stl/css :shortcut-key)} sc])])
|
||||||
|
|
||||||
(when (> (count children) 1)
|
(when (> (count children) 1)
|
||||||
[:span {:class (stl/css :submenu-icon)} i/arrow-refactor])
|
[:span {:class (stl/css :submenu-icon)} i/arrow-refactor])
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
[app.main.ui.shapes.bool :as bool]
|
[app.main.ui.shapes.bool :as bool]
|
||||||
[app.main.ui.shapes.shape :refer [shape-container]]
|
[app.main.ui.shapes.shape :refer [shape-container]]
|
||||||
[app.main.ui.workspace.shapes.common :refer [check-shape-props]]
|
[app.main.ui.workspace.shapes.common :refer [check-shape-props]]
|
||||||
|
[app.main.ui.workspace.shapes.debug :as wsd]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(defn bool-wrapper-factory
|
(defn bool-wrapper-factory
|
||||||
|
@ -38,5 +39,7 @@
|
||||||
|
|
||||||
[:> shape-container {:shape shape}
|
[:> shape-container {:shape shape}
|
||||||
[:& bool-shape {:shape shape
|
[:& bool-shape {:shape shape
|
||||||
:childs childs}]]))))
|
:childs childs}]
|
||||||
|
(when *assert*
|
||||||
|
[:& wsd/shape-debug {:shape shape}])]))))
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.record :as cr]
|
[app.common.record :as cr]
|
||||||
[app.main.ui.shapes.shape :refer [shape-container]]
|
[app.main.ui.shapes.shape :refer [shape-container]]
|
||||||
|
[app.main.ui.workspace.shapes.debug :as wsd]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(def ^:private excluded-attrs
|
(def ^:private excluded-attrs
|
||||||
|
@ -34,4 +35,6 @@
|
||||||
[props]
|
[props]
|
||||||
(let [shape (unchecked-get props "shape")]
|
(let [shape (unchecked-get props "shape")]
|
||||||
[:> shape-container {:shape shape}
|
[:> shape-container {:shape shape}
|
||||||
[:& component {:shape shape}]])))
|
[:& component {:shape shape}]
|
||||||
|
(when *assert*
|
||||||
|
[:& wsd/shape-debug {:shape shape}])])))
|
||||||
|
|
99
frontend/src/app/main/ui/workspace/shapes/debug.cljs
Normal file
99
frontend/src/app/main/ui/workspace/shapes/debug.cljs
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
;; 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
|
||||||
|
|
||||||
|
(ns app.main.ui.workspace.shapes.debug
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.files.helpers :as cfh]
|
||||||
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.common.geom.shapes.text :as gst]
|
||||||
|
[app.common.math :as mth]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.util.color :as uc]
|
||||||
|
[app.util.debug :as dbg]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(mf/defc debug-bounding-boxes
|
||||||
|
[{:keys [shape]}]
|
||||||
|
(let [points (->> (:points shape)
|
||||||
|
(map #(dm/fmt "%,%" (dm/get-prop % :x) (dm/get-prop % :y)))
|
||||||
|
(str/join " "))
|
||||||
|
color (mf/use-memo #(uc/random-color))
|
||||||
|
sr (:selrect shape)]
|
||||||
|
[:g.debug-bounding-boxes
|
||||||
|
[:rect {:transform (gsh/transform-str shape)
|
||||||
|
:x (:x sr)
|
||||||
|
:y (:y sr)
|
||||||
|
:width (:width sr)
|
||||||
|
:height (:height sr)
|
||||||
|
:fill color
|
||||||
|
:opacity 0.2}]
|
||||||
|
(for [p (:points shape)]
|
||||||
|
[:circle {:cx (dm/get-prop p :x)
|
||||||
|
:cy (dm/get-prop p :y)
|
||||||
|
:r 2
|
||||||
|
:fill color}])
|
||||||
|
[:polygon {:points points
|
||||||
|
:stroke-width 1
|
||||||
|
:stroke color}]]))
|
||||||
|
|
||||||
|
(mf/defc debug-text-bounds
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
(let [shape (unchecked-get props "shape")
|
||||||
|
zoom (mf/deref refs/selected-zoom)
|
||||||
|
bounding-box (gst/shape->rect shape)
|
||||||
|
ctx (js* "document.createElement(\"canvas\").getContext(\"2d\")")]
|
||||||
|
[:g {:transform (gsh/transform-str shape)}
|
||||||
|
[:rect {:x (:x bounding-box)
|
||||||
|
:y (:y bounding-box)
|
||||||
|
:width (:width bounding-box)
|
||||||
|
:height (:height bounding-box)
|
||||||
|
:style {:fill "none"
|
||||||
|
:stroke "orange"
|
||||||
|
:stroke-width (/ 1 zoom)}}]
|
||||||
|
|
||||||
|
(for [[index data] (d/enumerate (:position-data shape))]
|
||||||
|
(let [{:keys [x y width height]} data
|
||||||
|
res (dom/measure-text ctx (:font-size data) (:font-family data) (:text data))]
|
||||||
|
[:g {:key (dm/str index)}
|
||||||
|
;; Text fragment bounding box
|
||||||
|
[:rect {:x x
|
||||||
|
:y (- y height)
|
||||||
|
:width width
|
||||||
|
:height height
|
||||||
|
:style {:fill "none"
|
||||||
|
:stroke "red"
|
||||||
|
:stroke-width (/ 1 zoom)}}]
|
||||||
|
|
||||||
|
;; Text baseline
|
||||||
|
[:line {:x1 (mth/round x)
|
||||||
|
:y1 (mth/round (- (:y data) (:height data)))
|
||||||
|
:x2 (mth/round (+ x width))
|
||||||
|
:y2 (mth/round (- (:y data) (:height data)))
|
||||||
|
:style {:stroke "blue"
|
||||||
|
:stroke-width (/ 1 zoom)}}]
|
||||||
|
|
||||||
|
[:line {:x1 (:x data)
|
||||||
|
:y1 (- (:y data) (:descent res))
|
||||||
|
:x2 (+ (:x data) (:width data))
|
||||||
|
:y2 (- (:y data) (:descent res))
|
||||||
|
:style {:stroke "green"
|
||||||
|
:stroke-width (/ 2 zoom)}}]]))]))
|
||||||
|
|
||||||
|
(mf/defc shape-debug
|
||||||
|
[{:keys [shape]}]
|
||||||
|
[:*
|
||||||
|
(when ^boolean (dbg/enabled? :bounding-boxes)
|
||||||
|
[:& debug-bounding-boxes])
|
||||||
|
|
||||||
|
(when (and ^boolean (cfh/text-shape? shape)
|
||||||
|
^boolean (dbg/enabled? :text-outline)
|
||||||
|
^boolean (seq (:position-data shape)))
|
||||||
|
[:& debug-text-bounds {:shape shape}])])
|
|
@ -20,6 +20,7 @@
|
||||||
[app.main.ui.shapes.frame :as frame]
|
[app.main.ui.shapes.frame :as frame]
|
||||||
[app.main.ui.shapes.shape :refer [shape-container]]
|
[app.main.ui.shapes.shape :refer [shape-container]]
|
||||||
[app.main.ui.workspace.shapes.common :refer [check-shape-props]]
|
[app.main.ui.workspace.shapes.common :refer [check-shape-props]]
|
||||||
|
[app.main.ui.workspace.shapes.debug :as wsd]
|
||||||
[app.main.ui.workspace.shapes.frame.dynamic-modifiers :as fdm]
|
[app.main.ui.workspace.shapes.frame.dynamic-modifiers :as fdm]
|
||||||
[app.util.debug :as dbg]
|
[app.util.debug :as dbg]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
|
@ -193,5 +194,8 @@
|
||||||
[:g.frame-content
|
[:g.frame-content
|
||||||
{:id (dm/str "frame-content-" frame-id)
|
{:id (dm/str "frame-content-" frame-id)
|
||||||
:ref container-ref}
|
:ref container-ref}
|
||||||
[:& frame-shape {:shape shape :ref content-ref}]])]]))))
|
[:& frame-shape {:shape shape :ref content-ref}]])]
|
||||||
|
|
||||||
|
(when *assert*
|
||||||
|
[:& wsd/shape-debug {:shape shape}])]))))
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
[app.main.ui.shapes.group :as group]
|
[app.main.ui.shapes.group :as group]
|
||||||
[app.main.ui.shapes.shape :refer [shape-container]]
|
[app.main.ui.shapes.shape :refer [shape-container]]
|
||||||
[app.main.ui.workspace.shapes.common :refer [check-shape-props]]
|
[app.main.ui.workspace.shapes.common :refer [check-shape-props]]
|
||||||
|
[app.main.ui.workspace.shapes.debug :as wsd]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(defn group-wrapper-factory
|
(defn group-wrapper-factory
|
||||||
|
@ -30,5 +31,7 @@
|
||||||
[:> shape-container {:shape shape}
|
[:> shape-container {:shape shape}
|
||||||
[:& group-shape
|
[:& group-shape
|
||||||
{:shape shape
|
{:shape shape
|
||||||
:childs childs}]]))))
|
:childs childs}]
|
||||||
|
(when *assert*
|
||||||
|
[:& wsd/shape-debug {:shape shape}])]))))
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.ui.shapes.path :as path]
|
[app.main.ui.shapes.path :as path]
|
||||||
[app.main.ui.shapes.shape :refer [shape-container]]
|
[app.main.ui.shapes.shape :refer [shape-container]]
|
||||||
|
[app.main.ui.workspace.shapes.debug :as wsd]
|
||||||
[app.main.ui.workspace.shapes.path.common :as pc]
|
[app.main.ui.workspace.shapes.path.common :as pc]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
@ -38,4 +39,6 @@
|
||||||
|
|
||||||
[:> shape-container {:shape shape
|
[:> shape-container {:shape shape
|
||||||
:pointer-events (when editing? "none")}
|
:pointer-events (when editing? "none")}
|
||||||
[:& path/path-shape {:shape shape}]]))
|
[:& path/path-shape {:shape shape}]
|
||||||
|
(when *assert*
|
||||||
|
[:& wsd/shape-debug {:shape shape}])]))
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.ui.shapes.shape :refer [shape-container]]
|
[app.main.ui.shapes.shape :refer [shape-container]]
|
||||||
[app.main.ui.shapes.svg-raw :as svg-raw]
|
[app.main.ui.shapes.svg-raw :as svg-raw]
|
||||||
|
[app.main.ui.workspace.shapes.debug :as wsd]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(defn svg-raw-wrapper-factory
|
(defn svg-raw-wrapper-factory
|
||||||
|
@ -26,7 +27,9 @@
|
||||||
(if (contains? csvg/svg-group-safe-tags svg-tag)
|
(if (contains? csvg/svg-group-safe-tags svg-tag)
|
||||||
[:> shape-container {:shape shape}
|
[:> shape-container {:shape shape}
|
||||||
[:& svg-raw-shape {:shape shape
|
[:& svg-raw-shape {:shape shape
|
||||||
:childs childs}]]
|
:childs childs}]
|
||||||
|
(when *assert*
|
||||||
|
[:& wsd/shape-debug {:shape shape}])]
|
||||||
|
|
||||||
[:& svg-raw-shape {:shape shape
|
[:& svg-raw-shape {:shape shape
|
||||||
:childs childs}])))))
|
:childs childs}])))))
|
||||||
|
|
|
@ -6,63 +6,14 @@
|
||||||
|
|
||||||
(ns app.main.ui.workspace.shapes.text
|
(ns app.main.ui.workspace.shapes.text
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.geom.shapes :as gsh]
|
|
||||||
[app.common.geom.shapes.text :as gst]
|
|
||||||
[app.common.math :as mth]
|
|
||||||
[app.main.data.workspace.texts :as dwt]
|
[app.main.data.workspace.texts :as dwt]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.ui.shapes.shape :refer [shape-container]]
|
[app.main.ui.shapes.shape :refer [shape-container]]
|
||||||
[app.main.ui.shapes.text :as text]
|
[app.main.ui.shapes.text :as text]
|
||||||
[app.util.debug :as dbg]
|
[app.main.ui.workspace.shapes.debug :as wsd]
|
||||||
[app.util.dom :as dom]
|
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(mf/defc debug-text-bounds
|
|
||||||
{::mf/wrap-props false}
|
|
||||||
[props]
|
|
||||||
(let [shape (unchecked-get props "shape")
|
|
||||||
zoom (mf/deref refs/selected-zoom)
|
|
||||||
bounding-box (gst/shape->rect shape)
|
|
||||||
ctx (js* "document.createElement(\"canvas\").getContext(\"2d\")")]
|
|
||||||
[:g {:transform (gsh/transform-str shape)}
|
|
||||||
[:rect {:x (:x bounding-box)
|
|
||||||
:y (:y bounding-box)
|
|
||||||
:width (:width bounding-box)
|
|
||||||
:height (:height bounding-box)
|
|
||||||
:style {:fill "none"
|
|
||||||
:stroke "orange"
|
|
||||||
:stroke-width (/ 1 zoom)}}]
|
|
||||||
|
|
||||||
(for [[index data] (d/enumerate (:position-data shape))]
|
|
||||||
(let [{:keys [x y width height]} data
|
|
||||||
res (dom/measure-text ctx (:font-size data) (:font-family data) (:text data))]
|
|
||||||
[:g {:key (dm/str index)}
|
|
||||||
;; Text fragment bounding box
|
|
||||||
[:rect {:x x
|
|
||||||
:y (- y height)
|
|
||||||
:width width
|
|
||||||
:height height
|
|
||||||
:style {:fill "none"
|
|
||||||
:stroke "red"
|
|
||||||
:stroke-width (/ 1 zoom)}}]
|
|
||||||
|
|
||||||
;; Text baseline
|
|
||||||
[:line {:x1 (mth/round x)
|
|
||||||
:y1 (mth/round (- (:y data) (:height data)))
|
|
||||||
:x2 (mth/round (+ x width))
|
|
||||||
:y2 (mth/round (- (:y data) (:height data)))
|
|
||||||
:style {:stroke "blue"
|
|
||||||
:stroke-width (/ 1 zoom)}}]
|
|
||||||
|
|
||||||
[:line {:x1 (:x data)
|
|
||||||
:y1 (- (:y data) (:descent res))
|
|
||||||
:x2 (+ (:x data) (:width data))
|
|
||||||
:y2 (- (:y data) (:descent res))
|
|
||||||
:style {:stroke "green"
|
|
||||||
:stroke-width (/ 2 zoom)}}]]))]))
|
|
||||||
|
|
||||||
;; --- Text Wrapper for workspace
|
;; --- Text Wrapper for workspace
|
||||||
(mf/defc text-wrapper
|
(mf/defc text-wrapper
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
|
@ -84,6 +35,5 @@
|
||||||
[:g.text-shape {:key (dm/str shape-id)}
|
[:g.text-shape {:key (dm/str shape-id)}
|
||||||
[:& text/text-shape {:shape shape}]]
|
[:& text/text-shape {:shape shape}]]
|
||||||
|
|
||||||
(when (and ^boolean (dbg/enabled? :text-outline)
|
(when *assert*
|
||||||
^boolean (seq (:position-data shape)))
|
[:& wsd/shape-debug {:shape shape}])]))
|
||||||
[:& debug-text-bounds {:shape shape}])]))
|
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(dbg/toggle! option)
|
(dbg/toggle! option)
|
||||||
(js* "app.main.reinit()")))
|
(js* "app.main.reinit(true)")))
|
||||||
|
|
||||||
handle-close
|
handle-close
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
|
|
|
@ -555,7 +555,8 @@
|
||||||
:on-change #(set-justify % type)
|
:on-change #(set-justify % type)
|
||||||
:name (dm/str "grid-justify-items-" (d/name type))}
|
:name (dm/str "grid-justify-items-" (d/name type))}
|
||||||
(for [justify [:start :center :end :space-around :space-between :stretch]]
|
(for [justify [:start :center :end :space-around :space-between :stretch]]
|
||||||
[:& radio-button {:value (d/name justify)
|
[:& radio-button {:key (dm/str "justify-item-" (d/name justify))
|
||||||
|
:value (d/name justify)
|
||||||
:icon (get-layout-grid-icon-refactor :justify-items justify is-col?)
|
:icon (get-layout-grid-icon-refactor :justify-items justify is-col?)
|
||||||
:title (dm/str "Justify items " (d/name justify))
|
:title (dm/str "Justify items " (d/name justify))
|
||||||
:id (dm/str "justify-items-" (d/name justify) "-" (d/name type))}])]))
|
:id (dm/str "justify-items-" (d/name justify) "-" (d/name type))}])]))
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
[app.main.data.workspace.modifiers :as dwm]
|
[app.main.data.workspace.modifiers :as dwm]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.ui.context :as ctx]
|
[app.main.ui.context :as ctx]
|
||||||
|
[app.main.ui.flex-controls :as mfc]
|
||||||
[app.main.ui.hooks :as ui-hooks]
|
[app.main.ui.hooks :as ui-hooks]
|
||||||
[app.main.ui.measurements :as msr]
|
[app.main.ui.measurements :as msr]
|
||||||
[app.main.ui.shapes.export :as use]
|
[app.main.ui.shapes.export :as use]
|
||||||
|
@ -232,31 +233,32 @@
|
||||||
|
|
||||||
disabled-guides? (or drawing-tool transform drawing-path? node-editing?)
|
disabled-guides? (or drawing-tool transform drawing-path? node-editing?)
|
||||||
|
|
||||||
one-selected-shape? (= (count selected-shapes) 1)
|
single-select? (= (count selected-shapes) 1)
|
||||||
|
|
||||||
show-padding? (and (nil? transform)
|
first-shape (first selected-shapes)
|
||||||
one-selected-shape?
|
|
||||||
(= (:type (first selected-shapes)) :frame)
|
|
||||||
(= (:layout (first selected-shapes)) :flex)
|
|
||||||
(zero? (:rotation (first selected-shapes))))
|
|
||||||
|
|
||||||
|
show-padding?
|
||||||
|
(and (nil? transform)
|
||||||
|
single-select?
|
||||||
|
(= (:type first-shape) :frame)
|
||||||
|
(= (:layout first-shape) :flex)
|
||||||
|
(zero? (:rotation first-shape)))
|
||||||
|
|
||||||
show-margin? (and (nil? transform)
|
show-margin?
|
||||||
one-selected-shape?
|
(and (nil? transform)
|
||||||
(= (:layout selected-frame) :flex)
|
single-select?
|
||||||
(zero? (:rotation (first selected-shapes))))
|
(= (:layout selected-frame) :flex)
|
||||||
|
(zero? (:rotation first-shape)))
|
||||||
|
|
||||||
first-selected-shape (first selected-shapes)
|
selecting-first-level-frame? (and single-select? (cfh/root-frame? first-shape))
|
||||||
selecting-first-level-frame? (and one-selected-shape?
|
|
||||||
(cfh/root-frame? first-selected-shape))
|
|
||||||
|
|
||||||
offset-x (if selecting-first-level-frame?
|
offset-x (if selecting-first-level-frame?
|
||||||
(:x first-selected-shape)
|
(:x first-shape)
|
||||||
(:x selected-frame))
|
(:x selected-frame))
|
||||||
|
|
||||||
|
|
||||||
offset-y (if selecting-first-level-frame?
|
offset-y (if selecting-first-level-frame?
|
||||||
(:y (first selected-shapes))
|
(:y first-shape)
|
||||||
(:y selected-frame))
|
(:y selected-frame))
|
||||||
|
|
||||||
rule-area-size (/ rules/rule-area-size zoom)]
|
rule-area-size (/ rules/rule-area-size zoom)]
|
||||||
|
@ -439,24 +441,28 @@
|
||||||
:zoom zoom}])
|
:zoom zoom}])
|
||||||
|
|
||||||
(when show-padding?
|
(when show-padding?
|
||||||
[:*
|
[:& mfc/padding-control
|
||||||
[:& msr/padding
|
{:frame first-shape
|
||||||
{:frame (first selected-shapes)
|
:hover @frame-hover
|
||||||
:hover @frame-hover
|
:zoom zoom
|
||||||
:zoom zoom
|
:alt? @alt?
|
||||||
:alt? @alt?
|
:shift? @shift?
|
||||||
:shift? @shift?}]
|
:on-move-selected on-move-selected
|
||||||
|
:on-context-menu on-menu-selected}])
|
||||||
|
|
||||||
[:& msr/gap
|
(when show-padding?
|
||||||
{:frame (first selected-shapes)
|
[:& mfc/gap-control
|
||||||
:hover @frame-hover
|
{:frame first-shape
|
||||||
:zoom zoom
|
:hover @frame-hover
|
||||||
:alt? @alt?
|
:zoom zoom
|
||||||
:shift? @shift?}]])
|
:alt? @alt?
|
||||||
|
:shift? @shift?
|
||||||
|
:on-move-selected on-move-selected
|
||||||
|
:on-context-menu on-menu-selected}])
|
||||||
|
|
||||||
(when show-margin?
|
(when show-margin?
|
||||||
[:& msr/margin
|
[:& mfc/margin-control
|
||||||
{:shape (first selected-shapes)
|
{:shape first-shape
|
||||||
:parent selected-frame
|
:parent selected-frame
|
||||||
:hover @frame-hover
|
:hover @frame-hover
|
||||||
:zoom zoom
|
:zoom zoom
|
||||||
|
|
|
@ -43,7 +43,6 @@
|
||||||
(mf/deps id blocked hidden type selected edition drawing-tool text-editing?
|
(mf/deps id blocked hidden type selected edition drawing-tool text-editing?
|
||||||
node-editing? grid-editing? drawing-path? create-comment? @z? @space?
|
node-editing? grid-editing? drawing-path? create-comment? @z? @space?
|
||||||
panning workspace-read-only?)
|
panning workspace-read-only?)
|
||||||
|
|
||||||
(fn [bevent]
|
(fn [bevent]
|
||||||
;; We need to handle editor related stuff here because
|
;; We need to handle editor related stuff here because
|
||||||
;; handling on editor dom node does not works properly.
|
;; handling on editor dom node does not works properly.
|
||||||
|
@ -127,6 +126,7 @@
|
||||||
(not mod?)
|
(not mod?)
|
||||||
(not shift?)
|
(not shift?)
|
||||||
(not @space?))
|
(not @space?))
|
||||||
|
|
||||||
(dom/prevent-default bevent)
|
(dom/prevent-default bevent)
|
||||||
(dom/stop-propagation bevent)
|
(dom/stop-propagation bevent)
|
||||||
(when-not (or workspace-read-only? @z?)
|
(when-not (or workspace-read-only? @z?)
|
||||||
|
|
|
@ -23,8 +23,9 @@
|
||||||
(def rules-background "var(--panel-background-color)")
|
(def rules-background "var(--panel-background-color)")
|
||||||
(def selection-area-color "var(--color-primary)")
|
(def selection-area-color "var(--color-primary)")
|
||||||
(def selection-area-opacity 0.3)
|
(def selection-area-opacity 0.3)
|
||||||
(def over-number-size 50)
|
(def over-number-size 100)
|
||||||
(def over-number-opacity 0.7)
|
(def over-number-opacity 0.8)
|
||||||
|
(def over-number-percent 0.75)
|
||||||
|
|
||||||
(def font-size 12)
|
(def font-size 12)
|
||||||
(def font-family "worksans")
|
(def font-family "worksans")
|
||||||
|
@ -204,7 +205,29 @@
|
||||||
;; When using the format-number callls we consider if the guide is associated to a frame and we show the position relative to it with the offset
|
;; When using the format-number callls we consider if the guide is associated to a frame and we show the position relative to it with the offset
|
||||||
(let [rules-background rules-background]
|
(let [rules-background rules-background]
|
||||||
[:g.selection-area
|
[:g.selection-area
|
||||||
|
[:defs
|
||||||
|
[:linearGradient {:id "selection-gradient-start"}
|
||||||
|
[:stop {:offset "0%" :stop-color rules-background :stop-opacity 0}]
|
||||||
|
[:stop {:offset "40%" :stop-color rules-background :stop-opacity 1}]
|
||||||
|
[:stop {:offset "100%" :stop-color rules-background :stop-opacity 1}]]
|
||||||
|
|
||||||
|
[:linearGradient {:id "selection-gradient-end"}
|
||||||
|
[:stop {:offset "0%" :stop-color rules-background :stop-opacity 1}]
|
||||||
|
[:stop {:offset "60%" :stop-color rules-background :stop-opacity 1}]
|
||||||
|
[:stop {:offset "100%" :stop-color rules-background :stop-opacity 0}]]]
|
||||||
[:g
|
[:g
|
||||||
|
[:rect {:x (- (:x selection-rect) (* (* over-number-size over-number-percent) zoom-inverse))
|
||||||
|
:y (:y vbox)
|
||||||
|
:width (* over-number-size zoom-inverse)
|
||||||
|
:height (* rule-area-size zoom-inverse)
|
||||||
|
:fill "url('#selection-gradient-start')"}]
|
||||||
|
|
||||||
|
[:rect {:x (- (:x2 selection-rect) (* over-number-size (- 1 over-number-percent)))
|
||||||
|
:y (:y vbox)
|
||||||
|
:width (* over-number-size zoom-inverse)
|
||||||
|
:height (* rule-area-size zoom-inverse)
|
||||||
|
:fill "url('#selection-gradient-end')"}]
|
||||||
|
|
||||||
[:rect {:x (:x selection-rect)
|
[:rect {:x (:x selection-rect)
|
||||||
:y (:y vbox)
|
:y (:y vbox)
|
||||||
:width (:width selection-rect)
|
:width (:width selection-rect)
|
||||||
|
@ -212,15 +235,8 @@
|
||||||
:style {:fill selection-area-color
|
:style {:fill selection-area-color
|
||||||
:fill-opacity selection-area-opacity}}]
|
:fill-opacity selection-area-opacity}}]
|
||||||
|
|
||||||
[:rect {:x (- (:x selection-rect) (* over-number-size zoom-inverse))
|
|
||||||
:y (:y vbox)
|
|
||||||
:width (* over-number-size zoom-inverse)
|
|
||||||
:height (* rule-area-size zoom-inverse)
|
|
||||||
:style {:fill rules-background
|
|
||||||
:fill-opacity over-number-opacity}}]
|
|
||||||
|
|
||||||
[:text {:x (- (:x1 selection-rect) (* 4 zoom-inverse))
|
[:text {:x (- (:x1 selection-rect) (* 4 zoom-inverse))
|
||||||
:y (+ (:y vbox) (* 12 zoom-inverse))
|
:y (+ (:y vbox) (* 10.6 zoom-inverse))
|
||||||
:text-anchor "end"
|
:text-anchor "end"
|
||||||
:dominant-baseline "middle"
|
:dominant-baseline "middle"
|
||||||
:style {:font-size (* font-size zoom-inverse)
|
:style {:font-size (* font-size zoom-inverse)
|
||||||
|
@ -228,15 +244,8 @@
|
||||||
:fill selection-area-color}}
|
:fill selection-area-color}}
|
||||||
(fmt/format-number (- (:x1 selection-rect) offset-x))]
|
(fmt/format-number (- (:x1 selection-rect) offset-x))]
|
||||||
|
|
||||||
[:rect {:x (:x2 selection-rect)
|
|
||||||
:y (:y vbox)
|
|
||||||
:width (* over-number-size zoom-inverse)
|
|
||||||
:height (* rule-area-size zoom-inverse)
|
|
||||||
:style {:fill rules-background
|
|
||||||
:fill-opacity over-number-opacity}}]
|
|
||||||
|
|
||||||
[:text {:x (+ (:x2 selection-rect) (* 4 zoom-inverse))
|
[:text {:x (+ (:x2 selection-rect) (* 4 zoom-inverse))
|
||||||
:y (+ (:y vbox) (* 12 zoom-inverse))
|
:y (+ (:y vbox) (* 10.6 zoom-inverse))
|
||||||
:text-anchor "start"
|
:text-anchor "start"
|
||||||
:dominant-baseline "middle"
|
:dominant-baseline "middle"
|
||||||
:style {:font-size (* font-size zoom-inverse)
|
:style {:font-size (* font-size zoom-inverse)
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.colors :as cc]
|
[app.common.colors :as cc]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.math :as mth]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[cuerdas.core :as str]))
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
|
@ -82,3 +84,10 @@
|
||||||
(:name color)
|
(:name color)
|
||||||
(:color color)
|
(:color color)
|
||||||
(gradient-type->string (:type (:gradient color)))))
|
(gradient-type->string (:type (:gradient color)))))
|
||||||
|
|
||||||
|
(defn random-color
|
||||||
|
[]
|
||||||
|
(dm/fmt "rgb(%, %, %)"
|
||||||
|
(mth/floor (* (js/Math.random) 256))
|
||||||
|
(mth/floor (* (js/Math.random) 256))
|
||||||
|
(mth/floor (* (js/Math.random) 256))))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue