refactorted shortcut handling into a service

This commit is contained in:
zadam 2022-12-01 00:17:15 +01:00
parent 0985314fb7
commit 720fb0f73e
12 changed files with 72 additions and 62 deletions

View File

@ -13,6 +13,7 @@ import appContext from "./app_context.js";
import NoteContextAwareWidget from "../widgets/note_context_aware_widget.js";
import BasicWidget from "../widgets/basic_widget.js";
import SpacedUpdate from "./spaced_update.js";
import shortcutService from "./shortcuts.js";
/**
* This is the main frontend API interface for scripts. It's published in the local "api" object.
@ -509,7 +510,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
* @param {string} keyboardShortcut - e.g. "ctrl+shift+a"
* @param {function} handler
*/
this.bindGlobalShortcut = utils.bindGlobalShortcut;
this.bindGlobalShortcut = shortcutService.bindGlobalShortcut;
/**
* Trilium runs in backend and frontend process, when something is changed on the backend from script,

View File

@ -1,6 +1,6 @@
import server from "./server.js";
import utils from "./utils.js";
import appContext from "./app_context.js";
import shortcutService from "./shortcuts.js";
const keyboardActionRepo = {};
@ -31,7 +31,7 @@ async function setupActionsForElement(scope, $el, component) {
for (const action of actions) {
for (const shortcut of action.effectiveShortcuts) {
utils.bindElShortcut($el, shortcut, () => component.triggerCommand(action.actionName, {ntxId: appContext.tabManager.activeNtxId}));
shortcutService.bindElShortcut($el, shortcut, () => component.triggerCommand(action.actionName, {ntxId: appContext.tabManager.activeNtxId}));
}
}
}
@ -39,14 +39,14 @@ async function setupActionsForElement(scope, $el, component) {
getActionsForScope("window").then(actions => {
for (const action of actions) {
for (const shortcut of action.effectiveShortcuts) {
utils.bindGlobalShortcut(shortcut, () => appContext.triggerCommand(action.actionName, {ntxId: appContext.tabManager.activeNtxId}));
shortcutService.bindGlobalShortcut(shortcut, () => appContext.triggerCommand(action.actionName, {ntxId: appContext.tabManager.activeNtxId}));
}
}
});
server.get('keyboard-shortcuts-for-notes').then(shortcutForNotes => {
for (const shortcut in shortcutForNotes) {
utils.bindGlobalShortcut(shortcut, async () => {
shortcutService.bindGlobalShortcut(shortcut, async () => {
appContext.tabManager.getActiveContext().setNote(shortcutForNotes[shortcut]);
});
}
@ -64,7 +64,7 @@ function setElementActionHandler($el, actionName, handler) {
for (const shortcut of action.effectiveShortcuts) {
if (shortcut) {
utils.bindElShortcut($el, shortcut, handler);
shortcutService.bindElShortcut($el, shortcut, handler);
}
}
});

View File

@ -2,15 +2,16 @@
* Mac specific initialization
*/
import utils from "./utils.js";
import shortcutService from "./shortcuts.js";
function init() {
if (utils.isElectron() && utils.isMac()) {
utils.bindGlobalShortcut('meta+c', () => exec("copy"));
utils.bindGlobalShortcut('meta+v', () => exec('paste'));
utils.bindGlobalShortcut('meta+x', () => exec('cut'));
utils.bindGlobalShortcut('meta+a', () => exec('selectAll'));
utils.bindGlobalShortcut('meta+z', () => exec('undo'));
utils.bindGlobalShortcut('meta+y', () => exec('redo'));
shortcutService.bindGlobalShortcut('meta+c', () => exec("copy"));
shortcutService.bindGlobalShortcut('meta+v', () => exec('paste'));
shortcutService.bindGlobalShortcut('meta+x', () => exec('cut'));
shortcutService.bindGlobalShortcut('meta+a', () => exec('selectAll'));
shortcutService.bindGlobalShortcut('meta+z', () => exec('undo'));
shortcutService.bindGlobalShortcut('meta+y', () => exec('redo'));
}
}
@ -22,4 +23,4 @@ function exec(cmd) {
export default {
init
}
}

View File

@ -0,0 +1,36 @@
import utils from "./utils.js";
function bindGlobalShortcut(keyboardShortcut, handler) {
bindElShortcut($(document), keyboardShortcut, handler);
}
function bindElShortcut($el, keyboardShortcut, handler) {
if (utils.isDesktop()) {
keyboardShortcut = normalizeShortcut(keyboardShortcut);
$el.bind('keydown', keyboardShortcut, e => {
handler(e);
e.preventDefault();
e.stopPropagation();
});
}
}
/**
* Normalize to the form expected by the jquery.hotkeys.js
*/
function normalizeShortcut(shortcut) {
return shortcut
.toLowerCase()
.replace("enter", "return")
.replace("delete", "del")
.replace("ctrl+alt", "alt+ctrl")
.replace("meta+alt", "alt+meta"); // alt needs to be first;
}
export default {
bindGlobalShortcut,
bindElShortcut,
normalizeShortcut
}

View File

@ -132,35 +132,6 @@ function randomString(len) {
return text;
}
function bindGlobalShortcut(keyboardShortcut, handler) {
bindElShortcut($(document), keyboardShortcut, handler);
}
function bindElShortcut($el, keyboardShortcut, handler) {
if (isDesktop()) {
keyboardShortcut = normalizeShortcut(keyboardShortcut);
$el.bind('keydown', keyboardShortcut, e => {
handler(e);
e.preventDefault();
e.stopPropagation();
});
}
}
/**
* Normalize to the form expected by the jquery.hotkeys.js
*/
function normalizeShortcut(shortcut) {
return shortcut
.toLowerCase()
.replace("enter", "return")
.replace("delete", "del")
.replace("ctrl+alt", "alt+ctrl")
.replace("meta+alt", "alt+meta"); // alt needs to be first;
}
function isMobile() {
return window.device === "mobile"
// window.device is not available in setup
@ -387,8 +358,6 @@ export default {
formatLabel,
toObject,
randomString,
bindGlobalShortcut,
bindElShortcut,
isMobile,
isDesktop,
setCookie,
@ -402,7 +371,6 @@ export default {
focusSavedElement,
isHtmlEmpty,
clearBrowserCache,
normalizeShortcut,
copySelectionToClipboard,
dynamicRequire,
timeLimit,

View File

@ -8,6 +8,7 @@ import promotedAttributeDefinitionParser from '../../services/promoted_attribute
import NoteContextAwareWidget from "../note_context_aware_widget.js";
import SpacedUpdate from "../../services/spaced_update.js";
import utils from "../../services/utils.js";
import shortcutService from "../../services/shortcuts.js";
const TPL = `
<div class="attr-detail">
@ -271,8 +272,8 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
this.$widget = $(TPL);
utils.bindElShortcut(this.$widget, 'ctrl+return', () => this.saveAndClose());
utils.bindElShortcut(this.$widget, 'esc', () => this.cancelAndClose());
shortcutService.bindElShortcut(this.$widget, 'ctrl+return', () => this.saveAndClose());
shortcutService.bindElShortcut(this.$widget, 'esc', () => this.cancelAndClose());
this.$title = this.$widget.find('.attr-detail-title');

View File

@ -2,6 +2,7 @@ import noteAutocompleteService from '../../services/note_autocomplete.js';
import utils from "../../services/utils.js";
import appContext from "../../services/app_context.js";
import BasicWidget from "../basic_widget.js";
import shortcutService from "../../services/shortcuts.js";
const TPL = `<div class="jump-to-note-dialog modal mx-auto" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
@ -42,7 +43,7 @@ export default class JumpToNoteDialog extends BasicWidget {
this.$showInFullTextButton = this.$widget.find(".show-in-full-text-button");
this.$showInFullTextButton.on('click', e => this.showInFullText(e));
utils.bindElShortcut(this.$widget, 'ctrl+return', e => this.showInFullText(e));
shortcutService.bindElShortcut(this.$widget, 'ctrl+return', e => this.showInFullText(e));
}
async jumpToNoteEvent() {

View File

@ -3,6 +3,7 @@ import toastService from "../../services/toast.js";
import utils from "../../services/utils.js";
import appContext from "../../services/app_context.js";
import BasicWidget from "../basic_widget.js";
import shortcutService from "../../services/shortcuts.js";
const TPL = `
<div class="markdown-import-dialog modal fade mx-auto" tabindex="-1" role="dialog">
@ -42,7 +43,7 @@ export default class MarkdownImportDialog extends BasicWidget {
this.$widget.on('shown.bs.modal', () => this.$importTextarea.trigger('focus'));
utils.bindElShortcut(this.$widget, 'ctrl+return', () => this.sendForm());
shortcutService.bindElShortcut(this.$widget, 'ctrl+return', () => this.sendForm());
}
async convertMarkdownToHtml(text) {

View File

@ -1,10 +1,10 @@
import NoteContextAwareWidget from "./note_context_aware_widget.js";
import utils from "../services/utils.js";
import protectedSessionHolder from "../services/protected_session_holder.js";
import server from "../services/server.js";
import SpacedUpdate from "../services/spaced_update.js";
import appContext from "../services/app_context.js";
import branchService from "../services/branches.js";
import shortcutService from "../services/shortcuts.js";
const TPL = `
<div class="note-title-widget">
@ -58,13 +58,13 @@ export default class NoteTitleWidget extends NoteContextAwareWidget {
this.deleteNoteOnEscape = false;
});
utils.bindElShortcut(this.$noteTitle, 'esc', () => {
shortcutService.bindElShortcut(this.$noteTitle, 'esc', () => {
if (this.deleteNoteOnEscape && this.noteContext.isActive()) {
branchService.deleteNotes(Object.values(this.noteContext.note.parentToBranch));
}
});
utils.bindElShortcut(this.$noteTitle, 'return', () => {
shortcutService.bindElShortcut(this.$noteTitle, 'return', () => {
this.triggerCommand('focusOnDetail', {ntxId: this.noteContext.ntxId});
});
}

View File

@ -18,6 +18,7 @@ import syncService from "../services/sync.js";
import options from "../services/options.js";
import protectedSessionHolder from "../services/protected_session_holder.js";
import dialogService from "../services/dialog.js";
import shortcutService from "../services/shortcuts.js";
const TPL = `
<div class="tree-wrapper">
@ -1331,7 +1332,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
for (const action of actions) {
for (const shortcut of action.effectiveShortcuts) {
hotKeyMap[utils.normalizeShortcut(shortcut)] = node => {
hotKeyMap[shortcutService.normalizeShortcut(shortcut)] = node => {
const notePath = treeService.getNotePath(node);
this.triggerCommand(action.actionName, {node, notePath});

View File

@ -1,10 +1,10 @@
import BasicWidget from "./basic_widget.js";
import server from "../services/server.js";
import linkService from "../services/link.js";
import dateNotesService from "../services/date_notes.js";
import froca from "../services/froca.js";
import utils from "../services/utils.js";
import appContext from "../services/app_context.js";
import shortcutService from "../services/shortcuts.js";
const TPL = `
<div class="quick-search input-group input-group-sm">
@ -66,7 +66,7 @@ export default class QuickSearchWidget extends BasicWidget {
})
}
utils.bindElShortcut(this.$searchString, 'return', () => {
shortcutService.bindElShortcut(this.$searchString, 'return', () => {
if (this.$dropdownMenu.is(":visible")) {
this.search(); // just update already visible dropdown
} else {
@ -76,11 +76,11 @@ export default class QuickSearchWidget extends BasicWidget {
this.$searchString.focus();
});
utils.bindElShortcut(this.$searchString, 'down', () => {
shortcutService.bindElShortcut(this.$searchString, 'down', () => {
this.$dropdownMenu.find('.dropdown-item:first').focus();
});
utils.bindElShortcut(this.$searchString, 'esc', () => {
shortcutService.bindElShortcut(this.$searchString, 'esc', () => {
this.$dropdownToggle.dropdown('hide');
});
@ -120,7 +120,7 @@ export default class QuickSearchWidget extends BasicWidget {
appContext.tabManager.getActiveContext().setNote(note.noteId);
}
});
utils.bindElShortcut($link, 'return', () => {
shortcutService.bindElShortcut($link, 'return', () => {
this.$dropdownToggle.dropdown("hide");
appContext.tabManager.getActiveContext().setNote(note.noteId);
@ -140,9 +140,9 @@ export default class QuickSearchWidget extends BasicWidget {
$showInFullButton.on('click', () => this.showInFullSearch());
utils.bindElShortcut($showInFullButton, 'return', () => this.showInFullSearch());
shortcutService.bindElShortcut($showInFullButton, 'return', () => this.showInFullSearch());
utils.bindElShortcut(this.$dropdownMenu.find('.dropdown-item:first'), 'up', () => this.$searchString.focus());
shortcutService.bindElShortcut(this.$dropdownMenu.find('.dropdown-item:first'), 'up', () => this.$searchString.focus());
this.$dropdownToggle.dropdown('update');
}

View File

@ -1,7 +1,7 @@
import AbstractSearchOption from "./abstract_search_option.js";
import utils from "../../services/utils.js";
import SpacedUpdate from "../../services/spaced_update.js";
import server from "../../services/server.js";
import shortcutService from "../../services/shortcuts.js";
const TPL = `
<tr>
@ -45,7 +45,7 @@ export default class SearchString extends AbstractSearchOption {
this.$searchString = $option.find('.search-string');
this.$searchString.on('input', () => this.spacedUpdate.scheduleUpdate());
utils.bindElShortcut(this.$searchString, 'return', async () => {
shortcutService.bindElShortcut(this.$searchString, 'return', async () => {
// this also in effect disallows new lines in query string.
// on one hand this makes sense since search string is a label
// on the other hand it could be nice for structuring long search string. It's probably a niche case though.