gpui_base/motion/
reveal.rs1use gpui::{
2 AnyElement, App, AvailableSpace, Bounds, ContentMask, Element, ElementId, GlobalElementId,
3 InspectorElementId, IntoElement, LayoutId, Pixels, Style, Window, px, relative, size,
4};
5
6pub struct MotionReveal {
8 id: ElementId,
9 progress: f32,
10 child: AnyElement,
11}
12
13#[derive(Clone, Copy, Default)]
14struct RevealState {
15 height: Option<Pixels>,
16}
17
18impl MotionReveal {
19 pub fn new(id: impl Into<ElementId>, progress: f32, child: AnyElement) -> Self {
20 Self {
21 id: id.into(),
22 progress: progress.clamp(0.0, 1.0),
23 child,
24 }
25 }
26}
27
28impl IntoElement for MotionReveal {
29 type Element = Self;
30
31 fn into_element(self) -> Self::Element {
32 self
33 }
34}
35
36impl Element for MotionReveal {
37 type RequestLayoutState = ();
38 type PrepaintState = ();
39
40 fn id(&self) -> Option<ElementId> {
41 Some(self.id.clone())
42 }
43
44 fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
45 None
46 }
47
48 fn request_layout(
49 &mut self,
50 global_id: Option<&GlobalElementId>,
51 _: Option<&InspectorElementId>,
52 window: &mut Window,
53 cx: &mut App,
54 ) -> (LayoutId, Self::RequestLayoutState) {
55 let height = window.with_element_state(
56 global_id.expect("MotionReveal must have an id"),
57 |state: Option<RevealState>, _| {
58 let state = state.unwrap_or_default();
59 (state.height, state)
60 },
61 );
62 let mut style = Style::default();
63 style.size.width = relative(1.0).into();
64 match height {
65 None if self.progress > 0.0 => {}
66 None => style.size.height = px(0.0).into(),
67 Some(height) => style.size.height = (height * self.progress).into(),
68 }
69 (window.request_layout(style, None, cx), ())
70 }
71
72 fn prepaint(
73 &mut self,
74 global_id: Option<&GlobalElementId>,
75 _: Option<&InspectorElementId>,
76 bounds: Bounds<Pixels>,
77 _: &mut Self::RequestLayoutState,
78 window: &mut Window,
79 cx: &mut App,
80 ) -> Self::PrepaintState {
81 let measured = self.child.layout_as_root(
82 size(
83 AvailableSpace::Definite(bounds.size.width),
84 AvailableSpace::MinContent,
85 ),
86 window,
87 cx,
88 );
89 let changed = window.with_element_state(
90 global_id.expect("MotionReveal must have an id"),
91 |state: Option<RevealState>, _| {
92 let mut state = state.unwrap_or_default();
93 let changed = state.height != Some(measured.height);
94 state.height = Some(measured.height);
95 (changed, state)
96 },
97 );
98 if changed {
99 window.request_animation_frame();
100 }
101 window.with_content_mask(Some(ContentMask { bounds }), |window| {
102 self.child.prepaint_at(bounds.origin, window, cx);
103 });
104 }
105
106 fn paint(
107 &mut self,
108 _: Option<&GlobalElementId>,
109 _: Option<&InspectorElementId>,
110 bounds: Bounds<Pixels>,
111 _: &mut Self::RequestLayoutState,
112 _: &mut Self::PrepaintState,
113 window: &mut Window,
114 cx: &mut App,
115 ) {
116 window.with_content_mask(Some(ContentMask { bounds }), |window| {
117 self.child.paint(window, cx);
118 });
119 }
120}