mirror of
https://github.com/ilya-zlobintsev/LACT.git
synced 2025-02-25 18:55:26 -06:00
UI design improvements (#106)
* improve spacing arounds elements on the OC page * improved oc page looks * Improve the looks of the information page * use sections on the thermals page * update screenshots in README
This commit is contained in:
@@ -6,7 +6,7 @@ This application allows you to control your AMD GPU on a Linux system.
|
||||
|
||||
| GPU info | Overclocking | Fan control |
|
||||
|----------------------------------------------|----------------------------------------------|---------------------------------------------|
|
||||
|||
|
||||
||||
|
||||
|
||||
Current features:
|
||||
|
||||
|
||||
@@ -5,9 +5,11 @@ use gtk::*;
|
||||
use lact_client::schema::{DeviceInfo, DeviceStats};
|
||||
use vulkan_info::VulkanInfoFrame;
|
||||
|
||||
use super::{label_row, section_box, values_grid};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InformationPage {
|
||||
pub container: Grid,
|
||||
pub container: Box,
|
||||
gpu_name_label: Label,
|
||||
gpu_manufacturer_label: Label,
|
||||
vbios_version_label: Label,
|
||||
@@ -19,119 +21,32 @@ pub struct InformationPage {
|
||||
|
||||
impl InformationPage {
|
||||
pub fn new() -> Self {
|
||||
let container = Grid::new();
|
||||
let container = Box::new(Orientation::Vertical, 15);
|
||||
|
||||
container.set_margin_start(5);
|
||||
container.set_margin_end(5);
|
||||
container.set_margin_bottom(5);
|
||||
container.set_margin_top(5);
|
||||
let info_container = section_box("Basic Information");
|
||||
|
||||
container.set_column_homogeneous(true);
|
||||
|
||||
container.set_row_spacing(7);
|
||||
container.set_column_spacing(5);
|
||||
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::Start)
|
||||
.build();
|
||||
container.attach(&dummy_label, 0, 0, 1, 1);
|
||||
let dummy_label = Label::builder().selectable(true).halign(Align::End).build();
|
||||
values_grid.attach(&dummy_label, 0, 0, 1, 1);
|
||||
|
||||
container.attach(
|
||||
&{
|
||||
let label = Label::new(Some("GPU Model:"));
|
||||
label.set_halign(Align::End);
|
||||
label
|
||||
},
|
||||
0,
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
);
|
||||
let gpu_name_label = label_row("GPU Model:", &values_grid, 0, 0, true);
|
||||
let gpu_manufacturer_label = label_row("GPU Manufacturer:", &values_grid, 1, 0, true);
|
||||
let vbios_version_label = label_row("VBIOS Version:", &values_grid, 2, 0, true);
|
||||
let driver_label = label_row("Driver Version:", &values_grid, 3, 0, true);
|
||||
let vram_size_label = label_row("VRAM Size:", &values_grid, 4, 0, true);
|
||||
let link_speed_label = label_row("Link Speed:", &values_grid, 5, 0, true);
|
||||
|
||||
let gpu_name_label = value_label();
|
||||
container.attach(&gpu_name_label, 2, 0, 3, 1);
|
||||
info_container.append(&values_grid);
|
||||
container.append(&info_container);
|
||||
|
||||
container.attach(
|
||||
&{
|
||||
let label = Label::new(Some("GPU Manufacturer:"));
|
||||
label.set_halign(Align::End);
|
||||
label
|
||||
},
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
);
|
||||
|
||||
let gpu_manufacturer_label = value_label();
|
||||
container.attach(&gpu_manufacturer_label, 2, 1, 3, 1);
|
||||
|
||||
container.attach(
|
||||
&{
|
||||
let label = Label::new(Some("VBIOS Version:"));
|
||||
label.set_halign(Align::End);
|
||||
label
|
||||
},
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
);
|
||||
|
||||
let vbios_version_label = value_label();
|
||||
container.attach(&vbios_version_label, 2, 2, 3, 1);
|
||||
|
||||
container.attach(
|
||||
&{
|
||||
let label = Label::new(Some("Driver in use:"));
|
||||
label.set_halign(Align::End);
|
||||
label
|
||||
},
|
||||
0,
|
||||
3,
|
||||
2,
|
||||
1,
|
||||
);
|
||||
|
||||
let driver_label = value_label();
|
||||
container.attach(&driver_label, 2, 3, 3, 1);
|
||||
|
||||
container.attach(
|
||||
&{
|
||||
let label = Label::new(Some("VRAM Size:"));
|
||||
label.set_halign(Align::End);
|
||||
label
|
||||
},
|
||||
0,
|
||||
4,
|
||||
2,
|
||||
1,
|
||||
);
|
||||
|
||||
let vram_size_label = value_label();
|
||||
container.attach(&vram_size_label, 2, 4, 3, 1);
|
||||
|
||||
container.attach(
|
||||
&{
|
||||
let label = Label::new(Some("Link speed:"));
|
||||
label.set_halign(Align::End);
|
||||
label
|
||||
},
|
||||
0,
|
||||
5,
|
||||
2,
|
||||
1,
|
||||
);
|
||||
|
||||
let link_speed_label = value_label();
|
||||
link_speed_label.set_halign(Align::Start);
|
||||
|
||||
container.attach(&link_speed_label, 2, 5, 3, 1);
|
||||
let vulkan_container = section_box("Vulkan Information");
|
||||
|
||||
let vulkan_info_frame = VulkanInfoFrame::new();
|
||||
container.attach(&vulkan_info_frame.container, 0, 6, 5, 1);
|
||||
|
||||
vulkan_container.append(&vulkan_info_frame.container);
|
||||
container.append(&vulkan_container);
|
||||
|
||||
Self {
|
||||
container,
|
||||
@@ -211,10 +126,3 @@ impl InformationPage {
|
||||
.set_markup(&format!("<b>{vram_size} MiB</b>"));
|
||||
}
|
||||
}
|
||||
|
||||
fn value_label() -> Label {
|
||||
Label::builder()
|
||||
.selectable(true)
|
||||
.halign(Align::Start)
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
mod feature_model;
|
||||
|
||||
use crate::app::root_stack::{label_row, values_row};
|
||||
|
||||
use self::feature_model::FeatureModel;
|
||||
use super::value_label;
|
||||
use super::values_grid;
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
@@ -12,7 +14,7 @@ use tracing::trace;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VulkanInfoFrame {
|
||||
pub container: Frame,
|
||||
pub container: Box,
|
||||
device_name_label: Label,
|
||||
version_label: Label,
|
||||
features: Rc<RefCell<Vec<FeatureModel>>>,
|
||||
@@ -21,90 +23,34 @@ pub struct VulkanInfoFrame {
|
||||
|
||||
impl VulkanInfoFrame {
|
||||
pub fn new() -> Self {
|
||||
let container = Frame::new(None);
|
||||
|
||||
container.set_label_widget(Some(&{
|
||||
let label = Label::new(None);
|
||||
label.set_markup("<span font_desc='11'><b>Vulkan Information</b></span>");
|
||||
label
|
||||
}));
|
||||
container.set_label_align(0.5);
|
||||
let container = Box::new(Orientation::Vertical, 0);
|
||||
|
||||
let features = Rc::new(RefCell::new(Vec::new()));
|
||||
let extensions = Rc::new(RefCell::new(Vec::new()));
|
||||
|
||||
let vbox = Box::new(Orientation::Vertical, 5);
|
||||
let grid = values_grid();
|
||||
|
||||
let grid = Grid::new();
|
||||
let device_name_label = label_row("Device name", &grid, 0, 0, true);
|
||||
device_name_label.set_margin_top(5);
|
||||
device_name_label.set_margin_bottom(5);
|
||||
|
||||
grid.set_margin_start(5);
|
||||
grid.set_margin_end(5);
|
||||
grid.set_margin_bottom(5);
|
||||
grid.set_margin_top(5);
|
||||
let version_label = label_row("Version:", &grid, 1, 0, true);
|
||||
version_label.set_margin_top(5);
|
||||
version_label.set_margin_bottom(5);
|
||||
|
||||
grid.set_column_homogeneous(true);
|
||||
grid.set_row_homogeneous(false);
|
||||
|
||||
grid.set_row_spacing(7);
|
||||
grid.set_column_spacing(5);
|
||||
|
||||
grid.attach(
|
||||
&{
|
||||
let label = Label::new(Some("Device name:"));
|
||||
label.set_halign(Align::End);
|
||||
label
|
||||
},
|
||||
0,
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
);
|
||||
|
||||
let device_name_label = value_label();
|
||||
grid.attach(&device_name_label, 2, 0, 3, 1);
|
||||
|
||||
grid.attach(
|
||||
&{
|
||||
let label = Label::new(Some("Version:"));
|
||||
label.set_halign(Align::End);
|
||||
label
|
||||
},
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
);
|
||||
|
||||
let version_label = value_label();
|
||||
grid.attach(&version_label, 2, 1, 3, 1);
|
||||
|
||||
let features_label = Label::builder()
|
||||
.label("Features:")
|
||||
.halign(Align::End)
|
||||
.build();
|
||||
let show_features_button = Button::builder().label("Show").halign(Align::Start).build();
|
||||
let show_features_button = Button::builder().label("Show").halign(Align::End).build();
|
||||
show_features_button.connect_clicked(clone!(@strong features => move |_| {
|
||||
show_list_window("Vulkan features", &features.borrow());
|
||||
}));
|
||||
values_row("Features:", &grid, &show_features_button, 2, 0);
|
||||
|
||||
grid.attach(&features_label, 0, 2, 2, 1);
|
||||
grid.attach(&show_features_button, 2, 2, 2, 1);
|
||||
|
||||
let extensions_label = Label::builder()
|
||||
.label("Extensions:")
|
||||
.halign(Align::End)
|
||||
.build();
|
||||
let show_extensions_button = Button::builder().label("Show").halign(Align::Start).build();
|
||||
let show_extensions_button = Button::builder().label("Show").halign(Align::End).build();
|
||||
show_extensions_button.connect_clicked(clone!(@strong extensions => move |_| {
|
||||
show_list_window("Vulkan extensions", &extensions.borrow());
|
||||
}));
|
||||
values_row("Extensions:", &grid, &show_extensions_button, 3, 0);
|
||||
|
||||
grid.attach(&extensions_label, 0, 3, 2, 1);
|
||||
grid.attach(&show_extensions_button, 2, 3, 2, 1);
|
||||
|
||||
vbox.prepend(&grid);
|
||||
|
||||
container.set_child(Some(&vbox));
|
||||
container.append(&grid);
|
||||
|
||||
Self {
|
||||
container,
|
||||
|
||||
@@ -3,7 +3,11 @@ mod oc_page;
|
||||
mod software_page;
|
||||
mod thermals_page;
|
||||
|
||||
use gtk::*;
|
||||
use gtk::{
|
||||
prelude::IsA,
|
||||
traits::{BoxExt, GridExt},
|
||||
*,
|
||||
};
|
||||
|
||||
use self::software_page::software_page;
|
||||
use info_page::InformationPage;
|
||||
@@ -21,7 +25,12 @@ pub struct RootStack {
|
||||
|
||||
impl RootStack {
|
||||
pub fn new(system_info: SystemInfo, embedded_daemon: bool) -> Self {
|
||||
let container = Stack::builder().vexpand(true).build();
|
||||
let container = Stack::builder()
|
||||
.vexpand(true)
|
||||
.margin_top(15)
|
||||
.margin_start(50)
|
||||
.margin_end(50)
|
||||
.build();
|
||||
|
||||
let info_page = InformationPage::new();
|
||||
|
||||
@@ -46,3 +55,56 @@ impl RootStack {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fn section_box(title: &str) -> Box {
|
||||
let container = Box::builder()
|
||||
.orientation(Orientation::Vertical)
|
||||
.spacing(5)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.build();
|
||||
|
||||
let label = Label::builder()
|
||||
.use_markup(true)
|
||||
.label(format!("<span font_desc='13'><b>{title}</b></span>"))
|
||||
.halign(Align::Start)
|
||||
.margin_top(5)
|
||||
.margin_bottom(5)
|
||||
.build();
|
||||
|
||||
container.append(&label);
|
||||
container
|
||||
}
|
||||
|
||||
fn values_grid() -> Grid {
|
||||
Grid::builder()
|
||||
.margin_start(10)
|
||||
.margin_end(5)
|
||||
.row_spacing(10)
|
||||
.column_spacing(10)
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::section_box;
|
||||
use crate::app::root_stack::section_box;
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
@@ -17,9 +17,9 @@ pub struct ClocksFrame {
|
||||
|
||||
impl ClocksFrame {
|
||||
pub fn new() -> Self {
|
||||
let container = section_box("Maximum Clocks", 0, 5);
|
||||
let container = section_box("Maximum Clocks");
|
||||
|
||||
let tweaking_grid = Grid::new();
|
||||
let tweaking_grid = Grid::builder().row_spacing(5).build();
|
||||
let max_sclk_adjustment = oc_adjustment("GPU Clock (MHz)", &tweaking_grid, 0);
|
||||
let max_voltage_adjustment = oc_adjustment("GPU voltage (mV)", &tweaking_grid, 1);
|
||||
let max_mclk_adjustment = oc_adjustment("VRAM Clock (MHz)", &tweaking_grid, 2);
|
||||
@@ -28,7 +28,7 @@ impl ClocksFrame {
|
||||
.label("Defaults")
|
||||
.halign(Align::End)
|
||||
.build();
|
||||
tweaking_grid.attach(&reset_button, 4, 3, 1, 1);
|
||||
tweaking_grid.attach(&reset_button, 6, 3, 1, 1);
|
||||
|
||||
let clocks_data_unavailable_label = Label::new(Some("No clocks data available"));
|
||||
|
||||
@@ -141,6 +141,8 @@ fn oc_adjustment(title: &'static str, grid: &Grid, row: i32) -> Adjustment {
|
||||
.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);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
mod clocks_frame;
|
||||
mod performance_level_frame;
|
||||
mod power_cap_frame;
|
||||
mod stats_grid;
|
||||
mod stats_frame;
|
||||
|
||||
use clocks_frame::ClocksFrame;
|
||||
use gtk::prelude::*;
|
||||
@@ -9,13 +9,13 @@ use gtk::*;
|
||||
use lact_client::schema::{ClocksTableGen, DeviceStats, PerformanceLevel, SystemInfo};
|
||||
use performance_level_frame::PerformanceLevelFrame;
|
||||
use power_cap_frame::PowerCapFrame;
|
||||
use stats_grid::StatsGrid;
|
||||
use stats_frame::StatsFrame;
|
||||
use tracing::warn;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OcPage {
|
||||
pub container: Box,
|
||||
stats_grid: StatsGrid,
|
||||
stats_frame: StatsFrame,
|
||||
performance_level_frame: PerformanceLevelFrame,
|
||||
power_cap_frame: PowerCapFrame,
|
||||
pub clocks_frame: ClocksFrame,
|
||||
@@ -25,9 +25,7 @@ impl OcPage {
|
||||
pub fn new(system_info: &SystemInfo) -> Self {
|
||||
let container = Box::builder()
|
||||
.orientation(Orientation::Vertical)
|
||||
.spacing(5)
|
||||
.margin_top(5)
|
||||
.margin_bottom(5)
|
||||
.spacing(15)
|
||||
.build();
|
||||
|
||||
if system_info.amdgpu_overdrive_enabled == Some(false) {
|
||||
@@ -35,9 +33,8 @@ impl OcPage {
|
||||
container.append(&warning_frame);
|
||||
}
|
||||
|
||||
let stats_grid = StatsGrid::new();
|
||||
|
||||
container.append(&stats_grid.container);
|
||||
let stats_frame = StatsFrame::new();
|
||||
container.append(&stats_frame.container);
|
||||
|
||||
let power_cap_frame = PowerCapFrame::new();
|
||||
let performance_level_frame = PerformanceLevelFrame::new();
|
||||
@@ -49,7 +46,7 @@ impl OcPage {
|
||||
|
||||
Self {
|
||||
container,
|
||||
stats_grid,
|
||||
stats_frame,
|
||||
performance_level_frame,
|
||||
clocks_frame,
|
||||
power_cap_frame,
|
||||
@@ -57,7 +54,7 @@ impl OcPage {
|
||||
}
|
||||
|
||||
pub fn set_stats(&self, stats: &DeviceStats, initial: bool) {
|
||||
self.stats_grid.set_stats(stats);
|
||||
self.stats_frame.set_stats(stats);
|
||||
if initial {
|
||||
self.power_cap_frame.set_data(
|
||||
stats.power.cap_current,
|
||||
@@ -118,24 +115,6 @@ impl OcPage {
|
||||
}
|
||||
}
|
||||
|
||||
fn section_box(title: &str, spacing: i32, margin: i32) -> Box {
|
||||
let container = Box::builder()
|
||||
.orientation(Orientation::Vertical)
|
||||
.spacing(spacing)
|
||||
.margin_start(margin)
|
||||
.margin_end(margin)
|
||||
.build();
|
||||
|
||||
let label = Label::builder()
|
||||
.use_markup(true)
|
||||
.label(format!("<span font_desc='11'><b>{title}</b></span>"))
|
||||
.xalign(0.1)
|
||||
.build();
|
||||
|
||||
container.append(&label);
|
||||
container
|
||||
}
|
||||
|
||||
fn oc_warning_frame() -> Frame {
|
||||
let container = Frame::new(Some("Overclocking information"));
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use crate::app::root_stack::section_box;
|
||||
use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
use lact_client::schema::PerformanceLevel;
|
||||
|
||||
use super::section_box;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PerformanceLevelFrame {
|
||||
pub container: Box,
|
||||
@@ -12,7 +11,7 @@ pub struct PerformanceLevelFrame {
|
||||
|
||||
impl PerformanceLevelFrame {
|
||||
pub fn new() -> Self {
|
||||
let container = section_box("Performance level", 5, 5);
|
||||
let container = section_box("Performance level");
|
||||
|
||||
let root_box = Box::new(Orientation::Horizontal, 5);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::section_box;
|
||||
use crate::app::root_stack::section_box;
|
||||
use gtk::*;
|
||||
use gtk::{glib::clone, prelude::*};
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
@@ -13,7 +13,7 @@ pub struct PowerCapFrame {
|
||||
|
||||
impl PowerCapFrame {
|
||||
pub fn new() -> Self {
|
||||
let container = section_box("Power Usage Limit", 5, 5);
|
||||
let container = section_box("Power Usage Limit");
|
||||
let default_cap = Rc::new(Cell::new(None));
|
||||
|
||||
let value_suffix = "W";
|
||||
@@ -34,6 +34,8 @@ impl PowerCapFrame {
|
||||
.adjustment(&adjustment)
|
||||
.hexpand(true)
|
||||
.round_digits(0)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.build();
|
||||
|
||||
scale.set_draw_value(false);
|
||||
|
||||
137
lact-gui/src/app/root_stack/oc_page/stats_frame.rs
Normal file
137
lact-gui/src/app/root_stack/oc_page/stats_frame.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
use crate::app::root_stack::{label_row, section_box};
|
||||
use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
use lact_client::schema::{ClockspeedStats, DeviceStats, PowerStats, VoltageStats, VramStats};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StatsFrame {
|
||||
pub container: Box,
|
||||
vram_usage_bar: LevelBar,
|
||||
vram_usage_label: Label,
|
||||
gpu_clock_label: Label,
|
||||
vram_clock_label: Label,
|
||||
gpu_voltage_label: Label,
|
||||
power_usage_label: Label,
|
||||
gpu_temperature_label: Label,
|
||||
gpu_usage_label: Label,
|
||||
}
|
||||
|
||||
impl StatsFrame {
|
||||
pub fn new() -> Self {
|
||||
let container = section_box("Statistics");
|
||||
|
||||
let vram_usage_hbox = Box::new(Orientation::Horizontal, 5);
|
||||
|
||||
let vram_usage_title = name_label("VRAM Usage:");
|
||||
vram_usage_hbox.append(&vram_usage_title);
|
||||
|
||||
let vram_usage_overlay = Overlay::new();
|
||||
let vram_usage_bar = LevelBar::builder()
|
||||
.hexpand(true)
|
||||
.value(1.0)
|
||||
.orientation(Orientation::Horizontal)
|
||||
.build();
|
||||
let vram_usage_label = Label::new(None);
|
||||
|
||||
vram_usage_overlay.set_child(Some(&vram_usage_bar));
|
||||
vram_usage_overlay.add_overlay(&vram_usage_label);
|
||||
|
||||
vram_usage_hbox.append(&vram_usage_overlay);
|
||||
|
||||
container.append(&vram_usage_hbox);
|
||||
|
||||
let labels_grid = Grid::builder()
|
||||
.row_spacing(5)
|
||||
.column_spacing(20)
|
||||
.margin_top(5)
|
||||
.build();
|
||||
|
||||
let gpu_clock_label = label_row("GPU Core Clock:", &labels_grid, 0, 0, false);
|
||||
let vram_clock_label = label_row("GPU Memory Clock:", &labels_grid, 0, 2, false);
|
||||
let gpu_voltage_label = label_row("GPU Voltage:", &labels_grid, 1, 0, false);
|
||||
let gpu_usage_label = label_row("GPU Usage:", &labels_grid, 1, 2, false);
|
||||
let gpu_temperature_label = label_row("GPU Temperature:", &labels_grid, 2, 0, false);
|
||||
let power_usage_label = label_row("GPU Power Usage:", &labels_grid, 2, 2, false);
|
||||
|
||||
container.append(&labels_grid);
|
||||
|
||||
Self {
|
||||
container,
|
||||
vram_usage_bar,
|
||||
vram_usage_label,
|
||||
gpu_clock_label,
|
||||
vram_clock_label,
|
||||
gpu_voltage_label,
|
||||
power_usage_label,
|
||||
gpu_temperature_label,
|
||||
gpu_usage_label,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_stats(&self, stats: &DeviceStats) {
|
||||
let VramStats {
|
||||
total: total_vram,
|
||||
used: used_vram,
|
||||
} = stats.vram;
|
||||
|
||||
if let (Some(used_vram), Some(total_vram)) = (used_vram, total_vram) {
|
||||
self.vram_usage_bar
|
||||
.set_value(used_vram as f64 / total_vram as f64);
|
||||
}
|
||||
self.vram_usage_label.set_text(&format!(
|
||||
"{}/{} MiB",
|
||||
used_vram.unwrap_or(0) / 1024 / 1024,
|
||||
total_vram.unwrap_or(0) / 1024 / 1024,
|
||||
));
|
||||
|
||||
let ClockspeedStats {
|
||||
gpu_clockspeed,
|
||||
vram_clockspeed,
|
||||
} = stats.clockspeed;
|
||||
|
||||
self.gpu_clock_label
|
||||
.set_markup(&format!("<b>{}MHz</b>", gpu_clockspeed.unwrap_or(0)));
|
||||
self.vram_clock_label
|
||||
.set_markup(&format!("<b>{}MHz</b>", vram_clockspeed.unwrap_or(0)));
|
||||
|
||||
let VoltageStats {
|
||||
gpu: gpu_voltage, ..
|
||||
} = stats.voltage;
|
||||
|
||||
self.gpu_voltage_label.set_markup(&format!(
|
||||
"<b>{}V</b>",
|
||||
gpu_voltage.unwrap_or(0) as f64 / 1000f64
|
||||
));
|
||||
|
||||
let PowerStats {
|
||||
average: power_average,
|
||||
cap_current: power_cap_current,
|
||||
..
|
||||
} = stats.power;
|
||||
|
||||
self.power_usage_label.set_markup(&format!(
|
||||
"<b>{}/{}W</b>",
|
||||
power_average.unwrap_or(0.0),
|
||||
power_cap_current.unwrap_or(0.0)
|
||||
));
|
||||
|
||||
let maybe_temp = stats
|
||||
.temps
|
||||
.get("junction")
|
||||
.or_else(|| stats.temps.get("edge"));
|
||||
|
||||
if let Some(temp) = maybe_temp.and_then(|temp| temp.current) {
|
||||
self.gpu_temperature_label
|
||||
.set_markup(&format!("<b>{temp}°C</b>"));
|
||||
}
|
||||
|
||||
self.gpu_usage_label.set_markup(&format!(
|
||||
"<b>{}%</b>",
|
||||
stats.busy_percent.unwrap_or_default()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn name_label(text: &str) -> Label {
|
||||
Label::builder().label(text).halign(Align::Start).build()
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
use lact_client::schema::{ClockspeedStats, DeviceStats, PowerStats, VoltageStats, VramStats};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StatsGrid {
|
||||
pub container: Grid,
|
||||
vram_usage_bar: LevelBar,
|
||||
vram_usage_label: Label,
|
||||
gpu_clock_label: Label,
|
||||
vram_clock_label: Label,
|
||||
gpu_voltage_label: Label,
|
||||
power_usage_label: Label,
|
||||
gpu_temperature_label: Label,
|
||||
gpu_usage_label: Label,
|
||||
}
|
||||
|
||||
impl StatsGrid {
|
||||
pub fn new() -> Self {
|
||||
let container = Grid::new();
|
||||
|
||||
container.set_column_homogeneous(true);
|
||||
|
||||
container.set_row_spacing(7);
|
||||
|
||||
container.attach(&Label::new(Some("VRAM Usage")), 0, 0, 1, 1);
|
||||
|
||||
let vram_usage_overlay = Overlay::new();
|
||||
|
||||
let vram_usage_bar = LevelBar::new();
|
||||
|
||||
let vram_usage_label = Label::new(None);
|
||||
|
||||
{
|
||||
vram_usage_bar.set_orientation(Orientation::Horizontal);
|
||||
vram_usage_bar.set_value(1.0);
|
||||
|
||||
vram_usage_label.set_text("0/0 MiB");
|
||||
|
||||
vram_usage_overlay.set_child(Some(&vram_usage_bar));
|
||||
vram_usage_overlay.add_overlay(&vram_usage_label);
|
||||
|
||||
container.attach(&vram_usage_overlay, 1, 0, 2, 1);
|
||||
}
|
||||
|
||||
let gpu_clock_label = Label::new(None);
|
||||
{
|
||||
let gpu_clock_box = Box::new(Orientation::Horizontal, 5);
|
||||
|
||||
gpu_clock_box.append(&Label::new(Some("GPU Clock:")));
|
||||
|
||||
gpu_clock_label.set_markup("<b>0MHz</b>");
|
||||
|
||||
gpu_clock_box.append(&gpu_clock_label);
|
||||
|
||||
gpu_clock_box.set_halign(Align::Center);
|
||||
|
||||
container.attach(&gpu_clock_box, 0, 1, 1, 1);
|
||||
}
|
||||
|
||||
let vram_clock_label = Label::new(None);
|
||||
{
|
||||
let vram_clock_box = Box::new(Orientation::Horizontal, 5);
|
||||
|
||||
vram_clock_box.append(&Label::new(Some("VRAM Clock:")));
|
||||
|
||||
vram_clock_label.set_markup("<b>0MHz</b>");
|
||||
|
||||
vram_clock_box.append(&vram_clock_label);
|
||||
|
||||
vram_clock_box.set_halign(Align::Center);
|
||||
|
||||
container.attach(&vram_clock_box, 1, 1, 1, 1);
|
||||
}
|
||||
let gpu_voltage_label = Label::new(None);
|
||||
{
|
||||
let gpu_voltage_box = Box::new(Orientation::Horizontal, 5);
|
||||
|
||||
gpu_voltage_box.append(&Label::new(Some("GPU Voltage:")));
|
||||
|
||||
gpu_voltage_label.set_markup("<b>0.000V</b>");
|
||||
|
||||
gpu_voltage_box.append(&gpu_voltage_label);
|
||||
|
||||
gpu_voltage_box.set_halign(Align::Center);
|
||||
|
||||
container.attach(&gpu_voltage_box, 2, 1, 1, 1);
|
||||
}
|
||||
|
||||
let power_usage_label = Label::new(None);
|
||||
{
|
||||
let power_usage_box = Box::new(Orientation::Horizontal, 5);
|
||||
|
||||
power_usage_box.append(&Label::new(Some("Power Usage:")));
|
||||
|
||||
power_usage_label.set_markup("<b>00/000W</b>");
|
||||
|
||||
power_usage_box.append(&power_usage_label);
|
||||
|
||||
power_usage_box.set_halign(Align::Center);
|
||||
|
||||
container.attach(&power_usage_box, 0, 2, 1, 1);
|
||||
}
|
||||
|
||||
let gpu_temperature_label = Label::new(None);
|
||||
{
|
||||
let gpu_temperature_box = Box::new(Orientation::Horizontal, 5);
|
||||
|
||||
gpu_temperature_box.append(&Label::new(Some("GPU Temperature:")));
|
||||
|
||||
// gpu_temperature_label.set_markup("<b>0°C</b>");
|
||||
|
||||
gpu_temperature_box.append(&gpu_temperature_label);
|
||||
|
||||
gpu_temperature_box.set_halign(Align::Center);
|
||||
|
||||
container.attach(&gpu_temperature_box, 1, 2, 1, 1);
|
||||
}
|
||||
|
||||
let gpu_usage_label = Label::new(None);
|
||||
{
|
||||
let gpu_usage_box = Box::new(Orientation::Horizontal, 5);
|
||||
|
||||
gpu_usage_box.append(&Label::new(Some("GPU Usage:")));
|
||||
|
||||
gpu_usage_box.append(&gpu_usage_label);
|
||||
|
||||
gpu_usage_box.set_halign(Align::Center);
|
||||
|
||||
container.attach(&gpu_usage_box, 2, 2, 1, 1);
|
||||
}
|
||||
|
||||
Self {
|
||||
container,
|
||||
vram_usage_bar,
|
||||
vram_usage_label,
|
||||
gpu_clock_label,
|
||||
vram_clock_label,
|
||||
gpu_voltage_label,
|
||||
power_usage_label,
|
||||
gpu_temperature_label,
|
||||
gpu_usage_label,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_stats(&self, stats: &DeviceStats) {
|
||||
let VramStats {
|
||||
total: total_vram,
|
||||
used: used_vram,
|
||||
} = stats.vram;
|
||||
|
||||
if let (Some(used_vram), Some(total_vram)) = (used_vram, total_vram) {
|
||||
self.vram_usage_bar
|
||||
.set_value(used_vram as f64 / total_vram as f64);
|
||||
}
|
||||
self.vram_usage_label.set_text(&format!(
|
||||
"{}/{} MiB",
|
||||
used_vram.unwrap_or(0) / 1024 / 1024,
|
||||
total_vram.unwrap_or(0) / 1024 / 1024,
|
||||
));
|
||||
|
||||
let ClockspeedStats {
|
||||
gpu_clockspeed,
|
||||
vram_clockspeed,
|
||||
} = stats.clockspeed;
|
||||
|
||||
self.gpu_clock_label
|
||||
.set_markup(&format!("<b>{}MHz</b>", gpu_clockspeed.unwrap_or(0)));
|
||||
self.vram_clock_label
|
||||
.set_markup(&format!("<b>{}MHz</b>", vram_clockspeed.unwrap_or(0)));
|
||||
|
||||
let VoltageStats {
|
||||
gpu: gpu_voltage, ..
|
||||
} = stats.voltage;
|
||||
|
||||
self.gpu_voltage_label.set_markup(&format!(
|
||||
"<b>{}V</b>",
|
||||
gpu_voltage.unwrap_or(0) as f64 / 1000f64
|
||||
));
|
||||
|
||||
let PowerStats {
|
||||
average: power_average,
|
||||
cap_current: power_cap_current,
|
||||
..
|
||||
} = stats.power;
|
||||
|
||||
self.power_usage_label.set_markup(&format!(
|
||||
"<b>{}/{}W</b>",
|
||||
power_average.unwrap_or(0.0),
|
||||
power_cap_current.unwrap_or(0.0)
|
||||
));
|
||||
|
||||
let maybe_temp = stats
|
||||
.temps
|
||||
.get("junction")
|
||||
.or_else(|| stats.temps.get("edge"));
|
||||
|
||||
if let Some(temp) = maybe_temp.and_then(|temp| temp.current) {
|
||||
self.gpu_temperature_label
|
||||
.set_markup(&format!("<b>{temp}°C</b>"));
|
||||
}
|
||||
|
||||
self.gpu_usage_label.set_markup(&format!(
|
||||
"<b>{}%</b>",
|
||||
stats.busy_percent.unwrap_or_default()
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
use lact_client::schema::{default_fan_curve, DeviceStats, FanCurveMap};
|
||||
|
||||
use super::{label_row, section_box, values_grid, values_row};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ThermalsSettings {
|
||||
pub manual_fan_control: bool,
|
||||
@@ -15,7 +17,7 @@ pub struct ThermalsSettings {
|
||||
#[derive(Clone)]
|
||||
pub struct ThermalsPage {
|
||||
pub container: Box,
|
||||
temp_label: Label,
|
||||
temperatures_label: Label,
|
||||
fan_speed_label: Label,
|
||||
fan_control_enabled_switch: Switch,
|
||||
fan_curve_frame: FanCurveFrame,
|
||||
@@ -23,79 +25,40 @@ pub struct ThermalsPage {
|
||||
|
||||
impl ThermalsPage {
|
||||
pub fn new() -> Self {
|
||||
let container = Box::new(Orientation::Vertical, 5);
|
||||
let container = Box::new(Orientation::Vertical, 15);
|
||||
|
||||
let grid = Grid::new();
|
||||
let stats_section = section_box("Statistics");
|
||||
let stats_grid = values_grid();
|
||||
|
||||
grid.set_margin_start(5);
|
||||
grid.set_margin_end(5);
|
||||
grid.set_margin_bottom(5);
|
||||
grid.set_margin_top(5);
|
||||
let temperatures_label = label_row("Temperatures:", &stats_grid, 0, 0, false);
|
||||
let fan_speed_label = label_row("Fan speed:", &stats_grid, 1, 0, false);
|
||||
|
||||
grid.set_column_homogeneous(true);
|
||||
stats_section.append(&stats_grid);
|
||||
|
||||
grid.set_row_spacing(7);
|
||||
grid.set_column_spacing(5);
|
||||
container.append(&stats_section);
|
||||
|
||||
grid.attach(
|
||||
&{
|
||||
let label = Label::new(Some("Temperatures:"));
|
||||
label.set_halign(Align::End);
|
||||
label
|
||||
},
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
);
|
||||
|
||||
let temp_label = Label::new(None);
|
||||
temp_label.set_halign(Align::Start);
|
||||
|
||||
grid.attach(&temp_label, 2, 0, 1, 1);
|
||||
|
||||
grid.attach(
|
||||
&{
|
||||
let label = Label::new(Some("Fan speed:"));
|
||||
label.set_halign(Align::End);
|
||||
label
|
||||
},
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
);
|
||||
|
||||
let fan_speed_label = Label::new(None);
|
||||
fan_speed_label.set_halign(Align::Start);
|
||||
|
||||
grid.attach(&fan_speed_label, 2, 1, 1, 1);
|
||||
|
||||
grid.attach(
|
||||
&{
|
||||
let label = Label::new(Some("Automatic fan control:"));
|
||||
label.set_halign(Align::End);
|
||||
label
|
||||
},
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
);
|
||||
let fan_control_section = section_box("Fan control");
|
||||
let fan_control_grid = values_grid();
|
||||
|
||||
let fan_control_enabled_switch = Switch::builder()
|
||||
.active(true)
|
||||
.halign(Align::Start)
|
||||
.halign(Align::End)
|
||||
.hexpand(true)
|
||||
.sensitive(false)
|
||||
.build();
|
||||
values_row(
|
||||
"Automatic fan control:",
|
||||
&fan_control_grid,
|
||||
&fan_control_enabled_switch,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
grid.attach(&fan_control_enabled_switch, 2, 2, 1, 1);
|
||||
|
||||
container.prepend(&grid);
|
||||
fan_control_section.append(&fan_control_grid);
|
||||
|
||||
let fan_curve_frame = FanCurveFrame::new();
|
||||
|
||||
container.append(&fan_curve_frame.container);
|
||||
fan_control_section.append(&fan_curve_frame.container);
|
||||
|
||||
// Show/hide fan curve when the switch is toggled
|
||||
{
|
||||
@@ -111,9 +74,11 @@ impl ThermalsPage {
|
||||
});
|
||||
}
|
||||
|
||||
container.append(&fan_control_section);
|
||||
|
||||
Self {
|
||||
container,
|
||||
temp_label,
|
||||
temperatures_label,
|
||||
fan_speed_label,
|
||||
fan_control_enabled_switch,
|
||||
fan_curve_frame,
|
||||
@@ -130,10 +95,10 @@ impl ThermalsPage {
|
||||
let temperatures_text = if temperatures.is_empty() {
|
||||
String::from("No sensors found")
|
||||
} else {
|
||||
temperatures.join("\n")
|
||||
temperatures.join(", ")
|
||||
};
|
||||
|
||||
self.temp_label
|
||||
self.temperatures_label
|
||||
.set_markup(&format!("<b>{temperatures_text}</b>",));
|
||||
|
||||
match stats.fan.speed_current {
|
||||
|
||||
Reference in New Issue
Block a user