Skip to content

Commit bb4584b

Browse files
committed
Add gesture and lifecycle modifiers (onTapGesture, onAppear, task, onChange, disabled)
1 parent 5c4c19c commit bb4584b

1 file changed

Lines changed: 113 additions & 0 deletions

File tree

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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

Comments
 (0)