rg3d_ui/
tab_control.rs

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            // Hide everything but first tab content.
99            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}