fix: memory leaks in multiple dialogs due to reference cycles (#855)

This commit is contained in:
Ilya Zlobintsev
2026-01-11 13:58:58 +02:00
committed by GitHub
parent 8cf56f2128
commit 689e070368
6 changed files with 58 additions and 29 deletions
@@ -9,7 +9,8 @@ use gtk::{
gdk, gdk,
glib::{subclass::types::ObjectSubclassIsExt, types::StaticType, value::ToValue}, glib::{subclass::types::ObjectSubclassIsExt, types::StaticType, value::ToValue},
prelude::{ prelude::{
AdjustmentExt, BoxExt, ButtonExt, CheckButtonExt, OrientableExt, PopoverExt, WidgetExt, AdjustmentExt, BoxExt, ButtonExt, CheckButtonExt, ObjectExt, OrientableExt, PopoverExt,
WidgetExt,
}, },
}; };
use i18n_embed_fl::fl; use i18n_embed_fl::fl;
@@ -108,17 +109,23 @@ impl relm4::factory::FactoryComponent for PlotComponent {
set_actions: gdk::DragAction::MOVE, set_actions: gdk::DragAction::MOVE,
set_types: &[DynamicIndexValue::static_type()], set_types: &[DynamicIndexValue::static_type()],
connect_enter[root] => move |_, _, _| { connect_enter[root = root.downgrade()] => move |_, _, _| {
root.set_opacity(0.5); if let Some(root) = root.upgrade() {
root.set_opacity(0.5);
}
gdk::DragAction::MOVE gdk::DragAction::MOVE
}, },
connect_leave[root] => move |_| { connect_leave[root = root.downgrade()] => move |_| {
root.set_opacity(1.0); if let Some(root) = root.upgrade() {
root.set_opacity(1.0);
}
}, },
connect_drop[root, index, sender] => move |_, value, _, _| { connect_drop[root = root.downgrade(), index, sender] => move |_, value, _, _| {
root.set_opacity(1.0); if let Some(root) = root.upgrade() {
root.set_opacity(1.0);
}
if let Ok(DynamicIndexValue(source_index)) = value.get::<DynamicIndexValue>() { if let Ok(DynamicIndexValue(source_index)) = value.get::<DynamicIndexValue>() {
sender.output(GraphsWindowMsg::SwapPlots(index.clone(), source_index)).unwrap(); sender.output(GraphsWindowMsg::SwapPlots(index.clone(), source_index)).unwrap();
+7 -4
View File
@@ -22,8 +22,7 @@ use relm4::{
factory::FactoryVecDeque, factory::FactoryVecDeque,
prelude::DynamicIndex, prelude::DynamicIndex,
typed_view::list::{RelmListItem, TypedListView}, typed_view::list::{RelmListItem, TypedListView},
Component, ComponentController, ComponentParts, ComponentSender, RelmIterChildrenExt, Component, ComponentParts, ComponentSender, RelmIterChildrenExt, RelmWidgetExt,
RelmWidgetExt,
}; };
use std::sync::Arc; use std::sync::Arc;
use tracing::debug; use tracing::debug;
@@ -35,6 +34,8 @@ pub struct Header {
selector_label: String, selector_label: String,
system_info: SystemInfo, system_info: SystemInfo,
device_flags: Vec<DeviceFlag>, device_flags: Vec<DeviceFlag>,
new_profile_diag: Option<relm4::Controller<NewProfileDialog>>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -302,6 +303,7 @@ impl Component for Header {
profiles_info: ProfilesInfo::default(), profiles_info: ProfilesInfo::default(),
system_info, system_info,
device_flags: Vec::new(), device_flags: Vec::new(),
new_profile_diag: None,
}; };
let gpu_selector = &model.gpu_selector.view; let gpu_selector = &model.gpu_selector.view;
@@ -395,12 +397,13 @@ impl Component for Header {
HeaderMsg::CreateProfile => { HeaderMsg::CreateProfile => {
sender.input(HeaderMsg::ClosePopover); sender.input(HeaderMsg::ClosePopover);
let mut diag_controller = NewProfileDialog::builder() let diag_controller = NewProfileDialog::builder()
.launch(self.custom_profiles()) .launch(self.custom_profiles())
.forward(sender.output_sender(), |(name, base)| { .forward(sender.output_sender(), |(name, base)| {
AppMsg::CreateProfile(name, base) AppMsg::CreateProfile(name, base)
}); });
diag_controller.detach_runtime();
self.new_profile_diag = Some(diag_controller);
} }
HeaderMsg::RenameProfile(index) => { HeaderMsg::RenameProfile(index) => {
sender.input(HeaderMsg::ClosePopover); sender.input(HeaderMsg::ClosePopover);
@@ -67,8 +67,10 @@ impl Component for NewProfileDialog {
set_label: &fl!(I18N, "cancel"), set_label: &fl!(I18N, "cancel"),
set_hexpand: true, set_hexpand: true,
connect_clicked[root] => move |_| { connect_clicked[root = root.downgrade()] => move |_| {
root.hide(); if let Some(root) = root.upgrade() {
root.close();
}
}, },
}, },
@@ -122,7 +124,7 @@ impl Component for NewProfileDialog {
.output((self.name_buffer.text().to_string(), base)) .output((self.name_buffer.text().to_string(), base))
.unwrap(); .unwrap();
root.hide(); root.close();
} }
} }
} }
+10 -7
View File
@@ -2,6 +2,7 @@ pub mod profile_row;
use crate::app::{msg::AppMsg, APP_BROKER}; use crate::app::{msg::AppMsg, APP_BROKER};
use crate::I18N; use crate::I18N;
use gtk::prelude::ObjectExt;
use gtk::{ use gtk::{
pango, pango,
prelude::{ prelude::{
@@ -64,14 +65,16 @@ impl relm4::Component for ProfileRuleWindow {
set_default_size: (600, 300), set_default_size: (600, 300),
set_title: Some(&fl!(I18N, "profile-rules")), set_title: Some(&fl!(I18N, "profile-rules")),
set_transient_for: Some(&root_window), set_transient_for: Some(&root_window),
connect_response[root, sender] => move |_, response| { connect_response[root = root.downgrade(), sender] => move |_, response| {
match response { if let Some(root) = root.upgrade() {
gtk::ResponseType::Accept => { match response {
sender.input(ProfileRuleWindowMsg::Save); gtk::ResponseType::Accept => {
root.close(); sender.input(ProfileRuleWindowMsg::Save);
root.close();
}
gtk::ResponseType::Cancel => root.close(),
_ => (),
} }
gtk::ResponseType::Cancel => root.close(),
_ => (),
} }
}, },
+17 -5
View File
@@ -19,6 +19,8 @@ pub struct SoftwarePage {
vulkan_driver_selector: relm4::Controller<SimpleComboBox<String>>, vulkan_driver_selector: relm4::Controller<SimpleComboBox<String>>,
opencl_platform_selector: relm4::Controller<SimpleComboBox<String>>, opencl_platform_selector: relm4::Controller<SimpleComboBox<String>>,
vulkan_window: Option<relm4::Controller<VulkanFeaturesWindow>>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -250,6 +252,7 @@ impl relm4::SimpleComponent for SoftwarePage {
device_info: None, device_info: None,
vulkan_driver_selector, vulkan_driver_selector,
opencl_platform_selector, opencl_platform_selector,
vulkan_window: None,
}; };
let mut daemon_version = format!("{}-{}", system_info.version, system_info.profile); let mut daemon_version = format!("{}-{}", system_info.version, system_info.profile);
@@ -329,12 +332,18 @@ impl relm4::SimpleComponent for SoftwarePage {
} }
SoftwarePageMsg::ShowVulkanFeatures => { SoftwarePageMsg::ShowVulkanFeatures => {
if let Some(vulkan_info) = &self.selected_vulkan_info() { if let Some(vulkan_info) = &self.selected_vulkan_info() {
show_features_window("Vulkan Features", &vulkan_info.features); self.vulkan_window = Some(show_features_window(
"Vulkan Features",
&vulkan_info.features,
));
} }
} }
SoftwarePageMsg::ShowVulkanExtensions => { SoftwarePageMsg::ShowVulkanExtensions => {
if let Some(vulkan_info) = self.selected_vulkan_info() { if let Some(vulkan_info) = self.selected_vulkan_info() {
show_features_window("Vulkan Extensions", &vulkan_info.extensions); self.vulkan_window = Some(show_features_window(
"Vulkan Extensions",
&vulkan_info.extensions,
));
} }
} }
SoftwarePageMsg::SelectionChanged => (), SoftwarePageMsg::SelectionChanged => (),
@@ -366,7 +375,10 @@ impl SoftwarePage {
} }
} }
fn show_features_window(title: &str, values: &IndexMap<String, bool>) { fn show_features_window(
title: &str,
values: &IndexMap<String, bool>,
) -> relm4::Controller<VulkanFeaturesWindow> {
let values = values let values = values
.into_iter() .into_iter()
.map(|(name, &supported)| VulkanFeature { .map(|(name, &supported)| VulkanFeature {
@@ -375,9 +387,9 @@ fn show_features_window(title: &str, values: &IndexMap<String, bool>) {
}) })
.collect(); .collect();
let mut window_controller = VulkanFeaturesWindow::builder() let window_controller = VulkanFeaturesWindow::builder()
.launch((values, title.to_owned())) .launch((values, title.to_owned()))
.detach(); .detach();
window_controller.detach_runtime();
window_controller.widget().present(); window_controller.widget().present();
window_controller
} }
@@ -1,6 +1,6 @@
use gtk::{ use gtk::{
glib::GString, glib::GString,
prelude::{EditableExt, GtkWindowExt, OrientableExt, WidgetExt}, prelude::{EditableExt, GtkWindowExt, ObjectExt, OrientableExt, WidgetExt},
NoSelection, NoSelection,
}; };
use relm4::{ use relm4::{
@@ -39,8 +39,10 @@ impl SimpleComponent for VulkanFeaturesWindow {
sender.input(AppMsg::FilterChanged(entry.text())); sender.input(AppMsg::FilterChanged(entry.text()));
}, },
connect_stop_search[root] => move |_| { connect_stop_search[root = root.downgrade()] => move |_| {
root.close(); if let Some(root) = root.upgrade() {
root.close();
}
}, },
}, },