RadioBox & RadioGroup
This commit is contained in:
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))
|
||||
}
|
||||
Reference in New Issue
Block a user