tilo_app/framework/
page_part.rs1use std::cell::RefCell;
11use std::rc::Rc;
12
13use ui_core::paint::PaintCtx;
14use ui_core::presence::PresenceCtx;
15use ui_core::vello::kurbo::Rect;
16use ui_core::widget::{Column, Element, Embedded, Widget};
17use ui_core::winit::event::WindowEvent;
18
19pub type Asked<A> = Rc<RefCell<Vec<A>>>;
21
22pub fn asking<A: Clone + 'static>(asked: &Asked<A>, action: A) -> impl FnMut() + 'static {
24 let asked = Rc::clone(asked);
25 move || asked.borrow_mut().push(action.clone())
26}
27
28pub struct Placed<W: Widget> {
32 host: Embedded<W>,
33 placed: bool,
34}
35
36impl<W: Widget + 'static> Placed<W> {
37 pub fn new(widget: W) -> Self {
38 Self {
39 host: Embedded::new(widget),
40 placed: false,
41 }
42 }
43
44 pub fn replace(&mut self, widget: W) {
46 self.host = Embedded::new(widget);
47 }
48
49 pub fn unplace(&mut self) {
52 self.placed = false;
53 }
54
55 pub fn widget(&self) -> &W {
56 self.host.widget()
57 }
58
59 pub fn widget_mut(&mut self) -> &mut W {
60 self.host.widget_mut()
61 }
62
63 pub fn visible(&self) -> Option<&W> {
65 self.placed.then(|| self.host.widget())
66 }
67
68 pub fn visible_mut(&mut self) -> Option<&mut W> {
70 self.placed.then(|| self.host.widget_mut())
71 }
72
73 pub fn layout(&mut self, rect: Rect, ctx: &mut PaintCtx<'_>) -> f64 {
74 self.host.layout(rect, ctx.theme, ctx.text)
75 }
76
77 pub fn draw(&mut self, ctx: &mut PaintCtx<'_>, clip: Rect) {
80 let presence = PresenceCtx::default();
81 let outer = presence.push_viewport(clip);
82 self.host
83 .draw(ctx.scene, ctx.transform, ctx.text, ctx.theme, &presence);
84 presence.pop_viewport(outer);
85 self.placed = true;
86 }
87
88 pub fn place(
91 &mut self,
92 ctx: &mut PaintCtx<'_>,
93 (left, right): (f64, f64),
94 top: f64,
95 clip: Rect,
96 ) -> f64 {
97 let bottom = top + self.layout(Rect::new(left, top, right, top), ctx);
98 if bottom > clip.y0 && top < clip.y1 {
99 self.draw(ctx, clip);
100 }
101 bottom
102 }
103
104 pub fn handle_event(&mut self, event: &WindowEvent, ctx: &mut ui_core::widget::EventCtx<'_>) {
106 if !self.placed {
107 return;
108 }
109 self.host.handle_event(
110 event,
111 ctx.scale_factor,
112 ctx.modifiers,
113 &mut ctx.state,
114 ctx.path,
115 ctx.handled,
116 );
117 }
118
119 pub fn tooltip_at(&self, point: ui_core::vello::kurbo::Point) -> Option<String> {
120 fn find(widget: &dyn Widget, point: ui_core::vello::kurbo::Point) -> Option<String> {
121 for child in widget.children() {
122 if let Some(found) = find(child, point) {
123 return Some(found);
124 }
125 }
126 (widget.get_rect().contains(point))
127 .then(|| widget.tooltip().map(str::to_owned))
128 .flatten()
129 }
130 find(self.visible()?, point)
131 }
132}
133
134pub struct PagePart<A> {
141 pub host: Placed<Column>,
142 key: String,
143 asked: Asked<A>,
144}
145
146impl<A> Default for PagePart<A> {
147 fn default() -> Self {
148 Self::new()
149 }
150}
151
152impl<A> PagePart<A> {
153 #[must_use]
154 pub fn new() -> Self {
155 Self {
156 host: Placed::new(Column::new().with_no_padding()),
157 key: String::new(),
158 asked: Rc::default(),
159 }
160 }
161
162 pub fn child(&self) -> Option<&dyn Widget> {
165 self.host.visible().map(|column| column as &dyn Widget)
166 }
167
168 pub fn child_mut(&mut self) -> Option<&mut dyn Widget> {
170 self.host
171 .visible_mut()
172 .map(|column| column as &mut dyn Widget)
173 }
174
175 pub fn update(&mut self, key: String, build: impl FnOnce(&Asked<A>) -> Element) {
178 self.host.unplace();
179 if key == self.key {
180 return;
181 }
182 self.key = key;
183 let widget = build(&self.asked);
184 self.host.replace(
185 Column::new()
186 .with_no_padding()
187 .with_intrinsic_height()
188 .push(widget),
189 );
190 }
191
192 pub fn take_asked(&self) -> Vec<A> {
194 std::mem::take(&mut *self.asked.borrow_mut())
195 }
196
197 pub fn find(&self, id: &str) -> Option<Rect> {
199 ui_core::widget::find_test_id(self.host.widget(), id).map(ui_core::widget::Widget::get_rect)
200 }
201}