refactor: make power cap frame into a relm component

This commit is contained in:
Ilya Zlobintsev 2025-01-30 22:51:35 +02:00
parent d6c8329b80
commit 99ff21f121
4 changed files with 155 additions and 166 deletions

View File

@ -15,6 +15,12 @@ glib::wrapper! {
@implements gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget;
}
impl Default for OcAdjustment {
fn default() -> Self {
Object::builder().build()
}
}
impl OcAdjustment {
pub fn new(
value: f64,
@ -24,7 +30,7 @@ impl OcAdjustment {
page_increment: f64,
page_size: f64,
) -> Self {
let oc_adjustment: Self = Object::builder().build();
let oc_adjustment = Self::default();
let adjustment = oc_adjustment.imp().obj();
adjustment.set_lower(lower);

View File

@ -13,16 +13,15 @@ use amdgpu_sysfs::gpu_handle::{
use clocks_frame::ClocksFrame;
use gpu_stats_section::GpuStatsSection;
use gtk::{
glib::object::ObjectExt,
pango,
prelude::{BoxExt, ButtonExt, FrameExt, OrientableExt, WidgetExt},
};
use lact_daemon::MODULE_CONF_PATH;
use lact_schema::{request::SetClocksCommand, ClocksTable, PowerStates, SystemInfo};
use performance_frame::PerformanceFrame;
use power_cap_section::PowerCapSection;
use power_cap_section::{PowerCapMsg, PowerCapSection};
use power_states::power_states_frame::PowerStatesFrame;
use relm4::{ComponentParts, ComponentSender, RelmWidgetExt};
use relm4::{ComponentController, ComponentParts, ComponentSender, RelmWidgetExt};
use std::{cell::Cell, collections::HashMap, rc::Rc};
use tracing::warn;
@ -32,7 +31,7 @@ You can still change basic settings, but the more advanced clocks and voltage co
pub struct OcPage {
stats_section: GpuStatsSection,
pub performance_frame: PerformanceFrame,
power_cap_section: PowerCapSection,
power_cap_section: relm4::Controller<PowerCapSection>,
power_states_frame: PowerStatesFrame,
clocks_frame: ClocksFrame,
// TODO: refactor this out when child components use senders
@ -96,11 +95,7 @@ impl relm4::Component for OcPage {
model.stats_section.clone(),
model.power_cap_section.clone() {
connect_current_value_notify[sender] => move |_| {
sender.output(AppMsg::SettingsChanged).unwrap();
} @power_cap_notify,
},
model.power_cap_section.widget(),
model.performance_frame.container.clone(),
@ -116,10 +111,12 @@ impl relm4::Component for OcPage {
root: Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let power_cap_section = PowerCapSection::builder().launch(()).detach();
let model = Self {
stats_section: GpuStatsSection::new(),
performance_frame: PerformanceFrame::new(),
power_cap_section: PowerCapSection::new(),
power_cap_section,
power_states_frame: PowerStatesFrame::new(),
clocks_frame: ClocksFrame::new(),
signals_blocked: Rc::new(Cell::new(false)),
@ -143,27 +140,19 @@ impl relm4::Component for OcPage {
) {
self.signals_blocked.set(true);
match msg {
OcPageMsg::Update { update, initial } => match update {
OcPageMsg::Update { update, initial } => match &update {
PageUpdate::Stats(stats) => {
self.stats_section.set_stats(&stats);
self.power_states_frame.set_stats(&stats);
self.stats_section.set_stats(stats);
self.power_states_frame.set_stats(stats);
if initial {
self.power_cap_section
.block_signal(&widgets.power_cap_notify);
.emit(PowerCapMsg::Update(update.clone()));
self.power_cap_section
.set_max_value(stats.power.cap_max.unwrap_or_default());
self.power_cap_section
.set_min_value(stats.power.cap_min.unwrap_or_default());
self.power_cap_section
.set_default_value(stats.power.cap_default.unwrap_or_default());
if let Some(current_cap) = stats.power.cap_current {
self.power_cap_section.set_initial_value(current_cap);
self.power_cap_section.set_visible(true);
if stats.power.cap_current.is_some() {
self.power_cap_section.widget().set_visible(true);
} else {
self.power_cap_section.set_visible(false);
self.power_cap_section.widget().set_visible(false);
}
match stats.performance_level {
@ -173,9 +162,6 @@ impl relm4::Component for OcPage {
}
None => self.performance_frame.hide(),
}
self.power_cap_section
.unblock_signal(&widgets.power_cap_notify);
}
}
PageUpdate::Info(info) => {
@ -216,9 +202,10 @@ impl relm4::Component for OcPage {
self.signals_blocked.set(false);
let signals_blocked = self.signals_blocked.clone();
let signals_sender = sender.clone();
let f = move || {
if !signals_blocked.get() {
sender
signals_sender
.output(AppMsg::SettingsChanged)
.expect("Channel closed")
}
@ -226,6 +213,8 @@ impl relm4::Component for OcPage {
self.performance_frame.connect_settings_changed(f.clone());
self.clocks_frame.connect_clocks_changed(f.clone());
self.power_states_frame.connect_values_changed(f);
self.update_view(widgets, sender);
}
}
@ -240,7 +229,7 @@ impl OcPage {
}
pub fn get_power_cap(&self) -> Option<f64> {
self.power_cap_section.get_user_cap()
self.power_cap_section.model().get_user_cap()
}
pub fn get_clocks_commands(&self) -> Vec<SetClocksCommand> {

View File

@ -1,109 +1,136 @@
use crate::app::page_section::PageSection;
use gtk::glib::{self, subclass::types::ObjectSubclassIsExt, Object};
use crate::{
app::{
msg::AppMsg,
page_section::PageSection,
pages::{oc_adjustment::OcAdjustment, PageUpdate},
},
APP_BROKER,
};
use gtk::{
glib::object::ObjectExt,
prelude::{AdjustmentExt, BoxExt, ButtonExt, OrientableExt, RangeExt, ScaleExt, WidgetExt},
};
use lact_schema::PowerStats;
use relm4::{ComponentParts, ComponentSender, RelmWidgetExt};
use std::fmt::Write;
glib::wrapper! {
pub struct PowerCapSection(ObjectSubclass<imp::PowerCapSection>)
@extends PageSection, gtk::Box, gtk::Widget,
@implements gtk::Orientable, gtk::Accessible, gtk::Buildable;
#[derive(Default)]
pub struct PowerCapSection {
power: PowerStats,
adjustment: OcAdjustment,
value_text: String,
}
#[derive(Debug)]
pub enum PowerCapMsg {
Update(PageUpdate),
RefreshText,
Reset,
}
#[relm4::component(pub)]
impl relm4::Component for PowerCapSection {
type Init = ();
type Input = PowerCapMsg;
type Output = ();
type CommandOutput = ();
view! {
#[root]
PageSection::new("Power usage limit") {
append = &gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
gtk::Label {
#[watch]
set_label: &model.value_text,
},
gtk::Scale {
set_orientation: gtk::Orientation::Horizontal,
set_hexpand: true,
set_round_digits: 0,
set_margin_horizontal: 5,
set_draw_value: false,
set_adjustment: adjustment,
},
gtk::Button {
set_label: "Default",
connect_clicked => PowerCapMsg::Reset,
},
}
},
#[local_ref]
adjustment -> OcAdjustment {
connect_value_notify => move |_| {
APP_BROKER.send(AppMsg::SettingsChanged);
} @ value_notify,
connect_value_notify => PowerCapMsg::RefreshText,
connect_upper_notify => PowerCapMsg::RefreshText,
},
}
fn init(
_init: Self::Init,
root: Self::Root,
_sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let model = Self::default();
let adjustment = &model.adjustment;
let widgets = view_output!();
ComponentParts { model, widgets }
}
fn update_with_view(
&mut self,
widgets: &mut Self::Widgets,
msg: Self::Input,
sender: ComponentSender<Self>,
_root: &Self::Root,
) {
match msg {
PowerCapMsg::Update(PageUpdate::Stats(stats)) => {
// The signal blocking has to be manual,
// because relm's signal block macro feature doesn't seem to work with non-widget objects
self.adjustment.block_signal(&widgets.value_notify);
let power = stats.power;
self.adjustment.set_upper(power.cap_max.unwrap_or_default());
self.adjustment.set_lower(power.cap_min.unwrap_or_default());
self.adjustment
.set_initial_value(power.cap_current.unwrap_or_default());
self.adjustment.unblock_signal(&widgets.value_notify);
self.power = power;
}
PowerCapMsg::Update(PageUpdate::Info(_)) => (),
PowerCapMsg::RefreshText => {
self.value_text.clear();
write!(
self.value_text,
"{}/{} W",
self.adjustment.value(),
self.adjustment.upper()
)
.unwrap();
}
PowerCapMsg::Reset => {
self.adjustment
.set_value(self.power.cap_default.unwrap_or_default());
}
}
self.update_view(widgets, sender);
}
}
impl PowerCapSection {
pub fn new() -> Self {
Object::builder().build()
}
pub fn get_user_cap(&self) -> Option<f64> {
let imp = self.imp();
imp.adjustment.get_changed_value(true)
}
pub fn set_initial_value(&self, value: f64) {
self.imp().adjustment.set_initial_value(value);
self.adjustment.get_changed_value(true)
}
}
impl Default for PowerCapSection {
fn default() -> Self {
Self::new()
}
}
mod imp {
use crate::app::{page_section::PageSection, pages::oc_adjustment::OcAdjustment};
use gtk::{
glib::{self, clone, subclass::InitializingObject, types::StaticTypeExt, Properties},
prelude::{ButtonExt, ObjectExt},
subclass::{
prelude::*,
widget::{CompositeTemplateClass, WidgetImpl},
},
Button, CompositeTemplate,
};
use std::cell::RefCell;
#[derive(CompositeTemplate, Default, Properties)]
#[properties(wrapper_type = super::PowerCapSection)]
#[template(file = "ui/oc_page/power_cap_section.blp")]
pub struct PowerCapSection {
#[property(get, set)]
pub current_value: RefCell<f64>,
#[property(get, set)]
pub max_value: RefCell<f64>,
#[property(get, set)]
pub min_value: RefCell<f64>,
#[property(get, set)]
pub default_value: RefCell<f64>,
#[property(get, set)]
pub value_text: RefCell<String>,
#[template_child]
pub adjustment: TemplateChild<OcAdjustment>,
#[template_child]
pub reset_button: TemplateChild<Button>,
}
#[glib::object_subclass]
impl ObjectSubclass for PowerCapSection {
const NAME: &'static str = "PowerCapSection";
type Type = super::PowerCapSection;
type ParentType = PageSection;
fn class_init(class: &mut Self::Class) {
OcAdjustment::ensure_type();
class.bind_template();
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
#[glib::derived_properties]
impl ObjectImpl for PowerCapSection {
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
obj.connect_current_value_notify(move |section| {
let text = format!("{}/{} W", section.current_value(), section.max_value());
section.set_value_text(text);
});
obj.connect_max_value_notify(move |section| {
let text = format!("{}/{} W", section.current_value(), section.max_value());
section.set_value_text(text);
});
self.reset_button.connect_clicked(clone!(
#[strong]
obj,
move |_| {
obj.set_current_value(obj.default_value());
}
));
}
}
impl WidgetImpl for PowerCapSection {}
impl BoxImpl for PowerCapSection {}
}

View File

@ -1,33 +0,0 @@
using Gtk 4.0;
template $PowerCapSection: $PageSection {
name: "Power usage limit";
Box {
orientation: horizontal;
Label value_label {
label: bind template.value-text;
}
Scale {
orientation: horizontal;
hexpand: true;
round-digits: 0;
margin-start: 5;
margin-end: 5;
draw-value: false;
adjustment: adjustment;
}
Button reset_button {
label: "Default";
}
}
}
$OcAdjustment adjustment {
value: bind template.current-value bidirectional;
lower: bind template.min-value;
upper: bind template.max-value;
}