Skip to content

Commit

Permalink
fix: properly handle overlapping menu widgets (#217)
Browse files Browse the repository at this point in the history
Fixes #216
  • Loading branch information
knopp authored Oct 23, 2023
1 parent b3f61ba commit 113e8a8
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 9 deletions.
66 changes: 57 additions & 9 deletions super_context_menu/lib/src/desktop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import 'package:super_context_menu/src/menu_internal.dart';
import 'package:super_context_menu/super_context_menu.dart';
import 'package:super_native_extensions/raw_menu.dart' as raw;

// ignore: implementation_imports
import 'package:super_native_extensions/src/mutex.dart';

import 'scaffold/desktop/menu_session.dart';
import 'scaffold/desktop/menu_widget_builder.dart';
import 'util.dart';
Expand All @@ -24,7 +27,7 @@ class _ContextMenuDetector extends StatefulWidget {
final Widget child;
final HitTestBehavior hitTestBehavior;
final ContextMenuIsAllowed contextMenuIsAllowed;
final Function(Offset, Listenable) onShowContextMenu;
final Function(Offset, Listenable, Function(bool)) onShowContextMenu;

@override
State<StatefulWidget> createState() => _ContextMenuDetectorState();
Expand All @@ -36,6 +39,11 @@ class _ContextMenuDetectorState extends State<_ContextMenuDetector> {

final _onPointerUp = SimpleNotifier();

// Prevent nested detectors from showing context menu.
static _ContextMenuDetectorState? _activeDetector;

static final _mutex = Mutex();

bool _acceptPrimaryButton() {
final keys = RawKeyboard.instance.keysPressed;
return defaultTargetPlatform == TargetPlatform.macOS &&
Expand All @@ -55,19 +63,51 @@ class _ContextMenuDetectorState extends State<_ContextMenuDetector> {
return false;
}

@override
void dispose() {
super.dispose();
_mutex.protect(() async {
if (_activeDetector == this) {
_activeDetector = null;
}
});
}

/// Returns true if the context menu provider has produced a valid menu that
/// is being shown.
Future<bool> _showContextMenu(Offset position, Listenable onPointerUp) async {
final completer = Completer<bool>();
widget.onShowContextMenu(position, onPointerUp, (value) {
completer.complete(value);
});
return completer.future;
}

@override
Widget build(BuildContext context) {
return Listener(
behavior: widget.hitTestBehavior,
onPointerDown: (event) {
if (_canAcceptEvent(event)) {
_pointerDown = event.pointer;
_pointerDownStopwatch = Stopwatch()..start();
widget.onShowContextMenu(event.position, _onPointerUp);
}
_mutex.protect(() async {
if (_activeDetector != null) {
return;
}
if (_canAcceptEvent(event)) {
final menuResolved = await _showContextMenu(
event.position,
_onPointerUp,
);
if (menuResolved) {
_activeDetector = this;
_pointerDown = event.pointer;
_pointerDownStopwatch = Stopwatch()..start();
}
}
});
},
onPointerUp: (event) {
if (_pointerDown == event.pointer) {
_activeDetector = null;
_pointerDown = null;
// Pointer up would trigger currently selected item. Make sure we don't
// do this on simple right click.
Expand Down Expand Up @@ -108,11 +148,12 @@ class DesktopContextMenuWidget extends StatelessWidget {
return _ContextMenuDetector(
hitTestBehavior: hitTestBehavior,
contextMenuIsAllowed: contextMenuIsAllowed,
onShowContextMenu: (position, pointerUpListenable) {
_onContextMenu(
onShowContextMenu: (position, pointerUpListenable, onMenuresolved) {
_onShowContextMenu(
context,
position,
pointerUpListenable,
onMenuresolved,
);
},
// Used on web to determine whether to prevent browser context menu
Expand Down Expand Up @@ -140,10 +181,13 @@ class DesktopContextMenuWidget extends StatelessWidget {
);
}

void _onContextMenu(
/// [onMenuResolved] Will be called with true if the provider resolved a valid menu that will be shown,
/// false otherwise.
void _onShowContextMenu(
BuildContext context,
Offset globalPosition,
Listenable onInitialPointerUp,
Function(bool) onMenuResolved,
) async {
final onShowMenu = SimpleNotifier();
final onHideMenu = ValueNotifier<raw.MenuResult?>(null);
Expand All @@ -167,8 +211,10 @@ class DesktopContextMenuWidget extends StatelessWidget {
// ignore: use_build_context_synchronously
if (!context.mounted) {
onHideMenu.value = raw.MenuResult(itemSelected: false);
onMenuResolved(false);
return;
}
onMenuResolved(true);
onShowMenu.notify();
final request = raw.DesktopContextMenuRequest(
iconTheme: serializationOptions.iconTheme,
Expand All @@ -189,6 +235,8 @@ class DesktopContextMenuWidget extends StatelessWidget {
});
final res = await menuContext.showContextMenu(request);
onHideMenu.value = res;
} else {
onMenuResolved(false);
}
} finally {
onShowMenu.dispose();
Expand Down
1 change: 1 addition & 0 deletions super_context_menu/lib/src/menu_internal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class MenuContextDelegate implements raw.MenuContextDelegate {
_onShow.remove(request.configurationId);
_onHide.remove(request.configurationId);
_onPreviewAction.remove(request.configurationId);
continue;
}
return configuration;
}
Expand Down

0 comments on commit 113e8a8

Please sign in to comment.