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:
committed by
László Monda
parent
83b9f0d1e9
commit
f34cb2df56
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>`;
|
||||
}
|
||||
}
|
||||
|
||||
7
packages/uhk-web/src/app/models/key-action-remap.ts
Normal file
7
packages/uhk-web/src/app/models/key-action-remap.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { KeyAction } from 'uhk-common';
|
||||
|
||||
export interface KeyActionRemap {
|
||||
remapOnAllKeymap: boolean;
|
||||
remapOnAllLayer: boolean;
|
||||
action: KeyAction;
|
||||
}
|
||||
@@ -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: {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user