Skip to main content

nifty_macos/
areas.rs

1//! Areas that cover a stack without drawing anything, such as a context menu's or a drop
2//! target's: hidden views among a host's subviews, found by where they are.
3
4use objc2::rc::Retained;
5use objc2::{DowncastTarget, MainThreadOnly};
6use objc2_app_kit::NSView;
7use objc2_foundation::{NSPoint, NSRect};
8
9/// Hides `area`, a fresh view, so it takes no clicks and draws nothing.
10pub(crate) fn hidden<T: MainThreadOnly + AsRef<NSView>>(area: Retained<T>) -> Retained<T> {
11    (*area).as_ref().setHidden(true);
12    area
13}
14
15/// The smallest area of type `T` among `host`'s subviews at `point`, in `host`'s
16/// coordinates, which is the innermost stack's; of areas the same size, the one added last.
17pub(crate) fn innermost<T: DowncastTarget>(host: &NSView, point: NSPoint) -> Option<Retained<T>> {
18    let mut found: Option<(f64, Retained<T>)> = None;
19    for subview in host.subviews() {
20        let frame = subview.frame();
21        let Ok(area) = subview.downcast::<T>() else {
22            continue;
23        };
24        if !contains(frame, point) {
25            continue;
26        }
27        let size = frame.size.width * frame.size.height;
28        if found.as_ref().is_none_or(|(smallest, _)| size <= *smallest) {
29            found = Some((size, area));
30        }
31    }
32    found.map(|(_, area)| area)
33}
34
35fn contains(frame: NSRect, point: NSPoint) -> bool {
36    let (left, top) = (frame.origin.x, frame.origin.y);
37    (left..left + frame.size.width).contains(&point.x)
38        && (top..top + frame.size.height).contains(&point.y)
39}