feat(ui): macro ui improvement (#473)

* Remove the "Input the text you want to type with this macro action." sentence from the type text macro action.

* Move pointer and Scroll enhancement

* remove the extra vertical space above the mouse buttons

* macro delay enhancement

* not allow user select on a few elements

* fill the macro, keymap name the all space in the header
This commit is contained in:
Róbert Kiss
2017-10-28 16:54:08 +02:00
committed by László Monda
parent bd49e26978
commit 9885439b10
17 changed files with 139 additions and 46 deletions

View File

@@ -26,7 +26,7 @@
"unit-case": "lower", "unit-case": "lower",
"unit-no-unknown": true, "unit-no-unknown": true,
"unit-whitelist": ["px", "%", "deg", "ms", "em", "rem", "vh", "vv", "s"], "unit-whitelist": ["px", "%", "deg", "ms", "em", "rem", "vh", "vv", "s", "ch"],
"value-list-comma-space-after": "always-single-line", "value-list-comma-space-after": "always-single-line",
"value-list-comma-space-before": "never", "value-list-comma-space-before": "never",

View File

@@ -7,6 +7,7 @@
type="text" type="text"
(change)="editKeymapName($event.target.value)" (change)="editKeymapName($event.target.value)"
(keyup.enter)="name.blur()" (keyup.enter)="name.blur()"
(keyup)="calculateHeaderTextWidth($event.target.value)"
/> keymap /> keymap
(<input #abbr cancelable (<input #abbr cancelable
class="keymap__abbrev pane-title__abbrev" class="keymap__abbrev pane-title__abbrev"

View File

@@ -3,6 +3,7 @@ import {
Component, Component,
ElementRef, ElementRef,
EventEmitter, EventEmitter,
HostListener,
Input, Input,
OnChanges, OnChanges,
Output, Output,
@@ -16,6 +17,7 @@ import { Store } from '@ngrx/store';
import { AppState } from '../../../store'; import { AppState } from '../../../store';
import { KeymapActions } from '../../../store/actions'; import { KeymapActions } from '../../../store/actions';
import * as util from '../../../util';
const DEFAULT_TRASH_TITLE = '<span class="text-nowrap">Delete keymap</span>'; const DEFAULT_TRASH_TITLE = '<span class="text-nowrap">Delete keymap</span>';
@@ -50,6 +52,11 @@ export class KeymapHeaderComponent implements OnChanges {
} }
} }
@HostListener('window:resize')
windowResize(): void {
this.calculateHeaderTextWidth(this.keymap.name);
}
setDefault() { setDefault() {
if (!this.keymap.isDefault) { if (!this.keymap.isDefault) {
this.store.dispatch(KeymapActions.setDefault(this.keymap.abbreviation)); this.store.dispatch(KeymapActions.setDefault(this.keymap.abbreviation));
@@ -103,8 +110,16 @@ export class KeymapHeaderComponent implements OnChanges {
this.downloadClick.emit(); this.downloadClick.emit();
} }
calculateHeaderTextWidth(text): void {
const htmlInput = this.keymapName.nativeElement as HTMLInputElement;
const maxWidth = htmlInput.parentElement.offsetWidth - 530;
const textWidth = util.getContentWidth(window.getComputedStyle(htmlInput), text);
this.renderer.setStyle(htmlInput, 'width', Math.min(maxWidth, textWidth) + 'px');
}
private setName(): void { private setName(): void {
this.renderer.setProperty(this.keymapName.nativeElement, 'value', this.keymap.name); this.renderer.setProperty(this.keymapName.nativeElement, 'value', this.keymap.name);
this.calculateHeaderTextWidth(this.keymap.name);
} }
private setAbbreviation() { private setAbbreviation() {

View File

@@ -19,6 +19,7 @@
a { a {
border-top-right-radius: 0; border-top-right-radius: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
user-select: none;
&.selected { &.selected {
font-style: italic; font-style: italic;

View File

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

View File

@@ -14,3 +14,12 @@
} }
} }
} }
.form-group {
margin-bottom: 0;
}
.form-control {
width: 16ch;
display: inline-block;
}

View File

@@ -23,9 +23,19 @@ export class MacroDelayTabComponent extends MacroBaseComponent implements OnInit
@Input() macroAction: DelayMacroAction; @Input() macroAction: DelayMacroAction;
@ViewChild('macroDelayInput') input: ElementRef; @ViewChild('macroDelayInput') input: ElementRef;
delay: number;
presets: number[] = [0.3, 0.5, 0.8, 1, 2, 3, 4, 5]; presets: number[] = [0.3, 0.5, 0.8, 1, 2, 3, 4, 5];
get delay(): number {
return this._delay;
}
set delay(value: number) {
this._delay = value;
this.validate();
}
private _delay: number;
constructor() { super(); } constructor() { super(); }
ngOnInit() { ngOnInit() {
@@ -33,12 +43,11 @@ export class MacroDelayTabComponent extends MacroBaseComponent implements OnInit
this.macroAction = new DelayMacroAction(); 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;
this.validate(); // initial validation as it has defaults
} }
setDelay(value: number): void { setDelay(value: number): void {
this.delay = value; this._delay = value;
this.macroAction.delay = this.delay * 1000; this.macroAction.delay = this._delay * 1000;
this.validate(); this.validate();
} }

View File

@@ -36,29 +36,55 @@
<div class="col-xs-9 macro-mouse__actions" [ngSwitch]="activeTab"> <div class="col-xs-9 macro-mouse__actions" [ngSwitch]="activeTab">
<div #tab *ngSwitchCase="TabName.Move"> <div #tab *ngSwitchCase="TabName.Move">
<h4>Move pointer</h4> <h4>Move pointer</h4>
<p>Use negative values to move down or left from current position.</p>
<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['x']" (keyup)="validate()"> pixels <input id="move-mouse-x"
type="number"
class="form-control"
[(ngModel)]="macroAction['x']"
(keyup)="validate()"
min="-9999"
max="9999"
maxlength="4"> 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['y']" (keyup)="validate()"> pixels <input id="move-mouse-y"
type="number"
class="form-control"
[(ngModel)]="macroAction['y']"
(keyup)="validate()"
min="-9999"
max="9999"
maxlength="4"> pixels
</div> </div>
</div> </div>
</div> </div>
<div #tab *ngSwitchCase="TabName.Scroll"> <div #tab *ngSwitchCase="TabName.Scroll">
<h4>Scroll</h4> <h4>Scroll</h4>
<p>Use negative values to move down or left from current position.</p>
<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['x']" (keyup)="validate()"> pixels <input id="scroll-mouse-x"
type="number"
class="form-control"
[(ngModel)]="macroAction['x']"
(keyup)="validate()"
min="-9999"
max="9999"
maxlength="4"> 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['y']" (keyup)="validate()"> pixels <input id="scroll-mouse-y"
type="number"
class="form-control"
[(ngModel)]="macroAction['y']"
(keyup)="validate()"
min="-9999"
max="9999"
maxlength="4"> pixels
</div> </div>
</div> </div>
</div> </div>
@@ -66,11 +92,12 @@
<h4 *ngIf="activeTab === TabName.Click">Click mouse button</h4> <h4 *ngIf="activeTab === TabName.Click">Click mouse button</h4>
<h4 *ngIf="activeTab === TabName.Hold">Hold mouse button</h4> <h4 *ngIf="activeTab === TabName.Hold">Hold mouse button</h4>
<h4 *ngIf="activeTab === TabName.Release">Release mouse button</h4> <h4 *ngIf="activeTab === TabName.Release">Release mouse button</h4>
<div class="btn-group macro-mouse__buttons"> <div class="btn-group">
<button *ngFor="let buttonLabel of buttonLabels; let buttonIndex = index" <button *ngFor="let buttonLabel of buttonLabels; let buttonIndex = index"
class="btn btn-default" class="btn btn-default"
[class.btn-primary]="hasButton(buttonIndex)" [class.btn-primary]="hasButton(buttonIndex)"
(click)="setMouseClick(buttonIndex)">{{buttonLabel}}</button> (click)="setMouseClick(buttonIndex)">{{buttonLabel}}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -15,11 +15,6 @@
padding-left: 3rem; padding-left: 3rem;
padding-bottom: 1rem; padding-bottom: 1rem;
} }
&__buttons {
margin-top: 3rem;
margin-bottom: 1rem;
}
} }
.fa { .fa {
@@ -38,6 +33,6 @@
.form-control { .form-control {
display: inline-block; display: inline-block;
width: 60%; width: 10ch;
} }
} }

View File

@@ -26,7 +26,7 @@ enum TabName {
'../../macro-action-editor.component.scss', '../../macro-action-editor.component.scss',
'./macro-mouse.component.scss' './macro-mouse.component.scss'
], ],
host: { 'class': 'macro__mouse' } host: {'class': 'macro__mouse'}
}) })
export class MacroMouseTabComponent extends MacroBaseComponent implements OnInit { export class MacroMouseTabComponent extends MacroBaseComponent implements OnInit {
@Input() macroAction: MouseMacroAction; @Input() macroAction: MouseMacroAction;
@@ -130,14 +130,14 @@ export class MacroMouseTabComponent extends MacroBaseComponent implements OnInit
switch (this.macroAction.constructor) { switch (this.macroAction.constructor) {
case MoveMouseMacroAction: case MoveMouseMacroAction:
case ScrollMouseMacroAction: case ScrollMouseMacroAction:
const { x, y } = this.macroAction as MoveMouseMacroAction; const {x, y} = this.macroAction as MoveMouseMacroAction;
return x !== undefined && x !== null && y !== undefined && y !== null; return x !== undefined && x !== null && y !== undefined && y !== null &&
(x !== 0 || y !== 0) && x < 10000 && x > -10000 && y < 10000 && y > -10000;
case MouseButtonMacroAction: case MouseButtonMacroAction:
const { mouseButtonsMask } = this.macroAction as MouseButtonMacroAction; const {mouseButtonsMask} = this.macroAction as MouseButtonMacroAction;
return !!mouseButtonsMask; return !!mouseButtonsMask;
default: default:
return true; return true;
} }
} };
} }

View File

@@ -1,6 +1,5 @@
<div> <div>
<h4>Type text</h4> <h4>Type text</h4>
<p>Input the text you want to type with this macro action.</p>
<textarea #macroTextInput name="macro-text" (change)="onTextChange()" <textarea #macroTextInput name="macro-text" (change)="onTextChange()"
(keyup)="validate()" class="macro__text-input">{{ macroAction?.text }}</textarea> (keyup)="validate()" class="macro__text-input">{{ macroAction?.text }}</textarea>
</div> </div>

View File

@@ -7,7 +7,7 @@
type="text" type="text"
(change)="editMacroName($event.target.value)" (change)="editMacroName($event.target.value)"
(keyup.enter)="macroName.blur()" (keyup.enter)="macroName.blur()"
/> (keyup)="calculateHeaderTextWidth($event.target.value)">
<i class="glyphicon glyphicon-trash macro__remove pull-right" title="" <i class="glyphicon glyphicon-trash macro__remove pull-right" title=""
data-toggle="tooltip" data-toggle="tooltip"
data-placement="bottom" data-placement="bottom"

View File

@@ -32,7 +32,6 @@
border-bottom: 2px dotted #999; border-bottom: 2px dotted #999;
padding: 0; padding: 0;
margin: 0 0.25rem; margin: 0 0.25rem;
width: 330px;
text-overflow: ellipsis; text-overflow: ellipsis;
&:focus { &:focus {

View File

@@ -3,6 +3,7 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
ElementRef, ElementRef,
HostListener,
Input, Input,
OnChanges, OnChanges,
Renderer2, Renderer2,
@@ -13,7 +14,8 @@ import { Store } from '@ngrx/store';
import { Macro } from 'uhk-common'; import { Macro } from 'uhk-common';
import { MacroActions } from '../../../store/actions'; import { MacroActions } from '../../../store/actions';
import { AppState } from '../../../store/index'; import { AppState } from '../../../store';
import * as util from '../../../util';
@Component({ @Component({
selector: 'macro-header', selector: 'macro-header',
@@ -43,6 +45,11 @@ export class MacroHeaderComponent implements AfterViewInit, OnChanges {
} }
} }
@HostListener('window:resize')
windowResize(): void {
this.calculateHeaderTextWidth(this.macro.name);
}
removeMacro() { removeMacro() {
this.store.dispatch(MacroActions.removeMacro(this.macro.id)); this.store.dispatch(MacroActions.removeMacro(this.macro.id));
} }
@@ -60,12 +67,20 @@ export class MacroHeaderComponent implements AfterViewInit, OnChanges {
this.store.dispatch(MacroActions.editMacroName(this.macro.id, name)); this.store.dispatch(MacroActions.editMacroName(this.macro.id, name));
} }
calculateHeaderTextWidth(text): void {
const htmlInput = this.macroName.nativeElement as HTMLInputElement;
const maxWidth = htmlInput.parentElement.offsetWidth * 0.8;
const textWidth = util.getContentWidth(window.getComputedStyle(htmlInput), text);
this.renderer.setStyle(htmlInput, 'width', Math.min(maxWidth, textWidth) + 'px');
}
private setFocusOnName() { private setFocusOnName() {
this.macroName.nativeElement.select(); this.macroName.nativeElement.select();
} }
private setName(): void { private setName(): void {
this.renderer.setProperty(this.macroName.nativeElement, 'value', this.macro.name); this.renderer.setProperty(this.macroName.nativeElement, 'value', this.macro.name);
this.calculateHeaderTextWidth(this.macro.name);
} }
} }

View File

@@ -31,6 +31,7 @@
flex-shrink: 0; flex-shrink: 0;
border: 0; border: 0;
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
user-select: none;
icon { icon {
margin: 0 5px; margin: 0 5px;

View File

@@ -0,0 +1,17 @@
let canvas: HTMLCanvasElement;
export function getContentWidth(style: CSSStyleDeclaration, text: string): number {
if (!text) {
return 0;
}
if (!canvas) {
canvas = document.createElement('canvas');
}
const context = canvas.getContext('2d');
context.font = style.font;
const metrics = context.measureText(text);
return metrics.width;
}

View File

@@ -0,0 +1 @@
export * from './html-helper';