rt_graph/
graph_with_controls.rs1use crate::{Config, Graph, View, ViewMode};
2use gdk::prelude::*;
3use gtk::prelude::*;
4use std::{rc::Rc, cell::RefCell};
5
6pub struct GraphWithControls {
11 s: Rc<State>,
12}
13
14struct State {
15 controls_box: gtk::Box,
16
17 scrollbar: gtk::Scrollbar,
18 btn_zoom_x_out: gtk::Button,
19 btn_zoom_x_in: gtk::Button,
20 btn_follow: gtk::Button,
21
22 graph: RefCell<Graph>,
23}
24
25impl GraphWithControls {
26 pub fn build_ui<C>(config: Config, container: &C, gdk_window: &gdk::Window
28 ) -> GraphWithControls
29 where C: IsA<gtk::Container> + IsA<gtk::Widget>
30 {
31 let controls_box = gtk::BoxBuilder::new()
34 .orientation(gtk::Orientation::Vertical)
35 .spacing(0)
36 .build();
37 container.add(&controls_box);
38
39 let graph = Graph::build_ui(config, &controls_box, gdk_window);
40
41 let scrollbar = gtk::ScrollbarBuilder::new()
42 .orientation(gtk::Orientation::Horizontal)
43 .halign(gtk::Align::Start)
44 .build();
45 scrollbar.set_property_width_request(graph.width() as i32);
46 controls_box.add(&scrollbar);
47
48 let buttons_box = gtk::BoxBuilder::new()
49 .orientation(gtk::Orientation::Horizontal)
50 .height_request(35)
51 .build();
52 controls_box.add(&buttons_box);
53
54 let btn_follow = gtk::ButtonBuilder::new()
55 .label("Follow")
56 .build();
57 buttons_box.add(&btn_follow);
58
59 let btn_zoom_x_in = gtk::ButtonBuilder::new()
60 .label("Zoom X in")
61 .build();
62 buttons_box.add(&btn_zoom_x_in);
63
64 let btn_zoom_x_out = gtk::ButtonBuilder::new()
65 .label("Zoom X out")
66 .sensitive(false)
67 .build();
68 buttons_box.add(&btn_zoom_x_out);
69
70 let s = Rc::new(State {
73 controls_box: controls_box.clone(),
74
75 scrollbar: scrollbar.clone(),
76 btn_zoom_x_out: btn_zoom_x_out.clone(),
77 btn_zoom_x_in: btn_zoom_x_in.clone(),
78 btn_follow: btn_follow.clone(),
79
80 graph: RefCell::new(graph),
81 });
82 let g = GraphWithControls {
83 s: s.clone(),
84 };
85
86 update_controls(&g, &g.s.graph.borrow().view());
87
88 let gc = g.clone();
91 scrollbar.connect_change_value(move |_ctrl, _scroll_type, v| {
92 gc.s.graph.borrow().scroll(v);
93 Inhibit(false)
94 });
95
96 let gc = g.clone();
97 btn_follow.connect_clicked(move |_btn| {
98 gc.s.graph.borrow().set_follow()
99 });
100
101 let gc = g.clone();
102 btn_zoom_x_in.connect_clicked(move |_btn| {
103 let new = gc.s.graph.borrow().view().zoom_x / 2.0;
104 gc.s.graph.borrow().set_zoom_x(new);
105 });
106
107 let gc = g.clone();
108 btn_zoom_x_out.connect_clicked(move |_btn| {
109 let new = gc.s.graph.borrow().view().zoom_x * 2.0;
110 gc.s.graph.borrow().set_zoom_x(new);
111 });
112
113 {
114 let gc = g.clone();
116 s.graph.borrow_mut().view_observable().connect(move |view| {
117 update_controls(&gc, &view);
118 });
119 }
120
121 let gc = g.clone();
122 g.s.graph.borrow().drawing_area().add_events(gdk::EventMask::BUTTON_PRESS_MASK);
123 g.s.graph.borrow().drawing_area().connect_button_press_event(move |_ctrl, ev| {
124 drawing_area_button_press(&gc, ev)
125 });
126
127 controls_box.show_all();
129
130 g
131 }
132
133 fn clone(&self) -> GraphWithControls {
134 GraphWithControls {
135 s: self.s.clone()
136 }
137 }
138
139 pub fn show(&self) {
141 self.s.controls_box.show();
142 self.s.graph.borrow().show();
143 }
144
145 pub fn hide(&self) {
147 self.s.controls_box.hide();
148 self.s.graph.borrow().hide();
149 }
150}
151
152fn update_controls(g: &GraphWithControls, view: &View) {
154 trace!("update_controls view={:?}", view);
155 let s = &g.s;
156 let adj = s.scrollbar.get_adjustment();
157 let window_width_t = (s.graph.borrow().width() as f64) * view.zoom_x;
158
159 adj.set_upper(s.graph.borrow().last_t() as f64);
160 adj.set_lower(s.graph.borrow().first_t() as f64);
161 adj.set_step_increment(window_width_t / 4.0);
162 adj.set_page_increment(window_width_t / 2.0);
163 adj.set_page_size(window_width_t);
164
165 match view.mode {
166 ViewMode::Following =>
167 adj.set_value(s.graph.borrow().last_t() as f64),
168 ViewMode::Scrolled => adj.set_value(view.last_drawn_t as f64 -
169 ((s.graph.borrow().width() as f64) * view.zoom_x)),
170 }
171
172 s.btn_zoom_x_in.set_sensitive(view.zoom_x > s.graph.borrow().max_zoom_x());
173 s.btn_zoom_x_out.set_sensitive(view.zoom_x < s.graph.borrow().base_zoom_x());
174 s.btn_follow.set_sensitive(view.mode == ViewMode::Scrolled);
175}
176
177fn drawing_area_button_press(g: &GraphWithControls, ev: &gdk::EventButton) -> Inhibit {
178 let pos = ev.get_position();
179 let pt = g.s.graph.borrow().drawing_area_pos_to_point(pos.0, pos.1);
180 debug!("drawing_area button_press pos={:?} pt={:?}", pos, pt);
181
182 if let Some(pta) = pt {
183 let info_bar = gtk::InfoBarBuilder::new()
184 .halign(gtk::Align::Start)
185 .build();
186 g.s.controls_box.add(&info_bar);
187 info_bar.set_property_width_request(g.s.graph.borrow().width() as i32);
188
189 info_bar.get_content_area().add(>k::Label::new(Some("Time, [Values]:")));
190
191 let entry = gtk::EntryBuilder::new()
192 .text(&*format!("{}, {:?}", pta.t, pta.vals()))
193 .editable(false)
194 .hexpand(true)
195 .build();
196 info_bar.get_content_area().add(&entry);
197
198 let close_btn = gtk::ButtonBuilder::new()
199 .label("Close")
200 .build();
201 info_bar.get_action_area().unwrap().add(&close_btn);
202
203 let ibc = info_bar.clone();
204 let cbc = g.s.controls_box.clone();
205 close_btn.connect_clicked(move |_btn| {
206 cbc.remove(&ibc);
207 });
208
209 info_bar.show_all();
210 }
211
212 Inhibit(false)
213}