import { ChangeDetectorRef , Component , ElementRef , EventEmitter , HostListener , Input , OnChanges , OnDestroy , Output , ChangeDetectionStrategy , SimpleChanges , ViewChild } from '@angular/core' ; import { animate , state , style , transition , trigger } from '@angular/animations' ; import { Store } from '@ngrx/store' ; import { Key } from 'ts-keycode-enum' ; import { Subscription } from 'rxjs' ; import { KeyAction , KeyModifiers , KeystrokeAction , LayerName , Macro , MouseAction , PlayMacroAction , SwitchKeymapAction , SwitchLayerAction , SwitchLayerMode } from 'uhk-common' ; import { CaptureService } from '../../../../services/capture.service' ; import { MapperService } from '../../../../services/mapper.service' ; import { AppState } from '../../../../store' ; import { SvgKeyCaptureEvent , SvgKeyClickEvent } from '../../../../models/svg-key-events' ; import { OperatingSystem } from '../../../../models/operating-system' ; import { KeyModifierModel } from '../../../../models/key-modifier-model' ; import { StartKeypressCapturingAction , StopKeypressCapturingAction } from '../../../../store/actions/app' ; import { KeyActionDragAndDropService } from '../../../../services/key-action-drag-and-drop.service' ; import { SvgKeyboardKey } from './svg-keyboard-key.model' ; enum LabelTypes { KeystrokeKey , MouseKey , OneLineText , TwoLineText , TextIcon , SingleIcon , SwitchKeymap , IconText } @ Component ( { animations : [ trigger ( 'recording' , [ state ( 'inactive' , style ( { fill : 'rgba(204, 0, 0, 1)' } ) ) , state ( 'active' , style ( { fill : 'rgba(204, 0, 0, 0.6)' } ) ) , transition ( 'inactive <=> active' , animate ( '600ms ease-in-out' ) ) ] ) ] , selector : 'g[svg-keyboard-key]' , templateUrl : './svg-keyboard-key.component.html' , styleUrls : [ './svg-keyboard-key.component.scss' ] , changeDetection : ChangeDetectionStrategy . OnPush } ) export class SvgKeyboardKeyComponent implements OnChanges , OnDestroy { @ Input ( ) keyAction : KeyAction ; @ Input ( ) svgKey : SvgKeyboardKey ; @ Input ( ) capturingEnabled : boolean ; @ Input ( ) macroMap = new Map < number , Macro > ( ) ; @ Input ( ) blink : boolean ; @ Output ( ) keyClick = new EventEmitter < SvgKeyClickEvent > ( ) ; @ Output ( ) capture = new EventEmitter < SvgKeyCaptureEvent > ( ) ; @ ViewChild ( 'svgRec' , { static : false } ) svgRec : ElementRef < HTMLElement > ; enumLabelTypes = LabelTypes ; recordAnimation : string ; recording : boolean ; labelType : LabelTypes ; labelSource : any ; secondaryText : string ; private scanCodePressed = false ; private pressedShiftLocation = - 1 ; private pressedAltLocation = - 1 ; private altPressed = false ; private shiftPressed = false ; private subscriptions = new Subscription ( ) ; constructor ( private mapper , private store , private element , private cdRef , private captureService , private dragAndDropService ) { } @ HostListener ( 'click' , [ '$event' ] ) onClick ( e ) { this . reset ( ) ; this . keyClick . emit ( { keyTarget : this . element . nativeElement , shiftPressed : e . shiftKey , altPressed : e . altKey } ) ; this . pressedShiftLocation = - 1 ; this . pressedAltLocation = - 1 ; } @ HostListener ( 'mousedown' , [ '$event' ] ) onMouseDown ( e ) { if ( ( e . which === 0 || e . button === 0 ) ) { this . dragAndDropService . leftButtonDown ( { keyId : this . svgKey . id , element : this . element . nativeElement , event : e } ) ; } else if ( ( e . which === 2 || e . button === 2 ) && this . capturingEnabled ) { e . preventDefault ( ) ; this . element . nativeElement . focus ( ) ; if ( this . recording ) { this . reset ( ) ; } else { this . recording = true ; this . recordAnimation = 'active' ; this . shiftPressed = e . shiftKey ; this . altPressed = e . altKey ; this . store . dispatch ( new StartKeypressCapturingAction ( ) ) ; } } } @ HostListener ( 'document:keyup' , [ '$event' ] ) onKeyUp ( e ) { if ( e . keyCode === Key . Alt && this . pressedAltLocation > - 1 ) { this . pressedAltLocation = - 1 ; e . preventDefault ( ) ; } else if ( e . keyCode === Key . Shift && this . pressedShiftLocation > - 1 ) { this . pressedShiftLocation = - 1 ; e . preventDefault ( ) ; } else if ( this . scanCodePressed ) { e . preventDefault ( ) ; this . scanCodePressed = false ; } else if ( this . recording ) { e . preventDefault ( ) ; this . saveScanCode ( ) ; } } @ HostListener ( 'document:keydown' , [ '$event' ] ) onKeyDown ( e ) { const code = e . keyCode ; if ( this . recording ) { e . preventDefault ( ) ; if ( this . captureService . hasMap ( code ) ) { if ( this . pressedShiftLocation > - 1 ) { this . captureService . setModifier ( ( this . pressedShiftLocation === 1 ) , Key . Shift ) ; } if ( this . pressedAltLocation > - 1 ) { this . captureService . setModifier ( ( this . pressedAltLocation === 1 ) , Key . Alt ) ; } this . scanCodePressed = true ; this . saveScanCode ( this . captureService . getMap ( code ) ) ; } else { this . captureService . setModifier ( ( e . location === 1 ) , code ) ; } } else { if ( e . keyCode === Key . Shift ) { this . pressedShiftLocation = e . location ; } if ( e . keyCode === Key . Alt ) { this . pressedAltLocation = e . location ; } } } @ HostListener ( 'focusout' ) onFocusOut ( ) { this . reset ( ) ; } ngOnChanges ( changes ) { if ( changes [ 'keyAction' ] ) { this . setLabels ( ) ; } if ( changes [ 'blink' ] && changes [ 'blink' ] . currentValue ) { this . blinkSvgRec ( ) ; } } ngOnDestroy ( ) { this . subscriptions . unsubscribe ( ) ; } onRecordingAnimationDone ( ) { if ( this . recording && this . recordAnimation === 'inactive' ) { this . recordAnimation = 'active' ; } else { this . recordAnimation = 'inactive' ; } } private reset ( ) { this . recording = false ; this . captureService . initModifiers ( ) ; this . shiftPressed = false ; this . altPressed = false ; this . store . dispatch ( new StopKeypressCapturingAction ( ) ) ; } private saveScanCode ( code = 0 ) { const left = this . captureService . getModifiers ( true ) ; const right = this . captureService . getModifiers ( false ) ; if ( ! this . scanCodePressed && ! this . captureService . hasModifiers ( ) ) { return ; } this . capture . emit ( { captured : { code , left , right } , shiftPressed : this . shiftPressed , altPressed : this . altPressed } ) ; this . reset ( ) ; } private setLabels ( ) { this . labelType = LabelTypes . OneLineText ; this . labelSource = undefined ; this . secondaryText = undefined ; if ( ! this . keyAction ) { return ; } if ( this . svgKey . width < 20 ) { if ( this . keyAction instanceof KeystrokeAction ) { if ( ! this . keyAction . hasActiveModifier ( ) && ! this . keyAction . hasSecondaryRoleAction ( ) && this . keyAction . hasScancode ( ) ) { const text = this . mapper . scanCodeToText ( this . keyAction . scancode , this . keyAction . type ) ; if ( text . length === 1 ) { this . labelSource = text ; } } } if ( this . labelSource === undefined ) { this . labelSource = '•••' ; } } else if ( this . keyAction instanceof KeystrokeAction ) { const keyAction = this . keyAction as KeystrokeAction ; let newLabelSource ; this . secondaryText = this . mapper . getSecondaryRoleText ( keyAction . secondaryRoleAction ) ; if ( ! keyAction . hasActiveModifier ( ) && keyAction . hasScancode ( ) ) { const scancode = keyAction . scancode ; newLabelSource = this . mapper . scanCodeToText ( scancode , keyAction . type ) ; if ( this . mapper . hasScancodeIcon ( scancode , keyAction . type ) ) { this . labelSource = this . mapper . scanCodeToSvgImagePath ( scancode , keyAction . type ) ; this . labelType = LabelTypes . SingleIcon ; } else if ( newLabelSource !== undefined ) { if ( newLabelSource . length === 1 ) { this . labelSource = newLabelSource [ 0 ] ; this . labelType = LabelTypes . OneLineText ; } else if ( newLabelSource [ 1 ] . startsWith ( 'icon' ) ) { this . labelSource = { text : newLabelSource [ 0 ] , icon : this . mapper . getIcon ( newLabelSource [ 1 ] ) } ; this . labelType = LabelTypes . IconText ; } else { this . labelSource = newLabelSource ; this . labelType = LabelTypes . TwoLineText ; } } } else if ( keyAction . hasOnlyOneActiveModifier ( ) && ! keyAction . hasScancode ( ) ) { switch ( keyAction . modifierMask ) { case KeyModifiers . leftCtrl : case KeyModifiers . rightCtrl : this . labelSource = [ 'Ctrl' ] ; break ; case KeyModifiers . leftShift : case KeyModifiers . rightShift : this . labelSource = [ 'Shift' ] ; break ; case KeyModifiers . leftAlt : case KeyModifiers . rightAlt : this . labelSource = [ this . mapper . getOsSpecificText ( 'Alt' ) ] ; break ; case KeyModifiers . leftGui : case KeyModifiers . rightGui : if ( this . mapper . getOperatingSystem ( ) === OperatingSystem . Windows ) { this . labelSource = this . mapper . getIcon ( 'command' ) ; this . labelType = LabelTypes . SingleIcon ; } else { this . labelSource = [ this . mapper . getOsSpecificText ( 'Super' ) ] ; } break ; default : this . labelSource = [ 'Undefined' ] ; break ; } } else { this . labelType = LabelTypes . KeystrokeKey ; this . labelSource = this . keyAction ; } } else if ( this . keyAction instanceof SwitchLayerAction ) { const keyAction = this . keyAction as SwitchLayerAction ; let newLabelSource ; switch ( keyAction . layer ) { case LayerName . mod : newLabelSource = 'Mod' ; break ; case LayerName . fn : newLabelSource = 'Fn' ; break ; case LayerName . mouse : newLabelSource = 'Mouse' ; break ; default : break ; } if ( keyAction . switchLayerMode === SwitchLayerMode . toggle ) { this . labelType = LabelTypes . TextIcon ; this . labelSource = { text : newLabelSource , icon : this . mapper . getIcon ( 'toggle' ) } ; } else if ( keyAction . switchLayerMode === SwitchLayerMode . holdAndDoubleTapToggle ) { this . labelType = LabelTypes . TextIcon ; this . labelSource = { text : newLabelSource , icon : this . mapper . getIcon ( 'double-tap' ) } ; } else { this . labelType = LabelTypes . OneLineText ; this . labelSource = newLabelSource ; } } else if ( this . keyAction instanceof SwitchKeymapAction ) { const keyAction = this . keyAction as SwitchKeymapAction ; this . labelType = LabelTypes . SwitchKeymap ; this . labelSource = keyAction . keymapAbbreviation ; } else if ( this . keyAction instanceof PlayMacroAction ) { const keyAction = this . keyAction as PlayMacroAction ; const macro = this . macroMap . get ( keyAction . macroId ) ; this . labelType = LabelTypes . IconText ; this . labelSource = { icon : this . mapper . getIcon ( 'macro' ) , text : macro . name } ; } else if ( this . keyAction instanceof MouseAction ) { this . labelType = LabelTypes . MouseKey ; this . labelSource = this . keyAction ; } else { this . labelSource = undefined ; } } private blinkSvgRec ( ) { if ( this . svgRec ) { this . svgRec . nativeElement . classList . remove ( 'blink' ) ; setTimeout ( ( ) => { if ( this . svgRec ) { this . svgRec . nativeElement . classList . add ( 'blink' ) ; } } , 10 ) ; } } }