mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-05-08 15:18:13 +03:00
fix(keyboard): shortcuts, harden config and callback lifecycle
Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import '../../../consts.dart';
|
||||
import '../../../models/platform_model.dart';
|
||||
import 'shortcut_utils.dart';
|
||||
|
||||
/// Read the bindings JSON and produce a human-readable shortcut string for
|
||||
/// `actionId`, formatted for the current OS. Returns null if unbound, or —
|
||||
@@ -44,7 +45,7 @@ class ShortcutDisplay {
|
||||
// would press it expecting the local action and instead the keys would go
|
||||
// to the remote. Treat as unbound for display purposes.
|
||||
if (requireEnabled && parsed['pass_through'] == true) return null;
|
||||
final list = (parsed['bindings'] as List? ?? []).cast<Map<String, dynamic>>();
|
||||
final list = shortcutBindingMapsFrom(parsed['bindings']);
|
||||
final found = list.firstWhere(
|
||||
(b) => b['action'] == actionId,
|
||||
orElse: () => {},
|
||||
|
||||
@@ -109,9 +109,7 @@ class KeyboardShortcutsPageBodyState extends State<KeyboardShortcutsPageBody> {
|
||||
String? clearActionId,
|
||||
}) async {
|
||||
final json = _readJson();
|
||||
final list = ((json['bindings'] as List?) ?? <dynamic>[])
|
||||
.cast<Map<String, dynamic>>()
|
||||
.toList();
|
||||
final list = shortcutBindingMapsFrom(json['bindings']);
|
||||
list.removeWhere((b) {
|
||||
final a = b['action'];
|
||||
return a == actionId || (clearActionId != null && a == clearActionId);
|
||||
@@ -172,8 +170,7 @@ class KeyboardShortcutsPageBodyState extends State<KeyboardShortcutsPageBody> {
|
||||
|
||||
Future<void> _onEdit(KeyboardShortcutActionEntry entry) async {
|
||||
final json = _readJson();
|
||||
final bindings = ((json['bindings'] as List?) ?? <dynamic>[])
|
||||
.cast<Map<String, dynamic>>();
|
||||
final bindings = shortcutBindingMapsFrom(json['bindings']);
|
||||
final result = await showRecordingDialog(
|
||||
context: context,
|
||||
actionId: entry.id,
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
//
|
||||
// Modal dialog used by the Keyboard Shortcuts settings page to capture a new
|
||||
// key combination for a given action. The dialog listens for KeyDown events,
|
||||
// extracts the modifier set + non-modifier key, validates against the
|
||||
// "must include Ctrl+Alt+Shift (Cmd+Option+Shift on macOS)" rule, and reports
|
||||
// any conflict with another already-bound action.
|
||||
// extracts the modifier set + non-modifier key, validates that at least one
|
||||
// modifier is present, and reports any conflict with another already-bound
|
||||
// action.
|
||||
//
|
||||
// On Save, returns the new binding map ({action, mods, key}) plus the
|
||||
// optional id of the action whose binding should be cleared (the conflict
|
||||
@@ -147,8 +147,7 @@ class _RecordingDialogState extends State<_RecordingDialog> {
|
||||
final otherAction = b['action'] as String?;
|
||||
if (otherAction == null || otherAction == widget.actionId) continue;
|
||||
final otherKey = b['key'] as String?;
|
||||
final otherMods =
|
||||
((b['mods'] as List?) ?? const []).cast<String>().toSet();
|
||||
final otherMods = shortcutModSetFrom(b['mods']);
|
||||
if (otherKey == _key &&
|
||||
otherMods.length == _mods.length &&
|
||||
otherMods.containsAll(_mods)) {
|
||||
|
||||
@@ -229,6 +229,10 @@ List<KeyboardShortcutActionGroup> filterKeyboardShortcutActionGroupsForPlatform(
|
||||
if (!cap.includeScreenshotShortcut && id == kShortcutActionScreenshot) {
|
||||
return false;
|
||||
}
|
||||
if (!cap.includeScreenshotShortcut &&
|
||||
id == kShortcutActionToggleRelativeMouseMode) {
|
||||
return false;
|
||||
}
|
||||
if (!cap.includeTabShortcuts && isSwitchTabShortcutAction(id)) return false;
|
||||
if (!cap.includeToolbarShortcut && id == kShortcutActionToggleToolbar) {
|
||||
return false;
|
||||
|
||||
@@ -11,6 +11,30 @@ List<String> canonicalShortcutModsForSave(Set<String> mods) {
|
||||
];
|
||||
}
|
||||
|
||||
List<Map<String, dynamic>> shortcutBindingMapsFrom(dynamic rawBindings) {
|
||||
if (rawBindings is! Iterable) return <Map<String, dynamic>>[];
|
||||
final bindings = <Map<String, dynamic>>[];
|
||||
for (final raw in rawBindings) {
|
||||
if (raw is! Map) continue;
|
||||
final binding = <String, dynamic>{};
|
||||
for (final entry in raw.entries) {
|
||||
final key = entry.key;
|
||||
if (key is String) {
|
||||
binding[key] = entry.value;
|
||||
}
|
||||
}
|
||||
if (binding.isNotEmpty) {
|
||||
bindings.add(binding);
|
||||
}
|
||||
}
|
||||
return bindings;
|
||||
}
|
||||
|
||||
Set<String> shortcutModSetFrom(dynamic rawMods) {
|
||||
if (rawMods is! Iterable) return <String>{};
|
||||
return rawMods.whereType<String>().toSet();
|
||||
}
|
||||
|
||||
bool isSwitchTabShortcutAction(String? actionId) {
|
||||
return actionId == kShortcutActionSwitchTabNext ||
|
||||
actionId == kShortcutActionSwitchTabPrev;
|
||||
@@ -144,9 +168,7 @@ List<Map<String, dynamic>> filterDefaultBindingsForPlatform(
|
||||
ShortcutPlatformCapabilities cap,
|
||||
) {
|
||||
final filtered = <Map<String, dynamic>>[];
|
||||
for (final raw in bindings) {
|
||||
if (raw is! Map) continue;
|
||||
final binding = Map<String, dynamic>.from(raw);
|
||||
for (final binding in shortcutBindingMapsFrom(bindings)) {
|
||||
final action = binding['action'] as String?;
|
||||
if (!cap.includeFullscreenShortcut &&
|
||||
action == kShortcutActionToggleFullscreen) {
|
||||
@@ -155,6 +177,10 @@ List<Map<String, dynamic>> filterDefaultBindingsForPlatform(
|
||||
if (!cap.includeScreenshotShortcut && action == kShortcutActionScreenshot) {
|
||||
continue;
|
||||
}
|
||||
if (!cap.includeScreenshotShortcut &&
|
||||
action == kShortcutActionToggleRelativeMouseMode) {
|
||||
continue;
|
||||
}
|
||||
if (!cap.includeTabShortcuts && isSwitchTabShortcutAction(action)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user