diff --git a/dash-frontend/assets/gui/view/app_launcher.xml b/dash-frontend/assets/gui/view/app_launcher.xml
index 687fae2..7f1c314 100644
--- a/dash-frontend/assets/gui/view/app_launcher.xml
+++ b/dash-frontend/assets/gui/view/app_launcher.xml
@@ -37,18 +37,18 @@
-
-
-
-
-
+
+
+
+
+
@@ -61,4 +61,4 @@
-
+
\ No newline at end of file
diff --git a/wgui/src/components/button.rs b/wgui/src/components/button.rs
index c270e2b..268e0c8 100644
--- a/wgui/src/components/button.rs
+++ b/wgui/src/components/button.rs
@@ -1,11 +1,14 @@
use crate::{
animation::{Animation, AnimationEasing},
assets::AssetPath,
- components::{self, Component, ComponentBase, ComponentTrait, RefreshData, tooltip::ComponentTooltip},
+ components::{
+ self, Component, ComponentBase, ComponentTrait, RefreshData,
+ tooltip::{ComponentTooltip, TooltipTrait},
+ },
drawing::{self, Boundary, Color},
event::{CallbackDataCommon, EventListenerCollection, EventListenerID, EventListenerKind},
i18n::Translation,
- layout::{LayoutTask, WidgetID, WidgetPair},
+ layout::{WidgetID, WidgetPair},
renderer_vk::{
text::{FontWeight, TextStyle, custom_glyph::CustomGlyphData},
util::centered_matrix,
@@ -90,6 +93,12 @@ struct State {
last_pressed: Instant,
}
+impl TooltipTrait for State {
+ fn get(&mut self) -> &mut Option> {
+ &mut self.active_tooltip
+ }
+}
+
struct Data {
id_label: WidgetID, // Label
id_rect: WidgetID, // Rectangle
@@ -268,7 +277,7 @@ fn register_event_mouse_enter(
data: Rc,
state: Rc>,
listeners: &mut EventListenerCollection,
- info: Option,
+ tooltip_info: Option,
anim_mult: f32,
) -> EventListenerID {
listeners.register(
@@ -281,17 +290,7 @@ fn register_event_mouse_enter(
.alterables
.animate(anim_hover_create(state.clone(), event_data.widget_id, true, anim_mult));
- if let Some(info) = info.clone() {
- common.alterables.tasks.push(LayoutTask::ModifyLayoutState({
- let widget_to_watch = data.id_rect;
- let state = state.clone();
- Box::new(move |m| {
- state.borrow_mut().active_tooltip =
- Some(components::tooltip::show(m.layout, widget_to_watch, info.clone())?);
- Ok(())
- })
- }));
- }
+ ComponentTooltip::register_hover_in(common, &tooltip_info, data.id_rect, state.clone());
state.borrow_mut().hovered = true;
Ok(EventResult::Pass)
diff --git a/wgui/src/components/checkbox.rs b/wgui/src/components/checkbox.rs
index ba631cb..1aa52fd 100644
--- a/wgui/src/components/checkbox.rs
+++ b/wgui/src/components/checkbox.rs
@@ -9,7 +9,11 @@ use taffy::{
use crate::{
animation::{Animation, AnimationEasing},
- components::{Component, ComponentBase, ComponentTrait, RefreshData, radio_group::ComponentRadioGroup},
+ components::{
+ Component, ComponentBase, ComponentTrait, RefreshData,
+ radio_group::ComponentRadioGroup,
+ tooltip::{self, ComponentTooltip, TooltipTrait},
+ },
drawing::Color,
event::{CallbackDataCommon, EventListenerCollection, EventListenerID, EventListenerKind},
i18n::Translation,
@@ -31,6 +35,7 @@ pub struct Params {
pub checked: bool,
pub radio_group: Option>,
pub value: Option>,
+ pub tooltip: Option,
}
impl Default for Params {
@@ -42,6 +47,7 @@ impl Default for Params {
checked: false,
radio_group: None,
value: None,
+ tooltip: None,
}
}
}
@@ -59,6 +65,13 @@ struct State {
down: bool,
on_toggle: Option,
self_ref: Weak,
+ active_tooltip: Option>,
+}
+
+impl TooltipTrait for State {
+ fn get(&mut self) -> &mut Option> {
+ &mut self.active_tooltip
+ }
}
#[allow(clippy::struct_field_names)]
@@ -181,6 +194,7 @@ fn anim_hover_out(state: Rc>, widget_id: WidgetID, anim_mult: f32
fn register_event_mouse_enter(
state: Rc>,
listeners: &mut EventListenerCollection,
+ tooltip_info: Option,
anim_mult: f32,
) -> EventListenerID {
listeners.register(
@@ -190,6 +204,9 @@ fn register_event_mouse_enter(
common
.alterables
.animate(anim_hover_in(state.clone(), event_data.widget_id, anim_mult));
+
+ ComponentTooltip::register_hover_in(common, &tooltip_info, event_data.widget_id, state.clone());
+
state.borrow_mut().hovered = true;
Ok(EventResult::Pass)
}),
@@ -208,7 +225,13 @@ fn register_event_mouse_leave(
common
.alterables
.animate(anim_hover_out(state.clone(), event_data.widget_id, anim_mult));
- state.borrow_mut().hovered = false;
+
+ {
+ let mut state = state.borrow_mut();
+ state.hovered = false;
+ state.active_tooltip = None;
+ }
+
Ok(EventResult::Pass)
}),
)
@@ -402,6 +425,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
hovered: false,
on_toggle: None,
self_ref: Weak::new(),
+ active_tooltip: None,
}));
let base = ComponentBase {
@@ -410,7 +434,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
let mut widget = ess.layout.state.widgets.get(id_container).unwrap().state();
let anim_mult = ess.layout.state.globals.defaults().animation_mult;
vec![
- register_event_mouse_enter(state.clone(), &mut widget.event_listeners, anim_mult),
+ register_event_mouse_enter(state.clone(), &mut widget.event_listeners, params.tooltip, anim_mult),
register_event_mouse_leave(state.clone(), &mut widget.event_listeners, anim_mult),
register_event_mouse_press(state.clone(), &mut widget.event_listeners),
register_event_mouse_release(data.clone(), state.clone(), &mut widget.event_listeners),
diff --git a/wgui/src/components/slider.rs b/wgui/src/components/slider.rs
index 8ef9f18..433c8cf 100644
--- a/wgui/src/components/slider.rs
+++ b/wgui/src/components/slider.rs
@@ -5,7 +5,10 @@ use taffy::prelude::{length, percent};
use crate::{
animation::{Animation, AnimationEasing},
- components::{Component, ComponentBase, ComponentTrait, RefreshData},
+ components::{
+ Component, ComponentBase, ComponentTrait, RefreshData,
+ tooltip::{self, ComponentTooltip, TooltipTrait},
+ },
drawing::{self},
event::{
self, CallbackDataCommon, CallbackMetadata, EventAlterables, EventListenerCollection, EventListenerKind,
@@ -70,6 +73,7 @@ pub struct Params {
pub style: taffy::Style,
pub values: ValuesMinMax,
pub show_value: bool,
+ pub tooltip: Option,
}
struct State {
@@ -77,6 +81,13 @@ struct State {
hovered: bool,
values: ValuesMinMax,
on_value_changed: Option,
+ active_tooltip: Option>,
+}
+
+impl TooltipTrait for State {
+ fn get(&mut self) -> &mut Option> {
+ &mut self.active_tooltip
+ }
}
struct Data {
@@ -304,14 +315,18 @@ fn register_event_mouse_enter(
data: Rc,
state: Rc>,
listeners: &mut EventListenerCollection,
+ tooltip_info: Option,
anim_mult: f32,
) -> event::EventListenerID {
listeners.register(
EventListenerKind::MouseEnter,
- Box::new(move |common, _data, (), ()| {
+ Box::new(move |common, event_data, (), ()| {
common.alterables.trigger_haptics();
state.borrow_mut().hovered = true;
on_enter_anim(common, data.slider_handle_rect_id, anim_mult);
+
+ ComponentTooltip::register_hover_in(common, &tooltip_info, event_data.widget_id, state.clone());
+
Ok(EventResult::Pass)
}),
)
@@ -327,8 +342,14 @@ fn register_event_mouse_leave(
EventListenerKind::MouseLeave,
Box::new(move |common, _data, (), ()| {
common.alterables.trigger_haptics();
- state.borrow_mut().hovered = false;
+
+ {
+ let mut state = state.borrow_mut();
+ state.hovered = false;
+ state.active_tooltip = None;
+ }
on_leave_anim(common, data.slider_handle_rect_id, anim_mult);
+
Ok(EventResult::Pass)
}),
)
@@ -474,6 +495,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
hovered: false,
values: params.values,
on_value_changed: None,
+ active_tooltip: None,
};
let globals = ess.layout.state.globals.clone();
@@ -515,7 +537,13 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
let mut widget = ess.layout.state.widgets.get(body_id).unwrap().state();
let anim_mult = ess.layout.state.globals.defaults().animation_mult;
vec![
- register_event_mouse_enter(data.clone(), state.clone(), &mut widget.event_listeners, anim_mult),
+ register_event_mouse_enter(
+ data.clone(),
+ state.clone(),
+ &mut widget.event_listeners,
+ params.tooltip,
+ anim_mult,
+ ),
register_event_mouse_leave(data.clone(), state.clone(), &mut widget.event_listeners, anim_mult),
register_event_mouse_motion(data.clone(), state.clone(), &mut widget.event_listeners),
register_event_mouse_press(data.clone(), state.clone(), &mut widget.event_listeners),
diff --git a/wgui/src/components/tooltip.rs b/wgui/src/components/tooltip.rs
index 2e26bed..41dfcfa 100644
--- a/wgui/src/components/tooltip.rs
+++ b/wgui/src/components/tooltip.rs
@@ -1,10 +1,12 @@
-use glam::{Mat4, Vec3};
+use glam::{Mat4, Vec2, Vec3};
use std::{cell::RefCell, rc::Rc};
use taffy::prelude::length;
use crate::{
+ animation::{Animation, AnimationEasing},
components::{self, Component, ComponentBase, ComponentTrait, RefreshData},
drawing::Color,
+ event::CallbackDataCommon,
i18n::Translation,
layout::{self, LayoutTask, LayoutTasks, WidgetID, WidgetPair},
renderer_vk::text::{FontWeight, TextStyle},
@@ -17,6 +19,30 @@ use crate::{
},
};
+pub trait TooltipTrait {
+ fn get(&mut self) -> &mut Option>;
+}
+
+impl ComponentTooltip {
+ pub fn register_hover_in(
+ common: &mut CallbackDataCommon,
+ tooltip_info: &Option,
+ widget_to_watch: WidgetID,
+ state: Rc>,
+ ) {
+ let Some(info) = tooltip_info.clone() else {
+ return;
+ };
+ common.alterables.tasks.push(LayoutTask::ModifyLayoutState({
+ Box::new(move |m| {
+ let mut state = state.borrow_mut();
+ *state.get() = Some(components::tooltip::show(m.layout, widget_to_watch, info.clone())?);
+ Ok(())
+ })
+ }));
+ }
+}
+
#[derive(Clone, Default)]
pub enum TooltipSide {
Left,
@@ -78,8 +104,6 @@ impl ComponentTrait for ComponentTooltip {
}
}
-impl ComponentTooltip {}
-
impl Drop for ComponentTooltip {
fn drop(&mut self) {
self.tasks.push(LayoutTask::RemoveWidget(self.data.id_root));
@@ -180,7 +204,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
},
)?;
- let (_label, _) = ess.layout.add_child(
+ let (label, _) = ess.layout.add_child(
rect.id,
WidgetLabel::create(
&mut globals.get(),
@@ -208,6 +232,35 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
tasks: ess.layout.tasks.clone(),
});
+ let direction = match params.info.side {
+ TooltipSide::Left => Vec2::new(-1.0, 0.0),
+ TooltipSide::Right => Vec2::new(1.0, 0.0),
+ TooltipSide::Top => Vec2::new(0.0, -1.0),
+ TooltipSide::Bottom => Vec2::new(0.0, 1.0),
+ };
+
+ let anim_mult = ess.layout.state.globals.defaults().animation_mult;
+ ess.layout.animations.add(Animation::new(
+ rect.id,
+ (10.0 * anim_mult) as u32,
+ AnimationEasing::OutQuad,
+ Box::new(move |common, data| {
+ let rect = data.obj.get_as_mut::().unwrap(); /* safe */
+ let alpha = data.pos;
+ rect.params.color.a = alpha;
+ rect.params.border_color.a = alpha;
+
+ let dir_mult = (1.0 - data.pos) * 5.0;
+ data.data.transform = Mat4::from_translation(Vec3::new(direction.x * dir_mult, direction.y * dir_mult, 0.0));
+
+ if let Some(mut label) = common.state.widgets.get_as::(label.id) {
+ label.set_color(common, Color::new(1.0, 1.0, 1.0, alpha), true);
+ }
+
+ common.alterables.mark_redraw();
+ }),
+ ));
+
ess.layout.defer_component_refresh(Component(tooltip.clone()));
Ok((div, tooltip))
}
diff --git a/wgui/src/layout.rs b/wgui/src/layout.rs
index 2320809..f57031c 100644
--- a/wgui/src/layout.rs
+++ b/wgui/src/layout.rs
@@ -130,7 +130,7 @@ pub struct LayoutUpdateResult {
pub sounds_to_play: Vec,
}
-pub type ModifyLayoutStateFunc = Box anyhow::Result<()>>;
+pub type ModifyLayoutStateFunc = Box anyhow::Result<()>>;
pub enum LayoutTask {
RemoveWidget(WidgetID),
@@ -690,7 +690,7 @@ impl Layout {
self.remove_widget(widget_id);
}
LayoutTask::ModifyLayoutState(callback) => {
- (*callback)(ModifyLayoutStateData { layout: self })?;
+ callback(ModifyLayoutStateData { layout: self })?;
}
LayoutTask::PlaySound(sound) => {
if !self.sounds_to_play_once.contains(&sound) {
diff --git a/wgui/src/parser/component_button.rs b/wgui/src/parser/component_button.rs
index f441070..673dfe3 100644
--- a/wgui/src/parser/component_button.rs
+++ b/wgui/src/parser/component_button.rs
@@ -5,7 +5,9 @@ use crate::{
i18n::Translation,
layout::WidgetID,
parser::{
- AttribPair, ParserContext, ParserFile, get_asset_path_from_kv, parse_children, parse_f32, process_component,
+ AttribPair, ParserContext, ParserFile, get_asset_path_from_kv,
+ helpers::{TooltipAttribs, parse_attrib_tooltip},
+ parse_children, parse_f32, process_component,
style::{parse_color_opt, parse_round, parse_style, parse_text_style},
},
widget::util::WLength,
@@ -25,8 +27,7 @@ pub fn parse_component_button<'a>(
let mut hover_color: Option = None;
let mut hover_border_color: Option = None;
let mut round = WLength::Units(4.0);
- let mut tooltip: Option = None;
- let mut tooltip_side: Option = None;
+ let mut tooltip = TooltipAttribs::default();
let mut sticky: bool = false;
let mut long_press_time = 0.0;
let mut sprite_src: Option = None;
@@ -81,20 +82,6 @@ pub fn parse_component_button<'a>(
sprite_src = Some(asset_path);
}
}
- "tooltip" if !value.is_empty() => tooltip = Some(Translation::from_translation_key(value)),
- "tooltip_str" if !value.is_empty() => tooltip = Some(Translation::from_raw_text(value)),
- "tooltip_side" => {
- tooltip_side = match value {
- "left" => Some(tooltip::TooltipSide::Left),
- "right" => Some(tooltip::TooltipSide::Right),
- "top" => Some(tooltip::TooltipSide::Top),
- "bottom" => Some(tooltip::TooltipSide::Bottom),
- _ => {
- ctx.print_invalid_attrib(tag_name, key, value);
- None
- }
- }
- }
"sticky" => {
let mut sticky_i32 = 0;
sticky = ctx.parse_check_i32(tag_name, key, value, &mut sticky_i32) && sticky_i32 == 1;
@@ -102,7 +89,9 @@ pub fn parse_component_button<'a>(
"long_press_time" => {
long_press_time = parse_f32(value).unwrap_or(long_press_time);
}
- _ => {}
+ _ => {
+ parse_attrib_tooltip(ctx, tag_name, pair, &mut tooltip);
+ }
}
}
@@ -118,10 +107,7 @@ pub fn parse_component_button<'a>(
style,
text_style,
round,
- tooltip: tooltip.map(|text| tooltip::TooltipInfo {
- side: tooltip_side.map_or(tooltip::TooltipSide::Top, |f| f),
- text,
- }),
+ tooltip: tooltip.get_info(),
sticky,
long_press_time,
sprite_src,
diff --git a/wgui/src/parser/component_checkbox.rs b/wgui/src/parser/component_checkbox.rs
index 86793ee..252df4f 100644
--- a/wgui/src/parser/component_checkbox.rs
+++ b/wgui/src/parser/component_checkbox.rs
@@ -2,9 +2,15 @@ use crate::{
components::{Component, checkbox, radio_group::ComponentRadioGroup},
i18n::Translation,
layout::WidgetID,
- parser::{AttribPair, Fetchable, ParserContext, process_component, style::parse_style},
+ parser::{
+ AttribPair, Fetchable, ParserContext,
+ helpers::{TooltipAttribs, parse_attrib_tooltip},
+ process_component,
+ style::parse_style,
+ },
};
+#[derive(Clone, Copy)]
pub enum CheckboxKind {
CheckBox,
RadioBox,
@@ -21,6 +27,7 @@ pub fn parse_component_checkbox(
let mut translation = Translation::default();
let mut checked = 0;
let mut component_value = None;
+ let mut tooltip = TooltipAttribs::default();
let style = parse_style(ctx, attribs, tag_name);
@@ -46,7 +53,9 @@ pub fn parse_component_checkbox(
"checked" => {
ctx.parse_check_i32(tag_name, key, value, &mut checked);
}
- _ => {}
+ _ => {
+ parse_attrib_tooltip(ctx, tag_name, pair, &mut tooltip);
+ }
}
}
@@ -81,6 +90,7 @@ pub fn parse_component_checkbox(
style,
radio_group,
value: component_value,
+ tooltip: tooltip.get_info(),
},
)?;
diff --git a/wgui/src/parser/component_slider.rs b/wgui/src/parser/component_slider.rs
index b3c4ec1..ef09e8a 100644
--- a/wgui/src/parser/component_slider.rs
+++ b/wgui/src/parser/component_slider.rs
@@ -1,7 +1,12 @@
use crate::{
components::{Component, slider},
layout::WidgetID,
- parser::{AttribPair, ParserContext, process_component, style::parse_style},
+ parser::{
+ AttribPair, ParserContext,
+ helpers::{TooltipAttribs, parse_attrib_tooltip},
+ process_component,
+ style::parse_style,
+ },
widget::ConstructEssentials,
};
@@ -16,6 +21,7 @@ pub fn parse_component_slider(
let mut initial_value = 0.5;
let mut step = 1.0;
let mut show_value = 1;
+ let mut tooltip = TooltipAttribs::default();
let style = parse_style(ctx, attribs, tag_name);
@@ -37,7 +43,9 @@ pub fn parse_component_slider(
"show_value" => {
ctx.parse_check_i32(tag_name, key, value, &mut show_value);
}
- _ => {}
+ _ => {
+ parse_attrib_tooltip(ctx, tag_name, pair, &mut tooltip);
+ }
}
}
@@ -55,6 +63,7 @@ pub fn parse_component_slider(
step,
},
show_value: show_value != 0,
+ tooltip: tooltip.get_info(),
},
)?;
diff --git a/wgui/src/parser/helpers.rs b/wgui/src/parser/helpers.rs
new file mode 100644
index 0000000..061deb0
--- /dev/null
+++ b/wgui/src/parser/helpers.rs
@@ -0,0 +1,40 @@
+use crate::{
+ components::tooltip,
+ i18n::Translation,
+ parser::{AttribPair, ParserContext},
+};
+
+#[derive(Default)]
+pub struct TooltipAttribs {
+ tooltip: Option,
+ tooltip_side: Option,
+}
+
+impl TooltipAttribs {
+ pub fn get_info(self) -> Option {
+ self.tooltip.map(|text| tooltip::TooltipInfo {
+ text,
+ side: self.tooltip_side.map_or(tooltip::TooltipSide::Top, |f| f),
+ })
+ }
+}
+
+pub fn parse_attrib_tooltip(ctx: &mut ParserContext, tag_name: &str, pair: &AttribPair, tooltip: &mut TooltipAttribs) {
+ match pair.attrib.as_ref() {
+ "tooltip" if !pair.value.is_empty() => tooltip.tooltip = Some(Translation::from_translation_key(&pair.value)),
+ "tooltip_str" if !pair.value.is_empty() => tooltip.tooltip = Some(Translation::from_raw_text(&pair.value)),
+ "tooltip_side" => {
+ tooltip.tooltip_side = match pair.value.as_ref() {
+ "left" => Some(tooltip::TooltipSide::Left),
+ "right" => Some(tooltip::TooltipSide::Right),
+ "top" => Some(tooltip::TooltipSide::Top),
+ "bottom" => Some(tooltip::TooltipSide::Bottom),
+ _ => {
+ ctx.print_invalid_attrib(tag_name, &pair.attrib, &pair.value);
+ None
+ }
+ }
+ }
+ _ => {}
+ }
+}
diff --git a/wgui/src/parser/mod.rs b/wgui/src/parser/mod.rs
index d7f1f2e..90c5af0 100644
--- a/wgui/src/parser/mod.rs
+++ b/wgui/src/parser/mod.rs
@@ -3,6 +3,7 @@ mod component_checkbox;
mod component_radio_group;
mod component_slider;
mod component_tabs;
+mod helpers;
mod style;
mod widget_div;
mod widget_image;