From 4e95e4e655f4dd180b5ba3e41a6957ab9535fed2 Mon Sep 17 00:00:00 2001 From: david-swift Date: Thu, 14 Aug 2025 23:24:30 +0200 Subject: [PATCH] Implement proper self destruction of signals --- Sources/Core/Model/Signals/SignalData.swift | 26 ++++++++++++++----- Sources/Core/Model/Storage.swift | 2 +- Sources/Core/View/Dialogs/AlertDialog.swift | 7 ++--- .../Core/View/Dialogs/PreferencesDialog.swift | 8 +++--- Sources/Core/Window/Window.swift | 4 ++- 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/Sources/Core/Model/Signals/SignalData.swift b/Sources/Core/Model/Signals/SignalData.swift index 03c359a..0a80724 100644 --- a/Sources/Core/Model/Signals/SignalData.swift +++ b/Sources/Core/Model/Signals/SignalData.swift @@ -12,6 +12,8 @@ public class SignalData { /// The closure. public var closure: ([Any?]) -> Void + /// Destroy the class. + public var selfDestruction: (() -> Void)? /// The closure as a C handler. var handler: @convention(c) (UnsafeMutableRawPointer, UnsafeMutableRawPointer) -> Void { @@ -61,15 +63,20 @@ public class SignalData { } /// Initialize the signal data. - /// - Parameter closure: The signal's closure. - public convenience init(closure: @escaping () -> Void) { - self.init { _ in closure() } + /// - Parameters: + /// - closure: The signal's closure. + /// - destroy: The self destruction. + public convenience init(closure: @escaping () -> Void, destroy: (() -> Void)? = nil) { + self.init(closure: { _ in closure() }, destroy: destroy) } /// Initialize the signal data. - /// - Parameter closure: The signal's closure. - public init(closure: @escaping ([Any]) -> Void) { + /// - Parameters: + /// - closure: The signal's closure. + /// - destroy: The self destruction. + public init(closure: @escaping ([Any]) -> Void, destroy: (() -> Void)? = nil) { self.closure = closure + self.selfDestruction = destroy } /// Connect the signal data to a signal. @@ -88,12 +95,19 @@ public class SignalData { } else { callback = unsafeBitCast(handler, to: GCallback.self) } + let destroy: GClosureNotify = { data, _ in + guard let data else { + return + } + let signalData: SignalData = Unmanaged.fromOpaque(data).takeUnretainedValue() + signalData.selfDestruction?() + } g_signal_connect_data( pointer, signal, callback, Unmanaged.passUnretained(self).toOpaque().cast(), - nil, + destroy, G_CONNECT_AFTER ) } diff --git a/Sources/Core/Model/Storage.swift b/Sources/Core/Model/Storage.swift index ad1c125..16361ee 100644 --- a/Sources/Core/Model/Storage.swift +++ b/Sources/Core/Model/Storage.swift @@ -70,7 +70,7 @@ extension Storage { if let data = fields[name + id] as? SignalData { data.closure = handler } else { - let data = SignalData(closure: handler) + let data = SignalData(closure: handler) { [self] in fields[name + id] = nil } fields[name + id] = data data.connect(pointer: (pointer ?? opaquePointer)?.cast(), signal: name, argCount: argCount) } diff --git a/Sources/Core/View/Dialogs/AlertDialog.swift b/Sources/Core/View/Dialogs/AlertDialog.swift index 2a99662..6986518 100644 --- a/Sources/Core/View/Dialogs/AlertDialog.swift +++ b/Sources/Core/View/Dialogs/AlertDialog.swift @@ -125,8 +125,10 @@ public struct AlertDialog: AdwaitaWidget { ) where Data: ViewRenderData { storage.fields[Self.visibleID + id] = _visible child.updateStorage(storage, data: data, updateProperties: updateProperties, type: type) - if let storage = storage.content["extra-child"]?.first, visible { - extraChild?.updateStorage(storage, data: data, updateProperties: updateProperties, type: type) + defer { + if let storage = storage.content["extra-child"]?.first, visible { + extraChild?.updateStorage(storage, data: data, updateProperties: updateProperties, type: type) + } } guard updateProperties else { return @@ -168,7 +170,6 @@ public struct AlertDialog: AdwaitaWidget { let dialog = storage.content[Self.dialogID + id]?.first?.opaquePointer adw_dialog_close(dialog?.cast()) storage.content[Self.dialogID] = [] - storage.content["extra-child"] = nil } } } diff --git a/Sources/Core/View/Dialogs/PreferencesDialog.swift b/Sources/Core/View/Dialogs/PreferencesDialog.swift index 25d58a5..7b7db6c 100644 --- a/Sources/Core/View/Dialogs/PreferencesDialog.swift +++ b/Sources/Core/View/Dialogs/PreferencesDialog.swift @@ -173,9 +173,11 @@ public struct PreferencesDialog: AdwaitaWidget { if let storage = storage.content[.mainContent]?.first { child.updateStorage(storage, data: data, updateProperties: updateProperties, type: type) } - for (index, page) in pages.enumerated() { - if let preferences = storage.content["preferences-\(index)"] { - page.update(groups: preferences, data: data, updateProperties: updateProperties) + defer { + for (index, page) in pages.enumerated() { + if let preferences = storage.content["preferences-\(index)"] { + page.update(groups: preferences, data: data, updateProperties: updateProperties) + } } } guard updateProperties else { diff --git a/Sources/Core/Window/Window.swift b/Sources/Core/Window/Window.swift index af6cbf6..efa989c 100644 --- a/Sources/Core/Window/Window.swift +++ b/Sources/Core/Window/Window.swift @@ -121,10 +121,12 @@ public struct Window: AdwaitaSceneElement { .storage(data: .init(sceneStorage: storage, appStorage: app), type: AdwaitaMainView.self) adw_application_window_set_content(window.pointer?.cast(), viewStorage.opaquePointer?.cast()) storage.content[.mainContent] = [viewStorage] + let observeID = "destroy" let data = SignalData { storage.destroy = true + } destroy: { + window.signals[observeID] = nil } - let observeID = "destroy" data.connect(pointer: window.pointer, signal: observeID) window.signals[observeID] = data let template = getTemplate(content: content)