diff --git a/ui/Cargo.lock b/ui/Cargo.lock index 537d499..82788fc 100644 --- a/ui/Cargo.lock +++ b/ui/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + [[package]] name = "autocfg" version = "1.5.0" @@ -570,6 +576,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +dependencies = [ + "serde", +] + [[package]] name = "slab" version = "0.4.10" @@ -602,7 +617,7 @@ dependencies = [ "cfg-expr", "heck", "pkg-config", - "toml", + "toml 0.8.23", "version-compare", ] @@ -619,11 +634,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_edit", ] +[[package]] +name = "toml" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 1.0.0", + "toml_datetime 0.7.0", + "toml_parser", + "toml_writer", + "winnow", +] + [[package]] name = "toml_datetime" version = "0.6.11" @@ -633,6 +663,15 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +dependencies = [ + "serde", +] + [[package]] name = "toml_edit" version = "0.22.27" @@ -641,18 +680,36 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "winnow", ] +[[package]] +name = "toml_parser" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" + [[package]] name = "ui" version = "0.1.0" dependencies = [ + "anyhow", "glib-build-tools", "gtk4", "libadwaita", + "serde", + "toml 0.9.2", ] [[package]] diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 9684e1e..165bc6a 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -5,7 +5,10 @@ edition = "2024" [dependencies] gtk = { version = "0.10.0", package = "gtk4" } -adw = { version = "0.8.0", package = "libadwaita" } +adw = { version = "0.8.0", package = "libadwaita", features = ["v1_4"] } +serde = { version = "1.0", features = ["derive"] } +toml = "0.9.2" +anyhow = "1.0" [build-dependencies] glib-build-tools = "0.21.0" diff --git a/ui/rsc/pane/main.ui b/ui/rsc/pane/main.ui new file mode 100644 index 0000000..0fea93d --- /dev/null +++ b/ui/rsc/pane/main.ui @@ -0,0 +1,89 @@ + + + + diff --git a/ui/rsc/pane/sidebar.ui b/ui/rsc/pane/sidebar.ui new file mode 100644 index 0000000..62edd8d --- /dev/null +++ b/ui/rsc/pane/sidebar.ui @@ -0,0 +1,39 @@ + + + + diff --git a/ui/rsc/resources.gresource.xml b/ui/rsc/resources.gresource.xml index 47fcd0e..5ef5672 100644 --- a/ui/rsc/resources.gresource.xml +++ b/ui/rsc/resources.gresource.xml @@ -2,6 +2,8 @@ window.ui + pane/main.ui + pane/sidebar.ui pref/dropdown.ui pref/number.ui pref/entry.ui diff --git a/ui/rsc/window.ui b/ui/rsc/window.ui index 8eb1025..99ae122 100644 --- a/ui/rsc/window.ui +++ b/ui/rsc/window.ui @@ -11,132 +11,11 @@ 300 - - - - - - - - - Profiles - - - - - - - - - - - - vertical - 12 - 12 - 12 - 12 - - - Create New Profile - suggested-action - - - - - - - + - - - - - - - - - lsfg-vk Configuration Menu - - - - - - - - - never - - - vertical - 48 - 48 - 32 - 32 - 32 - - - - Frame Generation - - - - Multiplier - - - - - - Flow Scale - - - - - - Performance Mode - false - - - - - - - - Frame Generation - - - - HDR Mode - false - - - - - - Experimental Present Mode - 0 - - - - vsync/fifo - mailbox - immediate - - - - - - - - - - - - - - + diff --git a/ui/src/config.rs b/ui/src/config.rs new file mode 100644 index 0000000..75c4a9d --- /dev/null +++ b/ui/src/config.rs @@ -0,0 +1,129 @@ +use std::sync::{Arc, OnceLock, RwLock}; + +use anyhow::Context; + +pub mod structs; +pub use structs::*; + +/// Find the configuration file path based on environment variables +fn find_config_file() -> String { + if let Some(path) = std::env::var("LSFG_CONFIG").ok() { + return path; + } + + if let Some(xdg) = std::env::var("XDG_CONFIG_HOME").ok() { + return format!("{}/lsfg-vk/conf.toml", xdg); + } + + if let Some(home) = std::env::var("HOME").ok() { + return format!("{}/.config/lsfg-vk/conf.toml", home); + } + + "conf.toml".to_string() +} + +static CONFIG: OnceLock>> = OnceLock::new(); +static CONFIG_WRITER: OnceLock> = OnceLock::new(); + +/// +/// Load the configuration from the file and create a writer. +/// +pub fn load_config() -> Result<(), anyhow::Error> { + // load the configuration file + let path = find_config_file(); + let data = std::fs::read(path) + .context("Failed to read conf.toml")?; + let config: TomlConfig = toml::from_slice(&data) + .context("Failed to parse conf.toml")?; + CONFIG.set(Arc::new(RwLock::new(config))) + .ok().context("Failed to set configuration state")?; + + // create the configuration writer thread + let (tx, rx) = std::sync::mpsc::channel::<()>(); + CONFIG_WRITER.set(tx) + .ok().context("Failed to set configuration writer")?; + + std::thread::spawn(move || { + let config = CONFIG.get().unwrap(); + loop { + // wait for a signal to write the configuration + if let Err(_) = rx.recv() { + break; + } + + // wait a bit to avoid excessive writes + std::thread::sleep(std::time::Duration::from_millis(200)); + + // empty the channel + while rx.try_recv().is_ok() {} + + // write the configuration + eprintln!("Saving configuration..."); + if let Ok(config) = config.try_read() { + if let Err(e) = save_config(&config) { + eprintln!("Failed to save configuration: {}", e); + } else { + eprintln!("Configuration saved successfully"); + } + } else { + eprintln!("Failed to read configuration state"); + } + } + }); + Ok(()) +} + +/// +/// Get a snapshot of the current configuration +/// +pub fn get_config() -> Result { + let conf = CONFIG.get() + .expect("Configuration not loaded") + .try_read() + .map(|config| config.clone()); + if let Ok(config) = conf { + return Ok(config) + } + + anyhow::bail!("Failed to read configuration state") +} + +/// +/// Safely edit the configuration. +/// +pub fn edit_config(f: F) -> Result<(), anyhow::Error> +where + F: FnOnce(&mut TomlConfig) +{ + let mut config = CONFIG.get() + .expect("Configuration not loaded") + .write() + .map_err(|_| anyhow::anyhow!("Failed to acquire write lock on configuration"))?; + + f(&mut config); + + CONFIG_WRITER.get().unwrap().send(()) + .context("Failed to send configuration update signal") +} + +/// +/// Save the configuration to the file +/// +/// # Arguments +/// +/// `config` - The configuration to save +/// +pub fn save_config(config: &TomlConfig) -> Result<(), anyhow::Error> { + let path = find_config_file(); + + let parent = std::path::Path::new(&path).parent() + .context("Failed to get parent directory of config path")?; + std::fs::create_dir_all(parent) + .context("Failed to create config directory")?; + + let data = toml::to_string(config) + .context("Failed to serialize conf.toml")?; + std::fs::write(path, data) + .context("Failed to write conf.toml")?; + Ok(()) +} diff --git a/ui/src/config/structs.rs b/ui/src/config/structs.rs new file mode 100644 index 0000000..66121c1 --- /dev/null +++ b/ui/src/config/structs.rs @@ -0,0 +1,68 @@ +use serde::{Deserialize, Serialize}; + +// multiplier +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Multiplier(i64); +impl Default for Multiplier { + fn default() -> Self { Multiplier(2) } +} +impl From for Multiplier { + fn from(value: i64) -> Self { Multiplier(value) } +} + +// flow scale +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct FlowScale(f64); +impl Default for FlowScale { + fn default() -> Self { FlowScale(1.0) } +} +impl From for FlowScale { + fn from(value: f64) -> Self { FlowScale(value) } +} + +// present mode +#[derive(Debug, Clone, Deserialize, Serialize)] +pub enum PresentMode { + #[serde(rename = "fifo", alias = "vsync")] + Vsync, + #[serde(rename = "immediate")] + Immediate, + #[serde(rename = "mailbox")] + Mailbox, +} +impl Default for PresentMode { + fn default() -> Self { PresentMode::Vsync } +} + +/// Global configuration for the application +#[derive(Debug, Clone, Default, Deserialize, Serialize)] +pub struct TomlGlobal { + pub dll: Option +} + +/// Game-specific configuration +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct TomlGame { + pub exe: String, + + #[serde(default)] + pub multiplier: Multiplier, + #[serde(default)] + pub flow_scale: FlowScale, + #[serde(default)] + pub performance_mode: bool, + #[serde(default)] + pub hdr_mode: bool, + #[serde(default)] + pub experimental_present_mode: PresentMode +} + +/// Main configuration structure +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct TomlConfig { + pub version: i64, + #[serde(default)] + pub global: TomlGlobal, + #[serde(default)] + pub game: Vec +} diff --git a/ui/src/main.rs b/ui/src/main.rs index 51af8fb..2bd44e0 100644 --- a/ui/src/main.rs +++ b/ui/src/main.rs @@ -1,9 +1,130 @@ -mod ui; +use std::sync::{Arc, OnceLock, RwLock}; + +use adw::{self, subclass::prelude::ObjectSubclassIsExt}; +use gtk::{gio, prelude::*}; + +use crate::config::*; + +mod wrapper; +mod config; const APP_ID: &str = "gay.pancake.lsfg-vk.ConfigurationUi"; -fn main() { - let app = ui::App::new(APP_ID) - .expect("Failed to create application"); - app.run() +#[derive(Debug)] +struct State { + // ui state + selected_game: Option +} + +static STATE: OnceLock>> = OnceLock::new(); + +fn main() { + gio::resources_register_include!("ui.gresource") + .expect("Failed to register resources"); + config::load_config() + .expect("Failed to load configuration"); + + // prepare the application state + STATE.set(Arc::new(RwLock::new(State { + selected_game: Some(0) + }))).expect("Failed to set application state"); + + // start the application + let app = adw::Application::builder() + .application_id(APP_ID) + .build(); + app.connect_activate(build_ui); + app.run(); +} + +fn build_ui(app: &adw::Application) { + // create the main window + let window = wrapper::Window::new(app); + window.set_application(Some(app)); + + // register main pane signals + let main = window.imp().main.imp(); + + let pref_multiplier = main.pref_multiplier.imp(); + pref_multiplier.number.connect_value_changed(|dropdown| { + if let Ok(state) = STATE.get().unwrap().try_read() { + if state.selected_game.is_none() { + return; + } + + let multiplier = (dropdown.value() as i64).into(); + let _ = config::edit_config(|config| { + config.game[state.selected_game.unwrap()] + .multiplier = multiplier; + }); + } + }); + + let pref_flow_scale = main.pref_flow_scale.imp(); + pref_flow_scale.slider.connect_value_changed(|slider| { + if let Ok(state) = STATE.get().unwrap().try_read() { + if state.selected_game.is_none() { + return; + } + + let flow_scale = (slider.value() / 100.0).into(); + let _ = config::edit_config(|config| { + config.game[state.selected_game.unwrap()] + .flow_scale = flow_scale; + }); + } + }); + + let pref_performance_mode = main.pref_performance_mode.imp(); + pref_performance_mode.switch.connect_state_notify(|switch| { + if let Ok(state) = STATE.get().unwrap().try_read() { + if state.selected_game.is_none() { + return; + } + + let performance_mode = switch.state(); + let _ = config::edit_config(|config| { + config.game[state.selected_game.unwrap()] + .performance_mode = performance_mode; + }); + } + }); + + let pref_hdr_mode = main.pref_hdr_mode.imp(); + pref_hdr_mode.switch.connect_state_notify(|switch| { + if let Ok(state) = STATE.get().unwrap().try_read() { + if state.selected_game.is_none() { + return; + } + + let hdr_mode = switch.state(); + let _ = config::edit_config(|config| { + config.game[state.selected_game.unwrap()] + .hdr_mode = hdr_mode; + }); + } + }); + + let pref_experimental_present_mode = main.pref_experimental_present_mode.imp(); + pref_experimental_present_mode.dropdown.connect_selected_notify(|dropdown| { + if let Ok(state) = STATE.get().unwrap().try_read() { + if state.selected_game.is_none() { + return; + } + + let selected = match dropdown.selected() { + 0 => PresentMode::Vsync, + 1 => PresentMode::Mailbox, + 2 => PresentMode::Immediate, + _ => PresentMode::Vsync, + }; + config::edit_config(|config| { + config.game[state.selected_game.unwrap()] + .experimental_present_mode = selected; + }).unwrap(); + } + }); + + // present the window + window.present(); } diff --git a/ui/src/ui.rs b/ui/src/ui.rs deleted file mode 100644 index e11d734..0000000 --- a/ui/src/ui.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::sync::Arc; - -use adw; -use gtk::{gio, glib, prelude::*}; - -pub mod ui; -pub mod pref; - -pub struct App { - app: Arc, -} - -impl App { - pub fn new(appid: &str) -> Result { - gio::resources_register_include!("ui.gresource")?; - - let app = adw::Application::builder() - .application_id(appid) - .build(); - app.connect_activate(Self::build_ui); - - Ok(App { - app: Arc::new(app) - }) - } - - fn build_ui(app: &adw::Application) { - let window = ui::Window::new(app); - window.set_application(Some(app)); - window.present(); - } - - pub fn run(self){ - self.app.run(); - } -} diff --git a/ui/src/ui/ui.rs b/ui/src/ui/ui.rs deleted file mode 100644 index d59f2f4..0000000 --- a/ui/src/ui/ui.rs +++ /dev/null @@ -1,29 +0,0 @@ -use gtk::glib; -use gtk; -use adw; -use gtk::glib::types::StaticTypeExt; - -pub mod window; - -glib::wrapper! { - pub struct Window(ObjectSubclass) - @extends - adw::ApplicationWindow, adw::Window, - gtk::ApplicationWindow, gtk::Window, gtk::Widget, - @implements - gtk::gio::ActionGroup, gtk::gio::ActionMap, - gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, - gtk::Native, gtk::Root, gtk::ShortcutManager; -} - -impl Window { - pub fn new(app: &adw::Application) -> Self { - super::pref::PrefDropdown::ensure_type(); - super::pref::PrefSwitch::ensure_type(); - super::pref::PrefEntry::ensure_type(); - - glib::Object::builder() - .property("application", app) - .build() - } -} diff --git a/ui/src/ui/ui/window.rs b/ui/src/ui/ui/window.rs deleted file mode 100644 index 24b680b..0000000 --- a/ui/src/ui/ui/window.rs +++ /dev/null @@ -1,73 +0,0 @@ -use gtk::{prelude::RangeExt, subclass::prelude::*}; -use adw::subclass::prelude::*; -use crate::ui::pref::*; -use gtk::{glib, CompositeTemplate}; - -#[derive(CompositeTemplate, Default)] -#[template(resource = "/gay/pancake/lsfg-vk/window.ui")] -pub struct Window { - // main config elements - #[template_child] - pref_multiplier: TemplateChild, - #[template_child] - pref_flow_scale: TemplateChild, - #[template_child] - pref_performance_mode: TemplateChild, - #[template_child] - pref_hdr_mode: TemplateChild, - #[template_child] - pref_experimental_present_mode: TemplateChild, -} - -#[glib::object_subclass] -impl ObjectSubclass for Window { - const NAME: &'static str = "LSApplicationWindow"; - type Type = super::Window; - type ParentType = adw::ApplicationWindow; - - fn class_init(klass: &mut Self::Class) { - klass.bind_template(); - } - - fn instance_init(obj: &glib::subclass::InitializingObject) { - obj.init_template(); - } -} - -impl ObjectImpl for Window { - fn constructed(&self) { - self.parent_constructed(); - - self.pref_multiplier.get() - .imp().number.get().connect_value_changed(|dropdown| { - let selected = dropdown.value(); - println!("Multiplier changed: {}", selected); - }); - self.pref_flow_scale.get() - .imp().slider.get().connect_value_changed(|slider| { - let value = slider.value(); - println!("Flow scale changed: {}", value); - }); - self.pref_performance_mode.get() - .imp().switch.get().connect_state_notify(|switch| { - let state = switch.state(); - println!("Performance mode changed: {}", state); - }); - self.pref_hdr_mode.get() - .imp().switch.get().connect_state_notify(|switch| { - let state = switch.state(); - println!("HDR mode changed: {}", state); - }); - self.pref_experimental_present_mode.get() - .imp().dropdown.get().connect_selected_notify(|dropdown| { - let selected = dropdown.selected(); - println!("Experimental present mode changed: {}", selected); - }); - } -} - -impl WidgetImpl for Window {} -impl WindowImpl for Window {} -impl ApplicationWindowImpl for Window {} -impl AdwWindowImpl for Window {} -impl AdwApplicationWindowImpl for Window {} diff --git a/ui/src/wrapper.rs b/ui/src/wrapper.rs new file mode 100644 index 0000000..5a981df --- /dev/null +++ b/ui/src/wrapper.rs @@ -0,0 +1,77 @@ +use gtk::glib; +use gtk; +use adw; +use gtk::glib::types::StaticTypeExt; + +pub mod pane; +pub mod pref; + +pub mod imp { + use gtk::subclass::prelude::*; + use adw::subclass::prelude::*; + use crate::wrapper::pane::*; + use gtk::{glib, CompositeTemplate}; + + #[derive(CompositeTemplate, Default)] + #[template(resource = "/gay/pancake/lsfg-vk/window.ui")] + pub struct Window { + #[template_child] + pub main: TemplateChild, + #[template_child] + pub sidebar: TemplateChild, + } + + #[glib::object_subclass] + impl ObjectSubclass for Window { + const NAME: &'static str = "LSApplicationWindow"; + type Type = super::Window; + type ParentType = adw::ApplicationWindow; + + fn class_init(klass: &mut Self::Class) { + klass.bind_template(); + } + + fn instance_init(obj: &glib::subclass::InitializingObject) { + obj.init_template(); + } + } + + impl ObjectImpl for Window { + fn constructed(&self) { + self.parent_constructed(); + } + } + + impl WidgetImpl for Window {} + impl WindowImpl for Window {} + impl ApplicationWindowImpl for Window {} + impl AdwWindowImpl for Window {} + impl AdwApplicationWindowImpl for Window {} +} + +glib::wrapper! { + pub struct Window(ObjectSubclass) + @extends + adw::ApplicationWindow, adw::Window, + gtk::ApplicationWindow, gtk::Window, gtk::Widget, + @implements + gtk::gio::ActionGroup, gtk::gio::ActionMap, + gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, + gtk::Native, gtk::Root, gtk::ShortcutManager; +} + +impl Window { + pub fn new(app: &adw::Application) -> Self { + pref::PrefDropdown::ensure_type(); + pref::PrefEntry::ensure_type(); + pref::PrefNumber::ensure_type(); + pref::PrefSlider::ensure_type(); + pref::PrefSwitch::ensure_type(); + pane::PaneMain::ensure_type(); + pane::PaneSidebar::ensure_type(); + + glib::Object::builder() + .property("application", app) + .build() + } +} diff --git a/ui/src/wrapper/pane.rs b/ui/src/wrapper/pane.rs new file mode 100644 index 0000000..e9ac12e --- /dev/null +++ b/ui/src/wrapper/pane.rs @@ -0,0 +1,35 @@ +use gtk::glib; +use gtk; +use adw; + +pub mod main; +pub mod sidebar; + +glib::wrapper! { + pub struct PaneMain(ObjectSubclass) + @extends + adw::NavigationPage, gtk::Widget, + @implements + gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget; +} + +glib::wrapper! { + pub struct PaneSidebar(ObjectSubclass) + @extends + adw::NavigationPage, gtk::Widget, + @implements + gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget; +} + + +impl PaneMain { + pub fn new() -> Self { + glib::Object::new() + } +} + +impl PaneSidebar { + pub fn new() -> Self { + glib::Object::new() + } +} diff --git a/ui/src/wrapper/pane/main.rs b/ui/src/wrapper/pane/main.rs new file mode 100644 index 0000000..853ea59 --- /dev/null +++ b/ui/src/wrapper/pane/main.rs @@ -0,0 +1,43 @@ +use gtk::glib; +use gtk::subclass::prelude::*; +use adw::subclass::prelude::*; +use crate::wrapper::pref::*; + +#[derive(gtk::CompositeTemplate, Default)] +#[template(resource = "/gay/pancake/lsfg-vk/pane/main.ui")] +pub struct PaneMain { + #[template_child] + pub pref_multiplier: TemplateChild, + #[template_child] + pub pref_flow_scale: TemplateChild, + #[template_child] + pub pref_performance_mode: TemplateChild, + #[template_child] + pub pref_hdr_mode: TemplateChild, + #[template_child] + pub pref_experimental_present_mode: TemplateChild +} + +#[glib::object_subclass] +impl ObjectSubclass for PaneMain { + const NAME: &'static str = "LSPaneMain"; + type Type = super::PaneMain; + type ParentType = adw::NavigationPage; + + fn class_init(klass: &mut Self::Class) { + klass.bind_template(); + } + + fn instance_init(obj: &glib::subclass::InitializingObject) { + obj.init_template(); + } +} + +impl ObjectImpl for PaneMain { + fn constructed(&self) { + self.parent_constructed(); + } +} + +impl WidgetImpl for PaneMain {} +impl NavigationPageImpl for PaneMain {} diff --git a/ui/src/wrapper/pane/sidebar.rs b/ui/src/wrapper/pane/sidebar.rs new file mode 100644 index 0000000..c43c31e --- /dev/null +++ b/ui/src/wrapper/pane/sidebar.rs @@ -0,0 +1,33 @@ +use gtk::glib; +use gtk::subclass::prelude::*; +use adw::subclass::prelude::*; + +#[derive(gtk::CompositeTemplate, Default)] +#[template(resource = "/gay/pancake/lsfg-vk/pane/sidebar.ui")] +pub struct PaneSidebar { + +} + +#[glib::object_subclass] +impl ObjectSubclass for PaneSidebar { + const NAME: &'static str = "LSPaneSidebar"; + type Type = super::PaneSidebar; + type ParentType = adw::NavigationPage; + + fn class_init(klass: &mut Self::Class) { + klass.bind_template(); + } + + fn instance_init(obj: &glib::subclass::InitializingObject) { + obj.init_template(); + } +} + +impl ObjectImpl for PaneSidebar { + fn constructed(&self) { + self.parent_constructed(); + } +} + +impl WidgetImpl for PaneSidebar {} +impl NavigationPageImpl for PaneSidebar {} diff --git a/ui/src/ui/pref.rs b/ui/src/wrapper/pref.rs similarity index 100% rename from ui/src/ui/pref.rs rename to ui/src/wrapper/pref.rs diff --git a/ui/src/ui/pref/dropdown.rs b/ui/src/wrapper/pref/dropdown.rs similarity index 100% rename from ui/src/ui/pref/dropdown.rs rename to ui/src/wrapper/pref/dropdown.rs diff --git a/ui/src/ui/pref/entry.rs b/ui/src/wrapper/pref/entry.rs similarity index 100% rename from ui/src/ui/pref/entry.rs rename to ui/src/wrapper/pref/entry.rs diff --git a/ui/src/ui/pref/number.rs b/ui/src/wrapper/pref/number.rs similarity index 100% rename from ui/src/ui/pref/number.rs rename to ui/src/wrapper/pref/number.rs diff --git a/ui/src/ui/pref/slider.rs b/ui/src/wrapper/pref/slider.rs similarity index 100% rename from ui/src/ui/pref/slider.rs rename to ui/src/wrapper/pref/slider.rs diff --git a/ui/src/ui/pref/switch.rs b/ui/src/wrapper/pref/switch.rs similarity index 100% rename from ui/src/ui/pref/switch.rs rename to ui/src/wrapper/pref/switch.rs