1use azul_core::{
26 callbacks::{CoreCallback, CoreCallbackData, Update},
27 dom::{Dom, EventFilter, HoverEventFilter, IdOrClass, IdOrClass::Class, IdOrClassVec},
28 refany::{OptionRefAny, RefAny},
29};
30use azul_css::dynamic_selector::{CssPropertyWithConditions, CssPropertyWithConditionsVec};
31use azul_css::{
32 props::{
33 basic::{color::ColorU, StyleFontSize},
34 layout::{LayoutDisplay, LayoutPosition, LayoutFlexGrow, LayoutTop, LayoutLeft, LayoutPaddingLeft, LayoutPaddingRight, LayoutPaddingTop, LayoutPaddingBottom},
35 property::{CssProperty, StyleWhiteSpaceValue},
36 style::{StyleBackgroundContent, StyleBackgroundContentVec, StyleBorderTopLeftRadius, StyleBorderTopRightRadius, StyleBorderBottomLeftRadius, StyleBorderBottomRightRadius, StyleTextColor, StyleWhiteSpace, StyleOpacity},
37 },
38 AzString,
39};
40
41use crate::callbacks::CallbackInfo;
42
43static TOOLTIP_WRAPPER_CLASS: &[IdOrClass] =
44 &[Class(AzString::from_const_str("__azul-native-tooltip"))];
45static TOOLTIP_TIP_CLASS: &[IdOrClass] =
46 &[Class(AzString::from_const_str("__azul-native-tooltip-tip"))];
47
48const TIP_OFFSET_Y: isize = 22;
52const TIP_RADIUS: isize = 4;
53
54const TIP_BG_COLOR: ColorU = ColorU {
57 r: 51,
58 g: 51,
59 b: 51,
60 a: 240,
61};
62const TIP_TEXT_COLOR: ColorU = ColorU {
64 r: 255,
65 g: 255,
66 b: 255,
67 a: 255,
68};
69
70const TIP_BG_ITEMS: &[StyleBackgroundContent] = &[StyleBackgroundContent::Color(TIP_BG_COLOR)];
71const TIP_BG: StyleBackgroundContentVec = StyleBackgroundContentVec::from_const_slice(TIP_BG_ITEMS);
72
73static TOOLTIP_WRAPPER_STYLE: &[CssPropertyWithConditions] = &[
76 CssPropertyWithConditions::simple(CssProperty::const_display(LayoutDisplay::InlineBlock)),
77 CssPropertyWithConditions::simple(CssProperty::const_position(LayoutPosition::Relative)),
78 CssPropertyWithConditions::simple(CssProperty::const_flex_grow(LayoutFlexGrow::const_new(0))),
79];
80
81static TOOLTIP_TIP_STYLE: &[CssPropertyWithConditions] = &[
83 CssPropertyWithConditions::simple(CssProperty::const_position(LayoutPosition::Absolute)),
84 CssPropertyWithConditions::simple(CssProperty::const_top(LayoutTop::const_px(TIP_OFFSET_Y))),
85 CssPropertyWithConditions::simple(CssProperty::const_left(LayoutLeft::const_px(0))),
86 CssPropertyWithConditions::simple(CssProperty::const_padding_left(LayoutPaddingLeft::const_px(
87 8,
88 ))),
89 CssPropertyWithConditions::simple(CssProperty::const_padding_right(
90 LayoutPaddingRight::const_px(8),
91 )),
92 CssPropertyWithConditions::simple(CssProperty::const_padding_top(LayoutPaddingTop::const_px(4))),
93 CssPropertyWithConditions::simple(CssProperty::const_padding_bottom(
94 LayoutPaddingBottom::const_px(4),
95 )),
96 CssPropertyWithConditions::simple(CssProperty::const_border_top_left_radius(
97 StyleBorderTopLeftRadius::const_px(TIP_RADIUS),
98 )),
99 CssPropertyWithConditions::simple(CssProperty::const_border_top_right_radius(
100 StyleBorderTopRightRadius::const_px(TIP_RADIUS),
101 )),
102 CssPropertyWithConditions::simple(CssProperty::const_border_bottom_left_radius(
103 StyleBorderBottomLeftRadius::const_px(TIP_RADIUS),
104 )),
105 CssPropertyWithConditions::simple(CssProperty::const_border_bottom_right_radius(
106 StyleBorderBottomRightRadius::const_px(TIP_RADIUS),
107 )),
108 CssPropertyWithConditions::simple(CssProperty::const_background_content(TIP_BG)),
109 CssPropertyWithConditions::simple(CssProperty::const_text_color(StyleTextColor {
110 inner: TIP_TEXT_COLOR,
111 })),
112 CssPropertyWithConditions::simple(CssProperty::const_font_size(StyleFontSize::const_px(12))),
113 CssPropertyWithConditions::simple(CssProperty::WhiteSpace(StyleWhiteSpaceValue::Exact(
115 StyleWhiteSpace::Nowrap,
116 ))),
117 CssPropertyWithConditions::simple(CssProperty::const_opacity(StyleOpacity::const_new(0))),
119];
120
121#[derive(Debug, Clone, PartialEq, Eq)]
123#[repr(C)]
124pub struct Tooltip {
125 pub anchor: Dom,
127 pub text: AzString,
129 pub wrapper_style: CssPropertyWithConditionsVec,
131 pub tip_style: CssPropertyWithConditionsVec,
133}
134
135impl Default for Tooltip {
136 fn default() -> Self {
137 Self::new(Dom::default(), AzString::from_const_str(""))
138 }
139}
140
141impl Tooltip {
142 #[must_use] pub fn new(anchor: Dom, text: AzString) -> Self {
144 Self {
145 anchor,
146 text,
147 wrapper_style: CssPropertyWithConditionsVec::from_const_slice(TOOLTIP_WRAPPER_STYLE),
148 tip_style: CssPropertyWithConditionsVec::from_const_slice(TOOLTIP_TIP_STYLE),
149 }
150 }
151
152 #[inline]
154 pub fn set_text(&mut self, text: AzString) {
155 self.text = text;
156 }
157
158 #[inline]
160 #[must_use] pub fn with_text(mut self, text: AzString) -> Self {
161 self.set_text(text);
162 self
163 }
164
165 #[inline]
167 pub fn set_tip_style(&mut self, style: CssPropertyWithConditionsVec) {
168 self.tip_style = style;
169 }
170
171 #[inline]
173 #[must_use] pub fn with_tip_style(mut self, style: CssPropertyWithConditionsVec) -> Self {
174 self.set_tip_style(style);
175 self
176 }
177
178 #[inline]
179 #[must_use] pub fn swap_with_default(&mut self) -> Self {
180 let mut s = Self::default();
181 core::mem::swap(&mut s, self);
182 s
183 }
184
185 #[must_use] pub fn dom(self) -> Dom {
186 let marker = RefAny::new(());
189
190 let tip = Dom::create_text(self.text)
191 .with_ids_and_classes(IdOrClassVec::from_const_slice(TOOLTIP_TIP_CLASS))
192 .with_css_props(self.tip_style);
193
194 Dom::create_div()
195 .with_ids_and_classes(IdOrClassVec::from_const_slice(TOOLTIP_WRAPPER_CLASS))
196 .with_css_props(self.wrapper_style)
197 .with_callbacks(
198 vec![
199 CoreCallbackData {
200 event: EventFilter::Hover(HoverEventFilter::MouseEnter),
201 callback: CoreCallback {
202 cb: on_tooltip_enter as usize,
203 ctx: OptionRefAny::None,
204 },
205 refany: marker.clone(),
206 },
207 CoreCallbackData {
208 event: EventFilter::Hover(HoverEventFilter::MouseLeave),
209 callback: CoreCallback {
210 cb: on_tooltip_leave as usize,
211 ctx: OptionRefAny::None,
212 },
213 refany: marker,
214 },
215 ]
216 .into(),
217 )
218 .with_children(vec![self.anchor, tip].into())
220 }
221}
222
223fn tip_of_wrapper(info: &CallbackInfo) -> Option<azul_core::dom::DomNodeId> {
225 let wrapper = info.get_hit_node();
226 let anchor = info.get_first_child(wrapper)?;
227 info.get_next_sibling(anchor)
228}
229
230extern "C" fn on_tooltip_enter(_data: RefAny, mut info: CallbackInfo) -> Update {
232 if let Some(tip) = tip_of_wrapper(&info) {
233 info.set_css_property(tip, CssProperty::const_opacity(StyleOpacity::const_new(100)));
234 }
235 Update::DoNothing
236}
237
238extern "C" fn on_tooltip_leave(_data: RefAny, mut info: CallbackInfo) -> Update {
240 if let Some(tip) = tip_of_wrapper(&info) {
241 info.set_css_property(tip, CssProperty::const_opacity(StyleOpacity::const_new(0)));
242 }
243 Update::DoNothing
244}
245
246impl From<Tooltip> for Dom {
247 fn from(t: Tooltip) -> Self {
248 t.dom()
249 }
250}