mirror of
https://github.com/penpot/penpot.git
synced 2025-04-16 00:41:25 -05:00
Merge pull request #190 from uxbox/310/change-coordinate-origin
Change coordinate origin
This commit is contained in:
commit
b9210d45f3
27 changed files with 246 additions and 208 deletions
|
@ -1,3 +1,5 @@
|
|||
.projects-page {
|
||||
padding: 1rem;
|
||||
height: 100%;
|
||||
background-color: $color-white;
|
||||
}
|
||||
|
|
|
@ -1,20 +1,13 @@
|
|||
.recent-files-page {
|
||||
overflow: scroll;
|
||||
height: 100%;
|
||||
background-color: $color-white;
|
||||
}
|
||||
|
||||
.recent-files-row {
|
||||
padding: 1rem;
|
||||
border-top: 1px solid $color-gray-10;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-white;
|
||||
|
||||
.grid-item.project-th {
|
||||
box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
&.first {
|
||||
border-top: none;
|
||||
}
|
||||
|
|
|
@ -349,11 +349,18 @@
|
|||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
background-color: $color-canvas;
|
||||
|
||||
.img-th {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MULTISELECT OPTIONS BAR
|
||||
|
|
|
@ -88,7 +88,3 @@
|
|||
(when (= "main" (unchecked-get js/window app-sym))
|
||||
(reinit)))
|
||||
|
||||
(defn ^:export toggle-debug
|
||||
[]
|
||||
(swap! st/*debug* not))
|
||||
|
||||
|
|
|
@ -1237,6 +1237,9 @@
|
|||
(us/verify number? index)
|
||||
|
||||
(ptk/reify ::relocate-shape
|
||||
IUpdateGroup
|
||||
(get-ids [_] [id])
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (:current-page-id state)
|
||||
|
|
|
@ -70,7 +70,6 @@
|
|||
(let [frame (get objects (:frame-id shape))
|
||||
{:keys [width height rotation]} shape
|
||||
|
||||
center (gpt/center shape)
|
||||
shapev (-> (gpt/point width height))
|
||||
|
||||
;; Vector modifiers depending on the handler
|
||||
|
@ -140,7 +139,7 @@
|
|||
(watch [_ state stream]
|
||||
(let [stoper (rx/filter ms/mouse-up? stream)
|
||||
group (gsh/selection-rect shapes)
|
||||
group-center (gpt/center group)
|
||||
group-center (gsh/center group)
|
||||
initial-angle (gpt/angle (apply-zoom @ms/mouse-position) group-center)
|
||||
calculate-angle (fn [pos ctrl?]
|
||||
(let [angle (- (gpt/angle pos group-center) initial-angle)
|
||||
|
@ -161,7 +160,7 @@
|
|||
(rx/with-latest vector ms/mouse-position-ctrl)
|
||||
(rx/map (fn [[pos ctrl?]]
|
||||
(let [delta-angle (calculate-angle pos ctrl?)]
|
||||
(set-rotation delta-angle shapes))))
|
||||
(set-rotation delta-angle shapes group-center))))
|
||||
(rx/take-until stoper))
|
||||
(rx/of (apply-modifiers (map :id shapes))))))))
|
||||
|
||||
|
@ -275,7 +274,7 @@
|
|||
|
||||
;; Set-rotation is custom because applies different modifiers to each shape adjusting their position
|
||||
(defn set-rotation
|
||||
[delta-rotation shapes]
|
||||
[delta-rotation shapes center]
|
||||
(ptk/reify ::set-rotation
|
||||
IUpdateGroup
|
||||
(get-ids [_] (map :id shapes))
|
||||
|
@ -293,8 +292,7 @@
|
|||
(rotate-around-center [state angle center shapes]
|
||||
(reduce #(rotate-shape %1 angle %2 center) state shapes))]
|
||||
|
||||
(let [center (-> shapes gsh/selection-rect gpt/center)
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
(let [objects (get-in state [:workspace-data page-id :objects])
|
||||
id->obj #(get objects %)
|
||||
get-children (fn [shape] (map id->obj (helpers/get-children (:id shape) objects)))
|
||||
shapes (concat shapes (mapcat get-children shapes))]
|
||||
|
|
|
@ -22,13 +22,14 @@
|
|||
[uxbox.main.ui.shapes.text :as text]
|
||||
[uxbox.main.ui.shapes.group :as group]))
|
||||
|
||||
(def ^:private background-color "#E8E9EA") ;; $color-canvas
|
||||
(mf/defc background
|
||||
[]
|
||||
[:rect
|
||||
{:x 0 :y 0
|
||||
:width "100%"
|
||||
:height "100%"
|
||||
:fill "#AFB2BF"}])
|
||||
:fill background-color}])
|
||||
|
||||
(defn- calculate-dimensions
|
||||
[data]
|
||||
|
@ -75,7 +76,7 @@
|
|||
(let [group-wrapper (mf/use-memo (mf/deps objects) #(group-wrapper objects))]
|
||||
(when (and shape (not (:hidden shape)))
|
||||
(let [shape (geom/transform-shape frame shape)
|
||||
opts #js {:shape shape :frame frame}]
|
||||
opts #js {:shape shape}]
|
||||
(case (:type shape)
|
||||
:curve [:> path/path-shape opts]
|
||||
:text [:> text/text-shape opts]
|
||||
|
@ -84,7 +85,7 @@
|
|||
:path [:> path/path-shape opts]
|
||||
:image [:> image/image-shape opts]
|
||||
:circle [:> circle/circle-shape opts]
|
||||
:group [:> group-wrapper opts]
|
||||
:group [:> group-wrapper {:shape shape :frame frame}]
|
||||
nil))))))
|
||||
|
||||
(mf/defc page-svg
|
||||
|
|
|
@ -57,6 +57,14 @@
|
|||
(get-in % [:workspace-data page-id]))
|
||||
(l/derived st/state)))
|
||||
|
||||
(defn object-by-id
|
||||
[id]
|
||||
(letfn [(selector [state]
|
||||
(let [page-id (get-in state [:workspace-page :id])
|
||||
objects (get-in state [:workspace-data page-id :objects])]
|
||||
(get objects id)))]
|
||||
(l/derived selector st/state =)))
|
||||
|
||||
(defn objects-by-id
|
||||
[ids]
|
||||
(letfn [(selector [state]
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
[okulary.core :as l]
|
||||
[potok.core :as ptk]
|
||||
[uxbox.common.uuid :as uuid]
|
||||
[uxbox.util.storage :refer [storage]]))
|
||||
[uxbox.util.storage :refer [storage]]
|
||||
[uxbox.util.debug :refer [debug? logjs]]))
|
||||
|
||||
(enable-console-print!)
|
||||
|
||||
|
@ -34,13 +35,11 @@
|
|||
:else
|
||||
(str "unk: " (pr-str event))))
|
||||
|
||||
(defonce ^:dynamic *debug* (atom false))
|
||||
|
||||
(when *assert*
|
||||
(defonce debug-subscription
|
||||
(as-> stream $
|
||||
(rx/filter ptk/event? $)
|
||||
(rx/filter (fn [s] (deref *debug*)) $)
|
||||
#_(rx/filter ptk/event? $)
|
||||
(rx/filter (fn [s] (debug? :events)) $)
|
||||
(rx/subscribe $ (fn [event]
|
||||
(println "[stream]: " (repr-event event)))))))
|
||||
(defn emit!
|
||||
|
@ -62,3 +61,10 @@
|
|||
([props]
|
||||
(emit! #(merge % initial-state props))
|
||||
(rx/to-atom store state)))
|
||||
|
||||
(defn ^:export dump-state []
|
||||
(logjs "state" @state))
|
||||
|
||||
(defn ^:export dump-objects []
|
||||
(let [page-id (get @state :current-page-id)]
|
||||
(logjs "state" (get-in @state [:workspace-data page-id :objects]))))
|
||||
|
|
|
@ -51,11 +51,14 @@
|
|||
|
||||
(mf/defc recent-project
|
||||
[{:keys [project files first? locale] :as props}]
|
||||
(let [project-id (:id project)]
|
||||
(let [project-id (:id project)
|
||||
team-id (:team-id project)]
|
||||
[:div.recent-files-row
|
||||
{:class-name (when first? "first")}
|
||||
[:div.recent-files-row-title
|
||||
[:h2.recent-files-row-title-name (:name project)]
|
||||
[:h2.recent-files-row-title-name {:on-click #(st/emit! (rt/nav :dashboard-project {:team-id team-id
|
||||
:project-id project-id}))
|
||||
:style {:cursor "pointer"}} (:name project)]
|
||||
[:span.recent-files-row-title-info (str (:file-count project) " files")]
|
||||
(let [time (-> (:modified-at project)
|
||||
(dt/timeago {:locale locale}))]
|
||||
|
|
|
@ -23,11 +23,10 @@
|
|||
(when (debug? :bounding-boxes)
|
||||
(let [shape (unchecked-get props "shape")
|
||||
frame (unchecked-get props "frame")
|
||||
selrect (geom/transform-selrect frame shape)
|
||||
shape-path (-> shape
|
||||
(geom/transform-apply-modifiers)
|
||||
(geom/translate-to-frame frame))
|
||||
shape-center (geom/center shape-path)]
|
||||
selrect (-> shape
|
||||
(geom/selection-rect-shape)
|
||||
(geom/translate-to-frame frame))
|
||||
shape-center (geom/center selrect)]
|
||||
[:g
|
||||
[:text {:x (:x selrect)
|
||||
:y (- (:y selrect) 5)
|
||||
|
@ -37,10 +36,12 @@
|
|||
:stroke-width 0.1}
|
||||
(str/format "%s - (%s, %s)" (str/slice (str (:id shape)) 0 8) (fix (:x shape)) (fix (:y shape)))]
|
||||
|
||||
[:circle {:cx (:x shape-center) :cy (:y shape-center) :r 5 :fill "yellow"}]
|
||||
|
||||
[:rect {:x (:x selrect)
|
||||
:y (:y selrect)
|
||||
:width (:width selrect)
|
||||
:height (:height selrect)
|
||||
:style {:stroke "red" :fill "transparent" :stroke-width "1px" :stroke-opacity 0.5}}]])))
|
||||
:style {:stroke "red"
|
||||
:fill "transparent"
|
||||
:stroke-width "1px"
|
||||
:stroke-opacity 0.5
|
||||
:pointer-events "none"}}]])))
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
(declare circle-shape)
|
||||
|
||||
(mf/defc circle-wrapper
|
||||
[{:keys [shape frame] :as props}]
|
||||
[{:keys [shape] :as props}]
|
||||
(let [selected (mf/deref refs/selected-shapes)
|
||||
selected? (contains? selected (:id shape))
|
||||
on-mouse-down #(common/on-mouse-down % shape)
|
||||
|
@ -30,8 +30,7 @@
|
|||
[:g.shape {:class (when selected? "selected")
|
||||
:on-mouse-down on-mouse-down
|
||||
:on-context-menu on-context-menu}
|
||||
[:& circle-shape {:shape (geom/transform-shape frame shape)}]
|
||||
[:& bounding-box {:shape shape :frame frame}]]))
|
||||
[:& circle-shape {:shape shape}]]))
|
||||
|
||||
;; --- Circle Shape
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@
|
|||
|
||||
inv-zoom (/ 1 zoom)
|
||||
childs (mapv #(get objects %) (:shapes shape))
|
||||
ds-modifier (:displacement-modifier shape)
|
||||
ds-modifier (get-in shape [:modifiers :displacement])
|
||||
|
||||
label-pos (cond-> (gpt/point x (- y 10))
|
||||
(gmt/matrix? ds-modifier) (gpt/transform ds-modifier))
|
||||
|
|
|
@ -73,11 +73,9 @@
|
|||
|
||||
[:& group-shape
|
||||
{:frame frame
|
||||
:shape (geom/transform-shape frame shape)
|
||||
:shape shape
|
||||
:children children
|
||||
:is-child-selected? is-child-selected?}]
|
||||
(when (not is-child-selected?)
|
||||
[:& bounding-box {:shape shape :frame frame}])]))))
|
||||
:is-child-selected? is-child-selected?}]]))))
|
||||
|
||||
(defn group-shape
|
||||
[shape-wrapper]
|
||||
|
|
|
@ -15,15 +15,14 @@
|
|||
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||
[uxbox.main.ui.shapes.common :as common]
|
||||
[uxbox.util.interop :as itr]
|
||||
[uxbox.util.geom.matrix :as gmt]
|
||||
[uxbox.main.ui.shapes.bounding-box :refer [bounding-box]]))
|
||||
[uxbox.util.geom.matrix :as gmt]))
|
||||
|
||||
;; --- Image Wrapper
|
||||
|
||||
(declare image-shape)
|
||||
|
||||
(mf/defc image-wrapper
|
||||
[{:keys [shape frame] :as props}]
|
||||
[{:keys [shape] :as props}]
|
||||
(let [selected (mf/deref refs/selected-shapes)
|
||||
selected? (contains? selected (:id shape))
|
||||
on-mouse-down (mf/use-callback
|
||||
|
@ -37,8 +36,7 @@
|
|||
[:g.shape {:class (when selected? "selected")
|
||||
:on-mouse-down on-mouse-down
|
||||
:on-context-menu on-context-menu}
|
||||
[:& image-shape {:shape (geom/transform-shape frame shape)}]
|
||||
[:& bounding-box {:shape shape :frame frame}]]))
|
||||
[:& image-shape {:shape shape}]]))
|
||||
|
||||
;; --- Image Shape
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
(declare path-shape)
|
||||
|
||||
(mf/defc path-wrapper
|
||||
[{:keys [shape frame] :as props}]
|
||||
[{:keys [shape] :as props}]
|
||||
(let [selected (mf/deref refs/selected-shapes)
|
||||
selected? (contains? selected (:id shape))
|
||||
on-mouse-down (mf/use-callback
|
||||
|
@ -42,8 +42,7 @@
|
|||
[:g.shape {:on-double-click on-double-click
|
||||
:on-mouse-down on-mouse-down
|
||||
:on-context-menu on-context-menu}
|
||||
[:& path-shape {:shape (geom/transform-shape frame shape) :background? true}]
|
||||
[:& bounding-box {:shape shape :frame frame}]]))
|
||||
[:& path-shape {:shape shape :background? true}]]))
|
||||
|
||||
;; --- Path Shape
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||
[uxbox.main.ui.shapes.common :as common]
|
||||
[uxbox.util.interop :as itr]
|
||||
[uxbox.main.ui.shapes.bounding-box :refer [bounding-box]]
|
||||
[uxbox.main.ui.shapes.custom-stroke :refer [shape-custom-stroke]]))
|
||||
|
||||
;; --- Rect Wrapper
|
||||
|
@ -26,7 +25,6 @@
|
|||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
frame (unchecked-get props "frame")
|
||||
on-mouse-down (mf/use-callback
|
||||
(mf/deps shape)
|
||||
#(common/on-mouse-down % shape))
|
||||
|
@ -35,8 +33,7 @@
|
|||
#(common/on-context-menu % shape))]
|
||||
[:g.shape {:on-mouse-down on-mouse-down
|
||||
:on-context-menu on-context-menu}
|
||||
[:& rect-shape {:shape (geom/transform-shape frame shape) }]
|
||||
[:& bounding-box {:shape shape :frame frame}]]))
|
||||
[:& rect-shape {:shape shape}]]))
|
||||
|
||||
;; --- Rect Shape
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
[uxbox.main.ui.shapes.text :as text]
|
||||
[uxbox.main.ui.shapes.group :as group]
|
||||
[uxbox.main.ui.shapes.frame :as frame]
|
||||
[uxbox.main.ui.shapes.bounding-box :refer [bounding-box]]
|
||||
[uxbox.util.geom.shapes :as gsh]
|
||||
[uxbox.main.refs :as refs]))
|
||||
|
||||
(defn- shape-wrapper-memo-equals?
|
||||
|
@ -42,21 +44,24 @@
|
|||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
frame (unchecked-get props "frame")
|
||||
opts #js {:shape shape :frame frame}]
|
||||
opts #js {:shape (->> shape (gsh/transform-shape frame))
|
||||
:frame frame}]
|
||||
(when (and shape (not (:hidden shape)))
|
||||
(case (:type shape)
|
||||
:group [:> group-wrapper opts]
|
||||
:curve [:> path/path-wrapper opts]
|
||||
:text [:> text/text-wrapper opts]
|
||||
:icon [:> icon/icon-wrapper opts]
|
||||
:rect [:> rect/rect-wrapper opts]
|
||||
:path [:> path/path-wrapper opts]
|
||||
:image [:> image/image-wrapper opts]
|
||||
:circle [:> circle/circle-wrapper opts]
|
||||
[:*
|
||||
(case (:type shape)
|
||||
:group [:> group-wrapper opts]
|
||||
:curve [:> path/path-wrapper opts]
|
||||
:text [:> text/text-wrapper opts]
|
||||
:icon [:> icon/icon-wrapper opts]
|
||||
:rect [:> rect/rect-wrapper opts]
|
||||
:path [:> path/path-wrapper opts]
|
||||
:image [:> image/image-wrapper opts]
|
||||
:circle [:> circle/circle-wrapper opts]
|
||||
|
||||
;; Only used when drawing a new frame.
|
||||
:frame [:> frame-wrapper opts]
|
||||
nil))))
|
||||
;; Only used when drawing a new frame.
|
||||
:frame [:> frame-wrapper {:shape shape}]
|
||||
nil)
|
||||
[:& bounding-box {:shape shape :frame frame}]])))
|
||||
|
||||
(def group-wrapper (group/group-wrapper shape-wrapper))
|
||||
(def frame-wrapper (frame/frame-wrapper shape-wrapper))
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
(declare text-shape)
|
||||
|
||||
(mf/defc text-wrapper
|
||||
[{:keys [shape frame] :as props}]
|
||||
[{:keys [shape] :as props}]
|
||||
(let [{:keys [id x1 y1 content group]} shape
|
||||
selected (mf/deref refs/selected-shapes)
|
||||
edition (mf/deref refs/selected-edition)
|
||||
|
@ -67,8 +67,8 @@
|
|||
:on-mouse-down on-mouse-down
|
||||
:on-context-menu on-context-menu}
|
||||
(if edition?
|
||||
[:& text-shape-edit {:shape (geom/transform-shape frame shape)}]
|
||||
[:& text-shape {:shape (geom/transform-shape frame shape)
|
||||
[:& text-shape-edit {:shape shape}]
|
||||
[:& text-shape {:shape shape
|
||||
:selected? selected?}])]))
|
||||
|
||||
;; --- Text Rendering
|
||||
|
@ -284,7 +284,8 @@
|
|||
|
||||
(mf/use-effect on-mount)
|
||||
|
||||
[:foreignObject {:x x :y y :width width :height height :ref self-ref}
|
||||
[:foreignObject {:transform (geom/transform-matrix shape)
|
||||
:x x :y y :width width :height height :ref self-ref}
|
||||
[:> rslate/Slate {:editor editor
|
||||
:value @state
|
||||
:on-change on-change}
|
||||
|
@ -305,13 +306,7 @@
|
|||
|
||||
(mf/defc text-shape
|
||||
[{:keys [shape selected?] :as props}]
|
||||
(let [{:keys [id x y width height rotation content]} shape
|
||||
transform (when (and rotation (pos? rotation))
|
||||
(str/format "rotate(%s %s %s)"
|
||||
rotation
|
||||
(+ x (/ width 2))
|
||||
(+ y (/ height 2))))
|
||||
|
||||
(let [{:keys [id x y width height content]} shape
|
||||
content (parse-content content)
|
||||
editor (mf/use-memo #(rslate/withReact (slate/createEditor)))
|
||||
|
||||
|
@ -334,7 +329,7 @@
|
|||
|
||||
[:foreignObject {:x x
|
||||
:y y
|
||||
:transform transform
|
||||
:transform (geom/transform-matrix shape)
|
||||
:id (str id)
|
||||
:width width
|
||||
:height height}
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
frames (:frames data [])
|
||||
objects (:objects data)
|
||||
frame (get frames index)]
|
||||
|
||||
[:section.viewer-preview
|
||||
(cond
|
||||
(empty? frames)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
[uxbox.common.data :as d]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.data.viewer :as dv]
|
||||
[uxbox.main.data.helpers :as helpers]
|
||||
[uxbox.main.ui.components.dropdown :refer [dropdown']]
|
||||
[uxbox.main.ui.shapes.frame :as frame]
|
||||
[uxbox.main.exports :as exports]
|
||||
|
@ -83,7 +84,15 @@
|
|||
(let [modifier (-> (gpt/point (:x frame) (:y frame))
|
||||
(gpt/negate)
|
||||
(gmt/translate-matrix))
|
||||
frame (assoc frame :displacement-modifier modifier)
|
||||
|
||||
frame-id (:id frame)
|
||||
modifier-ids (concat [frame-id] (helpers/get-children frame-id objects))
|
||||
|
||||
update-fn (fn [state shape-id]
|
||||
(-> state
|
||||
(assoc-in [shape-id :modifiers :displacement] modifier)))
|
||||
objects (reduce update-fn objects modifier-ids)
|
||||
frame (assoc-in frame [:modifiers :displacement] modifier )
|
||||
|
||||
width (* (:width frame) zoom)
|
||||
height (* (:height frame) zoom)
|
||||
|
@ -96,9 +105,9 @@
|
|||
:height height
|
||||
:version "1.1"
|
||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||
:xmlns "http://www.w3.org/2000/svg"}
|
||||
:xmlns "http://www.w3.org/2000/svg"}
|
||||
[:& frame-wrapper {:shape frame
|
||||
:view-box vbox}]]))
|
||||
:view-box vbox}]]))
|
||||
|
||||
(mf/defc thumbnails-summary
|
||||
[{:keys [on-toggle-expand on-close total] :as props}]
|
||||
|
|
|
@ -35,15 +35,18 @@
|
|||
(declare handle-finish-drawing)
|
||||
(declare conditional-align)
|
||||
|
||||
(def ^:private default-color "#b1b2b5") ;; $color-gray-20
|
||||
|
||||
(def ^:private minimal-shapes
|
||||
[{:type :rect
|
||||
:name "Rect"
|
||||
:stroke-color "#000000"
|
||||
:fill-color default-color
|
||||
:stroke-alignment :center}
|
||||
{:type :image}
|
||||
{:type :icon}
|
||||
{:type :circle
|
||||
:name "Circle"}
|
||||
:name "Circle"
|
||||
:fill-color default-color}
|
||||
{:type :path
|
||||
:name "Path"
|
||||
:stroke-style :solid
|
||||
|
@ -305,17 +308,16 @@
|
|||
|
||||
(mf/defc generic-draw-area
|
||||
[{:keys [shape zoom]}]
|
||||
(let [{:keys [x y width height]} (geom/transform-selrect nil shape)]
|
||||
(let [{:keys [x y width height]} (geom/selection-rect-shape shape)]
|
||||
(when (and x y)
|
||||
[:g
|
||||
[:& shapes/shape-wrapper {:shape shape}]
|
||||
[:rect.main {:x x :y y
|
||||
:width width
|
||||
:height height
|
||||
:stroke-dasharray (str (/ 5.0 zoom) "," (/ 5 zoom))
|
||||
:style {:stroke "#333"
|
||||
:style {:stroke "#1FDEA7"
|
||||
:fill "transparent"
|
||||
:stroke-opacity "1"}}]])))
|
||||
:stroke-width "1"}}]])))
|
||||
|
||||
(mf/defc path-draw-area
|
||||
[{:keys [shape] :as props}]
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
[{:keys [shape zoom on-resize on-rotate] :as props}]
|
||||
(let [{:keys [x y width height rotation] :as shape} (geom/shape->rect-shape shape)
|
||||
radius (if (> (max width height) handler-size-threshold) 4.0 4.0)
|
||||
|
||||
transform (geom/transform-matrix shape)
|
||||
|
||||
resize-handlers {:top [(+ x (/ width 2 )) y]
|
||||
|
@ -85,14 +86,12 @@
|
|||
|
||||
[:g.controls
|
||||
[:rect.main {:transform transform
|
||||
:x x :y y
|
||||
:width width
|
||||
:height height
|
||||
;;:stroke-dasharray (str (/ 8.0 zoom) "," (/ 5 zoom))
|
||||
:vector-effect "non-scaling-stroke"
|
||||
:x (- x 1) :y (- y 1)
|
||||
:width (+ width 2)
|
||||
:height (+ height 2)
|
||||
:style {:stroke "#1FDEA7"
|
||||
:fill "transparent"
|
||||
:stroke-opacity "1"}}]
|
||||
:stroke-width "1"
|
||||
:fill "transparent"}}]
|
||||
|
||||
(for [[position [cx cy]] resize-handlers]
|
||||
(let [tp (gpt/transform (gpt/point cx cy) transform)]
|
||||
|
@ -156,12 +155,12 @@
|
|||
|
||||
(mf/defc text-edition-selection-handlers
|
||||
[{:keys [shape zoom] :as props}]
|
||||
(let [{:keys [x y width height] :as shape} shape]
|
||||
(let [{:keys [x y width height]} shape]
|
||||
[:g.controls
|
||||
[:rect.main {:x x :y y
|
||||
:transform (geom/transform-matrix shape)
|
||||
:width width
|
||||
:height height
|
||||
;; :stroke-dasharray (str (/ 5.0 zoom) "," (/ 5 zoom))
|
||||
:style {:stroke "#1FDEA7"
|
||||
:stroke-width "0.5"
|
||||
:stroke-opacity "1"
|
||||
|
@ -170,16 +169,20 @@
|
|||
(mf/defc multiple-selection-handlers
|
||||
[{:keys [shapes selected zoom objects] :as props}]
|
||||
(let [shape (geom/selection-rect shapes)
|
||||
shape-center (geom/center shape)
|
||||
on-resize #(do (dom/stop-propagation %2)
|
||||
(st/emit! (dw/start-resize %1 selected shape objects)))
|
||||
|
||||
on-rotate #(do (dom/stop-propagation %)
|
||||
(st/emit! (dw/start-rotate shapes)))]
|
||||
|
||||
[:& controls {:shape shape
|
||||
:zoom zoom
|
||||
:on-resize on-resize
|
||||
:on-rotate on-rotate}]))
|
||||
[:*
|
||||
[:& controls {:shape shape
|
||||
:zoom zoom
|
||||
:on-resize on-resize
|
||||
:on-rotate on-rotate}]
|
||||
(when (debug? :selection-center)
|
||||
[:circle {:cx (:x shape-center) :cy (:y shape-center) :r 5 :fill "yellow"}])]))
|
||||
|
||||
(mf/defc single-selection-handlers
|
||||
[{:keys [shape zoom objects] :as props}]
|
||||
|
|
|
@ -15,25 +15,43 @@
|
|||
[uxbox.main.refs :as refs]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.geom.shapes :as gsh]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.main.data.workspace :as udw]
|
||||
[uxbox.util.math :as math]
|
||||
[uxbox.util.i18n :refer [t] :as i18n]))
|
||||
|
||||
|
||||
;; -- User/drawing coords
|
||||
(defn user-coords-vector [shape]
|
||||
(let [{sel-x :x sel-y :y :as selrect}
|
||||
(-> shape
|
||||
gsh/shape->path
|
||||
(gsh/center-transform (:transform shape))
|
||||
gsh/shape->rect-shape)
|
||||
{rec-x :x rec-y :y} (-> shape gsh/shape->rect-shape)
|
||||
|
||||
dx (- rec-x sel-x)
|
||||
dy (- rec-y sel-y)]
|
||||
(gpt/point dx dy)))
|
||||
|
||||
(defn user->draw [{:keys [x y width height] :as shape}]
|
||||
(let [dv (user-coords-vector shape)]
|
||||
(-> shape (gsh/move dv))))
|
||||
|
||||
(defn draw->user [{:keys [x y width height] :as shape}]
|
||||
(let [dv (user-coords-vector shape)]
|
||||
(-> shape (gsh/move (gpt/negate dv)))))
|
||||
|
||||
(mf/defc measures-menu
|
||||
[{:keys [shape options] :as props}]
|
||||
(let [options (or options #{:size :position :rotation :radius})
|
||||
locale (i18n/use-locale)
|
||||
frame (deref (refs/object-by-id (:frame-id shape)))
|
||||
|
||||
data (deref refs/workspace-data)
|
||||
parent (get-in data [:objects (:frame-id shape)])
|
||||
|
||||
x (cond
|
||||
(:x shape) :x
|
||||
(:cx shape) :cx)
|
||||
|
||||
y (cond
|
||||
(:y shape) :y
|
||||
(:cy shape) :cy)
|
||||
shape (->> shape
|
||||
(gsh/transform-shape frame)
|
||||
(draw->user))
|
||||
|
||||
on-size-change
|
||||
(fn [event attr]
|
||||
|
@ -58,9 +76,12 @@
|
|||
(fn [event attr]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/parse-integer 0)
|
||||
(+ (attr parent)))] ; Convert back to absolute position before update
|
||||
(st/emit! (udw/update-position (:id shape) {attr value}))))
|
||||
(d/parse-integer 0))
|
||||
new-shape (-> shape
|
||||
(assoc attr value)
|
||||
(gsh/translate-from-frame frame)
|
||||
(user->draw))]
|
||||
(st/emit! (udw/update-position (:id shape) (select-keys new-shape [attr])))))
|
||||
|
||||
on-rotation-change
|
||||
(fn [event]
|
||||
|
@ -89,31 +110,31 @@
|
|||
|
||||
;; WIDTH & HEIGHT
|
||||
(when (options :size)
|
||||
[:div.row-flex
|
||||
[:span.element-set-subtitle (t locale "workspace.options.size")]
|
||||
[:div.lock-size {:class (when (:proportion-lock shape) "selected")
|
||||
:on-click on-proportion-lock-change}
|
||||
(if (:proportion-lock shape)
|
||||
i/lock
|
||||
i/unlock)]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:type "number"
|
||||
:min "0"
|
||||
:no-validate true
|
||||
:on-change on-width-change
|
||||
:value (str (-> (:width shape)
|
||||
(d/coalesce 0)
|
||||
(math/round)))}]]
|
||||
[:div.row-flex
|
||||
[:span.element-set-subtitle (t locale "workspace.options.size")]
|
||||
[:div.lock-size {:class (when (:proportion-lock shape) "selected")
|
||||
:on-click on-proportion-lock-change}
|
||||
(if (:proportion-lock shape)
|
||||
i/lock
|
||||
i/unlock)]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:type "number"
|
||||
:min "0"
|
||||
:no-validate true
|
||||
:on-change on-width-change
|
||||
:value (str (-> (:width shape)
|
||||
(d/coalesce 0)
|
||||
(math/round)))}]]
|
||||
|
||||
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:type "number"
|
||||
:min "0"
|
||||
:no-validate true
|
||||
:on-change on-height-change
|
||||
:value (str (-> (:height shape)
|
||||
(d/coalesce 0)
|
||||
(math/round)))}]]])
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:type "number"
|
||||
:min "0"
|
||||
:no-validate true
|
||||
:on-change on-height-change
|
||||
:value (str (-> (:height shape)
|
||||
(d/coalesce 0)
|
||||
(math/round)))}]]])
|
||||
|
||||
;; Circle RX RY
|
||||
(when (options :circle-size)
|
||||
|
@ -142,57 +163,61 @@
|
|||
;; POSITION
|
||||
(when (options :position)
|
||||
[:div.row-flex
|
||||
[:span.element-set-subtitle (t locale "workspace.options.position")]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:placeholder "x"
|
||||
:type "number"
|
||||
:no-validate true
|
||||
:on-change on-pos-x-change
|
||||
:value (str (-> (- (x shape) (:x parent)) ; Show to user position relative to frame
|
||||
(d/coalesce 0)
|
||||
(math/round)))}]]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:placeholder "y"
|
||||
:type "number"
|
||||
:no-validate true
|
||||
:on-change on-pos-y-change
|
||||
:value (str (-> (- (y shape) (:y parent))
|
||||
(d/coalesce 0)
|
||||
(math/round)))}]]])
|
||||
[:span.element-set-subtitle (t locale "workspace.options.position")]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:placeholder "x"
|
||||
:type "number"
|
||||
:no-validate true
|
||||
:on-change on-pos-x-change
|
||||
:value (:x shape)
|
||||
;;:value (str (-> (- (x shape) (:x parent)) ; Show to user position relative to frame
|
||||
;; (d/coalesce 0)
|
||||
;; (math/round)))
|
||||
}]]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text {:placeholder "y"
|
||||
:type "number"
|
||||
:no-validate true
|
||||
:on-change on-pos-y-change
|
||||
:value (:y shape)
|
||||
;;:value (str (-> (- (y shape) (:y parent))
|
||||
;; (d/coalesce 0)
|
||||
;; (math/round)))
|
||||
}]]])
|
||||
|
||||
(when (options :rotation)
|
||||
[:div.row-flex
|
||||
[:span.element-set-subtitle (t locale "workspace.options.rotation")]
|
||||
[:div.input-element.degrees
|
||||
[:input.input-text
|
||||
{:placeholder ""
|
||||
:type "number"
|
||||
:no-validate true
|
||||
[:div.row-flex
|
||||
[:span.element-set-subtitle (t locale "workspace.options.rotation")]
|
||||
[:div.input-element.degrees
|
||||
[:input.input-text
|
||||
{:placeholder ""
|
||||
:type "number"
|
||||
:no-validate true
|
||||
:min "0"
|
||||
:max "360"
|
||||
:on-change on-rotation-change
|
||||
:value (str (-> (:rotation shape)
|
||||
(d/coalesce 0)
|
||||
(math/round)))}]]
|
||||
[:input.slidebar
|
||||
{:type "range"
|
||||
:min "0"
|
||||
:max "360"
|
||||
:step "1"
|
||||
:no-validate true
|
||||
:on-change on-rotation-change
|
||||
:value (str (-> (:rotation shape)
|
||||
(d/coalesce 0)
|
||||
(math/round)))}]]
|
||||
[:input.slidebar
|
||||
{:type "range"
|
||||
:min "0"
|
||||
:max "360"
|
||||
:step "1"
|
||||
:no-validate true
|
||||
:on-change on-rotation-change
|
||||
:value (str (-> (:rotation shape)
|
||||
(d/coalesce 0)))}]])
|
||||
(d/coalesce 0)))}]])
|
||||
|
||||
(when (options :radius)
|
||||
[:div.row-flex
|
||||
[:span.element-set-subtitle (t locale "workspace.options.radius")]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "rx"
|
||||
:type "number"
|
||||
:on-change on-radius-change
|
||||
:value (str (-> (:rx shape)
|
||||
(d/coalesce 0)
|
||||
(math/round)))}]]
|
||||
[:div.input-element]])]]))
|
||||
[:div.row-flex
|
||||
[:span.element-set-subtitle (t locale "workspace.options.radius")]
|
||||
[:div.input-element.pixels
|
||||
[:input.input-text
|
||||
{:placeholder "rx"
|
||||
:type "number"
|
||||
:on-change on-radius-change
|
||||
:value (str (-> (:rx shape)
|
||||
(d/coalesce 0)
|
||||
(math/round)))}]]
|
||||
[:div.input-element]])]]))
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
(ns uxbox.util.debug
|
||||
"Debugging utils"
|
||||
(:require
|
||||
[uxbox.main.store :as store]))
|
||||
"Debugging utils")
|
||||
|
||||
(def debug-options #{:bounding-boxes :group :events :rotation-handler #_:simple-selection })
|
||||
(def debug-options #{:bounding-boxes :group :events :rotation-handler :selection-center #_:simple-selection })
|
||||
|
||||
(defonce ^:dynamic *debug* (atom #{}))
|
||||
|
||||
|
@ -13,6 +11,13 @@
|
|||
(defn -debug! [option] (swap! *debug* disj option))
|
||||
(defn debug? [option] (@*debug* option))
|
||||
|
||||
|
||||
(defn ^:export toggle-debug [name] (let [option (keyword name)]
|
||||
(if (debug? option)
|
||||
(-debug! option)
|
||||
(debug! option))))
|
||||
(defn ^:export debug-all [name] (debug-all!))
|
||||
|
||||
(defn tap
|
||||
"Transducer function that can execute a side-effect `effect-fn` per input"
|
||||
[effect-fn]
|
||||
|
@ -31,9 +36,4 @@
|
|||
(js/console.log str (clj->js val))
|
||||
val))
|
||||
|
||||
(defn dump-state []
|
||||
(logjs "state" @store/state))
|
||||
|
||||
(defn dump-objects []
|
||||
(let [page-id (get @store/state :current-page-id)]
|
||||
(logjs "state" (get-in @store/state [:workspace-data page-id :objects]))))
|
||||
|
|
|
@ -40,11 +40,6 @@
|
|||
(throw (ex-info "Invalid arguments" {:v v}))))
|
||||
([x y] (Point. x y)))
|
||||
|
||||
(defn center
|
||||
[{:keys [x y width height]}]
|
||||
(point (+ x (/ width 2))
|
||||
(+ y (/ height 2))))
|
||||
|
||||
(defn add
|
||||
"Returns the addition of the supplied value to both
|
||||
coordinates of the point as a new point."
|
||||
|
|
|
@ -376,6 +376,7 @@
|
|||
maxx (transduce (map :x) max segments)
|
||||
maxy (transduce (map :y) max segments)]
|
||||
(assoc shape
|
||||
:type :rect
|
||||
:x1 minx
|
||||
:y1 miny
|
||||
:x2 maxx
|
||||
|
@ -495,7 +496,7 @@
|
|||
|
||||
(defn translate-from-frame
|
||||
[shape {:keys [x y] :as frame}]
|
||||
(move shape (gpt/point (+ x) (+ y))))
|
||||
(move shape (gpt/point x y)))
|
||||
|
||||
;; --- Alignment
|
||||
|
||||
|
@ -736,13 +737,6 @@
|
|||
(gpt/divide (gpt/point (:width shape-path-temp-rec) (:height shape-path-temp-rec))
|
||||
(gpt/point (:width shape-path-temp-dim) (:height shape-path-temp-dim)))))
|
||||
|
||||
(defn transform-selrect
|
||||
[frame shape]
|
||||
(-> shape
|
||||
(transform-apply-modifiers)
|
||||
(translate-to-frame frame)
|
||||
(shape->rect-shape)))
|
||||
|
||||
(defn transform-rect-shape
|
||||
[shape]
|
||||
(let [;; Apply modifiers to the rect as a path so we have the end shape expected
|
||||
|
@ -788,6 +782,8 @@
|
|||
|
||||
new-shape (-> shape
|
||||
(merge rec)
|
||||
(update :x #(mth/precision % 2))
|
||||
(update :y #(mth/precision % 2))
|
||||
(update :transform #(gmt/multiply (or % (gmt/matrix)) stretch-matrix))
|
||||
(update :transform-inverse #(gmt/multiply stretch-matrix-inverse (or % (gmt/matrix)))))]
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue