Skip to content

Commit 943f25e

Browse files
committed
Add toolbar and placed toolbar items
1 parent 775ecc9 commit 943f25e

1 file changed

Lines changed: 83 additions & 0 deletions

File tree

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//
2+
// Toolbar.swift
3+
// AndroidSwiftUICore
4+
//
5+
// Toolbar items are views, not data, so — unlike `navigationTitle` — they can't
6+
// ride to the navigation chrome through the title sink. They travel as hidden
7+
// children of the screen node instead (the mechanism sheets and alerts use),
8+
// each tagged with its placement, and the interpreter lifts them into the bar.
9+
//
10+
11+
/// Where a toolbar item sits in the navigation chrome.
12+
public enum ToolbarItemPlacement: String, Sendable {
13+
case automatic
14+
case navigationBarLeading
15+
case navigationBarTrailing
16+
/// Replaces the screen's title.
17+
case principal
18+
case bottomBar
19+
}
20+
21+
/// A single positioned toolbar entry.
22+
public struct ToolbarItem<Content: View>: View {
23+
24+
internal let placement: ToolbarItemPlacement
25+
internal let content: Content
26+
27+
public init(
28+
placement: ToolbarItemPlacement = .automatic,
29+
@ViewBuilder content: () -> Content
30+
) {
31+
self.placement = placement
32+
self.content = content()
33+
}
34+
35+
public typealias Body = Never
36+
}
37+
38+
extension ToolbarItem: PrimitiveView {
39+
40+
public func _render(in context: ResolveContext) -> RenderNode {
41+
RenderNode(
42+
type: "ToolbarItem",
43+
id: context.path,
44+
props: ["placement": .string(placement.rawValue)],
45+
children: Evaluator.resolveChildren(content, context.descending("content"))
46+
)
47+
}
48+
}
49+
50+
public struct _ToolbarView<Content: View, Items: View>: View {
51+
internal let content: Content
52+
internal let items: Items
53+
public typealias Body = Never
54+
}
55+
56+
extension _ToolbarView: PrimitiveView {
57+
58+
public func _render(in context: ResolveContext) -> RenderNode {
59+
var node = Evaluator.resolve(content, context.descending("content"))
60+
let resolved = Evaluator.resolveChildren(items, context.descending("toolbar"))
61+
for item in resolved {
62+
if item.type == "ToolbarItem" {
63+
node.children.append(item)
64+
} else {
65+
// a bare view in the builder is an automatically-placed item
66+
node.children.append(RenderNode(
67+
type: "ToolbarItem",
68+
id: item.id + "/item",
69+
props: ["placement": .string(ToolbarItemPlacement.automatic.rawValue)],
70+
children: [item]
71+
))
72+
}
73+
}
74+
node.props["hasToolbar"] = .bool(true)
75+
return node
76+
}
77+
}
78+
79+
public extension View {
80+
func toolbar<Items: View>(@ViewBuilder content: () -> Items) -> _ToolbarView<Self, Items> {
81+
_ToolbarView(content: self, items: content())
82+
}
83+
}

0 commit comments

Comments
 (0)