RadioBox & RadioGroup
This commit is contained in:
@@ -322,10 +322,48 @@ _Translated by key_
|
|||||||
|
|
||||||
`box_size`: **float** (default: 24)
|
`box_size`: **float** (default: 24)
|
||||||
|
|
||||||
|
`value`: **string**
|
||||||
|
|
||||||
|
_optional value that will be sent with internal events_
|
||||||
|
|
||||||
`checked`: **int** (default: 0)
|
`checked`: **int** (default: 0)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Radiobox component
|
||||||
|
|
||||||
|
### `<RadioBox>`
|
||||||
|
|
||||||
|
### A radio-box with label.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
`text`: **string**
|
||||||
|
|
||||||
|
_Simple text_
|
||||||
|
|
||||||
|
`translation`: **string**
|
||||||
|
|
||||||
|
_Translated by key_
|
||||||
|
|
||||||
|
`box_size`: **float** (default: 24)
|
||||||
|
|
||||||
|
`value`: **string**
|
||||||
|
|
||||||
|
_optional value that will be set as the RadioGroup's value_
|
||||||
|
|
||||||
|
`checked`: **int** (default: 0)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## RadioGroup component
|
||||||
|
|
||||||
|
### `<RadioGroup>`
|
||||||
|
|
||||||
|
### A radio group. Place `<RadioBox>` components inside this.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
## Simple layout
|
## Simple layout
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::{Rc, Weak}};
|
||||||
use taffy::{
|
use taffy::{
|
||||||
prelude::{length, percent},
|
prelude::{length, percent},
|
||||||
AlignItems,
|
AlignItems,
|
||||||
@@ -6,7 +6,7 @@ use taffy::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
animation::{Animation, AnimationEasing},
|
animation::{Animation, AnimationEasing},
|
||||||
components::{Component, ComponentBase, ComponentTrait, RefreshData},
|
components::{radio_group::ComponentRadioGroup, Component, ComponentBase, ComponentTrait, RefreshData},
|
||||||
drawing::Color,
|
drawing::Color,
|
||||||
event::{CallbackDataCommon, EventListenerCollection, EventListenerID, EventListenerKind},
|
event::{CallbackDataCommon, EventListenerCollection, EventListenerID, EventListenerKind},
|
||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
@@ -25,6 +25,8 @@ pub struct Params {
|
|||||||
pub style: taffy::Style,
|
pub style: taffy::Style,
|
||||||
pub box_size: f32,
|
pub box_size: f32,
|
||||||
pub checked: bool,
|
pub checked: bool,
|
||||||
|
pub radio_group: Option<Rc<ComponentRadioGroup>>,
|
||||||
|
pub value: Option<Rc<str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Params {
|
impl Default for Params {
|
||||||
@@ -34,12 +36,15 @@ impl Default for Params {
|
|||||||
style: Default::default(),
|
style: Default::default(),
|
||||||
box_size: 24.0,
|
box_size: 24.0,
|
||||||
checked: false,
|
checked: false,
|
||||||
|
radio_group: None,
|
||||||
|
value: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CheckboxToggleEvent {
|
pub struct CheckboxToggleEvent {
|
||||||
pub checked: bool,
|
pub checked: bool,
|
||||||
|
pub value: Option<Rc<str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type CheckboxToggleCallback = Box<dyn Fn(&mut CallbackDataCommon, CheckboxToggleEvent) -> anyhow::Result<()>>;
|
pub type CheckboxToggleCallback = Box<dyn Fn(&mut CallbackDataCommon, CheckboxToggleEvent) -> anyhow::Result<()>>;
|
||||||
@@ -49,6 +54,7 @@ struct State {
|
|||||||
hovered: bool,
|
hovered: bool,
|
||||||
down: bool,
|
down: bool,
|
||||||
on_toggle: Option<CheckboxToggleCallback>,
|
on_toggle: Option<CheckboxToggleCallback>,
|
||||||
|
self_ref: Weak<ComponentCheckbox>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::struct_field_names)]
|
#[allow(clippy::struct_field_names)]
|
||||||
@@ -59,6 +65,8 @@ struct Data {
|
|||||||
//id_outer_box: WidgetID, // Rectangle, parent of container
|
//id_outer_box: WidgetID, // Rectangle, parent of container
|
||||||
id_inner_box: WidgetID, // Rectangle, parent of outer_box
|
id_inner_box: WidgetID, // Rectangle, parent of outer_box
|
||||||
id_label: WidgetID, // Label, parent of container
|
id_label: WidgetID, // Label, parent of container
|
||||||
|
value: Option<Rc<str>>, // arbitrary value assigned to the element
|
||||||
|
radio_group: Option<Weak<ComponentRadioGroup>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ComponentCheckbox {
|
pub struct ComponentCheckbox {
|
||||||
@@ -100,11 +108,27 @@ impl ComponentCheckbox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_checked(&self, common: &mut CallbackDataCommon, checked: bool) {
|
pub fn set_checked(&self, common: &mut CallbackDataCommon, checked: bool) {
|
||||||
self.state.borrow_mut().checked = checked;
|
{
|
||||||
|
let mut state = self.state.borrow_mut();
|
||||||
|
if state.checked == checked {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.checked = checked;
|
||||||
|
}
|
||||||
set_box_checked(&common.state.widgets, &self.data, checked);
|
set_box_checked(&common.state.widgets, &self.data, checked);
|
||||||
common.alterables.mark_redraw();
|
common.alterables.mark_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self) -> Option<Rc<str>> {
|
||||||
|
self.data.value.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set checked state without triggering visual changes.
|
||||||
|
pub(super) fn set_checked_internal(&self, checked: bool) {
|
||||||
|
self.state.borrow_mut().checked = checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn on_toggle(&self, func: CheckboxToggleCallback) {
|
pub fn on_toggle(&self, func: CheckboxToggleCallback) {
|
||||||
self.state.borrow_mut().on_toggle = Some(func);
|
self.state.borrow_mut().on_toggle = Some(func);
|
||||||
}
|
}
|
||||||
@@ -222,13 +246,19 @@ fn register_event_mouse_release(
|
|||||||
if state.down {
|
if state.down {
|
||||||
state.down = false;
|
state.down = false;
|
||||||
|
|
||||||
state.checked = !state.checked;
|
if let Some(self_ref) = state.self_ref.upgrade() && let Some(radio) = data.radio_group.as_ref().and_then(|r| r.upgrade()) {
|
||||||
|
radio.set_selected_internal(common, &self_ref)?;
|
||||||
|
state.checked = true; // can't uncheck radiobox by clicking the checked box again
|
||||||
|
} else {
|
||||||
|
state.checked = !state.checked;
|
||||||
|
}
|
||||||
|
|
||||||
set_box_checked(&common.state.widgets, &data, state.checked);
|
set_box_checked(&common.state.widgets, &data, state.checked);
|
||||||
|
|
||||||
if state.hovered
|
if state.hovered
|
||||||
&& let Some(on_toggle) = &state.on_toggle
|
&& let Some(on_toggle) = &state.on_toggle
|
||||||
{
|
{
|
||||||
on_toggle(common, CheckboxToggleEvent { checked: state.checked })?;
|
on_toggle(common, CheckboxToggleEvent { checked: state.checked, value: data.value.clone() })?;
|
||||||
}
|
}
|
||||||
Ok(EventResult::Consumed)
|
Ok(EventResult::Consumed)
|
||||||
} else {
|
} else {
|
||||||
@@ -263,6 +293,12 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
|||||||
//style.align_self = Some(taffy::AlignSelf::Start); // do not stretch self to the parent
|
//style.align_self = Some(taffy::AlignSelf::Start); // do not stretch self to the parent
|
||||||
style.gap = length(4.0);
|
style.gap = length(4.0);
|
||||||
|
|
||||||
|
let (round_5, round_8) = if params.radio_group.is_some() {
|
||||||
|
(WLength::Percent(1.0), WLength::Percent(1.0))
|
||||||
|
} else {
|
||||||
|
(WLength::Units(5.0), WLength::Units(8.0))
|
||||||
|
};
|
||||||
|
|
||||||
let globals = ess.layout.state.globals.clone();
|
let globals = ess.layout.state.globals.clone();
|
||||||
|
|
||||||
let (root, _) = ess.layout.add_child(
|
let (root, _) = ess.layout.add_child(
|
||||||
@@ -270,7 +306,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
|||||||
WidgetRectangle::create(WidgetRectangleParams {
|
WidgetRectangle::create(WidgetRectangleParams {
|
||||||
color: Color::new(1.0, 1.0, 1.0, 0.0),
|
color: Color::new(1.0, 1.0, 1.0, 0.0),
|
||||||
border_color: Color::new(1.0, 1.0, 1.0, 0.0),
|
border_color: Color::new(1.0, 1.0, 1.0, 0.0),
|
||||||
round: WLength::Units(5.0),
|
round: round_5,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
style,
|
style,
|
||||||
@@ -288,7 +324,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
|||||||
WidgetRectangle::create(WidgetRectangleParams {
|
WidgetRectangle::create(WidgetRectangleParams {
|
||||||
border: 2.0,
|
border: 2.0,
|
||||||
border_color: Color::new(1.0, 1.0, 1.0, 1.0),
|
border_color: Color::new(1.0, 1.0, 1.0, 1.0),
|
||||||
round: WLength::Units(8.0),
|
round: round_8,
|
||||||
color: Color::new(1.0, 1.0, 1.0, 0.0),
|
color: Color::new(1.0, 1.0, 1.0, 0.0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
@@ -304,7 +340,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
|||||||
let (inner_box, _) = ess.layout.add_child(
|
let (inner_box, _) = ess.layout.add_child(
|
||||||
outer_box.id,
|
outer_box.id,
|
||||||
WidgetRectangle::create(WidgetRectangleParams {
|
WidgetRectangle::create(WidgetRectangleParams {
|
||||||
round: WLength::Units(5.0),
|
round: round_5,
|
||||||
color: if params.checked { COLOR_CHECKED } else { COLOR_UNCHECKED },
|
color: if params.checked { COLOR_CHECKED } else { COLOR_UNCHECKED },
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
@@ -336,6 +372,8 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
|||||||
id_container,
|
id_container,
|
||||||
id_inner_box: inner_box.id,
|
id_inner_box: inner_box.id,
|
||||||
id_label: label.id,
|
id_label: label.id,
|
||||||
|
value: params.value,
|
||||||
|
radio_group: params.radio_group.as_ref().map(|x| Rc::downgrade(x)),
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = Rc::new(RefCell::new(State {
|
let state = Rc::new(RefCell::new(State {
|
||||||
@@ -343,6 +381,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
|||||||
down: false,
|
down: false,
|
||||||
hovered: false,
|
hovered: false,
|
||||||
on_toggle: None,
|
on_toggle: None,
|
||||||
|
self_ref: Weak::new(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let base = ComponentBase {
|
let base = ComponentBase {
|
||||||
@@ -361,6 +400,11 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
|||||||
|
|
||||||
let checkbox = Rc::new(ComponentCheckbox { base, data, state });
|
let checkbox = Rc::new(ComponentCheckbox { base, data, state });
|
||||||
|
|
||||||
|
if let Some(radio) = params.radio_group.as_ref() {
|
||||||
|
radio.register_child(checkbox.clone(), params.checked);
|
||||||
|
checkbox.state.borrow_mut().self_ref = Rc::downgrade(&checkbox);
|
||||||
|
}
|
||||||
|
|
||||||
ess.layout.defer_component_refresh(Component(checkbox.clone()));
|
ess.layout.defer_component_refresh(Component(checkbox.clone()));
|
||||||
Ok((root, checkbox))
|
Ok((root, checkbox))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use crate::{
|
|||||||
|
|
||||||
pub mod button;
|
pub mod button;
|
||||||
pub mod checkbox;
|
pub mod checkbox;
|
||||||
|
pub mod radio_group;
|
||||||
pub mod slider;
|
pub mod slider;
|
||||||
pub mod tooltip;
|
pub mod tooltip;
|
||||||
|
|
||||||
|
|||||||
138
wgui/src/components/radio_group.rs
Normal file
138
wgui/src/components/radio_group.rs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use taffy::Style;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
components::{checkbox::ComponentCheckbox, Component, ComponentBase, ComponentTrait, RefreshData},
|
||||||
|
event::CallbackDataCommon,
|
||||||
|
layout::WidgetPair,
|
||||||
|
widget::{div::WidgetDiv, ConstructEssentials},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct RadioValueChangeEvent {
|
||||||
|
pub value: Option<Rc<str>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type RadioValueChangeCallback = Box<dyn Fn(&mut CallbackDataCommon, RadioValueChangeEvent) -> anyhow::Result<()>>;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct State {
|
||||||
|
radio_boxes: Vec<Rc<ComponentCheckbox>>,
|
||||||
|
selected: Option<Rc<ComponentCheckbox>>,
|
||||||
|
on_value_changed: Option<RadioValueChangeCallback>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ComponentRadioGroup {
|
||||||
|
base: ComponentBase,
|
||||||
|
state: Rc<RefCell<State>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentRadioGroup {
|
||||||
|
pub(super) fn register_child(&self, child: Rc<ComponentCheckbox>, checked: bool) {
|
||||||
|
let mut state = self.state.borrow_mut();
|
||||||
|
if checked {
|
||||||
|
state.selected = Some(child.clone());
|
||||||
|
for radio_box in &state.radio_boxes {
|
||||||
|
radio_box.set_checked_internal(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.radio_boxes.push(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This doesn't `set_checked` on `selected` in order to avoid double borrow.
|
||||||
|
pub(super) fn set_selected_internal(
|
||||||
|
&self,
|
||||||
|
common: &mut CallbackDataCommon,
|
||||||
|
selected: &Rc<ComponentCheckbox>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let mut state = self.state.borrow_mut();
|
||||||
|
if state.selected.as_ref().is_some_and(|b| Rc::ptr_eq(b, selected)) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut selected_found = false;
|
||||||
|
for radio_box in &state.radio_boxes {
|
||||||
|
if Rc::ptr_eq(radio_box, selected) {
|
||||||
|
selected_found = true;
|
||||||
|
} else {
|
||||||
|
radio_box.set_checked(common, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !selected_found {
|
||||||
|
anyhow::bail!("RadioGroup set_active called with a non-child ComponentCheckbox!");
|
||||||
|
}
|
||||||
|
state.selected = Some(selected.clone());
|
||||||
|
|
||||||
|
if let Some(on_value_changed) = state.on_value_changed.as_ref() {
|
||||||
|
on_value_changed(
|
||||||
|
common,
|
||||||
|
RadioValueChangeEvent {
|
||||||
|
value: selected.get_value(),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_selected(&self, common: &mut CallbackDataCommon, selected: &Rc<ComponentCheckbox>) -> anyhow::Result<()> {
|
||||||
|
self.set_selected_internal(common, selected)?;
|
||||||
|
if let Some(selected) = self.state.borrow().selected.as_ref() {
|
||||||
|
selected.set_checked(common, true);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_value(&self) -> Option<Rc<str>> {
|
||||||
|
self.state.borrow().selected.as_ref().and_then(|b| b.get_value())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_value(&self, value: &str) -> anyhow::Result<()> {
|
||||||
|
let mut state = self.state.borrow_mut();
|
||||||
|
for radio_box in &state.radio_boxes {
|
||||||
|
if radio_box.get_value().is_some_and(|box_val| &*box_val == value) {
|
||||||
|
state.selected = Some(radio_box.clone());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
anyhow::bail!("No RadioBox found with value '{value}'")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_value_changed(&self, callback: RadioValueChangeCallback) {
|
||||||
|
self.state.borrow_mut().on_value_changed = Some(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentTrait for ComponentRadioGroup {
|
||||||
|
fn base(&self) -> &ComponentBase {
|
||||||
|
&self.base
|
||||||
|
}
|
||||||
|
|
||||||
|
fn base_mut(&mut self) -> &mut ComponentBase {
|
||||||
|
&mut self.base
|
||||||
|
}
|
||||||
|
|
||||||
|
fn refresh(&self, _data: &mut RefreshData) {
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn construct(
|
||||||
|
ess: &mut ConstructEssentials,
|
||||||
|
style: taffy::Style,
|
||||||
|
) -> anyhow::Result<(WidgetPair, Rc<ComponentRadioGroup>)> {
|
||||||
|
let (root, _) = ess.layout.add_child(ess.parent, WidgetDiv::create(), style)?;
|
||||||
|
|
||||||
|
let base = ComponentBase {
|
||||||
|
id: root.id,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let state = Rc::new(RefCell::new(State::default()));
|
||||||
|
|
||||||
|
let checkbox = Rc::new(ComponentRadioGroup { base, state });
|
||||||
|
|
||||||
|
ess.layout.defer_component_refresh(Component(checkbox.clone()));
|
||||||
|
Ok((root, checkbox))
|
||||||
|
}
|
||||||
@@ -265,6 +265,18 @@ impl Layout {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_parent(&self, widget_id: WidgetID) -> Option<(WidgetID, taffy::NodeId)> {
|
||||||
|
let Some(node_id) = self.state.nodes.get(widget_id) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.state.tree.parent(*node_id).map(|parent_id| {
|
||||||
|
let parent_widget_id = self.state.tree.get_node_context(parent_id).unwrap();
|
||||||
|
(*parent_widget_id, parent_id)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn collect_children_ids_recursive(&self, widget_id: WidgetID, out: &mut Vec<(WidgetID, taffy::NodeId)>) {
|
fn collect_children_ids_recursive(&self, widget_id: WidgetID, out: &mut Vec<(WidgetID, taffy::NodeId)>) {
|
||||||
let Some(node_id) = self.state.nodes.get(widget_id) else {
|
let Some(node_id) = self.state.nodes.get(widget_id) else {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,18 +1,27 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
components::{Component, checkbox},
|
components::{checkbox, radio_group::ComponentRadioGroup, Component},
|
||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{AttribPair, ParserContext, parse_check_f32, parse_check_i32, process_component, style::parse_style},
|
parser::{
|
||||||
|
parse_check_f32, parse_check_i32, process_component, style::parse_style, AttribPair, Fetchable, ParserContext,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub enum CheckboxKind {
|
||||||
|
CheckBox,
|
||||||
|
RadioBox,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_component_checkbox(
|
pub fn parse_component_checkbox(
|
||||||
ctx: &mut ParserContext,
|
ctx: &mut ParserContext,
|
||||||
parent_id: WidgetID,
|
parent_id: WidgetID,
|
||||||
attribs: &[AttribPair],
|
attribs: &[AttribPair],
|
||||||
|
kind: CheckboxKind,
|
||||||
) -> anyhow::Result<WidgetID> {
|
) -> anyhow::Result<WidgetID> {
|
||||||
let mut box_size = 24.0;
|
let mut box_size = 24.0;
|
||||||
let mut translation = Translation::default();
|
let mut translation = Translation::default();
|
||||||
let mut checked = 0;
|
let mut checked = 0;
|
||||||
|
let mut component_value = None;
|
||||||
|
|
||||||
let style = parse_style(attribs);
|
let style = parse_style(attribs);
|
||||||
|
|
||||||
@@ -25,6 +34,9 @@ pub fn parse_component_checkbox(
|
|||||||
"translation" => {
|
"translation" => {
|
||||||
translation = Translation::from_translation_key(value);
|
translation = Translation::from_translation_key(value);
|
||||||
}
|
}
|
||||||
|
"value" => {
|
||||||
|
component_value = Some(value.into());
|
||||||
|
}
|
||||||
"box_size" => {
|
"box_size" => {
|
||||||
parse_check_f32(value, &mut box_size);
|
parse_check_f32(value, &mut box_size);
|
||||||
}
|
}
|
||||||
@@ -35,6 +47,28 @@ pub fn parse_component_checkbox(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut radio_group = None;
|
||||||
|
|
||||||
|
if matches!(kind, CheckboxKind::RadioBox) {
|
||||||
|
let mut maybe_parent_id = Some(parent_id);
|
||||||
|
|
||||||
|
while let Some(parent_id) = maybe_parent_id {
|
||||||
|
if let Ok(radio) = ctx
|
||||||
|
.data_local
|
||||||
|
.fetch_component_from_widget_id_as::<ComponentRadioGroup>(parent_id)
|
||||||
|
{
|
||||||
|
radio_group = Some(radio);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
maybe_parent_id = ctx.layout.get_parent(parent_id).map(|(widget_id, _)| widget_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if radio_group.is_none() {
|
||||||
|
log::error!("RadioBox component without a Radio group!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let (widget, component) = checkbox::construct(
|
let (widget, component) = checkbox::construct(
|
||||||
&mut ctx.get_construct_essentials(parent_id),
|
&mut ctx.get_construct_essentials(parent_id),
|
||||||
checkbox::Params {
|
checkbox::Params {
|
||||||
@@ -42,6 +76,8 @@ pub fn parse_component_checkbox(
|
|||||||
text: translation,
|
text: translation,
|
||||||
checked: checked != 0,
|
checked: checked != 0,
|
||||||
style,
|
style,
|
||||||
|
radio_group,
|
||||||
|
value: component_value,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
22
wgui/src/parser/component_radio_group.rs
Normal file
22
wgui/src/parser/component_radio_group.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
use crate::{
|
||||||
|
components::{radio_group, Component},
|
||||||
|
layout::WidgetID,
|
||||||
|
parser::{parse_children, process_component, style::parse_style, AttribPair, ParserContext, ParserFile},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn parse_component_radio_group<'a>(
|
||||||
|
file: &'a ParserFile,
|
||||||
|
ctx: &mut ParserContext,
|
||||||
|
node: roxmltree::Node<'a, 'a>,
|
||||||
|
parent_id: WidgetID,
|
||||||
|
attribs: &[AttribPair],
|
||||||
|
) -> anyhow::Result<WidgetID> {
|
||||||
|
let style = parse_style(attribs);
|
||||||
|
|
||||||
|
let (widget, component) = radio_group::construct(&mut ctx.get_construct_essentials(parent_id), style)?;
|
||||||
|
|
||||||
|
process_component(ctx, Component(component), widget.id, attribs);
|
||||||
|
parse_children(file, ctx, node, widget.id)?;
|
||||||
|
|
||||||
|
Ok(widget.id)
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
mod component_button;
|
mod component_button;
|
||||||
mod component_checkbox;
|
mod component_checkbox;
|
||||||
|
mod component_radio_group;
|
||||||
mod component_slider;
|
mod component_slider;
|
||||||
mod style;
|
mod style;
|
||||||
mod widget_div;
|
mod widget_div;
|
||||||
@@ -9,15 +10,13 @@ mod widget_rectangle;
|
|||||||
mod widget_sprite;
|
mod widget_sprite;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assets::{AssetPath, AssetPathOwned, normalize_path},
|
assets::{normalize_path, AssetPath, AssetPathOwned},
|
||||||
components::{Component, ComponentWeak},
|
components::{Component, ComponentWeak},
|
||||||
drawing::{self},
|
drawing::{self},
|
||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
layout::{Layout, LayoutParams, LayoutState, Widget, WidgetID, WidgetMap, WidgetPair},
|
layout::{Layout, LayoutParams, LayoutState, Widget, WidgetID, WidgetMap, WidgetPair},
|
||||||
parser::{
|
parser::{
|
||||||
component_button::parse_component_button, component_checkbox::parse_component_checkbox,
|
component_button::parse_component_button, component_checkbox::{parse_component_checkbox, CheckboxKind}, component_radio_group::parse_component_radio_group, component_slider::parse_component_slider, widget_div::parse_widget_div, widget_image::parse_widget_image, widget_label::parse_widget_label, widget_rectangle::parse_widget_rectangle, widget_sprite::parse_widget_sprite
|
||||||
component_slider::parse_component_slider, widget_div::parse_widget_div, widget_image::parse_widget_image,
|
|
||||||
widget_label::parse_widget_label, widget_rectangle::parse_widget_rectangle, widget_sprite::parse_widget_sprite,
|
|
||||||
},
|
},
|
||||||
widget::ConstructEssentials,
|
widget::ConstructEssentials,
|
||||||
};
|
};
|
||||||
@@ -909,7 +908,13 @@ fn parse_child<'a>(
|
|||||||
new_widget_id = Some(parse_component_slider(ctx, parent_id, &attribs)?);
|
new_widget_id = Some(parse_component_slider(ctx, parent_id, &attribs)?);
|
||||||
}
|
}
|
||||||
"CheckBox" => {
|
"CheckBox" => {
|
||||||
new_widget_id = Some(parse_component_checkbox(ctx, parent_id, &attribs)?);
|
new_widget_id = Some(parse_component_checkbox(ctx, parent_id, &attribs, CheckboxKind::CheckBox)?);
|
||||||
|
}
|
||||||
|
"RadioBox" => {
|
||||||
|
new_widget_id = Some(parse_component_checkbox(ctx, parent_id, &attribs, CheckboxKind::RadioBox)?);
|
||||||
|
}
|
||||||
|
"RadioGroup" => {
|
||||||
|
new_widget_id = Some(parse_component_radio_group(file, ctx, child_node, parent_id, &attribs)?);
|
||||||
}
|
}
|
||||||
"" => { /* ignore */ }
|
"" => { /* ignore */ }
|
||||||
other_tag_name => {
|
other_tag_name => {
|
||||||
|
|||||||
Reference in New Issue
Block a user