0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-13 08:11:30 -05:00

🎉 Add space distribution of shapes

This commit is contained in:
Andrés Moya 2020-04-09 09:57:24 +02:00 committed by Alonso Torres
parent a0c5f32a42
commit e9d60913d0
8 changed files with 250 additions and 55 deletions

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500.00001 500.00001" width="500" height="500"><path d="M356.25 500c-4.487 0-8.975-2.066-12.712-6.2-2.194-2.431-4.466-6.131-5.044-8.228-.578-2.094-.994-107.944-.994-107.944V122.373s.416-105.848.997-107.942c.575-2.094 2.847-5.797 5.04-8.228 7.476-8.27 17.95-8.27 25.426 0 2.197 2.43 4.465 6.132 5.044 8.228.578 2.094.993 107.943.993 107.943v255.257s-.415 105.847-.997 107.94c-.575 2.095-2.846 5.798-5.04 8.229-3.738 4.134-8.225 6.2-12.713 6.2zm-212.5 0c-4.488 0-8.975-2.066-12.712-6.2-2.195-2.431-4.467-6.131-5.045-8.228-.579-2.094-.993-107.944-.993-107.944V122.373s.416-105.848.995-107.942c.577-2.094 2.849-5.797 5.043-8.228 7.474-8.27 17.95-8.27 25.426 0 2.194 2.43 4.465 6.132 5.042 8.228.58 2.094.994 107.943.994 107.943v255.257s-.417 105.847-.996 107.94c-.577 2.095-2.847 5.798-5.042 8.229-3.737 4.134-8.224 6.2-12.712 6.2zM287.5 392.425c0 4.197-4.346 7.575-9.745 7.575h-55.51c-5.4 0-9.745-3.378-9.745-7.575V107.577c0-4.198 4.346-7.577 9.745-7.577h55.51c5.399 0 9.745 3.38 9.745 7.577z" clip-rule="evenodd" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500.00001 500.00001" width="500" height="500"><path d="M500 143.75c0 4.488-2.066 8.975-6.2 12.712-2.431 2.194-6.131 4.467-8.228 5.045-2.094.578-107.944.993-107.944.993H122.373s-105.848-.415-107.942-.993c-2.095-.579-5.797-2.85-8.229-5.043-8.27-7.474-8.27-17.951 0-25.426 2.43-2.194 6.133-4.465 8.229-5.042 2.095-.581 107.943-.996 107.943-.996h255.257s105.847.416 107.94.997c2.095.578 5.798 2.847 8.229 5.042 4.134 3.736 6.2 8.223 6.2 12.711zm0 212.5c0 4.487-2.066 8.975-6.2 12.713-2.431 2.193-6.131 4.465-8.228 5.043-2.094.582-107.944.994-107.944.994H122.373s-105.848-.416-107.942-.994c-2.095-.578-5.797-2.85-8.229-5.043-8.27-7.475-8.27-17.95 0-25.425 2.43-2.197 6.133-4.466 8.229-5.044 2.095-.578 107.943-.994 107.943-.994h255.257s105.847.416 107.94.997c2.095.575 5.798 2.847 8.229 5.04 4.134 3.738 6.2 8.226 6.2 12.713zM392.425 212.5c4.197 0 7.575 4.347 7.575 9.746v55.509c0 5.4-3.378 9.745-7.575 9.745H107.576c-4.197 0-7.576-4.346-7.576-9.745v-55.51c0-5.398 3.38-9.745 7.576-9.745z" clip-rule="evenodd" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -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"

View file

@ -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;

View file

@ -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))

View file

@ -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

View file

@ -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?

View file

@ -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]]]))