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.
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
)
}

View File

@ -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)
}

View File

@ -125,9 +125,11 @@ public struct AlertDialog: AdwaitaWidget {
) where Data: ViewRenderData {
storage.fields[Self.visibleID + id] = _visible
child.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
}
}
}

View File

@ -173,11 +173,13 @@ public struct PreferencesDialog: AdwaitaWidget {
if let storage = storage.content[.mainContent]?.first {
child.updateStorage(storage, data: data, updateProperties: updateProperties, type: type)
}
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 {
return
}

View File

@ -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)