0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-10 00:58:26 -05:00

Merge pull request #2825 from penpot/palba-text-formatting-shortcuts

🎉 Shortcuts for text formatting
This commit is contained in:
Alejandro 2023-01-25 11:33:17 +01:00 committed by GitHub
commit e939db927e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 425 additions and 86 deletions

View file

@ -10,6 +10,7 @@
- Handoff visual improvements [Taiga #3124](https://tree.taiga.io/project/penpot/us/3124)
- Dynamic alignment only in sight [Github 1971](https://github.com/penpot/penpot/issues/1971)
- Add some accessibility to shortcut panel [Taiga #4713](https://tree.taiga.io/project/penpot/issue/4713)
- Add shortcuts for text editing [Taiga #2052](https://tree.taiga.io/project/penpot/us/2052)
- Second level boards treated as groups in terms of selection [Taiga #4269](https://tree.taiga.io/project/penpot/us/4269)
- Performance improvements both for backend and frontend
- Accessibility improvements for login area [Taiga #4353](https://tree.taiga.io/project/penpot/us/4353)

View file

@ -89,6 +89,10 @@
[key]
(-> key meta alt))
(defn alt-shift
[key]
(-> key alt shift))
(defn supr
[]
(if (cf/check-platform? :macos)
@ -133,17 +137,23 @@
[key cb]
(fn [event]
(log/debug :msg (str "Shortcut" key))
(.preventDefault event)
(when (aget event "preventDefault")
(.preventDefault event))
(cb event)))
(defn- bind!
[shortcuts]
(let [msbind (fn [command callback type]
(if type
(mousetrap/bind command callback type)
(mousetrap/bind command callback)))]
(->> shortcuts
(remove #(:disabled (second %)))
(run! (fn [[key {:keys [command fn type]}]]
(if (vector? command)
(run! #(mousetrap/bind % (wrap-cb key fn) type) command)
(mousetrap/bind command (wrap-cb key fn) type))))))
(let [callback (wrap-cb key fn)]
(if (vector? command)
(run! #(msbind % callback type) command)
(msbind command callback type))))))))
(defn- reset!
([]

View file

@ -17,6 +17,7 @@
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.shapes :as dws]
[app.main.data.workspace.text.shortcuts :as dwtxts]
[app.main.data.workspace.texts :as dwtxt]
[app.main.data.workspace.transforms :as dwt]
[app.main.data.workspace.undo :as dwu]
@ -107,6 +108,7 @@
:subsections [:edit]
:fn #(st/emit! :interrupt (dw/deselect-all true))}
;; MODIFY LAYERS
@ -526,7 +528,7 @@
:fn #(emit-when-no-readonly (dwly/pressed-opacity n))}])))))
(def shortcuts
(merge base-shortcuts opacity-shortcuts))
(merge base-shortcuts opacity-shortcuts dwtxts/shortcuts))
(defn get-tooltip [shortcut]
(assert (contains? shortcuts shortcut) (str shortcut))

View file

@ -0,0 +1,225 @@
;; 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 app.main.data.workspace.text.shortcuts
(:require
[app.common.data :as d]
[app.main.data.shortcuts :as ds]
[app.main.data.workspace.texts :as dwt]
[app.main.fonts :as fonts]
[app.main.refs :as refs]
[app.main.store :as st]
[cuerdas.core :as str]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Shortcuts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Shortcuts format https://github.com/ccampbell/mousetrap
(defn- generate-variant-props
[text-values variant-id]
(let [first-intersection (fn [list1 list2] (first (filter (set list1) list2)))
current-variant (:font-variant-id text-values)
bold-options (cond
(str/includes? current-variant "black")
["black" "bold" "700"]
(str/includes? current-variant "700")
["700" "black" "bold"]
:else
["bold" "black" "700"])
current-variant-no-italic (cond
(str/includes? current-variant "italic")
(subs current-variant 0 (- (count current-variant) 6))
(str/includes? current-variant "cursive")
(subs current-variant 0 (- (count current-variant) 7))
:else nil)
regular-options [current-variant-no-italic "regular" "normal" "400"]
italic-options [(when (and (not (str/includes? current-variant "bold"))
(not (str/includes? current-variant "black"))
(not (str/includes? current-variant "700")))
(str current-variant "italic"))
"italic" "cursive"]
bold-italic-options (cond
(str/includes? current-variant "black")
["blackitalic" "blackcursive" "bolditalic" "700italic" "boldcursive" "700cursive"]
(str/includes? current-variant "700")
["700italic" "700cursive" "bolditalic" "blackitalic" "boldcursive" "blackcursive"]
:else
["bolditalic" "700italic" "blackitalic" "boldcursive" "700cursive" "blackcursive"])
is-bold? (fn [variant-id] (some #(str/includes? variant-id %) bold-options))
is-italic? (fn [variant-id] (some #(str/includes? variant-id %) italic-options))
font-id (:font-id text-values)
fonts (deref fonts/fontsdb)
font (get fonts font-id)
variants (map :id (:variants font))
choose-regular (fn [] (first-intersection variants regular-options))
choose-bold (fn [] (first-intersection variants bold-options))
choose-italic (fn [] (first-intersection variants italic-options))
choose-bold-italic (fn [] (or (first-intersection variants bold-italic-options) (choose-bold)))
choose-italic-bold (fn [] (or (first-intersection variants bold-italic-options) (choose-italic)))
new-variant (let [toggle-bold? (= variant-id "bold")
toggle-italic? (= variant-id "italic")
bold? (is-bold? current-variant)
italic? (is-italic? current-variant)]
(cond
(and toggle-bold? bold? italic?) ;; it is bold+italic, set it to italic
(choose-italic)
(and toggle-bold? bold? (not italic?)) ;; it is bold, set it to regular
(choose-regular)
(and toggle-bold? (not bold?) italic?) ;; it is italic, set it to bold+italic
(choose-bold-italic)
(and toggle-bold? (not bold?) (not italic?)) ;; it is regular, set it to bold
(choose-bold)
(and toggle-italic? bold? italic?) ;; it is bold+italic, set it to bold
(choose-bold)
(and toggle-italic? bold? (not italic?)) ;; it is bold, set it to italic+bold
(choose-italic-bold)
(and toggle-italic? (not bold?) italic?) ;; it is italic, set it to regular
(choose-regular)
(and toggle-italic? (not bold?) (not italic?)) ;; it is regular, set it to italic
(choose-italic)))
new-weight (->> (:variants font)
(filter #(= (:id %) new-variant))
first
:weight)]
{:font-variant-id new-variant,
:font-weight new-weight}))
(defn- update-attrs [shape props]
(let [state-map (deref refs/workspace-editor-state)
editor-state (get state-map (:id shape))
text-values (d/merge
(dwt/current-root-values
{:shape shape
:attrs dwt/root-attrs})
(dwt/current-paragraph-values
{:editor-state editor-state
:shape shape
:attrs dwt/paragraph-attrs})
(dwt/current-text-values
{:editor-state editor-state
:shape shape
:attrs dwt/text-attrs}))
font-size (d/parse-double (:font-size text-values))
line-height (d/parse-double (:line-height text-values))
letter-spacing (d/parse-double (:letter-spacing text-values))
props (cond
(:font-size-inc props)
{:font-size (str (inc font-size))}
(:font-size-dec props)
{:font-size (str (dec font-size))}
(:line-height-inc props)
{:line-height (str (+ line-height 0.1))}
(:line-height-dec props)
{:line-height (str (- line-height 0.1))}
(:letter-spacing-inc props)
{:letter-spacing (str (+ letter-spacing 0.1))}
(:letter-spacing-dec props)
{:letter-spacing (str (- letter-spacing 0.1))}
(= (:text-decoration props) "underline") ;;toggle
(if (= (:text-decoration text-values) "underline")
{:text-decoration "none"}
props)
(= (:text-decoration props) "line-through") ;;toggle
(if (= (:text-decoration text-values) "line-through")
{:text-decoration "none"}
props)
(:font-variant-id props)
(generate-variant-props text-values (:font-variant-id props))
:else props)]
(when shape
(st/emit! (dwt/update-attrs (:id shape) props)))))
(defn- update-attrs-when-no-readonly [props]
(let [read-only? (deref refs/workspace-read-only?)
shapes (deref refs/selected-objects)
shape (first shapes)]
(when (and (not read-only?)
(= 1 (count shapes))
(= (:type shape) :text))
(update-attrs shape props))))
(def shortcuts
{:align-left {:tooltip (ds/meta (ds/alt "l"))
:command (ds/c-mod "alt+l")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:text-align "left"})}
:align-right {:tooltip (ds/meta (ds/alt "r"))
:command (ds/c-mod "alt+r")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:text-align "right"})}
:align-center {:tooltip (ds/meta (ds/alt "t"))
:command (ds/c-mod "alt+t")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:text-align "center"})}
:align-justify {:tooltip (ds/meta (ds/alt "j"))
:command (ds/c-mod "alt+j")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:text-align "justify"})}
:underline {:tooltip (ds/meta "u")
:command (ds/c-mod "u")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:text-decoration "underline"})}
:line-through {:tooltip (ds/alt (ds/meta-shift "5"))
:command "alt+shift+5"
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:text-decoration "line-through"})}
:font-size-inc {:tooltip (ds/meta-shift ds/up-arrow)
:command (ds/c-mod "shift+up")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:font-size-inc true})}
:font-size-dec {:tooltip (ds/meta-shift ds/down-arrow)
:command (ds/c-mod "shift+down")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:font-size-dec true})}
:line-height-inc {:tooltip (ds/alt-shift ds/up-arrow)
:command (ds/a-mod "shift+up")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:line-height-inc true})}
:line-height-dec {:tooltip (ds/alt-shift ds/down-arrow)
:command (ds/a-mod "shift+down")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:line-height-dec true})}
:letter-spacing-inc {:tooltip (ds/alt ds/up-arrow)
:command (ds/a-mod "up")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:letter-spacing-inc true})}
:letter-spacing-dec {:tooltip (ds/alt ds/down-arrow)
:command (ds/a-mod "down")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:letter-spacing-dec true})}
:bold {:tooltip (ds/meta "b")
:command (ds/c-mod "b")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:font-variant-id "bold"})}
:italic {:tooltip (ds/meta "i")
:command (ds/c-mod "i")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:font-variant-id "italic"})}})

View file

@ -28,6 +28,68 @@
[beicon.core :as rx]
[potok.core :as ptk]))
;; -- Attrs
(def text-typography-attrs
[:typography-ref-id
:typography-ref-file])
(def text-fill-attrs
[:fill-color
:fill-opacity
:fill-color-ref-id
:fill-color-ref-file
:fill-color-gradient])
(def text-font-attrs
[:font-id
:font-family
:font-variant-id
:font-size
:font-weight
:font-style])
(def text-align-attrs
[:text-align])
(def text-direction-attrs
[:text-direction])
(def text-spacing-attrs
[:line-height
:letter-spacing])
(def text-valign-attrs
[:vertical-align])
(def text-decoration-attrs
[:text-decoration])
(def text-transform-attrs
[:text-transform])
(def shape-attrs
[:grow-type])
(def root-attrs text-valign-attrs)
(def paragraph-attrs
(d/concat-vec
text-align-attrs
text-direction-attrs))
(def text-attrs
(d/concat-vec
text-typography-attrs
text-font-attrs
text-spacing-attrs
text-decoration-attrs
text-transform-attrs))
(def attrs (d/concat-set shape-attrs root-attrs paragraph-attrs text-attrs))
;; -- Editor
(defn update-editor
[editor]
(ptk/reify ::update-editor
@ -182,8 +244,8 @@
(defn- update-text-content
[shape pred-fn update-fn attrs]
(let [update-attrs #(update-fn % attrs)
transform #(txt/transform-nodes pred-fn update-attrs %)]
(let [update-attrs-fn #(update-fn % attrs)
transform #(txt/transform-nodes pred-fn update-attrs-fn %)]
(-> shape
(update :content transform))))
@ -525,3 +587,24 @@
(rx/take-until stopper))
(rx/of (update-position-data id position-data))))
(rx/empty))))))
(defn update-attrs
[id attrs]
(ptk/reify ::update-attrs
ptk/WatchEvent
(watch [_ _ _]
(rx/concat
(let [attrs (select-keys attrs root-attrs)]
(if-not (empty? attrs)
(rx/of (update-root-attrs {:id id :attrs attrs}))
(rx/empty)))
(let [attrs (select-keys attrs paragraph-attrs)]
(if-not (empty? attrs)
(rx/of (update-paragraph-attrs {:id id :attrs attrs}))
(rx/empty)))
(let [attrs (select-keys attrs text-attrs)]
(if-not (empty? attrs)
(rx/of (update-text-attrs {:id id :attrs attrs}))
(rx/empty)))))))

View file

@ -188,6 +188,7 @@
(fn [editor]
(st/emit! (dwt/update-editor editor))
(when editor
(dom/add-class! (dom/get-element-by-class "public-DraftEditor-content") "mousetrap")
(.focus ^js editor))))
handle-return

View file

@ -25,63 +25,7 @@
[cuerdas.core :as str]
[rumext.v2 :as mf]))
(def text-typography-attrs
[:typography-ref-id
:typography-ref-file])
(def text-fill-attrs
[:fill-color
:fill-opacity
:fill-color-ref-id
:fill-color-ref-file
:fill-color-gradient])
(def text-font-attrs
[:font-id
:font-family
:font-variant-id
:font-size
:font-weight
:font-style])
(def text-align-attrs
[:text-align])
(def text-direction-attrs
[:text-direction])
(def text-spacing-attrs
[:line-height
:letter-spacing])
(def text-valign-attrs
[:vertical-align])
(def text-decoration-attrs
[:text-decoration])
(def text-transform-attrs
[:text-transform])
(def shape-attrs
[:grow-type])
(def root-attrs text-valign-attrs)
(def paragraph-attrs
(d/concat-vec
text-align-attrs
text-direction-attrs))
(def text-attrs
(d/concat-vec
text-typography-attrs
text-font-attrs
text-spacing-attrs
text-decoration-attrs
text-transform-attrs))
(def attrs (d/concat-set shape-attrs root-attrs paragraph-attrs text-attrs))
(mf/defc text-align-options
[{:keys [values on-change on-blur] :as props}]
@ -237,20 +181,9 @@
(mf/use-callback
(mf/deps values)
(fn [id attrs]
(st/emit! (dwt/save-font (-> (merge txt/default-text-attrs values attrs)
(select-keys text-attrs))))
(let [attrs (select-keys attrs root-attrs)]
(when-not (empty? attrs)
(st/emit! (dwt/update-root-attrs {:id id :attrs attrs}))))
(let [attrs (select-keys attrs paragraph-attrs)]
(when-not (empty? attrs)
(st/emit! (dwt/update-paragraph-attrs {:id id :attrs attrs}))))
(let [attrs (select-keys attrs text-attrs)]
(when-not (empty? attrs)
(st/emit! (dwt/update-text-attrs {:id id :attrs attrs}))))))
(st/emit! (dwt/save-font (-> (merge txt/default-text-attrs values attrs)
(select-keys dwt/text-attrs)))
(dwt/update-attrs id attrs))))
on-change
(mf/use-callback
@ -279,9 +212,9 @@
(fn [_]
(let [set-values (-> (d/without-nils values)
(select-keys
(d/concat-vec text-font-attrs
text-spacing-attrs
text-transform-attrs)))
(d/concat-vec dwt/text-font-attrs
dwt/text-spacing-attrs
dwt/text-transform-attrs)))
typography (merge txt/default-typography set-values)
typography (generate-typography-name typography)
id (uuid/next)]

View file

@ -11,6 +11,7 @@
[app.common.geom.shapes :as gsh]
[app.common.pages.common :as cpc]
[app.common.text :as txt]
[app.main.data.workspace.texts :as dwt]
[app.main.refs :as refs]
[app.main.ui.hooks :as hooks]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-attrs blur-menu]]
@ -158,7 +159,7 @@
:shadow shadow-attrs
:blur blur-attrs
:stroke stroke-attrs
:text ot/attrs
:text dwt/attrs
:exports exports-attrs
:layout-container layout-container-flex-attrs
:layout-item layout-item-attrs})

View file

@ -7,7 +7,7 @@
(ns app.main.ui.workspace.sidebar.options.shapes.text
(:require
[app.common.data :as d]
[app.main.data.workspace.texts :as dwt]
[app.main.data.workspace.texts :as dwt :refer [text-fill-attrs root-attrs paragraph-attrs text-attrs]]
[app.main.refs :as refs]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]]
@ -19,7 +19,7 @@
[app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu]]
[app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-menu]]
[app.main.ui.workspace.sidebar.options.menus.stroke :refer [stroke-attrs stroke-menu]]
[app.main.ui.workspace.sidebar.options.menus.text :refer [text-menu text-fill-attrs root-attrs paragraph-attrs text-attrs]]
[app.main.ui.workspace.sidebar.options.menus.text :refer [text-menu]]
[rumext.v2 :as mf]))
(mf/defc options

View file

@ -217,7 +217,7 @@
(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?)
(hooks/setup-shortcuts node-editing? drawing-path? text-editing?)
(hooks/setup-active-frames base-objects hover-ids selected active-frames zoom transform vbox)
[:div.viewport

View file

@ -16,6 +16,7 @@
[app.main.data.workspace :as dw]
[app.main.data.workspace.path.shortcuts :as psc]
[app.main.data.workspace.shortcuts :as wsc]
[app.main.data.workspace.text.shortcuts :as tsc]
[app.main.store :as st]
[app.main.streams :as ms]
[app.main.ui.hooks :as hooks]
@ -336,11 +337,15 @@
;; this shortcuts outside the viewport?
(defn setup-shortcuts
[path-editing? drawing-path?]
[path-editing? drawing-path? text-editing?]
(hooks/use-shortcuts ::workspace wsc/shortcuts)
(mf/use-effect
(mf/deps path-editing? drawing-path?)
(fn []
(when (or drawing-path? path-editing?)
(st/emit! (dsc/push-shortcuts ::path psc/shortcuts))
#(st/emit! (dsc/pop-shortcuts ::path))))))
#(st/emit! (dsc/pop-shortcuts ::path)))
(when text-editing?
(st/emit! (dsc/push-shortcuts ::text tsc/shortcuts))
#(st/emit! (dsc/pop-shortcuts ::text))))))

View file

@ -2262,6 +2262,9 @@ msgstr "Paths"
msgid "shortcut-subsection.shape"
msgstr "Shapes"
msgid "shortcut-subsection.text-editor"
msgstr "Texts"
msgid "shortcut-subsection.tools"
msgstr "Tools"
@ -2635,6 +2638,42 @@ msgstr "Distribute vertically"
msgid "shortcuts.zoom-selected"
msgstr "Zoom to selected"
msgid "shortcuts.align-center"
msgstr "Align center"
msgid "shortcuts.align-justify"
msgstr "Align justify"
msgid "shortcuts.bold"
msgstr "Toggle bold"
msgid "shortcuts.italic"
msgstr "Toggle italic"
msgid "shortcuts.font-size-dec"
msgstr "Decrement font size"
msgid "shortcuts.font-size-inc"
msgstr "Increment font size"
msgid "shortcuts.letter-spacing-dec"
msgstr "Decrement letter spacing"
msgid "shortcuts.letter-spacing-inc"
msgstr "Increment letter spacing"
msgid "shortcuts.line-height-dec"
msgstr "Decrement line height"
msgid "shortcuts.line-height-inc"
msgstr "Increment line height"
msgid "shortcuts.line-through"
msgstr "Toggle line through"
msgid "shortcuts.underline"
msgstr "Toggle underline"
msgid "shortcuts.zoom-lense-increase"
msgstr "Zoom lense increase"

View file

@ -2354,6 +2354,9 @@ msgstr "Ruta"
msgid "shortcut-subsection.shape"
msgstr "Formas"
msgid "shortcut-subsection.text-editor"
msgstr "Textos"
msgid "shortcut-subsection.tools"
msgstr "Herramientas"
@ -2727,6 +2730,42 @@ msgstr "Distribuir verticalmente"
msgid "shortcuts.zoom-selected"
msgstr "Zoom a selección"
msgid "shortcuts.align-center"
msgstr "Alinear al centro"
msgid "shortcuts.align-justify"
msgstr "Alinear justificado"
msgid "shortcuts.bold"
msgstr "Alternar negrita"
msgid "shortcuts.italic"
msgstr "Alternar cursiva"
msgid "shortcuts.font-size-dec"
msgstr "Decrementar el tamaño de fuente"
msgid "shortcuts.font-size-inc"
msgstr "Incrementar el tamaño de fuente"
msgid "shortcuts.letter-spacing-dec"
msgstr "Decrementar el espaciado de letras"
msgid "shortcuts.letter-spacing-inc"
msgstr "Incrementar el espaciado de letras"
msgid "shortcuts.line-height-dec"
msgstr "Decrementar el interlineado"
msgid "shortcuts.line-height-inc"
msgstr "Incrementar el interlineado"
msgid "shortcuts.line-through"
msgstr "Alternar tachado"
msgid "shortcuts.underline"
msgstr "Alternar subrayado"
#: src/app/main/ui/dashboard/files.cljs
msgid "title.dashboard.files"
msgstr "%s - Penpot"