diff --git a/CHANGES.md b/CHANGES.md index 57d8fc11f3..7d555b47a8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -75,6 +75,7 @@ ### :bug: Bugs fixed +- Fix `Ctrl+'` "Show guides" shortcut on non-US keyboard layouts by matching the physical key location so it no longer collides with the snap-guides shortcut (by @RenzoMXD) [Github #8423](https://github.com/penpot/penpot/issues/8423) - Fix lost-update race on `team.features` during concurrent file creation: two simultaneous create-file requests on the same team could both read the same features snapshot, compute different unions, and have the second `UPDATE` silently overwrite the first; the write is now preceded by a `SELECT … FOR UPDATE` inside the same transaction so every update sees the latest committed state [Github #9197](https://github.com/penpot/penpot/issues/9197) - Fix Alt/Option to draw shapes from center point (by @offreal) [Github #8361](https://github.com/penpot/penpot/pull/8361) - Add token name on broken token pill on sidebar [Taiga #13527](https://tree.taiga.io/project/penpot/issue/13527) diff --git a/frontend/src/app/main/data/workspace/shortcuts.cljs b/frontend/src/app/main/data/workspace/shortcuts.cljs index c25c712681..7bd25c9f33 100644 --- a/frontend/src/app/main/data/workspace/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/shortcuts.cljs @@ -6,6 +6,7 @@ (ns app.main.data.workspace.shortcuts (:require + [app.config :as cf] [app.main.data.common :as dcm] [app.main.data.event :as ev] [app.main.data.exports.assets :as de] @@ -41,6 +42,19 @@ (-> (dw/toggle-layout-flag flag) (vary-meta assoc ::ev/origin "workspace-shortcuts"))) +(defn on-display-guides-keydown + [^js event] + (let [mod? (if (cf/check-platform? :macos) + (.-metaKey event) + (.-ctrlKey event)) + shift? (.-shiftKey event) + code (.-code event)] + (when (and mod? + (or (and (not shift?) (= "Quote" code)) + (and shift? (= "Backslash" code)))) + (.preventDefault event) + (st/emit! (toggle-layout-flag :display-guides))))) + (defn- emit-when-no-readonly [& events] (let [can-edit? (:can-edit (deref refs/permissions)) diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index 140b5d5dd1..b7a4f05c1a 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -461,6 +461,11 @@ [path-editing? drawing-path? text-editing? grid-editing?] (hooks/use-shortcuts ::workspace wsc/shortcuts) + (mf/with-effect [] + (.addEventListener js/window "keydown" wsc/on-display-guides-keydown) + (fn [] + (.removeEventListener js/window "keydown" wsc/on-display-guides-keydown))) + (mf/with-effect [path-editing? drawing-path? grid-editing?] (cond grid-editing? diff --git a/frontend/test/frontend_tests/data/workspace_shortcuts_test.cljs b/frontend/test/frontend_tests/data/workspace_shortcuts_test.cljs new file mode 100644 index 0000000000..4b0cd0a4bf --- /dev/null +++ b/frontend/test/frontend_tests/data/workspace_shortcuts_test.cljs @@ -0,0 +1,105 @@ +;; 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) KALEIDOS INC + +(ns frontend-tests.data.workspace-shortcuts-test + (:require + [app.config :as cf] + [app.main.data.workspace.shortcuts :as shortcuts] + [app.main.store :as st] + [cljs.test :as t :include-macros true])) + +(defn- keyboard-event + [{:keys [ctrl? meta? shift? code prevented?]}] + #js {:ctrlKey (boolean ctrl?) + :metaKey (boolean meta?) + :shiftKey (boolean shift?) + :code code + :preventDefault #(reset! prevented? true)}) + +(t/deftest display-guides-handler-detects-physical-keys + (t/testing "US layout uses Ctrl+Quote" + (let [prevented? (atom false) + emitted (atom [])] + (with-redefs [cf/check-platform? (constantly false) + st/emit! #(swap! emitted conj %)] + (shortcuts/on-display-guides-keydown + (keyboard-event {:ctrl? true :code "Quote" :prevented? prevented?}))) + (t/is (true? @prevented?)) + (t/is (= 1 (count @emitted))))) + + (t/testing "German layout uses Ctrl+Shift+Backslash" + (let [prevented? (atom false) + emitted (atom [])] + (with-redefs [cf/check-platform? (constantly false) + st/emit! #(swap! emitted conj %)] + (shortcuts/on-display-guides-keydown + (keyboard-event {:ctrl? true :shift? true :code "Backslash" :prevented? prevented?}))) + (t/is (true? @prevented?)) + (t/is (= 1 (count @emitted))))) + + (t/testing "macOS uses Meta+Quote" + (let [prevented? (atom false) + emitted (atom [])] + (with-redefs [cf/check-platform? (constantly true) + st/emit! #(swap! emitted conj %)] + (shortcuts/on-display-guides-keydown + (keyboard-event {:meta? true :code "Quote" :prevented? prevented?}))) + (t/is (true? @prevented?)) + (t/is (= 1 (count @emitted))))) + + (t/testing "macOS German layout uses Meta+Shift+Backslash" + (let [prevented? (atom false) + emitted (atom [])] + (with-redefs [cf/check-platform? (constantly true) + st/emit! #(swap! emitted conj %)] + (shortcuts/on-display-guides-keydown + (keyboard-event {:meta? true :shift? true :code "Backslash" :prevented? prevented?}))) + (t/is (true? @prevented?)) + (t/is (= 1 (count @emitted))))) + + (t/testing "macOS ignores Ctrl+Quote" + (let [prevented? (atom false) + emitted (atom [])] + (with-redefs [cf/check-platform? (constantly true) + st/emit! #(swap! emitted conj %)] + (shortcuts/on-display-guides-keydown + (keyboard-event {:ctrl? true :code "Quote" :prevented? prevented?}))) + (t/is (false? @prevented?)) + (t/is (empty? @emitted)))) + + (t/testing "macOS snap-guides shortcut stays out of this handler" + (let [prevented? (atom false) + emitted (atom [])] + (with-redefs [cf/check-platform? (constantly true) + st/emit! #(swap! emitted conj %)] + (shortcuts/on-display-guides-keydown + (keyboard-event {:meta? true :shift? true :code "Quote" :prevented? prevented?}))) + (t/is (false? @prevented?)) + (t/is (empty? @emitted)))) + + (t/testing "matching physical keys without the platform modifier are ignored" + (let [prevented? (atom false) + emitted (atom [])] + (with-redefs [cf/check-platform? (constantly false) + st/emit! #(swap! emitted conj %)] + (shortcuts/on-display-guides-keydown + (keyboard-event {:code "Quote" :prevented? prevented?})) + (shortcuts/on-display-guides-keydown + (keyboard-event {:shift? true :code "Backslash" :prevented? prevented?}))) + (t/is (false? @prevented?)) + (t/is (empty? @emitted)))) + + (t/testing "snap-guides and alignment shortcuts stay out of this handler" + (let [prevented? (atom false) + emitted (atom [])] + (with-redefs [cf/check-platform? (constantly false) + st/emit! #(swap! emitted conj %)] + (shortcuts/on-display-guides-keydown + (keyboard-event {:ctrl? true :shift? true :code "Quote" :prevented? prevented?})) + (shortcuts/on-display-guides-keydown + (keyboard-event {:ctrl? true :code "Backslash" :prevented? prevented?}))) + (t/is (false? @prevented?)) + (t/is (empty? @emitted))))) diff --git a/frontend/test/frontend_tests/runner.cljs b/frontend/test/frontend_tests/runner.cljs index 065ed3bf80..21816ba1a3 100644 --- a/frontend/test/frontend_tests/runner.cljs +++ b/frontend/test/frontend_tests/runner.cljs @@ -9,6 +9,7 @@ [frontend-tests.data.viewer-test] [frontend-tests.data.workspace-colors-test] [frontend-tests.data.workspace-media-test] + [frontend-tests.data.workspace-shortcuts-test] [frontend-tests.data.workspace-texts-test] [frontend-tests.data.workspace-thumbnails-test] [frontend-tests.errors-test] @@ -58,6 +59,7 @@ 'frontend-tests.data.viewer-test 'frontend-tests.data.workspace-colors-test 'frontend-tests.data.workspace-media-test + 'frontend-tests.data.workspace-shortcuts-test 'frontend-tests.data.workspace-texts-test 'frontend-tests.data.workspace-thumbnails-test 'frontend-tests.helpers-shapes-test