fix web break introduced in 38f130071 fix(linux): enable mouse side buttons in remote sessions (#14848)

This commit is contained in:
rustdesk
2026-04-29 17:37:46 +08:00
parent d4a1430c27
commit 68e07ed7eb
10 changed files with 266 additions and 10 deletions

View File

@@ -10,12 +10,14 @@ import 'package:flutter_hbb/common/widgets/audio_input.dart';
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
import 'package:flutter_hbb/desktop/pages/desktop_keyboard_shortcuts_page.dart';
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart';
import 'package:flutter_hbb/mobile/widgets/dialog.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/printer_model.dart';
import 'package:flutter_hbb/models/server_model.dart';
import 'package:flutter_hbb/models/shortcut_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:flutter_hbb/plugin/manager.dart';
import 'package:flutter_hbb/plugin/widgets/desktop_settings.dart';
@@ -421,11 +423,49 @@ class _GeneralState extends State<_General> {
if (!isWeb) audio(context),
if (!isWeb) record(context),
if (!isWeb) WaylandCard(),
other()
other(),
if (!bind.isIncomingOnly()) keyboardShortcuts(),
],
).marginOnly(bottom: _kListViewBottomMargin);
}
Widget keyboardShortcuts() {
// The bindings JSON (LocalConfig key `keyboard-shortcuts`) holds three
// flags + the bindings list: {enabled, pass_through, bindings}. When the
// master is off, the pass-through toggle and the Configure entry are
// hidden — both are meaningless without an active matcher.
return StatefulBuilder(builder: (context, setLocalState) {
final enabled = ShortcutModel.isEnabled();
return _Card(title: 'Keyboard Shortcuts', children: [
_OptionCheckBox(
context,
'Enable keyboard shortcuts in remote session',
kShortcutLocalConfigKey,
isServer: false,
optGetter: ShortcutModel.isEnabled,
optSetter: (_, v) async {
await ShortcutModel.setEnabled(v);
setLocalState(() {});
},
),
if (enabled) ...[
_OptionCheckBox(
context,
'Pass-through to remote',
kShortcutLocalConfigKey,
isServer: false,
optGetter: ShortcutModel.isPassThrough,
optSetter: (_, v) async {
await ShortcutModel.setPassThrough(v);
setLocalState(() {});
},
),
_ShortcutsConfigureRow(),
],
]);
});
}
Widget theme() {
final current = MyTheme.getThemeModePreference().toShortString();
onChanged(String value) async {
@@ -2946,6 +2986,37 @@ class _CountDownButtonState extends State<_CountDownButton> {
}
}
// Tappable row that pushes the shortcut configuration page.
class _ShortcutsConfigureRow extends StatelessWidget {
// ignore: unused_element
const _ShortcutsConfigureRow({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => const DesktopKeyboardShortcutsPage(),
));
},
child: Row(
children: [
Expanded(
child: Text(translate('Configure shortcuts...')),
),
Icon(Icons.arrow_forward_ios,
size: 16, color: disabledTextColor(context, true))
.marginOnly(right: 4),
],
).marginOnly(
left: _kCheckBoxLeftMargin,
top: 6,
bottom: 6,
),
);
}
}
//#endregion
//#region dialogs

View File

@@ -17,6 +17,7 @@ import '../../common/widgets/toolbar.dart';
import '../../models/model.dart';
import '../../models/input_model.dart';
import '../../models/platform_model.dart';
import '../../models/shortcut_model.dart';
import '../../common/shared_state.dart';
import '../../utils/image.dart';
import '../widgets/remote_toolbar.dart';
@@ -126,6 +127,20 @@ class _RemotePageState extends State<RemotePage>
_ffi.ffiModel.pi.platform, _ffi.dialogManager);
_ffi.recordingModel
.updateStatus(bind.sessionGetIsRecording(sessionId: _ffi.sessionId));
// Seed shortcut action callbacks once the session is ready, so that
// global keyboard shortcuts work even if the user never opens the
// toolbar menu. The returned list is intentionally discarded — the
// side effect of registering callbacks (inside toolbarControls) is
// what we want here.
if (mounted) {
toolbarControls(context, widget.id, _ffi);
// Register the default-bound actions that `toolbarControls` doesn't
// own (fullscreen, switch display, switch tab). Done in addition,
// not instead of, the toolbar registration above.
registerSessionShortcutActions(_ffi,
tabController: widget.tabController,
toolbarState: widget.toolbarState);
}
});
_ffi.canvasModel.initializeEdgeScrollFallback(this);
_ffi.start(

View File

@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/common/widgets/audio_input.dart';
import 'package:flutter_hbb/common/widgets/dialog.dart';
import 'package:flutter_hbb/common/widgets/keyboard_shortcuts/display.dart';
import 'package:flutter_hbb/common/widgets/toolbar.dart';
import 'package:flutter_hbb/models/chat_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
@@ -763,8 +764,31 @@ class _ControlMenu extends StatelessWidget {
if (e.divider) {
return Divider();
} else {
final hint = e.actionId == null
? null
: ShortcutDisplay.formatFor(e.actionId!);
final child = hint == null
? e.child
: Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(child: e.child),
Padding(
padding: const EdgeInsets.only(left: 16),
child: Text(
hint,
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(
color: Theme.of(context).hintColor,
),
),
),
],
);
return MenuButton(
child: e.child,
child: child,
onPressed: e.onPressed,
ffi: ffi,
trailingIcon: e.trailingIcon);