feat: add save to keyboard and remap shortcut keys (#712)

* feat: add save to keyboard and remap shortcut keys

* feat: Alt and Shift keys set the remapOnAllKeymap and remapOnAllLayer

* fix: control + enter trigger remap keymap
This commit is contained in:
Róbert Kiss
2018-07-08 14:31:48 +02:00
committed by László Monda
parent 648e8d5f2c
commit 84f378a276
12 changed files with 210 additions and 70 deletions

View File

@@ -9,7 +9,7 @@
</div>
<notifier-container></notifier-container>
<progress-button class="save-to-keyboard-button"
*ngIf="(saveToKeyboardState$ | async).showButton"
*ngIf="saveToKeyboardState.showButton"
[@showSaveToKeyboardButton]
[state]="saveToKeyboardState$ | async"
[state]="saveToKeyboardState"
(clicked)="clickedOnProgressButton($event)"></progress-button>

View File

@@ -1,6 +1,7 @@
import { Component, ViewEncapsulation } from '@angular/core';
import { Component, HostListener, ViewEncapsulation, OnDestroy } from '@angular/core';
import { animate, style, transition, trigger } from '@angular/animations';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { Action, Store } from '@ngrx/store';
import 'rxjs/add/operator/last';
@@ -34,17 +35,35 @@ import { ProgressButtonState } from './store/reducers/progress-button-state';
])
]
})
export class MainAppComponent {
export class MainAppComponent implements OnDestroy {
showUpdateAvailable$: Observable<boolean>;
deviceConfigurationLoaded$: Observable<boolean>;
runningInElectron$: Observable<boolean>;
saveToKeyboardState$: Observable<ProgressButtonState>;
saveToKeyboardState: ProgressButtonState;
private saveToKeyboardStateSubscription: Subscription;
constructor(private store: Store<AppState>) {
this.showUpdateAvailable$ = store.select(getShowAppUpdateAvailable);
this.deviceConfigurationLoaded$ = store.select(deviceConfigurationLoaded);
this.runningInElectron$ = store.select(runningInElectron);
this.saveToKeyboardState$ = store.select(saveToKeyboardState);
this.saveToKeyboardStateSubscription = store.select(saveToKeyboardState)
.subscribe(data => this.saveToKeyboardState = data);
}
ngOnDestroy(): void {
this.saveToKeyboardStateSubscription.unsubscribe();
}
@HostListener('document:keydown', ['$event'])
onKeyDown(event: KeyboardEvent) {
if (this.saveToKeyboardState.showButton &&
event.ctrlKey &&
event.key === 's' &&
!event.defaultPrevented) {
this.clickedOnProgressButton(this.saveToKeyboardState.action);
event.preventDefault();
}
}
updateApp() {

View File

@@ -9,6 +9,7 @@
[keyboardLayout]="keyboardLayout"
[description]="description"
[showDescription]="true"
oncontextmenu="return false;"
(keyClick)="keyClick.emit($event)"
(keyHover)="keyHover.emit($event)"
(capture)="capture.emit($event)"

Before

Width:  |  Height:  |  Size: 809 B

After

Width:  |  Height:  |  Size: 853 B

View File

@@ -3,6 +3,11 @@ import { animate, keyframes, state, style, transition, trigger } from '@angular/
import { Layer } from 'uhk-common';
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
import {
SvgKeyboardCaptureEvent,
SvgKeyboardKeyClickEvent,
SvgKeyHoverEvent
} from '../../../models/svg-key-events';
type AnimationKeyboard =
'init' |
@@ -82,9 +87,9 @@ export class KeyboardSliderComponent implements OnChanges {
@Input() selectedKey: { layerId: number, moduleId: number, keyId: number };
@Input() keyboardLayout = KeyboardLayout.ANSI;
@Input() description: string;
@Output() keyClick = new EventEmitter();
@Output() keyHover = new EventEmitter();
@Output() capture = new EventEmitter();
@Output() keyClick = new EventEmitter<SvgKeyboardKeyClickEvent>();
@Output() keyHover = new EventEmitter<SvgKeyHoverEvent>();
@Output() capture = new EventEmitter<SvgKeyboardCaptureEvent>();
@Output() descriptionChanged = new EventEmitter<string>();
layerAnimationState: AnimationKeyboard[];

View File

@@ -84,6 +84,8 @@ export class PopoverComponent implements OnChanges {
@Input() wrapPosition: any;
@Input() visible: boolean;
@Input() allowLayerDoubleTap: boolean;
@Input() remapOnAllKeymap: boolean;
@Input() remapOnAllLayer: boolean;
@Output() cancel = new EventEmitter<any>();
@Output() remap = new EventEmitter<KeyActionRemap>();
@@ -101,9 +103,6 @@ export class PopoverComponent implements OnChanges {
leftPosition: number = 0;
animationState: string;
remapOnAllKeymap: boolean;
remapOnAllLayer: boolean;
private readonly currentKeymap$ = new BehaviorSubject<Keymap>(undefined);
constructor(store: Store<AppState>) {
@@ -143,8 +142,6 @@ export class PopoverComponent implements OnChanges {
if (change['visible']) {
if (change['visible'].currentValue) {
this.animationState = 'opened';
this.remapOnAllKeymap = false;
this.remapOnAllLayer = false;
} else {
this.animationState = 'closed';
}
@@ -179,6 +176,14 @@ export class PopoverComponent implements OnChanges {
this.cancel.emit();
}
@HostListener('document:keydown.control.enter', ['$event'])
onKeyDown(event: KeyboardEvent) {
if (this.visible) {
this.onRemapKey();
event.preventDefault();
}
}
selectTab(tab: TabName): void {
this.activeTab = tab;
}

View File

@@ -13,9 +13,9 @@
[selectedKey]="selectedKey"
[@split]="moduleAnimationStates[i]"
[selected]="selectedKey?.moduleId === i"
(keyClick)="onKeyClick(i, $event.index, $event.keyTarget)"
(keyClick)="onKeyClick(i, $event)"
(keyHover)="onKeyHover($event.index, $event.event, $event.over, i)"
(capture)="onCapture(i, $event.index, $event.captured)" />
(capture)="onCapture(i, $event)" />
<svg:path [@fadeSeparator]="separatorAnimation"
[attr.d]="separator.d"

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -7,6 +7,12 @@ import { SvgModule } from '../module';
import { SvgModuleProviderService } from '../../../services/svg-module-provider.service';
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
import { SvgSeparator } from '../separator';
import {
SvgKeyHoverEvent,
SvgKeyboardKeyClickEvent,
SvgKeyboardCaptureEvent,
SvgModuleKeyClickEvent
} from '../../../models/svg-key-events';
@Component({
selector: 'svg-keyboard',
@@ -45,9 +51,9 @@ export class SvgKeyboardComponent implements OnInit {
@Input() keyboardLayout = KeyboardLayout.ANSI;
@Input() description: string;
@Input() showDescription = false;
@Output() keyClick = new EventEmitter();
@Output() keyHover = new EventEmitter();
@Output() capture = new EventEmitter();
@Output() keyClick = new EventEmitter<SvgKeyboardKeyClickEvent>();
@Output() keyHover = new EventEmitter<SvgKeyHoverEvent>();
@Output() capture = new EventEmitter<SvgKeyboardCaptureEvent>();
@Output() descriptionChanged = new EventEmitter<string>();
modules: SvgModule[];
@@ -79,19 +85,17 @@ export class SvgKeyboardComponent implements OnInit {
}
}
onKeyClick(moduleId: number, keyId: number, keyTarget: HTMLElement): void {
onKeyClick(moduleId: number, event: SvgModuleKeyClickEvent): void {
this.keyClick.emit({
moduleId,
keyId,
keyTarget
...event,
moduleId
});
}
onCapture(moduleId: number, keyId: number, captured: { code: number, left: boolean[], right: boolean[] }): void {
onCapture(moduleId: number, event: SvgKeyboardCaptureEvent): void {
this.capture.emit({
moduleId,
keyId,
captured
...event,
moduleId
});
}

View File

@@ -26,6 +26,7 @@ import { MapperService } from '../../../../services/mapper.service';
import { AppState } from '../../../../store';
import { getMacros } from '../../../../store/reducers/user-configuration';
import { SvgKeyCaptureEvent, SvgKeyClickEvent } from '../../../../models/svg-key-events';
enum LabelTypes {
KeystrokeKey,
@@ -82,8 +83,8 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
@Input() capturingEnabled: boolean;
@Input() active: boolean;
@Output() keyClick = new EventEmitter();
@Output() capture = new EventEmitter();
@Output() keyClick = new EventEmitter<SvgKeyClickEvent>();
@Output() capture = new EventEmitter<SvgKeyCaptureEvent>();
enumLabelTypes = LabelTypes;
@@ -96,6 +97,10 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
macros: Macro[];
private subscription: Subscription;
private scanCodePressed: boolean;
private pressedShiftLocation = -1;
private pressedAltLocation = -1;
private altPressed = false;
private shitPressed = false;
constructor(
private mapper: MapperService,
@@ -115,12 +120,16 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
@HostListener('click')
onClick() {
this.reset();
this.keyClick.emit(this.element.nativeElement);
this.keyClick.emit({
keyTarget: this.element.nativeElement,
shiftPressed: this.pressedShiftLocation > -1,
altPressed: this.pressedAltLocation > -1
});
}
@HostListener('mousedown', ['$event'])
onMouseDown(e: MouseEvent) {
if ((e.which === 2 || e.button === 1) && this.capturingEnabled) {
if ((e.which === 2 || e.button === 2) && this.capturingEnabled) {
e.preventDefault();
this.renderer.invokeElementMethod(this.element.nativeElement, 'focus');
@@ -129,13 +138,29 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
} else {
this.recording = true;
this.recordAnimation = 'active';
if (this.pressedShiftLocation > -1) {
this.shitPressed = true;
}
if (this.pressedAltLocation > -1) {
this.altPressed = true;
}
}
}
}
@HostListener('keyup', ['$event'])
@HostListener('document:keyup', ['$event'])
onKeyUp(e: KeyboardEvent) {
if (this.scanCodePressed) {
if (e.keyCode === 18 && this.pressedAltLocation > -1) {
this.pressedAltLocation = -1;
e.preventDefault();
}
else if (e.keyCode === 16 && this.pressedShiftLocation > -1) {
this.pressedShiftLocation = -1;
e.preventDefault();
}
else if (this.scanCodePressed) {
e.preventDefault();
this.scanCodePressed = false;
} else if (this.recording) {
@@ -144,7 +169,7 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
}
}
@HostListener('keydown', ['$event'])
@HostListener('document:keydown', ['$event'])
onKeyDown(e: KeyboardEvent) {
const code: number = e.keyCode;
@@ -152,11 +177,29 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
e.preventDefault();
if (this.captureService.hasMap(code)) {
// If the Alt or Shift key not released after start the capturing
// then add them as a modifier
if (this.pressedShiftLocation > -1) {
this.captureService.setModifier((this.pressedShiftLocation === 1), 16);
}
if (this.pressedAltLocation > -1) {
this.captureService.setModifier((this.pressedAltLocation === 1), 18);
}
this.saveScanCode(this.captureService.getMap(code));
this.scanCodePressed = true;
} else {
this.captureService.setModifier((e.location === 1), code);
}
} else {
if (e.keyCode === 16) {
this.pressedShiftLocation = e.location;
}
if (e.keyCode === 18) {
this.pressedAltLocation = e.location;
}
}
}
@@ -198,22 +241,25 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
this.recording = false;
this.changeAnimation = 'inactive';
this.captureService.initModifiers();
this.shitPressed = false;
this.altPressed = false;
}
private saveScanCode(code = 0) {
this.recording = false;
this.changeAnimation = 'inactive';
const left: boolean[] = this.captureService.getModifiers(true);
const right: boolean[] = this.captureService.getModifiers(false);
this.capture.emit({
code,
left,
right
captured: {
code,
left,
right
},
shiftPressed: this.shitPressed,
altPressed: this.altPressed
});
this.captureService.initModifiers();
this.reset();
}
private setLabels(): void {

View File

@@ -2,6 +2,12 @@ import { Component, EventEmitter, Input, Output, ChangeDetectionStrategy } from
import { KeyAction } from 'uhk-common';
import { SvgKeyboardKey } from '../keys';
import {
SvgKeyCaptureEvent,
SvgKeyClickEvent,
SvgModuleCaptureEvent,
SvgModuleKeyClickEvent
} from '../../../models/svg-key-events';
@Component({
selector: 'g[svg-module]',
@@ -17,18 +23,18 @@ export class SvgModuleComponent {
@Input() selected: boolean;
@Input() keybindAnimationEnabled: boolean;
@Input() capturingEnabled: boolean;
@Output() keyClick = new EventEmitter();
@Output() keyClick = new EventEmitter<SvgModuleKeyClickEvent>();
@Output() keyHover = new EventEmitter();
@Output() capture = new EventEmitter();
@Output() capture = new EventEmitter<SvgModuleCaptureEvent>();
constructor() {
this.keyboardKeys = [];
}
onKeyClick(index: number, keyTarget: HTMLElement): void {
onKeyClick(keyId: number, event: SvgKeyClickEvent): void {
this.keyClick.emit({
index,
keyTarget
...event,
keyId
});
}
@@ -40,10 +46,10 @@ export class SvgModuleComponent {
});
}
onCapture(index: number, captured: {code: number, left: boolean[], right: boolean[]}) {
onCapture(keyId: number, event: SvgKeyCaptureEvent) {
this.capture.emit({
index,
captured
...event,
keyId
});
}
}

View File

@@ -8,9 +8,9 @@
[halvesSplit]="halvesSplit"
[keyboardLayout]="keyboardLayout"
[description]="keymap.description"
(keyClick)="onKeyClick($event.moduleId, $event.keyId, $event.keyTarget)"
(keyHover)="onKeyHover($event.moduleId, $event.event, $event.over, $event.keyId)"
(capture)="onCapture($event.moduleId, $event.keyId, $event.captured)"
(keyClick)="onKeyClick($event)"
(keyHover)="onKeyHover($event)"
(capture)="onCapture($event)"
(descriptionChanged)="onDescriptionChanged($event)"
></keyboard-slider>
@@ -22,6 +22,8 @@
[currentKeymap]="keymap"
[currentLayer]="currentLayer"
[allowLayerDoubleTap]="allowLayerDoubleTap"
[remapOnAllKeymap]="remapOnAllKeymap"
[remapOnAllLayer]="remapOnAllLayer"
(cancel)="hidePopover()"
(remap)="onRemap($event)"></popover>

View File

@@ -43,6 +43,11 @@ import { PopoverComponent } from '../../popover';
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
import { ChangeKeymapDescription } from '../../../models/ChangeKeymapDescription';
import { KeyActionRemap } from '../../../models/key-action-remap';
import {
SvgKeyboardCaptureEvent,
SvgKeyboardKeyClickEvent,
SvgKeyHoverEvent
} from '../../../models/svg-key-events';
interface NameValuePair {
name: string;
@@ -65,7 +70,7 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
@Output() descriptionChanged = new EventEmitter<ChangeKeymapDescription>();
@ViewChild(PopoverComponent, { read: ElementRef }) popover: ElementRef;
@ViewChild(PopoverComponent, {read: ElementRef}) popover: ElementRef;
popoverShown: boolean;
keyEditConfig: { moduleId: number, keyId: number };
@@ -82,6 +87,9 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
layers: Layer[];
keyPosition: ClientRect;
wrapPosition: ClientRect;
remapOnAllKeymap: boolean;
remapOnAllLayer: boolean;
private wrapHost: HTMLElement;
private keyElement: HTMLElement;
@@ -139,36 +147,38 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
}
onKeyClick(moduleId: number, keyId: number, keyTarget: HTMLElement): void {
onKeyClick(event: SvgKeyboardKeyClickEvent): void {
if (!this.popoverShown && this.popoverEnabled) {
this.keyEditConfig = {
moduleId,
keyId
moduleId: event.moduleId,
keyId: event.keyId
};
this.selectedKey = { layerId: this.currentLayer, moduleId, keyId };
const keyActionToEdit: KeyAction = this.layers[this.currentLayer].modules[moduleId].keyActions[keyId];
this.keyElement = keyTarget;
this.selectedKey = {layerId: this.currentLayer, moduleId: event.moduleId, keyId: event.keyId};
const keyActionToEdit: KeyAction = this.layers[this.currentLayer].modules[event.moduleId].keyActions[event.keyId];
this.keyElement = event.keyTarget;
this.remapOnAllKeymap = event.shiftPressed;
this.remapOnAllLayer = event.altPressed;
this.showPopover(keyActionToEdit);
}
}
onKeyHover(moduleId: number, event: MouseEvent, over: boolean, keyId: number): void {
onKeyHover(event: SvgKeyHoverEvent): void {
if (this.tooltipEnabled) {
const keyActionToEdit: KeyAction = this.layers[this.currentLayer].modules[moduleId].keyActions[keyId];
const keyActionToEdit: KeyAction = this.layers[this.currentLayer].modules[event.moduleId].keyActions[event.keyId];
if (over) {
this.showTooltip(keyActionToEdit, event);
if (event.over) {
this.showTooltip(keyActionToEdit, event.event);
} else {
this.hideTooltip();
}
}
}
onCapture(moduleId: number, keyId: number, captured: { code: number, left: boolean[], right: boolean[] }): void {
onCapture(event: SvgKeyboardCaptureEvent): void {
const keystrokeAction: KeystrokeAction = new KeystrokeAction();
const modifiers = captured.left.concat(captured.right).map(x => x ? 1 : 0);
const modifiers = event.captured.left.concat(event.captured.right).map(x => x ? 1 : 0);
keystrokeAction.scancode = captured.code;
keystrokeAction.scancode = event.captured.code;
keystrokeAction.modifierMask = 0;
for (let i = 0; i < modifiers.length; ++i) {
@@ -179,11 +189,11 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
KeymapActions.saveKey(
this.keymap,
this.currentLayer,
moduleId,
keyId,
event.moduleId,
event.keyId,
{
remapOnAllKeymap: false,
remapOnAllLayer: false,
remapOnAllKeymap: event.shiftPressed,
remapOnAllLayer: event.altPressed,
action: keystrokeAction
})
);

View File

@@ -0,0 +1,42 @@
export interface SvgKeyClickEvent {
keyTarget: HTMLElement;
shiftPressed?: boolean;
altPressed?: boolean;
}
export interface SvgModuleKeyClickEvent extends SvgKeyClickEvent {
keyId: number;
}
export interface SvgKeyboardKeyClickEvent extends SvgModuleKeyClickEvent {
moduleId: number;
}
export interface KeyCaptureData {
code: number;
left: boolean[];
right: boolean[];
}
export interface SvgKeyCaptureEvent {
captured: KeyCaptureData;
shiftPressed?: boolean;
altPressed?: boolean;
}
export interface SvgModuleCaptureEvent extends SvgKeyCaptureEvent {
keyId: number;
}
export interface SvgKeyboardCaptureEvent extends SvgModuleCaptureEvent {
moduleId: number;
}
export interface SvgKeyHoverEvent {
keyId: number;
event: MouseEvent;
over: boolean;
moduleId: number;
shiftPressed?: boolean;
altPressed?: boolean;
}