mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-05-09 07:38:09 +03:00
Revert "fix web break introduced in 38f130071 fix(linux): enable mouse side buttons in remote sessions (#14848)" (#14973)
This reverts commit d5d0b01266.
This commit is contained in:
@@ -16,43 +16,16 @@ import 'package:get/get.dart';
|
|||||||
|
|
||||||
bool isEditOsPassword = false;
|
bool isEditOsPassword = false;
|
||||||
|
|
||||||
/// Action IDs that `toolbarControls` is the sole registrar for. Each call to
|
|
||||||
/// `toolbarControls` (e.g. opening the toolbar menu after a permission was
|
|
||||||
/// revoked or a state changed) wipes these so a previously-registered closure
|
|
||||||
/// can't outlive the menu entry that owns it. The for-loop at the bottom of
|
|
||||||
/// `toolbarControls` then re-registers whichever entries are still present in
|
|
||||||
/// the rebuilt menu list.
|
|
||||||
///
|
|
||||||
/// Actions registered elsewhere — `registerSessionShortcutActions` on desktop
|
|
||||||
/// owns toggle_recording, fullscreen, switch_display, switch_tab, close_tab,
|
|
||||||
/// toggle_toolbar — MUST NOT appear here, otherwise this list would clobber
|
|
||||||
/// their registration on every menu rebuild.
|
|
||||||
///
|
|
||||||
/// `kShortcutActionToggleRecording` is platform-conditional (mobile-only —
|
|
||||||
/// see the `!(isDesktop || isWeb)` guard in `toolbarControls`). It is handled
|
|
||||||
/// separately in the unregister pass rather than appearing in this const list.
|
|
||||||
const _kToolbarOwnedActionIds = <String>[
|
|
||||||
kShortcutActionSendCtrlAltDel,
|
|
||||||
kShortcutActionRestartRemote,
|
|
||||||
kShortcutActionInsertLock,
|
|
||||||
kShortcutActionToggleBlockInput,
|
|
||||||
kShortcutActionSwitchSides,
|
|
||||||
kShortcutActionRefresh,
|
|
||||||
kShortcutActionScreenshot,
|
|
||||||
];
|
|
||||||
|
|
||||||
class TTextMenu {
|
class TTextMenu {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
Widget? trailingIcon;
|
Widget? trailingIcon;
|
||||||
bool divider;
|
bool divider;
|
||||||
final String? actionId;
|
|
||||||
TTextMenu(
|
TTextMenu(
|
||||||
{required this.child,
|
{required this.child,
|
||||||
required this.onPressed,
|
required this.onPressed,
|
||||||
this.trailingIcon,
|
this.trailingIcon,
|
||||||
this.divider = false,
|
this.divider = false});
|
||||||
this.actionId});
|
|
||||||
|
|
||||||
Widget getChild() {
|
Widget getChild() {
|
||||||
if (trailingIcon != null) {
|
if (trailingIcon != null) {
|
||||||
@@ -121,20 +94,6 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
|||||||
final sessionId = ffi.sessionId;
|
final sessionId = ffi.sessionId;
|
||||||
final isDefaultConn = ffi.connType == ConnType.defaultConn;
|
final isDefaultConn = ffi.connType == ConnType.defaultConn;
|
||||||
|
|
||||||
// Wipe everything `toolbarControls` could have registered last call so
|
|
||||||
// stale closures (e.g. for a menu entry whose permission has since been
|
|
||||||
// revoked) don't outlive the menu rebuild. See _kToolbarOwnedActionIds.
|
|
||||||
for (final actionId in _kToolbarOwnedActionIds) {
|
|
||||||
ffi.shortcutModel.unregister(actionId);
|
|
||||||
}
|
|
||||||
// toggle_recording is platform-conditional — toolbarControls only builds
|
|
||||||
// the menu entry on `!(isDesktop || isWeb)`. On desktop the registration
|
|
||||||
// is owned by `registerSessionShortcutActions` and must NOT be touched
|
|
||||||
// here. See the recording menu entry below.
|
|
||||||
if (!(isDesktop || isWeb)) {
|
|
||||||
ffi.shortcutModel.unregister(kShortcutActionToggleRecording);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<TTextMenu> v = [];
|
List<TTextMenu> v = [];
|
||||||
// elevation
|
// elevation
|
||||||
if (isDefaultConn &&
|
if (isDefaultConn &&
|
||||||
@@ -270,8 +229,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
|||||||
v.add(
|
v.add(
|
||||||
TTextMenu(
|
TTextMenu(
|
||||||
child: Text('${translate("Insert Ctrl + Alt + Del")}'),
|
child: Text('${translate("Insert Ctrl + Alt + Del")}'),
|
||||||
onPressed: () => bind.sessionCtrlAltDel(sessionId: sessionId),
|
onPressed: () => bind.sessionCtrlAltDel(sessionId: sessionId)),
|
||||||
actionId: kShortcutActionSendCtrlAltDel),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// restart
|
// restart
|
||||||
@@ -284,8 +242,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
|||||||
TTextMenu(
|
TTextMenu(
|
||||||
child: Text(translate('Restart remote device')),
|
child: Text(translate('Restart remote device')),
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
showRestartRemoteDevice(pi, id, sessionId, ffi.dialogManager),
|
showRestartRemoteDevice(pi, id, sessionId, ffi.dialogManager)),
|
||||||
actionId: kShortcutActionRestartRemote),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// insertLock
|
// insertLock
|
||||||
@@ -293,8 +250,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
|||||||
v.add(
|
v.add(
|
||||||
TTextMenu(
|
TTextMenu(
|
||||||
child: Text(translate('Insert Lock')),
|
child: Text(translate('Insert Lock')),
|
||||||
onPressed: () => bind.sessionLockScreen(sessionId: sessionId),
|
onPressed: () => bind.sessionLockScreen(sessionId: sessionId)),
|
||||||
actionId: kShortcutActionInsertLock),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// blockUserInput
|
// blockUserInput
|
||||||
@@ -312,8 +268,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
|||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
value: '${blockInput.value ? 'un' : ''}block-input');
|
value: '${blockInput.value ? 'un' : ''}block-input');
|
||||||
blockInput.value = !blockInput.value;
|
blockInput.value = !blockInput.value;
|
||||||
},
|
}));
|
||||||
actionId: kShortcutActionToggleBlockInput));
|
|
||||||
}
|
}
|
||||||
// switchSides
|
// switchSides
|
||||||
if (isDefaultConn &&
|
if (isDefaultConn &&
|
||||||
@@ -325,15 +280,13 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
|||||||
v.add(TTextMenu(
|
v.add(TTextMenu(
|
||||||
child: Text(translate('Switch Sides')),
|
child: Text(translate('Switch Sides')),
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
showConfirmSwitchSidesDialog(sessionId, id, ffi.dialogManager),
|
showConfirmSwitchSidesDialog(sessionId, id, ffi.dialogManager)));
|
||||||
actionId: kShortcutActionSwitchSides));
|
|
||||||
}
|
}
|
||||||
// refresh
|
// refresh
|
||||||
if (pi.version.isNotEmpty) {
|
if (pi.version.isNotEmpty) {
|
||||||
v.add(TTextMenu(
|
v.add(TTextMenu(
|
||||||
child: Text(translate('Refresh')),
|
child: Text(translate('Refresh')),
|
||||||
onPressed: () => sessionRefreshVideo(sessionId, pi),
|
onPressed: () => sessionRefreshVideo(sessionId, pi),
|
||||||
actionId: kShortcutActionRefresh,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
// record
|
// record
|
||||||
@@ -355,8 +308,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onPressed: () => ffi.recordingModel.toggle(),
|
onPressed: () => ffi.recordingModel.toggle()));
|
||||||
actionId: kShortcutActionToggleRecording));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// to-do:
|
// to-do:
|
||||||
@@ -373,14 +325,6 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
|||||||
onPressed: ffi.ffiModel.timerScreenshot != null
|
onPressed: ffi.ffiModel.timerScreenshot != null
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
// Live cooldown check: the menu rebuilds onPressed=null
|
|
||||||
// whenever toolbarControls runs and finds timerScreenshot
|
|
||||||
// != null, but the keyboard-shortcut callback holds onto
|
|
||||||
// the originally-enabled closure across cooldown periods
|
|
||||||
// (toolbarControls only re-runs on menu open). Without
|
|
||||||
// this guard the second shortcut press during the 30s
|
|
||||||
// cooldown still fires sessionTakeScreenshot.
|
|
||||||
if (ffi.ffiModel.timerScreenshot != null) return;
|
|
||||||
if (pi.currentDisplay == kAllDisplayValue) {
|
if (pi.currentDisplay == kAllDisplayValue) {
|
||||||
msgBox(
|
msgBox(
|
||||||
sessionId,
|
sessionId,
|
||||||
@@ -398,7 +342,6 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actionId: kShortcutActionScreenshot,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -409,28 +352,6 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
|||||||
onPressed: () => onCopyFingerprint(FingerprintState.find(id).value),
|
onPressed: () => onCopyFingerprint(FingerprintState.find(id).value),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
// Register tagged callbacks with the shortcut model so global keyboard
|
|
||||||
// shortcuts can dispatch the same actions as the toolbar menu items.
|
|
||||||
//
|
|
||||||
// For action IDs already cleared at the top of this function (i.e. those
|
|
||||||
// in [_kToolbarOwnedActionIds] plus the conditional toggle_recording),
|
|
||||||
// the `else` branch below is a redundant idempotent no-op — `unregister`
|
|
||||||
// just calls `Map.remove` on something already absent.
|
|
||||||
//
|
|
||||||
// The branch is kept as **defense in depth** for the case where a future
|
|
||||||
// contributor tags a menu item with an actionId that they forget to add
|
|
||||||
// to [_kToolbarOwnedActionIds]: without this `else`, the original
|
|
||||||
// "stale-closure-outlives-disabled-state" bug (e.g. Screenshot cooldown
|
|
||||||
// bypass) would silently come back for that new action only.
|
|
||||||
for (final menu in v) {
|
|
||||||
final actionId = menu.actionId;
|
|
||||||
if (actionId == null) continue;
|
|
||||||
if (menu.onPressed != null) {
|
|
||||||
ffi.shortcutModel.register(actionId, menu.onPressed!);
|
|
||||||
} else {
|
|
||||||
ffi.shortcutModel.unregister(actionId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import 'package:flutter_hbb/common.dart';
|
|||||||
import 'package:flutter_hbb/models/state_model.dart';
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
export 'common/widgets/keyboard_shortcuts/shortcut_constants.dart';
|
|
||||||
|
|
||||||
const int kMaxVirtualDisplayCount = 4;
|
const int kMaxVirtualDisplayCount = 4;
|
||||||
const int kAllVirtualDisplay = -1;
|
const int kAllVirtualDisplay = -1;
|
||||||
|
|
||||||
|
|||||||
@@ -10,14 +10,12 @@ import 'package:flutter_hbb/common/widgets/audio_input.dart';
|
|||||||
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
|
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
|
||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/desktop_home_page.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/pages/desktop_tab_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart';
|
import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart';
|
||||||
import 'package:flutter_hbb/mobile/widgets/dialog.dart';
|
import 'package:flutter_hbb/mobile/widgets/dialog.dart';
|
||||||
import 'package:flutter_hbb/models/platform_model.dart';
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
import 'package:flutter_hbb/models/printer_model.dart';
|
import 'package:flutter_hbb/models/printer_model.dart';
|
||||||
import 'package:flutter_hbb/models/server_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/models/state_model.dart';
|
||||||
import 'package:flutter_hbb/plugin/manager.dart';
|
import 'package:flutter_hbb/plugin/manager.dart';
|
||||||
import 'package:flutter_hbb/plugin/widgets/desktop_settings.dart';
|
import 'package:flutter_hbb/plugin/widgets/desktop_settings.dart';
|
||||||
@@ -423,49 +421,11 @@ class _GeneralState extends State<_General> {
|
|||||||
if (!isWeb) audio(context),
|
if (!isWeb) audio(context),
|
||||||
if (!isWeb) record(context),
|
if (!isWeb) record(context),
|
||||||
if (!isWeb) WaylandCard(),
|
if (!isWeb) WaylandCard(),
|
||||||
other(),
|
other()
|
||||||
if (!bind.isIncomingOnly()) keyboardShortcuts(),
|
|
||||||
],
|
],
|
||||||
).marginOnly(bottom: _kListViewBottomMargin);
|
).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() {
|
Widget theme() {
|
||||||
final current = MyTheme.getThemeModePreference().toShortString();
|
final current = MyTheme.getThemeModePreference().toShortString();
|
||||||
onChanged(String value) async {
|
onChanged(String value) async {
|
||||||
@@ -2990,37 +2950,6 @@ 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
|
//#endregion
|
||||||
|
|
||||||
//#region dialogs
|
//#region dialogs
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import '../../common/widgets/toolbar.dart';
|
|||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
import '../../models/input_model.dart';
|
import '../../models/input_model.dart';
|
||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
import '../../models/shortcut_model.dart';
|
|
||||||
import '../../common/shared_state.dart';
|
import '../../common/shared_state.dart';
|
||||||
import '../../utils/image.dart';
|
import '../../utils/image.dart';
|
||||||
import '../widgets/remote_toolbar.dart';
|
import '../widgets/remote_toolbar.dart';
|
||||||
@@ -127,20 +126,6 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
_ffi.ffiModel.pi.platform, _ffi.dialogManager);
|
_ffi.ffiModel.pi.platform, _ffi.dialogManager);
|
||||||
_ffi.recordingModel
|
_ffi.recordingModel
|
||||||
.updateStatus(bind.sessionGetIsRecording(sessionId: _ffi.sessionId));
|
.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.canvasModel.initializeEdgeScrollFallback(this);
|
||||||
_ffi.start(
|
_ffi.start(
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/audio_input.dart';
|
import 'package:flutter_hbb/common/widgets/audio_input.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/dialog.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/common/widgets/toolbar.dart';
|
||||||
import 'package:flutter_hbb/models/chat_model.dart';
|
import 'package:flutter_hbb/models/chat_model.dart';
|
||||||
import 'package:flutter_hbb/models/state_model.dart';
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
@@ -764,31 +763,8 @@ class _ControlMenu extends StatelessWidget {
|
|||||||
if (e.divider) {
|
if (e.divider) {
|
||||||
return Divider();
|
return Divider();
|
||||||
} else {
|
} 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(
|
return MenuButton(
|
||||||
child: child,
|
child: e.child,
|
||||||
onPressed: e.onPressed,
|
onPressed: e.onPressed,
|
||||||
ffi: ffi,
|
ffi: ffi,
|
||||||
trailingIcon: e.trailingIcon);
|
trailingIcon: e.trailingIcon);
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import '../../common/widgets/remote_input.dart';
|
|||||||
import '../../models/input_model.dart';
|
import '../../models/input_model.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
import '../../models/shortcut_model.dart';
|
|
||||||
import '../../utils/image.dart';
|
import '../../utils/image.dart';
|
||||||
import '../widgets/dialog.dart';
|
import '../widgets/dialog.dart';
|
||||||
import '../widgets/custom_scale_widget.dart';
|
import '../widgets/custom_scale_widget.dart';
|
||||||
@@ -120,18 +119,6 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
_disableAndroidSoftKeyboard(
|
_disableAndroidSoftKeyboard(
|
||||||
isKeyboardVisible: keyboardVisibilityController.isVisible);
|
isKeyboardVisible: keyboardVisibilityController.isVisible);
|
||||||
// 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, gFFI);
|
|
||||||
// Mobile has no DesktopTabController, so tab-switch shortcuts
|
|
||||||
// remain unregistered (they will simply log a no-handler debug
|
|
||||||
// line if a mobile user binds one — they have no tabs to switch).
|
|
||||||
registerSessionShortcutActions(gFFI);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,8 @@ import '../../common/widgets/login.dart';
|
|||||||
import '../../consts.dart';
|
import '../../consts.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
import '../../models/shortcut_model.dart';
|
|
||||||
import '../widgets/dialog.dart';
|
import '../widgets/dialog.dart';
|
||||||
import 'home_page.dart';
|
import 'home_page.dart';
|
||||||
import 'mobile_keyboard_shortcuts_page.dart';
|
|
||||||
import 'scan_page.dart';
|
import 'scan_page.dart';
|
||||||
|
|
||||||
class SettingsPage extends StatefulWidget implements PageShape {
|
class SettingsPage extends StatefulWidget implements PageShape {
|
||||||
@@ -821,22 +819,6 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
showThemeSettings(gFFI.dialogManager);
|
showThemeSettings(gFFI.dialogManager);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SettingsTile.navigation(
|
|
||||||
leading: Icon(Icons.keyboard_outlined),
|
|
||||||
title: Text(translate('Keyboard Shortcuts')),
|
|
||||||
description: Text(ShortcutModel.isEnabled()
|
|
||||||
? translate('On')
|
|
||||||
: translate('Off')),
|
|
||||||
onPressed: (context) {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (_) => const MobileKeyboardShortcutsPage(),
|
|
||||||
)).then((_) {
|
|
||||||
if (mounted) setState(() {});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
if (!bind.isDisableAccount())
|
if (!bind.isDisableAccount())
|
||||||
SettingsTile.switchTile(
|
SettingsTile.switchTile(
|
||||||
title: Text(translate('note-at-conn-end-tip')),
|
title: Text(translate('note-at-conn-end-tip')),
|
||||||
@@ -1370,4 +1352,3 @@ SettingsTile _getPopupDialogRadioEntry({
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ class InputModel {
|
|||||||
/// which runs per-engine, so each isolate registers its own handler tied
|
/// which runs per-engine, so each isolate registers its own handler tied
|
||||||
/// to its own set of InputModels.
|
/// to its own set of InputModels.
|
||||||
static void initSideButtonChannel() {
|
static void initSideButtonChannel() {
|
||||||
if (!isLinux) return;
|
if (!Platform.isLinux) return;
|
||||||
if (_sideButtonChannelInitialized) return;
|
if (_sideButtonChannelInitialized) return;
|
||||||
_sideButtonChannelInitialized = true;
|
_sideButtonChannelInitialized = true;
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import 'package:flutter_hbb/models/peer_model.dart';
|
|||||||
import 'package:flutter_hbb/models/peer_tab_model.dart';
|
import 'package:flutter_hbb/models/peer_tab_model.dart';
|
||||||
import 'package:flutter_hbb/models/printer_model.dart';
|
import 'package:flutter_hbb/models/printer_model.dart';
|
||||||
import 'package:flutter_hbb/models/server_model.dart';
|
import 'package:flutter_hbb/models/server_model.dart';
|
||||||
import 'package:flutter_hbb/models/shortcut_model.dart';
|
|
||||||
import 'package:flutter_hbb/models/user_model.dart';
|
import 'package:flutter_hbb/models/user_model.dart';
|
||||||
import 'package:flutter_hbb/models/state_model.dart';
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
import 'package:flutter_hbb/models/desktop_render_texture.dart';
|
import 'package:flutter_hbb/models/desktop_render_texture.dart';
|
||||||
@@ -477,11 +476,6 @@ class FfiModel with ChangeNotifier {
|
|||||||
} else if (name == 'exit_relative_mouse_mode') {
|
} else if (name == 'exit_relative_mouse_mode') {
|
||||||
// Handle exit shortcut from rdev grab loop (Ctrl+Alt on Win/Linux, Cmd+G on macOS)
|
// Handle exit shortcut from rdev grab loop (Ctrl+Alt on Win/Linux, Cmd+G on macOS)
|
||||||
parent.target?.inputModel.exitRelativeMouseModeWithKeyRelease();
|
parent.target?.inputModel.exitRelativeMouseModeWithKeyRelease();
|
||||||
} else if (name == kShortcutEventName) {
|
|
||||||
final action = evt['action'];
|
|
||||||
if (action is String) {
|
|
||||||
parent.target?.shortcutModel.onTriggered(action);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
debugPrint('Event is not handled in the fixed branch: $name');
|
debugPrint('Event is not handled in the fixed branch: $name');
|
||||||
}
|
}
|
||||||
@@ -3629,7 +3623,6 @@ class FFI {
|
|||||||
late final ElevationModel elevationModel; // session
|
late final ElevationModel elevationModel; // session
|
||||||
late final CmFileModel cmFileModel; // cm
|
late final CmFileModel cmFileModel; // cm
|
||||||
late final TextureModel textureModel; //session
|
late final TextureModel textureModel; //session
|
||||||
late final ShortcutModel shortcutModel; // session
|
|
||||||
late final Peers recentPeersModel; // global
|
late final Peers recentPeersModel; // global
|
||||||
late final Peers favoritePeersModel; // global
|
late final Peers favoritePeersModel; // global
|
||||||
late final Peers lanPeersModel; // global
|
late final Peers lanPeersModel; // global
|
||||||
@@ -3659,7 +3652,6 @@ class FFI {
|
|||||||
elevationModel = ElevationModel(WeakReference(this));
|
elevationModel = ElevationModel(WeakReference(this));
|
||||||
cmFileModel = CmFileModel(WeakReference(this));
|
cmFileModel = CmFileModel(WeakReference(this));
|
||||||
textureModel = TextureModel(WeakReference(this));
|
textureModel = TextureModel(WeakReference(this));
|
||||||
shortcutModel = ShortcutModel(WeakReference(this));
|
|
||||||
recentPeersModel = Peers(
|
recentPeersModel = Peers(
|
||||||
name: PeersModelName.recent,
|
name: PeersModelName.recent,
|
||||||
loadEvent: LoadEvent.recent,
|
loadEvent: LoadEvent.recent,
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import 'package:uuid/uuid.dart';
|
|||||||
import 'dart:html' as html;
|
import 'dart:html' as html;
|
||||||
|
|
||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/common.dart' as common;
|
|
||||||
|
|
||||||
final _privateConstructorUsedError = UnsupportedError(
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
@@ -931,21 +930,6 @@ class RustdeskImpl {
|
|||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell the JS-side matcher (flutter/web/js/src/shortcut_matcher.ts) to
|
|
||||||
// re-read its bindings from LocalStorage. Mirrors the native call which
|
|
||||||
// refreshes the Rust matcher's in-memory cache.
|
|
||||||
void mainReloadKeyboardShortcuts({dynamic hint}) {
|
|
||||||
js.context.callMethod('reloadShortcuts', []);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Web has no Rust at runtime, so the defaults seed comes from the
|
|
||||||
// [kDefaultShortcutBindings] canonical in shortcut_constants.dart. Parity
|
|
||||||
// with Rust's `default_bindings()` is enforced by tests on both sides
|
|
||||||
// against `flutter/test/fixtures/default_keyboard_shortcuts.json`.
|
|
||||||
String mainGetDefaultKeyboardShortcuts({dynamic hint}) {
|
|
||||||
return jsonEncode(kDefaultShortcutBindings);
|
|
||||||
}
|
|
||||||
|
|
||||||
String mainGetInputSource({dynamic hint}) {
|
String mainGetInputSource({dynamic hint}) {
|
||||||
final inputSource =
|
final inputSource =
|
||||||
js.context.callMethod('getByName', ['option:local', 'input-source']);
|
js.context.callMethod('getByName', ['option:local', 'input-source']);
|
||||||
@@ -1192,15 +1176,6 @@ class RustdeskImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> mainInit({required String appDir, dynamic hint}) {
|
Future<void> mainInit({required String appDir, dynamic hint}) {
|
||||||
// JS -> Dart shortcut bridge. The matcher in flutter/web/js/src/
|
|
||||||
// shortcut_matcher.ts calls `window.onShortcutTriggered(actionId)` when a
|
|
||||||
// binding fires; route it to the active session's ShortcutModel.
|
|
||||||
// Web is single-window so `gFFI` is always the active session.
|
|
||||||
js.context['onShortcutTriggered'] = (dynamic action) {
|
|
||||||
if (action is String) {
|
|
||||||
common.gFFI.shortcutModel.onTriggered(action);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return Future.value();
|
return Future.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user