Skip to main content

nifty_macos/
context_menu.rs

1//! Context menus: a hidden view that marks the area a stack's menu covers, and the lookup
2//! the views widgets sit in use to find the menu under a right-click. A right-click that no
3//! control takes passes up the responder chain to those views.
4
5use objc2::rc::Retained;
6use objc2::{DefinedClass, MainThreadMarker, MainThreadOnly, define_class, msg_send};
7use objc2_app_kit::{NSEvent, NSMenu, NSView};
8
9use nifty_runtime::{MenuEntry, MenuHandler};
10
11use crate::areas;
12use crate::classes::MenuTarget;
13use crate::menus;
14
15#[derive(Debug)]
16pub(crate) struct MenuAreaIvars {
17    menu: Retained<NSMenu>,
18    /// Its items hold their target weakly, so the area keeps it.
19    _target: Retained<MenuTarget>,
20}
21
22define_class!(
23    // SAFETY: NSView has no subclassing requirements and `MenuArea` does not implement Drop.
24    #[unsafe(super = NSView)]
25    #[thread_kind = MainThreadOnly]
26    #[name = "NiftyMenuArea"]
27    #[ivars = MenuAreaIvars]
28    #[derive(Debug)]
29    pub(crate) struct MenuArea;
30);
31
32impl MenuArea {
33    /// An area with a menu of `entries`.
34    pub(crate) fn new(
35        mtm: MainThreadMarker,
36        entries: &[MenuEntry<usize>],
37        on_select: MenuHandler,
38    ) -> Retained<Self> {
39        let target = MenuTarget::new(mtm, on_select);
40        let menu = menus::context_menu(mtm, entries, &target);
41        let this = Self::alloc(mtm).set_ivars(MenuAreaIvars {
42            menu,
43            _target: target,
44        });
45        // SAFETY: `init` is NSView's designated initializer and takes no arguments.
46        areas::hidden(unsafe { msg_send![super(this), init] })
47    }
48}
49
50/// The menu of the innermost area under `event` among `host`'s subviews.
51pub(crate) fn menu_at(host: &NSView, event: &NSEvent) -> Option<Retained<NSMenu>> {
52    let point = host.convertPoint_fromView(event.locationInWindow(), None);
53    areas::innermost::<MenuArea>(host, point).map(|area| area.ivars().menu.clone())
54}