From 2f61e34f0f17a35482e784a2aaa2f55b723c1a95 Mon Sep 17 00:00:00 2001 From: Santtu Lakkala Date: Tue, 1 Oct 2024 17:54:05 +0300 Subject: [PATCH 1/3] Add locale page Signed-off-by: Santtu Lakkala --- src/application.rs | 12 +- src/control_panel_gui.gresource.xml | 1 + src/data_provider.rs | 28 +++++ src/language_region_settings_page.rs | 116 +++++++++++++++++ src/main.rs | 1 + src/settings.rs | 23 +++- src/ui/language_region_settings_page.ui | 158 ++++++++++++++++++++++++ src/ui/settings.ui | 17 +-- 8 files changed, 342 insertions(+), 14 deletions(-) create mode 100644 src/language_region_settings_page.rs create mode 100644 src/ui/language_region_settings_page.ui diff --git a/src/application.rs b/src/application.rs index 0608281..c4ef978 100755 --- a/src/application.rs +++ b/src/application.rs @@ -183,7 +183,17 @@ impl ControlPanelGuiApplication { match action { SettingsAction::AddNetwork => todo!(), SettingsAction::RemoveNetwork => todo!(), - SettingsAction::RegionNLanguage => todo!(), + SettingsAction::RegionNLanguage => { + let (locale, timezone) : (String, String) = value.get().unwrap(); + self.imp() + .data_provider + .borrow() + .set_locale(locale); + self.imp() + .data_provider + .borrow() + .set_timezone(timezone); + } SettingsAction::DateNTime => todo!(), SettingsAction::MouseSpeed => todo!(), SettingsAction::KeyboardLayout => todo!(), diff --git a/src/control_panel_gui.gresource.xml b/src/control_panel_gui.gresource.xml index 862ca4b..3279d65 100755 --- a/src/control_panel_gui.gresource.xml +++ b/src/control_panel_gui.gresource.xml @@ -14,6 +14,7 @@ ui/mouse_settings_page.ui ui/admin_settings_page.ui ui/display_settings_page.ui + ui/language_region_settings_page.ui gtk/help-overlay.ui ui/connection_config.ui ui/add_network_popup.ui diff --git a/src/data_provider.rs b/src/data_provider.rs index b54cf21..0c7bac9 100755 --- a/src/data_provider.rs +++ b/src/data_provider.rs @@ -273,6 +273,34 @@ pub mod imp { }); } + pub fn set_locale(&self, locale: String) { + let admin_client = self.admin_client.clone(); + thread::spawn(move || { + Runtime::new().unwrap().block_on(async move { + if let Err(error) = admin_client.read().unwrap().set_locale(locale).await { + println!("Locale request error {error}"); + } + else { + println!("Locale request sent"); + }; + }) + }); + } + + pub fn set_timezone(&self, timezone: String) { + let admin_client = self.admin_client.clone(); + thread::spawn(move || { + Runtime::new().unwrap().block_on(async move { + if let Err(error) = admin_client.read().unwrap().set_timezone(timezone).await { + println!("Timezone request error {error}"); + } + else { + println!("Timezone request sent"); + }; + }) + }); + } + pub fn restart_vm(&self, name: String) { println!("Restart is not implemented on client lib!"); //no restart in admin_client diff --git a/src/language_region_settings_page.rs b/src/language_region_settings_page.rs new file mode 100644 index 0000000..1bcc351 --- /dev/null +++ b/src/language_region_settings_page.rs @@ -0,0 +1,116 @@ +use glib::subclass::Signal; +use glib::Binding; +use gtk::prelude::*; +use gtk::subclass::prelude::*; +use gtk::{glib, CompositeTemplate, DropDown}; +use std::cell::RefCell; +use std::sync::OnceLock; + +use crate::settings_gobject::SettingsGObject; + +//+list of supported resolutions/modes ? + +mod imp { + use super::*; + + #[derive(Default, CompositeTemplate)] + #[template(resource = "/org/gnome/controlpanelgui/ui/language_region_settings_page.ui")] + pub struct LanguageRegionSettingsPage { + #[template_child] + pub language_switch: TemplateChild, + + #[template_child] + pub timezone_switch: TemplateChild, + + // Vector holding the bindings to properties of `Object` + pub bindings: RefCell>, + } + + #[glib::object_subclass] + impl ObjectSubclass for LanguageRegionSettingsPage { + const NAME: &'static str = "LanguageRegionSettingsPage"; + type Type = super::LanguageRegionSettingsPage; + type ParentType = gtk::Box; + + fn class_init(klass: &mut Self::Class) { + klass.bind_template(); + klass.bind_template_callbacks(); + } + + fn instance_init(obj: &glib::subclass::InitializingObject) { + obj.init_template(); + } + } + + #[gtk::template_callbacks] + impl LanguageRegionSettingsPage { + #[template_callback] + fn on_reset_clicked(&self) { + println!("Reset to defaults!"); + self.obj().emit_by_name::<()>("locale-default", &[]); + } + #[template_callback] + fn on_apply_clicked(&self) { + let locale = self.language_switch.selected(); + let timezone = self.timezone_switch.selected(); + println!("Language and timezone changed! {locale}, {timezone}"); + self.obj().emit_by_name::<()>("locale-timezone-changed", &[&locale, &timezone]); + } + } //end #[gtk::template_callbacks] + + impl ObjectImpl for LanguageRegionSettingsPage { + fn constructed(&self) { + // Call "constructed" on parent + self.parent_constructed(); + + // Setup + let obj = self.obj(); + obj.init(); + } + + fn signals() -> &'static [Signal] { + static SIGNALS: OnceLock> = OnceLock::new(); + SIGNALS.get_or_init(|| { + vec![ + Signal::builder("locale-timezone-changed") + .param_types([u32::static_type(), u32::static_type()]) + .build(), + Signal::builder("locale-default").build(), + ] + }) + } + } + impl WidgetImpl for LanguageRegionSettingsPage {} + impl BoxImpl for LanguageRegionSettingsPage {} +} + +glib::wrapper! { +pub struct LanguageRegionSettingsPage(ObjectSubclass) + @extends gtk::Widget, gtk::Box; +} + +impl Default for LanguageRegionSettingsPage { + fn default() -> Self { + Self::new() + } +} + +impl LanguageRegionSettingsPage { + pub fn new() -> Self { + glib::Object::builder().build() + } + pub fn init(&self) {} + + pub fn bind(&self, _settings_object: &SettingsGObject) { + //unbind previous ones + self.unbind(); + //make new + } + + pub fn unbind(&self) { + // Unbind all stored bindings + for binding in self.imp().bindings.borrow_mut().drain(..) { + binding.unbind(); + } + } +} diff --git a/src/main.rs b/src/main.rs index 79bb930..41191c7 100755 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,7 @@ mod info_settings_page; mod security_settings_page; mod wifi_settings_page; mod keyboard_settings_page; +mod language_region_settings_page; mod mouse_settings_page; mod display_settings_page; mod vm_control_action; diff --git a/src/settings.rs b/src/settings.rs index fd58793..6d3c9b4 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -15,6 +15,7 @@ use crate::info_settings_page::InfoSettingsPage; use crate::security_settings_page::SecuritySettingsPage; use crate::wifi_settings_page::WifiSettingsPage; use crate::keyboard_settings_page::KeyboardSettingsPage; +use crate::language_region_settings_page::LanguageRegionSettingsPage; use crate::mouse_settings_page::MouseSettingsPage; use crate::display_settings_page::DisplaySettingsPage; use crate::vm_control_action::VMControlAction; @@ -46,6 +47,8 @@ mod imp { pub audio_settings_page: TemplateChild, #[template_child] pub display_settings_page: TemplateChild, + #[template_child] + pub language_region_settings_page: TemplateChild, //pub vm_model: RefCell, @@ -94,7 +97,25 @@ mod imp { fn on_show_add_new_keyboard_popup(&self) { let action = SettingsAction::ShowAddKeyboardPopup; let empty = Variant::from(None::<()>.as_ref()); - self.obj().emit_by_name::<()>("settings-action", &[&action, &empty]); + self.obj() + .emit_by_name::<()>("settings-action", &[&action, &empty]); + } + + #[template_callback] + fn on_locale_timezone_changed(&self, locale: u32, timezone: u32) { + let action = SettingsAction::RegionNLanguage; + let variant = ( + match locale { + 0 => "en_US.UTF-8", + 1 => "ar_AE.UTF-8", + _ => return, + }, + match timezone { + 0 => "Asia/Abu_Dhabi", + 1 => "Europe/Helsinki", + _ => return }).to_variant(); + self.obj() + .emit_by_name::<()>("settings-action", &[&action, &variant]); } }//end #[gtk::template_callbacks] diff --git a/src/ui/language_region_settings_page.ui b/src/ui/language_region_settings_page.ui new file mode 100644 index 0000000..30ea169 --- /dev/null +++ b/src/ui/language_region_settings_page.ui @@ -0,0 +1,158 @@ + + + + + diff --git a/src/ui/settings.ui b/src/ui/settings.ui index 19a9ee3..c9d72e6 100644 --- a/src/ui/settings.ui +++ b/src/ui/settings.ui @@ -168,7 +168,7 @@ - region + locale Region &amp; Language @@ -292,17 +292,10 @@ - region + locale - - Region & Language - - 10 - 10 - 10 - 10 - start - start + + @@ -312,4 +305,4 @@ - \ No newline at end of file + From 9d0c4db4983ff44dca5ffbea56fa04d8947c8431 Mon Sep 17 00:00:00 2001 From: Santtu Lakkala Date: Fri, 11 Oct 2024 17:50:41 +0300 Subject: [PATCH 2/3] Minor refactoring Signed-off-by: Santtu Lakkala --- Cargo.lock | 1 + Cargo.toml | 1 + src/data_provider.rs | 117 ++++++++++++++++++------------------------- 3 files changed, 52 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33a6af1..ae65604 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -406,6 +406,7 @@ dependencies = [ name = "control_panel_gui" version = "0.1.0" dependencies = [ + "async-channel", "clap", "gettext-rs", "gio 0.20.4", diff --git a/Cargo.toml b/Cargo.toml index eb1c41b..7b97fa5 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ prost = "0.12" tokio = {version = "1.0", features = ["rt-multi-thread", "time", "macros"]} tokio-stream = "0.1" tonic = {version="0.11.0", features = ["tls"]} +async-channel = "2.3" givc-client = { git = "https://github.com/tiiuae/ghaf-givc", branch = "main" } givc-common = { git = "https://github.com/tiiuae/ghaf-givc", branch = "main" } diff --git a/src/data_provider.rs b/src/data_provider.rs index 0c7bac9..a38b18c 100755 --- a/src/data_provider.rs +++ b/src/data_provider.rs @@ -1,8 +1,9 @@ use std::cell::RefCell; +use glib::{IsA, Object}; use gtk::{self, gio, glib, prelude::*}; use gio::ListStore; use std::thread::{self, JoinHandle}; -use std::sync::{Arc, Mutex, RwLock, mpsc::{self, Sender, Receiver}, atomic::{AtomicBool, Ordering}}; +use std::sync::{Arc, Mutex, RwLock, atomic::{AtomicBool, Ordering}}; use tokio::time::{sleep, Duration, timeout}; use tokio::runtime::Runtime; @@ -18,22 +19,37 @@ use crate::{ADMIN_SERVICE_ADDR, ADMIN_SERVICE_PORT}; pub mod imp { use super::*; + trait TypedStore> { + fn get(&self, index: u32) -> Option; + fn typed_iter(&self) -> impl Iterator; + } + + impl> TypedStore for ListStore { + fn get(&self, index: u32) -> Option { + self.item(index).and_then(|item| item.downcast().ok()) + } + + fn typed_iter(&self) -> impl Iterator { + let s = self.clone(); + (0..).map_while(move |idx| s.get(idx)) + } + } + #[derive(Debug)] pub struct DataProvider { - store: Arc>, + store: ListStore, //settings: Arc>,//will be in use in the future pub status: bool, admin_client: Arc>, service_address: RefCell<(String, u16)>, handle: RefCell>>, - stop_signal: Arc, + stop_signal: RefCell>>, } #[derive(Debug)] pub enum EventExtended { InitialList(Vec), InnerEvent(Event), - BreakLoop, } impl DataProvider { @@ -41,13 +57,13 @@ pub mod imp { let init_store = ListStore::new::();//Self::fill_by_mock_data(); Self { - store: Arc::new(Mutex::new(init_store)), + store: init_store, //settings: Arc::new(Mutex::new(SettingsGObject::default())), status: false, admin_client: Arc::new(RwLock::new(AdminClient::new(address.clone(), port, None))), service_address: RefCell::new((address, port)), handle: RefCell::new(None), - stop_signal: Arc::new(AtomicBool::new(false)), + stop_signal: RefCell::new(None), } } @@ -57,10 +73,9 @@ pub mod imp { self.service_address.borrow().1); let admin_client = self.admin_client.clone(); let store = self.store.clone(); - let stop_signal = self.stop_signal.clone(); - self.stop_signal.store(false, Ordering::SeqCst); - let (event_tx, event_rx): (Sender, Receiver) = mpsc::channel(); + let (event_tx, event_rx) = async_channel::unbounded(); + let (quit_tx, mut quit_rx) = async_channel::bounded::<()>(1); let handle = thread::spawn(move || { Runtime::new().unwrap().block_on(async move { @@ -70,89 +85,61 @@ pub mod imp { let timeout_duration = Duration::from_secs(5); let Ok(Ok(result)) = timeout(timeout_duration, admin_client.read().unwrap().watch()).await else {println!("Watch call timeout/error"); return}; + println!("Connected!"); let list = result.initial.clone(); let _ = event_tx.send(EventExtended::InitialList(list)); - while !(stop_signal.load(Ordering::SeqCst)) { - if let Ok(event) = result.channel.try_recv() { - let _ = event_tx.send(EventExtended::InnerEvent(event)); - } else { - println!("Error received from client lib!"); + loop { + tokio::select! { + _ = quit_rx.recv() => break, + event = result.channel.recv() => { + println!("Got message: {event:?}"); + if let Ok(event) = event { + let _ = event_tx.send(EventExtended::InnerEvent(event)).await; + } else { + break; + } + } } - println!("Waiting for data..."); - sleep(Duration::new(2,0)).await; } - - println!("BreakLoop"); - let _ = event_tx.send(EventExtended::BreakLoop); }); }); *self.handle.borrow_mut() = Some(handle); - glib::source::idle_add_local(move || { - while let Ok(event_ext) = event_rx.try_recv() { + let store = self.store.clone(); + glib::spawn_future_local(async move { + while let Ok(event_ext) = event_rx.recv().await { match event_ext { EventExtended::InitialList(list) => { println!("Initial list: {:?}", list); - let store_inner = store.lock().unwrap(); - store_inner.remove_all(); + store.remove_all(); for vm in list { - store_inner.append(&VMGObject::new(vm.name, vm.description, vm.status, vm.trust_level)); + store.append(&VMGObject::new(vm.name, vm.description, vm.status, vm.trust_level)); } }, EventExtended::InnerEvent(event) => match event { Event::UnitStatusChanged(result) => { println!("Status: {:?}", result); - let store_inner = store.lock().unwrap(); - for i in 0..store_inner.n_items() { - if let Some(item) = store_inner.item(i) { - let obj = item.downcast_ref::().unwrap(); - if obj.name() == result.name { - obj.update(result); - break; - } - } + if let Some(obj) = store.typed_iter().find(|obj: &VMGObject| obj.name() == result.name) { + obj.update(result); } - //for some reason func is not found - /*store_inner.find_with_equal_func(|item| { - let obj = item.downcast_ref::().unwrap(); - if obj.name() == result.name { - obj.update(result); - true // Return true if the item is found and updated - } else { - false // Continue searching otherwise - } - });*/ }, Event::UnitShutdown(result) => { println!("Shutdown info: {:?}", result); - let store_inner = store.lock().unwrap(); - for i in 0..store_inner.n_items() { - if let Some(item) = store_inner.item(i) { - let obj = item.downcast_ref::().unwrap(); - if obj.name() == result.name { - obj.update(result); - break; - } - } + if let Some(obj) = store.typed_iter().find(|obj: &VMGObject| obj.name() == result.name) { + obj.update(result); } }, Event::UnitRegistered(result) => { println!("Unit registered {:?}", result); - let store_inner = store.lock().unwrap(); - store_inner.append(&VMGObject::new(result.name, result.description, result.status, result.trust_level)); + store.append(&VMGObject::new(result.name, result.description, result.status, result.trust_level)); }, }, - EventExtended::BreakLoop => { - println!("BreakLoop event received"); - break; - } } } - glib::ControlFlow::Continue }); } @@ -168,10 +155,6 @@ pub mod imp { } pub fn get_store(&self) -> ListStore { - self.store.lock().unwrap().clone() - } - - pub fn get_store_ref(&self) -> Arc> { self.store.clone() } @@ -190,8 +173,7 @@ pub mod imp { } pub fn add_vm(&self, vm: VMGObject) { - let store = self.store.lock().unwrap(); - store.append(&vm); + self.store.append(&vm); } pub fn reconnect(&self, addr: Option<(String, u16)>) { @@ -208,8 +190,9 @@ pub mod imp { pub fn disconnect(&self) { println!("Disconnect request..."); - self.stop_signal.store(true, Ordering::SeqCst); - if let Some(handle) = self.handle.borrow_mut().take() { + self.stop_signal.take(); + // self.stop_signal.store(true, Ordering::SeqCst); + if let Some(handle) = self.handle.take() { handle.join().unwrap(); //drop(self.admin_client.clone()); println!("Client thread stopped!"); From add52b1a24b6dadc8b1014513c847c59a80c4f7b Mon Sep 17 00:00:00 2001 From: Santtu Lakkala Date: Fri, 11 Oct 2024 17:36:40 +0300 Subject: [PATCH 3/3] Massive refactoring Signed-off-by: Santtu Lakkala --- Cargo.toml | 2 +- build.rs | 2 +- src/add_network_popup.rs | 39 ++-- src/admin_settings_page.rs | 25 +-- src/application.rs | 103 ++++++---- src/audio_settings.rs | 58 +++--- src/connection_config.rs | 38 ++-- src/data_provider.rs | 369 +++++++++++++++++++--------------- src/display_settings_page.rs | 35 ++-- src/info_settings_page.rs | 73 +++---- src/keyboard_settings_page.rs | 35 ++-- src/main.rs | 53 ++--- src/mouse_settings_page.rs | 43 ++-- src/security_icon.rs | 2 +- src/security_settings_page.rs | 19 +- src/settings.rs | 58 +++--- src/settings_action.rs | 5 +- src/settings_gobject.rs | 10 +- src/trust_level.rs | 15 +- src/vm_control_action.rs | 5 +- src/vm_gobject.rs | 6 +- src/vm_row.rs | 12 +- src/vm_row_2.rs | 35 ++-- src/vm_settings.rs | 76 ++++--- src/wifi_settings_page.rs | 33 ++- src/window.rs | 86 ++++---- 26 files changed, 654 insertions(+), 583 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7b97fa5..1698fdb 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ gio = { version = "0.20.0", features = ["v2_74"] } clap = { version = "4.4", features = ["derive"] } prost = "0.12" -tokio = {version = "1.0", features = ["rt-multi-thread", "time", "macros"]} +tokio = {version = "1.0", features = ["rt", "time", "macros"]} tokio-stream = "0.1" tonic = {version="0.11.0", features = ["tls"]} async-channel = "2.3" diff --git a/build.rs b/build.rs index 938aecf..d647671 100755 --- a/build.rs +++ b/build.rs @@ -6,4 +6,4 @@ fn main() -> Result<(), Box> { ); Ok(()) -} \ No newline at end of file +} diff --git a/src/add_network_popup.rs b/src/add_network_popup.rs index 7ee6a05..270ba79 100644 --- a/src/add_network_popup.rs +++ b/src/add_network_popup.rs @@ -1,10 +1,10 @@ -use std::cell::RefCell; -use std::sync::OnceLock; +use glib::subclass::Signal; +use glib::Binding; use gtk::prelude::*; use gtk::subclass::prelude::*; -use gtk::{gio, glib, CompositeTemplate, Entry, Button}; -use glib::Binding; -use glib::subclass::Signal; +use gtk::{gio, glib, Button, CompositeTemplate, Entry}; +use std::cell::RefCell; +use std::sync::OnceLock; mod imp { use super::*; @@ -34,8 +34,8 @@ mod imp { type ParentType = gtk::Window; fn class_init(klass: &mut Self::Class) { - klass.bind_template(); - klass.bind_template_callbacks(); + klass.bind_template(); + klass.bind_template_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { @@ -48,20 +48,21 @@ mod imp { #[template_callback] fn on_switch_state_changed(&self, value: bool) -> bool { self.password_entry.set_visibility(value); - false//propagate event futher + false //propagate event futher } #[template_callback] fn on_save_clicked(&self) { let name = self.name_entry.text().to_string(); let sec = self.security_entry.text().to_string(); let passwd = self.password_entry.text().to_string(); - self.obj().emit_by_name::<()>("new-network", &[&name, &sec, &passwd]); + self.obj() + .emit_by_name::<()>("new-network", &[&name, &sec, &passwd]); } #[template_callback] fn on_cancel_clicked(&self) { self.obj().close(); } - }//end #[gtk::template_callbacks] + } //end #[gtk::template_callbacks] impl ObjectImpl for AddNetworkPopup { fn constructed(&self) { @@ -76,11 +77,13 @@ mod imp { fn signals() -> &'static [Signal] { static SIGNALS: OnceLock> = OnceLock::new(); SIGNALS.get_or_init(|| { - vec![ - Signal::builder("new-network") - .param_types([String::static_type(), String::static_type(), String::static_type()]) - .build() - ] + vec![Signal::builder("new-network") + .param_types([ + String::static_type(), + String::static_type(), + String::static_type(), + ]) + .build()] }) } } @@ -105,7 +108,5 @@ impl AddNetworkPopup { glib::Object::builder().build() } - pub fn init(&self) { - - } -} \ No newline at end of file + pub fn init(&self) {} +} diff --git a/src/admin_settings_page.rs b/src/admin_settings_page.rs index 3bb5637..2eb6afd 100644 --- a/src/admin_settings_page.rs +++ b/src/admin_settings_page.rs @@ -1,10 +1,10 @@ -use std::cell::RefCell; -use std::sync::OnceLock; +use glib::subclass::Signal; +use glib::Binding; use gtk::prelude::*; use gtk::subclass::prelude::*; use gtk::{glib, CompositeTemplate, Notebook}; -use glib::Binding; -use glib::subclass::Signal; +use std::cell::RefCell; +use std::sync::OnceLock; use crate::settings_gobject::SettingsGObject; @@ -28,8 +28,8 @@ mod imp { type ParentType = gtk::Box; fn class_init(klass: &mut Self::Class) { - klass.bind_template(); - klass.bind_template_callbacks(); + klass.bind_template(); + klass.bind_template_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { @@ -44,7 +44,7 @@ mod imp { println!("Update clicked!"); self.obj().emit_by_name::<()>("update-request", &[]); } - }//end #[gtk::template_callbacks] + } //end #[gtk::template_callbacks] impl ObjectImpl for AdminSettingsPage { fn constructed(&self) { @@ -58,11 +58,9 @@ mod imp { fn signals() -> &'static [Signal] { static SIGNALS: OnceLock> = OnceLock::new(); SIGNALS.get_or_init(|| { - vec![ - Signal::builder("update-request") + vec![Signal::builder("update-request") //.param_types([u32::static_type()]) - .build(), - ] + .build()] }) } } @@ -85,9 +83,7 @@ impl AdminSettingsPage { pub fn new() -> Self { glib::Object::builder().build() } - pub fn init(&self) { - - } + pub fn init(&self) {} pub fn bind(&self, settings_object: &SettingsGObject) { //unbind previous ones @@ -102,4 +98,3 @@ impl AdminSettingsPage { } } } - diff --git a/src/application.rs b/src/application.rs index c4ef978..74fc1c3 100755 --- a/src/application.rs +++ b/src/application.rs @@ -1,18 +1,17 @@ - -use std::cell::RefCell; -use std::rc::Rc; -use gtk::prelude::*; use adw::subclass::prelude::*; -use gtk::{gio, glib, gdk}; -use gtk::CssProvider; use glib::Variant; +use gtk::prelude::*; +use gtk::CssProvider; +use gtk::{gdk, gio, glib}; +use std::cell::RefCell; +use std::rc::Rc; -use crate::ControlPanelGuiWindow; -use crate::data_provider::imp::DataProvider; +use crate::add_network_popup::AddNetworkPopup; use crate::connection_config::ConnectionConfig; -use crate::vm_control_action::VMControlAction; +use crate::data_provider::imp::DataProvider; use crate::settings_action::SettingsAction; -use crate::add_network_popup::AddNetworkPopup; +use crate::vm_control_action::VMControlAction; +use crate::ControlPanelGuiWindow; mod imp { use super::*; @@ -65,7 +64,11 @@ mod imp { // Ask the window manager/compositor to present the window window.present(); //connect on start - application.imp().data_provider.borrow().establish_connection(); + application + .imp() + .data_provider + .borrow() + .establish_connection(); } } @@ -80,13 +83,21 @@ glib::wrapper! { } impl ControlPanelGuiApplication { - pub fn new(application_id: &str, flags: &gio::ApplicationFlags, address: String, port: u16) -> Self { + pub fn new( + application_id: &str, + flags: &gio::ApplicationFlags, + address: String, + port: u16, + ) -> Self { let app: Self = glib::Object::builder() .property("application-id", application_id) .property("flags", flags) .build(); - app.imp().data_provider.borrow().set_service_address(address, port); + app.imp() + .data_provider + .borrow() + .set_service_address(address, port); app } @@ -117,30 +128,38 @@ impl ControlPanelGuiApplication { let about_action = gio::ActionEntry::builder("about") .activate(move |app: &Self, _, _| app.show_about()) .build(); - self.add_action_entries([reconnect_action, show_config_action, quit_action, about_action]); + self.add_action_entries([ + reconnect_action, + show_config_action, + quit_action, + about_action, + ]); } pub fn show_config(&self) { let window = self.active_window().unwrap(); - let address = self.imp().data_provider.borrow().get_current_service_address(); + let address = self + .imp() + .data_provider + .borrow() + .get_current_service_address(); let config = ConnectionConfig::new(address.0, address.1); config.set_transient_for(Some(&window)); config.set_modal(true); let app = self.clone(); - config.connect_local( - "new-config-applied", - false, - move |values| { - //the value[0] is self - let addr = values[1].get::().unwrap(); - let port = values[2].get::().unwrap(); - println!("New config applied: address {addr}, port {port}"); - app.imp().data_provider.borrow().reconnect(Some((addr, port as u16))); - None - }, - ); + config.connect_local("new-config-applied", false, move |values| { + //the value[0] is self + let addr = values[1].get::().unwrap(); + let port = values[2].get::().unwrap(); + println!("New config applied: address {addr}, port {port}"); + app.imp() + .data_provider + .borrow() + .reconnect(Some((addr, port as u16))); + None + }); config.present(); } @@ -164,7 +183,7 @@ impl ControlPanelGuiApplication { self.imp().data_provider.borrow().reconnect(None); } - pub fn get_store(&self) -> gio::ListStore{ + pub fn get_store(&self) -> gio::ListStore { self.imp().data_provider.borrow().get_store() } @@ -207,23 +226,21 @@ impl ControlPanelGuiApplication { let popup = AddNetworkPopup::new(); popup.set_transient_for(Some(&window)); popup.set_modal(true); - popup.connect_local( - "new-network", - false, - move |values| { - //the value[0] is self - let name = values[1].get::().unwrap(); - let security = values[2].get::().unwrap(); - let password = values[3].get::().unwrap(); - println!("New network: {name}, {security}, {password}"); - app.imp().data_provider.borrow().add_network(name, security, password); - None - }, - ); + popup.connect_local("new-network", false, move |values| { + //the value[0] is self + let name = values[1].get::().unwrap(); + let security = values[2].get::().unwrap(); + let password = values[3].get::().unwrap(); + println!("New network: {name}, {security}, {password}"); + app.imp() + .data_provider + .borrow() + .add_network(name, security, password); + None + }); popup.present(); - }, - SettingsAction::ShowAddKeyboardPopup => { } + SettingsAction::ShowAddKeyboardPopup => {} }; } diff --git a/src/audio_settings.rs b/src/audio_settings.rs index eb40684..a9d1437 100755 --- a/src/audio_settings.rs +++ b/src/audio_settings.rs @@ -1,10 +1,10 @@ -use std::cell::RefCell; -use std::sync::OnceLock; +use glib::subclass::Signal; +use glib::{Binding, Properties}; use gtk::prelude::*; use gtk::subclass::prelude::*; -use gtk::{glib, CompositeTemplate, Box, DropDown, Scale}; -use glib::{Binding, Properties}; -use glib::subclass::Signal; +use gtk::{glib, Box, CompositeTemplate, DropDown, Scale}; +use std::cell::RefCell; +use std::sync::OnceLock; mod imp { use super::*; @@ -40,8 +40,8 @@ mod imp { type ParentType = gtk::Box; fn class_init(klass: &mut Self::Class) { - klass.bind_template(); - klass.bind_template_callbacks(); + klass.bind_template(); + klass.bind_template_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { @@ -67,12 +67,14 @@ mod imp { #[template_callback] fn on_mic_volume_changed(&self, scale: &Scale) { let value = scale.value(); - self.obj().emit_by_name::<()>("mic-volume-changed", &[&value]); + self.obj() + .emit_by_name::<()>("mic-volume-changed", &[&value]); } #[template_callback] fn on_speaker_volume_changed(&self, scale: &Scale) { let value = scale.value(); - self.obj().emit_by_name::<()>("speaker-volume-changed", &[&value]); + self.obj() + .emit_by_name::<()>("speaker-volume-changed", &[&value]); } #[template_callback] fn on_reset_clicked(&self) { @@ -86,15 +88,16 @@ mod imp { let speaker = self.speaker_switch.selected(); let mic_volume = self.mic_volume.value(); let speaker_volume = self.speaker_volume.value(); - self.obj().emit_by_name::<()>("apply-new", &[&mic, &speaker, &mic_volume, &speaker_volume]); + self.obj() + .emit_by_name::<()>("apply-new", &[&mic, &speaker, &mic_volume, &speaker_volume]); } - }//end #[gtk::template_callbacks] + } //end #[gtk::template_callbacks] #[glib::derived_properties] impl ObjectImpl for AudioSettings { fn constructed(&self) { self.parent_constructed(); - + // After the object is constructed, bind the footer visibilty property let obj = self.obj(); obj.bind_property("footer-visible", &self.footer.get(), "visible") @@ -107,23 +110,27 @@ mod imp { SIGNALS.get_or_init(|| { vec![ Signal::builder("mic-changed") - .param_types([u32::static_type()]) - .build(), + .param_types([u32::static_type()]) + .build(), Signal::builder("speaker-changed") - .param_types([u32::static_type()]) - .build(), + .param_types([u32::static_type()]) + .build(), Signal::builder("mic-volume-changed") - .param_types([f64::static_type()]) - .build(), + .param_types([f64::static_type()]) + .build(), Signal::builder("speaker-volume-changed") - .param_types([f64::static_type()]) - .build(), - Signal::builder("set-defaults") - .build(), + .param_types([f64::static_type()]) + .build(), + Signal::builder("set-defaults").build(), Signal::builder("apply-new") - .param_types([u32::static_type(), u32::static_type(), f64::static_type(), f64::static_type()]) - .build(), - ] + .param_types([ + u32::static_type(), + u32::static_type(), + f64::static_type(), + f64::static_type(), + ]) + .build(), + ] }) } } @@ -199,4 +206,3 @@ impl AudioSettings { } */ } - diff --git a/src/connection_config.rs b/src/connection_config.rs index 8582cd6..c6d66ad 100644 --- a/src/connection_config.rs +++ b/src/connection_config.rs @@ -1,10 +1,10 @@ -use std::cell::RefCell; -use std::sync::OnceLock; +use glib::subclass::Signal; +use glib::Binding; use gtk::prelude::*; use gtk::subclass::prelude::*; -use gtk::{gio, glib, CompositeTemplate, Entry, Button}; -use glib::Binding; -use glib::subclass::Signal; +use gtk::{gio, glib, Button, CompositeTemplate, Entry}; +use std::cell::RefCell; +use std::sync::OnceLock; mod imp { use super::*; @@ -32,8 +32,8 @@ mod imp { type ParentType = gtk::Window; fn class_init(klass: &mut Self::Class) { - klass.bind_template(); - klass.bind_template_callbacks(); + klass.bind_template(); + klass.bind_template_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { @@ -46,13 +46,14 @@ mod imp { #[template_callback] fn on_apply_clicked(&self) { let (addr, port) = self.obj().get_config(); - self.obj().emit_by_name::<()>("new-config-applied", &[&addr, &port]); + self.obj() + .emit_by_name::<()>("new-config-applied", &[&addr, &port]); } #[template_callback] fn on_cancel_clicked(&self) { self.obj().close(); } - }//end #[gtk::template_callbacks] + } //end #[gtk::template_callbacks] impl ObjectImpl for ConnectionConfig { fn constructed(&self) { @@ -67,11 +68,9 @@ mod imp { fn signals() -> &'static [Signal] { static SIGNALS: OnceLock> = OnceLock::new(); SIGNALS.get_or_init(|| { - vec![ - Signal::builder("new-config-applied") + vec![Signal::builder("new-config-applied") .param_types([String::static_type(), u32::static_type()]) - .build() - ] + .build()] }) } } @@ -87,7 +86,7 @@ pub struct ConnectionConfig(ObjectSubclass) impl Default for ConnectionConfig { fn default() -> Self { - Self::new("".to_string(),1) + Self::new("".to_string(), 1) } } @@ -95,17 +94,18 @@ impl ConnectionConfig { pub fn new(address: String, port: u16) -> Self { let config_widget: Self = glib::Object::builder().build(); config_widget.imp().address_entry.set_text(address.as_str()); - config_widget.imp().port_entry.set_text(port.to_string().as_str()); + config_widget + .imp() + .port_entry + .set_text(port.to_string().as_str()); config_widget } - pub fn init(&self) { - - } + pub fn init(&self) {} pub fn get_config(&self) -> (String, u32) { let text = self.imp().port_entry.text().to_string(); let port = text.parse::().unwrap_or(0); (String::from(self.imp().address_entry.buffer().text()), port) } -} \ No newline at end of file +} diff --git a/src/data_provider.rs b/src/data_provider.rs index a38b18c..bc4d198 100755 --- a/src/data_provider.rs +++ b/src/data_provider.rs @@ -1,19 +1,21 @@ -use std::cell::RefCell; +use async_channel::Sender; +use gio::ListStore; use glib::{IsA, Object}; use gtk::{self, gio, glib, prelude::*}; -use gio::ListStore; +use std::cell::RefCell; +use std::rc::Rc; +use std::sync::{Arc, RwLock, RwLockReadGuard}; use std::thread::{self, JoinHandle}; -use std::sync::{Arc, Mutex, RwLock, atomic::{AtomicBool, Ordering}}; -use tokio::time::{sleep, Duration, timeout}; -use tokio::runtime::Runtime; +use tokio::runtime::{Builder, Handle}; +use tokio::time::{timeout, Duration}; use givc_client::{self, AdminClient}; -use givc_common::query::{QueryResult, Event, VMStatus, TrustLevel}; +use givc_common::query::{Event, TrustLevel, VMStatus}; //use givc_client::endpoint::{EndpointConfig, TlsConfig}; //use givc_common::types::*; use crate::vm_gobject::VMGObject; -use crate::settings_gobject::SettingsGObject; + use crate::{ADMIN_SERVICE_ADDR, ADMIN_SERVICE_PORT}; pub mod imp { @@ -35,6 +37,15 @@ pub mod imp { } } + type Task = Box< + dyn for<'a> FnOnce( + RwLockReadGuard<'a, AdminClient>, + ) -> std::pin::Pin< + Box> + 'a>, + > + Sync + + Send, + >; + #[derive(Debug)] pub struct DataProvider { store: ListStore, @@ -42,19 +53,24 @@ pub mod imp { pub status: bool, admin_client: Arc>, service_address: RefCell<(String, u16)>, - handle: RefCell>>, - stop_signal: RefCell>>, + handle: Rc>>, + join_handle: RefCell>>, + //task_runner: RefCell>)>>>, + task_runner: Rc>)>>>>, } - #[derive(Debug)] - pub enum EventExtended { - InitialList(Vec), - InnerEvent(Event), + macro_rules! adminclient { + (|$cl:ident| $block:expr) => { + Box::new(move |$cl| { + Box::pin(async move { $block.map_err(|e| e.to_string()) }) + }) + } } + impl DataProvider { pub fn new(address: String, port: u16) -> Self { - let init_store = ListStore::new::();//Self::fill_by_mock_data(); + let init_store = ListStore::new::(); //Self::fill_by_mock_data(); Self { store: init_store, @@ -62,82 +78,101 @@ pub mod imp { status: false, admin_client: Arc::new(RwLock::new(AdminClient::new(address.clone(), port, None))), service_address: RefCell::new((address, port)), - handle: RefCell::new(None), - stop_signal: RefCell::new(None), + join_handle: RefCell::new(None), + handle: Rc::new(RefCell::new(None)), + task_runner: Rc::new(RefCell::new(None)), } } pub fn establish_connection(&self) { - println!("Establishing connection, call watch method... Address: {}:{}", - self.service_address.borrow().0, - self.service_address.borrow().1); + println!( + "Establishing connection, call watch method... Address: {}:{}", + self.service_address.borrow().0, + self.service_address.borrow().1 + ); let admin_client = self.admin_client.clone(); - let store = self.store.clone(); + let _store = self.store.clone(); let (event_tx, event_rx) = async_channel::unbounded(); - let (quit_tx, mut quit_rx) = async_channel::bounded::<()>(1); - - let handle = thread::spawn(move || { - Runtime::new().unwrap().block_on(async move { - //let Ok(result) = admin_client.watch().await else {println!("Watch call error"); return}; - - //Await with timeout - let timeout_duration = Duration::from_secs(5); - let Ok(Ok(result)) = timeout(timeout_duration, admin_client.read().unwrap().watch()).await - else {println!("Watch call timeout/error"); return}; - println!("Connected!"); - - let list = result.initial.clone(); - let _ = event_tx.send(EventExtended::InitialList(list)); - - loop { - tokio::select! { - _ = quit_rx.recv() => break, - event = result.channel.recv() => { - println!("Got message: {event:?}"); - if let Ok(event) = event { - let _ = event_tx.send(EventExtended::InnerEvent(event)).await; - } else { - break; - } - } + let (task_tx, task_rx) = + async_channel::bounded::<(Task, async_channel::Sender>)>(1); + + let joinhandle = thread::spawn(move || { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(async move { + let timeout_duration = Duration::from_secs(5); + let Ok(Ok(result)) = + timeout(timeout_duration, admin_client.read().unwrap().watch()).await + else { + println!("Watch call timeout/error"); + return; + }; + println!("Connected!"); + + let _ = event_tx + .send((result.channel, result.initial, Handle::current().clone())) + .await; + while let Ok((task, resp)) = task_rx.recv().await { + let admin_client = admin_client.read().unwrap(); + let res = task(admin_client).await; + let _ = resp.send(res.map_err(|e| e.to_string())).await; } - } - }); + }); }); - *self.handle.borrow_mut() = Some(handle); - + *self.join_handle.borrow_mut() = Some(joinhandle); let store = self.store.clone(); + let selfhandle = self.handle.clone(); + let taskrunner = self.task_runner.clone(); glib::spawn_future_local(async move { - while let Ok(event_ext) = event_rx.recv().await { - match event_ext { - EventExtended::InitialList(list) => { - println!("Initial list: {:?}", list); - store.remove_all(); - for vm in list { - store.append(&VMGObject::new(vm.name, vm.description, vm.status, vm.trust_level)); + let Ok((channel, initial, handle)) = event_rx.recv().await else { + return; + }; + println!("Got stuff!"); + *selfhandle.borrow_mut() = Some(handle); + *taskrunner.borrow_mut() = Some(task_tx); + store.remove_all(); + for vm in initial { + store.append(&VMGObject::new( + vm.name, + vm.description, + vm.status, + vm.trust_level, + )); + } + + while let Ok(event) = channel.recv().await { + match event { + Event::UnitStatusChanged(result) => { + println!("Status: {:?}", result); + if let Some(obj) = store + .typed_iter() + .find(|obj: &VMGObject| obj.name() == result.name) + { + obj.update(result); + } + } + Event::UnitShutdown(result) => { + println!("Shutdown info: {:?}", result); + if let Some(obj) = store + .typed_iter() + .find(|obj: &VMGObject| obj.name() == result.name) + { + obj.update(result); } - }, - EventExtended::InnerEvent(event) => - match event { - Event::UnitStatusChanged(result) => { - println!("Status: {:?}", result); - if let Some(obj) = store.typed_iter().find(|obj: &VMGObject| obj.name() == result.name) { - obj.update(result); - } - }, - Event::UnitShutdown(result) => { - println!("Shutdown info: {:?}", result); - if let Some(obj) = store.typed_iter().find(|obj: &VMGObject| obj.name() == result.name) { - obj.update(result); - } - }, - Event::UnitRegistered(result) => { - println!("Unit registered {:?}", result); - store.append(&VMGObject::new(result.name, result.description, result.status, result.trust_level)); - }, - }, + } + Event::UnitRegistered(result) => { + println!("Unit registered {:?}", result); + store.append(&VMGObject::new( + result.name, + result.description, + result.status, + result.trust_level, + )); + } } } }); @@ -146,12 +181,26 @@ pub mod imp { fn fill_by_mock_data() -> ListStore { let init_store = ListStore::new::(); let mut vec: Vec = Vec::new(); - vec.push(VMGObject::new("VM1".to_string(), String::from("This is the file.pdf and very very long description"), - VMStatus::Running, TrustLevel::NotSecure)); - vec.push(VMGObject::new("VM2".to_string(), String::from("Google Chrome"), VMStatus::Paused, TrustLevel::Secure)); - vec.push(VMGObject::new("VM3".to_string(), String::from("AppFlowy"), VMStatus::Running, TrustLevel::Secure)); + vec.push(VMGObject::new( + "VM1".to_string(), + String::from("This is the file.pdf and very very long description"), + VMStatus::Running, + TrustLevel::NotSecure, + )); + vec.push(VMGObject::new( + "VM2".to_string(), + String::from("Google Chrome"), + VMStatus::Paused, + TrustLevel::Secure, + )); + vec.push(VMGObject::new( + "VM3".to_string(), + String::from("AppFlowy"), + VMStatus::Running, + TrustLevel::Secure, + )); init_store.extend_from_slice(&vec); - return init_store; + init_store } pub fn get_store(&self) -> ListStore { @@ -162,8 +211,9 @@ pub mod imp { self.service_address.clone().into_inner() } - pub fn set_service_address(&self, addr: String, port: u16) {//only when disconnected/watch stopped - if !(self.admin_client.try_write().is_err()) { + pub fn set_service_address(&self, addr: String, port: u16) { + //only when disconnected/watch stopped + if self.admin_client.try_write().is_ok() { println!("Set service address {addr}:{port}"); let mut service_address = self.service_address.borrow_mut(); *service_address = (addr.clone(), port); @@ -184,113 +234,105 @@ pub mod imp { if let Some((host, port)) = addr { self.set_service_address(host, port); } - + self.establish_connection(); } pub fn disconnect(&self) { println!("Disconnect request..."); - self.stop_signal.take(); - // self.stop_signal.store(true, Ordering::SeqCst); - if let Some(handle) = self.handle.take() { - handle.join().unwrap(); + self.task_runner.take(); + self.handle.take(); + if let Some(joinhandle) = self.join_handle.take() { + joinhandle.join().unwrap(); //drop(self.admin_client.clone()); println!("Client thread stopped!"); } } - pub fn start_vm(&self, name: String) { - let admin_client = self.admin_client.clone(); - thread::spawn(move || {//not sure it is really needed - Runtime::new().unwrap().block_on(async move { - //there is only one name in my disposal - if let Err(error) = admin_client.read().unwrap().start(name.clone(), Some(name), Vec::::new()).await { - println!("Start request error {error}"); - } - else { - println!("Start request sent"); - }; - }) + fn client_cmd) -> () + 'static>(&self, task: Task, cb: F) { + let (res_tx, res_rx) = async_channel::bounded(1); + let Some(tr) = self.task_runner.borrow().as_ref().cloned() else { + cb(Err("Not connected to admin".into())); + return; + }; + glib::spawn_future_local(async move { + // On error res_tx is dropped and res_rx.recv() will fail below + let _ = tr.send((task, res_tx)).await; + let res = match res_rx.recv().await { + Ok(res) => res, + Err(err) => Err(err.to_string()), + }; + cb(res); }); } + + pub fn start_vm(&self, name: String) { + self.client_cmd( + adminclient!(|client| client.start("".to_owned(), Some(name), vec![]).await), + |res| match res { + Ok(_) => println!("Start request sent"), + Err(error) => println!("Start request error {error}"), + }, + ); + } pub fn pause_vm(&self, name: String) { - let admin_client = self.admin_client.clone(); - thread::spawn(move || { - Runtime::new().unwrap().block_on(async move { - if let Err(error) = admin_client.read().unwrap().pause(name).await { - println!("Pause request error {error}"); - } - else { - println!("Pause request sent"); - }; - }) - }); + self.client_cmd( + adminclient!(|client| client.pause(name).await), + |res| match res { + Ok(_) => println!("Pause request sent"), + Err(error) => println!("Pause request error {error}"), + }, + ); } pub fn resume_vm(&self, name: String) { - let admin_client = self.admin_client.clone(); - thread::spawn(move || { - Runtime::new().unwrap().block_on(async move { - if let Err(error) = admin_client.read().unwrap().resume(name).await { - println!("Resume request error {error}"); - } - else { - println!("Resume request sent"); - }; - }) - }); + self.client_cmd( + adminclient!(|client| client.resume(name).await), + |res| match res { + Ok(_) => println!("Resume request sent"), + Err(error) => println!("Resume request error {error}"), + }, + ); } pub fn shutdown_vm(&self, name: String) { - let admin_client = self.admin_client.clone(); - thread::spawn(move || { - Runtime::new().unwrap().block_on(async move { - if let Err(error) = admin_client.read().unwrap().stop(name).await { - println!("Stop request error {error}"); - } - else { - println!("Stop request sent"); - }; - }) - }); + self.client_cmd( + adminclient!(|client| client.stop(name).await), + |res| match res { + Ok(_) => println!("Stop request sent"), + Err(error) => println!("Stop request error {error}"), + }, + ); } - pub fn set_locale(&self, locale: String) { - let admin_client = self.admin_client.clone(); - thread::spawn(move || { - Runtime::new().unwrap().block_on(async move { - if let Err(error) = admin_client.read().unwrap().set_locale(locale).await { - println!("Locale request error {error}"); - } - else { - println!("Locale request sent"); - }; - }) - }); + pub fn restart_vm(&self, _name: String) { + println!("Restart is not implemented on client lib!"); + //no restart in admin_client + //self.admin_client.restart(name); } - pub fn set_timezone(&self, timezone: String) { - let admin_client = self.admin_client.clone(); - thread::spawn(move || { - Runtime::new().unwrap().block_on(async move { - if let Err(error) = admin_client.read().unwrap().set_timezone(timezone).await { - println!("Timezone request error {error}"); - } - else { - println!("Timezone request sent"); - }; - }) - }); + pub fn set_locale(&self, locale: String) { + self.client_cmd( + adminclient!(|client| client.set_locale(locale).await), + |res| match res { + Ok(_) => println!("Locale set"), + Err(e) => println!("Locale setting failed: {e}"), + }, + ); } - pub fn restart_vm(&self, name: String) { - println!("Restart is not implemented on client lib!"); - //no restart in admin_client - //self.admin_client.restart(name); + pub fn set_timezone(&self, timezone: String) { + self.client_cmd( + adminclient!(|client| client.set_timezone(timezone).await), + |res| match res { + Ok(_) => println!("Timezone set"), + Err(e) => println!("Timezone setting failed: {e}"), + }, + ); } - pub fn add_network(&self, name: String, security: String, password: String) { + pub fn add_network(&self, _name: String, _security: String, _password: String) { println!("Not yet implemented!"); } } @@ -308,4 +350,3 @@ pub mod imp { } } } - diff --git a/src/display_settings_page.rs b/src/display_settings_page.rs index d355d49..67688b6 100644 --- a/src/display_settings_page.rs +++ b/src/display_settings_page.rs @@ -1,9 +1,9 @@ -use std::cell::RefCell; +use glib::subclass::Signal; +use glib::Binding; use gtk::prelude::*; use gtk::subclass::prelude::*; -use gtk::{glib, CompositeTemplate, DropDown, CheckButton}; -use glib::Binding; -use glib::subclass::Signal; +use gtk::{glib, CheckButton, CompositeTemplate, DropDown}; +use std::cell::RefCell; use std::sync::OnceLock; use crate::settings_gobject::SettingsGObject; @@ -34,8 +34,8 @@ mod imp { type ParentType = gtk::Box; fn class_init(klass: &mut Self::Class) { - klass.bind_template(); - klass.bind_template_callbacks(); + klass.bind_template(); + klass.bind_template_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { @@ -54,9 +54,10 @@ mod imp { fn on_apply_clicked(&self) { let value = self.resolution_switch.selected(); println!("Resolution changed! {}", value); - self.obj().emit_by_name::<()>("resolution-changed", &[&value]); + self.obj() + .emit_by_name::<()>("resolution-changed", &[&value]); } - }//end #[gtk::template_callbacks] + } //end #[gtk::template_callbacks] impl ObjectImpl for DisplaySettingsPage { fn constructed(&self) { @@ -73,11 +74,10 @@ mod imp { SIGNALS.get_or_init(|| { vec![ Signal::builder("resolution-changed") - .param_types([u32::static_type()]) - .build(), - Signal::builder("resolution-default") - .build(), - ] + .param_types([u32::static_type()]) + .build(), + Signal::builder("resolution-default").build(), + ] }) } } @@ -100,11 +100,9 @@ impl DisplaySettingsPage { pub fn new() -> Self { glib::Object::builder().build() } - pub fn init(&self) { - - } + pub fn init(&self) {} - pub fn bind(&self, settings_object: &SettingsGObject) { + pub fn bind(&self, _settings_object: &SettingsGObject) { //unbind previous ones self.unbind(); //make new @@ -118,7 +116,6 @@ impl DisplaySettingsPage { } //fn set_resolution(&self, index: u32) { - //wlr-randr --output eDP-1 --mode 1920x1200 + //wlr-randr --output eDP-1 --mode 1920x1200 //} } - diff --git a/src/info_settings_page.rs b/src/info_settings_page.rs index 4c3ca4c..9d74939 100644 --- a/src/info_settings_page.rs +++ b/src/info_settings_page.rs @@ -1,17 +1,20 @@ -use std::cell::RefCell; -use std::sync::OnceLock; -use gtk::prelude::*; -use gtk::subclass::prelude::*; -use gtk::{glib, CompositeTemplate, ProgressBar, ListView, NoSelection, SignalListItemFactory, ListItem, CustomFilter, FilterListModel}; +use glib::subclass::Signal; use glib::{Binding, Object}; use gtk::gio::ListStore; -use glib::subclass::Signal; +use gtk::prelude::*; +use gtk::subclass::prelude::*; +use gtk::{ + glib, CompositeTemplate, CustomFilter, FilterListModel, ListItem, ListView, NoSelection, + ProgressBar, SignalListItemFactory, +}; +use std::cell::RefCell; +use std::sync::OnceLock; +use crate::settings_gobject::SettingsGObject; +use crate::vm_control_action::VMControlAction; use crate::vm_gobject::VMGObject; use crate::vm_row_2::VMRow2; -use crate::settings_gobject::SettingsGObject; use givc_common::query::VMStatus; -use crate::vm_control_action::VMControlAction; mod imp { use super::*; @@ -39,8 +42,8 @@ mod imp { type ParentType = gtk::Box; fn class_init(klass: &mut Self::Class) { - klass.bind_template(); - //klass.bind_template_callbacks(); + klass.bind_template(); + //klass.bind_template_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { @@ -60,11 +63,9 @@ mod imp { fn signals() -> &'static [Signal] { static SIGNALS: OnceLock> = OnceLock::new(); SIGNALS.get_or_init(|| { - vec![ - Signal::builder("vm-control-action") + vec![Signal::builder("vm-control-action") .param_types([VMControlAction::static_type(), String::static_type()]) - .build(), - ] + .build()] }) } } @@ -101,14 +102,19 @@ impl InfoSettingsPage { fn setup_vm_rows(&self, model: ListStore) { //Set filter: only running VM's - let filter_model = FilterListModel::new(Some(model), Some(CustomFilter::new(|item: &Object| { - if let Some(vm_obj) = item.downcast_ref::() { - if (vm_obj.name().starts_with("microvm@")) && (vm_obj.status() == (VMStatus::Running as u8)) { - return true; + let filter_model = FilterListModel::new( + Some(model), + Some(CustomFilter::new(|item: &Object| { + if let Some(vm_obj) = item.downcast_ref::() { + if (vm_obj.name().starts_with("microvm@")) + && (vm_obj.status() == (VMStatus::Running as u8)) + { + return true; + } } - } - false - }))); + false + })), + ); // Wrap model with no selection and pass it to the list view let selection_model = NoSelection::new(Some(filter_model)); @@ -118,7 +124,7 @@ impl InfoSettingsPage { fn setup_factory(&self) { // Create a new factory let factory = SignalListItemFactory::new(); - + let this = self.clone(); // Create an empty `VMRow2` during setup factory.connect_setup(move |_, list_item| { @@ -126,18 +132,14 @@ impl InfoSettingsPage { let vm_row = VMRow2::new(); //connect signals let widget = this.clone(); - vm_row.connect_local( - "vm-control-action", - false, - move |values| { - //the value[0] is self - let vm_action = values[1].get::().unwrap(); - let vm_name = values[2].get::().unwrap(); - widget.emit_by_name::<()>("vm-control-action", &[&vm_action, &vm_name]); - None - }, - ); - + vm_row.connect_local("vm-control-action", false, move |values| { + //the value[0] is self + let vm_action = values[1].get::().unwrap(); + let vm_name = values[2].get::().unwrap(); + widget.emit_by_name::<()>("vm-control-action", &[&vm_action, &vm_name]); + None + }); + list_item .downcast_ref::() .expect("Needs to be ListItem") @@ -182,7 +184,7 @@ impl InfoSettingsPage { self.imp().vm_list_view.set_factory(Some(&factory)); } - pub fn bind(&self, settings_object: &SettingsGObject) { + pub fn bind(&self, _settings_object: &SettingsGObject) { //unbind previous ones self.unbind(); //make new @@ -195,4 +197,3 @@ impl InfoSettingsPage { } } } - diff --git a/src/keyboard_settings_page.rs b/src/keyboard_settings_page.rs index 0c8f111..4f35fc0 100644 --- a/src/keyboard_settings_page.rs +++ b/src/keyboard_settings_page.rs @@ -1,10 +1,10 @@ -use std::cell::RefCell; -use std::sync::OnceLock; +use glib::subclass::Signal; +use glib::Binding; use gtk::prelude::*; use gtk::subclass::prelude::*; -use gtk::{glib, CompositeTemplate, ListView, DropDown}; -use glib::Binding; -use glib::subclass::Signal; +use gtk::{glib, CompositeTemplate, DropDown, ListView}; +use std::cell::RefCell; +use std::sync::OnceLock; use crate::settings_gobject::SettingsGObject; @@ -30,8 +30,8 @@ mod imp { type ParentType = gtk::Box; fn class_init(klass: &mut Self::Class) { - klass.bind_template(); - klass.bind_template_callbacks(); + klass.bind_template(); + klass.bind_template_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { @@ -44,9 +44,10 @@ mod imp { #[template_callback] fn on_add_clicked(&self) { println!("Add new keyboard!"); - self.obj().emit_by_name::<()>("show-add-new-keyboard-popup", &[]); + self.obj() + .emit_by_name::<()>("show-add-new-keyboard-popup", &[]); } - }//end #[gtk::template_callbacks] + } //end #[gtk::template_callbacks] impl ObjectImpl for KeyboardSettingsPage { fn constructed(&self) { @@ -62,12 +63,11 @@ mod imp { static SIGNALS: OnceLock> = OnceLock::new(); SIGNALS.get_or_init(|| { vec![ - Signal::builder("show-add-new-keyboard-popup") - .build(), + Signal::builder("show-add-new-keyboard-popup").build(), Signal::builder("remove-keyboard") - .param_types([u32::static_type()]) - .build(), - ] + .param_types([u32::static_type()]) + .build(), + ] }) } } @@ -90,11 +90,9 @@ impl KeyboardSettingsPage { pub fn new() -> Self { glib::Object::builder().build() } - pub fn init(&self) { + pub fn init(&self) {} - } - - pub fn bind(&self, settings_object: &SettingsGObject) { + pub fn bind(&self, _settings_object: &SettingsGObject) { //unbind previous ones self.unbind(); //make new @@ -107,4 +105,3 @@ impl KeyboardSettingsPage { } } } - diff --git a/src/main.rs b/src/main.rs index 41191c7..9c8ea2e 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,35 +1,34 @@ - +mod add_network_popup; +mod admin_settings_page; mod application; -mod window; -mod connection_config; -mod vm_row; -mod vm_row_2; -mod vm_gobject; -mod vm_settings; mod audio_settings; -mod settings; -mod settings_gobject; +mod connection_config; mod data_provider; -mod security_icon; -mod admin_settings_page; +mod display_settings_page; mod info_settings_page; -mod security_settings_page; -mod wifi_settings_page; mod keyboard_settings_page; mod language_region_settings_page; mod mouse_settings_page; -mod display_settings_page; -mod vm_control_action; -mod trust_level; -mod add_network_popup; +mod security_icon; +mod security_settings_page; +mod settings; mod settings_action; +mod settings_gobject; +mod trust_level; +mod vm_control_action; +mod vm_gobject; +mod vm_row; +mod vm_row_2; +mod vm_settings; +mod wifi_settings_page; +mod window; use self::application::ControlPanelGuiApplication; use self::window::ControlPanelGuiWindow; use clap::Parser; -use gtk::{gio, glib}; use gtk::prelude::*; +use gtk::{gio, glib}; const ADMIN_SERVICE_ADDR: &str = "192.168.101.10"; const ADMIN_SERVICE_PORT: u16 = 9001; @@ -44,7 +43,8 @@ struct Args { port: Option, } -fn main() /*-> glib::ExitCode*/ { +fn main() /*-> glib::ExitCode*/ +{ //std::env::set_var("RUST_BACKTRACE", "full"); // Parse the command-line arguments @@ -52,16 +52,14 @@ fn main() /*-> glib::ExitCode*/ { let addr = if let Some(addr) = args.addr { addr - } - else { + } else { String::from(ADMIN_SERVICE_ADDR) }; let port = if let Some(port) = args.port { port - } - else { - ADMIN_SERVICE_PORT + } else { + ADMIN_SERVICE_PORT }; // Load resources @@ -71,7 +69,12 @@ fn main() /*-> glib::ExitCode*/ { // Create a new GtkApplication. The application manages our main loop, // application windows, integration with the window manager/compositor, and // desktop features such as file opening and single-instance applications. - let app = ControlPanelGuiApplication::new("org.gnome.controlpanelgui", &gio::ApplicationFlags::empty(), addr, port); + let app = ControlPanelGuiApplication::new( + "org.gnome.controlpanelgui", + &gio::ApplicationFlags::empty(), + addr, + port, + ); // Run the application. This function will block until the application // exits. Upon return, we have our exit code to return to the shell. (This diff --git a/src/mouse_settings_page.rs b/src/mouse_settings_page.rs index 45d7de2..f57438d 100644 --- a/src/mouse_settings_page.rs +++ b/src/mouse_settings_page.rs @@ -1,10 +1,10 @@ -use std::cell::RefCell; -use std::sync::OnceLock; +use glib::subclass::Signal; +use glib::Binding; use gtk::prelude::*; use gtk::subclass::prelude::*; -use gtk::{glib, CompositeTemplate, Switch, Scale}; -use glib::Binding; -use glib::subclass::Signal; +use gtk::{glib, CompositeTemplate, Scale, Switch}; +use std::cell::RefCell; +use std::sync::OnceLock; use crate::settings_gobject::SettingsGObject; @@ -30,8 +30,8 @@ mod imp { type ParentType = gtk::Box; fn class_init(klass: &mut Self::Class) { - klass.bind_template(); - klass.bind_template_callbacks(); + klass.bind_template(); + klass.bind_template_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { @@ -45,15 +45,17 @@ mod imp { fn on_mouse_speed_changed(&self, scale: &Scale) { let value = scale.value(); println!("Mouse speed changed: {}", value); - self.obj().emit_by_name::<()>("mouse-speed-changed", &[&value]); + self.obj() + .emit_by_name::<()>("mouse-speed-changed", &[&value]); } - #[template_callback] + #[template_callback] fn on_button_switch_state_changed(&self, value: bool) -> bool { println!("Mouse buttons switched: {}", value); - self.obj().emit_by_name::<()>("mouse-buttons-changed", &[&value]); - false//propagate event futher + self.obj() + .emit_by_name::<()>("mouse-buttons-changed", &[&value]); + false //propagate event futher } - }//end #[gtk::template_callbacks] + } //end #[gtk::template_callbacks] impl ObjectImpl for MouseSettingsPage { fn constructed(&self) { @@ -70,12 +72,12 @@ mod imp { SIGNALS.get_or_init(|| { vec![ Signal::builder("mouse-speed-changed") - .param_types([f64::static_type()]) - .build(), + .param_types([f64::static_type()]) + .build(), Signal::builder("mouse-buttons-changed") - .param_types([bool::static_type()]) - .build(), - ] + .param_types([bool::static_type()]) + .build(), + ] }) } } @@ -98,11 +100,9 @@ impl MouseSettingsPage { pub fn new() -> Self { glib::Object::builder().build() } - pub fn init(&self) { + pub fn init(&self) {} - } - - pub fn bind(&self, settings_object: &SettingsGObject) { + pub fn bind(&self, _settings_object: &SettingsGObject) { //unbind previous ones self.unbind(); //make new @@ -115,4 +115,3 @@ impl MouseSettingsPage { } } } - diff --git a/src/security_icon.rs b/src/security_icon.rs index b8ebf60..4fa9adb 100755 --- a/src/security_icon.rs +++ b/src/security_icon.rs @@ -14,4 +14,4 @@ impl SecurityIcon { _ => SecurityIcon::OK, } } -} \ No newline at end of file +} diff --git a/src/security_settings_page.rs b/src/security_settings_page.rs index 3ca9556..0f4bcc8 100644 --- a/src/security_settings_page.rs +++ b/src/security_settings_page.rs @@ -1,8 +1,8 @@ -use std::cell::RefCell; +use glib::Binding; use gtk::prelude::*; use gtk::subclass::prelude::*; -use gtk::{glib, CompositeTemplate, Button}; -use glib::Binding; +use gtk::{glib, Button, CompositeTemplate}; +use std::cell::RefCell; use crate::settings_gobject::SettingsGObject; @@ -28,8 +28,8 @@ mod imp { type ParentType = gtk::Box; fn class_init(klass: &mut Self::Class) { - klass.bind_template(); - //klass.bind_template_callbacks(); + klass.bind_template(); + //klass.bind_template_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { @@ -41,7 +41,7 @@ mod imp { impl SecuritySettingsPage { #[template_callback] fn on_row_selected(&self, row: >k::ListBoxRow) { - + } }*///end #[gtk::template_callbacks] @@ -74,11 +74,9 @@ impl SecuritySettingsPage { pub fn new() -> Self { glib::Object::builder().build() } - pub fn init(&self) { - - } + pub fn init(&self) {} - pub fn bind(&self, settings_object: &SettingsGObject) { + pub fn bind(&self, _settings_object: &SettingsGObject) { //unbind previous ones self.unbind(); //make new @@ -91,4 +89,3 @@ impl SecuritySettingsPage { } } } - diff --git a/src/settings.rs b/src/settings.rs index 6d3c9b4..b2a580a 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,25 +1,25 @@ -use std::cell::RefCell; -use std::sync::OnceLock; -use gtk::prelude::*; -use gtk::subclass::prelude::*; -use gtk::{glib, CompositeTemplate, Stack, ListBox}; +use glib::subclass::Signal; use glib::{Binding, Variant}; use gtk::gio::ListStore; -use glib::subclass::Signal; +use gtk::prelude::*; +use gtk::subclass::prelude::*; +use gtk::{glib, CompositeTemplate, ListBox, Stack}; +use std::cell::RefCell; +use std::sync::OnceLock; //use crate::vm_gobject::VMGObject; will be used in the future -use crate::audio_settings::AudioSettings; -use crate::settings_gobject::SettingsGObject; use crate::admin_settings_page::AdminSettingsPage; +use crate::audio_settings::AudioSettings; +use crate::display_settings_page::DisplaySettingsPage; use crate::info_settings_page::InfoSettingsPage; -use crate::security_settings_page::SecuritySettingsPage; -use crate::wifi_settings_page::WifiSettingsPage; use crate::keyboard_settings_page::KeyboardSettingsPage; use crate::language_region_settings_page::LanguageRegionSettingsPage; use crate::mouse_settings_page::MouseSettingsPage; -use crate::display_settings_page::DisplaySettingsPage; -use crate::vm_control_action::VMControlAction; +use crate::security_settings_page::SecuritySettingsPage; use crate::settings_action::SettingsAction; +use crate::settings_gobject::SettingsGObject; +use crate::vm_control_action::VMControlAction; +use crate::wifi_settings_page::WifiSettingsPage; mod imp { use super::*; @@ -63,8 +63,8 @@ mod imp { type ParentType = gtk::Box; fn class_init(klass: &mut Self::Class) { - klass.bind_template(); - klass.bind_template_callbacks(); + klass.bind_template(); + klass.bind_template_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { @@ -90,8 +90,9 @@ mod imp { #[template_callback] fn on_show_add_network_popup(&self) { let action = SettingsAction::ShowAddNetworkPopup; - let empty = Variant::from(None::<()>.as_ref()); - self.obj().emit_by_name::<()>("settings-action", &[&action, &empty]); + let empty = Variant::from(None::<&()>); + self.obj() + .emit_by_name::<()>("settings-action", &[&action, &empty]); } #[template_callback] fn on_show_add_new_keyboard_popup(&self) { @@ -117,7 +118,7 @@ mod imp { self.obj() .emit_by_name::<()>("settings-action", &[&action, &variant]); } - }//end #[gtk::template_callbacks] + } //end #[gtk::template_callbacks] impl ObjectImpl for Settings { fn constructed(&self) { @@ -133,12 +134,12 @@ mod imp { SIGNALS.get_or_init(|| { vec![ Signal::builder("vm-control-action") - .param_types([VMControlAction::static_type(), String::static_type()]) - .build(), + .param_types([VMControlAction::static_type(), String::static_type()]) + .build(), Signal::builder("settings-action") - .param_types([SettingsAction::static_type(), Variant::static_type()]) - .build(), - ] + .param_types([SettingsAction::static_type(), Variant::static_type()]) + .build(), + ] }) } } @@ -167,24 +168,22 @@ impl Settings { } pub fn init(&self) { let this = self.clone(); - self.imp().info_settings_page.connect_local( - "vm-control-action", - false, - move |values| { + self.imp() + .info_settings_page + .connect_local("vm-control-action", false, move |values| { //the value[0] is self let vm_action = values[1].get::().unwrap(); let vm_name = values[2].get::().unwrap(); this.emit_by_name::<()>("vm-control-action", &[&vm_action, &vm_name]); None - }, - ); + }); if let Some(row) = self.imp().settings_list_box.row_at_index(0) { self.imp().settings_list_box.select_row(Some(&row)); } } - pub fn bind(&self, settings_object: &SettingsGObject) { + pub fn bind(&self, _settings_object: &SettingsGObject) { //unbind previous ones self.unbind(); //make new @@ -197,4 +196,3 @@ impl Settings { } } } - diff --git a/src/settings_action.rs b/src/settings_action.rs index 7109c47..3a02361 100644 --- a/src/settings_action.rs +++ b/src/settings_action.rs @@ -1,6 +1,9 @@ +use glib::{ + value::{FromValue, ToValue, Value}, + Type, +}; use gtk::glib; use gtk::prelude::*; -use glib::{value::{FromValue, ToValue, Value}, Type}; #[derive(Debug, Clone, Copy)] #[repr(u8)] diff --git a/src/settings_gobject.rs b/src/settings_gobject.rs index 138f633..a4ffca3 100644 --- a/src/settings_gobject.rs +++ b/src/settings_gobject.rs @@ -1,8 +1,7 @@ -use std::cell::RefCell; use gtk::glib::{self, Object, Properties}; use gtk::prelude::*; use gtk::subclass::prelude::*; -use gtk::glib::subclass::prelude::*; +use std::cell::RefCell; pub mod imp { use super::*; @@ -12,8 +11,8 @@ pub mod imp { pub memory_usage: u32, pub cpu_load: u32, pub network_load: u32, - pub date: u32,//? - pub time: i64,//in sec + pub date: u32, //? + pub time: i64, //in sec pub wifi_on: bool, pub wifi_name: String, } @@ -54,6 +53,7 @@ impl Default for SettingsGObject { impl SettingsGObject { pub fn new() -> Self { - Object::builder()/*properties*/.build() + Object::builder() /*properties*/ + .build() } } diff --git a/src/trust_level.rs b/src/trust_level.rs index 906007d..057d327 100644 --- a/src/trust_level.rs +++ b/src/trust_level.rs @@ -1,31 +1,26 @@ -use gtk::glib; use glib::prelude::*; -use glib::{value::{FromValue, ToValue, Value}, Type, Enum}; +use glib::Enum; +use gtk::glib; //use strum::EnumString; // Derive necessary traits for automatic conversion #[derive(Debug, Clone, Copy, Enum)] #[enum_type(name = "TrustLevel")] #[repr(u8)] // Optional: Ensure each variant has a specific discriminant value +#[derive(Default)] pub enum TrustLevel { + #[default] Secure = 0, Warning = 1, NotSecure = 2, } -impl Default for TrustLevel { - fn default() -> Self { - TrustLevel::Secure - } -} - /*impl StaticType for TrustLevel { fn static_type() -> Type { u8::static_type() } }*/ - /*unsafe impl<'a> FromValue<'a> for TrustLevel { type Checker = glib::value::GenericValueTypeChecker; @@ -48,4 +43,4 @@ impl ToValue for TrustLevel { fn value_type(&self) -> Type { u8::static_type() } -}*/ \ No newline at end of file +}*/ diff --git a/src/vm_control_action.rs b/src/vm_control_action.rs index 449b45f..be8cc60 100644 --- a/src/vm_control_action.rs +++ b/src/vm_control_action.rs @@ -1,6 +1,9 @@ +use glib::{ + value::{FromValue, ToValue, Value}, + Type, +}; use gtk::glib; use gtk::prelude::*; -use glib::{value::{FromValue, ToValue, Value}, Type}; #[derive(Debug, Clone, Copy)] #[repr(u8)] diff --git a/src/vm_gobject.rs b/src/vm_gobject.rs index f6865af..8f5e32c 100755 --- a/src/vm_gobject.rs +++ b/src/vm_gobject.rs @@ -1,10 +1,10 @@ -use std::cell::RefCell; use gtk::glib::{self, Object, Properties}; use gtk::prelude::*; use gtk::subclass::prelude::*; +use std::cell::RefCell; -use givc_common::query::{QueryResult, VMStatus, TrustLevel}; //cannot be used as property! -//use crate::trust_level::TrustLevel as MyTrustLevel;//type is no recognised in #property +use givc_common::query::{QueryResult, TrustLevel, VMStatus}; //cannot be used as property! + //use crate::trust_level::TrustLevel as MyTrustLevel;//type is no recognised in #property mod imp { use super::*; diff --git a/src/vm_row.rs b/src/vm_row.rs index b62ee7c..ea7df3c 100755 --- a/src/vm_row.rs +++ b/src/vm_row.rs @@ -1,11 +1,11 @@ -use std::cell::RefCell; +use glib::Binding; use gtk::prelude::*; use gtk::subclass::prelude::*; use gtk::{glib, CompositeTemplate}; -use glib::Binding; +use std::cell::RefCell; -use crate::vm_gobject::VMGObject; use crate::security_icon::SecurityIcon; +use crate::vm_gobject::VMGObject; mod imp { use super::*; @@ -35,8 +35,8 @@ mod imp { type ParentType = gtk::Box; fn class_init(klass: &mut Self::Class) { - klass.bind_template(); - } + klass.bind_template(); + } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); @@ -71,7 +71,6 @@ impl VMRow { let security_icon = self.imp().security_icon.get(); let mut bindings = self.imp().bindings.borrow_mut(); - let title_binding = vm_object .bind_property("name", &title, "label") //.bidirectional() @@ -125,4 +124,3 @@ impl VMRow { } } } - diff --git a/src/vm_row_2.rs b/src/vm_row_2.rs index 1e4404d..fea01e6 100755 --- a/src/vm_row_2.rs +++ b/src/vm_row_2.rs @@ -1,13 +1,13 @@ -use std::cell::RefCell; -use std::sync::OnceLock; +use glib::subclass::Signal; +use glib::Binding; use gtk::prelude::*; use gtk::subclass::prelude::*; use gtk::{glib, CompositeTemplate, Label, MenuButton, Popover}; -use glib::Binding; -use glib::subclass::Signal; +use std::cell::RefCell; +use std::sync::OnceLock; -use crate::vm_gobject::VMGObject; use crate::vm_control_action::VMControlAction; +use crate::vm_gobject::VMGObject; mod imp { use super::*; @@ -35,9 +35,9 @@ mod imp { type ParentType = gtk::Box; fn class_init(klass: &mut Self::Class) { - klass.bind_template(); - klass.bind_template_callbacks(); - } + klass.bind_template(); + klass.bind_template_callbacks(); + } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); @@ -50,33 +50,34 @@ mod imp { fn on_vm_restart_clicked(&self) { let vm_name = self.title_label.label(); //emit signal - self.obj().emit_by_name::<()>("vm-control-action", &[&VMControlAction::Restart, &vm_name]); + self.obj() + .emit_by_name::<()>("vm-control-action", &[&VMControlAction::Restart, &vm_name]); //and close menu self.popover_menu.popdown(); } #[template_callback] fn on_vm_shutdown_clicked(&self) { let vm_name = self.title_label.label(); - self.obj().emit_by_name::<()>("vm-control-action", &[&VMControlAction::Shutdown, &vm_name]); + self.obj() + .emit_by_name::<()>("vm-control-action", &[&VMControlAction::Shutdown, &vm_name]); self.popover_menu.popdown(); } #[template_callback] fn on_vm_pause_clicked(&self) { let vm_name = self.title_label.label(); - self.obj().emit_by_name::<()>("vm-control-action", &[&VMControlAction::Pause, &vm_name]); + self.obj() + .emit_by_name::<()>("vm-control-action", &[&VMControlAction::Pause, &vm_name]); self.popover_menu.popdown(); } - }//end #[gtk::template_callbacks] + } //end #[gtk::template_callbacks] impl ObjectImpl for VMRow2 { fn signals() -> &'static [Signal] { static SIGNALS: OnceLock> = OnceLock::new(); SIGNALS.get_or_init(|| { - vec![ - Signal::builder("vm-control-action") + vec![Signal::builder("vm-control-action") .param_types([VMControlAction::static_type(), String::static_type()]) - .build(), - ] + .build()] }) } } @@ -106,7 +107,6 @@ impl VMRow2 { let subtitle = self.imp().subtitle_label.get(); let mut bindings = self.imp().bindings.borrow_mut(); - let title_binding = vm_object .bind_property("name", &title, "label") //.bidirectional() @@ -149,4 +149,3 @@ impl VMRow2 { } } } - diff --git a/src/vm_settings.rs b/src/vm_settings.rs index c16313f..14efeb5 100644 --- a/src/vm_settings.rs +++ b/src/vm_settings.rs @@ -1,15 +1,15 @@ -use std::cell::RefCell; -use std::sync::OnceLock; +use glib::subclass::Signal; +use glib::Binding; use gtk::prelude::*; use gtk::subclass::prelude::*; -use gtk::{glib, CompositeTemplate, Label, Image, MenuButton, Popover}; -use glib::Binding; -use glib::subclass::Signal; +use gtk::{glib, CompositeTemplate, Image, Label, MenuButton, Popover}; +use std::cell::RefCell; +use std::sync::OnceLock; -use crate::vm_gobject::VMGObject; use crate::audio_settings::AudioSettings; use crate::security_icon::SecurityIcon; use crate::vm_control_action::VMControlAction; +use crate::vm_gobject::VMGObject; mod imp { use super::*; @@ -52,8 +52,8 @@ mod imp { type ParentType = gtk::Box; fn class_init(klass: &mut Self::Class) { - klass.bind_template(); - klass.bind_template_callbacks(); + klass.bind_template(); + klass.bind_template_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { @@ -67,20 +67,23 @@ mod imp { fn on_vm_start_clicked(&self) { let vm_name = self.vm_name_label.label(); //emit signal - self.obj().emit_by_name::<()>("vm-control-action", &[&VMControlAction::Start, &vm_name]); + self.obj() + .emit_by_name::<()>("vm-control-action", &[&VMControlAction::Start, &vm_name]); //and close menu self.popover_menu.popdown(); } #[template_callback] fn on_vm_shutdown_clicked(&self) { let vm_name = self.vm_name_label.label(); - self.obj().emit_by_name::<()>("vm-control-action", &[&VMControlAction::Shutdown, &vm_name]); + self.obj() + .emit_by_name::<()>("vm-control-action", &[&VMControlAction::Shutdown, &vm_name]); self.popover_menu.popdown(); } #[template_callback] fn on_vm_pause_clicked(&self) { let vm_name = self.vm_name_label.label(); - self.obj().emit_by_name::<()>("vm-control-action", &[&VMControlAction::Pause, &vm_name]); + self.obj() + .emit_by_name::<()>("vm-control-action", &[&VMControlAction::Pause, &vm_name]); self.popover_menu.popdown(); } #[template_callback] @@ -100,7 +103,7 @@ mod imp { println!("Speaker volume: {}", value); //send message to client mod via channel in DataProvider } - }//end #[gtk::template_callbacks] + } //end #[gtk::template_callbacks] impl ObjectImpl for VMSettings { fn signals() -> &'static [Signal] { @@ -108,21 +111,21 @@ mod imp { SIGNALS.get_or_init(|| { vec![ Signal::builder("vm-control-action") - .param_types([VMControlAction::static_type(), String::static_type()]) - .build(), + .param_types([VMControlAction::static_type(), String::static_type()]) + .build(), Signal::builder("vm-mic-changed") - .param_types([u32::static_type()]) - .build(), + .param_types([u32::static_type()]) + .build(), Signal::builder("vm-speaker-changed") - .param_types([u32::static_type()]) - .build(), + .param_types([u32::static_type()]) + .build(), Signal::builder("vm-mic-volume-changed") - .param_types([f64::static_type()]) - .build(), + .param_types([f64::static_type()]) + .build(), Signal::builder("vm-speaker-volume-changed") - .param_types([f64::static_type()]) - .build() - ] + .param_types([f64::static_type()]) + .build(), + ] }) } } @@ -164,7 +167,6 @@ impl VMSettings { let security_label = self.imp().security_label.get(); let mut bindings = self.imp().bindings.borrow_mut(); - let name_binding = vm_object .bind_property("name", &name, "label") //.bidirectional() @@ -178,7 +180,8 @@ impl VMSettings { .sync_create() .transform_to(move |_, value: &glib::Value| { let status = value.get::().unwrap_or(0); - match status {//make struct like for icon? + match status { + //make struct like for icon? 0 => Some(glib::Value::from("Running")), 1 => Some(glib::Value::from("Powered off")), 2 => Some(glib::Value::from("Paused")), @@ -194,11 +197,20 @@ impl VMSettings { .sync_create() .transform_to(move |_, value: &glib::Value| { let status = value.get::().unwrap_or(0); - match status {//make struct like for icon? - 0 => Some(glib::Value::from("/org/gnome/controlpanelgui/icons/ellipse_green.svg")), - 1 => Some(glib::Value::from("/org/gnome/controlpanelgui/icons/ellipse_red.svg")), - 2 => Some(glib::Value::from("/org/gnome/controlpanelgui/icons/ellipse_yellow.svg")), - _ => Some(glib::Value::from("/org/gnome/controlpanelgui/icons/ellipse_red.svg")), + match status { + //make struct like for icon? + 0 => Some(glib::Value::from( + "/org/gnome/controlpanelgui/icons/ellipse_green.svg", + )), + 1 => Some(glib::Value::from( + "/org/gnome/controlpanelgui/icons/ellipse_red.svg", + )), + 2 => Some(glib::Value::from( + "/org/gnome/controlpanelgui/icons/ellipse_yellow.svg", + )), + _ => Some(glib::Value::from( + "/org/gnome/controlpanelgui/icons/ellipse_red.svg", + )), } }) .build(); @@ -228,7 +240,8 @@ impl VMSettings { .sync_create() .transform_to(move |_, value: &glib::Value| { let trust_level = value.get::().unwrap_or(0); - match trust_level {//make struct like for icon? + match trust_level { + //make struct like for icon? 0 => Some(glib::Value::from("Secure!")), 1 => Some(glib::Value::from("Security warning!")), 2 => Some(glib::Value::from("Security alert!")), @@ -247,4 +260,3 @@ impl VMSettings { } } } - diff --git a/src/wifi_settings_page.rs b/src/wifi_settings_page.rs index bb9a1e3..d0af388 100644 --- a/src/wifi_settings_page.rs +++ b/src/wifi_settings_page.rs @@ -1,10 +1,10 @@ -use std::cell::RefCell; -use std::sync::OnceLock; +use crate::glib::subclass::Signal; +use glib::Binding; use gtk::prelude::*; use gtk::subclass::prelude::*; -use gtk::{glib, CompositeTemplate, Switch, Image}; -use glib::Binding; -use crate::glib::subclass::Signal; +use gtk::{glib, CompositeTemplate, Image, Switch}; +use std::cell::RefCell; +use std::sync::OnceLock; //use gtk::gio::ListStore; will be needed for list of available networks use crate::settings_gobject::SettingsGObject; @@ -35,8 +35,8 @@ mod imp { type ParentType = gtk::Box; fn class_init(klass: &mut Self::Class) { - klass.bind_template(); - klass.bind_template_callbacks(); + klass.bind_template(); + klass.bind_template_callbacks(); } fn instance_init(obj: &glib::subclass::InitializingObject) { @@ -49,13 +49,13 @@ mod imp { #[template_callback] fn on_switch_state_changed(&self, value: bool) -> bool { println!("Wifi switched: {}", value); - false//propagate event futher + false //propagate event futher } #[template_callback] fn on_add_clicked(&self) { self.obj().emit_by_name::<()>("show-add-network-popup", &[]); } - }//end #[gtk::template_callbacks] + } //end #[gtk::template_callbacks] impl ObjectImpl for WifiSettingsPage { fn constructed(&self) { @@ -68,12 +68,7 @@ mod imp { } fn signals() -> &'static [Signal] { static SIGNALS: OnceLock> = OnceLock::new(); - SIGNALS.get_or_init(|| { - vec![ - Signal::builder("show-add-network-popup") - .build(), - ] - }) + SIGNALS.get_or_init(|| vec![Signal::builder("show-add-network-popup").build()]) } } impl WidgetImpl for WifiSettingsPage {} @@ -95,11 +90,9 @@ impl WifiSettingsPage { pub fn new() -> Self { glib::Object::builder().build() } - pub fn init(&self) { + pub fn init(&self) {} - } - - pub fn bind(&self, settings_object: &SettingsGObject) { + pub fn bind(&self, _settings_object: &SettingsGObject) { //unbind previous ones self.unbind(); //make new @@ -111,4 +104,4 @@ impl WifiSettingsPage { binding.unbind(); } } -} \ No newline at end of file +} diff --git a/src/window.rs b/src/window.rs index 8d0b663..2f6948c 100755 --- a/src/window.rs +++ b/src/window.rs @@ -1,16 +1,19 @@ -use std::rc::Rc; -use gtk::prelude::*; use adw::subclass::prelude::*; -use gtk::{gio, glib, CompositeTemplate, Stack, Image, Button, ToggleButton, MenuButton, ListView, SingleSelection, CustomFilter, SignalListItemFactory, FilterListModel, ListItem,}; -use glib::{Binding, Object, Variant}; +use glib::{Object, Variant}; +use gtk::prelude::*; +use gtk::{ + gio, glib, CompositeTemplate, CustomFilter, FilterListModel, Image, ListItem, ListView, + MenuButton, SignalListItemFactory, SingleSelection, Stack, ToggleButton, +}; +use std::rc::Rc; use crate::application::ControlPanelGuiApplication; +use crate::settings::Settings; +use crate::settings_action::SettingsAction; +use crate::vm_control_action::VMControlAction; use crate::vm_gobject::VMGObject; use crate::vm_row::VMRow; use crate::vm_settings::VMSettings; -use crate::settings::Settings; -use crate::vm_control_action::VMControlAction; -use crate::settings_action::SettingsAction; mod imp { use super::*; @@ -67,19 +70,19 @@ mod imp { impl ControlPanelGuiWindow { #[template_callback] fn switch_to_vm_view(&self) { - if (self.stack.visible_child_name() != Some("vm_view".into())) { + if self.stack.visible_child_name() != Some("vm_view".into()) { self.stack.set_visible_child_name("vm_view"); } } #[template_callback] fn switch_to_services_view(&self) { - if (self.stack.visible_child_name() != Some("vm_view".into())) { + if self.stack.visible_child_name() != Some("vm_view".into()) { self.stack.set_visible_child_name("vm_view"); } } #[template_callback] fn switch_to_settings_view(&self) { - if (self.stack.visible_child_name() != Some("settings_view".into())) { + if self.stack.visible_child_name() != Some("settings_view".into()) { self.stack.set_visible_child_name("settings_view"); } } @@ -94,7 +97,7 @@ mod imp { let app = self.obj().get_app_ref(); app.perform_setting_action(action, value); } - }//end #[gtk::template_callbacks] + } //end #[gtk::template_callbacks] impl ObjectImpl for ControlPanelGuiWindow { fn constructed(&self) { @@ -132,7 +135,7 @@ impl ControlPanelGuiWindow { self.set_destroy_with_parent(true); self.connect_close_request(glib::clone!(@strong self as window => move |_| { - println!("Close window request"); + println!("Close window request"); let app = window.get_app_ref(); app.clean_n_quit(); glib::Propagation::Stop // Returning Stop allows the window to be destroyed @@ -150,14 +153,18 @@ impl ControlPanelGuiWindow { #[inline(always)] fn get_app_ref(&self) -> Rc { - let binding = self.application().expect("Failed to get application"); - binding.downcast_ref::().expect("ControlPanelGuiApplication is expected!").clone().into() + let binding = self.application().expect("Failed to get application"); + binding + .downcast_ref::() + .expect("ControlPanelGuiApplication is expected!") + .clone() + .into() } fn setup_vm_rows(&self) { let app = self.get_app_ref(); - let model = app.get_store();//ListStore doc: "GLib type: GObject with reference counted clone semantics." + let model = app.get_store(); //ListStore doc: "GLib type: GObject with reference counted clone semantics." self.imp().settings_box.set_vm_model(model.clone()); @@ -191,26 +198,26 @@ impl ControlPanelGuiWindow { let title: Option = vm_obj.property("name"); let subtitle: Option = vm_obj.property("details"); println!("Property {}, {}", title.unwrap(), subtitle.unwrap()); - window.set_vm_details(&vm_obj); + window.set_vm_details(vm_obj); } } else { println!("No item selected"); } - }) + }), ); selection_model.connect_items_changed( glib::clone!(@strong self as window => move |selection_model, position, removed, added| { println!("Items changed at position {}, removed: {}, added: {}", position, removed, added); if let Some(selected_item) = selection_model.selected_item() { if let Some(vm_obj) = selected_item.downcast_ref::() { - window.set_vm_details(&vm_obj); + window.set_vm_details(vm_obj); } } else { println!("No item selected"); } }) ); - + self.imp().vm_list_view.set_model(Some(&selection_model)); self.bind_vm_settings_box_visibility(); @@ -218,7 +225,12 @@ impl ControlPanelGuiWindow { //bind filter change let filter_model_clone = filter_model.clone(); let selection_model_clone = selection_model.clone(); - let count = self.imp().vm_list_view.model().expect("no model!").n_items(); + let count = self + .imp() + .vm_list_view + .model() + .expect("no model!") + .n_items(); self.imp().vm_view_button.connect_toggled(move |button| { if button.is_active() { println!("Filter is about to change to vm"); @@ -226,18 +238,22 @@ impl ControlPanelGuiWindow { Self::set_default_selection(&selection_model_clone, count); } }); - self.imp().services_view_button.connect_toggled(move |button| { - if button.is_active() { - println!("Filter is about to change to services"); - filter_model.set_filter(Some(&services_filter)); - Self::set_default_selection(&selection_model, count); - } - }); + self.imp() + .services_view_button + .connect_toggled(move |button| { + if button.is_active() { + println!("Filter is about to change to services"); + filter_model.set_filter(Some(&services_filter)); + Self::set_default_selection(&selection_model, count); + } + }); } fn set_default_selection(selection_model: &SingleSelection, count: u32) { println!("Selection is about to change"); - if (count <= 0) {return}; + if count <= 0 { + return; + }; selection_model.set_selected(0); selection_model.selection_changed(0u32, count); } @@ -247,13 +263,13 @@ impl ControlPanelGuiWindow { let vm_settings_box = imp.vm_settings_box.clone().upcast::(); if let Some(model) = imp.vm_list_view.model() { model - .bind_property("n_items", &vm_settings_box, "visible") - .sync_create() - .transform_to(move |_, value: &glib::Value| { - let count = value.get::().unwrap_or(0); - Some(glib::Value::from(count != 0)) - }) - .build(); + .bind_property("n_items", &vm_settings_box, "visible") + .sync_create() + .transform_to(move |_, value: &glib::Value| { + let count = value.get::().unwrap_or(0); + Some(glib::Value::from(count != 0)) + }) + .build(); } }