Browse Source

Mark duplicate hotkeys

Benjamin Brandmeier 3 năm trước cách đây
mục cha
commit
72bc58332d

+ 5 - 0
tabby-core/src/api/hotkeyProvider.ts

@@ -3,6 +3,11 @@ export interface HotkeyDescription {
     name: string
 }
 
+export interface Hotkey {
+    strokes: string[] | string; // may be a sequence of strokes
+    isDuplicate: boolean;
+}
+
 /**
  * Extend to provide your own hotkeys. A corresponding [[ConfigProvider]]
  * must also provide the `hotkeys.foo` config options with the default values

+ 10 - 0
tabby-core/src/theme.paper.scss

@@ -256,6 +256,11 @@ multi-hotkey-input {
         }
     }
 
+    .item:has(.duplicate) {
+        background-color: theme-color('danger');
+        border: 1px solid theme-color('danger');
+    }
+
     .add {
         color: #777;
         padding: 4px 10px 0;
@@ -265,6 +270,11 @@ multi-hotkey-input {
         &:hover { background: darken($body-bg2, 5%); }
         &:active { background: darken($body-bg2, 15%); }
     }
+
+    .add:has(.duplicate), .item:has(.duplicate) .body, .item:has(.duplicate) .remove {
+        &:hover { background: darken(theme-color('danger'), 5%); }
+        &:active { background: darken(theme-color('danger'), 15%); }
+    }
 }
 
 hotkey-input-modal {

+ 10 - 0
tabby-core/src/theme.scss

@@ -162,6 +162,11 @@ multi-hotkey-input {
         }
     }
 
+    .item:has(.duplicate) {
+        background-color: theme-color('danger');
+        border: 1px solid theme-color('danger');
+    }
+
     .add {
         color: #777;
         padding: 4px 10px 0;
@@ -171,6 +176,11 @@ multi-hotkey-input {
         &:hover { background: darken($body-bg2, 5%); }
         &:active { background: darken($body-bg2, 15%); }
     }
+
+    .add:has(.duplicate), .item:has(.duplicate) .body, .item:has(.duplicate) .remove {
+        &:hover { background: darken(theme-color('danger'), 5%); }
+        &:active { background: darken(theme-color('danger'), 15%); }
+    }
 }
 
 hotkey-input-modal {

+ 2 - 2
tabby-settings/src/components/hotkeySettingsTab.component.pug

@@ -14,6 +14,6 @@ h3.mb-3(translate) Hotkeys
                 span.ml-2.text-muted ({{hotkey.id}})
             .col-4.pr-5
                 multi-hotkey-input(
-                    [model]='getHotkey(hotkey.id) || []',
-                    (modelChange)='setHotkey(hotkey.id, $event)'
+                    [hotkeys]='getHotkeys(hotkey.id) || []',
+                    (hotkeysChange)='setHotkeys(hotkey.id, $event)'
                 )

+ 23 - 6
tabby-settings/src/components/hotkeySettingsTab.component.ts

@@ -7,6 +7,7 @@ import {
     HotkeysService,
     HostAppService,
 } from 'tabby-core'
+import { Hotkey } from 'tabby-core/src/api/hotkeyProvider'
 
 _('Search hotkeys')
 
@@ -30,28 +31,44 @@ export class HotkeySettingsTabComponent {
         })
     }
 
-    getHotkey (id: string) {
+    getHotkeys (id: string): Hotkey[] {
         let ptr = this.config.store.hotkeys
         for (const token of id.split(/\./g)) {
             ptr = ptr[token]
         }
-        return ptr
+        return (ptr || []).map(hotkey => this.detectDuplicates(hotkey))
     }
 
-    setHotkey (id: string, value) {
+    setHotkeys (id: string, hotkeys: Hotkey[]) {
         let ptr = this.config.store
         let prop = 'hotkeys'
         for (const token of id.split(/\./g)) {
             ptr = ptr[prop]
             prop = token
         }
-        ptr[prop] = value
+        ptr[prop] = hotkeys.map(hotkey =>
+            hotkey.strokes.length === 1 && Array.isArray(hotkey.strokes)
+                ? hotkey.strokes[0]
+                : hotkey.strokes,
+        )
         this.config.save()
     }
 
     hotkeyFilterFn (hotkey: HotkeyDescription, query: string): boolean {
-        // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
-        const s = hotkey.name + hotkey.id + (this.getHotkey(hotkey.id) || []).toString()
+        const s = hotkey.name + hotkey.id + this.getHotkeys(hotkey.id).map(h => h.strokes).toString()
         return s.toLowerCase().includes(query.toLowerCase())
     }
+
+    private detectDuplicates (strokes: string[] | string): Hotkey {
+        const allHotkeys = Object
+            .values(this.config.store.hotkeys)
+            .filter((value: unknown) => Array.isArray(value))
+            .flat()
+
+        const isDuplicate = allHotkeys
+            .filter(hotkey => JSON.stringify(hotkey) === JSON.stringify(strokes))
+            .length > 1
+
+        return { strokes, isDuplicate }
+    }
 }

+ 6 - 4
tabby-settings/src/components/multiHotkeyInput.component.pug

@@ -1,6 +1,8 @@
-.item(*ngFor='let item of model')
-    .body((click)='editItem(item)')
-        .stroke(*ngFor='let stroke of item') {{stroke}}
-    .remove((click)='removeItem(item)') ×
+.item(*ngFor='let hotkey of hotkeys')
+    .body((click)='editItem(hotkey)')
+        .stroke(*ngFor='let stroke of hotkey.strokes')
+            span(*ngIf='!hotkey.isDuplicate', translate) {{stroke}}
+            span.duplicate(*ngIf='hotkey.isDuplicate', translate) {{stroke}}
+    .remove((click)='removeItem(hotkey)') ×
 
 .add((click)='addItem()', translate) Add...

+ 21 - 16
tabby-settings/src/components/multiHotkeyInput.component.ts

@@ -1,6 +1,7 @@
 import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core'
 import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 import { HotkeyInputModalComponent } from './hotkeyInputModal.component'
+import { Hotkey } from 'tabby-core/src/api/hotkeyProvider'
 
 /** @hidden */
 @Component({
@@ -10,37 +11,41 @@ import { HotkeyInputModalComponent } from './hotkeyInputModal.component'
     changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class MultiHotkeyInputComponent {
-    @Input() model: string[][] = []
-    @Output() modelChange = new EventEmitter()
+    @Input() hotkeys: Hotkey[] = []
+    @Output() hotkeysChange = new EventEmitter()
 
     constructor (
         private ngbModal: NgbModal,
     ) { }
 
     ngOnChanges (): void {
-        if (typeof this.model === 'string') {
-            this.model = [this.model]
-        }
-        this.model = this.model.map(item => typeof item === 'string' ? [item] : item)
+        this.hotkeys = this.hotkeys.map(hotkey => typeof hotkey.strokes === 'string' ? { ...hotkey, strokes: [hotkey.strokes] } : hotkey)
     }
 
-    editItem (item: string[]): void {
-        this.ngbModal.open(HotkeyInputModalComponent).result.then((value: string[]) => {
-            this.model[this.model.findIndex(x => x === item)] = value
-            this.model = this.model.slice()
-            this.modelChange.emit(this.model)
+    editItem (item: Hotkey): void {
+        this.ngbModal.open(HotkeyInputModalComponent).result.then((newStrokes: string[]) => {
+            this.hotkeys.find(hotkey => this.isEqual(hotkey, item))!.strokes = newStrokes
+            this.storeUpdatedHotkeys()
         })
     }
 
     addItem (): void {
         this.ngbModal.open(HotkeyInputModalComponent).result.then((value: string[]) => {
-            this.model = this.model.concat([value])
-            this.modelChange.emit(this.model)
+            this.hotkeys.push({ strokes: value, isDuplicate: false })
+            this.storeUpdatedHotkeys()
         })
     }
 
-    removeItem (item: string[]): void {
-        this.model = this.model.filter(x => x !== item)
-        this.modelChange.emit(this.model)
+    removeItem (item: Hotkey): void {
+        this.hotkeys = this.hotkeys.filter(x => x !== item)
+        this.storeUpdatedHotkeys()
+    }
+
+    private storeUpdatedHotkeys () {
+        this.hotkeysChange.emit(this.hotkeys)
+    }
+
+    private isEqual (h: Hotkey, item: Hotkey) {
+        return JSON.stringify(h.strokes) === JSON.stringify(item.strokes)
     }
 }