use crate::compat::Arc;
use crate::core::{Color, ObjectId, Point, Rect, Size};
use crate::widget::base_widgets::label::Label;
use crate::widget::capability::coercion::widget_as_mut;
use crate::widget::sample_data as sample;
use crate::widget::Widget;
use std::cell::RefCell;
use std::rc::Rc;
use crate::compat::{String, Vec};
use crate::widget::advanced_widgets::calendar::Calendar;
use crate::widget::advanced_widgets::dial::Dial;
use crate::widget::advanced_widgets::pie_menu::PieMenu;
use crate::widget::advanced_widgets::ribbon_bar::RibbonBar;
use crate::widget::advanced_widgets::tab_bar::TabBar;
use crate::widget::container_widgets::carousel::Carousel;
use crate::widget::container_widgets::groupbox::GroupBox;
use crate::widget::container_widgets::mdiarea::MdiArea;
use crate::widget::container_widgets::scrollarea::ScrollArea;
use crate::widget::container_widgets::splitter::Splitter;
use crate::widget::container_widgets::stackedwidget::StackedWidget;
use crate::widget::container_widgets::toolbox::ToolBox;
use crate::widget::cupertino::core::{MaterialNavigationRail, MaterialSnackbar};
use crate::widget::cupertino::segmented_control::CupertinoSegmentedControl;
use crate::widget::dialog::bottom_sheet::BottomSheet;
use crate::widget::dialog::dialog_widget::Dialog;
use crate::widget::dialog::input_dialog::InputDialog;
use crate::widget::dialog::message_box::MessageBox;
use crate::widget::dialog::modal_bottom_sheet::ModalBottomSheet;
use crate::widget::dialog::popover::Popover;
use crate::widget::dialog::progress_dialog::ProgressDialog;
use crate::widget::dialog::wizard::WizardDialog;
use crate::widget::display_widgets::mini_canvas::MiniCanvas;
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::input_widgets::shortcut_editor::{ShortcutEditor, ShortcutEntry};
use crate::widget::menu_toolbar::menu::Menu;
use crate::widget::menu_toolbar::menu_bar::MenuBar;
use crate::widget::menu_toolbar::menu_button::{MenuButton, MenuItem};
use crate::widget::menu_toolbar::tool_bar::ToolBar;
use crate::widget::misc_widgets::segmented_button::{Segment, SegmentedButton};
use crate::widget::nav_widgets::bottom_navigation_bar::BottomNavigationBar;
use crate::widget::nav_widgets::navigation_drawer::NavigationDrawer;
use crate::widget::nav_widgets::tab_view::TabView;
use crate::widget::overlay_widgets::refresh_control::RefreshControl;
use crate::widget::overlay_widgets::swipe_to_dismiss::SwipeToDismiss;
use crate::widget::special_widgets::breadcrumb::{Breadcrumb, BreadcrumbSegment};
use crate::widget::special_widgets::canvas::Canvas;
use crate::widget::special_widgets::chip::{Chip, ChipItem};
use crate::widget::special_widgets::command_palette::{CommandEntry, CommandPalette};
use crate::widget::special_widgets::finance::candlestick_chart::CandlestickChart;
use crate::widget::special_widgets::finance::depth_chart::DepthChart;
use crate::widget::special_widgets::finance::indicator_chart::IndicatorChart;
use crate::widget::special_widgets::finance::order_book::OrderBookWidget;
use crate::widget::special_widgets::finance::quote_board::QuoteBoard;
use crate::widget::special_widgets::finance::volume_chart::VolumeChart;
use crate::widget::special_widgets::finance::{Bar, BookLevel, OrderBook, PriceSeries, Quote};
use crate::widget::special_widgets::gantt_widget::{GanttTask, GanttWidget};
use crate::widget::special_widgets::grid::GridWidget;
use crate::widget::special_widgets::kanban_board::{KanbanBoard, KanbanCard, KanbanColumn};
use crate::widget::special_widgets::notification_center::{
NotificationCenter, NotificationItem, NotificationLevel,
};
use crate::widget::special_widgets::segmented_control::{SegmentItem, SegmentedControl};
use crate::widget::special_widgets::snackbar::Snackbar;
use crate::widget::special_widgets::timeline_widget::{TimelineItem, TimelineWidget};
use crate::widget::special_widgets::toast::{ToastItem, ToastLevel, ToastStack};
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::properties_panel::{
PropertiesPanel, PropertyEntry, PropertyValue,
};
use crate::widget::view_widgets::table_widget::{TableModel, TableWidget};
use crate::widget::view_widgets::tree_table::TreeTable;
use crate::widget::view_widgets::tree_view::{TreeView, VecTreeModel};
use crate::widget::view_widgets::virtual_list::VirtualList;
use crate::widget::view_widgets::virtual_table::VirtualTable;
use crate::widget::web_widgets::web_engine::WebEngineView;
use crate::widget::{Draw, SimpleRegistry};
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_today(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::from_views(sample::tree_nodes())));
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) => {
bar.clear();
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();
settle(widget, |w| {
let mut settled = false;
if let Some(sheet) = widget_as_mut::<BottomSheet>(w) {
settled = !sheet.tick(1000);
}
settled
});
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();
settle(widget, |w| {
let mut settled = false;
if let Some(dialog) = widget_as_mut::<Dialog>(w) {
settled = !dialog.tick(1000);
}
settled
});
true
}
None => false,
},
"message_box" => match widget_as_mut::<MessageBox>(widget) {
Some(message_box) => {
message_box.show();
settle(widget, |w| {
let mut settled = false;
if let Some(message_box) = widget_as_mut::<MessageBox>(w) {
settled = !message_box.tick(1000);
}
settled
});
true
}
None => false,
},
"ribbon_bar" => match widget_as_mut::<RibbonBar>(widget) {
Some(ribbon) => {
ribbon.clear();
let home = ribbon.add_tab("Home");
let insert = ribbon.add_tab("Insert");
let clipboard = ribbon.add_group(home, "Clipboard");
ribbon.add_large_item(home, clipboard, "Paste");
let font = ribbon.add_group(home, "Font");
ribbon.add_item(home, font, "Bold");
ribbon.add_item(home, font, "Italic");
let shapes = ribbon.add_group(insert, "Shapes");
ribbon.add_large_item(insert, shapes, "Chart");
true
}
None => false,
},
"pie_menu" => match widget_as_mut::<PieMenu>(widget) {
Some(menu) => {
for label in ["Copy", "Move", "Rename", "Delete"] {
menu.add_item(label);
}
menu.set_animation_progress(1.0);
true
}
None => false,
},
"kanban_board" => match widget_as_mut::<KanbanBoard>(widget) {
Some(board) => {
board.add_column(KanbanColumn::new("todo", "To Do"));
board.add_column(KanbanColumn::new("doing", "In Progress").with_wip_limit(2));
board.add_column(KanbanColumn::new("done", "Done"));
board.add_card(0, KanbanCard::new("card1", "Design the layout"));
board.add_card(
0,
KanbanCard::new("card2", "Write the spec").with_description("Owner: Ada"),
);
board.add_card(1, KanbanCard::new("card3", "Build the board"));
board.add_card(2, KanbanCard::new("card4", "Set up the repo").with_done(true));
true
}
None => false,
},
"timeline_widget" => match widget_as_mut::<TimelineWidget>(widget) {
Some(timeline) => {
timeline.set_viewport(0, 100);
timeline.set_items(vec![
TimelineItem::new("t1", "Kickoff", 0, 20),
TimelineItem::new("t2", "Design", 20, 55),
TimelineItem::new("t3", "Build", 55, 95),
]);
timeline.select_index(1);
true
}
None => false,
},
"gantt_widget" => match widget_as_mut::<GanttWidget>(widget) {
Some(gantt) => {
gantt.set_viewport(0, 100);
gantt.set_tasks(vec![
GanttTask::new("g1", "Research", 0, 25, 100),
GanttTask::new("g2", "Design", 25, 50, 60).depends_on(["g1"]),
GanttTask::new("g3", "Build", 50, 90, 10).depends_on(["g2"]),
GanttTask::milestone("m1", "Ship", 95),
]);
gantt.select_index(1);
true
}
None => false,
},
"breadcrumb" => match widget_as_mut::<Breadcrumb>(widget) {
Some(breadcrumb) => {
breadcrumb.set_segments(vec![
BreadcrumbSegment::new("root", "Home"),
BreadcrumbSegment::new("docs", "Documents"),
BreadcrumbSegment::new("project", "Project"),
]);
breadcrumb.set_selected_index(2);
true
}
None => false,
},
"snackbar" => match widget_as_mut::<Snackbar>(widget) {
Some(snackbar) => {
snackbar.show_with_action("Draft saved", "Undo");
settle(widget, |w| {
let mut settled = false;
if let Some(snackbar) = widget_as_mut::<Snackbar>(w) {
settled = !snackbar.tick(1000);
}
settled
});
true
}
None => false,
},
"notification_center" => match widget_as_mut::<NotificationCenter>(widget) {
Some(center) => {
center.push(NotificationItem::new(
"n1",
"Build finished",
"All 7463 tests passed.",
NotificationLevel::Info,
));
center.push(NotificationItem::new(
"n2",
"Low disk space",
"2.1 GB left on the root volume.",
NotificationLevel::Warning,
));
center.push(NotificationItem::new(
"n3",
"Update available",
"Version 2.9.0 is ready to install.",
NotificationLevel::Info,
));
settle(widget, |w| {
let mut settled = false;
if let Some(center) = widget_as_mut::<NotificationCenter>(w) {
settled = !center.tick(1000);
}
settled
});
true
}
None => false,
},
"shortcut_editor" => match widget_as_mut::<ShortcutEditor>(widget) {
Some(editor) => {
editor.add_shortcut(
ShortcutEntry::new("save", "Save", "File").with_keys(vec!["Ctrl", "S"]),
);
editor.add_shortcut(
ShortcutEntry::new("open", "Open", "File").with_keys(vec!["Ctrl", "O"]),
);
editor.add_shortcut(
ShortcutEntry::new("find", "Find", "Edit").with_keys(vec!["Ctrl", "F"]),
);
editor.add_shortcut(
ShortcutEntry::new("palette", "Command Palette", "View")
.with_keys(vec!["Ctrl", "Shift", "P"]),
);
true
}
None => false,
},
"wizard_dialog" => match widget_as_mut::<WizardDialog>(widget) {
Some(wizard) => {
wizard.set_title("Setup".to_string());
wizard.add_step("Welcome", false);
wizard.add_step("Options", true);
wizard.add_step("Finish", false);
true
}
None => false,
},
"progress_dialog" => match widget_as_mut::<ProgressDialog>(widget) {
Some(dialog) => {
dialog.set_title("Installing");
dialog.set_label_text("Copying files...");
dialog.set_value(45);
true
}
None => false,
},
"input_dialog" => match widget_as_mut::<InputDialog>(widget) {
Some(dialog) => {
dialog.set_title("Rename");
dialog.set_label_text("Name");
dialog.set_text_value("Document");
true
}
None => false,
},
"dial" => match widget_as_mut::<Dial>(widget) {
Some(dial) => {
dial.set_notches_visible(true);
dial.set_notch_target(10.0);
dial.set_value(65);
true
}
None => false,
},
"segmented_button" => match widget_as_mut::<SegmentedButton>(widget) {
Some(button) => {
button.segments_mut().clear();
button.add_segment(Segment::new("Day"));
button.add_segment(Segment::new("Week"));
button.add_segment(Segment::new("Month"));
button.set_selected_index(Some(1));
true
}
None => false,
},
"chip" => match widget_as_mut::<Chip>(widget) {
Some(chip) => {
let mut items = vec![
ChipItem::new("bug", "Bug"),
ChipItem::new("feature", "Feature"),
ChipItem::new("docs", "Docs"),
];
if let Some(first) = items.first_mut() {
first.selected = true;
}
chip.set_items(items);
true
}
None => false,
},
"bottom_navigation_bar" => match widget_as_mut::<BottomNavigationBar>(widget) {
Some(bar) => {
bar.add_item("home", "Home");
bar.add_item("search", "Search");
bar.add_item("bell", "Alerts");
bar.add_item("user", "Profile");
bar.set_selected_index(0);
true
}
None => false,
},
"material_navigation_rail" => match widget_as_mut::<MaterialNavigationRail>(widget) {
Some(rail) => {
rail.add_item("home", "Home");
rail.add_item("search", "Search");
rail.add_item("settings", "Settings");
rail.set_selected(1);
true
}
None => false,
},
"refresh_control" => match widget_as_mut::<RefreshControl>(widget) {
Some(refresh) => {
refresh.start_pull();
refresh.update_pull(refresh.threshold() + 16.0);
refresh.end_pull();
settle(widget, |w| {
let mut settled = false;
if let Some(refresh) = widget_as_mut::<RefreshControl>(w) {
settled = !refresh.tick(1000);
}
settled
});
true
}
None => false,
},
"candlestick_chart" => match widget_as_mut::<CandlestickChart>(widget) {
Some(chart) => {
chart.set_series(sample_price_series());
true
}
None => false,
},
"volume_chart" => match widget_as_mut::<VolumeChart>(widget) {
Some(chart) => {
chart.set_series(sample_price_series());
true
}
None => false,
},
"indicator_chart" => match widget_as_mut::<IndicatorChart>(widget) {
Some(chart) => {
chart.set_series(sample_price_series());
true
}
None => false,
},
"depth_chart" => match widget_as_mut::<DepthChart>(widget) {
Some(chart) => {
chart.set_book(sample_order_book());
true
}
None => false,
},
"order_book" => match widget_as_mut::<OrderBookWidget>(widget) {
Some(book) => {
book.set_book(sample_order_book());
true
}
None => false,
},
"quote_board" => match widget_as_mut::<QuoteBoard>(widget) {
Some(board) => {
board.set_quotes(sample_quotes());
true
}
None => false,
},
"virtual_list" | "data_view" => match widget_as_mut::<VirtualList>(widget) {
Some(list) => {
list.set_rows(sample_list_rows());
list.select_row(0);
true
}
None => false,
},
"tree_table" => match widget_as_mut::<TreeTable>(widget) {
Some(table) => {
table.set_rows(sample_list_rows());
true
}
None => false,
},
"properties_panel" => match widget_as_mut::<PropertiesPanel>(widget) {
Some(panel) => {
panel.add_property(PropertyEntry::new(
"Width",
PropertyValue::Number(240.0),
Some("Layout"),
None,
true,
));
panel.add_property(PropertyEntry::new(
"Height",
PropertyValue::Number(120.0),
Some("Layout"),
None,
true,
));
panel.add_property(PropertyEntry::new(
"Visible",
PropertyValue::Bool(true),
Some("State"),
None,
true,
));
panel.add_property(PropertyEntry::new(
"Title",
PropertyValue::Text("Hello".to_string()),
Some("State"),
None,
false,
));
true
}
None => false,
},
"tab_view" => match widget_as_mut::<TabView>(widget) {
Some(tabs) => {
tabs.add_tab(
"General",
Some(Box::new(Label::new(
"General page".to_string(),
Rect::new(0, 0, 200, 64),
))),
None::<String>,
);
tabs.add_tab(
"Advanced",
Some(Box::new(Label::new(
"Advanced options".to_string(),
Rect::new(0, 0, 200, 64),
))),
None::<String>,
);
tabs.add_tab("About", None, None::<String>);
tabs.set_current_index(0);
true
}
None => false,
},
"menu_bar" => match widget_as_mut::<MenuBar>(widget) {
Some(bar) => {
bar.add_menu("File");
bar.add_menu("Edit");
bar.add_menu("View");
true
}
None => false,
},
"tool_bar" => match widget_as_mut::<ToolBar>(widget) {
Some(bar) => {
bar.add_action("open", "Open");
bar.add_action("save", "Save");
bar.add_separator();
bar.add_action("run", "Run");
true
}
None => false,
},
"tool_box" => match widget_as_mut::<ToolBox>(widget) {
Some(toolbox) => {
toolbox.add_item("General".to_string(), None);
toolbox.add_item("Editor".to_string(), None);
toolbox.add_item("Files".to_string(), None);
toolbox.set_current_index(0);
true
}
None => false,
},
"carousel" => match widget_as_mut::<Carousel>(widget) {
Some(carousel) => {
carousel.add_page("Welcome", Color::rgb(33, 150, 243));
carousel.add_page("Features", Color::rgb(156, 39, 176));
carousel.add_page("Pricing", Color::rgb(0, 150, 136));
carousel.set_current(0);
true
}
None => false,
},
"toast_stack" => match widget_as_mut::<ToastStack>(widget) {
Some(stack) => {
stack.push(ToastItem::new("1", "Saved successfully", ToastLevel::Success, 3000));
stack.push(ToastItem::new("2", "Check your connection", ToastLevel::Warning, 3000));
stack.select_index(0);
true
}
None => false,
},
"grid" => match widget_as_mut::<GridWidget>(widget) {
Some(grid) => {
grid.set_rows(4);
grid.set_columns(6);
grid.set_spacing(2);
grid.set_line_color(Some(Some(Color::rgb(120, 120, 120))));
true
}
None => false,
},
"canvas" => match widget_as_mut::<Canvas>(widget) {
Some(canvas) => {
canvas.fill_rect(Rect::new(12, 16, 80, 40), Color::rgb(33, 150, 243));
canvas.draw_line(
Point::new(12, 72),
Point::new(228, 72),
Color::rgb(200, 200, 200),
);
canvas.fill_circle(Point::new(160, 60), 18, Color::rgb(255, 152, 0));
true
}
None => false,
},
"mini_canvas" => match widget_as_mut::<MiniCanvas>(widget) {
Some(canvas) => {
canvas.fill_rect(Rect::new(12, 16, 70, 32), Color::rgb(33, 150, 243));
canvas.draw_line(
Point::new(12, 70),
Point::new(220, 70),
Color::rgb(200, 200, 200),
);
canvas.fill_circle(Point::new(170, 56), 14, Color::rgb(255, 152, 0));
true
}
None => false,
},
"popover" => match widget_as_mut::<Popover>(widget) {
Some(popover) => {
popover.set_content(Box::new(Label::new(
"Popover content".to_string(),
Rect::new(0, 0, 180, 60),
)));
popover.show(Rect::new(20, 20, 80, 30));
true
}
None => false,
},
"material_snackbar" => match widget_as_mut::<MaterialSnackbar>(widget) {
Some(snackbar) => {
snackbar.set_message("Message sent");
snackbar.set_action_text("Undo");
snackbar.show();
true
}
None => false,
},
"cupertino_segmented_control" => match widget_as_mut::<CupertinoSegmentedControl>(widget) {
Some(control) => {
control.set_segments(vec![
"Day".to_string(),
"Week".to_string(),
"Month".to_string(),
]);
control.set_selected_index(1);
true
}
None => false,
},
"navigation_drawer" => match widget_as_mut::<NavigationDrawer>(widget) {
Some(drawer) => {
drawer.add_item("H", "Home");
drawer.add_item("S", "Search");
drawer.add_item("G", "Settings");
drawer.set_selected_index(0);
drawer.open();
true
}
None => false,
},
"web_engine_view" => match widget_as_mut::<WebEngineView>(widget) {
Some(view) => {
view.load_html("<h1>Web content</h1>");
true
}
None => false,
},
"swipe_to_dismiss" => match widget_as_mut::<SwipeToDismiss>(widget) {
Some(swipe) => {
swipe.set_child(Box::new(Label::new(
"Swipe me".to_string(),
Rect::new(0, 0, 200, 44),
)));
swipe.set_action_text("Delete");
swipe.set_dismiss_threshold(0.4);
true
}
None => false,
},
"mdi_area" => match widget_as_mut::<MdiArea>(widget) {
Some(area) => {
let registry = sample_registry();
register_sample_label(®istry, 1, Rect::new(16, 16, 160, 56), "Document 1");
register_sample_label(®istry, 2, Rect::new(48, 48, 160, 56), "Document 2");
area.set_registry(registry);
area.add_sub_window(1, Rect::new(16, 16, 160, 56));
area.add_sub_window(2, Rect::new(48, 48, 160, 56));
true
}
None => false,
},
"splitter" => match widget_as_mut::<Splitter>(widget) {
Some(splitter) => {
let registry = sample_registry();
register_sample_label(®istry, 1, Rect::new(0, 0, 100, 80), "Left pane");
register_sample_label(®istry, 2, Rect::new(100, 0, 140, 80), "Right pane");
splitter.set_registry(registry);
splitter.add_pane(1, 1);
splitter.add_pane(2, 1);
true
}
None => false,
},
"scroll_area" => match widget_as_mut::<ScrollArea>(widget) {
Some(area) => {
let registry = sample_registry();
register_sample_label(®istry, 1, Rect::new(0, 0, 160, 24), "Scrollable content");
area.set_registry(registry);
area.set_content_size(Size::new(160, 240));
true
}
None => false,
},
"stacked_widget" => match widget_as_mut::<StackedWidget>(widget) {
Some(stack) => {
let registry = sample_registry();
register_sample_label(®istry, 1, Rect::new(0, 0, 200, 80), "Page one");
register_sample_label(®istry, 2, Rect::new(0, 0, 200, 80), "Page two");
stack.set_registry(registry);
stack.add_widget(1);
stack.add_widget(2);
stack.set_current_index(0);
true
}
None => false,
},
_ => false,
}
}
fn settle(widget: &mut dyn Widget, mut step: impl FnMut(&mut dyn Widget) -> bool) {
const MAX_SETTLE_FRAMES: usize = 600;
for _ in 0..MAX_SETTLE_FRAMES {
if step(widget) {
return;
}
}
log::warn!(
"[sample_fill] control did not settle within {MAX_SETTLE_FRAMES} frames; snapshotting anyway"
);
}
fn sample_list_rows() -> Vec<String> {
["Alpha", "Bravo", "Charlie", "Delta", "Echo"]
.iter()
.map(|label| (*label).to_string())
.collect()
}
fn sample_registry() -> Rc<RefCell<SimpleRegistry>> {
Rc::new(RefCell::new(SimpleRegistry::new()))
}
fn register_sample_label(
registry: &Rc<RefCell<SimpleRegistry>>,
id: ObjectId,
rect: Rect,
text: &str,
) {
let mut label = Label::new(text.to_string(), rect);
registry.borrow_mut().register(id, move |context| label.draw(context), |_| {});
}
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"])),
]
}
fn sample_price_series() -> PriceSeries {
let mut series = PriceSeries::new();
let mut price: f64 = 100.0;
for index in 0..24 {
let drift = match index % 6 {
0 => 1.8,
1 => 0.9,
2 => -0.7,
3 => 1.4,
4 => -1.1,
_ => 0.6,
};
let open = price;
let close = open + drift;
let high = open.max(close) + 0.6;
let low = open.min(close) - 0.5;
let volume = 1200.0 + index as f64 * 137.0;
series
.push_with_label(Bar::new(open, high, low, close, volume), format!("{:02}:00", index));
price = close;
}
series
}
fn sample_order_book() -> OrderBook {
let mut book = OrderBook::new();
book.set_bids(
(0..5)
.map(|level| BookLevel::new(99.9 - level as f64 * 0.1, 12.0 + level as f64 * 7.0))
.collect(),
);
book.set_asks(
(0..5)
.map(|level| BookLevel::new(100.0 + level as f64 * 0.1, 10.0 + level as f64 * 6.0))
.collect(),
);
book
}
fn sample_quotes() -> Vec<Quote> {
let rows: [(&str, f64, f64, f64); 5] = [
("AAPL", 189.5, 187.2, 54_120_000.0),
("MSFT", 412.8, 416.1, 21_400_000.0),
("NVDA", 128.4, 121.9, 310_800_000.0),
("TSLA", 246.2, 249.7, 98_600_000.0),
("AMD", 162.7, 159.4, 44_900_000.0),
];
rows.iter()
.map(|(symbol, last, previous_close, volume)| {
let mut quote = Quote::new(symbol, *last, *previous_close);
quote.high = (*last).max(*previous_close) + 1.2;
quote.low = (*last).min(*previous_close) - 1.2;
quote.volume = *volume;
quote
})
.collect()
}
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())
}
fn fill_data(&self, row: usize, column: usize, out: &mut String) -> bool {
match sample::ROWS.get(row).and_then(|cells| cells.get(column)) {
Some(cell) => {
out.push_str(cell);
true
}
None => false,
}
}
}
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)
}
fn fill_cell(&self, row: usize, column: usize, out: &mut String) -> bool {
SampleTableModel.fill_data(row, column, out)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::widget::WidgetFactory;
fn declared_arm_names() -> Vec<&'static str> {
const SOURCE: &str = include_str!("sample_fill.rs");
let mut names = Vec::new();
let mut in_apply = false;
for line in SOURCE.lines() {
if line.starts_with("pub fn apply(name: &str") {
in_apply = true;
continue;
}
if !in_apply {
continue;
}
if line == "}" {
break;
}
let trimmed = line.trim();
if trimmed.starts_with("//") {
continue;
}
let Some((pattern, _)) = trimmed.split_once("=>") else { continue };
let mut rest = pattern;
while let Some(start) = rest.find('"') {
let after = &rest[start + 1..];
let Some(end) = after.find('"') else { break };
names.push(&after[..end]);
rest = &after[end + 1..];
}
}
names.sort_unstable();
names.dedup();
names
}
#[test]
fn every_filled_name_downcasts_to_the_type_the_registry_builds() {
let factory = WidgetFactory::new_with_defaults();
let claimed = declared_arm_names();
assert!(
claimed.len() >= 60,
"the arm scan found {} names, which is too few to be the real table",
claimed.len()
);
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 _theme_guard = crate::style::theme_test_guard();
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());
assert_eq!(
calendar.today(),
chrono::NaiveDate::from_ymd_opt(2026, 9, 16).expect("a valid literal date"),
"the painted day must come from the fixture too, or the picture rotates nightly"
);
let once = crate::widget::svg::render_to_svg(&mut calendar);
let twice = crate::widget::svg::render_to_svg(&mut calendar);
assert_eq!(once, twice, "a pinned calendar renders identically on every draw");
}
#[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);
}
}