From a9e075843532030c20dd496e067b700d0fc232ae Mon Sep 17 00:00:00 2001 From: takotori Date: Sun, 17 Mar 2024 14:56:48 +0100 Subject: [PATCH] add example plugin should be working now add plugins to sidebar add plugin system base Create function signatures for plugins Update crates --- Cargo.toml | 17 +- better_test_plugin/Cargo.toml | 13 + better_test_plugin/src/lib.rs | 84 ++++ .../audio/input/output_stream_entry.rs | 3 +- .../audio/input/source_box_handlers.rs | 3 +- .../audio/output/input_stream_entry.rs | 3 +- .../audio/output/sink_box_handlers.rs | 3 +- src/components/base/card_entry.rs | 3 +- src/components/base/list_entry.rs | 3 +- src/components/base/setting_box.rs | 5 +- src/components/base/utils.rs | 1 + src/components/bluetooth/bluetooth_box.rs | 4 +- src/components/mod.rs | 1 + src/components/plugin/function.rs | 30 ++ src/components/plugin/mod.rs | 1 + src/components/utils.rs | 3 +- src/components/wifi/saved_wifi_entry.rs | 3 +- src/components/wifi/wifi_box.rs | 4 +- src/components/wifi/wifi_entry.rs | 3 +- src/components/wifi/wifi_event_handlers.rs | 3 +- src/components/wifi/wifi_options.rs | 3 +- src/components/window/handle_sidebar_click.rs | 57 --- src/components/window/reset_window.rs | 465 +++++++++--------- src/components/window/reset_window_impl.rs | 2 +- src/components/window/sidebar_entry.rs | 72 ++- src/components/window/sidebar_entry_impl.rs | 12 +- src/main.rs | 2 +- 27 files changed, 468 insertions(+), 335 deletions(-) create mode 100644 better_test_plugin/Cargo.toml create mode 100644 better_test_plugin/src/lib.rs create mode 100644 src/components/plugin/function.rs create mode 100644 src/components/plugin/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 65e2c1e..aed336b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,13 +7,14 @@ repository = "https://github.com/Xetibo/ReSet" license = "GPL-3.0-only" [dependencies] -reset_daemon = "1.0.1" -re_set-lib = "1.0.0" -adw = { version = "0.5.3", package = "libadwaita", features = ["v1_4"] } +#reset_daemon = "1.1.0" +re_set-lib = { git = "https://github.com/Xetibo/ReSet-Lib" } +reset_daemon = { git = "https://github.com/Xetibo/ReSet-Daemon", branch = "dashie" } +adw = { version = "0.6.0", package = "libadwaita", features = ["v1_4"] } dbus = "0.9.7" -gtk = { version = "0.7.3", package = "gtk4", features = ["v4_12"] } -glib = "0.18.3" -tokio = { version = "1.33.0", features = [ +gtk = { version = "0.8.1", package = "gtk4", features = ["v4_12"] } +glib = "0.19.3" +tokio = { version = "1.36.0", features = [ "rt", "time", "net", @@ -21,8 +22,8 @@ tokio = { version = "1.33.0", features = [ "rt-multi-thread", "sync", ] } -fork = "0.1.22" +fork = "0.1.23" ipnetwork = "0.20.0" [build-dependencies] -glib-build-tools = "0.18.0" +glib-build-tools = "0.19.0" diff --git a/better_test_plugin/Cargo.toml b/better_test_plugin/Cargo.toml new file mode 100644 index 0000000..cb1a105 --- /dev/null +++ b/better_test_plugin/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "better_test_plugin" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["dylib"] + +[dependencies] +re_set-lib = { git = "https://github.com/Xetibo/ReSet-Lib" } +gtk = { version = "0.8.1", package = "gtk4", features = ["v4_12"] } +dbus = "0.9.7" +glib = "0.19.3" diff --git a/better_test_plugin/src/lib.rs b/better_test_plugin/src/lib.rs new file mode 100644 index 0000000..c820d9b --- /dev/null +++ b/better_test_plugin/src/lib.rs @@ -0,0 +1,84 @@ +use std::sync::Arc; +use std::time::Duration; + +use dbus::blocking::Connection; +use dbus::Error; +use gtk::{gio, Orientation}; +use gtk::prelude::{BoxExt, ButtonExt}; +use re_set_lib::utils::plugin::{PluginCapabilities, PluginImplementation, PluginTestFunc, SidebarInfo}; + +pub const BASE: &str = "org.Xetibo.ReSet.Daemon"; +pub const DBUS_PATH: &str = "/org/Xebito/ReSet/Plugins/test"; +pub const INTERFACE: &str = "org.Xetibo.ReSet.TestPlugin"; + +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub extern "C" fn capabilities() -> PluginCapabilities { + println!("frontend capabilities called"); + PluginCapabilities::new(vec!["frontend test"], PluginImplementation::Frontend) +} + +#[no_mangle] +pub extern "C" fn frontend_startup() { + println!("frontend startup called"); +} + +#[no_mangle] +pub extern "C" fn frontend_shutdown() { + println!("frontend shutdown called"); +} + +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub extern "C" fn frontend_data() -> (SidebarInfo, Vec) { + println!("frontend data called"); + let info = SidebarInfo { + name: "test", + icon_name: "microphone-disabled-symbolic", + parent: None, + }; + let box1 = gtk::Box::builder().orientation(Orientation::Vertical).build(); + let box2 = gtk::Box::builder().orientation(Orientation::Horizontal).build(); + + let label = Arc::new(LabelWrapper { + label: gtk::Label::builder().label("Hello, World!").build(), + }); + + let label2 = gtk::Label::builder().label("Bye, World!").build(); + let button = gtk::Button::builder().label("Click me!").build(); + box1.append(&label.label); + box2.append(&label2); + box2.append(&button); + + button.connect_clicked(move |_| { + let label = Arc::clone(&label); + gio::spawn_blocking(move || { + let conn = Connection::new_session().unwrap(); + let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); + let res: Result<(String, u32), Error> = proxy.method_call(INTERFACE, "Test", ()); + let (text, age) = res.unwrap(); + label.label.set_text(&format!("Name: {}, Age: {}", text, age)); + }); + }); + + let boxes = vec![ + box1, box2, + ]; + + (info, boxes) +} + +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub extern "C" fn frontend_tests() -> Vec { + println!("frontend tests called"); + vec![] +} + +pub struct LabelWrapper { + label: gtk::Label, +} + +unsafe impl Send for LabelWrapper {} + +unsafe impl Sync for LabelWrapper {} \ No newline at end of file diff --git a/src/components/audio/input/output_stream_entry.rs b/src/components/audio/input/output_stream_entry.rs index deefc99..4cb848c 100644 --- a/src/components/audio/input/output_stream_entry.rs +++ b/src/components/audio/input/output_stream_entry.rs @@ -10,7 +10,8 @@ use adw::prelude::{ButtonExt, ComboRowExt, PreferencesRowExt, RangeExt}; use dbus::blocking::Connection; use dbus::Error; use glib::subclass::types::ObjectSubclassIsExt; -use glib::{clone, Cast, Propagation}; +use glib::{clone, Propagation}; +use glib::prelude::Cast; use gtk::{gio, StringObject}; use re_set_lib::audio::audio_structures::OutputStream; diff --git a/src/components/audio/input/source_box_handlers.rs b/src/components/audio/input/source_box_handlers.rs index 939385f..233108b 100644 --- a/src/components/audio/input/source_box_handlers.rs +++ b/src/components/audio/input/source_box_handlers.rs @@ -4,7 +4,8 @@ use std::{ }; use adw::prelude::{ComboRowExt, PreferencesRowExt}; -use glib::{subclass::types::ObjectSubclassIsExt, Cast, ControlFlow, Propagation}; +use glib::{subclass::types::ObjectSubclassIsExt, ControlFlow, Propagation}; +use glib::prelude::Cast; use gtk::{ gio, prelude::{BoxExt, ButtonExt, CheckButtonExt, ListBoxRowExt, RangeExt}, diff --git a/src/components/audio/output/input_stream_entry.rs b/src/components/audio/output/input_stream_entry.rs index 350b268..c66b26a 100644 --- a/src/components/audio/output/input_stream_entry.rs +++ b/src/components/audio/output/input_stream_entry.rs @@ -10,7 +10,8 @@ use adw::prelude::{ButtonExt, ComboRowExt, PreferencesRowExt, RangeExt}; use dbus::blocking::Connection; use dbus::Error; use glib::subclass::types::ObjectSubclassIsExt; -use glib::{clone, Cast, Propagation}; +use glib::{clone, Propagation}; +use glib::prelude::Cast; use gtk::{gio, StringObject}; use re_set_lib::audio::audio_structures::InputStream; diff --git a/src/components/audio/output/sink_box_handlers.rs b/src/components/audio/output/sink_box_handlers.rs index 8039cd0..ea75d03 100644 --- a/src/components/audio/output/sink_box_handlers.rs +++ b/src/components/audio/output/sink_box_handlers.rs @@ -7,7 +7,8 @@ use adw::{ prelude::{ComboRowExt, PreferencesRowExt}, ComboRow, }; -use glib::{subclass::types::ObjectSubclassIsExt, Cast, Propagation}; +use glib::{subclass::types::ObjectSubclassIsExt, Propagation}; +use glib::prelude::Cast; use gtk::{ gio, prelude::{BoxExt, ButtonExt, CheckButtonExt, ListBoxRowExt, RangeExt}, diff --git a/src/components/base/card_entry.rs b/src/components/base/card_entry.rs index 150006e..d3f9308 100644 --- a/src/components/base/card_entry.rs +++ b/src/components/base/card_entry.rs @@ -5,7 +5,8 @@ use adw::prelude::{ComboRowExt, PreferencesRowExt}; use dbus::blocking::Connection; use dbus::Error; use glib::subclass::types::ObjectSubclassIsExt; -use glib::{clone, Cast}; +use glib::{clone}; +use glib::prelude::Cast; use gtk::{gio, StringList, StringObject}; use components::utils::create_dropdown_label_factory; diff --git a/src/components/base/list_entry.rs b/src/components/base/list_entry.rs index 27ed7c9..88e4a87 100644 --- a/src/components/base/list_entry.rs +++ b/src/components/base/list_entry.rs @@ -1,5 +1,6 @@ use crate::components::base::list_entry_impl; -use adw::glib::{IsA, Object}; +use adw::glib::{Object}; +use glib::prelude::IsA; use gtk::prelude::ListBoxRowExt; use gtk::Widget; diff --git a/src/components/base/setting_box.rs b/src/components/base/setting_box.rs index c6ca3d6..2afff96 100644 --- a/src/components/base/setting_box.rs +++ b/src/components/base/setting_box.rs @@ -1,11 +1,12 @@ use crate::components::base::setting_box_impl; -use adw::glib::{IsA, Object}; +use adw::glib::{Object}; +use glib::prelude::IsA; use gtk::prelude::BoxExt; use gtk::Widget; glib::wrapper! { pub struct SettingBox(ObjectSubclass) - @extends gtk::Box, gtk::Widget, + @extends gtk::Box, Widget, @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget; } diff --git a/src/components/base/utils.rs b/src/components/base/utils.rs index 1d20e76..3846aff 100644 --- a/src/components/base/utils.rs +++ b/src/components/base/utils.rs @@ -24,6 +24,7 @@ pub enum Position { Audio, AudioOutput, AudioInput, + Custom(String), #[default] Home, } diff --git a/src/components/bluetooth/bluetooth_box.rs b/src/components/bluetooth/bluetooth_box.rs index e83a817..0ff6ee9 100644 --- a/src/components/bluetooth/bluetooth_box.rs +++ b/src/components/bluetooth/bluetooth_box.rs @@ -8,7 +8,9 @@ use adw::subclass::prelude::ObjectSubclassIsExt; use dbus::blocking::Connection; use dbus::message::SignalArgs; use dbus::{Error, Path}; -use glib::{clone, Cast, ControlFlow, PropertySet}; +use glib::{clone, ControlFlow}; +use glib::prelude::Cast; +use glib::property::PropertySet; use gtk::glib::Variant; use gtk::prelude::{ActionableExt, ButtonExt, ListBoxRowExt, WidgetExt}; use gtk::{gio, StringObject}; diff --git a/src/components/mod.rs b/src/components/mod.rs index 6cd0ff9..ad9703a 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -4,3 +4,4 @@ pub mod bluetooth; pub mod utils; pub mod wifi; pub mod window; +mod plugin; diff --git a/src/components/plugin/function.rs b/src/components/plugin/function.rs new file mode 100644 index 0000000..556edaa --- /dev/null +++ b/src/components/plugin/function.rs @@ -0,0 +1,30 @@ +use std::cell::RefCell; +use std::rc::Rc; +use std::sync::Arc; + +use gtk::FlowBox; +use re_set_lib::utils::plugin::SidebarInfo; + +use crate::components::base::utils::{Listeners, Position}; + +extern "C" { + pub fn startup() -> SidebarInfo; + pub fn shutdown(); + pub fn run_test(); +} + +pub struct ReSetSidebarInfo { + pub name: &'static str, + pub icon_name: &'static str, + pub parent: Option<&'static str>, + pub click_event: fn(Arc, FlowBox, Rc>), +} + +#[repr(C)] +pub struct PluginSidebarInfo { + pub name: &'static str, + pub icon_name: &'static str, + pub parent: Option<&'static str>, + pub click_event: Rc>, Vec)>, + pub plugin_boxes: Vec, +} \ No newline at end of file diff --git a/src/components/plugin/mod.rs b/src/components/plugin/mod.rs new file mode 100644 index 0000000..7fd349b --- /dev/null +++ b/src/components/plugin/mod.rs @@ -0,0 +1 @@ +pub mod function; \ No newline at end of file diff --git a/src/components/utils.rs b/src/components/utils.rs index f803f73..5f49835 100644 --- a/src/components/utils.rs +++ b/src/components/utils.rs @@ -1,7 +1,8 @@ use adw::gdk::pango::EllipsizeMode; use adw::prelude::ListModelExtManual; use adw::{ActionRow, ComboRow}; -use glib::{Cast, Object}; +use glib::{Object}; +use glib::prelude::Cast; use gtk::prelude::{GObjectPropertyExpressionExt, ListBoxRowExt, ListItemExt, WidgetExt}; use gtk::{Align, SignalListItemFactory, StringObject}; diff --git a/src/components/wifi/saved_wifi_entry.rs b/src/components/wifi/saved_wifi_entry.rs index 5e4f6fd..c365d90 100644 --- a/src/components/wifi/saved_wifi_entry.rs +++ b/src/components/wifi/saved_wifi_entry.rs @@ -11,7 +11,8 @@ use adw::prelude::{ActionRowExt, ButtonExt, PreferencesGroupExt, PreferencesRowE use dbus::blocking::Connection; use dbus::{Error, Path}; use glib::subclass::types::ObjectSubclassIsExt; -use glib::{clone, PropertySet}; +use glib::{clone}; +use glib::property::PropertySet; use gtk::prelude::{BoxExt, ListBoxRowExt}; use gtk::{gio, Align, Button, Orientation}; diff --git a/src/components/wifi/wifi_box.rs b/src/components/wifi/wifi_box.rs index 58bf042..da6110a 100644 --- a/src/components/wifi/wifi_box.rs +++ b/src/components/wifi/wifi_box.rs @@ -14,7 +14,9 @@ use dbus::blocking::Connection; use dbus::message::SignalArgs; use dbus::Error; use dbus::Path; -use glib::{clone, Cast, ControlFlow, PropertySet}; +use glib::{clone, ControlFlow}; +use glib::prelude::Cast; +use glib::property::PropertySet; use gtk::glib::Variant; use gtk::prelude::ActionableExt; use gtk::{gio, StringList, StringObject}; diff --git a/src/components/wifi/wifi_entry.rs b/src/components/wifi/wifi_entry.rs index 5051c0f..e2d3b5d 100644 --- a/src/components/wifi/wifi_entry.rs +++ b/src/components/wifi/wifi_entry.rs @@ -4,12 +4,13 @@ use std::time::Duration; use crate::components::utils::{BASE, DBUS_PATH, WIRELESS}; use crate::components::wifi::utils::get_connection_settings; -use adw::glib::{Object, PropertySet}; +use adw::glib::{Object}; use adw::prelude::{ActionRowExt, ButtonExt, EditableExt, PopoverExt, PreferencesRowExt}; use adw::subclass::prelude::ObjectSubclassIsExt; use dbus::blocking::Connection; use dbus::Error; use glib::clone; +use glib::property::PropertySet; use gtk::prelude::{BoxExt, ListBoxRowExt, WidgetExt}; use gtk::{gio, Align, Button, Image, Orientation}; use re_set_lib::network::network_structures::{AccessPoint, WifiStrength}; diff --git a/src/components/wifi/wifi_event_handlers.rs b/src/components/wifi/wifi_event_handlers.rs index 0e5e2db..8a83a0a 100644 --- a/src/components/wifi/wifi_event_handlers.rs +++ b/src/components/wifi/wifi_event_handlers.rs @@ -1,7 +1,8 @@ use std::sync::Arc; use adw::prelude::{ComboRowExt, PreferencesGroupExt, PreferencesRowExt}; -use glib::{subclass::types::ObjectSubclassIsExt, PropertySet}; +use glib::{subclass::types::ObjectSubclassIsExt}; +use glib::property::PropertySet; use gtk::prelude::WidgetExt; use re_set_lib::{ network::network_structures::WifiStrength, diff --git a/src/components/wifi/wifi_options.rs b/src/components/wifi/wifi_options.rs index c8ef059..e58f36e 100644 --- a/src/components/wifi/wifi_options.rs +++ b/src/components/wifi/wifi_options.rs @@ -10,7 +10,8 @@ use adw::prelude::{ActionRowExt, ComboRowExt, PreferencesGroupExt}; use adw::subclass::prelude::ObjectSubclassIsExt; use dbus::arg::PropMap; use dbus::{Error, Path}; -use glib::{clone, PropertySet}; +use glib::{clone}; +use glib::property::PropertySet; use gtk::prelude::{ActionableExt, ButtonExt, EditableExt, ListBoxRowExt, WidgetExt}; use re_set_lib::network::connection::{ Connection, DNSMethod4, DNSMethod6, Enum, KeyManagement, TypeSettings, diff --git a/src/components/window/handle_sidebar_click.rs b/src/components/window/handle_sidebar_click.rs index 58696fe..a180108 100644 --- a/src/components/window/handle_sidebar_click.rs +++ b/src/components/window/handle_sidebar_click.rs @@ -162,60 +162,3 @@ fn handle_init( listeners.stop_bluetooth_listener(); false } - -// for future implementations -// pub const HANDLE_VPN_CLICK: fn(Arc, FlowBox) = -// |listeners: Arc, resetMain: FlowBox| { -// listeners.stop_network_listener(); -// listeners.stop_bluetooth_listener(); -// listeners.stop_audio_listener(); -// let label = Label::new(Some("not implemented yet")); -// resetMain.remove_all(); -// resetMain.insert(&label, -1); -// resetMain.set_max_children_per_line(1); -// }; -// -// pub const HANDLE_PERIPHERALS_CLICK: fn(Arc, FlowBox) = -// |listeners: Arc, resetMain: FlowBox| { -// listeners.stop_network_listener(); -// listeners.stop_audio_listener(); -// listeners.stop_bluetooth_listener(); -// let label = Label::new(Some("not implemented yet")); -// resetMain.remove_all(); -// resetMain.insert(&label, -1); -// resetMain.set_max_children_per_line(1); -// }; -// -// pub const HANDLE_MONITOR_CLICK: fn(Arc, FlowBox) = -// |listeners: Arc, resetMain: FlowBox| { -// listeners.stop_network_listener(); -// listeners.stop_audio_listener(); -// listeners.stop_bluetooth_listener(); -// let label = Label::new(Some("not implemented yet")); -// resetMain.remove_all(); -// resetMain.insert(&label, -1); -// resetMain.set_max_children_per_line(1); -// }; -// -// pub const HANDLE_MOUSE_CLICK: fn(Arc, FlowBox) = -// |listeners: Arc, resetMain: FlowBox| { -// listeners.stop_network_listener(); -// listeners.stop_audio_listener(); -// listeners.stop_bluetooth_listener(); -// let label = Label::new(Some("not implemented yet")); -// resetMain.remove_all(); -// resetMain.insert(&label, -1); -// resetMain.set_max_children_per_line(1); -// }; -// -// pub const HANDLE_KEYBOARD_CLICK: fn(Arc, FlowBox) = -// |listeners: Arc, resetMain: FlowBox| { -// listeners.stop_network_listener(); -// listeners.stop_audio_listener(); -// listeners.stop_bluetooth_listener(); -// let label = Label::new(Some("not implemented yet")); -// resetMain.remove_all(); -// resetMain.insert(&label, -1); -// resetMain.set_max_children_per_line(1); -// }; -// diff --git a/src/components/window/reset_window.rs b/src/components/window/reset_window.rs index e653cd9..0d0c05c 100644 --- a/src/components/window/reset_window.rs +++ b/src/components/window/reset_window.rs @@ -1,17 +1,22 @@ +use std::cell::RefCell; use std::rc::Rc; +use std::sync::Arc; +use adw::BreakpointCondition; use adw::glib::clone; use adw::subclass::prelude::ObjectSubclassIsExt; -use adw::BreakpointCondition; use glib::Object; +use gtk::{AccessibleRole, Align, Application, FlowBox, FlowBoxChild, Frame, gio, ListBoxRow, Orientation, StateFlags}; +use gtk::{DirectionType, prelude::*}; use gtk::gio::ActionEntry; -use gtk::{gio, AccessibleRole, Application, ListBoxRow, Orientation, StateFlags}; -use gtk::{prelude::*, DirectionType}; +use re_set_lib::utils::plugin_setup::FRONTEND_PLUGINS; +use crate::components::base::setting_box::SettingBox; +use crate::components::base::utils::{Listeners, Position}; +use crate::components::plugin::function::{PluginSidebarInfo, ReSetSidebarInfo}; use crate::components::window::handle_sidebar_click::*; use crate::components::window::reset_window_impl; use crate::components::window::sidebar_entry::SidebarEntry; -use crate::components::window::sidebar_entry_impl::Categories; glib::wrapper! { pub struct ReSetWindow(ObjectSubclass) @@ -39,6 +44,176 @@ impl ReSetWindow { window } + pub fn handle_dynamic_sidebar(&self) { + let self_imp = self.imp(); + self_imp + .reset_sidebar_breakpoint + .set_condition(BreakpointCondition::parse("max-width: 860sp").as_ref().ok()); + self_imp.reset_sidebar_breakpoint.add_setter( + &Object::from(self_imp.reset_overlay_split_view.get()), + "collapsed", + &true.to_value(), + ); + self_imp.reset_sidebar_breakpoint.add_setter( + &Object::from(self_imp.reset_sidebar_toggle.get()), + "visible", + &true.to_value(), + ); + } + + pub fn filter_list(&self) { + let text = self.imp().reset_search_entry.text().to_string(); + for (main_entry, sub_entriess) in self.imp().sidebar_entries.borrow().iter() { + if text.is_empty() { + main_entry.set_visible(true); + for sub_entry in sub_entriess { + sub_entry.set_visible(true); + } + continue; + } + if main_entry + .imp() + .name + .borrow() + .to_lowercase() + .contains(&text.to_lowercase()) + { + main_entry.set_visible(true); + } else { + main_entry.set_visible(false); + } + for sub_entry in sub_entriess { + if sub_entry + .imp() + .name + .borrow() + .to_lowercase() + .contains(&text.to_lowercase()) + { + sub_entry.set_visible(true); + main_entry.set_visible(true); + } else { + sub_entry.set_visible(false); + } + } + } + } + + pub fn toggle_sidebar(&self) { + if self.imp().reset_overlay_split_view.shows_sidebar() { + self.imp().reset_overlay_split_view.set_show_sidebar(false); + } else { + self.imp().reset_overlay_split_view.set_show_sidebar(true); + } + } + + pub fn setup_sidebar_entries(&self) { + let self_imp = self.imp(); + + let sidebar_list = vec![ + ReSetSidebarInfo { + name: "Connectivity", + icon_name: "network-wired-symbolic", + parent: None, + click_event: HANDLE_CONNECTIVITY_CLICK, + }, + ReSetSidebarInfo { + name: "WiFi", + icon_name: "network-wireless-symbolic", + parent: Some("Connectivity"), + click_event: HANDLE_WIFI_CLICK, + }, + ReSetSidebarInfo { + name: "Bluetooth", + icon_name: "bluetooth-symbolic", + parent: Some("Connectivity"), + click_event: HANDLE_BLUETOOTH_CLICK, + }, + ReSetSidebarInfo { + name: "Audio", + icon_name: "audio-headset-symbolic", + parent: None, + click_event: HANDLE_AUDIO_CLICK, + }, + ReSetSidebarInfo { + name: "Output", + icon_name: "audio-volume-high-symbolic", + parent: Some("Audio"), + click_event: HANDLE_VOLUME_CLICK, + }, + ReSetSidebarInfo { + name: "Input", + icon_name: "audio-input-microphone-symbolic", + parent: Some("Audio"), + click_event: HANDLE_MICROPHONE_CLICK, + }, + ]; + + let mut plugin_sidebar_list = vec![]; + unsafe { + for plugin in FRONTEND_PLUGINS.iter() { + let (sidebar_info, plugin_boxes) = (plugin.frontend_data)(); + let listeners = self_imp.listeners.clone(); + (plugin.frontend_startup)(); + + let event = Rc::new( + move |reset_main: FlowBox, position: Rc>, boxes: Vec| { + if handle_init(listeners.clone(), position, Position::Custom(String::from(sidebar_info.name))) { + return; + } + reset_main.remove_all(); + for plugin_box in &boxes { + let frame = wrap_in_flow_box_child(SettingBox::new(&plugin_box.clone())); + reset_main.insert(&frame, -1); + } + reset_main.set_max_children_per_line(boxes.len() as u32); + } + ); + + plugin_sidebar_list.push(PluginSidebarInfo { + name: sidebar_info.name, + icon_name: sidebar_info.icon_name, + parent: sidebar_info.parent, + click_event: event, + plugin_boxes, + }); + } + } + + HANDLE_VOLUME_CLICK( + self_imp.listeners.clone(), + self_imp.reset_main.clone(), + self_imp.position.clone(), + ); + + self_imp + .reset_sidebar_list + .connect_row_activated(clone!(@ weak self_imp => move |_, _| { + self_imp.reset_search_entry.set_text(""); + })); + + let mut i = 0; + for info in sidebar_list { + if info.parent.is_none() && i != 0 { + self_imp.reset_sidebar_list.insert(&create_separator(), i); + i += 1; + } + let entry = SidebarEntry::new(&info); + self_imp.reset_sidebar_list.insert(&entry, i); + i += 1; + } + + for info in plugin_sidebar_list { + if info.parent.is_none() && i != 0 { + self_imp.reset_sidebar_list.insert(&create_separator(), i); + i += 1; + } + let entry = SidebarEntry::new_plugin(&info); + self_imp.reset_sidebar_list.insert(&entry, i); + i += 1; + } + } + pub fn setup_shortcuts(&self) { let search_action = ActionEntry::builder("search") .activate(move |window: &Self, _, _| { @@ -139,221 +314,8 @@ impl ReSetWindow { error_popdown_action, ]); } - - pub fn handle_dynamic_sidebar(&self) { - let self_imp = self.imp(); - self_imp - .reset_sidebar_breakpoint - .set_condition(BreakpointCondition::parse("max-width: 860sp").as_ref().ok()); - self_imp.reset_sidebar_breakpoint.add_setter( - &Object::from(self_imp.reset_overlay_split_view.get()), - "collapsed", - &true.to_value(), - ); - self_imp.reset_sidebar_breakpoint.add_setter( - &Object::from(self_imp.reset_sidebar_toggle.get()), - "visible", - &true.to_value(), - ); - } - - pub fn filter_list(&self) { - let text = self.imp().reset_search_entry.text().to_string(); - for (main_entry, sub_entriess) in self.imp().sidebar_entries.borrow().iter() { - if text.is_empty() { - main_entry.set_visible(true); - for sub_entry in sub_entriess { - sub_entry.set_visible(true); - } - continue; - } - if main_entry - .imp() - .name - .borrow() - .to_lowercase() - .contains(&text.to_lowercase()) - { - main_entry.set_visible(true); - } else { - main_entry.set_visible(false); - } - for sub_entry in sub_entriess { - if sub_entry - .imp() - .name - .borrow() - .to_lowercase() - .contains(&text.to_lowercase()) - { - sub_entry.set_visible(true); - main_entry.set_visible(true); - } else { - sub_entry.set_visible(false); - } - } - } - } - - pub fn toggle_sidebar(&self) { - if self.imp().reset_overlay_split_view.shows_sidebar() { - self.imp().reset_overlay_split_view.set_show_sidebar(false); - } else { - self.imp().reset_overlay_split_view.set_show_sidebar(true); - } - } - - pub fn setup_sidebar_entries(&self) { - let self_imp = self.imp(); - let mut sidebar_entries = self_imp.sidebar_entries.borrow_mut(); - - let connectivity_list = vec![ - Rc::new(SidebarEntry::new( - "WiFi", - "network-wireless-symbolic", - Categories::Connectivity, - true, - HANDLE_WIFI_CLICK, - )), - Rc::new(SidebarEntry::new( - "Bluetooth", - "bluetooth-symbolic", - Categories::Connectivity, - true, - HANDLE_BLUETOOTH_CLICK, - )), - // uncommented when VPN is implemented - // SidebarEntry::new( - // "VPN", - // "network-vpn-symbolic", - // Categories::Connectivity, - // true, - // HANDLE_VPN_CLICK, - // ), - ]; - - sidebar_entries.push(( - Rc::new(SidebarEntry::new( - "Connectivity", - "network-wired-symbolic", - Categories::Connectivity, - false, - HANDLE_CONNECTIVITY_CLICK, - )), - connectivity_list, - )); - - let output = Rc::new(SidebarEntry::new( - "Output", - "audio-volume-high-symbolic", - Categories::Audio, - true, - HANDLE_VOLUME_CLICK, - )); - output.set_receives_default(true); - let audio_list = vec![ - output, - Rc::new(SidebarEntry::new( - "Input", - "audio-input-microphone-symbolic", - Categories::Audio, - true, - HANDLE_MICROPHONE_CLICK, - )), - ]; - - sidebar_entries.push(( - Rc::new(SidebarEntry::new( - "Audio", - "audio-headset-symbolic", - Categories::Audio, - false, - HANDLE_AUDIO_CLICK, - )), - audio_list, - )); - - // uncommented when implemented - // let peripheralsList = vec![ - // SidebarEntry::new( - // "Displays", - // "video-display-symbolic", - // Categories::Peripherals, - // true, - // HANDLE_MONITOR_CLICK, - // ), - // SidebarEntry::new( - // "Mouse", - // "input-mouse-symbolic", - // Categories::Peripherals, - // true, - // HANDLE_MOUSE_CLICK, - // ), - // SidebarEntry::new( - // "Keyboard", - // "input-keyboard-symbolic", - // Categories::Peripherals, - // true, - // HANDLE_KEYBOARD_CLICK, - // ), - // ]; - - // let home = SidebarEntry::new( - // "Home", - // "preferences-system-devices-symbolic", - // Categories::Peripherals, - // false, - // HANDLE_VOLUME_CLICK, - // ); - // - // sidebar_entries.push((home, Vec::new())); - - (HANDLE_VOLUME_CLICK)( - self_imp.listeners.clone(), - self_imp.reset_main.clone(), - self_imp.position.clone(), - ); - - self_imp - .reset_sidebar_list - .connect_row_activated(clone!(@ weak self_imp => move |_, _| { - self_imp.reset_search_entry.set_text(""); - })); - - for (main_entry, sub_entries) in sidebar_entries.iter() { - self_imp.reset_sidebar_list.append(&**main_entry); - for sub_entry in sub_entries { - // TODO change this to home when home offers dynamic selection - // this is just a placeholder for now, hence hardcoded - if &*sub_entry.imp().name.borrow() == "Output" { - self_imp.reset_sidebar_list.append(&**sub_entry); - self_imp.default_entry.replace(Some(sub_entry.clone())); - sub_entry.grab_focus(); - sub_entry.set_state_flags(StateFlags::SELECTED, false); - } else { - self_imp.reset_sidebar_list.append(&**sub_entry); - } - } - let separator = gtk::Separator::builder() - .margin_bottom(3) - .margin_top(3) - .orientation(Orientation::Horizontal) - .accessible_role(AccessibleRole::Separator) - .can_focus(false) - .build(); - let separator_row = ListBoxRow::builder() - .child(&separator) - .selectable(false) - .activatable(false) - .can_target(false) - // .focusable(false) - .accessible_role(AccessibleRole::Separator) - .build(); - // TODO how to simply skip this ? - self_imp.reset_sidebar_list.append(&separator_row); - } - } } + fn setup_callback(window: Rc) -> Rc { let self_imp = window.imp(); let activated_ref = window.clone(); @@ -386,12 +348,21 @@ fn setup_callback(window: Rc) -> Rc { *default_entry = None; } } - let click_event = result.imp().on_click_event.borrow().on_click_event; - (click_event)( - imp.listeners.clone(), - imp.reset_main.get(), - imp.position.clone(), - ); + let click_event = result.imp().on_click_event.borrow(); + if let Some(event) = click_event.on_click_event { + event( + imp.listeners.clone(), + imp.reset_main.get(), + imp.position.clone(), + ); + } else { + let event = click_event.on_plugin_click_event.clone(); + event( + imp.reset_main.get(), + imp.position.clone(), + result.imp().plugin_boxes.borrow().clone(), + ); + } }); self_imp.reset_close.connect_clicked(move |_| { @@ -399,3 +370,49 @@ fn setup_callback(window: Rc) -> Rc { }); window } + +pub fn create_separator() -> ListBoxRow { + let separator: gtk::Separator = gtk::Separator::builder() + .margin_bottom(3) + .margin_top(3) + .orientation(Orientation::Horizontal) + .accessible_role(AccessibleRole::Separator) + .can_focus(false) + .build(); + ListBoxRow::builder() + .child(&separator) + .selectable(false) + .activatable(false) + .can_target(false) + .accessible_role(AccessibleRole::Separator) + .build() +} + +fn handle_init( + listeners: Arc, + position: Rc>, + clicked_position: Position, +) -> bool { + { + let mut pos_borrow = position.borrow_mut(); + if *pos_borrow == clicked_position { + return true; + } + *pos_borrow = clicked_position; + } + listeners.stop_network_listener(); + listeners.stop_audio_listener(); + listeners.stop_bluetooth_listener(); + false +} + +fn wrap_in_flow_box_child(widget: SettingBox) -> FlowBoxChild { + let frame = Frame::new(None); + frame.set_child(Some(&widget)); + frame.add_css_class("resetSettingFrame"); + FlowBoxChild::builder() + .child(&frame) + .halign(Align::Fill) + .valign(Align::Start) + .build() +} diff --git a/src/components/window/reset_window_impl.rs b/src/components/window/reset_window_impl.rs index b58e69f..4c81c53 100644 --- a/src/components/window/reset_window_impl.rs +++ b/src/components/window/reset_window_impl.rs @@ -2,9 +2,9 @@ use std::cell::RefCell; use std::rc::Rc; use std::sync::Arc; -use adw::glib::StaticTypeExt; use adw::subclass::prelude::AdwApplicationWindowImpl; use adw::{Breakpoint, OverlaySplitView}; +use glib::prelude::StaticTypeExt; use glib::subclass::InitializingObject; use gtk::prelude::WidgetExt; use gtk::subclass::prelude::*; diff --git a/src/components/window/sidebar_entry.rs b/src/components/window/sidebar_entry.rs index 4af9971..e2eb81d 100644 --- a/src/components/window/sidebar_entry.rs +++ b/src/components/window/sidebar_entry.rs @@ -1,14 +1,13 @@ -use std::cell::RefCell; use std::rc::Rc; -use std::sync::Arc; -use crate::components::base::utils::{Listeners, Position}; use crate::components::window::sidebar_entry_impl; -use crate::components::window::sidebar_entry_impl::{Categories, SidebarAction}; +use crate::components::window::sidebar_entry_impl::{SidebarAction}; use adw::subclass::prelude::ObjectSubclassIsExt; use glib::Object; use gtk::prelude::*; -use gtk::FlowBox; +use crate::components::plugin::function::{PluginSidebarInfo, ReSetSidebarInfo}; + +use super::handle_sidebar_click::HANDLE_HOME; glib::wrapper! { pub struct SidebarEntry(ObjectSubclass) @@ -17,37 +16,62 @@ glib::wrapper! { } impl SidebarEntry { - pub fn new( - entry_name: &str, - icon_name: &str, - category: Categories, - is_subcategory: bool, - click_event: fn(Arc, FlowBox, Rc>), - ) -> Self { + pub fn new(info: &ReSetSidebarInfo) -> Self { let entry: SidebarEntry = Object::builder().build(); let entry_imp = entry.imp(); - entry_imp.reset_sidebar_label.get().set_text(entry_name); + entry_imp.reset_sidebar_label.get().set_text(info.name); entry_imp .reset_sidebar_image - .set_from_icon_name(Some(icon_name)); - entry_imp.category.set(category); - entry_imp.is_subcategory.set(is_subcategory); + .set_from_icon_name(Some(info.icon_name)); + + match &info.parent { + None => {} + Some(parent) => { + let mut name = entry_imp.parent.borrow_mut(); + *name = parent.to_string(); + entry.child().unwrap().set_margin_start(30); + } + } + { let mut name = entry_imp.name.borrow_mut(); - *name = String::from(entry_name); + *name = info.name.to_string(); let mut action = entry_imp.on_click_event.borrow_mut(); *action = SidebarAction { - on_click_event: click_event, + on_click_event: Some(info.click_event), + on_plugin_click_event: Rc::new(|_,_,_|{}), }; } - Self::set_margin(&entry); entry } - - fn set_margin(entry: &SidebarEntry) { - if entry.imp().is_subcategory.get() { - let option = entry.child().unwrap(); - option.set_margin_start(30); + + pub fn new_plugin(info: &PluginSidebarInfo) -> Self { + let entry: SidebarEntry = Object::builder().build(); + let entry_imp = entry.imp(); + entry_imp.reset_sidebar_label.get().set_text(info.name); + entry_imp + .reset_sidebar_image + .set_from_icon_name(Some(info.icon_name)); + entry_imp.plugin_boxes.borrow_mut().extend(info.plugin_boxes.clone()); + + match &info.parent { + None => {} + Some(parent) => { + let mut name = entry_imp.parent.borrow_mut(); + *name = parent.to_string(); + entry.child().unwrap().set_margin_start(30); + } } + + { + let mut name = entry_imp.name.borrow_mut(); + *name = info.name.to_string(); + let mut action = entry_imp.on_click_event.borrow_mut(); + *action = SidebarAction { + on_click_event: None, + on_plugin_click_event: info.click_event.clone(), + }; + } + entry } } diff --git a/src/components/window/sidebar_entry_impl.rs b/src/components/window/sidebar_entry_impl.rs index 81c4a06..f583b49 100644 --- a/src/components/window/sidebar_entry_impl.rs +++ b/src/components/window/sidebar_entry_impl.rs @@ -3,8 +3,8 @@ use std::rc::Rc; use std::sync::Arc; use glib::subclass::InitializingObject; -use gtk::subclass::prelude::*; use gtk::{CompositeTemplate, FlowBox, Image, Label, ListBoxRow}; +use gtk::subclass::prelude::*; use crate::components::base::utils::{Listeners, Position}; use crate::components::window::handle_sidebar_click::HANDLE_HOME; @@ -26,20 +26,22 @@ pub struct SidebarEntry { pub reset_sidebar_label: TemplateChild