From a4ed1f30a45c574ea3699516f6224ac6d31d4881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Tue, 7 Apr 2020 12:18:10 +0200 Subject: [PATCH 1/3] :tada: Add shape alignment buttons --- .../images/icons/shape-halign-center.svg | 1 + .../images/icons/shape-halign-left.svg | 1 + .../images/icons/shape-halign-right.svg | 1 + .../images/icons/shape-valign-bottom.svg | 1 + .../images/icons/shape-valign-center.svg | 1 + .../images/icons/shape-valign-top.svg | 1 + .../styles/common/dependencies/colors.scss | 2 +- frontend/resources/styles/main.scss | 1 + .../main/partials/sidebar-align-options.scss | 38 ++++++++++++++ frontend/src/uxbox/builtins/icons.cljs | 6 +++ .../main/ui/workspace/sidebar/align.cljs | 49 +++++++++++++++++++ .../main/ui/workspace/sidebar/options.cljs | 4 +- 12 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 frontend/resources/images/icons/shape-halign-center.svg create mode 100644 frontend/resources/images/icons/shape-halign-left.svg create mode 100644 frontend/resources/images/icons/shape-halign-right.svg create mode 100644 frontend/resources/images/icons/shape-valign-bottom.svg create mode 100644 frontend/resources/images/icons/shape-valign-center.svg create mode 100644 frontend/resources/images/icons/shape-valign-top.svg create mode 100644 frontend/resources/styles/main/partials/sidebar-align-options.scss create mode 100644 frontend/src/uxbox/main/ui/workspace/sidebar/align.cljs diff --git a/frontend/resources/images/icons/shape-halign-center.svg b/frontend/resources/images/icons/shape-halign-center.svg new file mode 100644 index 000000000..b861539f2 --- /dev/null +++ b/frontend/resources/images/icons/shape-halign-center.svg @@ -0,0 +1 @@ + diff --git a/frontend/resources/images/icons/shape-halign-left.svg b/frontend/resources/images/icons/shape-halign-left.svg new file mode 100644 index 000000000..ded065048 --- /dev/null +++ b/frontend/resources/images/icons/shape-halign-left.svg @@ -0,0 +1 @@ + diff --git a/frontend/resources/images/icons/shape-halign-right.svg b/frontend/resources/images/icons/shape-halign-right.svg new file mode 100644 index 000000000..fc1ed648f --- /dev/null +++ b/frontend/resources/images/icons/shape-halign-right.svg @@ -0,0 +1 @@ + diff --git a/frontend/resources/images/icons/shape-valign-bottom.svg b/frontend/resources/images/icons/shape-valign-bottom.svg new file mode 100644 index 000000000..7b0911188 --- /dev/null +++ b/frontend/resources/images/icons/shape-valign-bottom.svg @@ -0,0 +1 @@ + diff --git a/frontend/resources/images/icons/shape-valign-center.svg b/frontend/resources/images/icons/shape-valign-center.svg new file mode 100644 index 000000000..0c6dae309 --- /dev/null +++ b/frontend/resources/images/icons/shape-valign-center.svg @@ -0,0 +1 @@ + diff --git a/frontend/resources/images/icons/shape-valign-top.svg b/frontend/resources/images/icons/shape-valign-top.svg new file mode 100644 index 000000000..2f091c9c3 --- /dev/null +++ b/frontend/resources/images/icons/shape-valign-top.svg @@ -0,0 +1 @@ + diff --git a/frontend/resources/styles/common/dependencies/colors.scss b/frontend/resources/styles/common/dependencies/colors.scss index 8c92847b7..1e61ede39 100644 --- a/frontend/resources/styles/common/dependencies/colors.scss +++ b/frontend/resources/styles/common/dependencies/colors.scss @@ -21,7 +21,7 @@ $color-warning: #e6a16f; $color-danger: #de4762; $color-info: #59b9e2; -// Mixing Color varriable for creating both light and dark colors +// Mixing Color variable for creating both light and dark colors $mix-percentage-dark: 81%; $mix-percentage-darker: 60%; $mix-percentage-light: 80%; diff --git a/frontend/resources/styles/main.scss b/frontend/resources/styles/main.scss index 57c2bafe1..49156057c 100644 --- a/frontend/resources/styles/main.scss +++ b/frontend/resources/styles/main.scss @@ -51,6 +51,7 @@ @import 'main/partials/project-bar'; @import 'main/partials/sidebar'; @import 'main/partials/sidebar-tools'; +@import 'main/partials/sidebar-align-options'; @import 'main/partials/sidebar-element-options'; @import 'main/partials/sidebar-icons'; @import 'main/partials/sidebar-layers'; diff --git a/frontend/resources/styles/main/partials/sidebar-align-options.scss b/frontend/resources/styles/main/partials/sidebar-align-options.scss new file mode 100644 index 000000000..132b09781 --- /dev/null +++ b/frontend/resources/styles/main/partials/sidebar-align-options.scss @@ -0,0 +1,38 @@ +// 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) 2015-2016 Andrey Antukh +// Copyright (c) 2015-2016 Juan de la Cruz + +.align-options { + display: flex; + width: 100%; + justify-content: space-evenly; + border-bottom: solid 1px $color-gray-60; + + .align-button { + cursor: pointer; + padding: $small; + svg { + height: 16px; + width: 16px; + fill: $color-gray-light; + } + + &:hover { + background-color: $color-primary; + svg { + fill: $color-gray-50; + } + } + + &.disabled { + background-color: transparent; + cursor: default; + svg { + fill: $color-gray-dark; + } + } + } +} diff --git a/frontend/src/uxbox/builtins/icons.cljs b/frontend/src/uxbox/builtins/icons.cljs index aa209a29b..08f6d4b97 100644 --- a/frontend/src/uxbox/builtins/icons.cljs +++ b/frontend/src/uxbox/builtins/icons.cljs @@ -69,6 +69,12 @@ (def ruler-tool (icon-xref :ruler-tool)) (def save (icon-xref :save)) (def search (icon-xref :search)) +(def shape-halign-left (icon-xref :shape-halign-left)) +(def shape-halign-center (icon-xref :shape-halign-center)) +(def shape-halign-right (icon-xref :shape-halign-right)) +(def shape-valign-top (icon-xref :shape-valign-top)) +(def shape-valign-center (icon-xref :shape-valign-center)) +(def shape-valign-bottom (icon-xref :shape-valign-bottom)) (def size-horiz (icon-xref :size-horiz)) (def size-vert (icon-xref :size-vert)) (def stroke (icon-xref :stroke)) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/align.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/align.cljs new file mode 100644 index 000000000..98543b2ea --- /dev/null +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/align.cljs @@ -0,0 +1,49 @@ +;; 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) 2015-2016 Andrey Antukh +;; Copyright (c) 2015-2016 Juan de la Cruz + +(ns uxbox.main.ui.workspace.sidebar.align + (:require + [rumext.alpha :as mf] + [uxbox.builtins.icons :as i] + [uxbox.main.refs :as refs] + [uxbox.util.uuid :as uuid])) + +(mf/defc align-options + [] + (let [data (mf/deref refs/workspace-data) + objects (:objects data) + selected (mf/deref refs/selected-shapes) + + disabled (cond + (empty? selected) true + (> (count selected) 1) false + :else + (= uuid/zero (:frame-id (get objects (first selected))))) + + on-align-button-clicked + (fn [axis] (when-not disabled (println axis)))] + + [:div.align-options + [:div.align-button {:class (when disabled "disabled") + :on-click #(on-align-button-clicked :hleft)} + i/shape-halign-left] + [:div.align-button {:class (when disabled "disabled") + :on-click #(on-align-button-clicked :hcenter)} + i/shape-halign-center] + [:div.align-button {:class (when disabled "disabled") + :on-click #(on-align-button-clicked :hright)} + i/shape-halign-right] + [:div.align-button {:class (when disabled "disabled") + :on-click #(on-align-button-clicked :vtop)} + i/shape-valign-top] + [:div.align-button {:class (when disabled "disabled") + :on-click #(on-align-button-clicked :vcenter)} + i/shape-valign-center] + [:div.align-button {:class (when disabled "disabled") + :on-click #(on-align-button-clicked :vbottom)} + i/shape-valign-bottom]])) + diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options.cljs index 09f08a852..e821c5f5e 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options.cljs @@ -13,6 +13,7 @@ [uxbox.main.data.workspace :as udw] [uxbox.main.store :as st] [uxbox.main.refs :as refs] + [uxbox.main.ui.workspace.sidebar.align :refer [align-options]] [uxbox.main.ui.workspace.sidebar.options.frame :as frame] [uxbox.main.ui.workspace.sidebar.options.group :as group] [uxbox.main.ui.workspace.sidebar.options.rect :as rect] @@ -55,11 +56,12 @@ [{:keys [page selected] :as props}] (let [close #(st/emit! (udw/toggle-layout-flag :element-options)) selected (mf/deref refs/selected-shapes)] - [:div.elementa-options.tool-window + [:div.element-options.tool-window ;; [:div.tool-window-bar ;; [:div.tool-window-icon i/options] ;; [:span (tr "ds.settings.element-options")] ;; [:div.tool-window-close {:on-click close} i/close]] + [:& align-options] [:div.tool-window-content [:div.element-options (if (= (count selected) 1) From b798f7a988847e5a2241f24bf98350e1877fc571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Tue, 7 Apr 2020 12:34:25 +0200 Subject: [PATCH 2/3] :recycle: Rename text align icons for consistency --- .../icons/{align-center.svg => text-align-center.svg} | 2 +- .../icons/{align-justify.svg => text-align-justify.svg} | 2 +- .../images/icons/{align-left.svg => text-align-left.svg} | 2 +- .../icons/{align-right.svg => text-align-right.svg} | 2 +- frontend/src/uxbox/builtins/icons.cljs | 8 ++++---- .../src/uxbox/main/ui/workspace/sidebar/options/text.cljs | 8 ++++---- 6 files changed, 12 insertions(+), 12 deletions(-) rename frontend/resources/images/icons/{align-center.svg => text-align-center.svg} (98%) rename frontend/resources/images/icons/{align-justify.svg => text-align-justify.svg} (98%) rename frontend/resources/images/icons/{align-left.svg => text-align-left.svg} (98%) rename frontend/resources/images/icons/{align-right.svg => text-align-right.svg} (97%) diff --git a/frontend/resources/images/icons/align-center.svg b/frontend/resources/images/icons/text-align-center.svg similarity index 98% rename from frontend/resources/images/icons/align-center.svg rename to frontend/resources/images/icons/text-align-center.svg index fd33ddb42..304412d42 100644 --- a/frontend/resources/images/icons/align-center.svg +++ b/frontend/resources/images/icons/text-align-center.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/frontend/resources/images/icons/align-justify.svg b/frontend/resources/images/icons/text-align-justify.svg similarity index 98% rename from frontend/resources/images/icons/align-justify.svg rename to frontend/resources/images/icons/text-align-justify.svg index 290dd055e..acf125ade 100644 --- a/frontend/resources/images/icons/align-justify.svg +++ b/frontend/resources/images/icons/text-align-justify.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/frontend/resources/images/icons/align-left.svg b/frontend/resources/images/icons/text-align-left.svg similarity index 98% rename from frontend/resources/images/icons/align-left.svg rename to frontend/resources/images/icons/text-align-left.svg index bff66a31f..20f7e7caa 100644 --- a/frontend/resources/images/icons/align-left.svg +++ b/frontend/resources/images/icons/text-align-left.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/frontend/resources/images/icons/align-right.svg b/frontend/resources/images/icons/text-align-right.svg similarity index 97% rename from frontend/resources/images/icons/align-right.svg rename to frontend/resources/images/icons/text-align-right.svg index dbb51c79d..40dc9024d 100644 --- a/frontend/resources/images/icons/align-right.svg +++ b/frontend/resources/images/icons/text-align-right.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/frontend/src/uxbox/builtins/icons.cljs b/frontend/src/uxbox/builtins/icons.cljs index 08f6d4b97..e2683b90c 100644 --- a/frontend/src/uxbox/builtins/icons.cljs +++ b/frontend/src/uxbox/builtins/icons.cljs @@ -11,10 +11,10 @@ (def action (icon-xref :action)) (def actions (icon-xref :actions)) -(def align-center (icon-xref :align-center)) -(def align-justify (icon-xref :align-justify)) -(def align-left (icon-xref :align-left)) -(def align-right (icon-xref :align-right)) +(def text-align-center (icon-xref :text-align-center)) +(def text-align-justify (icon-xref :text-align-justify)) +(def text-align-left (icon-xref :text-align-left)) +(def text-align-right (icon-xref :text-align-right)) (def alignment (icon-xref :alignment)) (def arrow (icon-xref :arrow)) (def arrow-down (icon-xref :arrow-down)) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/text.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/text.cljs index 41fd6aaaf..9bddea6ed 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/text.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/text.cljs @@ -263,16 +263,16 @@ [:div.row-flex.align-icons [:span {:class (when (= text-align "left") "current") :on-click #(on-font-align-change % "left")} - i/align-left] + i/text-align-left] [:span {:class (when (= text-align "center") "current") :on-click #(on-font-align-change % "center")} - i/align-center] + i/text-align-center] [:span {:class (when (= text-align "right") "current") :on-click #(on-font-align-change % "right")} - i/align-right] + i/text-align-right] [:span {:class (when (= text-align "justify") "current") :on-click #(on-font-align-change % "justify")} - i/align-justify]]]])) + i/text-align-justify]]]])) (def +fonts+ [{:id "sourcesanspro" From 8e573abf9eaa9b3d0eac897cef807b4c0f82648a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Tue, 7 Apr 2020 15:27:58 +0200 Subject: [PATCH 3/3] :tada: Implement actual object alignment --- frontend/src/uxbox/main/data/workspace.cljs | 46 ++++++++++------ frontend/src/uxbox/main/geom.cljs | 52 +++++++++++++++++-- .../main/ui/workspace/sidebar/align.cljs | 9 ++-- 3 files changed, 85 insertions(+), 22 deletions(-) diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index d4bedb3b2..0902e1ac3 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -1447,21 +1447,37 @@ ;; --- Shape / Selection Alignment -(defn initial-selection-align - "Align the selection of shapes." - [ids] - (us/verify ::set-of-uuid ids) - (ptk/reify ::initialize-shapes-align-in-bulk - ptk/WatchEvent - (watch [_ state stream] - #_(let [shapes-by-id (get-in state [:workspace-data :objects]) - shapes (mapv #(get shapes-by-id %) ids) - sshape (geom/shapes->rect-shape shapes) - point (gpt/point (:x1 sshape) - (:y1 sshape))] - (->> (uwrk/align-point point) - (rx/map (fn [{:keys [x y] :as pt}] - (apply-displacement-in-bulk ids (gpt/subtract pt point))))))))) +(declare align-object-to-frame) +(declare align-objects-list) + +(defn align-objects + [axis] + (us/verify ::geom/axis axis) + (ptk/reify :align-objects + IBatchedChange + ptk/UpdateEvent + (update [_ state] + (let [page-id (::page-id state) + objects (get-in state [:workspace-data page-id :objects]) + selected (get-in state [:workspace-local :selected]) + moved-objs (if (= 1 (count selected)) + [(align-object-to-frame objects (first selected) axis)] + (align-objects-list objects selected axis)) + updated-objs (merge objects (d/index-by :id moved-objs))] + (assoc-in state [:workspace-data page-id :objects] updated-objs))))) + +(defn align-object-to-frame + [objects object-id axis] + (let [object (get objects object-id) + frame (get objects (:frame-id object))] + (geom/align-to-rect object frame axis))) + +(defn align-objects-list + [objects selected axis] + (let [selected-objs (map #(get objects %) selected) + rect (geom/selection-rect selected-objs)] + (map #(geom/align-to-rect % rect axis) selected-objs))) + ;; --- Temportal displacement for Shape / Selection diff --git a/frontend/src/uxbox/main/geom.cljs b/frontend/src/uxbox/main/geom.cljs index 0558e826b..11e318fcc 100644 --- a/frontend/src/uxbox/main/geom.cljs +++ b/frontend/src/uxbox/main/geom.cljs @@ -525,12 +525,12 @@ (gmt/matrix? displacement-modifier) (transform displacement-modifier))) -;; NOTE: we need applu `shape->rect-shape` 3 times because we need to +;; NOTE: we need apply `shape->rect-shape` 3 times because we need to ;; update the x1 x2 y1 y2 attributes on each step; this is because ;; some transform functions still uses that attributes. WE NEED TO ;; REFACTOR this, and remove any usage of the old xN yN attributes. -(def ^:private xf-resolve-shapes +(def ^:private xf-resolve-shape (comp (map shape->rect-shape) (map resolve-modifier) (map shape->rect-shape) @@ -541,7 +541,7 @@ "Returns a rect that contains all the shapes and is aware of the rotation of each shape. Mainly used for multiple selection." [shapes] - (let [shapes (into [] xf-resolve-shapes shapes) + (let [shapes (into [] xf-resolve-shape shapes) minx (transduce (map :x1) min shapes) miny (transduce (map :y1) min shapes) maxx (transduce (map :x2) max shapes) @@ -564,6 +564,52 @@ [shape {:keys [x y] :as frame}] (move shape (gpt/point (+ x) (+ y)))) + +;; --- Alignment + +(s/def ::axis #{:hleft :hcenter :hright :vtop :vcenter :vbottom}) + +(declare calc-align-pos) + +(defn align-to-rect + "Move the shape so that it is aligned with the given rectangle + in the given axis. Take account the form of the shape and the + possible rotation. What is aligned is the rectangle that wraps + the shape with the given rectangle." + [shape rect axis] + (let [wrapper-rect (selection-rect [shape]) + align-pos (calc-align-pos wrapper-rect rect axis) + delta {:x (- (:x align-pos) (:x wrapper-rect)) + :y (- (:y align-pos) (:y wrapper-rect))}] + (move shape delta))) + +(defn calc-align-pos + [wrapper-rect rect axis] + (case axis + :hleft (let [left (:x rect)] + {:x left + :y (:y wrapper-rect)}) + + :hcenter (let [center (+ (:x rect) (/ (:width rect) 2))] + {:x (- center (/ (:width wrapper-rect) 2)) + :y (:y wrapper-rect)}) + + :hright (let [right (+ (:x rect) (:width rect))] + {:x (- right (:width wrapper-rect)) + :y (:y wrapper-rect)}) + + :vtop (let [top (:y rect)] + {:x (:x wrapper-rect) + :y top}) + + :vcenter (let [center (+ (:y rect) (/ (:height rect) 2))] + {:x (:x wrapper-rect) + :y (- center (/ (:height wrapper-rect) 2))}) + + :vbottom (let [bottom (+ (:y rect) (:height rect))] + {:x (:x wrapper-rect) + :y (- bottom (:height wrapper-rect))}))) + ;; --- Helpers (defn contained-in? diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/align.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/align.cljs index 98543b2ea..f6e82b61f 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/align.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/align.cljs @@ -10,13 +10,14 @@ [rumext.alpha :as mf] [uxbox.builtins.icons :as i] [uxbox.main.refs :as refs] + [uxbox.main.store :as st] + [uxbox.main.data.workspace :as dw] [uxbox.util.uuid :as uuid])) (mf/defc align-options [] - (let [data (mf/deref refs/workspace-data) - objects (:objects data) - selected (mf/deref refs/selected-shapes) + (let [selected (mf/deref refs/selected-shapes) + objects (deref refs/objects) ; don't need to watch objects, only read the value disabled (cond (empty? selected) true @@ -25,7 +26,7 @@ (= uuid/zero (:frame-id (get objects (first selected))))) on-align-button-clicked - (fn [axis] (when-not disabled (println axis)))] + (fn [axis] (when-not disabled (st/emit! (dw/align-objects axis))))] [:div.align-options [:div.align-button {:class (when disabled "disabled")