Added capture to keys (#231)

Resolves #143
This commit is contained in:
Nejc Zdovc
2016-12-28 17:09:05 +01:00
committed by József Farkas
parent 0081e3e337
commit 6caeb11dc3
13 changed files with 231 additions and 79 deletions

View File

@@ -1,7 +1,9 @@
<svg-keyboard *ngFor="let layer of layers; let index = index; trackBy: trackKeyboard"
[@layerState]="layerAnimationState[index]"
[moduleConfig]="layer.modules"
[keybindAnimationEnabled]="keybindAnimationEnabled"
(keyClick)="keyClick.emit($event)"
(keyHover)="keyHover.emit($event)">
[@layerState]="layerAnimationState[index]"
[moduleConfig]="layer.modules"
[keybindAnimationEnabled]="keybindAnimationEnabled"
(keyClick)="keyClick.emit($event)"
(keyHover)="keyHover.emit($event)"
(capture)="capture.emit($event)"
>
</svg-keyboard>

Before

Width:  |  Height:  |  Size: 408 B

After

Width:  |  Height:  |  Size: 356 B

View File

@@ -81,6 +81,7 @@ export class KeyboardSliderComponent implements OnChanges {
@Input() keybindAnimationEnabled: boolean;
@Output() keyClick = new EventEmitter();
@Output() keyHover = new EventEmitter();
@Output() capture = new EventEmitter();
private layerAnimationState: AnimationKeyboard[];

View File

@@ -5,6 +5,7 @@ import { Select2OptionData, Select2TemplateFunction } from 'ng2-select2';
import { KeyAction, KeystrokeAction } from '../../../../config-serializer/config-items/key-action';
import { Tab } from '../tab';
import { MapperService } from '../../../../services/mapper.service';
@Component({
selector: 'keypress-tab',
@@ -28,7 +29,7 @@ export class KeypressTabComponent implements OnChanges, Tab {
private scanCode: number;
private selectedLongPressIndex: number;
constructor() {
constructor(private mapper: MapperService) {
this.leftModifiers = ['LShift', 'LCtrl', 'LSuper', 'LAlt'];
this.rightModifiers = ['RShift', 'RCtrl', 'RSuper', 'RAlt'];
this.scanCodeGroups = [{
@@ -89,17 +90,17 @@ export class KeypressTabComponent implements OnChanges, Tab {
// Restore modifiers
for (let i = 0; i < leftModifiersLength; ++i) {
this.leftModifierSelects[this.modifierMapper(i)] = ((keystrokeAction.modifierMask >> i) & 1) === 1;
this.leftModifierSelects[this.mapper.modifierMapper(i)] = ((keystrokeAction.modifierMask >> i) & 1) === 1;
}
for (let i = leftModifiersLength; i < leftModifiersLength + this.rightModifierSelects.length; ++i) {
let index: number = this.modifierMapper(i) - leftModifiersLength;
let index: number = this.mapper.modifierMapper(i) - leftModifiersLength;
this.rightModifierSelects[index] = ((keystrokeAction.modifierMask >> i) & 1) === 1;
}
// Restore longPressAction
if (keystrokeAction.longPressAction !== undefined) {
this.selectedLongPressIndex = this.modifierMapper(keystrokeAction.longPressAction);
this.selectedLongPressIndex = this.mapper.modifierMapper(keystrokeAction.longPressAction);
}
return true;
@@ -112,12 +113,12 @@ export class KeypressTabComponent implements OnChanges, Tab {
keystrokeAction.modifierMask = 0;
let modifiers = this.leftModifierSelects.concat(this.rightModifierSelects).map(x => x ? 1 : 0);
for (let i = 0; i < modifiers.length; ++i) {
keystrokeAction.modifierMask |= modifiers[i] << this.modifierMapper(i);
keystrokeAction.modifierMask |= modifiers[i] << this.mapper.modifierMapper(i);
}
keystrokeAction.longPressAction = this.selectedLongPressIndex === -1
? undefined
: this.modifierMapper(this.selectedLongPressIndex);
: this.mapper.modifierMapper(this.selectedLongPressIndex);
if (!this.keyActionValid(keystrokeAction)) {
throw new Error('KeyAction is invalid!');
@@ -157,12 +158,4 @@ export class KeypressTabComponent implements OnChanges, Tab {
onScancodeChange(event: {value: string}) {
this.scanCode = +event.value;
}
private modifierMapper(x: number) {
if (x < 8) {
return Math.floor(x / 2) * 4 + 1 - x; // 1, 0, 3, 2, 5, 4, 7, 6
} else {
return x;
}
};
}

View File

@@ -8,6 +8,7 @@
[keyActions]="moduleConfig[i].keyActions"
(keyClick)="onKeyClick(i, $event.index, $event.keyTarget)"
(keyHover)="onKeyHover($event.index, $event.event, $event.over, i)"
(capture)="onCapture(i, $event.index, $event.captured)"
/>
</svg:g>
</svg>

Before

Width:  |  Height:  |  Size: 745 B

After

Width:  |  Height:  |  Size: 817 B

View File

@@ -13,6 +13,7 @@ export class SvgKeyboardComponent implements OnInit {
@Input() keybindAnimationEnabled: boolean;
@Output() keyClick = new EventEmitter();
@Output() keyHover = new EventEmitter();
@Output() capture = new EventEmitter();
private modules: SvgModule[];
private svgAttributes: { viewBox: string, transform: string, fill: string };
@@ -34,6 +35,14 @@ export class SvgKeyboardComponent implements OnInit {
});
}
onCapture(moduleId: number, keyId: number, captured: {code: number, left: boolean[], right: boolean[]}): void {
this.capture.emit({
moduleId,
keyId,
captured
});
}
onKeyHover(keyId: number, event: MouseEvent, over: boolean, moduleId: number): void {
this.keyHover.emit({
moduleId,

View File

@@ -1,53 +1,65 @@
<svg:rect [@change]="animation"
(@change.done)="animationDone()"
<svg:rect [@change]="changeAnimation"
(@change.done)="onChangeAnimationDone()"
[id]="id" [attr.rx]="rx" [attr.ry]="ry"
[attr.height]="height" [attr.width]="width"
[attr.fill]="fill"
/>
<svg:g [ngSwitch]="labelType"
[attr.font-size]="19"
[attr.font-family]="'Helvetica'"
[attr.fill]="'white'">
<svg:g svg-keystroke-key *ngSwitchCase="enumLabelTypes.KeystrokeKey"
[height]="height"
[width]="width"
[keystrokeAction]="labelSource">
<template [ngIf]="recording">
<svg:circle
[@recording]="recordAnimation"
(@recording.done)="onRecordingAnimationDone()"
[attr.cx]="(width / 2)"
[attr.cy]="(height / 2)"
[attr.r]="10"
[attr.fill]="'#c00'"
></svg:circle>
</template>
<template [ngIf]="!recording">
<svg:g [ngSwitch]="labelType"
[attr.font-size]="19"
[attr.font-family]="'Helvetica'"
[attr.fill]="'white'">
<svg:g svg-keystroke-key *ngSwitchCase="enumLabelTypes.KeystrokeKey"
[height]="height"
[width]="width"
[keystrokeAction]="labelSource">
</svg:g>
<svg:g svg-one-line-text-key *ngSwitchCase="enumLabelTypes.OneLineText"
[height]="height"
[width]="width"
[text]="labelSource">
</svg:g>
<svg:g svg-two-line-text-key *ngSwitchCase="enumLabelTypes.TwoLineText"
[height]="height"
[width]="width"
[texts]="labelSource">
</svg:g>
<svg:g svg-text-icon-key *ngSwitchCase="enumLabelTypes.TextIcon"
[height]="height"
[width]="width"
[text]="labelSource.text"
[icon]="labelSource.icon">
</svg:g>
<svg:g svg-icon-text-key *ngSwitchCase="enumLabelTypes.IconText"
[height]="height"
[width]="width"
[icon]="labelSource.icon"
[text]="labelSource.text">
</svg:g>
<svg:g svg-single-icon-key *ngSwitchCase="enumLabelTypes.SingleIcon"
[height]="height"
[width]="width"
[icon]="labelSource">
</svg:g>
<svg:g svg-switch-keymap-key *ngSwitchCase="enumLabelTypes.SwitchKeymap"
[height]="height"
[width]="width"
[abbreviation]="labelSource">
</svg:g>
<svg *ngSwitchCase="enumLabelTypes.MouseKey" [attr.viewBox]="'0 0 100 100'"
[attr.width]="width"
[attr.height]="height">
<svg:g svg-mouse-key [mouseAction]="labelSource"></svg:g>
</svg>
</svg:g>
<svg:g svg-one-line-text-key *ngSwitchCase="enumLabelTypes.OneLineText"
[height]="height"
[width]="width"
[text]="labelSource">
</svg:g>
<svg:g svg-two-line-text-key *ngSwitchCase="enumLabelTypes.TwoLineText"
[height]="height"
[width]="width"
[texts]="labelSource">
</svg:g>
<svg:g svg-text-icon-key *ngSwitchCase="enumLabelTypes.TextIcon"
[height]="height"
[width]="width"
[text]="labelSource.text"
[icon]="labelSource.icon">
</svg:g>
<svg:g svg-icon-text-key *ngSwitchCase="enumLabelTypes.IconText"
[height]="height"
[width]="width"
[icon]="labelSource.icon"
[text]="labelSource.text">
</svg:g>
<svg:g svg-single-icon-key *ngSwitchCase="enumLabelTypes.SingleIcon"
[height]="height"
[width]="width"
[icon]="labelSource">
</svg:g>
<svg:g svg-switch-keymap-key *ngSwitchCase="enumLabelTypes.SwitchKeymap"
[height]="height"
[width]="width"
[abbreviation]="labelSource">
</svg:g>
<svg *ngSwitchCase="enumLabelTypes.MouseKey" [attr.viewBox]="'0 0 100 100'"
[attr.width]="width"
[attr.height]="height">
<svg:g svg-mouse-key [mouseAction]="labelSource"></svg:g>
</svg>
</svg:g>
</template>

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -4,6 +4,7 @@
}
cursor: pointer;
outline: none;
&:hover {
fill: #494949;

View File

@@ -1,6 +1,6 @@
import {
Component, ElementRef, EventEmitter, HostListener, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChange,
animate, group, style, transition, trigger
Component, ElementRef, EventEmitter, HostListener, Input, OnChanges, OnDestroy, OnInit, Output, Renderer,
SimpleChange, animate, group, state, style, transition, trigger
} from '@angular/core';
import { Store } from '@ngrx/store';
@@ -19,6 +19,7 @@ import {
import { KeyModifiers } from '../../../../config-serializer/config-items/KeyModifiers';
import { Macro } from '../../../../config-serializer/config-items/Macro';
import { CaptureService } from '../../../../services/capture.service';
import { MapperService } from '../../../../services/mapper.service';
import { AppState } from '../../../../store/index';
@@ -46,6 +47,15 @@ enum LabelTypes {
}))
])
])
]),
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]',
@@ -61,22 +71,79 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
@Input() keyAction: KeyAction;
@Input() keybindAnimationEnabled: boolean;
@Output() keyClick = new EventEmitter();
@Output() capture = new EventEmitter();
enumLabelTypes = LabelTypes;
public changeAnimation: string = 'inactive';
public recordAnimation: string;
private labelSource: any;
private labelType: LabelTypes;
private macros: Macro[];
private subscription: Subscription;
private animation: string = 'inactive';
private recording: boolean;
@HostListener('click') onClick() {
@HostListener('click')
onClick() {
this.reset();
this.keyClick.emit(this.element.nativeElement);
}
constructor(private mapper: MapperService, private store: Store<AppState>, private element: ElementRef) {
@HostListener('mousedown', ['$event'])
onMouseDown(e: MouseEvent) {
if (e.which === 2 || e.button === 1) {
e.preventDefault();
this.renderer.invokeElementMethod(this.element.nativeElement, 'focus');
if (this.recording) {
this.reset();
} else {
this.recording = true;
this.recordAnimation = 'active';
}
}
}
@HostListener('keyup')
onKeyUp() {
if (this.recording) {
this.saveScanCode();
}
}
@HostListener('keydown', ['$event'])
onKeyDown(e: KeyboardEvent) {
const code: number = e.keyCode;
if (this.recording) {
e.preventDefault();
if (this.captureService.hasMap(code)) {
this.saveScanCode(this.captureService.getMap(code));
} else {
this.captureService.setModifier((e.location === 1), code);
}
}
}
@HostListener('focusout')
onFocusOut() {
this.reset();
}
constructor(
private mapper: MapperService,
private store: Store<AppState>,
private element: ElementRef,
private captureService: CaptureService,
private renderer: Renderer
) {
this.subscription = store.let(getMacroEntities())
.subscribe((macros: Macro[]) => this.macros = macros);
this.reset();
this.captureService.populateMapping();
}
ngOnInit() {
@@ -84,22 +151,50 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
}
ngOnChanges(changes: { [propertyName: string]: SimpleChange }) {
/* tslint:disable:no-string-literal */
if (changes['keyAction']) {
this.setLabels();
if (this.keybindAnimationEnabled) {
this.animation = 'active';
this.changeAnimation = 'active';
}
}
/* tslint:enable:no-string-literal */
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
animationDone() {
this.animation = 'inactive';
onChangeAnimationDone() {
this.changeAnimation = 'inactive';
}
onRecordingAnimationDone() {
if (this.recording && this.recordAnimation === 'inactive') {
this.recordAnimation = 'active';
} else {
this.recordAnimation = 'inactive';
}
}
private reset() {
this.recording = false;
this.changeAnimation = 'inactive';
this.captureService.initModifiers();
}
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
});
this.captureService.initModifiers();
}
private setLabels(): void {

View File

@@ -7,6 +7,7 @@
[keyAction]="keyActions[i]"
[keybindAnimationEnabled]="keybindAnimationEnabled"
(keyClick)="onKeyClick(i, $event)"
(capture)="onCapture(i, $event)"
(mouseenter)="onKeyHover(i, $event, true)"
(mouseleave)="onKeyHover(i, $event, false)"
/>

Before

Width:  |  Height:  |  Size: 556 B

After

Width:  |  Height:  |  Size: 597 B

View File

@@ -16,6 +16,7 @@ export class SvgModuleComponent {
@Input() keybindAnimationEnabled: boolean;
@Output() keyClick = new EventEmitter();
@Output() keyHover = new EventEmitter();
@Output() capture = new EventEmitter();
constructor() {
this.keyboardKeys = [];
@@ -36,4 +37,10 @@ export class SvgModuleComponent {
});
}
onCapture(index: number, captured: {code: number, left: boolean[], right: boolean[]}) {
this.capture.emit({
index,
captured
});
}
}

View File

@@ -4,7 +4,9 @@
[currentLayer]="currentLayer"
[keybindAnimationEnabled]="keybindAnimationEnabled"
(keyClick)="onKeyClick($event.moduleId, $event.keyId, $event.keyTarget)"
(keyHover)="onKeyHover($event.moduleId, $event.event, $event.over, $event.keyId)"></keyboard-slider>
(keyHover)="onKeyHover($event.moduleId, $event.event, $event.over, $event.keyId)"
(capture)="onCapture($event.moduleId, $event.keyId, $event.captured)"
></keyboard-slider>
<popover tabindex="0" [visible]="popoverShown" [keyPosition]="keyPosition" [wrapPosition]="wrapPosition" [defaultKeyAction]="popoverInitKeyAction"
[currentKeymap]="keymap" [currentLayer]="currentLayer" (cancel)="hidePopover()" (remap)="onRemap($event)"></popover>
<div class="tooltip bottom"

View File

@@ -139,6 +139,27 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
}
}
onCapture(moduleId: number, keyId: number, captured: {code: number, left: boolean[], right: boolean[]}): void {
let keystrokeAction: KeystrokeAction = new KeystrokeAction();
const modifiers = captured.left.concat(captured.right).map(x => x ? 1 : 0);
keystrokeAction.scancode = captured.code;
keystrokeAction.modifierMask = 0;
for (let i = 0; i < modifiers.length; ++i) {
keystrokeAction.modifierMask |= modifiers[i] << this.mapper.modifierMapper(i);
}
this.store.dispatch(
KeymapActions.saveKey(
this.keymap,
this.currentLayer,
moduleId,
keyId,
keystrokeAction)
);
}
onRemap(keyAction: KeyAction): void {
this.store.dispatch(
KeymapActions.saveKey(
@@ -277,5 +298,4 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
selectLayer(index: number): void {
this.currentLayer = index;
}
}

View File

@@ -38,6 +38,14 @@ export class MapperService {
return 'assets/compiled_sprite.svg#' + this.nameToFileName.get(iconName);
}
public modifierMapper(x: number) {
if (x < 8) {
return Math.floor(x / 2) * 4 + 1 - x; // 1, 0, 3, 2, 5, 4, 7, 6
} else {
return x;
}
}
// TODO: read the mapping from JSON
private initScanCodeTextMap(): void {
this.scanCodeTextMap = new Map<number, string[]>();