mirror of
https://github.com/phranck/TUIkit.git
synced 2026-06-20 09:54:37 +00:00
Merge pull request #24 from phranck/refactor/singleton-elimination-phase1
Refactor: Remove 5 deprecated singletons (Phase 1)
This commit is contained in:
@@ -89,6 +89,9 @@ public final class EnvironmentStorage: @unchecked Sendable {
|
||||
/// The shared environment storage.
|
||||
public static let shared = EnvironmentStorage()
|
||||
|
||||
/// Lock protecting all mutable state.
|
||||
private let lock = NSLock()
|
||||
|
||||
/// The current environment values.
|
||||
private var current = EnvironmentValues()
|
||||
|
||||
@@ -99,20 +102,32 @@ public final class EnvironmentStorage: @unchecked Sendable {
|
||||
|
||||
/// The current environment values.
|
||||
public var environment: EnvironmentValues {
|
||||
get { current }
|
||||
set { current = newValue }
|
||||
get {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
return current
|
||||
}
|
||||
set {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
current = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Pushes a new environment onto the stack.
|
||||
///
|
||||
/// - Parameter environment: The environment to push.
|
||||
public func push(_ environment: EnvironmentValues) {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
stack.append(current)
|
||||
current = environment
|
||||
}
|
||||
|
||||
/// Pops the current environment and restores the previous one.
|
||||
public func pop() {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
if let previous = stack.popLast() {
|
||||
current = previous
|
||||
}
|
||||
@@ -132,6 +147,8 @@ public final class EnvironmentStorage: @unchecked Sendable {
|
||||
|
||||
/// Resets the environment to its initial state.
|
||||
public func reset() {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
current = EnvironmentValues()
|
||||
stack.removeAll()
|
||||
}
|
||||
|
||||
@@ -250,10 +250,6 @@ extension KeyEvent {
|
||||
/// Views can register handlers that are called when keys are pressed.
|
||||
/// Handlers are processed in reverse order (most recent first).
|
||||
public final class KeyEventDispatcher: @unchecked Sendable {
|
||||
/// The shared dispatcher instance.
|
||||
@available(*, deprecated, message: "Use TUIContext.keyEventDispatcher instead")
|
||||
public static let shared = KeyEventDispatcher()
|
||||
|
||||
/// Registered key handlers.
|
||||
private var handlers: [(KeyEvent) -> Bool] = []
|
||||
|
||||
|
||||
@@ -95,10 +95,6 @@ public struct PreferenceValues: @unchecked Sendable {
|
||||
|
||||
/// Thread-local storage for collecting preferences during rendering.
|
||||
public final class PreferenceStorage: @unchecked Sendable {
|
||||
/// The shared preference storage.
|
||||
@available(*, deprecated, message: "Use TUIContext.preferences instead")
|
||||
public static let shared = PreferenceStorage()
|
||||
|
||||
/// Stack of preference values for nested rendering.
|
||||
private var stack: [PreferenceValues] = [PreferenceValues()]
|
||||
|
||||
|
||||
@@ -7,92 +7,6 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
// MARK: - Lifecycle Tracker
|
||||
|
||||
/// Tracks which views have appeared to prevent duplicate onAppear calls.
|
||||
///
|
||||
/// Since views are recreated on each render, we use a token-based system
|
||||
/// to track which views have already triggered their onAppear action.
|
||||
public final class LifecycleTracker: @unchecked Sendable {
|
||||
/// Shared instance for the running application.
|
||||
@available(*, deprecated, message: "Use TUIContext.lifecycle instead")
|
||||
public static let shared = LifecycleTracker()
|
||||
|
||||
/// Lock protecting all mutable state.
|
||||
private let lock = NSLock()
|
||||
|
||||
/// Set of tokens that have appeared.
|
||||
private var appearedTokens: Set<String> = []
|
||||
|
||||
/// Set of tokens that are currently visible (for onDisappear tracking).
|
||||
private var visibleTokens: Set<String> = []
|
||||
|
||||
/// Tokens seen during the current render pass.
|
||||
private var currentRenderTokens: Set<String> = []
|
||||
|
||||
/// Creates a new lifecycle tracker.
|
||||
public init() {}
|
||||
|
||||
/// Marks the start of a new render pass.
|
||||
internal func beginRenderPass() {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
currentRenderTokens.removeAll()
|
||||
}
|
||||
|
||||
/// Marks the end of a render pass and triggers onDisappear for views that are no longer visible.
|
||||
internal func endRenderPass(onDisappear: [String: () -> Void]) {
|
||||
lock.lock()
|
||||
let disappearedTokens = visibleTokens.subtracting(currentRenderTokens)
|
||||
for token in disappearedTokens {
|
||||
appearedTokens.remove(token)
|
||||
}
|
||||
visibleTokens = currentRenderTokens
|
||||
lock.unlock()
|
||||
|
||||
// Execute callbacks outside the lock to avoid deadlocks
|
||||
for token in disappearedTokens {
|
||||
onDisappear[token]?()
|
||||
}
|
||||
}
|
||||
|
||||
/// Records that a view with the given token appeared.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - token: Unique identifier for the view.
|
||||
/// - action: The onAppear action to execute.
|
||||
/// - Returns: True if this is the first appearance (action should run).
|
||||
internal func recordAppear(token: String, action: () -> Void) -> Bool {
|
||||
lock.lock()
|
||||
currentRenderTokens.insert(token)
|
||||
|
||||
if !appearedTokens.contains(token) {
|
||||
appearedTokens.insert(token)
|
||||
lock.unlock()
|
||||
action()
|
||||
return true
|
||||
}
|
||||
lock.unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
/// Checks if a view has appeared before.
|
||||
internal func hasAppeared(token: String) -> Bool {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
return appearedTokens.contains(token)
|
||||
}
|
||||
|
||||
/// Resets all tracking state.
|
||||
internal func reset() {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
appearedTokens.removeAll()
|
||||
visibleTokens.removeAll()
|
||||
currentRenderTokens.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - OnAppear Modifier
|
||||
|
||||
/// A modifier that executes an action when a view first appears.
|
||||
@@ -123,43 +37,6 @@ extension OnAppearModifier: Renderable {
|
||||
|
||||
// MARK: - OnDisappear Modifier
|
||||
|
||||
/// Storage for onDisappear callbacks.
|
||||
public final class DisappearCallbackStorage: @unchecked Sendable {
|
||||
@available(*, deprecated, message: "Use TUIContext.lifecycle instead")
|
||||
public static let shared = DisappearCallbackStorage()
|
||||
|
||||
/// Lock protecting the callbacks dictionary.
|
||||
private let lock = NSLock()
|
||||
private var callbacks: [String: () -> Void] = [:]
|
||||
|
||||
/// Creates a new disappear callback storage.
|
||||
public init() {}
|
||||
|
||||
internal func register(token: String, action: @escaping () -> Void) {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
callbacks[token] = action
|
||||
}
|
||||
|
||||
internal func unregister(token: String) {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
callbacks.removeValue(forKey: token)
|
||||
}
|
||||
|
||||
internal var allCallbacks: [String: () -> Void] {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
return callbacks
|
||||
}
|
||||
|
||||
internal func reset() {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
callbacks.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
/// A modifier that executes an action when a view disappears.
|
||||
public struct OnDisappearModifier<Content: View>: View {
|
||||
/// The content view.
|
||||
@@ -212,47 +89,6 @@ public struct TaskModifier<Content: View>: View {
|
||||
}
|
||||
}
|
||||
|
||||
/// Storage for running tasks.
|
||||
public final class TaskStorage: @unchecked Sendable {
|
||||
@available(*, deprecated, message: "Use TUIContext.lifecycle instead")
|
||||
public static let shared = TaskStorage()
|
||||
|
||||
/// Lock protecting the tasks dictionary.
|
||||
private let lock = NSLock()
|
||||
private var tasks: [String: Task<Void, Never>] = [:]
|
||||
|
||||
/// Creates a new task storage.
|
||||
public init() {}
|
||||
|
||||
internal func startTask(token: String, priority: TaskPriority, operation: @escaping @Sendable () async -> Void) {
|
||||
lock.lock()
|
||||
// Cancel existing task if any
|
||||
tasks[token]?.cancel()
|
||||
|
||||
// Start new task
|
||||
tasks[token] = Task(priority: priority) {
|
||||
await operation()
|
||||
}
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
internal func cancelTask(token: String) {
|
||||
lock.lock()
|
||||
tasks[token]?.cancel()
|
||||
tasks.removeValue(forKey: token)
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
internal func reset() {
|
||||
lock.lock()
|
||||
for task in tasks.values {
|
||||
task.cancel()
|
||||
}
|
||||
tasks.removeAll()
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
extension TaskModifier: Renderable {
|
||||
public func renderToBuffer(context: RenderContext) -> FrameBuffer {
|
||||
let lifecycle = context.tuiContext.lifecycle
|
||||
|
||||
Reference in New Issue
Block a user