closable_tabs2/
closable_tabs2.rs

1// Shows how to create custom closable tabs
2
3#![allow(dead_code)]
4
5mod closable_tab {
6    use fltk::{
7        app, button,
8        enums::{Align, Color, FrameType},
9        group,
10        prelude::*,
11    };
12
13    #[derive(Copy, Clone)]
14    pub enum Message {
15        Foreground(i32),
16        Delete(i32),
17        InsertNew(i32),
18    }
19
20    fn create_tab_button(label: &str) -> group::Group {
21        let mut grp = group::Group::new(0, 0, 150, 40, None);
22        grp.set_align(Align::Left | Align::Inside);
23        let mut but_handle = button::Button::new(grp.x() + 5, grp.y() + 5, 110, 30, "");
24        but_handle.set_align(Align::Left | Align::Inside);
25        but_handle.set_label(label);
26        let mut but_close = button::Button::new(grp.x() + 120, grp.y() + 10, 20, 20, "@1+");
27        but_handle.set_frame(FrameType::FlatBox);
28        but_handle.clear_visible_focus();
29        but_close.set_frame(FrameType::FlatBox);
30        grp.end();
31        grp.set_frame(FrameType::UpFrame);
32        grp
33    }
34
35    // Public
36    pub struct ClosableTab<'a> {
37        current: Option<i32>, // The tab which is visible on the foreground
38        snd: &'a app::Sender<Message>,
39        contents: group::Group,
40        tab_labels: group::Pack,
41    }
42
43    impl<'a> ClosableTab<'a> {
44        pub fn new(x: i32, y: i32, w: i32, h: i32, snd: &'a app::Sender<Message>) -> Self {
45            let current = None;
46            let parent_grp = group::Group::new(x, y, w, h, None);
47            let mut tab_labels = group::Pack::new(x + 5, y, w - 10, 40, None);
48            tab_labels.set_spacing(3);
49            tab_labels.set_type(group::PackType::Horizontal);
50            tab_labels.end();
51            let mut contents = group::Group::new(x, y + 40, w, h - 40, None);
52            contents.set_frame(FrameType::NoBox);
53            contents.end();
54            parent_grp.end();
55            Self {
56                current,
57                snd,
58                contents,
59                tab_labels,
60            }
61        }
62
63        pub fn add(&mut self, child: &mut group::Group, label: &str) {
64            child.resize(
65                self.contents.x(),
66                self.contents.y(),
67                self.contents.w(),
68                self.contents.h(),
69            );
70            self.contents.add(child);
71            let but = create_tab_button(label);
72            self.tab_labels.add(&but);
73            but.child(1).unwrap().set_callback({
74                let curr_child = child.clone();
75                let contents = self.contents.clone();
76                let sndb = *self.snd;
77                move |_| {
78                    let idx = contents.find(&curr_child);
79                    sndb.send(Message::Delete(idx));
80                    app::redraw();
81                }
82            });
83            but.child(0).unwrap().set_callback({
84                let curr_child = child.clone();
85                let contents = self.contents.clone();
86                let sndb = *self.snd;
87                move |_| {
88                    let idx = contents.find(&curr_child);
89                    sndb.send(Message::Foreground(idx));
90                    app::redraw();
91                }
92            });
93        }
94
95        pub fn remove(&mut self, idx: i32) {
96            self.contents.remove_by_index(idx);
97            self.tab_labels.remove_by_index(idx);
98            if self.current == Some(idx) {
99                if idx > 1 {
100                    self.set_foreground(idx - 1);
101                } else if self.contents.children() > 0 {
102                    self.set_foreground(0);
103                }
104            }
105        }
106
107        /** No return variable, fails silently */
108        pub fn set_foreground(&mut self, fg_idx: i32) {
109            for idx in 0..self.contents.children() {
110                if idx != fg_idx {
111                    self.contents.child(idx).unwrap().hide();
112                    self.tab_labels
113                        .child(idx)
114                        .unwrap()
115                        .set_label_color(fltk::enums::Color::Inactive);
116                    self.tab_labels
117                        .child(idx)
118                        .unwrap()
119                        .set_color(fltk::enums::Color::Inactive);
120                    self.tab_labels
121                        .child(idx)
122                        .unwrap()
123                        .set_frame(FrameType::DownFrame);
124                } else {
125                    self.contents.child(idx).unwrap().show();
126                    self.tab_labels
127                        .child(idx)
128                        .unwrap()
129                        .set_label_color(Color::Selection);
130                    self.tab_labels
131                        .child(idx)
132                        .unwrap()
133                        .set_color(fltk::enums::Color::Selection);
134                    self.tab_labels
135                        .child(idx)
136                        .unwrap()
137                        .set_frame(FrameType::NoBox);
138                }
139                self.tab_labels.child(idx).unwrap().set_damage(true);
140                self.tab_labels.child(idx).unwrap().redraw();
141                self.current = Some(fg_idx);
142            }
143        }
144
145        /** Report which tab index is visible on foreground */
146        pub fn get_foreground(&self) -> Option<i32> {
147            self.current
148        }
149    }
150}
151
152fn main() {
153    use fltk::{prelude::*, *};
154    // Create groups to be used as content for tabs
155    pub fn create_tab(from: i32, to: i32) -> group::Group {
156        let grp = group::Group::new(0, 0, 800, 600, None);
157        for idx in from..to {
158            button::Button::new(
159                idx * 10 + (idx - from) * 42,
160                idx * 10 + (idx - from) * 42,
161                80,
162                40,
163                None,
164            )
165            .with_label(&format!("button {idx}"));
166        }
167        grp.end();
168        grp
169    }
170    let app = app::App::default();
171    let mut win = window::Window::default().with_size(800, 600);
172    let (s, r) = app::channel::<closable_tab::Message>();
173    let mut tabs = closable_tab::ClosableTab::new(0, 0, 800, 600, &s);
174    win.end();
175    win.show();
176    tabs.add(&mut create_tab(1, 3), "tab 1");
177    tabs.add(&mut create_tab(4, 7), "tab 2");
178    tabs.add(&mut create_tab(8, 11), "tab 3");
179    tabs.add(&mut create_tab(12, 15), "tab 4");
180    tabs.add(&mut create_tab(16, 22), "tab 5");
181    tabs.set_foreground(2);
182    while app.wait() {
183        use closable_tab::Message::*;
184        if let Some(msg) = r.recv() {
185            match msg {
186                Foreground(idx) => {
187                    tabs.set_foreground(idx);
188                }
189                Delete(idx) => {
190                    tabs.remove(idx);
191                }
192                InsertNew(_) => {}
193            }
194        }
195    }
196}