From 9c9aae8922d5ee1038f3c717f8ff5eb6c64421a0 Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Sat, 28 Sep 2024 10:30:54 +0200 Subject: [PATCH] feat: `TooltipContainer` (#900) --- crates/components/src/link.rs | 37 +++++----- crates/components/src/tooltip.rs | 89 ++++++++++++++++++++++--- crates/core/src/render/skia_measurer.rs | 12 +++- examples/switch_theme.rs | 45 +++++++++---- examples/tooltip.rs | 44 ++++++++++++ 5 files changed, 182 insertions(+), 45 deletions(-) create mode 100644 examples/tooltip.rs diff --git a/crates/components/src/link.rs b/crates/components/src/link.rs index cb3effc60..1aa6db3b9 100644 --- a/crates/components/src/link.rs +++ b/crates/components/src/link.rs @@ -15,7 +15,10 @@ use freya_hooks::{ }; use winit::event::MouseButton; -use crate::Tooltip; +use crate::{ + Tooltip, + TooltipContainer, +}; /// Tooltip configuration for the [`Link`] component. #[derive(Clone, PartialEq)] @@ -159,7 +162,7 @@ pub fn Link( Some(LinkTooltip::Custom(str)) => Some(str), }; - let main_rect = rsx! { + let link = rsx! { rect { onmouseenter, onmouseleave, @@ -169,27 +172,19 @@ pub fn Link( } }; - let Some(tooltip) = tooltip else { - return rsx!({ main_rect }); - }; - - rsx! { - rect { - {main_rect} - rect { - height: "0", - width: "0", - layer: "-999", - rect { - width: "100v", - if *is_hovering.read() { - Tooltip { - url: tooltip - } + if let Some(tooltip) = tooltip { + rsx!( + TooltipContainer { + tooltip: rsx!( + Tooltip { + text: tooltip } - } + ) + {link} } - } + ) + } else { + link } } diff --git a/crates/components/src/tooltip.rs b/crates/components/src/tooltip.rs index 6ec61d01a..09f0ee603 100644 --- a/crates/components/src/tooltip.rs +++ b/crates/components/src/tooltip.rs @@ -1,7 +1,11 @@ use dioxus::prelude::*; -use freya_elements::elements as dioxus_elements; +use freya_elements::{ + elements as dioxus_elements, + events::MouseEvent, +}; use freya_hooks::{ use_applied_theme, + use_node_signal, TooltipTheme, TooltipThemeWith, }; @@ -11,8 +15,8 @@ use freya_hooks::{ pub struct TooltipProps { /// Theme override. pub theme: Option, - /// Url as the Tooltip destination. - pub url: String, + /// Text to show in the [Tooltip]. + pub text: String, } /// `Tooltip` component @@ -20,7 +24,7 @@ pub struct TooltipProps { /// # Styling /// Inherits the [`TooltipTheme`](freya_hooks::TooltipTheme) #[allow(non_snake_case)] -pub fn Tooltip(TooltipProps { url, theme }: TooltipProps) -> Element { +pub fn Tooltip(TooltipProps { text, theme }: TooltipProps) -> Element { let theme = use_applied_theme!(&theme, tooltip); let TooltipTheme { background, @@ -31,12 +35,81 @@ pub fn Tooltip(TooltipProps { url, theme }: TooltipProps) -> Element { rsx!( rect { padding: "4 10", - shadow: "0 4 5 0 rgb(0, 0, 0, 0.1)", + shadow: "0 0 4 1 rgb(0, 0, 0, 0.1)", border: "1 solid {border_fill}", - corner_radius: "10", + corner_radius: "8", background: "{background}", - main_align: "center", - label { max_lines: "1", color: "{color}", "{url}" } + label { max_lines: "1", font_size: "14", color: "{color}", "{text}" } + } + ) +} + +#[derive(PartialEq, Clone, Copy, Debug)] +pub enum TooltipPosition { + Besides, + Below, +} + +/// `TooltipContainer` component. +/// +/// Provides a hoverable area where to show a [Tooltip]. +/// +/// # Example +#[component] +pub fn TooltipContainer( + tooltip: Element, + children: Element, + #[props(default = TooltipPosition::Below, into)] position: TooltipPosition, +) -> Element { + let mut is_hovering = use_signal(|| false); + let (reference, size) = use_node_signal(); + + let onmouseenter = move |_: MouseEvent| { + is_hovering.set(true); + }; + + let onmouseleave = move |_: MouseEvent| { + is_hovering.set(false); + }; + + let direction = match position { + TooltipPosition::Below => "vertical", + TooltipPosition::Besides => "horizontal", + }; + + rsx!( + rect { + direction, + reference, + onmouseenter, + onmouseleave, + {children}, + if *is_hovering.read() { + rect { + height: "0", + width: "0", + layer: "-999", + match position { + TooltipPosition::Below => rsx!( + rect { + width: "{size.read().area.width()}", + cross_align: "center", + padding: "5 0 0 0", + {tooltip} + } + ), + TooltipPosition::Besides => rsx!( + rect { + height: "{size.read().area.height()}", + main_align: "center", + padding: "0 0 0 5", + {tooltip} + } + ), + } + + } + } } ) } diff --git a/crates/core/src/render/skia_measurer.rs b/crates/core/src/render/skia_measurer.rs index dd98f41a8..3ba11be03 100644 --- a/crates/core/src/render/skia_measurer.rs +++ b/crates/core/src/render/skia_measurer.rs @@ -155,7 +155,11 @@ pub fn create_label( } let mut paragraph = paragraph_builder.build(); - paragraph.layout(area_size.width + 1.0); + paragraph.layout(if font_style.max_lines == Some(1) { + f32::MAX + } else { + area_size.width + 1.0 + }); // Measure the actual text height, ignoring the line height let mut height = paragraph.height(); @@ -268,7 +272,11 @@ pub fn create_paragraph( } let mut paragraph = paragraph_builder.build(); - paragraph.layout(area_size.width + 1.0); + paragraph.layout(if font_style.max_lines == Some(1) { + f32::MAX + } else { + area_size.width + 1.0 + }); // Measure the actual text height, ignoring the line height let mut height = paragraph.height(); diff --git a/examples/switch_theme.rs b/examples/switch_theme.rs index fd288b317..e3c0e6b3c 100644 --- a/examples/switch_theme.rs +++ b/examples/switch_theme.rs @@ -14,24 +14,41 @@ fn ThemeChanger() -> Element { let mut theme = use_theme(); rsx!( - Tile { - onselect: move |_| theme.set(DARK_THEME), - leading: rsx!( - Radio { - selected: theme.read().name == "dark", - }, + TooltipContainer { + position: TooltipPosition::Besides, + tooltip: rsx!( + Tooltip { + text: "Switch to Dark theme" + } ), - label { "Dark" } + Tile { + onselect: move |_| theme.set(DARK_THEME), + leading: rsx!( + Radio { + selected: theme.read().name == "dark", + }, + ), + label { "Dark" } + } } - Tile { - onselect: move |_| theme.set(LIGHT_THEME), - leading: rsx!( - Radio { - selected: theme.read().name == "light", - }, + TooltipContainer { + position: TooltipPosition::Besides, + tooltip: rsx!( + Tooltip { + text: "Switch to Light theme" + } ), - label { "Light" } + Tile { + onselect: move |_| theme.set(LIGHT_THEME), + leading: rsx!( + Radio { + selected: theme.read().name == "light", + }, + ), + label { "Light" } + } } + ) } diff --git a/examples/tooltip.rs b/examples/tooltip.rs new file mode 100644 index 000000000..6b40fa7e8 --- /dev/null +++ b/examples/tooltip.rs @@ -0,0 +1,44 @@ +#![cfg_attr( + all(not(debug_assertions), target_os = "windows"), + windows_subsystem = "windows" +)] + +use freya::prelude::*; + +fn main() { + launch(app); +} + +fn app() -> Element { + rsx!( + rect { + height: "fill", + width: "fill", + main_align: "center", + cross_align: "center", + direction: "horizontal", + spacing: "10", + TooltipContainer { + tooltip: rsx!( + Tooltip { + text: "Hello, World!" + } + ), + Button { + label { "Hello!!" } + } + } + TooltipContainer { + position: TooltipPosition::Besides, + tooltip: rsx!( + Tooltip { + text: "Hello, World!" + } + ), + Button { + label { "Hello!!" } + } + } + } + ) +}