Mercurial > tablet-emu
comparison src/gtk.rs @ 13:97e543f50f62
Split GTK UI into another module.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Sun, 01 Nov 2020 16:08:29 +0100 |
parents | |
children | adab13145994 |
comparison
equal
deleted
inserted
replaced
12:d43c31aff57c | 13:97e543f50f62 |
---|---|
1 // Tablet emulator, for people who don’t own one | |
2 // Copyright © 2020 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> | |
3 // | |
4 // This program is free software: you can redistribute it and/or modify | |
5 // it under the terms of the GNU Affero General Public License as published by | |
6 // the Free Software Foundation, either version 3 of the License, or | |
7 // (at your option) any later version. | |
8 // | |
9 // This program is distributed in the hope that it will be useful, | |
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 // GNU Affero General Public License for more details. | |
13 // | |
14 // You should have received a copy of the GNU Affero General Public License | |
15 // along with this program. If not, see <https://www.gnu.org/licenses/>. | |
16 | |
17 use gio::prelude::*; | |
18 use glib::clone; | |
19 use gtk::prelude::*; | |
20 | |
21 use std::env::args; | |
22 use std::io::ErrorKind; | |
23 use std::sync::Arc; | |
24 use input_linux::Key; | |
25 | |
26 use crate::{ | |
27 state::State, | |
28 DEFAULT_WIDTH, DEFAULT_HEIGHT, | |
29 }; | |
30 | |
31 fn build_main_menu(application: >k::Application) { | |
32 let quit = gio::SimpleAction::new("quit", None); | |
33 application.set_accels_for_action("app.quit", &["<Control>q"]); | |
34 application.add_action(&quit); | |
35 quit.connect_activate(clone!(@weak application => move |_, _| application.quit())); | |
36 | |
37 let about = gio::SimpleAction::new("about", None); | |
38 application.add_action(&about); | |
39 about.connect_activate(|_, _| { | |
40 let about = gtk::AboutDialog::new(); | |
41 about.set_program_name("TabletEmu"); | |
42 about.set_logo_icon_name(Some("input-tablet")); | |
43 about.set_website(Some("https://hg.linkmauve.fr/tablet-emu")); | |
44 about.set_version(Some("0.1")); | |
45 about.set_license_type(gtk::License::Agpl30); | |
46 about.set_copyright(Some("© 2020 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>")); | |
47 about.run(); | |
48 unsafe { | |
49 about.destroy(); | |
50 } | |
51 }); | |
52 | |
53 let menu = gio::Menu::new(); | |
54 { | |
55 let file = gio::Menu::new(); | |
56 file.append(Some("_Quit"), Some("app.quit")); | |
57 menu.append_submenu(Some("_File"), &file); | |
58 } | |
59 { | |
60 let help = gio::Menu::new(); | |
61 help.append(Some("_About"), Some("app.about")); | |
62 menu.append_submenu(Some("_Help"), &help); | |
63 } | |
64 application.set_menubar(Some(&menu)); | |
65 } | |
66 | |
67 fn build_ui(application: >k::Application) { | |
68 build_main_menu(application); | |
69 | |
70 let state = match State::new() { | |
71 Ok(state) => state, | |
72 Err(err) => { | |
73 match err.kind() { | |
74 ErrorKind::NotFound => { | |
75 eprintln!("Couldn’t find /dev/uinput: {}", err); | |
76 eprintln!("Maybe you forgot to `modprobe uinput`?"); | |
77 } | |
78 ErrorKind::PermissionDenied => { | |
79 eprintln!("Couldn’t open /dev/uinput for writing: {}", err); | |
80 eprintln!("Maybe you aren’t allowed to create input devices?"); | |
81 } | |
82 _ => eprintln!("Couldn’t open /dev/uinput for writing: {}", err), | |
83 } | |
84 std::process::exit(1); | |
85 } | |
86 }; | |
87 | |
88 let window = gtk::ApplicationWindow::new(application); | |
89 window.set_title("tablet-emu"); | |
90 window.set_position(gtk::WindowPosition::Center); | |
91 | |
92 let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0); | |
93 let tools_box = gtk::Box::new(gtk::Orientation::Vertical, 0); | |
94 | |
95 macro_rules! impl_tool { | |
96 ($tool:tt) => { | |
97 let tool = gtk::Button::with_mnemonic($tool); | |
98 let state_weak = Arc::downgrade(&state); | |
99 tool.connect_clicked(move |b| { | |
100 let state = state_weak.upgrade().unwrap(); | |
101 let mut state = state.lock().unwrap(); | |
102 let tool = match b.get_label().unwrap().as_str() { | |
103 "_Pen" => Key::ButtonToolPen, | |
104 "_Rubber" => Key::ButtonToolRubber, | |
105 "_Brush" => Key::ButtonToolBrush, | |
106 "P_encil" => Key::ButtonToolPencil, | |
107 "_Airbrush" => Key::ButtonToolAirbrush, | |
108 _ => unreachable!(), | |
109 }; | |
110 state.select_tool(tool); | |
111 }); | |
112 tools_box.add(&tool); | |
113 }; | |
114 }; | |
115 impl_tool!("_Pen"); | |
116 impl_tool!("_Rubber"); | |
117 impl_tool!("_Brush"); | |
118 impl_tool!("P_encil"); | |
119 impl_tool!("_Airbrush"); | |
120 | |
121 let drawing_area = gtk::DrawingArea::new(); | |
122 drawing_area.set_size_request(DEFAULT_WIDTH, DEFAULT_HEIGHT); | |
123 drawing_area.set_hexpand(true); | |
124 drawing_area.set_events( | |
125 gdk::EventMask::BUTTON_PRESS_MASK | |
126 | gdk::EventMask::BUTTON_RELEASE_MASK | |
127 | gdk::EventMask::POINTER_MOTION_MASK, | |
128 ); | |
129 let state_weak = Arc::downgrade(&state); | |
130 drawing_area.connect_configure_event(move |_, event| { | |
131 let state = state_weak.upgrade().unwrap(); | |
132 let mut state = state.lock().unwrap(); | |
133 state.set_size(event.get_size()); | |
134 true | |
135 }); | |
136 let state_weak = Arc::downgrade(&state); | |
137 drawing_area.connect_button_press_event(move |_, event| { | |
138 if event.get_button() != 1 { | |
139 return Inhibit(false); | |
140 } | |
141 | |
142 let state = state_weak.upgrade().unwrap(); | |
143 let mut state = state.lock().unwrap(); | |
144 let (x, y) = event.get_position(); | |
145 state.press(x, y).unwrap(); | |
146 Inhibit(false) | |
147 }); | |
148 let state_weak = Arc::downgrade(&state); | |
149 drawing_area.connect_button_release_event(move |_, event| { | |
150 if event.get_button() != 1 { | |
151 return Inhibit(false); | |
152 } | |
153 | |
154 let state = state_weak.upgrade().unwrap(); | |
155 let mut state = state.lock().unwrap(); | |
156 let (x, y) = event.get_position(); | |
157 state.release(x, y).unwrap(); | |
158 Inhibit(false) | |
159 }); | |
160 drawing_area.connect_motion_notify_event(move |_, event| { | |
161 let mut state = state.lock().unwrap(); | |
162 let (x, y) = event.get_position(); | |
163 state.motion(x, y).unwrap(); | |
164 Inhibit(false) | |
165 }); | |
166 drawing_area.connect_draw(move |_, ctx| { | |
167 //println!("drawing {}", drawing_area); | |
168 ctx.set_source_rgb(1., 0., 0.); | |
169 ctx.set_operator(cairo::Operator::Screen); | |
170 ctx.paint(); | |
171 Inhibit(false) | |
172 }); | |
173 | |
174 hbox.add(&tools_box); | |
175 hbox.add(&drawing_area); | |
176 | |
177 window.add(&hbox); | |
178 | |
179 window.show_all(); | |
180 } | |
181 | |
182 pub fn main() { | |
183 let application = gtk::Application::new( | |
184 Some("fr.linkmauve.TabletEmu"), | |
185 gio::ApplicationFlags::empty(), | |
186 ) | |
187 .expect("Initialisation failed…"); | |
188 application.connect_activate(build_ui); | |
189 application.run(&args().collect::<Vec<_>>()); | |
190 } |