freya_components/
tooltip.rs1use std::borrow::Cow;
2
3use freya_animation::{
4 easing::Function,
5 hook::{
6 AnimatedValue,
7 Ease,
8 OnChange,
9 OnCreation,
10 ReadAnimatedValue,
11 use_animation,
12 },
13 prelude::AnimNum,
14};
15use freya_core::prelude::*;
16use torin::{
17 prelude::{
18 Alignment,
19 Area,
20 Direction,
21 },
22 size::Size,
23};
24
25use crate::{
26 get_theme,
27 theming::component_themes::{
28 TooltipTheme,
29 TooltipThemePartial,
30 },
31};
32
33#[cfg_attr(feature = "docs",
52 doc = embed_doc_image::embed_image!("tooltip", "images/gallery_tooltip.png")
53)]
54#[derive(PartialEq, Clone)]
55pub struct Tooltip {
56 pub(crate) theme: Option<TooltipThemePartial>,
58 text: Cow<'static, str>,
60 key: DiffKey,
61}
62
63impl KeyExt for Tooltip {
64 fn write_key(&mut self) -> &mut DiffKey {
65 &mut self.key
66 }
67}
68
69impl Tooltip {
70 pub fn new(text: impl Into<Cow<'static, str>>) -> Self {
71 Self {
72 theme: None,
73 text: text.into(),
74 key: DiffKey::None,
75 }
76 }
77}
78
79impl Render for Tooltip {
80 fn render(&self) -> impl IntoElement {
81 let theme = get_theme!(&self.theme, tooltip);
82 let TooltipTheme {
83 background,
84 color,
85 border_fill,
86 } = theme;
87
88 rect()
89 .interactive(Interactive::No)
90 .padding((4., 10.))
91 .border(
92 Border::new()
93 .width(1.)
94 .alignment(BorderAlignment::Inner)
95 .fill(border_fill),
96 )
97 .background(background)
98 .corner_radius(8.)
99 .child(
100 label()
101 .max_lines(1)
102 .font_size(14.)
103 .color(color)
104 .text(self.text.clone()),
105 )
106 }
107
108 fn render_key(&self) -> DiffKey {
109 self.key.clone().or(self.default_key())
110 }
111}
112
113#[derive(PartialEq, Clone, Copy, Debug, Default)]
114pub enum TooltipPosition {
115 Besides,
116 #[default]
117 Below,
118}
119
120#[derive(PartialEq)]
121pub struct TooltipContainer {
122 tooltip: Tooltip,
123 children: Vec<Element>,
124 position: TooltipPosition,
125 key: DiffKey,
126}
127
128impl KeyExt for TooltipContainer {
129 fn write_key(&mut self) -> &mut DiffKey {
130 &mut self.key
131 }
132}
133
134impl ChildrenExt for TooltipContainer {
135 fn get_children(&mut self) -> &mut Vec<Element> {
136 &mut self.children
137 }
138}
139
140impl TooltipContainer {
141 pub fn new(tooltip: Tooltip) -> Self {
142 Self {
143 tooltip,
144 children: vec![],
145 position: TooltipPosition::Below,
146 key: DiffKey::None,
147 }
148 }
149
150 pub fn position(mut self, position: TooltipPosition) -> Self {
151 self.position = position;
152 self
153 }
154}
155
156impl Render for TooltipContainer {
157 fn render(&self) -> impl IntoElement {
158 let mut is_hovering = use_state(|| false);
159 let mut size = use_state(Area::default);
160
161 let animation = use_animation(move |conf| {
162 conf.on_change(OnChange::Rerun);
163 conf.on_creation(OnCreation::Finish);
164
165 let scale = AnimNum::new(0.8, 1.)
166 .time(350)
167 .ease(Ease::Out)
168 .function(Function::Expo);
169 let opacity = AnimNum::new(0., 1.)
170 .time(350)
171 .ease(Ease::Out)
172 .function(Function::Expo);
173
174 if is_hovering() {
175 (scale, opacity)
176 } else {
177 (scale.into_reversed(), opacity.into_reversed())
178 }
179 });
180
181 let (scale, opacity) = animation.read().value();
182
183 let on_pointer_enter = move |_| {
184 is_hovering.set(true);
185 };
186
187 let on_pointer_leave = move |_| {
188 is_hovering.set(false);
189 };
190
191 let on_sized = move |e: Event<SizedEventData>| {
192 size.set(e.area);
193 };
194
195 let direction = match self.position {
196 TooltipPosition::Below => Direction::vertical(),
197 TooltipPosition::Besides => Direction::horizontal(),
198 };
199
200 rect()
201 .direction(direction)
202 .on_sized(on_sized)
203 .on_pointer_enter(on_pointer_enter)
204 .on_pointer_leave(on_pointer_leave)
205 .children(self.children.clone())
206 .child(
207 rect()
208 .width(Size::px(0.))
209 .height(Size::px(0.))
210 .layer(Layer::Overlay)
211 .opacity(opacity)
212 .overflow(if opacity == 0. {
213 Overflow::Clip
214 } else {
215 Overflow::None
216 })
217 .child({
218 match self.position {
219 TooltipPosition::Below => rect()
220 .width(Size::px(size.read().width()))
221 .cross_align(Alignment::Center)
222 .main_align(Alignment::Center)
223 .scale(scale)
224 .padding((5., 0., 0., 0.))
225 .child(self.tooltip.clone()),
226 TooltipPosition::Besides => rect()
227 .height(Size::px(size.read().height()))
228 .cross_align(Alignment::Center)
229 .main_align(Alignment::Center)
230 .scale(scale)
231 .padding((0., 0., 0., 5.))
232 .child(self.tooltip.clone()),
233 }
234 }),
235 )
236 }
237
238 fn render_key(&self) -> DiffKey {
239 self.key.clone().or(self.default_key())
240 }
241}