Mercurial > tablet-emu
diff 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 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/src/gtk.rs @@ -0,0 +1,190 @@ +// Tablet emulator, for people who don’t own one +// Copyright © 2020 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +use gio::prelude::*; +use glib::clone; +use gtk::prelude::*; + +use std::env::args; +use std::io::ErrorKind; +use std::sync::Arc; +use input_linux::Key; + +use crate::{ + state::State, + DEFAULT_WIDTH, DEFAULT_HEIGHT, +}; + +fn build_main_menu(application: >k::Application) { + let quit = gio::SimpleAction::new("quit", None); + application.set_accels_for_action("app.quit", &["<Control>q"]); + application.add_action(&quit); + quit.connect_activate(clone!(@weak application => move |_, _| application.quit())); + + let about = gio::SimpleAction::new("about", None); + application.add_action(&about); + about.connect_activate(|_, _| { + let about = gtk::AboutDialog::new(); + about.set_program_name("TabletEmu"); + about.set_logo_icon_name(Some("input-tablet")); + about.set_website(Some("https://hg.linkmauve.fr/tablet-emu")); + about.set_version(Some("0.1")); + about.set_license_type(gtk::License::Agpl30); + about.set_copyright(Some("© 2020 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>")); + about.run(); + unsafe { + about.destroy(); + } + }); + + let menu = gio::Menu::new(); + { + let file = gio::Menu::new(); + file.append(Some("_Quit"), Some("app.quit")); + menu.append_submenu(Some("_File"), &file); + } + { + let help = gio::Menu::new(); + help.append(Some("_About"), Some("app.about")); + menu.append_submenu(Some("_Help"), &help); + } + application.set_menubar(Some(&menu)); +} + +fn build_ui(application: >k::Application) { + build_main_menu(application); + + let state = match State::new() { + Ok(state) => state, + Err(err) => { + match err.kind() { + ErrorKind::NotFound => { + eprintln!("Couldn’t find /dev/uinput: {}", err); + eprintln!("Maybe you forgot to `modprobe uinput`?"); + } + ErrorKind::PermissionDenied => { + eprintln!("Couldn’t open /dev/uinput for writing: {}", err); + eprintln!("Maybe you aren’t allowed to create input devices?"); + } + _ => eprintln!("Couldn’t open /dev/uinput for writing: {}", err), + } + std::process::exit(1); + } + }; + + let window = gtk::ApplicationWindow::new(application); + window.set_title("tablet-emu"); + window.set_position(gtk::WindowPosition::Center); + + let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0); + let tools_box = gtk::Box::new(gtk::Orientation::Vertical, 0); + + macro_rules! impl_tool { + ($tool:tt) => { + let tool = gtk::Button::with_mnemonic($tool); + let state_weak = Arc::downgrade(&state); + tool.connect_clicked(move |b| { + let state = state_weak.upgrade().unwrap(); + let mut state = state.lock().unwrap(); + let tool = match b.get_label().unwrap().as_str() { + "_Pen" => Key::ButtonToolPen, + "_Rubber" => Key::ButtonToolRubber, + "_Brush" => Key::ButtonToolBrush, + "P_encil" => Key::ButtonToolPencil, + "_Airbrush" => Key::ButtonToolAirbrush, + _ => unreachable!(), + }; + state.select_tool(tool); + }); + tools_box.add(&tool); + }; + }; + impl_tool!("_Pen"); + impl_tool!("_Rubber"); + impl_tool!("_Brush"); + impl_tool!("P_encil"); + impl_tool!("_Airbrush"); + + let drawing_area = gtk::DrawingArea::new(); + drawing_area.set_size_request(DEFAULT_WIDTH, DEFAULT_HEIGHT); + drawing_area.set_hexpand(true); + drawing_area.set_events( + gdk::EventMask::BUTTON_PRESS_MASK + | gdk::EventMask::BUTTON_RELEASE_MASK + | gdk::EventMask::POINTER_MOTION_MASK, + ); + let state_weak = Arc::downgrade(&state); + drawing_area.connect_configure_event(move |_, event| { + let state = state_weak.upgrade().unwrap(); + let mut state = state.lock().unwrap(); + state.set_size(event.get_size()); + true + }); + let state_weak = Arc::downgrade(&state); + drawing_area.connect_button_press_event(move |_, event| { + if event.get_button() != 1 { + return Inhibit(false); + } + + let state = state_weak.upgrade().unwrap(); + let mut state = state.lock().unwrap(); + let (x, y) = event.get_position(); + state.press(x, y).unwrap(); + Inhibit(false) + }); + let state_weak = Arc::downgrade(&state); + drawing_area.connect_button_release_event(move |_, event| { + if event.get_button() != 1 { + return Inhibit(false); + } + + let state = state_weak.upgrade().unwrap(); + let mut state = state.lock().unwrap(); + let (x, y) = event.get_position(); + state.release(x, y).unwrap(); + Inhibit(false) + }); + drawing_area.connect_motion_notify_event(move |_, event| { + let mut state = state.lock().unwrap(); + let (x, y) = event.get_position(); + state.motion(x, y).unwrap(); + Inhibit(false) + }); + drawing_area.connect_draw(move |_, ctx| { + //println!("drawing {}", drawing_area); + ctx.set_source_rgb(1., 0., 0.); + ctx.set_operator(cairo::Operator::Screen); + ctx.paint(); + Inhibit(false) + }); + + hbox.add(&tools_box); + hbox.add(&drawing_area); + + window.add(&hbox); + + window.show_all(); +} + +pub fn main() { + let application = gtk::Application::new( + Some("fr.linkmauve.TabletEmu"), + gio::ApplicationFlags::empty(), + ) + .expect("Initialisation failed…"); + application.connect_activate(build_ui); + application.run(&args().collect::<Vec<_>>()); +}