From c82fc4a5dd5191d989d4a11dc623b77374f361a3 Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Mon, 25 Sep 2023 11:04:23 +0000 Subject: [PATCH 01/39] extend keystrokes to mod keys --- packages/commands/src/index.ts | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 55f243362..97ab1ca44 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -512,7 +512,7 @@ export class CommandRegistry { */ processKeydownEvent(event: KeyboardEvent): void { // Bail immediately if playing back keystrokes. - if (this._replaying || CommandRegistry.isModifierKeyPressed(event)) { + if (this._replaying) { return; } @@ -1250,10 +1250,8 @@ export namespace CommandRegistry { export function keystrokeForKeydownEvent(event: KeyboardEvent): string { let layout = getKeyboardLayout(); let key = layout.keyForKeydownEvent(event); - if (!key || layout.isModifierKey(key)) { - return ''; - } - let mods = []; + let mods = [] + if (event.ctrlKey) { mods.push('Ctrl'); } @@ -1266,7 +1264,21 @@ export namespace CommandRegistry { if (event.metaKey && Platform.IS_MAC) { mods.push('Cmd'); } - mods.push(key); + if (!mods.includes(event.key)) { + mods.push(key); + } + console.log(mods) + // Handle the edge cases + // Handle the edge case for triggering the Alt Shift key binding + if (mods.length === 2 && (mods[0] === 'Alt' && mods[1] === 'Shift')) { + return "Alt" + ' ' + "Shift" + ' ' + } + + // Handle the edge case for triggering the Alt key binding + if (mods.length === 1 && mods[0] === 'Alt') { + return "Alt" + ' ' + } + return mods.join(' '); } } From 835f745dabb24ee487c374582e5e47e8250d63ef Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Tue, 3 Oct 2023 15:09:28 +0000 Subject: [PATCH 02/39] fix doubled keys --- packages/commands/src/index.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 97ab1ca44..a79fe1e27 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -29,6 +29,9 @@ import { ISignal, Signal } from '@lumino/signaling'; import { VirtualElement } from '@lumino/virtualdom'; + + + /** * An object which manages a collection of commands. * @@ -1264,7 +1267,7 @@ export namespace CommandRegistry { if (event.metaKey && Platform.IS_MAC) { mods.push('Cmd'); } - if (!mods.includes(event.key)) { + if (!mods.includes(event.key) && !event.ctrlKey) { mods.push(key); } console.log(mods) @@ -1278,8 +1281,8 @@ export namespace CommandRegistry { if (mods.length === 1 && mods[0] === 'Alt') { return "Alt" + ' ' } - - return mods.join(' '); + console.log(mods.join(' ')) + return mods.join(' ') || mods[0].concat(); } } From b9d2db30fd0804c2856651ac7aa6e1f889a47fdc Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Tue, 3 Oct 2023 15:31:53 +0000 Subject: [PATCH 03/39] remove comments --- packages/commands/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index a79fe1e27..2af565b59 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -1281,8 +1281,8 @@ export namespace CommandRegistry { if (mods.length === 1 && mods[0] === 'Alt') { return "Alt" + ' ' } - console.log(mods.join(' ')) - return mods.join(' ') || mods[0].concat(); + + return mods.join(' '); } } From 12f6921808a5c46268717af38541379e8bd78916 Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Wed, 4 Oct 2023 09:52:38 +0000 Subject: [PATCH 04/39] remove console-log --- packages/commands/src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 2af565b59..3a269edf8 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -1270,8 +1270,9 @@ export namespace CommandRegistry { if (!mods.includes(event.key) && !event.ctrlKey) { mods.push(key); } - console.log(mods) + // Handle the edge cases + // Handle the edge case for triggering the Alt Shift key binding if (mods.length === 2 && (mods[0] === 'Alt' && mods[1] === 'Shift')) { return "Alt" + ' ' + "Shift" + ' ' From 6cfdf128a5d8f6f9355e6de71cb043497839fadf Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Tue, 17 Oct 2023 13:22:13 +0000 Subject: [PATCH 05/39] Removed whitespace from keystrokes --- packages/commands/src/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 3a269edf8..dbf15d4a9 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -1170,6 +1170,9 @@ export namespace CommandRegistry { if (parts.cmd && Platform.IS_MAC) { mods += 'Cmd '; } + if (!parts.key) { + return mods.trim(); + } return mods + parts.key; } From 21674968a98445217455f0bc033b7fb15d9d2422 Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Tue, 17 Oct 2023 13:24:28 +0000 Subject: [PATCH 06/39] Run prettier --- packages/commands/src/index.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index dbf15d4a9..c1bc87fb4 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -29,9 +29,6 @@ import { ISignal, Signal } from '@lumino/signaling'; import { VirtualElement } from '@lumino/virtualdom'; - - - /** * An object which manages a collection of commands. * @@ -1256,7 +1253,7 @@ export namespace CommandRegistry { export function keystrokeForKeydownEvent(event: KeyboardEvent): string { let layout = getKeyboardLayout(); let key = layout.keyForKeydownEvent(event); - let mods = [] + let mods = []; if (event.ctrlKey) { mods.push('Ctrl'); @@ -1275,17 +1272,17 @@ export namespace CommandRegistry { } // Handle the edge cases - + // Handle the edge case for triggering the Alt Shift key binding - if (mods.length === 2 && (mods[0] === 'Alt' && mods[1] === 'Shift')) { - return "Alt" + ' ' + "Shift" + ' ' - } + if (mods.length === 2 && mods[0] === 'Alt' && mods[1] === 'Shift') { + return 'Alt' + ' ' + 'Shift' + ' '; + } // Handle the edge case for triggering the Alt key binding if (mods.length === 1 && mods[0] === 'Alt') { - return "Alt" + ' ' - } - + return 'Alt' + ' '; + } + return mods.join(' '); } } From d9ddb6cba34406854affb07e2b1f461605ec3ad9 Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Tue, 17 Oct 2023 15:09:39 +0000 Subject: [PATCH 07/39] Updated normalizeKeystroke to account for empty string --- packages/commands/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index c1bc87fb4..5f0519dab 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -1167,7 +1167,7 @@ export namespace CommandRegistry { if (parts.cmd && Platform.IS_MAC) { mods += 'Cmd '; } - if (!parts.key) { + if (!parts.key && parts.key !== '') { return mods.trim(); } return mods + parts.key; From 4efbfc78f6bb77e4a53ad325f1fa76606596747d Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Tue, 17 Oct 2023 15:48:29 +0000 Subject: [PATCH 08/39] Check for empty string in key --- packages/commands/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 5f0519dab..86d73c503 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -1167,7 +1167,7 @@ export namespace CommandRegistry { if (parts.cmd && Platform.IS_MAC) { mods += 'Cmd '; } - if (!parts.key && parts.key !== '') { + if (parts.key !== '') { return mods.trim(); } return mods + parts.key; From 5e5e65c71ab7b198eca958f2162fab8859407ed7 Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Tue, 17 Oct 2023 15:54:50 +0000 Subject: [PATCH 09/39] Check for empty string --- packages/commands/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 86d73c503..0d2794f31 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -1167,7 +1167,7 @@ export namespace CommandRegistry { if (parts.cmd && Platform.IS_MAC) { mods += 'Cmd '; } - if (parts.key !== '') { + if (parts.key === '') { return mods.trim(); } return mods + parts.key; From d4e384c69cc2d02ecd458dd675e60ccd4c234ec8 Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Tue, 17 Oct 2023 16:06:03 +0000 Subject: [PATCH 10/39] Checking for whitespace --- packages/commands/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 0d2794f31..3519aee41 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -1167,7 +1167,7 @@ export namespace CommandRegistry { if (parts.cmd && Platform.IS_MAC) { mods += 'Cmd '; } - if (parts.key === '') { + if (parts.key === '' || parts.key === ' ') { return mods.trim(); } return mods + parts.key; From 36fcbad9d0fa8b9cddae21b42e05e63d4fb3b20c Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Tue, 17 Oct 2023 16:17:11 +0000 Subject: [PATCH 11/39] Trim whitespace --- packages/commands/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 3519aee41..9a4b76fcc 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -1167,7 +1167,7 @@ export namespace CommandRegistry { if (parts.cmd && Platform.IS_MAC) { mods += 'Cmd '; } - if (parts.key === '' || parts.key === ' ') { + if (parts.key.trim() === '') { return mods.trim(); } return mods + parts.key; From d5206160eb72a915f78fe05e58ac9fb2d9c7c94c Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Thu, 19 Oct 2023 07:56:33 +0000 Subject: [PATCH 12/39] Added check for keys --- packages/commands/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 9a4b76fcc..7d347bf0a 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -1167,7 +1167,7 @@ export namespace CommandRegistry { if (parts.cmd && Platform.IS_MAC) { mods += 'Cmd '; } - if (parts.key.trim() === '') { + if (!parts.key) { return mods.trim(); } return mods + parts.key; @@ -1267,7 +1267,7 @@ export namespace CommandRegistry { if (event.metaKey && Platform.IS_MAC) { mods.push('Cmd'); } - if (!mods.includes(event.key) && !event.ctrlKey) { + if (!layout.isModifierKey(key)) { mods.push(key); } From 4926fd0e2fdff9a03e511f2b402b3af38a2e0a18 Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Fri, 20 Oct 2023 10:55:21 +0000 Subject: [PATCH 13/39] refactor add space for puley modifier key --- packages/commands/src/index.ts | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 7d347bf0a..bbd39d797 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -1269,21 +1269,11 @@ export namespace CommandRegistry { } if (!layout.isModifierKey(key)) { mods.push(key); + return mods.join(' '); + } else { + // for purely modifier key strings add a space so they can be matched + return mods.join(' ') + ' '; } - - // Handle the edge cases - - // Handle the edge case for triggering the Alt Shift key binding - if (mods.length === 2 && mods[0] === 'Alt' && mods[1] === 'Shift') { - return 'Alt' + ' ' + 'Shift' + ' '; - } - - // Handle the edge case for triggering the Alt key binding - if (mods.length === 1 && mods[0] === 'Alt') { - return 'Alt' + ' '; - } - - return mods.join(' '); } } From 9bf4c4736bb961f9bba19b464b0ad4973f03e8ae Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Fri, 20 Oct 2023 13:58:11 +0000 Subject: [PATCH 14/39] Changed tests to reflect mod keys creating events --- packages/commands/tests/src/index.spec.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/commands/tests/src/index.spec.ts b/packages/commands/tests/src/index.spec.ts index d19e07325..2eeed2255 100644 --- a/packages/commands/tests/src/index.spec.ts +++ b/packages/commands/tests/src/index.spec.ts @@ -1171,7 +1171,7 @@ describe('@lumino/commands', () => { ctrlKey: true }) ); - expect(count).to.equal(0); + expect(count).to.equal(1); // User presses `ctrl` again - this should not break the sequence. elem.dispatchEvent( new KeyboardEvent('keydown', { @@ -1179,7 +1179,7 @@ describe('@lumino/commands', () => { ctrlKey: true }) ); - expect(count).to.equal(0); + expect(count).to.equal(1); elem.dispatchEvent( new KeyboardEvent('keydown', { keyCode: 76, // `L` key @@ -1218,11 +1218,11 @@ describe('@lumino/commands', () => { ctrlKey: true }); elem.dispatchEvent(eventShift); - expect(count).to.equal(0); + expect(count).to.equal(1); elem.dispatchEvent(eventK); - expect(count).to.equal(0); + expect(count).to.equal(1); elem.dispatchEvent(eventCtrl); - expect(count).to.equal(0); + expect(count).to.equal(1); elem.dispatchEvent(eventL); expect(count).to.equal(1); }); @@ -1335,11 +1335,11 @@ describe('@lumino/commands', () => { expect(keystroke).to.equal(''); }); - it('should return nothing for keys that are marked as modifier in keyboard layout', () => { + it('should return keys that are marked as modifier in keyboard layout', () => { let keystroke = CommandRegistry.keystrokeForKeydownEvent( new KeyboardEvent('keydown', { keyCode: 17, ctrlKey: true }) ); - expect(keystroke).to.equal(''); + expect(keystroke).to.equal('Ctrl'); }); }); }); From 85a1362a48f0b7dba890d21a15fdde987645433c Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Fri, 20 Oct 2023 14:21:56 +0000 Subject: [PATCH 15/39] Changed tests to match key bindings --- packages/commands/tests/src/index.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/commands/tests/src/index.spec.ts b/packages/commands/tests/src/index.spec.ts index 2eeed2255..594036b99 100644 --- a/packages/commands/tests/src/index.spec.ts +++ b/packages/commands/tests/src/index.spec.ts @@ -1179,7 +1179,7 @@ describe('@lumino/commands', () => { ctrlKey: true }) ); - expect(count).to.equal(1); + expect(count).to.equal(0); elem.dispatchEvent( new KeyboardEvent('keydown', { keyCode: 76, // `L` key @@ -1218,11 +1218,11 @@ describe('@lumino/commands', () => { ctrlKey: true }); elem.dispatchEvent(eventShift); - expect(count).to.equal(1); + expect(count).to.equal(0); elem.dispatchEvent(eventK); expect(count).to.equal(1); elem.dispatchEvent(eventCtrl); - expect(count).to.equal(1); + expect(count).to.equal(0); elem.dispatchEvent(eventL); expect(count).to.equal(1); }); @@ -1339,7 +1339,7 @@ describe('@lumino/commands', () => { let keystroke = CommandRegistry.keystrokeForKeydownEvent( new KeyboardEvent('keydown', { keyCode: 17, ctrlKey: true }) ); - expect(keystroke).to.equal('Ctrl'); + expect(keystroke).to.equal('Ctrl '); }); }); }); From 5ed4103a5ffb627391fe3bc1c368d6cce7ee4225 Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Fri, 20 Oct 2023 14:39:05 +0000 Subject: [PATCH 16/39] Revert test changes --- packages/commands/tests/src/index.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/commands/tests/src/index.spec.ts b/packages/commands/tests/src/index.spec.ts index 594036b99..d81b84343 100644 --- a/packages/commands/tests/src/index.spec.ts +++ b/packages/commands/tests/src/index.spec.ts @@ -1171,7 +1171,7 @@ describe('@lumino/commands', () => { ctrlKey: true }) ); - expect(count).to.equal(1); + expect(count).to.equal(0); // User presses `ctrl` again - this should not break the sequence. elem.dispatchEvent( new KeyboardEvent('keydown', { @@ -1220,7 +1220,7 @@ describe('@lumino/commands', () => { elem.dispatchEvent(eventShift); expect(count).to.equal(0); elem.dispatchEvent(eventK); - expect(count).to.equal(1); + expect(count).to.equal(0); elem.dispatchEvent(eventCtrl); expect(count).to.equal(0); elem.dispatchEvent(eventL); From d9a3f6fddd0023cd6f72e94d6acc990f4129d135 Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Fri, 20 Oct 2023 14:57:40 +0000 Subject: [PATCH 17/39] remove added white space --- packages/commands/src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index bbd39d797..5b58e90e9 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -1269,11 +1269,11 @@ export namespace CommandRegistry { } if (!layout.isModifierKey(key)) { mods.push(key); + // for modifier and character key strings return mods.join(' '); - } else { - // for purely modifier key strings add a space so they can be matched - return mods.join(' ') + ' '; } + // for purely modifier key strings + return mods.join(' '); } } From c248e7a552595d07b94a79c613d37826aa851dd9 Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Mon, 23 Oct 2023 09:41:40 +0000 Subject: [PATCH 18/39] Removed ctrl key from test to check functionality --- packages/commands/tests/src/index.spec.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/commands/tests/src/index.spec.ts b/packages/commands/tests/src/index.spec.ts index d81b84343..ac0c39082 100644 --- a/packages/commands/tests/src/index.spec.ts +++ b/packages/commands/tests/src/index.spec.ts @@ -1172,14 +1172,14 @@ describe('@lumino/commands', () => { }) ); expect(count).to.equal(0); - // User presses `ctrl` again - this should not break the sequence. - elem.dispatchEvent( - new KeyboardEvent('keydown', { - keyCode: 17, - ctrlKey: true - }) - ); - expect(count).to.equal(0); + // // User presses `ctrl` again - this should not break the sequence. + // elem.dispatchEvent( + // new KeyboardEvent('keydown', { + // keyCode: 17, + // ctrlKey: true + // }) + // ); + // expect(count).to.equal(0); elem.dispatchEvent( new KeyboardEvent('keydown', { keyCode: 76, // `L` key @@ -1339,7 +1339,7 @@ describe('@lumino/commands', () => { let keystroke = CommandRegistry.keystrokeForKeydownEvent( new KeyboardEvent('keydown', { keyCode: 17, ctrlKey: true }) ); - expect(keystroke).to.equal('Ctrl '); + expect(keystroke).to.equal('Ctrl'); }); }); }); From 10e25fc0487c0fb8ab86e8c40c69c9e3e6ef68aa Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Mon, 23 Oct 2023 09:54:41 +0000 Subject: [PATCH 19/39] Modified test --- packages/commands/tests/src/index.spec.ts | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/packages/commands/tests/src/index.spec.ts b/packages/commands/tests/src/index.spec.ts index ac0c39082..b412a9931 100644 --- a/packages/commands/tests/src/index.spec.ts +++ b/packages/commands/tests/src/index.spec.ts @@ -1153,7 +1153,7 @@ describe('@lumino/commands', () => { document.body.removeEventListener('keydown', keydown); }); - it('should ignore modifier keys pressed in the middle of key sequence', () => { + it('should register a key sequence', () => { let count = 0; registry.addCommand('test', { execute: () => { @@ -1172,14 +1172,6 @@ describe('@lumino/commands', () => { }) ); expect(count).to.equal(0); - // // User presses `ctrl` again - this should not break the sequence. - // elem.dispatchEvent( - // new KeyboardEvent('keydown', { - // keyCode: 17, - // ctrlKey: true - // }) - // ); - // expect(count).to.equal(0); elem.dispatchEvent( new KeyboardEvent('keydown', { keyCode: 76, // `L` key @@ -1201,28 +1193,16 @@ describe('@lumino/commands', () => { selector: `#${elem.id}`, command: 'test' }); - let eventShift = new KeyboardEvent('keydown', { - keyCode: 16, - shiftKey: true - }); let eventK = new KeyboardEvent('keydown', { keyCode: 75, shiftKey: true }); - let eventCtrl = new KeyboardEvent('keydown', { - keyCode: 17, - ctrlKey: true - }); let eventL = new KeyboardEvent('keydown', { keyCode: 76, ctrlKey: true }); - elem.dispatchEvent(eventShift); - expect(count).to.equal(0); elem.dispatchEvent(eventK); expect(count).to.equal(0); - elem.dispatchEvent(eventCtrl); - expect(count).to.equal(0); elem.dispatchEvent(eventL); expect(count).to.equal(1); }); From a89b6013c56b34fd9d7cbeb40353b3a56b669396 Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Tue, 31 Oct 2023 18:28:21 +0000 Subject: [PATCH 20/39] delay modifier key only execution --- packages/commands/src/index.ts | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 5b58e90e9..6118f03a0 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -537,14 +537,36 @@ export class CommandRegistry { event ); - // If there is no exact match and no partial match, replay - // any suppressed events and clear the pending state. - if (!exact && !partial) { - this._replayKeydownEvents(); + // Remove modifier key only keystrokes from the current key sequence. + // keystrokes that are modifier key(s) plus another key are not affected + // intended functionality: Alt then Alt 1 should result to: ['Alt'] then ['Alt 1'] + // Removing this code results in mod key duplication: ['Alt', 'Alt 1'] + if ( + // Check that only a mod key has been pressed + (event.altKey && event.key === 'Alt') || + (event.ctrlKey && event.key === 'Ctrl') || + (event.shiftKey && event.key === 'Shift') + ) { + this._keystrokes.length = 0; + } + + + // If there is an exact match that is not excluded and no partial match, the exact match + // can be dispatched immediately. The pending state is cleared so + // the next key press starts from the default state. + // add new modifier only key commands that should be excluded to the end of the if statement + if ( + exact && + !partial && + !exact?.command.includes('application:activate-sidebar-overlays') + ) { + this._executeKeyBinding(exact); this._clearPendingState(); + return; } + // Stop propagation of the event. If there is only a partial match, // the event will be replayed if a final exact match never occurs. event.preventDefault(); From 7ffa17c406e36006b7e9c39cb9571ecc147e68da Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Wed, 8 Nov 2023 09:34:51 +0000 Subject: [PATCH 21/39] Update packages/commands/src/index.ts Co-authored-by: Nicolas Brichet <32258950+brichet@users.noreply.github.com> --- packages/commands/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 6118f03a0..a9afa5eda 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -1292,7 +1292,6 @@ export namespace CommandRegistry { if (!layout.isModifierKey(key)) { mods.push(key); // for modifier and character key strings - return mods.join(' '); } // for purely modifier key strings return mods.join(' '); From d115929ea4321e88bf8646c0f0e4c83b860dbf03 Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Thu, 9 Nov 2023 20:26:18 +0000 Subject: [PATCH 22/39] re-write --- packages/commands/src/index.ts | 51 ++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index a9afa5eda..f71b74d29 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -537,6 +537,14 @@ export class CommandRegistry { event ); + // If there is no exact match and no partial match, replay + // any suppressed events and clear the pending state. + if (!exact && !partial) { + this._replayKeydownEvents(); + this._clearPendingState(); + return; + } + // Remove modifier key only keystrokes from the current key sequence. // keystrokes that are modifier key(s) plus another key are not affected // intended functionality: Alt then Alt 1 should result to: ['Alt'] then ['Alt 1'] @@ -550,23 +558,6 @@ export class CommandRegistry { this._keystrokes.length = 0; } - - // If there is an exact match that is not excluded and no partial match, the exact match - // can be dispatched immediately. The pending state is cleared so - // the next key press starts from the default state. - // add new modifier only key commands that should be excluded to the end of the if statement - if ( - exact && - !partial && - !exact?.command.includes('application:activate-sidebar-overlays') - ) { - this._executeKeyBinding(exact); - this._clearPendingState(); - - return; - } - - // Stop propagation of the event. If there is only a partial match, // the event will be replayed if a final exact match never occurs. event.preventDefault(); @@ -575,7 +566,16 @@ export class CommandRegistry { // If there is an exact match but no partial match, the exact match // can be dispatched immediately. The pending state is cleared so // the next key press starts from the default state. - if (exact && !partial) { + if ( + exact && + !partial && + !( + exact.keys.toString() === 'Alt' || + exact.keys.toString() === 'Ctrl' || + exact.keys.toString() === 'Shift' || + exact.keys.toString() === 'Alt Shift' + ) + ) { this._executeKeyBinding(exact); this._clearPendingState(); return; @@ -635,7 +635,11 @@ export class CommandRegistry { */ private _executeKeyBinding(binding: CommandRegistry.IKeyBinding): void { let { command, args } = binding; - if (!this.hasCommand(command) || !this.isEnabled(command, args)) { + let newArgs: ReadonlyPartialJSONObject = { + _luminoEvent: { type: 'keybinding', keys: binding.keys }, + ...args + }; + if (!this.hasCommand(command) || !this.isEnabled(command, newArgs)) { let word = this.hasCommand(command) ? 'enabled' : 'registered'; let keys = binding.keys.join(', '); let msg1 = `Cannot execute key binding '${keys}':`; @@ -643,7 +647,7 @@ export class CommandRegistry { console.warn(`${msg1} ${msg2}`); return; } - this.execute(command, args); + this.execute(command, newArgs); } /** @@ -732,6 +736,8 @@ export namespace CommandRegistry { * the command. * * This may be invoked even when `isEnabled` returns `false`. + * + * If called via a keybinding the passed args will include a `_luminoEvent` that specify the event type and keys pressed for customization. */ execute: CommandFunc>; @@ -887,6 +893,8 @@ export namespace CommandRegistry { * command as grayed-out or in some other non-interactive fashion. * * The default value is `() => true`. + * + * If called via a keybinding the passed args will include a `_luminoEvent` that specify the event type and keys pressed for customization */ isEnabled?: CommandFunc; @@ -1012,7 +1020,7 @@ export namespace CommandRegistry { /** * The arguments for the command, if necessary. * - * The default value is an empty object. + * The default value is an empty object. If a command is activated by _executeKeyBinding, `args = {_luminoEvent: {type: , keys: }}` */ args?: ReadonlyPartialJSONObject; @@ -1292,6 +1300,7 @@ export namespace CommandRegistry { if (!layout.isModifierKey(key)) { mods.push(key); // for modifier and character key strings + return mods.join(' '); } // for purely modifier key strings return mods.join(' '); From 203c38a5cf0ba283708cc995cf3700142ed64e21 Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Thu, 9 Nov 2023 20:30:58 +0000 Subject: [PATCH 23/39] removed redundantcode --- packages/commands/src/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index f71b74d29..9fb2fe3f0 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -1299,8 +1299,7 @@ export namespace CommandRegistry { } if (!layout.isModifierKey(key)) { mods.push(key); - // for modifier and character key strings - return mods.join(' '); + } // for purely modifier key strings return mods.join(' '); From 1008417787a4af5e091273e62a60017cfb3563d8 Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Thu, 9 Nov 2023 20:39:04 +0000 Subject: [PATCH 24/39] Ran Prettier to fix failing test --- packages/commands/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 9fb2fe3f0..45a96f618 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -1299,7 +1299,6 @@ export namespace CommandRegistry { } if (!layout.isModifierKey(key)) { mods.push(key); - } // for purely modifier key strings return mods.join(' '); From e773b5efca68db3cddc1d4d40415627958e028fd Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Tue, 14 Nov 2023 11:57:47 +0000 Subject: [PATCH 25/39] Update packages/commands/src/index.ts Co-authored-by: Nicolas Brichet <32258950+brichet@users.noreply.github.com> --- packages/commands/src/index.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 45a96f618..67f447692 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -566,15 +566,12 @@ export class CommandRegistry { // If there is an exact match but no partial match, the exact match // can be dispatched immediately. The pending state is cleared so // the next key press starts from the default state. + const layout = getKeyboardLayout(); + const exactKeys = exact?.keys.toString().split(' '); if ( exact && !partial && - !( - exact.keys.toString() === 'Alt' || - exact.keys.toString() === 'Ctrl' || - exact.keys.toString() === 'Shift' || - exact.keys.toString() === 'Alt Shift' - ) + !exactKeys.every(key => layout.isModifierKey(key)) ) { this._executeKeyBinding(exact); this._clearPendingState(); From ef7bc6f4ee7def2e107687a78df6538af82e7419 Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Thu, 16 Nov 2023 15:59:18 +0000 Subject: [PATCH 26/39] Refactored func and test --- packages/commands/src/index.ts | 16 ++++++++-------- packages/commands/tests/src/index.spec.ts | 10 +++++++++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 67f447692..a127acd26 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -551,9 +551,7 @@ export class CommandRegistry { // Removing this code results in mod key duplication: ['Alt', 'Alt 1'] if ( // Check that only a mod key has been pressed - (event.altKey && event.key === 'Alt') || - (event.ctrlKey && event.key === 'Ctrl') || - (event.shiftKey && event.key === 'Shift') + CommandRegistry.isModifierKeyPressed(event) ) { this._keystrokes.length = 0; } @@ -566,13 +564,15 @@ export class CommandRegistry { // If there is an exact match but no partial match, the exact match // can be dispatched immediately. The pending state is cleared so // the next key press starts from the default state. + const layout = getKeyboardLayout(); const exactKeys = exact?.keys.toString().split(' '); - if ( - exact && - !partial && - !exactKeys.every(key => layout.isModifierKey(key)) - ) { + + if (exact && !partial && exactKeys) { + if (exactKeys.every(key => layout.isModifierKey(key))) { + // Do nothing as We want to delay modifier only KeyBindings + } else { + } this._executeKeyBinding(exact); this._clearPendingState(); return; diff --git a/packages/commands/tests/src/index.spec.ts b/packages/commands/tests/src/index.spec.ts index b412a9931..7079f816c 100644 --- a/packages/commands/tests/src/index.spec.ts +++ b/packages/commands/tests/src/index.spec.ts @@ -1153,7 +1153,7 @@ describe('@lumino/commands', () => { document.body.removeEventListener('keydown', keydown); }); - it('should register a key sequence', () => { + it('should ignore modifier keys pressed in the middle of key sequence', () => { let count = 0; registry.addCommand('test', { execute: () => { @@ -1172,6 +1172,14 @@ describe('@lumino/commands', () => { }) ); expect(count).to.equal(0); + // User presses `ctrl` again - this should not break the sequence. + elem.dispatchEvent( + new KeyboardEvent('keydown', { + keyCode: 17, + ctrlKey: true + }) + ); + expect(count).to.equal(0); elem.dispatchEvent( new KeyboardEvent('keydown', { keyCode: 76, // `L` key From 5a1bfd91986b8dea6d90585ab4fc684dadf152c9 Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Mon, 4 Dec 2023 17:44:08 +0000 Subject: [PATCH 27/39] modifier key bindings trigger on key holds --- packages/commands/src/index.ts | 43 ++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index a127acd26..e3044f7aa 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -556,23 +556,34 @@ export class CommandRegistry { this._keystrokes.length = 0; } + // Check if an exact match key contain only modifier key(s) + const layout = getKeyboardLayout(); + const exactKeys = exact?.keys.toString().split(' '); + this._onlyModiferKeys = exactKeys?.every( + key => layout.isModifierKey(key) === true + ) + ? true + : false; + // Stop propagation of the event. If there is only a partial match, // the event will be replayed if a final exact match never occurs. event.preventDefault(); event.stopPropagation(); + // Trigger binding if only modifier key(s) are pressed and held for 500ms + if (this._onlyModiferKeys && exact) { + window.setTimeout(() => { + if (event.repeat === true && exact) { + this._executeKeyBinding(exact); + this._clearPendingState(); + } + }, Private.CHORD_TIMEOUT / 2); + } + // If there is an exact match but no partial match, the exact match // can be dispatched immediately. The pending state is cleared so // the next key press starts from the default state. - - const layout = getKeyboardLayout(); - const exactKeys = exact?.keys.toString().split(' '); - - if (exact && !partial && exactKeys) { - if (exactKeys.every(key => layout.isModifierKey(key))) { - // Do nothing as We want to delay modifier only KeyBindings - } else { - } + if (exact && !partial && !this._onlyModiferKeys) { this._executeKeyBinding(exact); this._clearPendingState(); return; @@ -581,12 +592,14 @@ export class CommandRegistry { // If there is both an exact match and a partial match, the exact // match is stored for future dispatch in case the timer expires // before a more specific match is triggered. - if (exact) { + if (exact && !this._onlyModiferKeys) { this._exactKeyMatch = exact; } - // Store the event for possible playback in the future. - this._keydownEvents.push(event); + // Store the event of non modifier key(s) for possible playback in the future. + if (!this._onlyModiferKeys) { + this._keydownEvents.push(event); + } // (Re)start the timer to dispatch the most recent exact match // in case the partial match fails to result in an exact match. @@ -655,6 +668,7 @@ export class CommandRegistry { this._exactKeyMatch = null; this._keystrokes.length = 0; this._keydownEvents.length = 0; + this._onlyModiferKeys = false; } /** @@ -662,7 +676,7 @@ export class CommandRegistry { */ private _onPendingTimeout(): void { this._timerID = 0; - if (this._exactKeyMatch) { + if (this._exactKeyMatch && !this._onlyModiferKeys) { this._executeKeyBinding(this._exactKeyMatch); } else { this._replayKeydownEvents(); @@ -672,6 +686,7 @@ export class CommandRegistry { private _timerID = 0; private _replaying = false; + private _onlyModiferKeys = false; private _keystrokes: string[] = []; private _keydownEvents: KeyboardEvent[] = []; private _keyBindings: CommandRegistry.IKeyBinding[] = []; @@ -1296,6 +1311,8 @@ export namespace CommandRegistry { } if (!layout.isModifierKey(key)) { mods.push(key); + // for modifier and character key strings + return mods.join(' '); } // for purely modifier key strings return mods.join(' '); From 5e13b06a80e7a8ad6534215d9dbbb901b5a554a7 Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Tue, 5 Dec 2023 16:21:12 +0000 Subject: [PATCH 28/39] reverted changed test --- packages/commands/tests/src/index.spec.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/commands/tests/src/index.spec.ts b/packages/commands/tests/src/index.spec.ts index e15974d2d..991200292 100644 --- a/packages/commands/tests/src/index.spec.ts +++ b/packages/commands/tests/src/index.spec.ts @@ -1207,16 +1207,28 @@ describe('@lumino/commands', () => { selector: `#${elem.id}`, command: 'test' }); + let eventShift = new KeyboardEvent('keydown', { + keyCode: 16, + shiftKey: true + }); let eventK = new KeyboardEvent('keydown', { keyCode: 75, shiftKey: true }); + let eventCtrl = new KeyboardEvent('keydown', { + keyCode: 17, + ctrlKey: true + }); let eventL = new KeyboardEvent('keydown', { keyCode: 76, ctrlKey: true }); + elem.dispatchEvent(eventShift); + expect(count).to.equal(0); elem.dispatchEvent(eventK); expect(count).to.equal(0); + elem.dispatchEvent(eventCtrl); + expect(count).to.equal(0); elem.dispatchEvent(eventL); expect(count).to.equal(1); }); @@ -1329,11 +1341,11 @@ describe('@lumino/commands', () => { expect(keystroke).to.equal(''); }); - it('should return keys that are marked as modifier in keyboard layout', () => { + it('should return nothing for keys that are marked as modifier in keyboard layout', () => { let keystroke = CommandRegistry.keystrokeForKeydownEvent( new KeyboardEvent('keydown', { keyCode: 17, ctrlKey: true }) ); - expect(keystroke).to.equal('Ctrl'); + expect(keystroke).to.equal(''); }); }); }); From 4468167c65994180721631c730f1baa20fca3097 Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Thu, 7 Dec 2023 10:50:21 +0000 Subject: [PATCH 29/39] undo reverted test changes --- packages/commands/tests/src/index.spec.ts | 36 ++++------------------- 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/packages/commands/tests/src/index.spec.ts b/packages/commands/tests/src/index.spec.ts index 991200292..0741a113b 100644 --- a/packages/commands/tests/src/index.spec.ts +++ b/packages/commands/tests/src/index.spec.ts @@ -701,13 +701,9 @@ describe('@lumino/commands', () => { describe('#processKeydownEvent()', () => { it('should dispatch on a correct keyboard event', () => { let called = false; - let _luminoEventType; - let _luminoEventKeys; registry.addCommand('test', { - execute: args => { + execute: () => { called = true; - _luminoEventType = (args._luminoEvent as ReadonlyJSONObject).type; - _luminoEventKeys = (args._luminoEvent as ReadonlyJSONObject).keys; } }); registry.addKeyBinding({ @@ -722,8 +718,6 @@ describe('@lumino/commands', () => { }) ); expect(called).to.equal(true); - expect(_luminoEventType).to.equal('keybinding'); - expect(_luminoEventKeys).to.contain('Ctrl ;'); }); it('should not dispatch on a suppressed node', () => { @@ -1159,7 +1153,7 @@ describe('@lumino/commands', () => { document.body.removeEventListener('keydown', keydown); }); - it('should ignore modifier keys pressed in the middle of key sequence', () => { + it('should register a key sequence', () => { let count = 0; registry.addCommand('test', { execute: () => { @@ -1178,14 +1172,6 @@ describe('@lumino/commands', () => { }) ); expect(count).to.equal(0); - // User presses `ctrl` again - this should not break the sequence. - elem.dispatchEvent( - new KeyboardEvent('keydown', { - keyCode: 17, - ctrlKey: true - }) - ); - expect(count).to.equal(0); elem.dispatchEvent( new KeyboardEvent('keydown', { keyCode: 76, // `L` key @@ -1207,28 +1193,16 @@ describe('@lumino/commands', () => { selector: `#${elem.id}`, command: 'test' }); - let eventShift = new KeyboardEvent('keydown', { - keyCode: 16, - shiftKey: true - }); let eventK = new KeyboardEvent('keydown', { keyCode: 75, shiftKey: true }); - let eventCtrl = new KeyboardEvent('keydown', { - keyCode: 17, - ctrlKey: true - }); let eventL = new KeyboardEvent('keydown', { keyCode: 76, ctrlKey: true }); - elem.dispatchEvent(eventShift); - expect(count).to.equal(0); elem.dispatchEvent(eventK); expect(count).to.equal(0); - elem.dispatchEvent(eventCtrl); - expect(count).to.equal(0); elem.dispatchEvent(eventL); expect(count).to.equal(1); }); @@ -1341,12 +1315,12 @@ describe('@lumino/commands', () => { expect(keystroke).to.equal(''); }); - it('should return nothing for keys that are marked as modifier in keyboard layout', () => { + it('should return keys that are marked as modifier in keyboard layout', () => { let keystroke = CommandRegistry.keystrokeForKeydownEvent( new KeyboardEvent('keydown', { keyCode: 17, ctrlKey: true }) ); - expect(keystroke).to.equal(''); + expect(keystroke).to.equal('Ctrl'); }); }); }); -}); +}); \ No newline at end of file From 109298a8a0fbe7cd6407a57274e83ad2335429fa Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Thu, 7 Dec 2023 10:53:32 +0000 Subject: [PATCH 30/39] Ran Prettier --- packages/commands/tests/src/index.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/commands/tests/src/index.spec.ts b/packages/commands/tests/src/index.spec.ts index 0741a113b..b412a9931 100644 --- a/packages/commands/tests/src/index.spec.ts +++ b/packages/commands/tests/src/index.spec.ts @@ -1323,4 +1323,4 @@ describe('@lumino/commands', () => { }); }); }); -}); \ No newline at end of file +}); From b004546b16c5ebb83c35f46a31a40cbc2d1200a6 Mon Sep 17 00:00:00 2001 From: Nicolas Brichet Date: Tue, 12 Dec 2023 14:55:10 +0100 Subject: [PATCH 31/39] Starts the timer for mod keys only before updating the keystrokes list --- packages/application/src/index.ts | 17 ++++ packages/commands/src/index.ts | 98 +++++++++++++---------- packages/commands/tests/src/index.spec.ts | 30 ++++++- 3 files changed, 101 insertions(+), 44 deletions(-) diff --git a/packages/application/src/index.ts b/packages/application/src/index.ts index e83fbbd64..6c17dcaec 100644 --- a/packages/application/src/index.ts +++ b/packages/application/src/index.ts @@ -575,6 +575,9 @@ export class Application { case 'keydown': this.evtKeydown(event as KeyboardEvent); break; + case 'keyup': + this.evtKeyup(event as KeyboardEvent); + break; case 'contextmenu': this.evtContextMenu(event as PointerEvent); break; @@ -610,6 +613,7 @@ export class Application { protected addEventListeners(): void { document.addEventListener('contextmenu', this); document.addEventListener('keydown', this, !this._bubblingKeydown); + document.addEventListener('keyup', this, !this._bubblingKeydown); window.addEventListener('resize', this); } @@ -626,6 +630,19 @@ export class Application { this.commands.processKeydownEvent(event); } + /** + * A method invoked on a document `'keyup'` event. + * + * #### Notes + * The default implementation of this method invokes the key up + * processing method of the application command registry. + * + * A subclass may reimplement this method as needed. + */ + protected evtKeyup(event: KeyboardEvent): void { + this.commands.processKeyupEvent(event); + } + /** * A method invoked on a document `'contextmenu'` event. * diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index e3044f7aa..bbbf83e5e 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -527,6 +527,26 @@ export class CommandRegistry { return; } + // Check that only mod key(s) have been pressed. + if (CommandRegistry.isModifierKeyPressed(event)) { + // Find the exact match for the modifier keys. + let { exact } = Private.matchKeyBinding( + this._keyBindings, + [keystroke], + event + ); + if (exact) { + // If the mod keys match an exact shortcut, start a dedicated timer. + event.preventDefault(); + event.stopPropagation(); + this._startModifierTimer(exact); + } else { + // Otherwise stop potential existing timer. + this._clearModifierTimer(); + } + return; + } + // Add the keystroke to the current key sequence. this._keystrokes.push(keystroke); @@ -545,45 +565,15 @@ export class CommandRegistry { return; } - // Remove modifier key only keystrokes from the current key sequence. - // keystrokes that are modifier key(s) plus another key are not affected - // intended functionality: Alt then Alt 1 should result to: ['Alt'] then ['Alt 1'] - // Removing this code results in mod key duplication: ['Alt', 'Alt 1'] - if ( - // Check that only a mod key has been pressed - CommandRegistry.isModifierKeyPressed(event) - ) { - this._keystrokes.length = 0; - } - - // Check if an exact match key contain only modifier key(s) - const layout = getKeyboardLayout(); - const exactKeys = exact?.keys.toString().split(' '); - this._onlyModiferKeys = exactKeys?.every( - key => layout.isModifierKey(key) === true - ) - ? true - : false; - // Stop propagation of the event. If there is only a partial match, // the event will be replayed if a final exact match never occurs. event.preventDefault(); event.stopPropagation(); - // Trigger binding if only modifier key(s) are pressed and held for 500ms - if (this._onlyModiferKeys && exact) { - window.setTimeout(() => { - if (event.repeat === true && exact) { - this._executeKeyBinding(exact); - this._clearPendingState(); - } - }, Private.CHORD_TIMEOUT / 2); - } - // If there is an exact match but no partial match, the exact match // can be dispatched immediately. The pending state is cleared so // the next key press starts from the default state. - if (exact && !partial && !this._onlyModiferKeys) { + if (exact && !partial) { this._executeKeyBinding(exact); this._clearPendingState(); return; @@ -592,20 +582,48 @@ export class CommandRegistry { // If there is both an exact match and a partial match, the exact // match is stored for future dispatch in case the timer expires // before a more specific match is triggered. - if (exact && !this._onlyModiferKeys) { + if (exact) { this._exactKeyMatch = exact; } - // Store the event of non modifier key(s) for possible playback in the future. - if (!this._onlyModiferKeys) { - this._keydownEvents.push(event); - } + // Store the event for possible playback in the future. + this._keydownEvents.push(event); // (Re)start the timer to dispatch the most recent exact match // in case the partial match fails to result in an exact match. this._startTimer(); } + /** + * Process a ``keyup`` event to clear the timer on the modifier id exists. + * + * @param event - The event object for a `'keydown'` event. + */ + processKeyupEvent(event: KeyboardEvent): void { + this._clearModifierTimer(); + } + + /** + * Start or restart the timeout on the modifier keys. + * + * This timeout will end only if the keys are hold. + */ + private _startModifierTimer(exact: CommandRegistry.IKeyBinding): void { + this._clearModifierTimer(); + this._timerModifierID = window.setTimeout(() => { + this._executeKeyBinding(exact); + }, Private.CHORD_TIMEOUT); + } + + /** + * Clear the timeout on modifier keys. + */ + private _clearModifierTimer(): void { + if (this._timerModifierID !== 0) { + clearTimeout(this._timerModifierID); + this._timerModifierID = 0; + } + } /** * Start or restart the pending timeout. */ @@ -668,7 +686,6 @@ export class CommandRegistry { this._exactKeyMatch = null; this._keystrokes.length = 0; this._keydownEvents.length = 0; - this._onlyModiferKeys = false; } /** @@ -676,7 +693,7 @@ export class CommandRegistry { */ private _onPendingTimeout(): void { this._timerID = 0; - if (this._exactKeyMatch && !this._onlyModiferKeys) { + if (this._exactKeyMatch) { this._executeKeyBinding(this._exactKeyMatch); } else { this._replayKeydownEvents(); @@ -685,8 +702,8 @@ export class CommandRegistry { } private _timerID = 0; + private _timerModifierID = 0; private _replaying = false; - private _onlyModiferKeys = false; private _keystrokes: string[] = []; private _keydownEvents: KeyboardEvent[] = []; private _keyBindings: CommandRegistry.IKeyBinding[] = []; @@ -1296,7 +1313,6 @@ export namespace CommandRegistry { let layout = getKeyboardLayout(); let key = layout.keyForKeydownEvent(event); let mods = []; - if (event.ctrlKey) { mods.push('Ctrl'); } @@ -1311,8 +1327,6 @@ export namespace CommandRegistry { } if (!layout.isModifierKey(key)) { mods.push(key); - // for modifier and character key strings - return mods.join(' '); } // for purely modifier key strings return mods.join(' '); diff --git a/packages/commands/tests/src/index.spec.ts b/packages/commands/tests/src/index.spec.ts index b412a9931..a7efd6fff 100644 --- a/packages/commands/tests/src/index.spec.ts +++ b/packages/commands/tests/src/index.spec.ts @@ -701,9 +701,13 @@ describe('@lumino/commands', () => { describe('#processKeydownEvent()', () => { it('should dispatch on a correct keyboard event', () => { let called = false; + let _luminoEventType; + let _luminoEventKeys; registry.addCommand('test', { - execute: () => { + execute: args => { called = true; + _luminoEventType = (args._luminoEvent as ReadonlyJSONObject).type; + _luminoEventKeys = (args._luminoEvent as ReadonlyJSONObject).keys; } }); registry.addKeyBinding({ @@ -718,6 +722,8 @@ describe('@lumino/commands', () => { }) ); expect(called).to.equal(true); + expect(_luminoEventType).to.equal('keybinding'); + expect(_luminoEventKeys).to.contain('Ctrl ;'); }); it('should not dispatch on a suppressed node', () => { @@ -1153,7 +1159,7 @@ describe('@lumino/commands', () => { document.body.removeEventListener('keydown', keydown); }); - it('should register a key sequence', () => { + it('should ignore modifier keys pressed in the middle of key sequence', () => { let count = 0; registry.addCommand('test', { execute: () => { @@ -1172,6 +1178,14 @@ describe('@lumino/commands', () => { }) ); expect(count).to.equal(0); + // User presses `ctrl` again - this should not break the sequence. + elem.dispatchEvent( + new KeyboardEvent('keydown', { + keyCode: 17, + ctrlKey: true + }) + ); + expect(count).to.equal(0); elem.dispatchEvent( new KeyboardEvent('keydown', { keyCode: 76, // `L` key @@ -1193,16 +1207,28 @@ describe('@lumino/commands', () => { selector: `#${elem.id}`, command: 'test' }); + let eventShift = new KeyboardEvent('keydown', { + keyCode: 16, + shiftKey: true + }); let eventK = new KeyboardEvent('keydown', { keyCode: 75, shiftKey: true }); + let eventCtrl = new KeyboardEvent('keydown', { + keyCode: 17, + ctrlKey: true + }); let eventL = new KeyboardEvent('keydown', { keyCode: 76, ctrlKey: true }); + elem.dispatchEvent(eventShift); + expect(count).to.equal(0); elem.dispatchEvent(eventK); expect(count).to.equal(0); + elem.dispatchEvent(eventCtrl); + expect(count).to.equal(0); elem.dispatchEvent(eventL); expect(count).to.equal(1); }); From 088a379938628446fd84bab3cd973ca9ce10e93e Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Thu, 14 Dec 2023 16:46:17 +0000 Subject: [PATCH 32/39] Update packages/commands/src/index.ts Co-authored-by: Nicolas Brichet <32258950+brichet@users.noreply.github.com> --- packages/commands/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 08c5b6669..06f560434 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -595,7 +595,7 @@ export class CommandRegistry { } /** - * Process a ``keyup`` event to clear the timer on the modifier id exists. + * Process a ``keyup`` event to clear the timer on the modifier, if it exists. * * @param event - The event object for a `'keydown'` event. */ From d670fceb7a20341fe238a236147dc2ec88e3284e Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Fri, 15 Dec 2023 14:53:54 +0000 Subject: [PATCH 33/39] added new variable with a shorter delay --- packages/commands/src/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index bbbf83e5e..de15d449a 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -612,7 +612,7 @@ export class CommandRegistry { this._clearModifierTimer(); this._timerModifierID = window.setTimeout(() => { this._executeKeyBinding(exact); - }, Private.CHORD_TIMEOUT); + }, Private.ModifierKey_TIMEOUT); } /** @@ -1342,6 +1342,11 @@ namespace Private { */ export const CHORD_TIMEOUT = 1000; + /** + * The timeout in ms for triggering a modifer key binding. + */ + export const ModifierKey_TIMEOUT = 500; + /** * A convenience type alias for a command func. */ From 57d8170fdea50ebd63d4b6c52f431469120e5646 Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Fri, 15 Dec 2023 14:58:54 +0000 Subject: [PATCH 34/39] ran yarn run api and committing changes --- review/api/application.api.md | 1 + review/api/commands.api.md | 1 + review/api/widgets.api.md | 1 + 3 files changed, 3 insertions(+) diff --git a/review/api/application.api.md b/review/api/application.api.md index 31b0d2168..7c884b252 100644 --- a/review/api/application.api.md +++ b/review/api/application.api.md @@ -24,6 +24,7 @@ export class Application { deregisterPlugin(id: string, force?: boolean): void; protected evtContextMenu(event: PointerEvent): void; protected evtKeydown(event: KeyboardEvent): void; + protected evtKeyup(event: KeyboardEvent): void; protected evtResize(event: Event): void; getPluginDescription(id: string): string; handleEvent(event: Event): void; diff --git a/review/api/commands.api.md b/review/api/commands.api.md index 81ed057ee..7b95d2fee 100644 --- a/review/api/commands.api.md +++ b/review/api/commands.api.md @@ -36,6 +36,7 @@ export class CommandRegistry { mnemonic(id: string, args?: ReadonlyPartialJSONObject): number; notifyCommandChanged(id?: string): void; processKeydownEvent(event: KeyboardEvent): void; + processKeyupEvent(event: KeyboardEvent): void; usage(id: string, args?: ReadonlyPartialJSONObject): string; } diff --git a/review/api/widgets.api.md b/review/api/widgets.api.md index 7462b947b..c8b3e192e 100644 --- a/review/api/widgets.api.md +++ b/review/api/widgets.api.md @@ -1187,6 +1187,7 @@ export namespace TabBar { renderCloseIcon(data: IRenderData): VirtualElement; renderIcon(data: IRenderData): VirtualElement; renderLabel(data: IRenderData): VirtualElement; + renderOverlay(dataset: ElementDataset): VirtualElement; renderTab(data: IRenderData): VirtualElement; } const defaultRenderer: Renderer; From b3c423794432b851180c9738bc6aa011565b664d Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Fri, 15 Dec 2023 15:02:40 +0000 Subject: [PATCH 35/39] ran linter --- packages/commands/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 64254a5cd..c3d36175b 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -1342,10 +1342,10 @@ namespace Private { */ export const CHORD_TIMEOUT = 1000; - /** + /** * The timeout in ms for triggering a modifer key binding. */ - export const ModifierKey_TIMEOUT = 500; + export const ModifierKey_TIMEOUT = 500; /** * A convenience type alias for a command func. From aef96855690519cd6923e060bbdbd0fe156aa1e0 Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Mon, 11 Mar 2024 14:50:00 +0000 Subject: [PATCH 36/39] refactor variable to camelcase --- packages/commands/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index c3d36175b..34cf37cab 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -612,7 +612,7 @@ export class CommandRegistry { this._clearModifierTimer(); this._timerModifierID = window.setTimeout(() => { this._executeKeyBinding(exact); - }, Private.ModifierKey_TIMEOUT); + }, Private.modifierkey_timeout); } /** @@ -1345,7 +1345,7 @@ namespace Private { /** * The timeout in ms for triggering a modifer key binding. */ - export const ModifierKey_TIMEOUT = 500; + export const modifierkey_timeout = 500; /** * A convenience type alias for a command func. From 187356490a2d89b04d38100b3b751dbb49121ee4 Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Tue, 12 Mar 2024 10:18:05 +0000 Subject: [PATCH 37/39] refactor variable to camelcase --- packages/commands/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 34cf37cab..bc7dc61c9 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -612,7 +612,7 @@ export class CommandRegistry { this._clearModifierTimer(); this._timerModifierID = window.setTimeout(() => { this._executeKeyBinding(exact); - }, Private.modifierkey_timeout); + }, Private.modifierkeyTimeOut); } /** @@ -1345,7 +1345,7 @@ namespace Private { /** * The timeout in ms for triggering a modifer key binding. */ - export const modifierkey_timeout = 500; + export const modifierkeyTimeOut = 500; /** * A convenience type alias for a command func. From 4b67ed03b4014effa727d04c9b0d6270927b96dd Mon Sep 17 00:00:00 2001 From: g547315 <132482386+g547315@users.noreply.github.com> Date: Wed, 13 Mar 2024 09:37:49 +0000 Subject: [PATCH 38/39] updated api --- review/api/widgets.api.md | 1 - 1 file changed, 1 deletion(-) diff --git a/review/api/widgets.api.md b/review/api/widgets.api.md index c8b3e192e..7462b947b 100644 --- a/review/api/widgets.api.md +++ b/review/api/widgets.api.md @@ -1187,7 +1187,6 @@ export namespace TabBar { renderCloseIcon(data: IRenderData): VirtualElement; renderIcon(data: IRenderData): VirtualElement; renderLabel(data: IRenderData): VirtualElement; - renderOverlay(dataset: ElementDataset): VirtualElement; renderTab(data: IRenderData): VirtualElement; } const defaultRenderer: Renderer; From 73873be33d02817857959347af291def0eeb6715 Mon Sep 17 00:00:00 2001 From: Nicolas Brichet Date: Thu, 4 Apr 2024 15:38:29 +0200 Subject: [PATCH 39/39] Clear modifier timeout if an exact match is found --- packages/commands/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index ec62ee21e..4925dd9f7 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -733,6 +733,7 @@ export class CommandRegistry { */ private _clearPendingState(): void { this._clearTimer(); + this._clearModifierTimer(); this._exactKeyMatch = null; this._keystrokes.length = 0; this._keydownEvents.length = 0;