From a2503ce86b9525c37f96094983ee7f39b9509cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Cerveau?= Date: Thu, 20 Jan 2022 15:06:14 +0100 Subject: [PATCH] ui: introduce a new module - Cleanup up app.rs and use the new module ui - Remove display_plugin_list - Can now render a graph and get the parse launch line. --- src/app.rs | 262 ++++-------------------------------------- src/gps.ui | 41 +------ src/gps/pipeline.rs | 5 +- src/main.rs | 3 +- src/meson.build | 19 +-- src/plugindialogs.rs | 172 --------------------------- src/{ => ui}/about.rs | 0 src/ui/elements.rs | 160 ++++++++++++++++++++++++++ src/ui/logger.rs | 57 +++++++++ src/ui/message.rs | 70 +++++++++++ src/ui/mod.rs | 24 ++++ src/ui/properties.rs | 88 ++++++++++++++ src/ui/treeview.rs | 35 ++++++ 13 files changed, 476 insertions(+), 460 deletions(-) delete mode 100644 src/plugindialogs.rs rename src/{ => ui}/about.rs (100%) create mode 100644 src/ui/elements.rs create mode 100644 src/ui/logger.rs create mode 100644 src/ui/message.rs create mode 100644 src/ui/mod.rs create mode 100644 src/ui/properties.rs create mode 100644 src/ui/treeview.rs diff --git a/src/app.rs b/src/app.rs index 4aceed9..5488f9c 100644 --- a/src/app.rs +++ b/src/app.rs @@ -21,12 +21,11 @@ use glib::SignalHandlerId; use glib::Value; use gtk::gdk::Rectangle; use gtk::prelude::*; -use gtk::{ - gdk::BUTTON_SECONDARY, Application, ApplicationWindow, Box, Builder, Button, CellRendererText, - FileChooserAction, FileChooserDialog, Label, ListStore, Paned, PopoverMenu, ResponseType, - Statusbar, TreeView, TreeViewColumn, Viewport, Widget, -}; use gtk::{gio, gio::SimpleAction, glib, graphene}; +use gtk::{ + Application, ApplicationWindow, Builder, Button, FileChooserAction, FileChooserDialog, Paned, + PopoverMenu, ResponseType, Statusbar, Viewport, Widget, +}; use log::error; use once_cell::unsync::OnceCell; use std::cell::RefCell; @@ -34,11 +33,11 @@ use std::collections::HashMap; use std::ops; use std::rc::{Rc, Weak}; -use crate::about; use crate::gps::{ElementInfo, PadInfo, Pipeline, PipelineState}; use crate::logger; -use crate::plugindialogs; use crate::settings::Settings; +use crate::ui as GPSUI; + use crate::{GPS_DEBUG, GPS_ERROR, GPS_INFO, GPS_TRACE, GPS_WARN}; use crate::graphmanager::{GraphView, PortDirection, PortPresence}; @@ -209,7 +208,7 @@ impl GPSApp { application.add_action(&gio::SimpleAction::new("favorite.remove", None)); - application.add_action(&gio::SimpleAction::new("graph.add-plugin", None)); + application.add_action(&gio::SimpleAction::new("graph.check", None)); application.add_action(&gio::SimpleAction::new("port.delete", None)); @@ -220,7 +219,12 @@ impl GPSApp { application.add_action(&gio::SimpleAction::new("node.properties", None)); } - fn app_pop_menu_at_position(&self, widget: &impl IsA, x: f64, y: f64) -> PopoverMenu { + pub fn app_pop_menu_at_position( + &self, + widget: &impl IsA, + x: f64, + y: f64, + ) -> PopoverMenu { let mainwindow: ApplicationWindow = self .builder .object("mainwindow") @@ -265,7 +269,7 @@ impl GPSApp { } } - fn connect_app_menu_action< + pub fn connect_app_menu_action< F: Fn(&SimpleAction, std::option::Option<&glib::Variant>) + 'static, >( &self, @@ -331,219 +335,6 @@ impl GPSApp { file_chooser.show(); } - pub fn show_error_dialog(fatal: bool, message: &str) { - let app = gio::Application::default() - .expect("No default application") - .downcast::() - .expect("Default application has wrong type"); - - let dialog = gtk::MessageDialog::new( - app.active_window().as_ref(), - gtk::DialogFlags::MODAL, - gtk::MessageType::Error, - gtk::ButtonsType::Ok, - message, - ); - - dialog.connect_response(move |dialog, _| { - let app = gio::Application::default().expect("No default application"); - - dialog.destroy(); - - if fatal { - app.quit(); - } - }); - - dialog.set_resizable(false); - dialog.show(); - } - fn add_column_to_treeview(&self, tree_name: &str, column_name: &str, column_n: i32) { - let treeview: TreeView = self - .builder - .object(tree_name) - .expect("Couldn't get tree_name"); - let column = TreeViewColumn::new(); - let cell = CellRendererText::new(); - column.pack_start(&cell, true); - // Association of the view's column with the model's `id` column. - column.add_attribute(&cell, "text", column_n); - column.set_title(column_name); - treeview.append_column(&column); - } - - fn reset_logger_list(&self, logger_list: &TreeView) { - let model = ListStore::new(&[ - String::static_type(), - String::static_type(), - String::static_type(), - ]); - logger_list.set_model(Some(&model)); - } - - fn setup_logger_list(&self) { - self.add_column_to_treeview("treeview-logger", "TIME", 0); - self.add_column_to_treeview("treeview-logger", "LEVEL", 1); - self.add_column_to_treeview("treeview-logger", "LOG", 2); - let logger_list: TreeView = self - .builder - .object("treeview-logger") - .expect("Couldn't get treeview-logger"); - self.reset_logger_list(&logger_list); - } - - fn add_to_logger_list(&self, log_entry: &str) { - let logger_list: TreeView = self - .builder - .object("treeview-logger") - .expect("Couldn't get treeview-logger"); - if let Some(model) = logger_list.model() { - let list_store = model - .dynamic_cast::() - .expect("Could not cast to ListStore"); - let log: Vec<&str> = log_entry.splitn(3, ' ').collect(); - list_store.insert_with_values(None, &[(0, &log[0]), (1, &log[1]), (2, &log[2])]); - } - } - - fn reset_favorite_list(&self, favorite_list: &TreeView) { - let model = ListStore::new(&[String::static_type()]); - favorite_list.set_model(Some(&model)); - let favorites = Settings::get_favorites_list(); - for favorite in favorites { - model.insert_with_values(None, &[(0, &favorite)]); - } - } - - fn setup_favorite_list(&self) { - let favorite_list: TreeView = self - .builder - .object("treeview-favorites") - .expect("Couldn't get treeview-favorites"); - self.add_column_to_treeview("treeview-favorites", "Name", 0); - self.reset_favorite_list(&favorite_list); - let app_weak = self.downgrade(); - favorite_list.connect_row_activated(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() { - let element_name = model.get::(&iter, 0); - GPS_DEBUG!("{} selected", element_name); - app.add_new_element(&element_name); - } - }); - let gesture = gtk::GestureClick::new(); - gesture.set_button(0); - let app_weak = self.downgrade(); - gesture.connect_pressed( - glib::clone!(@weak favorite_list => move |gesture, _n_press, x, y| { - let app = upgrade_weak!(app_weak); - if gesture.current_button() == BUTTON_SECONDARY { - let selection = favorite_list.selection(); - if let Some((model, iter)) = selection.selected() { - let element_name = model - .get::(&iter, 0); - GPS_DEBUG!("Element {} selected", element_name); - - let pop_menu = app.app_pop_menu_at_position(&favorite_list, x, y); - let menu: gio::MenuModel = app - .builder - .object("fav_menu") - .expect("Couldn't get fav_menu model"); - pop_menu.set_menu_model(Some(&menu)); - - let app_weak = app.downgrade(); - app.connect_app_menu_action("favorite.remove", - move |_,_| { - let app = upgrade_weak!(app_weak); - Settings::remove_favorite(&element_name); - app.reset_favorite_list(&favorite_list); - } - ); - - pop_menu.show(); - } - - } - }), - ); - favorite_list.add_controller(&gesture); - } - - fn add_to_favorite_list(&self, element_name: String) { - let favorites = Settings::get_favorites_list(); - if !favorites.contains(&element_name) { - let favorite_list: TreeView = self - .builder - .object("treeview-favorites") - .expect("Couldn't get treeview-favorites"); - if let Some(model) = favorite_list.model() { - let list_store = model - .dynamic_cast::() - .expect("Could not cast to ListStore"); - list_store.insert_with_values(None, &[(0, &element_name)]); - Settings::add_favorite(&element_name); - } - } - } - - fn reset_elements_list(&self, elements_list: &TreeView) { - let model = ListStore::new(&[String::static_type()]); - elements_list.set_model(Some(&model)); - let elements = ElementInfo::elements_list().expect("Unable to obtain element's list"); - for element in elements { - model.insert_with_values(None, &[(0, &element.name)]); - } - } - - fn setup_elements_list(&self) { - let tree: TreeView = self - .builder - .object("treeview-elements") - .expect("Couldn't get treeview-elements"); - self.add_column_to_treeview("treeview-elements", "Name", 0); - self.reset_elements_list(&tree); - let app_weak = self.downgrade(); - tree.connect_row_activated(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() { - let element_name = model.get::(&iter, 0); - GPS_DEBUG!("{} selected", element_name); - app.add_new_element(&element_name); - } - }); - let app_weak = self.downgrade(); - tree.connect_cursor_changed(move |tree_view| { - let app = upgrade_weak!(app_weak); - let selection = tree_view.selection(); - if let Some((model, iter)) = selection.selected() { - let element_name = model.get::(&iter, 0); - let description = ElementInfo::element_description(&element_name) - .expect("Unable to get element description from GStreamer"); - let box_property: Box = app - .builder - .object("box-property") - .expect("Couldn't get treeview-elements"); - - while let Some(child) = box_property.first_child() { - box_property.remove(&child); - } - let label = Label::new(Some("")); - label.set_hexpand(true); - label.set_halign(gtk::Align::Start); - label.set_margin_start(4); - label.set_markup(&description); - box_property.append(&label); - } - }); - } - - pub fn display_plugin_list(app: &GPSApp) { - let elements = ElementInfo::elements_list().expect("Unable to obtain element's list"); - plugindialogs::display_plugin_list(app, &elements); - } - pub fn build_ui(&self, application: &Application) { let drawing_area_window: Viewport = self .builder @@ -561,10 +352,10 @@ impl GPSApp { .to_str() .expect("Unable to convert log file path to a string"), ); - self.setup_logger_list(); + GPSUI::logger::setup_logger_list(self); let _ = ready_rx.attach(None, move |msg: String| { let app = upgrade_weak!(app_weak, glib::Continue(false)); - app.add_to_logger_list(&msg); + GPSUI::logger::add_to_logger_list(&app, &msg); glib::Continue(true) }); @@ -627,13 +418,7 @@ impl GPSApp { let app_weak = self.downgrade(); self.connect_app_menu_action("about", move |_, _| { let app = upgrade_weak!(app_weak); - about::display_about_dialog(&app); - }); - - let app_weak = self.downgrade(); - self.connect_button_action("button-add-plugin", move |_| { - let app = upgrade_weak!(app_weak); - GPSApp::display_plugin_list(&app); + GPSUI::about::display_about_dialog(&app); }); let app_weak = self.downgrade(); @@ -721,10 +506,11 @@ impl GPSApp { pop_menu.set_menu_model(Some(&menu)); let app_weak = app.downgrade(); - app.connect_app_menu_action("graph.add-plugin", + app.connect_app_menu_action("graph.check", move |_,_| { let app = upgrade_weak!(app_weak); - GPSApp::display_plugin_list(&app); + let render_parse_launch = app.pipeline.borrow().render_gst_launch(&app.graphview.borrow()); + GPSUI::message::display_message_dialog(&render_parse_launch,gtk::MessageType::Info, |_| {}); } ); pop_menu.show(); @@ -800,7 +586,7 @@ impl GPSApp { let app = upgrade_weak!(app_weak); GPS_DEBUG!("node.add-to-favorite {}", node_id); if let Some(node) = app.graphview.borrow().node(node_id) { - app.add_to_favorite_list(node.name()); + GPSUI::elements::add_to_favorite_list(&app, node.name()); }; } ); @@ -848,7 +634,7 @@ impl GPSApp { let app = upgrade_weak!(app_weak); GPS_DEBUG!("node.properties {}", node_id); let node = app.graphview.borrow().node(node_id).unwrap(); - plugindialogs::display_plugin_properties(&app, &node.name(), node_id); + GPSUI::properties::display_plugin_properties(&app, &node.name(), node_id); } ); @@ -858,9 +644,9 @@ impl GPSApp { ); // Setup the favorite list - self.setup_favorite_list(); + GPSUI::elements::setup_favorite_list(self); // Setup the favorite list - self.setup_elements_list(); + GPSUI::elements::setup_elements_list(self); let _ = self .load_graph( diff --git a/src/gps.ui b/src/gps.ui index c2e8fb3..902dfa3 100644 --- a/src/gps.ui +++ b/src/gps.ui @@ -39,8 +39,8 @@
- _Add plugin - app.graph.add-plugin + _Check graph + app.graph.check
@@ -90,35 +90,6 @@ 1 10 - - mainwindow - 320 - 260 - True - - - 200s - - - True - True - - - - - - - - True - True - - - - - - - - mainwindow 320 @@ -178,14 +149,6 @@ 0 - - - 1 - gtk-add - 1 - list-add - - 1 diff --git a/src/gps/pipeline.rs b/src/gps/pipeline.rs index 9834a70..e28aed2 100644 --- a/src/gps/pipeline.rs +++ b/src/gps/pipeline.rs @@ -16,9 +16,10 @@ // along with this program. If not, see . // // SPDX-License-Identifier: GPL-3.0-only -use crate::app::GPSApp; + use crate::graphmanager::{GraphView, Node, NodeType, PortDirection}; use crate::logger; +use crate::ui::message as GPSMessage; use crate::GPS_INFO; use gst::glib; @@ -156,7 +157,7 @@ impl Pipeline { err.error(), err.debug() ); - GPSApp::show_error_dialog( + GPSMessage::display_error_dialog( false, format!( "Error from {:?}: {} ({:?})", diff --git a/src/main.rs b/src/main.rs index f17bfc4..cc3414c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,15 +18,14 @@ // SPDX-License-Identifier: GPL-3.0-only #[macro_use] mod macros; -mod about; mod app; mod common; mod config; mod graphmanager; +mod ui; #[macro_use] mod logger; mod gps; -mod plugindialogs; mod settings; use gtk::prelude::*; diff --git a/src/meson.build b/src/meson.build index 5ba8b64..8c63db6 100644 --- a/src/meson.build +++ b/src/meson.build @@ -17,24 +17,29 @@ run_command( ) rust_sources = files( + 'gps/pipeline.rs', + 'gps/element.rs', + 'gps/pad.rs', + 'gps/mod.rs', 'graphmanager/graphview.rs', 'graphmanager/link.rs', 'graphmanager/mod.rs', 'graphmanager/node.rs', 'graphmanager/port.rs', - 'about.rs', + 'ui/about.rs', + 'ui/elements.rs', + 'ui/logger.rs', + 'ui/message.rs', + 'ui/mod.rs', + 'ui/properties.rs', + 'ui/treeview.rs', 'app.rs', 'common.rs', 'logger.rs', 'macros.rs', 'main.rs', - 'gps/pipeline.rs', - 'gps/element.rs', - 'gps/pad.rs', - 'gps/mod.rs', - 'plugindialogs.rs', - 'settings.rs', + 'settings.rs', ) sources = [cargo_sources, rust_sources] diff --git a/src/plugindialogs.rs b/src/plugindialogs.rs deleted file mode 100644 index 074df4d..0000000 --- a/src/plugindialogs.rs +++ /dev/null @@ -1,172 +0,0 @@ -// plugindialogs.rs -// -// Copyright 2021 Stéphane Cerveau -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// SPDX-License-Identifier: GPL-3.0-only -use crate::app::GPSApp; -use crate::gps::ElementInfo; -use crate::logger; -use gtk::glib; -use gtk::prelude::*; -use gtk::TextBuffer; -use std::cell::RefCell; -use std::collections::HashMap; -use std::rc::Rc; - -use gtk::{ - Box, Button, CellRendererText, Dialog, Entry, Label, ListStore, TextView, TreeView, - TreeViewColumn, -}; - -fn create_and_fill_model(elements: &[ElementInfo]) -> ListStore { - // Creation of a model with two rows. - let model = ListStore::new(&[u32::static_type(), String::static_type()]); - - // Filling up the tree view. - for (i, entry) in elements.iter().enumerate() { - model.insert_with_values(None, &[(0, &(i as u32 + 1)), (1, &entry.name)]); - } - - model -} - -fn append_column(tree: &TreeView, id: i32) { - let column = TreeViewColumn::new(); - let cell = CellRendererText::new(); - - column.pack_start(&cell, true); - // Association of the view's column with the model's `id` column. - column.add_attribute(&cell, "text", id); - tree.append_column(&column); -} - -pub fn display_plugin_list(app: &GPSApp, elements: &[ElementInfo]) { - let dialog: Dialog = app - .builder - .object("dialog-plugin-list") - .expect("Couldn't get the dialog-plugin-list window"); - - if app.plugin_list_initialized.get().is_none() { - dialog.set_title(Some("Plugin list")); - dialog.set_default_size(640, 480); - - let text_view: TextView = app - .builder - .object("textview-plugin-list") - .expect("Couldn't get textview-plugin-list window"); - let text_buffer: TextBuffer = text_view.buffer(); - - let tree: TreeView = app - .builder - .object("treeview-plugin-list") - .expect("Couldn't get treeview-plugin-list 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(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 - .get::(&iter, 1); - let description = ElementInfo::element_description(&element_name).expect("Unable to get element description from GStreamer"); - text_buffer.set_text(""); - text_buffer.insert_markup(&mut text_buffer.end_iter(), &description); - } - - })); - let app_weak = app.downgrade(); - tree.connect_row_activated( - 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() { - let element_name = model - .get::(&iter, 1); - app.add_new_element(&element_name); - } - }), - ); - app.plugin_list_initialized.set(true).unwrap(); - } - - dialog.show(); -} - -pub fn display_plugin_properties(app: &GPSApp, element_name: &str, node_id: u32) { - let dialog: Dialog = app - .builder - .object("dialog-plugin-properties") - .expect("Couldn't get dialog-plugin-properties"); - - dialog.set_title(Some(&format!("{} properties", element_name))); - dialog.set_default_size(640, 480); - dialog.set_modal(true); - - let properties_box: Box = app - .builder - .object("box-plugin-properties") - .expect("Couldn't get box-plugin-properties"); - let update_properties: Rc>> = - Rc::new(RefCell::new(HashMap::new())); - let properties = ElementInfo::element_properties(element_name).unwrap(); - for (name, value) in properties { - let entry_box = Box::new(gtk::Orientation::Horizontal, 6); - let label = Label::new(Some(&name)); - label.set_hexpand(true); - label.set_halign(gtk::Align::Start); - label.set_margin_start(4); - entry_box.append(&label); - let entry: Entry = Entry::new(); - entry.set_text(&value); - entry.set_hexpand(true); - entry.set_halign(gtk::Align::Start); - entry.set_widget_name(&name); - entry.connect_changed( - glib::clone!(@weak entry, @strong update_properties => move |_| { - GPS_TRACE!("property changed: {}:{}", entry.widget_name(), entry.text()); - update_properties.borrow_mut().insert(entry.widget_name().to_string(), entry.text().to_string()); - }), - ); - entry_box.append(&entry); - properties_box.append(&entry_box); - } - let properties_apply_btn: Button = app - .builder - .object("button-apply-plugin-properties") - .expect("Couldn't get button-apply-plugin-properties"); - - let app_weak = app.downgrade(); - properties_apply_btn.connect_clicked( - glib::clone!(@strong update_properties, @weak dialog => move |_| { - let app = upgrade_weak!(app_weak); - app.update_element_properties(node_id, &update_properties.borrow()); - dialog.close(); - }), - ); - - dialog.show(); - for p in update_properties.borrow().values() { - GPS_TRACE!("updated properties {}", p); - } -} diff --git a/src/about.rs b/src/ui/about.rs similarity index 100% rename from src/about.rs rename to src/ui/about.rs diff --git a/src/ui/elements.rs b/src/ui/elements.rs new file mode 100644 index 0000000..19348f2 --- /dev/null +++ b/src/ui/elements.rs @@ -0,0 +1,160 @@ +// elements.rs +// +// Copyright 2022 Stéphane Cerveau +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: GPL-3.0-only + +use crate::app::GPSApp; +use crate::gps::ElementInfo; +use crate::logger; +use crate::settings::Settings; +use crate::ui::treeview; +use crate::GPS_DEBUG; +use gtk::prelude::*; +use gtk::{gdk::BUTTON_SECONDARY, Box, Label, ListStore, TreeView}; +use gtk::{gio, glib}; + +pub fn reset_favorite_list(favorite_list: &TreeView) { + let model = ListStore::new(&[String::static_type()]); + favorite_list.set_model(Some(&model)); + let favorites = Settings::get_favorites_list(); + for favorite in favorites { + model.insert_with_values(None, &[(0, &favorite)]); + } +} + +pub fn setup_favorite_list(app: &GPSApp) { + let favorite_list: TreeView = app + .builder + .object("treeview-favorites") + .expect("Couldn't get treeview-favorites"); + treeview::add_column_to_treeview(app, "treeview-favorites", "Name", 0); + reset_favorite_list(&favorite_list); + let app_weak = app.downgrade(); + favorite_list.connect_row_activated(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() { + let element_name = model.get::(&iter, 0); + GPS_DEBUG!("{} selected", element_name); + app.add_new_element(&element_name); + } + }); + let gesture = gtk::GestureClick::new(); + gesture.set_button(0); + let app_weak = app.downgrade(); + gesture.connect_pressed( + glib::clone!(@weak favorite_list => move |gesture, _n_press, x, y| { + let app = upgrade_weak!(app_weak); + if gesture.current_button() == BUTTON_SECONDARY { + let selection = favorite_list.selection(); + if let Some((model, iter)) = selection.selected() { + let element_name = model + .get::(&iter, 0); + GPS_DEBUG!("Element {} selected", element_name); + + let pop_menu = app.app_pop_menu_at_position(&favorite_list, x, y); + let menu: gio::MenuModel = app + .builder + .object("fav_menu") + .expect("Couldn't get fav_menu model"); + pop_menu.set_menu_model(Some(&menu)); + + app.connect_app_menu_action("favorite.remove", + move |_,_| { + Settings::remove_favorite(&element_name); + reset_favorite_list(&favorite_list); + } + ); + + pop_menu.show(); + } + + } + }), + ); + favorite_list.add_controller(&gesture); +} + +pub fn add_to_favorite_list(app: &GPSApp, element_name: String) { + let favorites = Settings::get_favorites_list(); + if !favorites.contains(&element_name) { + let favorite_list: TreeView = app + .builder + .object("treeview-favorites") + .expect("Couldn't get treeview-favorites"); + if let Some(model) = favorite_list.model() { + let list_store = model + .dynamic_cast::() + .expect("Could not cast to ListStore"); + list_store.insert_with_values(None, &[(0, &element_name)]); + Settings::add_favorite(&element_name); + } + } +} + +fn reset_elements_list(elements_list: &TreeView) { + let model = ListStore::new(&[String::static_type()]); + elements_list.set_model(Some(&model)); + let elements = ElementInfo::elements_list().expect("Unable to obtain element's list"); + for element in elements { + model.insert_with_values(None, &[(0, &element.name)]); + } +} + +pub fn setup_elements_list(app: &GPSApp) { + let tree: TreeView = app + .builder + .object("treeview-elements") + .expect("Couldn't get treeview-elements"); + treeview::add_column_to_treeview(app, "treeview-elements", "Name", 0); + reset_elements_list(&tree); + let app_weak = app.downgrade(); + tree.connect_row_activated(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() { + let element_name = model.get::(&iter, 0); + GPS_DEBUG!("{} selected", element_name); + app.add_new_element(&element_name); + } + }); + let app_weak = app.downgrade(); + tree.connect_cursor_changed(move |tree_view| { + let app = upgrade_weak!(app_weak); + let selection = tree_view.selection(); + if let Some((model, iter)) = selection.selected() { + let element_name = model.get::(&iter, 0); + let description = ElementInfo::element_description(&element_name) + .expect("Unable to get element description from GStreamer"); + let box_property: Box = app + .builder + .object("box-property") + .expect("Couldn't get treeview-elements"); + + while let Some(child) = box_property.first_child() { + box_property.remove(&child); + } + let label = Label::new(Some("")); + label.set_hexpand(true); + label.set_halign(gtk::Align::Start); + label.set_margin_start(4); + label.set_markup(&description); + label.set_selectable(true); + box_property.append(&label); + } + }); +} diff --git a/src/ui/logger.rs b/src/ui/logger.rs new file mode 100644 index 0000000..d942c2a --- /dev/null +++ b/src/ui/logger.rs @@ -0,0 +1,57 @@ +// logger.rs +// +// Copyright 2022 Stéphane Cerveau +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: GPL-3.0-only +use crate::app::GPSApp; +use crate::ui::treeview; +use gtk::prelude::*; + +use gtk::{ListStore, TreeView}; + +fn reset_logger_list(logger_list: &TreeView) { + let model = ListStore::new(&[ + String::static_type(), + String::static_type(), + String::static_type(), + ]); + logger_list.set_model(Some(&model)); +} + +pub fn setup_logger_list(app: &GPSApp) { + treeview::add_column_to_treeview(app, "treeview-logger", "TIME", 0); + treeview::add_column_to_treeview(app, "treeview-logger", "LEVEL", 1); + treeview::add_column_to_treeview(app, "treeview-logger", "LOG", 2); + let logger_list: TreeView = app + .builder + .object("treeview-logger") + .expect("Couldn't get treeview-logger"); + reset_logger_list(&logger_list); +} + +pub fn add_to_logger_list(app: &GPSApp, log_entry: &str) { + let logger_list: TreeView = app + .builder + .object("treeview-logger") + .expect("Couldn't get treeview-logger"); + if let Some(model) = logger_list.model() { + let list_store = model + .dynamic_cast::() + .expect("Could not cast to ListStore"); + let log: Vec<&str> = log_entry.splitn(3, ' ').collect(); + list_store.insert_with_values(None, &[(0, &log[0]), (1, &log[1]), (2, &log[2])]); + } +} diff --git a/src/ui/message.rs b/src/ui/message.rs new file mode 100644 index 0000000..f9d80ed --- /dev/null +++ b/src/ui/message.rs @@ -0,0 +1,70 @@ +// message.rs +// +// Copyright 2022 Stéphane Cerveau +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: GPL-3.0-only + +use gtk::gio; +use gtk::prelude::*; + +use gtk::{Application, Label, Widget}; + +pub fn display_message_dialog( + message: &str, + message_type: gtk::MessageType, + f: F, +) { + let app = gio::Application::default() + .expect("No default application") + .downcast::() + .expect("Default application has wrong type"); + + let dialog = gtk::MessageDialog::new( + app.active_window().as_ref(), + gtk::DialogFlags::MODAL, + message_type, + gtk::ButtonsType::Ok, + message, + ); + let message_area = dialog.message_area(); + let mut child = message_area.first_child(); + while child.is_some() { + let widget = child.unwrap(); + let label = widget + .dynamic_cast::