From d3d13781eed7998b6b1b6ee0b7cae59145269cc9 Mon Sep 17 00:00:00 2001 From: Pedro Lamas Date: Tue, 2 Apr 2024 11:58:20 +0100 Subject: [PATCH 1/7] feat: adds keyboard shortcuts Signed-off-by: Pedro Lamas --- components.d.ts | 1 + src/App.vue | 27 +++- .../common/KeyboardShortcutsDialog.vue | 125 ++++++++++++++++++ src/components/layout/AppBar.vue | 12 +- src/components/settings/GeneralSettings.vue | 23 ++++ src/components/ui/AppNavItem.vue | 82 ++++++++---- src/globals.ts | 13 ++ src/locales/en.yaml | 8 ++ src/scss/misc.scss | 4 + src/store/config/state.ts | 1 + src/store/config/types.ts | 1 + src/util/event-target-is-content-editable.ts | 25 ++++ 12 files changed, 295 insertions(+), 27 deletions(-) create mode 100644 src/components/common/KeyboardShortcutsDialog.vue create mode 100644 src/util/event-target-is-content-editable.ts diff --git a/components.d.ts b/components.d.ts index 65afaf4310..dd1589584a 100644 --- a/components.d.ts +++ b/components.d.ts @@ -49,6 +49,7 @@ declare module 'vue' { BedScrewsAdjustDialog: typeof import('./src/components/common/BedScrewsAdjustDialog.vue')['default'] CollapsableCard: typeof import('./src/components/common/CollapsableCard.vue')['default'] FlashMessage: typeof import('./src/components/common/FlashMessage.vue')['default'] + KeyboardShortcutsDialog: typeof import('./src/components/common/KeyboardShortcutsDialog.vue')['default'] KlippyStatusCard: typeof import('./src/components/common/KlippyStatusCard.vue')['default'] ManualProbeDialog: typeof import('./src/components/common/ManualProbeDialog.vue')['default'] PeripheralsDialog: typeof import('./src/components/common/PeripheralsDialog.vue')['default'] diff --git a/src/App.vue b/src/App.vue index 8295f71857..c7cf982242 100644 --- a/src/App.vue +++ b/src/App.vue @@ -85,6 +85,7 @@ + @@ -110,6 +111,7 @@ import type { FlashMessage } from '@/types' import { getFilesFromDataTransfer, hasFilesInDataTransfer } from './util/file-system-entry' import type { ThemeConfig } from '@/store/config/types' import ActionCommandPromptDialog from './components/common/ActionCommandPromptDialog.vue' +import KeyboardShortcutsDialog from './components/common/KeyboardShortcutsDialog.vue' @Component({ metaInfo () { @@ -122,13 +124,13 @@ import ActionCommandPromptDialog from './components/common/ActionCommandPromptDi components: { SpoolSelectionDialog, FileSystemDownloadDialog, - ActionCommandPromptDialog + ActionCommandPromptDialog, + KeyboardShortcutsDialog } }) export default class App extends Mixins(StateMixin, FilesMixin, BrowserMixin) { toolsdrawer: boolean | null = null navdrawer: boolean | null = null - showUpdateUI = false dragState = false customBackgroundImageStyle: Record = {} @@ -317,11 +319,16 @@ export default class App extends Mixins(StateMixin, FilesMixin, BrowserMixin) { } } + get enableKeyboardShortcuts (): boolean { + return this.$store.state.config.uiSettings.general.enableKeyboardShortcuts + } + mounted () { window.addEventListener('dragover', this.handleDragOver) window.addEventListener('dragenter', this.handleDragEnter) window.addEventListener('dragleave', this.handleDragLeave) window.addEventListener('drop', this.handleDrop) + window.addEventListener('keydown', this.handleKeyDown, false) // this.onLoadLocale(this.$i18n.locale) EventBus.bus.$on('flashMessage', (payload: FlashMessage) => { @@ -354,6 +361,7 @@ export default class App extends Mixins(StateMixin, FilesMixin, BrowserMixin) { window.removeEventListener('dragenter', this.handleDragEnter) window.removeEventListener('dragleave', this.handleDragLeave) window.removeEventListener('drop', this.handleDrop) + window.removeEventListener('keydown', this.handleKeyDown) } handleToolsDrawerChange () { @@ -425,6 +433,21 @@ export default class App extends Mixins(StateMixin, FilesMixin, BrowserMixin) { } } } + + handleKeyDown (event: KeyboardEvent) { + const { key, shiftKey, ctrlKey } = event + + if ( + this.enableKeyboardShortcuts && + key === 'F12' && + shiftKey && + ctrlKey + ) { + event.preventDefault() + + this.emergencyStop() + } + } } diff --git a/src/components/common/KeyboardShortcutsDialog.vue b/src/components/common/KeyboardShortcutsDialog.vue new file mode 100644 index 0000000000..45281b0092 --- /dev/null +++ b/src/components/common/KeyboardShortcutsDialog.vue @@ -0,0 +1,125 @@ + + + diff --git a/src/components/layout/AppBar.vue b/src/components/layout/AppBar.vue index 1086759dab..b4d6dcfabb 100644 --- a/src/components/layout/AppBar.vue +++ b/src/components/layout/AppBar.vue @@ -65,7 +65,13 @@ - {{ $t('app.general.tooltip.estop') }} + + {{ $t('app.general.tooltip.estop') }} + + @@ -335,6 +341,10 @@ export default class AppBar extends Mixins(StateMixin, ServicesMixin, FilesMixin return true } + get enableKeyboardShortcuts (): boolean { + return this.$store.state.config.uiSettings.general.enableKeyboardShortcuts + } + handleExitLayout () { this.$store.commit('config/setLayoutMode', false) } diff --git a/src/components/settings/GeneralSettings.vue b/src/components/settings/GeneralSettings.vue index ae31ef875f..292928a99c 100644 --- a/src/components/settings/GeneralSettings.vue +++ b/src/components/settings/GeneralSettings.vue @@ -66,6 +66,17 @@ + + + + + + - + + + {{ accelerator }} + - - diff --git a/src/globals.ts b/src/globals.ts index 64e1ce6bf5..29add50680 100644 --- a/src/globals.ts +++ b/src/globals.ts @@ -236,6 +236,19 @@ export const Globals = Object.freeze({ FILTERED_FOLDER_NAMES: ['.git'], FILTERED_FILES_PREFIX: ['.thumbs', 'thumbs'], FILTERED_FILES_EXTENSION: ['.ignoreme'], + KEYBOARD_SHORTCUTS: { + home: 'h', + console: 'c', + preview: 'p', + jobs: 'j', + history: 'i', + timelapse: 'l', + tune: 't', + diagnostics: 'g', + configure: 'x', + system: 'q', + settings: 's' + }, DOCS_ROOT: 'https://docs.fluidd.xyz', DOCS_REQUIRED_CONFIGURATION: 'https://docs.fluidd.xyz/configuration/initial_setup', DOCS_MULTIPLE_INSTANCES: 'https://docs.fluidd.xyz/configuration/multiple_printers', diff --git a/src/locales/en.yaml b/src/locales/en.yaml index 1160510971..99725a41ad 100644 --- a/src/locales/en.yaml +++ b/src/locales/en.yaml @@ -574,6 +574,7 @@ app: invert_x_control: Invert X control invert_y_control: Invert Y control invert_z_control: Invert Z control + keyboard_shortcuts: Keyboard Shortcuts language: Display Language last_result: Last result left_y: Left Y-Axis @@ -898,3 +899,10 @@ app: sensors: title: sensors: Sensors + keyboard_shortcuts: + title: + keyboard_shortcuts: Keyboard Shortcuts + label: + actions: Actions + navigation: Navigation + open_keyboard_shortcut_help: Open keyboard shortcut help diff --git a/src/scss/misc.scss b/src/scss/misc.scss index df014a1d07..a7c937b91f 100644 --- a/src/scss/misc.scss +++ b/src/scss/misc.scss @@ -75,3 +75,7 @@ input[type=number] { .theme--dark .spool-icon { stroke: rgba(0,0,0, 0.5); } + +.theme--dark.v-application kbd { + background: #4A4A4F !important; +} diff --git a/src/store/config/state.ts b/src/store/config/state.ts index 7fffb92ac0..e6b8fdf29f 100644 --- a/src/store/config/state.ts +++ b/src/store/config/state.ts @@ -46,6 +46,7 @@ export const defaultState = (): ConfigState => { sectionsToIgnorePendingConfigurationChanges: [], dateFormat: 'iso', timeFormat: 'iso', + enableKeyboardShortcuts: true, textSortOrder: 'default', showRateOfChange: false, showRelativeHumidity: true, diff --git a/src/store/config/types.ts b/src/store/config/types.ts index 410b15dd2f..2122cdb014 100644 --- a/src/store/config/types.ts +++ b/src/store/config/types.ts @@ -82,6 +82,7 @@ export interface GeneralConfig { sectionsToIgnorePendingConfigurationChanges: string[]; dateFormat: string; timeFormat: string; + enableKeyboardShortcuts: boolean; textSortOrder: TextSortOrder; showRateOfChange: boolean; showRelativeHumidity: boolean; diff --git a/src/util/event-target-is-content-editable.ts b/src/util/event-target-is-content-editable.ts new file mode 100644 index 0000000000..7f1a1fb8c9 --- /dev/null +++ b/src/util/event-target-is-content-editable.ts @@ -0,0 +1,25 @@ +const eventTargetIsContentEditable = (event: KeyboardEvent): boolean => { + if (event.target) { + const { isContentEditable, tagName, type, readOnly } = event.target as HTMLInputElement + + if (isContentEditable) { + return true + } + + return ( + !readOnly && + ( + tagName === 'TEXTAREA' || + tagName === 'SELECT' || + ( + tagName === 'INPUT' && + !['checkbox', 'radio', 'range', 'button', 'file', 'reset', 'submit', 'color'].includes(type) + ) + ) + ) + } + + return false +} + +export default eventTargetIsContentEditable From e9f284ab88378379062c3d59688fb9caf14e0cf8 Mon Sep 17 00:00:00 2001 From: Pedro Lamas Date: Thu, 4 Apr 2024 22:38:06 +0100 Subject: [PATCH 2/7] refactor: show only available keyboard shortcuts Replaced Ctrl+Shift+F12 with Ctrl+Alt+F12 for Emergency Stop Adds Ctrl+Shift+P for Pause and Ctrl+Shift+C for Cancel Signed-off-by: Pedro Lamas --- src/App.vue | 39 +++++++++++++++--- .../common/KeyboardShortcutsDialog.vue | 40 ++++++++++++++++--- src/components/layout/AppBar.vue | 2 +- src/components/layout/AppNavDrawer.vue | 4 -- src/components/ui/AppNavItem.vue | 5 ++- .../widgets/status/StatusControls.vue | 23 ----------- src/locales/en.yaml | 1 + src/mixins/state.ts | 22 ++++++++++ 8 files changed, 96 insertions(+), 40 deletions(-) diff --git a/src/App.vue b/src/App.vue index c7cf982242..08e6507025 100644 --- a/src/App.vue +++ b/src/App.vue @@ -435,17 +435,46 @@ export default class App extends Mixins(StateMixin, FilesMixin, BrowserMixin) { } handleKeyDown (event: KeyboardEvent) { - const { key, shiftKey, ctrlKey } = event + if (!this.enableKeyboardShortcuts) { + return + } + + const { key, ctrlKey, altKey, shiftKey } = event if ( - this.enableKeyboardShortcuts && - key === 'F12' && - shiftKey && - ctrlKey + ctrlKey && + altKey && + key === 'F12' ) { event.preventDefault() this.emergencyStop() + + return + } + + if ( + ctrlKey && + shiftKey && + !altKey + ) { + if ( + key === 'C' && ( + this.printerPrinting || + this.printerPaused + ) + ) { + event.preventDefault() + + this.cancelPrint() + } else if ( + key === 'P' && + this.printerPrinting + ) { + event.preventDefault() + + this.pausePrint() + } } } } diff --git a/src/components/common/KeyboardShortcutsDialog.vue b/src/components/common/KeyboardShortcutsDialog.vue index 45281b0092..f3d4b22c87 100644 --- a/src/components/common/KeyboardShortcutsDialog.vue +++ b/src/components/common/KeyboardShortcutsDialog.vue @@ -27,11 +27,11 @@ {{ $t('app.general.title.jobs') }} {{ keyboardShortcuts.jobs }} - + {{ $t('app.general.title.history') }} {{ keyboardShortcuts.history }} - + {{ $t('app.general.title.timelapse') }} {{ keyboardShortcuts.timelapse }} @@ -39,7 +39,7 @@ {{ $t('app.general.title.tune') }} {{ keyboardShortcuts.tune }} - + {{ $t('app.general.title.diagnostics') }} {{ keyboardShortcuts.diagnostics }} @@ -59,6 +59,23 @@ + + {{ $t('app.keyboard_shortcuts.label.printing') }} + + + + + {{ $t('app.general.btn.pause') }} + Ctrl + Shift + P + + + {{ $t('app.general.btn.cancel') }} + Ctrl + Shift + C + + + + + {{ $t('app.keyboard_shortcuts.label.actions') }} @@ -66,7 +83,7 @@ {{ $t('app.general.tooltip.estop') }} - Ctrl + Shift + F12 + Ctrl + Alt + F12 {{ $t('app.keyboard_shortcuts.label.open_keyboard_shortcut_help') }} @@ -96,16 +113,29 @@ export default class KeyboardShortcutsDialog extends Vue { return this.$store.state.config.uiSettings.general.enableKeyboardShortcuts } + get supportsHistory (): boolean { + return this.$store.getters['server/componentSupport']('history') + } + + get supportsTimelapse (): boolean { + return this.$store.getters['server/componentSupport']('timelapse') + } + + get enableDiagnostics (): boolean { + return this.$store.state.config.uiSettings.general.enableDiagnostics + } + handleKeyDown (event: KeyboardEvent) { if (!this.enableKeyboardShortcuts) { return } - const { key, ctrlKey } = event + const { key, ctrlKey, altKey } = event if ( key === '?' && !ctrlKey && + !altKey && !eventTargetIsContentEditable(event) ) { event.preventDefault() diff --git a/src/components/layout/AppBar.vue b/src/components/layout/AppBar.vue index b4d6dcfabb..b7589c10e2 100644 --- a/src/components/layout/AppBar.vue +++ b/src/components/layout/AppBar.vue @@ -69,7 +69,7 @@ {{ $t('app.general.tooltip.estop') }} diff --git a/src/components/layout/AppNavDrawer.vue b/src/components/layout/AppNavDrawer.vue index 3cdd82505d..f7837f511e 100644 --- a/src/components/layout/AppNavDrawer.vue +++ b/src/components/layout/AppNavDrawer.vue @@ -141,10 +141,6 @@ export default class AppNavDrawer extends Mixins(StateMixin, BrowserMixin) { return this.$store.getters['server/componentSupport']('timelapse') } - get supportsVersions () { - return this.$store.getters['server/componentSupport']('update_manager') - } - get enableDiagnostics () { return this.$store.state.config.uiSettings.general.enableDiagnostics } diff --git a/src/components/ui/AppNavItem.vue b/src/components/ui/AppNavItem.vue index 38d4fb04e1..93f9be78f0 100644 --- a/src/components/ui/AppNavItem.vue +++ b/src/components/ui/AppNavItem.vue @@ -78,12 +78,13 @@ export default class AppNavItem extends Mixins(StateMixin, BrowserMixin) { return } - const { key, shiftKey, ctrlKey } = event + const { key, ctrlKey, altKey, shiftKey } = event if ( key === this.accelerator && - !shiftKey && !ctrlKey && + !altKey && + !shiftKey && !eventTargetIsContentEditable(event) && this.$router.currentRoute.path !== this.to ) { diff --git a/src/components/widgets/status/StatusControls.vue b/src/components/widgets/status/StatusControls.vue index 5e76dea8b1..5c64a4c5f1 100644 --- a/src/components/widgets/status/StatusControls.vue +++ b/src/components/widgets/status/StatusControls.vue @@ -93,7 +93,6 @@