1use std::sync::OnceLock;
14use swiftui_native::prelude::*;
15use swiftui_native::{state, views};
16
17struct Message {
18 from: &'static str,
19 subject: &'static str,
20 unread: bool,
21}
22
23const MESSAGES: &[Message] = &[
24 Message { from: "Ada Lovelace", subject: "Notes on the Analytical Engine", unread: true },
25 Message { from: "Alan Turing", subject: "On computable numbers", unread: true },
26 Message { from: "Grace Hopper", subject: "Compiler progress", unread: false },
27 Message { from: "Barbara Liskov", subject: "Substitution", unread: false },
28];
29
30struct Inbox {
31 query: views::BoundState<String>,
32 searching: views::BoundState<bool>,
33}
34
35fn inbox() -> &'static Inbox {
36 static I: OnceLock<Inbox> = OnceLock::new();
37 I.get_or_init(|| Inbox {
38 query: views::BoundState::new(String::new()),
39 searching: views::BoundState::new(false),
40 })
41}
42
43fn matching() -> Vec<usize> {
46 let q = inbox().query.get().to_lowercase();
47 MESSAGES
48 .iter()
49 .enumerate()
50 .filter(|(_, m)| {
51 q.is_empty()
52 || m.from.to_lowercase().contains(&q)
53 || m.subject.to_lowercase().contains(&q)
54 })
55 .map(|(i, _)| i)
56 .collect()
57}
58
59fn detail(i: usize) -> View {
61 let m = &MESSAGES[i];
62 vstack_aligned_spacing(
63 &[
64 text(m.subject).text_style(TextStyle::Title).bold(),
65 text!("From {}", m.from).fg_system(SystemColor::Gray),
66 divider(),
67 text("Body text would go here.").fg_system(SystemColor::Gray),
68 spacer(),
69 ],
70 HorizontalAlignment::Leading,
71 Some(12.0),
72 )
73 .padding(20.0)
74 .navigation_title(m.subject)
75}
76
77fn row(i: usize) -> View {
78 let m = &MESSAGES[i];
79 hstack_spacing(
80 &[
81 view_if!(
83 m.unread,
84 circle_shape().fill_system(SystemColor::Blue).frame(8.0, 8.0),
85 empty().frame(8.0, 8.0)
86 ),
87 vstack_aligned(
88 &[
89 text(m.from).bold(),
90 text(m.subject).font_size(12.0).fg_system(SystemColor::Gray),
91 ],
92 HorizontalAlignment::Leading,
93 ),
94 spacer(),
95 ],
96 8.0,
97 )
98 .padding_edges(EdgeSet::Vertical, Some(4.0))
99 .a11y_label(&format!("{}, {}", m.from, m.subject))
100 .a11y_hint(if m.unread { "Unread" } else { "Read" })
101}
102
103fn linked_row(i: usize) -> View {
106 navigation_link_labelled(row(i), detail(i))
107 .swipe_actions(HorizontalEdge::Trailing, true, button("Delete", || {}))
108}
109
110fn body() -> View {
111 let i = inbox();
112 let rows = matching();
113 navigation_stack(
114 list(for_each_items(&rows, |&i| linked_row(i)))
115 .list_style(views::ListStyle::InsetListStyle)
116 .searchable(&i.query, &i.searching, SearchFieldPlacement::Automatic, "Search"),
117 )
118 .navigation_title("Inbox")
119 .toolbar(button("Compose", || {}).keyboard_shortcut(KeyboardShortcut::DefaultAction))
120}
121
122fn content_view() -> swiftui_native::ViewHandle {
123 state::root_view(|_| body().into_handle())
124}
125
126#[cfg(target_os = "macos")]
127#[path = "common/simulator.rs"]
128mod simulator;
129
130#[cfg(target_os = "macos")]
131fn main() {
132 if std::env::args().any(|a| a == "--sim" || a == "device") {
133 return simulator::main(&simulator::Bundle {
134 example: "inbox",
135 bundle_id: "dev.rswift.Inbox",
136 executable: "Inbox",
137 display_name: "Inbox",
138 launch_args: &[],
139 });
140 }
141 if std::env::args().any(|a| a == "--render") {
142 let out = concat!(env!("CARGO_MANIFEST_DIR"), "/../../target/inbox.png");
143 swiftui_native::render::render(&content_view(), 420, 520).write_png(out);
144 eprintln!("wrote {out}");
145 return;
146 }
147 show_window(&content_view(), "Inbox", 420.0, 520.0);
148}
149
150#[cfg(not(target_os = "macos"))]
151fn main() {
152 swiftui_native::app::run(content_view)
153}