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

@@ -8,12 +8,16 @@
<label text="Raw text" color="#FFFFFF" /> <label text="Raw text" color="#FFFFFF" />
<label translation="TESTBED.HELLO_WORLD" color="#FFFFFF" /> <label translation="TESTBED.HELLO_WORLD" color="#FFFFFF" />
<div margin_left="16" gap="8" flex_direction="column">
<label id="label_current_option" text="Click any of these buttons" size="20" weight="bold" />
<div>
<button id="button_red" text="Red button" width="220" height="32" color="#FF0000" />
<button id="button_aqua" text="Aqua button" width="220" height="32" color="#00FFFF" />
<button id="button_yellow" text="Yellow button" width="220" height="32" color="#FFFF00" />
</div>
<button id="button_click_me" text="Click me" width="128" height="24" color="#FFFFFF" />
</div>
<label id="label_current_option" />
<button margin_left="16" id="button_red" text="Red button" width="128" height="32" color="#FF0000" />
<button margin_left="16" id="button_aqua" text="Aqua button" width="128" height="32" color="#00FFFF" />
<button margin_left="16" id="button_yellow" text="Yellow button" width="128" height="32" color="#FFFF00" />
<div flex_direction="row" gap="16"> <div flex_direction="row" gap="16">
<div flex_direction="column" gap="8"> <div flex_direction="column" gap="8">

View File

@@ -108,7 +108,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
render_context.update_viewport(&mut shared_context, swapchain_size, scale)?; render_context.update_viewport(&mut shared_context, swapchain_size, scale)?;
println!("new swapchain_size: {swapchain_size:?}"); println!("new swapchain_size: {swapchain_size:?}");
let mut profiler = profiler::Profiler::new(100); let mut profiler = profiler::Profiler::new(1000);
let mut frame_index: u64 = 0; let mut frame_index: u64 = 0;
let mut timestep = Timestep::new(); let mut timestep = Timestep::new();

View File

@@ -1,8 +1,18 @@
use std::rc::Rc;
use crate::{assets, testbed::Testbed}; use crate::{assets, testbed::Testbed};
use glam::Vec2; use glam::Vec2;
use wgui::{ use wgui::{
components::button::ComponentButton, event::EventListenerCollection, globals::WguiGlobals, components::{
i18n::Translation, layout::Layout, parser::ParserState, Component,
button::{ButtonClickCallback, ComponentButton},
},
event::EventListenerCollection,
globals::WguiGlobals,
i18n::Translation,
layout::{Layout, Widget},
parser::ParserState,
widget::label::WidgetLabel,
}; };
pub struct TestbedGeneric { pub struct TestbedGeneric {
@@ -12,24 +22,63 @@ pub struct TestbedGeneric {
state: ParserState, state: ParserState,
} }
fn button_click_callback(
button: Component,
label: Widget,
text: &'static str,
) -> ButtonClickCallback {
Box::new(move |e| {
label.get_as_mut::<WidgetLabel>().set_text(
&mut e.state.globals.i18n(),
Translation::from_raw_text(text),
);
// FIXME: remove unwrap
button.try_cast::<ComponentButton>().unwrap().set_text(
e.state,
e.alterables,
Translation::from_raw_text("this button has been clicked"),
);
})
}
fn handle_button_click(button: Rc<ComponentButton>, label: Widget, text: &'static str) {
button.on_click(button_click_callback(
Component(button.clone()),
label,
text,
));
}
impl TestbedGeneric { impl TestbedGeneric {
pub fn new(listeners: &mut EventListenerCollection<(), ()>) -> anyhow::Result<Self> { pub fn new(listeners: &mut EventListenerCollection<(), ()>) -> anyhow::Result<Self> {
const XML_PATH: &str = "gui/various_widgets.xml"; const XML_PATH: &str = "gui/various_widgets.xml";
let globals = WguiGlobals::new(Box::new(assets::Asset {}))?; let globals = WguiGlobals::new(Box::new(assets::Asset {}))?;
let (mut layout, state) = let (layout, state) =
wgui::parser::new_layout_from_assets(globals, listeners, XML_PATH, false)?; wgui::parser::new_layout_from_assets(globals, listeners, XML_PATH, false)?;
let label_current_option = state.fetch_widget("label_current_option")?; let label_cur_option =
let b1 = state.fetch_component_as::<ComponentButton>("button_red")?; state.fetch_widget::<WidgetLabel>(&layout.state, "label_current_option")?;
let b2 = state.fetch_component_as::<ComponentButton>("button_aqua")?;
let b3 = state.fetch_component_as::<ComponentButton>("button_yellow")?;
b1.set_text( let button_click_me = state.fetch_component_as::<ComponentButton>("button_click_me")?;
&mut layout.state, let button = button_click_me.clone();
Translation::from_raw_text("hello, world!"), button_click_me.on_click(Box::new(move |e| {
button.set_text(
e.state,
e.alterables,
Translation::from_raw_text("congrats!"),
); );
}));
let button_red = state.fetch_component_as::<ComponentButton>("button_red")?;
let button_aqua = state.fetch_component_as::<ComponentButton>("button_aqua")?;
let button_yellow = state.fetch_component_as::<ComponentButton>("button_yellow")?;
handle_button_click(button_red, label_cur_option.clone(), "Clicked red");
handle_button_click(button_aqua, label_cur_option.clone(), "Clicked aqua");
handle_button_click(button_yellow, label_cur_option.clone(), "Clicked yellow");
Ok(Self { layout, state }) Ok(Self { layout, state })
} }

View File

@@ -97,9 +97,8 @@ impl Animation {
let widget_node = *state.nodes.get(self.target_widget).unwrap(); let widget_node = *state.nodes.get(self.target_widget).unwrap();
let layout = state.tree.layout(widget_node).unwrap(); // should always succeed let layout = state.tree.layout(widget_node).unwrap(); // should always succeed
let mut widget = widget.lock(); let mut widget_state = widget.state();
let (data, obj) = widget_state.get_data_obj_mut();
let (data, obj) = widget.get_data_obj_mut();
let data = &mut CallbackData { let data = &mut CallbackData {
widget_id: self.target_widget, widget_id: self.target_widget,

View File

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

View File

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

View File

@@ -5,7 +5,7 @@ use glam::{Mat4, Vec2};
use taffy::TraversePartialTree; use taffy::TraversePartialTree;
use crate::{ use crate::{
layout::BoxWidget, layout::Widget,
renderer_vk::text::custom_glyph::CustomGlyph, renderer_vk::text::custom_glyph::CustomGlyph,
transform_stack::{self, TransformStack}, transform_stack::{self, TransformStack},
widget::{self}, widget::{self},
@@ -38,7 +38,7 @@ impl Boundary {
} }
} }
#[derive(Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct Color { pub struct Color {
pub r: f32, pub r: f32,
pub g: f32, pub g: f32,
@@ -69,7 +69,7 @@ impl Default for Color {
} }
#[repr(u8)] #[repr(u8)]
#[derive(Default, Clone, Copy)] #[derive(Debug, Default, Clone, Copy)]
pub enum GradientMode { pub enum GradientMode {
#[default] #[default]
None, None,
@@ -108,7 +108,7 @@ fn draw_widget(
state: &mut DrawState, state: &mut DrawState,
node_id: taffy::NodeId, node_id: taffy::NodeId,
style: &taffy::Style, style: &taffy::Style,
widget: &BoxWidget, widget: &Widget,
parent_transform: &glam::Mat4, parent_transform: &glam::Mat4,
) { ) {
let Ok(l) = layout.state.tree.layout(node_id) else { let Ok(l) = layout.state.tree.layout(node_id) else {
@@ -116,7 +116,7 @@ fn draw_widget(
return; return;
}; };
let mut widget_state = widget.lock(); let mut widget_state = widget.state();
let transform = widget_state.data.transform * *parent_transform; let transform = widget_state.data.transform * *parent_transform;

View File

@@ -5,7 +5,7 @@ use crate::assets::AssetProvider;
// a string which optionally has translation key in it // a string which optionally has translation key in it
// it will hopefully support dynamic language changing soon // it will hopefully support dynamic language changing soon
// for now it's just a simple string container // for now it's just a simple string container
#[derive(Default)] #[derive(Debug, Default)]
pub struct Translation { pub struct Translation {
text: Rc<str>, text: Rc<str>,
translated: bool, // if true, `text` is a translation key translated: bool, // if true, `text` is a translation key

View File

@@ -1,4 +1,8 @@
use std::{collections::VecDeque, rc::Rc, sync::Arc}; use std::{
cell::{RefCell, RefMut},
collections::VecDeque,
rc::Rc,
};
use crate::{ use crate::{
animation::Animations, animation::Animations,
@@ -6,11 +10,10 @@ use crate::{
event::{self, EventAlterables, EventListenerCollection}, event::{self, EventAlterables, EventListenerCollection},
globals::WguiGlobals, globals::WguiGlobals,
transform_stack::Transform, transform_stack::Transform,
widget::{self, EventParams, WidgetObj, WidgetState, div::Div}, widget::{self, EventParams, WidgetObj, WidgetState, div::WidgetDiv},
}; };
use glam::{Vec2, vec2}; use glam::{Vec2, vec2};
use parking_lot::{MappedMutexGuard, Mutex, MutexGuard};
use slotmap::{HopSlotMap, SecondaryMap, new_key_type}; use slotmap::{HopSlotMap, SecondaryMap, new_key_type};
use taffy::{TaffyTree, TraversePartialTree}; use taffy::{TaffyTree, TraversePartialTree};
@@ -18,9 +21,26 @@ new_key_type! {
pub struct WidgetID; pub struct WidgetID;
} }
pub type BoxWidget = Arc<Mutex<WidgetState>>; #[derive(Clone)]
pub struct Widget(Rc<RefCell<WidgetState>>);
pub struct WidgetMap(HopSlotMap<WidgetID, BoxWidget>); impl Widget {
pub fn new(widget_state: WidgetState) -> Self {
Self(Rc::new(RefCell::new(widget_state)))
}
// panics on failure
// TODO: panic-less alternative
pub fn get_as_mut<T: 'static>(&self) -> RefMut<T> {
RefMut::map(self.0.borrow_mut(), |w| w.obj.get_as_mut::<T>())
}
pub fn state(&self) -> RefMut<WidgetState> {
self.0.borrow_mut()
}
}
pub struct WidgetMap(HopSlotMap<WidgetID, Widget>);
pub type WidgetNodeMap = SecondaryMap<WidgetID, taffy::NodeId>; pub type WidgetNodeMap = SecondaryMap<WidgetID, taffy::NodeId>;
impl WidgetMap { impl WidgetMap {
@@ -28,21 +48,21 @@ impl WidgetMap {
Self(HopSlotMap::with_key()) Self(HopSlotMap::with_key())
} }
pub fn get_as<T: 'static>(&self, handle: WidgetID) -> Option<MappedMutexGuard<T>> { pub fn get_as<T: 'static>(&self, handle: WidgetID) -> Option<RefMut<T>> {
let widget = self.0.get(handle)?; Some(self.0.get(handle)?.get_as_mut::<T>())
Some(MutexGuard::map(widget.lock(), |w| w.obj.get_as_mut::<T>()))
} }
pub fn get(&self, handle: WidgetID) -> Option<&BoxWidget> { pub fn get(&self, handle: WidgetID) -> Option<&Widget> {
self.0.get(handle) self.0.get(handle)
} }
pub fn insert(&mut self, obj: BoxWidget) -> WidgetID { pub fn insert(&mut self, obj: Widget) -> WidgetID {
self.0.insert(obj) self.0.insert(obj)
} }
// cast to specific widget type, does nothing if widget ID is expired // cast to specific widget type, does nothing if widget ID is expired
// panics in case if the widget type is wrong // panics in case if the widget type is wrong
// TODO: panic-less alternative
pub fn call<WIDGET, FUNC>(&self, widget_id: WidgetID, func: FUNC) pub fn call<WIDGET, FUNC>(&self, widget_id: WidgetID, func: FUNC)
where where
WIDGET: WidgetObj, WIDGET: WidgetObj,
@@ -53,10 +73,7 @@ impl WidgetMap {
return; return;
}; };
let mut lock = widget.lock(); func(&mut widget.get_as_mut::<WIDGET>());
let m = lock.obj.get_as_mut::<WIDGET>();
func(m);
} }
} }
@@ -65,13 +82,12 @@ pub struct LayoutState {
pub widgets: WidgetMap, pub widgets: WidgetMap,
pub nodes: WidgetNodeMap, pub nodes: WidgetNodeMap,
pub tree: taffy::tree::TaffyTree<WidgetID>, pub tree: taffy::tree::TaffyTree<WidgetID>,
pub alterables: EventAlterables,
} }
pub struct Layout { pub struct Layout {
pub state: LayoutState, pub state: LayoutState,
pub components_to_init: VecDeque<Rc<dyn Component>>, pub components_to_init: VecDeque<Component>,
pub root_widget: WidgetID, pub root_widget: WidgetID,
pub root_node: taffy::NodeId, pub root_node: taffy::NodeId,
@@ -90,11 +106,11 @@ fn add_child_internal(
widgets: &mut WidgetMap, widgets: &mut WidgetMap,
nodes: &mut WidgetNodeMap, nodes: &mut WidgetNodeMap,
parent_node: Option<taffy::NodeId>, parent_node: Option<taffy::NodeId>,
widget: WidgetState, widget_state: WidgetState,
style: taffy::Style, style: taffy::Style,
) -> anyhow::Result<(WidgetID, taffy::NodeId)> { ) -> anyhow::Result<(WidgetID, taffy::NodeId)> {
#[allow(clippy::arc_with_non_send_sync)] #[allow(clippy::arc_with_non_send_sync)]
let child_id = widgets.insert(Arc::new(Mutex::new(widget))); let child_id = widgets.insert(Widget::new(widget_state));
let child_node = tree.new_leaf_with_context(style, child_id)?; let child_node = tree.new_leaf_with_context(style, child_id)?;
if let Some(parent_node) = parent_node { if let Some(parent_node) = parent_node {
@@ -131,7 +147,7 @@ impl Layout {
let mut alterables = EventAlterables::default(); let mut alterables = EventAlterables::default();
while let Some(c) = self.components_to_init.pop_front() { while let Some(c) = self.components_to_init.pop_front() {
c.init(&mut InitData { c.0.init(&mut InitData {
state: &self.state, state: &self.state,
alterables: &mut alterables, alterables: &mut alterables,
}); });
@@ -142,7 +158,7 @@ impl Layout {
Ok(()) Ok(())
} }
pub fn defer_component_init(&mut self, component: Rc<dyn Component>) { pub fn defer_component_init(&mut self, component: Component) {
self.components_to_init.push_back(component); self.components_to_init.push_back(component);
} }
@@ -181,8 +197,6 @@ impl Layout {
anyhow::bail!("invalid widget"); anyhow::bail!("invalid widget");
}; };
let mut widget = widget.lock();
let transform = Transform { let transform = Transform {
pos: Vec2::new(l.location.x, l.location.y), pos: Vec2::new(l.location.x, l.location.y),
dim: Vec2::new(l.size.width, l.size.height), dim: Vec2::new(l.size.width, l.size.height),
@@ -203,6 +217,8 @@ impl Layout {
let listeners_vec = listeners.get(widget_id); let listeners_vec = listeners.get(widget_id);
let mut widget = widget.0.borrow_mut();
match widget.process_event( match widget.process_event(
widget_id, widget_id,
listeners_vec, listeners_vec,
@@ -283,7 +299,6 @@ impl Layout {
widgets: WidgetMap::new(), widgets: WidgetMap::new(),
nodes: WidgetNodeMap::default(), nodes: WidgetNodeMap::default(),
globals, globals,
alterables: EventAlterables::default(),
}; };
let (root_widget, root_node) = add_child_internal( let (root_widget, root_node) = add_child_internal(
@@ -291,7 +306,7 @@ impl Layout {
&mut state.widgets, &mut state.widgets,
&mut state.nodes, &mut state.nodes,
None, // no parent None, // no parent
Div::create()?, WidgetDiv::create()?,
taffy::Style { taffy::Style {
size: taffy::Size::auto(), size: taffy::Size::auto(),
..Default::default() ..Default::default()
@@ -343,7 +358,10 @@ impl Layout {
None => taffy::Size::ZERO, None => taffy::Size::ZERO,
Some(h) => { Some(h) => {
if let Some(w) = self.state.widgets.get(*h) { if let Some(w) = self.state.widgets.get(*h) {
w.lock().obj.measure(known_dimensions, available_space) w.0
.borrow_mut()
.obj
.measure(known_dimensions, available_space)
} else { } else {
taffy::Size::ZERO taffy::Size::ZERO
} }
@@ -352,13 +370,15 @@ impl Layout {
}, },
)?; )?;
let root_size = self.state.tree.layout(self.root_node).unwrap().size; let root_size = self.state.tree.layout(self.root_node).unwrap().size;
if self.content_size.x != root_size.width || self.content_size.y != root_size.height {
log::debug!( log::debug!(
"content size {:.0}x{:.0} → {:.0}x{:.0}", "content size changed: {:.0}x{:.0} → {:.0}x{:.0}",
self.content_size.x, self.content_size.x,
self.content_size.y, self.content_size.y,
root_size.width, root_size.width,
root_size.height root_size.height
); );
}
self.content_size = vec2(root_size.width, root_size.height); self.content_size = vec2(root_size.width, root_size.height);
} }
Ok(()) Ok(())
@@ -369,8 +389,6 @@ impl Layout {
self.animations.tick(&self.state, &mut alterables); self.animations.tick(&self.state, &mut alterables);
self.process_pending_components()?; self.process_pending_components()?;
self.process_alterables(alterables)?; self.process_alterables(alterables)?;
let state_alterables = std::mem::take(&mut self.state.alterables);
self.process_alterables(state_alterables)?;
Ok(()) Ok(())
} }

View File

@@ -1,5 +1,5 @@
use crate::{ use crate::{
components::button, components::{Component, button},
drawing::Color, drawing::Color,
i18n::Translation, i18n::Translation,
layout::WidgetID, layout::WidgetID,
@@ -69,7 +69,7 @@ pub fn parse_component_button<'a, U1, U2>(
}, },
)?; )?;
process_component(file, ctx, node, component)?; process_component(file, ctx, node, Component(component))?;
Ok(()) Ok(())
} }

View File

@@ -1,5 +1,5 @@
use crate::{ use crate::{
components::slider, components::{Component, slider},
layout::WidgetID, layout::WidgetID,
parser::{ parser::{
ParserContext, ParserFile, iter_attribs, parse_check_f32, process_component, style::parse_style, ParserContext, ParserFile, iter_attribs, parse_check_f32, process_component, style::parse_style,
@@ -48,7 +48,7 @@ pub fn parse_component_slider<'a, U1, U2>(
}, },
)?; )?;
process_component(file, ctx, node, component)?; process_component(file, ctx, node, Component(component))?;
Ok(()) Ok(())
} }

View File

@@ -7,13 +7,12 @@ mod widget_rectangle;
mod widget_sprite; mod widget_sprite;
use crate::{ use crate::{
any::AnyTrait,
assets::AssetProvider, assets::AssetProvider,
components::Component, components::{Component, ComponentTrait, ComponentWeak},
drawing::{self}, drawing::{self},
event::EventListenerCollection, event::EventListenerCollection,
globals::WguiGlobals, globals::WguiGlobals,
layout::{Layout, WidgetID}, layout::{Layout, LayoutState, Widget, WidgetID},
parser::{ parser::{
component_button::parse_component_button, component_slider::parse_component_slider, component_button::parse_component_button, component_slider::parse_component_slider,
widget_div::parse_widget_div, widget_label::parse_widget_label, widget_div::parse_widget_div, widget_label::parse_widget_label,
@@ -56,14 +55,14 @@ pub struct ParserState {
pub ids: HashMap<Rc<str>, WidgetID>, pub ids: HashMap<Rc<str>, WidgetID>,
macro_attribs: HashMap<Rc<str>, MacroAttribs>, macro_attribs: HashMap<Rc<str>, MacroAttribs>,
pub var_map: HashMap<Rc<str>, Rc<str>>, pub var_map: HashMap<Rc<str>, Rc<str>>,
pub components: Vec<Rc<dyn Component>>, pub components: Vec<Component>,
pub components_id_map: HashMap<Rc<str>, std::rc::Weak<dyn Component>>, pub components_id_map: HashMap<Rc<str>, std::rc::Weak<dyn ComponentTrait>>,
pub templates: HashMap<Rc<str>, Rc<Template>>, pub templates: HashMap<Rc<str>, Rc<Template>>,
pub path: PathBuf, pub path: PathBuf,
} }
impl ParserState { impl ParserState {
pub fn fetch_component(&self, id: &str) -> anyhow::Result<Rc<dyn Component>> { pub fn fetch_component(&self, id: &str) -> anyhow::Result<Component> {
let Some(weak) = self.components_id_map.get(id) else { let Some(weak) = self.components_id_map.get(id) else {
anyhow::bail!("Component by ID \"{}\" doesn't exist", id); anyhow::bail!("Component by ID \"{}\" doesn't exist", id);
}; };
@@ -72,27 +71,36 @@ impl ParserState {
anyhow::bail!("Component by ID \"{}\" doesn't exist", id); anyhow::bail!("Component by ID \"{}\" doesn't exist", id);
}; };
Ok(component) Ok(Component(component))
} }
pub fn fetch_component_as<T: 'static>(&self, id: &str) -> anyhow::Result<Rc<T>> { pub fn fetch_component_as<T: 'static>(&self, id: &str) -> anyhow::Result<Rc<T>> {
let component = self.fetch_component(id)?; let component = self.fetch_component(id)?;
// FIXME: check T type id if !(*component.0).as_any().is::<T>() {
log::warn!("fetch_component_as WIP"); anyhow::bail!("fetch_component_as({}): type not matching", id);
unsafe {
let raw = Rc::into_raw(component);
Ok(Rc::from_raw(raw as _))
}
} }
pub fn fetch_widget(&self, id: &str) -> anyhow::Result<WidgetID> { // safety: we already checked it above, should be safe to directly cast it
unsafe { Ok(Rc::from_raw(Rc::into_raw(component.0) as _)) }
}
pub fn get_widget_id(&self, id: &str) -> anyhow::Result<WidgetID> {
match self.ids.get(id) { match self.ids.get(id) {
Some(id) => Ok(*id), Some(id) => Ok(*id),
None => anyhow::bail!("Widget by ID \"{}\" doesn't exist", id), None => anyhow::bail!("Widget by ID \"{}\" doesn't exist", id),
} }
} }
pub fn fetch_widget<T: 'static>(&self, state: &LayoutState, id: &str) -> anyhow::Result<Widget> {
let widget_id = self.get_widget_id(id)?;
let widget = state
.widgets
.get(widget_id)
.ok_or_else(|| anyhow::anyhow!("fetch_widget_as({}): widget not found", id))?;
Ok(widget.clone())
}
pub fn process_template<U1, U2>( pub fn process_template<U1, U2>(
&mut self, &mut self,
template_name: &str, template_name: &str,
@@ -153,8 +161,8 @@ struct ParserContext<'a, U1, U2> {
macro_attribs: HashMap<Rc<str>, MacroAttribs>, macro_attribs: HashMap<Rc<str>, MacroAttribs>,
ids: HashMap<Rc<str>, WidgetID>, ids: HashMap<Rc<str>, WidgetID>,
templates: HashMap<Rc<str>, Rc<Template>>, templates: HashMap<Rc<str>, Rc<Template>>,
components: Vec<Rc<dyn Component>>, components: Vec<Component>,
components_id_map: HashMap<Rc<str>, std::rc::Weak<dyn Component>>, components_id_map: HashMap<Rc<str>, ComponentWeak>,
dev_mode: bool, dev_mode: bool,
} }
@@ -561,7 +569,7 @@ fn process_component<'a, U1, U2>(
file: &'a ParserFile, file: &'a ParserFile,
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext<U1, U2>,
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
component: Rc<dyn Component>, component: Component,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect(); let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
@@ -571,7 +579,7 @@ fn process_component<'a, U1, U2>(
"id" => { "id" => {
if ctx if ctx
.components_id_map .components_id_map
.insert(value.clone(), Rc::downgrade(&component)) .insert(value.clone(), component.weak())
.is_some() .is_some()
{ {
log::warn!("duplicate component ID \"{value}\" in the same layout file!"); log::warn!("duplicate component ID \"{value}\" in the same layout file!");

View File

@@ -1,7 +1,8 @@
use crate::{ use crate::{
layout::WidgetID, layout::WidgetID,
parser::{ parser::{
ParserContext, ParserFile, iter_attribs, parse_children, parse_widget_universal, style::parse_style, ParserContext, ParserFile, iter_attribs, parse_children, parse_widget_universal,
style::parse_style,
}, },
widget, widget,
}; };
@@ -17,7 +18,7 @@ pub fn parse_widget_div<'a, U1, U2>(
let (new_id, _) = ctx let (new_id, _) = ctx
.layout .layout
.add_child(parent_id, widget::div::Div::create()?, style)?; .add_child(parent_id, widget::div::WidgetDiv::create()?, style)?;
parse_widget_universal(file, ctx, node, new_id)?; parse_widget_universal(file, ctx, node, new_id)?;
parse_children(file, ctx, node, new_id)?; parse_children(file, ctx, node, new_id)?;

View File

@@ -5,7 +5,7 @@ use crate::{
ParserContext, ParserFile, iter_attribs, parse_children, parse_widget_universal, ParserContext, ParserFile, iter_attribs, parse_children, parse_widget_universal,
style::{parse_style, parse_text_style}, style::{parse_style, parse_text_style},
}, },
widget::text::{TextLabel, TextParams}, widget::label::{WidgetLabelParams, WidgetLabel},
}; };
pub fn parse_widget_label<'a, U1, U2>( pub fn parse_widget_label<'a, U1, U2>(
@@ -14,7 +14,7 @@ pub fn parse_widget_label<'a, U1, U2>(
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut params = TextParams::default(); let mut params = WidgetLabelParams::default();
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect(); let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
let style = parse_style(&attribs); let style = parse_style(&attribs);
@@ -36,7 +36,7 @@ pub fn parse_widget_label<'a, U1, U2>(
let (new_id, _) = let (new_id, _) =
ctx ctx
.layout .layout
.add_child(parent_id, TextLabel::create(&mut i18n, params)?, style)?; .add_child(parent_id, WidgetLabel::create(&mut i18n, params)?, style)?;
parse_widget_universal(file, ctx, node, new_id)?; parse_widget_universal(file, ctx, node, new_id)?;
parse_children(file, ctx, node, new_id)?; parse_children(file, ctx, node, new_id)?;

View File

@@ -6,7 +6,7 @@ use crate::{
print_invalid_attrib, print_invalid_attrib,
style::{parse_color, parse_round, parse_style}, style::{parse_color, parse_round, parse_style},
}, },
widget::{self, rectangle::RectangleParams}, widget::{self, rectangle::WidgetRectangleParams},
}; };
pub fn parse_widget_rectangle<'a, U1, U2>( pub fn parse_widget_rectangle<'a, U1, U2>(
@@ -15,7 +15,7 @@ pub fn parse_widget_rectangle<'a, U1, U2>(
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut params = RectangleParams::default(); let mut params = WidgetRectangleParams::default();
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect(); let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
let style = parse_style(&attribs); let style = parse_style(&attribs);
@@ -57,7 +57,7 @@ pub fn parse_widget_rectangle<'a, U1, U2>(
let (new_id, _) = ctx.layout.add_child( let (new_id, _) = ctx.layout.add_child(
parent_id, parent_id,
widget::rectangle::Rectangle::create(params)?, widget::rectangle::WidgetRectangle::create(params)?,
style, style,
)?; )?;

View File

@@ -5,7 +5,7 @@ use crate::{
style::parse_style, style::parse_style,
}, },
renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData}, renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData},
widget::sprite::{SpriteBox, SpriteBoxParams}, widget::sprite::{WidgetSprite, WidgetSpriteParams},
}; };
use super::{parse_color_hex, print_invalid_attrib}; use super::{parse_color_hex, print_invalid_attrib};
@@ -16,7 +16,7 @@ pub fn parse_widget_sprite<'a, U1, U2>(
node: roxmltree::Node<'a, 'a>, node: roxmltree::Node<'a, 'a>,
parent_id: WidgetID, parent_id: WidgetID,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut params = SpriteBoxParams::default(); let mut params = WidgetSpriteParams::default();
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect(); let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
let style = parse_style(&attribs); let style = parse_style(&attribs);
@@ -57,7 +57,7 @@ pub fn parse_widget_sprite<'a, U1, U2>(
let (new_id, _) = ctx let (new_id, _) = ctx
.layout .layout
.add_child(parent_id, SpriteBox::create(params)?, style)?; .add_child(parent_id, WidgetSprite::create(params)?, style)?;
parse_widget_universal(file, ctx, node, new_id)?; parse_widget_universal(file, ctx, node, new_id)?;
parse_children(file, ctx, node, new_id)?; parse_children(file, ctx, node, new_id)?;

View File

@@ -1,14 +1,14 @@
use super::{WidgetObj, WidgetState}; use super::{WidgetObj, WidgetState};
pub struct Div {} pub struct WidgetDiv {}
impl Div { impl WidgetDiv {
pub fn create() -> anyhow::Result<WidgetState> { pub fn create() -> anyhow::Result<WidgetState> {
WidgetState::new(Box::new(Self {})) WidgetState::new(Box::new(Self {}))
} }
} }
impl WidgetObj for Div { impl WidgetObj for WidgetDiv {
fn draw(&mut self, _state: &mut super::DrawState, _params: &super::DrawParams) { fn draw(&mut self, _state: &mut super::DrawState, _params: &super::DrawParams) {
// no-op // no-op
} }

View File

@@ -12,19 +12,19 @@ use crate::{
use super::{WidgetObj, WidgetState}; use super::{WidgetObj, WidgetState};
#[derive(Default)] #[derive(Default)]
pub struct TextParams { pub struct WidgetLabelParams {
pub content: Translation, pub content: Translation,
pub style: TextStyle, pub style: TextStyle,
} }
pub struct TextLabel { pub struct WidgetLabel {
params: TextParams, params: WidgetLabelParams,
buffer: Rc<RefCell<Buffer>>, buffer: Rc<RefCell<Buffer>>,
last_boundary: Boundary, last_boundary: Boundary,
} }
impl TextLabel { impl WidgetLabel {
pub fn create(i18n: &mut I18n, params: TextParams) -> anyhow::Result<WidgetState> { pub fn create(i18n: &mut I18n, params: WidgetLabelParams) -> anyhow::Result<WidgetState> {
let metrics = Metrics::from(&params.style); let metrics = Metrics::from(&params.style);
let attrs = Attrs::from(&params.style); let attrs = Attrs::from(&params.style);
let wrap = Wrap::from(&params.style); let wrap = Wrap::from(&params.style);
@@ -70,7 +70,7 @@ impl TextLabel {
} }
} }
impl WidgetObj for TextLabel { impl WidgetObj for WidgetLabel {
fn draw(&mut self, state: &mut super::DrawState, _params: &super::DrawParams) { fn draw(&mut self, state: &mut super::DrawState, _params: &super::DrawParams) {
let boundary = drawing::Boundary::construct(state.transform_stack); let boundary = drawing::Boundary::construct(state.transform_stack);

View File

@@ -14,9 +14,9 @@ use crate::{
}; };
pub mod div; pub mod div;
pub mod label;
pub mod rectangle; pub mod rectangle;
pub mod sprite; pub mod sprite;
pub mod text;
pub mod util; pub mod util;
pub struct WidgetData { pub struct WidgetData {
@@ -173,11 +173,15 @@ pub fn get_scrollbar_info(l: &taffy::Layout) -> Option<ScrollbarInfo> {
} }
impl dyn WidgetObj { impl dyn WidgetObj {
// panics on failure
// TODO: panic-less alternative
pub fn get_as<T: 'static>(&self) -> &T { pub fn get_as<T: 'static>(&self) -> &T {
let any = self.as_any(); let any = self.as_any();
any.downcast_ref::<T>().unwrap() any.downcast_ref::<T>().unwrap()
} }
// panics on failure
// TODO: panic-less alternative
pub fn get_as_mut<T: 'static>(&mut self) -> &mut T { pub fn get_as_mut<T: 'static>(&mut self) -> &mut T {
let any = self.as_any_mut(); let any = self.as_any_mut();
any.downcast_mut::<T>().unwrap() any.downcast_mut::<T>().unwrap()

View File

@@ -6,7 +6,7 @@ use crate::{
use super::{WidgetObj, WidgetState}; use super::{WidgetObj, WidgetState};
#[derive(Default)] #[derive(Default)]
pub struct RectangleParams { pub struct WidgetRectangleParams {
pub color: drawing::Color, pub color: drawing::Color,
pub color2: drawing::Color, pub color2: drawing::Color,
pub gradient: GradientMode, pub gradient: GradientMode,
@@ -17,17 +17,17 @@ pub struct RectangleParams {
pub round: WLength, pub round: WLength,
} }
pub struct Rectangle { pub struct WidgetRectangle {
pub params: RectangleParams, pub params: WidgetRectangleParams,
} }
impl Rectangle { impl WidgetRectangle {
pub fn create(params: RectangleParams) -> anyhow::Result<WidgetState> { pub fn create(params: WidgetRectangleParams) -> anyhow::Result<WidgetState> {
WidgetState::new(Box::new(Rectangle { params })) WidgetState::new(Box::new(WidgetRectangle { params }))
} }
} }
impl WidgetObj for Rectangle { impl WidgetObj for WidgetRectangle {
fn draw(&mut self, state: &mut super::DrawState, _params: &super::DrawParams) { fn draw(&mut self, state: &mut super::DrawState, _params: &super::DrawParams) {
let boundary = drawing::Boundary::construct(state.transform_stack); let boundary = drawing::Boundary::construct(state.transform_stack);

View File

@@ -12,24 +12,24 @@ use crate::{
use super::{WidgetObj, WidgetState}; use super::{WidgetObj, WidgetState};
#[derive(Default)] #[derive(Debug, Default)]
pub struct SpriteBoxParams { pub struct WidgetSpriteParams {
pub glyph_data: Option<CustomGlyphData>, pub glyph_data: Option<CustomGlyphData>,
pub color: Option<drawing::Color>, pub color: Option<drawing::Color>,
} }
#[derive(Default)] #[derive(Debug, Default)]
pub struct SpriteBox { pub struct WidgetSprite {
params: SpriteBoxParams, params: WidgetSpriteParams,
} }
impl SpriteBox { impl WidgetSprite {
pub fn create(params: SpriteBoxParams) -> anyhow::Result<WidgetState> { pub fn create(params: WidgetSpriteParams) -> anyhow::Result<WidgetState> {
WidgetState::new(Box::new(Self { params })) WidgetState::new(Box::new(Self { params }))
} }
} }
impl WidgetObj for SpriteBox { impl WidgetObj for WidgetSprite {
fn draw(&mut self, state: &mut super::DrawState, _params: &super::DrawParams) { fn draw(&mut self, state: &mut super::DrawState, _params: &super::DrawParams) {
let boundary = drawing::Boundary::construct(state.transform_stack); let boundary = drawing::Boundary::construct(state.transform_stack);

View File

@@ -19,11 +19,10 @@ use crate::{
overlay::{FrameMeta, OverlayBackend, ShouldRender, ui_transform}, overlay::{FrameMeta, OverlayBackend, ShouldRender, ui_transform},
}, },
graphics::{CommandBuffers, ExtentExt}, graphics::{CommandBuffers, ExtentExt},
gui,
state::AppState, state::AppState,
}; };
use super::{asset::GuiAsset, timer::GuiTimer, timestep::Timestep}; use super::{timer::GuiTimer, timestep::Timestep};
const MAX_SIZE: u32 = 2048; const MAX_SIZE: u32 = 2048;
const MAX_SIZE_VEC2: Vec2 = vec2(MAX_SIZE as _, MAX_SIZE as _); const MAX_SIZE_VEC2: Vec2 = vec2(MAX_SIZE as _, MAX_SIZE as _);

View File

@@ -8,8 +8,8 @@ use wgui::{
renderer_vk::util, renderer_vk::util,
taffy::{self, prelude::length}, taffy::{self, prelude::length},
widget::{ widget::{
div::Div, div::WidgetDiv,
rectangle::{Rectangle, RectangleParams}, rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength, util::WLength,
}, },
}; };
@@ -56,7 +56,7 @@ where
let (background, _) = panel.layout.add_child( let (background, _) = panel.layout.add_child(
panel.layout.root_widget, panel.layout.root_widget,
Rectangle::create(RectangleParams { WidgetRectangle::create(WidgetRectangleParams {
color: wgui::drawing::Color::new(0., 0., 0., 0.6), color: wgui::drawing::Color::new(0., 0., 0., 0.6),
round: WLength::Units(4.0), round: WLength::Units(4.0),
..Default::default() ..Default::default()
@@ -85,7 +85,7 @@ where
for row in 0..layout.key_sizes.len() { for row in 0..layout.key_sizes.len() {
let (div, _) = panel.layout.add_child( let (div, _) = panel.layout.add_child(
background, background,
Div::create().unwrap(), WidgetDiv::create().unwrap(),
taffy::Style { taffy::Style {
flex_direction: taffy::FlexDirection::Row, flex_direction: taffy::FlexDirection::Row,
..Default::default() ..Default::default()
@@ -106,7 +106,7 @@ where
let Some(key) = layout.get_key_data(keymap.as_ref(), has_altgr, col, row) else { let Some(key) = layout.get_key_data(keymap.as_ref(), has_altgr, col, row) else {
let _ = panel.layout.add_child( let _ = panel.layout.add_child(
div, div,
Div::create()?, WidgetDiv::create()?,
taffy::Style { taffy::Style {
size: taffy_size, size: taffy_size,
min_size: taffy_size, min_size: taffy_size,
@@ -173,7 +173,7 @@ where
.layout .layout
.state .state
.widgets .widgets
.get_as::<Rectangle>(*widget_id) .get_as::<WidgetRectangle>(*widget_id)
.unwrap(); // want panic .unwrap(); // want panic
Rc::new(KeyState { Rc::new(KeyState {
@@ -291,7 +291,7 @@ fn get_anim_transform(pos: f32, widget_size: Vec2) -> Mat4 {
) )
} }
fn set_anim_color(key_state: &KeyState, rect: &mut Rectangle, pos: f32) { fn set_anim_color(key_state: &KeyState, rect: &mut WidgetRectangle, pos: f32) {
let br1 = pos * 0.25; let br1 = pos * 0.25;
let br2 = pos * 0.15; let br2 = pos * 0.15;
@@ -314,7 +314,7 @@ fn on_enter_anim(
10, 10,
AnimationEasing::OutBack, AnimationEasing::OutBack,
Box::new(move |common, data| { Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<Rectangle>(); let rect = data.obj.get_as_mut::<WidgetRectangle>();
set_anim_color(&key_state, rect, data.pos); set_anim_color(&key_state, rect, data.pos);
data.data.transform = get_anim_transform(data.pos, data.widget_size); data.data.transform = get_anim_transform(data.pos, data.widget_size);
common.alterables.mark_redraw(); common.alterables.mark_redraw();
@@ -332,7 +332,7 @@ fn on_leave_anim(
15, 15,
AnimationEasing::OutQuad, AnimationEasing::OutQuad,
Box::new(move |common, data| { Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<Rectangle>(); let rect = data.obj.get_as_mut::<WidgetRectangle>();
set_anim_color(&key_state, rect, 1.0 - data.pos); set_anim_color(&key_state, rect, 1.0 - data.pos);
data.data.transform = get_anim_transform(1.0 - data.pos, data.widget_size); data.data.transform = get_anim_transform(1.0 - data.pos, data.widget_size);
common.alterables.mark_redraw(); common.alterables.mark_redraw();
@@ -348,7 +348,7 @@ fn on_press_anim(
if key_state.drawn_state.get() { if key_state.drawn_state.get() {
return; return;
} }
let rect = data.obj.get_as_mut::<Rectangle>(); let rect = data.obj.get_as_mut::<WidgetRectangle>();
rect.params.border_color = Color::new(1.0, 1.0, 1.0, 1.0); rect.params.border_color = Color::new(1.0, 1.0, 1.0, 1.0);
common.alterables.mark_redraw(); common.alterables.mark_redraw();
key_state.drawn_state.set(true); key_state.drawn_state.set(true);
@@ -362,7 +362,7 @@ fn on_release_anim(
if !key_state.drawn_state.get() { if !key_state.drawn_state.get() {
return; return;
} }
let rect = data.obj.get_as_mut::<Rectangle>(); let rect = data.obj.get_as_mut::<WidgetRectangle>();
rect.params.border_color = key_state.border_color; rect.params.border_color = key_state.border_color;
common.alterables.mark_redraw(); common.alterables.mark_redraw();
key_state.drawn_state.set(false); key_state.drawn_state.set(false);

View File

@@ -17,8 +17,8 @@ use wgui::{
prelude::{auto, length, percent}, prelude::{auto, length, percent},
}, },
widget::{ widget::{
rectangle::{Rectangle, RectangleParams}, label::{WidgetLabelParams, WidgetLabel},
text::{TextLabel, TextParams}, rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength, util::WLength,
}, },
}; };
@@ -176,7 +176,7 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box<dyn
.layout .layout
.add_child( .add_child(
panel.layout.root_widget, panel.layout.root_widget,
Rectangle::create(RectangleParams { WidgetRectangle::create(WidgetRectangleParams {
color: parse_color_hex("#1e2030").unwrap(), color: parse_color_hex("#1e2030").unwrap(),
border_color: parse_color_hex("#5e7090").unwrap(), border_color: parse_color_hex("#5e7090").unwrap(),
border: 1.0, border: 1.0,
@@ -196,9 +196,9 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box<dyn
let _ = panel.layout.add_child( let _ = panel.layout.add_child(
rect, rect,
TextLabel::create( WidgetLabel::create(
&mut i18n, &mut i18n,
TextParams { WidgetLabelParams {
content: Translation::from_raw_text(&title), content: Translation::from_raw_text(&title),
style: TextStyle { style: TextStyle {
color: parse_color_hex("#ffffff"), color: parse_color_hex("#ffffff"),
@@ -219,9 +219,9 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box<dyn
let _ = panel.layout.add_child( let _ = panel.layout.add_child(
rect, rect,
TextLabel::create( WidgetLabel::create(
&mut i18n, &mut i18n,
TextParams { WidgetLabelParams {
content: Translation::from_raw_text(&toast.body), content: Translation::from_raw_text(&toast.body),
style: TextStyle { style: TextStyle {
weight: Some(FontWeight::Bold), weight: Some(FontWeight::Bold),

View File

@@ -7,7 +7,7 @@ use regex::Regex;
use wgui::{ use wgui::{
event::{self, EventListenerKind}, event::{self, EventListenerKind},
i18n::Translation, i18n::Translation,
widget::text::TextLabel, widget::label::WidgetLabel,
}; };
use crate::{ use crate::{
@@ -46,7 +46,7 @@ where
.layout .layout
.state .state
.widgets .widgets
.get_as::<TextLabel>(*widget_id) .get_as::<WidgetLabel>(*widget_id)
.unwrap(); .unwrap();
let format = match role { let format = match role {
@@ -165,6 +165,6 @@ fn clock_on_tick(
|tz| format!("{}", Local::now().with_timezone(tz).format(&clock.format)), |tz| format!("{}", Local::now().with_timezone(tz).format(&clock.format)),
); );
let label = data.obj.get_as_mut::<TextLabel>(); let label = data.obj.get_as_mut::<WidgetLabel>();
label.set_text(&mut common.i18n(), Translation::from_raw_text(&date_time)); label.set_text(&mut common.i18n(), Translation::from_raw_text(&date_time));
} }