Skip to main content

button

Function button 

Source
pub fn button(label: &str, action: impl Fn() + 'static) -> View
Expand description

Create a Button.

Examples found in repository?
examples/inbox.rs (line 107)
105fn 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}
More examples
Hide additional examples
examples/scenes.rs (line 58)
48fn main_window() -> ViewHandle {
49    vstack(&[
50        text("A SwiftUI app, no Swift source")
51            .font_size(20.0)
52            .padding(8.0),
53        divider(),
54        text(&format!("count: {}", counter().get()))
55            .font_size(34.0)
56            .padding(12.0),
57        hstack(&[
58            button("−", || counter().set(counter().get() - 1)),
59            button("+", || counter().set(counter().get() + 1)),
60        ]),
61        spacer(),
62        text("⌘, for Settings · the ★ in the menu bar has a menu")
63            .font_size(11.0)
64            .opacity(0.6),
65    ])
66    .padding(24.0)
67    .frame(420.0, 300.0)
68    .focused_scene_value(counter_focus_key(), counter().get())
69    .into_handle()
70}
71
72/// The focused-value key the window publishes its count under.
73///
74/// `.focusedSceneValue(\.key, value)` is how a window tells the commands above
75/// it what is selected. Both halves are synthesised at run time: the
76/// `FocusedValueKey` conformance *and* the `WritableKeyPath<FocusedValues, Int?>`
77/// — `FocusedValues` exports no properties, so unlike `EnvironmentValues` there
78/// is no SwiftUI modifier holding a key path to borrow.
79///
80/// The menu cannot *read* it yet: that needs `@FocusedValue`, a `DynamicProperty`
81/// SwiftUI instantiates itself, which is a different mechanism from anything
82/// here. What this demonstrates is the publishing half, in a real app.
83fn counter_focus_key() -> &'static views::FocusKey {
84    static K: std::sync::OnceLock<views::FocusKey> = std::sync::OnceLock::new();
85    K.get_or_init(|| views::focus_key("sceneCounter"))
86}
87
88fn settings_pane() -> ViewHandle {
89    vstack(&[
90        text("Preferences").font_size(18.0),
91        divider(),
92        text("This pane is a `Settings` scene — the one ⌘, opens."),
93        text(&format!("It sees the same state: {}", counter().get()))
94            .opacity(0.7),
95    ])
96    .padding(24.0)
97    .frame(360.0, 200.0)
98    .into_handle()
99}
100
101fn utility_window() -> ViewHandle {
102    vstack(&[
103        text("Utility").font_size(16.0),
104        text("A `Window`, not a `WindowGroup`: one of it, addressed by id.")
105            .opacity(0.7),
106    ])
107    .padding(20.0)
108    .frame(320.0, 140.0)
109    .into_handle()
110}
111
112fn menu_bar_item() -> ViewHandle {
113    vstack(&[
114        text(&format!("count: {}", counter().get())),
115        button("Reset", || counter().set(0)),
116    ])
117    .padding(10.0)
118    .into_handle()
119}
120
121/// The whole app, as one scene.
122///
123/// Built afresh every time SwiftUI reads `App.body`, which is what makes the
124/// numbers in it live rather than a snapshot taken at launch.
125///
126/// macOS only, and not by choice: `Settings`, `Window`, `MenuBarExtra` and the
127/// whole of `Commands` are `@available(iOS, unavailable)`. iOS gets
128/// [`ios_body`], which is the part of this that exists there.
129#[cfg(target_os = "macos")]
130fn body() -> SceneHandle {
131    scenes! {
132        window_group { main_window() }
133        settings { settings_pane() }
134        window("Utility", "utility") { utility_window() }
135        menu_bar_extra("Count", "star.fill") { menu_bar_item() }
136        commands {
137            // A menu of its own, beside File and Edit.
138            menu("Counter") {
139                vstack(&[
140                    button("Increment", || counter().set(counter().get() + 1)),
141                    button("Decrement", || counter().set(counter().get() - 1)),
142                    divider(),
143                    button("Reset", || counter().set(0)),
144                ])
145            }
146            // In place of the standard File ▸ New.
147            replacing(NewItem) { button("New Count", || counter().set(0)) }
148            // Added after the standard Help entry.
149            after(Help) {
150                button("About this Rust app", || {
151                    eprintln!("built with no Swift compiler in sight");
152                })
153            }
154        }
155    }
156}
examples/swift_org_tutorial.rs (line 151)
129fn body() -> View {
130    let colour = COLORS[COLOR.load(Ordering::Relaxed) % COLORS.len()];
131
132    vstack![
133        // Text("Why not try…").font(.largeTitle.bold())
134        text("Why not try…").text_style(TextStyle::LargeTitle).bold(),
135        vstack![
136            // Circle().fill(colors.randomElement() ?? .blue).padding().overlay(…)
137            circle_shape().fill_system(colour).padding(16.0).overlay(
138                // Image(systemName: "figure.\(selected.lowercased())")
139                //     .font(.system(size: 144)).foregroundColor(.white)
140                image!("figure.{}", selected().to_lowercase())
141                    .font_size(144.0)
142                    .fg(Color::WHITE),
143            ),
144            // Text("\(selected)!").font(.title)
145            text!("{}!", selected()).text_style(TextStyle::Title),
146        ]
147        .transition(Transition::Slide)
148        .id(ID.load(Ordering::Relaxed)),
149        spacer(),
150        // Button("Try again") { … }.buttonStyle(.borderedProminent)
151        button("Try again", try_again).button_style(ButtonStyle::BorderedProminent),
152    ]
153}
examples/dashboard.rs (line 72)
54fn body() -> View {
55    vstack_aligned_spacing(
56        &[
57            text("System").text_style(TextStyle::LargeTitle).bold(),
58            grid(&[grid_row(&[tile(0), tile(1), tile(2)])]),
59            divider(),
60            // A gauge over the busiest reading, which is a different control
61            // from the rings and reads the same state.
62            vstack_aligned(
63                &[
64                    text("Busiest").font_size(12.0).fg_system(SystemColor::Gray),
65                    gauge(reading(1), 0.0, 1.0, text("Memory"))
66                        .gauge_style(views::GaugeStyle::AccessoryLinearCapacityGaugeStyle)
67                        .tint_system(SystemColor::Orange),
68                ],
69                HorizontalAlignment::Leading,
70            ),
71            spacer(),
72            button("Refresh", refresh)
73                .button_style(views::ButtonStyle::BorderedProminent)
74                .keyboard_shortcut(KeyboardShortcut::DefaultAction),
75        ],
76        HorizontalAlignment::Leading,
77        Some(16.0),
78    )
79    .padding(20.0)
80    // Animate whatever changed, rather than snapping to the new reading.
81    .animation(LOAD[0].load(Ordering::Relaxed) as i64)
82}
examples/settings.rs (line 79)
43fn body() -> View {
44    let s = settings();
45
46    form![
47        section![header: text("Account");
48            hstack![
49                text("Name"),
50                spacer(),
51                textfield_bound("Your name", &s.name).text_field_style(
52                    views::TextFieldStyle::RoundedBorderTextFieldStyle,
53                ),
54            ],
55            toggle_bound("Notifications", &s.notify),
56        ],
57        section![header: text("Playback");
58            vstack_aligned(&[
59                hstack![
60                    text("Volume"),
61                    spacer(),
62                    text!("{:.0}%", s.volume.get() * 100.0).fg_system(SystemColor::Gray),
63                ],
64                slider_bound(&s.volume, 0.0, 1.0),
65            ], HorizontalAlignment::Leading),
66            stepper_bound("Refresh every", &s.refresh, 1.0, 60.0, 1.0),
67        ],
68        section![header: text("Appearance");
69            picker_bound!(
70                "Theme",
71                &s.theme,
72                text(THEMES[0]).tag_int(0),
73                text(THEMES[1]).tag_int(1),
74                text(THEMES[2]).tag_int(2),
75            )
76            .picker_style(PickerStyle::InlinePickerStyle),
77        ],
78        section![
79            button("About", show_about)
80                .a11y_hint("Opens a panel describing this app"),
81        ],
82    ]
83    .form_style(FormStyle::GroupedFormStyle)
84    .sheet_bound(&s.about, about_panel())
85}
86
87/// The sheet, which the user can dismiss — the flag is Rust's, so closing it
88/// writes `false` back rather than leaving the two sides disagreeing.
89fn about_panel() -> View {
90    let s = settings();
91    vstack_spacing(
92        &[
93            system_image("gearshape.fill")
94                .symbol_rendering_mode(Some(views::SymbolRenderingMode::Hierarchical))
95                .view()
96                .font_size(48.0)
97                .fg_system(SystemColor::Blue),
98            text("Settings").text_style(TextStyle::Title).bold(),
99            text!("Signed in as {}", s.name.get()).fg_system(SystemColor::Gray),
100            spacer(),
101            button("Done", hide_about).button_style(views::ButtonStyle::BorderedProminent),
102        ],
103        16.0,
104    )
105    .padding(24.0)
106    .presentation_detents(&[views::PresentationDetent::Medium])
107    .presentation_drag_indicator(Visibility::Visible)
108}
examples/todo.rs (line 100)
83fn body() -> View {
84    let todos = items().lock().unwrap().clone();
85    let remaining = todos.iter().filter(|i| !i.done).count();
86
87    vstack_aligned_spacing(
88        &[
89            hstack![
90                text("Todo").text_style(TextStyle::LargeTitle).bold(),
91                spacer(),
92                text!("{remaining} left").fg_system(SystemColor::Gray),
93            ],
94            hstack_spacing(
95                &[
96                    textfield_bound("What needs doing?", draft())
97                        .text_field_style(views::TextFieldStyle::RoundedBorderTextFieldStyle)
98                        .on_submit(SubmitTrigger::Text, add)
99                        .submit_label(SubmitLabel::Done),
100                    button("Add", add).button_style(views::ButtonStyle::BorderedProminent),
101                ],
102                8.0,
103            ),
104            // The empty state is a branch, so it arrives and leaves rather than
105            // appearing from nothing.
106            view_if!(
107                todos.is_empty(),
108                vstack_spacing(
109                    &[
110                        system_image("checkmark.seal").view().font_size(40.0)
111                            .fg_system(SystemColor::Green),
112                        text("Nothing left to do").fg_system(SystemColor::Gray),
113                    ],
114                    8.0,
115                )
116                .frame_optional(None, Some(160.0), Alignment::Center)
117                .transition(Transition::ScaleWithOpacity { scale: 0.8, anchor: (0.5, 0.5) }),
118                list(for_each_items(
119                    &todos.iter().cloned().enumerate().collect::<Vec<_>>(),
120                    |(i, it)| row(*i, it),
121                ))
122                .list_style(views::ListStyle::PlainListStyle)
123            ),
124        ],
125        HorizontalAlignment::Leading,
126        Some(14.0),
127    )
128    .padding(20.0)
129}