Macro editing improvements (#342)

* refactor: Remove EditableMacroAction

* Fix smaller macro bugs
This commit is contained in:
József Farkas
2017-07-08 16:05:09 +02:00
committed by László Monda
parent b51b9c9cb7
commit 0c52fdf2f8
9 changed files with 119 additions and 292 deletions
@@ -1,12 +1,18 @@
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { import {
EditableMacroAction,
MacroAction, MacroAction,
DelayMacroAction,
KeyMacroAction,
ScrollMouseMacroAction,
MoveMouseMacroAction,
MouseButtonMacroAction,
TextMacroAction, TextMacroAction,
macroActionType macroActionType,
Helper as MacroActionHelper
} from '../../../config-serializer/config-items/macro-action'; } from '../../../config-serializer/config-items/macro-action';
import { MacroKeyTabComponent } from './tab/key'; import { MacroDelayTabComponent, MacroMouseTabComponent, MacroKeyTabComponent, MacroTextTabComponent } from './tab';
import { KeystrokeAction } from '../../../config-serializer/config-items/key-action';
enum TabName { enum TabName {
Keypress, Keypress,
@@ -27,34 +33,35 @@ export class MacroActionEditorComponent implements OnInit {
@Output() save = new EventEmitter<MacroAction>(); @Output() save = new EventEmitter<MacroAction>();
@Output() cancel = new EventEmitter<void>(); @Output() cancel = new EventEmitter<void>();
@ViewChild('tab') selectedTab: any; @ViewChild('tab') selectedTab: MacroTextTabComponent | MacroKeyTabComponent | MacroMouseTabComponent | MacroDelayTabComponent;
editableMacroAction: EditableMacroAction; editableMacroAction: MacroAction;
activeTab: TabName; activeTab: TabName;
/* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */ /* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */
TabName = TabName; TabName = TabName;
/* tslint:enable:variable-name */ /* tslint:enable:variable-name */
ngOnInit() { ngOnInit() {
const macroAction: MacroAction = this.macroAction ? this.macroAction : new TextMacroAction(); this.updateEditableMacroAction();
this.editableMacroAction = new EditableMacroAction(macroAction.toJsonObject());
const tab: TabName = this.getTabName(this.editableMacroAction); const tab: TabName = this.getTabName(this.editableMacroAction);
this.activeTab = tab; this.activeTab = tab;
} }
ngOnChanges() {
this.ngOnInit();
}
onCancelClick(): void { onCancelClick(): void {
this.cancel.emit(); this.cancel.emit();
} }
onSaveClick(): void { onSaveClick(): void {
try { try {
const action = this.editableMacroAction; // TODO: Refactor after getKeyMacroAction has been added to all tabs
if (action.isKeyAction()) { const action = this.selectedTab instanceof MacroKeyTabComponent ?
// Could updating the saved keys be done in a better way? this.selectedTab.getKeyMacroAction() :
const tab = this.selectedTab as MacroKeyTabComponent; this.selectedTab.macroAction;
action.fromKeyAction(tab.getKeyAction()); this.save.emit(action);
}
this.save.emit(action.toClass());
} catch (e) { } catch (e) {
// TODO: show error dialog // TODO: show error dialog
console.error(e); console.error(e);
@@ -63,43 +70,31 @@ export class MacroActionEditorComponent implements OnInit {
selectTab(tab: TabName): void { selectTab(tab: TabName): void {
this.activeTab = tab; this.activeTab = tab;
this.editableMacroAction.macroActionType = this.getTabMacroActionType(tab); if (tab === this.getTabName(this.macroAction)) {
} this.updateEditableMacroAction();
} else {
getTabName(action: EditableMacroAction): TabName { this.editableMacroAction = undefined;
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 { getTabName(action: MacroAction): TabName {
switch (tab) { if (action instanceof DelayMacroAction) {
case TabName.Delay: return TabName.Delay;
return macroActionType.DelayMacroAction; } else if (action instanceof TextMacroAction) {
case TabName.Keypress: return TabName.Text;
return macroActionType.KeyMacroAction; } else if (action instanceof KeyMacroAction) {
case TabName.Mouse: return TabName.Keypress;
return macroActionType.MouseButtonMacroAction; } else if (action instanceof MouseButtonMacroAction ||
case TabName.Text: action instanceof MoveMouseMacroAction ||
return macroActionType.TextMacroAction; action instanceof ScrollMouseMacroAction) {
default: return TabName.Mouse;
throw new Error('Could not get macro action type');
} }
return undefined;
}
private updateEditableMacroAction() {
const macroAction: MacroAction = this.macroAction ? this.macroAction : new TextMacroAction();
this.editableMacroAction = MacroActionHelper.createMacroAction(macroAction);
} }
} }
@@ -7,7 +7,7 @@ import {
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { EditableMacroAction } from '../../../../../config-serializer/config-items/macro-action'; import { DelayMacroAction } from '../../../../../config-serializer/config-items/macro-action';
const INITIAL_DELAY = 0.5; // In seconds const INITIAL_DELAY = 0.5; // In seconds
@@ -19,7 +19,7 @@ const INITIAL_DELAY = 0.5; // In seconds
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class MacroDelayTabComponent implements OnInit { export class MacroDelayTabComponent implements OnInit {
@Input() macroAction: EditableMacroAction; @Input() macroAction: DelayMacroAction;
@ViewChild('macroDelayInput') input: ElementRef; @ViewChild('macroDelayInput') input: ElementRef;
delay: number; delay: number;
@@ -28,6 +28,9 @@ export class MacroDelayTabComponent implements OnInit {
constructor() { } constructor() { }
ngOnInit() { ngOnInit() {
if (!this.macroAction) {
this.macroAction = new DelayMacroAction();
}
this.delay = this.macroAction.delay > 0 ? this.macroAction.delay / 1000 : INITIAL_DELAY; this.delay = this.macroAction.delay > 0 ? this.macroAction.delay / 1000 : INITIAL_DELAY;
} }
@@ -1,7 +1,7 @@
import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { KeyAction } from '../../../../../config-serializer/config-items/key-action'; import { KeyAction, KeystrokeAction } from '../../../../../config-serializer/config-items/key-action';
import { EditableMacroAction, MacroSubAction } from '../../../../../config-serializer/config-items/macro-action'; import { KeyMacroAction, MacroSubAction } from '../../../../../config-serializer/config-items/macro-action';
import { KeypressTabComponent } from '../../../../popover/tab'; import { KeypressTabComponent } from '../../../../popover/tab';
import { Tab } from '../../../../popover/tab'; import { Tab } from '../../../../popover/tab';
@@ -21,7 +21,7 @@ enum TabName {
host: { 'class': 'macro__mouse' } host: { 'class': 'macro__mouse' }
}) })
export class MacroKeyTabComponent implements OnInit { export class MacroKeyTabComponent implements OnInit {
@Input() macroAction: EditableMacroAction; @Input() macroAction: KeyMacroAction;
@ViewChild('tab') selectedTab: Tab; @ViewChild('tab') selectedTab: Tab;
@ViewChild('keypressTab') keypressTab: KeypressTabComponent; @ViewChild('keypressTab') keypressTab: KeypressTabComponent;
@@ -29,24 +29,26 @@ export class MacroKeyTabComponent implements OnInit {
TabName = TabName; TabName = TabName;
/* tslint:enable:variable-name */ /* tslint:enable:variable-name */
activeTab: TabName; activeTab: TabName;
defaultKeyAction: KeyAction; defaultKeyAction: KeystrokeAction;
ngOnInit() { ngOnInit() {
this.defaultKeyAction = this.macroAction.toKeystrokeAction(); if (!this.macroAction) {
this.macroAction = new KeyMacroAction();
}
this.defaultKeyAction = new KeystrokeAction(<any>this.macroAction);
this.selectTab(this.getTabName(this.macroAction)); this.selectTab(this.getTabName(this.macroAction));
} }
selectTab(tab: TabName): void { selectTab(tab: TabName): void {
this.activeTab = tab; this.activeTab = tab;
this.macroAction.action = this.getActionType(tab);
} }
getTabName(action: EditableMacroAction): TabName { getTabName(macroAction: KeyMacroAction): TabName {
if (!action.action || action.isOnlyPressAction()) { if (!macroAction.action) {
return TabName.Keypress; return TabName.Keypress;
} else if (action.isOnlyHoldAction()) { } else if (macroAction.action === MacroSubAction.hold) {
return TabName.Hold; return TabName.Hold;
} else if (action.isOnlyReleaseAction()) { } else if (macroAction.action === MacroSubAction.release) {
return TabName.Release; return TabName.Release;
} }
} }
@@ -64,8 +66,10 @@ export class MacroKeyTabComponent implements OnInit {
} }
} }
getKeyAction(): KeyAction { getKeyMacroAction(): KeyMacroAction {
return this.keypressTab.toKeyAction(); const keyMacroAction = Object.assign(new KeyMacroAction(), this.keypressTab.toKeyAction());
keyMacroAction.action = this.getActionType(this.activeTab);
return keyMacroAction;
} }
} }
@@ -40,11 +40,11 @@
<div class="form-horizontal"> <div class="form-horizontal">
<div class="form-group"> <div class="form-group">
<label for="move-mouse-x">X</label> <label for="move-mouse-x">X</label>
<input id="move-mouse-x" type="number" class="form-control" [(ngModel)]="macroAction.moveX"> pixels <input id="move-mouse-x" type="number" class="form-control" [(ngModel)]="macroAction.x"> pixels
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="move-mouse-y">Y</label> <label for="move-mouse-y">Y</label>
<input id="move-mouse-y" type="number" class="form-control" [(ngModel)]="macroAction.moveY"> pixels <input id="move-mouse-y" type="number" class="form-control" [(ngModel)]="macroAction.y"> pixels
</div> </div>
</div> </div>
</div> </div>
@@ -54,11 +54,11 @@
<div class="form-horizontal"> <div class="form-horizontal">
<div class="form-group"> <div class="form-group">
<label for="scroll-mouse-x">X</label> <label for="scroll-mouse-x">X</label>
<input id="scroll-mouse-x" type="number" class="form-control" [(ngModel)]="macroAction.scrollX"> pixels <input id="scroll-mouse-x" type="number" class="form-control" [(ngModel)]="macroAction.x"> pixels
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="scroll-mouse-y">Y</label> <label for="scroll-mouse-y">Y</label>
<input id="scroll-mouse-y" type="number" class="form-control" [(ngModel)]="macroAction.scrollY"> pixels <input id="scroll-mouse-y" type="number" class="form-control" [(ngModel)]="macroAction.y"> pixels
</div> </div>
</div> </div>
</div> </div>
@@ -1,8 +1,12 @@
import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { EditableMacroAction, MacroSubAction, macroActionType } from '../../../../../config-serializer/config-items/macro-action'; import {
MouseButtonMacroAction, MoveMouseMacroAction, ScrollMouseMacroAction, MacroSubAction, macroActionType
} from '../../../../../config-serializer/config-items/macro-action';
import { Tab } from '../../../../popover/tab'; import { Tab } from '../../../../popover/tab';
type MouseMacroAction = MouseButtonMacroAction | MoveMouseMacroAction | ScrollMouseMacroAction;
enum TabName { enum TabName {
Move, Move,
Scroll, Scroll,
@@ -21,7 +25,7 @@ enum TabName {
host: { 'class': 'macro__mouse' } host: { 'class': 'macro__mouse' }
}) })
export class MacroMouseTabComponent implements OnInit { export class MacroMouseTabComponent implements OnInit {
@Input() macroAction: EditableMacroAction; @Input() macroAction: MouseMacroAction;
@ViewChild('tab') selectedTab: Tab; @ViewChild('tab') selectedTab: Tab;
/* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */ /* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */
@@ -37,25 +41,46 @@ export class MacroMouseTabComponent implements OnInit {
} }
ngOnInit() { ngOnInit() {
if (!this.macroAction) {
this.macroAction = new MouseButtonMacroAction();
this.macroAction.action = MacroSubAction.press;
}
const tabName = this.getTabName(this.macroAction); const tabName = this.getTabName(this.macroAction);
this.selectTab(tabName); this.selectTab(tabName);
const buttonActions = [TabName.Click, TabName.Hold, TabName.Release]; const buttonActions = [TabName.Click, TabName.Hold, TabName.Release];
if (buttonActions.includes(this.activeTab)) { if (buttonActions.includes(this.activeTab)) {
this.selectedButtons = this.macroAction.getMouseButtons(); this.selectedButtons = (<MouseButtonMacroAction>this.macroAction).getMouseButtons();
} }
} }
ngOnChanges() {
this.ngOnInit();
}
selectTab(tab: TabName): void { selectTab(tab: TabName): void {
this.activeTab = tab; this.activeTab = tab;
this.macroAction.macroActionType = this.getMacroActionType(tab);
if (this.macroAction.macroActionType === macroActionType.MouseButtonMacroAction) { if (tab === this.getTabName(this.macroAction)) {
this.macroAction.action = this.getAction(tab); return;
}
switch (tab) {
case TabName.Scroll:
this.macroAction = new ScrollMouseMacroAction();
break;
case TabName.Move:
this.macroAction = new MoveMouseMacroAction();
break;
default:
this.macroAction = new MouseButtonMacroAction();
this.macroAction.action = this.getAction(tab);
break;
} }
} }
setMouseClick(index: number): void { setMouseClick(index: number): void {
this.selectedButtons[index] = !this.selectedButtons[index]; this.selectedButtons[index] = !this.selectedButtons[index];
this.macroAction.setMouseButtons(this.selectedButtons); (<MouseButtonMacroAction>this.macroAction).setMouseButtons(this.selectedButtons);
} }
hasButton(index: number): boolean { hasButton(index: number): boolean {
@@ -71,35 +96,25 @@ export class MacroMouseTabComponent implements OnInit {
case TabName.Release: case TabName.Release:
return MacroSubAction.release; return MacroSubAction.release;
default: default:
throw new Error('Invalid tab name'); throw new Error(`Invalid tab name: ${TabName[tab]}`);
} }
} }
getTabName(action: EditableMacroAction): TabName { getTabName(action: MouseMacroAction): TabName {
if (action.macroActionType === macroActionType.MouseButtonMacroAction) { if (action instanceof MouseButtonMacroAction) {
if (!action.action || action.isOnlyPressAction()) { if (!action.action || action.isOnlyPressAction()) {
return TabName.Click; return TabName.Click;
} else if (action.isOnlyPressAction()) { } else if (action.isOnlyHoldAction()) {
return TabName.Hold; return TabName.Hold;
} else if (action.isOnlyReleaseAction()) { } else if (action.isOnlyReleaseAction()) {
return TabName.Release; return TabName.Release;
} }
} else if (action.macroActionType === macroActionType.MoveMouseMacroAction) { } else if (action instanceof MoveMouseMacroAction) {
return TabName.Move; return TabName.Move;
} else if (action.macroActionType === macroActionType.ScrollMouseMacroAction) { } else if (action instanceof ScrollMouseMacroAction) {
return TabName.Scroll; return TabName.Scroll;
} }
return TabName.Move; 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;
}
}
} }
@@ -1,4 +1,5 @@
import { import {
OnInit,
AfterViewInit, AfterViewInit,
Component, Component,
ElementRef, ElementRef,
@@ -7,7 +8,7 @@ import {
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { EditableMacroAction } from '../../../../../config-serializer/config-items/macro-action'; import { TextMacroAction } from '../../../../../config-serializer/config-items/macro-action';
@Component({ @Component({
selector: 'macro-text-tab', selector: 'macro-text-tab',
@@ -15,12 +16,18 @@ import { EditableMacroAction } from '../../../../../config-serializer/config-ite
styleUrls: ['./macro-text.component.scss'], styleUrls: ['./macro-text.component.scss'],
host: { 'class': 'macro__text' } host: { 'class': 'macro__text' }
}) })
export class MacroTextTabComponent implements AfterViewInit { export class MacroTextTabComponent implements OnInit, AfterViewInit {
@Input() macroAction: EditableMacroAction; @Input() macroAction: TextMacroAction;
@ViewChild('macroTextInput') input: ElementRef; @ViewChild('macroTextInput') input: ElementRef;
constructor(private renderer: Renderer) {} constructor(private renderer: Renderer) {}
ngOnInit() {
if (!this.macroAction) {
this.macroAction = new TextMacroAction();
}
}
ngAfterViewInit() { ngAfterViewInit() {
this.renderer.invokeElementMethod(this.input.nativeElement, 'focus'); this.renderer.invokeElementMethod(this.input.nativeElement, 'focus');
} }
@@ -148,7 +148,7 @@ export class MacroItemComponent implements OnInit, OnChanges {
if (action.hasModifiers()) { if (action.hasModifiers()) {
// Press/hold/release modifiers // Press/hold/release modifiers
for (let i = KeyModifiers.leftCtrl; i !== KeyModifiers.rightGui; i <<= 1) { for (let i = KeyModifiers.leftCtrl; i <= KeyModifiers.rightGui; i <<= 1) {
if (action.isModifierActive(i)) { if (action.isModifierActive(i)) {
this.title += ' ' + KeyModifiers[i]; this.title += ' ' + KeyModifiers[i];
} }
@@ -1,196 +0,0 @@
import { KeyAction, KeystrokeAction, keyActionType } from '../key-action';
import { KeystrokeType } from '../key-action/keystroke-type';
import { DelayMacroAction } from './delay-macro-action';
import { KeyMacroAction } from './key-macro-action';
import { MacroAction, MacroSubAction, macroActionType } from './macro-action';
import { MouseButtonMacroAction } from './mouse-button-macro-action';
import { MoveMouseMacroAction } from './move-mouse-macro-action';
import { ScrollMouseMacroAction } from './scroll-mouse-macro-action';
import { TextMacroAction } from './text-macro-action';
interface JsObjectEditableMacroAction {
macroActionType: string;
action?: string;
scancode?: number;
type?: string;
modifierMask?: number;
mouseButtonsMask?: number;
x?: number;
y?: number;
delay?: number;
text?: string;
}
export class EditableMacroAction {
macroActionType: string;
action: MacroSubAction;
// Key macro action properties
type: number;
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.type = KeystrokeType[jsObject.type];
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,
type: KeystrokeType[this.type],
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 {
const data = keyAction.toJsonObject();
this.scancode = data.scancode;
this.type = KeystrokeType[data.type as string];
this.modifierMask = data.modifierMask;
}
toKeystrokeAction(): KeystrokeAction {
const data = this.toJsObject();
data.keyActionType = keyActionType.KeystrokeAction;
return <KeystrokeAction>(new KeystrokeAction().fromJsonObject(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[] {
const 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().fromJsonObject({
macroActionType: this.macroActionType,
delay: this.delay
});
// Text action
case macroActionType.TextMacroAction:
return new TextMacroAction().fromJsonObject({
macroActionType: this.macroActionType,
text: this.text
});
// Keypress action
case macroActionType.KeyMacroAction:
return new KeyMacroAction().fromJsonObject({
macroActionType: this.macroActionType,
action: MacroSubAction[this.action],
type: KeystrokeType[this.type],
scancode: this.scancode,
modifierMask: this.modifierMask
});
// Mouse actions
case macroActionType.MouseButtonMacroAction:
return new MouseButtonMacroAction().fromJsonObject({
macroActionType: this.macroActionType,
action: MacroSubAction[this.action],
mouseButtonsMask: this.mouseButtonsMask
});
case macroActionType.MoveMouseMacroAction:
return new MoveMouseMacroAction().fromJsonObject({
macroActionType: this.macroActionType,
x: this.moveX,
y: this.moveY
});
case macroActionType.ScrollMouseMacroAction:
return new ScrollMouseMacroAction().fromJsonObject({
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;
}
}
@@ -1,5 +1,4 @@
export * from './delay-macro-action'; export * from './delay-macro-action';
export * from './editable-macro-action';
export * from './key-macro-action'; export * from './key-macro-action';
export * from './macro-action'; export * from './macro-action';
export * from './move-mouse-macro-action'; export * from './move-mouse-macro-action';