1use crate::{
2 border::BorderBuilder,
3 brush::Brush,
4 button::{ButtonBuilder, ButtonMessage},
5 core::{color::Color, pool::Handle},
6 grid::{Column, GridBuilder, Row},
7 message::{MessageDirection, UiMessage},
8 widget::{Widget, WidgetBuilder, WidgetMessage},
9 BuildContext, Control, NodeHandleMapping, UiNode, UserInterface,
10};
11use std::{
12 any::{Any, TypeId},
13 ops::{Deref, DerefMut},
14};
15
16#[derive(Clone, PartialEq)]
17pub struct Tab {
18 header_button: Handle<UiNode>,
19 content: Handle<UiNode>,
20}
21
22#[derive(Clone)]
23pub struct TabControl {
24 widget: Widget,
25 tabs: Vec<Tab>,
26}
27
28crate::define_widget_deref!(TabControl);
29
30impl Control for TabControl {
31 fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
32 if type_id == TypeId::of::<Self>() {
33 Some(self)
34 } else {
35 None
36 }
37 }
38
39 fn resolve(&mut self, node_map: &NodeHandleMapping) {
40 for tab in self.tabs.iter_mut() {
41 node_map.resolve(&mut tab.header_button);
42 node_map.resolve(&mut tab.content);
43 }
44 }
45
46 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
47 self.widget.handle_routed_message(ui, message);
48
49 if let Some(ButtonMessage::Click) = message.data::<ButtonMessage>() {
50 for (i, tab) in self.tabs.iter().enumerate() {
51 if message.destination() == tab.header_button
52 && tab.header_button.is_some()
53 && tab.content.is_some()
54 {
55 for (j, other_tab) in self.tabs.iter().enumerate() {
56 ui.send_message(WidgetMessage::visibility(
57 other_tab.content,
58 MessageDirection::ToWidget,
59 j == i,
60 ));
61 }
62 break;
63 }
64 }
65 }
66 }
67}
68
69pub struct TabControlBuilder {
70 widget_builder: WidgetBuilder,
71 tabs: Vec<TabDefinition>,
72}
73
74pub struct TabDefinition {
75 pub header: Handle<UiNode>,
76 pub content: Handle<UiNode>,
77}
78
79impl TabControlBuilder {
80 pub fn new(widget_builder: WidgetBuilder) -> Self {
81 Self {
82 widget_builder,
83 tabs: Default::default(),
84 }
85 }
86
87 pub fn with_tab(mut self, tab: TabDefinition) -> Self {
88 self.tabs.push(tab);
89 self
90 }
91
92 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
93 let mut headers = Vec::new();
94 let mut content = Vec::new();
95 let tab_count = self.tabs.len();
96 for (i, tab) in self.tabs.into_iter().enumerate() {
97 headers.push(tab.header);
98 if i > 0 {
100 ctx[tab.content].set_visibility(false);
101 }
102 content.push(tab.content);
103 }
104
105 let tab_buttons = headers
106 .into_iter()
107 .enumerate()
108 .map(|(i, header)| {
109 ButtonBuilder::new(WidgetBuilder::new().on_column(i))
110 .with_content(header)
111 .build(ctx)
112 })
113 .collect::<Vec<Handle<UiNode>>>();
114
115 let headers_grid = GridBuilder::new(
116 WidgetBuilder::new()
117 .with_children(tab_buttons.iter().cloned())
118 .on_row(0),
119 )
120 .add_row(Row::auto())
121 .add_columns((0..tab_count).map(|_| Column::auto()).collect())
122 .build(ctx);
123
124 let content_grid = GridBuilder::new(
125 WidgetBuilder::new()
126 .with_children(content.iter().cloned())
127 .on_row(1),
128 )
129 .build(ctx);
130
131 let grid = GridBuilder::new(
132 WidgetBuilder::new()
133 .with_child(headers_grid)
134 .with_child(content_grid),
135 )
136 .add_column(Column::auto())
137 .add_row(Row::strict(30.0))
138 .add_row(Row::auto())
139 .build(ctx);
140
141 let tc = TabControl {
142 widget: self
143 .widget_builder
144 .with_child(
145 BorderBuilder::new(
146 WidgetBuilder::new()
147 .with_background(Brush::Solid(Color::from_rgba(0, 0, 0, 0)))
148 .with_child(grid),
149 )
150 .build(ctx),
151 )
152 .build(),
153 tabs: tab_buttons
154 .iter()
155 .zip(content)
156 .map(|(tab_button, content)| Tab {
157 header_button: *tab_button,
158 content,
159 })
160 .collect(),
161 };
162
163 ctx.add_node(UiNode::new(tc))
164 }
165}