diff options
author | 2023-07-12 03:59:15 +0200 | |
---|---|---|
committer | 2023-07-12 03:59:15 +0200 | |
commit | 9f2be29a286d435b3d1daa8025a74063c50713cb (patch) | |
tree | a713276a1162e2eae271f910e9f60e283112969e | |
parent | 63b2dc367e5c8b78255acea6269dfd173a26e36c (diff) | |
parent | 896a90decbd9ab57488620f27ae5c03ee0b9dab3 (diff) | |
download | iced-9f2be29a286d435b3d1daa8025a74063c50713cb.tar.gz iced-9f2be29a286d435b3d1daa8025a74063c50713cb.tar.bz2 iced-9f2be29a286d435b3d1daa8025a74063c50713cb.zip |
Merge pull request #1692 from tarkah/fix/tooltip-overlay
Use overlay for tooltip
Diffstat (limited to '')
-rw-r--r-- | widget/src/tooltip.rs | 269 |
1 files changed, 167 insertions, 102 deletions
diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index d425de01..2dc3da01 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -1,16 +1,15 @@ //! Display a widget over another. use crate::container; -use crate::core; use crate::core::event::{self, Event}; use crate::core::layout::{self, Layout}; use crate::core::mouse; use crate::core::overlay; use crate::core::renderer; use crate::core::text; -use crate::core::widget::Tree; +use crate::core::widget::{self, Widget}; use crate::core::{ - Clipboard, Element, Length, Padding, Pixels, Rectangle, Shell, Size, - Vector, Widget, + Clipboard, Element, Length, Padding, Pixels, Point, Rectangle, Shell, Size, + Vector, }; use crate::Text; @@ -107,14 +106,22 @@ where Renderer: text::Renderer, Renderer::Theme: container::StyleSheet + crate::text::StyleSheet, { - fn children(&self) -> Vec<Tree> { - vec![Tree::new(&self.content)] + fn children(&self) -> Vec<widget::Tree> { + vec![widget::Tree::new(&self.content)] } - fn diff(&self, tree: &mut Tree) { + fn diff(&self, tree: &mut widget::Tree) { tree.diff_children(std::slice::from_ref(&self.content)) } + fn state(&self) -> widget::tree::State { + widget::tree::State::new(State::default()) + } + + fn tag(&self) -> widget::tree::Tag { + widget::tree::Tag::of::<State>() + } + fn width(&self) -> Length { self.content.as_widget().width() } @@ -133,7 +140,7 @@ where fn on_event( &mut self, - tree: &mut Tree, + tree: &mut widget::Tree, event: Event, layout: Layout<'_>, cursor: mouse::Cursor, @@ -141,6 +148,13 @@ where clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { + let state = tree.state.downcast_mut::<State>(); + + *state = cursor + .position_over(layout.bounds()) + .map(|cursor_position| State::Hovered { cursor_position }) + .unwrap_or_default(); + self.content.as_widget_mut().on_event( &mut tree.children[0], event, @@ -154,7 +168,7 @@ where fn mouse_interaction( &self, - tree: &Tree, + tree: &widget::Tree, layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, @@ -171,7 +185,7 @@ where fn draw( &self, - tree: &Tree, + tree: &widget::Tree, renderer: &mut Renderer, theme: &Renderer::Theme, inherited_style: &renderer::Style, @@ -188,50 +202,50 @@ where cursor, viewport, ); - - let tooltip = &self.tooltip; - - draw( - renderer, - theme, - inherited_style, - layout, - cursor, - viewport, - self.position, - self.gap, - self.padding, - self.snap_within_viewport, - &self.style, - |renderer, limits| { - Widget::<(), Renderer>::layout(tooltip, renderer, limits) - }, - |renderer, defaults, layout, viewport| { - Widget::<(), Renderer>::draw( - tooltip, - &Tree::empty(), - renderer, - theme, - defaults, - layout, - cursor, - viewport, - ); - }, - ); } fn overlay<'b>( &'b mut self, - tree: &'b mut Tree, + tree: &'b mut widget::Tree, layout: Layout<'_>, renderer: &Renderer, ) -> Option<overlay::Element<'b, Message, Renderer>> { - self.content.as_widget_mut().overlay( + let state = tree.state.downcast_ref::<State>(); + + let content = self.content.as_widget_mut().overlay( &mut tree.children[0], layout, renderer, - ) + ); + + let tooltip = if let State::Hovered { cursor_position } = *state { + Some(overlay::Element::new( + layout.position(), + Box::new(Overlay { + tooltip: &self.tooltip, + cursor_position, + content_bounds: layout.bounds(), + snap_within_viewport: self.snap_within_viewport, + position: self.position, + gap: self.gap, + padding: self.padding, + style: &self.style, + }), + )) + } else { + None + }; + + if content.is_some() || tooltip.is_some() { + Some( + overlay::Group::with_children( + content.into_iter().chain(tooltip).collect(), + ) + .overlay(), + ) + } else { + None + } } } @@ -264,84 +278,107 @@ pub enum Position { Right, } -/// Draws a [`Tooltip`]. -pub fn draw<Renderer>( - renderer: &mut Renderer, - theme: &Renderer::Theme, - inherited_style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, +#[derive(Debug, Clone, Copy, Default)] +enum State { + #[default] + Idle, + Hovered { + cursor_position: Point, + }, +} + +struct Overlay<'a, 'b, Renderer> +where + Renderer: text::Renderer, + Renderer::Theme: container::StyleSheet + widget::text::StyleSheet, +{ + tooltip: &'b Text<'a, Renderer>, + cursor_position: Point, + content_bounds: Rectangle, + snap_within_viewport: bool, position: Position, gap: f32, padding: f32, - snap_within_viewport: bool, - style: &<Renderer::Theme as container::StyleSheet>::Style, - layout_text: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node, - draw_text: impl FnOnce(&mut Renderer, &renderer::Style, Layout<'_>, &Rectangle), -) where - Renderer: core::Renderer, - Renderer::Theme: container::StyleSheet, -{ - use container::StyleSheet; - - let bounds = layout.bounds(); - - if let Some(cursor_position) = cursor.position_over(bounds) { - let style = theme.appearance(style); + style: &'b <Renderer::Theme as container::StyleSheet>::Style, +} - let defaults = renderer::Style { - text_color: style.text_color.unwrap_or(inherited_style.text_color), - }; +impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer> + for Overlay<'a, 'b, Renderer> +where + Renderer: text::Renderer, + Renderer::Theme: container::StyleSheet + widget::text::StyleSheet, +{ + fn layout( + &self, + renderer: &Renderer, + bounds: Size, + _position: Point, + ) -> layout::Node { + let viewport = Rectangle::with_size(bounds); - let text_layout = layout_text( + let text_layout = Widget::<(), Renderer>::layout( + self.tooltip, renderer, &layout::Limits::new( Size::ZERO, - snap_within_viewport + self.snap_within_viewport .then(|| viewport.size()) .unwrap_or(Size::INFINITY), ) - .pad(Padding::new(padding)), + .pad(Padding::new(self.padding)), ); let text_bounds = text_layout.bounds(); - let x_center = bounds.x + (bounds.width - text_bounds.width) / 2.0; - let y_center = bounds.y + (bounds.height - text_bounds.height) / 2.0; + let x_center = self.content_bounds.x + + (self.content_bounds.width - text_bounds.width) / 2.0; + let y_center = self.content_bounds.y + + (self.content_bounds.height - text_bounds.height) / 2.0; let mut tooltip_bounds = { - let offset = match position { + let offset = match self.position { Position::Top => Vector::new( x_center, - bounds.y - text_bounds.height - gap - padding, + self.content_bounds.y + - text_bounds.height + - self.gap + - self.padding, ), Position::Bottom => Vector::new( x_center, - bounds.y + bounds.height + gap + padding, + self.content_bounds.y + + self.content_bounds.height + + self.gap + + self.padding, ), Position::Left => Vector::new( - bounds.x - text_bounds.width - gap - padding, + self.content_bounds.x + - text_bounds.width + - self.gap + - self.padding, y_center, ), Position::Right => Vector::new( - bounds.x + bounds.width + gap + padding, + self.content_bounds.x + + self.content_bounds.width + + self.gap + + self.padding, y_center, ), Position::FollowCursor => Vector::new( - cursor_position.x, - cursor_position.y - text_bounds.height, + self.cursor_position.x, + self.cursor_position.y - text_bounds.height, ), }; Rectangle { - x: offset.x - padding, - y: offset.y - padding, - width: text_bounds.width + padding * 2.0, - height: text_bounds.height + padding * 2.0, + x: offset.x - self.padding, + y: offset.y - self.padding, + width: text_bounds.width + self.padding * 2.0, + height: text_bounds.height + self.padding * 2.0, } }; - if snap_within_viewport { + if self.snap_within_viewport { if tooltip_bounds.x < viewport.x { tooltip_bounds.x = viewport.x; } else if viewport.x + viewport.width @@ -361,21 +398,49 @@ pub fn draw<Renderer>( } } - renderer.with_layer(Rectangle::with_size(Size::INFINITY), |renderer| { - container::draw_background(renderer, &style, tooltip_bounds); - - draw_text( - renderer, - &defaults, - Layout::with_offset( - Vector::new( - tooltip_bounds.x + padding, - tooltip_bounds.y + padding, - ), - &text_layout, - ), - viewport, - ) - }); + layout::Node::with_children( + tooltip_bounds.size(), + vec![text_layout.translate(Vector::new(self.padding, self.padding))], + ) + .translate(Vector::new(tooltip_bounds.x, tooltip_bounds.y)) + } + + fn draw( + &self, + renderer: &mut Renderer, + theme: &<Renderer as renderer::Renderer>::Theme, + inherited_style: &renderer::Style, + layout: Layout<'_>, + cursor_position: mouse::Cursor, + ) { + let style = <Renderer::Theme as container::StyleSheet>::appearance( + theme, self.style, + ); + + container::draw_background(renderer, &style, layout.bounds()); + + let defaults = renderer::Style { + text_color: style.text_color.unwrap_or(inherited_style.text_color), + }; + + Widget::<(), Renderer>::draw( + self.tooltip, + &widget::Tree::empty(), + renderer, + theme, + &defaults, + layout.children().next().unwrap(), + cursor_position, + &Rectangle::with_size(Size::INFINITY), + ); + } + + fn is_over( + &self, + _layout: Layout<'_>, + _renderer: &Renderer, + _cursor_position: Point, + ) -> bool { + false } } |