1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2023 The Android Open Source Project 2*6dbdd20aSAndroid Build Coastguard Worker// 3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*6dbdd20aSAndroid Build Coastguard Worker// 7*6dbdd20aSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*6dbdd20aSAndroid Build Coastguard Worker// 9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License. 14*6dbdd20aSAndroid Build Coastguard Worker 15*6dbdd20aSAndroid Build Coastguard Worker// This module provides hotkey detection using type-safe human-readable strings. 16*6dbdd20aSAndroid Build Coastguard Worker// 17*6dbdd20aSAndroid Build Coastguard Worker// The basic premise is this: Let's say you have a KeyboardEvent |event|, and 18*6dbdd20aSAndroid Build Coastguard Worker// you wanted to check whether it contains the hotkey 'Ctrl+O', you can execute 19*6dbdd20aSAndroid Build Coastguard Worker// the following function: 20*6dbdd20aSAndroid Build Coastguard Worker// 21*6dbdd20aSAndroid Build Coastguard Worker// checkHotkey('Shift+O', event); 22*6dbdd20aSAndroid Build Coastguard Worker// 23*6dbdd20aSAndroid Build Coastguard Worker// ...which will evaluate to true if 'Shift+O' is discovered in the event. 24*6dbdd20aSAndroid Build Coastguard Worker// 25*6dbdd20aSAndroid Build Coastguard Worker// This will only trigger when O is pressed while the Shift key is held, not O 26*6dbdd20aSAndroid Build Coastguard Worker// on it's own, and not if other modifiers such as Alt or Ctrl were also held. 27*6dbdd20aSAndroid Build Coastguard Worker// 28*6dbdd20aSAndroid Build Coastguard Worker// Modifiers include 'Shift', 'Ctrl', 'Alt', and 'Mod': 29*6dbdd20aSAndroid Build Coastguard Worker// - 'Shift' and 'Ctrl' are fairly self explanatory. 30*6dbdd20aSAndroid Build Coastguard Worker// - 'Alt' is 'option' on Macs. 31*6dbdd20aSAndroid Build Coastguard Worker// - 'Mod' is a special modifier which means 'Ctrl' on PC and 'Cmd' on Mac. 32*6dbdd20aSAndroid Build Coastguard Worker// Modifiers may be combined in various ways - check the |Modifier| type. 33*6dbdd20aSAndroid Build Coastguard Worker// 34*6dbdd20aSAndroid Build Coastguard Worker// By default hotkeys will not register when the event target is inside an 35*6dbdd20aSAndroid Build Coastguard Worker// editable element, such as <textarea> and some <input>s. 36*6dbdd20aSAndroid Build Coastguard Worker// Prefixing a hotkey with a bang '!' relaxes is requirement, meaning the hotkey 37*6dbdd20aSAndroid Build Coastguard Worker// will register inside editable fields. 38*6dbdd20aSAndroid Build Coastguard Worker 39*6dbdd20aSAndroid Build Coastguard Worker// E.g. '!Mod+Shift+P' will register when pressed when a text box has focus but 40*6dbdd20aSAndroid Build Coastguard Worker// 'Mod+Shift+P' (no bang) will not. 41*6dbdd20aSAndroid Build Coastguard Worker// Warning: Be careful using this with single key hotkeys, e.g. '!P' is usually 42*6dbdd20aSAndroid Build Coastguard Worker// never what you want! 43*6dbdd20aSAndroid Build Coastguard Worker// 44*6dbdd20aSAndroid Build Coastguard Worker// Some single-key hotkeys like '?' and '!' normally cannot be activated in 45*6dbdd20aSAndroid Build Coastguard Worker// without also pressing shift key, so the shift requirement is relaxed for 46*6dbdd20aSAndroid Build Coastguard Worker// these keys. 47*6dbdd20aSAndroid Build Coastguard Worker 48*6dbdd20aSAndroid Build Coastguard Workerimport {elementIsEditable} from './dom_utils'; 49*6dbdd20aSAndroid Build Coastguard Worker 50*6dbdd20aSAndroid Build Coastguard Workertype Alphabet = 51*6dbdd20aSAndroid Build Coastguard Worker | 'A' 52*6dbdd20aSAndroid Build Coastguard Worker | 'B' 53*6dbdd20aSAndroid Build Coastguard Worker | 'C' 54*6dbdd20aSAndroid Build Coastguard Worker | 'D' 55*6dbdd20aSAndroid Build Coastguard Worker | 'E' 56*6dbdd20aSAndroid Build Coastguard Worker | 'F' 57*6dbdd20aSAndroid Build Coastguard Worker | 'G' 58*6dbdd20aSAndroid Build Coastguard Worker | 'H' 59*6dbdd20aSAndroid Build Coastguard Worker | 'I' 60*6dbdd20aSAndroid Build Coastguard Worker | 'J' 61*6dbdd20aSAndroid Build Coastguard Worker | 'K' 62*6dbdd20aSAndroid Build Coastguard Worker | 'L' 63*6dbdd20aSAndroid Build Coastguard Worker | 'M' 64*6dbdd20aSAndroid Build Coastguard Worker | 'N' 65*6dbdd20aSAndroid Build Coastguard Worker | 'O' 66*6dbdd20aSAndroid Build Coastguard Worker | 'P' 67*6dbdd20aSAndroid Build Coastguard Worker | 'Q' 68*6dbdd20aSAndroid Build Coastguard Worker | 'R' 69*6dbdd20aSAndroid Build Coastguard Worker | 'S' 70*6dbdd20aSAndroid Build Coastguard Worker | 'T' 71*6dbdd20aSAndroid Build Coastguard Worker | 'U' 72*6dbdd20aSAndroid Build Coastguard Worker | 'V' 73*6dbdd20aSAndroid Build Coastguard Worker | 'W' 74*6dbdd20aSAndroid Build Coastguard Worker | 'X' 75*6dbdd20aSAndroid Build Coastguard Worker | 'Y' 76*6dbdd20aSAndroid Build Coastguard Worker | 'Z'; 77*6dbdd20aSAndroid Build Coastguard Workertype Number = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'; 78*6dbdd20aSAndroid Build Coastguard Workertype Special = 79*6dbdd20aSAndroid Build Coastguard Worker | 'Enter' 80*6dbdd20aSAndroid Build Coastguard Worker | 'Escape' 81*6dbdd20aSAndroid Build Coastguard Worker | 'Delete' 82*6dbdd20aSAndroid Build Coastguard Worker | '/' 83*6dbdd20aSAndroid Build Coastguard Worker | '?' 84*6dbdd20aSAndroid Build Coastguard Worker | '!' 85*6dbdd20aSAndroid Build Coastguard Worker | 'Space' 86*6dbdd20aSAndroid Build Coastguard Worker | 'ArrowUp' 87*6dbdd20aSAndroid Build Coastguard Worker | 'ArrowDown' 88*6dbdd20aSAndroid Build Coastguard Worker | 'ArrowLeft' 89*6dbdd20aSAndroid Build Coastguard Worker | 'ArrowRight' 90*6dbdd20aSAndroid Build Coastguard Worker | '[' 91*6dbdd20aSAndroid Build Coastguard Worker | ']' 92*6dbdd20aSAndroid Build Coastguard Worker | ',' 93*6dbdd20aSAndroid Build Coastguard Worker | '.'; 94*6dbdd20aSAndroid Build Coastguard Workerexport type Key = Alphabet | Number | Special; 95*6dbdd20aSAndroid Build Coastguard Workerexport type Modifier = 96*6dbdd20aSAndroid Build Coastguard Worker | '' 97*6dbdd20aSAndroid Build Coastguard Worker | 'Mod+' 98*6dbdd20aSAndroid Build Coastguard Worker | 'Shift+' 99*6dbdd20aSAndroid Build Coastguard Worker | 'Ctrl+' 100*6dbdd20aSAndroid Build Coastguard Worker | 'Alt+' 101*6dbdd20aSAndroid Build Coastguard Worker | 'Mod+Shift+' 102*6dbdd20aSAndroid Build Coastguard Worker | 'Mod+Alt+' 103*6dbdd20aSAndroid Build Coastguard Worker | 'Mod+Shift+Alt+' 104*6dbdd20aSAndroid Build Coastguard Worker | 'Ctrl+Shift+' 105*6dbdd20aSAndroid Build Coastguard Worker | 'Ctrl+Alt' 106*6dbdd20aSAndroid Build Coastguard Worker | 'Ctrl+Shift+Alt'; 107*6dbdd20aSAndroid Build Coastguard Workertype AllowInEditable = '!' | ''; 108*6dbdd20aSAndroid Build Coastguard Workerexport type Hotkey = `${AllowInEditable}${Modifier}${Key}`; 109*6dbdd20aSAndroid Build Coastguard Worker 110*6dbdd20aSAndroid Build Coastguard Worker// The following list of keys cannot be pressed wither with or without the 111*6dbdd20aSAndroid Build Coastguard Worker// presence of the Shift modifier on most keyboard layouts. Thus we should 112*6dbdd20aSAndroid Build Coastguard Worker// ignore shift in these cases. 113*6dbdd20aSAndroid Build Coastguard Workerconst shiftExceptions = [ 114*6dbdd20aSAndroid Build Coastguard Worker '0', 115*6dbdd20aSAndroid Build Coastguard Worker '1', 116*6dbdd20aSAndroid Build Coastguard Worker '2', 117*6dbdd20aSAndroid Build Coastguard Worker '3', 118*6dbdd20aSAndroid Build Coastguard Worker '4', 119*6dbdd20aSAndroid Build Coastguard Worker '5', 120*6dbdd20aSAndroid Build Coastguard Worker '6', 121*6dbdd20aSAndroid Build Coastguard Worker '7', 122*6dbdd20aSAndroid Build Coastguard Worker '8', 123*6dbdd20aSAndroid Build Coastguard Worker '9', 124*6dbdd20aSAndroid Build Coastguard Worker '/', 125*6dbdd20aSAndroid Build Coastguard Worker '?', 126*6dbdd20aSAndroid Build Coastguard Worker '!', 127*6dbdd20aSAndroid Build Coastguard Worker '[', 128*6dbdd20aSAndroid Build Coastguard Worker ']', 129*6dbdd20aSAndroid Build Coastguard Worker]; 130*6dbdd20aSAndroid Build Coastguard Worker 131*6dbdd20aSAndroid Build Coastguard Workerconst macModifierStrings: ReadonlyMap<Modifier, string> = new Map< 132*6dbdd20aSAndroid Build Coastguard Worker Modifier, 133*6dbdd20aSAndroid Build Coastguard Worker string 134*6dbdd20aSAndroid Build Coastguard Worker>([ 135*6dbdd20aSAndroid Build Coastguard Worker ['', ''], 136*6dbdd20aSAndroid Build Coastguard Worker ['Mod+', '⌘'], 137*6dbdd20aSAndroid Build Coastguard Worker ['Shift+', '⇧'], 138*6dbdd20aSAndroid Build Coastguard Worker ['Ctrl+', '⌃'], 139*6dbdd20aSAndroid Build Coastguard Worker ['Alt+', '⌥'], 140*6dbdd20aSAndroid Build Coastguard Worker ['Mod+Shift+', '⌘⇧'], 141*6dbdd20aSAndroid Build Coastguard Worker ['Mod+Alt+', '⌘⌥'], 142*6dbdd20aSAndroid Build Coastguard Worker ['Mod+Shift+Alt+', '⌘⇧⌥'], 143*6dbdd20aSAndroid Build Coastguard Worker ['Ctrl+Shift+', '⌃⇧'], 144*6dbdd20aSAndroid Build Coastguard Worker ['Ctrl+Alt', '⌃⌥'], 145*6dbdd20aSAndroid Build Coastguard Worker ['Ctrl+Shift+Alt', '⌃⇧⌥'], 146*6dbdd20aSAndroid Build Coastguard Worker]); 147*6dbdd20aSAndroid Build Coastguard Worker 148*6dbdd20aSAndroid Build Coastguard Workerconst pcModifierStrings: ReadonlyMap<Modifier, string> = new Map< 149*6dbdd20aSAndroid Build Coastguard Worker Modifier, 150*6dbdd20aSAndroid Build Coastguard Worker string 151*6dbdd20aSAndroid Build Coastguard Worker>([ 152*6dbdd20aSAndroid Build Coastguard Worker ['', ''], 153*6dbdd20aSAndroid Build Coastguard Worker ['Mod+', 'Ctrl+'], 154*6dbdd20aSAndroid Build Coastguard Worker ['Mod+Shift+', 'Ctrl+Shift+'], 155*6dbdd20aSAndroid Build Coastguard Worker ['Mod+Alt+', 'Ctrl+Alt+'], 156*6dbdd20aSAndroid Build Coastguard Worker ['Mod+Shift+Alt+', 'Ctrl+Shift+Alt+'], 157*6dbdd20aSAndroid Build Coastguard Worker]); 158*6dbdd20aSAndroid Build Coastguard Worker 159*6dbdd20aSAndroid Build Coastguard Worker// Represents a deconstructed hotkey. 160*6dbdd20aSAndroid Build Coastguard Workerexport interface HotkeyParts { 161*6dbdd20aSAndroid Build Coastguard Worker // The name of the primary key of this hotkey. 162*6dbdd20aSAndroid Build Coastguard Worker key: Key; 163*6dbdd20aSAndroid Build Coastguard Worker 164*6dbdd20aSAndroid Build Coastguard Worker // All the modifiers as one chunk. E.g. 'Mod+Shift+'. 165*6dbdd20aSAndroid Build Coastguard Worker modifier: Modifier; 166*6dbdd20aSAndroid Build Coastguard Worker 167*6dbdd20aSAndroid Build Coastguard Worker // Whether this hotkey should register when the event target is inside an 168*6dbdd20aSAndroid Build Coastguard Worker // editable field. 169*6dbdd20aSAndroid Build Coastguard Worker allowInEditable: boolean; 170*6dbdd20aSAndroid Build Coastguard Worker} 171*6dbdd20aSAndroid Build Coastguard Worker 172*6dbdd20aSAndroid Build Coastguard Worker// Deconstruct a hotkey from its string representation into its constituent 173*6dbdd20aSAndroid Build Coastguard Worker// parts. 174*6dbdd20aSAndroid Build Coastguard Workerexport function parseHotkey(hotkey: Hotkey): HotkeyParts | undefined { 175*6dbdd20aSAndroid Build Coastguard Worker const regex = /^(!?)((?:Mod\+|Shift\+|Alt\+|Ctrl\+)*)(.*)$/; 176*6dbdd20aSAndroid Build Coastguard Worker const result = hotkey.match(regex); 177*6dbdd20aSAndroid Build Coastguard Worker 178*6dbdd20aSAndroid Build Coastguard Worker if (!result) { 179*6dbdd20aSAndroid Build Coastguard Worker return undefined; 180*6dbdd20aSAndroid Build Coastguard Worker } 181*6dbdd20aSAndroid Build Coastguard Worker 182*6dbdd20aSAndroid Build Coastguard Worker return { 183*6dbdd20aSAndroid Build Coastguard Worker allowInEditable: result[1] === '!', 184*6dbdd20aSAndroid Build Coastguard Worker modifier: result[2] as Modifier, 185*6dbdd20aSAndroid Build Coastguard Worker key: result[3] as Key, 186*6dbdd20aSAndroid Build Coastguard Worker }; 187*6dbdd20aSAndroid Build Coastguard Worker} 188*6dbdd20aSAndroid Build Coastguard Worker 189*6dbdd20aSAndroid Build Coastguard Worker// Print the hotkey in a human readable format. 190*6dbdd20aSAndroid Build Coastguard Workerexport function formatHotkey( 191*6dbdd20aSAndroid Build Coastguard Worker hotkey: Hotkey, 192*6dbdd20aSAndroid Build Coastguard Worker spoof?: Platform, 193*6dbdd20aSAndroid Build Coastguard Worker): string | undefined { 194*6dbdd20aSAndroid Build Coastguard Worker const parsed = parseHotkey(hotkey); 195*6dbdd20aSAndroid Build Coastguard Worker return parsed && formatHotkeyParts(parsed, spoof); 196*6dbdd20aSAndroid Build Coastguard Worker} 197*6dbdd20aSAndroid Build Coastguard Worker 198*6dbdd20aSAndroid Build Coastguard Workerfunction formatHotkeyParts( 199*6dbdd20aSAndroid Build Coastguard Worker {modifier, key}: HotkeyParts, 200*6dbdd20aSAndroid Build Coastguard Worker spoof?: Platform, 201*6dbdd20aSAndroid Build Coastguard Worker): string { 202*6dbdd20aSAndroid Build Coastguard Worker return `${formatModifier(modifier, spoof)}${key}`; 203*6dbdd20aSAndroid Build Coastguard Worker} 204*6dbdd20aSAndroid Build Coastguard Worker 205*6dbdd20aSAndroid Build Coastguard Workerfunction formatModifier(modifier: Modifier, spoof?: Platform): string { 206*6dbdd20aSAndroid Build Coastguard Worker const platform = spoof || getPlatform(); 207*6dbdd20aSAndroid Build Coastguard Worker const strings = platform === 'Mac' ? macModifierStrings : pcModifierStrings; 208*6dbdd20aSAndroid Build Coastguard Worker return strings.get(modifier) ?? modifier; 209*6dbdd20aSAndroid Build Coastguard Worker} 210*6dbdd20aSAndroid Build Coastguard Worker 211*6dbdd20aSAndroid Build Coastguard Worker// Like |KeyboardEvent| but all fields apart from |key| are optional. 212*6dbdd20aSAndroid Build Coastguard Workerexport type KeyboardEventLike = Pick<KeyboardEvent, 'key'> & 213*6dbdd20aSAndroid Build Coastguard Worker Partial<KeyboardEvent>; 214*6dbdd20aSAndroid Build Coastguard Worker 215*6dbdd20aSAndroid Build Coastguard Worker// Check whether |hotkey| is present in the keyboard event |event|. 216*6dbdd20aSAndroid Build Coastguard Workerexport function checkHotkey( 217*6dbdd20aSAndroid Build Coastguard Worker hotkey: Hotkey, 218*6dbdd20aSAndroid Build Coastguard Worker event: KeyboardEventLike, 219*6dbdd20aSAndroid Build Coastguard Worker spoofPlatform?: Platform, 220*6dbdd20aSAndroid Build Coastguard Worker): boolean { 221*6dbdd20aSAndroid Build Coastguard Worker const result = parseHotkey(hotkey); 222*6dbdd20aSAndroid Build Coastguard Worker if (!result) { 223*6dbdd20aSAndroid Build Coastguard Worker return false; 224*6dbdd20aSAndroid Build Coastguard Worker } 225*6dbdd20aSAndroid Build Coastguard Worker 226*6dbdd20aSAndroid Build Coastguard Worker const {key, allowInEditable} = result; 227*6dbdd20aSAndroid Build Coastguard Worker const {target = null} = event; 228*6dbdd20aSAndroid Build Coastguard Worker 229*6dbdd20aSAndroid Build Coastguard Worker const inEditable = elementIsEditable(target); 230*6dbdd20aSAndroid Build Coastguard Worker if (inEditable && !allowInEditable) { 231*6dbdd20aSAndroid Build Coastguard Worker return false; 232*6dbdd20aSAndroid Build Coastguard Worker } 233*6dbdd20aSAndroid Build Coastguard Worker return compareKeys(event, key) && checkMods(event, result, spoofPlatform); 234*6dbdd20aSAndroid Build Coastguard Worker} 235*6dbdd20aSAndroid Build Coastguard Worker 236*6dbdd20aSAndroid Build Coastguard Worker// Return true if |key| matches the event's key. 237*6dbdd20aSAndroid Build Coastguard Workerfunction compareKeys(e: KeyboardEventLike, key: Key): boolean { 238*6dbdd20aSAndroid Build Coastguard Worker return e.key.toLowerCase() === key.toLowerCase(); 239*6dbdd20aSAndroid Build Coastguard Worker} 240*6dbdd20aSAndroid Build Coastguard Worker 241*6dbdd20aSAndroid Build Coastguard Worker// Return true if modifiers specified in |mods| match those in the event. 242*6dbdd20aSAndroid Build Coastguard Workerfunction checkMods( 243*6dbdd20aSAndroid Build Coastguard Worker event: KeyboardEventLike, 244*6dbdd20aSAndroid Build Coastguard Worker hotkey: HotkeyParts, 245*6dbdd20aSAndroid Build Coastguard Worker spoofPlatform?: Platform, 246*6dbdd20aSAndroid Build Coastguard Worker): boolean { 247*6dbdd20aSAndroid Build Coastguard Worker const platform = spoofPlatform ?? getPlatform(); 248*6dbdd20aSAndroid Build Coastguard Worker 249*6dbdd20aSAndroid Build Coastguard Worker const {key, modifier} = hotkey; 250*6dbdd20aSAndroid Build Coastguard Worker 251*6dbdd20aSAndroid Build Coastguard Worker const { 252*6dbdd20aSAndroid Build Coastguard Worker ctrlKey = false, 253*6dbdd20aSAndroid Build Coastguard Worker altKey = false, 254*6dbdd20aSAndroid Build Coastguard Worker shiftKey = false, 255*6dbdd20aSAndroid Build Coastguard Worker metaKey = false, 256*6dbdd20aSAndroid Build Coastguard Worker } = event; 257*6dbdd20aSAndroid Build Coastguard Worker 258*6dbdd20aSAndroid Build Coastguard Worker const wantShift = modifier.includes('Shift'); 259*6dbdd20aSAndroid Build Coastguard Worker const wantAlt = modifier.includes('Alt'); 260*6dbdd20aSAndroid Build Coastguard Worker const wantCtrl = 261*6dbdd20aSAndroid Build Coastguard Worker platform === 'Mac' 262*6dbdd20aSAndroid Build Coastguard Worker ? modifier.includes('Ctrl') 263*6dbdd20aSAndroid Build Coastguard Worker : modifier.includes('Ctrl') || modifier.includes('Mod'); 264*6dbdd20aSAndroid Build Coastguard Worker const wantMeta = platform === 'Mac' && modifier.includes('Mod'); 265*6dbdd20aSAndroid Build Coastguard Worker 266*6dbdd20aSAndroid Build Coastguard Worker // For certain keys we relax the shift requirement, as they usually cannot be 267*6dbdd20aSAndroid Build Coastguard Worker // pressed without the shift key on English keyboards. 268*6dbdd20aSAndroid Build Coastguard Worker const shiftOk = 269*6dbdd20aSAndroid Build Coastguard Worker shiftExceptions.includes(key as string) || shiftKey === wantShift; 270*6dbdd20aSAndroid Build Coastguard Worker 271*6dbdd20aSAndroid Build Coastguard Worker return ( 272*6dbdd20aSAndroid Build Coastguard Worker metaKey === wantMeta && 273*6dbdd20aSAndroid Build Coastguard Worker Boolean(shiftOk) && 274*6dbdd20aSAndroid Build Coastguard Worker altKey === wantAlt && 275*6dbdd20aSAndroid Build Coastguard Worker ctrlKey === wantCtrl 276*6dbdd20aSAndroid Build Coastguard Worker ); 277*6dbdd20aSAndroid Build Coastguard Worker} 278*6dbdd20aSAndroid Build Coastguard Worker 279*6dbdd20aSAndroid Build Coastguard Workerexport type Platform = 'Mac' | 'PC'; 280*6dbdd20aSAndroid Build Coastguard Worker 281*6dbdd20aSAndroid Build Coastguard Worker// Get the current platform (PC or Mac). 282*6dbdd20aSAndroid Build Coastguard Workerexport function getPlatform(): Platform { 283*6dbdd20aSAndroid Build Coastguard Worker return window.navigator.platform.indexOf('Mac') !== -1 ? 'Mac' : 'PC'; 284*6dbdd20aSAndroid Build Coastguard Worker} 285*6dbdd20aSAndroid Build Coastguard Worker 286*6dbdd20aSAndroid Build Coastguard Worker// Returns a cross-platform check for whether the event has "Mod" key pressed 287*6dbdd20aSAndroid Build Coastguard Worker// (e.g. as a part of Mod-Click UX pattern). 288*6dbdd20aSAndroid Build Coastguard Worker// On Mac, Mod-click is actually Command-click and on PC it's Control-click, 289*6dbdd20aSAndroid Build Coastguard Worker// so this function handles this for all platforms. 290*6dbdd20aSAndroid Build Coastguard Workerexport function hasModKey(event: { 291*6dbdd20aSAndroid Build Coastguard Worker readonly metaKey: boolean; 292*6dbdd20aSAndroid Build Coastguard Worker readonly ctrlKey: boolean; 293*6dbdd20aSAndroid Build Coastguard Worker}): boolean { 294*6dbdd20aSAndroid Build Coastguard Worker if (getPlatform() === 'Mac') { 295*6dbdd20aSAndroid Build Coastguard Worker return event.metaKey; 296*6dbdd20aSAndroid Build Coastguard Worker } else { 297*6dbdd20aSAndroid Build Coastguard Worker return event.ctrlKey; 298*6dbdd20aSAndroid Build Coastguard Worker } 299*6dbdd20aSAndroid Build Coastguard Worker} 300*6dbdd20aSAndroid Build Coastguard Worker 301*6dbdd20aSAndroid Build Coastguard Workerexport function modKey(): {metaKey?: boolean; ctrlKey?: boolean} { 302*6dbdd20aSAndroid Build Coastguard Worker if (getPlatform() === 'Mac') { 303*6dbdd20aSAndroid Build Coastguard Worker return {metaKey: true}; 304*6dbdd20aSAndroid Build Coastguard Worker } else { 305*6dbdd20aSAndroid Build Coastguard Worker return {ctrlKey: true}; 306*6dbdd20aSAndroid Build Coastguard Worker } 307*6dbdd20aSAndroid Build Coastguard Worker} 308