use crate::compat::Arc;
use crate::widget::capability::coercion::widget_as_mut;
use crate::widget::sample_data as sample;
use crate::widget::Widget;
use crate::compat::{String, Vec};
use crate::widget::advanced_widgets::calendar::Calendar;
use crate::widget::advanced_widgets::tab_bar::TabBar;
use crate::widget::container_widgets::groupbox::GroupBox;
use crate::widget::dialog::bottom_sheet::BottomSheet;
use crate::widget::dialog::dialog_widget::Dialog;
use crate::widget::dialog::message_box::MessageBox;
use crate::widget::dialog::modal_bottom_sheet::ModalBottomSheet;
use crate::widget::display_widgets::slider::{Slider, TickPosition};
use crate::widget::display_widgets::switch::Switch;
use crate::widget::input_widgets::cascader::{Cascader, CascaderOption};
use crate::widget::input_widgets::combobox::ComboBox;
use crate::widget::input_widgets::dropdown::Dropdown;
use crate::widget::input_widgets::editable_combo_box::EditableComboBox;
use crate::widget::input_widgets::font_combo_box::FontComboBox;
use crate::widget::input_widgets::listbox::ListBox;
use crate::widget::input_widgets::multi_select_combo_box::{MultiSelectComboBox, MultiSelectItem};
use crate::widget::menu_toolbar::menu::Menu;
use crate::widget::menu_toolbar::menu_button::{MenuButton, MenuItem};
use crate::widget::special_widgets::command_palette::{CommandEntry, CommandPalette};
use crate::widget::special_widgets::segmented_control::{SegmentItem, SegmentedControl};
use crate::widget::view_widgets::data_grid::DataGrid;
use crate::widget::view_widgets::data_source::IncrementalTableDataSource;
use crate::widget::view_widgets::grid_table::GridTableWidget;
use crate::widget::view_widgets::list_view::{ListView, VecListModel};
use crate::widget::view_widgets::table_widget::{TableModel, TableWidget};
use crate::widget::view_widgets::tree_view::{TreeView, VecTreeModel};
use crate::widget::view_widgets::virtual_table::VirtualTable;
pub fn apply(name: &str, widget: &mut dyn Widget) -> bool {
match name {
"group_box" => match widget_as_mut::<GroupBox>(widget) {
Some(group_box) => {
group_box.set_checkable(true);
group_box.set_checked(false);
true
}
None => false,
},
"check_box" => match widget_as_mut::<CheckBox>(widget) {
Some(checkbox) => {
checkbox.set_checked(true);
true
}
None => false,
},
"switch" => match widget_as_mut::<Switch>(widget) {
Some(switch) => {
switch.set_checked(true);
true
}
None => false,
},
"slider" => match widget_as_mut::<Slider>(widget) {
Some(slider) => {
slider.set_value(30);
slider.set_tick_position(TickPosition::TicksBelow);
slider.set_tick_interval(25);
true
}
None => false,
},
"calendar" => match widget_as_mut::<Calendar>(widget) {
Some(calendar) => {
match chrono::NaiveDate::from_ymd_opt(2026, 9, 16) {
Some(sample_day) => {
calendar.set_selected_date(sample_day);
calendar.set_vertical_header_visible(true);
true
}
None => false,
}
}
None => false,
},
"table" | "table_widget" => match widget_as_mut::<TableWidget>(widget) {
Some(table) => {
table.set_model(Arc::new(SampleTableModel));
true
}
None => false,
},
"data_grid" => match widget_as_mut::<DataGrid>(widget) {
Some(grid) => {
grid.set_data_source(Arc::new(SampleTableSource));
true
}
None => false,
},
"grid_table" => match widget_as_mut::<GridTableWidget>(widget) {
Some(grid) => {
grid.set_data_source(Arc::new(SampleTableSource));
true
}
None => false,
},
"virtual_table" => match widget_as_mut::<VirtualTable>(widget) {
Some(table) => {
table.set_data_source(Arc::new(SampleTableSource));
true
}
None => false,
},
"list_box" => match widget_as_mut::<ListBox>(widget) {
Some(list) => {
for item in sample::list_items() {
list.add_item(item);
}
list.set_current_row(Some(0));
true
}
None => false,
},
"combo_box" => match widget_as_mut::<ComboBox>(widget) {
Some(combo) => {
for item in sample::list_items() {
combo.add_item(item);
}
combo.set_current_index(Some(0));
true
}
None => false,
},
"editable_combo_box" => match widget_as_mut::<EditableComboBox>(widget) {
Some(combo) => {
for item in sample::list_items() {
combo.add_item(item);
}
true
}
None => false,
},
"font_combo_box" => match widget_as_mut::<FontComboBox>(widget) {
Some(combo) => {
for name in sample::FONT_NAMES {
combo.add_font(name.to_string());
}
combo.set_current_index(0);
true
}
None => false,
},
"multi_select_combo_box" => match widget_as_mut::<MultiSelectComboBox>(widget) {
Some(combo) => {
for (index, item) in sample::list_items().into_iter().enumerate() {
combo.add_item(MultiSelectItem::new(index as u64, item));
}
true
}
None => false,
},
"dropdown" => match widget_as_mut::<Dropdown>(widget) {
Some(dropdown) => {
dropdown.set_items(sample::list_items());
dropdown.set_selected_index(0);
true
}
None => false,
},
"cascader" => match widget_as_mut::<Cascader>(widget) {
Some(cascader) => {
cascader.set_options(sample_cascade());
cascader.set_selected_path(vec![0]);
cascader.expand();
true
}
None => false,
},
"list_view" => match widget_as_mut::<ListView>(widget) {
Some(list) => {
list.set_model(Arc::new(VecListModel::new(sample::list_items())));
list.select_row(0);
true
}
None => false,
},
"tree_view" => match widget_as_mut::<TreeView>(widget) {
Some(tree) => {
tree.set_model(Arc::new(VecTreeModel::new(sample::tree_rows())));
tree.select_node(0);
true
}
None => false,
},
"menu" | "context_menu" => match widget_as_mut::<Menu>(widget) {
Some(menu) => {
for item in sample::menu_items() {
menu.add_action(item);
}
true
}
None => false,
},
"menu_button" => match widget_as_mut::<MenuButton>(widget) {
Some(button) => {
for (index, item) in sample::menu_items().into_iter().enumerate() {
button.add_item(MenuItem::new(index as u64, &item));
}
true
}
None => false,
},
"command_palette" => match widget_as_mut::<CommandPalette>(widget) {
Some(palette) => {
palette.set_entries(
sample::menu_items()
.into_iter()
.enumerate()
.map(|(index, title)| CommandEntry::new(format!("cmd{index}"), title))
.collect(),
);
true
}
None => false,
},
"tab_bar" => match widget_as_mut::<TabBar>(widget) {
Some(bar) => {
for title in sample::tab_titles() {
bar.add_tab(title);
}
true
}
None => false,
},
"segmented_control" => match widget_as_mut::<SegmentedControl>(widget) {
Some(control) => {
control.set_items(vec![
SegmentItem::new("overview", "Overview"),
SegmentItem::new("details", "Details"),
SegmentItem::new("history", "History"),
]);
true
}
None => false,
},
"bottom_sheet" => match widget_as_mut::<BottomSheet>(widget) {
Some(sheet) => {
sheet.open();
while sheet.tick(1000) {}
true
}
None => false,
},
"modal_bottom_sheet" => match widget_as_mut::<ModalBottomSheet>(widget) {
Some(sheet) => {
sheet.show();
true
}
None => false,
},
"dialog" => match widget_as_mut::<Dialog>(widget) {
Some(dialog) => {
dialog.open();
while dialog.tick(1000) {}
true
}
None => false,
},
"message_box" => match widget_as_mut::<MessageBox>(widget) {
Some(message_box) => {
message_box.show();
while message_box.tick(1000) {}
true
}
None => false,
},
_ => false,
}
}
use crate::widget::base_widgets::checkbox::CheckBox;
fn sample_cascade() -> Vec<CascaderOption> {
let leaves = |options: &[&str]| -> Vec<CascaderOption> {
options
.iter()
.enumerate()
.map(|(index, label)| CascaderOption::new(format!("leaf{index}"), (*label).to_string()))
.collect()
};
vec![
CascaderOption::branch("items", "Items", leaves(&["Widget", "Gadget", "Cog"])),
CascaderOption::branch("parts", "Parts", leaves(&["Bolt", "Nut", "Washer"])),
]
}
struct SampleTableModel;
impl TableModel for SampleTableModel {
fn row_count(&self) -> usize {
sample::ROWS.len()
}
fn column_count(&self) -> usize {
sample::HEADERS.len()
}
fn data(&self, row: usize, column: usize) -> Option<String> {
sample::ROWS.get(row).and_then(|cells| cells.get(column)).map(|cell| (*cell).to_string())
}
}
struct SampleTableSource;
impl IncrementalTableDataSource for SampleTableSource {
fn row_count(&self) -> usize {
sample::ROWS.len()
}
fn column_count(&self) -> usize {
sample::HEADERS.len()
}
fn data(&self, row: usize, column: usize) -> Option<String> {
SampleTableModel.data(row, column)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::widget::WidgetFactory;
#[test]
fn every_filled_name_downcasts_to_the_type_the_registry_builds() {
let factory = WidgetFactory::new_with_defaults();
let claimed = [
"table",
"table_widget",
"virtual_table",
"data_grid",
"grid_table",
"list_box",
"combo_box",
"editable_combo_box",
"dropdown",
"font_combo_box",
"multi_select_combo_box",
"cascader",
"list_view",
"tree_view",
"menu",
"context_menu",
"menu_button",
"command_palette",
"tab_bar",
"segmented_control",
"calendar",
];
for name in claimed {
let Some(mut widget) =
factory.create(name, crate::widget::census::CENSUS_RECT, "Sample")
else {
panic!("{name} is claimed as fillable but the factory cannot build it");
};
assert!(
apply(name, widget.as_mut()),
"{name}: the fill matched nothing, so its snapshot would be an empty frame again"
);
}
}
#[test]
fn a_control_without_a_data_concept_is_not_filled() {
let factory = WidgetFactory::new_with_defaults();
for name in ["button", "label", "divider", "line_edit"] {
let Some(mut widget) =
factory.create(name, crate::widget::census::CENSUS_RECT, "Sample")
else {
continue;
};
assert!(!apply(name, widget.as_mut()), "{name} has no data to fill");
}
}
#[test]
fn a_state_feature_is_switched_on_for_its_own_snapshot() {
use crate::widget::display_widgets::slider::{Slider, TickPosition};
use crate::widget::display_widgets::switch::Switch;
let factory = WidgetFactory::new_with_defaults();
for name in ["check_box", "switch", "slider", "group_box"] {
let Some(mut widget) =
factory.create(name, crate::widget::census::CENSUS_RECT, "Sample")
else {
panic!("{name} is published by the registry but could not be built");
};
assert!(apply(name, widget.as_mut()), "{name} owns a state feature");
}
let mut slider = Slider::new(crate::core::Rect::new(0, 0, 120, 8));
assert_eq!(slider.tick_position(), TickPosition::NoTicks, "the constructor default");
assert!(apply("slider", &mut slider));
assert_ne!(slider.tick_position(), TickPosition::NoTicks, "and the fill moved it");
assert!(slider.tick_interval() > 0, "or the draw guard still refuses the marks");
let mut switch = Switch::new(crate::core::Rect::new(0, 0, 60, 30));
assert!(!switch.is_checked(), "the constructor default");
assert!(apply("switch", &mut switch));
assert!(switch.is_checked(), "and the fill turned it on");
}
#[test]
fn the_calendar_sample_is_pinned_to_a_fixed_day() {
let rect = crate::core::Rect::new(0, 0, 240, 120);
let mut calendar = Calendar::new(rect);
assert_eq!(
calendar.selected_date(),
chrono::Local::now().date_naive(),
"the constructor still opens on today, which a host depends on"
);
assert!(apply("calendar", &mut calendar), "the calendar owns a clock-derived default");
assert_eq!(
calendar.selected_date(),
chrono::NaiveDate::from_ymd_opt(2026, 9, 16).expect("a valid literal date"),
"the sample must name a fixed day or the snapshot changes every midnight"
);
assert_eq!(calendar.display_month(), calendar.selected_date());
}
#[test]
fn both_table_routes_expose_the_same_cells() {
for row in 0..sample::ROWS.len() {
for column in 0..sample::HEADERS.len() {
assert_eq!(
SampleTableModel.data(row, column),
SampleTableSource.data(row, column),
"row {row} column {column} disagrees between the two table routes"
);
}
}
assert_eq!(SampleTableModel.row_count(), SampleTableSource.row_count());
assert_eq!(SampleTableModel.column_count(), SampleTableSource.column_count());
assert_eq!(SampleTableModel.data(99, 0), None);
assert_eq!(SampleTableSource.data(0, 99), None);
}
}