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/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/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..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))
@@ -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/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
new file mode 100644
index 000000000..f6e82b61f
--- /dev/null
+++ b/frontend/src/uxbox/main/ui/workspace/sidebar/align.cljs
@@ -0,0 +1,50 @@
+;; 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.main.store :as st]
+ [uxbox.main.data.workspace :as dw]
+ [uxbox.util.uuid :as uuid]))
+
+(mf/defc align-options
+ []
+ (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
+ (> (count selected) 1) false
+ :else
+ (= uuid/zero (:frame-id (get objects (first selected)))))
+
+ on-align-button-clicked
+ (fn [axis] (when-not disabled (st/emit! (dw/align-objects 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)
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"