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-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-before": "never",

View File

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

View File

@@ -3,6 +3,7 @@ import {
Component,
ElementRef,
EventEmitter,
HostListener,
Input,
OnChanges,
Output,
@@ -16,6 +17,7 @@ import { Store } from '@ngrx/store';
import { AppState } from '../../../store';
import { KeymapActions } from '../../../store/actions';
import * as util from '../../../util';
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() {
if (!this.keymap.isDefault) {
this.store.dispatch(KeymapActions.setDefault(this.keymap.abbreviation));
@@ -103,8 +110,16 @@ export class KeymapHeaderComponent implements OnChanges {
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 {
this.renderer.setProperty(this.keymapName.nativeElement, 'value', this.keymap.name);
this.calculateHeaderTextWidth(this.keymap.name);
}
private setAbbreviation() {

View File

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

View File

@@ -1,11 +1,12 @@
<div class="macro-delay">
<div class="row">
<div class="col-xs-12">
<h4>Enter delay in seconds</h4>
<h4>Delay</h4>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<div class="col-xs-12">
<div class="form-group">
<input #macroDelayInput
type="number"
min="0"
@@ -13,14 +14,17 @@
step="0.1"
placeholder="Delay amount"
class="form-control"
[value]="delay"
(change)="setDelay(macroDelayInput.value)">
[ngModel]="delay"
(ngModelChange)="setDelay(macroDelayInput.value)">
<label>seconds</label>
</div>
</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>
<button *ngFor="let delay of presets" class="btn btn-sm btn-default" (click)="setDelay(delay)">{{delay}}s
</button>
</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;
@ViewChild('macroDelayInput') input: ElementRef;
delay: number;
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(); }
ngOnInit() {
@@ -33,12 +43,11 @@ export class MacroDelayTabComponent extends MacroBaseComponent implements OnInit
this.macroAction = new DelayMacroAction();
}
this.delay = this.macroAction.delay > 0 ? this.macroAction.delay / 1000 : INITIAL_DELAY;
this.validate(); // initial validation as it has defaults
}
setDelay(value: number): void {
this.delay = value;
this.macroAction.delay = this.delay * 1000;
this._delay = value;
this.macroAction.delay = this._delay * 1000;
this.validate();
}

View File

@@ -36,29 +36,55 @@
<div class="col-xs-9 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['x']" (keyup)="validate()"> pixels
<label for="move-mouse-x">X:</label>
<input id="move-mouse-x"
type="number"
class="form-control"
[(ngModel)]="macroAction['x']"
(keyup)="validate()"
min="-9999"
max="9999"
maxlength="4"> 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['y']" (keyup)="validate()"> pixels
<label for="move-mouse-y">Y:</label>
<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 #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['x']" (keyup)="validate()"> pixels
<label for="scroll-mouse-x">X:</label>
<input id="scroll-mouse-x"
type="number"
class="form-control"
[(ngModel)]="macroAction['x']"
(keyup)="validate()"
min="-9999"
max="9999"
maxlength="4"> 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['y']" (keyup)="validate()"> pixels
<label for="scroll-mouse-y">Y:</label>
<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>
@@ -66,11 +92,12 @@
<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">
<div class="btn-group">
<button *ngFor="let buttonLabel of buttonLabels; let buttonIndex = index"
class="btn btn-default"
[class.btn-primary]="hasButton(buttonIndex)"
(click)="setMouseClick(buttonIndex)">{{buttonLabel}}</button>
(click)="setMouseClick(buttonIndex)">{{buttonLabel}}
</button>
</div>
</div>
</div>

View File

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

View File

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

View File

@@ -1,6 +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" (change)="onTextChange()"
(keyup)="validate()" class="macro__text-input">{{ macroAction?.text }}</textarea>
</div>

View File

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

View File

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

View File

@@ -3,6 +3,7 @@ import {
ChangeDetectionStrategy,
Component,
ElementRef,
HostListener,
Input,
OnChanges,
Renderer2,
@@ -13,7 +14,8 @@ import { Store } from '@ngrx/store';
import { Macro } from 'uhk-common';
import { MacroActions } from '../../../store/actions';
import { AppState } from '../../../store/index';
import { AppState } from '../../../store';
import * as util from '../../../util';
@Component({
selector: 'macro-header',
@@ -43,6 +45,11 @@ export class MacroHeaderComponent implements AfterViewInit, OnChanges {
}
}
@HostListener('window:resize')
windowResize(): void {
this.calculateHeaderTextWidth(this.macro.name);
}
removeMacro() {
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));
}
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() {
this.macroName.nativeElement.select();
}
private setName(): void {
this.renderer.setProperty(this.macroName.nativeElement, 'value', this.macro.name);
this.calculateHeaderTextWidth(this.macro.name);
}
}

View File

@@ -31,6 +31,7 @@
flex-shrink: 0;
border: 0;
border-bottom: 1px solid #ddd;
user-select: none;
icon {
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';