mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2025-10-30 07:01:10 +00:00
ui: configuration system and basic switching
This commit is contained in:
parent
3dba2a7a3d
commit
cbc536c23e
22 changed files with 709 additions and 272 deletions
67
ui/Cargo.lock
generated
67
ui/Cargo.lock
generated
|
|
@ -2,6 +2,12 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.98"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
|
@ -570,6 +576,15 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.10"
|
version = "0.4.10"
|
||||||
|
|
@ -602,7 +617,7 @@ dependencies = [
|
||||||
"cfg-expr",
|
"cfg-expr",
|
||||||
"heck",
|
"heck",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"toml",
|
"toml 0.8.23",
|
||||||
"version-compare",
|
"version-compare",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -619,11 +634,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned 0.6.9",
|
||||||
"toml_datetime",
|
"toml_datetime 0.6.11",
|
||||||
"toml_edit",
|
"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]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.6.11"
|
version = "0.6.11"
|
||||||
|
|
@ -633,6 +663,15 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.22.27"
|
version = "0.22.27"
|
||||||
|
|
@ -641,18 +680,36 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned 0.6.9",
|
||||||
"toml_datetime",
|
"toml_datetime 0.6.11",
|
||||||
"winnow",
|
"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]]
|
[[package]]
|
||||||
name = "ui"
|
name = "ui"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"glib-build-tools",
|
"glib-build-tools",
|
||||||
"gtk4",
|
"gtk4",
|
||||||
"libadwaita",
|
"libadwaita",
|
||||||
|
"serde",
|
||||||
|
"toml 0.9.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gtk = { version = "0.10.0", package = "gtk4" }
|
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]
|
[build-dependencies]
|
||||||
glib-build-tools = "0.21.0"
|
glib-build-tools = "0.21.0"
|
||||||
|
|
|
||||||
89
ui/rsc/pane/main.ui
Normal file
89
ui/rsc/pane/main.ui
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<template class="LSPaneMain" parent="AdwNavigationPage">
|
||||||
|
<child>
|
||||||
|
<object class="AdwToolbarView">
|
||||||
|
<!-- Header -->
|
||||||
|
<child type="top">
|
||||||
|
<object class="AdwHeaderBar">
|
||||||
|
<property name="title-widget">
|
||||||
|
<object class="AdwWindowTitle">
|
||||||
|
<property name="title">lsfg-vk Configuration Menu</property>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<!-- Content -->
|
||||||
|
<property name="content">
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="hscrollbar-policy">never</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="main_box">
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="margin-start">48</property>
|
||||||
|
<property name="margin-end">48</property>
|
||||||
|
<property name="margin-top">32</property>
|
||||||
|
<property name="margin-bottom">32</property>
|
||||||
|
<property name="spacing">32</property>
|
||||||
|
<child>
|
||||||
|
<!-- Frame Generation -->
|
||||||
|
<object class="AdwPreferencesGroup">
|
||||||
|
<property name="title">Frame Generation</property>
|
||||||
|
<!-- Frame Generation: Multiplier -->
|
||||||
|
<child>
|
||||||
|
<object class="LSPrefNumber" id="pref_multiplier">
|
||||||
|
<property name="opt-name">Multiplier</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<!-- Frame Generation: Flow Scale -->
|
||||||
|
<child>
|
||||||
|
<object class="LSPrefSlider" id="pref_flow_scale">
|
||||||
|
<property name="opt-name">Flow Scale</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<!-- Frame Generation: Performance Mode -->
|
||||||
|
<child>
|
||||||
|
<object class="LSPrefSwitch" id="pref_performance_mode">
|
||||||
|
<property name="opt-name">Performance Mode</property>
|
||||||
|
<property name="default-state">false</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<!-- Misc -->
|
||||||
|
<object class="AdwPreferencesGroup">
|
||||||
|
<property name="title">Misc</property>
|
||||||
|
<!-- Misc: HDR Mode -->
|
||||||
|
<child>
|
||||||
|
<object class="LSPrefSwitch" id="pref_hdr_mode">
|
||||||
|
<property name="opt-name">HDR Mode</property>
|
||||||
|
<property name="default-state">false</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<!-- Misc: Experimental Present Mode -->
|
||||||
|
<child>
|
||||||
|
<object class="LSPrefDropdown" id="pref_experimental_present_mode">
|
||||||
|
<property name="opt-name">Experimental Present Mode</property>
|
||||||
|
<property name="default-selection">0</property>
|
||||||
|
<property name="options">
|
||||||
|
<object class="GtkStringList">
|
||||||
|
<items>
|
||||||
|
<item>VSync/FIFO</item>
|
||||||
|
<item>Mailbox</item>
|
||||||
|
<item>Immediate</item>
|
||||||
|
</items>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</template>
|
||||||
|
</interface>
|
||||||
39
ui/rsc/pane/sidebar.ui
Normal file
39
ui/rsc/pane/sidebar.ui
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<template class="LSPaneSidebar" parent="AdwNavigationPage">
|
||||||
|
<child>
|
||||||
|
<object class="AdwToolbarView">
|
||||||
|
<!-- Header -->
|
||||||
|
<child type="top">
|
||||||
|
<object class="AdwHeaderBar">
|
||||||
|
<property name="title-widget">
|
||||||
|
<object class="AdwWindowTitle">
|
||||||
|
<property name="title">Profiles</property>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<!-- TODO: content -->
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<child type="bottom">
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="margin-start">12</property>
|
||||||
|
<property name="margin-end">12</property>
|
||||||
|
<property name="margin-top">12</property>
|
||||||
|
<property name="margin-bottom">12</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="label">Create New Profile</property>
|
||||||
|
<property name="css-classes">suggested-action</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</template>
|
||||||
|
</interface>
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
<gresources>
|
<gresources>
|
||||||
<gresource prefix="/gay/pancake/lsfg-vk/">
|
<gresource prefix="/gay/pancake/lsfg-vk/">
|
||||||
<file compressed="true" preprocess="xml-stripblanks">window.ui</file>
|
<file compressed="true" preprocess="xml-stripblanks">window.ui</file>
|
||||||
|
<file compressed="true" preprocess="xml-stripblanks">pane/main.ui</file>
|
||||||
|
<file compressed="true" preprocess="xml-stripblanks">pane/sidebar.ui</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">pref/dropdown.ui</file>
|
<file compressed="true" preprocess="xml-stripblanks">pref/dropdown.ui</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">pref/number.ui</file>
|
<file compressed="true" preprocess="xml-stripblanks">pref/number.ui</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">pref/entry.ui</file>
|
<file compressed="true" preprocess="xml-stripblanks">pref/entry.ui</file>
|
||||||
|
|
|
||||||
125
ui/rsc/window.ui
125
ui/rsc/window.ui
|
|
@ -11,132 +11,11 @@
|
||||||
<property name="max-sidebar-width">300</property>
|
<property name="max-sidebar-width">300</property>
|
||||||
<!-- Split View: Left Sidebar -->
|
<!-- Split View: Left Sidebar -->
|
||||||
<property name="sidebar">
|
<property name="sidebar">
|
||||||
<object class="AdwNavigationPage">
|
<object class="LSPaneSidebar" id="sidebar"/>
|
||||||
<child>
|
|
||||||
<object class="AdwToolbarView">
|
|
||||||
<!-- Left Sidebar: Header -->
|
|
||||||
<child type="top">
|
|
||||||
<object class="AdwHeaderBar">
|
|
||||||
<property name="title-widget">
|
|
||||||
<object class="AdwWindowTitle">
|
|
||||||
<property name="title">Profiles</property>
|
|
||||||
</object>
|
|
||||||
</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
|
|
||||||
<!-- Left Sidebar: Content -->
|
|
||||||
<!-- TODO: content -->
|
|
||||||
|
|
||||||
<!-- Left Sidebar: Footer -->
|
|
||||||
<child type="bottom">
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="margin-start">12</property>
|
|
||||||
<property name="margin-end">12</property>
|
|
||||||
<property name="margin-top">12</property>
|
|
||||||
<property name="margin-bottom">12</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButton">
|
|
||||||
<property name="label">Create New Profile</property>
|
|
||||||
<property name="css-classes">suggested-action</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</property>
|
</property>
|
||||||
<!-- Split View: Main Content -->
|
<!-- Split View: Main Content -->
|
||||||
<property name="content">
|
<property name="content">
|
||||||
<object class="AdwNavigationPage">
|
<object class="LSPaneMain" id="main"/>
|
||||||
<child>
|
|
||||||
<object class="AdwToolbarView">
|
|
||||||
<!-- Main Content: Header -->
|
|
||||||
<child type="top">
|
|
||||||
<object class="AdwHeaderBar">
|
|
||||||
<property name="title-widget">
|
|
||||||
<object class="AdwWindowTitle">
|
|
||||||
<property name="title">lsfg-vk Configuration Menu</property>
|
|
||||||
</object>
|
|
||||||
</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
|
|
||||||
<!-- Main Content: Content -->
|
|
||||||
<property name="content">
|
|
||||||
<object class="GtkScrolledWindow">
|
|
||||||
<property name="hscrollbar-policy">never</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="main_box">
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="margin-start">48</property>
|
|
||||||
<property name="margin-end">48</property>
|
|
||||||
<property name="margin-top">32</property>
|
|
||||||
<property name="margin-bottom">32</property>
|
|
||||||
<property name="spacing">32</property>
|
|
||||||
<child>
|
|
||||||
<!-- Content: Frame Generation -->
|
|
||||||
<object class="AdwPreferencesGroup">
|
|
||||||
<property name="title">Frame Generation</property>
|
|
||||||
<!-- Frame Generation: Multiplier -->
|
|
||||||
<child>
|
|
||||||
<object class="LSPrefNumber" id="pref_multiplier">
|
|
||||||
<property name="opt-name">Multiplier</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<!-- Frame Generation: Flow Scale -->
|
|
||||||
<child>
|
|
||||||
<object class="LSPrefSlider" id="pref_flow_scale">
|
|
||||||
<property name="opt-name">Flow Scale</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<!-- Frame Generation: Performance Mode -->
|
|
||||||
<child>
|
|
||||||
<object class="LSPrefSwitch" id="pref_performance_mode">
|
|
||||||
<property name="opt-name">Performance Mode</property>
|
|
||||||
<property name="default-state">false</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<!-- Content: Misc -->
|
|
||||||
<object class="AdwPreferencesGroup">
|
|
||||||
<property name="title">Frame Generation</property>
|
|
||||||
<!-- Misc: HDR Mode -->
|
|
||||||
<child>
|
|
||||||
<object class="LSPrefSwitch" id="pref_hdr_mode">
|
|
||||||
<property name="opt-name">HDR Mode</property>
|
|
||||||
<property name="default-state">false</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<!-- Misc: Experimental Present Mode -->
|
|
||||||
<child>
|
|
||||||
<object class="LSPrefDropdown" id="pref_experimental_present_mode">
|
|
||||||
<property name="opt-name">Experimental Present Mode</property>
|
|
||||||
<property name="default-selection">0</property>
|
|
||||||
<property name="options">
|
|
||||||
<object class="GtkStringList">
|
|
||||||
<items>
|
|
||||||
<item>vsync/fifo</item>
|
|
||||||
<item>mailbox</item>
|
|
||||||
<item>immediate</item>
|
|
||||||
</items>
|
|
||||||
</object>
|
|
||||||
</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</property>
|
</property>
|
||||||
</object>
|
</object>
|
||||||
</property>
|
</property>
|
||||||
|
|
|
||||||
129
ui/src/config.rs
Normal file
129
ui/src/config.rs
Normal file
|
|
@ -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<Arc<RwLock<TomlConfig>>> = OnceLock::new();
|
||||||
|
static CONFIG_WRITER: OnceLock<std::sync::mpsc::Sender<()>> = 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<TomlConfig, anyhow::Error> {
|
||||||
|
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: 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(())
|
||||||
|
}
|
||||||
68
ui/src/config/structs.rs
Normal file
68
ui/src/config/structs.rs
Normal file
|
|
@ -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<i64> 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<f64> 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<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<TomlGame>
|
||||||
|
}
|
||||||
131
ui/src/main.rs
131
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";
|
const APP_ID: &str = "gay.pancake.lsfg-vk.ConfigurationUi";
|
||||||
|
|
||||||
fn main() {
|
#[derive(Debug)]
|
||||||
let app = ui::App::new(APP_ID)
|
struct State {
|
||||||
.expect("Failed to create application");
|
// ui state
|
||||||
app.run()
|
selected_game: Option<usize>
|
||||||
|
}
|
||||||
|
|
||||||
|
static STATE: OnceLock<Arc<RwLock<State>>> = 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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
36
ui/src/ui.rs
36
ui/src/ui.rs
|
|
@ -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<adw::Application>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub fn new(appid: &str) -> Result<Self, glib::Error> {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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<window::Window>)
|
|
||||||
@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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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<PrefNumber>,
|
|
||||||
#[template_child]
|
|
||||||
pref_flow_scale: TemplateChild<PrefSlider>,
|
|
||||||
#[template_child]
|
|
||||||
pref_performance_mode: TemplateChild<PrefSwitch>,
|
|
||||||
#[template_child]
|
|
||||||
pref_hdr_mode: TemplateChild<PrefSwitch>,
|
|
||||||
#[template_child]
|
|
||||||
pref_experimental_present_mode: TemplateChild<PrefDropdown>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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<Self>) {
|
|
||||||
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 {}
|
|
||||||
77
ui/src/wrapper.rs
Normal file
77
ui/src/wrapper.rs
Normal file
|
|
@ -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<PaneMain>,
|
||||||
|
#[template_child]
|
||||||
|
pub sidebar: TemplateChild<PaneSidebar>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<Self>) {
|
||||||
|
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<imp::Window>)
|
||||||
|
@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()
|
||||||
|
}
|
||||||
|
}
|
||||||
35
ui/src/wrapper/pane.rs
Normal file
35
ui/src/wrapper/pane.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
use gtk::glib;
|
||||||
|
use gtk;
|
||||||
|
use adw;
|
||||||
|
|
||||||
|
pub mod main;
|
||||||
|
pub mod sidebar;
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct PaneMain(ObjectSubclass<main::PaneMain>)
|
||||||
|
@extends
|
||||||
|
adw::NavigationPage, gtk::Widget,
|
||||||
|
@implements
|
||||||
|
gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct PaneSidebar(ObjectSubclass<sidebar::PaneSidebar>)
|
||||||
|
@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()
|
||||||
|
}
|
||||||
|
}
|
||||||
43
ui/src/wrapper/pane/main.rs
Normal file
43
ui/src/wrapper/pane/main.rs
Normal file
|
|
@ -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<PrefNumber>,
|
||||||
|
#[template_child]
|
||||||
|
pub pref_flow_scale: TemplateChild<PrefSlider>,
|
||||||
|
#[template_child]
|
||||||
|
pub pref_performance_mode: TemplateChild<PrefSwitch>,
|
||||||
|
#[template_child]
|
||||||
|
pub pref_hdr_mode: TemplateChild<PrefSwitch>,
|
||||||
|
#[template_child]
|
||||||
|
pub pref_experimental_present_mode: TemplateChild<PrefDropdown>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<Self>) {
|
||||||
|
obj.init_template();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectImpl for PaneMain {
|
||||||
|
fn constructed(&self) {
|
||||||
|
self.parent_constructed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetImpl for PaneMain {}
|
||||||
|
impl NavigationPageImpl for PaneMain {}
|
||||||
33
ui/src/wrapper/pane/sidebar.rs
Normal file
33
ui/src/wrapper/pane/sidebar.rs
Normal file
|
|
@ -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<Self>) {
|
||||||
|
obj.init_template();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectImpl for PaneSidebar {
|
||||||
|
fn constructed(&self) {
|
||||||
|
self.parent_constructed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetImpl for PaneSidebar {}
|
||||||
|
impl NavigationPageImpl for PaneSidebar {}
|
||||||
Loading…
Add table
Reference in a new issue