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