|
| 1 | +// |
| 2 | +// InteractionModifiers.swift |
| 3 | +// AndroidSwiftUICore |
| 4 | +// |
| 5 | +// Gesture and lifecycle modifiers. These carry a closure, so they register a |
| 6 | +// callback against the resolve context (via `_CallbackModifier`) and embed its |
| 7 | +// id in the emitted node; the interpreter wires the id to a Compose |
| 8 | +// `clickable`, `DisposableEffect`, or `LaunchedEffect`. |
| 9 | +// |
| 10 | + |
| 11 | +// MARK: - onTapGesture |
| 12 | + |
| 13 | +public struct _OnTapGestureModifier: RenderModifier, _CallbackModifier { |
| 14 | + let action: () -> Void |
| 15 | + public var _modifierNode: ModifierNode { ModifierNode(kind: "onTapGesture") } |
| 16 | + public func _callbackNode(in context: ResolveContext) -> ModifierNode { |
| 17 | + let id = context.callbacks.register(.void(action)) |
| 18 | + return ModifierNode(kind: "onTapGesture", args: ["action": .int(Int(id))]) |
| 19 | + } |
| 20 | +} |
| 21 | + |
| 22 | +public extension View { |
| 23 | + func onTapGesture(perform action: @escaping () -> Void) -> ModifiedContent<Self, _OnTapGestureModifier> { |
| 24 | + modifier(_OnTapGestureModifier(action: action)) |
| 25 | + } |
| 26 | +} |
| 27 | + |
| 28 | +// MARK: - onAppear / onDisappear |
| 29 | + |
| 30 | +public struct _OnAppearModifier: RenderModifier, _CallbackModifier { |
| 31 | + let action: () -> Void |
| 32 | + public var _modifierNode: ModifierNode { ModifierNode(kind: "onAppear") } |
| 33 | + public func _callbackNode(in context: ResolveContext) -> ModifierNode { |
| 34 | + let id = context.callbacks.register(.void(action)) |
| 35 | + return ModifierNode(kind: "onAppear", args: ["action": .int(Int(id))]) |
| 36 | + } |
| 37 | +} |
| 38 | + |
| 39 | +public struct _OnDisappearModifier: RenderModifier, _CallbackModifier { |
| 40 | + let action: () -> Void |
| 41 | + public var _modifierNode: ModifierNode { ModifierNode(kind: "onDisappear") } |
| 42 | + public func _callbackNode(in context: ResolveContext) -> ModifierNode { |
| 43 | + let id = context.callbacks.register(.void(action)) |
| 44 | + return ModifierNode(kind: "onDisappear", args: ["action": .int(Int(id))]) |
| 45 | + } |
| 46 | +} |
| 47 | + |
| 48 | +public extension View { |
| 49 | + func onAppear(perform action: @escaping () -> Void) -> ModifiedContent<Self, _OnAppearModifier> { |
| 50 | + modifier(_OnAppearModifier(action: action)) |
| 51 | + } |
| 52 | + func onDisappear(perform action: @escaping () -> Void) -> ModifiedContent<Self, _OnDisappearModifier> { |
| 53 | + modifier(_OnDisappearModifier(action: action)) |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +// MARK: - task |
| 58 | + |
| 59 | +public struct _TaskModifier: RenderModifier, _CallbackModifier { |
| 60 | + let action: @Sendable () async -> Void |
| 61 | + public var _modifierNode: ModifierNode { ModifierNode(kind: "task") } |
| 62 | + public func _callbackNode(in context: ResolveContext) -> ModifierNode { |
| 63 | + // The interpreter fires this once on appear; the closure runs as a Task. |
| 64 | + // (v1 does not cancel it on disappear.) |
| 65 | + let action = self.action |
| 66 | + let id = context.callbacks.register(.void { Task { await action() } }) |
| 67 | + return ModifierNode(kind: "task", args: ["action": .int(Int(id))]) |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +public extension View { |
| 72 | + func task(_ action: @escaping @Sendable () async -> Void) -> ModifiedContent<Self, _TaskModifier> { |
| 73 | + modifier(_TaskModifier(action: action)) |
| 74 | + } |
| 75 | +} |
| 76 | + |
| 77 | +// MARK: - onChange |
| 78 | + |
| 79 | +public struct _OnChangeModifier<V: Equatable>: RenderModifier, _CallbackModifier { |
| 80 | + let value: V |
| 81 | + let action: () -> Void |
| 82 | + public var _modifierNode: ModifierNode { ModifierNode(kind: "onChange") } |
| 83 | + public func _callbackNode(in context: ResolveContext) -> ModifierNode { |
| 84 | + let id = context.callbacks.register(.void(action)) |
| 85 | + // The interpreter fires the action when this token changes between |
| 86 | + // evaluations (skipping the first composition). |
| 87 | + return ModifierNode(kind: "onChange", args: [ |
| 88 | + "token": .string(String(describing: value)), |
| 89 | + "action": .int(Int(id)), |
| 90 | + ]) |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +public extension View { |
| 95 | + func onChange<V: Equatable>(of value: V, perform action: @escaping () -> Void) -> ModifiedContent<Self, _OnChangeModifier<V>> { |
| 96 | + modifier(_OnChangeModifier(value: value, action: action)) |
| 97 | + } |
| 98 | +} |
| 99 | + |
| 100 | +// MARK: - disabled |
| 101 | + |
| 102 | +public struct _DisabledModifier: RenderModifier { |
| 103 | + let disabled: Bool |
| 104 | + public var _modifierNode: ModifierNode { |
| 105 | + ModifierNode(kind: "disabled", args: ["value": .bool(disabled)]) |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +public extension View { |
| 110 | + func disabled(_ disabled: Bool) -> ModifiedContent<Self, _DisabledModifier> { |
| 111 | + modifier(_DisabledModifier(disabled: disabled)) |
| 112 | + } |
| 113 | +} |
0 commit comments