tree/
tree.rs

1#![allow(dead_code)]
2
3use fltk::{
4    app,
5    button::Button,
6    enums::Event,
7    frame::Frame,
8    prelude::*,
9    tree::{Tree, TreeItem, TreeReason, TreeSelect},
10    window::Window,
11};
12use std::cell::RefCell;
13use std::env;
14use std::rc::Rc;
15
16#[derive(PartialEq)]
17enum State {
18    MovingUp,
19    MovingDown,
20    Undefined,
21}
22
23fn verify_open_till_root(opt: &Option<fltk::tree::TreeItem>) -> bool {
24    let mut par = opt.clone();
25    loop {
26        match par.as_ref().unwrap().parent() {
27            Some(p) => {
28                if p.is_close() {
29                    return false;
30                } else {
31                    par = Some(p.clone());
32                }
33            }
34            None => return true,
35        }
36    }
37}
38
39struct TreeMouseFocus {
40    t_widget: fltk::tree::Tree,
41    previous_focus: Rc<RefCell<Option<TreeItem>>>,
42}
43
44impl TreeMouseFocus {
45    fn new(x: i32, y: i32, width: i32, height: i32, title: &'static str) -> Self {
46        let mut t_widget = Tree::new(x, y, width, height, title);
47        let previous_focus = Rc::new(RefCell::new(None::<TreeItem>));
48        let pfr = Rc::clone(&previous_focus);
49        t_widget.set_callback_reason(TreeReason::Selected);
50        t_widget.set_callback(|_t| println!("clicked an item"));
51        t_widget.handle(move |t, e| match e {
52            Event::Move => {
53                let (_, mouse_y) = app::event_coords();
54                let mut state = State::Undefined;
55                let mut pf = pfr.borrow_mut();
56                loop {
57                    match &*pf {
58                        Some(item) => {
59                            let item_y = item.y();
60                            match state {
61                                State::MovingUp => {
62                                    if verify_open_till_root(&pf) {
63                                        if mouse_y < item_y {
64                                            *pf = pf.as_ref().unwrap().prev();
65                                            continue;
66                                        };
67                                        break;
68                                    } else {
69                                        *pf = pf.as_ref().unwrap().prev();
70                                        continue;
71                                    }
72                                }
73                                State::MovingDown => {
74                                    if verify_open_till_root(&pf) {
75                                        if mouse_y > item_y + item.h() {
76                                            *pf = pf.as_ref().unwrap().next();
77                                            continue;
78                                        };
79                                        break;
80                                    } else {
81                                        *pf = pf.as_ref().unwrap().next();
82                                        continue;
83                                    }
84                                }
85                                State::Undefined => {
86                                    if mouse_y < item_y {
87                                        *pf = pf.as_ref().unwrap().prev();
88                                        state = State::MovingUp;
89                                        continue;
90                                    };
91                                    if mouse_y > item_y + item.h() {
92                                        *pf = pf.as_ref().unwrap().next();
93                                        state = State::MovingDown;
94                                        continue;
95                                    };
96                                    return true; // If in same range, don't update 'previous_focus'
97                                }
98                            }
99                        }
100                        // End up here if y is outside tree boundaries, or no tree item is present
101                        None => match &state {
102                            State::MovingUp | State::MovingDown => return true,
103                            State::Undefined => {
104                                *pf = t.first();
105                                state = State::MovingDown;
106                                if pf.is_none() {
107                                    return true;
108                                }
109                                continue;
110                            }
111                        },
112                    };
113                }
114                if verify_open_till_root(&pf) {
115                    t.take_focus().ok();
116                    t.set_item_focus(pf.as_ref().unwrap());
117                    println!("Set focus to item: {:?}", pf.as_ref().unwrap().label());
118                }
119                true
120            }
121            _ => false,
122        });
123        Self {
124            t_widget,
125            previous_focus,
126        }
127    }
128
129    fn add(&mut self, path: &str) -> Option<TreeItem> {
130        self.t_widget.add(path)
131    }
132
133    /// Caution, variable 'previous focus' must be set to None, as it
134    /// otherwise could try to refer to an already freed memory location,
135    /// when this TreeItem is removed.
136    fn remove(&mut self, item: &TreeItem) -> Result<(), FltkError> {
137        *self.previous_focus.borrow_mut() = None;
138        self.t_widget.remove(item)
139    }
140
141    fn get_items(&self) -> Option<Vec<TreeItem>> {
142        self.t_widget.get_items()
143    }
144}
145
146fn main() {
147    let path = env::current_dir().unwrap();
148    let path: String = path
149        .to_str()
150        .unwrap()
151        .chars()
152        .map(|c| match c {
153            '\\' => '/', // change window paths to posix paths
154            _ => c,
155        })
156        .collect();
157
158    let app = app::App::default().with_scheme(app::Scheme::Gtk);
159    let mut wind = Window::default().with_size(400, 300);
160    let mut but = Button::new(260, 255, 80, 40, "Get Items");
161    let _frame = Frame::new(20, 255, 160, 40, "Focus follow mouse");
162    let mut tree = TreeMouseFocus::new(5, 10, 190, 240, "");
163    tree.add(&path);
164
165    let mut items = tree.get_items().unwrap();
166    items.as_mut_slice()[0].set_label("/");
167
168    let mut tree2 = Tree::new(205, 10, 190, 240, "");
169    tree2.set_select_mode(TreeSelect::Multi);
170    tree2.add("First");
171    tree2.add("First/1st");
172    tree2.add("First/2nd/3rd");
173    tree2.add("Second");
174    tree2.add("Third");
175
176    tree2.set_when(fltk::enums::When::ReleaseAlways);
177
178    wind.make_resizable(true);
179    wind.show();
180
181    but.set_callback({
182        let tree2 = tree2.clone();
183        move |_| match tree2.get_selected_items() {
184            None => println!("No items selected"),
185            Some(vals) => print!(
186                "In total {} items selected:\n{}",
187                vals.len(),
188                vals.iter()
189                    .map(|i| tree2.item_pathname(i).unwrap() + "\n")
190                    .collect::<String>()
191            ),
192        }
193    });
194
195    app.run().unwrap();
196}