diff --git a/CHANGES.md b/CHANGES.md index 3cfd19aaf..e46292bc8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,7 @@ - Add copy invitation link to the invitation options [Taiga #4213](https://tree.taiga.io/project/penpot/us/4213) - Dynamic alignment only in sight [Taiga #3537](https://tree.taiga.io/project/penpot/us/3537) - Improve naming of layers [Taiga #4036](https://tree.taiga.io/project/penpot/us/4036) +- Add zoom lense [Taiga #4691](https://tree.taiga.io/project/penpot/us/4691) ### :bug: Bugs fixed diff --git a/frontend/src/app/main/data/workspace/shortcuts.cljs b/frontend/src/app/main/data/workspace/shortcuts.cljs index d7e81b0c4..fa924e138 100644 --- a/frontend/src/app/main/data/workspace/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/shortcuts.cljs @@ -245,7 +245,7 @@ :command "t" :subsections [:tools] :fn #(emit-when-no-readonly dwtxt/start-edit-if-selected - (dwd/select-for-drawing :text))} + (dwd/select-for-drawing :text))} :draw-path {:tooltip "P" :command "p" @@ -419,14 +419,14 @@ :subsections [:panels] :fn #(do (r/set-resize-type! :bottom) (emit-when-no-readonly (dw/remove-layout-flag :textpalette) - (toggle-layout-flag :colorpalette)))} + (toggle-layout-flag :colorpalette)))} :toggle-textpalette {:tooltip (ds/alt "T") :command (ds/a-mod "t") :subsections [:panels] :fn #(do (r/set-resize-type! :bottom) (emit-when-no-readonly (dw/remove-layout-flag :colorpalette) - (toggle-layout-flag :textpalette)))} + (toggle-layout-flag :textpalette)))} :hide-ui {:tooltip "\\" :command "\\" @@ -460,6 +460,16 @@ :subsections [:zoom-workspace] :fn #(st/emit! dw/zoom-to-selected-shape)} + :zoom-lense-increase {:tooltip "Z" + :command "z" + :subsections [:zoom-workspace] + :fn identity} + + :zoom-lense-decrease {:tooltip (ds/alt "Z") + :command "alt+z" + :subsections [:zoom-workspace] + :fn identity} + ;; NAVIGATION diff --git a/frontend/src/app/main/streams.cljs b/frontend/src/app/main/streams.cljs index a586077a9..9f63c8572 100644 --- a/frontend/src/app/main/streams.cljs +++ b/frontend/src/app/main/streams.cljs @@ -194,3 +194,14 @@ (rx/dedupe))] (rx/subscribe-with ob sub) sub)) + +(defonce keyboard-z + (let [sub (rx/behavior-subject nil) + ob (->> st/stream + (rx/filter keyboard-event?) + (rx/filter kbd/z?) + (rx/filter (comp not kbd/editing?)) + (rx/map #(= :down (:type %))) + (rx/dedupe))] + (rx/subscribe-with ob sub) + sub)) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index b0aba960e..8b9fbc7f5 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -106,6 +106,7 @@ alt? (mf/use-state false) mod? (mf/use-state false) space? (mf/use-state false) + z? (mf/use-state false) cursor (mf/use-state (utils/get-cursor :pointer-inner)) hover-ids (mf/use-state nil) hover (mf/use-state nil) @@ -154,9 +155,9 @@ workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) mode-inspect? (= options-mode :inspect) - on-click (actions/on-click hover selected edition drawing-path? drawing-tool space? selrect) + on-click (actions/on-click hover selected edition drawing-path? drawing-tool space? selrect z?) on-context-menu (actions/on-context-menu hover hover-ids workspace-read-only?) - on-double-click (actions/on-double-click hover hover-ids drawing-path? base-objects edition workspace-read-only?) + on-double-click (actions/on-double-click hover hover-ids drawing-path? base-objects edition drawing-tool z? workspace-read-only?) on-drag-enter (actions/on-drag-enter) on-drag-over (actions/on-drag-over) on-drop (actions/on-drop file) @@ -212,8 +213,8 @@ (hooks/setup-dom-events viewport-ref zoom disable-paste in-viewport? workspace-read-only?) (hooks/setup-viewport-size viewport-ref) - (hooks/setup-cursor cursor alt? mod? space? panning drawing-tool drawing-path? node-editing? workspace-read-only?) - (hooks/setup-keyboard alt? mod? space?) + (hooks/setup-cursor cursor alt? mod? space? panning drawing-tool drawing-path? node-editing? z? workspace-read-only?) + (hooks/setup-keyboard alt? mod? space? z?) (hooks/setup-hover-shapes page-id move-stream base-objects transform selected mod? hover hover-ids hover-top-frame-id @hover-disabled? focus zoom show-measures?) (hooks/setup-viewport-modifiers modifiers base-objects) (hooks/setup-shortcuts node-editing? drawing-path?) diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index 1a85587f2..5999b8424 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -34,8 +34,7 @@ (defn on-mouse-down [{:keys [id blocked hidden type]} selected edition drawing-tool text-editing? - node-editing? drawing-path? create-comment? space? panning - workspace-read-only?] + node-editing? drawing-path? create-comment? space? panning workspace-read-only?] (mf/use-callback (mf/deps id blocked hidden type selected edition drawing-tool text-editing? node-editing? drawing-path? create-comment? @space? @@ -140,9 +139,9 @@ (reset! frame-hover nil)))) (defn on-click - [hover selected edition drawing-path? drawing-tool space? selrect] + [hover selected edition drawing-path? drawing-tool space? selrect z?] (mf/use-callback - (mf/deps @hover selected edition drawing-path? drawing-tool @space? selrect) + (mf/deps @hover selected edition drawing-path? drawing-tool @space? selrect @z?) (fn [event] (when (and (nil? selrect) (or (dom/class? (dom/get-target event) "viewport-controls") @@ -151,7 +150,9 @@ shift? (kbd/shift? event) alt? (kbd/alt? event) meta? (kbd/meta? event) - hovering? (some? @hover)] + hovering? (some? @hover) + raw-pt (dom/get-client-position event) + pt (uwvv/point->viewport raw-pt)] (st/emit! (ms/->MouseEvent :click ctrl? shift? alt? meta?)) (when (and hovering? @@ -159,42 +160,52 @@ (not edition) (not drawing-path?) (not drawing-tool)) - (st/emit! (dw/select-shape (:id @hover) shift?)))))))) + (st/emit! (dw/select-shape (:id @hover) shift?))) + + (when (and @z? + (not @space?) + (not edition) + (not drawing-path?) + (not drawing-tool)) + (if alt? + (st/emit! (dw/decrease-zoom pt)) + (st/emit! (dw/increase-zoom pt))))))))) (defn on-double-click - [hover hover-ids drawing-path? objects edition workspace-read-only?] + [hover hover-ids drawing-path? objects edition drawing-tool z? workspace-read-only?] (mf/use-callback - (mf/deps @hover @hover-ids drawing-path? edition workspace-read-only?) + (mf/deps @hover @hover-ids drawing-path? edition drawing-tool @z? workspace-read-only?) (fn [event] (dom/stop-propagation event) - (let [ctrl? (kbd/ctrl? event) - shift? (kbd/shift? event) - alt? (kbd/alt? event) - meta? (kbd/meta? event) + (when (not z?) + (let [ctrl? (kbd/ctrl? event) + shift? (kbd/shift? event) + alt? (kbd/alt? event) + meta? (kbd/meta? event) - {:keys [id type] :as shape} (or @hover (get objects (first @hover-ids))) + {:keys [id type] :as shape} (or @hover (get objects (first @hover-ids))) - editable? (contains? #{:text :rect :path :image :circle} type)] + editable? (contains? #{:text :rect :path :image :circle} type)] - (st/emit! (ms/->MouseEvent :double-click ctrl? shift? alt? meta?)) + (st/emit! (ms/->MouseEvent :double-click ctrl? shift? alt? meta?)) ;; Emit asynchronously so the double click to exit shapes won't break - (timers/schedule - (fn [] - (when (and (not drawing-path?) shape) - (cond - (and editable? (not= id edition) (not workspace-read-only?)) - (st/emit! (dw/select-shape id) - (dw/start-editing-selected)) + (timers/schedule + (fn [] + (when (and (not drawing-path?) shape) + (cond + (and editable? (not= id edition) (not workspace-read-only?)) + (st/emit! (dw/select-shape id) + (dw/start-editing-selected)) - :else - (let [;; We only get inside childrens of the hovering shape - hover-ids (->> @hover-ids (filter (partial cph/is-child? objects id))) - selected (get objects (first hover-ids))] - (when (some? selected) - (reset! hover selected) - (st/emit! (dw/select-shape (:id selected))))))))))))) + :else + (let [;; We only get inside childrens of the hovering shape + hover-ids (->> @hover-ids (filter (partial cph/is-child? objects id))) + selected (get objects (first hover-ids))] + (when (some? selected) + (reset! hover selected) + (st/emit! (dw/select-shape (:id selected)))))))))))))) (defn on-context-menu [hover hover-ids workspace-read-only?] diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index 261ee5493..1d3dcb6df 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -71,13 +71,18 @@ ;; We schedule the event so it fires after `initialize-page` event (timers/schedule #(st/emit! (dw/initialize-viewport size))))))) -(defn setup-cursor [cursor alt? mod? space? panning drawing-tool drawing-path? path-editing? workspace-read-only?] +(defn setup-cursor [cursor alt? mod? space? panning drawing-tool drawing-path? path-editing? z? workspace-read-only?] (mf/use-effect - (mf/deps @cursor @alt? @mod? @space? panning drawing-tool drawing-path? path-editing? workspace-read-only?) + (mf/deps @cursor @alt? @mod? @space? panning drawing-tool drawing-path? path-editing? z? workspace-read-only?) (fn [] (let [show-pen? (or (= drawing-tool :path) (and drawing-path? (not= drawing-tool :curve))) + show-zoom? (and @z? + (not @space?) + (not drawing-path?) + (not drawing-tool)) + new-cursor (cond (and @mod? @space?) (utils/get-cursor :zoom) @@ -86,6 +91,8 @@ (= drawing-tool :frame) (utils/get-cursor :create-artboard) (= drawing-tool :rect) (utils/get-cursor :create-rectangle) (= drawing-tool :circle) (utils/get-cursor :create-ellipse) + (and show-zoom? (not @alt?)) (utils/get-cursor :zoom-in) + (and show-zoom? @alt?) (utils/get-cursor :zoom-out) show-pen? (utils/get-cursor :pen) (= drawing-tool :curve) (utils/get-cursor :pencil) drawing-tool (utils/get-cursor :create-shape) @@ -98,10 +105,11 @@ (when (not= @cursor new-cursor) (reset! cursor new-cursor)))))) -(defn setup-keyboard [alt? mod? space?] +(defn setup-keyboard [alt? mod? space? z?] (hooks/use-stream ms/keyboard-alt #(reset! alt? %)) (hooks/use-stream ms/keyboard-mod #(reset! mod? %)) - (hooks/use-stream ms/keyboard-space #(reset! space? %))) + (hooks/use-stream ms/keyboard-space #(reset! space? %)) + (hooks/use-stream ms/keyboard-z #(reset! z? %))) (defn group-empty-space? "Given a group `group-id` check if `hover-ids` contains any of its children. If it doesn't means diff --git a/frontend/src/app/main/ui/workspace/viewport/utils.cljs b/frontend/src/app/main/ui/workspace/viewport/utils.cljs index 549489e4c..750c79730 100644 --- a/frontend/src/app/main/ui/workspace/viewport/utils.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/utils.cljs @@ -31,7 +31,7 @@ :duplicate cur/duplicate :zoom cur/zoom :zoom-in cur/zoom-in - :zooom-out cur/zoom-out + :zoom-out cur/zoom-out cur/pointer-inner)) ;; Ensure that the label has always the same font diff --git a/frontend/src/app/util/keyboard.cljs b/frontend/src/app/util/keyboard.cljs index 09d5dd72b..ccb14ddf6 100644 --- a/frontend/src/app/util/keyboard.cljs +++ b/frontend/src/app/util/keyboard.cljs @@ -6,13 +6,19 @@ (ns app.util.keyboard (:require - [app.config :as cfg])) + [app.config :as cfg] + [cuerdas.core :as str])) (defn is-key? [^string key] (fn [^js e] (= (.-key e) key))) +(defn is-key-ignore-case? + [^string key] + (fn [^js e] + (= (str/upper (.-key e)) (str/upper key)))) + (defn ^boolean alt? [^js event] (.-altKey event)) @@ -38,6 +44,7 @@ (def esc? (is-key? "Escape")) (def enter? (is-key? "Enter")) (def space? (is-key? " ")) +(def z? (is-key-ignore-case? "z")) (def up-arrow? (is-key? "ArrowUp")) (def down-arrow? (is-key? "ArrowDown")) (def left-arrow? (is-key? "ArrowLeft")) diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 912c9df45..68035ff76 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -2559,6 +2559,12 @@ msgstr "Distribute vertically" msgid "shortcuts.zoom-selected" msgstr "Zoom to selected" +msgid "shortcuts.zoom-lense-increase" +msgstr "Zoom lense increase" + +msgid "shortcuts.zoom-lense-decrease" +msgstr "Zoom lense decrease" + #: src/app/main/ui/dashboard/files.cljs msgid "title.dashboard.files" msgstr "%s - Penpot" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 43868c137..37e58d679 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -3380,6 +3380,12 @@ msgstr "Pantalla completa" msgid "workspace.header.zoom-selected" msgstr "Zoom a selección" +msgid "shortcuts.zoom-lense-increase" +msgstr "Incrementar zoom a objetivo" + +msgid "shortcuts.zoom-lense-decrease" +msgstr "Decrementar zoom a objetivo" + #: src/app/main/ui/workspace/libraries.cljs msgid "workspace.libraries.add" msgstr "Añadir"