Skip to content

Commit

Permalink
Adding Hotkey.allowOverlap flag (to allow "conflicts"/overlap of hotk…
Browse files Browse the repository at this point in the history
…eys), see #1621
  • Loading branch information
jonathanolson committed Mar 28, 2024
1 parent 5868022 commit f0155e5
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 11 deletions.
9 changes: 7 additions & 2 deletions js/input/Hotkey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ type SelfOptions = {
// Fire continuously at this interval (milliseconds)
fireOnHoldCustomInterval?: number;

// TODO: consider attach:false https://github.com/phetsims/scenery/issues/1621
// For each main `key`, the hotkey system will only allow one hotkey with allowOverlap:false to be active at any time.
// This is provided to allow multiple hotkeys with the same keys to fire. Default is false.
allowOverlap?: boolean;
};

export type HotkeyOptions = SelfOptions & EnabledComponentOptions;
Expand All @@ -74,6 +76,7 @@ export default class Hotkey extends EnabledComponent {
public readonly fireOnDown: boolean;
public readonly fireOnHold: boolean;
public readonly fireOnHoldTiming: HotkeyFireOnHoldTiming;
public readonly allowOverlap: boolean;

// A Property that tracks whether the hotkey is currently pressed.
// Will be true if it meets the following conditions:
Expand Down Expand Up @@ -106,7 +109,8 @@ export default class Hotkey extends EnabledComponent {
fireOnHold: false,
fireOnHoldTiming: 'browser',
fireOnHoldCustomDelay: 400,
fireOnHoldCustomInterval: 100
fireOnHoldCustomInterval: 100,
allowOverlap: false
}, providedOptions );

super( options );
Expand All @@ -119,6 +123,7 @@ export default class Hotkey extends EnabledComponent {
this.fireOnDown = options.fireOnDown;
this.fireOnHold = options.fireOnHold;
this.fireOnHoldTiming = options.fireOnHoldTiming;
this.allowOverlap = options.allowOverlap;

// Create a timer to handle the optional fire-on-hold feature.
if ( this.fireOnHold && this.fireOnHoldTiming === 'custom' ) {
Expand Down
22 changes: 13 additions & 9 deletions js/input/hotkeyManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,12 @@ class HotkeyManager {
* 2. All modifier keys in the hotkey's modifierKeys pressed
* 3. All modifier keys not in the hotkey's modifierKeys (but in the other hotkeys above) not pressed
*/
private getHotkeyForMainKey( mainKey: EnglishKey ): Hotkey | null {
private getHotkeysForMainKey( mainKey: EnglishKey ): Hotkey[] {
const englishKeysDown = this.englishKeysDownProperty.value;

// If the main key isn't down, there's no way it could be active
if ( !englishKeysDown.has( mainKey ) ) {
return null;
return [];
}

const compatibleKeys = [ ...this.enabledHotkeysProperty.value ].filter( hotkey => {
Expand All @@ -205,9 +205,13 @@ class HotkeyManager {
} );
} );

assert && assert( compatibleKeys.length < 2, `Key conflict detected: ${compatibleKeys.map( hotkey => hotkey.getHotkeyString() )}` );
if ( assert ) {
const conflictingKeys = compatibleKeys.filter( hotkey => !hotkey.allowOverlap );

return compatibleKeys[ 0 ] ?? null;
assert && assert( conflictingKeys.length < 2, `Key conflict detected: ${conflictingKeys.map( hotkey => hotkey.getHotkeyString() )}` );
}

return compatibleKeys;
}

/**
Expand All @@ -216,7 +220,7 @@ class HotkeyManager {
*/
private updateHotkeyStatus(): void {
for ( const hotkey of this.enabledHotkeysProperty.value ) {
const shouldBeActive = this.getHotkeyForMainKey( hotkey.key ) === hotkey;
const shouldBeActive = this.getHotkeysForMainKey( hotkey.key ).includes( hotkey );
const isActive = this.activeHotkeys.has( hotkey );

if ( shouldBeActive && !isActive ) {
Expand Down Expand Up @@ -271,8 +275,8 @@ class HotkeyManager {

this.englishKeysDownProperty.value = new Set( [ ...this.englishKeysDownProperty.value, englishKey ] );

const hotkey = this.getHotkeyForMainKey( englishKey );
if ( hotkey ) {
const hotkeys = this.getHotkeysForMainKey( englishKey );
for ( const hotkey of hotkeys ) {
this.addActiveHotkey( hotkey, keyboardEvent, true );
}
}
Expand All @@ -282,8 +286,8 @@ class HotkeyManager {

private onKeyUp( englishKey: EnglishKey, keyboardEvent: KeyboardEvent ): void {

const hotkey = this.getHotkeyForMainKey( englishKey );
if ( hotkey ) {
const hotkeys = this.getHotkeysForMainKey( englishKey );
for ( const hotkey of hotkeys ) {
this.removeActiveHotkey( hotkey, keyboardEvent, true );
}

Expand Down

0 comments on commit f0155e5

Please sign in to comment.