From 1447baef40a228321a00b9b84ad57931177d8bc2 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 08:37:54 +0100
Subject: [PATCH 01/54] feat!: add libadwaita dependency

---
 Cargo.lock          | 37 +++++++++++++++++++++++++++++++++++--
 lact-gui/Cargo.toml |  1 +
 lact-gui/src/lib.rs |  2 +-
 3 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 41a02d7b..5d6416f3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1156,7 +1156,6 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
 dependencies = [
  "autocfg",
  "hashbrown 0.12.3",
- "serde",
 ]
 
 [[package]]
@@ -1167,6 +1166,7 @@ checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
 dependencies = [
  "equivalent",
  "hashbrown 0.14.2",
+ "serde",
 ]
 
 [[package]]
@@ -1257,6 +1257,7 @@ dependencies = [
  "gtk4",
  "lact-client",
  "lact-daemon",
+ "libadwaita",
  "pretty_assertions",
  "tracing",
  "tracing-subscriber",
@@ -1268,7 +1269,7 @@ version = "0.5.1"
 dependencies = [
  "amdgpu-sysfs",
  "clap",
- "indexmap 1.9.3",
+ "indexmap 2.1.0",
  "serde",
  "serde_json",
 ]
@@ -1279,6 +1280,38 @@ version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
+[[package]]
+name = "libadwaita"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fe7e70c06507ed10a16cda707f358fbe60fe0dc237498f78c686ade92fd979c"
+dependencies = [
+ "gdk-pixbuf",
+ "gdk4",
+ "gio",
+ "glib",
+ "gtk4",
+ "libadwaita-sys",
+ "libc",
+ "pango",
+]
+
+[[package]]
+name = "libadwaita-sys"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e10aaa38de1d53374f90deeb4535209adc40cc5dba37f9704724169bceec69a"
+dependencies = [
+ "gdk4-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "gtk4-sys",
+ "libc",
+ "pango-sys",
+ "system-deps",
+]
+
 [[package]]
 name = "libc"
 version = "0.2.150"
diff --git a/lact-gui/Cargo.toml b/lact-gui/Cargo.toml
index 3f06c07a..260a4268 100644
--- a/lact-gui/Cargo.toml
+++ b/lact-gui/Cargo.toml
@@ -15,6 +15,7 @@ gtk = { version = "0.7", package = "gtk4", features = ["v4_6", "blueprint"] }
 tracing = "0.1"
 tracing-subscriber = { version = "0.3", features = ["env-filter"] }
 anyhow = "1.0"
+libadwaita = { version = "0.5.3", features = ["v1_4"] }
 
 [dev-dependencies]
 pretty_assertions = "1.4.0"
diff --git a/lact-gui/src/lib.rs b/lact-gui/src/lib.rs
index 876b3ff9..33d145e1 100644
--- a/lact-gui/src/lib.rs
+++ b/lact-gui/src/lib.rs
@@ -17,7 +17,7 @@ pub fn run(args: GuiArgs) -> anyhow::Result<()> {
         .context("Invalid log level")?;
     tracing_subscriber::fmt().with_env_filter(env_filter).init();
 
-    if let Err(err) = gtk::init() {
+    if let Err(err) = libadwaita::init() {
         return Err(anyhow!("Cannot initialize GTK: {err}"));
     }
 

From bc5f83c3cf0390d2c8b4b9458b0bf6f9e86f4a30 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 08:58:10 +0100
Subject: [PATCH 02/54] feat: adwaita styling for daemon info dialog; can copy
 enable text with a button

---
 lact-gui/src/app/mod.rs | 58 +++++++++++++++++++++++------------------
 1 file changed, 33 insertions(+), 25 deletions(-)

diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index 1cf4a339..d82b1de1 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -15,6 +15,7 @@ use lact_client::schema::request::{ConfirmCommand, SetClocksCommand};
 use lact_client::schema::DeviceStats;
 use lact_client::DaemonClient;
 use lact_daemon::MODULE_CONF_PATH;
+use libadwaita::prelude::MessageDialogExt;
 use root_stack::RootStack;
 use std::cell::RefCell;
 use std::rc::Rc;
@@ -155,44 +156,51 @@ impl App {
                         format!("Error info: {err:#}\n\n")
                     }).unwrap_or_default();
 
-                    let text = format!("Could not connect to daemon, running in embedded mode. \n\
-                        Please make sure the lactd service is running. \n\
-                        Using embedded mode, you will not be able to change any settings. \n\n\
-                        {error_text}\
-                        To enable the daemon, run the following command:");
+                    let enable_text = "sudo systemctl enable --now lactd";
 
-                    let text_label = Label::new(Some(&text));
                     let enable_label = Entry::builder()
-                        .text("sudo systemctl enable --now lactd")
+                        .text(enable_text)
+                        .css_classes(["card"])
                         .editable(false)
+                        .hexpand(true)
                         .build();
 
-                    let vbox = Box::builder()
-                        .orientation(Orientation::Vertical)
-                        .margin_top(10)
-                        .margin_bottom(10)
-                        .margin_start(10)
-                        .margin_end(10)
+                    let hbox = Box::builder()
+                        .orientation(Orientation::Horizontal)
+                        .spacing(6)
+                        .build();
+                    let copy_btn = Button::builder()
+                        .css_classes(["circular", "flat"])
+                        .tooltip_text("Copy")
+                        .icon_name("edit-copy-symbolic")
                         .build();
 
-                    let close_button = Button::builder().label("Close").build();
+                    copy_btn.connect_clicked(move |_| {
+                        match gdk::Display::default() {
+                            None => eprintln!("Failed to get default gdk display"),
+                            Some(d) => d.clipboard().set_text(&enable_text)
+                        }
+                    });
 
-                    vbox.append(&text_label);
-                    vbox.append(&enable_label);
-                    vbox.append(&close_button);
+                    hbox.append(&enable_label);
+                    hbox.append(&copy_btn);
 
-                    let diag = MessageDialog::builder()
+                    let diag = libadwaita::MessageDialog::builder()
                         .title("Daemon info")
-                        .message_type(MessageType::Warning)
-                        .child(&vbox)
+                        .heading("Could not connect to daemon")
+                        .body(format!("Running in embedded mode.\n\
+                            Please make sure the lactd service is running.\n\
+                            Using embedded mode, you will not be able to change any settings.\n\n\
+                            {error_text}\
+                            To enable the daemon, run the following command:"))
+                        .extra_child(&hbox)
+                        .modal(true)
                         .transient_for(&app.window)
+                        .hide_on_close(true)
                         .build();
 
-                    close_button.connect_clicked(clone!(@strong diag => move |_| diag.hide()));
-
-                    diag.run_async(|diag, _| {
-                        diag.hide();
-                    })
+                    diag.add_response("close", "_Close");
+                    diag.present();
                 }
             }));
 

From d67e97726023568c9251b6f36b00572739bf46c7 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 11:27:42 +0100
Subject: [PATCH 03/54] feat: sidebar layout

---
 lact-gui/src/app/mod.rs                     | 108 ++++++++++++++++----
 lact-gui/src/app/{header.rs => toolbars.rs} |  49 ++++-----
 2 files changed, 113 insertions(+), 44 deletions(-)
 rename lact-gui/src/app/{header.rs => toolbars.rs} (59%)

diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index d82b1de1..244c697e 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -1,8 +1,8 @@
 mod apply_revealer;
-mod header;
 mod info_row;
 mod page_section;
 mod root_stack;
+mod toolbars;
 
 use crate::{APP_ID, GUI_VERSION};
 use anyhow::{anyhow, Context};
@@ -10,17 +10,17 @@ use apply_revealer::ApplyRevealer;
 use glib::clone;
 use gtk::glib::{timeout_future, ControlFlow};
 use gtk::{gio::ApplicationFlags, prelude::*, *};
-use header::Header;
 use lact_client::schema::request::{ConfirmCommand, SetClocksCommand};
 use lact_client::schema::DeviceStats;
 use lact_client::DaemonClient;
 use lact_daemon::MODULE_CONF_PATH;
-use libadwaita::prelude::MessageDialogExt;
+use libadwaita::prelude::{AdwApplicationWindowExt, MessageDialogExt};
 use root_stack::RootStack;
 use std::cell::RefCell;
 use std::rc::Rc;
 use std::sync::atomic::AtomicBool;
 use std::time::Duration;
+use toolbars::Toolbars;
 use tracing::{debug, error, trace, warn};
 
 // In ms
@@ -28,9 +28,9 @@ const STATS_POLL_INTERVAL: u64 = 250;
 
 #[derive(Clone)]
 pub struct App {
-    application: Application,
-    pub window: ApplicationWindow,
-    pub header: Header,
+    application: libadwaita::Application,
+    pub window: libadwaita::ApplicationWindow,
+    pub toolbars: Toolbars,
     root_stack: RootStack,
     apply_revealer: ApplyRevealer,
     daemon_client: DaemonClient,
@@ -38,18 +38,16 @@ pub struct App {
 
 impl App {
     pub fn new(daemon_client: DaemonClient) -> Self {
-        let application = Application::new(Some(APP_ID), ApplicationFlags::default());
+        let application = libadwaita::Application::new(Some(APP_ID), ApplicationFlags::default());
 
-        let header = Header::new();
-        let window = ApplicationWindow::builder()
+        let toolbars = Toolbars::new();
+        let window = libadwaita::ApplicationWindow::builder()
             .title("LACT")
             .default_width(600)
             .default_height(820)
             .icon_name(APP_ID)
             .build();
 
-        window.set_titlebar(Some(&header.container));
-
         let system_info_buf = daemon_client
             .get_system_info()
             .expect("Could not fetch system info");
@@ -62,22 +60,89 @@ impl App {
 
         let root_stack = RootStack::new(system_info, daemon_client.embedded);
 
-        header.set_switcher_stack(&root_stack.container);
+        {
+            let stack = root_stack.container.clone();
+            let title = toolbars.title.clone();
+            root_stack.container.connect_visible_child_notify(move |_| {
+                if let Some(child) = stack.visible_child() {
+                    title.set_title(stack.page(&child).title().unwrap().as_str())
+                }
+            });
+        }
 
-        let root_box = Box::new(Orientation::Vertical, 5);
+        let root_view = libadwaita::ToolbarView::new();
 
-        root_box.append(&root_stack.container);
+        root_view.add_top_bar(&toolbars.headerbar);
+        root_view.set_content(Some(&root_stack.container));
 
         let apply_revealer = ApplyRevealer::new();
 
-        root_box.append(&apply_revealer.container);
+        root_view.add_bottom_bar(&apply_revealer.container);
+
+        let sidebar_view = libadwaita::ToolbarView::new();
+
+        let stack_sidebar = StackSidebar::builder()
+            .stack(&root_stack.container)
+            .vexpand(true)
+            .build();
+        stack_sidebar.remove_css_class("sidebar");
+        sidebar_view.add_top_bar(
+            &libadwaita::HeaderBar::builder()
+                .title_widget(&libadwaita::WindowTitle::builder().title("LACT").build())
+                .build(),
+        );
+        sidebar_view.set_content(Some(
+            &ScrolledWindow::builder()
+                .child(&stack_sidebar)
+                .vexpand(true)
+                .hscrollbar_policy(PolicyType::Never)
+                .build(),
+        ));
+        sidebar_view.add_bottom_bar(&toolbars.gpu_selector);
+
+        let split_view = libadwaita::NavigationSplitView::builder()
+            .sidebar(
+                &libadwaita::NavigationPage::builder()
+                    .child(&sidebar_view)
+                    .build(),
+            )
+            .content(
+                &libadwaita::NavigationPage::builder()
+                    .child(&root_view)
+                    .build(),
+            )
+            .build();
+
+        {
+            let split_view = split_view.clone();
+            let lb = stack_sidebar
+                .first_child()
+                .unwrap()
+                .first_child()
+                .unwrap()
+                .first_child()
+                .unwrap()
+                .downcast::<ListBox>()
+                .unwrap();
+            lb.connect_row_activated(move |_lb, _row| {
+                split_view.set_show_content(true);
+            });
+        }
+
+        let breakpoint = libadwaita::Breakpoint::new(libadwaita::BreakpointCondition::new_length(
+            libadwaita::BreakpointConditionLengthType::MaxWidth,
+            800.0,
+            libadwaita::LengthUnit::Sp,
+        ));
+        breakpoint.add_setter(&split_view, "collapsed", &glib::Value::from(true));
+        window.add_breakpoint(breakpoint);
 
-        window.set_child(Some(&root_box));
+        window.set_content(Some(&split_view));
 
         App {
             application,
             window,
-            header,
+            toolbars,
             root_stack,
             apply_revealer,
             daemon_client,
@@ -91,7 +156,7 @@ impl App {
 
                 let current_gpu_id = Rc::new(RefCell::new(String::new()));
 
-                app.header.connect_gpu_selection_changed(clone!(@strong app, @strong current_gpu_id => move |gpu_id| {
+                app.toolbars.connect_gpu_selection_changed(clone!(@strong app, @strong current_gpu_id => move |gpu_id| {
                     debug!("GPU Selection changed");
                     app.set_info(&gpu_id);
                     *current_gpu_id.borrow_mut() = gpu_id;
@@ -103,7 +168,7 @@ impl App {
                     .list_devices()
                     .expect("Could not list devices");
                 let devices = devices_buf.inner().expect("Could not access devices");
-                app.header.set_devices(&devices);
+                app.toolbars.set_devices(&devices);
 
                 app.root_stack.oc_page.clocks_frame.connect_clocks_reset(clone!(@strong app, @strong current_gpu_id => move || {
                     debug!("Resetting clocks");
@@ -474,6 +539,7 @@ impl App {
 
     fn enable_overclocking(&self) {
         let text = format!("This will enable the overdrive feature of the amdgpu driver by creating a file at <b>{MODULE_CONF_PATH}</b>. Are you sure you want to do this?");
+        // TODO: to libadwaita
         let dialog = MessageDialog::builder()
             .title("Enable Overclocking")
             .use_markup(true)
@@ -508,6 +574,7 @@ impl App {
 
     fn ask_confirmation(&self, gpu_id: String, mut delay: u64) {
         let text = confirmation_text(delay);
+        // TODO: to libadwaita
         let dialog = MessageDialog::builder()
             .title("Confirm settings")
             .text(text)
@@ -562,9 +629,10 @@ enum GuiUpdateMsg {
     GpuStats(DeviceStats),
 }
 
-fn show_error(parent: &ApplicationWindow, err: anyhow::Error) {
+fn show_error(parent: &libadwaita::ApplicationWindow, err: anyhow::Error) {
     let text = format!("{err:?}");
     warn!("{}", text.trim());
+    // TODO: to libadwaita
     let diag = MessageDialog::builder()
         .title("Error")
         .message_type(MessageType::Error)
diff --git a/lact-gui/src/app/header.rs b/lact-gui/src/app/toolbars.rs
similarity index 59%
rename from lact-gui/src/app/header.rs
rename to lact-gui/src/app/toolbars.rs
index abd9e133..22f9d1dc 100644
--- a/lact-gui/src/app/header.rs
+++ b/lact-gui/src/app/toolbars.rs
@@ -4,37 +4,38 @@ use lact_client::schema::DeviceListEntry;
 use pango::EllipsizeMode;
 
 #[derive(Clone)]
-pub struct Header {
-    pub container: HeaderBar,
-    gpu_selector: ComboBoxText,
-    switcher: StackSwitcher,
+pub struct Toolbars {
+    pub headerbar: libadwaita::HeaderBar,
+    pub gpu_selector: ComboBoxText,
+    pub title: libadwaita::WindowTitle,
 }
 
-impl Header {
+impl Toolbars {
     pub fn new() -> Self {
-        let container = HeaderBar::new();
-
-        container.set_title_widget(Some(&Box::default())); // Workaround to hide the title
-
-        container.set_show_title_buttons(true);
-
-        let gpu_selector = ComboBoxText::new();
-        container.pack_start(&gpu_selector);
-
-        let switcher = StackSwitcher::new();
-        container.pack_start(&switcher);
-
-        Header {
-            container,
+        let title = libadwaita::WindowTitle::builder()
+            .title("Information")
+            .build();
+
+        let headerbar = libadwaita::HeaderBar::builder()
+            .title_widget(&title)
+            .show_title(true)
+            .build();
+
+        // TODO: new gtk select
+        let gpu_selector = ComboBoxText::builder()
+            .margin_top(12)
+            .margin_bottom(12)
+            .margin_start(12)
+            .margin_end(12)
+            .build();
+
+        Self {
+            headerbar,
             gpu_selector,
-            switcher,
+            title,
         }
     }
 
-    pub fn set_switcher_stack(&self, stack: &Stack) {
-        self.switcher.set_stack(Some(stack));
-    }
-
     pub fn set_devices(&self, gpus: &[DeviceListEntry<'_>]) {
         for (i, entry) in gpus.iter().enumerate() {
             let name = format!("{i}: {}", entry.name.unwrap_or_default());

From 2afee4233e8138e70b91dee1413a32c71ec2166b Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 12:54:44 +0100
Subject: [PATCH 04/54] feat: boxed-list style for info page

---
 lact-gui/src/app/mod.rs                       |   5 +-
 lact-gui/src/app/page_section.rs              |   3 -
 lact-gui/src/app/root_stack/info_page/mod.rs  | 317 ++++++++++--------
 .../root_stack/info_page/vulkan_info/mod.rs   |  87 ++---
 lact-gui/src/app/root_stack/mod.rs            |  10 +-
 lact-gui/ui/page_section.blp                  |   8 +-
 6 files changed, 227 insertions(+), 203 deletions(-)

diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index 244c697e..219bb0f7 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -43,8 +43,10 @@ impl App {
         let toolbars = Toolbars::new();
         let window = libadwaita::ApplicationWindow::builder()
             .title("LACT")
-            .default_width(600)
+            .default_width(700)
             .default_height(820)
+            .width_request(300)
+            .height_request(200)
             .icon_name(APP_ID)
             .build();
 
@@ -104,6 +106,7 @@ impl App {
             .sidebar(
                 &libadwaita::NavigationPage::builder()
                     .child(&sidebar_view)
+                    .title("LACT")
                     .build(),
             )
             .content(
diff --git a/lact-gui/src/app/page_section.rs b/lact-gui/src/app/page_section.rs
index 22155e16..489eba49 100644
--- a/lact-gui/src/app/page_section.rs
+++ b/lact-gui/src/app/page_section.rs
@@ -67,9 +67,6 @@ mod imp {
             let obj = self.obj();
 
             obj.bind_property("name", &self.section_label.get(), "label")
-                .transform_to(|_, value: String| {
-                    Some(format!("<span font_desc='13'><b>{value}</b></span>"))
-                })
                 .build();
         }
     }
diff --git a/lact-gui/src/app/root_stack/info_page/mod.rs b/lact-gui/src/app/root_stack/info_page/mod.rs
index 4ca3b532..de125e58 100644
--- a/lact-gui/src/app/root_stack/info_page/mod.rs
+++ b/lact-gui/src/app/root_stack/info_page/mod.rs
@@ -1,31 +1,58 @@
 mod vulkan_info;
 
-use super::{label_row, values_grid};
 use crate::app::page_section::PageSection;
 use gtk::prelude::*;
 use gtk::*;
 use lact_client::schema::{DeviceInfo, DeviceStats};
+use libadwaita::prelude::ActionRowExt;
 use vulkan_info::VulkanInfoFrame;
 
+#[derive(Clone)]
+pub struct InfoRow {
+    pub container: libadwaita::ActionRow,
+    content_label: Label,
+}
+
+impl InfoRow {
+    pub fn new(title: &str) -> Self {
+        let container = libadwaita::ActionRow::builder().title(title).build();
+        let label = Label::builder()
+            .css_classes(["dim-label"])
+            .ellipsize(pango::EllipsizeMode::End)
+            .selectable(true)
+            .build();
+        container.add_suffix(&label);
+
+        Self {
+            container,
+            content_label: label,
+        }
+    }
+
+    pub fn set_content(&self, content: &str) {
+        self.content_label.set_label(content);
+    }
+}
+
 #[derive(Clone)]
 pub struct InformationPage {
     pub container: ScrolledWindow,
-    gpu_name_label: Label,
-    gpu_manufacturer_label: Label,
-    family_name: Label,
-    asic_name: Label,
-    vbios_version_label: Label,
-    driver_label: Label,
-    vram_size_label: Label,
-    vram_type_label: Label,
-    vram_peak_bw_label: Label,
-    compute_units_label: Label,
-    l1_cache_label: Label,
-    l2_cache_label: Label,
-    l3_cache_label: Label,
-    resizable_bar_enabled: Label,
-    cpu_accessible_vram_label: Label,
-    link_speed_label: Label,
+    gpu_name_row: InfoRow,
+    gpu_manufacturer_row: InfoRow,
+    family_name_row: InfoRow,
+    asic_name_row: InfoRow,
+    vbios_version_row: InfoRow,
+    driver_row: InfoRow,
+    vram_size_row: InfoRow,
+    vram_type_row: InfoRow,
+    vram_peak_bw_row: InfoRow,
+    compute_units_row: InfoRow,
+    l1_cache_row: InfoRow,
+    l2_cache_row: InfoRow,
+    l3_cache_row: InfoRow,
+    resizable_bar_enabled_row: InfoRow,
+    cpu_accessible_vram_row: InfoRow,
+    link_speed_row: InfoRow,
     vulkan_info_frame: VulkanInfoFrame,
     vulkan_unavailable_label: Label,
 }
@@ -34,44 +61,53 @@ impl InformationPage {
     pub fn new() -> Self {
         let vbox = Box::builder()
             .orientation(Orientation::Vertical)
-            .spacing(15)
-            .margin_start(20)
-            .margin_end(20)
+            .spacing(12)
             .build();
 
         let info_container = PageSection::new("Hardware Information");
 
-        let values_grid = values_grid();
-
-        // Dummy label to prevent the gpu name label from stealing focus
-        let dummy_label = Label::builder().selectable(true).halign(Align::End).build();
-        values_grid.attach(&dummy_label, 0, 0, 1, 1);
-
-        let mut row = 0;
-        let gpu_name_label = sequential_label_row("GPU Model:", &values_grid, &mut row);
-        let gpu_manufacturer_label =
-            sequential_label_row("GPU Manufacturer:", &values_grid, &mut row);
-        let family_name = sequential_label_row("GPU Family:", &values_grid, &mut row);
-        let asic_name = sequential_label_row("ASIC Name:", &values_grid, &mut row);
-        let compute_units_label = sequential_label_row("Compute Units:", &values_grid, &mut row);
-        let vbios_version_label = sequential_label_row("VBIOS Version:", &values_grid, &mut row);
-        let driver_label = sequential_label_row("Driver Used:", &values_grid, &mut row);
-
-        let vram_size_label = sequential_label_row("VRAM Size:", &values_grid, &mut row);
-        let vram_type_label = sequential_label_row("VRAM Type:", &values_grid, &mut row);
-        let vram_peak_bw_label =
-            sequential_label_row("Peak VRAM Bandwidth:", &values_grid, &mut row);
-
-        let l1_cache_label = sequential_label_row("L1 Cache (Per CU):", &values_grid, &mut row);
-        let l2_cache_label = sequential_label_row("L2 Cache:", &values_grid, &mut row);
-        let l3_cache_label = sequential_label_row("L3 Cache:", &values_grid, &mut row);
-
-        let resizable_bar_enabled = sequential_label_row("Resizeable BAR:", &values_grid, &mut row);
-        let cpu_accessible_vram_label =
-            sequential_label_row("CPU Accessible VRAM:", &values_grid, &mut row);
-        let link_speed_label = sequential_label_row("Link Speed:", &values_grid, &mut row);
-
-        info_container.append(&values_grid);
+        let info_listbox = ListBox::builder()
+            .css_classes(["boxed-list"])
+            .selection_mode(SelectionMode::None)
+            .build();
+
+        let gpu_name_row = InfoRow::new("GPU Model");
+        let gpu_manufacturer_row = InfoRow::new("GPU Manufacturer");
+        let family_name_row = InfoRow::new("GPU Family");
+        let asic_name_row = InfoRow::new("ASIC Name");
+        let compute_units_row = InfoRow::new("Compute Units");
+        let vbios_version_row = InfoRow::new("VBIOS Version");
+        let driver_row = InfoRow::new("Driver Used");
+
+        let vram_size_row = InfoRow::new("VRAM Size");
+        let vram_type_row = InfoRow::new("VRAM Type");
+        let vram_peak_bw_row = InfoRow::new("Peak VRAM Bandwidth");
+
+        let l1_cache_row = InfoRow::new("L1 Cache (Per CU)");
+        let l2_cache_row = InfoRow::new("L2 Cache");
+        let l3_cache_row = InfoRow::new("L3 Cache");
+
+        let resizable_bar_enabled_row = InfoRow::new("Resizeable BAR");
+        let cpu_accessible_vram_row = InfoRow::new("CPU Accessible VRAM");
+        let link_speed_row = InfoRow::new("Link Speed");
+
+        info_listbox.append(&gpu_name_row.container);
+        info_listbox.append(&gpu_manufacturer_row.container);
+        info_listbox.append(&family_name_row.container);
+        info_listbox.append(&asic_name_row.container);
+        info_listbox.append(&compute_units_row.container);
+        info_listbox.append(&vbios_version_row.container);
+        info_listbox.append(&driver_row.container);
+        info_listbox.append(&vram_size_row.container);
+        info_listbox.append(&vram_type_row.container);
+        info_listbox.append(&vram_peak_bw_row.container);
+        info_listbox.append(&l1_cache_row.container);
+        info_listbox.append(&l2_cache_row.container);
+        info_listbox.append(&l3_cache_row.container);
+        info_listbox.append(&resizable_bar_enabled_row.container);
+        info_listbox.append(&cpu_accessible_vram_row.container);
+        info_listbox.append(&link_speed_row.container);
+        info_container.append(&info_listbox);
         vbox.append(&info_container);
 
         let vulkan_container = PageSection::new("Vulkan Information");
@@ -81,71 +117,77 @@ impl InformationPage {
 
         let vulkan_unavailable_label = Label::builder()
             .label("Vulkan is not available on this GPU")
+            .css_classes(["error"])
             .visible(false)
-            .margin_start(10)
-            .margin_end(10)
-            .halign(Align::Start)
+            .xalign(0.0)
             .build();
         vulkan_container.append(&vulkan_unavailable_label);
 
         vbox.append(&vulkan_container);
 
+        let clamp = libadwaita::Clamp::builder()
+            .maximum_size(600)
+            .margin_top(24)
+            .margin_bottom(24)
+            .child(&vbox)
+            .build();
+
         let container = ScrolledWindow::builder()
             .hscrollbar_policy(PolicyType::Never)
-            .child(&vbox)
+            .child(&clamp)
             .build();
 
         Self {
             container,
-            gpu_name_label,
-            gpu_manufacturer_label,
-            vbios_version_label,
-            driver_label,
-            vram_size_label,
-            link_speed_label,
+            gpu_name_row,
+            gpu_manufacturer_row,
+            vbios_version_row,
+            driver_row,
+            vram_size_row,
+            link_speed_row,
             vulkan_info_frame,
-            family_name,
-            asic_name,
-            vram_type_label,
-            resizable_bar_enabled,
-            cpu_accessible_vram_label,
-            compute_units_label,
-            vram_peak_bw_label,
-            l1_cache_label,
-            l2_cache_label,
-            l3_cache_label,
+            family_name_row,
+            asic_name_row,
+            vram_type_row,
+            resizable_bar_enabled_row,
+            cpu_accessible_vram_row,
+            compute_units_row,
+            vram_peak_bw_row,
+            l1_cache_row,
+            l2_cache_row,
+            l3_cache_row,
             vulkan_unavailable_label,
         }
     }
 
     pub fn set_info(&self, gpu_info: &DeviceInfo) {
-        let gpu_name = gpu_info
-            .pci_info
-            .as_ref()
-            .and_then(|pci_info| {
-                pci_info
-                    .subsystem_pci_info
-                    .model
-                    .as_deref()
-                    .or(pci_info.device_pci_info.model.as_deref())
-            })
-            .unwrap_or_default();
-        self.gpu_name_label
-            .set_markup(&format!("<b>{gpu_name}</b>"));
-
-        let gpu_manufacturer = gpu_info
-            .pci_info
-            .as_ref()
-            .and_then(|pci_info| {
-                pci_info
-                    .subsystem_pci_info
-                    .vendor
-                    .as_deref()
-                    .or(pci_info.device_pci_info.model.as_deref())
-            })
-            .unwrap_or_default();
-        self.gpu_manufacturer_label
-            .set_markup(&format!("<b>{gpu_manufacturer}</b>"));
+        self.gpu_name_row.set_content(
+            gpu_info
+                .pci_info
+                .as_ref()
+                .and_then(|pci_info| {
+                    pci_info
+                        .subsystem_pci_info
+                        .model
+                        .as_deref()
+                        .or(pci_info.device_pci_info.model.as_deref())
+                })
+                .unwrap_or_default(),
+        );
+
+        self.gpu_manufacturer_row.set_content(
+            gpu_info
+                .pci_info
+                .as_ref()
+                .and_then(|pci_info| {
+                    pci_info
+                        .subsystem_pci_info
+                        .vendor
+                        .as_deref()
+                        .or(pci_info.device_pci_info.model.as_deref())
+                })
+                .unwrap_or_default(),
+        );
 
         let mut family_name = "Unknown";
         let mut asic_name = "Unknown";
@@ -179,47 +221,35 @@ impl InformationPage {
             }
         }
 
-        self.family_name
-            .set_markup(&format!("<b>{family_name}</b>"));
-        self.asic_name.set_markup(&format!("<b>{asic_name}</b>"));
-        self.compute_units_label
-            .set_markup(&format!("<b>{compute_units}</b>"));
-        self.vram_type_label
-            .set_markup(&format!("<b>{vram_type}</b>"));
-        self.vram_peak_bw_label
-            .set_markup(&format!("<b>{vram_max_bw} GiB/s</b>"));
-
-        self.l1_cache_label
-            .set_markup(&format!("<b>{l1_cache}</b>"));
-        self.l2_cache_label
-            .set_markup(&format!("<b>{l2_cache}</b>"));
-        self.l3_cache_label
-            .set_markup(&format!("<b>{l3_cache}</b>"));
-
-        self.resizable_bar_enabled
-            .set_markup(&format!("<b>{resizeable_bar_enabled}</b>"));
-        self.cpu_accessible_vram_label
-            .set_markup(&format!("<b>{cpu_accessible_vram} MiB</b>"));
-
-        let vbios_version = gpu_info.vbios_version.as_deref().unwrap_or("Unknown");
-        self.vbios_version_label
-            .set_markup(&format!("<b>{vbios_version}</b>"));
-
-        self.driver_label
-            .set_markup(&format!("<b>{}</b>", gpu_info.driver));
-
-        let link_speed = gpu_info
-            .link_info
-            .current_speed
-            .as_deref()
-            .unwrap_or("Unknown");
-        let link_width = gpu_info
-            .link_info
-            .current_width
-            .as_deref()
-            .unwrap_or("Unknown");
-        self.link_speed_label
-            .set_markup(&format!("<b>{link_speed} x{link_width}</b>",));
+        self.family_name_row.set_content(family_name);
+        self.asic_name_row.set_content(asic_name);
+        self.compute_units_row.set_content(&compute_units);
+        self.vbios_version_row
+            .set_content(gpu_info.vbios_version.as_deref().unwrap_or("Unknown"));
+        self.driver_row.set_content(gpu_info.driver);
+        self.vram_type_row.set_content(vram_type);
+        self.vram_peak_bw_row
+            .set_content(&format!("{vram_max_bw} GiB/s"));
+        self.l1_cache_row.set_content(&l1_cache);
+        self.l2_cache_row.set_content(&l2_cache);
+        self.l3_cache_row.set_content(&l3_cache);
+        self.resizable_bar_enabled_row
+            .set_content(resizeable_bar_enabled);
+        self.cpu_accessible_vram_row
+            .set_content(&cpu_accessible_vram);
+        self.link_speed_row.set_content(&format!(
+            "{link_speed} x{link_width}",
+            link_speed = gpu_info
+                .link_info
+                .current_speed
+                .as_deref()
+                .unwrap_or("Unknown"),
+            link_width = gpu_info
+                .link_info
+                .current_width
+                .as_deref()
+                .unwrap_or("Unknown")
+        ));
 
         if let Some(vulkan_info) = &gpu_info.vulkan_info {
             self.vulkan_info_frame.set_info(vulkan_info);
@@ -236,13 +266,6 @@ impl InformationPage {
             || "Unknown".to_owned(),
             |size| (size / 1024 / 1024).to_string(),
         );
-        self.vram_size_label
-            .set_markup(&format!("<b>{vram_size} MiB</b>"));
+        self.vram_size_row.set_content(&format!("{vram_size} MiB"));
     }
 }
-
-fn sequential_label_row(title: &str, parent: &Grid, row: &mut i32) -> Label {
-    let label = label_row(title, parent, *row, 0, true);
-    *row += 1;
-    label
-}
diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
index 6da11dd7..c3c25fd1 100644
--- a/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
+++ b/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
@@ -2,60 +2,73 @@ mod feature_window;
 
 use self::feature_window::VulkanFeaturesWindow;
 
-use super::values_grid;
 use crate::app::root_stack::info_page::vulkan_info::feature_window::feature::VulkanFeature;
-use crate::app::root_stack::{label_row, values_row};
 use glib::clone;
 use gtk::prelude::*;
 use gtk::*;
 use lact_client::schema::VulkanInfo;
+use libadwaita::prelude::ActionRowExt;
 use tracing::trace;
 
+use super::InfoRow;
+
 #[derive(Clone)]
 pub struct VulkanInfoFrame {
-    pub container: Box,
-    device_name_label: Label,
-    version_label: Label,
-    driver_name_label: Label,
-    driver_version_label: Label,
+    pub container: ListBox,
+    device_name_row: InfoRow,
+    version_row: InfoRow,
+    driver_name_row: InfoRow,
+    driver_version_row: InfoRow,
     features_model: gio::ListStore,
     extensions_model: gio::ListStore,
 }
 
 impl VulkanInfoFrame {
     pub fn new() -> Self {
-        let container = Box::new(Orientation::Vertical, 0);
+        let container = ListBox::builder()
+            .css_classes(["boxed-list"])
+            .selection_mode(SelectionMode::None)
+            .build();
 
         let features_model = gio::ListStore::new::<VulkanFeature>();
         let extensions_model = gio::ListStore::new::<VulkanFeature>();
 
-        let grid = values_grid();
-
-        let device_name_label = label_row("Device name", &grid, 0, 0, true);
-        let version_label = label_row("Vulkan version:", &grid, 1, 0, true);
-        let driver_name_label = label_row("Driver name:", &grid, 2, 0, true);
-        let driver_version_label = label_row("Driver version:", &grid, 3, 0, true);
-
-        let show_features_button = Button::builder().label("Show").halign(Align::End).build();
-        show_features_button.connect_clicked(clone!(@strong features_model => move |_| {
+        let device_name_row = InfoRow::new("Device name");
+        let version_row = InfoRow::new("Vulkan version");
+        let driver_name_row = InfoRow::new("Driver name");
+        let driver_version_row = InfoRow::new("Driver version");
+
+        container.append(&device_name_row.container);
+        container.append(&version_row.container);
+        container.append(&driver_name_row.container);
+        container.append(&driver_version_row.container);
+
+        let features_row = libadwaita::ActionRow::builder()
+            .activatable(true)
+            .title("Features")
+            .build();
+        features_row.add_suffix(&Image::from_icon_name("go-next-symbolic"));
+        features_row.connect_activated(clone!(@strong features_model => move |_| {
             show_features_window("Vulkan features", features_model.clone());
         }));
-        values_row("Features:", &grid, &show_features_button, 4, 0);
-
-        let show_extensions_button = Button::builder().label("Show").halign(Align::End).build();
-        show_extensions_button.connect_clicked(clone!(@strong extensions_model => move |_| {
+        container.append(&features_row);
+
+        let extensions_row = libadwaita::ActionRow::builder()
+            .activatable(true)
+            .title("Extensions")
+            .build();
+        extensions_row.add_suffix(&Image::from_icon_name("go-next-symbolic"));
+        extensions_row.connect_activated(clone!(@strong extensions_model => move |_| {
             show_features_window("Vulkan extensions", extensions_model.clone());
         }));
-        values_row("Extensions:", &grid, &show_extensions_button, 5, 0);
-
-        container.append(&grid);
+        container.append(&extensions_row);
 
         Self {
             container,
-            device_name_label,
-            version_label,
-            driver_name_label,
-            driver_version_label,
+            device_name_row,
+            version_row,
+            driver_name_row,
+            driver_version_row,
             features_model,
             extensions_model,
         }
@@ -64,20 +77,14 @@ impl VulkanInfoFrame {
     pub fn set_info(&self, vulkan_info: &VulkanInfo) {
         trace!("setting vulkan info: {:?}", vulkan_info);
 
-        self.device_name_label
-            .set_markup(&format!("<b>{}</b>", vulkan_info.device_name));
-        self.version_label
-            .set_markup(&format!("<b>{}</b>", vulkan_info.api_version));
+        self.device_name_row.set_content(&vulkan_info.device_name);
+        self.version_row.set_content(&vulkan_info.api_version);
 
-        self.driver_name_label.set_markup(&format!(
-            "<b>{}</b>",
-            vulkan_info.driver.name.as_deref().unwrap_or_default(),
-        ));
+        self.driver_name_row
+            .set_content(&vulkan_info.driver.name.as_deref().unwrap_or_default());
 
-        self.driver_version_label.set_markup(&format!(
-            "<b>{}</b>",
-            vulkan_info.driver.info.as_deref().unwrap_or_default(),
-        ));
+        self.driver_version_row
+            .set_content(&vulkan_info.driver.info.as_deref().unwrap_or_default());
 
         self.features_model.remove_all();
         for (name, supported) in &vulkan_info.features {
diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs
index 68f3e731..3a190270 100644
--- a/lact-gui/src/app/root_stack/mod.rs
+++ b/lact-gui/src/app/root_stack/mod.rs
@@ -21,12 +21,7 @@ pub struct RootStack {
 
 impl RootStack {
     pub fn new(system_info: SystemInfo, embedded_daemon: bool) -> Self {
-        let container = Stack::builder()
-            .vexpand(true)
-            .margin_top(15)
-            .margin_start(30)
-            .margin_end(30)
-            .build();
+        let container = Stack::builder().vexpand(true).hexpand(true).build();
 
         let info_page = InformationPage::new();
 
@@ -52,6 +47,7 @@ impl RootStack {
     }
 }
 
+#[deprecated]
 fn values_row<W: IsA<Widget>>(
     title: &str,
     parent: &Grid,
@@ -65,6 +61,7 @@ fn values_row<W: IsA<Widget>>(
     parent.attach(value_child, column_offset + 1, row, 1, 1);
 }
 
+#[deprecated]
 fn label_row(title: &str, parent: &Grid, row: i32, column_offset: i32, selectable: bool) -> Label {
     let value_label = Label::builder()
         .halign(Align::End)
@@ -76,6 +73,7 @@ fn label_row(title: &str, parent: &Grid, row: i32, column_offset: i32, selectabl
     value_label
 }
 
+#[deprecated]
 fn values_grid() -> Grid {
     Grid::builder()
         .margin_start(10)
diff --git a/lact-gui/ui/page_section.blp b/lact-gui/ui/page_section.blp
index a00d6ac3..f8e61900 100644
--- a/lact-gui/ui/page_section.blp
+++ b/lact-gui/ui/page_section.blp
@@ -2,14 +2,10 @@ using Gtk 4.0;
 
 template $PageSection: Box {
     orientation: vertical;
-    spacing: 5;
-    margin-start: 5;
-    margin-end: 5;
+    spacing: 12;
 
     Label section_label {
+        styles ["title-3"]
         halign: start;
-        use-markup: true;
-        margin-top: 5;
-        margin-bottom: 5;
     }
 }

From c8f89d6d12399d48dc709ddf581d2268468a1cf9 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 13:00:55 +0100
Subject: [PATCH 05/54] chore: rename InfoRow to LabelRow and move to
 root_stack mod

---
 lact-gui/src/app/root_stack/info_page/mod.rs  | 99 +++++++------------
 .../root_stack/info_page/vulkan_info/mod.rs   | 20 ++--
 lact-gui/src/app/root_stack/mod.rs            | 28 ++++++
 3 files changed, 73 insertions(+), 74 deletions(-)

diff --git a/lact-gui/src/app/root_stack/info_page/mod.rs b/lact-gui/src/app/root_stack/info_page/mod.rs
index de125e58..cde7cfed 100644
--- a/lact-gui/src/app/root_stack/info_page/mod.rs
+++ b/lact-gui/src/app/root_stack/info_page/mod.rs
@@ -1,58 +1,31 @@
 mod vulkan_info;
 
+use super::LabelRow;
 use crate::app::page_section::PageSection;
 use gtk::prelude::*;
 use gtk::*;
 use lact_client::schema::{DeviceInfo, DeviceStats};
-use libadwaita::prelude::ActionRowExt;
 use vulkan_info::VulkanInfoFrame;
 
-#[derive(Clone)]
-pub struct InfoRow {
-    pub container: libadwaita::ActionRow,
-    content_label: Label,
-}
-
-impl InfoRow {
-    pub fn new(title: &str) -> Self {
-        let container = libadwaita::ActionRow::builder().title(title).build();
-        let label = Label::builder()
-            .css_classes(["dim-label"])
-            .ellipsize(pango::EllipsizeMode::End)
-            .selectable(true)
-            .build();
-        container.add_suffix(&label);
-
-        Self {
-            container,
-            content_label: label,
-        }
-    }
-
-    pub fn set_content(&self, content: &str) {
-        self.content_label.set_label(content);
-    }
-}
-
 #[derive(Clone)]
 pub struct InformationPage {
     pub container: ScrolledWindow,
-    gpu_name_row: InfoRow,
-    gpu_manufacturer_row: InfoRow,
-    family_name_row: InfoRow,
-    asic_name_row: InfoRow,
-    vbios_version_row: InfoRow,
-    driver_row: InfoRow,
-    vram_size_row: InfoRow,
-    vram_type_row: InfoRow,
-    vram_peak_bw_row: InfoRow,
-    compute_units_row: InfoRow,
-    l1_cache_row: InfoRow,
-    l2_cache_row: InfoRow,
-    l3_cache_row: InfoRow,
-    resizable_bar_enabled_row: InfoRow,
-    cpu_accessible_vram_row: InfoRow,
-    link_speed_row: InfoRow,
+    gpu_name_row: LabelRow,
+    gpu_manufacturer_row: LabelRow,
+    family_name_row: LabelRow,
+    asic_name_row: LabelRow,
+    vbios_version_row: LabelRow,
+    driver_row: LabelRow,
+    vram_size_row: LabelRow,
+    vram_type_row: LabelRow,
+    vram_peak_bw_row: LabelRow,
+    compute_units_row: LabelRow,
+    l1_cache_row: LabelRow,
+    l2_cache_row: LabelRow,
+    l3_cache_row: LabelRow,
+    resizable_bar_enabled_row: LabelRow,
+    cpu_accessible_vram_row: LabelRow,
+    link_speed_row: LabelRow,
     vulkan_info_frame: VulkanInfoFrame,
     vulkan_unavailable_label: Label,
 }
@@ -71,25 +44,25 @@ impl InformationPage {
             .selection_mode(SelectionMode::None)
             .build();
 
-        let gpu_name_row = InfoRow::new("GPU Model");
-        let gpu_manufacturer_row = InfoRow::new("GPU Manufacturer");
-        let family_name_row = InfoRow::new("GPU Family");
-        let asic_name_row = InfoRow::new("ASIC Name");
-        let compute_units_row = InfoRow::new("Compute Units");
-        let vbios_version_row = InfoRow::new("VBIOS Version");
-        let driver_row = InfoRow::new("Driver Used");
-
-        let vram_size_row = InfoRow::new("VRAM Size");
-        let vram_type_row = InfoRow::new("VRAM Type");
-        let vram_peak_bw_row = InfoRow::new("Peak VRAM Bandwidth");
-
-        let l1_cache_row = InfoRow::new("L1 Cache (Per CU)");
-        let l2_cache_row = InfoRow::new("L2 Cache");
-        let l3_cache_row = InfoRow::new("L3 Cache");
-
-        let resizable_bar_enabled_row = InfoRow::new("Resizeable BAR");
-        let cpu_accessible_vram_row = InfoRow::new("CPU Accessible VRAM");
-        let link_speed_row = InfoRow::new("Link Speed");
+        let gpu_name_row = LabelRow::new("GPU Model");
+        let gpu_manufacturer_row = LabelRow::new("GPU Manufacturer");
+        let family_name_row = LabelRow::new("GPU Family");
+        let asic_name_row = LabelRow::new("ASIC Name");
+        let compute_units_row = LabelRow::new("Compute Units");
+        let vbios_version_row = LabelRow::new("VBIOS Version");
+        let driver_row = LabelRow::new("Driver Used");
+
+        let vram_size_row = LabelRow::new("VRAM Size");
+        let vram_type_row = LabelRow::new("VRAM Type");
+        let vram_peak_bw_row = LabelRow::new("Peak VRAM Bandwidth");
+
+        let l1_cache_row = LabelRow::new("L1 Cache (Per CU)");
+        let l2_cache_row = LabelRow::new("L2 Cache");
+        let l3_cache_row = LabelRow::new("L3 Cache");
+
+        let resizable_bar_enabled_row = LabelRow::new("Resizeable BAR");
+        let cpu_accessible_vram_row = LabelRow::new("CPU Accessible VRAM");
+        let link_speed_row = LabelRow::new("Link Speed");
 
         info_listbox.append(&gpu_name_row.container);
         info_listbox.append(&gpu_manufacturer_row.container);
diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
index c3c25fd1..ed216995 100644
--- a/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
+++ b/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
@@ -1,8 +1,8 @@
 mod feature_window;
 
 use self::feature_window::VulkanFeaturesWindow;
-
 use crate::app::root_stack::info_page::vulkan_info::feature_window::feature::VulkanFeature;
+use crate::app::root_stack::LabelRow;
 use glib::clone;
 use gtk::prelude::*;
 use gtk::*;
@@ -10,15 +10,13 @@ use lact_client::schema::VulkanInfo;
 use libadwaita::prelude::ActionRowExt;
 use tracing::trace;
 
-use super::InfoRow;
-
 #[derive(Clone)]
 pub struct VulkanInfoFrame {
     pub container: ListBox,
-    device_name_row: InfoRow,
-    version_row: InfoRow,
-    driver_name_row: InfoRow,
-    driver_version_row: InfoRow,
+    device_name_row: LabelRow,
+    version_row: LabelRow,
+    driver_name_row: LabelRow,
+    driver_version_row: LabelRow,
     features_model: gio::ListStore,
     extensions_model: gio::ListStore,
 }
@@ -33,10 +31,10 @@ impl VulkanInfoFrame {
         let features_model = gio::ListStore::new::<VulkanFeature>();
         let extensions_model = gio::ListStore::new::<VulkanFeature>();
 
-        let device_name_row = InfoRow::new("Device name");
-        let version_row = InfoRow::new("Vulkan version");
-        let driver_name_row = InfoRow::new("Driver name");
-        let driver_version_row = InfoRow::new("Driver version");
+        let device_name_row = LabelRow::new("Device name");
+        let version_row = LabelRow::new("Vulkan version");
+        let driver_name_row = LabelRow::new("Driver name");
+        let driver_version_row = LabelRow::new("Driver version");
 
         container.append(&device_name_row.container);
         container.append(&version_row.container);
diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs
index 3a190270..70ab1a3d 100644
--- a/lact-gui/src/app/root_stack/mod.rs
+++ b/lact-gui/src/app/root_stack/mod.rs
@@ -4,6 +4,7 @@ mod software_page;
 mod thermals_page;
 
 use gtk::{prelude::IsA, traits::GridExt, *};
+use libadwaita::prelude::ActionRowExt;
 
 use self::software_page::SoftwarePage;
 use info_page::InformationPage;
@@ -82,3 +83,30 @@ fn values_grid() -> Grid {
         .column_spacing(10)
         .build()
 }
+
+#[derive(Clone)]
+pub struct LabelRow {
+    pub container: libadwaita::ActionRow,
+    content_label: Label,
+}
+
+impl LabelRow {
+    pub fn new(title: &str) -> Self {
+        let container = libadwaita::ActionRow::builder().title(title).build();
+        let label = Label::builder()
+            .css_classes(["dim-label"])
+            .ellipsize(pango::EllipsizeMode::End)
+            .selectable(true)
+            .build();
+        container.add_suffix(&label);
+
+        Self {
+            container,
+            content_label: label,
+        }
+    }
+
+    pub fn set_content(&self, content: &str) {
+        self.content_label.set_label(content);
+    }
+}

From 5717a02cc5076a59ab6aabbfd036555548f5df6d Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 13:23:58 +0100
Subject: [PATCH 06/54] feat: boxed-list style for software page

---
 lact-gui/src/app/root_stack/info_page/mod.rs |   9 +-
 lact-gui/src/app/root_stack/mod.rs           |  23 +++-
 lact-gui/src/app/root_stack/software_page.rs | 121 +++++++------------
 lact-gui/ui/software_page.blp                |  25 ----
 4 files changed, 63 insertions(+), 115 deletions(-)
 delete mode 100644 lact-gui/ui/software_page.blp

diff --git a/lact-gui/src/app/root_stack/info_page/mod.rs b/lact-gui/src/app/root_stack/info_page/mod.rs
index cde7cfed..5037b80b 100644
--- a/lact-gui/src/app/root_stack/info_page/mod.rs
+++ b/lact-gui/src/app/root_stack/info_page/mod.rs
@@ -1,6 +1,6 @@
 mod vulkan_info;
 
-use super::LabelRow;
+use super::{list_clamp, LabelRow};
 use crate::app::page_section::PageSection;
 use gtk::prelude::*;
 use gtk::*;
@@ -98,12 +98,7 @@ impl InformationPage {
 
         vbox.append(&vulkan_container);
 
-        let clamp = libadwaita::Clamp::builder()
-            .maximum_size(600)
-            .margin_top(24)
-            .margin_bottom(24)
-            .child(&vbox)
-            .build();
+        let clamp = list_clamp(&vbox);
 
         let container = ScrolledWindow::builder()
             .hscrollbar_policy(PolicyType::Never)
diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs
index 70ab1a3d..4734edc5 100644
--- a/lact-gui/src/app/root_stack/mod.rs
+++ b/lact-gui/src/app/root_stack/mod.rs
@@ -3,12 +3,11 @@ mod oc_page;
 mod software_page;
 mod thermals_page;
 
+use self::software_page::software_page;
 use gtk::{prelude::IsA, traits::GridExt, *};
-use libadwaita::prelude::ActionRowExt;
-
-use self::software_page::SoftwarePage;
 use info_page::InformationPage;
 use lact_client::schema::SystemInfo;
+use libadwaita::prelude::ActionRowExt;
 use oc_page::OcPage;
 use thermals_page::ThermalsPage;
 
@@ -36,7 +35,7 @@ impl RootStack {
 
         container.add_titled(&thermals_page.container, Some("thermals_page"), "Thermals");
 
-        let software_page = SoftwarePage::new(system_info, embedded_daemon);
+        let software_page = software_page(system_info, embedded_daemon);
         container.add_titled(&software_page, Some("software_page"), "Software");
 
         Self {
@@ -106,7 +105,23 @@ impl LabelRow {
         }
     }
 
+    pub fn new_with_content(title: &str, content: &str) -> Self {
+        let row = Self::new(title);
+        row.set_content(content);
+        row
+    }
+
     pub fn set_content(&self, content: &str) {
         self.content_label.set_label(content);
     }
 }
+
+pub fn list_clamp(child: &impl IsA<Widget>) -> libadwaita::Clamp {
+    libadwaita::Clamp::builder()
+        .maximum_size(600)
+        .margin_top(24)
+        .margin_bottom(24)
+        .child(child)
+        .valign(Align::Start)
+        .build()
+}
diff --git a/lact-gui/src/app/root_stack/software_page.rs b/lact-gui/src/app/root_stack/software_page.rs
index d215524b..366f91e9 100644
--- a/lact-gui/src/app/root_stack/software_page.rs
+++ b/lact-gui/src/app/root_stack/software_page.rs
@@ -1,83 +1,46 @@
+use super::{list_clamp, LabelRow};
 use crate::GUI_VERSION;
-use gtk::glib::{self, Object};
 use lact_client::schema::SystemInfo;
 
-glib::wrapper! {
-    pub struct SoftwarePage(ObjectSubclass<imp::SoftwarePage>)
-        @extends gtk::Box, gtk::Widget,
-        @implements gtk::Orientable, gtk::Accessible, gtk::Buildable;
-}
-
-impl SoftwarePage {
-    pub fn new(system_info: SystemInfo, embedded: bool) -> Self {
-        let mut daemon_version = format!("{}-{}", system_info.version, system_info.profile);
-        if embedded {
-            daemon_version.push_str("-embedded");
-        }
-
-        let gui_profile = if cfg!(debug_assertions) {
-            "debug"
-        } else {
-            "release"
-        };
-        let gui_version = format!("{GUI_VERSION}-{gui_profile}");
-
-        Object::builder()
-            .property("daemon-version", daemon_version)
-            .property("gui-version", gui_version)
-            .property("kernel-version", system_info.kernel_version)
-            .build()
-    }
-}
-
-mod imp {
-    #![allow(clippy::enum_variant_names)]
-    use crate::app::{info_row::InfoRow, page_section::PageSection};
-    use glib::Properties;
-    use gtk::{
-        glib::{self, subclass::InitializingObject},
-        prelude::*,
-        subclass::{
-            prelude::*,
-            widget::{CompositeTemplateClass, WidgetImpl},
-        },
-        CompositeTemplate,
-    };
-    use std::cell::RefCell;
-
-    #[derive(CompositeTemplate, Default, Properties)]
-    #[properties(wrapper_type = super::SoftwarePage)]
-    #[template(file = "ui/software_page.blp")]
-    pub struct SoftwarePage {
-        #[property(get, set)]
-        daemon_version: RefCell<String>,
-        #[property(get, set)]
-        gui_version: RefCell<String>,
-        #[property(get, set)]
-        kernel_version: RefCell<String>,
-    }
-
-    #[glib::object_subclass]
-    impl ObjectSubclass for SoftwarePage {
-        const NAME: &'static str = "SoftwarePage";
-        type Type = super::SoftwarePage;
-        type ParentType = gtk::Box;
-
-        fn class_init(class: &mut Self::Class) {
-            InfoRow::ensure_type();
-            PageSection::ensure_type();
-
-            class.bind_template();
-        }
-
-        fn instance_init(obj: &InitializingObject<Self>) {
-            obj.init_template();
-        }
-    }
-
-    #[glib::derived_properties]
-    impl ObjectImpl for SoftwarePage {}
-
-    impl WidgetImpl for SoftwarePage {}
-    impl BoxImpl for SoftwarePage {}
+pub fn software_page(system_info: SystemInfo, embedded: bool) -> gtk::ScrolledWindow {
+    let listbox = gtk::ListBox::builder()
+        .css_classes(["boxed-list"])
+        .selection_mode(gtk::SelectionMode::None)
+        .build();
+
+    listbox.append(
+        &LabelRow::new_with_content(
+            "LACT Daemon",
+            &format!(
+                "{}-{}{}",
+                system_info.version,
+                system_info.profile,
+                if embedded { "-embedded" } else { "" }
+            ),
+        )
+        .container,
+    );
+
+    listbox.append(
+        &LabelRow::new_with_content(
+            "LACT GUI",
+            &format!(
+                "{}-{}",
+                GUI_VERSION,
+                if cfg!(debug_assertions) {
+                    "debug"
+                } else {
+                    "release"
+                }
+            ),
+        )
+        .container,
+    );
+
+    listbox.append(&LabelRow::new_with_content("LACT GUI", &system_info.kernel_version).container);
+
+    gtk::ScrolledWindow::builder()
+        .hscrollbar_policy(gtk::PolicyType::Never)
+        .child(&list_clamp(&listbox))
+        .build()
 }
diff --git a/lact-gui/ui/software_page.blp b/lact-gui/ui/software_page.blp
deleted file mode 100644
index dd3df7cc..00000000
--- a/lact-gui/ui/software_page.blp
+++ /dev/null
@@ -1,25 +0,0 @@
-using Gtk 4.0;
-
-template $SoftwarePage: Box {
-    orientation: vertical;
-    spacing: 10;
-    margin-start: 5;
-    margin-end: 5;
-    margin-top: 5;
-    margin-bottom: 5;
-
-    $InfoRow {
-        name: "LACT Daemon:";
-        value: bind template.daemon_version;
-    }
-
-    $InfoRow {
-        name: "LACT GUI:";
-        value: bind template.gui_version;
-    }
-
-    $InfoRow {
-        name: "Kernel Version:";
-        value: bind template.kernel_version;
-    }
-}

From 5767699a22a6cb2468e09e34eedad235a4fac3a8 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 15:38:48 +0100
Subject: [PATCH 07/54] feat: adwaita styling (as much as possible with
 listview) for vulkan features/extensions window

---
 .../vulkan_info/feature_window/mod.rs         |  6 ++--
 .../vulkan_info/feature_window/row.rs         | 10 ++++++
 lact-gui/ui/vulkan_feature_row.blp            | 11 +++---
 lact-gui/ui/vulkan_features_window.blp        | 36 ++++++++++++-------
 4 files changed, 43 insertions(+), 20 deletions(-)

diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs
index 3b42e7fb..349cf70a 100644
--- a/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs
+++ b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs
@@ -6,7 +6,7 @@ use gtk::{gio, glib};
 
 glib::wrapper! {
     pub struct VulkanFeaturesWindow(ObjectSubclass<imp::VulkanFeaturesWindow>)
-        @extends gtk::Box, gtk::Widget, gtk::Window,
+        @extends gtk::Box, gtk::Widget, gtk::Window, libadwaita::Window,
         @implements gtk::Orientable, gtk::Accessible, gtk::Buildable;
 }
 
@@ -33,6 +33,7 @@ mod imp {
         CompositeTemplate, Expression, FilterListModel, PropertyExpression, SearchEntry,
         SignalListItemFactory, StringFilter, TemplateChild,
     };
+    use libadwaita::subclass::window::AdwWindowImpl;
     use std::cell::RefCell;
 
     #[derive(CompositeTemplate, Properties, Default)]
@@ -57,7 +58,7 @@ mod imp {
     impl ObjectSubclass for VulkanFeaturesWindow {
         const NAME: &'static str = "VulkanFeaturesWindow";
         type Type = super::VulkanFeaturesWindow;
-        type ParentType = gtk::Window;
+        type ParentType = libadwaita::Window;
 
         fn class_init(class: &mut Self::Class) {
             class.bind_template();
@@ -116,5 +117,6 @@ mod imp {
 
     impl WidgetImpl for VulkanFeaturesWindow {}
     impl WindowImpl for VulkanFeaturesWindow {}
+    impl AdwWindowImpl for VulkanFeaturesWindow {}
     impl ApplicationWindowImpl for VulkanFeaturesWindow {}
 }
diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/row.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/row.rs
index 8eeca90a..23c9ecc6 100644
--- a/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/row.rs
+++ b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/row.rs
@@ -77,6 +77,16 @@ mod imp {
                 })
                 .sync_create()
                 .build();
+            obj.bind_property("feature", &self.available_image.get(), "tooltip-text")
+                .transform_to(|_, feature: VulkanFeature| {
+                    if feature.supported() {
+                        Some("Supported")
+                    } else {
+                        Some("Unsupported")
+                    }
+                })
+                .sync_create()
+                .build();
         }
     }
 
diff --git a/lact-gui/ui/vulkan_feature_row.blp b/lact-gui/ui/vulkan_feature_row.blp
index a87daccc..b368da8b 100644
--- a/lact-gui/ui/vulkan_feature_row.blp
+++ b/lact-gui/ui/vulkan_feature_row.blp
@@ -4,13 +4,14 @@ template $VulkanFeatureRow: Box {
   focus-on-click: false;
   hexpand: true;
   hexpand-set: true;
-  margin-bottom: 10;
-  margin-end: 20;
-  margin-start: 20;
-  margin-top: 10;
+  margin-bottom: 12;
+  margin-end: 24;
+  margin-start: 24;
+  margin-top: 12;
 
   Label name_label {
-    halign: start;
+    xalign: 0.0;
+    ellipsize: end;
     hexpand: true;
     label: 'feature name';
     selectable: true;
diff --git a/lact-gui/ui/vulkan_features_window.blp b/lact-gui/ui/vulkan_features_window.blp
index 2b84a5b5..0ea3a315 100644
--- a/lact-gui/ui/vulkan_features_window.blp
+++ b/lact-gui/ui/vulkan_features_window.blp
@@ -1,23 +1,33 @@
 using Gtk 4.0;
+using Adw 1;
 
-template $VulkanFeaturesWindow: Window {
+template $VulkanFeaturesWindow: Adw.Window {
   default-height: 700;
   default-width: 500;
 
-  Box {
-    orientation: vertical;
-
-    SearchEntry search_entry {}
-
-    ScrolledWindow {
-      vexpand: true;
+  Adw.ToolbarView {
+    [top] Adw.HeaderBar {}
+    [top] SearchEntry search_entry {
+        margin-top: 6;
+        margin-bottom: 12;
+        margin-start: 12;
+        margin-end: 6;
+    }
 
-      ListView {
-        factory: features_factory;
-        model: selection_model;
-        show-separators: true;
+    content: Box {
+      orientation: vertical;
+  
+      ScrolledWindow {
+        vexpand: true;
+        hscrollbar-policy: never;
+  
+        ListView {
+          factory: features_factory;
+          model: selection_model;
+          show-separators: true;
+        }
       }
-    }
+    };
   }
 }
 

From 1ecc1197eb8e83cda15eee1c4c78ef0e416906be Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 16:05:22 +0100
Subject: [PATCH 08/54] feat: use libadwaita message dialog instead of gtk's

---
 lact-gui/src/app/mod.rs                       | 110 ++++++++++--------
 .../src/app/root_stack/thermals_page/mod.rs   |  12 +-
 lact-gui/src/app/toolbars.rs                  |   4 +-
 3 files changed, 71 insertions(+), 55 deletions(-)

diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index 219bb0f7..37c21a33 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -264,7 +264,6 @@ impl App {
                         .extra_child(&hbox)
                         .modal(true)
                         .transient_for(&app.window)
-                        .hide_on_close(true)
                         .build();
 
                     diag.add_response("close", "_Close");
@@ -541,50 +540,62 @@ impl App {
     }
 
     fn enable_overclocking(&self) {
-        let text = format!("This will enable the overdrive feature of the amdgpu driver by creating a file at <b>{MODULE_CONF_PATH}</b>. Are you sure you want to do this?");
-        // TODO: to libadwaita
-        let dialog = MessageDialog::builder()
-            .title("Enable Overclocking")
-            .use_markup(true)
-            .text(text)
-            .message_type(MessageType::Question)
-            .buttons(ButtonsType::OkCancel)
+        let text = format!("This will enable the overdrive feature of the amdgpu driver by creating a file at <b>{MODULE_CONF_PATH}</b>");
+        let dialog = libadwaita::MessageDialog::builder()
+            .heading("Enable Overclocking")
+            .body_use_markup(true)
+            .body(text)
+            .modal(true)
             .transient_for(&self.window)
             .build();
 
-        dialog.run_async(clone!(@strong self as app => move |diag, response| {
-            if response == ResponseType::Ok {
+        let res_ok = "ok";
+        let res_cancel = "cancel";
+
+        dialog.add_response(res_cancel, "_Cancel");
+        dialog.add_response(res_ok, "_Ok");
+        dialog.set_response_appearance(res_cancel, libadwaita::ResponseAppearance::Destructive);
+        dialog.set_response_appearance(res_ok, libadwaita::ResponseAppearance::Suggested);
+
+        dialog.connect_response(None, clone!(@strong self as app => move |_, response| {
+            if response == res_ok {
                 match app.daemon_client.enable_overdrive().and_then(|buffer| buffer.inner()) {
                     Ok(_) => {
-                        let success_dialog = MessageDialog::builder()
-                            .title("Success")
-                            .text("Overclocking successfully enabled. A system reboot is required to apply the changes")
-                            .message_type(MessageType::Info)
-                            .buttons(ButtonsType::Ok)
+                        let success_dialog = libadwaita::MessageDialog::builder()
+                            .heading("Success")
+                            .body("Overclocking successfully enabled. A system reboot is required to apply the changes")
+                            .modal(true)
+                            .transient_for(&app.window)
                             .build();
-                        success_dialog.run_async(move |diag, _| {
-                            diag.hide();
-                        });
+                        success_dialog.add_response("ok", "_Ok");
+                        success_dialog.present();
                     }
                     Err(err) => {
                         show_error(&app.window, err);
                     }
                 }
             }
-            diag.hide();
         }));
+
+        dialog.present();
     }
 
     fn ask_confirmation(&self, gpu_id: String, mut delay: u64) {
         let text = confirmation_text(delay);
-        // TODO: to libadwaita
-        let dialog = MessageDialog::builder()
-            .title("Confirm settings")
-            .text(text)
-            .message_type(MessageType::Question)
-            .buttons(ButtonsType::YesNo)
+        let dialog = libadwaita::MessageDialog::builder()
+            .heading("Confirm settings")
+            .body(text)
+            .modal(true)
             .transient_for(&self.window)
             .build();
+
+        let res_yes = "yes";
+        let res_no = "no";
+
+        dialog.add_response(res_no, "_No");
+        dialog.add_response(res_yes, "_Yes");
+        dialog.set_response_appearance(res_no, libadwaita::ResponseAppearance::Destructive);
+        dialog.set_response_appearance(res_yes, libadwaita::ResponseAppearance::Suggested);
         let confirmed = Rc::new(AtomicBool::new(false));
 
         glib::source::timeout_add_local(
@@ -597,7 +608,7 @@ impl App {
                 delay -= 1;
 
                 let text = confirmation_text(delay);
-                dialog.set_text(Some(&text));
+                dialog.set_body(&text);
 
                 if delay == 0 {
                     dialog.hide();
@@ -610,21 +621,25 @@ impl App {
             }),
         );
 
-        dialog.run_async(clone!(@strong self as app => move |diag, response| {
-            confirmed.store(true, std::sync::atomic::Ordering::SeqCst);
+        dialog.connect_response(
+            None,
+            clone!(@strong self as app => move |diag, response| {
+                confirmed.store(true, std::sync::atomic::Ordering::SeqCst);
 
-            let command = match response {
-                ResponseType::Yes => ConfirmCommand::Confirm,
-                _ => ConfirmCommand::Revert,
-            };
+                let command = match response {
+                    res if res == res_yes => ConfirmCommand::Confirm,
+                    _ => ConfirmCommand::Revert,
+                };
 
-            diag.hide();
+                diag.hide();
 
-            if let Err(err) = app.daemon_client.confirm_pending_config(command) {
-                show_error(&app.window, err);
-            }
-            app.set_initial(&gpu_id);
-        }));
+                if let Err(err) = app.daemon_client.confirm_pending_config(command) {
+                    show_error(&app.window, err);
+                }
+                app.set_initial(&gpu_id);
+            }),
+        );
+        dialog.present();
     }
 }
 
@@ -635,17 +650,16 @@ enum GuiUpdateMsg {
 fn show_error(parent: &libadwaita::ApplicationWindow, err: anyhow::Error) {
     let text = format!("{err:?}");
     warn!("{}", text.trim());
-    // TODO: to libadwaita
-    let diag = MessageDialog::builder()
-        .title("Error")
-        .message_type(MessageType::Error)
-        .text(&text)
-        .buttons(ButtonsType::Close)
+    let diag = libadwaita::MessageDialog::builder()
+        .heading("Error")
+        .body(&text)
+        .modal(true)
         .transient_for(parent)
         .build();
-    diag.run_async(|diag, _| {
-        diag.hide();
-    })
+
+    diag.add_response("close", "_Close");
+
+    diag.present();
 }
 
 fn confirmation_text(seconds_left: u64) -> String {
diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs
index 24ba097a..c7066332 100644
--- a/lact-gui/src/app/root_stack/thermals_page/mod.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs
@@ -4,6 +4,7 @@ use glib::clone;
 use gtk::prelude::*;
 use gtk::*;
 use lact_client::schema::{default_fan_curve, DeviceStats, FanControlMode, FanCurveMap};
+use libadwaita::prelude::MessageDialogExt;
 
 use crate::app::page_section::PageSection;
 
@@ -243,9 +244,10 @@ fn static_speed_adj(parent_box: &Box) -> Adjustment {
 }
 
 fn show_fan_control_warning() {
-    let diag = MessageDialog::new(None::<&Window>, DialogFlags::empty(), MessageType::Warning, ButtonsType::Ok,
-                        "Warning! Due to a driver bug, a reboot may be required for fan control to properly switch back to automatic.");
-    diag.run_async(|diag, _| {
-        diag.hide();
-    })
+    let diag = libadwaita::MessageDialog::builder()
+        .heading("Warning")
+        .body("Due to a driver bug, a reboot may be required for fan control to properly switch back to automatic")
+        .build();
+    diag.add_response("ok", "_Ok");
+    diag.present();
 }
diff --git a/lact-gui/src/app/toolbars.rs b/lact-gui/src/app/toolbars.rs
index 22f9d1dc..d8afe01b 100644
--- a/lact-gui/src/app/toolbars.rs
+++ b/lact-gui/src/app/toolbars.rs
@@ -21,7 +21,7 @@ impl Toolbars {
             .show_title(true)
             .build();
 
-        // TODO: new gtk select
+        // WARN: gtk::ComboBoxText is deprecated, use gtk::DropDown instead
         let gpu_selector = ComboBoxText::builder()
             .margin_top(12)
             .margin_bottom(12)
@@ -42,7 +42,7 @@ impl Toolbars {
             self.gpu_selector.append(Some(entry.id), &name);
         }
 
-        //limits the length of gpu names in combobox
+        // limits the length of gpu names in combobox
         for cell in self.gpu_selector.cells() {
             cell.set_property("width-chars", 10);
             cell.set_property("ellipsize", EllipsizeMode::End);

From d7c1c88f0aa26374ac88cc3fcd1d851d168e2daf Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 16:17:26 +0100
Subject: [PATCH 09/54] fix: ensure fan control warning dialog has a reference
 to the root win to set it as transient for

---
 lact-gui/src/app/mod.rs                       |  2 +-
 lact-gui/src/app/root_stack/mod.rs            |  8 ++++++--
 .../src/app/root_stack/thermals_page/mod.rs   | 19 +++++++++----------
 3 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index 37c21a33..e5b9fa98 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -60,7 +60,7 @@ impl App {
             show_error(&window, err);
         }
 
-        let root_stack = RootStack::new(system_info, daemon_client.embedded);
+        let root_stack = RootStack::new(window.clone(), system_info, daemon_client.embedded);
 
         {
             let stack = root_stack.container.clone();
diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs
index 4734edc5..ccb71474 100644
--- a/lact-gui/src/app/root_stack/mod.rs
+++ b/lact-gui/src/app/root_stack/mod.rs
@@ -20,7 +20,11 @@ pub struct RootStack {
 }
 
 impl RootStack {
-    pub fn new(system_info: SystemInfo, embedded_daemon: bool) -> Self {
+    pub fn new(
+        root_win: libadwaita::ApplicationWindow,
+        system_info: SystemInfo,
+        embedded_daemon: bool,
+    ) -> Self {
         let container = Stack::builder().vexpand(true).hexpand(true).build();
 
         let info_page = InformationPage::new();
@@ -31,7 +35,7 @@ impl RootStack {
 
         container.add_titled(&oc_page.container, Some("oc_page"), "OC");
 
-        let thermals_page = ThermalsPage::new();
+        let thermals_page = ThermalsPage::new(root_win);
 
         container.add_titled(&thermals_page.container, Some("thermals_page"), "Thermals");
 
diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs
index c7066332..8741626c 100644
--- a/lact-gui/src/app/root_stack/thermals_page/mod.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs
@@ -1,17 +1,14 @@
 mod fan_curve_frame;
 
+use self::fan_curve_frame::FanCurveFrame;
+use super::{label_row, values_grid};
+use crate::app::page_section::PageSection;
 use glib::clone;
 use gtk::prelude::*;
 use gtk::*;
 use lact_client::schema::{default_fan_curve, DeviceStats, FanControlMode, FanCurveMap};
 use libadwaita::prelude::MessageDialogExt;
 
-use crate::app::page_section::PageSection;
-
-use self::fan_curve_frame::FanCurveFrame;
-
-use super::{label_row, values_grid};
-
 #[derive(Debug)]
 pub struct ThermalsSettings {
     pub manual_fan_control: bool,
@@ -32,7 +29,7 @@ pub struct ThermalsPage {
 }
 
 impl ThermalsPage {
-    pub fn new() -> Self {
+    pub fn new(root_win: libadwaita::ApplicationWindow) -> Self {
         let container = Box::builder()
             .orientation(Orientation::Vertical)
             .spacing(15)
@@ -83,9 +80,9 @@ impl ThermalsPage {
 
         container.append(&fan_control_section);
 
-        fan_control_mode_stack.connect_visible_child_name_notify(|stack| {
+        fan_control_mode_stack.connect_visible_child_name_notify(move |stack| {
             if stack.visible_child_name() == Some("automatic".into()) {
-                show_fan_control_warning()
+                show_fan_control_warning(&root_win)
             }
         });
 
@@ -243,10 +240,12 @@ fn static_speed_adj(parent_box: &Box) -> Adjustment {
     adjustment
 }
 
-fn show_fan_control_warning() {
+fn show_fan_control_warning(root_win: &libadwaita::ApplicationWindow) {
     let diag = libadwaita::MessageDialog::builder()
         .heading("Warning")
         .body("Due to a driver bug, a reboot may be required for fan control to properly switch back to automatic")
+        .modal(true)
+        .transient_for(root_win)
         .build();
     diag.add_response("ok", "_Ok");
     diag.present();

From d44b44f8b4b79c6fe875b755699ac8f158cef583 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 17:37:05 +0100
Subject: [PATCH 10/54] feat: boxed-list style for thermal stats

---
 lact-gui/src/app/root_stack/mod.rs            |  2 +
 .../src/app/root_stack/thermals_page/mod.rs   | 74 +++++++++++--------
 2 files changed, 44 insertions(+), 32 deletions(-)

diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs
index ccb71474..f991b2fc 100644
--- a/lact-gui/src/app/root_stack/mod.rs
+++ b/lact-gui/src/app/root_stack/mod.rs
@@ -99,6 +99,8 @@ impl LabelRow {
         let label = Label::builder()
             .css_classes(["dim-label"])
             .ellipsize(pango::EllipsizeMode::End)
+            .xalign(1.0)
+            .justify(Justification::Right)
             .selectable(true)
             .build();
         container.add_suffix(&label);
diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs
index 8741626c..c2482cae 100644
--- a/lact-gui/src/app/root_stack/thermals_page/mod.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs
@@ -1,13 +1,13 @@
 mod fan_curve_frame;
 
 use self::fan_curve_frame::FanCurveFrame;
-use super::{label_row, values_grid};
+use super::{list_clamp, LabelRow};
 use crate::app::page_section::PageSection;
 use glib::clone;
 use gtk::prelude::*;
 use gtk::*;
 use lact_client::schema::{default_fan_curve, DeviceStats, FanControlMode, FanCurveMap};
-use libadwaita::prelude::MessageDialogExt;
+use libadwaita::prelude::*;
 
 #[derive(Debug)]
 pub struct ThermalsSettings {
@@ -19,9 +19,9 @@ pub struct ThermalsSettings {
 
 #[derive(Clone)]
 pub struct ThermalsPage {
-    pub container: Box,
-    temperatures_label: Label,
-    fan_speed_label: Label,
+    pub container: ScrolledWindow,
+    temperatures_row: LabelRow,
+    fan_speed_row: LabelRow,
     fan_static_speed_adjustment: Adjustment,
     fan_curve_frame: FanCurveFrame,
     fan_control_mode_stack: Stack,
@@ -30,22 +30,27 @@ pub struct ThermalsPage {
 
 impl ThermalsPage {
     pub fn new(root_win: libadwaita::ApplicationWindow) -> Self {
-        let container = Box::builder()
+        let vbox = Box::builder()
             .orientation(Orientation::Vertical)
-            .spacing(15)
-            .margin_start(20)
-            .margin_end(20)
+            .spacing(12)
             .build();
 
         let stats_section = PageSection::new("Statistics");
-        let stats_grid = values_grid();
+        let stats_listbox = ListBox::builder()
+            .css_classes(["boxed-list"])
+            .selection_mode(SelectionMode::None)
+            .build();
+
+        let temperatures_row = LabelRow::new("Temperatures");
+
+        let fan_speed_row = LabelRow::new("Fan speed");
 
-        let temperatures_label = label_row("Temperatures:", &stats_grid, 0, 0, false);
-        let fan_speed_label = label_row("Fan speed:", &stats_grid, 1, 0, false);
+        stats_listbox.append(&temperatures_row.container);
+        stats_listbox.append(&fan_speed_row.container);
 
-        stats_section.append(&stats_grid);
+        stats_section.append(&stats_listbox);
 
-        container.append(&stats_section);
+        vbox.append(&stats_section);
 
         let fan_curve_frame = FanCurveFrame::new();
 
@@ -78,7 +83,7 @@ impl ThermalsPage {
         fan_control_section.append(&fan_control_mode_stack_switcher);
         fan_control_section.append(&fan_control_mode_stack);
 
-        container.append(&fan_control_section);
+        vbox.append(&fan_control_section);
 
         fan_control_mode_stack.connect_visible_child_name_notify(move |stack| {
             if stack.visible_child_name() == Some("automatic".into()) {
@@ -86,10 +91,15 @@ impl ThermalsPage {
             }
         });
 
+        let container = ScrolledWindow::builder()
+            .hscrollbar_policy(PolicyType::Never)
+            .child(&list_clamp(&vbox))
+            .build();
+
         Self {
             container,
-            temperatures_label,
-            fan_speed_label,
+            temperatures_row,
+            fan_speed_row,
             fan_static_speed_adjustment,
             fan_curve_frame,
             fan_control_mode_stack,
@@ -107,23 +117,23 @@ impl ThermalsPage {
         let temperatures_text = if temperatures.is_empty() {
             String::from("No sensors found")
         } else {
-            temperatures.join(", ")
+            temperatures.join(" | ")
         };
 
-        self.temperatures_label
-            .set_markup(&format!("<b>{temperatures_text}</b>",));
-
-        match stats.fan.speed_current {
-            Some(fan_speed_current) => self.fan_speed_label.set_markup(&format!(
-                "<b>{} RPM ({}%)</b>",
-                fan_speed_current,
-                (fan_speed_current as f64
-                    / stats.fan.speed_max.unwrap_or(fan_speed_current) as f64
-                    * 100.0)
-                    .round()
-            )),
-            None => self.fan_speed_label.set_text("No fan detected"),
-        }
+        self.temperatures_row.set_content(&temperatures_text);
+
+        self.fan_speed_row
+            .set_content(&match stats.fan.speed_current {
+                Some(fan_speed_current) => format!(
+                    "{} RPM ({}%)",
+                    fan_speed_current,
+                    (fan_speed_current as f64
+                        / stats.fan.speed_max.unwrap_or(fan_speed_current) as f64
+                        * 100.0)
+                        .round()
+                ),
+                None => "No fan detected".into(),
+            });
 
         if initial {
             self.fan_control_mode_stack_switcher.set_visible(true);

From 535523dbdd70e8520422a7b6bbeef42f1f9663a6 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 18:02:28 +0100
Subject: [PATCH 11/54] feat: move apply revealer to main headerbar as linked
 box

---
 lact-gui/src/app/apply_box.rs                 | 57 +++++++++++++
 lact-gui/src/app/apply_revealer.rs            | 50 -----------
 .../src/app/{toolbars.rs => gpu_selector.rs}  | 33 ++------
 lact-gui/src/app/mod.rs                       | 82 +++++++++----------
 4 files changed, 105 insertions(+), 117 deletions(-)
 create mode 100644 lact-gui/src/app/apply_box.rs
 delete mode 100644 lact-gui/src/app/apply_revealer.rs
 rename lact-gui/src/app/{toolbars.rs => gpu_selector.rs} (55%)

diff --git a/lact-gui/src/app/apply_box.rs b/lact-gui/src/app/apply_box.rs
new file mode 100644
index 00000000..58054b2d
--- /dev/null
+++ b/lact-gui/src/app/apply_box.rs
@@ -0,0 +1,57 @@
+use gtk::prelude::*;
+use gtk::*;
+
+#[derive(Clone)]
+pub struct ApplyBox {
+    pub container: Box,
+    apply_button: Button,
+    reset_button: Button,
+}
+
+impl ApplyBox {
+    pub fn new() -> Self {
+        let container = Box::builder()
+            .orientation(Orientation::Horizontal)
+            .css_classes(["linked"])
+            .visible(false)
+            .build();
+
+        let apply_button = Button::builder()
+            .css_classes(["suggested-action"])
+            .label("Apply")
+            .build();
+        let reset_button = Button::builder()
+            .icon_name("view-refresh-symbolic")
+            .tooltip_text("Reset")
+            .build();
+
+        container.append(&apply_button);
+        container.append(&reset_button);
+
+        Self {
+            container,
+            apply_button,
+            reset_button,
+        }
+    }
+
+    pub fn show(&self) {
+        self.container.set_visible(true);
+    }
+
+    pub fn hide(&self) {
+        self.container.set_visible(false);
+    }
+
+    pub fn connect_apply_button_clicked<F: Fn() + 'static>(&self, f: F) {
+        self.apply_button.connect_clicked(move |_| {
+            f();
+        });
+    }
+
+    pub fn connect_reset_button_clicked<F: Fn() + 'static>(&self, f: F) {
+        self.reset_button.connect_clicked(move |_| {
+            f();
+        });
+    }
+}
diff --git a/lact-gui/src/app/apply_revealer.rs b/lact-gui/src/app/apply_revealer.rs
deleted file mode 100644
index dd531851..00000000
--- a/lact-gui/src/app/apply_revealer.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-use gtk::prelude::*;
-use gtk::*;
-
-#[derive(Clone)]
-pub struct ApplyRevealer {
-    pub container: Revealer,
-    apply_button: Button,
-    reset_button: Button,
-}
-
-impl ApplyRevealer {
-    pub fn new() -> Self {
-        let container = Revealer::builder().transition_duration(150).build();
-        let vbox = Box::new(Orientation::Horizontal, 5);
-
-        let apply_button = Button::builder().label("Apply").hexpand(true).build();
-        let reset_button = Button::builder().label("Reset").build();
-
-        vbox.append(&apply_button);
-        vbox.append(&reset_button);
-
-        container.set_child(Some(&vbox));
-
-        Self {
-            container,
-            apply_button,
-            reset_button,
-        }
-    }
-
-    pub fn show(&self) {
-        self.container.set_reveal_child(true);
-    }
-
-    pub fn hide(&self) {
-        self.container.set_reveal_child(false);
-    }
-
-    pub fn connect_apply_button_clicked<F: Fn() + 'static>(&self, f: F) {
-        self.apply_button.connect_clicked(move |_| {
-            f();
-        });
-    }
-
-    pub fn connect_reset_button_clicked<F: Fn() + 'static>(&self, f: F) {
-        self.reset_button.connect_clicked(move |_| {
-            f();
-        });
-    }
-}
diff --git a/lact-gui/src/app/toolbars.rs b/lact-gui/src/app/gpu_selector.rs
similarity index 55%
rename from lact-gui/src/app/toolbars.rs
rename to lact-gui/src/app/gpu_selector.rs
index d8afe01b..4ef757bb 100644
--- a/lact-gui/src/app/toolbars.rs
+++ b/lact-gui/src/app/gpu_selector.rs
@@ -4,55 +4,40 @@ use lact_client::schema::DeviceListEntry;
 use pango::EllipsizeMode;
 
 #[derive(Clone)]
-pub struct Toolbars {
-    pub headerbar: libadwaita::HeaderBar,
-    pub gpu_selector: ComboBoxText,
-    pub title: libadwaita::WindowTitle,
+pub struct GpuSelector {
+    pub dropdown: ComboBoxText,
 }
 
-impl Toolbars {
+impl GpuSelector {
     pub fn new() -> Self {
-        let title = libadwaita::WindowTitle::builder()
-            .title("Information")
-            .build();
-
-        let headerbar = libadwaita::HeaderBar::builder()
-            .title_widget(&title)
-            .show_title(true)
-            .build();
-
         // WARN: gtk::ComboBoxText is deprecated, use gtk::DropDown instead
-        let gpu_selector = ComboBoxText::builder()
+        let dropdown = ComboBoxText::builder()
             .margin_top(12)
             .margin_bottom(12)
             .margin_start(12)
             .margin_end(12)
             .build();
 
-        Self {
-            headerbar,
-            gpu_selector,
-            title,
-        }
+        Self { dropdown }
     }
 
     pub fn set_devices(&self, gpus: &[DeviceListEntry<'_>]) {
         for (i, entry) in gpus.iter().enumerate() {
             let name = format!("{i}: {}", entry.name.unwrap_or_default());
-            self.gpu_selector.append(Some(entry.id), &name);
+            self.dropdown.append(Some(entry.id), &name);
         }
 
         // limits the length of gpu names in combobox
-        for cell in self.gpu_selector.cells() {
+        for cell in self.dropdown.cells() {
             cell.set_property("width-chars", 10);
             cell.set_property("ellipsize", EllipsizeMode::End);
         }
 
-        self.gpu_selector.set_active(Some(0));
+        self.dropdown.set_active(Some(0));
     }
 
     pub fn connect_gpu_selection_changed<F: Fn(String) + 'static>(&self, f: F) {
-        self.gpu_selector.connect_changed(move |gpu_selector| {
+        self.dropdown.connect_changed(move |gpu_selector| {
             if let Some(selected_id) = gpu_selector.active_id() {
                 f(selected_id.to_string());
             }
diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index e5b9fa98..71bed330 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -1,26 +1,26 @@
-mod apply_revealer;
+mod apply_box;
+mod gpu_selector;
 mod info_row;
 mod page_section;
 mod root_stack;
-mod toolbars;
 
+use self::apply_box::ApplyBox;
 use crate::{APP_ID, GUI_VERSION};
 use anyhow::{anyhow, Context};
-use apply_revealer::ApplyRevealer;
 use glib::clone;
+use gpu_selector::GpuSelector;
 use gtk::glib::{timeout_future, ControlFlow};
 use gtk::{gio::ApplicationFlags, prelude::*, *};
 use lact_client::schema::request::{ConfirmCommand, SetClocksCommand};
 use lact_client::schema::DeviceStats;
 use lact_client::DaemonClient;
 use lact_daemon::MODULE_CONF_PATH;
-use libadwaita::prelude::{AdwApplicationWindowExt, MessageDialogExt};
+use libadwaita::prelude::{AdwApplicationWindowExt, MessageDialogExt, NavigationPageExt};
 use root_stack::RootStack;
 use std::cell::RefCell;
 use std::rc::Rc;
 use std::sync::atomic::AtomicBool;
 use std::time::Duration;
-use toolbars::Toolbars;
 use tracing::{debug, error, trace, warn};
 
 // In ms
@@ -30,9 +30,9 @@ const STATS_POLL_INTERVAL: u64 = 250;
 pub struct App {
     application: libadwaita::Application,
     pub window: libadwaita::ApplicationWindow,
-    pub toolbars: Toolbars,
+    pub gpu_selector: GpuSelector,
     root_stack: RootStack,
-    apply_revealer: ApplyRevealer,
+    apply_box: ApplyBox,
     daemon_client: DaemonClient,
 }
 
@@ -40,7 +40,7 @@ impl App {
     pub fn new(daemon_client: DaemonClient) -> Self {
         let application = libadwaita::Application::new(Some(APP_ID), ApplicationFlags::default());
 
-        let toolbars = Toolbars::new();
+        let gpu_selector = GpuSelector::new();
         let window = libadwaita::ApplicationWindow::builder()
             .title("LACT")
             .default_width(700)
@@ -62,24 +62,14 @@ impl App {
 
         let root_stack = RootStack::new(window.clone(), system_info, daemon_client.embedded);
 
-        {
-            let stack = root_stack.container.clone();
-            let title = toolbars.title.clone();
-            root_stack.container.connect_visible_child_notify(move |_| {
-                if let Some(child) = stack.visible_child() {
-                    title.set_title(stack.page(&child).title().unwrap().as_str())
-                }
-            });
-        }
-
         let root_view = libadwaita::ToolbarView::new();
 
-        root_view.add_top_bar(&toolbars.headerbar);
-        root_view.set_content(Some(&root_stack.container));
-
-        let apply_revealer = ApplyRevealer::new();
+        let root_view_headerbar = libadwaita::HeaderBar::builder().show_title(true).build();
+        let apply_box = ApplyBox::new();
+        root_view_headerbar.pack_end(&apply_box.container);
 
-        root_view.add_bottom_bar(&apply_revealer.container);
+        root_view.add_top_bar(&root_view_headerbar);
+        root_view.set_content(Some(&root_stack.container));
 
         let sidebar_view = libadwaita::ToolbarView::new();
 
@@ -88,11 +78,7 @@ impl App {
             .vexpand(true)
             .build();
         stack_sidebar.remove_css_class("sidebar");
-        sidebar_view.add_top_bar(
-            &libadwaita::HeaderBar::builder()
-                .title_widget(&libadwaita::WindowTitle::builder().title("LACT").build())
-                .build(),
-        );
+        sidebar_view.add_top_bar(&libadwaita::HeaderBar::builder().show_title(true).build());
         sidebar_view.set_content(Some(
             &ScrolledWindow::builder()
                 .child(&stack_sidebar)
@@ -100,8 +86,12 @@ impl App {
                 .hscrollbar_policy(PolicyType::Never)
                 .build(),
         ));
-        sidebar_view.add_bottom_bar(&toolbars.gpu_selector);
+        sidebar_view.add_bottom_bar(&gpu_selector.dropdown);
 
+        let root_nav_page = libadwaita::NavigationPage::builder()
+            .child(&root_view)
+            .title("Information")
+            .build();
         let split_view = libadwaita::NavigationSplitView::builder()
             .sidebar(
                 &libadwaita::NavigationPage::builder()
@@ -109,11 +99,7 @@ impl App {
                     .title("LACT")
                     .build(),
             )
-            .content(
-                &libadwaita::NavigationPage::builder()
-                    .child(&root_view)
-                    .build(),
-            )
+            .content(&root_nav_page)
             .build();
 
         {
@@ -132,6 +118,16 @@ impl App {
             });
         }
 
+        {
+            let stack = root_stack.container.clone();
+            let root_nav_page = root_nav_page.clone();
+            root_stack.container.connect_visible_child_notify(move |_| {
+                if let Some(child) = stack.visible_child() {
+                    root_nav_page.set_title(stack.page(&child).title().unwrap().as_str())
+                }
+            });
+        }
+
         let breakpoint = libadwaita::Breakpoint::new(libadwaita::BreakpointCondition::new_length(
             libadwaita::BreakpointConditionLengthType::MaxWidth,
             800.0,
@@ -145,9 +141,9 @@ impl App {
         App {
             application,
             window,
-            toolbars,
+            gpu_selector,
             root_stack,
-            apply_revealer,
+            apply_box,
             daemon_client,
         }
     }
@@ -159,7 +155,7 @@ impl App {
 
                 let current_gpu_id = Rc::new(RefCell::new(String::new()));
 
-                app.toolbars.connect_gpu_selection_changed(clone!(@strong app, @strong current_gpu_id => move |gpu_id| {
+                app.gpu_selector.connect_gpu_selection_changed(clone!(@strong app, @strong current_gpu_id => move |gpu_id| {
                     debug!("GPU Selection changed");
                     app.set_info(&gpu_id);
                     *current_gpu_id.borrow_mut() = gpu_id;
@@ -171,7 +167,7 @@ impl App {
                     .list_devices()
                     .expect("Could not list devices");
                 let devices = devices_buf.inner().expect("Could not access devices");
-                app.toolbars.set_devices(&devices);
+                app.gpu_selector.set_devices(&devices);
 
                 app.root_stack.oc_page.clocks_frame.connect_clocks_reset(clone!(@strong app, @strong current_gpu_id => move || {
                     debug!("Resetting clocks");
@@ -190,7 +186,7 @@ impl App {
                     }
                 }));
 
-                app.apply_revealer.connect_apply_button_clicked(
+                app.apply_box.connect_apply_button_clicked(
                     clone!(@strong app, @strong current_gpu_id => move || {
                         glib::idle_add_local_once(clone!(@strong app, @strong current_gpu_id => move || {
                             if let Err(err) = app.apply_settings(current_gpu_id.clone()) {
@@ -204,7 +200,7 @@ impl App {
                         }));
                     }),
                 );
-                app.apply_revealer.connect_reset_button_clicked(clone!(@strong app, @strong current_gpu_id => move || {
+                app.apply_box.connect_reset_button_clicked(clone!(@strong app, @strong current_gpu_id => move || {
                     let gpu_id = current_gpu_id.borrow().clone();
                     app.set_initial(&gpu_id)
                 }));
@@ -351,9 +347,9 @@ impl App {
 
         // Show apply button on setting changes
         // This is done here because new widgets may appear after applying settings (like fan curve points) which should be connected
-        let show_revealer = clone!(@strong self.apply_revealer as apply_revealer => move || {
+        let show_revealer = clone!(@strong self.apply_box as apply_box => move || {
                 debug!("settings changed, showing apply button");
-                apply_revealer.show();
+                apply_box.show();
         });
 
         self.root_stack
@@ -364,7 +360,7 @@ impl App {
             .oc_page
             .connect_settings_changed(show_revealer);
 
-        self.apply_revealer.hide();
+        self.apply_box.hide();
     }
 
     fn start_stats_update_loop(&self, current_gpu_id: Rc<RefCell<String>>) {

From f88065b12257617a22baca6b8bda66b2bce23894 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 18:05:27 +0100
Subject: [PATCH 12/54] fix: features window margins

---
 lact-gui/ui/vulkan_features_window.blp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lact-gui/ui/vulkan_features_window.blp b/lact-gui/ui/vulkan_features_window.blp
index 0ea3a315..49ce435a 100644
--- a/lact-gui/ui/vulkan_features_window.blp
+++ b/lact-gui/ui/vulkan_features_window.blp
@@ -8,10 +8,10 @@ template $VulkanFeaturesWindow: Adw.Window {
   Adw.ToolbarView {
     [top] Adw.HeaderBar {}
     [top] SearchEntry search_entry {
-        margin-top: 6;
-        margin-bottom: 12;
-        margin-start: 12;
-        margin-end: 6;
+      margin-top: 6;
+      margin-bottom: 6;
+      margin-start: 12;
+      margin-end: 12;
     }
 
     content: Box {

From 1508619739a9c3f009c7438970d36d8d0ccf4ca0 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 18:47:13 +0100
Subject: [PATCH 13/54] feat: redesigned fan curve section

---
 .../thermals_page/fan_curve_frame/mod.rs      | 73 ++++++++++---------
 .../fan_curve_frame/point_adjustment.rs       | 17 ++++-
 2 files changed, 55 insertions(+), 35 deletions(-)

diff --git a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs
index bacc7033..b04e3acb 100644
--- a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs
@@ -2,11 +2,10 @@ mod point_adjustment;
 
 use self::point_adjustment::PointAdjustment;
 use glib::clone;
-use gtk::graphene::Point;
-use gtk::gsk::Transform;
 use gtk::prelude::*;
 use gtk::*;
 use lact_client::schema::{default_fan_curve, FanCurveMap};
+use libadwaita::prelude::BinExt;
 use std::cell::RefCell;
 use std::collections::BTreeMap;
 use std::rc::Rc;
@@ -14,56 +13,65 @@ use std::rc::Rc;
 #[derive(Clone)]
 pub struct FanCurveFrame {
     pub container: Box,
-    curve_container: Frame,
+    curve_container: libadwaita::Bin,
     points: Rc<RefCell<Vec<PointAdjustment>>>,
 }
 
 impl FanCurveFrame {
     pub fn new() -> Self {
-        let root_box = Box::new(Orientation::Vertical, 5);
+        let root_box = Box::builder()
+            .orientation(Orientation::Vertical)
+            .css_classes(["card"])
+            .height_request(450)
+            .build();
 
         let hbox = Box::new(Orientation::Horizontal, 5);
 
-        let curve_container = Frame::new(Some("Fan Curve"));
-        curve_container.set_hexpand(true);
-
-        curve_container.set_margin_start(10);
-        curve_container.set_margin_end(10);
-        curve_container.set_margin_top(10);
-
-        let ratio_title_label = Label::builder().label("Fan speed (%)").build();
+        let curve_container = libadwaita::Bin::new();
 
-        let fixed = Fixed::new();
-        fixed.put(&ratio_title_label, 0.0, 0.0);
-
-        // This is a workaround to rotate the label that only looks good at the default window size
-        // Unfortunately there's no other way to do this (short of implementing custom rendering for a widget) as gtk4 removed the `angle` property for labels
-        let rotation_transform = Transform::new()
-            .rotate(-90.0)
-            .translate(&Point::new(-200.0, 10.0));
-        fixed.set_child_transform(&ratio_title_label, Some(&rotation_transform));
-
-        hbox.append(&fixed);
         hbox.append(&curve_container);
 
-        let temperature_title_label = Label::new(Some("Temperature (°C)"));
-
         let buttons_box = Box::builder()
             .orientation(Orientation::Horizontal)
-            .spacing(5)
-            .halign(Align::End)
+            .hexpand(true)
+            .spacing(12)
+            .margin_bottom(12)
+            .halign(Align::Center)
             .build();
 
-        let add_button = Button::builder().icon_name("list-add-symbolic").build();
-        let remove_button = Button::builder().icon_name("list-remove-symbolic").build();
-        let default_button = Button::builder().label("Default").build();
+        let add_button = Button::builder()
+            .icon_name("list-add-symbolic")
+            .css_classes(["circular"])
+            .tooltip_text("Add point")
+            .build();
+        let remove_button = Button::builder()
+            .icon_name("list-remove-symbolic")
+            .css_classes(["circular"])
+            .tooltip_text("Remove last point")
+            .build();
+        let default_button = Button::builder()
+            .css_classes(["circular"])
+            .child(
+                &Label::builder()
+                    .label("Reset")
+                    .margin_start(12)
+                    .margin_end(12)
+                    .build(),
+            )
+            .build();
 
-        buttons_box.append(&default_button);
         buttons_box.append(&remove_button);
+        buttons_box.append(&default_button);
         buttons_box.append(&add_button);
 
         root_box.append(&hbox);
-        root_box.append(&temperature_title_label);
+        root_box.append(
+            &Separator::builder()
+                .orientation(Orientation::Horizontal)
+                .margin_top(12)
+                .margin_bottom(12)
+                .build(),
+        );
         root_box.append(&buttons_box);
 
         let points = Rc::new(RefCell::new(Vec::new()));
@@ -119,7 +127,6 @@ impl FanCurveFrame {
 
         let points_container = Box::builder()
             .orientation(Orientation::Horizontal)
-            .spacing(5)
             .vexpand(true)
             .build();
 
diff --git a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/point_adjustment.rs b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/point_adjustment.rs
index 5cecd0df..576874a0 100644
--- a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/point_adjustment.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/point_adjustment.rs
@@ -1,6 +1,7 @@
 use glib::clone;
 use gtk::{
     glib,
+    prelude::ScaleExt,
     traits::{AdjustmentExt, BoxExt},
     Adjustment, Box, Label, MenuButton, Orientation, Popover, Scale, SpinButton,
 };
@@ -22,24 +23,36 @@ impl PointAdjustment {
             .hexpand(true)
             .vexpand(true)
             .inverted(true)
+            .tooltip_text("Fan speed (%)")
+            .digits(3)
+            .draw_value(true)
             .build();
+        scale.set_format_value_func(|_, v| format!("{:.1}%", v * 100.0));
         container.append(&scale);
 
         let temperature_adjustment = Adjustment::new(temperature.into(), 0.0, 100.0, 1.0, 1.0, 0.0);
         let temperature_selector = SpinButton::new(Some(&temperature_adjustment), 1.0, 0);
 
         // Using the built-in MenuButton label function creates an empty icon
-        let temperature_label = Label::new(Some(&temperature.to_string()));
+        let temperature_label = Label::builder()
+            .label(&format!("{}°C", temperature))
+            .margin_start(6)
+            .margin_end(6)
+            .build();
 
         temperature_adjustment.connect_value_changed(
             clone!(@strong temperature_label => move |temperature_adjustment| {
                 let temperature = temperature_adjustment.value();
-                temperature_label.set_text(&temperature.to_string());
+                temperature_label.set_text(&format!("{}°C", temperature));
             }),
         );
 
         let popover = Popover::builder().child(&temperature_selector).build();
         let temperature_button = MenuButton::builder()
+            .hexpand(false)
+            .halign(gtk::Align::Center)
+            .css_classes(["circular"])
+            .tooltip_text("Temperature")
             .popover(&popover)
             .child(&temperature_label)
             .build();

From 9a38eb181b6c829616338d1a08e7265c6015064d Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 19:59:00 +0100
Subject: [PATCH 14/54] feat: card style for static fan speed section

---
 .../fan_curve_frame/point_adjustment.rs       |  2 +-
 .../src/app/root_stack/thermals_page/mod.rs   | 30 +++++++++++--------
 2 files changed, 19 insertions(+), 13 deletions(-)

diff --git a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/point_adjustment.rs b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/point_adjustment.rs
index 576874a0..288498e9 100644
--- a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/point_adjustment.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/point_adjustment.rs
@@ -23,7 +23,7 @@ impl PointAdjustment {
             .hexpand(true)
             .vexpand(true)
             .inverted(true)
-            .tooltip_text("Fan speed (%)")
+            .tooltip_text("Fan speed")
             .digits(3)
             .draw_value(true)
             .build();
diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs
index c2482cae..79a8a562 100644
--- a/lact-gui/src/app/root_stack/thermals_page/mod.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs
@@ -56,8 +56,11 @@ impl ThermalsPage {
 
         let fan_static_speed_frame = Box::builder()
             .orientation(Orientation::Horizontal)
-            .spacing(5)
-            .valign(Align::Start)
+            .spacing(12)
+            .margin_top(12)
+            .margin_bottom(12)
+            .margin_start(12)
+            .margin_end(12)
             .build();
         let fan_static_speed_adjustment = static_speed_adj(&fan_static_speed_frame);
 
@@ -78,7 +81,15 @@ impl ThermalsPage {
 
         fan_control_mode_stack.add_titled(&fan_curve_frame.container, Some("curve"), "Curve");
 
-        fan_control_mode_stack.add_titled(&fan_static_speed_frame, Some("static"), "Static");
+        fan_control_mode_stack.add_titled(
+            &libadwaita::Bin::builder()
+                .css_classes(["card"])
+                .valign(Align::Start)
+                .child(&fan_static_speed_frame)
+                .build(),
+            Some("static"),
+            "Static",
+        );
 
         fan_control_section.append(&fan_control_mode_stack_switcher);
         fan_control_section.append(&fan_control_mode_stack);
@@ -212,10 +223,7 @@ impl ThermalsPage {
 }
 
 fn static_speed_adj(parent_box: &Box) -> Adjustment {
-    let label = Label::builder()
-        .label("Speed (in %)")
-        .halign(Align::Start)
-        .build();
+    let label = Label::builder().label("Speed").halign(Align::Start).build();
 
     let adjustment = Adjustment::new(0.0, 0.0, 100.0, 0.1, 1.0, 0.0);
 
@@ -223,22 +231,20 @@ fn static_speed_adj(parent_box: &Box) -> Adjustment {
         .orientation(Orientation::Horizontal)
         .adjustment(&adjustment)
         .hexpand(true)
-        .margin_start(5)
-        .margin_end(5)
         .build();
 
     let value_selector = SpinButton::new(Some(&adjustment), 1.0, 1);
-    let value_label = Label::new(None);
+    let value_label = Label::builder().margin_start(12).margin_end(12).build();
 
     let popover = Popover::builder().child(&value_selector).build();
     let value_button = MenuButton::builder()
+        .css_classes(["circular"])
         .popover(&popover)
         .child(&value_label)
         .build();
 
     adjustment.connect_value_changed(clone!(@strong value_label => move |adjustment| {
-        let value = adjustment.value();
-        value_label.set_text(&format!("{value:.1}"));
+        value_label.set_text(&format!("{:.1}%", adjustment.value()));
     }));
 
     adjustment.set_value(50.0);

From 3d694240af70c3798bebe82e3b7b71de63311af1 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 21:16:58 +0100
Subject: [PATCH 15/54] feat: make curve card scrollable horizontally to
 accomodate smaller window sizes

---
 .../app/root_stack/thermals_page/fan_curve_frame/mod.rs  | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs
index b04e3acb..852aded7 100644
--- a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs
@@ -5,7 +5,6 @@ use glib::clone;
 use gtk::prelude::*;
 use gtk::*;
 use lact_client::schema::{default_fan_curve, FanCurveMap};
-use libadwaita::prelude::BinExt;
 use std::cell::RefCell;
 use std::collections::BTreeMap;
 use std::rc::Rc;
@@ -13,7 +12,7 @@ use std::rc::Rc;
 #[derive(Clone)]
 pub struct FanCurveFrame {
     pub container: Box,
-    curve_container: libadwaita::Bin,
+    curve_container: ScrolledWindow,
     points: Rc<RefCell<Vec<PointAdjustment>>>,
 }
 
@@ -27,7 +26,9 @@ impl FanCurveFrame {
 
         let hbox = Box::new(Orientation::Horizontal, 5);
 
-        let curve_container = libadwaita::Bin::new();
+        let curve_container = ScrolledWindow::builder()
+            .vscrollbar_policy(PolicyType::Never)
+            .build();
 
         hbox.append(&curve_container);
 
@@ -68,7 +69,6 @@ impl FanCurveFrame {
         root_box.append(
             &Separator::builder()
                 .orientation(Orientation::Horizontal)
-                .margin_top(12)
                 .margin_bottom(12)
                 .build(),
         );
@@ -127,6 +127,7 @@ impl FanCurveFrame {
 
         let points_container = Box::builder()
             .orientation(Orientation::Horizontal)
+            .margin_bottom(12)
             .vexpand(true)
             .build();
 

From 03abe31becbcb9f7e8177b34b14a00d4a5c05eeb Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 21:17:51 +0100
Subject: [PATCH 16/54] feat: use heading style for info row instead of attrs

---
 lact-gui/src/app/info_row.rs | 6 +-----
 lact-gui/ui/info_row.blp     | 5 +++--
 2 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/lact-gui/src/app/info_row.rs b/lact-gui/src/app/info_row.rs
index 426c5255..a4c21b75 100644
--- a/lact-gui/src/app/info_row.rs
+++ b/lact-gui/src/app/info_row.rs
@@ -19,7 +19,6 @@ mod imp {
     use glib::Properties;
     use gtk::{
         glib::{self, subclass::InitializingObject},
-        pango::AttrList,
         prelude::*,
         subclass::{
             prelude::*,
@@ -27,7 +26,7 @@ mod imp {
         },
         CompositeTemplate, Label, TemplateChild,
     };
-    use std::{cell::RefCell, str::FromStr};
+    use std::cell::RefCell;
 
     #[derive(CompositeTemplate, Default, Properties)]
     #[properties(wrapper_type = super::InfoRow)]
@@ -61,9 +60,6 @@ mod imp {
     impl ObjectImpl for InfoRow {
         fn constructed(&self) {
             self.parent_constructed();
-
-            let attr_list = AttrList::from_str("0 -1 weight bold").unwrap();
-            self.value_label.set_attributes(Some(&attr_list));
         }
     }
 
diff --git a/lact-gui/ui/info_row.blp b/lact-gui/ui/info_row.blp
index 045fdeff..ba5bd188 100644
--- a/lact-gui/ui/info_row.blp
+++ b/lact-gui/ui/info_row.blp
@@ -12,9 +12,10 @@ template $InfoRow: Box {
     }
 
     Label value_label {
+        styles ["heading"]
         label: bind template.value;
-        halign: end;
+        xalign: 1.0;
+        justify: right;
         selectable: true;
-        use-markup: true;
     }
 }

From 51d47d1bff82a74d83f74cf45a39763bb31dbde1 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 21:18:09 +0100
Subject: [PATCH 17/54] feat: minimum window size up to 350

---
 lact-gui/src/app/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index 71bed330..2e683b99 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -45,7 +45,7 @@ impl App {
             .title("LACT")
             .default_width(700)
             .default_height(820)
-            .width_request(300)
+            .width_request(350)
             .height_request(200)
             .icon_name(APP_ID)
             .build();

From c35beb32b74a30e08a3f15adecb8fe88539d3e18 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 21:18:36 +0100
Subject: [PATCH 18/54] feat: card style for overclock stats section

---
 lact-gui/src/app/root_stack/mod.rs            |  40 +-----
 .../app/root_stack/oc_page/clocks_frame.rs    |   3 +-
 .../root_stack/oc_page/gpu_stats_section.rs   |   2 +-
 lact-gui/src/app/root_stack/oc_page/mod.rs    |   8 +-
 lact-gui/ui/oc_page/gpu_stats_section.blp     | 120 +++++++++++-------
 5 files changed, 80 insertions(+), 93 deletions(-)

diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs
index f991b2fc..77739d60 100644
--- a/lact-gui/src/app/root_stack/mod.rs
+++ b/lact-gui/src/app/root_stack/mod.rs
@@ -4,7 +4,7 @@ mod software_page;
 mod thermals_page;
 
 use self::software_page::software_page;
-use gtk::{prelude::IsA, traits::GridExt, *};
+use gtk::{prelude::IsA, *};
 use info_page::InformationPage;
 use lact_client::schema::SystemInfo;
 use libadwaita::prelude::ActionRowExt;
@@ -33,7 +33,7 @@ impl RootStack {
 
         let oc_page = OcPage::new(&system_info);
 
-        container.add_titled(&oc_page.container, Some("oc_page"), "OC");
+        container.add_titled(&oc_page.container, Some("oc_page"), "Overclock");
 
         let thermals_page = ThermalsPage::new(root_win);
 
@@ -51,42 +51,6 @@ impl RootStack {
     }
 }
 
-#[deprecated]
-fn values_row<W: IsA<Widget>>(
-    title: &str,
-    parent: &Grid,
-    value_child: &W,
-    row: i32,
-    column_offset: i32,
-) {
-    let title_label = Label::builder().label(title).halign(Align::Start).build();
-
-    parent.attach(&title_label, column_offset, row, 1, 1);
-    parent.attach(value_child, column_offset + 1, row, 1, 1);
-}
-
-#[deprecated]
-fn label_row(title: &str, parent: &Grid, row: i32, column_offset: i32, selectable: bool) -> Label {
-    let value_label = Label::builder()
-        .halign(Align::End)
-        .hexpand(true)
-        .selectable(selectable)
-        .build();
-    values_row(title, parent, &value_label, row, column_offset);
-
-    value_label
-}
-
-#[deprecated]
-fn values_grid() -> Grid {
-    Grid::builder()
-        .margin_start(10)
-        .margin_end(5)
-        .row_spacing(10)
-        .column_spacing(10)
-        .build()
-}
-
 #[derive(Clone)]
 pub struct LabelRow {
     pub container: libadwaita::ActionRow,
diff --git a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
index 8297f599..263e0fd3 100644
--- a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
@@ -88,8 +88,7 @@ impl ClocksFrame {
 
         let clocks_data_unavailable_label = Label::builder()
             .label("No clocks data available")
-            .margin_start(10)
-            .margin_end(10)
+            .css_classes(["error"])
             .halign(Align::Start)
             .build();
 
diff --git a/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs b/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs
index c1183b57..2093fbef 100644
--- a/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs
+++ b/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs
@@ -56,7 +56,7 @@ impl GpuStatsSection {
             .or(power_average);
 
         self.set_power_usage(format!(
-            "<b>{}/{}W</b>",
+            "{}/{}W",
             power_current.unwrap_or(0.0),
             power_cap_current.unwrap_or(0.0)
         ));
diff --git a/lact-gui/src/app/root_stack/oc_page/mod.rs b/lact-gui/src/app/root_stack/oc_page/mod.rs
index a5bca9d5..8261cbfc 100644
--- a/lact-gui/src/app/root_stack/oc_page/mod.rs
+++ b/lact-gui/src/app/root_stack/oc_page/mod.rs
@@ -22,6 +22,8 @@ use performance_frame::PerformanceFrame;
 use std::collections::HashMap;
 use tracing::warn;
 
+use super::list_clamp;
+
 const OVERCLOCKING_DISABLED_TEXT: &str = "Overclocking support is not enabled! \
 You can still change basic settings, but the more advanced clocks and voltage control will not be available.";
 
@@ -45,9 +47,7 @@ impl OcPage {
 
         let vbox = Box::builder()
             .orientation(Orientation::Vertical)
-            .spacing(15)
-            .margin_start(20)
-            .margin_end(20)
+            .spacing(12)
             .build();
 
         let mut enable_overclocking_button = None;
@@ -78,7 +78,7 @@ impl OcPage {
         vbox.append(&power_states_frame);
         vbox.append(&clocks_frame.container);
 
-        container.set_child(Some(&vbox));
+        container.set_child(Some(&list_clamp(&vbox)));
 
         Self {
             container,
diff --git a/lact-gui/ui/oc_page/gpu_stats_section.blp b/lact-gui/ui/oc_page/gpu_stats_section.blp
index 76105c13..7f068cd0 100644
--- a/lact-gui/ui/oc_page/gpu_stats_section.blp
+++ b/lact-gui/ui/oc_page/gpu_stats_section.blp
@@ -1,74 +1,98 @@
 using Gtk 4.0;
+using Adw 1;
 
 template $GpuStatsSection: $PageSection {
     name: "Statistics";
-    spacing: 10;
+    spacing: 12;
 
     Box {
-        orientation: horizontal;
-        spacing: 5;
+        styles ["card"]
+        orientation: vertical;
+        spacing: 12;
 
-        Label {
-            label: "VRAM Usage:";
-        }
+        Box {
+            orientation: vertical;
+            margin-top: 12;
+            margin-start: 12;
+            margin-end: 12;
+            spacing: 6;
 
-        Overlay {
-            LevelBar vram_usage_bar {
-                hexpand: true;
-                value: bind template.vram-usage;
-                orientation: horizontal;
+            Label {
+                label: "VRAM Usage";
+                xalign: 0.0;
             }
 
-            [overlay]
-            Label vram_usage_label {
-                label: bind template.vram-usage-text;
+            Overlay {
+                LevelBar vram_usage_bar {
+                    hexpand: true;
+                    height-request: 18;
+                    value: bind template.vram-usage;
+                    orientation: horizontal;
+                }
+
+                [overlay]
+                Label vram_usage_label {
+                    label: bind template.vram-usage-text;
+                }
             }
         }
-    }
 
-    Box {
-        orientation: horizontal;
-        spacing: 10;
+        Separator {
+            orientation: horizontal;
+        }
 
         Box {
-            orientation: vertical;
-            hexpand: true;
-            spacing: 5;
+            orientation: horizontal;
+            margin-bottom: 12;
+            margin-start: 12;
+            margin-end: 12;
+            homogeneous: true;
+            spacing: 12;
 
-            $InfoRow {
-                name: "GPU Core Clock:";
-                value: bind template.core-clock;
-            }
+            Box {
+                orientation: vertical;
+                hexpand: true;
+                spacing: 6;
 
-            $InfoRow {
-                name: "GPU Voltage:";
-                value: bind template.voltage;
-            }
+                $InfoRow {
+                    name: "GPU Core Clock:";
+                    value: bind template.core-clock;
+                }
+
+                $InfoRow {
+                    name: "GPU Voltage:";
+                    value: bind template.voltage;
+                }
 
-            $InfoRow {
-                name: "GPU Temperature (hotspot):";
-                value: bind template.temperature;
+                $InfoRow {
+                    name: "GPU Temperature (hotspot):";
+                    value: bind template.temperature;
+                }
             }
-        }
 
-        Box {
-            orientation: vertical;
-            hexpand: true;
-            spacing: 5;
+            //Separator {
+            //    orientation: vertical;
+            //}
 
-            $InfoRow {
-                name: "GPU Memory Clock:";
-                value: bind template.vram-clock;
-            }
+            Box {
+                orientation: vertical;
+                hexpand: true;
+                spacing: 6;
 
-            $InfoRow {
-                name: "GPU Usage:";
-                value: bind template.gpu-usage;
-            }
+                $InfoRow {
+                    name: "GPU Memory Clock:";
+                    value: bind template.vram-clock;
+                }
+
+                $InfoRow {
+                    name: "GPU Usage:";
+                    value: bind template.gpu-usage;
+                }
 
-            $InfoRow {
-                name: "Power Usage:";
-                value: bind template.power-usage;
+                $InfoRow {
+                    name: "Power Usage:";
+                    value: bind template.power-usage;
+                }
             }
         }
     }

From d420599fffb6e258b9c9e960fb3b2a1b6a7e44eb Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 21:31:16 +0100
Subject: [PATCH 19/54] feat: start/end margin of 6 for clamps

---
 lact-gui/src/app/root_stack/mod.rs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs
index 77739d60..0a18884a 100644
--- a/lact-gui/src/app/root_stack/mod.rs
+++ b/lact-gui/src/app/root_stack/mod.rs
@@ -91,6 +91,8 @@ pub fn list_clamp(child: &impl IsA<Widget>) -> libadwaita::Clamp {
         .maximum_size(600)
         .margin_top(24)
         .margin_bottom(24)
+        .margin_start(6)
+        .margin_end(6)
         .child(child)
         .valign(Align::Start)
         .build()

From c08b5f0f90e1a82f8364186ad0b0f96fe58a5855 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 21:31:27 +0100
Subject: [PATCH 20/54] fix: minimum size up to 420

---
 lact-gui/src/app/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index 2e683b99..413d01ba 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -45,7 +45,7 @@ impl App {
             .title("LACT")
             .default_width(700)
             .default_height(820)
-            .width_request(350)
+            .width_request(420)
             .height_request(200)
             .icon_name(APP_ID)
             .build();

From 1be6269514cb066392ea5b3f16bb8b650bd926d3 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 21:31:59 +0100
Subject: [PATCH 21/54] feat: shorten overclock gpu stats labels

---
 lact-gui/ui/oc_page/gpu_stats_section.blp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/lact-gui/ui/oc_page/gpu_stats_section.blp b/lact-gui/ui/oc_page/gpu_stats_section.blp
index 7f068cd0..e41c6ecf 100644
--- a/lact-gui/ui/oc_page/gpu_stats_section.blp
+++ b/lact-gui/ui/oc_page/gpu_stats_section.blp
@@ -2,7 +2,7 @@ using Gtk 4.0;
 using Adw 1;
 
 template $GpuStatsSection: $PageSection {
-    name: "Statistics";
+    name: "GPU Statistics";
     spacing: 12;
 
     Box {
@@ -55,17 +55,17 @@ template $GpuStatsSection: $PageSection {
                 spacing: 6;
 
                 $InfoRow {
-                    name: "GPU Core Clock:";
+                    name: "Core Clock:";
                     value: bind template.core-clock;
                 }
 
                 $InfoRow {
-                    name: "GPU Voltage:";
+                    name: "Voltage:";
                     value: bind template.voltage;
                 }
 
                 $InfoRow {
-                    name: "GPU Temperature (hotspot):";
+                    name: "Temp (hotspot):";
                     value: bind template.temperature;
                 }
             }
@@ -80,12 +80,12 @@ template $GpuStatsSection: $PageSection {
                 spacing: 6;
 
                 $InfoRow {
-                    name: "GPU Memory Clock:";
+                    name: "Memory Clock:";
                     value: bind template.vram-clock;
                 }
 
                 $InfoRow {
-                    name: "GPU Usage:";
+                    name: "Usage:";
                     value: bind template.gpu-usage;
                 }
 

From 2567a6518435720c539c09d26fa5413f34ca99af Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 21:32:23 +0100
Subject: [PATCH 22/54] feat: card style for power usage limit section

---
 lact-gui/ui/oc_page/power_cap_section.blp | 44 ++++++++++++++---------
 1 file changed, 27 insertions(+), 17 deletions(-)

diff --git a/lact-gui/ui/oc_page/power_cap_section.blp b/lact-gui/ui/oc_page/power_cap_section.blp
index 942ec8fc..56aff82b 100644
--- a/lact-gui/ui/oc_page/power_cap_section.blp
+++ b/lact-gui/ui/oc_page/power_cap_section.blp
@@ -1,27 +1,37 @@
 using Gtk 4.0;
+using Adw 1;
 
 template $PowerCapSection: $PageSection {
     name: "Power usage limit";
     
-    Box {
-        orientation: horizontal;
-        
-        Label value_label {
-            label: bind template.value-text;
-        }
+    Adw.Bin {
+        styles ["card"]
 
-        Scale {
+        Box {
             orientation: horizontal;
-            hexpand: true;
-            round-digits: 0;
-            margin-start: 5;
-            margin-end: 5;
-            draw-value: false;
-            adjustment: adjustment;
-        }
-        
-        Button reset_button {
-            label: "Default";
+            margin-top: 12;
+            margin-bottom: 12;
+            margin-start: 12;
+            margin-end: 12;
+            spacing: 6;
+            
+            Label value_label {
+                label: bind template.value-text;
+            }
+
+            Scale {
+                orientation: horizontal;
+                hexpand: true;
+                round-digits: 0;
+                draw-value: false;
+                adjustment: adjustment;
+            }
+            
+            Button reset_button {
+                styles ["circular", "flat"]
+                icon-name: "view-refresh-symbolic";
+                tooltip-text: "Reset to default";
+            }
         }
     }
 }

From 101f70f366863598f85b25e5f355d5890bef3a56 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 22:32:45 +0100
Subject: [PATCH 23/54] feat: redesign performance and power states sections

---
 .../root_stack/oc_page/performance_frame.rs   | 113 ++++++++----------
 .../power_states/power_states_frame.rs        |  24 ++--
 lact-gui/ui/oc_page/gpu_stats_section.blp     |   2 +-
 lact-gui/ui/oc_page/power_states_frame.blp    |  31 ++---
 4 files changed, 70 insertions(+), 100 deletions(-)

diff --git a/lact-gui/src/app/root_stack/oc_page/performance_frame.rs b/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
index 33f1c049..a5a29510 100644
--- a/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
@@ -5,16 +5,14 @@ use gtk::*;
 use lact_client::schema::amdgpu_sysfs::gpu_handle::{
     power_profile_mode::PowerProfileModesTable, PerformanceLevel,
 };
+use libadwaita::prelude::{ComboRowExt, ActionRowExt};
 use std::{cell::RefCell, rc::Rc, str::FromStr};
 
 #[derive(Clone)]
 pub struct PerformanceFrame {
     pub container: PageSection,
-    level_drop_down: DropDown,
-    mode_drop_down: DropDown,
-    description_label: Label,
-    manual_info_button: MenuButton,
-    mode_box: Box,
+    level_row: libadwaita::ComboRow,
+    mode_row: libadwaita::ComboRow,
     modes_table: Rc<RefCell<Option<PowerProfileModesTable>>>,
 }
 
@@ -22,68 +20,54 @@ impl PerformanceFrame {
     pub fn new() -> Self {
         let container = PageSection::new("Performance");
 
+        let listbox = ListBox::builder()
+            .css_classes(["boxed-list"])
+            .selection_mode(SelectionMode::None)
+            .build();
+
         let levels_model: StringList = ["Automatic", "Highest Clocks", "Lowest Clocks", "Manual"]
             .into_iter()
             .collect();
 
-        let level_box = Box::new(Orientation::Horizontal, 10);
-
-        let level_drop_down = DropDown::builder()
+        let level_row = libadwaita::ComboRow::builder()
             .model(&levels_model)
+            .title("Performance level")
+            .subtitle("")
+            .subtitle_lines(0)
             .sensitive(false)
             .build();
-        let description_label = Label::builder().halign(Align::End).hexpand(true).build();
-        let perfromance_title_label = Label::builder().label("Performance level:").build();
 
-        level_box.append(&perfromance_title_label);
-        level_box.append(&description_label);
-        level_box.append(&level_drop_down);
+        listbox.append(&level_row);
 
-        container.append(&level_box);
+        let filler_model: StringList = [""].into_iter().collect();
 
-        let mode_box = Box::new(Orientation::Horizontal, 10);
-
-        let mode_drop_down = DropDown::builder()
+        let mode_row = libadwaita::ComboRow::builder()
+            .model(&filler_model)
+            .title("Power level mode")
+            .subtitle("Set \"Performance level\" to \"Manual\" to use power states and modes")
+            .subtitle_lines(0)
             .sensitive(false)
-            .halign(Align::End)
             .build();
 
-        let unavailable_label = Label::new(Some(
-            "Performance level has to be set to \"manual\" to use power states and modes",
-        ));
-        let mode_info_popover = Popover::builder().child(&unavailable_label).build();
-        let manual_info_button = MenuButton::builder()
-            .icon_name("dialog-information-symbolic")
-            .hexpand(true)
-            .halign(Align::End)
-            .popover(&mode_info_popover)
-            .build();
+        listbox.append(&mode_row);
 
-        let mode_title_label = Label::new(Some("Power level mode:"));
-        mode_box.append(&mode_title_label);
-        mode_box.append(&manual_info_button);
-        mode_box.append(&mode_drop_down);
-
-        container.append(&mode_box);
+        container.append(&listbox);
 
         let frame = Self {
             container,
-            level_drop_down,
-            mode_drop_down,
-            description_label,
-            manual_info_button,
-            mode_box,
+            level_row,
+            mode_row,
             modes_table: Rc::new(RefCell::new(None)),
         };
 
         frame
-            .level_drop_down
+            .level_row
             .connect_selected_notify(clone!(@strong frame => move |_| {
                 frame.update_from_selection();
             }));
 
         frame
-            .mode_drop_down
+            .mode_row
             .connect_selected_notify(clone!(@strong frame => move |_| {
                 frame.update_from_selection();
             }));
@@ -92,18 +76,18 @@ impl PerformanceFrame {
     }
 
     pub fn set_active_level(&self, level: PerformanceLevel) {
-        self.level_drop_down.set_sensitive(true);
+        self.level_row.set_sensitive(true);
         match level {
-            PerformanceLevel::Auto => self.level_drop_down.set_selected(0),
-            PerformanceLevel::High => self.level_drop_down.set_selected(1),
-            PerformanceLevel::Low => self.level_drop_down.set_selected(2),
-            PerformanceLevel::Manual => self.level_drop_down.set_selected(3),
+            PerformanceLevel::Auto => self.level_row.set_selected(0),
+            PerformanceLevel::High => self.level_row.set_selected(1),
+            PerformanceLevel::Low => self.level_row.set_selected(2),
+            PerformanceLevel::Manual => self.level_row.set_selected(3),
         };
         self.update_from_selection();
     }
 
     pub fn set_power_profile_modes(&self, table: Option<PowerProfileModesTable>) {
-        self.mode_box.set_visible(table.is_some());
+        self.mode_row.set_visible(table.is_some());
 
         match &table {
             Some(table) => {
@@ -114,41 +98,42 @@ impl PerformanceFrame {
                     .position(|key| *key == table.active)
                     .expect("No active mode") as u32;
 
-                self.mode_drop_down.set_model(Some(&model));
-                self.mode_drop_down.set_selected(active_pos);
+                self.mode_row.set_model(Some(&model));
+                self.mode_row.set_selected(active_pos);
+
+                // set mode_row sensitivity because it gets reset to sensitive
+                // after setting the model
+                self.update_from_selection();
 
-                self.mode_drop_down.show();
+                self.mode_row.show();
             }
             None => {
-                self.mode_drop_down.hide();
+                self.mode_row.hide();
             }
         }
         self.modes_table.replace(table);
     }
 
     pub fn connect_settings_changed<F: Fn() + 'static + Clone>(&self, f: F) {
-        self.level_drop_down
+        self.level_row
             .connect_selected_notify(clone!(@strong f => move |_| f()));
-        self.mode_drop_down.connect_selected_notify(move |_| f());
+        self.mode_row.connect_selected_notify(move |_| f());
     }
 
     pub fn get_selected_performance_level(&self) -> PerformanceLevel {
-        let selected_item = self
-            .level_drop_down
-            .selected_item()
-            .expect("No selected item");
+        let selected_item = self.level_row.selected_item().expect("No selected item");
         let string_object = selected_item.downcast_ref::<StringObject>().unwrap();
         PerformanceLevel::from_str(string_object.string().as_str())
             .expect("Unrecognized selected performance level")
     }
 
     pub fn get_selected_power_profile_mode(&self) -> Option<u16> {
-        if self.mode_drop_down.is_sensitive() {
+        if self.mode_row.is_sensitive() {
             self.modes_table.borrow().as_ref().map(|table| {
                 let selected_index = table
                     .modes
                     .keys()
-                    .nth(self.mode_drop_down.selected() as usize)
+                    .nth(self.mode_row.selected() as usize)
                     .expect("Selected mode out of range");
                 *selected_index
             })
@@ -160,7 +145,7 @@ impl PerformanceFrame {
     fn update_from_selection(&self) {
         let mut enable_mode_control = false;
 
-        let text = match self.level_drop_down.selected() {
+        self.level_row.set_subtitle(match self.level_row.selected() {
             0 => "Automatically adjust GPU and VRAM clocks. (Default)",
             1 => "Always use the highest clockspeeds for GPU and VRAM.",
             2 => "Always use the lowest clockspeeds for GPU and VRAM.",
@@ -169,12 +154,8 @@ impl PerformanceFrame {
                 "Manual performance control."
             }
             _ => unreachable!(),
-        };
-        self.description_label.set_text(text);
-        self.mode_drop_down.set_sensitive(enable_mode_control);
-
-        self.manual_info_button.set_visible(!enable_mode_control);
-        self.mode_drop_down.set_hexpand(enable_mode_control);
+        });
+        self.mode_row.set_sensitive(enable_mode_control);
     }
 
     pub fn show(&self) {
diff --git a/lact-gui/src/app/root_stack/oc_page/power_states/power_states_frame.rs b/lact-gui/src/app/root_stack/oc_page/power_states/power_states_frame.rs
index a49f1dfe..c37bcd95 100644
--- a/lact-gui/src/app/root_stack/oc_page/power_states/power_states_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/power_states/power_states_frame.rs
@@ -1,14 +1,12 @@
 use std::collections::HashMap;
 
-use gtk::{
-    glib::{self, subclass::types::ObjectSubclassIsExt, Object},
-    prelude::WidgetExt,
-};
+use crate::app::page_section::PageSection;
+use gtk::glib::{self, subclass::types::ObjectSubclassIsExt, Object};
 use lact_client::schema::{amdgpu_sysfs::gpu_handle::PowerLevelKind, DeviceStats, PowerStates};
 
 glib::wrapper! {
     pub struct PowerStatesFrame(ObjectSubclass<imp::PowerStatesFrame>)
-        @extends gtk::Widget,
+        @extends PageSection, gtk::Box, gtk::Widget,
         @implements gtk::Accessible, gtk::Buildable;
 }
 
@@ -20,11 +18,6 @@ impl PowerStatesFrame {
     pub fn set_power_states(&self, states: PowerStates) {
         let imp = self.imp();
 
-        imp.expander.set_sensitive(!states.is_empty());
-        if states.is_empty() {
-            imp.expander.set_expanded(false);
-        }
-
         imp.core_states_list.set_power_states(states.core, "MHz");
         imp.vram_states_list.set_power_states(states.vram, "MHz");
     }
@@ -65,7 +58,10 @@ impl Default for PowerStatesFrame {
 }
 
 mod imp {
-    use crate::app::root_stack::oc_page::power_states::power_states_list::PowerStatesList;
+    use crate::app::{
+        page_section::PageSection,
+        root_stack::oc_page::power_states::power_states_list::PowerStatesList,
+    };
     use gtk::{
         glib::{self, subclass::InitializingObject, Properties, StaticTypeExt},
         prelude::ObjectExt,
@@ -73,7 +69,7 @@ mod imp {
             prelude::*,
             widget::{CompositeTemplateClass, WidgetImpl},
         },
-        CompositeTemplate, Expander,
+        CompositeTemplate,
     };
     use std::sync::atomic::AtomicBool;
 
@@ -81,8 +77,6 @@ mod imp {
     #[properties(wrapper_type = super::PowerStatesFrame)]
     #[template(file = "ui/oc_page/power_states_frame.blp")]
     pub struct PowerStatesFrame {
-        #[template_child]
-        pub expander: TemplateChild<Expander>,
         #[template_child]
         pub core_states_list: TemplateChild<PowerStatesList>,
         #[template_child]
@@ -96,7 +90,7 @@ mod imp {
     impl ObjectSubclass for PowerStatesFrame {
         const NAME: &'static str = "PowerStatesFrame";
         type Type = super::PowerStatesFrame;
-        type ParentType = gtk::Box;
+        type ParentType = PageSection;
 
         fn class_init(class: &mut Self::Class) {
             PowerStatesList::ensure_type();
diff --git a/lact-gui/ui/oc_page/gpu_stats_section.blp b/lact-gui/ui/oc_page/gpu_stats_section.blp
index e41c6ecf..6253af34 100644
--- a/lact-gui/ui/oc_page/gpu_stats_section.blp
+++ b/lact-gui/ui/oc_page/gpu_stats_section.blp
@@ -2,7 +2,7 @@ using Gtk 4.0;
 using Adw 1;
 
 template $GpuStatsSection: $PageSection {
-    name: "GPU Statistics";
+    name: "GPU statistics";
     spacing: 12;
 
     Box {
diff --git a/lact-gui/ui/oc_page/power_states_frame.blp b/lact-gui/ui/oc_page/power_states_frame.blp
index 0271aa91..a6652956 100644
--- a/lact-gui/ui/oc_page/power_states_frame.blp
+++ b/lact-gui/ui/oc_page/power_states_frame.blp
@@ -1,26 +1,21 @@
 using Gtk 4.0;
 
-template $PowerStatesFrame: Box {
-    Expander expander {
-        label: "Power states";
+template $PowerStatesFrame: $PageSection {
+    name: "Power states";
+    spacing: 12;
 
-        Box {
-            margin-start: 10;
-            margin-end: 10;
-            margin-top: 10;
-            margin-bottom: 10;
-            spacing: 10;
-            orientation: horizontal;
+    Box {
+        orientation: horizontal;
+        spacing: 12;
 
-            $PowerStatesList core_states_list {
-                title: "GPU power states";
-                sensitive: bind template.configurable;
-            }
+        $PowerStatesList core_states_list {
+            title: "GPU power states";
+            sensitive: bind template.configurable;
+        }
 
-            $PowerStatesList vram_states_list {
-                title: "VRAM power states";
-                sensitive: bind template.configurable;
-            }
+        $PowerStatesList vram_states_list {
+            title: "VRAM power states";
+            sensitive: bind template.configurable;
         }
     }
 }

From 175a6582b611cc3ccde4cc460b7373c03bcd02f0 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 23:18:16 +0100
Subject: [PATCH 24/54] feat: redesign clock frame section

---
 .../app/root_stack/oc_page/clocks_frame.rs    | 219 ++++++++----------
 1 file changed, 103 insertions(+), 116 deletions(-)

diff --git a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
index 263e0fd3..0a43ef76 100644
--- a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
@@ -4,22 +4,21 @@ use gtk::prelude::*;
 use gtk::*;
 use lact_client::schema::amdgpu_sysfs;
 use lact_client::schema::amdgpu_sysfs::gpu_handle::overdrive::{ClocksTable, ClocksTableGen};
+use libadwaita::prelude::ActionRowExt;
 use std::rc::Rc;
 use std::sync::atomic::{AtomicBool, Ordering};
 use tracing::debug;
 
 const VOLTAGE_OFFSET_RANGE: f64 = 250.0;
-const WARNING_TEXT: &str = "Warning: changing these values may lead to system instability and potentially damage your hardware!";
 
 // The AtomicBool stores if the value was changed
 #[derive(Clone)]
 pub struct ClocksFrame {
     pub container: PageSection,
-    tweaking_grid: Grid,
-    modes_switcher_box: Box,
-    basic_togglebutton: ToggleButton,
-    advanced_togglebutton: ToggleButton,
-    min_values_grid: Grid,
+    max_values_box: Box,
+    heading_listbox: ListBox,
+    advanced_switch_row: libadwaita::SwitchRow,
+    min_values_box: Box,
     min_sclk_adjustment: (Adjustment, Rc<AtomicBool>),
     min_mclk_adjustment: (Adjustment, Rc<AtomicBool>),
     min_voltage_adjustment: (Adjustment, Rc<AtomicBool>),
@@ -28,7 +27,6 @@ pub struct ClocksFrame {
     max_voltage_adjustment: (Adjustment, Rc<AtomicBool>),
     voltage_offset_adjustment: (Adjustment, Rc<AtomicBool>),
     reset_button: Button,
-    warning_label: Label,
     clocks_data_unavailable_label: Label,
 }
 
@@ -36,55 +34,94 @@ impl ClocksFrame {
     pub fn new() -> Self {
         let container = PageSection::new("Clockspeed and voltage");
 
-        let warning_label = Label::builder()
-            .label(WARNING_TEXT)
-            .wrap_mode(pango::WrapMode::Word)
-            .halign(Align::Start)
-            .margin_top(5)
-            .margin_bottom(5)
+        let heading_listbox = ListBox::builder()
+            .css_classes(["boxed-list"])
+            .selection_mode(SelectionMode::None)
             .build();
-        container.append(&warning_label);
 
-        let modes_switcher_box = Box::new(Orientation::Horizontal, 0);
+        heading_listbox.append(&libadwaita::ActionRow::builder()
+            .css_classes(["warning"])
+            .title("Warning!")
+            .subtitle("Changing these values may lead to system instability and potentially damage your hardware!")
+            .subtitle_lines(0)
+            .build());
 
-        let modes_switcher_label = Label::builder()
-            .label("Configuration mode:")
-            .hexpand(true)
-            .halign(Align::Start)
+        let advanced_switch_row = libadwaita::SwitchRow::builder()
+            .title("Advanced mode")
+            .active(false)
             .build();
-        let basic_togglebutton = ToggleButton::builder().label("Basic").build();
-        let advanced_togglebutton = ToggleButton::builder().label("Advanced").build();
-
-        modes_switcher_box.append(&modes_switcher_label);
-        modes_switcher_box.append(&basic_togglebutton);
-        modes_switcher_box.append(&advanced_togglebutton);
+        heading_listbox.append(&advanced_switch_row);
 
-        container.append(&modes_switcher_box);
+        container.append(&heading_listbox);
 
-        let min_values_grid = Grid::builder().row_spacing(5).build();
+        let min_values_box = Box::builder()
+            .orientation(Orientation::Vertical)
+            .spacing(12)
+            .build();
+        min_values_box.append(
+            &Label::builder()
+                .label("Minimum Values")
+                .xalign(0.0)
+                .css_classes(["title-4"])
+                .build(),
+        );
+        let min_values_listbox = ListBox::builder()
+            .css_classes(["boxed-list"])
+            .selection_mode(SelectionMode::None)
+            .build();
+        min_values_box.append(&min_values_listbox);
 
-        let min_sclk_adjustment = oc_adjustment("Minimum GPU Clock (MHz)", &min_values_grid, 0);
-        let min_mclk_adjustment = oc_adjustment("Minimum VRAM Clock (MHz)", &min_values_grid, 1);
-        let min_voltage_adjustment = oc_adjustment("Minimum GPU voltage (mV)", &min_values_grid, 2);
+        let min_sclk_adjustment = oc_adjustment("Minimum GPU Clock (MHz)", &min_values_listbox);
+        let min_mclk_adjustment = oc_adjustment("Minimum VRAM Clock (MHz)", &min_values_listbox);
+        let min_voltage_adjustment = oc_adjustment("Minimum GPU voltage (mV)", &min_values_listbox);
 
-        container.append(&min_values_grid);
+        container.append(&min_values_box);
 
-        let tweaking_grid = Grid::builder().row_spacing(5).build();
+        let max_values_box = Box::builder()
+            .orientation(Orientation::Vertical)
+            .spacing(12)
+            .build();
+        max_values_box.append(
+            &Label::builder()
+                .label("Maximum Values")
+                .xalign(0.0)
+                .css_classes(["title-4"])
+                .build(),
+        );
+        let max_values_listbox = ListBox::builder()
+            .css_classes(["boxed-list"])
+            .selection_mode(SelectionMode::None)
+            .build();
+        max_values_box.append(&max_values_listbox);
 
-        let max_sclk_adjustment = oc_adjustment("Maximum GPU Clock (MHz)", &tweaking_grid, 1);
-        let max_voltage_adjustment = oc_adjustment("Maximum GPU voltage (mV)", &tweaking_grid, 2);
-        let max_mclk_adjustment = oc_adjustment("Maximum VRAM Clock (MHz)", &tweaking_grid, 3);
-        let voltage_offset_adjustment = oc_adjustment("GPU voltage offset (mV)", &tweaking_grid, 4);
+        let max_sclk_adjustment = oc_adjustment("Maximum GPU Clock (MHz)", &max_values_listbox);
+        let max_voltage_adjustment = oc_adjustment("Maximum GPU voltage (mV)", &max_values_listbox);
+        let max_mclk_adjustment = oc_adjustment("Maximum VRAM Clock (MHz)", &max_values_listbox);
+        let voltage_offset_adjustment =
+            oc_adjustment("GPU voltage offset (mV)", &max_values_listbox);
 
         let reset_button = Button::builder()
             .label("Reset")
-            .halign(Align::Fill)
-            .margin_top(5)
-            .margin_bottom(5)
-            .tooltip_text("Warning: this resets all clock settings to defaults!")
-            .css_classes(["destructive-action"])
+            .child(
+                &Label::builder()
+                    .label("Reset")
+                    .margin_start(12)
+                    .margin_end(12)
+                    .build(),
+            )
+            .valign(Align::Center)
+            .halign(Align::Center)
+            .css_classes(["destructive-action", "circular"])
             .build();
-        tweaking_grid.attach(&reset_button, 6, 5, 1, 1);
+        let reset_row = libadwaita::ActionRow::builder()
+            .title("Reset values")
+            .subtitle(
+                "Warning: this will reset all clock and voltage settings to their default values",
+            )
+            .subtitle_lines(0)
+            .build();
+        reset_row.add_suffix(&reset_button);
+        max_values_listbox.append(&reset_row);
 
         let clocks_data_unavailable_label = Label::builder()
             .label("No clocks data available")
@@ -92,12 +129,14 @@ impl ClocksFrame {
             .halign(Align::Start)
             .build();
 
-        container.append(&tweaking_grid);
+        container.append(&max_values_box);
         container.append(&clocks_data_unavailable_label);
 
         let frame = Self {
             container,
-            tweaking_grid,
+            max_values_box,
+            heading_listbox,
+            advanced_switch_row,
             min_sclk_adjustment,
             min_mclk_adjustment,
             min_voltage_adjustment,
@@ -107,24 +146,15 @@ impl ClocksFrame {
             reset_button,
             clocks_data_unavailable_label,
             voltage_offset_adjustment,
-            advanced_togglebutton,
-            basic_togglebutton,
-            min_values_grid,
-            warning_label,
-            modes_switcher_box,
+            min_values_box,
         };
 
         frame.set_configuration_mode(false);
 
         frame
-            .basic_togglebutton
-            .connect_clicked(clone!(@strong frame => move |button| {
-                frame.set_configuration_mode(!button.is_active());
-            }));
-        frame
-            .advanced_togglebutton
-            .connect_clicked(clone!(@strong frame => move |button| {
-                frame.set_configuration_mode(button.is_active());
+            .advanced_switch_row
+            .connect_active_notify(clone!(@strong frame => move |row| {
+                frame.set_configuration_mode(row.is_active());
             }));
 
         frame
@@ -258,16 +288,14 @@ impl ClocksFrame {
     }
 
     pub fn show(&self) {
-        self.tweaking_grid.show();
-        self.modes_switcher_box.show();
-        self.warning_label.show();
+        self.max_values_box.show();
+        self.heading_listbox.show();
         self.clocks_data_unavailable_label.hide();
     }
 
     pub fn hide(&self) {
-        self.tweaking_grid.hide();
-        self.modes_switcher_box.hide();
-        self.warning_label.hide();
+        self.max_values_box.hide();
+        self.heading_listbox.hide();
         self.clocks_data_unavailable_label.show();
     }
 
@@ -291,7 +319,7 @@ impl ClocksFrame {
     }
 
     pub fn get_settings(&self) -> ClocksSettings {
-        if self.tweaking_grid.is_visible() {
+        if self.max_values_box.is_visible() {
             let min_core_clock = get_adjustment_value(&self.min_sclk_adjustment);
             let min_memory_clock = get_adjustment_value(&self.min_mclk_adjustment);
             let min_voltage = get_adjustment_value(&self.min_voltage_adjustment);
@@ -320,10 +348,7 @@ impl ClocksFrame {
     }
 
     fn set_configuration_mode(&self, advanced: bool) {
-        self.advanced_togglebutton.set_active(advanced);
-        self.basic_togglebutton.set_active(!advanced);
-
-        self.min_values_grid.set_visible(advanced);
+        self.min_values_box.set_visible(advanced);
     }
 }
 
@@ -342,64 +367,26 @@ fn extract_value_and_range(
     Some((value, min, max))
 }
 
-fn oc_adjustment(title: &'static str, grid: &Grid, row: i32) -> (Adjustment, Rc<AtomicBool>) {
-    let label = Label::builder().label(title).halign(Align::Start).build();
-
+fn oc_adjustment(title: &'static str, listbox: &ListBox) -> (Adjustment, Rc<AtomicBool>) {
     let adjustment = Adjustment::new(0.0, 0.0, 0.0, 1.0, 10.0, 0.0);
 
-    let scale = Scale::builder()
-        .orientation(Orientation::Horizontal)
+    let value_selector = libadwaita::SpinRow::builder()
+        .title(title)
         .adjustment(&adjustment)
-        .hexpand(true)
-        .round_digits(0)
-        .digits(0)
-        .value_pos(PositionType::Right)
-        .margin_start(5)
-        .margin_end(5)
-        .build();
-
-    let value_selector = SpinButton::new(Some(&adjustment), 1.0, 0);
-    let value_label = Label::new(None);
-
-    let popover = Popover::builder().child(&value_selector).build();
-    let value_button = MenuButton::builder()
-        .popover(&popover)
-        .child(&value_label)
         .build();
 
     let changed = Rc::new(AtomicBool::new(false));
 
-    adjustment.connect_value_changed(
-        clone!(@strong value_label, @strong changed => move |adjustment| {
-            changed.store(true, Ordering::SeqCst);
-
-            let value = adjustment.value();
-            value_label.set_text(&value.to_string());
-        }),
-    );
-
-    adjustment.connect_changed(
-        clone!(@strong label, @strong value_label, @strong scale, @strong value_button => move |adjustment| {
-            let value = adjustment.value();
-            value_label.set_text(&value.to_string());
-
-            if adjustment.upper() == 0.0 {
-                label.hide();
-                value_label.hide();
-                scale.hide();
-                value_button.hide();
-            } else {
-                label.show();
-                value_label.show();
-                scale.show();
-                value_button.show();
-            }
+    adjustment.connect_value_changed(clone!(@strong changed => move |_| {
+        changed.store(true, Ordering::SeqCst);
+    }));
+
+    adjustment.connect_changed(clone!(@strong value_selector => move |adjustment| {
+            value_selector.set_sensitive(adjustment.upper() == 0.0);
         }
     ));
 
-    grid.attach(&label, 0, row, 1, 1);
-    grid.attach(&scale, 1, row, 4, 1);
-    grid.attach(&value_button, 6, row, 4, 1);
+    listbox.append(&value_selector);
 
     (adjustment, changed)
 }

From 9df6134e8f16e577d266b0f77493899965fc1bde Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 26 Nov 2023 23:36:57 +0100
Subject: [PATCH 25/54] fix: proper label for kernel version

---
 lact-gui/src/app/root_stack/software_page.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lact-gui/src/app/root_stack/software_page.rs b/lact-gui/src/app/root_stack/software_page.rs
index 366f91e9..b95d9127 100644
--- a/lact-gui/src/app/root_stack/software_page.rs
+++ b/lact-gui/src/app/root_stack/software_page.rs
@@ -23,7 +23,7 @@ pub fn software_page(system_info: SystemInfo, embedded: bool) -> gtk::ScrolledWi
 
     listbox.append(
         &LabelRow::new_with_content(
-            "LACT GUI",
+            "Kernel version",
             &format!(
                 "{}-{}",
                 GUI_VERSION,

From 807064af1ad74a9f481a77daa32281cc0ba16b16 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Mon, 27 Nov 2023 07:00:21 +0100
Subject: [PATCH 26/54] feat: redesign overclock warning frame

---
 lact-gui/src/app/root_stack/oc_page/mod.rs | 71 ++++++++++++++--------
 1 file changed, 47 insertions(+), 24 deletions(-)

diff --git a/lact-gui/src/app/root_stack/oc_page/mod.rs b/lact-gui/src/app/root_stack/oc_page/mod.rs
index 8261cbfc..a112ddbe 100644
--- a/lact-gui/src/app/root_stack/oc_page/mod.rs
+++ b/lact-gui/src/app/root_stack/oc_page/mod.rs
@@ -19,14 +19,10 @@ use lact_client::schema::{
 };
 use performance_frame::PerformanceFrame;
 // use power_cap_frame::PowerCapFrame;
+use super::list_clamp;
 use std::collections::HashMap;
 use tracing::warn;
 
-use super::list_clamp;
-
-const OVERCLOCKING_DISABLED_TEXT: &str = "Overclocking support is not enabled! \
-You can still change basic settings, but the more advanced clocks and voltage control will not be available.";
-
 #[derive(Clone)]
 pub struct OcPage {
     pub container: ScrolledWindow,
@@ -170,36 +166,63 @@ impl OcPage {
     }
 }
 
-fn oc_warning_frame() -> (Frame, Button) {
-    let container = Frame::new(Some("Overclocking information"));
-
-    container.set_label_align(0.3);
-
+fn oc_warning_frame() -> (libadwaita::Bin, Button) {
     let vbox = Box::builder()
         .orientation(Orientation::Vertical)
-        .spacing(5)
-        .margin_top(10)
-        .margin_bottom(10)
-        .margin_start(10)
-        .margin_end(10)
+        .spacing(6)
+        .margin_top(12)
+        .margin_bottom(12)
+        .margin_start(12)
+        .margin_end(12)
         .build();
 
-    let warning_label = Label::builder()
-        .use_markup(true)
-        .label(OVERCLOCKING_DISABLED_TEXT)
-        .wrap(true)
-        .wrap_mode(pango::WrapMode::Word)
+    let heading_box = Box::builder()
+        .orientation(Orientation::Horizontal)
+        .spacing(6)
+        .hexpand(false)
+        .halign(Align::Start)
         .build();
 
+    heading_box.append(
+        &Image::builder()
+            .icon_name("dialog-warning-symbolic")
+            .css_classes(["warning"])
+            .build(),
+    );
+    heading_box.append(
+        &Label::builder()
+            .css_classes(["warning", "heading"])
+            .label("Warning")
+            .xalign(0.0)
+            .build(),
+    );
+
+    vbox.append(&heading_box);
+
+    vbox.append(
+        &Label::builder()
+            .label(concat!(
+                "Overclocking support is not enabled. You can still change ",
+                "basic settings, but clocks and voltage control will not be available",
+            ))
+            .wrap(true)
+            .xalign(0.0)
+            .wrap_mode(pango::WrapMode::Word)
+            .build(),
+    );
+
     let enable_button = Button::builder()
         .label("Enable Overclocking")
         .halign(Align::End)
         .build();
 
-    vbox.append(&warning_label);
     vbox.append(&enable_button);
 
-    container.set_child(Some(&vbox));
-
-    (container, enable_button)
+    (
+        libadwaita::Bin::builder()
+            .css_classes(["card"])
+            .child(&vbox)
+            .build(),
+        enable_button,
+    )
 }

From ee9ab7e94e38ba6b29808feac1c6ebdb51572277 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Mon, 27 Nov 2023 07:03:47 +0100
Subject: [PATCH 27/54] chore: bigger default window size

---
 lact-gui/src/app/mod.rs                       |  4 ++--
 .../root_stack/oc_page/performance_frame.rs   | 23 ++++++++++---------
 2 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index 413d01ba..75c60a99 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -43,8 +43,8 @@ impl App {
         let gpu_selector = GpuSelector::new();
         let window = libadwaita::ApplicationWindow::builder()
             .title("LACT")
-            .default_width(700)
-            .default_height(820)
+            .default_width(820)
+            .default_height(750)
             .width_request(420)
             .height_request(200)
             .icon_name(APP_ID)
diff --git a/lact-gui/src/app/root_stack/oc_page/performance_frame.rs b/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
index a5a29510..5723e758 100644
--- a/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
@@ -5,7 +5,7 @@ use gtk::*;
 use lact_client::schema::amdgpu_sysfs::gpu_handle::{
     power_profile_mode::PowerProfileModesTable, PerformanceLevel,
 };
-use libadwaita::prelude::{ComboRowExt, ActionRowExt};
+use libadwaita::prelude::{ActionRowExt, ComboRowExt};
 use std::{cell::RefCell, rc::Rc, str::FromStr};
 
 #[derive(Clone)]
@@ -145,16 +145,17 @@ impl PerformanceFrame {
     fn update_from_selection(&self) {
         let mut enable_mode_control = false;
 
-        self.level_row.set_subtitle(match self.level_row.selected() {
-            0 => "Automatically adjust GPU and VRAM clocks. (Default)",
-            1 => "Always use the highest clockspeeds for GPU and VRAM.",
-            2 => "Always use the lowest clockspeeds for GPU and VRAM.",
-            3 => {
-                enable_mode_control = true;
-                "Manual performance control."
-            }
-            _ => unreachable!(),
-        });
+        self.level_row
+            .set_subtitle(match self.level_row.selected() {
+                0 => "Automatically adjust GPU and VRAM clocks. (Default)",
+                1 => "Always use the highest clockspeeds for GPU and VRAM.",
+                2 => "Always use the lowest clockspeeds for GPU and VRAM.",
+                3 => {
+                    enable_mode_control = true;
+                    "Manual performance control."
+                }
+                _ => unreachable!(),
+            });
         self.mode_row.set_sensitive(enable_mode_control);
     }
 

From 71487b23b50a05802bed6c622d74843526c474dc Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Mon, 27 Nov 2023 18:02:48 +0100
Subject: [PATCH 28/54] fix: add libadwaita package to package recipe

---
 pkg/recipes/lact/recipe.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/pkg/recipes/lact/recipe.yml b/pkg/recipes/lact/recipe.yml
index a5f1d433..439464a6 100644
--- a/pkg/recipes/lact/recipe.yml
+++ b/pkg/recipes/lact/recipe.yml
@@ -16,9 +16,9 @@ metadata:
     arch: [ gtk4 ]
   build_depends:
     all: [ curl, make, dbus, clang ]
-    debian-12+ubuntu-2204: [ libgtk-4-dev, pkg-config, build-essential, libdrm-dev ]
-    fedora-38+fedora-39: [ gtk4-devel, gcc, libdrm-devel, blueprint-compiler ]
-    arch: [ gtk4, blueprint-compiler ]
+    debian-12+ubuntu-2204: [ libgtk-4-dev, pkg-config, build-essential, libdrm-dev, libadwaita-1-dev ]
+    fedora-38+fedora-39: [ gtk4-devel, gcc, libdrm-devel, blueprint-compiler, libadwaita-devel ]
+    arch: [ gtk4, blueprint-compiler, libadwaita ]
   all_images: true
 env:
   RUSTUP_URL: https://sh.rustup.rs

From fe6e54e1c19bc76b4e4266a5177fae41e04c633f Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Fri, 1 Dec 2023 10:20:09 +0100
Subject: [PATCH 29/54] feat: view switcher bottom bar based navigation

---
 lact-gui/src/app/gpu_selector.rs   | 14 +++---
 lact-gui/src/app/mod.rs            | 74 +++---------------------------
 lact-gui/src/app/root_stack/mod.rs | 12 ++---
 3 files changed, 21 insertions(+), 79 deletions(-)

diff --git a/lact-gui/src/app/gpu_selector.rs b/lact-gui/src/app/gpu_selector.rs
index 4ef757bb..f1668b1d 100644
--- a/lact-gui/src/app/gpu_selector.rs
+++ b/lact-gui/src/app/gpu_selector.rs
@@ -11,12 +11,14 @@ pub struct GpuSelector {
 impl GpuSelector {
     pub fn new() -> Self {
         // WARN: gtk::ComboBoxText is deprecated, use gtk::DropDown instead
-        let dropdown = ComboBoxText::builder()
-            .margin_top(12)
-            .margin_bottom(12)
-            .margin_start(12)
-            .margin_end(12)
-            .build();
+        let dropdown = ComboBoxText::new();
+
+        dropdown
+            .first_child()
+            .unwrap()
+            .first_child()
+            .unwrap()
+            .add_css_class("flat");
 
         Self { dropdown }
     }
diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index 75c60a99..b45a252e 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -64,79 +64,19 @@ impl App {
 
         let root_view = libadwaita::ToolbarView::new();
 
-        let root_view_headerbar = libadwaita::HeaderBar::builder().show_title(true).build();
+        let root_view_headerbar = libadwaita::HeaderBar::builder().show_title(false).build();
         let apply_box = ApplyBox::new();
+        root_view_headerbar.pack_start(&gpu_selector.dropdown);
         root_view_headerbar.pack_end(&apply_box.container);
 
         root_view.add_top_bar(&root_view_headerbar);
-        root_view.set_content(Some(&root_stack.container));
-
-        let sidebar_view = libadwaita::ToolbarView::new();
-
-        let stack_sidebar = StackSidebar::builder()
+        root_view.add_bottom_bar(&libadwaita::ViewSwitcherBar::builder()
+            .reveal(true)
             .stack(&root_stack.container)
-            .vexpand(true)
-            .build();
-        stack_sidebar.remove_css_class("sidebar");
-        sidebar_view.add_top_bar(&libadwaita::HeaderBar::builder().show_title(true).build());
-        sidebar_view.set_content(Some(
-            &ScrolledWindow::builder()
-                .child(&stack_sidebar)
-                .vexpand(true)
-                .hscrollbar_policy(PolicyType::Never)
-                .build(),
-        ));
-        sidebar_view.add_bottom_bar(&gpu_selector.dropdown);
-
-        let root_nav_page = libadwaita::NavigationPage::builder()
-            .child(&root_view)
-            .title("Information")
-            .build();
-        let split_view = libadwaita::NavigationSplitView::builder()
-            .sidebar(
-                &libadwaita::NavigationPage::builder()
-                    .child(&sidebar_view)
-                    .title("LACT")
-                    .build(),
-            )
-            .content(&root_nav_page)
-            .build();
-
-        {
-            let split_view = split_view.clone();
-            let lb = stack_sidebar
-                .first_child()
-                .unwrap()
-                .first_child()
-                .unwrap()
-                .first_child()
-                .unwrap()
-                .downcast::<ListBox>()
-                .unwrap();
-            lb.connect_row_activated(move |_lb, _row| {
-                split_view.set_show_content(true);
-            });
-        }
-
-        {
-            let stack = root_stack.container.clone();
-            let root_nav_page = root_nav_page.clone();
-            root_stack.container.connect_visible_child_notify(move |_| {
-                if let Some(child) = stack.visible_child() {
-                    root_nav_page.set_title(stack.page(&child).title().unwrap().as_str())
-                }
-            });
-        }
-
-        let breakpoint = libadwaita::Breakpoint::new(libadwaita::BreakpointCondition::new_length(
-            libadwaita::BreakpointConditionLengthType::MaxWidth,
-            800.0,
-            libadwaita::LengthUnit::Sp,
-        ));
-        breakpoint.add_setter(&split_view, "collapsed", &glib::Value::from(true));
-        window.add_breakpoint(breakpoint);
+            .build());
+        root_view.set_content(Some(&root_stack.container));
 
-        window.set_content(Some(&split_view));
+        window.set_content(Some(&root_view));
 
         App {
             application,
diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs
index 0a18884a..06f7f147 100644
--- a/lact-gui/src/app/root_stack/mod.rs
+++ b/lact-gui/src/app/root_stack/mod.rs
@@ -13,7 +13,7 @@ use thermals_page::ThermalsPage;
 
 #[derive(Clone)]
 pub struct RootStack {
-    pub container: Stack,
+    pub container: libadwaita::ViewStack,
     pub info_page: InformationPage,
     pub thermals_page: ThermalsPage,
     pub oc_page: OcPage,
@@ -25,22 +25,22 @@ impl RootStack {
         system_info: SystemInfo,
         embedded_daemon: bool,
     ) -> Self {
-        let container = Stack::builder().vexpand(true).hexpand(true).build();
+        let container = libadwaita::ViewStack::builder().vexpand(true).hexpand(true).build();
 
         let info_page = InformationPage::new();
 
-        container.add_titled(&info_page.container, Some("info_page"), "Information");
+        container.add_titled_with_icon(&info_page.container, Some("info_page"), "Information", "info-symbolic");
 
         let oc_page = OcPage::new(&system_info);
 
-        container.add_titled(&oc_page.container, Some("oc_page"), "Overclock");
+        container.add_titled_with_icon(&oc_page.container, Some("oc_page"), "Overclock", "power-profile-performance-symbolic");
 
         let thermals_page = ThermalsPage::new(root_win);
 
-        container.add_titled(&thermals_page.container, Some("thermals_page"), "Thermals");
+        container.add_titled_with_icon(&thermals_page.container, Some("thermals_page"), "Thermals", "temperature-symbolic");
 
         let software_page = software_page(system_info, embedded_daemon);
-        container.add_titled(&software_page, Some("software_page"), "Software");
+        container.add_titled_with_icon(&software_page, Some("software_page"), "Software", "preferences-other-symbolic");
 
         Self {
             container,

From a34bd1bdfc75c5168ac1523191de62ad9e67ee81 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Fri, 1 Dec 2023 10:44:41 +0100
Subject: [PATCH 30/54] feat: headerbar in its own module; derive debug for
 many structs

---
 lact-gui/src/app/apply_box.rs                 |  2 +-
 lact-gui/src/app/gpu_selector.rs              |  2 +-
 lact-gui/src/app/headerbar.rs                 | 25 ++++++++++++++++++
 lact-gui/src/app/mod.rs                       | 26 +++++++++----------
 lact-gui/src/app/root_stack/info_page/mod.rs  |  2 +-
 .../root_stack/info_page/vulkan_info/mod.rs   |  2 +-
 lact-gui/src/app/root_stack/mod.rs            |  4 +--
 .../app/root_stack/oc_page/clocks_frame.rs    |  2 +-
 lact-gui/src/app/root_stack/oc_page/mod.rs    |  2 +-
 .../root_stack/oc_page/performance_frame.rs   |  2 +-
 .../thermals_page/fan_curve_frame/mod.rs      |  2 +-
 .../fan_curve_frame/point_adjustment.rs       |  2 +-
 .../src/app/root_stack/thermals_page/mod.rs   |  2 +-
 13 files changed, 50 insertions(+), 25 deletions(-)
 create mode 100644 lact-gui/src/app/headerbar.rs

diff --git a/lact-gui/src/app/apply_box.rs b/lact-gui/src/app/apply_box.rs
index 58054b2d..dbe331a5 100644
--- a/lact-gui/src/app/apply_box.rs
+++ b/lact-gui/src/app/apply_box.rs
@@ -1,7 +1,7 @@
 use gtk::prelude::*;
 use gtk::*;
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct ApplyBox {
     pub container: Box,
     apply_button: Button,
diff --git a/lact-gui/src/app/gpu_selector.rs b/lact-gui/src/app/gpu_selector.rs
index f1668b1d..3f7ae931 100644
--- a/lact-gui/src/app/gpu_selector.rs
+++ b/lact-gui/src/app/gpu_selector.rs
@@ -3,7 +3,7 @@ use gtk::*;
 use lact_client::schema::DeviceListEntry;
 use pango::EllipsizeMode;
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct GpuSelector {
     pub dropdown: ComboBoxText,
 }
diff --git a/lact-gui/src/app/headerbar.rs b/lact-gui/src/app/headerbar.rs
new file mode 100644
index 00000000..f40bf774
--- /dev/null
+++ b/lact-gui/src/app/headerbar.rs
@@ -0,0 +1,25 @@
+use super::{apply_box::ApplyBox, gpu_selector::GpuSelector};
+
+#[derive(Debug, Clone)]
+pub struct Headerbar {
+    pub container: libadwaita::HeaderBar,
+    pub gpu_selector: GpuSelector,
+    pub apply_box: ApplyBox,
+}
+
+impl Headerbar {
+    pub fn new() -> Self {
+        let container = libadwaita::HeaderBar::builder().show_title(false).build();
+        let gpu_selector = GpuSelector::new();
+        let apply_box = ApplyBox::new();
+
+        container.pack_start(&gpu_selector.dropdown);
+        container.pack_end(&apply_box.container);
+
+        Self {
+            container,
+            gpu_selector,
+            apply_box,
+        }
+    }
+}
diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index b45a252e..fc5fa145 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -1,10 +1,12 @@
 mod apply_box;
 mod gpu_selector;
+mod headerbar;
 mod info_row;
 mod page_section;
 mod root_stack;
 
 use self::apply_box::ApplyBox;
+use self::headerbar::Headerbar;
 use crate::{APP_ID, GUI_VERSION};
 use anyhow::{anyhow, Context};
 use glib::clone;
@@ -15,7 +17,7 @@ use lact_client::schema::request::{ConfirmCommand, SetClocksCommand};
 use lact_client::schema::DeviceStats;
 use lact_client::DaemonClient;
 use lact_daemon::MODULE_CONF_PATH;
-use libadwaita::prelude::{AdwApplicationWindowExt, MessageDialogExt, NavigationPageExt};
+use libadwaita::prelude::{AdwApplicationWindowExt, MessageDialogExt};
 use root_stack::RootStack;
 use std::cell::RefCell;
 use std::rc::Rc;
@@ -40,7 +42,6 @@ impl App {
     pub fn new(daemon_client: DaemonClient) -> Self {
         let application = libadwaita::Application::new(Some(APP_ID), ApplicationFlags::default());
 
-        let gpu_selector = GpuSelector::new();
         let window = libadwaita::ApplicationWindow::builder()
             .title("LACT")
             .default_width(820)
@@ -64,16 +65,15 @@ impl App {
 
         let root_view = libadwaita::ToolbarView::new();
 
-        let root_view_headerbar = libadwaita::HeaderBar::builder().show_title(false).build();
-        let apply_box = ApplyBox::new();
-        root_view_headerbar.pack_start(&gpu_selector.dropdown);
-        root_view_headerbar.pack_end(&apply_box.container);
+        let headerbar = Headerbar::new();
 
-        root_view.add_top_bar(&root_view_headerbar);
-        root_view.add_bottom_bar(&libadwaita::ViewSwitcherBar::builder()
-            .reveal(true)
-            .stack(&root_stack.container)
-            .build());
+        root_view.add_top_bar(&headerbar.container);
+        root_view.add_bottom_bar(
+            &libadwaita::ViewSwitcherBar::builder()
+                .reveal(true)
+                .stack(&root_stack.container)
+                .build(),
+        );
         root_view.set_content(Some(&root_stack.container));
 
         window.set_content(Some(&root_view));
@@ -81,9 +81,9 @@ impl App {
         App {
             application,
             window,
-            gpu_selector,
+            gpu_selector: headerbar.gpu_selector,
             root_stack,
-            apply_box,
+            apply_box: headerbar.apply_box,
             daemon_client,
         }
     }
diff --git a/lact-gui/src/app/root_stack/info_page/mod.rs b/lact-gui/src/app/root_stack/info_page/mod.rs
index 5037b80b..4d01573b 100644
--- a/lact-gui/src/app/root_stack/info_page/mod.rs
+++ b/lact-gui/src/app/root_stack/info_page/mod.rs
@@ -7,7 +7,7 @@ use gtk::*;
 use lact_client::schema::{DeviceInfo, DeviceStats};
 use vulkan_info::VulkanInfoFrame;
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct InformationPage {
     pub container: ScrolledWindow,
     gpu_name_row: LabelRow,
diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
index ed216995..eb25bc6e 100644
--- a/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
+++ b/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
@@ -10,7 +10,7 @@ use lact_client::schema::VulkanInfo;
 use libadwaita::prelude::ActionRowExt;
 use tracing::trace;
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct VulkanInfoFrame {
     pub container: ListBox,
     device_name_row: LabelRow,
diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs
index 06f7f147..00ee7b2a 100644
--- a/lact-gui/src/app/root_stack/mod.rs
+++ b/lact-gui/src/app/root_stack/mod.rs
@@ -11,7 +11,7 @@ use libadwaita::prelude::ActionRowExt;
 use oc_page::OcPage;
 use thermals_page::ThermalsPage;
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct RootStack {
     pub container: libadwaita::ViewStack,
     pub info_page: InformationPage,
@@ -51,7 +51,7 @@ impl RootStack {
     }
 }
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct LabelRow {
     pub container: libadwaita::ActionRow,
     content_label: Label,
diff --git a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
index 0a43ef76..216b34d4 100644
--- a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
@@ -12,7 +12,7 @@ use tracing::debug;
 const VOLTAGE_OFFSET_RANGE: f64 = 250.0;
 
 // The AtomicBool stores if the value was changed
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct ClocksFrame {
     pub container: PageSection,
     max_values_box: Box,
diff --git a/lact-gui/src/app/root_stack/oc_page/mod.rs b/lact-gui/src/app/root_stack/oc_page/mod.rs
index a112ddbe..3d086b4e 100644
--- a/lact-gui/src/app/root_stack/oc_page/mod.rs
+++ b/lact-gui/src/app/root_stack/oc_page/mod.rs
@@ -23,7 +23,7 @@ use super::list_clamp;
 use std::collections::HashMap;
 use tracing::warn;
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct OcPage {
     pub container: ScrolledWindow,
     stats_section: GpuStatsSection,
diff --git a/lact-gui/src/app/root_stack/oc_page/performance_frame.rs b/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
index 5723e758..52187ba8 100644
--- a/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
@@ -8,7 +8,7 @@ use lact_client::schema::amdgpu_sysfs::gpu_handle::{
 use libadwaita::prelude::{ActionRowExt, ComboRowExt};
 use std::{cell::RefCell, rc::Rc, str::FromStr};
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct PerformanceFrame {
     pub container: PageSection,
     level_row: libadwaita::ComboRow,
diff --git a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs
index 852aded7..970d078a 100644
--- a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs
@@ -9,7 +9,7 @@ use std::cell::RefCell;
 use std::collections::BTreeMap;
 use std::rc::Rc;
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct FanCurveFrame {
     pub container: Box,
     curve_container: ScrolledWindow,
diff --git a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/point_adjustment.rs b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/point_adjustment.rs
index 288498e9..7cf5c2d5 100644
--- a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/point_adjustment.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/point_adjustment.rs
@@ -6,7 +6,7 @@ use gtk::{
     Adjustment, Box, Label, MenuButton, Orientation, Popover, Scale, SpinButton,
 };
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct PointAdjustment {
     pub temperature: Adjustment,
     pub ratio: Adjustment,
diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs
index 79a8a562..5a9f4ce5 100644
--- a/lact-gui/src/app/root_stack/thermals_page/mod.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs
@@ -17,7 +17,7 @@ pub struct ThermalsSettings {
     pub curve: Option<FanCurveMap>,
 }
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct ThermalsPage {
     pub container: ScrolledWindow,
     temperatures_row: LabelRow,

From 2bf7435bf752dfc5af43158eee3b777b7e38c121 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Fri, 1 Dec 2023 11:40:16 +0100
Subject: [PATCH 31/54] feat: isolate info dialog into module

---
 lact-gui/src/app/dialogs.rs                   | 28 +++++++
 lact-gui/src/app/mod.rs                       | 78 ++++++++-----------
 lact-gui/src/app/root_stack/mod.rs            | 35 +++++++--
 .../src/app/root_stack/thermals_page/mod.rs   | 36 +++++----
 4 files changed, 109 insertions(+), 68 deletions(-)
 create mode 100644 lact-gui/src/app/dialogs.rs

diff --git a/lact-gui/src/app/dialogs.rs b/lact-gui/src/app/dialogs.rs
new file mode 100644
index 00000000..2cad3e3d
--- /dev/null
+++ b/lact-gui/src/app/dialogs.rs
@@ -0,0 +1,28 @@
+use gtk::prelude::*;
+use libadwaita::prelude::MessageDialogExt;
+use tracing::warn;
+
+#[macro_export]
+macro_rules! info_dialog {
+    ($parent:expr, $heading:expr, $body:expr, $response_id:expr, $response_txt:expr) => {{
+        let diag = libadwaita::MessageDialog::builder()
+            .heading($heading)
+            .body($body)
+            .modal(true)
+            .transient_for($parent)
+            .build();
+
+        diag.add_response($response_id, $response_txt);
+
+        diag.present();
+
+        diag
+    }};
+}
+
+pub fn show_error(parent: &impl IsA<gtk::Window>, err: anyhow::Error) {
+    let text = format!("{err:?}");
+    warn!("{}", text.trim());
+
+    info_dialog!(parent, "Error", &text, "close", "_Close");
+}
diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index fc5fa145..5266982a 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -1,4 +1,5 @@
 mod apply_box;
+mod dialogs;
 mod gpu_selector;
 mod headerbar;
 mod info_row;
@@ -7,7 +8,8 @@ mod root_stack;
 
 use self::apply_box::ApplyBox;
 use self::headerbar::Headerbar;
-use crate::{APP_ID, GUI_VERSION};
+use crate::app::dialogs::show_error;
+use crate::{info_dialog, APP_ID, GUI_VERSION};
 use anyhow::{anyhow, Context};
 use glib::clone;
 use gpu_selector::GpuSelector;
@@ -61,7 +63,7 @@ impl App {
             show_error(&window, err);
         }
 
-        let root_stack = RootStack::new(window.clone(), system_info, daemon_client.embedded);
+        let root_stack = RootStack::new(&window, system_info, daemon_client.embedded);
 
         let root_view = libadwaita::ToolbarView::new();
 
@@ -189,21 +191,17 @@ impl App {
                     hbox.append(&enable_label);
                     hbox.append(&copy_btn);
 
-                    let diag = libadwaita::MessageDialog::builder()
-                        .title("Daemon info")
-                        .heading("Could not connect to daemon")
-                        .body(format!("Running in embedded mode.\n\
+                    let diag = info_dialog!(
+                        &app.window,
+                        "Could not connect to daemon",
+                        format!("Running in embedded mode.\n\
                             Please make sure the lactd service is running.\n\
                             Using embedded mode, you will not be able to change any settings.\n\n\
                             {error_text}\
-                            To enable the daemon, run the following command:"))
-                        .extra_child(&hbox)
-                        .modal(true)
-                        .transient_for(&app.window)
-                        .build();
-
-                    diag.add_response("close", "_Close");
-                    diag.present();
+                            To enable the daemon, run the following command:"),
+                        "close",
+                        "_Close");
+                    diag.set_extra_child(Some(&hbox));
                 }
             }));
 
@@ -493,25 +491,28 @@ impl App {
         dialog.set_response_appearance(res_cancel, libadwaita::ResponseAppearance::Destructive);
         dialog.set_response_appearance(res_ok, libadwaita::ResponseAppearance::Suggested);
 
-        dialog.connect_response(None, clone!(@strong self as app => move |_, response| {
-            if response == res_ok {
-                match app.daemon_client.enable_overdrive().and_then(|buffer| buffer.inner()) {
-                    Ok(_) => {
-                        let success_dialog = libadwaita::MessageDialog::builder()
-                            .heading("Success")
-                            .body("Overclocking successfully enabled. A system reboot is required to apply the changes")
-                            .modal(true)
-                            .transient_for(&app.window)
-                            .build();
-                        success_dialog.add_response("ok", "_Ok");
-                        success_dialog.present();
-                    }
-                    Err(err) => {
-                        show_error(&app.window, err);
+        dialog.connect_response(
+            None,
+            clone!(@strong self as app => move |_, response| {
+                if response == res_ok {
+                    match app.daemon_client.enable_overdrive().and_then(|buffer| buffer.inner()) {
+                        Ok(_) => {
+                            info_dialog!(
+                                &app.window,
+                                "Success",
+                                concat!(
+                                    "Overclocking successfully enabled. ",
+                                    "A system reboot is required to apply the changes"),
+                                "ok",
+                                "_Ok");
+                        }
+                        Err(err) => {
+                            show_error(&app.window, err);
+                        }
                     }
                 }
-            }
-        }));
+            }),
+        );
 
         dialog.present();
     }
@@ -583,21 +584,6 @@ enum GuiUpdateMsg {
     GpuStats(DeviceStats),
 }
 
-fn show_error(parent: &libadwaita::ApplicationWindow, err: anyhow::Error) {
-    let text = format!("{err:?}");
-    warn!("{}", text.trim());
-    let diag = libadwaita::MessageDialog::builder()
-        .heading("Error")
-        .body(&text)
-        .modal(true)
-        .transient_for(parent)
-        .build();
-
-    diag.add_response("close", "_Close");
-
-    diag.present();
-}
-
 fn confirmation_text(seconds_left: u64) -> String {
     format!("Do you want to keep the new settings? (Reverting in {seconds_left} seconds)")
 }
diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs
index 00ee7b2a..68084723 100644
--- a/lact-gui/src/app/root_stack/mod.rs
+++ b/lact-gui/src/app/root_stack/mod.rs
@@ -21,26 +21,49 @@ pub struct RootStack {
 
 impl RootStack {
     pub fn new(
-        root_win: libadwaita::ApplicationWindow,
+        root_win: &impl IsA<Window>,
         system_info: SystemInfo,
         embedded_daemon: bool,
     ) -> Self {
-        let container = libadwaita::ViewStack::builder().vexpand(true).hexpand(true).build();
+        let container = libadwaita::ViewStack::builder()
+            .vexpand(true)
+            .hexpand(true)
+            .build();
 
         let info_page = InformationPage::new();
 
-        container.add_titled_with_icon(&info_page.container, Some("info_page"), "Information", "info-symbolic");
+        container.add_titled_with_icon(
+            &info_page.container,
+            Some("info_page"),
+            "Information",
+            "info-symbolic",
+        );
 
         let oc_page = OcPage::new(&system_info);
 
-        container.add_titled_with_icon(&oc_page.container, Some("oc_page"), "Overclock", "power-profile-performance-symbolic");
+        container.add_titled_with_icon(
+            &oc_page.container,
+            Some("oc_page"),
+            "Overclock",
+            "power-profile-performance-symbolic",
+        );
 
         let thermals_page = ThermalsPage::new(root_win);
 
-        container.add_titled_with_icon(&thermals_page.container, Some("thermals_page"), "Thermals", "temperature-symbolic");
+        container.add_titled_with_icon(
+            &thermals_page.container,
+            Some("thermals_page"),
+            "Thermals",
+            "temperature-symbolic",
+        );
 
         let software_page = software_page(system_info, embedded_daemon);
-        container.add_titled_with_icon(&software_page, Some("software_page"), "Software", "preferences-other-symbolic");
+        container.add_titled_with_icon(
+            &software_page,
+            Some("software_page"),
+            "Software",
+            "preferences-other-symbolic",
+        );
 
         Self {
             container,
diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs
index 5a9f4ce5..66198d9f 100644
--- a/lact-gui/src/app/root_stack/thermals_page/mod.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs
@@ -2,7 +2,7 @@ mod fan_curve_frame;
 
 use self::fan_curve_frame::FanCurveFrame;
 use super::{list_clamp, LabelRow};
-use crate::app::page_section::PageSection;
+use crate::{app::page_section::PageSection, info_dialog};
 use glib::clone;
 use gtk::prelude::*;
 use gtk::*;
@@ -29,7 +29,7 @@ pub struct ThermalsPage {
 }
 
 impl ThermalsPage {
-    pub fn new(root_win: libadwaita::ApplicationWindow) -> Self {
+    pub fn new(root_win: &impl IsA<Window>) -> Self {
         let vbox = Box::builder()
             .orientation(Orientation::Vertical)
             .spacing(12)
@@ -96,11 +96,13 @@ impl ThermalsPage {
 
         vbox.append(&fan_control_section);
 
-        fan_control_mode_stack.connect_visible_child_name_notify(move |stack| {
-            if stack.visible_child_name() == Some("automatic".into()) {
-                show_fan_control_warning(&root_win)
-            }
-        });
+        fan_control_mode_stack.connect_visible_child_name_notify(
+            clone!(@strong root_win => move |stack| {
+                if stack.visible_child_name() == Some("automatic".into()) {
+                    show_fan_control_warning(&root_win)
+                }
+            }),
+        );
 
         let container = ScrolledWindow::builder()
             .hscrollbar_policy(PolicyType::Never)
@@ -256,13 +258,15 @@ fn static_speed_adj(parent_box: &Box) -> Adjustment {
     adjustment
 }
 
-fn show_fan_control_warning(root_win: &libadwaita::ApplicationWindow) {
-    let diag = libadwaita::MessageDialog::builder()
-        .heading("Warning")
-        .body("Due to a driver bug, a reboot may be required for fan control to properly switch back to automatic")
-        .modal(true)
-        .transient_for(root_win)
-        .build();
-    diag.add_response("ok", "_Ok");
-    diag.present();
+fn show_fan_control_warning(root_win: &impl IsA<Window>) {
+    info_dialog!(
+        root_win,
+        "Warning",
+        concat!(
+            "Due to a driver bug, a reboot may be required for fan control ",
+            "to properly switch back to automatic"
+        ),
+        "ok",
+        "_Ok"
+    );
 }

From cf67c1e5d9ccb40c71f7e91e68751b8b2aa6bab3 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Fri, 1 Dec 2023 16:50:30 +0100
Subject: [PATCH 32/54] feat!: cargo feature to enable libadwaita; disabled by
 default

---
 Makefile                                      |   3 +
 lact-gui/Cargo.toml                           |   5 +-
 lact-gui/src/app/dialogs.rs                   |  26 ++-
 lact-gui/src/app/headerbar.rs                 |  12 ++
 lact-gui/src/app/mod.rs                       | 165 ++++++++++++++--
 .../vulkan_info/feature_window/mod.rs         |  40 +++-
 .../root_stack/info_page/vulkan_info/mod.rs   |  64 ++++---
 lact-gui/src/app/root_stack/mod.rs            | 178 ++++++++++++++----
 .../app/root_stack/oc_page/clocks_frame.rs    |  75 ++++++--
 lact-gui/src/app/root_stack/oc_page/mod.rs    |  26 +--
 .../root_stack/oc_page/performance_frame.rs   | 127 ++++++++++---
 .../root_stack/oc_page/power_cap_section.rs   |  23 +++
 .../src/app/root_stack/thermals_page/mod.rs   |  23 ++-
 lact-gui/src/lib.rs                           |   6 +
 lact-gui/ui/oc_page/power_cap_section_gtk.blp |  36 ++++
 lact-gui/ui/vulkan_feature_row.blp            |  36 ++--
 lact-gui/ui/vulkan_features_window.blp        |  56 +++---
 lact-gui/ui/vulkan_features_window_gtk.blp    |  45 +++++
 18 files changed, 761 insertions(+), 185 deletions(-)
 create mode 100644 lact-gui/ui/oc_page/power_cap_section_gtk.blp
 create mode 100644 lact-gui/ui/vulkan_features_window_gtk.blp

diff --git a/Makefile b/Makefile
index 78c2549b..504e30e2 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,9 @@ PREFIX ?= /usr/local
 build-release:
 	cargo build --release
 
+build-libadwaita-release:
+	cargo build --release --features libadwaita
+
 install:
 	install -Dm755 target/release/lact $(DESTDIR)$(PREFIX)/bin/lact
 	install -Dm644 res/lactd.service $(DESTDIR)$(PREFIX)/lib/systemd/system/lactd.service
diff --git a/lact-gui/Cargo.toml b/lact-gui/Cargo.toml
index 260a4268..efbeff5e 100644
--- a/lact-gui/Cargo.toml
+++ b/lact-gui/Cargo.toml
@@ -1,12 +1,13 @@
 [package]
 name = "lact-gui"
 version = "0.5.1"
-authors = ["Ilya Zlobintsev <ilya.zl@protonmail.com>"]
+authors = ["Ilya Zlobintsev <ilya.zl@protonmail.com>", "Gabriele Musco <gabmus@disroot.org>"]
 edition = "2021"
 
 [features]
 default = ["gtk-tests"]
 gtk-tests = []
+libadwaita = ["dep:libadwaita"]
 
 [dependencies]
 lact-client = { path = "../lact-client" }
@@ -15,7 +16,7 @@ gtk = { version = "0.7", package = "gtk4", features = ["v4_6", "blueprint"] }
 tracing = "0.1"
 tracing-subscriber = { version = "0.3", features = ["env-filter"] }
 anyhow = "1.0"
-libadwaita = { version = "0.5.3", features = ["v1_4"] }
+libadwaita = { version = "0.5.3", features = ["v1_4"], optional = true }
 
 [dev-dependencies]
 pretty_assertions = "1.4.0"
diff --git a/lact-gui/src/app/dialogs.rs b/lact-gui/src/app/dialogs.rs
index 2cad3e3d..af26c896 100644
--- a/lact-gui/src/app/dialogs.rs
+++ b/lact-gui/src/app/dialogs.rs
@@ -1,7 +1,10 @@
 use gtk::prelude::*;
-use libadwaita::prelude::MessageDialogExt;
 use tracing::warn;
 
+#[cfg(feature = "libadwaita")]
+use libadwaita::prelude::MessageDialogExt;
+
+#[cfg(feature = "libadwaita")]
 #[macro_export]
 macro_rules! info_dialog {
     ($parent:expr, $heading:expr, $body:expr, $response_id:expr, $response_txt:expr) => {{
@@ -20,6 +23,27 @@ macro_rules! info_dialog {
     }};
 }
 
+#[cfg(not(feature = "libadwaita"))]
+#[macro_export]
+macro_rules! info_dialog {
+    ($parent:expr, $heading:expr, $body:expr, $response_id:expr, $response_txt:expr) => {{
+        let diag = gtk::MessageDialog::builder()
+            .title($heading)
+            .text($body)
+            .modal(true)
+            .transient_for($parent)
+            .build();
+
+        diag.add_button($response_txt, gtk::ResponseType::Close);
+
+        diag.run_async(move |d, _| {
+            d.close();
+        });
+
+        diag
+    }};
+}
+
 pub fn show_error(parent: &impl IsA<gtk::Window>, err: anyhow::Error) {
     let text = format!("{err:?}");
     warn!("{}", text.trim());
diff --git a/lact-gui/src/app/headerbar.rs b/lact-gui/src/app/headerbar.rs
index f40bf774..de7cf279 100644
--- a/lact-gui/src/app/headerbar.rs
+++ b/lact-gui/src/app/headerbar.rs
@@ -2,14 +2,26 @@ use super::{apply_box::ApplyBox, gpu_selector::GpuSelector};
 
 #[derive(Debug, Clone)]
 pub struct Headerbar {
+    #[cfg(feature = "libadwaita")]
     pub container: libadwaita::HeaderBar,
+
+    #[cfg(not(feature = "libadwaita"))]
+    pub container: gtk::HeaderBar,
+
     pub gpu_selector: GpuSelector,
     pub apply_box: ApplyBox,
 }
 
 impl Headerbar {
     pub fn new() -> Self {
+        #[cfg(feature = "libadwaita")]
         let container = libadwaita::HeaderBar::builder().show_title(false).build();
+
+        #[cfg(not(feature = "libadwaita"))]
+        let container = gtk::HeaderBar::builder()
+            .title_widget(&gtk::Label::new(None))
+            .build();
+
         let gpu_selector = GpuSelector::new();
         let apply_box = ApplyBox::new();
 
diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index 5266982a..4fe4d84c 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -19,7 +19,6 @@ use lact_client::schema::request::{ConfirmCommand, SetClocksCommand};
 use lact_client::schema::DeviceStats;
 use lact_client::DaemonClient;
 use lact_daemon::MODULE_CONF_PATH;
-use libadwaita::prelude::{AdwApplicationWindowExt, MessageDialogExt};
 use root_stack::RootStack;
 use std::cell::RefCell;
 use std::rc::Rc;
@@ -27,13 +26,24 @@ use std::sync::atomic::AtomicBool;
 use std::time::Duration;
 use tracing::{debug, error, trace, warn};
 
+#[cfg(feature = "libadwaita")]
+use libadwaita::prelude::{AdwApplicationWindowExt, MessageDialogExt};
+
 // In ms
 const STATS_POLL_INTERVAL: u64 = 250;
 
 #[derive(Clone)]
 pub struct App {
+    #[cfg(feature = "libadwaita")]
     application: libadwaita::Application,
+    #[cfg(feature = "libadwaita")]
     pub window: libadwaita::ApplicationWindow,
+
+    #[cfg(not(feature = "libadwaita"))]
+    application: Application,
+    #[cfg(not(feature = "libadwaita"))]
+    pub window: ApplicationWindow,
+
     pub gpu_selector: GpuSelector,
     root_stack: RootStack,
     apply_box: ApplyBox,
@@ -42,8 +52,13 @@ pub struct App {
 
 impl App {
     pub fn new(daemon_client: DaemonClient) -> Self {
+        #[cfg(feature = "libadwaita")]
         let application = libadwaita::Application::new(Some(APP_ID), ApplicationFlags::default());
 
+        #[cfg(not(feature = "libadwaita"))]
+        let application = Application::new(Some(APP_ID), ApplicationFlags::default());
+
+        #[cfg(feature = "libadwaita")]
         let window = libadwaita::ApplicationWindow::builder()
             .title("LACT")
             .default_width(820)
@@ -53,6 +68,16 @@ impl App {
             .icon_name(APP_ID)
             .build();
 
+        #[cfg(not(feature = "libadwaita"))]
+        let window = ApplicationWindow::builder()
+            .title("LACT")
+            .default_width(820)
+            .default_height(750)
+            .width_request(420)
+            .height_request(200)
+            .icon_name(APP_ID)
+            .build();
+
         let system_info_buf = daemon_client
             .get_system_info()
             .expect("Could not fetch system info");
@@ -65,20 +90,38 @@ impl App {
 
         let root_stack = RootStack::new(&window, system_info, daemon_client.embedded);
 
-        let root_view = libadwaita::ToolbarView::new();
-
         let headerbar = Headerbar::new();
 
-        root_view.add_top_bar(&headerbar.container);
-        root_view.add_bottom_bar(
-            &libadwaita::ViewSwitcherBar::builder()
-                .reveal(true)
-                .stack(&root_stack.container)
-                .build(),
-        );
-        root_view.set_content(Some(&root_stack.container));
+        #[cfg(feature = "libadwaita")]
+        {
+            let root_view = libadwaita::ToolbarView::new();
+            root_view.add_top_bar(&headerbar.container);
+            root_view.add_bottom_bar(
+                &libadwaita::ViewSwitcherBar::builder()
+                    .reveal(true)
+                    .stack(&root_stack.container)
+                    .build(),
+            );
+            root_view.set_content(Some(&root_stack.container));
+            window.set_content(Some(&root_view));
+        }
 
-        window.set_content(Some(&root_view));
+        #[cfg(not(feature = "libadwaita"))]
+        {
+            let root_view = Box::builder().orientation(Orientation::Vertical).build();
+            root_view.append(&root_stack.container);
+            root_view.append(&Separator::new(Orientation::Horizontal));
+            root_view.append(
+                &StackSwitcher::builder()
+                    .stack(&root_stack.container)
+                    .halign(Align::Center)
+                    .vexpand(false)
+                    .hexpand(true)
+                    .build(),
+            );
+            window.set_titlebar(Some(&headerbar.container));
+            window.set_child(Some(&root_view));
+        }
 
         App {
             application,
@@ -201,7 +244,16 @@ impl App {
                             To enable the daemon, run the following command:"),
                         "close",
                         "_Close");
+
+                    #[cfg(feature = "libadwaita")]
                     diag.set_extra_child(Some(&hbox));
+
+                    #[cfg(not(feature = "libadwaita"))]
+                    {
+                        hbox.set_margin_start(12);
+                        hbox.set_margin_end(12);
+                        diag.first_child().unwrap().first_child().unwrap().downcast::<Box>().unwrap().append(&hbox);
+                    }
                 }
             }));
 
@@ -473,6 +525,7 @@ impl App {
         Ok(())
     }
 
+    #[cfg(feature = "libadwaita")]
     fn enable_overclocking(&self) {
         let text = format!("This will enable the overdrive feature of the amdgpu driver by creating a file at <b>{MODULE_CONF_PATH}</b>");
         let dialog = libadwaita::MessageDialog::builder()
@@ -517,6 +570,42 @@ impl App {
         dialog.present();
     }
 
+    #[cfg(not(feature = "libadwaita"))]
+    fn enable_overclocking(&self) {
+        let text = format!("This will enable the overdrive feature of the amdgpu driver by creating a file at <b>{MODULE_CONF_PATH}</b>. Are you sure you want to do this?");
+        let dialog = MessageDialog::builder()
+            .title("Enable Overclocking")
+            .use_markup(true)
+            .text(text)
+            .message_type(MessageType::Question)
+            .buttons(ButtonsType::OkCancel)
+            .transient_for(&self.window)
+            .build();
+
+        dialog.run_async(clone!(@strong self as app => move |diag, response| {
+            if response == ResponseType::Ok {
+                match app.daemon_client.enable_overdrive().and_then(|buffer| buffer.inner()) {
+                    Ok(_) => {
+                        let success_dialog = MessageDialog::builder()
+                            .title("Success")
+                            .text("Overclocking successfully enabled. A system reboot is required to apply the changes")
+                            .message_type(MessageType::Info)
+                            .buttons(ButtonsType::Ok)
+                            .build();
+                        success_dialog.run_async(move |diag, _| {
+                            diag.hide();
+                        });
+                    }
+                    Err(err) => {
+                        show_error(&app.window, err);
+                    }
+                }
+            }
+            diag.hide();
+        }));
+    }
+
+    #[cfg(feature = "libadwaita")]
     fn ask_confirmation(&self, gpu_id: String, mut delay: u64) {
         let text = confirmation_text(delay);
         let dialog = libadwaita::MessageDialog::builder()
@@ -578,6 +667,58 @@ impl App {
         );
         dialog.present();
     }
+
+    #[cfg(not(feature = "libadwaita"))]
+    fn ask_confirmation(&self, gpu_id: String, mut delay: u64) {
+        let text = confirmation_text(delay);
+        let dialog = MessageDialog::builder()
+            .title("Confirm settings")
+            .text(text)
+            .message_type(MessageType::Question)
+            .buttons(ButtonsType::YesNo)
+            .transient_for(&self.window)
+            .build();
+        let confirmed = Rc::new(AtomicBool::new(false));
+
+        glib::source::timeout_add_local(
+            Duration::from_secs(1),
+            clone!(@strong dialog, @strong self as app, @strong gpu_id, @strong confirmed => move || {
+                if confirmed.load(std::sync::atomic::Ordering::SeqCst) {
+                    return ControlFlow::Break;
+
+                }
+                delay -= 1;
+
+                let text = confirmation_text(delay);
+                dialog.set_text(Some(&text));
+
+                if delay == 0 {
+                    dialog.hide();
+                    app.set_initial(&gpu_id);
+
+                    ControlFlow::Break
+                }  else {
+                    ControlFlow::Continue
+                }
+            }),
+        );
+
+        dialog.run_async(clone!(@strong self as app => move |diag, response| {
+            confirmed.store(true, std::sync::atomic::Ordering::SeqCst);
+
+            let command = match response {
+                ResponseType::Yes => ConfirmCommand::Confirm,
+                _ => ConfirmCommand::Revert,
+            };
+
+            diag.hide();
+
+            if let Err(err) = app.daemon_client.confirm_pending_config(command) {
+                show_error(&app.window, err);
+            }
+            app.set_initial(&gpu_id);
+        }));
+    }
 }
 
 enum GuiUpdateMsg {
diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs
index 349cf70a..4046dbde 100644
--- a/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs
+++ b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs
@@ -4,12 +4,20 @@ mod row;
 use glib::Object;
 use gtk::{gio, glib};
 
+#[cfg(feature = "libadwaita")]
 glib::wrapper! {
     pub struct VulkanFeaturesWindow(ObjectSubclass<imp::VulkanFeaturesWindow>)
         @extends gtk::Box, gtk::Widget, gtk::Window, libadwaita::Window,
         @implements gtk::Orientable, gtk::Accessible, gtk::Buildable;
 }
 
+#[cfg(not(feature = "libadwaita"))]
+glib::wrapper! {
+    pub struct VulkanFeaturesWindow(ObjectSubclass<imp::VulkanFeaturesWindow>)
+        @extends gtk::Box, gtk::Widget, gtk::Window,
+        @implements gtk::Orientable, gtk::Accessible, gtk::Buildable;
+}
+
 impl VulkanFeaturesWindow {
     pub fn new(title: &str, model: gio::ListModel) -> Self {
         Object::builder()
@@ -33,9 +41,12 @@ mod imp {
         CompositeTemplate, Expression, FilterListModel, PropertyExpression, SearchEntry,
         SignalListItemFactory, StringFilter, TemplateChild,
     };
-    use libadwaita::subclass::window::AdwWindowImpl;
     use std::cell::RefCell;
 
+    #[cfg(feature = "libadwaita")]
+    use libadwaita::subclass::window::AdwWindowImpl;
+
+    #[cfg(feature = "libadwaita")]
     #[derive(CompositeTemplate, Properties, Default)]
     #[properties(wrapper_type = super::VulkanFeaturesWindow)]
     #[template(file = "ui/vulkan_features_window.blp")]
@@ -54,12 +65,36 @@ mod imp {
         search_entry: TemplateChild<SearchEntry>,
     }
 
+    #[cfg(not(feature = "libadwaita"))]
+    #[derive(CompositeTemplate, Properties, Default)]
+    #[properties(wrapper_type = super::VulkanFeaturesWindow)]
+    #[template(file = "ui/vulkan_features_window_gtk.blp")]
+    pub struct VulkanFeaturesWindow {
+        #[property(get, set)]
+        model: RefCell<Option<gio::ListModel>>,
+        #[template_child]
+        features_factory: TemplateChild<SignalListItemFactory>,
+
+        #[template_child]
+        filter_model: TemplateChild<FilterListModel>,
+
+        #[template_child]
+        search_filter: TemplateChild<StringFilter>,
+        #[template_child]
+        search_entry: TemplateChild<SearchEntry>,
+    }
+
     #[glib::object_subclass]
     impl ObjectSubclass for VulkanFeaturesWindow {
         const NAME: &'static str = "VulkanFeaturesWindow";
         type Type = super::VulkanFeaturesWindow;
+
+        #[cfg(feature = "libadwaita")]
         type ParentType = libadwaita::Window;
 
+        #[cfg(not(feature = "libadwaita"))]
+        type ParentType = gtk::Window;
+
         fn class_init(class: &mut Self::Class) {
             class.bind_template();
         }
@@ -117,6 +152,9 @@ mod imp {
 
     impl WidgetImpl for VulkanFeaturesWindow {}
     impl WindowImpl for VulkanFeaturesWindow {}
+
+    #[cfg(feature = "libadwaita")]
     impl AdwWindowImpl for VulkanFeaturesWindow {}
+
     impl ApplicationWindowImpl for VulkanFeaturesWindow {}
 }
diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
index eb25bc6e..b4915c34 100644
--- a/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
+++ b/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
@@ -2,14 +2,16 @@ mod feature_window;
 
 use self::feature_window::VulkanFeaturesWindow;
 use crate::app::root_stack::info_page::vulkan_info::feature_window::feature::VulkanFeature;
-use crate::app::root_stack::LabelRow;
+use crate::app::root_stack::{action_row, LabelRow};
 use glib::clone;
 use gtk::prelude::*;
 use gtk::*;
 use lact_client::schema::VulkanInfo;
-use libadwaita::prelude::ActionRowExt;
 use tracing::trace;
 
+#[cfg(feature = "libadwaita")]
+use libadwaita::prelude::ActionRowExt;
+
 #[derive(Debug, Clone)]
 pub struct VulkanInfoFrame {
     pub container: ListBox,
@@ -41,25 +43,45 @@ impl VulkanInfoFrame {
         container.append(&driver_name_row.container);
         container.append(&driver_version_row.container);
 
-        let features_row = libadwaita::ActionRow::builder()
-            .activatable(true)
-            .title("Features")
-            .build();
-        features_row.add_suffix(&Image::from_icon_name("go-next-symbolic"));
-        features_row.connect_activated(clone!(@strong features_model => move |_| {
-            show_features_window("Vulkan features", features_model.clone());
-        }));
-        container.append(&features_row);
-
-        let extensions_row = libadwaita::ActionRow::builder()
-            .activatable(true)
-            .title("Extensions")
-            .build();
-        extensions_row.add_suffix(&Image::from_icon_name("go-next-symbolic"));
-        extensions_row.connect_activated(clone!(@strong extensions_model => move |_| {
-            show_features_window("Vulkan extensions", extensions_model.clone());
-        }));
-        container.append(&extensions_row);
+        #[cfg(feature = "libadwaita")]
+        {
+            let features_row = libadwaita::ActionRow::builder()
+                .activatable(true)
+                .title("Features")
+                .build();
+            features_row.add_suffix(&Image::from_icon_name("go-next-symbolic"));
+            features_row.connect_activated(clone!(@strong features_model => move |_| {
+                show_features_window("Vulkan features", features_model.clone());
+            }));
+            container.append(&features_row);
+
+            let extensions_row = libadwaita::ActionRow::builder()
+                .activatable(true)
+                .title("Extensions")
+                .build();
+            extensions_row.add_suffix(&Image::from_icon_name("go-next-symbolic"));
+            extensions_row.connect_activated(clone!(@strong extensions_model => move |_| {
+                show_features_window("Vulkan extensions", extensions_model.clone());
+            }));
+            container.append(&extensions_row);
+        }
+
+        #[cfg(not(feature = "libadwaita"))]
+        {
+            let features_btn = Button::builder().label("View").build();
+            features_btn.connect_clicked(clone!(@strong features_model => move |_| {
+                show_features_window("Vulkan features", features_model.clone());
+            }));
+            let features_row = action_row("Features", None, &[&features_btn], None);
+            container.append(&features_row);
+
+            let extensions_btn = Button::builder().label("View").build();
+            extensions_btn.connect_clicked(clone!(@strong extensions_model => move |_| {
+                show_features_window("Vulkan extensions", extensions_model.clone());
+            }));
+            let extensions_row = action_row("Extensions", None, &[&extensions_btn], None);
+            container.append(&extensions_row);
+        }
 
         Self {
             container,
diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs
index 68084723..54462e67 100644
--- a/lact-gui/src/app/root_stack/mod.rs
+++ b/lact-gui/src/app/root_stack/mod.rs
@@ -7,13 +7,21 @@ use self::software_page::software_page;
 use gtk::{prelude::IsA, *};
 use info_page::InformationPage;
 use lact_client::schema::SystemInfo;
-use libadwaita::prelude::ActionRowExt;
 use oc_page::OcPage;
 use thermals_page::ThermalsPage;
+use traits::BoxExt;
+use traits::WidgetExt;
+
+#[cfg(feature = "libadwaita")]
+use libadwaita::prelude::ActionRowExt;
 
 #[derive(Debug, Clone)]
 pub struct RootStack {
+    #[cfg(feature = "libadwaita")]
     pub container: libadwaita::ViewStack,
+    #[cfg(not(feature = "libadwaita"))]
+    pub container: Stack,
+
     pub info_page: InformationPage,
     pub thermals_page: ThermalsPage,
     pub oc_page: OcPage,
@@ -25,45 +33,54 @@ impl RootStack {
         system_info: SystemInfo,
         embedded_daemon: bool,
     ) -> Self {
+        #[cfg(feature = "libadwaita")]
         let container = libadwaita::ViewStack::builder()
             .vexpand(true)
             .hexpand(true)
             .build();
+        #[cfg(not(feature = "libadwaita"))]
+        let container = Stack::builder().vexpand(true).hexpand(true).build();
 
         let info_page = InformationPage::new();
-
-        container.add_titled_with_icon(
-            &info_page.container,
-            Some("info_page"),
-            "Information",
-            "info-symbolic",
-        );
-
         let oc_page = OcPage::new(&system_info);
-
-        container.add_titled_with_icon(
-            &oc_page.container,
-            Some("oc_page"),
-            "Overclock",
-            "power-profile-performance-symbolic",
-        );
-
         let thermals_page = ThermalsPage::new(root_win);
+        let software_page = software_page(system_info, embedded_daemon);
 
-        container.add_titled_with_icon(
-            &thermals_page.container,
-            Some("thermals_page"),
-            "Thermals",
-            "temperature-symbolic",
-        );
+        #[cfg(feature = "libadwaita")]
+        {
+            container.add_titled_with_icon(
+                &info_page.container,
+                Some("info_page"),
+                "Information",
+                "info-symbolic",
+            );
+            container.add_titled_with_icon(
+                &oc_page.container,
+                Some("oc_page"),
+                "Overclock",
+                "power-profile-performance-symbolic",
+            );
+            container.add_titled_with_icon(
+                &thermals_page.container,
+                Some("thermals_page"),
+                "Thermals",
+                "temperature-symbolic",
+            );
+            container.add_titled_with_icon(
+                &software_page,
+                Some("software_page"),
+                "Software",
+                "preferences-other-symbolic",
+            );
+        }
 
-        let software_page = software_page(system_info, embedded_daemon);
-        container.add_titled_with_icon(
-            &software_page,
-            Some("software_page"),
-            "Software",
-            "preferences-other-symbolic",
-        );
+        #[cfg(not(feature = "libadwaita"))]
+        {
+            container.add_titled(&info_page.container, Some("info_page"), "Information");
+            container.add_titled(&oc_page.container, Some("oc_page"), "Overclock");
+            container.add_titled(&thermals_page.container, Some("thermals_page"), "Thermals");
+            container.add_titled(&software_page, Some("software_page"), "Software");
+        }
 
         Self {
             container,
@@ -76,13 +93,17 @@ impl RootStack {
 
 #[derive(Debug, Clone)]
 pub struct LabelRow {
+    #[cfg(feature = "libadwaita")]
     pub container: libadwaita::ActionRow,
+
+    #[cfg(not(feature = "libadwaita"))]
+    pub container: ListBoxRow,
+
     content_label: Label,
 }
 
 impl LabelRow {
     pub fn new(title: &str) -> Self {
-        let container = libadwaita::ActionRow::builder().title(title).build();
         let label = Label::builder()
             .css_classes(["dim-label"])
             .ellipsize(pango::EllipsizeMode::End)
@@ -90,7 +111,8 @@ impl LabelRow {
             .justify(Justification::Right)
             .selectable(true)
             .build();
-        container.add_suffix(&label);
+
+        let container = action_row(title, None, &[&label], None);
 
         Self {
             container,
@@ -109,6 +131,7 @@ impl LabelRow {
     }
 }
 
+#[cfg(feature = "libadwaita")]
 pub fn list_clamp(child: &impl IsA<Widget>) -> libadwaita::Clamp {
     libadwaita::Clamp::builder()
         .maximum_size(600)
@@ -120,3 +143,94 @@ pub fn list_clamp(child: &impl IsA<Widget>) -> libadwaita::Clamp {
         .valign(Align::Start)
         .build()
 }
+
+#[cfg(not(feature = "libadwaita"))]
+pub fn list_clamp(child: &impl IsA<Widget>) -> Box {
+    let container = Box::builder()
+        .margin_top(24)
+        .margin_bottom(24)
+        .margin_start(6)
+        .margin_end(6)
+        .orientation(Orientation::Vertical)
+        .valign(Align::Start)
+        .build();
+    container.append(child);
+
+    container
+}
+
+#[cfg(feature = "libadwaita")]
+pub fn action_row(
+    title: &str,
+    subtitle: Option<&str>,
+    suffixes: &[&impl IsA<Widget>],
+    css_classes: Option<&[&str]>,
+) -> libadwaita::ActionRow {
+    let ar = libadwaita::ActionRow::builder()
+        .subtitle_lines(0)
+        .title(title)
+        .build();
+
+    if let Some(css) = css_classes {
+        css.iter().for_each(|cls| ar.add_css_class(cls));
+    }
+
+    if let Some(sub) = subtitle {
+        ar.set_subtitle(sub);
+    }
+    suffixes.iter().for_each(|suf| {
+        ar.add_suffix(*suf);
+    });
+    ar
+}
+
+#[cfg(not(feature = "libadwaita"))]
+pub fn action_row(
+    title: &str,
+    subtitle: Option<&str>,
+    suffixes: &[&impl IsA<Widget>],
+    css_classes: Option<&[&str]>,
+) -> ListBoxRow {
+    let inner = Box::builder()
+        .orientation(Orientation::Horizontal)
+        .margin_top(12)
+        .margin_bottom(12)
+        .margin_start(12)
+        .margin_end(12)
+        .build();
+
+    if let Some(css) = css_classes {
+        css.iter().for_each(|cls| inner.add_css_class(cls));
+    }
+
+    let title_label = Label::builder()
+        .label(title)
+        .css_classes(["heading"])
+        .hexpand(true)
+        .xalign(0.0)
+        .wrap(true)
+        .wrap_mode(pango::WrapMode::Word)
+        .build();
+    if let Some(sub) = subtitle {
+        let vert = Box::new(Orientation::Vertical, 6);
+        vert.append(&title_label);
+        vert.append(
+            &Label::builder()
+                .label(sub)
+                .hexpand(true)
+                .xalign(0.0)
+                .wrap(true)
+                .wrap_mode(pango::WrapMode::Word)
+                .build(),
+        );
+        inner.append(&vert);
+    } else {
+        inner.append(&title_label);
+    }
+
+    suffixes.iter().for_each(|suf| {
+        inner.append(*suf);
+    });
+
+    ListBoxRow::builder().child(&inner).build()
+}
diff --git a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
index 216b34d4..dbe7dce5 100644
--- a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
@@ -1,14 +1,17 @@
 use crate::app::page_section::PageSection;
+use crate::app::root_stack::action_row;
 use glib::clone;
 use gtk::prelude::*;
 use gtk::*;
 use lact_client::schema::amdgpu_sysfs;
 use lact_client::schema::amdgpu_sysfs::gpu_handle::overdrive::{ClocksTable, ClocksTableGen};
-use libadwaita::prelude::ActionRowExt;
 use std::rc::Rc;
 use std::sync::atomic::{AtomicBool, Ordering};
 use tracing::debug;
 
+#[cfg(feature = "libadwaita")]
+use libadwaita::prelude::ActionRowExt;
+
 const VOLTAGE_OFFSET_RANGE: f64 = 250.0;
 
 // The AtomicBool stores if the value was changed
@@ -17,7 +20,13 @@ pub struct ClocksFrame {
     pub container: PageSection,
     max_values_box: Box,
     heading_listbox: ListBox,
+
+    #[cfg(feature = "libadwaita")]
     advanced_switch_row: libadwaita::SwitchRow,
+
+    #[cfg(not(feature = "libadwaita"))]
+    advanced_switch_row: Switch,
+
     min_values_box: Box,
     min_sclk_adjustment: (Adjustment, Rc<AtomicBool>),
     min_mclk_adjustment: (Adjustment, Rc<AtomicBool>),
@@ -39,18 +48,32 @@ impl ClocksFrame {
             .selection_mode(SelectionMode::None)
             .build();
 
-        heading_listbox.append(&libadwaita::ActionRow::builder()
-            .css_classes(["warning"])
-            .title("Warning!")
-            .subtitle("Changing these values may lead to system instability and potentially damage your hardware!")
-            .subtitle_lines(0)
-            .build());
+        let warning_row = action_row(
+            "Warning!",
+            Some("Changing these values may lead to system instability and potentially damage your hardware!"),
+            &Vec::<&Widget>::new(),
+            Some(&vec!["warning"]));
+
+        heading_listbox.append(&warning_row);
+
+        #[cfg(feature = "libadwaita")]
+        let advanced_switch_row = {
+            let row = libadwaita::SwitchRow::builder()
+                .title("Advanced mode")
+                .active(false)
+                .build();
+            heading_listbox.append(&row);
+            row
+        };
+
+        #[cfg(not(feature = "libadwaita"))]
+        let advanced_switch_row = {
+            let switch = Switch::builder().active(false).build();
+            let row = action_row("Advanced mode", None, &[&switch], None);
 
-        let advanced_switch_row = libadwaita::SwitchRow::builder()
-            .title("Advanced mode")
-            .active(false)
-            .build();
-        heading_listbox.append(&advanced_switch_row);
+            heading_listbox.append(&row);
+            switch
+        };
 
         container.append(&heading_listbox);
 
@@ -113,14 +136,14 @@ impl ClocksFrame {
             .halign(Align::Center)
             .css_classes(["destructive-action", "circular"])
             .build();
-        let reset_row = libadwaita::ActionRow::builder()
-            .title("Reset values")
-            .subtitle(
-                "Warning: this will reset all clock and voltage settings to their default values",
-            )
-            .subtitle_lines(0)
-            .build();
-        reset_row.add_suffix(&reset_button);
+
+        let reset_row = action_row(
+            "Reset values",
+            Some("Warning: this will reset all clock and voltage settings to their default values"),
+            &[&reset_button],
+            None,
+        );
+
         max_values_listbox.append(&reset_row);
 
         let clocks_data_unavailable_label = Label::builder()
@@ -370,11 +393,23 @@ fn extract_value_and_range(
 fn oc_adjustment(title: &'static str, listbox: &ListBox) -> (Adjustment, Rc<AtomicBool>) {
     let adjustment = Adjustment::new(0.0, 0.0, 0.0, 1.0, 10.0, 0.0);
 
+    #[cfg(feature = "libadwaita")]
     let value_selector = libadwaita::SpinRow::builder()
         .title(title)
         .adjustment(&adjustment)
         .build();
 
+    #[cfg(not(feature = "libadwaita"))]
+    let value_selector = {
+        let spin_btn = SpinButton::builder()
+            .adjustment(&adjustment)
+            .valign(Align::Center)
+            .build();
+        let row = action_row(title, None, &[&spin_btn], None);
+        row.set_child(Some(&spin_btn));
+        spin_btn
+    };
+
     let changed = Rc::new(AtomicBool::new(false));
 
     adjustment.connect_value_changed(clone!(@strong changed => move |_| {
diff --git a/lact-gui/src/app/root_stack/oc_page/mod.rs b/lact-gui/src/app/root_stack/oc_page/mod.rs
index 3d086b4e..d914cf87 100644
--- a/lact-gui/src/app/root_stack/oc_page/mod.rs
+++ b/lact-gui/src/app/root_stack/oc_page/mod.rs
@@ -8,6 +8,7 @@ mod power_states;
 
 use self::power_cap_section::PowerCapSection;
 use self::power_states::power_states_frame::PowerStatesFrame;
+use super::list_clamp;
 use clocks_frame::ClocksFrame;
 use gpu_stats_section::GpuStatsSection;
 use gtk::*;
@@ -18,8 +19,6 @@ use lact_client::schema::{
     DeviceStats, SystemInfo,
 };
 use performance_frame::PerformanceFrame;
-// use power_cap_frame::PowerCapFrame;
-use super::list_clamp;
 use std::collections::HashMap;
 use tracing::warn;
 
@@ -28,7 +27,6 @@ pub struct OcPage {
     pub container: ScrolledWindow,
     stats_section: GpuStatsSection,
     pub performance_frame: PerformanceFrame,
-    // power_cap_frame: PowerCapFrame,
     power_cap_section: PowerCapSection,
     pub power_states_frame: PowerStatesFrame,
     pub clocks_frame: ClocksFrame,
@@ -51,7 +49,17 @@ impl OcPage {
         if system_info.amdgpu_overdrive_enabled == Some(false) {
             let (warning_frame, button) = oc_warning_frame();
             enable_overclocking_button = Some(button);
-            vbox.append(&warning_frame);
+
+            #[cfg(feature = "libadwaita")]
+            vbox.append(
+                &libadwaita::Bin::builder()
+                    .css_classes(["card"])
+                    .child(&warning_frame)
+                    .build(),
+            );
+
+            #[cfg(not(feature = "libadwaita"))]
+            vbox.append(&Frame::builder().child(&warning_frame).build());
         }
 
         let stats_section = GpuStatsSection::new();
@@ -166,7 +174,7 @@ impl OcPage {
     }
 }
 
-fn oc_warning_frame() -> (libadwaita::Bin, Button) {
+fn oc_warning_frame() -> (Box, Button) {
     let vbox = Box::builder()
         .orientation(Orientation::Vertical)
         .spacing(6)
@@ -218,11 +226,5 @@ fn oc_warning_frame() -> (libadwaita::Bin, Button) {
 
     vbox.append(&enable_button);
 
-    (
-        libadwaita::Bin::builder()
-            .css_classes(["card"])
-            .child(&vbox)
-            .build(),
-        enable_button,
-    )
+    (vbox, enable_button)
 }
diff --git a/lact-gui/src/app/root_stack/oc_page/performance_frame.rs b/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
index 52187ba8..5b5d25e8 100644
--- a/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
@@ -1,18 +1,31 @@
-use crate::app::page_section::PageSection;
+use crate::app::{page_section::PageSection, root_stack::action_row};
 use glib::clone;
 use gtk::prelude::*;
 use gtk::*;
 use lact_client::schema::amdgpu_sysfs::gpu_handle::{
     power_profile_mode::PowerProfileModesTable, PerformanceLevel,
 };
-use libadwaita::prelude::{ActionRowExt, ComboRowExt};
 use std::{cell::RefCell, rc::Rc, str::FromStr};
 
+#[cfg(feature = "libadwaita")]
+use libadwaita::prelude::{ActionRowExt, ComboRowExt};
+
 #[derive(Debug, Clone)]
 pub struct PerformanceFrame {
     pub container: PageSection,
+
+    #[cfg(feature = "libadwaita")]
     level_row: libadwaita::ComboRow,
+    #[cfg(feature = "libadwaita")]
     mode_row: libadwaita::ComboRow,
+
+    #[cfg(not(feature = "libadwaita"))]
+    level_row: DropDown,
+    #[cfg(not(feature = "libadwaita"))]
+    level_subtitle: Label,
+    #[cfg(not(feature = "libadwaita"))]
+    mode_row: DropDown,
+
     modes_table: Rc<RefCell<Option<PowerProfileModesTable>>>,
 }
 
@@ -29,33 +42,83 @@ impl PerformanceFrame {
             .into_iter()
             .collect();
 
-        let level_row = libadwaita::ComboRow::builder()
-            .model(&levels_model)
-            .title("Performance level")
-            .subtitle("")
-            .subtitle_lines(0)
-            .sensitive(false)
-            .build();
+        #[cfg(feature = "libadwaita")]
+        let level_row = {
+            let row = libadwaita::ComboRow::builder()
+                .model(&levels_model)
+                .title("Performance level")
+                .subtitle("")
+                .subtitle_lines(0)
+                .sensitive(false)
+                .build();
+            listbox.append(&row);
+            row
+        };
 
-        listbox.append(&level_row);
+        #[cfg(not(feature = "libadwaita"))]
+        let level_subtitle;
+        #[cfg(not(feature = "libadwaita"))]
+        let level_row = {
+            let dropdown = DropDown::builder()
+                .model(&levels_model)
+                .sensitive(false)
+                .valign(Align::Center)
+                .build();
+            let row = action_row("Performance level", Some(""), &[&dropdown], None);
+            level_subtitle = row
+                .first_child()
+                .unwrap()
+                .first_child()
+                .unwrap()
+                .first_child()
+                .unwrap()
+                .next_sibling()
+                .unwrap()
+                .downcast::<Label>()
+                .unwrap();
+            listbox.append(&row);
+            dropdown
+        };
 
         let filler_model: StringList = [""].into_iter().collect();
 
-        let mode_row = libadwaita::ComboRow::builder()
-            .model(&filler_model)
-            .title("Power level mode")
-            .subtitle("Set \"Performance level\" to \"Manual\" to use power states and modes")
-            .subtitle_lines(0)
-            .sensitive(false)
-            .build();
+        #[cfg(feature = "libadwaita")]
+        let mode_row = {
+            let row = libadwaita::ComboRow::builder()
+                .model(&filler_model)
+                .title("Power level mode")
+                .subtitle("Set \"Performance level\" to \"Manual\" to use power states and modes")
+                .subtitle_lines(0)
+                .sensitive(false)
+                .build();
+            listbox.append(&row);
+            row
+        };
 
-        listbox.append(&mode_row);
+        #[cfg(not(feature = "libadwaita"))]
+        let mode_row = {
+            let dropdown = DropDown::builder()
+                .model(&filler_model)
+                .sensitive(false)
+                .valign(Align::Center)
+                .build();
+            let row = action_row(
+                "Power level mode",
+                Some("Set \"Performance level\" to \"Manual\" to use power states and modes"),
+                &[&dropdown],
+                None,
+            );
+            listbox.append(&row);
+            dropdown
+        };
 
         container.append(&listbox);
 
         let frame = Self {
             container,
             level_row,
+            #[cfg(not(feature = "libadwaita"))]
+            level_subtitle,
             mode_row,
             modes_table: Rc::new(RefCell::new(None)),
         };
@@ -145,17 +208,23 @@ impl PerformanceFrame {
     fn update_from_selection(&self) {
         let mut enable_mode_control = false;
 
-        self.level_row
-            .set_subtitle(match self.level_row.selected() {
-                0 => "Automatically adjust GPU and VRAM clocks. (Default)",
-                1 => "Always use the highest clockspeeds for GPU and VRAM.",
-                2 => "Always use the lowest clockspeeds for GPU and VRAM.",
-                3 => {
-                    enable_mode_control = true;
-                    "Manual performance control."
-                }
-                _ => unreachable!(),
-            });
+        let subtitle = match self.level_row.selected() {
+            0 => "Automatically adjust GPU and VRAM clocks. (Default)",
+            1 => "Always use the highest clockspeeds for GPU and VRAM.",
+            2 => "Always use the lowest clockspeeds for GPU and VRAM.",
+            3 => {
+                enable_mode_control = true;
+                "Manual performance control."
+            }
+            _ => unreachable!(),
+        };
+
+        #[cfg(feature = "libadwaita")]
+        self.level_row.set_subtitle(subtitle);
+
+        #[cfg(not(feature = "libadwaita"))]
+        self.level_subtitle.set_text(subtitle);
+
         self.mode_row.set_sensitive(enable_mode_control);
     }
 
diff --git a/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs b/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs
index 1ad30a40..99937681 100644
--- a/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs
+++ b/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs
@@ -41,6 +41,7 @@ mod imp {
     };
     use std::cell::RefCell;
 
+    #[cfg(feature = "libadwaita")]
     #[derive(CompositeTemplate, Default, Properties)]
     #[properties(wrapper_type = super::PowerCapSection)]
     #[template(file = "ui/oc_page/power_cap_section.blp")]
@@ -62,6 +63,28 @@ mod imp {
         pub reset_button: TemplateChild<Button>,
     }
 
+    #[cfg(not(feature = "libadwaita"))]
+    #[derive(CompositeTemplate, Default, Properties)]
+    #[properties(wrapper_type = super::PowerCapSection)]
+    #[template(file = "ui/oc_page/power_cap_section_gtk.blp")]
+    pub struct PowerCapSection {
+        #[property(get, set)]
+        pub current_value: RefCell<f64>,
+        #[property(get, set)]
+        pub max_value: RefCell<f64>,
+        #[property(get, set)]
+        pub min_value: RefCell<f64>,
+        #[property(get, set)]
+        pub default_value: RefCell<f64>,
+        #[property(get, set)]
+        pub value_text: RefCell<String>,
+
+        #[template_child]
+        pub adjustment: TemplateChild<OcAdjustment>,
+        #[template_child]
+        pub reset_button: TemplateChild<Button>,
+    }
+
     #[glib::object_subclass]
     impl ObjectSubclass for PowerCapSection {
         const NAME: &'static str = "PowerCapSection";
diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs
index 66198d9f..6d182bf3 100644
--- a/lact-gui/src/app/root_stack/thermals_page/mod.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs
@@ -7,6 +7,8 @@ use glib::clone;
 use gtk::prelude::*;
 use gtk::*;
 use lact_client::schema::{default_fan_curve, DeviceStats, FanControlMode, FanCurveMap};
+
+#[cfg(feature = "libadwaita")]
 use libadwaita::prelude::*;
 
 #[derive(Debug)]
@@ -81,15 +83,18 @@ impl ThermalsPage {
 
         fan_control_mode_stack.add_titled(&fan_curve_frame.container, Some("curve"), "Curve");
 
-        fan_control_mode_stack.add_titled(
-            &libadwaita::Bin::builder()
-                .css_classes(["card"])
-                .valign(Align::Start)
-                .child(&fan_static_speed_frame)
-                .build(),
-            Some("static"),
-            "Static",
-        );
+        #[cfg(feature = "libadwaita")]
+        let static_speed_container = libadwaita::Bin::builder()
+            .css_classes(["card"])
+            .valign(Align::Start)
+            .child(&fan_static_speed_frame)
+            .build();
+        #[cfg(not(feature = "libadwaita"))]
+        let static_speed_container = Frame::builder()
+            .valign(Align::Start)
+            .child(&fan_static_speed_frame)
+            .build();
+        fan_control_mode_stack.add_titled(&static_speed_container, Some("static"), "Static");
 
         fan_control_section.append(&fan_control_mode_stack_switcher);
         fan_control_section.append(&fan_control_mode_stack);
diff --git a/lact-gui/src/lib.rs b/lact-gui/src/lib.rs
index 33d145e1..959c6a3e 100644
--- a/lact-gui/src/lib.rs
+++ b/lact-gui/src/lib.rs
@@ -17,7 +17,13 @@ pub fn run(args: GuiArgs) -> anyhow::Result<()> {
         .context("Invalid log level")?;
     tracing_subscriber::fmt().with_env_filter(env_filter).init();
 
+    #[cfg(feature = "libadwaita")]
     if let Err(err) = libadwaita::init() {
+        return Err(anyhow!("Cannot initialize Libadwaita: {err}"));
+    }
+
+    #[cfg(not(feature = "libadwaita"))]
+    if let Err(err) = gtk::init() {
         return Err(anyhow!("Cannot initialize GTK: {err}"));
     }
 
diff --git a/lact-gui/ui/oc_page/power_cap_section_gtk.blp b/lact-gui/ui/oc_page/power_cap_section_gtk.blp
new file mode 100644
index 00000000..8d86b01b
--- /dev/null
+++ b/lact-gui/ui/oc_page/power_cap_section_gtk.blp
@@ -0,0 +1,36 @@
+using Gtk 4.0;
+
+template $PowerCapSection: $PageSection {
+    name: "Power usage limit";
+
+    Box {
+        orientation: horizontal;
+        margin-top: 12;
+        margin-bottom: 12;
+        spacing: 6;
+        
+        Label value_label {
+            label: bind template.value-text;
+        }
+
+        Scale {
+            orientation: horizontal;
+            hexpand: true;
+            round-digits: 0;
+            draw-value: false;
+            adjustment: adjustment;
+        }
+        
+        Button reset_button {
+            styles ["circular", "flat"]
+            icon-name: "view-refresh-symbolic";
+            tooltip-text: "Reset to default";
+        }
+    }
+}
+
+$OcAdjustment adjustment {
+    value: bind template.current-value bidirectional;
+    lower: bind template.min-value;
+    upper: bind template.max-value;
+}
diff --git a/lact-gui/ui/vulkan_feature_row.blp b/lact-gui/ui/vulkan_feature_row.blp
index b368da8b..0913ac67 100644
--- a/lact-gui/ui/vulkan_feature_row.blp
+++ b/lact-gui/ui/vulkan_feature_row.blp
@@ -1,24 +1,24 @@
 using Gtk 4.0;
 
 template $VulkanFeatureRow: Box {
-  focus-on-click: false;
-  hexpand: true;
-  hexpand-set: true;
-  margin-bottom: 12;
-  margin-end: 24;
-  margin-start: 24;
-  margin-top: 12;
-
-  Label name_label {
-    xalign: 0.0;
-    ellipsize: end;
+    focus-on-click: false;
     hexpand: true;
-    label: 'feature name';
-    selectable: true;
-  }
+    hexpand-set: true;
+    margin-bottom: 12;
+    margin-end: 24;
+    margin-start: 24;
+    margin-top: 12;
+
+    Label name_label {
+        xalign: 0.0;
+        ellipsize: end;
+        hexpand: true;
+        label: 'feature name';
+        selectable: true;
+    }
 
-  Image available_image {
-    halign: end;
-    icon-name: 'action-unavailable-symbolic';
-  }
+    Image available_image {
+        halign: end;
+        icon-name: 'action-unavailable-symbolic';
+    }
 }
diff --git a/lact-gui/ui/vulkan_features_window.blp b/lact-gui/ui/vulkan_features_window.blp
index 49ce435a..27297584 100644
--- a/lact-gui/ui/vulkan_features_window.blp
+++ b/lact-gui/ui/vulkan_features_window.blp
@@ -2,44 +2,44 @@ using Gtk 4.0;
 using Adw 1;
 
 template $VulkanFeaturesWindow: Adw.Window {
-  default-height: 700;
-  default-width: 500;
+    default-height: 700;
+    default-width: 500;
 
-  Adw.ToolbarView {
-    [top] Adw.HeaderBar {}
-    [top] SearchEntry search_entry {
-      margin-top: 6;
-      margin-bottom: 6;
-      margin-start: 12;
-      margin-end: 12;
-    }
-
-    content: Box {
-      orientation: vertical;
-  
-      ScrolledWindow {
-        vexpand: true;
-        hscrollbar-policy: never;
-  
-        ListView {
-          factory: features_factory;
-          model: selection_model;
-          show-separators: true;
+    Adw.ToolbarView {
+        [top] Adw.HeaderBar {}
+        [top] SearchEntry search_entry {
+            margin-top: 6;
+            margin-bottom: 6;
+            margin-start: 12;
+            margin-end: 12;
         }
-      }
-    };
-  }
+
+        content: Box {
+            orientation: vertical;
+    
+            ScrolledWindow {
+                vexpand: true;
+                hscrollbar-policy: never;
+    
+                ListView {
+                    factory: features_factory;
+                    model: selection_model;
+                    show-separators: true;
+                }
+            }
+        };
+    }
 }
 
 SignalListItemFactory features_factory {}
 
 NoSelection selection_model {
-  model: filter_model;
+    model: filter_model;
 }
 
 StringFilter search_filter {}
 
 FilterListModel filter_model {
-  filter: search_filter;
-  incremental: true;
+    filter: search_filter;
+    incremental: true;
 }
diff --git a/lact-gui/ui/vulkan_features_window_gtk.blp b/lact-gui/ui/vulkan_features_window_gtk.blp
new file mode 100644
index 00000000..3da9435f
--- /dev/null
+++ b/lact-gui/ui/vulkan_features_window_gtk.blp
@@ -0,0 +1,45 @@
+using Gtk 4.0;
+
+template $VulkanFeaturesWindow: Window {
+    default-height: 700;
+    default-width: 500;
+
+    Box {
+        orientation: vertical;
+
+        [top] SearchEntry search_entry {
+            margin-top: 6;
+            margin-bottom: 6;
+            margin-start: 12;
+            margin-end: 12;
+        }
+
+        Box {
+            orientation: vertical;
+    
+            ScrolledWindow {
+                vexpand: true;
+                hscrollbar-policy: never;
+    
+                ListView {
+                    factory: features_factory;
+                    model: selection_model;
+                    show-separators: true;
+                }
+            }
+        }
+    }
+}
+
+SignalListItemFactory features_factory {}
+
+NoSelection selection_model {
+    model: filter_model;
+}
+
+StringFilter search_filter {}
+
+FilterListModel filter_model {
+    filter: search_filter;
+    incremental: true;
+}

From 220b2672824d829ab7be2f483c7db76d060dded9 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Fri, 1 Dec 2023 20:14:20 +0100
Subject: [PATCH 33/54] feat: recipe for libadwaita packages on distros that
 support it

---
 pkg/recipes/lact-libadwaita/recipe.yml | 33 ++++++++++++++++++++++++++
 pkg/recipes/lact/recipe.yml            |  6 ++---
 2 files changed, 36 insertions(+), 3 deletions(-)
 create mode 100644 pkg/recipes/lact-libadwaita/recipe.yml

diff --git a/pkg/recipes/lact-libadwaita/recipe.yml b/pkg/recipes/lact-libadwaita/recipe.yml
new file mode 100644
index 00000000..d272f60a
--- /dev/null
+++ b/pkg/recipes/lact-libadwaita/recipe.yml
@@ -0,0 +1,33 @@
+metadata:
+  name: lact-libadwaita
+  description: AMDGPU control utility (libadwaita GUI)
+  arch: x86_64
+  license: MIT
+  version: 0.5.1
+  maintainer: ilya-zlobintsev
+  url: https://github.com/ilya-zlobintsev/lact
+  source:
+    - '../../..'
+  provides: [ lact ]
+  depends:
+    all: [ hwdata ]
+    fedora-39: [ gtk4, libdrm, libadwaita ]
+    arch: [ gtk4, libadwaita ]
+  build_depends:
+    all: [ curl, make, dbus, clang ]
+    fedora-39: [ gtk4-devel, gcc, libdrm-devel, blueprint-compiler, libadwaita-devel ]
+    arch: [ gtk4, blueprint-compiler, libadwaita ]
+  all_images: true
+env:
+  RUSTUP_URL: https://sh.rustup.rs
+configure:
+  steps:
+    - cmd: curl -o /tmp/install_rust.sh $RUSTUP_URL
+    - cmd: sh /tmp/install_rust.sh -y --default-toolchain stable
+build:
+  steps:
+    - cmd: bash -c "source $HOME/.cargo/env && make build-libadwaita-release"
+install:
+  steps:
+    - cmd: bash -c "cd $PKGER_BLD_DIR && DESTDIR=$PKGER_OUT_DIR PREFIX=/usr make install"
+
diff --git a/pkg/recipes/lact/recipe.yml b/pkg/recipes/lact/recipe.yml
index 439464a6..a5f1d433 100644
--- a/pkg/recipes/lact/recipe.yml
+++ b/pkg/recipes/lact/recipe.yml
@@ -16,9 +16,9 @@ metadata:
     arch: [ gtk4 ]
   build_depends:
     all: [ curl, make, dbus, clang ]
-    debian-12+ubuntu-2204: [ libgtk-4-dev, pkg-config, build-essential, libdrm-dev, libadwaita-1-dev ]
-    fedora-38+fedora-39: [ gtk4-devel, gcc, libdrm-devel, blueprint-compiler, libadwaita-devel ]
-    arch: [ gtk4, blueprint-compiler, libadwaita ]
+    debian-12+ubuntu-2204: [ libgtk-4-dev, pkg-config, build-essential, libdrm-dev ]
+    fedora-38+fedora-39: [ gtk4-devel, gcc, libdrm-devel, blueprint-compiler ]
+    arch: [ gtk4, blueprint-compiler ]
   all_images: true
 env:
   RUSTUP_URL: https://sh.rustup.rs

From c7e3a94a84e52ffa6d31a8e46f9bd40d765dceb4 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 2 Dec 2023 01:10:52 +0100
Subject: [PATCH 34/54] chore: cfg_attr for power cap section instead of
 duplicating entire struct definition

---
 .../root_stack/oc_page/power_cap_section.rs   | 26 ++-----------------
 1 file changed, 2 insertions(+), 24 deletions(-)

diff --git a/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs b/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs
index 99937681..c6d3db63 100644
--- a/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs
+++ b/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs
@@ -41,32 +41,10 @@ mod imp {
     };
     use std::cell::RefCell;
 
-    #[cfg(feature = "libadwaita")]
     #[derive(CompositeTemplate, Default, Properties)]
     #[properties(wrapper_type = super::PowerCapSection)]
-    #[template(file = "ui/oc_page/power_cap_section.blp")]
-    pub struct PowerCapSection {
-        #[property(get, set)]
-        pub current_value: RefCell<f64>,
-        #[property(get, set)]
-        pub max_value: RefCell<f64>,
-        #[property(get, set)]
-        pub min_value: RefCell<f64>,
-        #[property(get, set)]
-        pub default_value: RefCell<f64>,
-        #[property(get, set)]
-        pub value_text: RefCell<String>,
-
-        #[template_child]
-        pub adjustment: TemplateChild<OcAdjustment>,
-        #[template_child]
-        pub reset_button: TemplateChild<Button>,
-    }
-
-    #[cfg(not(feature = "libadwaita"))]
-    #[derive(CompositeTemplate, Default, Properties)]
-    #[properties(wrapper_type = super::PowerCapSection)]
-    #[template(file = "ui/oc_page/power_cap_section_gtk.blp")]
+    #[cfg_attr(feature = "libadwaita", template(file = "ui/oc_page/power_cap_section.blp"))]
+    #[cfg_attr(not(feature = "libadwaita"), template(file = "ui/oc_page/power_cap_section_gtk.blp"))]
     pub struct PowerCapSection {
         #[property(get, set)]
         pub current_value: RefCell<f64>,

From e75fc42316c85aaf6eb256bbc4f07ee3edb0e022 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 2 Dec 2023 01:12:57 +0100
Subject: [PATCH 35/54] feat: frame around plain gtk power cap section

---
 lact-gui/ui/oc_page/power_cap_section_gtk.blp | 46 ++++++++++---------
 1 file changed, 25 insertions(+), 21 deletions(-)

diff --git a/lact-gui/ui/oc_page/power_cap_section_gtk.blp b/lact-gui/ui/oc_page/power_cap_section_gtk.blp
index 8d86b01b..a115b28c 100644
--- a/lact-gui/ui/oc_page/power_cap_section_gtk.blp
+++ b/lact-gui/ui/oc_page/power_cap_section_gtk.blp
@@ -3,28 +3,32 @@ using Gtk 4.0;
 template $PowerCapSection: $PageSection {
     name: "Power usage limit";
 
-    Box {
-        orientation: horizontal;
-        margin-top: 12;
-        margin-bottom: 12;
-        spacing: 6;
-        
-        Label value_label {
-            label: bind template.value-text;
-        }
-
-        Scale {
+    Frame {
+        Box {
             orientation: horizontal;
-            hexpand: true;
-            round-digits: 0;
-            draw-value: false;
-            adjustment: adjustment;
-        }
-        
-        Button reset_button {
-            styles ["circular", "flat"]
-            icon-name: "view-refresh-symbolic";
-            tooltip-text: "Reset to default";
+            margin-top: 12;
+            margin-bottom: 12;
+            margin-start: 12;
+            margin-end: 12;
+            spacing: 6;
+            
+            Label value_label {
+                label: bind template.value-text;
+            }
+
+            Scale {
+                orientation: horizontal;
+                hexpand: true;
+                round-digits: 0;
+                draw-value: false;
+                adjustment: adjustment;
+            }
+            
+            Button reset_button {
+                styles ["circular", "flat"]
+                icon-name: "view-refresh-symbolic";
+                tooltip-text: "Reset to default";
+            }
         }
     }
 }

From b58a856e189e2fe2183e48ca3549b4f41eaf20fb Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 2 Dec 2023 01:37:05 +0100
Subject: [PATCH 36/54] fix: title in oc adjustment spinner row

---
 lact-gui/src/app/root_stack/oc_page/clocks_frame.rs | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
index dbe7dce5..ee24567c 100644
--- a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
@@ -398,6 +398,8 @@ fn oc_adjustment(title: &'static str, listbox: &ListBox) -> (Adjustment, Rc<Atom
         .title(title)
         .adjustment(&adjustment)
         .build();
+    #[cfg(feature = "libadwaita")]
+    listbox.append(&value_selector);
 
     #[cfg(not(feature = "libadwaita"))]
     let value_selector = {
@@ -406,7 +408,9 @@ fn oc_adjustment(title: &'static str, listbox: &ListBox) -> (Adjustment, Rc<Atom
             .valign(Align::Center)
             .build();
         let row = action_row(title, None, &[&spin_btn], None);
-        row.set_child(Some(&spin_btn));
+
+        listbox.append(&row);
+
         spin_btn
     };
 
@@ -421,8 +425,6 @@ fn oc_adjustment(title: &'static str, listbox: &ListBox) -> (Adjustment, Rc<Atom
         }
     ));
 
-    listbox.append(&value_selector);
-
     (adjustment, changed)
 }
 

From ad3a2aabe4210e333ba5d3fee4c66877001ba033 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 2 Dec 2023 01:37:35 +0100
Subject: [PATCH 37/54] feat: frame around plain gtk gpu stats section

---
 .../root_stack/oc_page/gpu_stats_section.rs   |  3 +-
 lact-gui/ui/oc_page/gpu_stats_section.blp     |  4 -
 lact-gui/ui/oc_page/gpu_stats_section_gtk.blp | 97 +++++++++++++++++++
 3 files changed, 99 insertions(+), 5 deletions(-)
 create mode 100644 lact-gui/ui/oc_page/gpu_stats_section_gtk.blp

diff --git a/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs b/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs
index 2093fbef..8653d599 100644
--- a/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs
+++ b/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs
@@ -84,7 +84,8 @@ mod imp {
 
     #[derive(CompositeTemplate, Default, Properties)]
     #[properties(wrapper_type = super::GpuStatsSection)]
-    #[template(file = "ui/oc_page/gpu_stats_section.blp")]
+    #[cfg_attr(feature = "libadwaita", template(file = "ui/oc_page/gpu_stats_section.blp"))]
+    #[cfg_attr(not(feature = "libadwaita"), template(file = "ui/oc_page/gpu_stats_section_gtk.blp"))]
     pub struct GpuStatsSection {
         #[property(get, set)]
         core_clock: RefCell<String>,
diff --git a/lact-gui/ui/oc_page/gpu_stats_section.blp b/lact-gui/ui/oc_page/gpu_stats_section.blp
index 6253af34..023ecaad 100644
--- a/lact-gui/ui/oc_page/gpu_stats_section.blp
+++ b/lact-gui/ui/oc_page/gpu_stats_section.blp
@@ -70,10 +70,6 @@ template $GpuStatsSection: $PageSection {
                 }
             }
 
-            //Separator {
-            //    orientation: vertical;
-            //}
-
             Box {
                 orientation: vertical;
                 hexpand: true;
diff --git a/lact-gui/ui/oc_page/gpu_stats_section_gtk.blp b/lact-gui/ui/oc_page/gpu_stats_section_gtk.blp
new file mode 100644
index 00000000..4d78a1d4
--- /dev/null
+++ b/lact-gui/ui/oc_page/gpu_stats_section_gtk.blp
@@ -0,0 +1,97 @@
+using Gtk 4.0;
+using Adw 1;
+
+template $GpuStatsSection: $PageSection {
+    name: "GPU statistics";
+    spacing: 12;
+
+    Frame {
+        styles ["view"]
+        Box {
+            orientation: vertical;
+            spacing: 12;
+
+            Box {
+                orientation: vertical;
+                margin-top: 12;
+                margin-start: 12;
+                margin-end: 12;
+                spacing: 6;
+
+                Label {
+                    label: "VRAM Usage";
+                    xalign: 0.0;
+                }
+
+                Overlay {
+                    LevelBar vram_usage_bar {
+                        hexpand: true;
+                        height-request: 18;
+                        value: bind template.vram-usage;
+                        orientation: horizontal;
+                    }
+
+                    [overlay]
+                    Label vram_usage_label {
+                        label: bind template.vram-usage-text;
+                    }
+                }
+            }
+
+            Separator {
+                orientation: horizontal;
+            }
+
+            Box {
+                orientation: horizontal;
+                margin-bottom: 12;
+                margin-start: 12;
+                margin-end: 12;
+                homogeneous: true;
+                spacing: 12;
+
+                Box {
+                    orientation: vertical;
+                    hexpand: true;
+                    spacing: 6;
+
+                    $InfoRow {
+                        name: "Core Clock:";
+                        value: bind template.core-clock;
+                    }
+
+                    $InfoRow {
+                        name: "Voltage:";
+                        value: bind template.voltage;
+                    }
+
+                    $InfoRow {
+                        name: "Temp (hotspot):";
+                        value: bind template.temperature;
+                    }
+                }
+
+                Box {
+                    orientation: vertical;
+                    hexpand: true;
+                    spacing: 6;
+
+                    $InfoRow {
+                        name: "Memory Clock:";
+                        value: bind template.vram-clock;
+                    }
+
+                    $InfoRow {
+                        name: "Usage:";
+                        value: bind template.gpu-usage;
+                    }
+
+                    $InfoRow {
+                        name: "Power Usage:";
+                        value: bind template.power-usage;
+                    }
+                }
+            }
+        }
+    }
+}

From 9dd29e29182211ea2e11ac6439445c33d21bc078 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 2 Dec 2023 01:37:54 +0100
Subject: [PATCH 38/54] feat: view css class for plain gtk power cap section

---
 lact-gui/ui/oc_page/power_cap_section_gtk.blp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lact-gui/ui/oc_page/power_cap_section_gtk.blp b/lact-gui/ui/oc_page/power_cap_section_gtk.blp
index a115b28c..d9c79465 100644
--- a/lact-gui/ui/oc_page/power_cap_section_gtk.blp
+++ b/lact-gui/ui/oc_page/power_cap_section_gtk.blp
@@ -4,6 +4,7 @@ template $PowerCapSection: $PageSection {
     name: "Power usage limit";
 
     Frame {
+        styles ["view"]
         Box {
             orientation: horizontal;
             margin-top: 12;

From b981bd39665f516a821781ba281c304080f111fd Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 2 Dec 2023 01:38:16 +0100
Subject: [PATCH 39/54] fix: plain gtk action rows are not activatable

---
 lact-gui/src/app/root_stack/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs
index 54462e67..4e8af03c 100644
--- a/lact-gui/src/app/root_stack/mod.rs
+++ b/lact-gui/src/app/root_stack/mod.rs
@@ -232,5 +232,5 @@ pub fn action_row(
         inner.append(*suf);
     });
 
-    ListBoxRow::builder().child(&inner).build()
+    ListBoxRow::builder().activatable(false).child(&inner).build()
 }

From e0bda110e8a91d807f644a71de40b824959e84b4 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 2 Dec 2023 01:38:41 +0100
Subject: [PATCH 40/54] feat: view css class for plain gtk static speed
 container

---
 lact-gui/src/app/root_stack/thermals_page/mod.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs
index 6d182bf3..741d127c 100644
--- a/lact-gui/src/app/root_stack/thermals_page/mod.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs
@@ -91,6 +91,7 @@ impl ThermalsPage {
             .build();
         #[cfg(not(feature = "libadwaita"))]
         let static_speed_container = Frame::builder()
+            .css_classes(["view"])
             .valign(Align::Start)
             .child(&fan_static_speed_frame)
             .build();

From 2246ffa6fa1cbce92835e984f153ce45f2aae155 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 2 Dec 2023 01:39:10 +0100
Subject: [PATCH 41/54] feat: frame around plain gtk fan curve frame

---
 .../thermals_page/fan_curve_frame/mod.rs          | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs
index 970d078a..a66e1675 100644
--- a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs
@@ -11,7 +11,12 @@ use std::rc::Rc;
 
 #[derive(Debug, Clone)]
 pub struct FanCurveFrame {
+    #[cfg(feature = "libadwaita")]
     pub container: Box,
+
+    #[cfg(not(feature = "libadwaita"))]
+    pub container: Frame,
+
     curve_container: ScrolledWindow,
     points: Rc<RefCell<Vec<PointAdjustment>>>,
 }
@@ -24,7 +29,7 @@ impl FanCurveFrame {
             .height_request(450)
             .build();
 
-        let hbox = Box::new(Orientation::Horizontal, 5);
+        let hbox = Box::new(Orientation::Horizontal, 6);
 
         let curve_container = ScrolledWindow::builder()
             .vscrollbar_policy(PolicyType::Never)
@@ -77,7 +82,15 @@ impl FanCurveFrame {
         let points = Rc::new(RefCell::new(Vec::new()));
 
         let curve_frame = Self {
+            #[cfg(feature = "libadwaita")]
             container: root_box,
+
+            #[cfg(not(feature = "libadwaita"))]
+            container: Frame::builder()
+                .css_classes(["view"])
+                .child(&root_box)
+                .build(),
+
             curve_container,
             points,
         };

From 7f2a3d244511f7bd26f6d581e7b4faa74774367a Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 2 Dec 2023 01:44:43 +0100
Subject: [PATCH 42/54] feat: cfg_attr for feature window instead of
 duplicating entite struct declaration

---
 .../vulkan_info/feature_window/mod.rs         | 23 ++-----------------
 1 file changed, 2 insertions(+), 21 deletions(-)

diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs
index 4046dbde..1a7f96d0 100644
--- a/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs
+++ b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs
@@ -46,29 +46,10 @@ mod imp {
     #[cfg(feature = "libadwaita")]
     use libadwaita::subclass::window::AdwWindowImpl;
 
-    #[cfg(feature = "libadwaita")]
-    #[derive(CompositeTemplate, Properties, Default)]
-    #[properties(wrapper_type = super::VulkanFeaturesWindow)]
-    #[template(file = "ui/vulkan_features_window.blp")]
-    pub struct VulkanFeaturesWindow {
-        #[property(get, set)]
-        model: RefCell<Option<gio::ListModel>>,
-        #[template_child]
-        features_factory: TemplateChild<SignalListItemFactory>,
-
-        #[template_child]
-        filter_model: TemplateChild<FilterListModel>,
-
-        #[template_child]
-        search_filter: TemplateChild<StringFilter>,
-        #[template_child]
-        search_entry: TemplateChild<SearchEntry>,
-    }
-
-    #[cfg(not(feature = "libadwaita"))]
     #[derive(CompositeTemplate, Properties, Default)]
     #[properties(wrapper_type = super::VulkanFeaturesWindow)]
-    #[template(file = "ui/vulkan_features_window_gtk.blp")]
+    #[cfg_attr(feature = "libadwaita", template(file = "ui/vulkan_features_window.blp"))]
+    #[cfg_attr(not(feature = "libadwaita"), template(file = "ui/vulkan_features_window_gtk.blp"))]
     pub struct VulkanFeaturesWindow {
         #[property(get, set)]
         model: RefCell<Option<gio::ListModel>>,

From 7ce1cd75d5519d11e056458aa723242fcd9851f4 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 2 Dec 2023 01:45:50 +0100
Subject: [PATCH 43/54] fix: remove adw from gpu stats section blueprint

---
 lact-gui/ui/oc_page/gpu_stats_section_gtk.blp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/lact-gui/ui/oc_page/gpu_stats_section_gtk.blp b/lact-gui/ui/oc_page/gpu_stats_section_gtk.blp
index 4d78a1d4..d71aefbb 100644
--- a/lact-gui/ui/oc_page/gpu_stats_section_gtk.blp
+++ b/lact-gui/ui/oc_page/gpu_stats_section_gtk.blp
@@ -1,5 +1,4 @@
 using Gtk 4.0;
-using Adw 1;
 
 template $GpuStatsSection: $PageSection {
     name: "GPU statistics";

From f8d1f3b3294db8190f5381c53e085c3f9e0b8d6c Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 2 Dec 2023 02:14:04 +0100
Subject: [PATCH 44/54] chore: rename libadwaita package to adw

---
 lact-gui/Cargo.toml                           |  4 +--
 lact-gui/src/app/dialogs.rs                   |  4 +--
 lact-gui/src/app/headerbar.rs                 |  4 +--
 lact-gui/src/app/mod.rs                       | 26 +++++++++----------
 .../vulkan_info/feature_window/mod.rs         | 16 ++++++++----
 .../root_stack/info_page/vulkan_info/mod.rs   |  6 ++---
 lact-gui/src/app/root_stack/mod.rs            | 21 ++++++++-------
 .../app/root_stack/oc_page/clocks_frame.rs    |  9 +++----
 .../root_stack/oc_page/gpu_stats_section.rs   | 10 +++++--
 lact-gui/src/app/root_stack/oc_page/mod.rs    |  2 +-
 .../root_stack/oc_page/performance_frame.rs   | 10 +++----
 .../root_stack/oc_page/power_cap_section.rs   | 10 +++++--
 .../src/app/root_stack/thermals_page/mod.rs   |  4 +--
 lact-gui/src/lib.rs                           |  2 +-
 14 files changed, 73 insertions(+), 55 deletions(-)

diff --git a/lact-gui/Cargo.toml b/lact-gui/Cargo.toml
index efbeff5e..95abeceb 100644
--- a/lact-gui/Cargo.toml
+++ b/lact-gui/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2021"
 [features]
 default = ["gtk-tests"]
 gtk-tests = []
-libadwaita = ["dep:libadwaita"]
+libadwaita = ["dep:adw"]
 
 [dependencies]
 lact-client = { path = "../lact-client" }
@@ -16,7 +16,7 @@ gtk = { version = "0.7", package = "gtk4", features = ["v4_6", "blueprint"] }
 tracing = "0.1"
 tracing-subscriber = { version = "0.3", features = ["env-filter"] }
 anyhow = "1.0"
-libadwaita = { version = "0.5.3", features = ["v1_4"], optional = true }
+adw = { package = "libadwaita", version = "0.5.3", features = ["v1_4"], optional = true }
 
 [dev-dependencies]
 pretty_assertions = "1.4.0"
diff --git a/lact-gui/src/app/dialogs.rs b/lact-gui/src/app/dialogs.rs
index af26c896..5c734874 100644
--- a/lact-gui/src/app/dialogs.rs
+++ b/lact-gui/src/app/dialogs.rs
@@ -2,13 +2,13 @@ use gtk::prelude::*;
 use tracing::warn;
 
 #[cfg(feature = "libadwaita")]
-use libadwaita::prelude::MessageDialogExt;
+use adw::prelude::MessageDialogExt;
 
 #[cfg(feature = "libadwaita")]
 #[macro_export]
 macro_rules! info_dialog {
     ($parent:expr, $heading:expr, $body:expr, $response_id:expr, $response_txt:expr) => {{
-        let diag = libadwaita::MessageDialog::builder()
+        let diag = adw::MessageDialog::builder()
             .heading($heading)
             .body($body)
             .modal(true)
diff --git a/lact-gui/src/app/headerbar.rs b/lact-gui/src/app/headerbar.rs
index de7cf279..839da3d6 100644
--- a/lact-gui/src/app/headerbar.rs
+++ b/lact-gui/src/app/headerbar.rs
@@ -3,7 +3,7 @@ use super::{apply_box::ApplyBox, gpu_selector::GpuSelector};
 #[derive(Debug, Clone)]
 pub struct Headerbar {
     #[cfg(feature = "libadwaita")]
-    pub container: libadwaita::HeaderBar,
+    pub container: adw::HeaderBar,
 
     #[cfg(not(feature = "libadwaita"))]
     pub container: gtk::HeaderBar,
@@ -15,7 +15,7 @@ pub struct Headerbar {
 impl Headerbar {
     pub fn new() -> Self {
         #[cfg(feature = "libadwaita")]
-        let container = libadwaita::HeaderBar::builder().show_title(false).build();
+        let container = adw::HeaderBar::builder().show_title(false).build();
 
         #[cfg(not(feature = "libadwaita"))]
         let container = gtk::HeaderBar::builder()
diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index 4fe4d84c..52513969 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -27,7 +27,7 @@ use std::time::Duration;
 use tracing::{debug, error, trace, warn};
 
 #[cfg(feature = "libadwaita")]
-use libadwaita::prelude::{AdwApplicationWindowExt, MessageDialogExt};
+use adw::prelude::{AdwApplicationWindowExt, MessageDialogExt};
 
 // In ms
 const STATS_POLL_INTERVAL: u64 = 250;
@@ -35,9 +35,9 @@ const STATS_POLL_INTERVAL: u64 = 250;
 #[derive(Clone)]
 pub struct App {
     #[cfg(feature = "libadwaita")]
-    application: libadwaita::Application,
+    application: adw::Application,
     #[cfg(feature = "libadwaita")]
-    pub window: libadwaita::ApplicationWindow,
+    pub window: adw::ApplicationWindow,
 
     #[cfg(not(feature = "libadwaita"))]
     application: Application,
@@ -53,13 +53,13 @@ pub struct App {
 impl App {
     pub fn new(daemon_client: DaemonClient) -> Self {
         #[cfg(feature = "libadwaita")]
-        let application = libadwaita::Application::new(Some(APP_ID), ApplicationFlags::default());
+        let application = adw::Application::new(Some(APP_ID), ApplicationFlags::default());
 
         #[cfg(not(feature = "libadwaita"))]
         let application = Application::new(Some(APP_ID), ApplicationFlags::default());
 
         #[cfg(feature = "libadwaita")]
-        let window = libadwaita::ApplicationWindow::builder()
+        let window = adw::ApplicationWindow::builder()
             .title("LACT")
             .default_width(820)
             .default_height(750)
@@ -94,10 +94,10 @@ impl App {
 
         #[cfg(feature = "libadwaita")]
         {
-            let root_view = libadwaita::ToolbarView::new();
+            let root_view = adw::ToolbarView::new();
             root_view.add_top_bar(&headerbar.container);
             root_view.add_bottom_bar(
-                &libadwaita::ViewSwitcherBar::builder()
+                &adw::ViewSwitcherBar::builder()
                     .reveal(true)
                     .stack(&root_stack.container)
                     .build(),
@@ -528,7 +528,7 @@ impl App {
     #[cfg(feature = "libadwaita")]
     fn enable_overclocking(&self) {
         let text = format!("This will enable the overdrive feature of the amdgpu driver by creating a file at <b>{MODULE_CONF_PATH}</b>");
-        let dialog = libadwaita::MessageDialog::builder()
+        let dialog = adw::MessageDialog::builder()
             .heading("Enable Overclocking")
             .body_use_markup(true)
             .body(text)
@@ -541,8 +541,8 @@ impl App {
 
         dialog.add_response(res_cancel, "_Cancel");
         dialog.add_response(res_ok, "_Ok");
-        dialog.set_response_appearance(res_cancel, libadwaita::ResponseAppearance::Destructive);
-        dialog.set_response_appearance(res_ok, libadwaita::ResponseAppearance::Suggested);
+        dialog.set_response_appearance(res_cancel, adw::ResponseAppearance::Destructive);
+        dialog.set_response_appearance(res_ok, adw::ResponseAppearance::Suggested);
 
         dialog.connect_response(
             None,
@@ -608,7 +608,7 @@ impl App {
     #[cfg(feature = "libadwaita")]
     fn ask_confirmation(&self, gpu_id: String, mut delay: u64) {
         let text = confirmation_text(delay);
-        let dialog = libadwaita::MessageDialog::builder()
+        let dialog = adw::MessageDialog::builder()
             .heading("Confirm settings")
             .body(text)
             .modal(true)
@@ -620,8 +620,8 @@ impl App {
 
         dialog.add_response(res_no, "_No");
         dialog.add_response(res_yes, "_Yes");
-        dialog.set_response_appearance(res_no, libadwaita::ResponseAppearance::Destructive);
-        dialog.set_response_appearance(res_yes, libadwaita::ResponseAppearance::Suggested);
+        dialog.set_response_appearance(res_no, adw::ResponseAppearance::Destructive);
+        dialog.set_response_appearance(res_yes, adw::ResponseAppearance::Suggested);
         let confirmed = Rc::new(AtomicBool::new(false));
 
         glib::source::timeout_add_local(
diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs
index 1a7f96d0..5329888c 100644
--- a/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs
+++ b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs
@@ -7,7 +7,7 @@ use gtk::{gio, glib};
 #[cfg(feature = "libadwaita")]
 glib::wrapper! {
     pub struct VulkanFeaturesWindow(ObjectSubclass<imp::VulkanFeaturesWindow>)
-        @extends gtk::Box, gtk::Widget, gtk::Window, libadwaita::Window,
+        @extends gtk::Box, gtk::Widget, gtk::Window, adw::Window,
         @implements gtk::Orientable, gtk::Accessible, gtk::Buildable;
 }
 
@@ -44,12 +44,18 @@ mod imp {
     use std::cell::RefCell;
 
     #[cfg(feature = "libadwaita")]
-    use libadwaita::subclass::window::AdwWindowImpl;
+    use adw::subclass::window::AdwWindowImpl;
 
     #[derive(CompositeTemplate, Properties, Default)]
     #[properties(wrapper_type = super::VulkanFeaturesWindow)]
-    #[cfg_attr(feature = "libadwaita", template(file = "ui/vulkan_features_window.blp"))]
-    #[cfg_attr(not(feature = "libadwaita"), template(file = "ui/vulkan_features_window_gtk.blp"))]
+    #[cfg_attr(
+        feature = "libadwaita",
+        template(file = "ui/vulkan_features_window.blp")
+    )]
+    #[cfg_attr(
+        not(feature = "libadwaita"),
+        template(file = "ui/vulkan_features_window_gtk.blp")
+    )]
     pub struct VulkanFeaturesWindow {
         #[property(get, set)]
         model: RefCell<Option<gio::ListModel>>,
@@ -71,7 +77,7 @@ mod imp {
         type Type = super::VulkanFeaturesWindow;
 
         #[cfg(feature = "libadwaita")]
-        type ParentType = libadwaita::Window;
+        type ParentType = adw::Window;
 
         #[cfg(not(feature = "libadwaita"))]
         type ParentType = gtk::Window;
diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
index b4915c34..e378b4c1 100644
--- a/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
+++ b/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
@@ -10,7 +10,7 @@ use lact_client::schema::VulkanInfo;
 use tracing::trace;
 
 #[cfg(feature = "libadwaita")]
-use libadwaita::prelude::ActionRowExt;
+use adw::prelude::ActionRowExt;
 
 #[derive(Debug, Clone)]
 pub struct VulkanInfoFrame {
@@ -45,7 +45,7 @@ impl VulkanInfoFrame {
 
         #[cfg(feature = "libadwaita")]
         {
-            let features_row = libadwaita::ActionRow::builder()
+            let features_row = adw::ActionRow::builder()
                 .activatable(true)
                 .title("Features")
                 .build();
@@ -55,7 +55,7 @@ impl VulkanInfoFrame {
             }));
             container.append(&features_row);
 
-            let extensions_row = libadwaita::ActionRow::builder()
+            let extensions_row = adw::ActionRow::builder()
                 .activatable(true)
                 .title("Extensions")
                 .build();
diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs
index 4e8af03c..69b4c429 100644
--- a/lact-gui/src/app/root_stack/mod.rs
+++ b/lact-gui/src/app/root_stack/mod.rs
@@ -13,12 +13,12 @@ use traits::BoxExt;
 use traits::WidgetExt;
 
 #[cfg(feature = "libadwaita")]
-use libadwaita::prelude::ActionRowExt;
+use adw::prelude::ActionRowExt;
 
 #[derive(Debug, Clone)]
 pub struct RootStack {
     #[cfg(feature = "libadwaita")]
-    pub container: libadwaita::ViewStack,
+    pub container: adw::ViewStack,
     #[cfg(not(feature = "libadwaita"))]
     pub container: Stack,
 
@@ -34,7 +34,7 @@ impl RootStack {
         embedded_daemon: bool,
     ) -> Self {
         #[cfg(feature = "libadwaita")]
-        let container = libadwaita::ViewStack::builder()
+        let container = adw::ViewStack::builder()
             .vexpand(true)
             .hexpand(true)
             .build();
@@ -94,7 +94,7 @@ impl RootStack {
 #[derive(Debug, Clone)]
 pub struct LabelRow {
     #[cfg(feature = "libadwaita")]
-    pub container: libadwaita::ActionRow,
+    pub container: adw::ActionRow,
 
     #[cfg(not(feature = "libadwaita"))]
     pub container: ListBoxRow,
@@ -132,8 +132,8 @@ impl LabelRow {
 }
 
 #[cfg(feature = "libadwaita")]
-pub fn list_clamp(child: &impl IsA<Widget>) -> libadwaita::Clamp {
-    libadwaita::Clamp::builder()
+pub fn list_clamp(child: &impl IsA<Widget>) -> adw::Clamp {
+    adw::Clamp::builder()
         .maximum_size(600)
         .margin_top(24)
         .margin_bottom(24)
@@ -165,8 +165,8 @@ pub fn action_row(
     subtitle: Option<&str>,
     suffixes: &[&impl IsA<Widget>],
     css_classes: Option<&[&str]>,
-) -> libadwaita::ActionRow {
-    let ar = libadwaita::ActionRow::builder()
+) -> adw::ActionRow {
+    let ar = adw::ActionRow::builder()
         .subtitle_lines(0)
         .title(title)
         .build();
@@ -232,5 +232,8 @@ pub fn action_row(
         inner.append(*suf);
     });
 
-    ListBoxRow::builder().activatable(false).child(&inner).build()
+    ListBoxRow::builder()
+        .activatable(false)
+        .child(&inner)
+        .build()
 }
diff --git a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
index ee24567c..7c1f176c 100644
--- a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
@@ -9,9 +9,6 @@ use std::rc::Rc;
 use std::sync::atomic::{AtomicBool, Ordering};
 use tracing::debug;
 
-#[cfg(feature = "libadwaita")]
-use libadwaita::prelude::ActionRowExt;
-
 const VOLTAGE_OFFSET_RANGE: f64 = 250.0;
 
 // The AtomicBool stores if the value was changed
@@ -22,7 +19,7 @@ pub struct ClocksFrame {
     heading_listbox: ListBox,
 
     #[cfg(feature = "libadwaita")]
-    advanced_switch_row: libadwaita::SwitchRow,
+    advanced_switch_row: adw::SwitchRow,
 
     #[cfg(not(feature = "libadwaita"))]
     advanced_switch_row: Switch,
@@ -58,7 +55,7 @@ impl ClocksFrame {
 
         #[cfg(feature = "libadwaita")]
         let advanced_switch_row = {
-            let row = libadwaita::SwitchRow::builder()
+            let row = adw::SwitchRow::builder()
                 .title("Advanced mode")
                 .active(false)
                 .build();
@@ -394,7 +391,7 @@ fn oc_adjustment(title: &'static str, listbox: &ListBox) -> (Adjustment, Rc<Atom
     let adjustment = Adjustment::new(0.0, 0.0, 0.0, 1.0, 10.0, 0.0);
 
     #[cfg(feature = "libadwaita")]
-    let value_selector = libadwaita::SpinRow::builder()
+    let value_selector = adw::SpinRow::builder()
         .title(title)
         .adjustment(&adjustment)
         .build();
diff --git a/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs b/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs
index 8653d599..2bbadee6 100644
--- a/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs
+++ b/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs
@@ -84,8 +84,14 @@ mod imp {
 
     #[derive(CompositeTemplate, Default, Properties)]
     #[properties(wrapper_type = super::GpuStatsSection)]
-    #[cfg_attr(feature = "libadwaita", template(file = "ui/oc_page/gpu_stats_section.blp"))]
-    #[cfg_attr(not(feature = "libadwaita"), template(file = "ui/oc_page/gpu_stats_section_gtk.blp"))]
+    #[cfg_attr(
+        feature = "libadwaita",
+        template(file = "ui/oc_page/gpu_stats_section.blp")
+    )]
+    #[cfg_attr(
+        not(feature = "libadwaita"),
+        template(file = "ui/oc_page/gpu_stats_section_gtk.blp")
+    )]
     pub struct GpuStatsSection {
         #[property(get, set)]
         core_clock: RefCell<String>,
diff --git a/lact-gui/src/app/root_stack/oc_page/mod.rs b/lact-gui/src/app/root_stack/oc_page/mod.rs
index d914cf87..a9c314b0 100644
--- a/lact-gui/src/app/root_stack/oc_page/mod.rs
+++ b/lact-gui/src/app/root_stack/oc_page/mod.rs
@@ -52,7 +52,7 @@ impl OcPage {
 
             #[cfg(feature = "libadwaita")]
             vbox.append(
-                &libadwaita::Bin::builder()
+                &adw::Bin::builder()
                     .css_classes(["card"])
                     .child(&warning_frame)
                     .build(),
diff --git a/lact-gui/src/app/root_stack/oc_page/performance_frame.rs b/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
index 5b5d25e8..5dbf54c9 100644
--- a/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
@@ -8,16 +8,16 @@ use lact_client::schema::amdgpu_sysfs::gpu_handle::{
 use std::{cell::RefCell, rc::Rc, str::FromStr};
 
 #[cfg(feature = "libadwaita")]
-use libadwaita::prelude::{ActionRowExt, ComboRowExt};
+use adw::prelude::{ActionRowExt, ComboRowExt};
 
 #[derive(Debug, Clone)]
 pub struct PerformanceFrame {
     pub container: PageSection,
 
     #[cfg(feature = "libadwaita")]
-    level_row: libadwaita::ComboRow,
+    level_row: adw::ComboRow,
     #[cfg(feature = "libadwaita")]
-    mode_row: libadwaita::ComboRow,
+    mode_row: adw::ComboRow,
 
     #[cfg(not(feature = "libadwaita"))]
     level_row: DropDown,
@@ -44,7 +44,7 @@ impl PerformanceFrame {
 
         #[cfg(feature = "libadwaita")]
         let level_row = {
-            let row = libadwaita::ComboRow::builder()
+            let row = adw::ComboRow::builder()
                 .model(&levels_model)
                 .title("Performance level")
                 .subtitle("")
@@ -84,7 +84,7 @@ impl PerformanceFrame {
 
         #[cfg(feature = "libadwaita")]
         let mode_row = {
-            let row = libadwaita::ComboRow::builder()
+            let row = adw::ComboRow::builder()
                 .model(&filler_model)
                 .title("Power level mode")
                 .subtitle("Set \"Performance level\" to \"Manual\" to use power states and modes")
diff --git a/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs b/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs
index c6d3db63..c87d2bd2 100644
--- a/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs
+++ b/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs
@@ -43,8 +43,14 @@ mod imp {
 
     #[derive(CompositeTemplate, Default, Properties)]
     #[properties(wrapper_type = super::PowerCapSection)]
-    #[cfg_attr(feature = "libadwaita", template(file = "ui/oc_page/power_cap_section.blp"))]
-    #[cfg_attr(not(feature = "libadwaita"), template(file = "ui/oc_page/power_cap_section_gtk.blp"))]
+    #[cfg_attr(
+        feature = "libadwaita",
+        template(file = "ui/oc_page/power_cap_section.blp")
+    )]
+    #[cfg_attr(
+        not(feature = "libadwaita"),
+        template(file = "ui/oc_page/power_cap_section_gtk.blp")
+    )]
     pub struct PowerCapSection {
         #[property(get, set)]
         pub current_value: RefCell<f64>,
diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs
index 741d127c..f5e4c006 100644
--- a/lact-gui/src/app/root_stack/thermals_page/mod.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs
@@ -9,7 +9,7 @@ use gtk::*;
 use lact_client::schema::{default_fan_curve, DeviceStats, FanControlMode, FanCurveMap};
 
 #[cfg(feature = "libadwaita")]
-use libadwaita::prelude::*;
+use adw::prelude::*;
 
 #[derive(Debug)]
 pub struct ThermalsSettings {
@@ -84,7 +84,7 @@ impl ThermalsPage {
         fan_control_mode_stack.add_titled(&fan_curve_frame.container, Some("curve"), "Curve");
 
         #[cfg(feature = "libadwaita")]
-        let static_speed_container = libadwaita::Bin::builder()
+        let static_speed_container = adw::Bin::builder()
             .css_classes(["card"])
             .valign(Align::Start)
             .child(&fan_static_speed_frame)
diff --git a/lact-gui/src/lib.rs b/lact-gui/src/lib.rs
index 959c6a3e..8546b036 100644
--- a/lact-gui/src/lib.rs
+++ b/lact-gui/src/lib.rs
@@ -18,7 +18,7 @@ pub fn run(args: GuiArgs) -> anyhow::Result<()> {
     tracing_subscriber::fmt().with_env_filter(env_filter).init();
 
     #[cfg(feature = "libadwaita")]
-    if let Err(err) = libadwaita::init() {
+    if let Err(err) = adw::init() {
         return Err(anyhow!("Cannot initialize Libadwaita: {err}"));
     }
 

From bbf786807645a8ed4cbd351d6bba78e0b2912698 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 2 Dec 2023 11:20:07 +0100
Subject: [PATCH 45/54] chore: rename libadwaita feature to adw

---
 Makefile                                      |  4 +--
 lact-gui/Cargo.toml                           |  2 +-
 lact-gui/src/app/dialogs.rs                   |  6 ++--
 lact-gui/src/app/headerbar.rs                 |  8 ++---
 lact-gui/src/app/mod.rs                       | 34 +++++++++----------
 .../vulkan_info/feature_window/mod.rs         | 19 +++++------
 .../root_stack/info_page/vulkan_info/mod.rs   |  6 ++--
 lact-gui/src/app/root_stack/mod.rs            | 26 +++++++-------
 .../app/root_stack/oc_page/clocks_frame.rs    | 14 ++++----
 .../root_stack/oc_page/gpu_stats_section.rs   |  7 ++--
 lact-gui/src/app/root_stack/oc_page/mod.rs    |  4 +--
 .../root_stack/oc_page/performance_frame.rs   | 28 +++++++--------
 .../root_stack/oc_page/power_cap_section.rs   |  7 ++--
 .../thermals_page/fan_curve_frame/mod.rs      |  8 ++---
 .../src/app/root_stack/thermals_page/mod.rs   |  6 ++--
 lact-gui/src/lib.rs                           |  4 +--
 .../{lact-libadwaita => lact-adw}/recipe.yml  |  4 +--
 17 files changed, 89 insertions(+), 98 deletions(-)
 rename pkg/recipes/{lact-libadwaita => lact-adw}/recipe.yml (89%)

diff --git a/Makefile b/Makefile
index 504e30e2..3b6c2b91 100644
--- a/Makefile
+++ b/Makefile
@@ -5,8 +5,8 @@ PREFIX ?= /usr/local
 build-release:
 	cargo build --release
 
-build-libadwaita-release:
-	cargo build --release --features libadwaita
+build-adw-release:
+	cargo build --release --features adw
 
 install:
 	install -Dm755 target/release/lact $(DESTDIR)$(PREFIX)/bin/lact
diff --git a/lact-gui/Cargo.toml b/lact-gui/Cargo.toml
index 95abeceb..c7b89758 100644
--- a/lact-gui/Cargo.toml
+++ b/lact-gui/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2021"
 [features]
 default = ["gtk-tests"]
 gtk-tests = []
-libadwaita = ["dep:adw"]
+adw = ["dep:adw"]
 
 [dependencies]
 lact-client = { path = "../lact-client" }
diff --git a/lact-gui/src/app/dialogs.rs b/lact-gui/src/app/dialogs.rs
index 5c734874..51034d3f 100644
--- a/lact-gui/src/app/dialogs.rs
+++ b/lact-gui/src/app/dialogs.rs
@@ -1,10 +1,10 @@
 use gtk::prelude::*;
 use tracing::warn;
 
-#[cfg(feature = "libadwaita")]
+#[cfg(feature = "adw")]
 use adw::prelude::MessageDialogExt;
 
-#[cfg(feature = "libadwaita")]
+#[cfg(feature = "adw")]
 #[macro_export]
 macro_rules! info_dialog {
     ($parent:expr, $heading:expr, $body:expr, $response_id:expr, $response_txt:expr) => {{
@@ -23,7 +23,7 @@ macro_rules! info_dialog {
     }};
 }
 
-#[cfg(not(feature = "libadwaita"))]
+#[cfg(not(feature = "adw"))]
 #[macro_export]
 macro_rules! info_dialog {
     ($parent:expr, $heading:expr, $body:expr, $response_id:expr, $response_txt:expr) => {{
diff --git a/lact-gui/src/app/headerbar.rs b/lact-gui/src/app/headerbar.rs
index 839da3d6..64d21099 100644
--- a/lact-gui/src/app/headerbar.rs
+++ b/lact-gui/src/app/headerbar.rs
@@ -2,10 +2,10 @@ use super::{apply_box::ApplyBox, gpu_selector::GpuSelector};
 
 #[derive(Debug, Clone)]
 pub struct Headerbar {
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     pub container: adw::HeaderBar,
 
-    #[cfg(not(feature = "libadwaita"))]
+    #[cfg(not(feature = "adw"))]
     pub container: gtk::HeaderBar,
 
     pub gpu_selector: GpuSelector,
@@ -14,10 +14,10 @@ pub struct Headerbar {
 
 impl Headerbar {
     pub fn new() -> Self {
-        #[cfg(feature = "libadwaita")]
+        #[cfg(feature = "adw")]
         let container = adw::HeaderBar::builder().show_title(false).build();
 
-        #[cfg(not(feature = "libadwaita"))]
+        #[cfg(not(feature = "adw"))]
         let container = gtk::HeaderBar::builder()
             .title_widget(&gtk::Label::new(None))
             .build();
diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index 52513969..7fda96e2 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -26,7 +26,7 @@ use std::sync::atomic::AtomicBool;
 use std::time::Duration;
 use tracing::{debug, error, trace, warn};
 
-#[cfg(feature = "libadwaita")]
+#[cfg(feature = "adw")]
 use adw::prelude::{AdwApplicationWindowExt, MessageDialogExt};
 
 // In ms
@@ -34,14 +34,14 @@ const STATS_POLL_INTERVAL: u64 = 250;
 
 #[derive(Clone)]
 pub struct App {
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     application: adw::Application,
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     pub window: adw::ApplicationWindow,
 
-    #[cfg(not(feature = "libadwaita"))]
+    #[cfg(not(feature = "adw"))]
     application: Application,
-    #[cfg(not(feature = "libadwaita"))]
+    #[cfg(not(feature = "adw"))]
     pub window: ApplicationWindow,
 
     pub gpu_selector: GpuSelector,
@@ -52,13 +52,13 @@ pub struct App {
 
 impl App {
     pub fn new(daemon_client: DaemonClient) -> Self {
-        #[cfg(feature = "libadwaita")]
+        #[cfg(feature = "adw")]
         let application = adw::Application::new(Some(APP_ID), ApplicationFlags::default());
 
-        #[cfg(not(feature = "libadwaita"))]
+        #[cfg(not(feature = "adw"))]
         let application = Application::new(Some(APP_ID), ApplicationFlags::default());
 
-        #[cfg(feature = "libadwaita")]
+        #[cfg(feature = "adw")]
         let window = adw::ApplicationWindow::builder()
             .title("LACT")
             .default_width(820)
@@ -68,7 +68,7 @@ impl App {
             .icon_name(APP_ID)
             .build();
 
-        #[cfg(not(feature = "libadwaita"))]
+        #[cfg(not(feature = "adw"))]
         let window = ApplicationWindow::builder()
             .title("LACT")
             .default_width(820)
@@ -92,7 +92,7 @@ impl App {
 
         let headerbar = Headerbar::new();
 
-        #[cfg(feature = "libadwaita")]
+        #[cfg(feature = "adw")]
         {
             let root_view = adw::ToolbarView::new();
             root_view.add_top_bar(&headerbar.container);
@@ -106,7 +106,7 @@ impl App {
             window.set_content(Some(&root_view));
         }
 
-        #[cfg(not(feature = "libadwaita"))]
+        #[cfg(not(feature = "adw"))]
         {
             let root_view = Box::builder().orientation(Orientation::Vertical).build();
             root_view.append(&root_stack.container);
@@ -245,10 +245,10 @@ impl App {
                         "close",
                         "_Close");
 
-                    #[cfg(feature = "libadwaita")]
+                    #[cfg(feature = "adw")]
                     diag.set_extra_child(Some(&hbox));
 
-                    #[cfg(not(feature = "libadwaita"))]
+                    #[cfg(not(feature = "adw"))]
                     {
                         hbox.set_margin_start(12);
                         hbox.set_margin_end(12);
@@ -525,7 +525,7 @@ impl App {
         Ok(())
     }
 
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     fn enable_overclocking(&self) {
         let text = format!("This will enable the overdrive feature of the amdgpu driver by creating a file at <b>{MODULE_CONF_PATH}</b>");
         let dialog = adw::MessageDialog::builder()
@@ -570,7 +570,7 @@ impl App {
         dialog.present();
     }
 
-    #[cfg(not(feature = "libadwaita"))]
+    #[cfg(not(feature = "adw"))]
     fn enable_overclocking(&self) {
         let text = format!("This will enable the overdrive feature of the amdgpu driver by creating a file at <b>{MODULE_CONF_PATH}</b>. Are you sure you want to do this?");
         let dialog = MessageDialog::builder()
@@ -605,7 +605,7 @@ impl App {
         }));
     }
 
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     fn ask_confirmation(&self, gpu_id: String, mut delay: u64) {
         let text = confirmation_text(delay);
         let dialog = adw::MessageDialog::builder()
@@ -668,7 +668,7 @@ impl App {
         dialog.present();
     }
 
-    #[cfg(not(feature = "libadwaita"))]
+    #[cfg(not(feature = "adw"))]
     fn ask_confirmation(&self, gpu_id: String, mut delay: u64) {
         let text = confirmation_text(delay);
         let dialog = MessageDialog::builder()
diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs
index 5329888c..3ea707a6 100644
--- a/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs
+++ b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_window/mod.rs
@@ -4,14 +4,14 @@ mod row;
 use glib::Object;
 use gtk::{gio, glib};
 
-#[cfg(feature = "libadwaita")]
+#[cfg(feature = "adw")]
 glib::wrapper! {
     pub struct VulkanFeaturesWindow(ObjectSubclass<imp::VulkanFeaturesWindow>)
         @extends gtk::Box, gtk::Widget, gtk::Window, adw::Window,
         @implements gtk::Orientable, gtk::Accessible, gtk::Buildable;
 }
 
-#[cfg(not(feature = "libadwaita"))]
+#[cfg(not(feature = "adw"))]
 glib::wrapper! {
     pub struct VulkanFeaturesWindow(ObjectSubclass<imp::VulkanFeaturesWindow>)
         @extends gtk::Box, gtk::Widget, gtk::Window,
@@ -43,17 +43,14 @@ mod imp {
     };
     use std::cell::RefCell;
 
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     use adw::subclass::window::AdwWindowImpl;
 
     #[derive(CompositeTemplate, Properties, Default)]
     #[properties(wrapper_type = super::VulkanFeaturesWindow)]
+    #[cfg_attr(feature = "adw", template(file = "ui/vulkan_features_window.blp"))]
     #[cfg_attr(
-        feature = "libadwaita",
-        template(file = "ui/vulkan_features_window.blp")
-    )]
-    #[cfg_attr(
-        not(feature = "libadwaita"),
+        not(feature = "adw"),
         template(file = "ui/vulkan_features_window_gtk.blp")
     )]
     pub struct VulkanFeaturesWindow {
@@ -76,10 +73,10 @@ mod imp {
         const NAME: &'static str = "VulkanFeaturesWindow";
         type Type = super::VulkanFeaturesWindow;
 
-        #[cfg(feature = "libadwaita")]
+        #[cfg(feature = "adw")]
         type ParentType = adw::Window;
 
-        #[cfg(not(feature = "libadwaita"))]
+        #[cfg(not(feature = "adw"))]
         type ParentType = gtk::Window;
 
         fn class_init(class: &mut Self::Class) {
@@ -140,7 +137,7 @@ mod imp {
     impl WidgetImpl for VulkanFeaturesWindow {}
     impl WindowImpl for VulkanFeaturesWindow {}
 
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     impl AdwWindowImpl for VulkanFeaturesWindow {}
 
     impl ApplicationWindowImpl for VulkanFeaturesWindow {}
diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
index e378b4c1..997a28a1 100644
--- a/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
+++ b/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs
@@ -9,7 +9,7 @@ use gtk::*;
 use lact_client::schema::VulkanInfo;
 use tracing::trace;
 
-#[cfg(feature = "libadwaita")]
+#[cfg(feature = "adw")]
 use adw::prelude::ActionRowExt;
 
 #[derive(Debug, Clone)]
@@ -43,7 +43,7 @@ impl VulkanInfoFrame {
         container.append(&driver_name_row.container);
         container.append(&driver_version_row.container);
 
-        #[cfg(feature = "libadwaita")]
+        #[cfg(feature = "adw")]
         {
             let features_row = adw::ActionRow::builder()
                 .activatable(true)
@@ -66,7 +66,7 @@ impl VulkanInfoFrame {
             container.append(&extensions_row);
         }
 
-        #[cfg(not(feature = "libadwaita"))]
+        #[cfg(not(feature = "adw"))]
         {
             let features_btn = Button::builder().label("View").build();
             features_btn.connect_clicked(clone!(@strong features_model => move |_| {
diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs
index 69b4c429..0c0333fe 100644
--- a/lact-gui/src/app/root_stack/mod.rs
+++ b/lact-gui/src/app/root_stack/mod.rs
@@ -12,14 +12,14 @@ use thermals_page::ThermalsPage;
 use traits::BoxExt;
 use traits::WidgetExt;
 
-#[cfg(feature = "libadwaita")]
+#[cfg(feature = "adw")]
 use adw::prelude::ActionRowExt;
 
 #[derive(Debug, Clone)]
 pub struct RootStack {
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     pub container: adw::ViewStack,
-    #[cfg(not(feature = "libadwaita"))]
+    #[cfg(not(feature = "adw"))]
     pub container: Stack,
 
     pub info_page: InformationPage,
@@ -33,12 +33,12 @@ impl RootStack {
         system_info: SystemInfo,
         embedded_daemon: bool,
     ) -> Self {
-        #[cfg(feature = "libadwaita")]
+        #[cfg(feature = "adw")]
         let container = adw::ViewStack::builder()
             .vexpand(true)
             .hexpand(true)
             .build();
-        #[cfg(not(feature = "libadwaita"))]
+        #[cfg(not(feature = "adw"))]
         let container = Stack::builder().vexpand(true).hexpand(true).build();
 
         let info_page = InformationPage::new();
@@ -46,7 +46,7 @@ impl RootStack {
         let thermals_page = ThermalsPage::new(root_win);
         let software_page = software_page(system_info, embedded_daemon);
 
-        #[cfg(feature = "libadwaita")]
+        #[cfg(feature = "adw")]
         {
             container.add_titled_with_icon(
                 &info_page.container,
@@ -74,7 +74,7 @@ impl RootStack {
             );
         }
 
-        #[cfg(not(feature = "libadwaita"))]
+        #[cfg(not(feature = "adw"))]
         {
             container.add_titled(&info_page.container, Some("info_page"), "Information");
             container.add_titled(&oc_page.container, Some("oc_page"), "Overclock");
@@ -93,10 +93,10 @@ impl RootStack {
 
 #[derive(Debug, Clone)]
 pub struct LabelRow {
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     pub container: adw::ActionRow,
 
-    #[cfg(not(feature = "libadwaita"))]
+    #[cfg(not(feature = "adw"))]
     pub container: ListBoxRow,
 
     content_label: Label,
@@ -131,7 +131,7 @@ impl LabelRow {
     }
 }
 
-#[cfg(feature = "libadwaita")]
+#[cfg(feature = "adw")]
 pub fn list_clamp(child: &impl IsA<Widget>) -> adw::Clamp {
     adw::Clamp::builder()
         .maximum_size(600)
@@ -144,7 +144,7 @@ pub fn list_clamp(child: &impl IsA<Widget>) -> adw::Clamp {
         .build()
 }
 
-#[cfg(not(feature = "libadwaita"))]
+#[cfg(not(feature = "adw"))]
 pub fn list_clamp(child: &impl IsA<Widget>) -> Box {
     let container = Box::builder()
         .margin_top(24)
@@ -159,7 +159,7 @@ pub fn list_clamp(child: &impl IsA<Widget>) -> Box {
     container
 }
 
-#[cfg(feature = "libadwaita")]
+#[cfg(feature = "adw")]
 pub fn action_row(
     title: &str,
     subtitle: Option<&str>,
@@ -184,7 +184,7 @@ pub fn action_row(
     ar
 }
 
-#[cfg(not(feature = "libadwaita"))]
+#[cfg(not(feature = "adw"))]
 pub fn action_row(
     title: &str,
     subtitle: Option<&str>,
diff --git a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
index 7c1f176c..44909bea 100644
--- a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
@@ -18,10 +18,10 @@ pub struct ClocksFrame {
     max_values_box: Box,
     heading_listbox: ListBox,
 
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     advanced_switch_row: adw::SwitchRow,
 
-    #[cfg(not(feature = "libadwaita"))]
+    #[cfg(not(feature = "adw"))]
     advanced_switch_row: Switch,
 
     min_values_box: Box,
@@ -53,7 +53,7 @@ impl ClocksFrame {
 
         heading_listbox.append(&warning_row);
 
-        #[cfg(feature = "libadwaita")]
+        #[cfg(feature = "adw")]
         let advanced_switch_row = {
             let row = adw::SwitchRow::builder()
                 .title("Advanced mode")
@@ -63,7 +63,7 @@ impl ClocksFrame {
             row
         };
 
-        #[cfg(not(feature = "libadwaita"))]
+        #[cfg(not(feature = "adw"))]
         let advanced_switch_row = {
             let switch = Switch::builder().active(false).build();
             let row = action_row("Advanced mode", None, &[&switch], None);
@@ -390,15 +390,15 @@ fn extract_value_and_range(
 fn oc_adjustment(title: &'static str, listbox: &ListBox) -> (Adjustment, Rc<AtomicBool>) {
     let adjustment = Adjustment::new(0.0, 0.0, 0.0, 1.0, 10.0, 0.0);
 
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     let value_selector = adw::SpinRow::builder()
         .title(title)
         .adjustment(&adjustment)
         .build();
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     listbox.append(&value_selector);
 
-    #[cfg(not(feature = "libadwaita"))]
+    #[cfg(not(feature = "adw"))]
     let value_selector = {
         let spin_btn = SpinButton::builder()
             .adjustment(&adjustment)
diff --git a/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs b/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs
index 2bbadee6..acffc5a8 100644
--- a/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs
+++ b/lact-gui/src/app/root_stack/oc_page/gpu_stats_section.rs
@@ -84,12 +84,9 @@ mod imp {
 
     #[derive(CompositeTemplate, Default, Properties)]
     #[properties(wrapper_type = super::GpuStatsSection)]
+    #[cfg_attr(feature = "adw", template(file = "ui/oc_page/gpu_stats_section.blp"))]
     #[cfg_attr(
-        feature = "libadwaita",
-        template(file = "ui/oc_page/gpu_stats_section.blp")
-    )]
-    #[cfg_attr(
-        not(feature = "libadwaita"),
+        not(feature = "adw"),
         template(file = "ui/oc_page/gpu_stats_section_gtk.blp")
     )]
     pub struct GpuStatsSection {
diff --git a/lact-gui/src/app/root_stack/oc_page/mod.rs b/lact-gui/src/app/root_stack/oc_page/mod.rs
index a9c314b0..9c3f4728 100644
--- a/lact-gui/src/app/root_stack/oc_page/mod.rs
+++ b/lact-gui/src/app/root_stack/oc_page/mod.rs
@@ -50,7 +50,7 @@ impl OcPage {
             let (warning_frame, button) = oc_warning_frame();
             enable_overclocking_button = Some(button);
 
-            #[cfg(feature = "libadwaita")]
+            #[cfg(feature = "adw")]
             vbox.append(
                 &adw::Bin::builder()
                     .css_classes(["card"])
@@ -58,7 +58,7 @@ impl OcPage {
                     .build(),
             );
 
-            #[cfg(not(feature = "libadwaita"))]
+            #[cfg(not(feature = "adw"))]
             vbox.append(&Frame::builder().child(&warning_frame).build());
         }
 
diff --git a/lact-gui/src/app/root_stack/oc_page/performance_frame.rs b/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
index 5dbf54c9..f8dfd075 100644
--- a/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/performance_frame.rs
@@ -7,23 +7,23 @@ use lact_client::schema::amdgpu_sysfs::gpu_handle::{
 };
 use std::{cell::RefCell, rc::Rc, str::FromStr};
 
-#[cfg(feature = "libadwaita")]
+#[cfg(feature = "adw")]
 use adw::prelude::{ActionRowExt, ComboRowExt};
 
 #[derive(Debug, Clone)]
 pub struct PerformanceFrame {
     pub container: PageSection,
 
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     level_row: adw::ComboRow,
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     mode_row: adw::ComboRow,
 
-    #[cfg(not(feature = "libadwaita"))]
+    #[cfg(not(feature = "adw"))]
     level_row: DropDown,
-    #[cfg(not(feature = "libadwaita"))]
+    #[cfg(not(feature = "adw"))]
     level_subtitle: Label,
-    #[cfg(not(feature = "libadwaita"))]
+    #[cfg(not(feature = "adw"))]
     mode_row: DropDown,
 
     modes_table: Rc<RefCell<Option<PowerProfileModesTable>>>,
@@ -42,7 +42,7 @@ impl PerformanceFrame {
             .into_iter()
             .collect();
 
-        #[cfg(feature = "libadwaita")]
+        #[cfg(feature = "adw")]
         let level_row = {
             let row = adw::ComboRow::builder()
                 .model(&levels_model)
@@ -55,9 +55,9 @@ impl PerformanceFrame {
             row
         };
 
-        #[cfg(not(feature = "libadwaita"))]
+        #[cfg(not(feature = "adw"))]
         let level_subtitle;
-        #[cfg(not(feature = "libadwaita"))]
+        #[cfg(not(feature = "adw"))]
         let level_row = {
             let dropdown = DropDown::builder()
                 .model(&levels_model)
@@ -82,7 +82,7 @@ impl PerformanceFrame {
 
         let filler_model: StringList = [""].into_iter().collect();
 
-        #[cfg(feature = "libadwaita")]
+        #[cfg(feature = "adw")]
         let mode_row = {
             let row = adw::ComboRow::builder()
                 .model(&filler_model)
@@ -95,7 +95,7 @@ impl PerformanceFrame {
             row
         };
 
-        #[cfg(not(feature = "libadwaita"))]
+        #[cfg(not(feature = "adw"))]
         let mode_row = {
             let dropdown = DropDown::builder()
                 .model(&filler_model)
@@ -117,7 +117,7 @@ impl PerformanceFrame {
         let frame = Self {
             container,
             level_row,
-            #[cfg(not(feature = "libadwaita"))]
+            #[cfg(not(feature = "adw"))]
             level_subtitle,
             mode_row,
             modes_table: Rc::new(RefCell::new(None)),
@@ -219,10 +219,10 @@ impl PerformanceFrame {
             _ => unreachable!(),
         };
 
-        #[cfg(feature = "libadwaita")]
+        #[cfg(feature = "adw")]
         self.level_row.set_subtitle(subtitle);
 
-        #[cfg(not(feature = "libadwaita"))]
+        #[cfg(not(feature = "adw"))]
         self.level_subtitle.set_text(subtitle);
 
         self.mode_row.set_sensitive(enable_mode_control);
diff --git a/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs b/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs
index c87d2bd2..184ec792 100644
--- a/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs
+++ b/lact-gui/src/app/root_stack/oc_page/power_cap_section.rs
@@ -43,12 +43,9 @@ mod imp {
 
     #[derive(CompositeTemplate, Default, Properties)]
     #[properties(wrapper_type = super::PowerCapSection)]
+    #[cfg_attr(feature = "adw", template(file = "ui/oc_page/power_cap_section.blp"))]
     #[cfg_attr(
-        feature = "libadwaita",
-        template(file = "ui/oc_page/power_cap_section.blp")
-    )]
-    #[cfg_attr(
-        not(feature = "libadwaita"),
+        not(feature = "adw"),
         template(file = "ui/oc_page/power_cap_section_gtk.blp")
     )]
     pub struct PowerCapSection {
diff --git a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs
index a66e1675..0aca67a4 100644
--- a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame/mod.rs
@@ -11,10 +11,10 @@ use std::rc::Rc;
 
 #[derive(Debug, Clone)]
 pub struct FanCurveFrame {
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     pub container: Box,
 
-    #[cfg(not(feature = "libadwaita"))]
+    #[cfg(not(feature = "adw"))]
     pub container: Frame,
 
     curve_container: ScrolledWindow,
@@ -82,10 +82,10 @@ impl FanCurveFrame {
         let points = Rc::new(RefCell::new(Vec::new()));
 
         let curve_frame = Self {
-            #[cfg(feature = "libadwaita")]
+            #[cfg(feature = "adw")]
             container: root_box,
 
-            #[cfg(not(feature = "libadwaita"))]
+            #[cfg(not(feature = "adw"))]
             container: Frame::builder()
                 .css_classes(["view"])
                 .child(&root_box)
diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs
index f5e4c006..0e61c0ef 100644
--- a/lact-gui/src/app/root_stack/thermals_page/mod.rs
+++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs
@@ -8,7 +8,7 @@ use gtk::prelude::*;
 use gtk::*;
 use lact_client::schema::{default_fan_curve, DeviceStats, FanControlMode, FanCurveMap};
 
-#[cfg(feature = "libadwaita")]
+#[cfg(feature = "adw")]
 use adw::prelude::*;
 
 #[derive(Debug)]
@@ -83,13 +83,13 @@ impl ThermalsPage {
 
         fan_control_mode_stack.add_titled(&fan_curve_frame.container, Some("curve"), "Curve");
 
-        #[cfg(feature = "libadwaita")]
+        #[cfg(feature = "adw")]
         let static_speed_container = adw::Bin::builder()
             .css_classes(["card"])
             .valign(Align::Start)
             .child(&fan_static_speed_frame)
             .build();
-        #[cfg(not(feature = "libadwaita"))]
+        #[cfg(not(feature = "adw"))]
         let static_speed_container = Frame::builder()
             .css_classes(["view"])
             .valign(Align::Start)
diff --git a/lact-gui/src/lib.rs b/lact-gui/src/lib.rs
index 8546b036..0fd52336 100644
--- a/lact-gui/src/lib.rs
+++ b/lact-gui/src/lib.rs
@@ -17,12 +17,12 @@ pub fn run(args: GuiArgs) -> anyhow::Result<()> {
         .context("Invalid log level")?;
     tracing_subscriber::fmt().with_env_filter(env_filter).init();
 
-    #[cfg(feature = "libadwaita")]
+    #[cfg(feature = "adw")]
     if let Err(err) = adw::init() {
         return Err(anyhow!("Cannot initialize Libadwaita: {err}"));
     }
 
-    #[cfg(not(feature = "libadwaita"))]
+    #[cfg(not(feature = "adw"))]
     if let Err(err) = gtk::init() {
         return Err(anyhow!("Cannot initialize GTK: {err}"));
     }
diff --git a/pkg/recipes/lact-libadwaita/recipe.yml b/pkg/recipes/lact-adw/recipe.yml
similarity index 89%
rename from pkg/recipes/lact-libadwaita/recipe.yml
rename to pkg/recipes/lact-adw/recipe.yml
index d272f60a..e16f7b83 100644
--- a/pkg/recipes/lact-libadwaita/recipe.yml
+++ b/pkg/recipes/lact-adw/recipe.yml
@@ -1,5 +1,5 @@
 metadata:
-  name: lact-libadwaita
+  name: lact-adw
   description: AMDGPU control utility (libadwaita GUI)
   arch: x86_64
   license: MIT
@@ -26,7 +26,7 @@ configure:
     - cmd: sh /tmp/install_rust.sh -y --default-toolchain stable
 build:
   steps:
-    - cmd: bash -c "source $HOME/.cargo/env && make build-libadwaita-release"
+    - cmd: bash -c "source $HOME/.cargo/env && make build-adw-release"
 install:
   steps:
     - cmd: bash -c "cd $PKGER_BLD_DIR && DESTDIR=$PKGER_OUT_DIR PREFIX=/usr make install"

From ddcedda706b5f3ccbb7fdc683cb1656157aca5ec Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 2 Dec 2023 12:52:37 +0100
Subject: [PATCH 46/54] feat: add about dialog

---
 lact-gui/src/app/headerbar.rs | 56 ++++++++++++++++++++++++++++++++++-
 lact-gui/src/app/mod.rs       |  2 +-
 2 files changed, 56 insertions(+), 2 deletions(-)

diff --git a/lact-gui/src/app/headerbar.rs b/lact-gui/src/app/headerbar.rs
index 64d21099..f1735622 100644
--- a/lact-gui/src/app/headerbar.rs
+++ b/lact-gui/src/app/headerbar.rs
@@ -1,4 +1,9 @@
 use super::{apply_box::ApplyBox, gpu_selector::GpuSelector};
+use gtk::{
+    gio::ActionEntry,
+    glib::{self, clone, IsA},
+    prelude::{ActionMapExtManual, GtkWindowExt},
+};
 
 #[derive(Debug, Clone)]
 pub struct Headerbar {
@@ -13,7 +18,7 @@ pub struct Headerbar {
 }
 
 impl Headerbar {
-    pub fn new() -> Self {
+    pub fn new(app: &impl IsA<gtk::Application>, root_win: &gtk::ApplicationWindow) -> Self {
         #[cfg(feature = "adw")]
         let container = adw::HeaderBar::builder().show_title(false).build();
 
@@ -25,7 +30,56 @@ impl Headerbar {
         let gpu_selector = GpuSelector::new();
         let apply_box = ApplyBox::new();
 
+        #[cfg(not(feature = "adw"))]
+        let about_dialog = gtk::AboutDialog::builder()
+            .hide_on_close(true)
+            .modal(true)
+            .transient_for(root_win)
+            .program_name("LACT")
+            .icon_name("io.github.lact-linux")
+            .version(std::env!("CARGO_PKG_VERSION"))
+            .license_type(gtk::License::MitX11)
+            .copyright("The LACT Developers")
+            .authors(
+                std::env!("CARGO_PKG_AUTHORS")
+                    .split(':')
+                    .collect::<Vec<&str>>(),
+            )
+            .build();
+
+        #[cfg(feature = "adw")]
+        let about_dialog = adw::AboutWindow::builder()
+            .hide_on_close(true)
+            .modal(true)
+            .transient_for(root_win)
+            .application_name("LACT")
+            .application_icon("io.github.lact-linux")
+            .version(std::env!("CARGO_PKG_VERSION"))
+            .license_type(gtk::License::MitX11)
+            .copyright("The LACT Developers")
+            .developers(
+                std::env!("CARGO_PKG_AUTHORS")
+                    .split(':')
+                    .collect::<Vec<&str>>(),
+            )
+            .build();
+
+        let menu = gtk::gio::Menu::new();
+        menu.append_item(&gtk::gio::MenuItem::new(Some("About"), Some("win.about")));
+
+        root_win.add_action_entries([ActionEntry::builder("about")
+            .activate(clone!(@weak about_dialog => move |_, _, _| {
+                about_dialog.present();
+            }))
+            .build()]);
+
         container.pack_start(&gpu_selector.dropdown);
+        container.pack_end(
+            &gtk::MenuButton::builder()
+                .icon_name("open-menu-symbolic")
+                .menu_model(&menu)
+                .build(),
+        );
         container.pack_end(&apply_box.container);
 
         Self {
diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index 7fda96e2..2f9db1c4 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -90,7 +90,7 @@ impl App {
 
         let root_stack = RootStack::new(&window, system_info, daemon_client.embedded);
 
-        let headerbar = Headerbar::new();
+        let headerbar = Headerbar::new(&application, &window.clone().upcast::<ApplicationWindow>());
 
         #[cfg(feature = "adw")]
         {

From e471498feb887f6170e0d6958e71b4b18ac6732b Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 2 Dec 2023 12:55:03 +0100
Subject: [PATCH 47/54] feat: add ctrl q shortcut to quit

---
 lact-gui/src/app/mod.rs | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index 2f9db1c4..7c6f3427 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -13,6 +13,7 @@ use crate::{info_dialog, APP_ID, GUI_VERSION};
 use anyhow::{anyhow, Context};
 use glib::clone;
 use gpu_selector::GpuSelector;
+use gtk::gio::ActionEntry;
 use gtk::glib::{timeout_future, ControlFlow};
 use gtk::{gio::ApplicationFlags, prelude::*, *};
 use lact_client::schema::request::{ConfirmCommand, SetClocksCommand};
@@ -78,6 +79,13 @@ impl App {
             .icon_name(APP_ID)
             .build();
 
+        window.add_action_entries([ActionEntry::builder("quit")
+            .activate(clone!(@weak application => move |_, _, _| {
+                application.quit();
+            }))
+            .build()]);
+        application.set_accels_for_action("win.quit", &["<Primary>Q"]);
+
         let system_info_buf = daemon_client
             .get_system_info()
             .expect("Could not fetch system info");

From 336cae5f3a446970f7d63ef7761c4d7ebd714d1b Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 2 Dec 2023 12:58:46 +0100
Subject: [PATCH 48/54] feat: add website and issue tracker urls to about
 dialog

---
 lact-gui/src/app/headerbar.rs | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/lact-gui/src/app/headerbar.rs b/lact-gui/src/app/headerbar.rs
index f1735622..08323670 100644
--- a/lact-gui/src/app/headerbar.rs
+++ b/lact-gui/src/app/headerbar.rs
@@ -62,10 +62,15 @@ impl Headerbar {
                     .split(':')
                     .collect::<Vec<&str>>(),
             )
+            .website("https://github.com/ilya-zlobintsev/LACT")
+            .issue_url("https://github.com/ilya-zlobintsev/LACT/issues")
             .build();
 
         let menu = gtk::gio::Menu::new();
-        menu.append_item(&gtk::gio::MenuItem::new(Some("About"), Some("win.about")));
+        menu.append_item(&gtk::gio::MenuItem::new(
+            Some("About LACT"),
+            Some("win.about"),
+        ));
 
         root_win.add_action_entries([ActionEntry::builder("about")
             .activate(clone!(@weak about_dialog => move |_, _, _| {

From b5f859950a7ef32feeca9eeb3c1d3b048f999af9 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 9 Dec 2023 18:18:18 +0000
Subject: [PATCH 49/54] feat: move all clockspeed and voltage controls to the
 same listbox

---
 .../app/root_stack/oc_page/clocks_frame.rs    | 114 ++++++------------
 1 file changed, 38 insertions(+), 76 deletions(-)

diff --git a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
index 44909bea..45dafbbc 100644
--- a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
@@ -15,8 +15,7 @@ const VOLTAGE_OFFSET_RANGE: f64 = 250.0;
 #[derive(Debug, Clone)]
 pub struct ClocksFrame {
     pub container: PageSection,
-    max_values_box: Box,
-    heading_listbox: ListBox,
+    listbox: ListBox,
 
     #[cfg(feature = "adw")]
     advanced_switch_row: adw::SwitchRow,
@@ -24,14 +23,13 @@ pub struct ClocksFrame {
     #[cfg(not(feature = "adw"))]
     advanced_switch_row: Switch,
 
-    min_values_box: Box,
-    min_sclk_adjustment: (Adjustment, Rc<AtomicBool>),
-    min_mclk_adjustment: (Adjustment, Rc<AtomicBool>),
-    min_voltage_adjustment: (Adjustment, Rc<AtomicBool>),
-    max_sclk_adjustment: (Adjustment, Rc<AtomicBool>),
-    max_mclk_adjustment: (Adjustment, Rc<AtomicBool>),
-    max_voltage_adjustment: (Adjustment, Rc<AtomicBool>),
-    voltage_offset_adjustment: (Adjustment, Rc<AtomicBool>),
+    min_sclk_adjustment: (Adjustment, Rc<AtomicBool>, Widget),
+    min_mclk_adjustment: (Adjustment, Rc<AtomicBool>, Widget),
+    min_voltage_adjustment: (Adjustment, Rc<AtomicBool>, Widget),
+    max_sclk_adjustment: (Adjustment, Rc<AtomicBool>, Widget),
+    max_mclk_adjustment: (Adjustment, Rc<AtomicBool>, Widget),
+    max_voltage_adjustment: (Adjustment, Rc<AtomicBool>, Widget),
+    voltage_offset_adjustment: (Adjustment, Rc<AtomicBool>, Widget),
     reset_button: Button,
     clocks_data_unavailable_label: Label,
 }
@@ -40,7 +38,7 @@ impl ClocksFrame {
     pub fn new() -> Self {
         let container = PageSection::new("Clockspeed and voltage");
 
-        let heading_listbox = ListBox::builder()
+        let listbox = ListBox::builder()
             .css_classes(["boxed-list"])
             .selection_mode(SelectionMode::None)
             .build();
@@ -51,7 +49,7 @@ impl ClocksFrame {
             &Vec::<&Widget>::new(),
             Some(&vec!["warning"]));
 
-        heading_listbox.append(&warning_row);
+        listbox.append(&warning_row);
 
         #[cfg(feature = "adw")]
         let advanced_switch_row = {
@@ -59,7 +57,7 @@ impl ClocksFrame {
                 .title("Advanced mode")
                 .active(false)
                 .build();
-            heading_listbox.append(&row);
+            listbox.append(&row);
             row
         };
 
@@ -68,57 +66,20 @@ impl ClocksFrame {
             let switch = Switch::builder().active(false).build();
             let row = action_row("Advanced mode", None, &[&switch], None);
 
-            heading_listbox.append(&row);
+            listbox.append(&row);
             switch
         };
 
-        container.append(&heading_listbox);
+        container.append(&listbox);
 
-        let min_values_box = Box::builder()
-            .orientation(Orientation::Vertical)
-            .spacing(12)
-            .build();
-        min_values_box.append(
-            &Label::builder()
-                .label("Minimum Values")
-                .xalign(0.0)
-                .css_classes(["title-4"])
-                .build(),
-        );
-        let min_values_listbox = ListBox::builder()
-            .css_classes(["boxed-list"])
-            .selection_mode(SelectionMode::None)
-            .build();
-        min_values_box.append(&min_values_listbox);
-
-        let min_sclk_adjustment = oc_adjustment("Minimum GPU Clock (MHz)", &min_values_listbox);
-        let min_mclk_adjustment = oc_adjustment("Minimum VRAM Clock (MHz)", &min_values_listbox);
-        let min_voltage_adjustment = oc_adjustment("Minimum GPU voltage (mV)", &min_values_listbox);
-
-        container.append(&min_values_box);
+        let min_sclk_adjustment = oc_adjustment("Minimum GPU Clock (MHz)", &listbox);
+        let min_mclk_adjustment = oc_adjustment("Minimum VRAM Clock (MHz)", &listbox);
+        let min_voltage_adjustment = oc_adjustment("Minimum GPU voltage (mV)", &listbox);
 
-        let max_values_box = Box::builder()
-            .orientation(Orientation::Vertical)
-            .spacing(12)
-            .build();
-        max_values_box.append(
-            &Label::builder()
-                .label("Maximum Values")
-                .xalign(0.0)
-                .css_classes(["title-4"])
-                .build(),
-        );
-        let max_values_listbox = ListBox::builder()
-            .css_classes(["boxed-list"])
-            .selection_mode(SelectionMode::None)
-            .build();
-        max_values_box.append(&max_values_listbox);
-
-        let max_sclk_adjustment = oc_adjustment("Maximum GPU Clock (MHz)", &max_values_listbox);
-        let max_voltage_adjustment = oc_adjustment("Maximum GPU voltage (mV)", &max_values_listbox);
-        let max_mclk_adjustment = oc_adjustment("Maximum VRAM Clock (MHz)", &max_values_listbox);
-        let voltage_offset_adjustment =
-            oc_adjustment("GPU voltage offset (mV)", &max_values_listbox);
+        let max_sclk_adjustment = oc_adjustment("Maximum GPU Clock (MHz)", &listbox);
+        let max_voltage_adjustment = oc_adjustment("Maximum GPU voltage (mV)", &listbox);
+        let max_mclk_adjustment = oc_adjustment("Maximum VRAM Clock (MHz)", &listbox);
+        let voltage_offset_adjustment = oc_adjustment("GPU voltage offset (mV)", &listbox);
 
         let reset_button = Button::builder()
             .label("Reset")
@@ -141,7 +102,7 @@ impl ClocksFrame {
             None,
         );
 
-        max_values_listbox.append(&reset_row);
+        listbox.append(&reset_row);
 
         let clocks_data_unavailable_label = Label::builder()
             .label("No clocks data available")
@@ -149,13 +110,11 @@ impl ClocksFrame {
             .halign(Align::Start)
             .build();
 
-        container.append(&max_values_box);
         container.append(&clocks_data_unavailable_label);
 
         let frame = Self {
             container,
-            max_values_box,
-            heading_listbox,
+            listbox,
             advanced_switch_row,
             min_sclk_adjustment,
             min_mclk_adjustment,
@@ -166,7 +125,6 @@ impl ClocksFrame {
             reset_button,
             clocks_data_unavailable_label,
             voltage_offset_adjustment,
-            min_values_box,
         };
 
         frame.set_configuration_mode(false);
@@ -308,14 +266,12 @@ impl ClocksFrame {
     }
 
     pub fn show(&self) {
-        self.max_values_box.show();
-        self.heading_listbox.show();
+        self.listbox.show();
         self.clocks_data_unavailable_label.hide();
     }
 
     pub fn hide(&self) {
-        self.max_values_box.hide();
-        self.heading_listbox.hide();
+        self.listbox.hide();
         self.clocks_data_unavailable_label.show();
     }
 
@@ -339,7 +295,7 @@ impl ClocksFrame {
     }
 
     pub fn get_settings(&self) -> ClocksSettings {
-        if self.max_values_box.is_visible() {
+        if self.listbox.is_visible() {
             let min_core_clock = get_adjustment_value(&self.min_sclk_adjustment);
             let min_memory_clock = get_adjustment_value(&self.min_mclk_adjustment);
             let min_voltage = get_adjustment_value(&self.min_voltage_adjustment);
@@ -368,7 +324,9 @@ impl ClocksFrame {
     }
 
     fn set_configuration_mode(&self, advanced: bool) {
-        self.min_values_box.set_visible(advanced);
+        self.min_sclk_adjustment.2.set_visible(advanced);
+        self.min_mclk_adjustment.2.set_visible(advanced);
+        self.min_voltage_adjustment.2.set_visible(advanced);
     }
 }
 
@@ -387,7 +345,7 @@ fn extract_value_and_range(
     Some((value, min, max))
 }
 
-fn oc_adjustment(title: &'static str, listbox: &ListBox) -> (Adjustment, Rc<AtomicBool>) {
+fn oc_adjustment(title: &'static str, listbox: &ListBox) -> (Adjustment, Rc<AtomicBool>, Widget) {
     let adjustment = Adjustment::new(0.0, 0.0, 0.0, 1.0, 10.0, 0.0);
 
     #[cfg(feature = "adw")]
@@ -397,9 +355,11 @@ fn oc_adjustment(title: &'static str, listbox: &ListBox) -> (Adjustment, Rc<Atom
         .build();
     #[cfg(feature = "adw")]
     listbox.append(&value_selector);
+    #[cfg(feature = "adw")]
+    let widget = value_selector.clone();
 
     #[cfg(not(feature = "adw"))]
-    let value_selector = {
+    let (widget, value_selector) = {
         let spin_btn = SpinButton::builder()
             .adjustment(&adjustment)
             .valign(Align::Center)
@@ -408,7 +368,7 @@ fn oc_adjustment(title: &'static str, listbox: &ListBox) -> (Adjustment, Rc<Atom
 
         listbox.append(&row);
 
-        spin_btn
+        (row, spin_btn)
     };
 
     let changed = Rc::new(AtomicBool::new(false));
@@ -422,7 +382,7 @@ fn oc_adjustment(title: &'static str, listbox: &ListBox) -> (Adjustment, Rc<Atom
         }
     ));
 
-    (adjustment, changed)
+    (adjustment, changed, widget.upcast::<Widget>())
 }
 
 #[derive(Debug, Default)]
@@ -436,7 +396,9 @@ pub struct ClocksSettings {
     pub voltage_offset: Option<i32>,
 }
 
-fn get_adjustment_value((adjustment, changed): &(Adjustment, Rc<AtomicBool>)) -> Option<i32> {
+fn get_adjustment_value(
+    (adjustment, changed, _): &(Adjustment, Rc<AtomicBool>, Widget),
+) -> Option<i32> {
     let changed = changed.load(Ordering::SeqCst);
 
     if changed {
@@ -451,7 +413,7 @@ fn get_adjustment_value((adjustment, changed): &(Adjustment, Rc<AtomicBool>)) ->
     }
 }
 
-fn emit_changed(adjustment: &(Adjustment, Rc<AtomicBool>)) {
+fn emit_changed(adjustment: &(Adjustment, Rc<AtomicBool>, Widget)) {
     adjustment.0.emit_by_name::<()>("changed", &[]);
     adjustment.1.store(false, Ordering::SeqCst);
 }

From 0b345200c3da6d0fd47c3d67cbdf9cb9b643d7bb Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 9 Dec 2023 18:32:53 +0000
Subject: [PATCH 50/54] feat: use scale for clock/voltage adjustments instead
 of spinbutton

---
 .../app/root_stack/oc_page/clocks_frame.rs    | 29 ++++++-------------
 1 file changed, 9 insertions(+), 20 deletions(-)

diff --git a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
index 45dafbbc..d2bf3391 100644
--- a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
@@ -348,28 +348,17 @@ fn extract_value_and_range(
 fn oc_adjustment(title: &'static str, listbox: &ListBox) -> (Adjustment, Rc<AtomicBool>, Widget) {
     let adjustment = Adjustment::new(0.0, 0.0, 0.0, 1.0, 10.0, 0.0);
 
-    #[cfg(feature = "adw")]
-    let value_selector = adw::SpinRow::builder()
-        .title(title)
+    let value_selector = Scale::builder()
         .adjustment(&adjustment)
+        .orientation(Orientation::Horizontal)
+        .hexpand(true)
+        .valign(Align::Center)
+        .value_pos(PositionType::Right)
+        .draw_value(true)
         .build();
-    #[cfg(feature = "adw")]
-    listbox.append(&value_selector);
-    #[cfg(feature = "adw")]
-    let widget = value_selector.clone();
-
-    #[cfg(not(feature = "adw"))]
-    let (widget, value_selector) = {
-        let spin_btn = SpinButton::builder()
-            .adjustment(&adjustment)
-            .valign(Align::Center)
-            .build();
-        let row = action_row(title, None, &[&spin_btn], None);
-
-        listbox.append(&row);
+    let row = action_row(title, None, &[&value_selector], None);
 
-        (row, spin_btn)
-    };
+    listbox.append(&row);
 
     let changed = Rc::new(AtomicBool::new(false));
 
@@ -382,7 +371,7 @@ fn oc_adjustment(title: &'static str, listbox: &ListBox) -> (Adjustment, Rc<Atom
         }
     ));
 
-    (adjustment, changed, widget.upcast::<Widget>())
+    (adjustment, changed, row.upcast::<Widget>())
 }
 
 #[derive(Debug, Default)]

From 28a61fb6e3c20d55a6fea4f741110e06c6c35429 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 9 Dec 2023 18:37:04 +0000
Subject: [PATCH 51/54] feat: stack switcher in headerbar for plain gtk version

---
 lact-gui/src/app/mod.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs
index 7c6f3427..882d8744 100644
--- a/lact-gui/src/app/mod.rs
+++ b/lact-gui/src/app/mod.rs
@@ -119,14 +119,14 @@ impl App {
             let root_view = Box::builder().orientation(Orientation::Vertical).build();
             root_view.append(&root_stack.container);
             root_view.append(&Separator::new(Orientation::Horizontal));
-            root_view.append(
+            headerbar.container.set_title_widget(Some(
                 &StackSwitcher::builder()
                     .stack(&root_stack.container)
                     .halign(Align::Center)
                     .vexpand(false)
                     .hexpand(true)
                     .build(),
-            );
+            ));
             window.set_titlebar(Some(&headerbar.container));
             window.set_child(Some(&root_view));
         }

From ad3b1f9964cf6cfd5f3692e6d11784dba45daf0a Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 9 Dec 2023 18:47:22 +0000
Subject: [PATCH 52/54] fix: hide unavailable clock/voltage rows

---
 .../app/root_stack/oc_page/clocks_frame.rs    | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
index d2bf3391..bf9768c2 100644
--- a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
+++ b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs
@@ -324,9 +324,13 @@ impl ClocksFrame {
     }
 
     fn set_configuration_mode(&self, advanced: bool) {
-        self.min_sclk_adjustment.2.set_visible(advanced);
-        self.min_mclk_adjustment.2.set_visible(advanced);
-        self.min_voltage_adjustment.2.set_visible(advanced);
+        for (adj, _, w) in [
+            &self.min_sclk_adjustment,
+            &self.min_mclk_adjustment,
+            &self.min_voltage_adjustment,
+        ] {
+            w.set_visible(advanced && adj.upper() > 0.0)
+        }
     }
 }
 
@@ -366,10 +370,11 @@ fn oc_adjustment(title: &'static str, listbox: &ListBox) -> (Adjustment, Rc<Atom
         changed.store(true, Ordering::SeqCst);
     }));
 
-    adjustment.connect_changed(clone!(@strong value_selector => move |adjustment| {
-            value_selector.set_sensitive(adjustment.upper() == 0.0);
-        }
-    ));
+    adjustment.connect_changed(clone!(@strong row => move |adjustment| {
+        let active = adjustment.upper() > 0.0;
+        row.set_visible(active);
+        row.set_sensitive(active);
+    }));
 
     (adjustment, changed, row.upcast::<Widget>())
 }

From abc7c3b43780ef220af57eac02a1674c05b8fc7c Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sat, 9 Dec 2023 19:24:41 +0000
Subject: [PATCH 53/54] chore: remove duplicate recipe

---
 pkg/recipes/lact-adw/recipe.yml | 33 ---------------------------------
 1 file changed, 33 deletions(-)
 delete mode 100644 pkg/recipes/lact-adw/recipe.yml

diff --git a/pkg/recipes/lact-adw/recipe.yml b/pkg/recipes/lact-adw/recipe.yml
deleted file mode 100644
index e16f7b83..00000000
--- a/pkg/recipes/lact-adw/recipe.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-metadata:
-  name: lact-adw
-  description: AMDGPU control utility (libadwaita GUI)
-  arch: x86_64
-  license: MIT
-  version: 0.5.1
-  maintainer: ilya-zlobintsev
-  url: https://github.com/ilya-zlobintsev/lact
-  source:
-    - '../../..'
-  provides: [ lact ]
-  depends:
-    all: [ hwdata ]
-    fedora-39: [ gtk4, libdrm, libadwaita ]
-    arch: [ gtk4, libadwaita ]
-  build_depends:
-    all: [ curl, make, dbus, clang ]
-    fedora-39: [ gtk4-devel, gcc, libdrm-devel, blueprint-compiler, libadwaita-devel ]
-    arch: [ gtk4, blueprint-compiler, libadwaita ]
-  all_images: true
-env:
-  RUSTUP_URL: https://sh.rustup.rs
-configure:
-  steps:
-    - cmd: curl -o /tmp/install_rust.sh $RUSTUP_URL
-    - cmd: sh /tmp/install_rust.sh -y --default-toolchain stable
-build:
-  steps:
-    - cmd: bash -c "source $HOME/.cargo/env && make build-adw-release"
-install:
-  steps:
-    - cmd: bash -c "cd $PKGER_BLD_DIR && DESTDIR=$PKGER_OUT_DIR PREFIX=/usr make install"
-

From 400e4b157a9eafd51615c2e5e0730c06e1b1e6a6 Mon Sep 17 00:00:00 2001
From: Gabriele Musco <gabmus@disroot.org>
Date: Sun, 10 Dec 2023 12:22:39 +0000
Subject: [PATCH 54/54] fix: missing icons

---
 Makefile                                          | 1 +
 lact-gui/src/app/root_stack/mod.rs                | 4 ++--
 res/io.github.lact-linux.thermometer-symbolic.svg | 2 ++
 3 files changed, 5 insertions(+), 2 deletions(-)
 create mode 100644 res/io.github.lact-linux.thermometer-symbolic.svg

diff --git a/Makefile b/Makefile
index 14ead4a7..5cc844ea 100644
--- a/Makefile
+++ b/Makefile
@@ -17,6 +17,7 @@ install:
 	install -Dm644 res/io.github.lact-linux.desktop $(DESTDIR)$(PREFIX)/share/applications/io.github.lact-linux.desktop
 	install -Dm644 res/io.github.lact-linux.png $(DESTDIR)$(PREFIX)/share/pixmaps/io.github.lact-linux.png
 	install -Dm644 res/io.github.lact-linux.svg $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/io.github.lact-linux.svg
+	install -Dm644 res/io.github.lact-linux.thermometer-symbolic.svg $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/status/io.github.lact-linux.thermometer-symbolic.svg
 
 uninstall:
 	rm $(DESTDIR)$(PREFIX)/bin/lact
diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs
index 0c0333fe..f48999f3 100644
--- a/lact-gui/src/app/root_stack/mod.rs
+++ b/lact-gui/src/app/root_stack/mod.rs
@@ -52,7 +52,7 @@ impl RootStack {
                 &info_page.container,
                 Some("info_page"),
                 "Information",
-                "info-symbolic",
+                "preferences-system-details-symbolic",
             );
             container.add_titled_with_icon(
                 &oc_page.container,
@@ -64,7 +64,7 @@ impl RootStack {
                 &thermals_page.container,
                 Some("thermals_page"),
                 "Thermals",
-                "temperature-symbolic",
+                "io.github.lact-linux.thermometer-symbolic",
             );
             container.add_titled_with_icon(
                 &software_page,
diff --git a/res/io.github.lact-linux.thermometer-symbolic.svg b/res/io.github.lact-linux.thermometer-symbolic.svg
new file mode 100644
index 00000000..e978770d
--- /dev/null
+++ b/res/io.github.lact-linux.thermometer-symbolic.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="16px" viewBox="0 0 16 16" width="16px"><filter id="a" height="100%" width="100%" x="0%" y="0%"><feColorMatrix color-interpolation-filters="sRGB" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/></filter><mask id="b"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.5"/></g></mask><clipPath id="c"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="d"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.7"/></g></mask><clipPath id="e"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="f"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.35"/></g></mask><clipPath id="g"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><g mask="url(#b)"><g clip-path="url(#c)" transform="matrix(1 0 0 1 -340 -476)"><path d="m 550 182 c -0.351562 0.003906 -0.695312 0.101562 -1 0.28125 v 3.4375 c 0.304688 0.179688 0.648438 0.277344 1 0.28125 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 c -0.339844 0 -0.679688 0.058594 -1 0.175781 v 6.824219 h 4 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#d)"><g clip-path="url(#e)" transform="matrix(1 0 0 1 -340 -476)"><path d="m 569 182 v 4 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 v 7 h 3 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#f)"><g clip-path="url(#g)" transform="matrix(1 0 0 1 -340 -476)"><path d="m 573 182.269531 v 3.449219 c 0.613281 -0.355469 0.996094 -1.007812 1 -1.71875 c 0 -0.714844 -0.382812 -1.375 -1 -1.730469 z m 0 4.90625 v 6.824219 h 2 v -4 c 0 -1.269531 -0.800781 -2.402344 -2 -2.824219 z m 0 0"/></g></g><g fill="#222222"><path d="m 9 12 c 0 0.550781 -0.449219 1 -1 1 s -1 -0.449219 -1 -1 s 0.449219 -1 1 -1 s 1 0.449219 1 1 z m 0 0"/><path d="m 8 4 h 2 v 1 h -2 z m 0 0"/><path d="m 8 6 h 2 v 1 h -2 z m 0 0"/><path d="m 8 8 h 2 v 1 h -2 z m 0 0"/><path d="m 8 0 c -1.644531 0 -3 1.355469 -3 3 v 6.363281 c -0.621094 0.707031 -1 1.628907 -1 2.636719 c 0 2.199219 1.800781 4 4 4 s 4 -1.800781 4 -4 c 0 -1.007812 -0.378906 -1.929688 -1 -2.636719 v -6.363281 c 0 -1.644531 -1.355469 -3 -3 -3 z m 0 2 c 0.570312 0 1 0.429688 1 1 v 7.269531 c 0.617188 0.355469 1 1.015625 1 1.730469 c 0 1.105469 -0.894531 2 -2 2 s -2 -0.894531 -2 -2 c 0.003906 -0.710938 0.382812 -1.371094 1 -1.726562 v -7.273438 c 0 -0.570312 0.429688 -1 1 -1 z m 0 0"/></g></svg>
