Skip to main content

ui/
cover.rs

1//! Where overlays sit in the frame being drawn, for native views over the
2//! window (a webview) that nothing gpui paints can cover.
3//!
4//! A cover is a rectangle recorded during prepaint, stamped with its place in
5//! the frame's prepaint order. gpui prepaints an element that paints over
6//! another after that other one: a later sibling, a `deferred` layer, the
7//! tooltip, the drag preview. So a native view takes a [`mark`] in its own
8//! prepaint and asks [`covered`] in its paint, when every cover of the frame
9//! is recorded.
10//!
11//! [`crate::surface`]'s cards and the dialog and sheet scrims record
12//! themselves. Anything else an app floats puts a [`cover`] in it.
13//!
14//! A cover in a view gpui reuses from the last frame (`AnyView::cached`) is not
15//! recorded.
16
17use std::{
18    collections::HashMap,
19    sync::atomic::{AtomicU64, Ordering},
20};
21
22use gpui::{App, Bounds, Global, IntoElement, Pixels, Styled, Window, WindowId, canvas};
23
24/// A place in a frame's prepaint order.
25#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
26pub struct Mark(u64);
27
28/// An element filling its parent, recorded as a cover. Its parent's box is
29/// what it records, so it goes in the overlay's outermost box.
30pub fn cover() -> impl IntoElement {
31    canvas(record, |_, _, _, _| {}).absolute().size_full()
32}
33
34/// Records `bounds` as covered. Call it from prepaint.
35pub fn record(bounds: Bounds<Pixels>, window: &mut Window, cx: &mut App) {
36    let bounds = bounds.intersect(&window.content_mask().bounds);
37    let mark = next();
38    let id = window.window_handle().window_id();
39    let covers = cx.default_global::<Covers>();
40    if let Some(frame) = covers.0.get_mut(&id) {
41        frame.push((mark, bounds));
42        return;
43    }
44    covers.0.insert(id, vec![(mark, bounds)]);
45    // Runs before the next frame is drawn.
46    window.on_next_frame(move |_, cx| {
47        cx.default_global::<Covers>().0.remove(&id);
48    });
49}
50
51/// The current place in the prepaint order. Call it from prepaint.
52pub fn mark() -> Mark {
53    next()
54}
55
56/// Whether a cover recorded after `mark` in this frame overlaps `bounds`. Call
57/// it from paint.
58pub fn covered(mark: Mark, bounds: Bounds<Pixels>, window: &Window, cx: &App) -> bool {
59    cx.try_global::<Covers>()
60        .and_then(|covers| covers.0.get(&window.window_handle().window_id()))
61        .is_some_and(|frame| {
62            frame
63                .iter()
64                .any(|(at, cover)| *at > mark && cover.intersects(&bounds))
65        })
66}
67
68fn next() -> Mark {
69    static NEXT: AtomicU64 = AtomicU64::new(1);
70    Mark(NEXT.fetch_add(1, Ordering::Relaxed))
71}
72
73/// This frame's covers, per window.
74#[derive(Default)]
75struct Covers(HashMap<WindowId, Vec<(Mark, Bounds<Pixels>)>>);
76
77impl Global for Covers {}