198 lines
6.4 KiB
Swift

//
// ViewStorage.swift
// Adwaita
//
// Created by david-swift on 31.08.23.
//
import CAdw
/// Store a rendered view in a view storage.
public class ViewStorage {
/// The pointer.
public var pointer: OpaquePointer?
/// The view's content.
public var content: [String: [ViewStorage]]
/// The view's state (used in `StateWrapper`).
public var state: [String: StateProtocol]
/// The signal handlers.
public var handlers: [String: SignalData] = [:]
/// Other properties.
public var fields: [String: Any] = [:]
/// Initialize a view storage.
/// - Parameters:
/// - pointer: The pointer to the Gtk widget.
/// - content: The view's content.
/// - state: The view's state.
public init(
_ pointer: OpaquePointer?,
content: [String: [ViewStorage]] = [:],
state: [String: StateProtocol] = [:]
) {
self.pointer = pointer
self.content = content
self.state = state
}
/// Data to pass to signal handlers.
public class SignalData {
/// The closure.
public var closure: ([Any]) -> Void
/// The closure as a C handler.
var handler: @convention(c) (UnsafeMutableRawPointer, UnsafeMutableRawPointer) -> Void {
{ _, data in
let data = unsafeBitCast(data, to: SignalData.self)
data.closure([])
}
}
/// The closure as a C handler with three parameters.
var threeParamsHandler: @convention(c) (
UnsafeMutableRawPointer,
UnsafeRawPointer?,
UnsafeMutableRawPointer
) -> Void {
{ _, arg1, data in
let data = unsafeBitCast(data, to: SignalData.self)
data.closure([arg1])
}
}
/// The closure as a C handler with four parameters.
var fourParamsHandler: @convention(c) (
UnsafeMutableRawPointer,
UnsafeRawPointer?,
UnsafeRawPointer?,
UnsafeMutableRawPointer
) -> Void {
{ _, arg1, arg2, data in
let data = unsafeBitCast(data, to: SignalData.self)
data.closure([arg1, arg2])
}
}
/// The closure as a C handler with five parameters.
var fiveParamsHandler: @convention(c) (
UnsafeMutableRawPointer,
UnsafeRawPointer?,
Double,
Double,
UnsafeMutableRawPointer
) -> Void {
{ _, arg1, arg2, arg3, data in
let data = unsafeBitCast(data, to: SignalData.self)
data.closure([arg1, arg2, arg3])
}
}
/// Initialize the signal data.
/// - Parameter closure: The signal's closure.
public convenience init(closure: @escaping () -> Void) {
self.init { _ in closure() }
}
/// Initialize the signal data.
/// - Parameter closure: The signal's closure.
public init(closure: @escaping ([Any]) -> Void) {
self.closure = closure
}
}
/// Connect a handler to the observer of a property.
/// - Parameters:
/// - name: The property's name.
/// - id: The handlers id to separate form others connecting to the signal.
/// - connectFlags: The GConnectFlags.
/// - handler: The signal's handler.
public func notify(
name: String,
id: String = "",
connectFlags: GConnectFlags = G_CONNECT_AFTER,
handler: @escaping () -> Void
) {
let name = "notify::" + name
connectSignal(name: name, id: id, connectFlags: connectFlags, argCount: 1, handler: handler)
}
/// Connect a handler to a signal.
/// - Parameters:
/// - name: The signal's name.
/// - id: The handlers id to separate form others connecting to the signal.
/// - connectFlags: The GConnectFlags.
/// - argCount: The number of additional arguments (without the first and the last one).
/// - handler: The signal's handler.
public func connectSignal(
name: String,
id: String = "",
connectFlags: GConnectFlags = G_CONNECT_AFTER,
argCount: Int = 0,
handler: @escaping () -> Void
) {
connectSignal(name: name, id: id, connectFlags: connectFlags, argCount: argCount) { _ in
handler()
}
}
/// Connect a handler to a signal.
/// - Parameters:
/// - name: The signal's name.
/// - id: The handlers id to separate form others connecting to the signal.
/// - connectFlags: The GConnectFlags.
/// - argCount: The number of additional arguments (without the first and the last one).
/// - handler: The signal's handler.
public func connectSignal(
name: String,
id: String = "",
connectFlags: GConnectFlags = G_CONNECT_AFTER,
argCount: Int = 0,
handler: @escaping ([Any]) -> Void
) {
if let data = handlers[name + id] {
data.closure = handler
} else {
let data = SignalData(closure: handler)
handlers[name + id] = data
let callback: GCallback
let three = 3
let two = 2
if argCount >= three {
callback = unsafeBitCast(data.fiveParamsHandler, to: GCallback.self)
} else if argCount == two {
callback = unsafeBitCast(data.fourParamsHandler, to: GCallback.self)
} else if argCount == 1 {
callback = unsafeBitCast(data.threeParamsHandler, to: GCallback.self)
} else {
callback = unsafeBitCast(data.handler, to: GCallback.self)
}
g_signal_connect_data(
pointer?.cast(),
name,
callback,
Unmanaged.passUnretained(data).toOpaque().cast(),
nil,
connectFlags
)
}
}
/// Modify the view.
/// - Parameter modify: The modification function.
public func modify(_ modify: (OpaquePointer?) -> Void) {
modify(pointer)
}
/// Convert the pointer to a pointer of a certain type and modify the view.
/// - Parameters:
/// - type: The pointer's type.
/// - modify: The modification function.
public func modify<T>(_ type: T.Type, _ modify: (UnsafeMutablePointer<T>?) -> Void) {
modify(pointer?.cast())
}
}