Primitives · a raise keeps its state under its owner's id
A raise inside another was namespaced by its place in the layer's queue, so one that came before it moved its id path and rebuilt its keyed state and focus: an open date popup would lose its month when a tip appeared. raise now takes its owner's id and draws under it, and float and float_height pass the owner's id on from every call site. A test holds a focused box in a raise while another comes before it.
This commit is contained in:
1 parent
341420d1e4
commit
51ade29032
25 files changed
+188
-40
No files matched your search
@@ -48,7 +48,7 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
|
||||
- gpui's `Window::dispatch_event` returns a private type, so the gallery scripts mouse input by posting `NSEvent`s to its own queue.
|
||||
- `hover()` needs an element id.
|
||||
- gpui applies hover and group-hover styles once an element has a hitbox, in prepaint and paint, but text takes its color at layout, so a hover cannot recolor text. What shows on hover rests `invisible()` and turns `visible()` through `group_hover` (`chat::ConversationItem`, `messaging::ChatMessage`).
|
||||
- gpui 0.2.2 panics on a deferred draw inside another. Whatever draws over the page goes through `primitives::raise`: at the top it defers; inside something raised it lays out on its own and draws last there, so a list opened in a dialog lies over the dialog. `scripts/check.sh` refuses gpui's `deferred` anywhere else.
|
||||
- gpui 0.2.2 panics on a deferred draw inside another. Whatever draws over the page goes through `primitives::raise`: at the top it defers; inside something raised it lays out on its own and draws last there, so a list opened in a dialog lies over the dialog. A raise takes its owner's id and draws under it, so its keyed state and focus hold while other raises come and go. `scripts/check.sh` refuses gpui's `deferred` anywhere else.
|
||||
- `occlude()` blocks the pointer for everything painted before it, its own ancestors too. Put it on the outermost box that should stop the pointer, as the toast stack does, or the ancestors' hover never fires.
|
||||
- A drag that pans, turns or slides goes through gpui's drag, whose moves reach the owner in or out of its box; `on_mouse_move` stops at the box's edge. gpui sends a drag's moves to every listener of its type, so each payload carries its owner's `EntityId` and handlers check it.
|
||||
- gpui 0.2.2 paints pictures without a transform, so a picture turns by its pixels: `media::turn` turns decoded frames by quarter turns off the main thread, an asset keyed by the picture and the turn.
|
||||
|
||||
@@ -349,3 +349,4 @@ Per-component lines live in `tasks/`. Tags there:
|
||||
| T29c | 1 | FAIL | a long chip ran past its tag field at 280px |
|
||||
| T29c | 2 | PASS | |
|
||||
| T29d | 1 | FAIL | the time dialog's rule for a time ahead of now had no test; its test found that a picker opened in a dialog panicked on a deferred draw inside another |
|
||||
| T29d | 2 | FAIL | a raise inside a raise took its place in the queue as its namespace, so one appearing before it rebuilt its state and dropped its focus |
|
||||
@@ -198,7 +198,9 @@ impl RenderOnce for LinkEditor {
|
||||
}),
|
||||
),
|
||||
);
|
||||
div().id(self.id).child(float(anchor, 1, panel, window, cx))
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.child(float(self.id, anchor, 1, panel, window, cx))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ impl RenderOnce for ToastViewport {
|
||||
cards.push(card(&self.id, ix, &self.toaster, window, cx));
|
||||
}
|
||||
let stack = div()
|
||||
.id(self.id)
|
||||
.id(self.id.clone())
|
||||
.occlude()
|
||||
.flex()
|
||||
.flex_col()
|
||||
@@ -235,6 +235,7 @@ impl RenderOnce for ToastViewport {
|
||||
})
|
||||
.children(cards);
|
||||
raise(
|
||||
(self.id, "raised"),
|
||||
anchored()
|
||||
.position(point(viewport.width - margin, viewport.height - margin))
|
||||
.anchor(Corner::BottomRight)
|
||||
|
||||
@@ -265,6 +265,7 @@ impl RenderOnce for Cascader {
|
||||
});
|
||||
let close = state.clone();
|
||||
float(
|
||||
self.id.clone(),
|
||||
anchor,
|
||||
deepest,
|
||||
surface((self.id.clone(), "columns"), cx)
|
||||
|
||||
@@ -228,6 +228,7 @@ fn period_picker(
|
||||
};
|
||||
let grid = period_grid(&key, build(shown), grid, window, cx);
|
||||
float(
|
||||
key.clone(),
|
||||
anchor,
|
||||
6,
|
||||
surface((key.clone(), "popup"), cx)
|
||||
|
||||
@@ -246,6 +246,7 @@ impl RenderOnce for DatePicker {
|
||||
}
|
||||
let (escape, done) = (close.clone(), close.clone());
|
||||
float(
|
||||
id.clone(),
|
||||
anchor,
|
||||
CALENDAR_ROWS,
|
||||
surface((id.clone(), "popup"), cx)
|
||||
@@ -347,6 +348,7 @@ impl RenderOnce for DateRangePicker {
|
||||
}
|
||||
let (hover, pick, escape) = (state.clone(), state.clone(), close.clone());
|
||||
float(
|
||||
id.clone(),
|
||||
anchor,
|
||||
CALENDAR_ROWS,
|
||||
surface((id.clone(), "popup"), cx)
|
||||
|
||||
@@ -256,6 +256,7 @@ impl RenderOnce for TimePicker {
|
||||
let columns = time_columns(&id, time, step, commit.clone(), window, cx);
|
||||
let out = close.clone();
|
||||
float(
|
||||
id.clone(),
|
||||
anchor,
|
||||
CALENDAR_ROWS,
|
||||
surface((id.clone(), "popup"), cx)
|
||||
@@ -368,6 +369,7 @@ impl RenderOnce for DateTimePicker {
|
||||
);
|
||||
let out = close.clone();
|
||||
float(
|
||||
id.clone(),
|
||||
anchor,
|
||||
CALENDAR_ROWS,
|
||||
surface((id.clone(), "popup"), cx)
|
||||
|
||||
@@ -77,6 +77,7 @@ impl RenderOnce for WeekPicker {
|
||||
}
|
||||
let (escape, out, on_change) = (close.clone(), close.clone(), on_change.clone());
|
||||
float(
|
||||
id.clone(),
|
||||
anchor,
|
||||
CALENDAR_ROWS,
|
||||
surface((id.clone(), "popup"), cx)
|
||||
|
||||
@@ -166,8 +166,9 @@ pub(crate) fn surface(id: impl Into<ElementId>, cx: &App) -> Stateful<Div> {
|
||||
.text_size(theme.text_size(TextSize::Sm))
|
||||
}
|
||||
|
||||
/// Floats `content` of about `rows` rows under `anchor`, or over it when only above has room.
|
||||
/// Floats `content` of about `rows` rows under `anchor`, or over it when only above has room; `id` is its owner's.
|
||||
pub(crate) fn float(
|
||||
id: impl Into<ElementId>,
|
||||
anchor: Bounds<Pixels>,
|
||||
rows: usize,
|
||||
content: impl IntoElement,
|
||||
@@ -178,7 +179,7 @@ pub(crate) fn float(
|
||||
let rem = window.rem_size();
|
||||
let row = theme.control_height(ControlSize::Md).to_pixels(rem);
|
||||
let height = (row * (rows + 1) as f32).min(theme.list_max_height().to_pixels(rem));
|
||||
float_height(anchor, height, content, window, cx)
|
||||
float_height(id, anchor, height, content, window, cx)
|
||||
}
|
||||
|
||||
/// Whether `height` goes over `anchor`: it does not fit below, and above has more room.
|
||||
@@ -187,8 +188,9 @@ pub(crate) fn opens_up(anchor: Bounds<Pixels>, height: Pixels, viewport: Pixels)
|
||||
height > below && anchor.top() > below
|
||||
}
|
||||
|
||||
/// Floats `content` of a known `height` under `anchor`, or over it when only above has room.
|
||||
/// Floats `content` of a known `height` under `anchor`, or over it when only above has room; `id` is its owner's.
|
||||
pub(crate) fn float_height(
|
||||
id: impl Into<ElementId>,
|
||||
anchor: Bounds<Pixels>,
|
||||
height: Pixels,
|
||||
content: impl IntoElement,
|
||||
@@ -220,7 +222,7 @@ pub(crate) fn float_height(
|
||||
}),
|
||||
)
|
||||
};
|
||||
raise(placed.snap_to_window())
|
||||
raise(id, placed.snap_to_window())
|
||||
.with_priority(1)
|
||||
.into_any_element()
|
||||
}
|
||||
@@ -298,7 +300,7 @@ impl Popup<'_> {
|
||||
})
|
||||
})
|
||||
});
|
||||
let list = surface(self.id, cx)
|
||||
let list = surface(self.id.clone(), cx)
|
||||
.debug_selector(|| "option-list".into())
|
||||
.min_w(theme.tooltip_max_width())
|
||||
.when(anchor.size.width > Pixels::ZERO, |list| {
|
||||
@@ -319,7 +321,7 @@ impl Popup<'_> {
|
||||
self.scroll.zip(self.reveal),
|
||||
|list, (handle, (ix, done))| list.child(revealer(handle, ix, done)),
|
||||
);
|
||||
float(anchor, self.rows.len(), list, window, cx)
|
||||
float(self.id, anchor, self.rows.len(), list, window, cx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ impl RenderOnce for FloatingPanel {
|
||||
};
|
||||
let panel = self
|
||||
.base
|
||||
.id(self.id)
|
||||
.id(self.id.clone())
|
||||
.flex()
|
||||
.flex_col()
|
||||
.overflow_hidden()
|
||||
@@ -149,7 +149,7 @@ impl RenderOnce for FloatingPanel {
|
||||
.child(self.title),
|
||||
)
|
||||
.child(div().p_3().children(self.body));
|
||||
raise(anchored().position(at).child(panel))
|
||||
raise((self.id, "raised"), anchored().position(at).child(panel))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-4
@@ -382,10 +382,25 @@ pub(crate) fn hang(
|
||||
}
|
||||
});
|
||||
let placed = match spot {
|
||||
Spot::Under => float(state.read(cx).anchor, menu.entries.len(), root, window, cx),
|
||||
Spot::At(point) => raise(anchored().position(point).snap_to_window().child(
|
||||
root.with_animation((id.clone(), "in"), enter.clone(), |root, t| root.opacity(t)),
|
||||
))
|
||||
Spot::Under => float(
|
||||
(id.clone(), "panel"),
|
||||
state.read(cx).anchor,
|
||||
menu.entries.len(),
|
||||
root,
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
Spot::At(point) => raise(
|
||||
(id.clone(), "panel"),
|
||||
anchored()
|
||||
.position(point)
|
||||
.snap_to_window()
|
||||
.child(
|
||||
root.with_animation((id.clone(), "in"), enter.clone(), |root, t| {
|
||||
root.opacity(t)
|
||||
}),
|
||||
),
|
||||
)
|
||||
.with_priority(1)
|
||||
.into_any_element(),
|
||||
};
|
||||
@@ -393,6 +408,7 @@ pub(crate) fn hang(
|
||||
let row = state.read(cx).levels[level - 1].row;
|
||||
(row != Bounds::default()).then(|| {
|
||||
raise(
|
||||
(id.clone(), format!("level-{level}")),
|
||||
anchored().position(row.top_right()).snap_to_window().child(
|
||||
draw::panel(id, menu, state, level, &close, None, cx)
|
||||
.mt_neg_1()
|
||||
|
||||
@@ -303,6 +303,7 @@ impl RenderOnce for PieMenu {
|
||||
});
|
||||
region.child(
|
||||
raise(
|
||||
(self.id.clone(), "raised"),
|
||||
anchored()
|
||||
.position(point(Pixels::ZERO, Pixels::ZERO))
|
||||
.child(
|
||||
|
||||
@@ -197,6 +197,7 @@ impl RenderOnce for StatusSetter {
|
||||
done(window, cx);
|
||||
});
|
||||
float_height(
|
||||
popup_id.clone(),
|
||||
anchor,
|
||||
height,
|
||||
surface((popup_id, "popup"), cx)
|
||||
|
||||
@@ -236,7 +236,14 @@ impl RenderOnce for NavigationMenu {
|
||||
.on_mouse_down_out(move |_, _, cx| show(&close, None, cx))
|
||||
.children(links);
|
||||
let rows = entries[entry].1.len().div_ceil(2) * 2;
|
||||
float(state.read(cx).anchors[entry], rows, content, window, cx)
|
||||
float(
|
||||
(self.id.clone(), format!("menu-{entry}")),
|
||||
state.read(cx).anchors[entry],
|
||||
rows,
|
||||
content,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let (keys, key_entries, enter) = (state.clone(), entries.clone(), pick);
|
||||
div()
|
||||
|
||||
@@ -271,6 +271,7 @@ impl RenderOnce for Dialog {
|
||||
let viewport = window.viewport_size();
|
||||
let top = cx.theme().titlebar_height().to_pixels(window.rem_size());
|
||||
return raise(
|
||||
(self.id.clone(), "raised"),
|
||||
anchored().position(point(Pixels::ZERO, top)).child(
|
||||
div()
|
||||
.w(viewport.width)
|
||||
|
||||
@@ -69,6 +69,7 @@ impl RenderOnce for FloatingToolbar {
|
||||
let lift = cx.theme().float_gap().to_pixels(window.rem_size());
|
||||
host.child(
|
||||
raise(
|
||||
(self.id.clone(), "raised"),
|
||||
anchored()
|
||||
.position(anchor.origin - point(gpui::Pixels::ZERO, lift))
|
||||
.anchor(Corner::BottomLeft)
|
||||
|
||||
@@ -132,6 +132,13 @@ impl RenderOnce for HoverCard {
|
||||
.size_full(),
|
||||
);
|
||||
let (anchor, height) = (state.read(cx).host, state.read(cx).height);
|
||||
host.child(float_height(anchor, height, card, window, cx))
|
||||
host.child(float_height(
|
||||
self.id.clone(),
|
||||
anchor,
|
||||
height,
|
||||
card,
|
||||
window,
|
||||
cx,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -291,8 +291,11 @@ impl RenderOnce for Lightbox {
|
||||
),
|
||||
),
|
||||
);
|
||||
raise(anchored().position(Point::default()).child(stage))
|
||||
.with_priority(1)
|
||||
.into_any_element()
|
||||
raise(
|
||||
(self.id.clone(), "raised"),
|
||||
anchored().position(Point::default()).child(stage),
|
||||
)
|
||||
.with_priority(1)
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
@@ -176,6 +176,13 @@ impl RenderOnce for Popover {
|
||||
.left_0()
|
||||
.size_full(),
|
||||
);
|
||||
host.child(float_height(anchor, height, panel, window, cx))
|
||||
host.child(float_height(
|
||||
self.id.clone(),
|
||||
anchor,
|
||||
height,
|
||||
panel,
|
||||
window,
|
||||
cx,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -193,13 +193,21 @@ fn light(
|
||||
div()
|
||||
.child(
|
||||
raise(
|
||||
(id.clone(), "raised"),
|
||||
anchored()
|
||||
.position(Point::default())
|
||||
.child(div().w(viewport.width).h(viewport.height).child(frame)),
|
||||
)
|
||||
.with_priority(1),
|
||||
)
|
||||
.child(float_height(target, card_height, card, window, cx))
|
||||
.child(float_height(
|
||||
(id.clone(), "card"),
|
||||
target,
|
||||
card_height,
|
||||
card,
|
||||
window,
|
||||
cx,
|
||||
))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
|
||||
@@ -76,12 +76,13 @@ impl RenderOnce for Backdrop {
|
||||
scrim.on_mouse_down(MouseButton::Left, move |_, window, cx| dismiss(window, cx))
|
||||
})
|
||||
.with_animation(
|
||||
self.id,
|
||||
self.id.clone(),
|
||||
Animation::new(fade).with_easing(motion::ease_out_cubic),
|
||||
|scrim, t| scrim.opacity(t),
|
||||
);
|
||||
|
||||
raise(
|
||||
(self.id, "raised"),
|
||||
anchored().position(Point::default()).child(
|
||||
div()
|
||||
.relative()
|
||||
|
||||
+16
-15
@@ -5,8 +5,8 @@ use gpui::{
|
||||
InspectorElementId, IntoElement, LayoutId, Pixels, Point, Position, Style, Window, size,
|
||||
};
|
||||
|
||||
/// What was raised while a layer prepaints its content, each with where it sits and its priority.
|
||||
type Queue = Vec<(AnyElement, Point<Pixels>, usize)>;
|
||||
/// What was raised while a layer prepaints its content, each by its owner's id, with where it sits and its priority.
|
||||
type Queue = Vec<(ElementId, AnyElement, Point<Pixels>, usize)>;
|
||||
|
||||
thread_local! {
|
||||
static LAYERS: RefCell<Vec<Queue>> = const { RefCell::new(Vec::new()) };
|
||||
@@ -16,12 +16,13 @@ fn nested() -> bool {
|
||||
LAYERS.with_borrow(|layers| !layers.is_empty())
|
||||
}
|
||||
|
||||
/// Draws `child` over the page, as gpui's `deferred` does. gpui cannot defer a draw inside a deferred one, so what is raised inside something raised is laid out and drawn last within it: a list opened in a dialog lies over the dialog.
|
||||
pub fn raise(child: impl IntoElement) -> Raised {
|
||||
/// Draws `child` over the page, as gpui's `deferred` does. gpui cannot defer a draw inside a deferred one, so what is raised inside something raised is laid out and drawn last within it, under `id`: a list opened in a dialog lies over the dialog and keeps its state.
|
||||
pub fn raise(id: impl Into<ElementId>, child: impl IntoElement) -> Raised {
|
||||
let layer = Layer {
|
||||
child: child.into_any_element(),
|
||||
};
|
||||
Raised {
|
||||
id: id.into(),
|
||||
child: Some(layer.into_any_element()),
|
||||
priority: 0,
|
||||
nested: false,
|
||||
@@ -30,6 +31,7 @@ pub fn raise(child: impl IntoElement) -> Raised {
|
||||
|
||||
/// An element drawn over the page; see [`raise`].
|
||||
pub struct Raised {
|
||||
id: ElementId,
|
||||
child: Option<AnyElement>,
|
||||
priority: usize,
|
||||
nested: bool,
|
||||
@@ -100,7 +102,7 @@ impl Element for Raised {
|
||||
let queue = layers
|
||||
.last_mut()
|
||||
.expect("a nested element prepaints in its layer");
|
||||
queue.push((child, bounds.origin, self.priority));
|
||||
queue.push((self.id.clone(), child, bounds.origin, self.priority));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -141,7 +143,7 @@ fn collect(f: impl FnOnce()) -> Queue {
|
||||
|
||||
impl Element for Layer {
|
||||
type RequestLayoutState = ();
|
||||
type PrepaintState = Vec<AnyElement>;
|
||||
type PrepaintState = Vec<(ElementId, AnyElement)>;
|
||||
|
||||
fn id(&self) -> Option<ElementId> {
|
||||
None
|
||||
@@ -171,11 +173,11 @@ impl Element for Layer {
|
||||
_: &mut (),
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Vec<AnyElement> {
|
||||
) -> Vec<(ElementId, AnyElement)> {
|
||||
let mut raised = collect(|| {
|
||||
self.child.prepaint(window, cx);
|
||||
});
|
||||
raised.sort_by_key(|(_, _, priority)| *priority);
|
||||
raised.sort_by_key(|(_, _, _, priority)| *priority);
|
||||
let viewport = window.viewport_size();
|
||||
let space = size(
|
||||
AvailableSpace::Definite(viewport.width),
|
||||
@@ -183,12 +185,11 @@ impl Element for Layer {
|
||||
);
|
||||
raised
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(ix, (mut element, at, _))| {
|
||||
window.with_element_namespace(("raised", ix), |window| {
|
||||
.map(|(id, mut element, at, _)| {
|
||||
window.with_element_namespace(id.clone(), |window| {
|
||||
element.prepaint_as_root(at, space, window, cx)
|
||||
});
|
||||
element
|
||||
(id, element)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -199,13 +200,13 @@ impl Element for Layer {
|
||||
_: Option<&InspectorElementId>,
|
||||
_: Bounds<Pixels>,
|
||||
_: &mut (),
|
||||
raised: &mut Vec<AnyElement>,
|
||||
raised: &mut Vec<(ElementId, AnyElement)>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
self.child.paint(window, cx);
|
||||
for (ix, element) in raised.iter_mut().enumerate() {
|
||||
window.with_element_namespace(("raised", ix), |window| element.paint(window, cx));
|
||||
for (id, element) in raised.iter_mut() {
|
||||
window.with_element_namespace(id.clone(), |window| element.paint(window, cx));
|
||||
}
|
||||
}
|
||||
}
|
||||
+81
-2
@@ -1,6 +1,8 @@
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use gpui::{
|
||||
Context, IntoElement, Modifiers, ParentElement, Render, Styled, TestAppContext, Window,
|
||||
anchored, div, point, prelude::*, px,
|
||||
App, Context, Entity, FocusHandle, IntoElement, Modifiers, ParentElement, Render, RenderOnce,
|
||||
Styled, TestAppContext, Window, anchored, div, point, prelude::*, px,
|
||||
};
|
||||
|
||||
use super::raise;
|
||||
@@ -27,11 +29,13 @@ impl Render for Stack {
|
||||
.size(px(200.0))
|
||||
.on_click(move |_, _, cx| cover.update(cx, |stack, _| stack.heard.push("cover")));
|
||||
div().size_full().child(raise(
|
||||
"sheet",
|
||||
anchored().position(point(px(0.0), px(0.0))).child(
|
||||
div()
|
||||
.relative()
|
||||
.size(px(200.0))
|
||||
.child(raise(
|
||||
"button",
|
||||
anchored().position(point(px(50.0), px(50.0))).child(button),
|
||||
))
|
||||
.child(sheet),
|
||||
@@ -51,3 +55,78 @@ fn what_is_raised_inside_a_raise_lies_over_it(cx: &mut TestAppContext) {
|
||||
["inner"]
|
||||
);
|
||||
}
|
||||
|
||||
/// A focusable box whose focus handle lives in keyed state; each draw tells the test which one it drew.
|
||||
#[derive(IntoElement)]
|
||||
struct Held {
|
||||
seen: Rc<RefCell<Vec<Entity<FocusHandle>>>>,
|
||||
}
|
||||
|
||||
impl RenderOnce for Held {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let kept = window.use_keyed_state("kept", cx, |_, cx| cx.focus_handle());
|
||||
self.seen.borrow_mut().push(kept.clone());
|
||||
let handle = kept.read(cx).clone();
|
||||
div().id("held").size(px(40.0)).track_focus(&handle)
|
||||
}
|
||||
}
|
||||
|
||||
/// A raised sheet holding a raised tip that comes and goes, then a raised box that must stay itself.
|
||||
struct Nest {
|
||||
tip: bool,
|
||||
seen: Rc<RefCell<Vec<Entity<FocusHandle>>>>,
|
||||
}
|
||||
|
||||
impl Render for Nest {
|
||||
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
|
||||
let tip = self.tip.then(|| {
|
||||
raise(
|
||||
"tip",
|
||||
anchored()
|
||||
.position(point(px(0.0), px(0.0)))
|
||||
.child(div().child("tip")),
|
||||
)
|
||||
});
|
||||
let held = Held {
|
||||
seen: self.seen.clone(),
|
||||
};
|
||||
div().size_full().child(raise(
|
||||
"sheet",
|
||||
anchored().position(point(px(0.0), px(0.0))).child(
|
||||
div().size(px(200.0)).children(tip).child(raise(
|
||||
"held",
|
||||
anchored().position(point(px(50.0), px(50.0))).child(held),
|
||||
)),
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn a_raise_keeps_its_state_while_another_comes_before_it(cx: &mut TestAppContext) {
|
||||
cx.update(Theme::init);
|
||||
let seen = Rc::new(RefCell::new(Vec::new()));
|
||||
let (view, cx) = cx.add_window_view(|_, _| Nest {
|
||||
tip: false,
|
||||
seen: seen.clone(),
|
||||
});
|
||||
cx.run_until_parked();
|
||||
let first = seen.borrow().last().cloned().expect("the box drew");
|
||||
let handle = first.read_with(cx, |handle, _| handle.clone());
|
||||
cx.update(|window, _| window.focus(&handle));
|
||||
view.update(cx, |nest, cx| {
|
||||
nest.tip = true;
|
||||
cx.notify();
|
||||
});
|
||||
cx.run_until_parked();
|
||||
let last = seen.borrow().last().cloned().expect("the box drew again");
|
||||
assert_eq!(
|
||||
last.entity_id(),
|
||||
first.entity_id(),
|
||||
"the box kept its state"
|
||||
);
|
||||
assert!(
|
||||
cx.update(|window, _| handle.is_focused(window)),
|
||||
"the box kept focus"
|
||||
);
|
||||
}
|
||||
@@ -139,6 +139,7 @@ impl RenderOnce for TooltipTrigger {
|
||||
let (delay, follow) = (self.delay, self.follow);
|
||||
let content = self.content;
|
||||
|
||||
let id = self.id.clone();
|
||||
div()
|
||||
.id(self.id)
|
||||
.child(self.child)
|
||||
@@ -177,6 +178,7 @@ impl RenderOnce for TooltipTrigger {
|
||||
let body = content(window, cx);
|
||||
let below = cx.theme().cursor_offset().to_pixels(window.rem_size());
|
||||
trigger.child(raise(
|
||||
(id, "raised"),
|
||||
anchored()
|
||||
.position(mouse + point(Pixels::ZERO, below))
|
||||
.snap_to_window()
|
||||
|
||||
Reference in new issue
Block a user