Implement proper self destruction of signals
Some checks failed
Deploy Docs / publish (push) Waiting to run
SwiftLint / SwiftLint (push) Has been cancelled

This commit is contained in:
david-swift 2025-08-14 23:24:30 +02:00
parent 049b5b54f8
commit 4e95e4e655
5 changed files with 33 additions and 14 deletions

View File

@ -12,6 +12,8 @@ public class SignalData {
/// The closure. /// The closure.
public var closure: ([Any?]) -> Void public var closure: ([Any?]) -> Void
/// Destroy the class.
public var selfDestruction: (() -> Void)?
/// The closure as a C handler. /// The closure as a C handler.
var handler: @convention(c) (UnsafeMutableRawPointer, UnsafeMutableRawPointer) -> Void { var handler: @convention(c) (UnsafeMutableRawPointer, UnsafeMutableRawPointer) -> Void {
@ -61,15 +63,20 @@ public class SignalData {
} }
/// Initialize the signal data. /// Initialize the signal data.
/// - Parameter closure: The signal's closure. /// - Parameters:
public convenience init(closure: @escaping () -> Void) { /// - closure: The signal's closure.
self.init { _ in 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. /// Initialize the signal data.
/// - Parameter closure: The signal's closure. /// - Parameters:
public init(closure: @escaping ([Any]) -> Void) { /// - closure: The signal's closure.
/// - destroy: The self destruction.
public init(closure: @escaping ([Any]) -> Void, destroy: (() -> Void)? = nil) {
self.closure = closure self.closure = closure
self.selfDestruction = destroy
} }
/// Connect the signal data to a signal. /// Connect the signal data to a signal.
@ -88,12 +95,19 @@ public class SignalData {
} else { } else {
callback = unsafeBitCast(handler, to: GCallback.self) 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( g_signal_connect_data(
pointer, pointer,
signal, signal,
callback, callback,
Unmanaged.passUnretained(self).toOpaque().cast(), Unmanaged.passUnretained(self).toOpaque().cast(),
nil, destroy,
G_CONNECT_AFTER G_CONNECT_AFTER
) )
} }

View File

@ -70,7 +70,7 @@ extension Storage {
if let data = fields[name + id] as? SignalData { if let data = fields[name + id] as? SignalData {
data.closure = handler data.closure = handler
} else { } else {
let data = SignalData(closure: handler) let data = SignalData(closure: handler) { [self] in fields[name + id] = nil }
fields[name + id] = data fields[name + id] = data
data.connect(pointer: (pointer ?? opaquePointer)?.cast(), signal: name, argCount: argCount) data.connect(pointer: (pointer ?? opaquePointer)?.cast(), signal: name, argCount: argCount)
} }

View File

@ -125,9 +125,11 @@ public struct AlertDialog: AdwaitaWidget {
) where Data: ViewRenderData { ) where Data: ViewRenderData {
storage.fields[Self.visibleID + id] = _visible storage.fields[Self.visibleID + id] = _visible
child.updateStorage(storage, data: data, updateProperties: updateProperties, type: type) child.updateStorage(storage, data: data, updateProperties: updateProperties, type: type)
defer {
if let storage = storage.content["extra-child"]?.first, visible { if let storage = storage.content["extra-child"]?.first, visible {
extraChild?.updateStorage(storage, data: data, updateProperties: updateProperties, type: type) extraChild?.updateStorage(storage, data: data, updateProperties: updateProperties, type: type)
} }
}
guard updateProperties else { guard updateProperties else {
return return
} }
@ -168,7 +170,6 @@ public struct AlertDialog: AdwaitaWidget {
let dialog = storage.content[Self.dialogID + id]?.first?.opaquePointer let dialog = storage.content[Self.dialogID + id]?.first?.opaquePointer
adw_dialog_close(dialog?.cast()) adw_dialog_close(dialog?.cast())
storage.content[Self.dialogID] = [] storage.content[Self.dialogID] = []
storage.content["extra-child"] = nil
} }
} }
} }

View File

@ -173,11 +173,13 @@ public struct PreferencesDialog: AdwaitaWidget {
if let storage = storage.content[.mainContent]?.first { if let storage = storage.content[.mainContent]?.first {
child.updateStorage(storage, data: data, updateProperties: updateProperties, type: type) child.updateStorage(storage, data: data, updateProperties: updateProperties, type: type)
} }
defer {
for (index, page) in pages.enumerated() { for (index, page) in pages.enumerated() {
if let preferences = storage.content["preferences-\(index)"] { if let preferences = storage.content["preferences-\(index)"] {
page.update(groups: preferences, data: data, updateProperties: updateProperties) page.update(groups: preferences, data: data, updateProperties: updateProperties)
} }
} }
}
guard updateProperties else { guard updateProperties else {
return return
} }

View File

@ -121,10 +121,12 @@ public struct Window: AdwaitaSceneElement {
.storage(data: .init(sceneStorage: storage, appStorage: app), type: AdwaitaMainView.self) .storage(data: .init(sceneStorage: storage, appStorage: app), type: AdwaitaMainView.self)
adw_application_window_set_content(window.pointer?.cast(), viewStorage.opaquePointer?.cast()) adw_application_window_set_content(window.pointer?.cast(), viewStorage.opaquePointer?.cast())
storage.content[.mainContent] = [viewStorage] storage.content[.mainContent] = [viewStorage]
let observeID = "destroy"
let data = SignalData { let data = SignalData {
storage.destroy = true storage.destroy = true
} destroy: {
window.signals[observeID] = nil
} }
let observeID = "destroy"
data.connect(pointer: window.pointer, signal: observeID) data.connect(pointer: window.pointer, signal: observeID)
window.signals[observeID] = data window.signals[observeID] = data
let template = getTemplate(content: content) let template = getTemplate(content: content)