Macro editor (#87)

This commit is contained in:
Mikko Lakomaa
2016-09-13 22:22:11 +03:00
committed by József Farkas
parent 8a76da8df5
commit fbb4a1cc49
61 changed files with 1638 additions and 429 deletions

2
.gitignore vendored
View File

@@ -4,3 +4,5 @@ npm-debug.log
dist
.idea
*.iml
*.sublime-project
*.sublime-workspace

View File

@@ -36,16 +36,19 @@
"@angular/common": "2.0.0-rc.6",
"@angular/compiler": "2.0.0-rc.6",
"@angular/core": "2.0.0-rc.6",
"@angular/forms": "2.0.0-rc.6",
"@angular/platform-browser": "2.0.0-rc.6",
"@angular/platform-browser-dynamic": "2.0.0-rc.6",
"@angular/router": "3.0.0-rc.2",
"bootstrap": "^3.3.7",
"browser-stdout": "^1.3.0",
"core-js": "2.4.1",
"dragula": "^3.7.1",
"font-awesome": "^4.6.3",
"handlebars": "^4.0.5",
"jquery": "3.1.0",
"json-loader": "^0.5.4",
"ng2-dragula": "^1.2.0",
"ng2-select2": "0.4.2",
"rxjs": "5.0.0-beta.11",
"select2": "^4.0.3",

View File

@@ -1,14 +1,24 @@
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { DragulaModule } from 'ng2-dragula/ng2-dragula';
import { Select2Component } from 'ng2-select2/ng2-select2';
import { MainAppComponent, appRoutingProviders, routing } from './main-app';
import { ContenteditableDirective } from './directives/contenteditable';
import { KeymapAddComponent, KeymapComponent } from './components/keymap';
import { LayersComponent } from './components/layers';
import { LegacyLoaderComponent } from './components/legacy-loader';
import { MacroComponent } from './components/macro';
import {
MacroActionEditorComponent,
MacroComponent,
MacroDelayTabComponent,
MacroItemComponent,
MacroKeyTabComponent,
MacroMouseTabComponent,
MacroTextTabComponent
} from './components/macro';
import { NotificationComponent } from './components/notification';
import { PopoverComponent } from './components/popover';
import {
@@ -19,7 +29,6 @@ import {
MouseTabComponent,
NoneTabComponent
} from './components/popover/tab';
import { MacroItemComponent } from './components/popover/tab/macro';
import { CaptureKeystrokeButtonComponent } from './components/popover/widgets/capture-keystroke';
import { IconComponent } from './components/popover/widgets/icon';
import { SideMenuComponent } from './components/side-menu';
@@ -36,6 +45,7 @@ import {
} from './components/svg/keys';
import { SvgModuleComponent } from './components/svg/module';
import { SvgKeyboardWrapComponent } from './components/svg/wrap';
import { MainAppComponent, appRoutingProviders, routing } from './main-app';
import { DataProviderService } from './services/data-provider.service';
import { MapperService } from './services/mapper.service';
@@ -46,7 +56,6 @@ import {UhkConfigurationService} from './services/uhk-configuration.service';
Select2Component,
MainAppComponent,
KeymapComponent,
MacroComponent,
LegacyLoaderComponent,
NotificationComponent,
SvgIconTextKeyComponent,
@@ -73,10 +82,19 @@ import {UhkConfigurationService} from './services/uhk-configuration.service';
NoneTabComponent,
CaptureKeystrokeButtonComponent,
IconComponent,
MacroItemComponent
MacroComponent,
MacroItemComponent,
MacroActionEditorComponent,
MacroDelayTabComponent,
MacroKeyTabComponent,
MacroMouseTabComponent,
MacroTextTabComponent,
ContenteditableDirective
],
imports: [
BrowserModule,
FormsModule,
DragulaModule,
routing
],
providers: [

View File

@@ -1,2 +1,5 @@
export * from './macro.component';
export * from './macro.routes';
export * from './macro-item';
export * from './macro-action-editor';
export * from './macro-action-editor/tab';

View File

@@ -0,0 +1 @@
export { MacroActionEditorComponent } from './macro-action-editor.component';

View File

@@ -0,0 +1,46 @@
<div class="action--editor">
<div class="row">
<div class="col-xs-4 editor__tab-links">
<ul class="nav nav-pills nav-stacked">
<li #macroText [class.active]="activeTab === TabName.Text" (click)="selectTab(TabName.Text)">
<a>
<i class="fa fa-font"></i>
<span>Type text</span>
</a>
</li>
<li #macroKeypress [class.active]="activeTab === TabName.Keypress" (click)="selectTab(TabName.Keypress)">
<a>
<i class="fa fa-keyboard-o"></i>
<span>Key action</span>
</a>
</li>
<li #macroMouse [class.active]="activeTab === TabName.Mouse" (click)="selectTab(TabName.Mouse)">
<a>
<i class="fa fa-mouse-pointer"></i>
<span>Mouse action</span>
</a>
</li>
<li #macroDelay [class.active]="activeTab === TabName.Delay" (click)="selectTab(TabName.Delay)">
<a>
<i class="fa fa-clock-o"></i>
<span>Delay</span>
</a>
</li>
</ul>
</div>
<div class="col-xs-8 editor__tabs" [ngSwitch]="activeTab">
<macro-text-tab #tab *ngSwitchCase="TabName.Text" [macroAction]="editableMacroAction"></macro-text-tab>
<macro-key-tab #tab *ngSwitchCase="TabName.Keypress" [macroAction]="editableMacroAction"></macro-key-tab>
<macro-mouse-tab #tab *ngSwitchCase="TabName.Mouse" [macroAction]="editableMacroAction"></macro-mouse-tab>
<macro-delay-tab #tab *ngSwitchCase="TabName.Delay" [macroAction]="editableMacroAction"></macro-delay-tab>
</div>
</div>
<div class="row">
<div class="col-xs-12 flex-button-wrapper editor__actions-container">
<div class="editor__actions">
<button class="btn btn-sm btn-default flex-button" type="button" (click)="onCancelClick()"> Cancel </button>
<button class="btn btn-sm btn-primary flex-button" type="button" (click)="onSaveClick()"> Save </button>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,80 @@
.action--editor {
padding-top: 0;
padding-bottom: 0;
border-radius: 0;
border: 0;
}
.nav {
padding-bottom: 1rem;
li {
a {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
&.selected {
font-style: italic;
}
&:hover {
cursor: pointer;
}
}
&.active {
z-index: 2;
a {
&.selected {
font-style: normal;
}
&:after {
content: '';
display: block;
position: absolute;
width: 0;
height: 0;
top: 0;
right: -4rem;
border-color: transparent transparent transparent #337ab7;
border-style: solid;
border-width: 2rem;
}
}
}
}
}
.editor {
&__tabs,
&__tab-links {
padding-top: 1rem;
}
&__tabs {
border-left: 1px solid #ddd;
margin-left: -1.6rem;
padding-left: 3rem;
}
&__actions {
float: right;
&-container {
background: #f5f5f5;
border-top: 1px solid #ddd;
padding: 1rem 1.5rem;
}
}
}
.flex-button-wrapper {
display: flex;
flex-direction: row-reverse;
}
.flex-button {
align-self: flex-end;
}

View File

@@ -0,0 +1,107 @@
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import {
EditableMacroAction,
MacroAction,
TextMacroAction,
macroActionType
} from '../../../config-serializer/config-items/macro-action';
import { MacroKeyTabComponent } from './tab/macro-key';
enum TabName {
Keypress,
Text,
Mouse,
Delay
};
@Component({
selector: 'macro-action-editor',
template: require('./macro-action-editor.component.html'),
styles: [require('./macro-action-editor.component.scss')],
host: { 'class': 'macro-action-editor' }
})
export class MacroActionEditorComponent implements OnInit {
@Input() macroAction: MacroAction;
@Output() save = new EventEmitter<MacroAction>();
@Output() cancel = new EventEmitter<void>();
@ViewChild('tab') selectedTab: any;
private editableMacroAction: EditableMacroAction;
private activeTab: TabName;
/* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */
/* tslint:disable:no-unused-variable: It is used in the template. */
private TabName = TabName;
/* tslint:enable:no-unused-variable */
/* tslint:enable:variable-name */
ngOnInit() {
let macroAction: MacroAction = this.macroAction ? this.macroAction : new TextMacroAction();
this.editableMacroAction = new EditableMacroAction(macroAction.toJsObject());
let tab: TabName = this.getTabName(this.editableMacroAction);
this.activeTab = tab;
}
onCancelClick(): void {
this.cancel.emit();
}
onSaveClick(): void {
try {
const action = this.editableMacroAction;
if (action.isKeyAction()) {
// Could updating the saved keys be done in a better way?
const tab = this.selectedTab as MacroKeyTabComponent;
action.fromKeyAction(tab.getKeyAction());
}
this.save.emit(action.toClass());
} catch (e) {
// TODO: show error dialog
console.error(e);
}
}
selectTab(tab: TabName): void {
this.activeTab = tab;
this.editableMacroAction.macroActionType = this.getTabMacroActionType(tab);
}
getTabName(action: EditableMacroAction): TabName {
switch (action.macroActionType) {
// Delay action
case macroActionType.DelayMacroAction:
return TabName.Delay;
// Text action
case macroActionType.TextMacroAction:
return TabName.Text;
// Keypress actions
case macroActionType.KeyMacroAction:
return TabName.Keypress;
// Mouse actions
case macroActionType.MouseButtonMacroAction:
case macroActionType.MoveMouseMacroAction:
case macroActionType.ScrollMouseMacroAction:
return TabName.Mouse;
default:
return TabName.Keypress;
}
}
getTabMacroActionType(tab: TabName): string {
switch (tab) {
case TabName.Delay:
return macroActionType.DelayMacroAction;
case TabName.Keypress:
return macroActionType.KeyMacroAction;
case TabName.Mouse:
return macroActionType.MouseButtonMacroAction;
case TabName.Text:
return macroActionType.TextMacroAction;
default:
throw new Error('Could not get macro action type');
}
}
}

View File

@@ -0,0 +1,4 @@
export { MacroDelayTabComponent } from './macro-delay';
export { MacroKeyTabComponent } from './macro-key';
export { MacroMouseTabComponent } from './macro-mouse';
export { MacroTextTabComponent } from './macro-text';

View File

@@ -0,0 +1 @@
export { MacroDelayTabComponent } from './macro-delay.component';

View File

@@ -0,0 +1,26 @@
<div class="macro-delay">
<div class="row">
<div class="col-xs-12">
<h4>Enter delay in seconds</h4>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<input #macroDelayInput
type="number"
min="0"
max="1000"
step="0.1"
placeholder="Delay amount"
class="form-control"
[(ngModel)]="delay"
(ngModelChange)="setDelay($event)">
</div>
</div>
<div class="row macro-delay__presets">
<div class="col-xs-12">
<h6>Choose a preset</h6>
<button *ngFor="let delay of presets" class="btn btn-sm btn-default"(click)="setDelay(delay)">{{delay}}s</button>
</div>
</div>
</div>

View File

@@ -0,0 +1,16 @@
:host {
display: flex;
flex-direction: column;
position: relative;
}
.macro-delay {
&__presets {
margin-top: 1rem;
button {
margin-right: 0.25rem;
margin-bottom: 0.25rem;
}
}
}

View File

@@ -0,0 +1,35 @@
import { AfterViewInit, Component, ElementRef, Input, OnInit, Renderer, ViewChild } from '@angular/core';
import { EditableMacroAction } from '../../../../../config-serializer/config-items/macro-action';
const INITIAL_DELAY = 0.5; // In seconds
@Component({
selector: 'macro-delay-tab',
template: require('./macro-delay.component.html'),
styles: [require('./macro-delay.component.scss')],
host: { 'class': 'macro__delay' }
})
export class MacroDelayTabComponent implements AfterViewInit, OnInit {
@Input() macroAction: EditableMacroAction;
@ViewChild('macroDelayInput') input: ElementRef;
private delay: number;
/* tslint:disable:no-unused-variable: It is used in the template. */
private presets: number[] = [0.3, 0.5, 0.8, 1, 2, 3, 4, 5];
/* tslint:enable:no-unused-variable */
constructor(private renderer: Renderer) { }
ngOnInit() {
this.delay = this.macroAction.delay > 0 ? this.macroAction.delay / 1000 : INITIAL_DELAY;
}
ngAfterViewInit() {
this.renderer.invokeElementMethod(this.input.nativeElement, 'focus');
}
setDelay(value: number): void {
this.delay = value;
this.macroAction.delay = this.delay * 1000;
}
}

View File

@@ -0,0 +1 @@
export { MacroKeyTabComponent } from './macro-key.component';

View File

@@ -0,0 +1,32 @@
<div class="col-xs-12 macro-key__container">
<div class="col-xs-4 macro-key__types">
<ul class="nav nav-pills nav-stacked">
<li #keyMove [class.active]="activeTab === TabName.Keypress" (click)="selectTab(TabName.Keypress)">
<a>
<i class="fa fa-hand-pointer-o"></i>
<span>Press key</span>
</a>
</li>
<li #keyHold [class.active]="activeTab === TabName.Hold" (click)="selectTab(TabName.Hold)">
<a>
<i class="fa fa-hand-rock-o"></i>
<span>Hold key</span>
</a>
</li>
<li #keyRelease [class.active]="activeTab === TabName.Release" (click)="selectTab(TabName.Release)">
<a>
<i class="fa fa-hand-paper-o"></i>
<span>Release key</span>
</a>
</li>
</ul>
</div>
<div class="col-xs-8 macro-key__action-container">
<div class="macro-key__action">
<h4 *ngIf="activeTab === TabName.Keypress">Press key</h4>
<h4 *ngIf="activeTab === TabName.Hold">Hold key</h4>
<h4 *ngIf="activeTab === TabName.Release">Release key</h4>
<keypress-tab #keypressTab [defaultKeyAction]="defaultKeyAction" [longPressEnabled]="false"></keypress-tab>
</div>
</div>
</div>

View File

@@ -0,0 +1,25 @@
.macro-key {
&__container {
padding: 0;
}
&__types {
margin-left: 0;
padding: 0 0 1rem;
}
&__action {
&-container {
margin-top: -1rem;
padding-top: 1rem;
border-left: 1px solid #ddd;
}
padding-left: 3rem;
padding-bottom: 1rem;
}
}
.fa {
min-width: 14px;
}

View File

@@ -0,0 +1,74 @@
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { KeyAction } from '../../../../../config-serializer/config-items/key-action';
import { EditableMacroAction, MacroSubAction } from '../../../../../config-serializer/config-items/macro-action';
import { KeypressTabComponent } from '../../../../popover/tab';
import { Tab } from '../../../../popover/tab';
enum TabName {
Keypress,
Hold,
Release
}
@Component({
selector: 'macro-key-tab',
template: require('./macro-key.component.html'),
styles: [
require('../../macro-action-editor.component.scss'),
require('./macro-key.component.scss')
],
host: { 'class': 'macro__mouse' }
})
export class MacroKeyTabComponent implements OnInit {
@Input() macroAction: EditableMacroAction;
@ViewChild('tab') selectedTab: Tab;
@ViewChild('keypressTab') keypressTab: KeypressTabComponent;
private defaultKeyAction: KeyAction;
private activeTab: TabName;
/* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */
/* tslint:disable:no-unused-variable: It is used in the template. */
private TabName = TabName;
/* tslint:enable:no-unused-variable */
/* tslint:enable:variable-name */
ngOnInit() {
this.defaultKeyAction = this.macroAction.toKeystrokeAction();
this.selectTab(this.getTabName(this.macroAction));
}
selectTab(tab: TabName): void {
this.activeTab = tab;
this.macroAction.action = this.getActionType(tab);
}
getTabName(action: EditableMacroAction): TabName {
if (!action.action || action.isOnlyPressAction()) {
return TabName.Keypress;
} else if (action.isOnlyHoldAction()) {
return TabName.Hold;
} else if (action.isOnlyReleaseAction()) {
return TabName.Release;
}
}
getActionType(tab: TabName): MacroSubAction {
switch (tab) {
case TabName.Keypress:
return MacroSubAction.press;
case TabName.Hold:
return MacroSubAction.hold;
case TabName.Release:
return MacroSubAction.release;
default:
throw new Error('Invalid tab type');
}
}
getKeyAction(): KeyAction {
return this.keypressTab.toKeyAction();
}
}

View File

@@ -0,0 +1 @@
export { MacroMouseTabComponent } from './macro-mouse.component';

View File

@@ -0,0 +1,77 @@
<div class="col-xs-12 macro-mouse__container">
<div class="col-xs-4 macro-mouse__types">
<ul class="nav nav-pills nav-stacked">
<li #mouseMove [class.active]="activeTab === TabName.Move" (click)="selectTab(TabName.Move)">
<a>
<i class="fa fa-arrows"></i>
<span>Move pointer</span>
</a>
</li>
<li #mouseScroll [class.active]="activeTab === TabName.Scroll" (click)="selectTab(TabName.Scroll)">
<a>
<i class="fa fa-arrows-v"></i>
<span>Scroll</span>
</a>
</li>
<li #mouseClick [class.active]="activeTab === TabName.Click" (click)="selectTab(TabName.Click)">
<a>
<i class="fa fa-mouse-pointer"></i>
<span>Click button</span>
</a>
</li>
<li #mouseHold [class.active]="activeTab === TabName.Hold" (click)="selectTab(TabName.Hold)">
<a>
<i class="fa fa-hand-rock-o"></i>
<span>Hold button</span>
</a>
</li>
<li #mouseRelease [class.active]="activeTab === TabName.Release" (click)="selectTab(TabName.Release)">
<a>
<i class="fa fa-hand-paper-o"></i>
<span>Release button</span>
</a>
</li>
</ul>
</div>
<div class="col-xs-8 macro-mouse__actions" [ngSwitch]="activeTab">
<div #tab *ngSwitchCase="TabName.Move">
<h4>Move pointer</h4>
<p>Use negative values to move down or left from current position.</p>
<div class="form-horizontal">
<div class="form-group">
<label for="move-mouse-x">X</label>
<input id="move-mouse-x" type="number" class="form-control" [(ngModel)]="macroAction.moveX"> pixels
</div>
<div class="form-group">
<label for="move-mouse-y">Y</label>
<input id="move-mouse-y" type="number" class="form-control" [(ngModel)]="macroAction.moveY"> pixels
</div>
</div>
</div>
<div #tab *ngSwitchCase="TabName.Scroll">
<h4>Scroll</h4>
<p>Use negative values to move down or left from current position.</p>
<div class="form-horizontal">
<div class="form-group">
<label for="scroll-mouse-x">X</label>
<input id="scroll-mouse-x" type="number" class="form-control" [(ngModel)]="macroAction.scrollX"> pixels
</div>
<div class="form-group">
<label for="scroll-mouse-y">Y</label>
<input id="scroll-mouse-y" type="number" class="form-control" [(ngModel)]="macroAction.scrollY"> pixels
</div>
</div>
</div>
<div #tab *ngIf="activeTab === TabName.Click || activeTab === TabName.Hold || activeTab === TabName.Release">
<h4 *ngIf="activeTab === TabName.Click">Click mouse button</h4>
<h4 *ngIf="activeTab === TabName.Hold">Hold mouse button</h4>
<h4 *ngIf="activeTab === TabName.Release">Release mouse button</h4>
<div class="btn-group macro-mouse__buttons">
<button *ngFor="let buttonLabel of buttonLabels; let buttonIndex = index"
class="btn btn-default"
[class.btn-primary]="hasButton(buttonIndex)"
(click)="setMouseClick(buttonIndex)">{{buttonLabel}}</button>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,43 @@
.macro-mouse {
&__container {
padding: 0;
}
&__types {
border-right: 1px solid #ddd;
border-left: 0;
margin-top: -1rem;
margin-left: 0;
padding: 1rem 0;
}
&__actions {
padding-left: 3rem;
padding-bottom: 1rem;
}
&__buttons {
margin-top: 3rem;
margin-bottom: 1rem;
}
}
.fa {
min-width: 14px;
}
.form-horizontal {
.form-group {
margin: 0 0 0.5rem;
}
label {
display: inline-block;
margin-right: 0.5rem;
}
.form-control {
display: inline-block;
width: 60%;
}
}

View File

@@ -0,0 +1,107 @@
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { EditableMacroAction, MacroSubAction, macroActionType } from '../../../../../config-serializer/config-items/macro-action';
import { Tab } from '../../../../popover/tab';
enum TabName {
Move,
Scroll,
Click,
Hold,
Release
}
@Component({
selector: 'macro-mouse-tab',
template: require('./macro-mouse.component.html'),
styles: [
require('../../macro-action-editor.component.scss'),
require('./macro-mouse.component.scss')
],
host: { 'class': 'macro__mouse' }
})
export class MacroMouseTabComponent implements OnInit {
@Input() macroAction: EditableMacroAction;
@ViewChild('tab') selectedTab: Tab;
private activeTab: TabName;
private buttonLabels: string[];
private selectedButtons: boolean[];
/* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */
/* tslint:disable:no-unused-variable: It is used in the template. */
private TabName = TabName;
/* tslint:enable:no-unused-variable */
/* tslint:enable:variable-name */
constructor() {
this.buttonLabels = ['Left', 'Middle', 'Right'];
this.selectedButtons = Array(this.buttonLabels.length).fill(false);
}
ngOnInit() {
const tabName = this.getTabName(this.macroAction);
this.selectTab(tabName);
const buttonActions = [TabName.Click, TabName.Hold, TabName.Release];
if (buttonActions.includes(this.activeTab)) {
this.selectedButtons = this.macroAction.getMouseButtons();
}
}
selectTab(tab: TabName): void {
this.activeTab = tab;
this.macroAction.macroActionType = this.getMacroActionType(tab);
if (this.macroAction.macroActionType === macroActionType.MouseButtonMacroAction) {
this.macroAction.action = this.getAction(tab);
}
}
setMouseClick(index: number): void {
this.selectedButtons[index] = !this.selectedButtons[index];
this.macroAction.setMouseButtons(this.selectedButtons);
}
hasButton(index: number): boolean {
return this.selectedButtons[index];
}
getAction(tab: TabName): MacroSubAction {
switch (tab) {
case TabName.Click:
return MacroSubAction.press;
case TabName.Hold:
return MacroSubAction.hold;
case TabName.Release:
return MacroSubAction.release;
default:
throw new Error('Invalid tab name');
}
}
getTabName(action: EditableMacroAction): TabName {
if (action.macroActionType === macroActionType.MouseButtonMacroAction) {
if (!action.action || action.isOnlyPressAction()) {
return TabName.Click;
} else if (action.isOnlyPressAction()) {
return TabName.Hold;
} else if (action.isOnlyReleaseAction()) {
return TabName.Release;
}
} else if (action.macroActionType === macroActionType.MoveMouseMacroAction) {
return TabName.Move;
} else if (action.macroActionType === macroActionType.ScrollMouseMacroAction) {
return TabName.Scroll;
}
return TabName.Move;
}
getMacroActionType(tab: TabName): string {
if (tab === TabName.Click || tab === TabName.Hold || tab === TabName.Release) {
return macroActionType.MouseButtonMacroAction;
} else if (tab === TabName.Move) {
return macroActionType.MoveMouseMacroAction;
} else if (tab === TabName.Scroll) {
return macroActionType.ScrollMouseMacroAction;
}
}
}

View File

@@ -0,0 +1 @@
export { MacroTextTabComponent } from './macro-text.component';

View File

@@ -0,0 +1,5 @@
<div>
<h4>Type text</h4>
<p>Input the text you want to type with this macro action.</p>
<textarea #macroTextInput name="macro-text" [(ngModel)]="macroAction.text" class="macro__text-input"></textarea>
</div>

View File

@@ -0,0 +1,11 @@
:host {
display: flex;
flex-direction: column;
position: relative;
}
.macro__text-input {
width: 100%;
min-height: 10rem;
margin-bottom: 1rem;
}

View File

@@ -0,0 +1,28 @@
import {
AfterViewInit,
Component,
ElementRef,
Input,
Renderer,
ViewChild
} from '@angular/core';
import { EditableMacroAction } from '../../../../../config-serializer/config-items/macro-action';
@Component({
selector: 'macro-text-tab',
template: require('./macro-text.component.html'),
styles: [require('./macro-text.component.scss')],
host: { 'class': 'macro__text' }
})
export class MacroTextTabComponent implements AfterViewInit {
@Input() macroAction: EditableMacroAction;
@ViewChild('macroTextInput') input: ElementRef;
constructor(private renderer: Renderer) {}
ngAfterViewInit() {
this.renderer.invokeElementMethod(this.input.nativeElement, 'focus');
}
}

View File

@@ -0,0 +1 @@
export { MacroItemComponent } from './macro-item.component';

View File

@@ -0,0 +1,15 @@
<div class="list-group-item action--item" [class.is-editing]="editing">
<span *ngIf="moveable" class="glyphicon glyphicon-option-vertical action--movable" aria-hidden="true"></span>
<icon [name]="iconName"></icon>
<div class="action--title" (click)="editAction()">{{ title }}</div>
<icon *ngIf="deletable" name="trash" (click)="deleteAction()"></icon>
<icon *ngIf="editable" name="pencil" (click)="editAction()"></icon>
</div>
<div class="list-group-item macro-action-editor__container" *ngIf="editable && editing">
<macro-action-editor
#macroActionEditor
*ngIf="editable && editing"
[macroAction]="macroAction"
(cancel)="cancelEdit()"
(save)="saveEditedAction($event)"></macro-action-editor>
</div>

View File

@@ -0,0 +1,53 @@
:host {
&.macro-item:first-of-type {
.list-group-item {
border-radius: 4px 4px 0 0;
}
}
&.macro-item:last-of-type {
.list-group-item {
border-bottom: 0;
}
}
.action {
&--item {
display: flex;
flex-shrink: 0;
border: 0;
border-bottom: 1px solid #ddd;
icon {
margin: 0 5px;
}
> div {
display: flex;
flex: 1;
}
&:first-child {
border-radius: 0;
}
&.is-editing {
background: #f5f5f5;
}
}
&--movable {
&:hover {
cursor: move;
}
}
}
.macro-action-editor__container {
padding-top: 0;
padding-bottom: 0;
border-radius: 0;
border-left: 0;
border-right: 0;
}
}

View File

@@ -0,0 +1,195 @@
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
import { KeyModifiers } from '../../../config-serializer/config-items/KeyModifiers';
import {
DelayMacroAction,
KeyMacroAction,
MacroAction,
MouseButtonMacroAction,
MoveMouseMacroAction,
ScrollMouseMacroAction,
TextMacroAction
} from '../../../config-serializer/config-items/macro-action';
import { MapperService } from '../../../services/mapper.service';
@Component({
selector: 'macro-item',
template: require('./macro-item.component.html'),
styles: [require('./macro-item.component.scss')],
host: { 'class': 'macro-item' }
})
export class MacroItemComponent implements OnInit, OnChanges {
@Input() macroAction: MacroAction;
@Input() editable: boolean;
@Input() deletable: boolean;
@Input() moveable: boolean;
@Output() save = new EventEmitter<MacroAction>();
@Output() cancel = new EventEmitter<void>();
@Output() edit = new EventEmitter<void>();
@Output() delete = new EventEmitter<void>();
private title: string;
private iconName: string;
private editing: boolean;
constructor(private mapper: MapperService) { }
ngOnInit() {
this.updateView();
if (!this.macroAction) {
this.editing = true;
}
}
ngOnChanges(changes: SimpleChanges) {
/* tslint:disable:no-string-literal */
if (changes['macroAction']) {
/* tslint:enable:no-string-literal */
this.updateView();
}
}
saveEditedAction(editedAction: MacroAction): void {
// @todo save this to keyboard
console.log('Saved action', editedAction);
this.macroAction = editedAction;
this.editing = false;
this.updateView();
this.save.emit(editedAction);
}
editAction(): void {
if (!this.editable) {
return;
}
this.editing = true;
this.edit.emit();
}
cancelEdit(): void {
this.editing = false;
this.cancel.emit();
}
deleteAction(): void {
this.delete.emit();
}
private updateView(): void {
if (!this.macroAction) {
this.title = 'New macro action';
} else if (this.macroAction instanceof DelayMacroAction) {
// Delay
this.iconName = 'clock';
let action: DelayMacroAction = this.macroAction as DelayMacroAction;
const delay = action.delay > 0 ? action.delay / 1000 : 0;
this.title = `Delay of ${delay}s`;
} else if (this.macroAction instanceof TextMacroAction) {
// Write text
let action: TextMacroAction = this.macroAction as TextMacroAction;
this.iconName = 'font';
this.title = `Write text: ${action.text}`;
} else if (this.macroAction instanceof KeyMacroAction) {
// Key pressed/held/released
const action: KeyMacroAction = this.macroAction as KeyMacroAction;
this.setKeyActionContent(action);
} else if (this.macroAction instanceof MouseButtonMacroAction) {
// Mouse button clicked/held/released
const action: MouseButtonMacroAction = this.macroAction as MouseButtonMacroAction;
this.setMouseButtonActionContent(action);
} else if (this.macroAction instanceof MoveMouseMacroAction || this.macroAction instanceof ScrollMouseMacroAction) {
// Mouse moved or scrolled
this.setMouseMoveScrollActionContent(this.macroAction);
} else {
this.title = this.macroAction.constructor.name;
}
}
private setKeyActionContent(action: KeyMacroAction): void {
if (!action.hasScancode() && !action.hasModifiers()) {
this.title = 'Invalid keypress';
return;
}
if (action.isPressAction()) {
// Press key
this.iconName = 'hand-pointer';
this.title = 'Press key: ';
} else if (action.isHoldAction()) {
// Hold key
this.iconName = 'hand-rock';
this.title = 'Hold key: ';
} else if (action.isReleaseAction()) {
// Release key
this.iconName = 'hand-paper';
this.title = 'Release key: ';
}
if (action.hasScancode()) {
const scancode: string = this.mapper.scanCodeToText(action.scancode).join(' ');
if (scancode) {
this.title += scancode;
}
}
if (action.hasModifiers()) {
// Press/hold/release modifiers
for (let i = KeyModifiers.leftCtrl; i !== KeyModifiers.rightGui; i <<= 1) {
if (action.isModifierActive(i)) {
this.title += ' ' + KeyModifiers[i];
}
}
}
}
private setMouseMoveScrollActionContent(action: MacroAction): void {
let typedAction: any;
if (action instanceof MoveMouseMacroAction) {
// Move mouse pointer
this.iconName = 'mouse-pointer';
this.title = 'Move pointer';
typedAction = this.macroAction as MoveMouseMacroAction;
} else {
// Scroll mouse
this.iconName = 'mouse-pointer';
this.title = 'Scroll';
typedAction = this.macroAction as ScrollMouseMacroAction;
}
let needAnd: boolean;
if (Math.abs(typedAction.x) !== 0) {
this.title += ` by ${Math.abs(typedAction.x)}px ${typedAction.x > 0 ? 'left' : 'right'}`;
needAnd = true;
}
if (Math.abs(typedAction.y) !== 0) {
this.title += ` ${needAnd ? 'and' : 'by'} ${Math.abs(typedAction.y)}px ${typedAction.y > 0 ? 'down' : 'up'}`;
}
}
private setMouseButtonActionContent(action: MouseButtonMacroAction): void {
// Press/hold/release mouse buttons
if (action.isOnlyPressAction()) {
this.iconName = 'mouse-pointer';
this.title = 'Click mouse button: ';
} else if (action.isOnlyHoldAction()) {
this.iconName = 'hand-rock';
this.title = 'Hold mouse button: ';
} else if (action.isOnlyReleaseAction()) {
this.iconName = 'hand-paper';
this.title = 'Release mouse button: ';
}
const buttonLabels: string[] = ['Left', 'Middle', 'Right'];
const selectedButtons: boolean[] = action.getMouseButtons();
const selectedButtonLabels: string[] = [];
selectedButtons.forEach((isSelected, idx) => {
if (isSelected && buttonLabels[idx]) {
selectedButtonLabels.push(buttonLabels[idx]);
}
});
this.title += selectedButtonLabels.join(', ');
}
}

View File

@@ -1,74 +1,38 @@
<div class="macro--edit main-content__inner">
<div class="row">
<h1 class="col-xs-12 pane-title">
<i class="fa fa-play"></i>
<span class="macro__name pane-title__name" contenteditable="true">Macro1</span> <i class="fa"></i>
<span class="macro__name pane-title__name"
#macroNameInput
contenteditable="true"
[contenteditableUpdateOnEnter]="true"
[(contenteditableModel)]="macro.name"
(contenteditableModelChange)="onNameChange()">{{macro.name}}</span>
</h1>
</div>
<div class="row">
<div id="listWithHandle" class="list-group col-xs-10 col-xs-offset-1">
<div class="list-group-item action--item">
<span class="glyphicon glyphicon-option-vertical move-handle" aria-hidden="true"></span>
<i class="fa fa-mouse-pointer"></i> Move pointer by 100px leftward
<i class="glyphicon glyphicon-pencil action--edit"></i>
<i class="glyphicon glyphicon-trash action--trash"></i>
</div>
<div class="list-group-item macro-settings" style="display: none;">
<div class="helper"></div>
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut
labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo
dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor
sit amet.</p>
<div class="row">
<div class="col-sm-12 flex-button-wrapper">
<button class="btn btn-primary btn-sm pull-right flex-button settings-save">Save</button>
<button class="btn btn-default btn-sm pull-right flex-button settings-cancel" style="margin-right: .5em;">Cancel</button>
</div>
</div>
</div>
<div class="list-group-item action--item">
<span class="glyphicon glyphicon-option-vertical move-handle" aria-hidden="true"></span>
<i class="fa fa-square"></i> Press letter A
<i class="glyphicon glyphicon-pencil action--edit"></i>
<i class="glyphicon glyphicon-trash action--trash"></i>
</div>
<div class="list-group-item action--item">
<span class="glyphicon glyphicon-option-vertical move-handle" aria-hidden="true"></span>
<i class="fa fa-square"></i> Press Alt+Tab
<i class="glyphicon glyphicon-pencil action--edit"></i>
<i class="glyphicon glyphicon-trash action--trash"></i>
</div>
<div class="list-group-item action--item">
<span class="glyphicon glyphicon-option-vertical move-handle" aria-hidden="true"></span>
<i class="fa fa-clock-o"></i> Delay of 235ms
<i class="glyphicon glyphicon-pencil action--edit"></i>
<i class="glyphicon glyphicon-trash action--trash"></i>
</div>
<div class="list-group-item action--item">
<span class="glyphicon glyphicon-option-vertical move-handle" aria-hidden="true"></span>
<i class="fa fa-mouse-pointer"></i> Press button 1
<i class="glyphicon glyphicon-pencil action--edit"></i>
<i class="glyphicon glyphicon-trash action--trash"></i>
</div>
<div class="list-group-item action--item">
<span class="glyphicon glyphicon-option-vertical move-handle" aria-hidden="true"></span>
<i class="fa fa-mouse-pointer"></i> Scroll by 150px downward
<i class="glyphicon glyphicon-pencil action--edit"></i>
<i class="glyphicon glyphicon-trash action--trash"></i>
<div class="row list-container">
<div class="col-xs-10 col-xs-offset-1 list-group" [dragula]="'macroActions'" [dragulaModel]="macro.macroActions.elements">
<div class="macro-actions-container">
<macro-item *ngFor="let macroAction of macro.macroActions.elements; let macroActionIndex = index"
[macroAction]="macroAction"
[editable]="true"
[deletable]="true"
[moveable]="dragEnabled"
(save)="onSaveAction($event, index)"
(edit)="onEditAction(macroActionIndex)"
(cancel)="onCancelEditAction()"
(delete)="onDeleteAction(macroActionIndex)"></macro-item>
</div>
<div class="list-group add-new__action-container">
<div class="list-group-item action--item add-new__action-item no-reorder">
<a href="#" class="add-new__action-item--link"><i class="fa fa-plus"></i> Add new action item</a>
<a class="add-new__action-item--link" (click)="addAction()"><i class="fa fa-plus"></i> Add new action item</a>
</div>
<div class="list-group-item new-macro-settings">
<div class="list-group-item new-macro-settings" *ngIf="hasChanges">
<div class="helper"></div>
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut
labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo
dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor
sit amet.</p>
<p>Remember to save your changes!</p>
<div class="row">
<div class="col-sm-12 flex-button-wrapper">
<button class="btn btn-primary btn-sm pull-right flex-button settings-save">Save</button>
<button class="btn btn-default btn-sm pull-right flex-button settings-cancel" style="margin-right: .5em;">Cancel</button>
<button class="btn btn-primary btn-sm pull-right flex-button settings-save" (click)="saveMacro()">Save macro</button>
<button class="btn btn-default btn-sm pull-right flex-button settings-cancel" style="margin-right: .5em;" (click)="discardChanges()">Discard changes</button>
</div>
</div>
</div>

View File

@@ -1,3 +1,20 @@
:host {
display: flex;
flex-direction: column;
height: 100%;
.list-container {
display: flex;
flex: 1;
> div {
display: flex;
flex-direction: column;
flex: 1;
}
}
}
.main-wrapper {
width: 500px;
}
@@ -6,26 +23,42 @@ h1 {
margin-bottom: 3rem;
}
.action--item {
padding-left: 8px;
.action {
&--edit__form {
background-color: #fff;
margin-left: -0.5rem;
margin-right: -15px;
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #ddd;
}
.action--item.active,
.action--item.active:hover {
&--item {
padding-left: 8px;
&.active,
&.active:hover {
background-color: white;
font-weight: bold;
color: black;
border-color: black;
z-index: 10;
}
}
}
.macro__name {
border-bottom: 2px dotted #999;
padding: 0 0.5rem;
margin: 0 0.25rem;
}
.macro-settings {
border: 1px solid black;
border-top-color: #999;
z-index: 100;
}
.macro-settings .helper {
.helper {
position: absolute;
display: block;
height: 13px;
@@ -34,44 +67,25 @@ h1 {
left: 0;
top: -14px;
}
}
.action--item.active.callout,
.macro-settings.callout {
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.5);
}
.macro-actions-container {
overflow: auto;
margin-bottom: 0;
border-radius: 4px 4px 0 0;
border: 1px solid #ddd;
border-bottom: 0;
}
.list-group-item .move-handle:hover {
cursor: move;
}
.action--edit {
float: right;
}
.action--edit:hover {
color: #337ab7;
cursor: pointer;
}
.action--trash {
float: right;
margin-right: 1rem;
}
.action--trash:hover {
color: #d9534f;
cursor: pointer;
}
.action--edit__form {
background-color: #fff;
margin-left: -0.5rem;
margin-right: -15px;
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #ddd;
}
.flex-button-wrapper {
display: flex;
flex-direction: row-reverse;
@@ -81,13 +95,22 @@ h1 {
align-self: flex-end;
}
.add-new__action-item:hover {
.add-new__action-container {
border-top: 1px solid #ddd;
}
.add-new__action-item {
border-radius: 0 0 4px 4px;
border-top: 0;
&:hover {
cursor: pointer;
}
.add-new__action-item--link,
.add-new__action-item--link:active,
.add-new__action-item--link:hover {
&--link,
&--link:active,
&--link:hover {
text-decoration: none;
color: #337ab7;
}
}

View File

@@ -1,12 +1,116 @@
import { Component } from '@angular/core';
import { Component, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import { DragulaService } from 'ng2-dragula/ng2-dragula';
import { Macro } from '../../config-serializer/config-items/Macro';
import { MacroAction } from '../../config-serializer/config-items/macro-action';
import { MacroItemComponent } from './macro-item/macro-item.component';
import { UhkConfigurationService } from '../../services/uhk-configuration.service';
@Component({
selector: 'macro',
template: require('./macro.component.html'),
styles: [require('./macro.component.scss')]
styles: [require('./macro.component.scss')],
viewProviders: [DragulaService]
})
export class MacroComponent {
export class MacroComponent implements OnInit, OnDestroy {
@ViewChildren(MacroItemComponent) macroItems: QueryList<MacroItemComponent>;
constructor() {
private macro: Macro;
private routeSubscription: Subscription;
private hasChanges: boolean = false;
private dragEnabled: boolean;
constructor(
private uhkConfigurationService: UhkConfigurationService,
private route: ActivatedRoute,
private dragulaService: DragulaService
) {
/* tslint:disable:no-unused-variable: Used by Dragula. */
dragulaService.setOptions('macroActions', {
moves: function (el: any, container: any, handle: any) {
return handle.className.includes('action--movable');
}
});
/* tslint:enable:no-unused-variable */
}
ngOnInit() {
this.routeSubscription = this.route.params.subscribe((params: { id: string }) => {
const id: number = Number(params.id);
this.macro = this.getMacro(id);
this.dragEnabled = true;
this.hasChanges = false;
});
}
getMacro(id: number): Macro {
const config = this.uhkConfigurationService.getUhkConfiguration();
const macro: Macro = config.macros.elements.find(item => item.id === id);
if (macro) {
// Clone macro for editing
return new Macro().fromJsObject(macro.toJsObject());
}
// @todo replace with notification
throw new Error('Macro not found');
}
saveMacro() {
// @todo Save macro to keyboard
}
addAction() {
this.hideOtherActionEditors(this.macro.macroActions.elements.length);
this.macro.macroActions.elements.push(undefined);
}
discardChanges() {
const id: number = this.macro.id;
this.macro = this.getMacro(id);
this.hasChanges = false;
}
hideOtherActionEditors(index: number) {
this.macroItems.toArray().forEach((macroItem: MacroItemComponent, idx: number) => {
if (idx !== index) {
macroItem.cancelEdit();
}
});
}
onNameChange() {
this.hasChanges = true;
}
onEditAction(index: number) {
// Hide other editors when clicking edit button of a macro action
this.hideOtherActionEditors(index);
this.dragEnabled = false;
}
onCancelEditAction() {
this.dragEnabled = true;
}
onSaveAction(macroAction: MacroAction, index: number) {
this.dragEnabled = true;
this.hasChanges = true;
this.macro.macroActions.elements[index] = macroAction;
}
onDeleteAction(index: number) {
// @ todo show confirm action dialog
this.macro.macroActions.elements.splice(index, 1);
this.hasChanges = true;
}
ngOnDestroy() {
this.routeSubscription.unsubscribe();
}
}

View File

@@ -42,7 +42,7 @@
</div>
</div>
<div class="row" [ngSwitch]="activeTab">
<keypress-tab #tab *ngSwitchCase="TabName.Keypress" class="popover-content" [defaultKeyAction]="defaultKeyAction"></keypress-tab>
<keypress-tab #tab *ngSwitchCase="TabName.Keypress" class="popover-content" [defaultKeyAction]="defaultKeyAction" [longPressEnabled]="true"></keypress-tab>
<layer-tab #tab *ngSwitchCase="TabName.Layer" class="popover-content" [defaultKeyAction]="defaultKeyAction"></layer-tab>
<mouse-tab #tab *ngSwitchCase="TabName.Mouse" class="popover-content" [defaultKeyAction]="defaultKeyAction"></mouse-tab>
<macro-tab #tab *ngSwitchCase="TabName.Macro" class="popover-content" [defaultKeyAction]="defaultKeyAction"></macro-tab>

View File

@@ -21,7 +21,7 @@
</div>
</div>
</div>
<div class="long-press-container">
<div class="long-press-container" *ngIf="longPressEnabled">
<b class="setting-label">Long press action:</b>
<select2 #longPressSelect [data]="longPressGroups" [value]="selectedLongPressIndex.toString()" (valueChanged)="onLongpressChange($event)"
[width]="140"></select2>

View File

@@ -13,6 +13,7 @@ import {Tab} from '../tab';
})
export class KeypressTabComponent implements OnInit, Tab {
@Input() defaultKeyAction: KeyAction;
@Input() longPressEnabled: boolean;
private leftModifiers: string[];
private rightModifiers: string[];

View File

@@ -1,2 +1 @@
export * from './macro-item.component';
export * from './macro-tab.component';

View File

@@ -1,5 +0,0 @@
<icon *ngIf="moveable" name="option-vertical"></icon>
<icon [name]="iconName"></icon>
<div> {{ title }} </div>
<icon *ngIf="deletable" name="trash"></icon>
<icon *ngIf="editable" name="pencil"></icon>

View File

@@ -1,13 +0,0 @@
:host {
display: flex;
flex-shrink: 0;
icon {
margin: 0 5px;
}
div {
display: flex;
flex: 1;
}
}

View File

@@ -1,102 +0,0 @@
/// <reference path="../../../../config-serializer/Function.d.ts" />
import { Component, Input, OnChanges, OnInit } from '@angular/core';
import {KeyModifiers} from '../../../../config-serializer/config-items/KeyModifiers';
import {
DelayMacroAction,
KeyMacroAction,
MacroAction,
MoveMouseMacroAction,
ScrollMouseMacroAction,
TextMacroAction
} from '../../../../config-serializer/config-items/macro-action';
import {MapperService} from '../../../../services/mapper.service';
@Component({
selector: 'macro-item',
template: require('./macro-item.component.html'),
styles: [require('./macro-item.component.scss')]
})
export class MacroItemComponent implements OnInit, OnChanges {
@Input() macroAction: MacroAction;
@Input() editable: boolean;
@Input() deletable: boolean;
@Input() moveable: boolean;
private iconName: string;
private title: string;
constructor(private mapper: MapperService) { }
ngOnInit() {
this.updateView();
}
ngOnChanges() {
// TODO: check if macroAction changed
this.updateView();
}
private updateView(): void {
this.title = this.macroAction.constructor.name;
if (this.macroAction instanceof MoveMouseMacroAction) {
this.iconName = 'mouse-pointer';
this.title = 'Move pointer';
let action: MoveMouseMacroAction = this.macroAction as MoveMouseMacroAction;
let needAnd: boolean;
if (Math.abs(action.x) > 0) {
this.title += ` by ${Math.abs(action.x)}px ${action.x > 0 ? 'left' : 'right'}ward`;
needAnd = true;
}
if (Math.abs(action.y) > 0) {
this.title += ` ${needAnd ? 'and' : 'by'} ${Math.abs(action.y)}px ${action.y > 0 ? 'down' : 'up'}ward`;
}
} else if (this.macroAction instanceof DelayMacroAction) {
this.iconName = 'clock';
let action: DelayMacroAction = this.macroAction as DelayMacroAction;
this.title = `Delay of ${action.delay}ms`;
} else if (this.macroAction instanceof TextMacroAction) {
let action: TextMacroAction = this.macroAction as TextMacroAction;
this.title = `Write text: ${action.text}`;
} else if (this.macroAction instanceof ScrollMouseMacroAction) {
this.iconName = 'mouse-pointer';
this.title = 'Scroll';
let action: ScrollMouseMacroAction = this.macroAction as ScrollMouseMacroAction;
let needAnd: boolean;
if (Math.abs(action.x) > 0) {
this.title += ` by ${Math.abs(action.x)}px ${action.x > 0 ? 'left' : 'right'}ward`;
needAnd = true;
}
if (Math.abs(action.y) > 0) {
this.title += ` ${needAnd ? 'and' : 'by'} ${Math.abs(action.y)}px ${action.y > 0 ? 'down' : 'up'}ward`;
}
} else if (this.macroAction instanceof KeyMacroAction) {
const keyMacroAction: KeyMacroAction = <KeyMacroAction>this.macroAction;
this.title += 'KeyMacroAction: ';
if (keyMacroAction.isPressAction()) {
this.title = 'Press';
} else if (keyMacroAction.isHoldAction()) {
this.title = 'Hold';
} else {
this.title = 'Release';
}
if (keyMacroAction.hasScancode()) {
this.title += ' ' + this.mapper.scanCodeToText(keyMacroAction.scancode).join(' ');
}
this.iconName = 'square';
for (let i = KeyModifiers.leftCtrl; i !== KeyModifiers.rightCtrl; i <<= 1) {
if (keyMacroAction.isModifierActive(i)) {
this.title += ' ' + KeyModifiers[i];
}
}
}
// TODO: finish for all MacroAction
}
}

View File

@@ -2,10 +2,12 @@
<b> Play macro: </b>
<select2 [data]="macroOptions" [value]="macroOptions[selectedMacroIndex + 1].id" (valueChanged)="onChange($event)" [width]="'100%'"></select2>
</div>
<div class="macro-action-container">
<div class="macro-action-container" [class.inactive]="selectedMacroIndex === -1">
<template [ngIf]="selectedMacroIndex >= 0">
<div class="list-group">
<macro-item *ngFor="let macroAction of macros[selectedMacroIndex].macroActions.elements"
[macroAction]="macroAction">
</macro-item>
</div>
</template>
</div>

View File

@@ -25,22 +25,16 @@
margin: 20px 0;
overflow-x: hidden;
overflow-y: auto;
macro-item {
border-radius: 4px;
border: 1px solid #ddd;
padding: 10px;
margin-bottom: -1px;
&.inactive {
border: 0;
}
macro-item:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
macro-item:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
.list-group {
margin-bottom: 0;
border: 0;
}
}
}

View File

@@ -1,7 +1,6 @@
import {Component, Input, OnInit} from '@angular/core';
import {KeyAction, MouseAction, MouseActionParam} from '../../../../config-serializer/config-items/key-action';
import {Tab} from '../tab';
@Component({

View File

@@ -1,8 +1,12 @@
<div [ngSwitch]="name">
<span *ngSwitchCase="'option-vertical'" class="glyphicon glyphicon-option-vertical" aria-hidden="true"></span>
<i *ngSwitchCase="'square'" class="fa fa-square"></i>
<i *ngSwitchCase="'hand-pointer'" class="fa fa-hand-pointer-o"></i>
<i *ngSwitchCase="'hand-rock'" class="fa fa-hand-rock-o"></i>
<i *ngSwitchCase="'hand-paper'" class="fa fa-hand-paper-o"></i>
<i *ngSwitchCase="'mouse-pointer'" class="fa fa-mouse-pointer"></i>
<i *ngSwitchCase="'clock'" class="fa fa-clock-o"></i>
<i *ngSwitchCase="'font'" class="fa fa-font"></i>
<i *ngSwitchCase="'trash'" class="glyphicon glyphicon-trash action--trash"></i>
<i *ngSwitchCase="'pencil'" class="glyphicon glyphicon-pencil action--edit"></i>
<i *ngSwitchCase="'question-circle'" class ="fa fa-question-circle"></i>

View File

@@ -2,3 +2,21 @@
display: flex;
align-items: center;
}
.action {
&--edit {
&:hover {
color: #337ab7;
cursor: pointer;
}
}
&--trash {
&:hover {
color: #d9534f;
cursor: pointer;
}
}
}

View File

@@ -19,7 +19,7 @@
<li class="sidebar__level-1--item">
<div class="sidebar__level-1">
<i class="fa fa-play"></i> Macros
<a href="#" class="btn btn-default pull-right btn-sm">
<a class="btn btn-default pull-right btn-sm" (click)="addMacro()">
<i class="fa fa-plus"></i>
</a>
<i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'macro')"></i>

View File

@@ -0,0 +1,189 @@
import { KeyAction, KeystrokeAction, keyActionType } from '../key-action';
import { DelayMacroAction } from './DelayMacroAction';
import { KeyMacroAction } from './KeyMacroAction';
import { MacroAction, MacroSubAction, macroActionType } from './MacroAction';
import { MouseButtonMacroAction } from './MouseButtonMacroAction';
import { MoveMouseMacroAction } from './MoveMouseMacroAction';
import { ScrollMouseMacroAction } from './ScrollMouseMacroAction';
import { TextMacroAction } from './TextMacroAction';
interface JsObjectEditableMacroAction {
macroActionType: string;
action?: string;
scancode?: number;
modifierMask?: number;
mouseButtonsMask?: number;
x?: number;
y?: number;
delay?: number;
text?: string;
}
export class EditableMacroAction {
macroActionType: string;
action: MacroSubAction;
// Key macro action properties
scancode: number;
modifierMask: number;
// Mouse macro action properties
mouseButtonsMask: number;
moveX: number;
moveY: number;
scrollX: number;
scrollY: number;
// Delay macro action properties
delay: number;
// Text macro action properties
text: string;
constructor(jsObject?: JsObjectEditableMacroAction) {
if (!jsObject) {
return;
}
this.macroActionType = jsObject.macroActionType;
switch (this.macroActionType) {
case macroActionType.KeyMacroAction:
this.action = MacroSubAction[jsObject.action];
this.scancode = jsObject.scancode;
this.modifierMask = jsObject.modifierMask;
break;
case macroActionType.MouseButtonMacroAction:
this.action = MacroSubAction[jsObject.action];
this.mouseButtonsMask = jsObject.mouseButtonsMask;
break;
case macroActionType.MoveMouseMacroAction:
this.moveX = jsObject.x;
this.moveY = jsObject.y;
break;
case macroActionType.ScrollMouseMacroAction:
this.scrollX = jsObject.x;
this.scrollY = jsObject.y;
break;
case macroActionType.TextMacroAction:
this.text = jsObject.text;
break;
case macroActionType.DelayMacroAction:
this.delay = jsObject.delay;
break;
default:
break;
}
}
toJsObject(): any {
return {
macroActionType: this.macroActionType,
action: this.action,
delay: this.delay,
text: this.text,
scancode: this.scancode,
modifierMask: this.modifierMask,
mouseButtonsMask: this.mouseButtonsMask,
mouseMove: {
x: this.moveX,
y: this.moveY
},
mouseScroll: {
x: this.scrollX,
y: this.scrollY
}
};
}
fromKeyAction(keyAction: KeyAction): void {
let data = keyAction.toJsObject();
this.scancode = data.scancode;
this.modifierMask = data.modifierMask;
}
toKeystrokeAction(): KeystrokeAction {
let data = this.toJsObject();
data.keyActionType = keyActionType.KeystrokeAction;
return <KeystrokeAction>(new KeystrokeAction().fromJsObject(data));
}
setMouseButtons(buttonStates: boolean[]): void {
let bitmask = 0;
for (let i = 0; i < buttonStates.length; i++) {
bitmask |= Number(buttonStates[i]) << i;
}
this.mouseButtonsMask = bitmask;
}
getMouseButtons(): boolean[] {
let enabledMouseButtons: boolean[] = [];
for (let bitmask = this.mouseButtonsMask; bitmask; bitmask >>>= 1) {
enabledMouseButtons.push(Boolean(bitmask & 1));
}
return enabledMouseButtons;
}
toClass(): MacroAction {
switch (this.macroActionType) {
// Delay action
case macroActionType.DelayMacroAction:
return new DelayMacroAction().fromJsObject({
macroActionType: this.macroActionType,
delay: this.delay
});
// Text action
case macroActionType.TextMacroAction:
return new TextMacroAction().fromJsObject({
macroActionType: this.macroActionType,
text: this.text
});
// Keypress action
case macroActionType.KeyMacroAction:
return new KeyMacroAction().fromJsObject({
macroActionType: this.macroActionType,
action: MacroSubAction[this.action],
scancode: this.scancode,
modifierMask: this.modifierMask
});
// Mouse actions
case macroActionType.MouseButtonMacroAction:
return new MouseButtonMacroAction().fromJsObject({
macroActionType: this.macroActionType,
action: MacroSubAction[this.action],
mouseButtonsMask: this.mouseButtonsMask
});
case macroActionType.MoveMouseMacroAction:
return new MoveMouseMacroAction().fromJsObject({
macroActionType: this.macroActionType,
x: this.moveX,
y: this.moveY
});
case macroActionType.ScrollMouseMacroAction:
return new ScrollMouseMacroAction().fromJsObject({
macroActionType: this.macroActionType,
x: this.scrollX,
y: this.scrollY
});
default:
throw new Error('Macro action type is missing or not implemented.');
}
}
isKeyAction(): boolean {
return this.macroActionType === macroActionType.KeyMacroAction;
}
isMouseButtonAction(): boolean {
return this.macroActionType === macroActionType.MouseButtonMacroAction;
}
isOnlyHoldAction(): boolean {
return this.action === MacroSubAction.hold;
}
isOnlyPressAction(): boolean {
return this.action === MacroSubAction.press;
}
isOnlyReleaseAction(): boolean {
return this.action === MacroSubAction.release;
}
}

View File

@@ -1,37 +0,0 @@
import {assertUInt8} from '../../assert';
import {UhkBuffer} from '../../UhkBuffer';
import {MacroAction, MacroActionId, macroActionType} from './MacroAction';
export class HoldMouseButtonsMacroAction extends MacroAction {
@assertUInt8
mouseButtonsMask: number;
_fromJsObject(jsObject: any): HoldMouseButtonsMacroAction {
this.assertMacroActionType(jsObject);
this.mouseButtonsMask = jsObject.mouseButtonsMask;
return this;
}
_fromBinary(buffer: UhkBuffer): HoldMouseButtonsMacroAction {
this.readAndAssertMacroActionId(buffer);
this.mouseButtonsMask = buffer.readUInt8();
return this;
}
_toJsObject(): any {
return {
macroActionType: macroActionType.HoldMouseButtonsMacroAction,
mouseButtonsMask: this.mouseButtonsMask
};
}
_toBinary(buffer: UhkBuffer) {
buffer.writeUInt8(MacroActionId.HoldMouseButtonsMacroAction);
buffer.writeUInt8(this.mouseButtonsMask);
}
toString(): string {
return `<HoldMouseButtonsMacroAction mouseButtonsMask="${this.mouseButtonsMask}">`;
}
}

View File

@@ -1,16 +1,10 @@
import { assertEnum, assertUInt8 } from '../../assert';
import { UhkBuffer} from '../../UhkBuffer';
import { KeyModifiers } from '../KeyModifiers';
import { MacroAction, MacroActionId, macroActionType } from './MacroAction';
import { MacroAction, MacroActionId, MacroSubAction, macroActionType } from './MacroAction';
const NUM_OF_COMBINATIONS = 3; // Cases: scancode, modifer, both
enum Action {
press = 0,
hold = 1,
release = 2
}
interface JsObjectKeyMacroAction {
macroActionType: string;
action: string;
@@ -20,8 +14,8 @@ interface JsObjectKeyMacroAction {
export class KeyMacroAction extends MacroAction {
@assertEnum(Action)
action: Action;
@assertEnum(MacroSubAction)
action: MacroSubAction;
@assertUInt8
scancode: number;
@@ -31,7 +25,7 @@ export class KeyMacroAction extends MacroAction {
_fromJsObject(jsObject: JsObjectKeyMacroAction): KeyMacroAction {
this.assertMacroActionType(jsObject);
this.action = Action[jsObject.action];
this.action = MacroSubAction[jsObject.action];
this.scancode = jsObject.scancode;
this.modifierMask = jsObject.modifierMask;
return this;
@@ -54,7 +48,7 @@ export class KeyMacroAction extends MacroAction {
_toJsObject(): any {
let jsObject: JsObjectKeyMacroAction = {
macroActionType: macroActionType.KeyMacroAction,
action: Action[this.action]
action: MacroSubAction[this.action]
};
if (this.hasScancode()) {
@@ -104,14 +98,14 @@ export class KeyMacroAction extends MacroAction {
}
isHoldAction(): boolean {
return this.action === Action.hold;
return this.action === MacroSubAction.hold;
}
isPressAction(): boolean {
return this.action === Action.press;
return this.action === MacroSubAction.press;
}
isReleaseAction(): boolean {
return this.action === Action.release;
return this.action === MacroSubAction.release;
}
}

View File

@@ -16,20 +16,29 @@ export enum MacroActionId {
ReleaseKeyMacroAction with scancode and modifiers 8
*/
LastKeyMacroAction = 8,
MouseButtonMacroAction = 9,
/*
9 - 11 are reserved for MouseButtonMacroAction
PressMouseButtonsMacroAction = 9,
HoldMouseButtonsMacroAction = 10,
ReleaseMouseButtonsMacroAction = 11,
*/
LastMouseButtonMacroAction = 11,
MoveMouseMacroAction = 12,
ScrollMouseMacroAction = 13,
DelayMacroAction = 14,
TextMacroAction = 15
}
export enum MacroSubAction {
press = 0,
hold = 1,
release = 2
}
export let macroActionType = {
KeyMacroAction : 'key',
PressMouseButtonsMacroAction : 'pressMouseButtons',
HoldMouseButtonsMacroAction : 'holdMouseButtons',
ReleaseMouseButtonsMacroAction : 'releaseMouseButtons',
MouseButtonMacroAction : 'mouseButton',
MoveMouseMacroAction : 'moveMouse',
ScrollMouseMacroAction : 'scrollMouse',
DelayMacroAction : 'delay',
@@ -53,8 +62,12 @@ export abstract class MacroAction extends Serializable<MacroAction> {
if (readMacroActionId < MacroActionId.KeyMacroAction || readMacroActionId > MacroActionId.LastKeyMacroAction) {
throw `Invalid ${classname} first byte: ${readMacroActionId}`;
}
} else if (macroActionId === MacroActionId.MouseButtonMacroAction) {
if (readMacroActionId < MacroActionId.MouseButtonMacroAction ||
readMacroActionId > MacroActionId.LastMouseButtonMacroAction) {
throw `Invalid ${classname} first byte: ${readMacroActionId}`;
}
else if (readMacroActionId !== macroActionId) {
} else if (readMacroActionId !== macroActionId) {
throw `Invalid ${classname} first byte: ${readMacroActionId}`;
}
return readMacroActionId;

View File

@@ -1,12 +1,10 @@
import {ClassArray} from '../../ClassArray';
import {UhkBuffer} from '../../UhkBuffer';
import {DelayMacroAction} from './DelayMacroAction';
import {HoldMouseButtonsMacroAction} from './HoldMouseButtonsMacroAction';
import {KeyMacroAction} from './KeyMacroAction';
import {MacroAction, MacroActionId, macroActionType} from './MacroAction';
import {MouseButtonMacroAction} from './MouseButtonMacroAction';
import {MoveMouseMacroAction} from './MoveMouseMacroAction';
import {PressMouseButtonsMacroAction} from './PressMouseButtonsMacroAction';
import {ReleaseMouseButtonsMacroAction} from './ReleaseMouseButtonsMacroAction';
import {ScrollMouseMacroAction} from './ScrollMouseMacroAction';
import {TextMacroAction} from './TextMacroAction';
@@ -16,12 +14,8 @@ export class MacroActions extends ClassArray<MacroAction> {
switch (jsObject.macroActionType) {
case macroActionType.KeyMacroAction:
return new KeyMacroAction().fromJsObject(jsObject);
case macroActionType.PressMouseButtonsMacroAction:
return new PressMouseButtonsMacroAction().fromJsObject(jsObject);
case macroActionType.HoldMouseButtonsMacroAction:
return new HoldMouseButtonsMacroAction().fromJsObject(jsObject);
case macroActionType.ReleaseMouseButtonsMacroAction:
return new ReleaseMouseButtonsMacroAction().fromJsObject(jsObject);
case macroActionType.MouseButtonMacroAction:
return new MouseButtonMacroAction().fromJsObject(jsObject);
case macroActionType.MoveMouseMacroAction:
return new MoveMouseMacroAction().fromJsObject(jsObject);
case macroActionType.ScrollMouseMacroAction:
@@ -48,12 +42,8 @@ export class MacroActions extends ClassArray<MacroAction> {
return new KeyMacroAction().fromBinary(buffer);
}
switch (macroActionFirstByte) {
case MacroActionId.PressMouseButtonsMacroAction:
return new PressMouseButtonsMacroAction().fromBinary(buffer);
case MacroActionId.HoldMouseButtonsMacroAction:
return new HoldMouseButtonsMacroAction().fromBinary(buffer);
case MacroActionId.ReleaseMouseButtonsMacroAction:
return new ReleaseMouseButtonsMacroAction().fromBinary(buffer);
case MacroActionId.MouseButtonMacroAction:
return new MouseButtonMacroAction().fromBinary(buffer);
case MacroActionId.MoveMouseMacroAction:
return new MoveMouseMacroAction().fromBinary(buffer);
case MacroActionId.ScrollMouseMacroAction:

View File

@@ -0,0 +1,85 @@
import { assertEnum, assertUInt8 } from '../../assert';
import { UhkBuffer } from '../../UhkBuffer';
import { MacroAction, MacroActionId, MacroSubAction, macroActionType } from './MacroAction';
export enum MouseButtons {
Left = 1 << 0,
Middle = 1 << 1,
Right = 1 << 2
};
interface JsObjectMouseButtonMacroAction {
macroActionType: string;
action: string;
mouseButtonsMask?: number;
}
export class MouseButtonMacroAction extends MacroAction {
@assertEnum(MacroSubAction)
action: MacroSubAction;
@assertUInt8
mouseButtonsMask: number;
_fromJsObject(jsObject: JsObjectMouseButtonMacroAction): MouseButtonMacroAction {
this.assertMacroActionType(jsObject);
this.action = MacroSubAction[jsObject.action];
this.mouseButtonsMask = jsObject.mouseButtonsMask;
return this;
}
_fromBinary(buffer: UhkBuffer): MouseButtonMacroAction {
this.readAndAssertMacroActionId(buffer);
this.mouseButtonsMask = buffer.readUInt8();
return this;
}
_toJsObject(): any {
return {
macroActionType: macroActionType.MouseButtonMacroAction,
action: MacroSubAction[this.action],
mouseButtonsMask: this.mouseButtonsMask
};
}
_toBinary(buffer: UhkBuffer): void {
buffer.writeUInt8(MacroActionId.MouseButtonMacroAction);
buffer.writeUInt8(this.mouseButtonsMask);
}
setMouseButtons(buttonStates: boolean[]): void {
let bitmask = 0;
for (let i = 0; i < buttonStates.length; i++) {
bitmask |= Number(buttonStates[i]) << i;
}
this.mouseButtonsMask = bitmask;
}
getMouseButtons(): boolean[] {
let enabledMouseButtons: boolean[] = [];
for (let bitmask = this.mouseButtonsMask; bitmask; bitmask >>>= 1) {
enabledMouseButtons.push(Boolean(bitmask & 1));
}
return enabledMouseButtons;
}
toString(): string {
return `<MouseButtonMacroAction mouseButtonsMask="${this.mouseButtonsMask}">`;
}
hasButtons(): boolean {
return this.mouseButtonsMask !== 0;
}
isOnlyHoldAction(): boolean {
return this.action === MacroSubAction.hold;
}
isOnlyPressAction(): boolean {
return this.action === MacroSubAction.press;
}
isOnlyReleaseAction(): boolean {
return this.action === MacroSubAction.release;
}
}

View File

@@ -1,37 +0,0 @@
import {assertUInt8} from '../../assert';
import {UhkBuffer} from '../../UhkBuffer';
import {MacroAction, MacroActionId, macroActionType} from './MacroAction';
export class PressMouseButtonsMacroAction extends MacroAction {
@assertUInt8
mouseButtonsMask: number;
_fromJsObject(jsObject: any): PressMouseButtonsMacroAction {
this.assertMacroActionType(jsObject);
this.mouseButtonsMask = jsObject.mouseButtonsMask;
return this;
}
_fromBinary(buffer: UhkBuffer): PressMouseButtonsMacroAction {
this.readAndAssertMacroActionId(buffer);
this.mouseButtonsMask = buffer.readUInt8();
return this;
}
_toJsObject(): any {
return {
macroActionType: macroActionType.PressMouseButtonsMacroAction,
mouseButtonsMask: this.mouseButtonsMask
};
}
_toBinary(buffer: UhkBuffer) {
buffer.writeUInt8(MacroActionId.PressMouseButtonsMacroAction);
buffer.writeUInt8(this.mouseButtonsMask);
}
toString(): string {
return `<PressMouseButtonsMacroAction mouseButtonsMask="${this.mouseButtonsMask}">`;
}
}

View File

@@ -1,37 +0,0 @@
import {assertUInt8} from '../../assert';
import {UhkBuffer} from '../../UhkBuffer';
import {MacroAction, MacroActionId, macroActionType} from './MacroAction';
export class ReleaseMouseButtonsMacroAction extends MacroAction {
@assertUInt8
mouseButtonsMask: number;
_fromJsObject(jsObject: any): ReleaseMouseButtonsMacroAction {
this.assertMacroActionType(jsObject);
this.mouseButtonsMask = jsObject.mouseButtonsMask;
return this;
}
_fromBinary(buffer: UhkBuffer): ReleaseMouseButtonsMacroAction {
this.readAndAssertMacroActionId(buffer);
this.mouseButtonsMask = buffer.readUInt8();
return this;
}
_toJsObject(): any {
return {
macroActionType: macroActionType.ReleaseMouseButtonsMacroAction,
mouseButtonsMask: this.mouseButtonsMask
};
}
_toBinary(buffer: UhkBuffer) {
buffer.writeUInt8(MacroActionId.ReleaseMouseButtonsMacroAction);
buffer.writeUInt8(this.mouseButtonsMask);
}
toString(): string {
return `<ReleaseMouseButtonsMacroAction mouseButtonsMask="${this.mouseButtonsMask}">`;
}
}

View File

@@ -1,10 +1,9 @@
export * from './DelayMacroAction';
export * from './HoldMouseButtonsMacroAction';
export * from './EditableMacroAction';
export * from './KeyMacroAction';
export * from './MacroAction';
export * from './MacroActions';
export * from './MoveMouseMacroAction';
export * from './PressMouseButtonsMacroAction';
export * from './ReleaseMouseButtonsMacroAction';
export * from './MouseButtonMacroAction';
export * from './ScrollMouseMacroAction';
export * from './TextMacroAction';

View File

@@ -1015,15 +1015,18 @@
"modifierMask": 133
},
{
"macroActionType": "pressMouseButtons",
"macroActionType": "mouseButton",
"action": "press",
"mouseButtonsMask": 9
},
{
"macroActionType": "holdMouseButtons",
"macroActionType": "mouseButton",
"action": "hold",
"mouseButtonsMask": 12
},
{
"macroActionType": "releaseMouseButtons",
"macroActionType": "mouseButton",
"action": "release",
"mouseButtonsMask": 104
},
{
@@ -1058,7 +1061,8 @@
"scancode": 111
},
{
"macroActionType": "releaseMouseButtons",
"macroActionType": "mouseButton",
"action": "release",
"mouseButtonsMask": 104
},
{

View File

@@ -0,0 +1,45 @@
import {Directive, ElementRef, EventEmitter, Input, OnChanges, Output, Renderer, SimpleChanges } from '@angular/core';
const KEY_ENTER = 13;
@Directive({
selector: '[contenteditableModel]',
host: {
'(blur)': 'onBlur()',
'(keypress)': 'onKeypress($event)'
}
})
export class ContenteditableDirective implements OnChanges {
@Input('contenteditableModel') model: any;
@Input('contenteditableUpdateOnEnter') updateOnEnter: boolean;
@Output('contenteditableModelChange') update = new EventEmitter();
private lastViewModel: any;
constructor(private elRef: ElementRef, private renderer: Renderer) {}
ngOnChanges(changes: SimpleChanges) {
if (changes[this.lastViewModel]) {
this.lastViewModel = this.model;
this.refreshView();
}
}
onKeypress(event: any) {
if (this.updateOnEnter && (event.which === KEY_ENTER)) {
// Finish editing when Enter pressed
this.renderer.invokeElementMethod(this.elRef.nativeElement, 'blur');
return false;
}
}
onBlur() {
let value = this.elRef.nativeElement.innerText;
this.lastViewModel = value;
this.update.emit(value);
}
private refreshView() {
this.elRef.nativeElement.innerText = this.model;
}
}

View File

@@ -0,0 +1 @@
export { ContenteditableDirective } from './contenteditable.directive';

View File

@@ -17,7 +17,7 @@ main-app {
.select2 {
&-container {
z-index: 100000;
z-index: 1100;
}
&-results {
@@ -33,7 +33,7 @@ main-app {
position: fixed;
right: -50px;
bottom: 40px;
z-index: 999999;
z-index: 2000;
/* stylelint-disable indentation */
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);

View File

@@ -167,7 +167,7 @@ ul {
position: absolute;
top: 2rem;
right: 2rem;
z-index: 10000;
z-index: 2000;
background-color: #333;
color: #eee;
@@ -270,7 +270,7 @@ svg.uhk {
position: fixed;
right: -50px;
bottom: 40px;
z-index: 999999;
z-index: 2000;
/* stylelint-disable indentation */
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);

View File

@@ -43,7 +43,7 @@
}
.select2-container {
z-index: 100000;
z-index: 100;
}
.nav-tabs > li > a {

View File

@@ -86,7 +86,8 @@ export class MapperService {
undefined, // 79 Right arrow
undefined, // 80 Left arrow
undefined, // 81 Down arrow
undefined // 82 Up arrow
undefined, // 82 Up arrow
['Num Lock'] // 83
];
private scanCodeFileName: Map<number, string>;
@@ -99,7 +100,7 @@ export class MapperService {
}
public scanCodeToText(scanCode: number): string[] {
return this.scanCodeTextMap[scanCode] || [ 'Unkown' ];
return this.scanCodeTextMap[scanCode] || [ 'Unknown' ];
}
public scanCodeToSvgImagePath(scanCode: number): string {