diff --git a/frontend/resources/images/icons/shape-hdistribute.svg b/frontend/resources/images/icons/shape-hdistribute.svg
new file mode 100644
index 000000000..8e250a0a6
--- /dev/null
+++ b/frontend/resources/images/icons/shape-hdistribute.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/resources/images/icons/shape-vdistribute.svg b/frontend/resources/images/icons/shape-vdistribute.svg
new file mode 100644
index 000000000..f7271ede1
--- /dev/null
+++ b/frontend/resources/images/icons/shape-vdistribute.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/resources/locales.json b/frontend/resources/locales.json
index b0c7a5658..95c981219 100644
--- a/frontend/resources/locales.json
+++ b/frontend/resources/locales.json
@@ -145,19 +145,19 @@
}
},
"dashboard.sidebar.drafts" : {
- "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:113" ],
+ "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:112" ],
"translations" : {
"en" : "Drafts"
}
},
"dashboard.sidebar.libraries" : {
- "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:120" ],
+ "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:119" ],
"translations" : {
"en" : "Libraries"
}
},
"dashboard.sidebar.recent" : {
- "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:106" ],
+ "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:105" ],
"translations" : {
"en" : "Recent"
}
@@ -188,11 +188,11 @@
}
},
"ds.cancel" : {
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:115" ],
"translations" : {
"en" : null,
"fr" : null
- },
- "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:115" ]
+ }
},
"ds.confirm-cancel" : {
"used-in" : [ "src/uxbox/main/ui/confirm.cljs:19" ],
@@ -223,18 +223,18 @@
"unused" : true
},
"ds.history.pinned" : {
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:96" ],
"translations" : {
"en" : null,
"fr" : null
- },
- "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:96" ]
+ }
},
"ds.history.versions" : {
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:93" ],
"translations" : {
"en" : null,
"fr" : null
- },
- "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:93" ]
+ }
},
"ds.icons-collection.new" : {
"translations" : {
@@ -258,18 +258,18 @@
}
},
"ds.search.placeholder" : {
- "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:176" ],
+ "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:175" ],
"translations" : {
"en" : "Search...",
"fr" : "Rechercher..."
}
},
"ds.settings.document-history" : {
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:87" ],
"translations" : {
"en" : null,
"fr" : null
- },
- "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:87" ]
+ }
},
"ds.store-colors-title" : {
"translations" : {
@@ -628,49 +628,49 @@
}
},
"settings.profile.lang" : {
- "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:92" ],
+ "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:88" ],
"translations" : {
"en" : "Default language",
"fr" : "Langue par défaut"
}
},
"settings.profile.profile-saved" : {
- "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:51" ],
+ "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:47" ],
"translations" : {
"en" : "Profile saved successfully!",
"fr" : "Profil enregistré avec succès !"
}
},
"settings.profile.section-basic-data" : {
- "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:66" ],
+ "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:62" ],
"translations" : {
"en" : "Name, username and email",
"fr" : "Nom, nom d'utilisateur et adresse email"
}
},
"settings.profile.your-avatar" : {
- "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:138" ],
+ "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:134" ],
"translations" : {
"en" : "Your avatar",
"fr" : "Votre avatar"
}
},
"settings.profile.your-email" : {
- "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:87" ],
+ "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:83" ],
"translations" : {
"en" : null,
"fr" : null
}
},
"settings.profile.your-name" : {
- "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:75" ],
+ "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:71" ],
"translations" : {
"en" : "Your name",
"fr" : "Votre nom complet"
}
},
"settings.update-settings" : {
- "used-in" : [ "src/uxbox/main/ui/settings/notifications.cljs:42", "src/uxbox/main/ui/settings/password.cljs:102", "src/uxbox/main/ui/settings/profile.cljs:105" ],
+ "used-in" : [ "src/uxbox/main/ui/settings/notifications.cljs:42", "src/uxbox/main/ui/settings/password.cljs:102", "src/uxbox/main/ui/settings/profile.cljs:101" ],
"translations" : {
"en" : "Update settings",
"fr" : "Mettre à jour les paramètres"
@@ -730,6 +730,54 @@
"en" : "Sitemap"
}
},
+ "workspace.align.hcenter" : {
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/align.cljs:51" ],
+ "translations" : {
+ "en" : "Align horizontal center"
+ }
+ },
+ "workspace.align.hdistribute" : {
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/align.cljs:63" ],
+ "translations" : {
+ "en" : "Distribute horizontal spacing"
+ }
+ },
+ "workspace.align.hleft" : {
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/align.cljs:45" ],
+ "translations" : {
+ "en" : "Align left"
+ }
+ },
+ "workspace.align.hright" : {
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/align.cljs:57" ],
+ "translations" : {
+ "en" : "Align right"
+ }
+ },
+ "workspace.align.vbottom" : {
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/align.cljs:82" ],
+ "translations" : {
+ "en" : "Align bottom"
+ }
+ },
+ "workspace.align.vcenter" : {
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/align.cljs:76" ],
+ "translations" : {
+ "en" : "Align vertical center"
+ }
+ },
+ "workspace.align.vdistribute" : {
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/align.cljs:88" ],
+ "translations" : {
+ "en" : "Distribute vertical spacing"
+ }
+ },
+ "workspace.align.vtop" : {
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/align.cljs:70" ],
+ "translations" : {
+ "en" : "Align top"
+ }
+ },
"workspace.header.menu.hide-grid" : {
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:120" ],
"translations" : {
@@ -897,21 +945,21 @@
"unused" : true
},
"workspace.options.position" : {
- "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:97", "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:95", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:126", "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:99", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:95", "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:94" ],
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:97", "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:99", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:95", "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:95", "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:94", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:126" ],
"translations" : {
"en" : "Position",
"fr" : "Position"
}
},
"workspace.options.radius" : {
- "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:132", "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:141", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:137", "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:136" ],
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:141", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:137", "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:132", "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:136" ],
"translations" : {
"en" : "Radius",
"fr" : "TODO"
}
},
"workspace.options.rotation" : {
- "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:110", "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:118", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:114", "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:113" ],
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:118", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:114", "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:110", "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:113" ],
"translations" : {
"en" : "Rotation",
"fr" : "TODO"
@@ -925,7 +973,7 @@
}
},
"workspace.options.size" : {
- "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:70", "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:72", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:101", "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:72", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:68", "src/uxbox/main/ui/workspace/sidebar/options/page.cljs:115", "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:67" ],
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/page.cljs:115", "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:70", "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:72", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:68", "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:72", "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:67", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:101" ],
"translations" : {
"en" : "Size",
"fr" : "Taille"
@@ -938,35 +986,35 @@
}
},
"workspace.options.stroke" : {
- "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:73", "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:127" ],
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:74", "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:129" ],
"translations" : {
"en" : "Stroke",
"fr" : null
}
},
"workspace.options.stroke.dashed" : {
- "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:114" ],
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:115" ],
"translations" : {
"en" : "Dashed",
"fr" : "Tiré"
}
},
"workspace.options.stroke.dotted" : {
- "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:113" ],
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:114" ],
"translations" : {
"en" : "Dotted",
"fr" : "Pointillé"
}
},
"workspace.options.stroke.mixed" : {
- "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:115" ],
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:116" ],
"translations" : {
"en" : "Mixed",
"fr" : "Mixte"
}
},
"workspace.options.stroke.solid" : {
- "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:112" ],
+ "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:113" ],
"translations" : {
"en" : "Solid",
"fr" : "Solide"
diff --git a/frontend/resources/styles/main/partials/sidebar-align-options.scss b/frontend/resources/styles/main/partials/sidebar-align-options.scss
index 132b09781..706afcb6e 100644
--- a/frontend/resources/styles/main/partials/sidebar-align-options.scss
+++ b/frontend/resources/styles/main/partials/sidebar-align-options.scss
@@ -7,13 +7,23 @@
.align-options {
display: flex;
- width: 100%;
- justify-content: space-evenly;
border-bottom: solid 1px $color-gray-60;
+ height: 37px;
+ padding: 0 $x-small;
+
+ .align-group {
+ display: flex;
+ justify-content: space-evenly;
+ width: 100%;
+
+ &:not(:last-child) {
+ border-right: solid 1px $color-gray-60;
+ }
+ }
.align-button {
cursor: pointer;
- padding: $small;
+ padding: $small $x-small;
svg {
height: 16px;
width: 16px;
diff --git a/frontend/src/uxbox/builtins/icons.cljs b/frontend/src/uxbox/builtins/icons.cljs
index e2683b90c..9e12c7a00 100644
--- a/frontend/src/uxbox/builtins/icons.cljs
+++ b/frontend/src/uxbox/builtins/icons.cljs
@@ -72,9 +72,11 @@
(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-hdistribute (icon-xref :shape-hdistribute))
(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 shape-vdistribute (icon-xref :shape-vdistribute))
(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 d27f78807..7deb6acd9 100644
--- a/frontend/src/uxbox/main/data/workspace.cljs
+++ b/frontend/src/uxbox/main/data/workspace.cljs
@@ -1445,14 +1445,14 @@
(rx/of (commit-changes [rchange] [uchange]))))))
-;; --- Shape / Selection Alignment
+;; --- Shape / Selection Alignment and Distribution
(declare align-object-to-frame)
(declare align-objects-list)
(defn align-objects
[axis]
- (us/verify ::geom/axis axis)
+ (us/verify ::geom/align-axis axis)
(ptk/reify :align-objects
IBatchedChange
ptk/UpdateEvent
@@ -1478,6 +1478,21 @@
rect (geom/selection-rect selected-objs)]
(map #(geom/align-to-rect % rect axis) selected-objs)))
+(defn distribute-objects
+ [axis]
+ (us/verify ::geom/dist-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])
+ selected-objs (map #(get objects %) selected)
+ moved-objs (geom/distribute-space selected-objs axis)
+ updated-objs (merge objects (d/index-by :id moved-objs))]
+ (assoc-in state [:workspace-data page-id :objects] updated-objs)))))
+
;; --- Temportal displacement for Shape / Selection
diff --git a/frontend/src/uxbox/main/geom.cljs b/frontend/src/uxbox/main/geom.cljs
index d73a83376..6a6ca56ad 100644
--- a/frontend/src/uxbox/main/geom.cljs
+++ b/frontend/src/uxbox/main/geom.cljs
@@ -107,7 +107,6 @@
;; --- Size
-(declare size-rect)
(declare size-circle)
(declare size-path)
@@ -141,6 +140,39 @@
(merge shape {:width (* rx 2)
:height (* ry 2)}))
+;; --- Center
+
+(declare center-rect)
+(declare center-circle)
+(declare center-path)
+
+(defn center
+ "Calculate the center of the shape."
+ [shape]
+ (case (:type shape)
+ :circle (center-circle shape)
+ :curve (center-path shape)
+ :path (center-path shape)
+ (center-rect shape)))
+
+(defn- center-rect
+ [{:keys [x y width height] :as shape}]
+ (gpt/point (+ x (/ width 2)) (+ y (/ height 2))))
+
+(defn- center-circle
+ [{:keys [cx cy] :as shape}]
+ (gpt/point cx cy))
+
+(defn- center-path
+ [{:keys [segments x1 y1 x2 y2] :as shape}]
+ (if (and x1 y1 x2 y2)
+ (gpt/point (/ (+ x1 x2) 2) (/ (+ y1 y2) 2))
+ (let [minx (apply min (map :x segments))
+ miny (apply min (map :y segments))
+ maxx (apply max (map :x segments))
+ maxy (apply max (map :y segments))]
+ (gpt/point (/ (+ minx maxx) 2) (/ (+ miny maxy) 2)))))
+
;; --- Proportions
(declare assign-proportions-path)
@@ -566,10 +598,9 @@
[shape {:keys [x y] :as frame}]
(move shape (gpt/point (+ x) (+ y))))
-
;; --- Alignment
-(s/def ::axis #{:hleft :hcenter :hright :vtop :vcenter :vbottom})
+(s/def ::align-axis #{:hleft :hcenter :hright :vtop :vcenter :vbottom})
(declare calc-align-pos)
@@ -612,6 +643,51 @@
{:x (:x wrapper-rect)
:y (- bottom (:height wrapper-rect))})))
+;; --- Distribute
+
+(s/def ::dist-axis #{:horizontal :vertical})
+
+(defn distribute-space
+ "Distribute equally the space between shapes in the given axis. If
+ there is no space enough, it does nothing. It takes into account
+ the form of the shape and the rotation, what is distributed is
+ the wrapping recangles of the shapes."
+ [shapes axis]
+ (let [coord (if (= axis :horizontal) :x :y)
+ other-coord (if (= axis :horizontal) :y :x)
+ size (if (= axis :horizontal) :width :height)
+ ; The rectangle that wraps the whole selection
+ wrapper-rect (selection-rect shapes)
+ ; Sort shapes by the center point in the given axis
+ sorted-shapes (sort-by #(coord (center %)) shapes)
+ ; Each shape wrapped in its own rectangle
+ wrapped-shapes (map #(selection-rect [%]) sorted-shapes)
+ ; The total space between shapes
+ space (reduce - (size wrapper-rect) (map size wrapped-shapes))]
+
+ (if (<= space 0)
+ shapes
+ (let [unit-space (/ space (- (count wrapped-shapes) 1))
+ ; Calculate the distance we need to move each shape.
+ ; The new position of each one is the position of the
+ ; previous one plus its size plus the unit space.
+ deltas (loop [shapes' wrapped-shapes
+ start-pos (coord wrapper-rect)
+ deltas []]
+
+ (let [first-shape (first shapes')
+ delta (- start-pos (coord first-shape))
+ new-pos (+ start-pos (size first-shape) unit-space)]
+
+ (if (= (count shapes') 1)
+ (conj deltas delta)
+ (recur (rest shapes')
+ new-pos
+ (conj deltas delta)))))]
+
+ (map #(move %1 {coord %2 other-coord 0}) sorted-shapes deltas)))))
+
+
;; --- 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 f6e82b61f..fae75dc99 100644
--- a/frontend/src/uxbox/main/ui/workspace/sidebar/align.cljs
+++ b/frontend/src/uxbox/main/ui/workspace/sidebar/align.cljs
@@ -12,6 +12,7 @@
[uxbox.main.refs :as refs]
[uxbox.main.store :as st]
[uxbox.main.data.workspace :as dw]
+ [uxbox.util.i18n :as i18n :refer [t]]
[uxbox.util.uuid :as uuid]))
(mf/defc align-options
@@ -25,26 +26,67 @@
:else
(= uuid/zero (:frame-id (get objects (first selected)))))
+ disabled-distribute (cond
+ (empty? selected) true
+ (< (count selected) 2) true
+ :else false)
+
+ locale (i18n/use-locale)
+
on-align-button-clicked
- (fn [axis] (when-not disabled (st/emit! (dw/align-objects axis))))]
+ (fn [axis] (when-not disabled (st/emit! (dw/align-objects axis))))
+
+ on-distribute-button-clicked
+ (fn [axis] (when-not disabled-distribute (st/emit! (dw/distribute-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]]))
+ [:div.align-group
+ [:div.align-button.tooltip.tooltip-bottom
+ {:alt (t locale "workspace.align.hleft")
+ :class (when disabled "disabled")
+ :on-click #(on-align-button-clicked :hleft)}
+ i/shape-halign-left]
+
+ [:div.align-button.tooltip.tooltip-bottom
+ {:alt (t locale "workspace.align.hcenter")
+ :class (when disabled "disabled")
+ :on-click #(on-align-button-clicked :hcenter)}
+ i/shape-halign-center]
+
+ [:div.align-button.tooltip.tooltip-bottom
+ {:alt (t locale "workspace.align.hright")
+ :class (when disabled "disabled")
+ :on-click #(on-align-button-clicked :hright)}
+ i/shape-halign-right]
+
+ [:div.align-button.tooltip.tooltip-bottom
+ {:alt (t locale "workspace.align.hdistribute")
+ :class (when disabled-distribute "disabled")
+ :on-click #(on-distribute-button-clicked :horizontal)}
+ i/shape-hdistribute]]
+
+ [:div.align-group
+ [:div.align-button.tooltip.tooltip-bottom
+ {:alt (t locale "workspace.align.vtop")
+ :class (when disabled "disabled")
+ :on-click #(on-align-button-clicked :vtop)}
+ i/shape-valign-top]
+
+ [:div.align-button.tooltip.tooltip-bottom
+ {:alt (t locale "workspace.align.vcenter")
+ :class (when disabled "disabled")
+ :on-click #(on-align-button-clicked :vcenter)}
+ i/shape-valign-center]
+
+ [:div.align-button.tooltip.tooltip-bottom
+ {:alt (t locale "workspace.align.vbottom")
+ :class (when disabled "disabled")
+ :on-click #(on-align-button-clicked :vbottom)}
+ i/shape-valign-bottom]
+
+ [:div.align-button.tooltip.tooltip-bottom
+ {:alt (t locale "workspace.align.vdistribute")
+ :class (when disabled-distribute "disabled")
+ :on-click #(on-distribute-button-clicked :vertical)}
+ i/shape-vdistribute]]]))