Skip to content

Commit 65be3f8

Browse files
tarkahhecrj
authored andcommitted
Use overlay for Tooltip widget
1 parent 63b2dc3 commit 65be3f8

1 file changed

Lines changed: 160 additions & 97 deletions

File tree

widget/src/tooltip.rs

Lines changed: 160 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
//! Display a widget over another.
22
use crate::container;
3-
use crate::core;
43
use crate::core::event::{self, Event};
54
use crate::core::layout::{self, Layout};
65
use crate::core::mouse;
76
use crate::core::overlay;
87
use crate::core::renderer;
98
use crate::core::text;
10-
use crate::core::widget::Tree;
9+
use crate::core::widget::{self, Widget};
1110
use crate::core::{
12-
Clipboard, Element, Length, Padding, Pixels, Rectangle, Shell, Size,
13-
Vector, Widget,
11+
Clipboard, Element, Length, Padding, Pixels, Point, Rectangle, Shell, Size,
12+
Vector,
1413
};
1514
use crate::Text;
1615

@@ -107,14 +106,22 @@ where
107106
Renderer: text::Renderer,
108107
Renderer::Theme: container::StyleSheet + crate::text::StyleSheet,
109108
{
110-
fn children(&self) -> Vec<Tree> {
111-
vec![Tree::new(&self.content)]
109+
fn children(&self) -> Vec<widget::Tree> {
110+
vec![widget::Tree::new(&self.content)]
112111
}
113112

114-
fn diff(&self, tree: &mut Tree) {
113+
fn diff(&self, tree: &mut widget::Tree) {
115114
tree.diff_children(std::slice::from_ref(&self.content))
116115
}
117116

117+
fn state(&self) -> widget::tree::State {
118+
widget::tree::State::new(State::default())
119+
}
120+
121+
fn tag(&self) -> widget::tree::Tag {
122+
widget::tree::Tag::of::<State>()
123+
}
124+
118125
fn width(&self) -> Length {
119126
self.content.as_widget().width()
120127
}
@@ -133,14 +140,21 @@ where
133140

134141
fn on_event(
135142
&mut self,
136-
tree: &mut Tree,
143+
tree: &mut widget::Tree,
137144
event: Event,
138145
layout: Layout<'_>,
139146
cursor: mouse::Cursor,
140147
renderer: &Renderer,
141148
clipboard: &mut dyn Clipboard,
142149
shell: &mut Shell<'_, Message>,
143150
) -> event::Status {
151+
let state = tree.state.downcast_mut::<State>();
152+
153+
*state = cursor
154+
.position_over(layout.bounds())
155+
.map(|cursor_position| State::Hovered { cursor_position })
156+
.unwrap_or_default();
157+
144158
self.content.as_widget_mut().on_event(
145159
&mut tree.children[0],
146160
event,
@@ -154,7 +168,7 @@ where
154168

155169
fn mouse_interaction(
156170
&self,
157-
tree: &Tree,
171+
tree: &widget::Tree,
158172
layout: Layout<'_>,
159173
cursor: mouse::Cursor,
160174
viewport: &Rectangle,
@@ -171,7 +185,7 @@ where
171185

172186
fn draw(
173187
&self,
174-
tree: &Tree,
188+
tree: &widget::Tree,
175189
renderer: &mut Renderer,
176190
theme: &Renderer::Theme,
177191
inherited_style: &renderer::Style,
@@ -188,50 +202,50 @@ where
188202
cursor,
189203
viewport,
190204
);
191-
192-
let tooltip = &self.tooltip;
193-
194-
draw(
195-
renderer,
196-
theme,
197-
inherited_style,
198-
layout,
199-
cursor,
200-
viewport,
201-
self.position,
202-
self.gap,
203-
self.padding,
204-
self.snap_within_viewport,
205-
&self.style,
206-
|renderer, limits| {
207-
Widget::<(), Renderer>::layout(tooltip, renderer, limits)
208-
},
209-
|renderer, defaults, layout, viewport| {
210-
Widget::<(), Renderer>::draw(
211-
tooltip,
212-
&Tree::empty(),
213-
renderer,
214-
theme,
215-
defaults,
216-
layout,
217-
cursor,
218-
viewport,
219-
);
220-
},
221-
);
222205
}
223206

224207
fn overlay<'b>(
225208
&'b mut self,
226-
tree: &'b mut Tree,
209+
tree: &'b mut widget::Tree,
227210
layout: Layout<'_>,
228211
renderer: &Renderer,
229212
) -> Option<overlay::Element<'b, Message, Renderer>> {
230-
self.content.as_widget_mut().overlay(
213+
let state = tree.state.downcast_ref::<State>();
214+
215+
let content = self.content.as_widget_mut().overlay(
231216
&mut tree.children[0],
232217
layout,
233218
renderer,
234-
)
219+
);
220+
221+
let tooltip = if let State::Hovered { cursor_position } = *state {
222+
Some(overlay::Element::new(
223+
layout.position(),
224+
Box::new(Overlay {
225+
tooltip: &self.tooltip,
226+
cursor_position,
227+
content_bounds: layout.bounds(),
228+
snap_within_viewport: self.snap_within_viewport,
229+
position: self.position,
230+
gap: self.gap,
231+
padding: self.padding,
232+
style: &self.style,
233+
}),
234+
))
235+
} else {
236+
None
237+
};
238+
239+
if content.is_some() || tooltip.is_some() {
240+
Some(
241+
overlay::Group::with_children(
242+
content.into_iter().chain(tooltip).collect(),
243+
)
244+
.overlay(),
245+
)
246+
} else {
247+
None
248+
}
235249
}
236250
}
237251

@@ -264,72 +278,93 @@ pub enum Position {
264278
Right,
265279
}
266280

267-
/// Draws a [`Tooltip`].
268-
pub fn draw<Renderer>(
269-
renderer: &mut Renderer,
270-
theme: &Renderer::Theme,
271-
inherited_style: &renderer::Style,
272-
layout: Layout<'_>,
273-
cursor: mouse::Cursor,
274-
viewport: &Rectangle,
281+
#[derive(Debug, Clone, Copy, Default)]
282+
enum State {
283+
#[default]
284+
Idle,
285+
Hovered {
286+
cursor_position: Point,
287+
},
288+
}
289+
290+
struct Overlay<'a, 'b, Renderer>
291+
where
292+
Renderer: text::Renderer,
293+
Renderer::Theme: container::StyleSheet + widget::text::StyleSheet,
294+
{
295+
tooltip: &'b Text<'a, Renderer>,
296+
cursor_position: Point,
297+
content_bounds: Rectangle,
298+
snap_within_viewport: bool,
275299
position: Position,
276300
gap: f32,
277301
padding: f32,
278-
snap_within_viewport: bool,
279-
style: &<Renderer::Theme as container::StyleSheet>::Style,
280-
layout_text: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node,
281-
draw_text: impl FnOnce(&mut Renderer, &renderer::Style, Layout<'_>, &Rectangle),
282-
) where
283-
Renderer: core::Renderer,
284-
Renderer::Theme: container::StyleSheet,
285-
{
286-
use container::StyleSheet;
287-
288-
let bounds = layout.bounds();
302+
style: &'b <Renderer::Theme as container::StyleSheet>::Style,
303+
}
289304

290-
if let Some(cursor_position) = cursor.position_over(bounds) {
291-
let style = theme.appearance(style);
305+
impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer>
306+
for Overlay<'a, 'b, Renderer>
307+
where
308+
Renderer: text::Renderer,
309+
Renderer::Theme: container::StyleSheet + widget::text::StyleSheet,
310+
{
311+
fn layout(
312+
&self,
313+
renderer: &Renderer,
314+
bounds: Size,
315+
_position: Point,
316+
) -> layout::Node {
317+
let viewport = Rectangle::with_size(bounds);
292318

293-
let defaults = renderer::Style {
294-
text_color: style.text_color.unwrap_or(inherited_style.text_color),
295-
};
319+
let gap = f32::from(self.gap);
296320

297-
let text_layout = layout_text(
321+
let text_layout = Widget::<(), Renderer>::layout(
322+
self.tooltip,
298323
renderer,
299324
&layout::Limits::new(
300325
Size::ZERO,
301-
snap_within_viewport
326+
self.snap_within_viewport
302327
.then(|| viewport.size())
303328
.unwrap_or(Size::INFINITY),
304329
)
305-
.pad(Padding::new(padding)),
330+
.pad(Padding::new(self.padding)),
306331
);
307332

333+
let padding = f32::from(self.padding);
334+
308335
let text_bounds = text_layout.bounds();
309-
let x_center = bounds.x + (bounds.width - text_bounds.width) / 2.0;
310-
let y_center = bounds.y + (bounds.height - text_bounds.height) / 2.0;
336+
let x_center = self.content_bounds.x
337+
+ (self.content_bounds.width - text_bounds.width) / 2.0;
338+
let y_center = self.content_bounds.y
339+
+ (self.content_bounds.height - text_bounds.height) / 2.0;
311340

312341
let mut tooltip_bounds = {
313-
let offset = match position {
342+
let offset = match self.position {
314343
Position::Top => Vector::new(
315344
x_center,
316-
bounds.y - text_bounds.height - gap - padding,
345+
self.content_bounds.y - text_bounds.height - gap - padding,
317346
),
318347
Position::Bottom => Vector::new(
319348
x_center,
320-
bounds.y + bounds.height + gap + padding,
349+
self.content_bounds.y
350+
+ self.content_bounds.height
351+
+ gap
352+
+ padding,
321353
),
322354
Position::Left => Vector::new(
323-
bounds.x - text_bounds.width - gap - padding,
355+
self.content_bounds.x - text_bounds.width - gap - padding,
324356
y_center,
325357
),
326358
Position::Right => Vector::new(
327-
bounds.x + bounds.width + gap + padding,
359+
self.content_bounds.x
360+
+ self.content_bounds.width
361+
+ gap
362+
+ padding,
328363
y_center,
329364
),
330365
Position::FollowCursor => Vector::new(
331-
cursor_position.x,
332-
cursor_position.y - text_bounds.height,
366+
self.cursor_position.x,
367+
self.cursor_position.y - text_bounds.height,
333368
),
334369
};
335370

@@ -341,7 +376,7 @@ pub fn draw<Renderer>(
341376
}
342377
};
343378

344-
if snap_within_viewport {
379+
if self.snap_within_viewport {
345380
if tooltip_bounds.x < viewport.x {
346381
tooltip_bounds.x = viewport.x;
347382
} else if viewport.x + viewport.width
@@ -361,21 +396,49 @@ pub fn draw<Renderer>(
361396
}
362397
}
363398

364-
renderer.with_layer(Rectangle::with_size(Size::INFINITY), |renderer| {
365-
container::draw_background(renderer, &style, tooltip_bounds);
366-
367-
draw_text(
368-
renderer,
369-
&defaults,
370-
Layout::with_offset(
371-
Vector::new(
372-
tooltip_bounds.x + padding,
373-
tooltip_bounds.y + padding,
374-
),
375-
&text_layout,
376-
),
377-
viewport,
378-
)
379-
});
399+
layout::Node::with_children(
400+
tooltip_bounds.size(),
401+
vec![text_layout.translate(Vector::new(padding, padding))],
402+
)
403+
.translate(Vector::new(tooltip_bounds.x, tooltip_bounds.y))
404+
}
405+
406+
fn draw(
407+
&self,
408+
renderer: &mut Renderer,
409+
theme: &<Renderer as renderer::Renderer>::Theme,
410+
inherited_style: &renderer::Style,
411+
layout: Layout<'_>,
412+
cursor_position: mouse::Cursor,
413+
) {
414+
let style = <Renderer::Theme as container::StyleSheet>::appearance(
415+
theme, self.style,
416+
);
417+
418+
container::draw_background(renderer, &style, layout.bounds());
419+
420+
let defaults = renderer::Style {
421+
text_color: style.text_color.unwrap_or(inherited_style.text_color),
422+
};
423+
424+
Widget::<(), Renderer>::draw(
425+
self.tooltip,
426+
&widget::Tree::empty(),
427+
renderer,
428+
theme,
429+
&defaults,
430+
layout.children().next().unwrap(),
431+
cursor_position,
432+
&Rectangle::with_size(Size::INFINITY),
433+
);
434+
}
435+
436+
fn is_over(
437+
&self,
438+
_layout: Layout<'_>,
439+
_renderer: &Renderer,
440+
_cursor_position: Point,
441+
) -> bool {
442+
false
380443
}
381444
}

0 commit comments

Comments
 (0)