From 679811419659a9d2444f03795231c551d595b6c2 Mon Sep 17 00:00:00 2001 From: tophf Date: Fri, 6 Aug 2021 13:04:00 +0300 Subject: [PATCH] add buttons to hotkey input, reset on Del/BackSpace --- edit/beautify.js | 5 ++- edit/edit.css | 5 ++- edit/edit.js | 4 +- edit/util.js | 102 ++++++++++++++++++++++++++++------------------- 4 files changed, 70 insertions(+), 46 deletions(-) diff --git a/edit/beautify.js b/edit/beautify.js index b0ef385e..659046d8 100644 --- a/edit/beautify.js +++ b/edit/beautify.js @@ -85,7 +85,10 @@ function createBeautifyUI(scope, options) { ]), $create('p.beautify-hint', [ $create('span', t('styleBeautifyHint') + '\u00A0'), - createHotkeyInput('editor.beautify.hotkey', () => moveFocus($('#help-popup'), 0)), + createHotkeyInput('editor.beautify.hotkey', { + buttons: false, + onDone: () => moveFocus($('#help-popup'), 0), + }), ]), $create('.buttons', [ $create('button', { diff --git a/edit/edit.css b/edit/edit.css index d8f33b30..b2e9bbde 100644 --- a/edit/edit.css +++ b/edit/edit.css @@ -795,6 +795,7 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high #help-popup .buttons { text-align: center; + margin-top: .75em; } .non-windows #help-popup .buttons { direction: rtl; @@ -820,8 +821,8 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high #help-popup .rules p { margin: .25em 0; } -#help-popup .buttons button { - margin-right: 3px; +#help-popup .buttons button:nth-child(n + 2) { + margin-left: .5em; } /************ lint ************/ diff --git a/edit/edit.js b/edit/edit.js index 2b9e44f7..d98a29e6 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -361,13 +361,13 @@ editor.livePreview = (() => { $('#colorpicker-settings').onclick = function (event) { event.preventDefault(); - const input = createHotkeyInput('editor.colorpicker.hotkey', () => helpPopup.close()); + const input = createHotkeyInput('editor.colorpicker.hotkey', {onDone: () => helpPopup.close()}); const popup = helpPopup.show(t('helpKeyMapHotkey'), input); const bounds = this.getBoundingClientRect(); popup.style.left = bounds.right + 10 + 'px'; popup.style.top = bounds.top - popup.clientHeight / 2 + 'px'; popup.style.right = 'auto'; - input.focus(); + $('input', popup).focus(); }; function invokeColorpicker(cm) { diff --git a/edit/util.js b/edit/util.js index 7bcddad7..028da6c1 100644 --- a/edit/util.js +++ b/edit/util.js @@ -103,49 +103,69 @@ function clipString(str, limit = 100) { } /* exported createHotkeyInput */ -function createHotkeyInput(prefId, onDone = () => {}) { - return $create('input', { - type: 'search', +function createHotkeyInput(prefId, {buttons = true, onDone}) { + const RX_ERR = new RegExp('^(' + [ + /Space/, + /(Shift-)?./, // a single character + /(?=.)(Shift-?|Ctrl-?|Control-?|Alt-?|Meta-?)*(Escape|Tab|Page(Up|Down)|Arrow(Up|Down|Left|Right)|Home|End)?/, + ].map(r => r.source || r).join('|') + ')$', 'i'); + const initialValue = prefs.get(prefId); + const input = $create('input', { spellcheck: false, - value: prefs.get(prefId), - onkeydown(event) { - const key = CodeMirror.keyName(event); - if (key === 'Tab' || key === 'Shift-Tab') { - return; - } - event.preventDefault(); - event.stopPropagation(); - switch (key) { - case 'Enter': - if (this.checkValidity()) onDone(true); - return; - case 'Esc': - onDone(false); - return; - default: - // disallow: [Shift?] characters, modifiers-only, [modifiers?] + Esc, Tab, nav keys - if (!key || new RegExp('^(' + [ - '(Back)?Space', - '(Shift-)?.', // a single character - '(Shift-?|Ctrl-?|Alt-?|Cmd-?){0,2}(|Esc|Tab|(Page)?(Up|Down)|Left|Right|Home|End|Insert|Delete)', - ].join('|') + ')$', 'i').test(key)) { - this.value = key || this.value; - this.setCustomValidity('Not allowed'); - return; - } - } - this.value = key; - this.setCustomValidity(''); - prefs.set(prefId, key); - }, - oninput() { - // fired on pressing "x" to clear the field - prefs.set(prefId, ''); - }, - onpaste(event) { - event.preventDefault(); - }, + onpaste: e => onkeydown(e, e.clipboardData.getData('text')), + onkeydown, }); + buttons = buttons && [ + ['confirmOK', 'Enter'], + ['undo', initialValue], + ['genericResetLabel', ''], + ].map(([label, val]) => + $create('button', {onclick: e => onkeydown(e, val)}, t(label))); + const [btnOk, btnUndo, btnReset] = buttons || []; + onkeydown(null, initialValue); + return buttons + ? $create('fragment', [input, $create('.buttons', buttons)]) + : input; + + function onkeydown(e, key) { + let newValue; + if (e && e.type === 'keydown') { + key = getEventKeyName(e); + } + switch (e && key) { + case 'Tab': + case 'Shift-Tab': + return; + case 'BackSpace': + case 'Delete': + newValue = ''; + break; + case 'Enter': + if (input.checkValidity() && onDone) onDone(); + break; + case 'Escape': + if (onDone) onDone(); + break; + default: + newValue = key.replace(/\b.$/, c => c.toUpperCase()); + } + if (newValue != null) { + const error = RX_ERR.test(newValue) ? t('genericError') : ''; + if (e && !error) prefs.set(prefId, newValue); + input.setCustomValidity(error); + input.value = newValue; + input.focus(); + if (buttons) { + btnOk.disabled = Boolean(error); + btnUndo.disabled = newValue === initialValue; + btnReset.disabled = !newValue; + } + } + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + } } /* exported showCodeMirrorPopup */