dash-frontend: Use native <Button>s, children support

This commit is contained in:
Aleksander
2025-09-13 12:49:36 +02:00
parent c2628c0a15
commit 9efe298f65
13 changed files with 117 additions and 82 deletions

View File

@@ -64,7 +64,7 @@
## div widget
`<div>`
### `<div>`
### The most simple element
@@ -76,7 +76,7 @@ _None_
## label widget
`<label>`
### `<label>`
### A simple text element
@@ -104,7 +104,7 @@ _Text size in pixel units_
## rectangle widget
`<rectangle>`
### `<rectangle>`
### A styled rectangle
@@ -130,7 +130,7 @@ _2nd gradient color_
## sprite widget
`<sprite>`
### `<sprite>`
### Image widget, supports raster and svg vector
@@ -150,7 +150,7 @@ _External image path_
## Button component
`<Button>`
### `<Button>`
### A clickable, decorated button
@@ -170,11 +170,15 @@ _Translated by key_
`border_color`: #FFAABB | #FFAABBCC
#### Info
Child widgets are supported and can be added directly in XML.
---
## Slider component
`<Slider>`
### `<Slider>`
### A simple slider.
@@ -194,7 +198,7 @@ _Initial slider value_
## Checkbox component
`<CheckBox>`
### `<CheckBox>`
### A check-box with label.

View File

@@ -101,6 +101,11 @@ pub struct Layout {
pub animations: Animations,
}
#[derive(Default)]
pub struct LayoutParams {
pub resize_to_parent: bool,
}
fn add_child_internal(
tree: &mut taffy::TaffyTree<WidgetID>,
widgets: &mut WidgetMap,
@@ -289,7 +294,7 @@ impl Layout {
Ok(())
}
pub fn new(globals: WguiGlobals) -> anyhow::Result<Self> {
pub fn new(globals: WguiGlobals, params: &LayoutParams) -> anyhow::Result<Self> {
let mut state = LayoutState {
tree: TaffyTree::new(),
widgets: WidgetMap::new(),
@@ -304,7 +309,11 @@ impl Layout {
None, // no parent
WidgetDiv::create(),
taffy::Style {
size: taffy::Size::auto(),
size: if params.resize_to_parent {
taffy::Size::percent(1.0)
} else {
taffy::Size::auto()
},
..Default::default()
},
)?;
@@ -322,6 +331,60 @@ impl Layout {
})
}
fn try_recompute_layout(&mut self, size: Vec2) -> anyhow::Result<()> {
if !self.state.tree.dirty(self.root_node)? && self.prev_size == size {
// Nothing to do
return Ok(());
}
self.needs_redraw = true;
log::debug!("re-computing layout, size {}x{}", size.x, size.y);
self.prev_size = size;
self.state.tree.compute_layout_with_measure(
self.root_node,
taffy::Size {
width: taffy::AvailableSpace::Definite(size.x),
height: taffy::AvailableSpace::Definite(size.y),
},
|known_dimensions, available_space, _node_id, node_context, _style| {
if let taffy::Size {
width: Some(width),
height: Some(height),
} = known_dimensions
{
return taffy::Size { width, height };
}
match node_context {
None => taffy::Size::ZERO,
Some(h) => {
if let Some(w) = self.state.widgets.get(*h) {
w.0
.borrow_mut()
.obj
.measure(known_dimensions, available_space)
} else {
taffy::Size::ZERO
}
}
}
},
)?;
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!(
"content size changed: {:.0}x{:.0} → {:.0}x{:.0}",
self.content_size.x,
self.content_size.y,
root_size.width,
root_size.height
);
}
self.content_size = vec2(root_size.width, root_size.height);
Ok(())
}
pub fn update(&mut self, size: Vec2, timestep_alpha: f32) -> anyhow::Result<()> {
let mut alterables = EventAlterables::default();
@@ -330,53 +393,7 @@ impl Layout {
.process(&self.state, &mut alterables, timestep_alpha);
self.process_alterables(alterables)?;
if self.state.tree.dirty(self.root_node)? || self.prev_size != size {
self.needs_redraw = true;
log::debug!("re-computing layout, size {}x{}", size.x, size.y);
self.prev_size = size;
self.state.tree.compute_layout_with_measure(
self.root_node,
taffy::Size {
width: taffy::AvailableSpace::Definite(size.x),
height: taffy::AvailableSpace::Definite(size.y),
},
|known_dimensions, available_space, _node_id, node_context, _style| {
if let taffy::Size {
width: Some(width),
height: Some(height),
} = known_dimensions
{
return taffy::Size { width, height };
}
match node_context {
None => taffy::Size::ZERO,
Some(h) => {
if let Some(w) = self.state.widgets.get(*h) {
w.0
.borrow_mut()
.obj
.measure(known_dimensions, available_space)
} else {
taffy::Size::ZERO
}
}
}
},
)?;
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!(
"content size changed: {:.0}x{:.0} → {:.0}x{:.0}",
self.content_size.x,
self.content_size.y,
root_size.width,
root_size.height
);
}
self.content_size = vec2(root_size.width, root_size.height);
}
self.try_recompute_layout(size)?;
Ok(())
}

View File

@@ -4,7 +4,7 @@ use crate::{
i18n::Translation,
layout::WidgetID,
parser::{
ParserContext, ParserFile, iter_attribs, process_component,
ParserContext, ParserFile, iter_attribs, parse_children, process_component,
style::{parse_color, parse_color_opt, parse_round, parse_style, parse_text_style},
},
widget::util::WLength,
@@ -70,6 +70,7 @@ pub fn parse_component_button<'a, U1, U2>(
)?;
process_component(file, ctx, node, Component(component));
parse_children(file, ctx, node, new_id)?;
Ok(new_id)
}

View File

@@ -3,8 +3,8 @@ use crate::{
i18n::Translation,
layout::WidgetID,
parser::{
ParserContext, ParserFile, iter_attribs, parse_check_f32, parse_check_i32, process_component,
style::parse_style,
ParserContext, ParserFile, iter_attribs, parse_check_f32, parse_check_i32, parse_children,
process_component, style::parse_style,
},
};

View File

@@ -2,7 +2,8 @@ use crate::{
components::{Component, slider},
layout::WidgetID,
parser::{
ParserContext, ParserFile, iter_attribs, parse_check_f32, process_component, style::parse_style,
ParserContext, ParserFile, iter_attribs, parse_check_f32, parse_children, process_component,
style::parse_style,
},
};

View File

@@ -13,7 +13,7 @@ use crate::{
drawing::{self},
event::EventListenerCollection,
globals::WguiGlobals,
layout::{Layout, LayoutState, Widget, WidgetID, WidgetMap},
layout::{Layout, LayoutParams, LayoutState, Widget, WidgetID, WidgetMap},
parser::{
component_button::parse_component_button, component_checkbox::parse_component_checkbox,
component_slider::parse_component_slider, widget_div::parse_widget_div,
@@ -797,8 +797,9 @@ pub fn parse_from_assets<U1, U2>(
pub fn new_layout_from_assets<U1, U2>(
listeners: &mut EventListenerCollection<U1, U2>,
doc_params: &ParseDocumentParams,
layout_params: &LayoutParams,
) -> anyhow::Result<(Layout, ParserState)> {
let mut layout = Layout::new(doc_params.globals.clone())?;
let mut layout = Layout::new(doc_params.globals.clone(), layout_params)?;
let widget = layout.root_widget;
let state = parse_from_assets(doc_params, &mut layout, listeners, widget)?;
Ok((layout, state))