dash-frontend: Use native <Button>s, children support
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
<rectangle position="absolute" color="#333333" width="100%" height="100%" />
|
<rectangle position="absolute" color="#333333" width="100%" height="100%" />
|
||||||
|
|
||||||
<!-- left/right separator (menu and rest) -->
|
<!-- left/right separator (menu and rest) -->
|
||||||
<div flex_direction="row" gap="8" width="950" height="550">
|
<div flex_direction="row" gap="8" width="100%" height="100%">
|
||||||
<!-- LEFT MENU -->
|
<!-- LEFT MENU -->
|
||||||
<div id="menu" width="48" min_width="48" max_width="48" height="100%" align_items="center" justify_content="center">
|
<div id="menu" width="48" min_width="48" max_width="48" height="100%" align_items="center" justify_content="center">
|
||||||
<rectangle
|
<rectangle
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
<layout>
|
<layout>
|
||||||
<template name="MenuButton">
|
<template name="MenuButton">
|
||||||
<rectangle
|
<Button
|
||||||
width="120"
|
width="120"
|
||||||
height="82"
|
height="82"
|
||||||
round="8"
|
|
||||||
border="2"
|
|
||||||
border_color="#FFFFFF77"
|
|
||||||
color="#00000033"
|
color="#00000033"
|
||||||
align_items="center"
|
border_color="#FFFFFF77"
|
||||||
justify_content="center"
|
round="8">
|
||||||
flex_direction="column"
|
<div gap="8"
|
||||||
gap="8"
|
align_items="center"
|
||||||
>
|
justify_content="center"
|
||||||
<sprite src="${icon}" width="32" height="32" />
|
flex_direction="column">
|
||||||
<label weight="bold" color="#FFFFFF" size="18" text="${text}" />
|
<sprite src="${icon}" width="32" height="32" />
|
||||||
</rectangle>
|
<label weight="bold" color="#FFFFFF" size="18" text="${text}" />
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
</layout>
|
</layout>
|
||||||
@@ -2,7 +2,7 @@ use glam::Vec2;
|
|||||||
use wgui::{
|
use wgui::{
|
||||||
event::EventListenerCollection,
|
event::EventListenerCollection,
|
||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
layout::Layout,
|
layout::{Layout, LayoutParams},
|
||||||
parser::{ParseDocumentParams, ParserState},
|
parser::{ParseDocumentParams, ParserState},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -30,6 +30,9 @@ impl Frontend {
|
|||||||
path: "gui/dashboard.xml",
|
path: "gui/dashboard.xml",
|
||||||
extra: Default::default(),
|
extra: Default::default(),
|
||||||
},
|
},
|
||||||
|
&LayoutParams {
|
||||||
|
resize_to_parent: true,
|
||||||
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(Self { layout, state })
|
Ok(Self { layout, state })
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use glam::Vec2;
|
|||||||
use wgui::{
|
use wgui::{
|
||||||
event::EventListenerCollection,
|
event::EventListenerCollection,
|
||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
layout::Layout,
|
layout::{Layout, LayoutParams},
|
||||||
parser::{ParseDocumentParams, ParserState},
|
parser::{ParseDocumentParams, ParserState},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -27,6 +27,7 @@ impl TestbedAny {
|
|||||||
path: &path,
|
path: &path,
|
||||||
extra: Default::default(),
|
extra: Default::default(),
|
||||||
},
|
},
|
||||||
|
&LayoutParams::default(),
|
||||||
)?;
|
)?;
|
||||||
Ok(Self { layout, state })
|
Ok(Self { layout, state })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use wgui::{
|
|||||||
event::EventListenerCollection,
|
event::EventListenerCollection,
|
||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::{Layout, Widget},
|
layout::{Layout, LayoutParams, Widget},
|
||||||
parser::{ParseDocumentExtra, ParseDocumentParams, ParserState},
|
parser::{ParseDocumentExtra, ParseDocumentParams, ParserState},
|
||||||
widget::{label::WidgetLabel, rectangle::WidgetRectangle},
|
widget::{label::WidgetLabel, rectangle::WidgetRectangle},
|
||||||
};
|
};
|
||||||
@@ -81,6 +81,9 @@ impl TestbedGeneric {
|
|||||||
path: XML_PATH,
|
path: XML_PATH,
|
||||||
extra,
|
extra,
|
||||||
},
|
},
|
||||||
|
&LayoutParams {
|
||||||
|
resize_to_parent: true,
|
||||||
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let label_cur_option = state.fetch_widget(&layout.state, "label_current_option")?;
|
let label_cur_option = state.fetch_widget(&layout.state, "label_current_option")?;
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
|
|
||||||
## div widget
|
## div widget
|
||||||
|
|
||||||
`<div>`
|
### `<div>`
|
||||||
|
|
||||||
### The most simple element
|
### The most simple element
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ _None_
|
|||||||
|
|
||||||
## label widget
|
## label widget
|
||||||
|
|
||||||
`<label>`
|
### `<label>`
|
||||||
|
|
||||||
### A simple text element
|
### A simple text element
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ _Text size in pixel units_
|
|||||||
|
|
||||||
## rectangle widget
|
## rectangle widget
|
||||||
|
|
||||||
`<rectangle>`
|
### `<rectangle>`
|
||||||
|
|
||||||
### A styled rectangle
|
### A styled rectangle
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ _2nd gradient color_
|
|||||||
|
|
||||||
## sprite widget
|
## sprite widget
|
||||||
|
|
||||||
`<sprite>`
|
### `<sprite>`
|
||||||
|
|
||||||
### Image widget, supports raster and svg vector
|
### Image widget, supports raster and svg vector
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ _External image path_
|
|||||||
|
|
||||||
## Button component
|
## Button component
|
||||||
|
|
||||||
`<Button>`
|
### `<Button>`
|
||||||
|
|
||||||
### A clickable, decorated button
|
### A clickable, decorated button
|
||||||
|
|
||||||
@@ -170,11 +170,15 @@ _Translated by key_
|
|||||||
|
|
||||||
`border_color`: #FFAABB | #FFAABBCC
|
`border_color`: #FFAABB | #FFAABBCC
|
||||||
|
|
||||||
|
#### Info
|
||||||
|
|
||||||
|
Child widgets are supported and can be added directly in XML.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slider component
|
## Slider component
|
||||||
|
|
||||||
`<Slider>`
|
### `<Slider>`
|
||||||
|
|
||||||
### A simple slider.
|
### A simple slider.
|
||||||
|
|
||||||
@@ -194,7 +198,7 @@ _Initial slider value_
|
|||||||
|
|
||||||
## Checkbox component
|
## Checkbox component
|
||||||
|
|
||||||
`<CheckBox>`
|
### `<CheckBox>`
|
||||||
|
|
||||||
### A check-box with label.
|
### A check-box with label.
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,11 @@ pub struct Layout {
|
|||||||
pub animations: Animations,
|
pub animations: Animations,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct LayoutParams {
|
||||||
|
pub resize_to_parent: bool,
|
||||||
|
}
|
||||||
|
|
||||||
fn add_child_internal(
|
fn add_child_internal(
|
||||||
tree: &mut taffy::TaffyTree<WidgetID>,
|
tree: &mut taffy::TaffyTree<WidgetID>,
|
||||||
widgets: &mut WidgetMap,
|
widgets: &mut WidgetMap,
|
||||||
@@ -289,7 +294,7 @@ impl Layout {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(globals: WguiGlobals) -> anyhow::Result<Self> {
|
pub fn new(globals: WguiGlobals, params: &LayoutParams) -> anyhow::Result<Self> {
|
||||||
let mut state = LayoutState {
|
let mut state = LayoutState {
|
||||||
tree: TaffyTree::new(),
|
tree: TaffyTree::new(),
|
||||||
widgets: WidgetMap::new(),
|
widgets: WidgetMap::new(),
|
||||||
@@ -304,7 +309,11 @@ impl Layout {
|
|||||||
None, // no parent
|
None, // no parent
|
||||||
WidgetDiv::create(),
|
WidgetDiv::create(),
|
||||||
taffy::Style {
|
taffy::Style {
|
||||||
size: taffy::Size::auto(),
|
size: if params.resize_to_parent {
|
||||||
|
taffy::Size::percent(1.0)
|
||||||
|
} else {
|
||||||
|
taffy::Size::auto()
|
||||||
|
},
|
||||||
..Default::default()
|
..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<()> {
|
pub fn update(&mut self, size: Vec2, timestep_alpha: f32) -> anyhow::Result<()> {
|
||||||
let mut alterables = EventAlterables::default();
|
let mut alterables = EventAlterables::default();
|
||||||
|
|
||||||
@@ -330,53 +393,7 @@ impl Layout {
|
|||||||
.process(&self.state, &mut alterables, timestep_alpha);
|
.process(&self.state, &mut alterables, timestep_alpha);
|
||||||
|
|
||||||
self.process_alterables(alterables)?;
|
self.process_alterables(alterables)?;
|
||||||
|
self.try_recompute_layout(size)?;
|
||||||
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);
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::{
|
|||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{
|
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},
|
style::{parse_color, parse_color_opt, parse_round, parse_style, parse_text_style},
|
||||||
},
|
},
|
||||||
widget::util::WLength,
|
widget::util::WLength,
|
||||||
@@ -70,6 +70,7 @@ pub fn parse_component_button<'a, U1, U2>(
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
process_component(file, ctx, node, Component(component));
|
process_component(file, ctx, node, Component(component));
|
||||||
|
parse_children(file, ctx, node, new_id)?;
|
||||||
|
|
||||||
Ok(new_id)
|
Ok(new_id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use crate::{
|
|||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{
|
parser::{
|
||||||
ParserContext, ParserFile, iter_attribs, parse_check_f32, parse_check_i32, process_component,
|
ParserContext, ParserFile, iter_attribs, parse_check_f32, parse_check_i32, parse_children,
|
||||||
style::parse_style,
|
process_component, style::parse_style,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ use crate::{
|
|||||||
components::{Component, 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, parse_children, process_component,
|
||||||
|
style::parse_style,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use crate::{
|
|||||||
drawing::{self},
|
drawing::{self},
|
||||||
event::EventListenerCollection,
|
event::EventListenerCollection,
|
||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
layout::{Layout, LayoutState, Widget, WidgetID, WidgetMap},
|
layout::{Layout, LayoutParams, LayoutState, Widget, WidgetID, WidgetMap},
|
||||||
parser::{
|
parser::{
|
||||||
component_button::parse_component_button, component_checkbox::parse_component_checkbox,
|
component_button::parse_component_button, component_checkbox::parse_component_checkbox,
|
||||||
component_slider::parse_component_slider, widget_div::parse_widget_div,
|
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>(
|
pub fn new_layout_from_assets<U1, U2>(
|
||||||
listeners: &mut EventListenerCollection<U1, U2>,
|
listeners: &mut EventListenerCollection<U1, U2>,
|
||||||
doc_params: &ParseDocumentParams,
|
doc_params: &ParseDocumentParams,
|
||||||
|
layout_params: &LayoutParams,
|
||||||
) -> anyhow::Result<(Layout, ParserState)> {
|
) -> 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 widget = layout.root_widget;
|
||||||
let state = parse_from_assets(doc_params, &mut layout, listeners, widget)?;
|
let state = parse_from_assets(doc_params, &mut layout, listeners, widget)?;
|
||||||
Ok((layout, state))
|
Ok((layout, state))
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use wgui::{
|
|||||||
MouseButtonIndex, MouseDownEvent, MouseLeaveEvent, MouseMotionEvent, MouseUpEvent,
|
MouseButtonIndex, MouseDownEvent, MouseLeaveEvent, MouseMotionEvent, MouseUpEvent,
|
||||||
MouseWheelEvent,
|
MouseWheelEvent,
|
||||||
},
|
},
|
||||||
layout::Layout,
|
layout::{Layout, LayoutParams},
|
||||||
parser::ParserState,
|
parser::ParserState,
|
||||||
renderer_vk::context::Context as WguiContext,
|
renderer_vk::context::Context as WguiContext,
|
||||||
};
|
};
|
||||||
@@ -50,6 +50,7 @@ impl<S> GuiPanel<S> {
|
|||||||
path,
|
path,
|
||||||
extra: Default::default(),
|
extra: Default::default(),
|
||||||
},
|
},
|
||||||
|
&LayoutParams::default(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let context = WguiContext::new(&mut app.wgui_shared, 1.0)?;
|
let context = WguiContext::new(&mut app.wgui_shared, 1.0)?;
|
||||||
@@ -70,7 +71,7 @@ impl<S> GuiPanel<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_blank(app: &mut AppState, state: S) -> anyhow::Result<Self> {
|
pub fn new_blank(app: &mut AppState, state: S) -> anyhow::Result<Self> {
|
||||||
let layout = Layout::new(app.wgui_globals.clone())?;
|
let layout = Layout::new(app.wgui_globals.clone(), &LayoutParams::default())?;
|
||||||
let context = WguiContext::new(&mut app.wgui_shared, 1.0)?;
|
let context = WguiContext::new(&mut app.wgui_shared, 1.0)?;
|
||||||
let mut timestep = Timestep::new();
|
let mut timestep = Timestep::new();
|
||||||
timestep.set_tps(60.0);
|
timestep.set_tps(60.0);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use wgui::{
|
|||||||
animation::{Animation, AnimationEasing},
|
animation::{Animation, AnimationEasing},
|
||||||
drawing::Color,
|
drawing::Color,
|
||||||
event::{self, CallbackMetadata, EventListenerKind},
|
event::{self, CallbackMetadata, EventListenerKind},
|
||||||
|
layout::LayoutParams,
|
||||||
renderer_vk::util,
|
renderer_vk::util,
|
||||||
taffy::{self, prelude::length},
|
taffy::{self, prelude::length},
|
||||||
widget::{
|
widget::{
|
||||||
@@ -80,8 +81,11 @@ where
|
|||||||
extra: Default::default(),
|
extra: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (_, mut gui_state_key) =
|
let (_, mut gui_state_key) = wgui::parser::new_layout_from_assets(
|
||||||
wgui::parser::new_layout_from_assets(&mut panel.listeners, &parse_doc_params)?;
|
&mut panel.listeners,
|
||||||
|
&parse_doc_params,
|
||||||
|
&LayoutParams::default(),
|
||||||
|
)?;
|
||||||
|
|
||||||
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(
|
||||||
|
|||||||
Reference in New Issue
Block a user