From 228bc7e586462fd73785d5de65346d2897cf64ac Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 23 Sep 2026 13:50:14 +0800 Subject: [PATCH] fix(keyboard): shortcuts, keep an empty binding list on re-enable setEnabled seeded the defaults whenever the binding list was empty, so a user who cleared every binding got all defaults back after disable and enable. Seed only when the config has never held a bindings list. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D --- .../widgets/keyboard_shortcuts/shortcut_utils.dart | 6 ++++++ flutter/lib/models/shortcut_model.dart | 9 ++++----- flutter/test/keyboard_shortcuts_test.dart | 13 +++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/flutter/lib/common/widgets/keyboard_shortcuts/shortcut_utils.dart b/flutter/lib/common/widgets/keyboard_shortcuts/shortcut_utils.dart index ba0481a69..a67538afd 100644 --- a/flutter/lib/common/widgets/keyboard_shortcuts/shortcut_utils.dart +++ b/flutter/lib/common/widgets/keyboard_shortcuts/shortcut_utils.dart @@ -35,6 +35,12 @@ Set shortcutModSetFrom(dynamic rawMods) { return rawMods.whereType().toSet(); } +/// Whether enabling shortcuts should seed the default bindings. Only a config +/// that has never held a bindings list is seeded; an empty list means the +/// user cleared every binding and must stay empty. +bool shouldSeedDefaultShortcutBindings(Map config) => + !config.containsKey('bindings'); + bool isSwitchTabShortcutAction(String? actionId) { return actionId == kShortcutActionSwitchTabNext || actionId == kShortcutActionSwitchTabPrev; diff --git a/flutter/lib/models/shortcut_model.dart b/flutter/lib/models/shortcut_model.dart index 0a107ef5a..e8d17fed8 100644 --- a/flutter/lib/models/shortcut_model.dart +++ b/flutter/lib/models/shortcut_model.dart @@ -135,7 +135,7 @@ class ShortcutModel { /// Flip the master `enabled` flag and persist. On the first enable we seed /// the default bindings so common combos work out of the box; otherwise we - /// preserve whatever the user already has. Refreshes the matcher cache so + /// preserve whatever the user already has, an empty list included. Refreshes the matcher cache so /// the change takes effect immediately (Rust on native, JS via the bridge /// on Web). static Future setEnabled(bool v) async { @@ -149,14 +149,13 @@ class ShortcutModel { } } json['enabled'] = v; - final list = shortcutBindingMapsFrom(json['bindings']); - if (v && list.isEmpty) { + if (v && shouldSeedDefaultShortcutBindings(json)) { json['bindings'] = filterDefaultBindingsForPlatform( jsonDecode(bind.mainGetDefaultKeyboardShortcuts()) as List, currentPlatformCapabilities(), ); - } else { - json['bindings'] = list; + } else if (json.containsKey('bindings')) { + json['bindings'] = shortcutBindingMapsFrom(json['bindings']); } await bind.mainSetLocalOption( key: kShortcutLocalConfigKey, value: jsonEncode(json)); diff --git a/flutter/test/keyboard_shortcuts_test.dart b/flutter/test/keyboard_shortcuts_test.dart index e5c15154b..f1f7d3a69 100644 --- a/flutter/test/keyboard_shortcuts_test.dart +++ b/flutter/test/keyboard_shortcuts_test.dart @@ -397,6 +397,19 @@ void main() { expect(physicalKeyName(PhysicalKeyboardKey.numpad1), isNull); }); + test('enable seeds defaults only for a config without bindings', () { + expect(shouldSeedDefaultShortcutBindings({}), isTrue); + expect( + shouldSeedDefaultShortcutBindings( + {'enabled': false, 'pass_through': true}), + isTrue); + // The user cleared every binding: re-enabling must not bring defaults back. + expect( + shouldSeedDefaultShortcutBindings( + {'enabled': false, 'bindings': []}), + isFalse); + }); + test('non-US layouts record and match the physical key', () { // AZERTY: the key labelled "A" sits where US QWERTY has Q. The native // matcher only sees the physical position (USB HID usage), so the