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:
Ilya Zlobintsev
2023-02-26 11:40:13 +02:00
committed by GitHub
parent 2067641fee
commit ff0188fef2
11 changed files with 287 additions and 495 deletions

View File

@@ -6,7 +6,7 @@ This application allows you to control your AMD GPU on a Linux system.
| GPU info | Overclocking | Fan control |
|----------------------------------------------|----------------------------------------------|---------------------------------------------|
|![image](https://user-images.githubusercontent.com/22796665/221357224-21163f94-1afd-4bb8-96bd-24f9ce1816ce.png)|![image](https://user-images.githubusercontent.com/22796665/221357297-9c03bcb5-0742-459b-bffb-9e75c93df25b.png)|![image](https://user-images.githubusercontent.com/22796665/221357332-6d26a65f-d522-4b04-86a8-c9820b334416.png)
|![image](https://user-images.githubusercontent.com/22796665/221402316-7ff140ee-9013-4599-9263-bdbf15906896.png)|![image](https://user-images.githubusercontent.com/22796665/221402327-f5f69c29-8f07-4e3e-9b77-6f064b26d6f0.png)|![image](https://user-images.githubusercontent.com/22796665/221402354-06c1a2a1-4849-4953-99ea-cab94e8413af.png)|
Current features:

View File

@@ -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()
}

View File

@@ -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,

View File

@@ -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()
}

View File

@@ -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);

View File

@@ -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"));

View File

@@ -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);

View File

@@ -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);

View 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()
}

View File

@@ -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()
));
}
}

View File

@@ -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 {