feat: remap key on all keymaps / layers (#668)

* add: popover checkboxs

* feat: add KeyActionRemap

* fix: template driven form checkbox name

* fix: delete key action only if it SwitchLayerAction

* feat: use remap on all keymaps/layers checkbox values in SAVE_KEY action

* feat: set default value to the remapOnAllKeymap and remapOnAllLayer checkbox

* fix: layer mapping
This commit is contained in:
Róbert Kiss
2018-06-10 21:50:49 +02:00
committed by László Monda
parent 83b9f0d1e9
commit f34cb2df56
10 changed files with 158 additions and 53 deletions

View File

@@ -76,8 +76,41 @@
></none-tab>
</div>
<div class="popover-action">
<button class="btn btn-sm btn-default" type="button" (click)="onCancelClick()"> Cancel </button>
<button class="btn btn-sm btn-primary" [class.disabled]="!keyActionValid" type="button" (click)="onRemapKey()"> Remap Key </button>
<form class="form-inline d-inline-block popover-action-form">
<div class="checkbox">
<label>
<input type="checkbox"
name="remapOnAllKeymap"
[(ngModel)]="remapOnAllKeymap"> Remap on all keymaps
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox"
name="remapOnAllLayer"
[(ngModel)]="remapOnAllLayer"> Remap on all layers
</label>
</div>
<div class="d-inline-block">
<icon name="question-circle"
data-toggle="tooltip"
html="true"
maxWidth="500"
title="<ul class='no-indent text-left'>
<li>When 'Remap on all keymaps' is checked and the 'Remap key' button is clicked, remap the key on the current layer of all keymaps.</li>
<li>When 'Remap on all layers' is checked and the 'Remap key' button is clicked, remap the key on all layers of the current keymap.</li>
<li>When both 'Remap on all keymaps' and 'Remap on all layers' is checked and the 'Remap key' button is clicked, remap the key on all layers of all keymaps.</li>
</ul>"
data-placement="bottom"></icon>
</div>
</form>
<div class="d-inline-block pull-right">
<button class="btn btn-sm btn-default" type="button" (click)="onCancelClick()"> Cancel</button>
<button class="btn btn-sm btn-primary" [class.disabled]="!keyActionValid" type="button"
(click)="onRemapKey()"> Remap Key
</button>
</div>
</div>
</div>
<div class="popover-overlay" [class.display]="visible" (click)="onOverlay()"></div>

View File

@@ -70,7 +70,6 @@
background-color: #f7f7f7;
border-top: 1px solid #ebebeb;
border-radius: 0 0 5px 5px;
text-align: right;
}
.popover-title {
@@ -133,3 +132,11 @@
margin-top: -1rem;
}
}
.popover-action-form {
margin-top: 4px;
label {
margin-right: 5px;
}
}

View File

@@ -31,6 +31,7 @@ import { Tab } from './tab';
import { AppState } from '../../store';
import { getKeymaps } from '../../store/reducers/user-configuration';
import { KeyActionRemap } from '../../models/key-action-remap';
enum TabName {
Keypress,
@@ -59,8 +60,8 @@ enum TabName {
})),
transition('opened => closed', [
animate('200ms ease-out', keyframes([
style({ transform: 'translateY(0)', visibility: 'visible', opacity: 1, offset: 0 }),
style({ transform: 'translateY(30px)', visibility: 'hidden', opacity: 0, offset: 1 })
style({transform: 'translateY(0)', visibility: 'visible', opacity: 1, offset: 0}),
style({transform: 'translateY(30px)', visibility: 'hidden', opacity: 0, offset: 1})
]))
]),
transition('closed => opened', [
@@ -68,8 +69,8 @@ enum TabName {
visibility: 'visible'
}),
animate('200ms ease-out', keyframes([
style({ transform: 'translateY(30px)', opacity: 0, offset: 0 }),
style({ transform: 'translateY(0)', opacity: 1, offset: 1 })
style({transform: 'translateY(30px)', opacity: 0, offset: 0}),
style({transform: 'translateY(0)', opacity: 1, offset: 1})
]))
])
])
@@ -85,7 +86,7 @@ export class PopoverComponent implements OnChanges {
@Input() allowLayerDoubleTap: boolean;
@Output() cancel = new EventEmitter<any>();
@Output() remap = new EventEmitter<KeyAction>();
@Output() remap = new EventEmitter<KeyActionRemap>();
@ViewChild('tab') selectedTab: Tab;
@ViewChild('popover') popoverHost: ElementRef;
@@ -100,6 +101,9 @@ export class PopoverComponent implements OnChanges {
leftPosition: number = 0;
animationState: string;
remapOnAllKeymap: boolean;
remapOnAllLayer: boolean;
private readonly currentKeymap$ = new BehaviorSubject<Keymap>(undefined);
constructor(store: Store<AppState>) {
@@ -139,6 +143,8 @@ export class PopoverComponent implements OnChanges {
if (change['visible']) {
if (change['visible'].currentValue) {
this.animationState = 'opened';
this.remapOnAllKeymap = false;
this.remapOnAllLayer = false;
} else {
this.animationState = 'closed';
}
@@ -156,8 +162,11 @@ export class PopoverComponent implements OnChanges {
onRemapKey(): void {
if (this.keyActionValid) {
try {
const keyAction = this.selectedTab.toKeyAction();
this.remap.emit(keyAction);
this.remap.emit({
remapOnAllKeymap: this.remapOnAllKeymap,
remapOnAllLayer: this.remapOnAllLayer,
action: this.selectedTab.toKeyAction()
});
} catch (e) {
// TODO: show error dialog
console.error(e);

View File

@@ -42,6 +42,7 @@ import { KeymapActions } from '../../../store/actions';
import { PopoverComponent } from '../../popover';
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
import { ChangeKeymapDescription } from '../../../models/ChangeKeymapDescription';
import { KeyActionRemap } from '../../../models/key-action-remap';
interface NameValuePair {
name: string;
@@ -181,11 +182,15 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
this.currentLayer,
moduleId,
keyId,
keystrokeAction)
{
remapOnAllKeymap: false,
remapOnAllLayer: false,
action: keystrokeAction
})
);
}
onRemap(keyAction: KeyAction): void {
onRemap(keyAction: KeyActionRemap): void {
this.store.dispatch(
KeymapActions.saveKey(
this.keymap,

View File

@@ -9,15 +9,10 @@ export class TooltipDirective implements AfterContentInit, OnChanges {
@HostBinding('attr.data-placement') placement: string;
@Input('title') title: string;
@Input('html') html: boolean;
@Input() maxWidth: number;
private customTooltipTemplate = `
<div class="tooltip">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner"></div>
</div>
`;
constructor(private elementRef: ElementRef, private sanitizer: DomSanitizer) { }
constructor(private elementRef: ElementRef, private sanitizer: DomSanitizer) {
}
ngAfterContentInit() {
this.init();
@@ -33,7 +28,7 @@ export class TooltipDirective implements AfterContentInit, OnChanges {
(<any>jQuery(this.elementRef.nativeElement)).tooltip({
placement: this.placement,
html: this.html,
template: this.customTooltipTemplate,
template: this.getCustomTemplate(),
title: this.title
});
}
@@ -42,7 +37,7 @@ export class TooltipDirective implements AfterContentInit, OnChanges {
(<any>jQuery(this.elementRef.nativeElement)).tooltip({
placement: this.placement,
html: this.html,
template: this.customTooltipTemplate,
template: this.getCustomTemplate(),
title: this.title
});
@@ -50,4 +45,17 @@ export class TooltipDirective implements AfterContentInit, OnChanges {
.attr('title', this.title))
.tooltip('fixTitle');
}
private getCustomTemplate(): string {
let style = '';
if (this.maxWidth) {
style = `style="max-width: ${this.maxWidth}px;"`;
}
return `<div class="tooltip">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner" ${style}></div>
</div>`;
}
}

View File

@@ -0,0 +1,7 @@
import { KeyAction } from 'uhk-common';
export interface KeyActionRemap {
remapOnAllKeymap: boolean;
remapOnAllLayer: boolean;
action: KeyAction;
}

View File

@@ -1,7 +1,8 @@
import { Action } from '@ngrx/store';
import { KeyAction, Keymap, Macro } from 'uhk-common';
import { Keymap, Macro } from 'uhk-common';
import { UndoUserConfigData } from '../../models/undo-user-config-data';
import { ChangeKeymapDescription } from '../../models/ChangeKeymapDescription';
import { KeyActionRemap } from '../../models/key-action-remap';
export type KeymapAction =
KeymapActions.AddKeymapAction |
@@ -60,7 +61,7 @@ export namespace KeymapActions {
layer: number;
module: number;
key: number;
keyAction: KeyAction;
keyAction: KeyActionRemap;
}
};
@@ -172,7 +173,11 @@ export namespace KeymapActions {
};
}
export function saveKey(keymap: Keymap, layer: number, module: number, key: number, keyAction: KeyAction): SaveKeyAction {
export function saveKey(keymap: Keymap,
layer: number,
module: number,
key: number,
keyAction: KeyActionRemap): SaveKeyAction {
return {
type: KeymapActions.SAVE_KEY,
payload: {

View File

@@ -30,7 +30,11 @@ describe('user-configuration reducer', () => {
layer: 0,
module: 0,
key: 0,
keyAction: keystrokeAction
keyAction: {
remapOnAllKeymap: false,
remapOnAllLayer: false,
action: keystrokeAction
}
}
};
const result = reducer(state, saveKeyAction);
@@ -62,7 +66,12 @@ describe('user-configuration reducer', () => {
layer: 0,
module: 0,
key: 0,
keyAction: switchLayerAction
keyAction: {
remapOnAllKeymap: false,
remapOnAllLayer: false,
action: switchLayerAction
}
}
};
const result = reducer(state, saveKeyAction);
@@ -243,7 +252,12 @@ describe('user-configuration reducer', () => {
layer: 0,
module: 0,
key: 2,
keyAction: switchLayerAction
keyAction: {
remapOnAllKeymap: false,
remapOnAllLayer: false,
action: switchLayerAction
}
}
};
const result = reducer(state, saveKeyAction);

View File

@@ -68,7 +68,7 @@ export function reducer(state = initialState, action: Action & { payload?: any }
break;
}
const newKeymap = Object.assign(new Keymap(), keymapToRename, { name });
const newKeymap = Object.assign(new Keymap(), keymapToRename, {name});
changedUserConfiguration.keymaps = insertItemInNameOrder(
state.keymaps,
@@ -139,38 +139,46 @@ export function reducer(state = initialState, action: Action & { payload?: any }
const keyIndex: number = action.payload.key;
const layerIndex: number = action.payload.layer;
const moduleIndex: number = action.payload.module;
const newKeyAction = KeyActionHelper.createKeyAction(action.payload.keyAction);
const newKeymap: Keymap = Object.assign(new Keymap(), action.payload.keymap);
newKeymap.layers = newKeymap.layers.slice();
newKeymap.layers = newKeymap.layers.map((layer, index) => {
const newLayer = Object.assign(new Layer(), layer);
if (index === layerIndex) {
setKeyActionToLayer(newLayer, moduleIndex, keyIndex, newKeyAction);
}
// If the key action is a SwitchLayerAction then set the same SwitchLayerAction
// on the target layer
else if (newKeyAction instanceof SwitchLayerAction) {
if (index - 1 === newKeyAction.layer) {
const clonedAction = KeyActionHelper.createKeyAction(action.payload.keyAction);
setKeyActionToLayer(newLayer, moduleIndex, keyIndex, clonedAction);
} else {
setKeyActionToLayer(newLayer, moduleIndex, keyIndex, null);
}
}
return newLayer;
});
const keyActionRemap = action.payload.keyAction;
const newKeyAction = keyActionRemap.action;
const newKeymap: Keymap = action.payload.keymap;
changedUserConfiguration.keymaps = state.keymaps.map(keymap => {
if (keymap.abbreviation === newKeymap.abbreviation) {
keymap = newKeymap;
if (keyActionRemap.remapOnAllKeymap || keymap.abbreviation === newKeymap.abbreviation) {
keymap = new Keymap(keymap);
keymap.layers = keymap.layers.map((layer, index) => {
if (keyActionRemap.remapOnAllLayer || index === layerIndex) {
layer = new Layer(layer);
const clonedAction = KeyActionHelper.createKeyAction(newKeyAction);
const isSwitchLayerAction = newKeyAction instanceof SwitchLayerAction;
// If the key action is a SwitchLayerAction then set the same SwitchLayerAction
// on the target layer and remove SwitchLayerAction from other layers
if (isSwitchLayerAction) {
if (index - 1 === (newKeyAction as SwitchLayerAction).layer) {
setKeyActionToLayer(layer, moduleIndex, keyIndex, clonedAction);
} else {
const actionOnLayer = layer.modules[moduleIndex].keyActions[keyIndex];
if (actionOnLayer && actionOnLayer instanceof SwitchLayerAction) {
setKeyActionToLayer(layer, moduleIndex, keyIndex, null);
}
}
}
else {
setKeyActionToLayer(layer, moduleIndex, keyIndex, clonedAction);
}
}
return layer;
});
}
return keymap;
});
break;
}
case KeymapActions.CHECK_MACRO:
changedUserConfiguration.keymaps = state.keymaps.map(keymap => {
keymap = Object.assign(new Keymap(), keymap);

View File

@@ -73,6 +73,11 @@ ul.btn-list {
}
}
ul.no-indent {
padding-left: 1em;
margin-bottom: 0;
}
.h1, .h2, .h3, h1, h2, h3 {
margin-top: 10px;
}
@@ -178,3 +183,7 @@ pre {
.ok-button {
min-width: 100px;
}
.d-inline-block {
display: inline-block;
}