@@ -16,43 +16,16 @@ import 'package:get/get.dart';
1616
1717bool isEditOsPassword = false ;
1818
19- /// Action IDs that `toolbarControls` is the sole registrar for. Each call to
20- /// `toolbarControls` (e.g. opening the toolbar menu after a permission was
21- /// revoked or a state changed) wipes these so a previously-registered closure
22- /// can't outlive the menu entry that owns it. The for-loop at the bottom of
23- /// `toolbarControls` then re-registers whichever entries are still present in
24- /// the rebuilt menu list.
25- ///
26- /// Actions registered elsewhere — `registerSessionShortcutActions` on desktop
27- /// owns toggle_recording, fullscreen, switch_display, switch_tab, close_tab,
28- /// toggle_toolbar — MUST NOT appear here, otherwise this list would clobber
29- /// their registration on every menu rebuild.
30- ///
31- /// `kShortcutActionToggleRecording` is platform-conditional (mobile-only —
32- /// see the `!(isDesktop || isWeb)` guard in `toolbarControls` ). It is handled
33- /// separately in the unregister pass rather than appearing in this const list.
34- const _kToolbarOwnedActionIds = < String > [
35- kShortcutActionSendCtrlAltDel,
36- kShortcutActionRestartRemote,
37- kShortcutActionInsertLock,
38- kShortcutActionToggleBlockInput,
39- kShortcutActionSwitchSides,
40- kShortcutActionRefresh,
41- kShortcutActionScreenshot,
42- ];
43-
4419class TTextMenu {
4520 final Widget child;
4621 final VoidCallback ? onPressed;
4722 Widget ? trailingIcon;
4823 bool divider;
49- final String ? actionId;
5024 TTextMenu (
5125 {required this .child,
5226 required this .onPressed,
5327 this .trailingIcon,
54- this .divider = false ,
55- this .actionId});
28+ this .divider = false });
5629
5730 Widget getChild () {
5831 if (trailingIcon != null ) {
@@ -121,20 +94,6 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
12194 final sessionId = ffi.sessionId;
12295 final isDefaultConn = ffi.connType == ConnType .defaultConn;
12396
124- // Wipe everything `toolbarControls` could have registered last call so
125- // stale closures (e.g. for a menu entry whose permission has since been
126- // revoked) don't outlive the menu rebuild. See _kToolbarOwnedActionIds.
127- for (final actionId in _kToolbarOwnedActionIds) {
128- ffi.shortcutModel.unregister (actionId);
129- }
130- // toggle_recording is platform-conditional — toolbarControls only builds
131- // the menu entry on `!(isDesktop || isWeb)`. On desktop the registration
132- // is owned by `registerSessionShortcutActions` and must NOT be touched
133- // here. See the recording menu entry below.
134- if (! (isDesktop || isWeb)) {
135- ffi.shortcutModel.unregister (kShortcutActionToggleRecording);
136- }
137-
13897 List <TTextMenu > v = [];
13998 // elevation
14099 if (isDefaultConn &&
@@ -270,8 +229,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
270229 v.add (
271230 TTextMenu (
272231 child: Text ('${translate ("Insert Ctrl + Alt + Del" )}' ),
273- onPressed: () => bind.sessionCtrlAltDel (sessionId: sessionId),
274- actionId: kShortcutActionSendCtrlAltDel),
232+ onPressed: () => bind.sessionCtrlAltDel (sessionId: sessionId)),
275233 );
276234 }
277235 // restart
@@ -284,17 +242,15 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
284242 TTextMenu (
285243 child: Text (translate ('Restart remote device' )),
286244 onPressed: () =>
287- showRestartRemoteDevice (pi, id, sessionId, ffi.dialogManager),
288- actionId: kShortcutActionRestartRemote),
245+ showRestartRemoteDevice (pi, id, sessionId, ffi.dialogManager)),
289246 );
290247 }
291248 // insertLock
292249 if (isDefaultConn && ! ffiModel.viewOnly && ffi.ffiModel.keyboard) {
293250 v.add (
294251 TTextMenu (
295252 child: Text (translate ('Insert Lock' )),
296- onPressed: () => bind.sessionLockScreen (sessionId: sessionId),
297- actionId: kShortcutActionInsertLock),
253+ onPressed: () => bind.sessionLockScreen (sessionId: sessionId)),
298254 );
299255 }
300256 // blockUserInput
@@ -312,8 +268,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
312268 sessionId: sessionId,
313269 value: '${blockInput .value ? 'un' : '' }block-input' );
314270 blockInput.value = ! blockInput.value;
315- },
316- actionId: kShortcutActionToggleBlockInput));
271+ }));
317272 }
318273 // switchSides
319274 if (isDefaultConn &&
@@ -325,15 +280,13 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
325280 v.add (TTextMenu (
326281 child: Text (translate ('Switch Sides' )),
327282 onPressed: () =>
328- showConfirmSwitchSidesDialog (sessionId, id, ffi.dialogManager),
329- actionId: kShortcutActionSwitchSides));
283+ showConfirmSwitchSidesDialog (sessionId, id, ffi.dialogManager)));
330284 }
331285 // refresh
332286 if (pi.version.isNotEmpty) {
333287 v.add (TTextMenu (
334288 child: Text (translate ('Refresh' )),
335289 onPressed: () => sessionRefreshVideo (sessionId, pi),
336- actionId: kShortcutActionRefresh,
337290 ));
338291 }
339292 // record
@@ -355,8 +308,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
355308 )
356309 ],
357310 ),
358- onPressed: () => ffi.recordingModel.toggle (),
359- actionId: kShortcutActionToggleRecording));
311+ onPressed: () => ffi.recordingModel.toggle ()));
360312 }
361313
362314 // to-do:
@@ -373,14 +325,6 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
373325 onPressed: ffi.ffiModel.timerScreenshot != null
374326 ? null
375327 : () {
376- // Live cooldown check: the menu rebuilds onPressed=null
377- // whenever toolbarControls runs and finds timerScreenshot
378- // != null, but the keyboard-shortcut callback holds onto
379- // the originally-enabled closure across cooldown periods
380- // (toolbarControls only re-runs on menu open). Without
381- // this guard the second shortcut press during the 30s
382- // cooldown still fires sessionTakeScreenshot.
383- if (ffi.ffiModel.timerScreenshot != null ) return ;
384328 if (pi.currentDisplay == kAllDisplayValue) {
385329 msgBox (
386330 sessionId,
@@ -398,7 +342,6 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
398342 });
399343 }
400344 },
401- actionId: kShortcutActionScreenshot,
402345 ));
403346 }
404347 }
@@ -409,28 +352,6 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
409352 onPressed: () => onCopyFingerprint (FingerprintState .find (id).value),
410353 ));
411354 }
412- // Register tagged callbacks with the shortcut model so global keyboard
413- // shortcuts can dispatch the same actions as the toolbar menu items.
414- //
415- // For action IDs already cleared at the top of this function (i.e. those
416- // in [_kToolbarOwnedActionIds] plus the conditional toggle_recording),
417- // the `else` branch below is a redundant idempotent no-op — `unregister`
418- // just calls `Map.remove` on something already absent.
419- //
420- // The branch is kept as **defense in depth** for the case where a future
421- // contributor tags a menu item with an actionId that they forget to add
422- // to [_kToolbarOwnedActionIds]: without this `else`, the original
423- // "stale-closure-outlives-disabled-state" bug (e.g. Screenshot cooldown
424- // bypass) would silently come back for that new action only.
425- for (final menu in v) {
426- final actionId = menu.actionId;
427- if (actionId == null ) continue ;
428- if (menu.onPressed != null ) {
429- ffi.shortcutModel.register (actionId, menu.onPressed! );
430- } else {
431- ffi.shortcutModel.unregister (actionId);
432- }
433- }
434355 return v;
435356}
436357
0 commit comments