Seperate electron and web target building
1
shared/src/components/svg/keyboard/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './svg-keyboard.component';
|
||||
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" [attr.viewBox]="svgAttributes.viewBox" height="100%" width="100%">
|
||||
<svg:g [attr.transform]="svgAttributes.transform" [attr.fill]="svgAttributes.fill">
|
||||
<svg:g svg-module *ngFor="let module of modules; let i = index"
|
||||
[coverages]="module.coverages"
|
||||
[keyboardKeys]="module.keyboardKeys"
|
||||
[keybindAnimationEnabled]="keybindAnimationEnabled"
|
||||
[capturingEnabled]="capturingEnabled"
|
||||
[attr.transform]="module.attributes.transform"
|
||||
[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>
|
||||
|
After Width: | Height: | Size: 871 B |
@@ -0,0 +1,6 @@
|
||||
:host {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
74
shared/src/components/svg/keyboard/svg-keyboard.component.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
|
||||
import { Module } from '../../../config-serializer/config-items/Module';
|
||||
import { SvgModule } from '../module';
|
||||
|
||||
@Component({
|
||||
selector: 'svg-keyboard',
|
||||
template: require('./svg-keyboard.component.html'),
|
||||
styles: [require('./svg-keyboard.component.scss')]
|
||||
})
|
||||
export class SvgKeyboardComponent implements OnInit {
|
||||
@Input() moduleConfig: Module[];
|
||||
@Input() keybindAnimationEnabled: boolean;
|
||||
@Input() capturingEnabled: boolean;
|
||||
@Output() keyClick = new EventEmitter();
|
||||
@Output() keyHover = new EventEmitter();
|
||||
@Output() capture = new EventEmitter();
|
||||
|
||||
private modules: SvgModule[];
|
||||
private svgAttributes: { viewBox: string, transform: string, fill: string };
|
||||
|
||||
constructor() {
|
||||
this.modules = [];
|
||||
this.svgAttributes = this.getKeyboardSvgAttributes();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.modules = this.getSvgModules();
|
||||
}
|
||||
|
||||
onKeyClick(moduleId: number, keyId: number, keyTarget: HTMLElement): void {
|
||||
this.keyClick.emit({
|
||||
moduleId,
|
||||
keyId,
|
||||
keyTarget
|
||||
});
|
||||
}
|
||||
|
||||
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,
|
||||
event,
|
||||
over,
|
||||
keyId
|
||||
});
|
||||
}
|
||||
|
||||
private getKeyboardSvgAttributes(): { viewBox: string, transform: string, fill: string } {
|
||||
let svg: any = this.getBaseLayer();
|
||||
return {
|
||||
viewBox: svg.$.viewBox,
|
||||
transform: svg.g[0].$.transform,
|
||||
fill: svg.g[0].$.fill
|
||||
};
|
||||
}
|
||||
|
||||
private getSvgModules(): SvgModule[] {
|
||||
let modules = this.getBaseLayer().g[0].g.map((obj: any) => new SvgModule(obj));
|
||||
return [modules[1], modules[0]]; // TODO: remove if the svg will be correct
|
||||
}
|
||||
|
||||
private getBaseLayer(): any {
|
||||
return require('xml!../../../../../images/base-layer.svg').svg;
|
||||
}
|
||||
|
||||
}
|
||||
13
shared/src/components/svg/keys/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export { SvgIconTextKeyComponent } from './svg-icon-text-key';
|
||||
export { SvgKeyboardKeyComponent, SvgKeyboardKey } from './svg-keyboard-key';
|
||||
export { SvgKeystrokeKeyComponent } from './svg-keystroke-key';
|
||||
export { SvgMouseKeyComponent } from './svg-mouse-key';
|
||||
export { SvgMouseClickKeyComponent } from './svg-mouse-click-key';
|
||||
export { SvgMouseMoveKeyComponent } from './svg-mouse-move-key';
|
||||
export { SvgMouseSpeedKeyComponent } from './svg-mouse-speed-key';
|
||||
export { SvgMouseScrollKeyComponent } from './svg-mouse-scroll-key';
|
||||
export { SvgOneLineTextKeyComponent } from './svg-one-line-text-key';
|
||||
export { SvgSingleIconKeyComponent } from './svg-single-icon-key';
|
||||
export { SvgSwitchKeymapKeyComponent } from './svg-switch-keymap-key';
|
||||
export { SvgTextIconKeyComponent } from './svg-text-icon-key';
|
||||
export { SvgTwoLineTextKeyComponent } from './svg-two-line-text-key';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './svg-icon-text-key.component';
|
||||
@@ -0,0 +1,13 @@
|
||||
<svg:use [attr.xlink:href]="icon"
|
||||
[attr.width]="useWidth"
|
||||
[attr.height]="useHeight"
|
||||
[attr.x]="useX"
|
||||
[attr.y]="useY">
|
||||
</svg:use>
|
||||
<svg:text
|
||||
[attr.x]="0"
|
||||
[attr.y]="textY"
|
||||
[attr.text-anchor]="'middle'"
|
||||
[attr.font-size]="11">
|
||||
<tspan [attr.x]="spanX">{{ text }}</tspan>
|
||||
</svg:text>
|
||||
|
After Width: | Height: | Size: 331 B |
@@ -0,0 +1,31 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'g[svg-icon-text-key]',
|
||||
template: require('./svg-icon-text-key.component.html')
|
||||
})
|
||||
export class SvgIconTextKeyComponent implements OnInit {
|
||||
@Input() width: number;
|
||||
@Input() height: number;
|
||||
@Input() icon: string;
|
||||
@Input() text: string;
|
||||
|
||||
private useWidth: number;
|
||||
private useHeight: number;
|
||||
private useX: number;
|
||||
private useY: number;
|
||||
private textY: number;
|
||||
private spanX: number;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.useWidth = this.width / 3;
|
||||
this.useHeight = this.height / 3;
|
||||
this.useX = (this.width > 2 * this.height) ? 0 : this.width / 3;
|
||||
this.useY = (this.width > 2 * this.height) ? this.height / 3 : this.height / 10;
|
||||
this.textY = (this.width > 2 * this.height) ? this.height / 2 : this.height * 0.6;
|
||||
this.spanX = (this.width > 2 * this.height) ? this.width * 0.6 : this.width / 2;
|
||||
}
|
||||
}
|
||||
2
shared/src/components/svg/keys/svg-keyboard-key/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './svg-keyboard-key.component';
|
||||
export * from './svg-keyboard-key.model';
|
||||
@@ -0,0 +1,65 @@
|
||||
<svg:rect [@change]="changeAnimation"
|
||||
(@change.done)="onChangeAnimationDone()"
|
||||
[id]="id" [attr.rx]="rx" [attr.ry]="ry"
|
||||
[attr.height]="height" [attr.width]="width"
|
||||
[attr.fill]="fill"
|
||||
/>
|
||||
<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>
|
||||
</template>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,12 @@
|
||||
:host {
|
||||
/deep/ text {
|
||||
dominant-baseline: central;
|
||||
}
|
||||
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
|
||||
&:hover {
|
||||
fill: #494949;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
import {
|
||||
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';
|
||||
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
import {
|
||||
KeyAction,
|
||||
KeystrokeAction,
|
||||
LayerName,
|
||||
MouseAction,
|
||||
PlayMacroAction,
|
||||
SwitchKeymapAction,
|
||||
SwitchLayerAction
|
||||
} from '../../../../config-serializer/config-items/key-action';
|
||||
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';
|
||||
import { getMacroEntities } from '../../../../store/reducers/macro';
|
||||
|
||||
enum LabelTypes {
|
||||
KeystrokeKey,
|
||||
MouseKey,
|
||||
OneLineText,
|
||||
TwoLineText,
|
||||
TextIcon,
|
||||
SingleIcon,
|
||||
SwitchKeymap,
|
||||
IconText
|
||||
}
|
||||
|
||||
@Component({
|
||||
animations: [
|
||||
trigger('change', [
|
||||
transition('inactive => active', [
|
||||
style({ fill: '#fff' }),
|
||||
group([
|
||||
animate('1s ease-out', style({
|
||||
fill: '#333'
|
||||
}))
|
||||
])
|
||||
])
|
||||
]),
|
||||
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]',
|
||||
template: require('./svg-keyboard-key.component.html'),
|
||||
styles: [require('./svg-keyboard-key.component.scss')]
|
||||
})
|
||||
export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
|
||||
@Input() id: string;
|
||||
@Input() rx: string;
|
||||
@Input() ry: string;
|
||||
@Input() height: number;
|
||||
@Input() width: number;
|
||||
@Input() keyAction: KeyAction;
|
||||
@Input() keybindAnimationEnabled: boolean;
|
||||
@Input() capturingEnabled: 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 recording: boolean;
|
||||
private scanCodePressed: boolean;
|
||||
|
||||
@HostListener('click')
|
||||
onClick() {
|
||||
this.reset();
|
||||
this.keyClick.emit(this.element.nativeElement);
|
||||
}
|
||||
|
||||
@HostListener('mousedown', ['$event'])
|
||||
onMouseDown(e: MouseEvent) {
|
||||
if ((e.which === 2 || e.button === 1) && this.capturingEnabled) {
|
||||
e.preventDefault();
|
||||
this.renderer.invokeElementMethod(this.element.nativeElement, 'focus');
|
||||
|
||||
if (this.recording) {
|
||||
this.reset();
|
||||
} else {
|
||||
this.recording = true;
|
||||
this.recordAnimation = 'active';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('keyup', ['$event'])
|
||||
onKeyUpe(e: KeyboardEvent) {
|
||||
if (this.scanCodePressed) {
|
||||
e.preventDefault();
|
||||
this.scanCodePressed = false;
|
||||
} else if (this.recording) {
|
||||
e.preventDefault();
|
||||
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));
|
||||
this.scanCodePressed = true;
|
||||
} 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();
|
||||
this.scanCodePressed = false;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.setLabels();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: { [propertyName: string]: SimpleChange }) {
|
||||
if (changes['keyAction']) {
|
||||
this.setLabels();
|
||||
if (this.keybindAnimationEnabled) {
|
||||
this.changeAnimation = 'active';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
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 {
|
||||
if (!this.keyAction) {
|
||||
this.labelSource = undefined;
|
||||
this.labelType = LabelTypes.OneLineText;
|
||||
return;
|
||||
}
|
||||
|
||||
this.labelType = LabelTypes.OneLineText;
|
||||
|
||||
if (this.keyAction instanceof KeystrokeAction) {
|
||||
let keyAction: KeystrokeAction = this.keyAction as KeystrokeAction;
|
||||
let newLabelSource: string[];
|
||||
|
||||
if (!keyAction.hasActiveModifier() && keyAction.hasScancode()) {
|
||||
let scancode: number = keyAction.scancode;
|
||||
newLabelSource = this.mapper.scanCodeToText(scancode);
|
||||
if (this.mapper.hasScancodeIcon(scancode)) {
|
||||
this.labelSource = this.mapper.scanCodeToSvgImagePath(scancode);
|
||||
this.labelType = LabelTypes.SingleIcon;
|
||||
} else if (newLabelSource !== undefined) {
|
||||
if (newLabelSource.length === 1) {
|
||||
this.labelSource = newLabelSource[0];
|
||||
this.labelType = LabelTypes.OneLineText;
|
||||
} else {
|
||||
this.labelSource = newLabelSource;
|
||||
this.labelType = LabelTypes.TwoLineText;
|
||||
}
|
||||
}
|
||||
} else if (keyAction.hasOnlyOneActiveModifier() && !keyAction.hasScancode()) {
|
||||
newLabelSource = [];
|
||||
switch (keyAction.modifierMask) {
|
||||
case KeyModifiers.leftCtrl:
|
||||
case KeyModifiers.rightCtrl:
|
||||
newLabelSource.push('Ctrl');
|
||||
break;
|
||||
case KeyModifiers.leftShift:
|
||||
case KeyModifiers.rightShift:
|
||||
newLabelSource.push('Shift');
|
||||
break;
|
||||
case KeyModifiers.leftAlt:
|
||||
case KeyModifiers.rightAlt:
|
||||
newLabelSource.push('Alt');
|
||||
break;
|
||||
case KeyModifiers.leftGui:
|
||||
case KeyModifiers.rightGui:
|
||||
newLabelSource.push('Super');
|
||||
break;
|
||||
default:
|
||||
newLabelSource.push('Undefined');
|
||||
break;
|
||||
}
|
||||
this.labelSource = newLabelSource;
|
||||
} else {
|
||||
this.labelType = LabelTypes.KeystrokeKey;
|
||||
this.labelSource = this.keyAction;
|
||||
}
|
||||
} else if (this.keyAction instanceof SwitchLayerAction) {
|
||||
let keyAction: SwitchLayerAction = this.keyAction as SwitchLayerAction;
|
||||
let newLabelSource: string;
|
||||
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.isLayerToggleable) {
|
||||
this.labelType = LabelTypes.TextIcon;
|
||||
this.labelSource = {
|
||||
text: newLabelSource,
|
||||
icon: this.mapper.getIcon('toggle')
|
||||
};
|
||||
} else {
|
||||
this.labelType = LabelTypes.OneLineText;
|
||||
this.labelSource = newLabelSource;
|
||||
}
|
||||
} else if (this.keyAction instanceof SwitchKeymapAction) {
|
||||
let keyAction: SwitchKeymapAction = this.keyAction as SwitchKeymapAction;
|
||||
this.labelType = LabelTypes.SwitchKeymap;
|
||||
this.labelSource = keyAction.keymapAbbreviation;
|
||||
} else if (this.keyAction instanceof PlayMacroAction) {
|
||||
let keyAction: PlayMacroAction = this.keyAction as PlayMacroAction;
|
||||
const macro: Macro = this.macros.find((_macro: Macro) => _macro.id === 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export interface SvgKeyboardKey {
|
||||
id: string;
|
||||
x: string;
|
||||
y: string;
|
||||
rx: string;
|
||||
ry: string;
|
||||
height: number;
|
||||
width: number;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './svg-keystroke-key.component';
|
||||
@@ -0,0 +1,32 @@
|
||||
<svg [attr.viewBox]="viewBox" [attr.width]="textContainer.width" [attr.height]="textContainer.height"
|
||||
[attr.x]="textContainer.x" [attr.y]="textContainer.y" [ngSwitch]="labelType">
|
||||
<svg:g svg-one-line-text-key *ngSwitchCase="'one-line'"
|
||||
[height]="height"
|
||||
[width]="width"
|
||||
[text]="labelSource">
|
||||
</svg:g>
|
||||
<svg:g svg-two-line-text-key *ngSwitchCase="'two-line'"
|
||||
[height]="height"
|
||||
[width]="width"
|
||||
[texts]="labelSource">
|
||||
</svg:g>
|
||||
</svg>
|
||||
<svg [attr.viewBox]="viewBox" [attr.width]="modifierContainer.width" [attr.height]="modifierContainer.height" [attr.x]="modifierContainer.x"
|
||||
[attr.y]="modifierContainer.y" preserveAspectRatio="none">
|
||||
<svg viewBox="0 0 100 100" [attr.width]="shift.width" [attr.height]="shift.height" [attr.x]="shift.x" [attr.y]="shift.y"
|
||||
preserveAspectRatio="none" [class.disabled]="shift.disabled">
|
||||
<svg:use [attr.xlink:href]="modifierIconNames.shift" />
|
||||
</svg>
|
||||
<svg viewBox="0 0 100 100" [attr.width]="control.width" [attr.height]="control.height" [attr.x]="control.x" [attr.y]="control.y"
|
||||
preserveAspectRatio="none" [class.disabled]="control.disabled">
|
||||
<svg:text [attr.text-anchor]="'middle'" [attr.x]="50" [attr.y]="50">C</svg:text>
|
||||
</svg>
|
||||
<svg viewBox="0 0 100 100" [attr.width]="option.width" [attr.height]="option.height" [attr.x]="option.x" [attr.y]="option.y"
|
||||
preserveAspectRatio="none" [class.disabled]="option.disabled">
|
||||
<svg:use [attr.xlink:href]="modifierIconNames.option" />
|
||||
</svg>
|
||||
<svg viewBox="0 0 100 100" [attr.width]="command.width" [attr.height]="command.height" [attr.x]="command.x" [attr.y]="command.y"
|
||||
preserveAspectRatio="none" [class.disabled]="command.disabled">
|
||||
<svg:use [attr.xlink:href]="modifierIconNames.command" />
|
||||
</svg>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,7 @@
|
||||
.disabled {
|
||||
fill: gray;
|
||||
}
|
||||
|
||||
text {
|
||||
font-size: 100px;
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
import { Component, Input, OnChanges, OnInit } from '@angular/core';
|
||||
|
||||
import { KeystrokeAction } from '../../../../config-serializer/config-items/key-action';
|
||||
import { KeyModifiers } from '../../../../config-serializer/config-items/KeyModifiers';
|
||||
import { MapperService } from '../../../../services/mapper.service';
|
||||
|
||||
class SvgAttributes {
|
||||
width: number;
|
||||
height: number;
|
||||
x: number;
|
||||
y: number;
|
||||
disabled: boolean;
|
||||
|
||||
constructor() {
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
enum Modifiers {
|
||||
Shift, Control, Alt, Command
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'g[svg-keystroke-key]',
|
||||
template: require('./svg-keystroke-key.component.html'),
|
||||
styles: [require('./svg-keystroke-key.component.scss')]
|
||||
})
|
||||
export class SvgKeystrokeKeyComponent implements OnInit, OnChanges {
|
||||
@Input() height: number;
|
||||
@Input() width: number;
|
||||
@Input() keystrokeAction: KeystrokeAction;
|
||||
|
||||
private viewBox: string;
|
||||
private textContainer: SvgAttributes;
|
||||
private modifierContainer: SvgAttributes;
|
||||
private shift: SvgAttributes;
|
||||
private control: SvgAttributes;
|
||||
private option: SvgAttributes;
|
||||
private command: SvgAttributes;
|
||||
|
||||
private labelSource: any;
|
||||
private labelType: 'empty' | 'one-line' | 'two-line' | 'icon';
|
||||
|
||||
private modifierIconNames: {
|
||||
shift?: string,
|
||||
option?: string,
|
||||
command?: string
|
||||
};
|
||||
|
||||
constructor(private mapper: MapperService) {
|
||||
this.modifierIconNames = {};
|
||||
this.textContainer = new SvgAttributes();
|
||||
this.modifierContainer = new SvgAttributes();
|
||||
this.shift = new SvgAttributes();
|
||||
this.control = new SvgAttributes();
|
||||
this.option = new SvgAttributes();
|
||||
this.command = new SvgAttributes();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.viewBox = [0, 0, this.width, this.height].join(' ');
|
||||
this.modifierIconNames.shift = this.mapper.getIcon('shift');
|
||||
this.modifierIconNames.option = this.mapper.getIcon('option');
|
||||
this.modifierIconNames.command = this.mapper.getIcon('command');
|
||||
|
||||
let bottomSideMode: boolean = this.width < this.height * 1.8;
|
||||
|
||||
const heightWidthRatio = this.height / this.width;
|
||||
|
||||
if (bottomSideMode) {
|
||||
const maxIconWidth = this.width / 4;
|
||||
const maxIconHeight = this.height;
|
||||
const iconScalingFactor = 0.8;
|
||||
let iconWidth = iconScalingFactor * heightWidthRatio * maxIconWidth;
|
||||
let iconHeight = iconScalingFactor * maxIconHeight;
|
||||
this.modifierContainer.width = this.width;
|
||||
this.modifierContainer.height = this.height / 5;
|
||||
this.modifierContainer.y = this.height - this.modifierContainer.height;
|
||||
this.shift.width = iconWidth;
|
||||
this.shift.height = iconHeight;
|
||||
this.shift.x = (maxIconWidth - iconWidth) / 2;
|
||||
this.shift.y = (maxIconHeight - iconHeight) / 2;
|
||||
this.control.width = iconWidth;
|
||||
this.control.height = iconHeight;
|
||||
this.control.x = this.shift.x + maxIconWidth;
|
||||
this.control.y = this.shift.y;
|
||||
this.option.width = iconWidth;
|
||||
this.option.height = iconHeight;
|
||||
this.option.x = this.control.x + maxIconWidth;
|
||||
this.option.y = this.shift.y;
|
||||
this.command.width = iconWidth;
|
||||
this.command.height = iconHeight;
|
||||
this.command.x = this.option.x + maxIconWidth;
|
||||
this.command.y = this.shift.y;
|
||||
this.textContainer.y = -this.modifierContainer.height / 2;
|
||||
} else {
|
||||
this.modifierContainer.width = this.width / 4;
|
||||
this.modifierContainer.height = this.height;
|
||||
this.modifierContainer.x = this.width - this.modifierContainer.width;
|
||||
|
||||
const length = Math.min(this.modifierContainer.width / 2, this.modifierContainer.height / 2);
|
||||
|
||||
const iconScalingFactor = 0.8;
|
||||
const iconWidth = iconScalingFactor * this.width * (length / this.modifierContainer.width);
|
||||
const iconHeight = iconScalingFactor * this.height * (length / this.modifierContainer.height);
|
||||
this.shift.width = iconWidth;
|
||||
this.shift.height = iconHeight;
|
||||
this.shift.x = this.width / 4 - iconWidth / 2;
|
||||
this.shift.y = this.height / 4 - iconHeight / 2;
|
||||
this.control.width = iconWidth;
|
||||
this.control.height = iconHeight;
|
||||
this.control.x = this.shift.x + this.width / 2;
|
||||
this.control.y = this.shift.y;
|
||||
this.option.width = iconWidth;
|
||||
this.option.height = iconHeight;
|
||||
this.option.x = this.shift.x;
|
||||
this.option.y = this.shift.y + this.height / 2;
|
||||
this.command.width = iconWidth;
|
||||
this.command.height = iconHeight;
|
||||
this.command.x = this.option.x + this.width / 2;
|
||||
this.command.y = this.option.y;
|
||||
this.textContainer.x = -this.modifierContainer.width / 2;
|
||||
}
|
||||
|
||||
this.textContainer.width = this.width;
|
||||
this.textContainer.height = this.height;
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
let newLabelSource: string[];
|
||||
if (this.keystrokeAction.hasScancode()) {
|
||||
let scancode: number = this.keystrokeAction.scancode;
|
||||
newLabelSource = this.mapper.scanCodeToText(scancode);
|
||||
if (newLabelSource) {
|
||||
if (newLabelSource.length === 1) {
|
||||
this.labelSource = newLabelSource[0];
|
||||
this.labelType = 'one-line';
|
||||
} else {
|
||||
this.labelSource = newLabelSource;
|
||||
this.labelType = 'two-line';
|
||||
}
|
||||
} else {
|
||||
this.labelSource = this.mapper.scanCodeToSvgImagePath(scancode);
|
||||
this.labelType = 'icon';
|
||||
}
|
||||
} else {
|
||||
this.labelType = 'empty';
|
||||
}
|
||||
|
||||
this.shift.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftShift | KeyModifiers.rightShift);
|
||||
this.control.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftCtrl | KeyModifiers.rightCtrl);
|
||||
this.option.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftAlt | KeyModifiers.rightAlt);
|
||||
this.command.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftGui | KeyModifiers.rightGui);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './svg-mouse-click-key';
|
||||
@@ -0,0 +1,16 @@
|
||||
<svg:use [attr.xlink:href]="icon" width="20" height="20" x="10" y="25">
|
||||
</svg:use>
|
||||
<svg:text
|
||||
[attr.x]="60"
|
||||
[attr.y]="0"
|
||||
[attr.text-anchor]="'middle'"
|
||||
[attr.font-size]="25">
|
||||
<tspan dy="34"> Click </tspan>
|
||||
</svg:text>
|
||||
<svg:text
|
||||
[attr.x]="50"
|
||||
[attr.y]="0"
|
||||
[attr.text-anchor]="'middle'"
|
||||
[attr.font-size]="25">
|
||||
<tspan dy="70"> {{ button }} </tspan>
|
||||
</svg:text>
|
||||
|
After Width: | Height: | Size: 403 B |
@@ -0,0 +1,19 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
import { MapperService } from '../../../../services/mapper.service';
|
||||
|
||||
@Component({
|
||||
selector: 'g[svg-mouse-click-key]',
|
||||
template: require('./svg-mouse-click-key.html')
|
||||
})
|
||||
export class SvgMouseClickKeyComponent implements OnInit {
|
||||
@Input() button: string;
|
||||
|
||||
private icon: string;
|
||||
|
||||
constructor(private mapper: MapperService) {
|
||||
this.icon = this.mapper.getIcon('mouse');
|
||||
}
|
||||
|
||||
ngOnInit() { }
|
||||
}
|
||||
1
shared/src/components/svg/keys/svg-mouse-key/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './svg-mouse-key';
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg:g [ngSwitch]="type">
|
||||
<svg:g *ngSwitchCase="'click'" svg-mouse-click-key [button]="param"></svg:g>
|
||||
<svg:g *ngSwitchCase="'move'" svg-mouse-move-key [direction]="param"></svg:g>
|
||||
<svg:g *ngSwitchCase="'scroll'" svg-mouse-scroll-key [direction]="param"></svg:g>
|
||||
<svg:g *ngSwitchCase="'speed'" svg-mouse-speed-key [plus]="param"></svg:g>
|
||||
</svg:g>
|
||||
|
After Width: | Height: | Size: 378 B |
@@ -0,0 +1,74 @@
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
|
||||
import { MouseAction, MouseActionParam } from '../../../../config-serializer/config-items/key-action';
|
||||
|
||||
@Component({
|
||||
selector: 'g[svg-mouse-key]',
|
||||
template: require('./svg-mouse-key.html')
|
||||
})
|
||||
export class SvgMouseKeyComponent implements OnChanges {
|
||||
@Input() mouseAction: MouseAction;
|
||||
private type: 'click' | 'scroll' | 'move' | 'speed';
|
||||
private param: any;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnChanges() {
|
||||
switch (this.mouseAction.mouseAction) {
|
||||
case MouseActionParam.leftClick:
|
||||
this.type = 'click';
|
||||
this.param = 'Left';
|
||||
break;
|
||||
case MouseActionParam.rightClick:
|
||||
this.type = 'click';
|
||||
this.param = 'Right';
|
||||
break;
|
||||
case MouseActionParam.middleClick:
|
||||
this.type = 'click';
|
||||
this.param = 'Middle';
|
||||
break;
|
||||
case MouseActionParam.scrollDown:
|
||||
this.type = 'scroll';
|
||||
this.param = 'down';
|
||||
break;
|
||||
case MouseActionParam.scrollLeft:
|
||||
this.type = 'scroll';
|
||||
this.param = 'left';
|
||||
break;
|
||||
case MouseActionParam.scrollRight:
|
||||
this.type = 'scroll';
|
||||
this.param = 'right';
|
||||
break;
|
||||
case MouseActionParam.scrollUp:
|
||||
this.type = 'scroll';
|
||||
this.param = 'up';
|
||||
break;
|
||||
case MouseActionParam.moveDown:
|
||||
this.type = 'move';
|
||||
this.param = 'down';
|
||||
break;
|
||||
case MouseActionParam.moveLeft:
|
||||
this.type = 'move';
|
||||
this.param = 'left';
|
||||
break;
|
||||
case MouseActionParam.moveRight:
|
||||
this.type = 'move';
|
||||
this.param = 'right';
|
||||
break;
|
||||
case MouseActionParam.moveUp:
|
||||
this.type = 'move';
|
||||
this.param = 'up';
|
||||
break;
|
||||
case MouseActionParam.accelerate:
|
||||
this.type = 'speed';
|
||||
this.param = true;
|
||||
break;
|
||||
case MouseActionParam.decelerate:
|
||||
this.type = 'speed';
|
||||
this.param = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './svg-mouse-move-key';
|
||||
@@ -0,0 +1,9 @@
|
||||
<svg:use [attr.xlink:href]="mouseIcon" width="20" height="20" x="8" y="25"></svg:use>
|
||||
<svg:text
|
||||
[attr.x]="60"
|
||||
[attr.y]="0"
|
||||
[attr.text-anchor]="'middle'"
|
||||
[attr.font-size]="24">
|
||||
<tspan dy="34"> Move </tspan>
|
||||
</svg:text>
|
||||
<svg:use [attr.xlink:href]="directionIcon" width="30" height="30" x="35" y="55"></svg:use>
|
||||
|
After Width: | Height: | Size: 332 B |
@@ -0,0 +1,21 @@
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
|
||||
import { MapperService } from '../../../../services/mapper.service';
|
||||
|
||||
@Component({
|
||||
selector: 'g[svg-mouse-move-key]',
|
||||
template: require('./svg-mouse-move-key.html')
|
||||
})
|
||||
export class SvgMouseMoveKeyComponent implements OnChanges {
|
||||
@Input() direction: string;
|
||||
|
||||
private mouseIcon: string;
|
||||
private directionIcon: string;
|
||||
|
||||
constructor(private mapper: MapperService) { }
|
||||
|
||||
ngOnChanges() {
|
||||
this.mouseIcon = this.mapper.getIcon('mouse');
|
||||
this.directionIcon = this.mapper.getIcon(`${this.direction}-arrow`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './svg-mouse-scroll-key';
|
||||
@@ -0,0 +1,9 @@
|
||||
<svg:use [attr.xlink:href]="mouseIcon" width="20" height="20" x="8" y="25"></svg:use>
|
||||
<svg:text
|
||||
[attr.x]="60"
|
||||
[attr.y]="0"
|
||||
[attr.text-anchor]="'middle'"
|
||||
[attr.font-size]="24">
|
||||
<tspan dy="34"> Scroll </tspan>
|
||||
</svg:text>
|
||||
<svg:use [attr.xlink:href]="directionIcon" width="30" height="30" x="35" y="55"></svg:use>
|
||||
|
After Width: | Height: | Size: 334 B |
@@ -0,0 +1,21 @@
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
|
||||
import { MapperService } from '../../../../services/mapper.service';
|
||||
|
||||
@Component({
|
||||
selector: 'g[svg-mouse-scroll-key]',
|
||||
template: require('./svg-mouse-scroll-key.html')
|
||||
})
|
||||
export class SvgMouseScrollKeyComponent implements OnChanges {
|
||||
@Input() direction: string;
|
||||
|
||||
private mouseIcon: string;
|
||||
private directionIcon: string;
|
||||
|
||||
constructor(private mapper: MapperService) { }
|
||||
|
||||
ngOnChanges() {
|
||||
this.mouseIcon = this.mapper.getIcon('mouse');
|
||||
this.directionIcon = this.mapper.getIcon(`scroll-${this.direction}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './svg-mouse-speed-key';
|
||||
@@ -0,0 +1,16 @@
|
||||
<svg:use [attr.xlink:href]="icon" width="20" height="20" x="4" y="25">
|
||||
</svg:use>
|
||||
<svg:text
|
||||
[attr.x]="60"
|
||||
[attr.y]="0"
|
||||
[attr.text-anchor]="'middle'"
|
||||
[attr.font-size]="25">
|
||||
<tspan dy="34"> Speed </tspan>
|
||||
</svg:text>
|
||||
<svg:text
|
||||
[attr.x]="50"
|
||||
[attr.y]="0"
|
||||
[attr.text-anchor]="'middle'"
|
||||
[attr.font-size]="30">
|
||||
<tspan dy="70"> {{ sign }} </tspan>
|
||||
</svg:text>
|
||||
|
After Width: | Height: | Size: 400 B |
@@ -0,0 +1,22 @@
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
|
||||
import { MapperService } from '../../../../services/mapper.service';
|
||||
|
||||
@Component({
|
||||
selector: 'g[svg-mouse-speed-key]',
|
||||
template: require('./svg-mouse-speed-key.html')
|
||||
})
|
||||
export class SvgMouseSpeedKeyComponent implements OnChanges {
|
||||
@Input() plus: boolean;
|
||||
|
||||
private icon: string;
|
||||
private sign: string;
|
||||
|
||||
constructor(private mapper: MapperService) {
|
||||
this.icon = this.mapper.getIcon('mouse');
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
this.sign = this.plus ? '+' : '-';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './svg-one-line-text-key.component';
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg:text
|
||||
[attr.x]="0"
|
||||
[attr.y]="textY"
|
||||
[attr.text-anchor]="'middle'">
|
||||
<tspan [attr.x]="spanX" dy="0">{{ text }}</tspan>
|
||||
</svg:text>
|
||||
|
After Width: | Height: | Size: 154 B |
@@ -0,0 +1,21 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'g[svg-one-line-text-key]',
|
||||
template: require('./svg-one-line-text-key.component.html')
|
||||
})
|
||||
export class SvgOneLineTextKeyComponent implements OnInit {
|
||||
@Input() height: number;
|
||||
@Input() width: number;
|
||||
@Input() text: string;
|
||||
|
||||
private textY: number;
|
||||
private spanX: number;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
this.textY = this.height / 2;
|
||||
this.spanX = this.width / 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './svg-single-icon-key.component';
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg:use [attr.xlink:href]="icon"
|
||||
[attr.width]="svgWidth" [attr.height]="svgHeight"
|
||||
[attr.x]="svgWidth" [attr.y]="svgHeight">
|
||||
</svg:use>
|
||||
|
After Width: | Height: | Size: 144 B |
@@ -0,0 +1,21 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'g[svg-single-icon-key]',
|
||||
template: require('./svg-single-icon-key.component.html')
|
||||
})
|
||||
export class SvgSingleIconKeyComponent implements OnInit {
|
||||
@Input() width: number;
|
||||
@Input() height: number;
|
||||
@Input() icon: string;
|
||||
|
||||
private svgHeight: number;
|
||||
private svgWidth: number;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
this.svgWidth = this.width / 3;
|
||||
this.svgHeight = this.height / 3;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './svg-switch-keymap-key.component';
|
||||
@@ -0,0 +1,12 @@
|
||||
<svg:use [attr.xlink:href]="icon"
|
||||
[attr.width]="useWidth"
|
||||
[attr.height]="useHeight"
|
||||
[attr.x]="useX"
|
||||
[attr.y]="useY">
|
||||
</svg:use>
|
||||
<svg:text
|
||||
[attr.x]="0"
|
||||
[attr.y]="textY"
|
||||
[attr.text-anchor]="'middle'">
|
||||
<tspan [attr.x]="spanX">{{ abbreviation }}</tspan>
|
||||
</svg:text>
|
||||
|
After Width: | Height: | Size: 313 B |
@@ -0,0 +1,34 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
import { MapperService } from '../../../../services/mapper.service';
|
||||
|
||||
@Component({
|
||||
selector: 'g[svg-switch-keymap-key]',
|
||||
template: require('./svg-switch-keymap-key.component.html')
|
||||
})
|
||||
export class SvgSwitchKeymapKeyComponent implements OnInit {
|
||||
@Input() width: number;
|
||||
@Input() height: number;
|
||||
@Input() abbreviation: string;
|
||||
|
||||
private icon: string;
|
||||
private useWidth: number;
|
||||
private useHeight: number;
|
||||
private useX: number;
|
||||
private useY: number;
|
||||
private textY: number;
|
||||
private spanX: number;
|
||||
|
||||
constructor(private mapperService: MapperService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.icon = this.mapperService.getIcon('switch-keymap');
|
||||
|
||||
this.useWidth = this.width / 4;
|
||||
this.useHeight = this.height / 4;
|
||||
this.useX = this.width * 3 / 8;
|
||||
this.useY = this.height / 5;
|
||||
this.textY = this.height * 2 / 3;
|
||||
this.spanX = this.width / 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './svg-text-icon-key.component';
|
||||
@@ -0,0 +1,12 @@
|
||||
<svg:text
|
||||
[attr.x]="0"
|
||||
[attr.y]="textY"
|
||||
[attr.text-anchor]="textAnchor">
|
||||
<tspan [attr.x]="spanX">{{ text }}</tspan>
|
||||
</svg:text>
|
||||
<svg:use [attr.xlink:href]="icon"
|
||||
[attr.width]="useWidth"
|
||||
[attr.height]="useHeight"
|
||||
[attr.x]="useX"
|
||||
[attr.y]="useY">
|
||||
</svg:use>
|
||||
|
After Width: | Height: | Size: 309 B |
@@ -0,0 +1,32 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'g[svg-text-icon-key]',
|
||||
template: require('./svg-text-icon-key.component.html')
|
||||
})
|
||||
export class SvgTextIconKeyComponent implements OnInit {
|
||||
@Input() width: number;
|
||||
@Input() height: number;
|
||||
@Input() text: string;
|
||||
@Input() icon: string;
|
||||
|
||||
private useWidth: number;
|
||||
private useHeight: number;
|
||||
private useX: number;
|
||||
private useY: number;
|
||||
private textY: number;
|
||||
private textAnchor: string;
|
||||
private spanX: number;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
this.useWidth = this.width / 3;
|
||||
this.useHeight = this.height / 3;
|
||||
this.useX = (this.width > 2 * this.height) ? this.width * 0.6 : this.width / 3;
|
||||
this.useY = (this.width > 2 * this.height) ? this.height / 3 : this.height / 2;
|
||||
this.textY = (this.width > 2 * this.height) ? this.height / 2 : this.height / 3;
|
||||
this.textAnchor = (this.width > 2 * this.height) ? 'end' : 'middle';
|
||||
this.spanX = (this.width > 2 * this.height) ? 0.6 * this.width : this.width / 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './svg-two-line-text-key.component';
|
||||
@@ -0,0 +1,11 @@
|
||||
<svg:text
|
||||
[attr.x]="0"
|
||||
[attr.y]="textY"
|
||||
[attr.text-anchor]="'middle'">
|
||||
<tspan
|
||||
*ngFor="let text of texts; let index = index"
|
||||
[attr.x]="spanX"
|
||||
[attr.y]="spanYs[index]"
|
||||
dy="0"
|
||||
>{{ text }}</tspan>
|
||||
</svg:text>
|
||||
|
After Width: | Height: | Size: 306 B |
@@ -0,0 +1,27 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'g[svg-two-line-text-key]',
|
||||
template: require('./svg-two-line-text-key.component.html')
|
||||
})
|
||||
export class SvgTwoLineTextKeyComponent implements OnInit {
|
||||
@Input() height: number;
|
||||
@Input() width: number;
|
||||
@Input() texts: string[];
|
||||
|
||||
private textY: number;
|
||||
private spanX: number;
|
||||
private spanYs: number[];
|
||||
|
||||
constructor() {
|
||||
this.spanYs = [];
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.textY = this.height / 2;
|
||||
this.spanX = this.width / 2;
|
||||
for (let i = 0; i < this.texts.length; ++i) {
|
||||
this.spanYs.push((0.75 - i * 0.5) * this.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
shared/src/components/svg/module/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './svg-module.component';
|
||||
export * from './svg-module.model';
|
||||
14
shared/src/components/svg/module/svg-module.component.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<svg:path *ngFor="let path of coverages" [attr.d]="path.$.d" />
|
||||
<svg:g svg-keyboard-key *ngFor="let key of keyboardKeys; let i = index"
|
||||
[id]="key.id"
|
||||
[rx]="key.rx" [ry]="key.ry"
|
||||
[width]="key.width" [height]="key.height"
|
||||
[attr.transform]="'translate(' + key.x + ' ' + key.y + ')'"
|
||||
[keyAction]="keyActions[i]"
|
||||
[keybindAnimationEnabled]="keybindAnimationEnabled"
|
||||
[capturingEnabled]="capturingEnabled"
|
||||
(keyClick)="onKeyClick(i, $event)"
|
||||
(capture)="onCapture(i, $event)"
|
||||
(mouseenter)="onKeyHover(i, $event, true)"
|
||||
(mouseleave)="onKeyHover(i, $event, false)"
|
||||
/>
|
||||
|
After Width: | Height: | Size: 643 B |
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
position: relative;
|
||||
}
|
||||
47
shared/src/components/svg/module/svg-module.component.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
|
||||
import { KeyAction } from '../../../config-serializer/config-items/key-action';
|
||||
|
||||
import { SvgKeyboardKey } from '../keys';
|
||||
|
||||
@Component({
|
||||
selector: 'g[svg-module]',
|
||||
template: require('./svg-module.component.html'),
|
||||
styles: [require('./svg-module.component.scss')]
|
||||
})
|
||||
export class SvgModuleComponent {
|
||||
@Input() coverages: any[];
|
||||
@Input() keyboardKeys: SvgKeyboardKey[];
|
||||
@Input() keyActions: KeyAction[];
|
||||
@Input() keybindAnimationEnabled: boolean;
|
||||
@Input() capturingEnabled: boolean;
|
||||
@Output() keyClick = new EventEmitter();
|
||||
@Output() keyHover = new EventEmitter();
|
||||
@Output() capture = new EventEmitter();
|
||||
|
||||
constructor() {
|
||||
this.keyboardKeys = [];
|
||||
}
|
||||
|
||||
onKeyClick(index: number, keyTarget: HTMLElement): void {
|
||||
this.keyClick.emit({
|
||||
index,
|
||||
keyTarget
|
||||
});
|
||||
}
|
||||
|
||||
onKeyHover(index: number, event: MouseEvent, over: boolean): void {
|
||||
this.keyHover.emit({
|
||||
index,
|
||||
event,
|
||||
over
|
||||
});
|
||||
}
|
||||
|
||||
onCapture(index: number, captured: {code: number, left: boolean[], right: boolean[]}) {
|
||||
this.capture.emit({
|
||||
index,
|
||||
captured
|
||||
});
|
||||
}
|
||||
}
|
||||
17
shared/src/components/svg/module/svg-module.model.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { SvgKeyboardKey } from '../keys';
|
||||
|
||||
export class SvgModule {
|
||||
private coverages: any[];
|
||||
private keyboardKeys: SvgKeyboardKey[];
|
||||
private attributes: any;
|
||||
|
||||
constructor(obj: { rect: any[], path: any[], $: Object }) {
|
||||
this.keyboardKeys = obj.rect.map(rect => rect.$).map(rect => {
|
||||
rect.height = +rect.height;
|
||||
rect.width = +rect.width;
|
||||
return rect;
|
||||
});
|
||||
this.coverages = obj.path;
|
||||
this.attributes = obj.$;
|
||||
}
|
||||
}
|
||||
1
shared/src/components/svg/wrap/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './svg-keyboard-wrap.component';
|
||||
@@ -0,0 +1,25 @@
|
||||
<template ngIf="layers">
|
||||
<layers [class.disabled]="popoverShown" (select)="selectLayer($event.index)" [current]="currentLayer"></layers>
|
||||
<keyboard-slider [layers]="layers"
|
||||
[currentLayer]="currentLayer"
|
||||
[keybindAnimationEnabled]="keybindAnimationEnabled"
|
||||
[capturingEnabled]="popoverEnabled"
|
||||
(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)"
|
||||
></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"
|
||||
[class.in]="tooltipData.show"
|
||||
[style.top.px]="tooltipData.posTop"
|
||||
[style.left.px]="tooltipData.posLeft"
|
||||
>
|
||||
<div class="tooltip-arrow"></div>
|
||||
<div class="tooltip-inner">
|
||||
<p *ngFor="let item of tooltipData.content | async">
|
||||
{{ item.name }}: {{ item.value }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,50 @@
|
||||
:host {
|
||||
width: 100%;
|
||||
display: block;
|
||||
|
||||
&.space {
|
||||
margin-bottom: 405px;
|
||||
}
|
||||
}
|
||||
|
||||
keyboard-slider {
|
||||
display: block;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
/* TODO create dynamic */
|
||||
height: 500px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: fixed;
|
||||
transform: translate(-50%, -6px);
|
||||
display: none;
|
||||
|
||||
&-inner {
|
||||
background: #fff;
|
||||
color: #000;
|
||||
box-shadow: 0 1px 5px #000;
|
||||
text-align: left;
|
||||
|
||||
p {
|
||||
margin-bottom: 2px;
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.bottom {
|
||||
.tooltip-arrow {
|
||||
border-bottom-color: #fff;
|
||||
top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
&.in {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
343
shared/src/components/svg/wrap/svg-keyboard-wrap.component.ts
Normal file
@@ -0,0 +1,343 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
ElementRef,
|
||||
Renderer,
|
||||
HostBinding,
|
||||
HostListener,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
SimpleChanges
|
||||
} from '@angular/core';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/observable/of';
|
||||
import 'rxjs/add/operator/map';
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { MapperService } from '../../../services/mapper.service';
|
||||
|
||||
import {
|
||||
KeyAction,
|
||||
KeystrokeAction,
|
||||
LayerName,
|
||||
MouseAction,
|
||||
MouseActionParam,
|
||||
PlayMacroAction,
|
||||
SwitchKeymapAction,
|
||||
SwitchLayerAction
|
||||
} from '../../../config-serializer/config-items/key-action';
|
||||
import { Keymap } from '../../../config-serializer/config-items/Keymap';
|
||||
import { Layer } from '../../../config-serializer/config-items/Layer';
|
||||
import { LongPressAction } from '../../../config-serializer/config-items/LongPressAction';
|
||||
import { camelCaseToSentence, capitalizeFirstLetter } from '../../../util';
|
||||
|
||||
import { AppState } from '../../../store';
|
||||
import { KeymapActions } from '../../../store/actions';
|
||||
import { PopoverComponent } from '../../popover';
|
||||
|
||||
interface NameValuePair {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'svg-keyboard-wrap',
|
||||
template: require('./svg-keyboard-wrap.component.html'),
|
||||
styles: [require('./svg-keyboard-wrap.component.scss')],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
|
||||
@Input() keymap: Keymap;
|
||||
@Input() popoverEnabled: boolean = true;
|
||||
@Input() tooltipEnabled: boolean = false;
|
||||
|
||||
@ViewChild(PopoverComponent, { read: ElementRef }) popover: ElementRef;
|
||||
|
||||
private popoverShown: boolean;
|
||||
private keyEditConfig: { moduleId: number, keyId: number };
|
||||
private popoverInitKeyAction: KeyAction;
|
||||
private keybindAnimationEnabled: boolean;
|
||||
private currentLayer: number = 0;
|
||||
private tooltipData: {
|
||||
posTop: number,
|
||||
posLeft: number,
|
||||
content: Observable<NameValuePair[]>,
|
||||
show: boolean
|
||||
};
|
||||
private layers: Layer[];
|
||||
private keyPosition: ClientRect;
|
||||
private wrapPosition: ClientRect;
|
||||
private wrapHost: HTMLElement;
|
||||
private keyElement: HTMLElement;
|
||||
|
||||
@HostBinding('class.space') get space() {
|
||||
return this.popoverEnabled;
|
||||
}
|
||||
|
||||
@HostListener('window:resize')
|
||||
onResize() {
|
||||
if (this.wrapHost) {
|
||||
this.wrapPosition = this.wrapHost.getBoundingClientRect();
|
||||
}
|
||||
|
||||
if (this.keyElement) {
|
||||
this.keyPosition = this.keyElement.getBoundingClientRect();
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
private store: Store<AppState>,
|
||||
private mapper: MapperService,
|
||||
private element: ElementRef,
|
||||
private renderer: Renderer
|
||||
) {
|
||||
this.keyEditConfig = {
|
||||
moduleId: undefined,
|
||||
keyId: undefined
|
||||
};
|
||||
|
||||
this.tooltipData = {
|
||||
posTop: 0,
|
||||
posLeft: 0,
|
||||
content: Observable.of([]),
|
||||
show: false
|
||||
};
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.wrapHost = this.element.nativeElement;
|
||||
this.wrapPosition = this.wrapHost.getBoundingClientRect();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
const keymapChanges = changes['keymap'];
|
||||
if (keymapChanges) {
|
||||
this.popoverShown = false;
|
||||
this.layers = this.keymap.layers;
|
||||
if (keymapChanges.previousValue.abbreviation !== keymapChanges.currentValue.abbreviation) {
|
||||
this.currentLayer = 0;
|
||||
this.keybindAnimationEnabled = keymapChanges.isFirstChange();
|
||||
} else {
|
||||
this.keybindAnimationEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onKeyClick(moduleId: number, keyId: number, keyTarget: HTMLElement): void {
|
||||
if (!this.popoverShown && this.popoverEnabled) {
|
||||
this.keyEditConfig = {
|
||||
moduleId,
|
||||
keyId
|
||||
};
|
||||
|
||||
const keyActionToEdit: KeyAction = this.layers[this.currentLayer].modules[moduleId].keyActions[keyId];
|
||||
this.keyElement = keyTarget;
|
||||
this.showPopover(keyActionToEdit);
|
||||
}
|
||||
}
|
||||
|
||||
onKeyHover(moduleId: number, event: MouseEvent, over: boolean, keyId: number): void {
|
||||
if (this.tooltipEnabled) {
|
||||
const keyActionToEdit: KeyAction = this.layers[this.currentLayer].modules[moduleId].keyActions[keyId];
|
||||
|
||||
if (over) {
|
||||
this.showTooltip(keyActionToEdit, event);
|
||||
} else {
|
||||
this.hideTooltip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
this.keymap,
|
||||
this.currentLayer,
|
||||
this.keyEditConfig.moduleId,
|
||||
this.keyEditConfig.keyId,
|
||||
keyAction)
|
||||
);
|
||||
this.hidePopover();
|
||||
}
|
||||
|
||||
showPopover(keyAction: KeyAction): void {
|
||||
this.keyPosition = this.keyElement.getBoundingClientRect();
|
||||
this.popoverInitKeyAction = keyAction;
|
||||
this.popoverShown = true;
|
||||
this.renderer.invokeElementMethod(this.popover.nativeElement, 'focus');
|
||||
}
|
||||
|
||||
showTooltip(keyAction: KeyAction, event: MouseEvent): void {
|
||||
if (keyAction === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const el: Element = event.target as Element || event.srcElement;
|
||||
const position: ClientRect = el.getBoundingClientRect();
|
||||
let posLeft: number = this.tooltipData.posLeft;
|
||||
let posTop: number = this.tooltipData.posTop;
|
||||
|
||||
if (el.tagName === 'g') {
|
||||
posLeft = position.left + (position.width / 2);
|
||||
posTop = position.top + position.height;
|
||||
}
|
||||
|
||||
this.tooltipData = {
|
||||
posLeft: posLeft,
|
||||
posTop: posTop,
|
||||
content: this.getKeyActionContent(keyAction),
|
||||
show: true
|
||||
};
|
||||
}
|
||||
|
||||
hideTooltip() {
|
||||
this.tooltipData.show = false;
|
||||
}
|
||||
|
||||
hidePopover(): void {
|
||||
this.popoverShown = false;
|
||||
}
|
||||
|
||||
selectLayer(index: number): void {
|
||||
this.currentLayer = index;
|
||||
}
|
||||
|
||||
getSelectedLayer(): number {
|
||||
return this.currentLayer;
|
||||
}
|
||||
|
||||
private getKeyActionContent(keyAction: KeyAction): Observable<NameValuePair[]> {
|
||||
if (keyAction instanceof KeystrokeAction) {
|
||||
const keystrokeAction: KeystrokeAction = keyAction;
|
||||
const content: NameValuePair[] = [];
|
||||
content.push({
|
||||
name: 'Action type',
|
||||
value: 'Keystroke'
|
||||
});
|
||||
|
||||
if (keystrokeAction.hasScancode()) {
|
||||
let value: string = keystrokeAction.scancode.toString();
|
||||
const scanCodeTexts: string = (this.mapper.scanCodeToText(keystrokeAction.scancode) || []).join(', ');
|
||||
if (scanCodeTexts.length > 0) {
|
||||
value += ' (' + scanCodeTexts + ')';
|
||||
}
|
||||
content.push({
|
||||
name: 'Scancode',
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
if (keystrokeAction.hasActiveModifier()) {
|
||||
content.push({
|
||||
name: 'Modifiers',
|
||||
value: keystrokeAction.getModifierList().join(', ')
|
||||
});
|
||||
}
|
||||
|
||||
if (keystrokeAction.hasLongPressAction()) {
|
||||
content.push({
|
||||
name: 'Long press',
|
||||
value: LongPressAction[keystrokeAction.longPressAction]
|
||||
});
|
||||
}
|
||||
return Observable.of(content);
|
||||
} else if (keyAction instanceof MouseAction) {
|
||||
const mouseAction: MouseAction = keyAction;
|
||||
const content: NameValuePair[] =
|
||||
[
|
||||
{
|
||||
name: 'Action type',
|
||||
value: 'Mouse'
|
||||
},
|
||||
{
|
||||
name: 'Action',
|
||||
value: camelCaseToSentence(MouseActionParam[mouseAction.mouseAction])
|
||||
}
|
||||
];
|
||||
return Observable.of(content);
|
||||
} else if (keyAction instanceof PlayMacroAction) {
|
||||
const playMacroAction: PlayMacroAction = keyAction;
|
||||
return this.store
|
||||
.select(appState => appState.macros)
|
||||
.map(macroState => macroState.entities.find(macro => {
|
||||
return macro.id === playMacroAction.macroId;
|
||||
}).name)
|
||||
.map(macroName => {
|
||||
const content: NameValuePair[] = [
|
||||
{
|
||||
name: 'Action type',
|
||||
value: 'Play macro'
|
||||
},
|
||||
{
|
||||
name: 'Macro name',
|
||||
value: macroName
|
||||
}
|
||||
];
|
||||
return content;
|
||||
});
|
||||
} else if (keyAction instanceof SwitchKeymapAction) {
|
||||
const switchKeymapAction: SwitchKeymapAction = keyAction;
|
||||
return this.store
|
||||
.select(appState => appState.keymaps.entities)
|
||||
.map(keymaps => keymaps.find(keymap => keymap.abbreviation === switchKeymapAction.keymapAbbreviation).name)
|
||||
.map(keymapName => {
|
||||
const content: NameValuePair[] = [
|
||||
{
|
||||
name: 'Action type',
|
||||
value: 'Switch keymap'
|
||||
},
|
||||
{
|
||||
name: 'Keymap',
|
||||
value: keymapName
|
||||
}
|
||||
];
|
||||
return content;
|
||||
});
|
||||
} else if (keyAction instanceof SwitchLayerAction) {
|
||||
const switchLayerAction: SwitchLayerAction = keyAction;
|
||||
const content: NameValuePair[] =
|
||||
[
|
||||
{
|
||||
name: 'Action type',
|
||||
value: 'Switch layer'
|
||||
},
|
||||
{
|
||||
name: 'Layer',
|
||||
value: capitalizeFirstLetter(LayerName[switchLayerAction.layer])
|
||||
},
|
||||
{
|
||||
name: 'Toogle',
|
||||
value: switchLayerAction.isLayerToggleable ? 'On' : 'Off'
|
||||
}
|
||||
];
|
||||
return Observable.of(content);
|
||||
}
|
||||
|
||||
return Observable.of([]);
|
||||
}
|
||||
}
|
||||