tree keyboard shortcuts

This commit is contained in:
zadam 2019-11-21 22:24:07 +01:00
parent 0e5028acd3
commit 465c3b87a7
4 changed files with 258 additions and 171 deletions

View file

@ -52,13 +52,7 @@ function setActionHandler(actionName, handler) {
}
async function triggerAction(actionName) {
await keyboardActionsLoaded;
const action = keyboardActionRepo[actionName];
if (!action) {
throw new Error(`Cannot find action ${actionName}`);
}
const action = getAction(actionName);
if (!action.handler) {
throw new Error(`Action ${actionName} has no handler`);
@ -67,7 +61,20 @@ async function triggerAction(actionName) {
await action.handler();
}
async function getAction(actionName) {
await keyboardActionsLoaded;
const action = keyboardActionRepo[actionName];
if (!action) {
throw new Error(`Cannot find action ${actionName}`);
}
return action;
}
export default {
setActionHandler,
triggerAction
triggerAction,
getAction
};

View file

@ -9,9 +9,7 @@ import server from './server.js';
import treeCache from './tree_cache.js';
import toastService from "./toast.js";
import treeBuilder from "./tree_builder.js";
import treeKeyBindings from "./tree_keybindings.js";
import Branch from '../entities/branch.js';
import NoteShort from '../entities/note_short.js';
import treeKeyBindingService from "./tree_keybindings.js";
import hoistedNoteService from '../services/hoisted_note.js';
import optionsService from "../services/options.js";
import TreeContextMenu from "./tree_context_menu.js";
@ -430,7 +428,7 @@ async function treeInitialized() {
setFrontendAsLoaded();
}
function initFancyTree(tree) {
async function initFancyTree(tree) {
utils.assertArguments(tree);
$tree.fancytree({
@ -473,7 +471,7 @@ function initFancyTree(tree) {
collapse: (event, data) => setExpandedToServer(data.node.data.branchId, false),
init: (event, data) => treeInitialized(), // don't collapse to short form
hotkeys: {
keydown: treeKeyBindings
keydown: await treeKeyBindingService.getKeyboardBindings()
},
dnd5: dragAndDropSetup,
lazyLoad: function(event, data) {
@ -754,7 +752,7 @@ async function sortAlphabetically(noteId) {
async function showTree() {
const tree = await loadTree();
initFancyTree(tree);
await initFancyTree(tree);
}
ws.subscribeToMessages(message => {

View file

@ -5,148 +5,9 @@ import hoistedNoteService from "./hoisted_note.js";
import clipboard from "./clipboard.js";
import treeCache from "./tree_cache.js";
import searchNoteService from "./search_notes.js";
import keyboardActionService from "./keyboard_actions.js";
const keyBindings = {
"del": node => {
treeChangesService.deleteNodes(treeService.getSelectedOrActiveNodes(node));
},
"ctrl+up": node => {
const beforeNode = node.getPrevSibling();
if (beforeNode !== null) {
treeChangesService.moveBeforeNode([node], beforeNode);
}
return false;
},
"ctrl+down": node => {
const afterNode = node.getNextSibling();
if (afterNode !== null) {
treeChangesService.moveAfterNode([node], afterNode);
}
return false;
},
"ctrl+left": node => {
treeChangesService.moveNodeUpInHierarchy(node);
return false;
},
"ctrl+right": node => {
const toNode = node.getPrevSibling();
if (toNode !== null) {
treeChangesService.moveToNode([node], toNode);
}
return false;
},
"shift+up": () => {
const node = treeService.getFocusedNode();
if (!node) {
return;
}
if (node.isActive()) {
node.setSelected(true);
}
node.navigate($.ui.keyCode.UP, false).then(() => {
const currentNode = treeService.getFocusedNode();
if (currentNode.isSelected()) {
node.setSelected(false);
}
currentNode.setSelected(true);
});
return false;
},
"shift+down": () => {
const node = treeService.getFocusedNode();
if (!node) {
return;
}
if (node.isActive()) {
node.setSelected(true);
}
node.navigate($.ui.keyCode.DOWN, false).then(() => {
const currentNode = treeService.getFocusedNode();
if (currentNode.isSelected()) {
node.setSelected(false);
}
currentNode.setSelected(true);
});
return false;
},
"f2": async node => {
const editBranchPrefixDialog = await import("../dialogs/branch_prefix.js");
editBranchPrefixDialog.showDialog(node);
},
"alt+-": node => {
treeService.collapseTree(node);
},
"alt+s": node => {
treeService.sortAlphabetically(node.data.noteId);
return false;
},
"ctrl+a": node => {
for (const child of node.getParent().getChildren()) {
child.setSelected(true);
}
return false;
},
"ctrl+c": node => {
clipboard.copy(treeService.getSelectedOrActiveNodes(node));
return false;
},
"ctrl+x": node => {
clipboard.cut(treeService.getSelectedOrActiveNodes(node));
return false;
},
"ctrl+v": node => {
clipboard.pasteInto(node);
return false;
},
"return": node => {
noteDetailService.focusOnTitle();
return false;
},
"backspace": async node => {
if (!await hoistedNoteService.isRootNode(node)) {
node.getParent().setActive().then(treeService.clearSelectedNodes);
}
},
"ctrl+h": node => {
hoistedNoteService.getHoistedNoteId().then(async hoistedNoteId => {
if (node.data.noteId === hoistedNoteId) {
hoistedNoteService.unhoist();
}
else {
const note = await treeCache.getNote(node.data.noteId);
if (note.type !== 'search') {
hoistedNoteService.setHoistedNoteId(node.data.noteId);
}
}
});
return false;
},
const fixedKeyBindings = {
// code below shouldn't be necessary normally, however there's some problem with interaction with context menu plugin
// after opening context menu, standard shortcuts don't work, but they are detected here
// so we essentially takeover the standard handling with our implementation.
@ -169,12 +30,173 @@ const keyBindings = {
node.navigate($.ui.keyCode.DOWN, true).then(treeService.clearSelectedNodes);
return false;
}
};
const templates = {
"DeleteNote": node => {
treeChangesService.deleteNodes(treeService.getSelectedOrActiveNodes(node));
},
"ctrl+shift+s": node => {
"MoveNoteUp": node => {
const beforeNode = node.getPrevSibling();
if (beforeNode !== null) {
treeChangesService.moveBeforeNode([node], beforeNode);
}
return false;
},
"MoveNoteDown": node => {
const afterNode = node.getNextSibling();
if (afterNode !== null) {
treeChangesService.moveAfterNode([node], afterNode);
}
return false;
},
"MoveNoteUpInHierarchy": node => {
treeChangesService.moveNodeUpInHierarchy(node);
return false;
},
"MoveNoteDownInHierarchy": node => {
const toNode = node.getPrevSibling();
if (toNode !== null) {
treeChangesService.moveToNode([node], toNode);
}
return false;
},
"AddNoteAboveToSelection": () => {
const node = treeService.getFocusedNode();
if (!node) {
return;
}
if (node.isActive()) {
node.setSelected(true);
}
node.navigate($.ui.keyCode.UP, false).then(() => {
const currentNode = treeService.getFocusedNode();
if (currentNode.isSelected()) {
node.setSelected(false);
}
currentNode.setSelected(true);
});
return false;
},
"AddNoteBelowToSelection": () => {
const node = treeService.getFocusedNode();
if (!node) {
return;
}
if (node.isActive()) {
node.setSelected(true);
}
node.navigate($.ui.keyCode.DOWN, false).then(() => {
const currentNode = treeService.getFocusedNode();
if (currentNode.isSelected()) {
node.setSelected(false);
}
currentNode.setSelected(true);
});
return false;
},
// TODO: this shouldn't be tree specific shortcut
"EditBranchPrefix": async node => {
const editBranchPrefixDialog = await import("../dialogs/branch_prefix.js");
editBranchPrefixDialog.showDialog(node);
},
"CollapseSubtree": node => {
treeService.collapseTree(node);
},
"SortChildNotes": node => {
treeService.sortAlphabetically(node.data.noteId);
return false;
},
"SelectAllNotesInParent": node => {
for (const child of node.getParent().getChildren()) {
child.setSelected(true);
}
return false;
},
"CopyNotesToClipboard": node => {
clipboard.copy(treeService.getSelectedOrActiveNodes(node));
return false;
},
"CutNotesToClipboard": node => {
clipboard.cut(treeService.getSelectedOrActiveNodes(node));
return false;
},
"PasteNotesFromClipboard": node => {
clipboard.pasteInto(node);
return false;
},
"EditNoteTitle": node => {
noteDetailService.focusOnTitle();
return false;
},
"ActivateParentNote": async node => {
if (!await hoistedNoteService.isRootNode(node)) {
node.getParent().setActive().then(treeService.clearSelectedNodes);
}
},
// TODO: this should probably be app-global shortcut
"ToggleNoteHoisting": node => {
hoistedNoteService.getHoistedNoteId().then(async hoistedNoteId => {
if (node.data.noteId === hoistedNoteId) {
hoistedNoteService.unhoist();
}
else {
const note = await treeCache.getNote(node.data.noteId);
if (note.type !== 'search') {
hoistedNoteService.setHoistedNoteId(node.data.noteId);
}
}
});
return false;
},
"SearchInSubtree": node => {
searchNoteService.searchInSubtree(node.data.noteId);
return false;
}
};
export default keyBindings;
async function getKeyboardBindings() {
const bindings = Object.assign({}, fixedKeyBindings);
for (const actionName in templates) {
const action = await keyboardActionService.getAction(actionName);
for (const shortcut of action.effectiveShortcuts || []) {
bindings[shortcut] = templates[actionName];
}
}
return bindings;
}
export default {
getKeyboardBindings
};

View file

@ -114,19 +114,89 @@ const DEFAULT_KEYBOARD_ACTIONS = [
defaultShortcuts: ["CommandOrControl+return"]
},
{
actionName: "ClipboardCopy",
actionName: "DeleteNote",
defaultShortcuts: ["Delete"],
description: "Delete note"
},
{
actionName: "MoveNoteUp",
defaultShortcuts: ["Ctrl+Up"],
description: "Move note up"
},
{
actionName: "MoveNoteDown",
defaultShortcuts: ["Ctrl+Down"],
description: "Move note down"
},
{
actionName: "MoveNoteUpInHierarchy",
defaultShortcuts: ["Ctrl+Left"],
description: "Move note up in hierarchy"
},
{
actionName: "MoveNoteDownInHierarchy",
defaultShortcuts: ["Ctrl+Right"],
description: "Move note down in hierarchy"
},
{
actionName: "AddNoteAboveToSelection",
defaultShortcuts: ["Shift+Up"],
description: "Add note above to the selection"
},
{
actionName: "AddNoteBelowToSelection",
defaultShortcuts: ["Shift+Down"],
description: "Add note above to the selection"
},
{
actionName: "CopyNotesToClipboard",
defaultShortcuts: ["CommandOrControl+C"],
description: "Copy selected notes to the clipboard"
},
{
actionName: "ClipboardPaste",
actionName: "PasteNotesFromClipboard",
defaultShortcuts: ["CommandOrControl+V"],
description: "Paste notes from the clipboard into active note"
},
{
actionName: "ClipboardCut",
actionName: "CutNotesToClipboard",
defaultShortcuts: ["CommandOrControl+X"],
description: "Copy selected notes to the clipboard"
description: "Cut selected notes to the clipboard"
},
{
actionName: "EditBranchPrefix",
defaultShortcuts: ["F2"],
description: "Show Edit branch prefix dialog"
},
{
actionName: "CollapseSubtree",
defaultShortcuts: ["Alt+-"],
description: "Collapses subtree of current note"
},
{
actionName: "SortChildNotes",
defaultShortcuts: ["Alt+s"],
description: "Sort child notes"
},
{
actionName: "ActivateParentNote",
defaultShortcuts: ["Backspace"],
description: "Activates parent note of currently active note"
},
{
actionName: "ToggleNoteHoisting",
defaultShortcuts: ["Alt+h"],
description: "Toggles note hoisting of active note"
},
{
actionName: "SearchInSubtree",
defaultShortcuts: ["CommandOrControl+Shift+S"],
description: "Search for notes in the active note's subtree"
},
{
actionName: "EditNoteTitle",
defaultShortcuts: ["return"],
description: "Edit active note title"
},
{
actionName: "SelectAllNotesInParent",
@ -136,16 +206,6 @@ const DEFAULT_KEYBOARD_ACTIONS = [
{
separator: "Text note operations"
},
{
actionName: "Undo",
defaultShortcuts: ["CommandOrControl+Z"],
description: "Undo last text operation (applicable on MacOS only)"
},
{
actionName: "Redo",
defaultShortcuts: ["CommandOrControl+Y"],
description: "Undo last text operation (applicable on MacOS only)"
},
{
actionName: "AddLinkToText",
defaultShortcuts: ["CommandOrControl+L"],