From 25b2d1f8bfbf087ea1bb83bcc4e92c279ee7afa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Cerveau?= Date: Tue, 23 Nov 2021 18:04:57 +0100 Subject: [PATCH] GPS: port to GTK4 --- Cargo.lock | 207 +++++++++++------ Cargo.toml | 2 +- README.md | 4 +- src/app.rs | 131 ++++++----- src/gps.ui | 582 +++++++--------------------------------------- src/pluginlist.rs | 45 ++-- 6 files changed, 307 insertions(+), 664 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aefeec5..2688bc2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,30 +8,6 @@ version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" -[[package]] -name = "atk" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a83b21d2aa75e464db56225e1bda2dd5993311ba1095acaa8fa03d1ae67026ba" -dependencies = [ - "atk-sys", - "bitflags", - "glib 0.14.8", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "badcf670157c84bb8b1cf6b5f70b650fed78da2033c9eed84c4e49b11cbe83ea" -dependencies = [ - "glib-sys 0.14.0", - "gobject-sys 0.14.0", - "libc", - "system-deps 3.2.0", -] - [[package]] name = "autocfg" version = "1.0.1" @@ -77,6 +53,15 @@ dependencies = [ "smallvec", ] +[[package]] +name = "cfg-expr" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edae0b9625d1fce32f7d64b71784d9b1bf8469ec1a9c417e44aaf16a9cbd7571" +dependencies = [ + "smallvec", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -167,22 +152,6 @@ dependencies = [ "slab", ] -[[package]] -name = "gdk" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d749dcfc00d8de0d7c3a289e04a04293eb5ba3d8a4e64d64911d481fa9933b" -dependencies = [ - "bitflags", - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib 0.14.8", - "libc", - "pango", -] - [[package]] name = "gdk-pixbuf" version = "0.14.0" @@ -209,20 +178,36 @@ dependencies = [ ] [[package]] -name = "gdk-sys" -version = "0.14.0" +name = "gdk4" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e091b3d3d6696949ac3b3fb3c62090e5bfd7bd6850bef5c3c5ea701de1b1f1e" +checksum = "f97a162c17214d1bf981af3f683156a0b1667dd1927057c4f0a68513251ecf0f" +dependencies = [ + "bitflags", + "cairo-rs", + "gdk-pixbuf", + "gdk4-sys", + "gio", + "glib 0.14.8", + "libc", + "pango", +] + +[[package]] +name = "gdk4-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9498f4e06969fb96a4e4234dfe1d308a3ac6b120b3c6d93e3ec5c77fe88bc6d5" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", "gio-sys", "glib-sys 0.14.0", "gobject-sys 0.14.0", + "graphene-sys", "libc", "pango-sys", - "pkg-config", - "system-deps 3.2.0", + "system-deps 5.0.0", ] [[package]] @@ -366,13 +351,68 @@ dependencies = [ "system-deps 3.2.0", ] +[[package]] +name = "graphene-rs" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3380f132530ef9eb9e0a2bac180e30390aa5e49892d20294f822a974117a563" +dependencies = [ + "glib 0.14.8", + "graphene-sys", + "libc", +] + +[[package]] +name = "graphene-sys" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a9ac7450b3aa80792513a3c029920a2ede419de13fb5169a4e51b07a5685332" +dependencies = [ + "glib-sys 0.14.0", + "libc", + "pkg-config", + "system-deps 3.2.0", +] + +[[package]] +name = "gsk4" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff59ca46c4fc5087fd7a0c3770a71ea4b6e94f8c24c12e2c2e8538f9f6fd764" +dependencies = [ + "bitflags", + "cairo-rs", + "gdk4", + "glib 0.14.8", + "graphene-rs", + "gsk4-sys", + "libc", + "pango", +] + +[[package]] +name = "gsk4-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aa53ce70234da02f9954339d988d5ab853d746a8f47a4ae17735ff873545b5" +dependencies = [ + "cairo-sys-rs", + "gdk4-sys", + "glib-sys 0.14.0", + "gobject-sys 0.14.0", + "graphene-sys", + "libc", + "pango-sys", + "system-deps 5.0.0", +] + [[package]] name = "gst_pipeline_studio" version = "0.1.0" dependencies = [ "anyhow", "gstreamer", - "gtk", + "gtk4", ] [[package]] @@ -412,54 +452,37 @@ dependencies = [ ] [[package]] -name = "gtk" -version = "0.14.3" +name = "gtk4" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb51122dd3317e9327ec1e4faa151d1fa0d95664cd8fb8dcfacf4d4d29ac70c" +checksum = "58a04f421d1485ba4739e723199f5828bca05ab4e622ed39a96a342b6b1a6a3d" dependencies = [ - "atk", "bitflags", "cairo-rs", "field-offset", "futures-channel", - "gdk", "gdk-pixbuf", + "gdk4", "gio", "glib 0.14.8", - "gtk-sys", - "gtk3-macros", + "graphene-rs", + "gsk4", + "gtk4-macros", + "gtk4-sys", "libc", "once_cell", "pango", - "pkg-config", ] [[package]] -name = "gtk-sys" -version = "0.14.0" +name = "gtk4-macros" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c14c8d3da0545785a7c5a120345b3abb534010fb8ae0f2ef3f47c027fba303e" -dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys 0.14.0", - "gobject-sys 0.14.0", - "libc", - "pango-sys", - "system-deps 3.2.0", -] - -[[package]] -name = "gtk3-macros" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21de1da96dc117443fb03c2e270b2d34b7de98d0a79a19bbb689476173745b79" +checksum = "5068d4354af02454f44687adc613100aa98ae11e273cdcac84f89dc08be2b4a1" dependencies = [ "anyhow", "heck", + "itertools 0.10.1", "proc-macro-crate 1.1.0", "proc-macro-error", "proc-macro2", @@ -467,6 +490,25 @@ dependencies = [ "syn", ] +[[package]] +name = "gtk4-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e20a64c8f0ddcff8902ff04c130747f2fb7834a43530f75d03d6c71335733b49" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk4-sys", + "gio-sys", + "glib-sys 0.14.0", + "gobject-sys 0.14.0", + "graphene-sys", + "gsk4-sys", + "libc", + "pango-sys", + "system-deps 5.0.0", +] + [[package]] name = "heck" version = "0.3.3" @@ -802,7 +844,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "480c269f870722b3b08d2f13053ce0c2ab722839f472863c3e2d61ff3a1c2fa6" dependencies = [ "anyhow", - "cfg-expr", + "cfg-expr 0.8.1", "heck", "itertools 0.10.1", "pkg-config", @@ -813,6 +855,19 @@ dependencies = [ "version-compare 0.0.11", ] +[[package]] +name = "system-deps" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18db855554db7bd0e73e06cf7ba3df39f97812cb11d3f75e71c39bf45171797e" +dependencies = [ + "cfg-expr 0.9.0", + "heck", + "pkg-config", + "toml", + "version-compare 0.0.11", +] + [[package]] name = "thiserror" version = "1.0.30" diff --git a/Cargo.toml b/Cargo.toml index 7ce9952..45a6a42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,6 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -gtk = "0.14.3" +gtk = { version = "0.3", package = "gtk4" } anyhow = "1" gstreamer = "0.16" diff --git a/README.md b/README.md index ca0d0d8..ffef6c6 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,13 @@ Check https://rustup.rs for alternative installation options. ### Ubuntu/Debian/etc ```sh -apt install libgtk-3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev +apt install libgtk-4-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev ``` ### Fedora/RedHat/SuSE/etc ```sh -dnf install gtk3-devel gstreamer1-devel gstreamer1-plugins-base-devel +dnf install gtk4-devel gstreamer1-devel gstreamer1-plugins-base-devel ``` ## Getting started diff --git a/src/app.rs b/src/app.rs index dc077f3..5195384 100644 --- a/src/app.rs +++ b/src/app.rs @@ -17,11 +17,11 @@ // // SPDX-License-Identifier: GPL-3.0-only use gtk::cairo::Context; -use gtk::glib; use gtk::prelude::*; +use gtk::{gio, glib}; use gtk::{ - AboutDialog, AccelFlags, AccelGroup, ApplicationWindow, Builder, Button, DrawingArea, EventBox, - FileChooserDialog, MenuItem, ResponseType, Viewport, WindowPosition, + AboutDialog, Application, ApplicationWindow, Builder, Button, DrawingArea, FileChooserDialog, + ResponseType, Statusbar, Viewport, }; use std::cell::RefCell; use std::rc::{Rc, Weak}; @@ -79,8 +79,7 @@ impl GPSApp { let builder = Builder::from_string(glade_src); let window: ApplicationWindow = builder.object("mainwindow").expect("Couldn't get window"); window.set_application(Some(application)); - window.set_title("GstPipelineStudio"); - window.set_position(WindowPosition::Center); + window.set_title(Some("GstPipelineStudio")); window.set_size_request(800, 600); let pipeline = Pipeline::new().expect("Unable to initialize the pipeline"); let drawing_area = DrawingArea::new(); @@ -109,10 +108,11 @@ impl GPSApp { // When the application is activated show the UI. This happens when the first process is // started, and in the first process whenever a second process is started let app_weak = app.downgrade(); - application.connect_activate(move |_| { + + application.connect_activate(glib::clone!(@weak application => move |_| { let app = upgrade_weak!(app_weak); - app.build_ui(); - }); + app.build_ui(&application); + })); let app_container = RefCell::new(Some(app)); application.connect_shutdown(move |_| { @@ -124,58 +124,74 @@ impl GPSApp { }); } - pub fn build_ui(&self) { - let view_port: Viewport = self - .builder - .object("drawing_area") - .expect("Couldn't get window"); - let event_box = EventBox::new(); - event_box.add(&self.drawing_area); - view_port.add(&event_box); - + pub fn build_ui(&self, application: &Application) { let app_weak = self.downgrade(); - self.drawing_area.connect_draw(move |w, c| { - let app = upgrade_weak!(app_weak, gtk::Inhibit(false)); - println!("w: {} c:{}", w, c); + let drawing_area = gtk::DrawingArea::builder() + .content_height(24) + .content_width(24) + .build(); + + drawing_area.set_draw_func(move |_, c, width, height| { + let app = upgrade_weak!(app_weak); + println!("w: {} h: {} c:{}", width, height, c); let mut graph = app.graph.borrow_mut(); let elements = graph.elements(); draw_elements(&elements, c); - gtk::Inhibit(false) - }); - let app_weak = self.downgrade(); - event_box.connect_button_release_event(move |_w, evt| { - let app = upgrade_weak!(app_weak, gtk::Inhibit(false)); - let mut element: Element = Default::default(); - element.position.0 = evt.position().0; - element.position.1 = evt.position().1; - app.add_new_element(element); - app.drawing_area.queue_draw(); - gtk::Inhibit(false) + c.paint().expect("Invalid cairo surface state"); }); + let drawing_area_window: Viewport = self + .builder + .object("drawing_area") + .expect("Couldn't get window"); + drawing_area_window.set_child(Some(&drawing_area)); + + // let app_weak = self.downgrade(); + // event_box.connect_button_release_event(move |_w, evt| { + // let app = upgrade_weak!(app_weak, gtk::Inhibit(false)); + // let mut element: Element = Default::default(); + // element.position.0 = evt.position().0; + // element.position.1 = evt.position().1; + // app.add_new_element(element); + // app.drawing_area.queue_draw(); + // gtk::Inhibit(false) + // }); let window = &self.window; - window.show_all(); + window.show(); + let status_bar: Statusbar = self + .builder + .object("status_bar") + .expect("Couldn't get window"); + status_bar.push(status_bar.context_id("Description"), "GPS is ready"); + + let action = gio::SimpleAction::new("quit", None); + action.connect_activate({ + let app = application.downgrade(); + move |_, _| { + let app = app.upgrade().unwrap(); + app.quit(); + } + }); + application.add_action(&action); + application.set_accels_for_action("app.quit", &["q"]); + + let action = gio::SimpleAction::new("new-window", None); + + application.add_action(&action); + application.set_accels_for_action("app.new-window", &["n"]); - let quit: MenuItem = self - .builder - .object("menu-quit") - .expect("Couldn't get window"); - let about: MenuItem = self - .builder - .object("menu-about") - .expect("Couldn't get window"); let about_dialog: AboutDialog = self .builder .object("dialog-about") .expect("Couldn't get window"); - about.connect_activate(move |_| { - about_dialog.connect_delete_event(|dialog, _| { - dialog.hide(); - gtk::Inhibit(true) - }); - - about_dialog.show_all(); + let action = gio::SimpleAction::new("about", None); + action.connect_activate({ + move |_, _| { + about_dialog.show(); + } }); + application.add_action(&action); + application.set_accels_for_action("app.about", &["a"]); // Create a dialog to select GStreamer elements. let add_button: Button = self @@ -204,29 +220,16 @@ impl GPSApp { ("Open", ResponseType::Ok), ("Cancel", ResponseType::Cancel) ]); - open_dialog.set_select_multiple(true); + open_dialog.connect_response(|open_dialog, response| { if response == ResponseType::Ok { - let files = open_dialog.filenames(); - println!("Files: {:?}", files); + let file = open_dialog.file().expect("Couldn't get file"); + println!("Files: {:?}", file); } open_dialog.close(); }); - open_dialog.show_all(); + open_dialog.show(); })); - - let accel_group = AccelGroup::new(); - window.add_accel_group(&accel_group); - - quit.connect_activate(glib::clone!(@weak window => move |_| { - window.close(); - })); - - // `Primary` is `Ctrl` on Windows and Linux, and `command` on macOS - // It isn't available directly through `gdk::ModifierType`, since it has - // different values on different platforms. - let (key, modifier) = gtk::accelerator_parse("Q"); - quit.add_accelerator("activate", &accel_group, key, modifier, AccelFlags::VISIBLE); } // Downgrade to a weak reference diff --git a/src/gps.ui b/src/gps.ui index 571a838..bbf2c64 100644 --- a/src/gps.ui +++ b/src/gps.ui @@ -1,590 +1,184 @@ - - + + +
+ + _New Window + app.new-window + <primary>n + + + _About GstPipelineStudio + app.about + + + _Quit GstPipelineStudio + app.quit + +
+
- False - dialog GstPipelineStudio Stéphane Cerveau - image-missing - - - False - vertical - 2 - - - False - end - - - - - - - - - False - False - 0 - - - - - - - - - - False - dialog - - - False - vertical - 2 - - - False - end - - - - - - - - - False - False - 0 - - - - - - - + True + - False + mainwindow 320 260 - dialog - - - False - vertical - 2 - - - False - end - - - - - - - - - False - False - 0 - - + True + + + horizontal - - True - False + + True True - - True - True - True - in - - - True - True - - - - - - - - False - True - 0 - - - - - True - True - True - in - - - True - True - False - - - - - False - True - 1 - + + + + + + + True + True + + - - False - True - 1 - - False - 6 GstPipelineStudio 800 600 + + + 0 + True + + + win.open + Open + + + + - True - False vertical 1 - - - True - False - end - 10 - 10 - 10 - 10 - 6 - 6 - vertical - 2 - - - False - True - end - 0 - - - - - True - False - - - True - False - _File - True - - - True - False - - - gtk-new - True - False - True - True - - - - - gtk-open - True - False - True - True - - - - - gtk-save - True - False - True - True - - - - - gtk-save-as - True - False - True - True - - - - - True - False - - - - - gtk-quit - True - False - True - True - - - - - - - - - True - False - _Graph - True - - - True - False - - - gtk-add - True - False - True - True - - - - - gtk-open - True - False - True - True - - - - - True - False - - - - - gtk-media-play - True - False - True - True - - - - - gtk-media-pause - True - False - True - True - - - - - gtk-media-stop - True - False - True - True - - - - - gtk-delete - True - False - True - True - - - - - - - - - True - False - _Help - True - - - True - False - - - gtk-about - True - False - True - True - - - - - - - - - False - True - 0 - - - True - False - False + 0 - - True - False - start + + 1 gtk-add - True - True - True - True - True + 1 + list-add - - True - True - 0 - + 1 gtk-open - True - True - True - True - True + 1 + document-open-symbolic - - True - True - 1 - - True - True - True - True - - - True - False - gtk-media-play - - + 1 + 1 + media-playback-start-symbolic - - True - True - 2 - - True - True - True - True - - - True - False - gtk-media-pause - - + 1 + 1 + media-playback-pause-symbolic - - True - True - 3 - - True - True - True - True - - - True - False - gtk-media-stop - - + 1 + 1 + media-playback-stop-symbolic - - True - True - 4 - - - True - True - True - - - True - False - gtk-clear - - + + 1 + 1 + clear - - True - True - 6 - - - False - True - 0 - - True - True + 1 1 - - False - True - 1 - - - False - True - 1 - - True - False + True True - - True - True - in - + + True + True + - True - False - + - - True - True - 0 - - True - True - in - + True + True + - True - True - - - column - + - - False - True - end - 1 - - - False - True - 3 - - True - True end - in - - - True - True - - - - - + + + - - False - True - 4 - + + + + diff --git a/src/pluginlist.rs b/src/pluginlist.rs index 2e86dd4..522f195 100644 --- a/src/pluginlist.rs +++ b/src/pluginlist.rs @@ -20,15 +20,11 @@ use crate::app::GPSApp; use crate::graph::Element; use crate::pipeline::ElementInfo; use crate::pipeline::Pipeline; +use gtk::prelude::*; use gtk::TextBuffer; -use gtk::{ - glib::{self, clone}, - prelude::*, -}; +use gtk::{gio, glib}; -use gtk::{ - CellRendererText, Dialog, ListStore, TextView, TreeView, TreeViewColumn, WindowPosition, -}; +use gtk::{CellRendererText, Dialog, ListStore, TextView, TreeView, TreeViewColumn}; fn create_and_fill_model(elements: &[ElementInfo]) -> ListStore { // Creation of a model with two rows. @@ -41,6 +37,7 @@ fn create_and_fill_model(elements: &[ElementInfo]) -> ListStore { &[(0, &(i as u32 + 1)), (1, &entry.name.as_ref().unwrap())], ); } + model } @@ -60,37 +57,35 @@ pub fn display_plugin_list(app: &GPSApp, elements: &[ElementInfo]) { .object("dialog-plugin-list") .expect("Couldn't get window"); - dialog.set_title("Plugin list"); - dialog.set_position(WindowPosition::Center); + dialog.set_title(Some("Plugin list")); dialog.set_default_size(640, 480); - let tree: TreeView = app - .builder - .object("treeview-plugin-list") - .expect("Couldn't get window"); - let text_view: TextView = app .builder .object("textview-plugin-list") .expect("Couldn't get window"); - let text_buffer: TextBuffer = text_view - .buffer() - .expect("Couldn't get buffer from text_view"); + let text_buffer: TextBuffer = text_view.buffer(); + + let tree: TreeView = app + .builder + .object("treeview-plugin-list") + .expect("Couldn't get window"); if tree.n_columns() < 2 { append_column(&tree, 0); append_column(&tree, 1); } + tree.set_search_column(1); let model = create_and_fill_model(elements); // Setting the model into the view. tree.set_model(Some(&model)); // The closure responds to selection changes by connection to "::cursor-changed" signal, // that gets emitted when the cursor moves (focus changes). - tree.connect_cursor_changed(clone!(@weak dialog, @weak text_buffer => move |tree_view| { + tree.connect_cursor_changed(glib::clone!(@weak dialog, @weak text_buffer => move |tree_view| { let selection = tree_view.selection(); if let Some((model, iter)) = selection.selected() { let element_name = model - .value(&iter, 1) + .get(&iter, 1) .get::() .expect("Treeview selection, column 1"); let description = Pipeline::element_description(&element_name).expect("Unable to get element list from GStreamer"); @@ -101,7 +96,7 @@ pub fn display_plugin_list(app: &GPSApp, elements: &[ElementInfo]) { })); let app_weak = app.downgrade(); tree.connect_row_activated( - clone!(@weak dialog => move |tree_view, _tree_path, _tree_column| { + glib::clone!(@weak dialog => move |tree_view, _tree_path, _tree_column| { let app = upgrade_weak!(app_weak); let selection = tree_view.selection(); if let Some((model, iter)) = selection.selected() { @@ -110,7 +105,7 @@ pub fn display_plugin_list(app: &GPSApp, elements: &[ElementInfo]) { // let element = Element { name: model - .value(&iter, 1) + .get(&iter, 1) .get::() .expect("Treeview selection, column 1"), position: (100.0,100.0), @@ -118,7 +113,7 @@ pub fn display_plugin_list(app: &GPSApp, elements: &[ElementInfo]) { }; let element_name = model - .value(&iter, 1) + .get(&iter, 1) .get::() .expect("Treeview selection, column 1"); app.add_new_element(element); @@ -128,9 +123,5 @@ pub fn display_plugin_list(app: &GPSApp, elements: &[ElementInfo]) { }), ); - dialog.connect_delete_event(|dialog, _| { - dialog.hide(); - gtk::Inhibit(true) - }); - dialog.show_all(); + dialog.show(); }