Skip to content

Commit 9f2be29

Browse files
authored
Merge pull request #1692 from tarkah/fix/tooltip-overlay
Use overlay for tooltip
2 parents 63b2dc3 + 896a90d commit 9f2be29

1 file changed

Lines changed: 167 additions & 102 deletions

File tree

widget/src/tooltip.rs

Lines changed: 167 additions & 102 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,84 +278,107 @@ 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();
289-
290-
if let Some(cursor_position) = cursor.position_over(bounds) {
291-
let style = theme.appearance(style);
302+
style: &'b <Renderer::Theme as container::StyleSheet>::Style,
303+
}
292304

293-
let defaults = renderer::Style {
294-
text_color: style.text_color.unwrap_or(inherited_style.text_color),
295-
};
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);
296318

297-
let text_layout = layout_text(
319+
let text_layout = Widget::<(), Renderer>::layout(
320+
self.tooltip,
298321
renderer,
299322
&layout::Limits::new(
300323
Size::ZERO,
301-
snap_within_viewport
324+
self.snap_within_viewport
302325
.then(|| viewport.size())
303326
.unwrap_or(Size::INFINITY),
304327
)
305-
.pad(Padding::new(padding)),
328+
.pad(Padding::new(self.padding)),
306329
);
307330

308331
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;
332+
let x_center = self.content_bounds.x
333+
+ (self.content_bounds.width - text_bounds.width) / 2.0;
334+
let y_center = self.content_bounds.y
335+
+ (self.content_bounds.height - text_bounds.height) / 2.0;
311336

312337
let mut tooltip_bounds = {
313-
let offset = match position {
338+
let offset = match self.position {
314339
Position::Top => Vector::new(
315340
x_center,
316-
bounds.y - text_bounds.height - gap - padding,
341+
self.content_bounds.y
342+
- text_bounds.height
343+
- self.gap
344+
- self.padding,
317345
),
318346
Position::Bottom => Vector::new(
319347
x_center,
320-
bounds.y + bounds.height + gap + padding,
348+
self.content_bounds.y
349+
+ self.content_bounds.height
350+
+ self.gap
351+
+ self.padding,
321352
),
322353
Position::Left => Vector::new(
323-
bounds.x - text_bounds.width - gap - padding,
354+
self.content_bounds.x
355+
- text_bounds.width
356+
- self.gap
357+
- self.padding,
324358
y_center,
325359
),
326360
Position::Right => Vector::new(
327-
bounds.x + bounds.width + gap + padding,
361+
self.content_bounds.x
362+
+ self.content_bounds.width
363+
+ self.gap
364+
+ self.padding,
328365
y_center,
329366
),
330367
Position::FollowCursor => Vector::new(
331-
cursor_position.x,
332-
cursor_position.y - text_bounds.height,
368+
self.cursor_position.x,
369+
self.cursor_position.y - text_bounds.height,
333370
),
334371
};
335372

336373
Rectangle {
337-
x: offset.x - padding,
338-
y: offset.y - padding,
339-
width: text_bounds.width + padding * 2.0,
340-
height: text_bounds.height + padding * 2.0,
374+
x: offset.x - self.padding,
375+
y: offset.y - self.padding,
376+
width: text_bounds.width + self.padding * 2.0,
377+
height: text_bounds.height + self.padding * 2.0,
341378
}
342379
};
343380

344-
if snap_within_viewport {
381+
if self.snap_within_viewport {
345382
if tooltip_bounds.x < viewport.x {
346383
tooltip_bounds.x = viewport.x;
347384
} else if viewport.x + viewport.width
@@ -361,21 +398,49 @@ pub fn draw<Renderer>(
361398
}
362399
}
363400

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-
});
401+
layout::Node::with_children(
402+
tooltip_bounds.size(),
403+
vec![text_layout.translate(Vector::new(self.padding, self.padding))],
404+
)
405+
.translate(Vector::new(tooltip_bounds.x, tooltip_bounds.y))
406+
}
407+
408+
fn draw(
409+
&self,
410+
renderer: &mut Renderer,
411+
theme: &<Renderer as renderer::Renderer>::Theme,
412+
inherited_style: &renderer::Style,
413+
layout: Layout<'_>,
414+
cursor_position: mouse::Cursor,
415+
) {
416+
let style = <Renderer::Theme as container::StyleSheet>::appearance(
417+
theme, self.style,
418+
);
419+
420+
container::draw_background(renderer, &style, layout.bounds());
421+
422+
let defaults = renderer::Style {
423+
text_color: style.text_color.unwrap_or(inherited_style.text_color),
424+
};
425+
426+
Widget::<(), Renderer>::draw(
427+
self.tooltip,
428+
&widget::Tree::empty(),
429+
renderer,
430+
theme,
431+
&defaults,
432+
layout.children().next().unwrap(),
433+
cursor_position,
434+
&Rectangle::with_size(Size::INFINITY),
435+
);
436+
}
437+
438+
fn is_over(
439+
&self,
440+
_layout: Layout<'_>,
441+
_renderer: &Renderer,
442+
_cursor_position: Point,
443+
) -> bool {
444+
false
380445
}
381446
}

0 commit comments

Comments
 (0)