From 16ecb1a9cb0838ba3629d47c45681008f613dd7c Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Sun, 4 Dec 2016 22:59:43 +0000 Subject: [PATCH] Overhaul keyboard handling and introduce editor actions --- Gruntfile.js | 20 ++- editor/js/keymap.json | 38 +++++ editor/js/main.js | 46 +++--- editor/js/ui/actions.js | 35 +++++ editor/js/ui/clipboard.js | 10 +- editor/js/ui/common/menu.js | 45 +++--- editor/js/ui/common/tabs.js | 14 ++ editor/js/ui/editor.js | 15 +- editor/js/ui/keyboard.js | 260 ++++++++++++++++++++++++++------- editor/js/ui/library.js | 3 + editor/js/ui/palette-editor.js | 6 +- editor/js/ui/search.js | 10 +- editor/js/ui/sidebar.js | 10 +- editor/js/ui/subflow.js | 2 + editor/js/ui/tab-config.js | 5 +- editor/js/ui/tab-info.js | 2 +- editor/js/ui/typeSearch.js | 4 +- editor/js/ui/view.js | 115 +++++++++------ editor/js/ui/workspaces.js | 15 +- nodes/core/core/58-debug.html | 5 +- 20 files changed, 490 insertions(+), 170 deletions(-) create mode 100644 editor/js/keymap.json create mode 100644 editor/js/ui/actions.js diff --git a/Gruntfile.js b/Gruntfile.js index bf6994aca..f9531d1c2 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -117,6 +117,7 @@ module.exports = function(grunt) { "editor/js/ui/common/tabs.js", "editor/js/ui/common/typedInput.js", "editor/js/ui/utils.js", + "editor/js/ui/actions.js", "editor/js/ui/deploy.js", "editor/js/ui/keyboard.js", "editor/js/ui/workspaces.js", @@ -194,6 +195,11 @@ module.exports = function(grunt) { 'red/api/locales/en-US/editor.json', 'red/runtime/locales/en-US/runtime.json' ] + }, + keymaps: { + src: [ + 'editor/js/keymap.json' + ] } }, attachCopyright: { @@ -230,7 +236,7 @@ module.exports = function(grunt) { files: [ 'editor/js/**/*.js' ], - tasks: ['concat','uglify','attachCopyright:js'] + tasks: ['copy:build','concat','uglify','attachCopyright:js'] }, sass: { files: [ @@ -246,6 +252,12 @@ module.exports = function(grunt) { ], tasks: ['jsonlint:messages'] }, + keymaps: { + files: [ + 'editor/js/keymap.json' + ], + tasks: ['jsonlint:keymaps','copy:build'] + }, misc: { files: [ 'CHANGELOG.md' @@ -284,6 +296,10 @@ module.exports = function(grunt) { src: 'editor/js/main.js', dest: 'public/red/main.js' }, + { + src: 'editor/js/keymap.json', + dest: 'public/red/keymap.json' + }, { cwd: 'editor/images', src: '**', @@ -443,7 +459,7 @@ module.exports = function(grunt) { grunt.registerTask('build', 'Builds editor content', - ['clean:build','concat:build','concat:vendor','copy:build','uglify:build','sass:build','jsonlint:messages','attachCopyright']); + ['clean:build','jsonlint','concat:build','concat:vendor','copy:build','uglify:build','sass:build','attachCopyright']); grunt.registerTask('dev', 'Developer mode: run node-red, watch for source changes and build/restart', diff --git a/editor/js/keymap.json b/editor/js/keymap.json new file mode 100644 index 000000000..2923e5e97 --- /dev/null +++ b/editor/js/keymap.json @@ -0,0 +1,38 @@ +{ + "*": { + "ctrl-shift-p":"core:manage-palette", + "ctrl-f": "core:search", + "ctrl-=": "core:zoom-in", + "ctrl--": "core:zoom-out", + "ctrl-0": "core:zoom-reset", + "ctrl-enter": "core:confirm-edit-tray", + "ctrl-escape": "core:cancel-edit-tray", + "ctrl-g i": "core:show-info-tab", + "ctrl-g d": "core:show-debug-tab", + "ctrl-g c": "core:show-config-tab" + }, + "workspace": { + "ctrl-e": "core:export", + "ctrl-i": "core:import", + "backspace": "core:delete", + "delete": "core:delete", + "enter": "core:edit", + "ctrl-c": "core:copy", + "ctrl-x": "core:cut", + "ctrl-v": "core:paste", + "ctrl-z": "core:undo", + "ctrl-a": "core:select-all", + "shift-?": "core:show-help", + "ctrl-space": "core:toggle-sidebar", + "up": "core:move-selection-up", + "right": "core:move-selection-right", + "down": "core:move-selection-down", + "left": "core:move-selection-left", + "shift-up": "core:step-selection-up", + "shift-right": "core:step-selection-right", + "shift-down": "core:step-selection-down", + "shift-left": "core:step-selection-left", + "ctrl-shift-j": "core:show-previous-tab", + "ctrl-shift-k": "core:show-next-tab" + } +} diff --git a/editor/js/main.js b/editor/js/main.js index d2e438fed..1038facda 100644 --- a/editor/js/main.js +++ b/editor/js/main.js @@ -15,7 +15,6 @@ **/ (function() { - function loadNodeList() { $.ajax({ headers: { @@ -170,12 +169,11 @@ } function loadEditor() { - var menuOptions = []; menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[ - {id:"menu-item-view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:RED.view.toggleShowGrid}, - {id:"menu-item-view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:RED.view.toggleSnapGrid}, - {id:"menu-item-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:toggleStatus, selected: true}, + {id:"menu-item-view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:"core:toggle-show-grid"}, + {id:"menu-item-view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:"core:toggle-snap-grid"}, + {id:"menu-item-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:"core:toggle-status", selected: true}, null, // {id:"menu-item-bidi",label:RED._("menu.label.view.textDir"),options:[ // {id:"menu-item-bidi-default",toggle:"text-direction",label:RED._("menu.label.view.defaultDir"),selected: true, onselect:function(s) { if(s){RED.text.bidi.setTextDirection("")}}}, @@ -184,48 +182,46 @@ // {id:"menu-item-bidi-auto",toggle:"text-direction",label:RED._("menu.label.view.auto"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("auto")}}} // ]}, // null, - {id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:RED.sidebar.toggleSidebar, selected: true} + {id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:"core:toggle-sidebar", selected: true} ]}); menuOptions.push(null); menuOptions.push({id:"menu-item-import",label:RED._("menu.label.import"),options:[ - {id:"menu-item-import-clipboard",label:RED._("menu.label.clipboard"),onselect:RED.clipboard.import}, + {id:"menu-item-import-clipboard",label:RED._("menu.label.clipboard"),onselect:"core:import"}, {id:"menu-item-import-library",label:RED._("menu.label.library"),options:[]} ]}); menuOptions.push({id:"menu-item-export",label:RED._("menu.label.export"),disabled:true,options:[ - {id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),disabled:true,onselect:RED.clipboard.export}, - {id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:RED.library.export} + {id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),disabled:true,onselect:"core:export"}, + {id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:"core:library-export"} ]}); menuOptions.push(null); - menuOptions.push({id:"menu-item-search",label:RED._("menu.label.search"),onselect:RED.search.show}); + menuOptions.push({id:"menu-item-search",label:RED._("menu.label.search"),onselect:"core:search"}); menuOptions.push(null); - menuOptions.push({id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:function() {}}); + menuOptions.push({id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:"core:show-config-tab"}); menuOptions.push({id:"menu-item-workspace",label:RED._("menu.label.flows"),options:[ - {id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:RED.workspaces.add}, - {id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:RED.workspaces.edit}, - {id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:RED.workspaces.remove} + {id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:"core:add-flow"}, + {id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:"core:edit-flow"}, + {id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:"core:remove-flow"} ]}); menuOptions.push({id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [ - {id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:RED.subflow.createSubflow}, - {id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:RED.subflow.convertToSubflow}, + {id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:"core:create-subflow"}, + {id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:"core:convert-to-subflow"}, ]}); menuOptions.push(null); if (RED.settings.theme('palette.editable') !== false) { RED.palette.editor.init(); - menuOptions.push({id:"menu-item-edit-palette",label:RED._("menu.label.editPalette"),onselect:RED.palette.editor.show}); + menuOptions.push({id:"menu-item-edit-palette",label:RED._("menu.label.editPalette"),onselect:"core:manage-palette"}); menuOptions.push(null); } - menuOptions.push({id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:RED.keyboard.showHelp}); + menuOptions.push({id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:"core:show-help"}); menuOptions.push({id:"menu-item-help", label: RED.settings.theme("menu.menu-item-help.label","Node-RED website"), href: RED.settings.theme("menu.menu-item-help.url","http://nodered.org/docs") }); - menuOptions.push({id:"menu-item-node-red-version", label:"v"+RED.settings.version, onselect: showAbout }); + menuOptions.push({id:"menu-item-node-red-version", label:"v"+RED.settings.version, onselect: "core:show-about" }); - RED.menu.init({id:"btn-sidemenu",options: menuOptions}); RED.user.init(); - RED.library.init(); RED.palette.init(); RED.sidebar.init(); @@ -235,15 +231,21 @@ RED.search.init(); RED.view.init(); RED.editor.init(); + RED.keyboard.init(); + + RED.menu.init({id:"btn-sidemenu",options: menuOptions}); + RED.deploy.init(RED.settings.theme("deployButton",null)); - RED.keyboard.add("workspace", /* ? */ 191,{shift:true},function() {RED.keyboard.showHelp();d3.event.preventDefault();}); + RED.actions.add("core:show-about", showAbout); + RED.comms.connect(); $("#main-container").show(); $(".header-toolbar").show(); + loadNodeList(); } diff --git a/editor/js/ui/actions.js b/editor/js/ui/actions.js new file mode 100644 index 000000000..78b9a4c99 --- /dev/null +++ b/editor/js/ui/actions.js @@ -0,0 +1,35 @@ +RED.actions = (function() { + var actions = { + + } + + function addAction(name,handler) { + actions[name] = handler; + } + function removeAction(name) { + delete actions[name]; + } + function getAction(name) { + return actions[name]; + } + function invokeAction(name) { + if (actions.hasOwnProperty(name)) { + actions[name](); + } + } + function listActions() { + var result = []; + Object.keys(actions).forEach(function(action) { + var shortcut = RED.keyboard.getShortcut(action); + result.push({id:action,scope:shortcut?shortcut.scope:undefined,key:shortcut?shortcut.key:undefined}) + }) + return result; + } + return { + add: addAction, + remove: removeAction, + get: getAction, + invoke: invokeAction, + list: listActions + } +})(); diff --git a/editor/js/ui/clipboard.js b/editor/js/ui/clipboard.js index b648e19ff..b446b40cd 100644 --- a/editor/js/ui/clipboard.js +++ b/editor/js/ui/clipboard.js @@ -257,7 +257,7 @@ RED.clipboard = (function() { function hideDropTarget() { $("#dropTarget").hide(); - RED.keyboard.remove(/* ESCAPE */ 27); + RED.keyboard.remove("escape"); } return { @@ -274,13 +274,15 @@ RED.clipboard = (function() { RED.menu.setDisabled("menu-item-export-library",false); } }); - RED.keyboard.add("workspace", /* e */ 69,{ctrl:true},function(){exportNodes();d3.event.preventDefault();}); - RED.keyboard.add("workspace", /* i */ 73,{ctrl:true},function(){importNodes();d3.event.preventDefault();}); + + RED.actions.add("core:export",exportNodes); + RED.actions.add("core:import",importNodes); + $('#chart').on("dragenter",function(event) { if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) { $("#dropTarget").css({display:'table'}); - RED.keyboard.add("*", /* ESCAPE */ 27,hideDropTarget); + RED.keyboard.add("*", "escape" ,hideDropTarget); } }); diff --git a/editor/js/ui/common/menu.js b/editor/js/ui/common/menu.js index b2a7ee6c6..79fc3a286 100644 --- a/editor/js/ui/common/menu.js +++ b/editor/js/ui/common/menu.js @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ - - - RED.menu = (function() { var menuItems = {}; @@ -34,17 +31,17 @@ RED.menu = (function() { var savedStateActive = isSavedStateActive(opt.id); if (savedStateActive) { link.addClass("active"); - opt.onselect.call(opt, true); + triggerAction(opt.id,true); } else if (savedStateActive === false) { link.removeClass("active"); - opt.onselect.call(opt, false); + triggerAction(opt.id,false); } else if (opt.hasOwnProperty("selected")) { if (opt.selected) { link.addClass("active"); } else { link.removeClass("active"); } - opt.onselect.call(opt, opt.selected); + triggerAction(opt.id,opt.selected); } } @@ -107,10 +104,12 @@ RED.menu = (function() { setSelected(opt.id, !selected); } } else { - opt.onselect.call(opt); + triggerAction(opt.id); } }); - setInitialState(); + if (opt.toggle) { + setInitialState(); + } } else if (opt.href) { link.attr("target","_blank").attr("href",opt.href); } else if (!opt.options) { @@ -164,6 +163,19 @@ RED.menu = (function() { } } + function triggerAction(id, args) { + var opt = menuItems[id]; + var callback = opt.onselect; + if (typeof opt.onselect === 'string') { + callback = RED.actions.get(opt.onselect); + } + if (callback) { + callback.call(opt,args); + } else { + console.log("No callback for",id,opt.onselect); + } + } + function isSavedStateActive(id) { return RED.settings.get("menu-" + id); } @@ -187,11 +199,15 @@ RED.menu = (function() { $("#"+id).removeClass("active"); } if (opt && opt.onselect) { - opt.onselect.call(opt,state); + triggerAction(opt.id,state); } setSavedState(id, state); } + function toggleSelected(id) { + setSelected(id,!isSelected(id)); + } + function setDisabled(id,state) { if (state) { $("#"+id).parent().addClass("disabled"); @@ -231,16 +247,6 @@ RED.menu = (function() { var opt = menuItems[id]; if (opt) { opt.onselect = action; - // $("#"+id).click(function() { - // if ($(this).parent().hasClass("disabled")) { - // return; - // } - // if (menuItems[id].toggle) { - // setSelected(id,!isSelected(id)); - // } else { - // menuItems[id].onselect.call(menuItems[id]); - // } - // }); } } @@ -248,6 +254,7 @@ RED.menu = (function() { init: createMenu, setSelected: setSelected, isSelected: isSelected, + toggleSelected: toggleSelected, setDisabled: setDisabled, addItem: addItem, removeItem: removeItem, diff --git a/editor/js/ui/common/tabs.js b/editor/js/ui/common/tabs.js index 783db3d41..3f96d074b 100644 --- a/editor/js/ui/common/tabs.js +++ b/editor/js/ui/common/tabs.js @@ -126,6 +126,18 @@ RED.tabs = (function() { },100); } } + function activatePreviousTab() { + var previous = ul.find("li.active").prev(); + if (previous.length > 0) { + activateTab(previous.find("a")); + } + } + function activateNextTab() { + var next = ul.find("li.active").next(); + if (next.length > 0) { + activateTab(next.find("a")); + } + } function updateTabWidths() { var tabs = ul.find("li.red-ui-tab"); @@ -303,6 +315,8 @@ RED.tabs = (function() { }, removeTab: removeTab, activateTab: activateTab, + nextTab: activateNextTab, + previousTab: activatePreviousTab, resize: updateTabWidths, count: function() { return ul.find("li.red-ui-tab").size(); diff --git a/editor/js/ui/editor.js b/editor/js/ui/editor.js index 915c29649..90a55e1d2 100644 --- a/editor/js/ui/editor.js +++ b/editor/js/ui/editor.js @@ -1532,14 +1532,13 @@ RED.editor = (function() { return { init: function() { RED.tray.init(); - $(window).on('keydown', function(evt) { - if (evt.keyCode === $.ui.keyCode.ESCAPE && (evt.metaKey || evt.ctrlKey)) { - $("#node-dialog-cancel").click(); - $("#node-config-dialog-cancel").click(); - } else if (evt.keyCode === $.ui.keyCode.ENTER && (evt.metaKey || evt.ctrlKey)) { - $("#node-dialog-ok").click(); - $("#node-config-dialog-ok").click(); - } + RED.actions.add("core:confirm-edit-tray", function() { + $("#node-dialog-ok").click(); + $("#node-config-dialog-ok").click(); + }); + RED.actions.add("core:cancel-edit-tray", function() { + $("#node-dialog-cancel").click(); + $("#node-config-dialog-cancel").click(); }); }, edit: showEditDialog, diff --git a/editor/js/ui/keyboard.js b/editor/js/ui/keyboard.js index e70e7d731..a7f815b0f 100644 --- a/editor/js/ui/keyboard.js +++ b/editor/js/ui/keyboard.js @@ -16,9 +16,98 @@ RED.keyboard = (function() { var handlers = {}; + var partialState; + + var keyMap = { + "left":37, + "up":38, + "right":39, + "down":40, + "escape":27, + "enter": 13, + "backspace": 8, + "delete": 46, + "space": 32, + ";":186, + "=":187, + ",":188, + "-":189, + ".":190, + "/":191, + "\\":220, + "'":222, + "?":191 // <- QWERTY specific + } + var metaKeyCodes = { + 16:true, + 17:true, + 18: true, + 91:true, + 93: true + } + var actionToKeyMap = {} + + // FF generates some different keycodes because reasons. + var firefoxKeyCodeMap = { + 59:186, + 61:187, + 173:189 + } + + function init() { + $.getJSON("red/keymap.json",function(data) { + for (var scope in data) { + if (data.hasOwnProperty(scope)) { + var keys = data[scope]; + for (var key in keys) { + if (keys.hasOwnProperty(key)) { + addHandler(scope,key,keys[key]); + } + } + } + } + }) + RED.actions.add("core:show-help", showKeyboardHelp); + + } + function parseKeySpecifier(key) { + var parts = key.toLowerCase().split("-"); + var modifiers = {}; + var keycode; + var blank = 0; + for (var i=0;i 1) { + return null; + } else { + keycode = parts[i].toUpperCase().charCodeAt(0); + } + break; + } + } + return [keycode,modifiers]; + } function resolveKeyEvent(evt) { - var slot = handlers; + var slot = partialState||handlers; if (evt.ctrlKey || evt.metaKey) { slot = slot.ctrl; } @@ -28,9 +117,19 @@ RED.keyboard = (function() { if (slot && evt.altKey) { slot = slot.alt; } - if (slot && slot[evt.keyCode]) { - var handler = slot[evt.keyCode]; - if (handler.scope && handler.scope !== "*") { + var keyCode = firefoxKeyCodeMap[evt.keyCode] || evt.keyCode; + if (slot && slot[keyCode]) { + var handler = slot[keyCode]; + if (!handler.scope) { + if (partialState) { + partialState = null; + return resolveKeyEvent(evt); + } else { + partialState = handler; + evt.preventDefault(); + return null; + } + } else if (handler.scope && handler.scope !== "*") { var target = evt.target; while (target.nodeName !== 'BODY' && target.id !== handler.scope) { target = target.parentElement; @@ -39,66 +138,124 @@ RED.keyboard = (function() { handler = null; } } + partialState = null; return handler; + } else if (partialState) { + partialState = null; + return resolveKeyEvent(evt); } } d3.select(window).on("keydown",function() { + if (metaKeyCodes[d3.event.keyCode]) { + return; + } var handler = resolveKeyEvent(d3.event); if (handler && handler.ondown) { - handler.ondown(); - } - }); - d3.select(window).on("keyup",function() { - var handler = resolveKeyEvent(d3.event); - if (handler && handler.onup) { - handler.onup(); + if (typeof handler.ondown === "string") { + RED.actions.invoke(handler.ondown); + } else { + handler.ondown(); + } + d3.event.preventDefault(); } }); - function addHandler(scope,key,modifiers,ondown,onup) { + function addHandler(scope,key,modifiers,ondown) { var mod = modifiers; var cbdown = ondown; - var cbup = onup; - if (typeof modifiers == "function") { + if (typeof modifiers == "function" || typeof modifiers === "string") { mod = {}; cbdown = modifiers; - cbup = ondown; + } + var keys = []; + var i=0; + if (typeof key === 'string') { + if (typeof cbdown === 'string') { + actionToKeyMap[cbdown] = {scope:scope,key:key}; + } + var parts = key.split(" "); + for (i=0;i'; + function showKeyboardHelp() { if (!RED.settings.theme("menu.menu-item-keyboard-shortcuts",true)) { return; @@ -107,30 +264,30 @@ RED.keyboard = (function() { dialog = $('
'+ '
'+ ''+ - ''+ + ''+ ''+ - ''+ + ''+ ''+ ''+ ''+ ''+ - ''+ - ''+ + ''+ + ''+ '
Ctrl/⌘ + a'+RED._("keyboard.selectAll")+'
'+cmdCtrlKey+' + a'+RED._("keyboard.selectAll")+'
Shift + Click'+RED._("keyboard.selectAllConnected")+'
Ctrl/⌘ + Click'+RED._("keyboard.addRemoveNode")+'
'+cmdCtrlKey+' + Click'+RED._("keyboard.addRemoveNode")+'
 
Enter'+RED._("keyboard.editSelected")+'
Delete / Backspace'+RED._("keyboard.deleteSelected")+'
 
Ctrl/⌘ + i'+RED._("keyboard.importNode")+'
Ctrl/⌘ + e'+RED._("keyboard.exportNode")+'
'+cmdCtrlKey+' + i'+RED._("keyboard.importNode")+'
'+cmdCtrlKey+' + e'+RED._("keyboard.exportNode")+'
'+ '
'+ '
'+ ''+ - ''+ - ''+ - ''+ + ''+ + ''+ + ''+ ''+ ''+ ''+ ''+ - ''+ - ''+ - ''+ - ''+ + ''+ + ''+ + ''+ + ''+ '
Ctrl/⌘ + Space'+RED._("keyboard.toggleSidebar")+'
Ctrl/⌘ + .'+RED._("keyboard.searchBox")+'
Ctrl/⌘ + Shift + p'+RED._("keyboard.managePalette")+'
'+cmdCtrlKey+' + Space'+RED._("keyboard.toggleSidebar")+'
'+cmdCtrlKey+' + f'+RED._("keyboard.searchBox")+'
'+cmdCtrlKey+' + Shift + p'+RED._("keyboard.managePalette")+'
 
'+RED._("keyboard.nudgeNode")+'
Shift + '+RED._("keyboard.moveNode")+'
 
Ctrl/⌘ + c'+RED._("keyboard.copyNode")+'
Ctrl/⌘ + x'+RED._("keyboard.cutNode")+'
Ctrl/⌘ + v'+RED._("keyboard.pasteNode")+'
Ctrl/⌘ + z'+RED._("keyboard.undoChange")+'
'+cmdCtrlKey+' + c'+RED._("keyboard.copyNode")+'
'+cmdCtrlKey+' + x'+RED._("keyboard.cutNode")+'
'+cmdCtrlKey+' + v'+RED._("keyboard.pasteNode")+'
'+cmdCtrlKey+' + z'+RED._("keyboard.undoChange")+'
'+ '
'+ '
') @@ -148,9 +305,12 @@ RED.keyboard = (function() { } return { + init: init, add: addHandler, remove: removeHandler, - showHelp: showKeyboardHelp + getShortcut: function(actionName) { + return actionToKeyMap[actionName]; + } } })(); diff --git a/editor/js/ui/library.js b/editor/js/ui/library.js index e9435317b..4291bf4a1 100644 --- a/editor/js/ui/library.js +++ b/editor/js/ui/library.js @@ -410,6 +410,9 @@ RED.library = (function() { return { init: function() { + + RED.actions.add("core:library-export",exportFlow); + RED.events.on("view:selection-changed",function(selection) { if (!selection.nodes) { RED.menu.setDisabled("menu-item-export",true); diff --git a/editor/js/ui/palette-editor.js b/editor/js/ui/palette-editor.js index 9caf1024d..d5a537699 100644 --- a/editor/js/ui/palette-editor.js +++ b/editor/js/ui/palette-editor.js @@ -302,10 +302,10 @@ RED.palette.editor = (function() { filterInput.focus(); },250); RED.events.emit("palette-editor:open"); - RED.keyboard.add("*",/* ESCAPE */ 27,function(){hidePaletteEditor();d3.event.preventDefault();}); + RED.keyboard.add("*","escape",function(){hidePaletteEditor()}); } function hidePaletteEditor() { - RED.keyboard.remove("*"); + RED.keyboard.remove("escape"); $("#main-container").removeClass("palette-expanded"); $("#header-shade").hide(); $("#editor-shade").hide(); @@ -425,7 +425,7 @@ RED.palette.editor = (function() { RED.events.on("type-search:open",function() { disabled = true; }); RED.events.on("type-search:close",function() { disabled = false; }); - RED.keyboard.add("*", /* p */ 80,{shift:true,ctrl:true},function() {RED.palette.editor.show();d3.event.preventDefault();}); + RED.actions.add("core:manage-palette",RED.palette.editor.show); editorTabs = RED.tabs.create({ id:"palette-editor-tabs", diff --git a/editor/js/ui/search.js b/editor/js/ui/search.js index 032fc69b7..9f30dbd4f 100644 --- a/editor/js/ui/search.js +++ b/editor/js/ui/search.js @@ -238,8 +238,11 @@ RED.search = (function() { } function show() { + if (disabled) { + return; + } if (!visible) { - RED.keyboard.add("*",/* ESCAPE */ 27,function(){hide();d3.event.preventDefault();}); + RED.keyboard.add("*","escape",function(){hide()}); $("#header-shade").show(); $("#editor-shade").show(); $("#palette-shade").show(); @@ -257,7 +260,7 @@ RED.search = (function() { } function hide() { if (visible) { - RED.keyboard.remove(/* ESCAPE */ 27); + RED.keyboard.remove("escape"); visible = false; $("#header-shade").hide(); $("#editor-shade").hide(); @@ -274,7 +277,8 @@ RED.search = (function() { } function init() { - RED.keyboard.add("*",/* . */ 190,{ctrl:true},function(){if (!disabled) { show(); } d3.event.preventDefault();}); + RED.actions.add("core:search",show); + RED.events.on("editor:open",function() { disabled = true; }); RED.events.on("editor:close",function() { disabled = false; }); RED.events.on("palette-editor:open",function() { disabled = true; }); diff --git a/editor/js/ui/sidebar.js b/editor/js/ui/sidebar.js index 9a3e75dae..3100476b0 100644 --- a/editor/js/ui/sidebar.js +++ b/editor/js/ui/sidebar.js @@ -202,12 +202,18 @@ RED.sidebar = (function() { } function init () { - RED.keyboard.add("*",/* SPACE */ 32,{ctrl:true},function(){RED.menu.setSelected("menu-item-sidebar",!RED.menu.isSelected("menu-item-sidebar"));d3.event.preventDefault();}); + RED.actions.add("core:toggle-sidebar",function(state){ + if (state === undefined) { + RED.menu.toggleSelected("menu-item-sidebar"); + } else { + toggleSidebar(state); + } + }); showSidebar(); RED.sidebar.info.init(); RED.sidebar.config.init(); // hide info bar at start if screen rather narrow... - if ($(window).width() < 600) { toggleSidebar(); } + if ($(window).width() < 600) { RED.menu.setSelected("menu-item-sidebar",false); } } return { diff --git a/editor/js/ui/subflow.js b/editor/js/ui/subflow.js index 8b7d2b03a..995cb3df5 100644 --- a/editor/js/ui/subflow.js +++ b/editor/js/ui/subflow.js @@ -380,6 +380,8 @@ RED.subflow = (function() { } }); + RED.actions.add("core:create-subflow",createSubflow); + RED.actions.add("core:convert-to-subflow",convertToSubflow); } function createSubflow() { diff --git a/editor/js/ui/tab-config.js b/editor/js/ui/tab-config.js index 68b7dc90e..41516029e 100644 --- a/editor/js/ui/tab-config.js +++ b/editor/js/ui/tab-config.js @@ -236,10 +236,7 @@ RED.sidebar.config = (function() { visible: false, onchange: function() { refreshConfigNodeList(); } }); - - RED.menu.setAction('menu-item-config-nodes',function() { - RED.sidebar.show('config'); - }) + RED.actions.add("core:show-config-tab",function() {RED.sidebar.show('config')}); $("#workspace-config-node-collapse-all").on("click", function(e) { e.preventDefault(); diff --git a/editor/js/ui/tab-info.js b/editor/js/ui/tab-info.js index 1daf683ff..5f4b5406d 100644 --- a/editor/js/ui/tab-info.js +++ b/editor/js/ui/tab-info.js @@ -42,7 +42,7 @@ RED.sidebar.info = (function() { content: content, enableOnEdit: true }); - + RED.actions.add("core:show-info-tab",show); } function show() { diff --git a/editor/js/ui/typeSearch.js b/editor/js/ui/typeSearch.js index d9b02338e..0105d1799 100644 --- a/editor/js/ui/typeSearch.js +++ b/editor/js/ui/typeSearch.js @@ -168,7 +168,7 @@ RED.typeSearch = (function() { } function show(opts) { if (!visible) { - RED.keyboard.add("*",/* ESCAPE */ 27,function(){hide();d3.event.preventDefault();}); + RED.keyboard.add("*","escape",function(){hide()}); if (dialog === null) { createDialog(); } @@ -195,7 +195,7 @@ RED.typeSearch = (function() { } function hide(fast) { if (visible) { - RED.keyboard.remove(/* ESCAPE */ 27); + RED.keyboard.remove("escape"); visible = false; if (dialog !== null) { searchResultsDiv.slideUp(fast?50:200,function() { diff --git a/editor/js/ui/view.js b/editor/js/ui/view.js index 18e9f0471..4a40c28cf 100644 --- a/editor/js/ui/view.js +++ b/editor/js/ui/view.js @@ -267,6 +267,7 @@ RED.view = (function() { } function init() { + RED.events.on("workspace:change",function(event) { var chart = $("#chart"); if (event.old !== 0) { @@ -384,28 +385,47 @@ RED.view = (function() { } }); - RED.keyboard.add("workspace",/* backspace */ 8,function(){deleteSelection();d3.event.preventDefault();}); - RED.keyboard.add("workspace",/* delete */ 46,function(){deleteSelection();d3.event.preventDefault();}); - RED.keyboard.add("workspace",/* enter */ 13, function() { editSelection(); d3.event.preventDefault();}); + RED.actions.add("core:copy",copySelection); + RED.actions.add("core:cut",function(){copySelection();deleteSelection();}); + RED.actions.add("core:paste",function(){importNodes(clipboard);}); + RED.actions.add("core:delete",deleteSelection); + RED.actions.add("core:edit",editSelection); + RED.actions.add("core:undo",RED.history.pop); + RED.actions.add("core:select-all",selectAll); + RED.actions.add("core:zoom-in",zoomIn); + RED.actions.add("core:zoom-out",zoomOut); + RED.actions.add("core:zoom-reset",zoomZero); - RED.keyboard.add("workspace",/* c */ 67,{ctrl:true},function(){copySelection();d3.event.preventDefault();}); - RED.keyboard.add("workspace",/* x */ 88,{ctrl:true},function(){copySelection();deleteSelection();d3.event.preventDefault();}); + RED.actions.add("core:toggle-show-grid",function(state) { + if (state === undefined) { + RED.menu.toggleSelected("menu-item-view-show-grid"); + } else { + toggleShowGrid(state); + } + }); + RED.actions.add("core:toggle-snap-grid",function(state) { + if (state === undefined) { + RED.menu.toggleSelected("menu-item-view-snap-grid"); + } else { + toggleSnapGrid(state); + } + }); + RED.actions.add("core:toggle-status",function(state) { + if (state === undefined) { + RED.menu.toggleSelected("menu-item-status"); + } else { + toggleStatus(state); + } + }); - RED.keyboard.add("workspace",/* z */ 90,{ctrl:true},function(){RED.history.pop();}); - RED.keyboard.add("workspace",/* a */ 65,{ctrl:true},function(){selectAll();d3.event.preventDefault();}); - RED.keyboard.add("*",/* = */ 187,{ctrl:true},function(){zoomIn();d3.event.preventDefault();}); - RED.keyboard.add("*",/* - */ 189,{ctrl:true},function(){zoomOut();d3.event.preventDefault();}); - RED.keyboard.add("*",/* 0 */ 48,{ctrl:true},function(){zoomZero();d3.event.preventDefault();}); - RED.keyboard.add("workspace",/* v */ 86,{ctrl:true},function(){importNodes(clipboard);d3.event.preventDefault();}); - - RED.keyboard.add("workspace",/* up */ 38, function() { moveSelection(0,-1);d3.event.preventDefault();},endKeyboardMove); - RED.keyboard.add("workspace",/* up */ 38, {shift:true}, function() { moveSelection(0,-20); d3.event.preventDefault();},endKeyboardMove); - RED.keyboard.add("workspace",/* down */ 40, function() { moveSelection(0,1);d3.event.preventDefault();},endKeyboardMove); - RED.keyboard.add("workspace",/* down */ 40, {shift:true}, function() { moveSelection(0,20); d3.event.preventDefault();},endKeyboardMove); - RED.keyboard.add("workspace",/* left */ 37, function() { moveSelection(-1,0);d3.event.preventDefault();},endKeyboardMove); - RED.keyboard.add("workspace",/* left */ 37, {shift:true}, function() { moveSelection(-20,0); d3.event.preventDefault();},endKeyboardMove); - RED.keyboard.add("workspace",/* right */ 39, function() { moveSelection(1,0);d3.event.preventDefault();},endKeyboardMove); - RED.keyboard.add("workspace",/* right */ 39, {shift:true}, function() { moveSelection(20,0); d3.event.preventDefault();},endKeyboardMove); + RED.actions.add("core:move-selection-up", function() { moveSelection(0,-1);}); + RED.actions.add("core:step-selection-up", function() { moveSelection(0,-20);}); + RED.actions.add("core:move-selection-right", function() { moveSelection(1,0);}); + RED.actions.add("core:step-selection-right", function() { moveSelection(20,0);}); + RED.actions.add("core:move-selection-down", function() { moveSelection(0,1);}); + RED.actions.add("core:step-selection-down", function() { moveSelection(0,20);}); + RED.actions.add("core:move-selection-left", function() { moveSelection(-1,0);}); + RED.actions.add("core:step-selection-left", function() { moveSelection(-20,0);}); } @@ -959,7 +979,7 @@ RED.view = (function() { } } if (mouse_mode == RED.state.IMPORT_DRAGGING) { - RED.keyboard.remove(/* ESCAPE */ 27); + RED.keyboard.remove("escape"); updateActiveNodes(); RED.nodes.dirty(true); } @@ -1103,6 +1123,7 @@ RED.view = (function() { } function endKeyboardMove() { + endMoveSet = false; if (moving_set.length > 0) { var ns = []; for (var i=0;i 0) { + if (!endMoveSet) { + $(document).one('keyup',endKeyboardMove); + endMoveSet = true; + } var minX = 0; var minY = 0; var node; for (var i=0;i 0; } } - RED.keyboard.add("*",/* ESCAPE */ 27,function(){ - RED.keyboard.remove(/* ESCAPE */ 27); + RED.keyboard.add("*","escape",function(){ + RED.keyboard.remove("escape"); clearSelection(); RED.history.pop(); mouse_mode = 0; @@ -2483,6 +2511,24 @@ RED.view = (function() { } } + function toggleShowGrid(state) { + if (state) { + grid.style("visibility","visible"); + } else { + grid.style("visibility","hidden"); + } + } + function toggleSnapGrid(state) { + snapGrid = state; + redraw(); + } + function toggleStatus(s) { + showStatus = s; + RED.nodes.eachNode(function(n) { n.dirty = true;}); + //TODO: subscribe/unsubscribe here + redraw(); + } + return { init: init, state:function(state) { @@ -2502,16 +2548,6 @@ RED.view = (function() { }, focus: focusView, importNodes: importNodes, - status: function(s) { - if (s == null) { - return showStatus; - } else { - showStatus = s; - RED.nodes.eachNode(function(n) { n.dirty = true;}); - //TODO: subscribe/unsubscribe here - redraw(); - } - }, calculateTextWidth: calculateTextWidth, select: function(selection) { if (typeof selection !== "undefined") { @@ -2538,17 +2574,6 @@ RED.view = (function() { } return selection; }, - toggleShowGrid: function(state) { - if (state) { - grid.style("visibility","visible"); - } else { - grid.style("visibility","hidden"); - } - }, - toggleSnapGrid: function(state) { - snapGrid = state; - redraw(); - }, scale: function() { return scaleFactor; }, diff --git a/editor/js/ui/workspaces.js b/editor/js/ui/workspaces.js index f19e49543..5bfe7f4ca 100644 --- a/editor/js/ui/workspaces.js +++ b/editor/js/ui/workspaces.js @@ -170,6 +170,9 @@ RED.workspaces = (function() { createWorkspaceTabs(); RED.events.on("sidebar:resize",workspace_tabs.resize); + RED.actions.add("core:show-next-tab",workspace_tabs.nextTab); + RED.actions.add("core:show-previous-tab",workspace_tabs.previousTab); + RED.menu.setAction('menu-item-workspace-delete',function() { deleteWorkspace(RED.nodes.workspace(activeWorkspace)); }); @@ -177,6 +180,14 @@ RED.workspaces = (function() { $(window).resize(function() { workspace_tabs.resize(); }); + + RED.actions.add("core:add-flow",addWorkspace); + RED.actions.add("core:edit-flow",editWorkspace); + RED.actions.add("core:remove-flow",removeWorkspace); + } + + function editWorkspace(id) { + showRenameWorkspaceDialog(id||activeWorkspace); } function removeWorkspace(ws) { @@ -201,9 +212,7 @@ RED.workspaces = (function() { add: addWorkspace, remove: removeWorkspace, order: setWorkspaceOrder, - edit: function(id) { - showRenameWorkspaceDialog(id||activeWorkspace); - }, + edit: editWorkspace, contains: function(id) { return workspace_tabs.contains(id); }, diff --git a/nodes/core/core/58-debug.html b/nodes/core/core/58-debug.html index c784bbdf2..ada8a1c12 100644 --- a/nodes/core/core/58-debug.html +++ b/nodes/core/core/58-debug.html @@ -144,8 +144,7 @@ toolbar: uiComponents.footer, enableOnEdit: true }); - - + RED.actions.add("core:show-debug-tab",function() { RED.sidebar.show('debug')}); var that = this; RED._debug = function(msg) { @@ -222,6 +221,8 @@ RED.sidebar.removeTab("debug"); RED.events.off("workspace:change", this.refreshMessageList); window.removeEventListener("message",this.handleWindowMessage); + RED.actions.remove("core:show-debug"); + delete RED._debug; }, oneditprepare: function() {