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

View File

@@ -1,12 +1,18 @@
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import {
EditableMacroAction,
MacroAction,
DelayMacroAction,
KeyMacroAction,
ScrollMouseMacroAction,
MoveMouseMacroAction,
MouseButtonMacroAction,
TextMacroAction,
macroActionType
macroActionType,
Helper as MacroActionHelper
} 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 {
Keypress,
@@ -27,34 +33,35 @@ export class MacroActionEditorComponent implements OnInit {
@Output() save = new EventEmitter<MacroAction>();
@Output() cancel = new EventEmitter<void>();
@ViewChild('tab') selectedTab: any;
@ViewChild('tab') selectedTab: MacroTextTabComponent | MacroKeyTabComponent | MacroMouseTabComponent | MacroDelayTabComponent;
editableMacroAction: EditableMacroAction;
editableMacroAction: MacroAction;
activeTab: TabName;
/* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */
TabName = TabName;
/* tslint:enable:variable-name */
ngOnInit() {
const macroAction: MacroAction = this.macroAction ? this.macroAction : new TextMacroAction();
this.editableMacroAction = new EditableMacroAction(macroAction.toJsonObject());
this.updateEditableMacroAction();
const tab: TabName = this.getTabName(this.editableMacroAction);
this.activeTab = tab;
}
ngOnChanges() {
this.ngOnInit();
}
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());
// TODO: Refactor after getKeyMacroAction has been added to all tabs
const action = this.selectedTab instanceof MacroKeyTabComponent ?
this.selectedTab.getKeyMacroAction() :
this.selectedTab.macroAction;
this.save.emit(action);
} catch (e) {
// TODO: show error dialog
console.error(e);
@@ -63,43 +70,31 @@ export class MacroActionEditorComponent implements OnInit {
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;
if (tab === this.getTabName(this.macroAction)) {
this.updateEditableMacroAction();
} else {
this.editableMacroAction = undefined;
}
}
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');
getTabName(action: MacroAction): TabName {
if (action instanceof DelayMacroAction) {
return TabName.Delay;
} else if (action instanceof TextMacroAction) {
return TabName.Text;
} else if (action instanceof KeyMacroAction) {
return TabName.Keypress;
} else if (action instanceof MouseButtonMacroAction ||
action instanceof MoveMouseMacroAction ||
action instanceof ScrollMouseMacroAction) {
return TabName.Mouse;
}
return undefined;
}
private updateEditableMacroAction() {
const macroAction: MacroAction = this.macroAction ? this.macroAction : new TextMacroAction();
this.editableMacroAction = MacroActionHelper.createMacroAction(macroAction);
}
}

View File

@@ -7,7 +7,7 @@ import {
ViewChild
} 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
@@ -19,7 +19,7 @@ const INITIAL_DELAY = 0.5; // In seconds
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MacroDelayTabComponent implements OnInit {
@Input() macroAction: EditableMacroAction;
@Input() macroAction: DelayMacroAction;
@ViewChild('macroDelayInput') input: ElementRef;
delay: number;
@@ -28,6 +28,9 @@ export class MacroDelayTabComponent implements OnInit {
constructor() { }
ngOnInit() {
if (!this.macroAction) {
this.macroAction = new DelayMacroAction();
}
this.delay = this.macroAction.delay > 0 ? this.macroAction.delay / 1000 : INITIAL_DELAY;
}

View File

@@ -1,7 +1,7 @@
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 { KeyAction, KeystrokeAction } from '../../../../../config-serializer/config-items/key-action';
import { KeyMacroAction, MacroSubAction } from '../../../../../config-serializer/config-items/macro-action';
import { KeypressTabComponent } from '../../../../popover/tab';
import { Tab } from '../../../../popover/tab';
@@ -21,7 +21,7 @@ enum TabName {
host: { 'class': 'macro__mouse' }
})
export class MacroKeyTabComponent implements OnInit {
@Input() macroAction: EditableMacroAction;
@Input() macroAction: KeyMacroAction;
@ViewChild('tab') selectedTab: Tab;
@ViewChild('keypressTab') keypressTab: KeypressTabComponent;
@@ -29,24 +29,26 @@ export class MacroKeyTabComponent implements OnInit {
TabName = TabName;
/* tslint:enable:variable-name */
activeTab: TabName;
defaultKeyAction: KeyAction;
defaultKeyAction: KeystrokeAction;
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));
}
selectTab(tab: TabName): void {
this.activeTab = tab;
this.macroAction.action = this.getActionType(tab);
}
getTabName(action: EditableMacroAction): TabName {
if (!action.action || action.isOnlyPressAction()) {
getTabName(macroAction: KeyMacroAction): TabName {
if (!macroAction.action) {
return TabName.Keypress;
} else if (action.isOnlyHoldAction()) {
} else if (macroAction.action === MacroSubAction.hold) {
return TabName.Hold;
} else if (action.isOnlyReleaseAction()) {
} else if (macroAction.action === MacroSubAction.release) {
return TabName.Release;
}
}
@@ -64,8 +66,10 @@ export class MacroKeyTabComponent implements OnInit {
}
}
getKeyAction(): KeyAction {
return this.keypressTab.toKeyAction();
getKeyMacroAction(): KeyMacroAction {
const keyMacroAction = Object.assign(new KeyMacroAction(), this.keypressTab.toKeyAction());
keyMacroAction.action = this.getActionType(this.activeTab);
return keyMacroAction;
}
}

View File

@@ -40,11 +40,11 @@
<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
<input id="move-mouse-x" type="number" class="form-control" [(ngModel)]="macroAction.x"> 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
<input id="move-mouse-y" type="number" class="form-control" [(ngModel)]="macroAction.y"> pixels
</div>
</div>
</div>
@@ -54,11 +54,11 @@
<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
<input id="scroll-mouse-x" type="number" class="form-control" [(ngModel)]="macroAction.x"> 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
<input id="scroll-mouse-y" type="number" class="form-control" [(ngModel)]="macroAction.y"> pixels
</div>
</div>
</div>
@@ -68,10 +68,10 @@
<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)"
class="btn btn-default"
[class.btn-primary]="hasButton(buttonIndex)"
(click)="setMouseClick(buttonIndex)">{{buttonLabel}}</button>
</div>
</div>
</div>
</div>
</div>

View File

@@ -1,8 +1,12 @@
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';
type MouseMacroAction = MouseButtonMacroAction | MoveMouseMacroAction | ScrollMouseMacroAction;
enum TabName {
Move,
Scroll,
@@ -21,7 +25,7 @@ enum TabName {
host: { 'class': 'macro__mouse' }
})
export class MacroMouseTabComponent implements OnInit {
@Input() macroAction: EditableMacroAction;
@Input() macroAction: MouseMacroAction;
@ViewChild('tab') selectedTab: Tab;
/* 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() {
if (!this.macroAction) {
this.macroAction = new MouseButtonMacroAction();
this.macroAction.action = MacroSubAction.press;
}
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();
this.selectedButtons = (<MouseButtonMacroAction>this.macroAction).getMouseButtons();
}
}
ngOnChanges() {
this.ngOnInit();
}
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);
if (tab === this.getTabName(this.macroAction)) {
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 {
this.selectedButtons[index] = !this.selectedButtons[index];
this.macroAction.setMouseButtons(this.selectedButtons);
(<MouseButtonMacroAction>this.macroAction).setMouseButtons(this.selectedButtons);
}
hasButton(index: number): boolean {
@@ -71,35 +96,25 @@ export class MacroMouseTabComponent implements OnInit {
case TabName.Release:
return MacroSubAction.release;
default:
throw new Error('Invalid tab name');
throw new Error(`Invalid tab name: ${TabName[tab]}`);
}
}
getTabName(action: EditableMacroAction): TabName {
if (action.macroActionType === macroActionType.MouseButtonMacroAction) {
getTabName(action: MouseMacroAction): TabName {
if (action instanceof MouseButtonMacroAction) {
if (!action.action || action.isOnlyPressAction()) {
return TabName.Click;
} else if (action.isOnlyPressAction()) {
} else if (action.isOnlyHoldAction()) {
return TabName.Hold;
} else if (action.isOnlyReleaseAction()) {
return TabName.Release;
}
} else if (action.macroActionType === macroActionType.MoveMouseMacroAction) {
} else if (action instanceof MoveMouseMacroAction) {
return TabName.Move;
} else if (action.macroActionType === macroActionType.ScrollMouseMacroAction) {
} else if (action instanceof 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

@@ -1,4 +1,5 @@
import {
OnInit,
AfterViewInit,
Component,
ElementRef,
@@ -7,7 +8,7 @@ import {
ViewChild
} from '@angular/core';
import { EditableMacroAction } from '../../../../../config-serializer/config-items/macro-action';
import { TextMacroAction } from '../../../../../config-serializer/config-items/macro-action';
@Component({
selector: 'macro-text-tab',
@@ -15,12 +16,18 @@ import { EditableMacroAction } from '../../../../../config-serializer/config-ite
styleUrls: ['./macro-text.component.scss'],
host: { 'class': 'macro__text' }
})
export class MacroTextTabComponent implements AfterViewInit {
@Input() macroAction: EditableMacroAction;
export class MacroTextTabComponent implements OnInit, AfterViewInit {
@Input() macroAction: TextMacroAction;
@ViewChild('macroTextInput') input: ElementRef;
constructor(private renderer: Renderer) {}
ngOnInit() {
if (!this.macroAction) {
this.macroAction = new TextMacroAction();
}
}
ngAfterViewInit() {
this.renderer.invokeElementMethod(this.input.nativeElement, 'focus');
}

View File

@@ -148,7 +148,7 @@ export class MacroItemComponent implements OnInit, OnChanges {
if (action.hasModifiers()) {
// 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)) {
this.title += ' ' + KeyModifiers[i];
}

View File

@@ -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;
}
}

View File

@@ -1,5 +1,4 @@
export * from './delay-macro-action';
export * from './editable-macro-action';
export * from './key-macro-action';
export * from './macro-action';
export * from './move-mouse-macro-action';