wgui: interactable components, rename TextLabel -> WidgetLabel

This commit is contained in:
Aleksander
2025-08-10 11:46:01 +02:00
parent 91e584383f
commit 93a3fee349
26 changed files with 294 additions and 174 deletions

View File

@@ -3,15 +3,15 @@ use taffy::{AlignItems, JustifyContent, prelude::length};
use crate::{
animation::{Animation, AnimationEasing},
components::{Component, InitData},
components::{Component, ComponentTrait, InitData},
drawing::{self, Color},
event::{EventAlterables, EventListenerCollection, EventListenerKind, ListenerHandleVec},
i18n::Translation,
layout::{Layout, LayoutState, WidgetID},
renderer_vk::text::{FontWeight, TextStyle},
widget::{
rectangle::{Rectangle, RectangleParams},
text::{TextLabel, TextParams},
label::{WidgetLabel, WidgetLabelParams},
rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength,
},
};
@@ -38,9 +38,16 @@ impl Default for Params {
}
}
pub struct ButtonClickEvent<'a> {
pub state: &'a LayoutState,
pub alterables: &'a mut EventAlterables,
}
pub type ButtonClickCallback = Box<dyn Fn(ButtonClickEvent)>;
struct State {
hovered: bool,
down: bool,
on_click: Option<ButtonClickCallback>,
}
struct Data {
@@ -59,27 +66,30 @@ pub struct ComponentButton {
listener_handles: ListenerHandleVec,
}
impl Component for ComponentButton {
impl ComponentTrait for ComponentButton {
fn init(&self, _data: &mut InitData) {}
}
impl ComponentButton {
pub fn set_text(&self, state: &mut LayoutState, text: Translation) {
pub fn set_text(&self, state: &LayoutState, alterables: &mut EventAlterables, text: Translation) {
let globals = state.globals.clone();
state
.widgets
.call(self.data.text_id, |label: &mut TextLabel| {
.call(self.data.text_id, |label: &mut WidgetLabel| {
label.set_text(&mut globals.i18n(), text);
});
let mut alterables = EventAlterables::default();
alterables.mark_redraw();
alterables.mark_dirty(self.data.text_node);
}
pub fn on_click(&self, func: ButtonClickCallback) {
self.state.borrow_mut().on_click = Some(func);
}
}
fn anim_hover(rect: &mut Rectangle, data: &Data, pos: f32) {
fn anim_hover(rect: &mut WidgetRectangle, data: &Data, pos: f32) {
let brightness = pos * 0.5;
let border_brightness = pos;
rect.params.color.r = data.initial_color.r + brightness;
@@ -97,7 +107,7 @@ fn anim_hover_in(data: Rc<Data>, widget_id: WidgetID) -> Animation {
5,
AnimationEasing::OutQuad,
Box::new(move |common, anim_data| {
let rect = anim_data.obj.get_as_mut::<Rectangle>();
let rect = anim_data.obj.get_as_mut::<WidgetRectangle>();
anim_hover(rect, &data, anim_data.pos);
common.alterables.mark_redraw();
}),
@@ -110,7 +120,7 @@ fn anim_hover_out(data: Rc<Data>, widget_id: WidgetID) -> Animation {
8,
AnimationEasing::OutQuad,
Box::new(move |common, anim_data| {
let rect = anim_data.obj.get_as_mut::<Rectangle>();
let rect = anim_data.obj.get_as_mut::<WidgetRectangle>();
anim_hover(rect, &data, 1.0 - anim_data.pos);
common.alterables.mark_redraw();
}),
@@ -167,7 +177,7 @@ fn register_event_mouse_press<U1, U2>(
listener_handles,
data.rect_id,
EventListenerKind::MousePress,
Box::new(move |common, event_data, _, _| {
Box::new(move |common, _event_data, _, _| {
common.alterables.trigger_haptics();
let mut state = state.borrow_mut();
@@ -196,7 +206,12 @@ fn register_event_mouse_release<U1, U2>(
state.down = false;
if state.hovered {
//TODO: click event
if let Some(on_click) = &state.on_click {
on_click(ButtonClickEvent {
state: common.state,
alterables: &mut common.alterables,
});
}
}
}
}),
@@ -220,7 +235,7 @@ pub fn construct<U1, U2>(
let (rect_id, _) = layout.add_child(
parent,
Rectangle::create(RectangleParams {
WidgetRectangle::create(WidgetRectangleParams {
color: params.color,
color2: params
.color
@@ -237,9 +252,9 @@ pub fn construct<U1, U2>(
let (text_id, text_node) = layout.add_child(
rect_id,
TextLabel::create(
WidgetLabel::create(
&mut globals.i18n(),
TextParams {
WidgetLabelParams {
content: params.text,
style: TextStyle {
weight: Some(FontWeight::Bold),
@@ -268,6 +283,7 @@ pub fn construct<U1, U2>(
let state = Rc::new(RefCell::new(State {
down: false,
hovered: false,
on_click: None,
}));
let mut lhandles = ListenerHandleVec::default();
@@ -283,6 +299,6 @@ pub fn construct<U1, U2>(
listener_handles: lhandles,
});
layout.defer_component_init(button.clone());
layout.defer_component_init(Component(button.clone()));
Ok(button)
}

View File

@@ -1,3 +1,5 @@
use std::rc::Rc;
use crate::{any::AnyTrait, event::EventAlterables, layout::LayoutState};
pub mod button;
@@ -8,6 +10,26 @@ pub struct InitData<'a> {
pub alterables: &'a mut EventAlterables,
}
pub trait Component: AnyTrait {
pub trait ComponentTrait: AnyTrait {
fn init(&self, data: &mut InitData);
}
#[derive(Clone)]
pub struct Component(pub Rc<dyn ComponentTrait>);
pub type ComponentWeak = std::rc::Weak<dyn ComponentTrait>;
impl Component {
pub fn weak(&self) -> ComponentWeak {
Rc::downgrade(&self.0)
}
pub fn try_cast<T: 'static>(&self) -> anyhow::Result<Rc<T>> {
if !(*self.0).as_any().is::<T>() {
anyhow::bail!("try_cast: type not matching");
}
// safety: we already checked it above, should be safe to directly cast it
unsafe { Ok(Rc::from_raw(Rc::into_raw(self.0.clone()) as _)) }
}
}

View File

@@ -5,7 +5,7 @@ use taffy::prelude::{length, percent};
use crate::{
animation::{Animation, AnimationEasing},
components::{Component, InitData},
components::{Component, ComponentTrait, InitData},
drawing::{self},
event::{
self, CallbackDataCommon, EventAlterables, EventListenerCollection, EventListenerKind,
@@ -18,9 +18,9 @@ use crate::{
util,
},
widget::{
div::Div,
rectangle::{Rectangle, RectangleParams},
text::{TextLabel, TextParams},
div::WidgetDiv,
label::{WidgetLabel, WidgetLabelParams},
rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength,
},
};
@@ -70,7 +70,7 @@ pub struct ComponentSlider {
listener_handles: ListenerHandleVec,
}
impl Component for ComponentSlider {
impl ComponentTrait for ComponentSlider {
fn init(&self, init_data: &mut InitData) {
let mut state = self.state.borrow_mut();
let value = state.values.value;
@@ -129,7 +129,7 @@ impl State {
self.set_value(common.state, data, common.alterables, val);
}
fn update_text(&self, i18n: &mut I18n, text: &mut TextLabel, value: f32) {
fn update_text(&self, i18n: &mut I18n, text: &mut WidgetLabel, value: f32) {
// round displayed value, should be sufficient for now
text.set_text(
i18n,
@@ -153,7 +153,7 @@ impl State {
alterables.set_style(data.slider_handle_node, style);
state
.widgets
.call(data.slider_text_id, |label: &mut TextLabel| {
.call(data.slider_text_id, |label: &mut WidgetLabel| {
self.update_text(&mut state.globals.i18n(), label, value);
});
}
@@ -174,7 +174,7 @@ fn get_anim_transform(pos: f32, widget_size: Vec2) -> Mat4 {
)
}
fn anim_rect(rect: &mut Rectangle, pos: f32) {
fn anim_rect(rect: &mut WidgetRectangle, pos: f32) {
rect.params.color = drawing::Color::lerp(&HANDLE_COLOR, &HANDLE_COLOR_HOVERED, pos);
rect.params.border_color =
drawing::Color::lerp(&HANDLE_BORDER_COLOR, &HANDLE_BORDER_COLOR_HOVERED, pos);
@@ -186,7 +186,7 @@ fn on_enter_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) {
20,
AnimationEasing::OutBack,
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<Rectangle>();
let rect = data.obj.get_as_mut::<WidgetRectangle>();
data.data.transform = get_anim_transform(data.pos, data.widget_size);
anim_rect(rect, data.pos);
common.alterables.mark_redraw();
@@ -200,7 +200,7 @@ fn on_leave_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) {
10,
AnimationEasing::OutQuad,
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<Rectangle>();
let rect = data.obj.get_as_mut::<WidgetRectangle>();
data.data.transform = get_anim_transform(1.0 - data.pos, data.widget_size);
anim_rect(rect, 1.0 - data.pos);
common.alterables.mark_redraw();
@@ -318,11 +318,11 @@ pub fn construct<U1, U2>(
style.min_size = style.size;
style.max_size = style.size;
let (body_id, slider_body_node) = layout.add_child(parent, Div::create()?, style)?;
let (body_id, slider_body_node) = layout.add_child(parent, WidgetDiv::create()?, style)?;
let (_background_id, _) = layout.add_child(
body_id,
Rectangle::create(RectangleParams {
WidgetRectangle::create(WidgetRectangleParams {
color: BODY_COLOR,
round: WLength::Percent(1.0),
border_color: BODY_BORDER_COLOR,
@@ -361,11 +361,11 @@ pub fn construct<U1, U2>(
// invisible outer handle body
let (slider_handle_id, slider_handle_node) =
layout.add_child(body_id, Div::create()?, slider_handle_style)?;
layout.add_child(body_id, WidgetDiv::create()?, slider_handle_style)?;
let (slider_handle_rect_id, _) = layout.add_child(
slider_handle_id,
Rectangle::create(RectangleParams {
WidgetRectangle::create(WidgetRectangleParams {
color: HANDLE_COLOR,
border_color: HANDLE_BORDER_COLOR,
border: 2.0,
@@ -393,9 +393,9 @@ pub fn construct<U1, U2>(
let (slider_text_id, _) = layout.add_child(
slider_handle_id,
TextLabel::create(
WidgetLabel::create(
&mut i18n,
TextParams {
WidgetLabelParams {
content: Translation::default(),
style: TextStyle {
weight: Some(FontWeight::Bold),
@@ -432,6 +432,6 @@ pub fn construct<U1, U2>(
listener_handles: lhandles,
});
layout.defer_component_init(slider.clone());
layout.defer_component_init(Component(slider.clone()));
Ok(slider)
}