mirror of
https://github.com/ilya-zlobintsev/LACT.git
synced 2025-02-25 18:55:26 -06:00
feat: use relm4 for the UI (only main parts) (#375)
* feat: use relm4 for vulkan window * feat: WIP relm4 for app * feat: applying settings * wip * feat: implement all the main actions * feat: avoid full reloads when they are not needed * fix: don't save vbios dump when the dialog was cancelled * feat: OC control * feat: use relm for the software page
This commit is contained in:
parent
30df3ee11b
commit
e411d155da
110
Cargo.lock
generated
110
Cargo.lock
generated
@ -920,6 +920,18 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d"
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"nanorand",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@ -978,6 +990,12 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
||||
|
||||
[[package]]
|
||||
name = "fragile"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
|
||||
|
||||
[[package]]
|
||||
name = "freetype-sys"
|
||||
version = "0.20.1"
|
||||
@ -997,6 +1015,7 @@ checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
@ -1078,10 +1097,13 @@ version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
@ -1161,8 +1183,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1621,6 +1645,8 @@ dependencies = [
|
||||
"plotters",
|
||||
"plotters-cairo",
|
||||
"pretty_assertions",
|
||||
"relm4",
|
||||
"relm4-components",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
@ -1835,6 +1861,15 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanorand"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.29.0"
|
||||
@ -2294,6 +2329,52 @@ version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||
|
||||
[[package]]
|
||||
name = "relm4"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf0363f92b6a7eefd985b47f27b7ae168dd2fd5cd4013a338c9b111c33744d1f"
|
||||
dependencies = [
|
||||
"flume",
|
||||
"fragile",
|
||||
"futures",
|
||||
"gtk4",
|
||||
"libadwaita",
|
||||
"once_cell",
|
||||
"relm4-css",
|
||||
"relm4-macros",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "relm4-components"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb3d67f2982131c5e6047af4278d8fe750266767e57b58bc15f2e11e190eef36"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"relm4",
|
||||
"tracker",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "relm4-css"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d3b924557df1cddc687b60b313c4b76620fdbf0e463afa4b29f67193ccf37f9"
|
||||
|
||||
[[package]]
|
||||
name = "relm4-macros"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc5885640821d60062497737dd42fd04248d13c7ecccee620caaa4b210fe9905"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rle-decode-fast"
|
||||
version = "1.0.3"
|
||||
@ -2518,6 +2599,15 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@ -2792,6 +2882,26 @@ dependencies = [
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracker"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce5c98457ff700aaeefcd4a4a492096e78a2af1dd8523c66e94a3adb0fdbd415"
|
||||
dependencies = [
|
||||
"tracker-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracker-macros"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc19eb2373ccf3d1999967c26c3d44534ff71ae5d8b9dacf78f4b13132229e48"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ttf-parser"
|
||||
version = "0.20.0"
|
||||
|
@ -100,7 +100,7 @@ impl DaemonClient {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn list_devices<'a>(&self) -> anyhow::Result<ResponseBuffer<Vec<DeviceListEntry<'a>>>> {
|
||||
pub fn list_devices(&self) -> anyhow::Result<ResponseBuffer<Vec<DeviceListEntry>>> {
|
||||
self.make_request(Request::ListDevices)
|
||||
}
|
||||
|
||||
|
@ -277,15 +277,18 @@ impl<'a> Handler {
|
||||
.context("No controller with such id")?)
|
||||
}
|
||||
|
||||
pub fn list_devices(&'a self) -> Vec<DeviceListEntry<'a>> {
|
||||
pub fn list_devices(&'a self) -> Vec<DeviceListEntry> {
|
||||
self.gpu_controllers
|
||||
.iter()
|
||||
.map(|(id, controller)| {
|
||||
let name = controller
|
||||
.pci_info
|
||||
.as_ref()
|
||||
.and_then(|pci_info| pci_info.device_pci_info.model.as_deref());
|
||||
DeviceListEntry { id, name }
|
||||
.and_then(|pci_info| pci_info.device_pci_info.model.clone());
|
||||
DeviceListEntry {
|
||||
id: id.to_owned(),
|
||||
name,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
@ -14,13 +14,15 @@ const PP_OVERDRIVE_MASK: u64 = 0x4000;
|
||||
pub const PP_FEATURE_MASK_PATH: &str = "/sys/module/amdgpu/parameters/ppfeaturemask";
|
||||
pub const MODULE_CONF_PATH: &str = "/etc/modprobe.d/99-amdgpu-overdrive.conf";
|
||||
|
||||
pub async fn info() -> anyhow::Result<SystemInfo<'static>> {
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
pub async fn info() -> anyhow::Result<SystemInfo> {
|
||||
let version = env!("CARGO_PKG_VERSION").to_owned();
|
||||
let profile = if cfg!(debug_assertions) {
|
||||
"debug"
|
||||
} else {
|
||||
"release"
|
||||
};
|
||||
}
|
||||
.to_owned();
|
||||
|
||||
let kernel_output = Command::new("uname")
|
||||
.arg("-r")
|
||||
.output()
|
||||
@ -42,7 +44,7 @@ pub async fn info() -> anyhow::Result<SystemInfo<'static>> {
|
||||
profile,
|
||||
kernel_version,
|
||||
amdgpu_overdrive_enabled,
|
||||
commit: Some(GIT_COMMIT),
|
||||
commit: Some(GIT_COMMIT.to_owned()),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -8,10 +8,12 @@ edition = "2021"
|
||||
default = ["gtk-tests"]
|
||||
gtk-tests = []
|
||||
bench = []
|
||||
adw = ["dep:adw", "relm4/libadwaita"]
|
||||
|
||||
[dependencies]
|
||||
lact-client = { path = "../lact-client" }
|
||||
lact-daemon = { path = "../lact-daemon", default-features = false }
|
||||
lact-schema = { path = "../lact-schema", features = ["args"] }
|
||||
|
||||
amdgpu-sysfs = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
@ -23,6 +25,8 @@ gtk = { version = "0.9", package = "gtk4", features = ["v4_6", "blueprint"] }
|
||||
adw = { package = "libadwaita", version = "0.7.0", features = [
|
||||
"v1_4",
|
||||
], optional = true }
|
||||
relm4 = "0.9.0"
|
||||
relm4-components = "0.9.0"
|
||||
|
||||
plotters = { version = "0.3.5", default-features = false, features = [
|
||||
"datetime",
|
||||
@ -38,7 +42,6 @@ itertools = "0.13.0"
|
||||
criterion = "0.5.1"
|
||||
pretty_assertions = "1.4.0"
|
||||
lact-gui = { path = ".", features = ["bench"] }
|
||||
lact-schema = { path = "../lact-schema", features = ["args"] }
|
||||
|
||||
[[bench]]
|
||||
name = "gui"
|
||||
|
@ -1,50 +1,62 @@
|
||||
use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
use gtk::prelude::{BoxExt, ButtonExt, OrientableExt, WidgetExt};
|
||||
use relm4::{ComponentParts, ComponentSender, SimpleComponent};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ApplyRevealer {
|
||||
pub container: Revealer,
|
||||
apply_button: Button,
|
||||
reset_button: Button,
|
||||
shown: bool,
|
||||
}
|
||||
|
||||
impl ApplyRevealer {
|
||||
pub fn new() -> Self {
|
||||
let container = Revealer::builder().transition_duration(150).build();
|
||||
let vbox = Box::new(Orientation::Horizontal, 5);
|
||||
#[derive(Debug)]
|
||||
pub enum ApplyRevealerMsg {
|
||||
Show,
|
||||
Hide,
|
||||
}
|
||||
|
||||
let apply_button = Button::builder().label("Apply").hexpand(true).build();
|
||||
let reset_button = Button::builder().label("Reset").build();
|
||||
#[relm4::component(pub)]
|
||||
impl SimpleComponent for ApplyRevealer {
|
||||
type Init = ();
|
||||
|
||||
vbox.append(&apply_button);
|
||||
vbox.append(&reset_button);
|
||||
type Input = ApplyRevealerMsg;
|
||||
type Output = super::AppMsg;
|
||||
|
||||
container.set_child(Some(&vbox));
|
||||
view! {
|
||||
gtk::Revealer {
|
||||
#[watch]
|
||||
set_reveal_child: model.shown,
|
||||
|
||||
Self {
|
||||
container,
|
||||
apply_button,
|
||||
reset_button,
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Horizontal,
|
||||
set_spacing: 5,
|
||||
|
||||
gtk::Button {
|
||||
set_label: "Apply",
|
||||
set_hexpand: true,
|
||||
connect_clicked[sender] => move |_| { sender.output(super::AppMsg::ApplyChanges).unwrap(); },
|
||||
},
|
||||
|
||||
gtk::Button {
|
||||
set_label: "Revert",
|
||||
connect_clicked[sender] => move |_| { sender.output(super::AppMsg::RevertChanges).unwrap(); },
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show(&self) {
|
||||
self.container.set_reveal_child(true);
|
||||
fn init(
|
||||
_init: Self::Init,
|
||||
root: Self::Root,
|
||||
sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
let model = Self { shown: false };
|
||||
|
||||
let widgets = view_output!();
|
||||
|
||||
ComponentParts { widgets, model }
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
|
||||
match msg {
|
||||
ApplyRevealerMsg::Show => self.shown = true,
|
||||
ApplyRevealerMsg::Hide => self.shown = false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
54
lact-gui/src/app/confirmation_dialog.rs
Normal file
54
lact-gui/src/app/confirmation_dialog.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use gtk::prelude::{DialogExt, GtkWindowExt, WidgetExt};
|
||||
use relm4::{ComponentParts, ComponentSender, SimpleComponent};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ConfirmationOptions {
|
||||
pub title: &'static str,
|
||||
pub message: String,
|
||||
pub buttons_type: gtk::ButtonsType,
|
||||
}
|
||||
|
||||
pub struct ConfirmationDialog {}
|
||||
|
||||
#[relm4::component(pub)]
|
||||
impl SimpleComponent for ConfirmationDialog {
|
||||
type Init = (ConfirmationOptions, gtk::ApplicationWindow);
|
||||
type Input = ();
|
||||
type Output = gtk::ResponseType;
|
||||
|
||||
view! {
|
||||
gtk::MessageDialog {
|
||||
set_transient_for: Some(&parent),
|
||||
set_title: Some(options.title),
|
||||
set_use_markup: true,
|
||||
|
||||
connect_response[sender] => move |diag, response| {
|
||||
sender.output(response).unwrap();
|
||||
diag.close();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_assignments)]
|
||||
fn init(
|
||||
(options, parent): Self::Init,
|
||||
mut root: Self::Root,
|
||||
sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
let model = Self {};
|
||||
|
||||
root = gtk::MessageDialog::new(
|
||||
Some(&parent),
|
||||
gtk::DialogFlags::MODAL,
|
||||
gtk::MessageType::Question,
|
||||
options.buttons_type,
|
||||
options.message,
|
||||
);
|
||||
|
||||
let widgets = view_output!();
|
||||
|
||||
root.show();
|
||||
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
}
|
@ -1,9 +1,96 @@
|
||||
use super::{AppMsg, DebugSnapshot, DisableOverdrive, DumpVBios, ResetConfig, ShowGraphsWindow};
|
||||
use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
use lact_client::schema::{DeviceListEntry, SystemInfo};
|
||||
use pango::EllipsizeMode;
|
||||
use lact_client::schema::DeviceListEntry;
|
||||
use relm4::{
|
||||
Component, ComponentController, ComponentParts, ComponentSender, Controller, SimpleComponent,
|
||||
};
|
||||
use relm4_components::simple_combo_box::SimpleComboBox;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Header {
|
||||
gpu_selector: Controller<SimpleComboBox<DeviceListEntry>>,
|
||||
}
|
||||
|
||||
#[relm4::component(pub)]
|
||||
impl SimpleComponent for Header {
|
||||
type Init = (Vec<DeviceListEntry>, gtk::Stack);
|
||||
type Input = ();
|
||||
type Output = AppMsg;
|
||||
|
||||
view! {
|
||||
gtk::HeaderBar {
|
||||
set_show_title_buttons: true,
|
||||
|
||||
#[wrap(Some)]
|
||||
set_title_widget = &StackSwitcher {
|
||||
set_stack: Some(&stack),
|
||||
},
|
||||
|
||||
#[local_ref]
|
||||
pack_start = gpu_selector -> ComboBoxText,
|
||||
|
||||
pack_end = >k::MenuButton {
|
||||
set_icon_name: "open-menu-symbolic",
|
||||
set_menu_model: Some(&app_menu),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menu! {
|
||||
app_menu: {
|
||||
section! {
|
||||
"Show historical charts" => ShowGraphsWindow,
|
||||
},
|
||||
section! {
|
||||
"Generate debug snapshot" => DebugSnapshot,
|
||||
"Dump VBIOS" => DumpVBios,
|
||||
} ,
|
||||
section! {
|
||||
"Disable overclocking support" => DisableOverdrive,
|
||||
"Reset all configuration" => ResetConfig,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init(
|
||||
(variants, stack): Self::Init,
|
||||
root: Self::Root,
|
||||
sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
let gpu_selector = SimpleComboBox::builder()
|
||||
.launch(SimpleComboBox {
|
||||
variants,
|
||||
active_index: Some(0),
|
||||
})
|
||||
.forward(sender.output_sender(), |_| AppMsg::ReloadData {
|
||||
full: true,
|
||||
});
|
||||
|
||||
// limits the length of gpu names in combobox
|
||||
for cell in gpu_selector.widget().cells() {
|
||||
cell.set_property("width-chars", 10);
|
||||
cell.set_property("ellipsize", pango::EllipsizeMode::End);
|
||||
}
|
||||
|
||||
let model = Self { gpu_selector };
|
||||
|
||||
let gpu_selector = model.gpu_selector.widget();
|
||||
let widgets = view_output!();
|
||||
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub fn selected_gpu_id(&self) -> Option<String> {
|
||||
self.gpu_selector
|
||||
.model()
|
||||
.get_active_elem()
|
||||
.map(|model| model.id.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/*#[derive(Clone)]
|
||||
pub struct Header {
|
||||
pub container: HeaderBar,
|
||||
gpu_selector: ComboBoxText,
|
||||
@ -80,4 +167,4 @@ impl Header {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
@ -13,6 +13,14 @@ impl InfoRow {
|
||||
.property("value", value)
|
||||
.build()
|
||||
}
|
||||
|
||||
pub fn new_selectable(name: &str, value: &str) -> Self {
|
||||
Object::builder()
|
||||
.property("name", name)
|
||||
.property("value", value)
|
||||
.property("selectable", true)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
mod imp {
|
||||
|
File diff suppressed because it is too large
Load Diff
39
lact-gui/src/app/msg.rs
Normal file
39
lact-gui/src/app/msg.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use super::confirmation_dialog::ConfirmationOptions;
|
||||
use lact_schema::DeviceStats;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AppMsg {
|
||||
Error(Rc<anyhow::Error>),
|
||||
ReloadData { full: bool },
|
||||
Stats(DeviceStats),
|
||||
ApplyChanges,
|
||||
RevertChanges,
|
||||
ResetClocks,
|
||||
ResetPmfw,
|
||||
ShowGraphsWindow,
|
||||
DumpVBios,
|
||||
DebugSnapshot,
|
||||
EnableOverdrive,
|
||||
DisableOverdrive,
|
||||
ResetConfig,
|
||||
AskConfirmation(ConfirmationOptions, Box<AppMsg>),
|
||||
}
|
||||
|
||||
impl AppMsg {
|
||||
pub fn ask_confirmation(
|
||||
inner: AppMsg,
|
||||
title: &'static str,
|
||||
message: impl Into<String>,
|
||||
buttons_type: gtk::ButtonsType,
|
||||
) -> Self {
|
||||
Self::AskConfirmation(
|
||||
ConfirmationOptions {
|
||||
title,
|
||||
message: message.into(),
|
||||
buttons_type,
|
||||
},
|
||||
Box::new(inner),
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
use gtk::{
|
||||
glib::GString,
|
||||
prelude::{EditableExt, GtkWindowExt, OrientableExt, WidgetExt},
|
||||
NoSelection,
|
||||
};
|
||||
use relm4::{
|
||||
typed_view::list::{RelmListItem, TypedListView},
|
||||
view, ComponentParts, ComponentSender, SimpleComponent,
|
||||
};
|
||||
|
||||
pub struct VulkanFeaturesWindow {
|
||||
features_view: TypedListView<VulkanFeature, NoSelection>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AppMsg {
|
||||
FilterChanged(GString),
|
||||
}
|
||||
|
||||
#[relm4::component(pub)]
|
||||
impl SimpleComponent for VulkanFeaturesWindow {
|
||||
type Init = (Vec<VulkanFeature>, String);
|
||||
|
||||
type Input = AppMsg;
|
||||
type Output = ();
|
||||
|
||||
view! {
|
||||
gtk::Window {
|
||||
set_title: Some(&title),
|
||||
set_default_width: 500,
|
||||
set_default_height: 700,
|
||||
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
|
||||
#[name = "search_entry"]
|
||||
gtk::SearchEntry {
|
||||
connect_search_changed[sender] => move |entry| {
|
||||
sender.input(AppMsg::FilterChanged(entry.text()));
|
||||
},
|
||||
|
||||
connect_stop_search[root] => move |_| {
|
||||
root.close();
|
||||
},
|
||||
},
|
||||
|
||||
gtk::ScrolledWindow {
|
||||
#[local_ref]
|
||||
features_list -> gtk::ListView {
|
||||
set_show_separators: true,
|
||||
},
|
||||
|
||||
set_vexpand: true,
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
add_controller = gtk::ShortcutController {
|
||||
set_scope: gtk::ShortcutScope::Global,
|
||||
|
||||
add_shortcut = gtk::Shortcut {
|
||||
set_trigger: Some(gtk::ShortcutTrigger::parse_string("Escape|<Ctrl>w").unwrap()),
|
||||
set_action: Some(gtk::ShortcutAction::parse_string("action(window.close)").unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init(
|
||||
(features, title): Self::Init,
|
||||
root: Self::Root,
|
||||
sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
let mut features_view: TypedListView<VulkanFeature, NoSelection> = TypedListView::new();
|
||||
features_view.extend_from_iter(features);
|
||||
|
||||
let mut model = VulkanFeaturesWindow { features_view };
|
||||
|
||||
let features_list = &model.features_view.view;
|
||||
|
||||
let widgets = view_output!();
|
||||
|
||||
model.features_view.add_filter({
|
||||
let search_entry = widgets.search_entry.clone();
|
||||
move |feature| {
|
||||
feature
|
||||
.name
|
||||
.to_lowercase()
|
||||
.contains(&search_entry.text().to_lowercase())
|
||||
}
|
||||
});
|
||||
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
|
||||
match msg {
|
||||
AppMsg::FilterChanged(filter) => {
|
||||
self.features_view.set_filter_status(0, false);
|
||||
if !filter.is_empty() {
|
||||
self.features_view.set_filter_status(0, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VulkanFeature {
|
||||
pub name: String,
|
||||
pub supported: bool,
|
||||
}
|
||||
|
||||
pub struct VulkanFeatureWidgets {
|
||||
label: gtk::Label,
|
||||
image: gtk::Image,
|
||||
}
|
||||
|
||||
impl RelmListItem for VulkanFeature {
|
||||
type Root = gtk::Box;
|
||||
type Widgets = VulkanFeatureWidgets;
|
||||
|
||||
fn setup(_: >k::ListItem) -> (Self::Root, Self::Widgets) {
|
||||
view! {
|
||||
root_box = gtk::Box {
|
||||
set_focus_on_click: true,
|
||||
set_hexpand: true,
|
||||
set_hexpand_set: true,
|
||||
set_margin_start: 20,
|
||||
set_margin_end: 20,
|
||||
set_margin_top: 10,
|
||||
set_margin_bottom: 10,
|
||||
|
||||
#[name = "label"]
|
||||
gtk::Label {
|
||||
set_halign: gtk::Align::Start,
|
||||
set_hexpand: true,
|
||||
set_selectable: true,
|
||||
},
|
||||
|
||||
#[name = "image"]
|
||||
gtk::Image {
|
||||
set_halign: gtk::Align::End,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let widgets = VulkanFeatureWidgets { label, image };
|
||||
(root_box, widgets)
|
||||
}
|
||||
|
||||
fn bind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) {
|
||||
widgets.label.set_label(&self.name);
|
||||
|
||||
let icon = match self.supported {
|
||||
true => "emblem-ok-symbolic",
|
||||
false => "action-unavailable-symbolic",
|
||||
};
|
||||
widgets.image.set_icon_name(Some(icon));
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
use gtk::glib::{self, Object};
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct VulkanFeature(ObjectSubclass<imp::VulkanFeature>);
|
||||
}
|
||||
|
||||
impl VulkanFeature {
|
||||
pub fn new(name: String, supported: bool) -> Self {
|
||||
Object::builder()
|
||||
.property("name", name)
|
||||
.property("supported", supported)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VulkanFeature {
|
||||
fn default() -> Self {
|
||||
Self::new(String::new(), false)
|
||||
}
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use gio::subclass::prelude::*;
|
||||
use gtk::{
|
||||
gio,
|
||||
glib::{self, Properties},
|
||||
prelude::*,
|
||||
};
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
#[derive(Debug, Default, Properties)]
|
||||
#[properties(wrapper_type = super::VulkanFeature)]
|
||||
pub struct VulkanFeature {
|
||||
#[property(set, get)]
|
||||
pub name: RefCell<String>,
|
||||
#[property(set, get)]
|
||||
pub supported: Cell<bool>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for VulkanFeature {
|
||||
const NAME: &'static str = "VulkanFeature";
|
||||
type Type = super::VulkanFeature;
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for VulkanFeature {}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
pub mod feature;
|
||||
mod row;
|
||||
|
||||
use glib::Object;
|
||||
use gtk::{gio, glib};
|
||||
|
||||
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()
|
||||
.property("title", title)
|
||||
.property("model", model)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use super::{feature::VulkanFeature, row::VulkanFeatureRow};
|
||||
use glib::Properties;
|
||||
use gtk::{
|
||||
gio,
|
||||
glib::{self, clone, subclass::InitializingObject},
|
||||
prelude::*,
|
||||
subclass::{
|
||||
prelude::*,
|
||||
widget::{CompositeTemplateClass, WidgetImpl},
|
||||
},
|
||||
CompositeTemplate, Expression, FilterListModel, PropertyExpression, SearchEntry,
|
||||
SignalListItemFactory, StringFilter, TemplateChild,
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[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>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for VulkanFeaturesWindow {
|
||||
const NAME: &'static str = "VulkanFeaturesWindow";
|
||||
type Type = super::VulkanFeaturesWindow;
|
||||
type ParentType = gtk::Window;
|
||||
|
||||
fn class_init(class: &mut Self::Class) {
|
||||
class.bind_template();
|
||||
}
|
||||
|
||||
fn instance_init(obj: &InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for VulkanFeaturesWindow {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let obj = self.obj();
|
||||
|
||||
obj.bind_property("model", &self.filter_model.get(), "model")
|
||||
.sync_create()
|
||||
.build();
|
||||
|
||||
let expression =
|
||||
PropertyExpression::new(VulkanFeature::static_type(), Expression::NONE, "name");
|
||||
self.search_filter.set_expression(Some(&expression));
|
||||
|
||||
self.search_entry.connect_search_changed(clone!(
|
||||
#[strong(rename_to = filter)]
|
||||
self.search_filter,
|
||||
move |entry| {
|
||||
if entry.text().is_empty() {
|
||||
filter.set_search(None);
|
||||
} else {
|
||||
filter.set_search(Some(entry.text().as_str()));
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
self.search_entry.connect_stop_search(clone!(
|
||||
#[weak(rename_to = win)]
|
||||
obj,
|
||||
move |_search| {
|
||||
win.close();
|
||||
}
|
||||
));
|
||||
|
||||
self.features_factory.connect_setup(|_, list_item| {
|
||||
let feature = VulkanFeature::default();
|
||||
let row = VulkanFeatureRow::new(feature);
|
||||
list_item.set_child(Some(&row));
|
||||
});
|
||||
|
||||
self.features_factory.connect_bind(|_, list_item| {
|
||||
let feature = list_item
|
||||
.item()
|
||||
.and_downcast::<VulkanFeature>()
|
||||
.expect("The item has to be a VulkanFeature");
|
||||
|
||||
let row = list_item
|
||||
.child()
|
||||
.and_downcast::<VulkanFeatureRow>()
|
||||
.expect("The child has to be a VulkanFeatureRow");
|
||||
row.set_feature(feature);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for VulkanFeaturesWindow {}
|
||||
impl WindowImpl for VulkanFeaturesWindow {}
|
||||
impl ApplicationWindowImpl for VulkanFeaturesWindow {}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
use super::feature::VulkanFeature;
|
||||
use glib::Object;
|
||||
use gtk::glib;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct VulkanFeatureRow(ObjectSubclass<imp::VulkanFeatureRow>)
|
||||
@extends gtk::Box, gtk::Widget,
|
||||
@implements gtk::Orientable, gtk::Accessible, gtk::Buildable;
|
||||
}
|
||||
|
||||
impl VulkanFeatureRow {
|
||||
pub fn new(feature: VulkanFeature) -> Self {
|
||||
Object::builder().property("feature", feature).build()
|
||||
}
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use crate::app::root_stack::info_page::vulkan_info::feature_window::feature::VulkanFeature;
|
||||
use glib::Properties;
|
||||
use gtk::{
|
||||
glib::{self, subclass::InitializingObject},
|
||||
prelude::*,
|
||||
subclass::{
|
||||
prelude::*,
|
||||
widget::{CompositeTemplateClass, WidgetImpl},
|
||||
},
|
||||
CompositeTemplate, Image, Label, TemplateChild,
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[derive(CompositeTemplate, Default, Properties)]
|
||||
#[properties(wrapper_type = super::VulkanFeatureRow)]
|
||||
#[template(file = "ui/vulkan_feature_row.blp")]
|
||||
pub struct VulkanFeatureRow {
|
||||
#[template_child]
|
||||
name_label: TemplateChild<Label>,
|
||||
#[template_child]
|
||||
available_image: TemplateChild<Image>,
|
||||
|
||||
#[property(get, set)]
|
||||
feature: RefCell<VulkanFeature>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for VulkanFeatureRow {
|
||||
const NAME: &'static str = "VulkanFeatureRow";
|
||||
type Type = super::VulkanFeatureRow;
|
||||
type ParentType = gtk::Box;
|
||||
|
||||
fn class_init(class: &mut Self::Class) {
|
||||
class.bind_template();
|
||||
}
|
||||
|
||||
fn instance_init(obj: &InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for VulkanFeatureRow {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
let obj = self.obj();
|
||||
|
||||
obj.bind_property("feature", &self.name_label.get(), "label")
|
||||
.transform_to(|_, feature: VulkanFeature| Some(feature.name()))
|
||||
.sync_create()
|
||||
.build();
|
||||
|
||||
obj.bind_property("feature", &self.available_image.get(), "icon-name")
|
||||
.transform_to(|_, feature: VulkanFeature| {
|
||||
if feature.supported() {
|
||||
Some("emblem-ok-symbolic")
|
||||
} else {
|
||||
Some("action-unavailable-symbolic")
|
||||
}
|
||||
})
|
||||
.sync_create()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for VulkanFeatureRow {}
|
||||
impl BoxImpl for VulkanFeatureRow {}
|
||||
}
|
@ -1,14 +1,15 @@
|
||||
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 feature_window::{VulkanFeature, VulkanFeaturesWindow};
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
use lact_client::schema::VulkanInfo;
|
||||
use relm4::{Component, ComponentController};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use tracing::trace;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -18,16 +19,16 @@ pub struct VulkanInfoFrame {
|
||||
version_label: Label,
|
||||
driver_name_label: Label,
|
||||
driver_version_label: Label,
|
||||
features_model: gio::ListStore,
|
||||
extensions_model: gio::ListStore,
|
||||
features: Rc<RefCell<Vec<VulkanFeature>>>,
|
||||
extensions: Rc<RefCell<Vec<VulkanFeature>>>,
|
||||
}
|
||||
|
||||
impl VulkanInfoFrame {
|
||||
pub fn new() -> Self {
|
||||
let container = Box::new(Orientation::Vertical, 0);
|
||||
|
||||
let features_model = gio::ListStore::new::<VulkanFeature>();
|
||||
let extensions_model = gio::ListStore::new::<VulkanFeature>();
|
||||
let features: Rc<RefCell<Vec<VulkanFeature>>> = Rc::default();
|
||||
let extensions: Rc<RefCell<Vec<VulkanFeature>>> = Rc::default();
|
||||
|
||||
let grid = values_grid();
|
||||
grid.set_margin_start(0);
|
||||
@ -41,9 +42,9 @@ impl VulkanInfoFrame {
|
||||
let show_features_button = Button::builder().label("Show").halign(Align::End).build();
|
||||
show_features_button.connect_clicked(clone!(
|
||||
#[strong]
|
||||
features_model,
|
||||
features,
|
||||
move |_| {
|
||||
show_features_window("Vulkan features", features_model.clone());
|
||||
show_features_window("Vulkan features", features.clone());
|
||||
}
|
||||
));
|
||||
values_row("Features:", &grid, &show_features_button, 4, 0);
|
||||
@ -51,9 +52,9 @@ impl VulkanInfoFrame {
|
||||
let show_extensions_button = Button::builder().label("Show").halign(Align::End).build();
|
||||
show_extensions_button.connect_clicked(clone!(
|
||||
#[strong]
|
||||
extensions_model,
|
||||
extensions,
|
||||
move |_| {
|
||||
show_features_window("Vulkan extensions", extensions_model.clone());
|
||||
show_features_window("Vulkan extensions", extensions.clone());
|
||||
}
|
||||
));
|
||||
values_row("Extensions:", &grid, &show_extensions_button, 5, 0);
|
||||
@ -66,8 +67,8 @@ impl VulkanInfoFrame {
|
||||
version_label,
|
||||
driver_name_label,
|
||||
driver_version_label,
|
||||
features_model,
|
||||
extensions_model,
|
||||
features,
|
||||
extensions,
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,21 +90,35 @@ impl VulkanInfoFrame {
|
||||
vulkan_info.driver.info.as_deref().unwrap_or_default(),
|
||||
));
|
||||
|
||||
self.features_model.remove_all();
|
||||
let mut features = self.features.borrow_mut();
|
||||
features.clear();
|
||||
|
||||
for (name, supported) in &vulkan_info.features {
|
||||
let feature = VulkanFeature::new(name.to_string(), *supported);
|
||||
self.features_model.append(&feature);
|
||||
let feature = VulkanFeature {
|
||||
name: name.to_string(),
|
||||
supported: *supported,
|
||||
};
|
||||
features.push(feature);
|
||||
}
|
||||
|
||||
self.extensions_model.remove_all();
|
||||
let mut extensions = self.extensions.borrow_mut();
|
||||
extensions.clear();
|
||||
for (name, supported) in &vulkan_info.extensions {
|
||||
let extension = VulkanFeature::new(name.to_string(), *supported);
|
||||
self.extensions_model.append(&extension);
|
||||
let extension = VulkanFeature {
|
||||
name: name.to_string(),
|
||||
supported: *supported,
|
||||
};
|
||||
extensions.push(extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn show_features_window(title: &str, model: gio::ListStore) {
|
||||
let window = VulkanFeaturesWindow::new(title, model.into());
|
||||
window.present();
|
||||
fn show_features_window(title: &str, values: Rc<RefCell<Vec<VulkanFeature>>>) {
|
||||
let features = values.borrow().iter().cloned().collect();
|
||||
|
||||
let mut window_controller = VulkanFeaturesWindow::builder()
|
||||
.launch((features, title.to_owned()))
|
||||
.detach();
|
||||
window_controller.detach_runtime();
|
||||
window_controller.widget().present();
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ mod software_page;
|
||||
mod thermals_page;
|
||||
|
||||
use gtk::{prelude::*, *};
|
||||
use relm4::{Component, ComponentController};
|
||||
|
||||
use self::software_page::SoftwarePage;
|
||||
use info_page::InformationPage;
|
||||
@ -41,8 +42,11 @@ impl RootStack {
|
||||
|
||||
container.add_titled(&thermals_page.container, Some("thermals_page"), "Thermals");
|
||||
|
||||
let software_page = SoftwarePage::new(system_info, embedded_daemon);
|
||||
container.add_titled(&software_page, Some("software_page"), "Software");
|
||||
let mut software_page = SoftwarePage::builder()
|
||||
.launch((system_info, embedded_daemon))
|
||||
.detach();
|
||||
container.add_titled(software_page.widget(), Some("software_page"), "Software");
|
||||
software_page.detach_runtime();
|
||||
|
||||
Self {
|
||||
container,
|
||||
|
@ -1,16 +1,39 @@
|
||||
use crate::GUI_VERSION;
|
||||
use gtk::glib::{self, Object};
|
||||
use crate::{app::info_row::InfoRow, GUI_VERSION};
|
||||
use gtk::prelude::*;
|
||||
use lact_client::schema::{SystemInfo, GIT_COMMIT};
|
||||
use relm4::{ComponentParts, ComponentSender, SimpleComponent};
|
||||
use std::fmt::Write;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct SoftwarePage(ObjectSubclass<imp::SoftwarePage>)
|
||||
@extends gtk::Box, gtk::Widget,
|
||||
@implements gtk::Orientable, gtk::Accessible, gtk::Buildable;
|
||||
}
|
||||
pub struct SoftwarePage {}
|
||||
|
||||
#[relm4::component(pub)]
|
||||
impl SimpleComponent for SoftwarePage {
|
||||
type Init = (SystemInfo, bool);
|
||||
type Input = ();
|
||||
type Output = ();
|
||||
|
||||
view! {
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_spacing: 10,
|
||||
set_margin_start: 5,
|
||||
set_margin_end: 5,
|
||||
set_margin_top: 5,
|
||||
set_margin_bottom: 5,
|
||||
|
||||
append = &InfoRow::new_selectable("LACT Daemon:", &daemon_version),
|
||||
append = &InfoRow::new_selectable("LACT GUI:", &gui_version),
|
||||
append = &InfoRow::new_selectable("Kernel Version:", &system_info.kernel_version),
|
||||
}
|
||||
}
|
||||
|
||||
fn init(
|
||||
(system_info, embedded): Self::Init,
|
||||
root: Self::Root,
|
||||
_sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
let model = Self {};
|
||||
|
||||
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");
|
||||
@ -26,62 +49,8 @@ impl SoftwarePage {
|
||||
};
|
||||
let gui_version = format!("{GUI_VERSION}-{gui_profile} (commit {GIT_COMMIT})");
|
||||
|
||||
Object::builder()
|
||||
.property("daemon-version", daemon_version)
|
||||
.property("gui-version", gui_version)
|
||||
.property("kernel-version", system_info.kernel_version)
|
||||
.build()
|
||||
let widgets = view_output!();
|
||||
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
}
|
||||
|
||||
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 {}
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
pub mod app;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use app::App;
|
||||
use lact_client::{schema::args::GuiArgs, DaemonClient};
|
||||
use app::AppModel;
|
||||
use lact_client::DaemonClient;
|
||||
use lact_schema::args::GuiArgs;
|
||||
use relm4::RelmApp;
|
||||
use std::os::unix::net::UnixStream;
|
||||
use tracing::{debug, error, info, metadata::LevelFilter};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
@ -22,9 +24,10 @@ pub fn run(args: GuiArgs) -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
let (connection, connection_err) = create_connection()?;
|
||||
let app = App::new(connection);
|
||||
|
||||
app.run(connection_err)
|
||||
let app = RelmApp::new(APP_ID).with_args(vec![]);
|
||||
app.run::<AppModel>((connection, connection_err));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_connection() -> anyhow::Result<(DaemonClient, Option<anyhow::Error>)> {
|
||||
|
@ -1,28 +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;
|
||||
selectable: true;
|
||||
}
|
||||
|
||||
$InfoRow {
|
||||
name: "LACT GUI:";
|
||||
value: bind template.gui_version;
|
||||
selectable: true;
|
||||
}
|
||||
|
||||
$InfoRow {
|
||||
name: "Kernel Version:";
|
||||
value: bind template.kernel_version;
|
||||
selectable: true;
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
using Gtk 4.0;
|
||||
|
||||
template $VulkanFeatureRow: Box {
|
||||
focus-on-click: false;
|
||||
hexpand: true;
|
||||
hexpand-set: true;
|
||||
margin-bottom: 10;
|
||||
margin-end: 20;
|
||||
margin-start: 20;
|
||||
margin-top: 10;
|
||||
|
||||
Label name_label {
|
||||
halign: start;
|
||||
hexpand: true;
|
||||
label: 'feature name';
|
||||
selectable: true;
|
||||
}
|
||||
|
||||
Image available_image {
|
||||
halign: end;
|
||||
icon-name: 'action-unavailable-symbolic';
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
using Gtk 4.0;
|
||||
|
||||
template $VulkanFeaturesWindow: Window {
|
||||
default-height: 700;
|
||||
default-width: 500;
|
||||
|
||||
Box {
|
||||
orientation: vertical;
|
||||
|
||||
SearchEntry search_entry {}
|
||||
|
||||
ScrolledWindow {
|
||||
vexpand: true;
|
||||
|
||||
ListView {
|
||||
factory: features_factory;
|
||||
model: selection_model;
|
||||
show-separators: true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShortcutController {
|
||||
scope: global;
|
||||
|
||||
Shortcut {
|
||||
trigger: "Escape|<Ctrl>w";
|
||||
action: "action(window.close)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SignalListItemFactory features_factory {}
|
||||
|
||||
NoSelection selection_model {
|
||||
model: filter_model;
|
||||
}
|
||||
|
||||
StringFilter search_filter {}
|
||||
|
||||
FilterListModel filter_model {
|
||||
filter: search_filter;
|
||||
incremental: true;
|
||||
}
|
@ -23,6 +23,7 @@ use serde_with::skip_serializing_none;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::{BTreeMap, HashMap},
|
||||
fmt,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
@ -58,18 +59,27 @@ pub fn default_fan_curve() -> FanCurveMap {
|
||||
pub struct Pong;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct SystemInfo<'a> {
|
||||
pub version: &'a str,
|
||||
pub commit: Option<&'a str>,
|
||||
pub profile: &'a str,
|
||||
pub struct SystemInfo {
|
||||
pub version: String,
|
||||
pub commit: Option<String>,
|
||||
pub profile: String,
|
||||
pub kernel_version: String,
|
||||
pub amdgpu_overdrive_enabled: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct DeviceListEntry<'a> {
|
||||
pub id: &'a str,
|
||||
pub name: Option<&'a str>,
|
||||
pub struct DeviceListEntry {
|
||||
pub id: String,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
impl fmt::Display for DeviceListEntry {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.name {
|
||||
Some(name) => name.fmt(f),
|
||||
None => self.id.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
|
Loading…
Reference in New Issue
Block a user