Move view and keyboard into user settings dialog

This commit is contained in:
Nick O'Leary
2017-04-28 20:49:01 +01:00
parent 8135da71bd
commit 5938143002
17 changed files with 643 additions and 124 deletions

View File

@@ -21,7 +21,7 @@ RED.actions = (function() {
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})
result.push({id:action,scope:shortcut?shortcut.scope:undefined,key:shortcut?shortcut.key:undefined,user:shortcut?shortcut.user:undefined})
})
return result;
}

View File

@@ -170,7 +170,7 @@
var that = this;
var count = 0;
if (!this.activeFilter) {
this.element.children().show();
return this.element.children().show();
}
var items = this.items();
items.each(function (i,el) {

View File

@@ -28,7 +28,17 @@ RED.menu = (function() {
}
function setInitialState() {
var savedStateActive = isSavedStateActive(opt.id);
var savedStateActive = RED.settings.get("menu-" + opt.id);
if (opt.setting) {
// May need to migrate pre-0.17 setting
if (savedStateActive !== null) {
RED.settings.set(opt.setting,savedStateActive);
RED.settings.remove("menu-" + opt.id);
} else {
savedStateActive = RED.settings.get(opt.setting);
}
}
if (savedStateActive) {
link.addClass("active");
triggerAction(opt.id,true);
@@ -176,18 +186,10 @@ RED.menu = (function() {
}
}
function isSavedStateActive(id) {
return RED.settings.get("menu-" + id);
}
function isSelected(id) {
return $("#" + id).hasClass("active");
}
function setSavedState(id, state) {
RED.settings.set("menu-" + id, state);
}
function setSelected(id,state) {
if (isSelected(id) == state) {
return;
@@ -201,7 +203,7 @@ RED.menu = (function() {
if (opt && opt.onselect) {
triggerAction(opt.id,state);
}
setSavedState(id, state);
RED.settings.set(opt.setting||("menu-"+opt.id), state);
}
function toggleSelected(id) {

View File

@@ -26,6 +26,9 @@ RED.tabs = (function() {
var wrapper = ul.wrap( "<div>" ).parent();
var scrollContainer = ul.wrap( "<div>" ).parent();
wrapper.addClass("red-ui-tabs");
if (options.vertical) {
wrapper.addClass("red-ui-tabs-vertical");
}
if (options.addButton && typeof options.addButton === 'function') {
wrapper.addClass("red-ui-tabs-add");
var addButton = $('<div class="red-ui-tab-button"><a href="#"><i class="fa fa-plus"></i></a></div>').appendTo(wrapper);
@@ -146,6 +149,9 @@ RED.tabs = (function() {
}
function updateTabWidths() {
if (options.vertical) {
return;
}
var tabs = ul.find("li.red-ui-tab");
var width = wrapper.width();
var tabCount = tabs.size();

View File

@@ -48,6 +48,7 @@ RED.keyboard = (function() {
93: true
}
var actionToKeyMap = {}
var defaultKeyMap = {};
// FF generates some different keycodes because reasons.
var firefoxKeyCodeMap = {
@@ -57,20 +58,45 @@ RED.keyboard = (function() {
}
function init() {
var userKeymap = RED.settings.get('keymap') || {};
$.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]);
if (!userKeymap.hasOwnProperty(keys[key])) {
addHandler(scope,key,keys[key],false);
defaultKeyMap[keys[key]] = {
scope:scope,
key:key,
user:false
};
}
}
}
}
}
for (var action in userKeymap) {
if (userKeymap.hasOwnProperty(action)) {
var obj = userKeymap[action];
if (obj.hasOwnProperty('key')) {
addHandler(obj.scope, obj.key, action, true);
}
}
}
})
RED.actions.add("core:show-help", showKeyboardHelp);
}
function revertToDefault(action) {
var currentAction = actionToKeyMap[action];
if (currentAction) {
removeHandler(currentAction.key);
}
if (defaultKeyMap.hasOwnProperty(action)) {
var obj = defaultKeyMap[action];
addHandler(obj.scope, obj.key, action, false);
}
}
function parseKeySpecifier(key) {
var parts = key.toLowerCase().split("-");
@@ -126,10 +152,12 @@ RED.keyboard = (function() {
if (partialState) {
partialState = null;
return resolveKeyEvent(evt);
} else {
} else if (Object.keys(handler).length > 0) {
partialState = handler;
evt.preventDefault();
return null;
} else {
return null;
}
} else if (handler.scope && handler.scope !== "*") {
var target = evt.target;
@@ -174,6 +202,9 @@ RED.keyboard = (function() {
if (typeof key === 'string') {
if (typeof cbdown === 'string') {
actionToKeyMap[cbdown] = {scope:scope,key:key};
if (typeof ondown === 'boolean') {
actionToKeyMap[cbdown].user = ondown;
}
}
var parts = key.split(" ");
for (i=0;i<parts.length;i++) {
@@ -181,7 +212,6 @@ RED.keyboard = (function() {
if (parsedKey) {
keys.push(parsedKey);
} else {
console.log("Unrecognised key specifier:",key)
return;
}
}
@@ -217,7 +247,7 @@ RED.keyboard = (function() {
var keys = [];
var i=0;
if (typeof key === 'string') {
delete actionToKeyMap[key];
var parts = key.split(" ");
for (i=0;i<parts.length;i++) {
var parsedKey = parseKeySpecifier(parts[i]);
@@ -249,78 +279,19 @@ RED.keyboard = (function() {
}
slot = slot[key];
}
if (typeof slot.ondown === "string") {
if (typeof modifiers === 'boolean' && modifiers) {
actionToKeyMap[slot.ondown] = {user: modifiers}
} else {
delete actionToKeyMap[slot.ondown];
}
}
delete slot.scope;
delete slot.ondown;
}
var shortcutDialog = null;
var cmdCtrlKey = '<span class="help-key">'+(isMac?'&#8984;':'Ctrl')+'</span>';
function showKeyboardHelp() {
if (!RED.settings.theme("menu.menu-item-keyboard-shortcuts",true)) {
return;
}
if (!shortcutDialog) {
shortcutDialog = $('<div id="keyboard-help-dialog" class="hide">'+
'<div class="keyboard-shortcut-entry keyboard-shortcut-list-header">'+
'<div class="keyboard-shortcut-entry-key">shortcut</div>'+
'<div class="keyboard-shortcut-entry-key">action</div>'+
'<div class="keyboard-shortcut-entry-scope">scope</div>'+
'</div>'+
'<ol id="keyboard-shortcut-list"></ol>'+
'</div>')
.appendTo("body");
var shortcutList = $('#keyboard-shortcut-list').editableList({
addButton: false,
scrollOnAdd: false,
addItem: function(container,i,object) {
var item = $('<div class="keyboard-shortcut-entry">').appendTo(container);
var key = $('<div class="keyboard-shortcut-entry-key">').appendTo(item);
if (object.key) {
key.append(formatKey(object.key));
} else {
item.addClass("keyboard-shortcut-entry-unassigned");
key.html(RED._('keyboard.unassigned'));
}
var text = object.id.replace(/(^.+:([a-z]))|(-([a-z]))/g,function() {
if (arguments[5] === 0) {
return arguments[2].toUpperCase();
} else {
return " "+arguments[4].toUpperCase();
}
});
var label = $('<div>').html(text).appendTo(item);
if (object.scope) {
$('<div class="keyboard-shortcut-entry-scope">').html(object.scope).appendTo(item);
}
},
});
var shortcuts = RED.actions.list();
shortcuts.sort(function(A,B) {
return A.id.localeCompare(B.id);
});
shortcuts.forEach(function(s) {
shortcutList.editableList('addItem',s);
})
shortcutDialog.dialog({
modal: true,
autoOpen: false,
width: "800",
height: "400",
title:RED._("keyboard.title"),
resizable: false
});
}
shortcutDialog.dialog("open");
}
function formatKey(key) {
var formattedKey = isMac?key.replace(/ctrl-?/,"&#8984;"):key;
formattedKey = isMac?formattedKey.replace(/alt-?/,"&#8997;"):key;
@@ -332,6 +303,206 @@ RED.keyboard = (function() {
return '<span class="help-key-block"><span class="help-key">'+formattedKey.split(" ").join('</span> <span class="help-key">')+'</span></span>';
}
function validateKey(key) {
key = key.trim();
var parts = key.split(" ");
for (i=0;i<parts.length;i++) {
var parsedKey = parseKeySpecifier(parts[i]);
if (!parsedKey) {
return false;
}
}
return true;
}
function editShortcut(e) {
e.preventDefault();
var container = $(this);
var object = container.data('data');
if (!container.hasClass('keyboard-shortcut-entry-expanded')) {
endEditShortcut();
var key = container.find(".keyboard-shortcut-entry-key");
var scope = container.find(".keyboard-shortcut-entry-scope");
container.addClass('keyboard-shortcut-entry-expanded');
var keyInput = $('<input type="text">').attr('placeholder',RED._('keyboard.unassigned')).val(object.key||"").appendTo(key);
keyInput.on("keyup",function(e) {
if (e.keyCode === 13) {
return endEditShortcut();
}
var currentVal = $(this).val();
currentVal = currentVal.trim();
var valid = (currentVal === "" || RED.keyboard.validateKey(currentVal));
$(this).toggleClass("input-error",!valid);
})
var scopeSelect = $('<select><option value="*">global</option><option value="workspace">workspace</option></select>').appendTo(scope);
scopeSelect.val(object.scope||'*');
var div = $('<div class="keyboard-shortcut-edit button-group-vertical"></div>').appendTo(scope);
var okButton = $('<button class="editor-button editor-button-small"><i class="fa fa-check"></i></button>').appendTo(div);
var revertButton = $('<button class="editor-button editor-button-small"><i class="fa fa-reply"></i></button>').appendTo(div);
okButton.click(function(e) {
e.stopPropagation();
endEditShortcut();
});
revertButton.click(function(e) {
e.stopPropagation();
RED.keyboard.revertToDefault(object.id);
container.empty();
container.removeClass('keyboard-shortcut-entry-expanded');
var shortcut = RED.keyboard.getShortcut(object.id);
var userKeymap = RED.settings.get('keymap') || {};
delete userKeymap[object.id];
RED.settings.set('keymap',userKeymap);
var obj = {
id:object.id,
scope:shortcut?shortcut.scope:undefined,
key:shortcut?shortcut.key:undefined,
user:shortcut?shortcut.user:undefined
}
buildShortcutRow(container,obj);
})
keyInput.focus();
}
}
function endEditShortcut(cancel) {
var container = $('.keyboard-shortcut-entry-expanded');
if (container.length === 1) {
var object = container.data('data');
var keyInput = container.find(".keyboard-shortcut-entry-key input");
var scopeSelect = container.find(".keyboard-shortcut-entry-scope select");
if (!cancel) {
var key = keyInput.val().trim();
var scope = scopeSelect.val();
var valid = (key === "" || RED.keyboard.validateKey(key));
if (valid) {
var current = RED.keyboard.getShortcut(object.id);
if ((!current && key) || (current && (current.scope !== scope || current.key !== key))) {
var keyDiv = container.find(".keyboard-shortcut-entry-key");
var scopeDiv = container.find(".keyboard-shortcut-entry-scope");
keyDiv.empty();
scopeDiv.empty();
if (object.key) {
RED.keyboard.remove(object.key,true);
}
container.find(".keyboard-shortcut-entry-text i").css("opacity",1);
if (key === "") {
keyDiv.parent().addClass("keyboard-shortcut-entry-unassigned");
keyDiv.append($('<span>').text(RED._('keyboard.unassigned')) );
delete object.key;
delete object.scope;
} else {
keyDiv.parent().removeClass("keyboard-shortcut-entry-unassigned");
keyDiv.append(RED.keyboard.formatKey(key))
$("<span>").text(scope).appendTo(scopeDiv);
object.key = key;
object.scope = scope;
RED.keyboard.add(object.scope,object.key,object.id,true);
}
var userKeymap = RED.settings.get('keymap') || {};
userKeymap[object.id] = RED.keyboard.getShortcut(object.id);
RED.settings.set('keymap',userKeymap);
}
}
}
keyInput.remove();
scopeSelect.remove();
$('.keyboard-shortcut-edit').remove();
container.removeClass('keyboard-shortcut-entry-expanded');
}
}
function buildShortcutRow(container,object) {
var item = $('<div class="keyboard-shortcut-entry">').appendTo(container);
container.data('data',object);
var text = object.id.replace(/(^.+:([a-z]))|(-([a-z]))/g,function() {
if (arguments[5] === 0) {
return arguments[2].toUpperCase();
} else {
return " "+arguments[4].toUpperCase();
}
});
var label = $('<div>').addClass("keyboard-shortcut-entry-text").text(text).appendTo(item);
var user = $('<i class="fa fa-user"></i>').prependTo(label);
if (!object.user) {
user.css("opacity",0);
}
var key = $('<div class="keyboard-shortcut-entry-key">').appendTo(item);
if (object.key) {
key.append(RED.keyboard.formatKey(object.key));
} else {
item.addClass("keyboard-shortcut-entry-unassigned");
key.append($('<span>').text(RED._('keyboard.unassigned')) );
}
var scope = $('<div class="keyboard-shortcut-entry-scope">').appendTo(item);
$("<span>").text(object.scope === '*'?'global':object.scope||"").appendTo(scope);
container.click(editShortcut);
}
function getSettingsPane() {
var pane = $('<div id="user-settings-tab-keyboard"></div>');
$('<div class="keyboard-shortcut-entry keyboard-shortcut-list-header">'+
'<div class="keyboard-shortcut-entry-key keyboard-shortcut-entry-text"><input type="text" placeholder="filter actions"></div>'+
'<div class="keyboard-shortcut-entry-key">shortcut</div>'+
'<div class="keyboard-shortcut-entry-scope">scope</div>'+
'</div>').appendTo(pane);
pane.find("input").searchBox({
delay: 100,
change: function() {
var filterValue = $(this).val().trim();
if (filterValue === "") {
shortcutList.editableList('filter', null);
} else {
filterValue = filterValue.replace(/\s/g,"");
shortcutList.editableList('filter', function(data) {
return data.id.toLowerCase().replace(/^.*:/,"").replace("-","").indexOf(filterValue) > -1;
})
}
}
});
var shortcutList = $('<ol class="keyboard-shortcut-list"></ol>').css({
position: "absolute",
top: "32px",
bottom: "0",
left: "0",
right: "0"
}).appendTo(pane).editableList({
addButton: false,
scrollOnAdd: false,
addItem: function(container,i,object) {
buildShortcutRow(container,object);
},
});
var shortcuts = RED.actions.list();
shortcuts.sort(function(A,B) {
var Aid = A.id.replace(/^.*:/,"").replace(/[ -]/g,"").toLowerCase();
var Bid = B.id.replace(/^.*:/,"").replace(/[ -]/g,"").toLowerCase();
return Aid.localeCompare(Bid);
});
shortcuts.forEach(function(s) {
shortcutList.editableList('addItem',s);
});
return pane;
}
return {
init: init,
add: addHandler,
@@ -339,7 +510,10 @@ RED.keyboard = (function() {
getShortcut: function(actionName) {
return actionToKeyMap[actionName];
},
formatKey: formatKey
revertToDefault: revertToDefault,
formatKey: formatKey,
validateKey: validateKey,
getSettingsPane: getSettingsPane
}
})();

View File

@@ -74,7 +74,7 @@ RED.sidebar.info = (function() {
tipClose.click(function(e) {
e.preventDefault();
RED.actions.invoke("core:toggle-show-tips");
RED.notify("You can re-open the tips from the side menu");
RED.notify(RED._("sidebar.info.showTips"));
});
RED.sidebar.addTab({
@@ -267,7 +267,7 @@ RED.sidebar.info = (function() {
RED.actions.add("core:toggle-show-tips",function(state) {
if (state === undefined) {
RED.menu.toggleSelected("menu-item-show-tips");
RED.userSettings.toggle("view-show-tips");
} else {
enabled = state;
if (enabled) {

View File

@@ -16,14 +16,15 @@
RED.userSettings = (function() {
var trayWidth = null;
var trayWidth = 700;
var settingsVisible = false;
function show() {
function show(initialTab) {
if (settingsVisible) {
return;
}
settingsVisible = true;
var tabContainer;
var trayOptions = {
title: "User Settings",
@@ -42,17 +43,20 @@ RED.userSettings = (function() {
},
open: function(tray) {
var trayBody = tray.find('.editor-tray-body');
var tabContainer = $('<div></div>',{id:"user-settings-tabs-container"}).appendTo(trayBody);
$('<ul></ul>',{id:"user-settings-tabs"}).appendTo(trayBody);
$('<ul></ul>',{id:"user-settings-tabs"}).appendTo(tabContainer);
var tabContents = $('<div></div>',{id:"user-settings-tabs-content"}).appendTo(trayBody);
$('<div class="hide" id="user-settings-tab-view">View Tab</div>').appendTo(tabContents);
$('<div class="hide" id="user-settings-tab-keyboard">Keyboard Tab</div>').appendTo(tabContents);
$('<div class="hide" id="user-settings-tab-something">Something Tab</div>').appendTo(tabContents);
createViewPane().hide().appendTo(tabContents);
RED.keyboard.getSettingsPane().hide().appendTo(tabContents);
$('<div id="user-settings-tab-palette"></div>').appendTo(tabContents);
var tabs = RED.tabs.create({
id: "user-settings-tabs",
vertical: true,
onchange: function(tab) {
$("#user-settings-tabs-content").children().hide();
$("#" + tab.id).show();
@@ -64,15 +68,31 @@ RED.userSettings = (function() {
});
tabs.addTab({
id: "user-settings-tab-keyboard",
label: "Keyboard Shortcuts"
label: "Keyboard"
});
tabs.addTab({
id: "user-settings-tab-something",
label: "Something Else"
id: "user-settings-tab-palette",
label: "Palette"
});
if (initialTab) {
tabs.activateTab("user-settings-tab-"+initialTab)
}
},
close: function() {
settingsVisible = false;
viewSettings.forEach(function(section) {
section.options.forEach(function(opt) {
var input = $("#user-settings-"+opt.setting);
if (opt.toggle) {
setSelected(opt.setting,input.prop('checked'));
} else {
setSelected(opt.setting,input.val());
}
});
})
},
show: function() {}
}
@@ -82,10 +102,99 @@ RED.userSettings = (function() {
RED.tray.show(trayOptions);
}
var viewSettings = [
{
title: "Grid",
options: [
{setting:"view-show-grid",label:"menu.label.view.showGrid",toggle:true,onchange:"core:toggle-show-grid"},
{setting:"view-snap-grid",label:"menu.label.view.snapGrid",toggle:true,onchange:"core:toggle-snap-grid"},
{setting:"view-grid-size",label:"menu.label.view.gridSize",type:"number",default: 20, onchange:RED.view.gridSize}
]
},
{
title: "Nodes",
options: [
{setting:"view-node-status",label:"menu.label.displayStatus",toggle:true,onchange:"core:toggle-status", selected: true}
]
},
{
title: "Other",
options: [
{setting:"view-show-tips",label:"menu.label.showTips",toggle:true,default:true,onchange:"core:toggle-show-tips"}
]
}
];
var allSettings = {};
function createViewPane() {
var pane = $('<div id="user-settings-tab-view" class="node-help"></div>');
viewSettings.forEach(function(section) {
$('<h3></h3>').text(section.title).appendTo(pane);
section.options.forEach(function(opt) {
var initialState = RED.settings.get(opt.setting);
var row = $('<div class="user-settings-row"></div>').appendTo(pane);
var input;
if (opt.toggle) {
input = $('<label for="user-settings-'+opt.setting+'"><input id="user-settings-'+opt.setting+'" type="checkbox"> '+RED._(opt.label)+'</label>').appendTo(row).find("input");
input.prop('checked',initialState);
} else {
$('<label for="user-settings-'+opt.setting+'">'+RED._(opt.label)+'</label>').appendTo(row);
$('<input id="user-settings-'+opt.setting+'" type="'+(opt.type||"text")+'">').appendTo(row).val(initialState);
}
});
})
return pane;
}
function setSelected(id, value) {
var opt = allSettings[id];
RED.settings.set(opt.setting,value);
var callback = opt.onchange;
if (typeof callback === 'string') {
callback = RED.actions.get(callback);
}
if (callback) {
callback.call(opt,value);
}
}
function toggle(id) {
var opt = allSettings[id];
var state = RED.settings.get(opt.setting);
setSelected(id,!state);
}
function init() {
RED.actions.add("core:show-user-settings",show);
RED.actions.add("core:show-help", function() { show('keyboard')});
viewSettings.forEach(function(section) {
section.options.forEach(function(opt) {
allSettings[opt.setting] = opt;
if (opt.onchange) {
var value = RED.settings.get(opt.setting);
if (value === null && opt.hasOwnProperty('default')) {
value = opt.default;
RED.settings.set(opt.setting,value);
}
var callback = opt.onchange;
if (typeof callback === 'string') {
callback = RED.actions.get(callback);
}
if (callback) {
callback.call(opt,value);
}
}
});
});
}
return {
init: init
init: init,
toggle: toggle
};
})();

View File

@@ -239,6 +239,41 @@ RED.view = (function() {
});
grid.style("visibility","hidden");
updateGrid();
function updateGrid() {
grid.selectAll("line.horizontal").remove();
grid.selectAll("line.horizontal").data(gridScale.ticks(space_width/gridSize)).enter()
.append("line")
.attr(
{
"class":"horizontal",
"x1" : 0,
"x2" : space_width,
"y1" : function(d){ return gridScale(d);},
"y2" : function(d){ return gridScale(d);},
"fill" : "none",
"shape-rendering" : "crispEdges",
"stroke" : "#eee",
"stroke-width" : "1px"
});
grid.selectAll("line.vertical").remove();
grid.selectAll("line.vertical").data(gridScale.ticks(space_width/gridSize)).enter()
.append("line")
.attr(
{
"class":"vertical",
"y1" : 0,
"y2" : space_width,
"x1" : function(d){ return gridScale(d);},
"x2" : function(d){ return gridScale(d);},
"fill" : "none",
"shape-rendering" : "crispEdges",
"stroke" : "#eee",
"stroke-width" : "1px"
});
}
var dragGroup = vis.append("g");
var drag_lines = [];
@@ -408,21 +443,21 @@ RED.view = (function() {
RED.actions.add("core:toggle-show-grid",function(state) {
if (state === undefined) {
RED.menu.toggleSelected("menu-item-view-show-grid");
RED.userSettings.toggle("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");
RED.userSettings.toggle("view-snap-grid");
} else {
toggleSnapGrid(state);
}
});
RED.actions.add("core:toggle-status",function(state) {
if (state === undefined) {
RED.menu.toggleSelected("menu-item-status");
RED.userSettings.toggle("view-node-status");
} else {
toggleStatus(state);
}
@@ -2743,6 +2778,14 @@ RED.view = (function() {
RED.sidebar.config.show(id);
}
}
},
gridSize: function(v) {
if (v === undefined) {
return gridSize;
} else {
gridSize = v;
updateGrid();
}
}
};