Skip to main content

Ui

Struct Ui 

Source
pub struct Ui { /* private fields */ }
Expand description

Owned UI tree and screen stack. Build once, then mutate node properties. Draining events avoids callbacks borrowing the game and UI together.

use ponko::*;
let mut ui = Ui::default();
let root = ui.add(UiWidget::Panel, ui_layout::Style::default());
ui.push_screen(root, false);
ui.frame(vec2(800.0,600.0), &UiInput::default());

Implementations§

Source§

impl Ui

Source

pub fn add(&mut self, widget: UiWidget, style: Style) -> UiId

Add a detached node. Push it as a screen or use add_child instead.

use ponko::*;
let mut ui = Ui::default();
let root = ui.add(UiWidget::Panel, ui_layout::Style::default());
Examples found in repository?
examples/ui_panel.rs (lines 20-32)
10fn setup(ctx: &mut Ctx) {
11    ctx.camera3d.position = vec3(0.0, 1.6, 1.0);
12    ctx.camera3d.look_at(vec3(0.0, 1.5, -2.0));
13    let floor_material = ctx.create_material(MaterialDesc::color(Color::hex(0x6B7B58)));
14    let floor = ctx.create_mesh(&MeshData::plane(12.0), floor_material);
15    ctx.world.spawn((Transform3D::default(), Model::new(floor)));
16
17    // ANCHOR: panel_create
18    // A 1.2 x 0.8 m terminal, 600 x 400 pixels, with a button.
19    let (mut panel, model) = ctx.create_ui_panel(600, 400, vec2(1.2, 0.8));
20    let root = panel.ui.add(
21        UiWidget::Panel,
22        Style {
23            size: Size {
24                width: percent(1.0),
25                height: percent(1.0),
26            },
27            flex_direction: FlexDirection::Column,
28            padding: ui_layout::Rect::length(20.0),
29            gap: Size::length(12.0),
30            ..Default::default()
31        },
32    );
33    let buy = panel.ui.add_child(
34        root,
35        UiWidget::Button,
36        Style {
37            size: Size {
38                width: length(200.0),
39                height: length(60.0),
40            },
41            flex_shrink: 0.0,
42            ..Default::default()
43        },
44    );
45    panel.ui.set_text(buy, "Buy");
46    panel.ui.push_screen(root, false);
47    ctx.world
48        .spawn((Transform3D::at(vec3(0.0, 1.5, -2.0)), model, panel, Terminal { buy }));
49    // ANCHOR_END: panel_create
50}
More examples
Hide additional examples
examples/ui_shop.rs (lines 19-31)
15fn main() {
16    Game::new("Shop", 900, 700).run(|ctx| {
17        // ANCHOR: shop_tree
18        let ui = &mut ctx.ui_retained;
19        let root = ui.add(
20            UiWidget::Panel,
21            Style {
22                size: Size {
23                    width: percent(1.0),
24                    height: percent(1.0),
25                },
26                flex_direction: FlexDirection::Column,
27                padding: ui_layout::Rect::length(24.0),
28                gap: Size::length(12.0),
29                ..Default::default()
30            },
31        );
32        let title = ui.add_child(root, UiWidget::Label, fixed(400.0, 32.0));
33        ui.set_text(title, "General store — choose an item");
34        let search = ui.add_child(root, UiWidget::TextInput, fixed(360.0, 36.0));
35        ui.set_text(search, "Your order note");
36        let scroll = ui.add_child(
37            root,
38            UiWidget::Scroll,
39            Style {
40                size: Size {
41                    width: percent(1.0),
42                    height: length(360.0),
43                },
44                min_size: Size::length(0.0),
45                flex_shrink: 1.0,
46                ..Default::default()
47            },
48        );
49        let grid = ui.add_child(
50            scroll,
51            UiWidget::Panel,
52            Style {
53                display: Display::Grid,
54                size: Size {
55                    width: percent(0.95),
56                    height: auto(),
57                },
58                grid_template_columns: vec![fr(1.0), fr(1.0), fr(1.0)],
59                grid_auto_rows: vec![length(76.0)],
60                gap: Size::length(8.0),
61                flex_shrink: 0.0,
62                ..Default::default()
63            },
64        );
65        let mut items = Vec::new();
66        for i in 0..36 {
67            let item = ui.add_child(
68                grid,
69                UiWidget::Button,
70                Style {
71                    min_size: Size::length(0.0),
72                    padding: ui_layout::Rect::length(8.0),
73                    ..Default::default()
74                },
75            );
76            ui.set_text(item, format!("Item {}\n{} coins", i + 1, (i + 1) * 3));
77            ui.set_paint(
78                item,
79                UiPaint {
80                    wrap: true,
81                    border_width: 1.0,
82                    ..Default::default()
83                },
84            );
85            items.push(item);
86        }
87        let gift = ui.add_child(root, UiWidget::Toggle, fixed(220.0, 28.0));
88        ui.set_text(gift, "Gift wrap");
89        let buy = ui.add_child(root, UiWidget::Button, fixed(220.0, 40.0));
90        ui.set_text(buy, "Buy selected item");
91        ui.set_enabled(buy, false);
92        ui.push_screen(root, false);
93        // ANCHOR_END: shop_tree
94
95        // ANCHOR: shop_dialog
96        // A detached screen can be reused after it is popped.
97        let dialog = ui.add(
98            UiWidget::Panel,
99            Style {
100                size: Size {
101                    width: length(360.0),
102                    height: length(180.0),
103                },
104                margin: ui_layout::Rect {
105                    left: auto(),
106                    right: auto(),
107                    top: length(180.0),
108                    bottom: auto(),
109                },
110                padding: ui_layout::Rect::length(20.0),
111                flex_direction: FlexDirection::Column,
112                gap: Size::length(12.0),
113                ..Default::default()
114            },
115        );
116        let message = ui.add_child(dialog, UiWidget::Label, fixed(320.0, 40.0));
117        let confirm = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 36.0));
118        ui.set_text(confirm, "Confirm purchase");
119        let cancel = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 30.0));
120        ui.set_text(cancel, "Back");
121        // ANCHOR_END: shop_dialog
122
123        let mut selected = None;
124        // ANCHOR: shop_events
125        move |ctx: &mut Ctx| {
126            // UI input/layout ran before this closure. Drain first, then mutate.
127            let mut clicked = None;
128            for event in ctx.ui_retained.events() {
129                if let UiEvent::Clicked(id) = event {
130                    clicked = Some(id);
131                }
132            }
133            if let Some(id) = clicked {
134                if let Some(index) = items.iter().position(|&item| item == id) {
135                    selected = Some(index);
136                    ctx.ui_retained.set_enabled(buy, true);
137                    ctx.ui_retained.set_text(buy, format!("Buy item {}", index + 1));
138                } else if id == buy {
139                    ctx.ui_retained
140                        .set_text(message, format!("Buy item {}?", selected.unwrap() + 1));
141                    ctx.ui_retained.push_screen(dialog, true);
142                } else if id == confirm || id == cancel {
143                    ctx.ui_retained.pop_screen();
144                    if id == confirm {
145                        ctx.ui_retained.set_text(title, "Purchase complete!");
146                    }
147                }
148            }
149            // Tab/arrows or d-pad navigate; Enter/South confirms; Escape/East
150            // pops the top screen. A modal screen also owns the outside pointer.
151            if !ctx.mouse_over_ui() { /* world pointer actions */ }
152            if !ctx.ui_wants_keyboard() { /* world keyboard/gamepad actions */ }
153        }
154        // ANCHOR_END: shop_events
155    });
156}
Source

pub fn add_child( &mut self, parent: UiId, widget: UiWidget, style: Style, ) -> UiId

Add a child. Panics if the parent was removed or belongs to another UI.

use ponko::*;
let mut ui = Ui::default();
let root = ui.add(UiWidget::Panel, ui_layout::Style::default());
let child = ui.add_child(root, UiWidget::Label, ui_layout::Style::default());
Examples found in repository?
examples/ui_panel.rs (lines 33-44)
10fn setup(ctx: &mut Ctx) {
11    ctx.camera3d.position = vec3(0.0, 1.6, 1.0);
12    ctx.camera3d.look_at(vec3(0.0, 1.5, -2.0));
13    let floor_material = ctx.create_material(MaterialDesc::color(Color::hex(0x6B7B58)));
14    let floor = ctx.create_mesh(&MeshData::plane(12.0), floor_material);
15    ctx.world.spawn((Transform3D::default(), Model::new(floor)));
16
17    // ANCHOR: panel_create
18    // A 1.2 x 0.8 m terminal, 600 x 400 pixels, with a button.
19    let (mut panel, model) = ctx.create_ui_panel(600, 400, vec2(1.2, 0.8));
20    let root = panel.ui.add(
21        UiWidget::Panel,
22        Style {
23            size: Size {
24                width: percent(1.0),
25                height: percent(1.0),
26            },
27            flex_direction: FlexDirection::Column,
28            padding: ui_layout::Rect::length(20.0),
29            gap: Size::length(12.0),
30            ..Default::default()
31        },
32    );
33    let buy = panel.ui.add_child(
34        root,
35        UiWidget::Button,
36        Style {
37            size: Size {
38                width: length(200.0),
39                height: length(60.0),
40            },
41            flex_shrink: 0.0,
42            ..Default::default()
43        },
44    );
45    panel.ui.set_text(buy, "Buy");
46    panel.ui.push_screen(root, false);
47    ctx.world
48        .spawn((Transform3D::at(vec3(0.0, 1.5, -2.0)), model, panel, Terminal { buy }));
49    // ANCHOR_END: panel_create
50}
More examples
Hide additional examples
examples/ui_shop.rs (line 32)
15fn main() {
16    Game::new("Shop", 900, 700).run(|ctx| {
17        // ANCHOR: shop_tree
18        let ui = &mut ctx.ui_retained;
19        let root = ui.add(
20            UiWidget::Panel,
21            Style {
22                size: Size {
23                    width: percent(1.0),
24                    height: percent(1.0),
25                },
26                flex_direction: FlexDirection::Column,
27                padding: ui_layout::Rect::length(24.0),
28                gap: Size::length(12.0),
29                ..Default::default()
30            },
31        );
32        let title = ui.add_child(root, UiWidget::Label, fixed(400.0, 32.0));
33        ui.set_text(title, "General store — choose an item");
34        let search = ui.add_child(root, UiWidget::TextInput, fixed(360.0, 36.0));
35        ui.set_text(search, "Your order note");
36        let scroll = ui.add_child(
37            root,
38            UiWidget::Scroll,
39            Style {
40                size: Size {
41                    width: percent(1.0),
42                    height: length(360.0),
43                },
44                min_size: Size::length(0.0),
45                flex_shrink: 1.0,
46                ..Default::default()
47            },
48        );
49        let grid = ui.add_child(
50            scroll,
51            UiWidget::Panel,
52            Style {
53                display: Display::Grid,
54                size: Size {
55                    width: percent(0.95),
56                    height: auto(),
57                },
58                grid_template_columns: vec![fr(1.0), fr(1.0), fr(1.0)],
59                grid_auto_rows: vec![length(76.0)],
60                gap: Size::length(8.0),
61                flex_shrink: 0.0,
62                ..Default::default()
63            },
64        );
65        let mut items = Vec::new();
66        for i in 0..36 {
67            let item = ui.add_child(
68                grid,
69                UiWidget::Button,
70                Style {
71                    min_size: Size::length(0.0),
72                    padding: ui_layout::Rect::length(8.0),
73                    ..Default::default()
74                },
75            );
76            ui.set_text(item, format!("Item {}\n{} coins", i + 1, (i + 1) * 3));
77            ui.set_paint(
78                item,
79                UiPaint {
80                    wrap: true,
81                    border_width: 1.0,
82                    ..Default::default()
83                },
84            );
85            items.push(item);
86        }
87        let gift = ui.add_child(root, UiWidget::Toggle, fixed(220.0, 28.0));
88        ui.set_text(gift, "Gift wrap");
89        let buy = ui.add_child(root, UiWidget::Button, fixed(220.0, 40.0));
90        ui.set_text(buy, "Buy selected item");
91        ui.set_enabled(buy, false);
92        ui.push_screen(root, false);
93        // ANCHOR_END: shop_tree
94
95        // ANCHOR: shop_dialog
96        // A detached screen can be reused after it is popped.
97        let dialog = ui.add(
98            UiWidget::Panel,
99            Style {
100                size: Size {
101                    width: length(360.0),
102                    height: length(180.0),
103                },
104                margin: ui_layout::Rect {
105                    left: auto(),
106                    right: auto(),
107                    top: length(180.0),
108                    bottom: auto(),
109                },
110                padding: ui_layout::Rect::length(20.0),
111                flex_direction: FlexDirection::Column,
112                gap: Size::length(12.0),
113                ..Default::default()
114            },
115        );
116        let message = ui.add_child(dialog, UiWidget::Label, fixed(320.0, 40.0));
117        let confirm = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 36.0));
118        ui.set_text(confirm, "Confirm purchase");
119        let cancel = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 30.0));
120        ui.set_text(cancel, "Back");
121        // ANCHOR_END: shop_dialog
122
123        let mut selected = None;
124        // ANCHOR: shop_events
125        move |ctx: &mut Ctx| {
126            // UI input/layout ran before this closure. Drain first, then mutate.
127            let mut clicked = None;
128            for event in ctx.ui_retained.events() {
129                if let UiEvent::Clicked(id) = event {
130                    clicked = Some(id);
131                }
132            }
133            if let Some(id) = clicked {
134                if let Some(index) = items.iter().position(|&item| item == id) {
135                    selected = Some(index);
136                    ctx.ui_retained.set_enabled(buy, true);
137                    ctx.ui_retained.set_text(buy, format!("Buy item {}", index + 1));
138                } else if id == buy {
139                    ctx.ui_retained
140                        .set_text(message, format!("Buy item {}?", selected.unwrap() + 1));
141                    ctx.ui_retained.push_screen(dialog, true);
142                } else if id == confirm || id == cancel {
143                    ctx.ui_retained.pop_screen();
144                    if id == confirm {
145                        ctx.ui_retained.set_text(title, "Purchase complete!");
146                    }
147                }
148            }
149            // Tab/arrows or d-pad navigate; Enter/South confirms; Escape/East
150            // pops the top screen. A modal screen also owns the outside pointer.
151            if !ctx.mouse_over_ui() { /* world pointer actions */ }
152            if !ctx.ui_wants_keyboard() { /* world keyboard/gamepad actions */ }
153        }
154        // ANCHOR_END: shop_events
155    });
156}
Source

pub fn remove(&mut self, id: UiId)

Remove a subtree. Stale IDs stay invalid; removing a screen drops it.

use ponko::*;
let mut ui = Ui::default();
let id = ui.add(UiWidget::Panel, ui_layout::Style::default());
ui.remove(id);
assert!(ui.rect(id).is_none());
Source

pub fn set_layout(&mut self, id: UiId, style: Style)

Replace layout style (flex, grid, absolute positioning and constraints).

use ponko::*;
let mut ui = Ui::default(); let id = ui.add(UiWidget::Panel, ui_layout::Style::default());
ui.set_layout(id, ui_layout::Style::default());
Source

pub fn layout_style(&self, id: UiId) -> Option<&Style>

Returns the current layout style, or None for a removed node.

use ponko::*;
let mut ui = Ui::default();
let id = ui.add(UiWidget::Panel, ui_layout::Style::default());
assert!(ui.layout_style(id).is_some());
Source

pub fn set_paint(&mut self, id: UiId, paint: UiPaint)

Replace visual style.

use ponko::*;
let mut ui = Ui::default(); let id = ui.add(UiWidget::Panel, ui_layout::Style::default());
ui.set_paint(id, UiPaint::default());
Examples found in repository?
examples/ui_shop.rs (lines 77-84)
15fn main() {
16    Game::new("Shop", 900, 700).run(|ctx| {
17        // ANCHOR: shop_tree
18        let ui = &mut ctx.ui_retained;
19        let root = ui.add(
20            UiWidget::Panel,
21            Style {
22                size: Size {
23                    width: percent(1.0),
24                    height: percent(1.0),
25                },
26                flex_direction: FlexDirection::Column,
27                padding: ui_layout::Rect::length(24.0),
28                gap: Size::length(12.0),
29                ..Default::default()
30            },
31        );
32        let title = ui.add_child(root, UiWidget::Label, fixed(400.0, 32.0));
33        ui.set_text(title, "General store — choose an item");
34        let search = ui.add_child(root, UiWidget::TextInput, fixed(360.0, 36.0));
35        ui.set_text(search, "Your order note");
36        let scroll = ui.add_child(
37            root,
38            UiWidget::Scroll,
39            Style {
40                size: Size {
41                    width: percent(1.0),
42                    height: length(360.0),
43                },
44                min_size: Size::length(0.0),
45                flex_shrink: 1.0,
46                ..Default::default()
47            },
48        );
49        let grid = ui.add_child(
50            scroll,
51            UiWidget::Panel,
52            Style {
53                display: Display::Grid,
54                size: Size {
55                    width: percent(0.95),
56                    height: auto(),
57                },
58                grid_template_columns: vec![fr(1.0), fr(1.0), fr(1.0)],
59                grid_auto_rows: vec![length(76.0)],
60                gap: Size::length(8.0),
61                flex_shrink: 0.0,
62                ..Default::default()
63            },
64        );
65        let mut items = Vec::new();
66        for i in 0..36 {
67            let item = ui.add_child(
68                grid,
69                UiWidget::Button,
70                Style {
71                    min_size: Size::length(0.0),
72                    padding: ui_layout::Rect::length(8.0),
73                    ..Default::default()
74                },
75            );
76            ui.set_text(item, format!("Item {}\n{} coins", i + 1, (i + 1) * 3));
77            ui.set_paint(
78                item,
79                UiPaint {
80                    wrap: true,
81                    border_width: 1.0,
82                    ..Default::default()
83                },
84            );
85            items.push(item);
86        }
87        let gift = ui.add_child(root, UiWidget::Toggle, fixed(220.0, 28.0));
88        ui.set_text(gift, "Gift wrap");
89        let buy = ui.add_child(root, UiWidget::Button, fixed(220.0, 40.0));
90        ui.set_text(buy, "Buy selected item");
91        ui.set_enabled(buy, false);
92        ui.push_screen(root, false);
93        // ANCHOR_END: shop_tree
94
95        // ANCHOR: shop_dialog
96        // A detached screen can be reused after it is popped.
97        let dialog = ui.add(
98            UiWidget::Panel,
99            Style {
100                size: Size {
101                    width: length(360.0),
102                    height: length(180.0),
103                },
104                margin: ui_layout::Rect {
105                    left: auto(),
106                    right: auto(),
107                    top: length(180.0),
108                    bottom: auto(),
109                },
110                padding: ui_layout::Rect::length(20.0),
111                flex_direction: FlexDirection::Column,
112                gap: Size::length(12.0),
113                ..Default::default()
114            },
115        );
116        let message = ui.add_child(dialog, UiWidget::Label, fixed(320.0, 40.0));
117        let confirm = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 36.0));
118        ui.set_text(confirm, "Confirm purchase");
119        let cancel = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 30.0));
120        ui.set_text(cancel, "Back");
121        // ANCHOR_END: shop_dialog
122
123        let mut selected = None;
124        // ANCHOR: shop_events
125        move |ctx: &mut Ctx| {
126            // UI input/layout ran before this closure. Drain first, then mutate.
127            let mut clicked = None;
128            for event in ctx.ui_retained.events() {
129                if let UiEvent::Clicked(id) = event {
130                    clicked = Some(id);
131                }
132            }
133            if let Some(id) = clicked {
134                if let Some(index) = items.iter().position(|&item| item == id) {
135                    selected = Some(index);
136                    ctx.ui_retained.set_enabled(buy, true);
137                    ctx.ui_retained.set_text(buy, format!("Buy item {}", index + 1));
138                } else if id == buy {
139                    ctx.ui_retained
140                        .set_text(message, format!("Buy item {}?", selected.unwrap() + 1));
141                    ctx.ui_retained.push_screen(dialog, true);
142                } else if id == confirm || id == cancel {
143                    ctx.ui_retained.pop_screen();
144                    if id == confirm {
145                        ctx.ui_retained.set_text(title, "Purchase complete!");
146                    }
147                }
148            }
149            // Tab/arrows or d-pad navigate; Enter/South confirms; Escape/East
150            // pops the top screen. A modal screen also owns the outside pointer.
151            if !ctx.mouse_over_ui() { /* world pointer actions */ }
152            if !ctx.ui_wants_keyboard() { /* world keyboard/gamepad actions */ }
153        }
154        // ANCHOR_END: shop_events
155    });
156}
Source

pub fn set_text(&mut self, id: UiId, text: impl AsRef<str>)

Set label or input text. Input control characters are discarded.

use ponko::*;
let mut ui = Ui::default(); let id = ui.add(UiWidget::Label, ui_layout::Style::default());
ui.set_text(id, "Shop"); assert_eq!(ui.text(id), Some("Shop"));
Examples found in repository?
examples/ui_panel.rs (line 45)
10fn setup(ctx: &mut Ctx) {
11    ctx.camera3d.position = vec3(0.0, 1.6, 1.0);
12    ctx.camera3d.look_at(vec3(0.0, 1.5, -2.0));
13    let floor_material = ctx.create_material(MaterialDesc::color(Color::hex(0x6B7B58)));
14    let floor = ctx.create_mesh(&MeshData::plane(12.0), floor_material);
15    ctx.world.spawn((Transform3D::default(), Model::new(floor)));
16
17    // ANCHOR: panel_create
18    // A 1.2 x 0.8 m terminal, 600 x 400 pixels, with a button.
19    let (mut panel, model) = ctx.create_ui_panel(600, 400, vec2(1.2, 0.8));
20    let root = panel.ui.add(
21        UiWidget::Panel,
22        Style {
23            size: Size {
24                width: percent(1.0),
25                height: percent(1.0),
26            },
27            flex_direction: FlexDirection::Column,
28            padding: ui_layout::Rect::length(20.0),
29            gap: Size::length(12.0),
30            ..Default::default()
31        },
32    );
33    let buy = panel.ui.add_child(
34        root,
35        UiWidget::Button,
36        Style {
37            size: Size {
38                width: length(200.0),
39                height: length(60.0),
40            },
41            flex_shrink: 0.0,
42            ..Default::default()
43        },
44    );
45    panel.ui.set_text(buy, "Buy");
46    panel.ui.push_screen(root, false);
47    ctx.world
48        .spawn((Transform3D::at(vec3(0.0, 1.5, -2.0)), model, panel, Terminal { buy }));
49    // ANCHOR_END: panel_create
50}
51
52// ANCHOR: panel_events
53fn terminal(ctx: &mut Ctx) {
54    // Aim with the middle of the screen instead of the mouse (for a locked
55    // cursor, or a controller in VR).
56    let (eye, forward) = (ctx.camera3d.position, ctx.camera3d.forward());
57    ctx.set_ui_pointer_ray(Some((eye, forward)));
58    ctx.world
59        .query::<(&mut UiPanel3D, &Terminal)>()
60        .for_each(|(panel, terminal)| {
61            while let Some(event) = panel.ui.next_event() {
62                if event == UiEvent::Clicked(terminal.buy) {
63                    panel.ui.set_text(terminal.buy, "Bought!");
64                }
65            }
66        });
67}
More examples
Hide additional examples
examples/ui_shop.rs (line 33)
15fn main() {
16    Game::new("Shop", 900, 700).run(|ctx| {
17        // ANCHOR: shop_tree
18        let ui = &mut ctx.ui_retained;
19        let root = ui.add(
20            UiWidget::Panel,
21            Style {
22                size: Size {
23                    width: percent(1.0),
24                    height: percent(1.0),
25                },
26                flex_direction: FlexDirection::Column,
27                padding: ui_layout::Rect::length(24.0),
28                gap: Size::length(12.0),
29                ..Default::default()
30            },
31        );
32        let title = ui.add_child(root, UiWidget::Label, fixed(400.0, 32.0));
33        ui.set_text(title, "General store — choose an item");
34        let search = ui.add_child(root, UiWidget::TextInput, fixed(360.0, 36.0));
35        ui.set_text(search, "Your order note");
36        let scroll = ui.add_child(
37            root,
38            UiWidget::Scroll,
39            Style {
40                size: Size {
41                    width: percent(1.0),
42                    height: length(360.0),
43                },
44                min_size: Size::length(0.0),
45                flex_shrink: 1.0,
46                ..Default::default()
47            },
48        );
49        let grid = ui.add_child(
50            scroll,
51            UiWidget::Panel,
52            Style {
53                display: Display::Grid,
54                size: Size {
55                    width: percent(0.95),
56                    height: auto(),
57                },
58                grid_template_columns: vec![fr(1.0), fr(1.0), fr(1.0)],
59                grid_auto_rows: vec![length(76.0)],
60                gap: Size::length(8.0),
61                flex_shrink: 0.0,
62                ..Default::default()
63            },
64        );
65        let mut items = Vec::new();
66        for i in 0..36 {
67            let item = ui.add_child(
68                grid,
69                UiWidget::Button,
70                Style {
71                    min_size: Size::length(0.0),
72                    padding: ui_layout::Rect::length(8.0),
73                    ..Default::default()
74                },
75            );
76            ui.set_text(item, format!("Item {}\n{} coins", i + 1, (i + 1) * 3));
77            ui.set_paint(
78                item,
79                UiPaint {
80                    wrap: true,
81                    border_width: 1.0,
82                    ..Default::default()
83                },
84            );
85            items.push(item);
86        }
87        let gift = ui.add_child(root, UiWidget::Toggle, fixed(220.0, 28.0));
88        ui.set_text(gift, "Gift wrap");
89        let buy = ui.add_child(root, UiWidget::Button, fixed(220.0, 40.0));
90        ui.set_text(buy, "Buy selected item");
91        ui.set_enabled(buy, false);
92        ui.push_screen(root, false);
93        // ANCHOR_END: shop_tree
94
95        // ANCHOR: shop_dialog
96        // A detached screen can be reused after it is popped.
97        let dialog = ui.add(
98            UiWidget::Panel,
99            Style {
100                size: Size {
101                    width: length(360.0),
102                    height: length(180.0),
103                },
104                margin: ui_layout::Rect {
105                    left: auto(),
106                    right: auto(),
107                    top: length(180.0),
108                    bottom: auto(),
109                },
110                padding: ui_layout::Rect::length(20.0),
111                flex_direction: FlexDirection::Column,
112                gap: Size::length(12.0),
113                ..Default::default()
114            },
115        );
116        let message = ui.add_child(dialog, UiWidget::Label, fixed(320.0, 40.0));
117        let confirm = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 36.0));
118        ui.set_text(confirm, "Confirm purchase");
119        let cancel = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 30.0));
120        ui.set_text(cancel, "Back");
121        // ANCHOR_END: shop_dialog
122
123        let mut selected = None;
124        // ANCHOR: shop_events
125        move |ctx: &mut Ctx| {
126            // UI input/layout ran before this closure. Drain first, then mutate.
127            let mut clicked = None;
128            for event in ctx.ui_retained.events() {
129                if let UiEvent::Clicked(id) = event {
130                    clicked = Some(id);
131                }
132            }
133            if let Some(id) = clicked {
134                if let Some(index) = items.iter().position(|&item| item == id) {
135                    selected = Some(index);
136                    ctx.ui_retained.set_enabled(buy, true);
137                    ctx.ui_retained.set_text(buy, format!("Buy item {}", index + 1));
138                } else if id == buy {
139                    ctx.ui_retained
140                        .set_text(message, format!("Buy item {}?", selected.unwrap() + 1));
141                    ctx.ui_retained.push_screen(dialog, true);
142                } else if id == confirm || id == cancel {
143                    ctx.ui_retained.pop_screen();
144                    if id == confirm {
145                        ctx.ui_retained.set_text(title, "Purchase complete!");
146                    }
147                }
148            }
149            // Tab/arrows or d-pad navigate; Enter/South confirms; Escape/East
150            // pops the top screen. A modal screen also owns the outside pointer.
151            if !ctx.mouse_over_ui() { /* world pointer actions */ }
152            if !ctx.ui_wants_keyboard() { /* world keyboard/gamepad actions */ }
153        }
154        // ANCHOR_END: shop_events
155    });
156}
Source

pub fn text(&self, id: UiId) -> Option<&str>

Read text, or None for a stale ID.

use ponko::*;
let mut ui = Ui::default(); let id = ui.add(UiWidget::Label, ui_layout::Style::default());
assert_eq!(ui.text(id), Some(""));
Source

pub fn set_enabled(&mut self, id: UiId, enabled: bool)

Enable/disable a node and all descendant interaction.

use ponko::*;
let mut ui = Ui::default(); let id = ui.add(UiWidget::Button, ui_layout::Style::default());
ui.set_enabled(id, false);
Examples found in repository?
examples/ui_shop.rs (line 91)
15fn main() {
16    Game::new("Shop", 900, 700).run(|ctx| {
17        // ANCHOR: shop_tree
18        let ui = &mut ctx.ui_retained;
19        let root = ui.add(
20            UiWidget::Panel,
21            Style {
22                size: Size {
23                    width: percent(1.0),
24                    height: percent(1.0),
25                },
26                flex_direction: FlexDirection::Column,
27                padding: ui_layout::Rect::length(24.0),
28                gap: Size::length(12.0),
29                ..Default::default()
30            },
31        );
32        let title = ui.add_child(root, UiWidget::Label, fixed(400.0, 32.0));
33        ui.set_text(title, "General store — choose an item");
34        let search = ui.add_child(root, UiWidget::TextInput, fixed(360.0, 36.0));
35        ui.set_text(search, "Your order note");
36        let scroll = ui.add_child(
37            root,
38            UiWidget::Scroll,
39            Style {
40                size: Size {
41                    width: percent(1.0),
42                    height: length(360.0),
43                },
44                min_size: Size::length(0.0),
45                flex_shrink: 1.0,
46                ..Default::default()
47            },
48        );
49        let grid = ui.add_child(
50            scroll,
51            UiWidget::Panel,
52            Style {
53                display: Display::Grid,
54                size: Size {
55                    width: percent(0.95),
56                    height: auto(),
57                },
58                grid_template_columns: vec![fr(1.0), fr(1.0), fr(1.0)],
59                grid_auto_rows: vec![length(76.0)],
60                gap: Size::length(8.0),
61                flex_shrink: 0.0,
62                ..Default::default()
63            },
64        );
65        let mut items = Vec::new();
66        for i in 0..36 {
67            let item = ui.add_child(
68                grid,
69                UiWidget::Button,
70                Style {
71                    min_size: Size::length(0.0),
72                    padding: ui_layout::Rect::length(8.0),
73                    ..Default::default()
74                },
75            );
76            ui.set_text(item, format!("Item {}\n{} coins", i + 1, (i + 1) * 3));
77            ui.set_paint(
78                item,
79                UiPaint {
80                    wrap: true,
81                    border_width: 1.0,
82                    ..Default::default()
83                },
84            );
85            items.push(item);
86        }
87        let gift = ui.add_child(root, UiWidget::Toggle, fixed(220.0, 28.0));
88        ui.set_text(gift, "Gift wrap");
89        let buy = ui.add_child(root, UiWidget::Button, fixed(220.0, 40.0));
90        ui.set_text(buy, "Buy selected item");
91        ui.set_enabled(buy, false);
92        ui.push_screen(root, false);
93        // ANCHOR_END: shop_tree
94
95        // ANCHOR: shop_dialog
96        // A detached screen can be reused after it is popped.
97        let dialog = ui.add(
98            UiWidget::Panel,
99            Style {
100                size: Size {
101                    width: length(360.0),
102                    height: length(180.0),
103                },
104                margin: ui_layout::Rect {
105                    left: auto(),
106                    right: auto(),
107                    top: length(180.0),
108                    bottom: auto(),
109                },
110                padding: ui_layout::Rect::length(20.0),
111                flex_direction: FlexDirection::Column,
112                gap: Size::length(12.0),
113                ..Default::default()
114            },
115        );
116        let message = ui.add_child(dialog, UiWidget::Label, fixed(320.0, 40.0));
117        let confirm = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 36.0));
118        ui.set_text(confirm, "Confirm purchase");
119        let cancel = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 30.0));
120        ui.set_text(cancel, "Back");
121        // ANCHOR_END: shop_dialog
122
123        let mut selected = None;
124        // ANCHOR: shop_events
125        move |ctx: &mut Ctx| {
126            // UI input/layout ran before this closure. Drain first, then mutate.
127            let mut clicked = None;
128            for event in ctx.ui_retained.events() {
129                if let UiEvent::Clicked(id) = event {
130                    clicked = Some(id);
131                }
132            }
133            if let Some(id) = clicked {
134                if let Some(index) = items.iter().position(|&item| item == id) {
135                    selected = Some(index);
136                    ctx.ui_retained.set_enabled(buy, true);
137                    ctx.ui_retained.set_text(buy, format!("Buy item {}", index + 1));
138                } else if id == buy {
139                    ctx.ui_retained
140                        .set_text(message, format!("Buy item {}?", selected.unwrap() + 1));
141                    ctx.ui_retained.push_screen(dialog, true);
142                } else if id == confirm || id == cancel {
143                    ctx.ui_retained.pop_screen();
144                    if id == confirm {
145                        ctx.ui_retained.set_text(title, "Purchase complete!");
146                    }
147                }
148            }
149            // Tab/arrows or d-pad navigate; Enter/South confirms; Escape/East
150            // pops the top screen. A modal screen also owns the outside pointer.
151            if !ctx.mouse_over_ui() { /* world pointer actions */ }
152            if !ctx.ui_wants_keyboard() { /* world keyboard/gamepad actions */ }
153        }
154        // ANCHOR_END: shop_events
155    });
156}
Source

pub fn set_value(&mut self, id: UiId, value: f32)

Slider value (0..1), or toggle value (zero/off, one/on).

use ponko::*;
let mut ui = Ui::default(); let id = ui.add(UiWidget::Slider, ui_layout::Style::default());
ui.set_value(id, 0.5); assert_eq!(ui.value(id), Some(0.5));
Source

pub fn value(&self, id: UiId) -> Option<f32>

Read the current slider/toggle value.

use ponko::*;
let mut ui = Ui::default(); let id = ui.add(UiWidget::Toggle, ui_layout::Style::default());
assert_eq!(ui.value(id), Some(0.0));
Source

pub fn set_scroll(&mut self, id: UiId, offset: Vec2)

Set scroll offset in unscaled layout units; clamped during layout.

use ponko::*;
let mut ui = Ui::default(); let id = ui.add(UiWidget::Scroll, ui_layout::Style::default());
ui.set_scroll(id, vec2(0.0,40.0));
Source

pub fn scroll(&self, id: UiId) -> Option<Vec2>

Current clamped offset, after layout.

use ponko::*;
let mut ui = Ui::default(); let id = ui.add(UiWidget::Scroll, ui_layout::Style::default());
assert_eq!(ui.scroll(id), Some(Vec2::ZERO));
Source

pub fn push_screen(&mut self, root: UiId, modal: bool)

Push a detached root; only the top screen is interactive. modal draws a dim overlay below this screen. Duplicate/parented roots panic.

use ponko::*;
let mut ui = Ui::default(); let root = ui.add(UiWidget::Panel, ui_layout::Style::default());
ui.push_screen(root, true);
Examples found in repository?
examples/ui_panel.rs (line 46)
10fn setup(ctx: &mut Ctx) {
11    ctx.camera3d.position = vec3(0.0, 1.6, 1.0);
12    ctx.camera3d.look_at(vec3(0.0, 1.5, -2.0));
13    let floor_material = ctx.create_material(MaterialDesc::color(Color::hex(0x6B7B58)));
14    let floor = ctx.create_mesh(&MeshData::plane(12.0), floor_material);
15    ctx.world.spawn((Transform3D::default(), Model::new(floor)));
16
17    // ANCHOR: panel_create
18    // A 1.2 x 0.8 m terminal, 600 x 400 pixels, with a button.
19    let (mut panel, model) = ctx.create_ui_panel(600, 400, vec2(1.2, 0.8));
20    let root = panel.ui.add(
21        UiWidget::Panel,
22        Style {
23            size: Size {
24                width: percent(1.0),
25                height: percent(1.0),
26            },
27            flex_direction: FlexDirection::Column,
28            padding: ui_layout::Rect::length(20.0),
29            gap: Size::length(12.0),
30            ..Default::default()
31        },
32    );
33    let buy = panel.ui.add_child(
34        root,
35        UiWidget::Button,
36        Style {
37            size: Size {
38                width: length(200.0),
39                height: length(60.0),
40            },
41            flex_shrink: 0.0,
42            ..Default::default()
43        },
44    );
45    panel.ui.set_text(buy, "Buy");
46    panel.ui.push_screen(root, false);
47    ctx.world
48        .spawn((Transform3D::at(vec3(0.0, 1.5, -2.0)), model, panel, Terminal { buy }));
49    // ANCHOR_END: panel_create
50}
More examples
Hide additional examples
examples/ui_shop.rs (line 92)
15fn main() {
16    Game::new("Shop", 900, 700).run(|ctx| {
17        // ANCHOR: shop_tree
18        let ui = &mut ctx.ui_retained;
19        let root = ui.add(
20            UiWidget::Panel,
21            Style {
22                size: Size {
23                    width: percent(1.0),
24                    height: percent(1.0),
25                },
26                flex_direction: FlexDirection::Column,
27                padding: ui_layout::Rect::length(24.0),
28                gap: Size::length(12.0),
29                ..Default::default()
30            },
31        );
32        let title = ui.add_child(root, UiWidget::Label, fixed(400.0, 32.0));
33        ui.set_text(title, "General store — choose an item");
34        let search = ui.add_child(root, UiWidget::TextInput, fixed(360.0, 36.0));
35        ui.set_text(search, "Your order note");
36        let scroll = ui.add_child(
37            root,
38            UiWidget::Scroll,
39            Style {
40                size: Size {
41                    width: percent(1.0),
42                    height: length(360.0),
43                },
44                min_size: Size::length(0.0),
45                flex_shrink: 1.0,
46                ..Default::default()
47            },
48        );
49        let grid = ui.add_child(
50            scroll,
51            UiWidget::Panel,
52            Style {
53                display: Display::Grid,
54                size: Size {
55                    width: percent(0.95),
56                    height: auto(),
57                },
58                grid_template_columns: vec![fr(1.0), fr(1.0), fr(1.0)],
59                grid_auto_rows: vec![length(76.0)],
60                gap: Size::length(8.0),
61                flex_shrink: 0.0,
62                ..Default::default()
63            },
64        );
65        let mut items = Vec::new();
66        for i in 0..36 {
67            let item = ui.add_child(
68                grid,
69                UiWidget::Button,
70                Style {
71                    min_size: Size::length(0.0),
72                    padding: ui_layout::Rect::length(8.0),
73                    ..Default::default()
74                },
75            );
76            ui.set_text(item, format!("Item {}\n{} coins", i + 1, (i + 1) * 3));
77            ui.set_paint(
78                item,
79                UiPaint {
80                    wrap: true,
81                    border_width: 1.0,
82                    ..Default::default()
83                },
84            );
85            items.push(item);
86        }
87        let gift = ui.add_child(root, UiWidget::Toggle, fixed(220.0, 28.0));
88        ui.set_text(gift, "Gift wrap");
89        let buy = ui.add_child(root, UiWidget::Button, fixed(220.0, 40.0));
90        ui.set_text(buy, "Buy selected item");
91        ui.set_enabled(buy, false);
92        ui.push_screen(root, false);
93        // ANCHOR_END: shop_tree
94
95        // ANCHOR: shop_dialog
96        // A detached screen can be reused after it is popped.
97        let dialog = ui.add(
98            UiWidget::Panel,
99            Style {
100                size: Size {
101                    width: length(360.0),
102                    height: length(180.0),
103                },
104                margin: ui_layout::Rect {
105                    left: auto(),
106                    right: auto(),
107                    top: length(180.0),
108                    bottom: auto(),
109                },
110                padding: ui_layout::Rect::length(20.0),
111                flex_direction: FlexDirection::Column,
112                gap: Size::length(12.0),
113                ..Default::default()
114            },
115        );
116        let message = ui.add_child(dialog, UiWidget::Label, fixed(320.0, 40.0));
117        let confirm = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 36.0));
118        ui.set_text(confirm, "Confirm purchase");
119        let cancel = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 30.0));
120        ui.set_text(cancel, "Back");
121        // ANCHOR_END: shop_dialog
122
123        let mut selected = None;
124        // ANCHOR: shop_events
125        move |ctx: &mut Ctx| {
126            // UI input/layout ran before this closure. Drain first, then mutate.
127            let mut clicked = None;
128            for event in ctx.ui_retained.events() {
129                if let UiEvent::Clicked(id) = event {
130                    clicked = Some(id);
131                }
132            }
133            if let Some(id) = clicked {
134                if let Some(index) = items.iter().position(|&item| item == id) {
135                    selected = Some(index);
136                    ctx.ui_retained.set_enabled(buy, true);
137                    ctx.ui_retained.set_text(buy, format!("Buy item {}", index + 1));
138                } else if id == buy {
139                    ctx.ui_retained
140                        .set_text(message, format!("Buy item {}?", selected.unwrap() + 1));
141                    ctx.ui_retained.push_screen(dialog, true);
142                } else if id == confirm || id == cancel {
143                    ctx.ui_retained.pop_screen();
144                    if id == confirm {
145                        ctx.ui_retained.set_text(title, "Purchase complete!");
146                    }
147                }
148            }
149            // Tab/arrows or d-pad navigate; Enter/South confirms; Escape/East
150            // pops the top screen. A modal screen also owns the outside pointer.
151            if !ctx.mouse_over_ui() { /* world pointer actions */ }
152            if !ctx.ui_wants_keyboard() { /* world keyboard/gamepad actions */ }
153        }
154        // ANCHOR_END: shop_events
155    });
156}
Source

pub fn pop_screen(&mut self) -> Option<UiId>

Pop a screen, restoring the previous focus. Nodes remain reusable.

use ponko::*;
let mut ui = Ui::default(); let root = ui.add(UiWidget::Panel, ui_layout::Style::default());
ui.push_screen(root, true); assert_eq!(ui.pop_screen(), Some(root));
Examples found in repository?
examples/ui_shop.rs (line 143)
15fn main() {
16    Game::new("Shop", 900, 700).run(|ctx| {
17        // ANCHOR: shop_tree
18        let ui = &mut ctx.ui_retained;
19        let root = ui.add(
20            UiWidget::Panel,
21            Style {
22                size: Size {
23                    width: percent(1.0),
24                    height: percent(1.0),
25                },
26                flex_direction: FlexDirection::Column,
27                padding: ui_layout::Rect::length(24.0),
28                gap: Size::length(12.0),
29                ..Default::default()
30            },
31        );
32        let title = ui.add_child(root, UiWidget::Label, fixed(400.0, 32.0));
33        ui.set_text(title, "General store — choose an item");
34        let search = ui.add_child(root, UiWidget::TextInput, fixed(360.0, 36.0));
35        ui.set_text(search, "Your order note");
36        let scroll = ui.add_child(
37            root,
38            UiWidget::Scroll,
39            Style {
40                size: Size {
41                    width: percent(1.0),
42                    height: length(360.0),
43                },
44                min_size: Size::length(0.0),
45                flex_shrink: 1.0,
46                ..Default::default()
47            },
48        );
49        let grid = ui.add_child(
50            scroll,
51            UiWidget::Panel,
52            Style {
53                display: Display::Grid,
54                size: Size {
55                    width: percent(0.95),
56                    height: auto(),
57                },
58                grid_template_columns: vec![fr(1.0), fr(1.0), fr(1.0)],
59                grid_auto_rows: vec![length(76.0)],
60                gap: Size::length(8.0),
61                flex_shrink: 0.0,
62                ..Default::default()
63            },
64        );
65        let mut items = Vec::new();
66        for i in 0..36 {
67            let item = ui.add_child(
68                grid,
69                UiWidget::Button,
70                Style {
71                    min_size: Size::length(0.0),
72                    padding: ui_layout::Rect::length(8.0),
73                    ..Default::default()
74                },
75            );
76            ui.set_text(item, format!("Item {}\n{} coins", i + 1, (i + 1) * 3));
77            ui.set_paint(
78                item,
79                UiPaint {
80                    wrap: true,
81                    border_width: 1.0,
82                    ..Default::default()
83                },
84            );
85            items.push(item);
86        }
87        let gift = ui.add_child(root, UiWidget::Toggle, fixed(220.0, 28.0));
88        ui.set_text(gift, "Gift wrap");
89        let buy = ui.add_child(root, UiWidget::Button, fixed(220.0, 40.0));
90        ui.set_text(buy, "Buy selected item");
91        ui.set_enabled(buy, false);
92        ui.push_screen(root, false);
93        // ANCHOR_END: shop_tree
94
95        // ANCHOR: shop_dialog
96        // A detached screen can be reused after it is popped.
97        let dialog = ui.add(
98            UiWidget::Panel,
99            Style {
100                size: Size {
101                    width: length(360.0),
102                    height: length(180.0),
103                },
104                margin: ui_layout::Rect {
105                    left: auto(),
106                    right: auto(),
107                    top: length(180.0),
108                    bottom: auto(),
109                },
110                padding: ui_layout::Rect::length(20.0),
111                flex_direction: FlexDirection::Column,
112                gap: Size::length(12.0),
113                ..Default::default()
114            },
115        );
116        let message = ui.add_child(dialog, UiWidget::Label, fixed(320.0, 40.0));
117        let confirm = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 36.0));
118        ui.set_text(confirm, "Confirm purchase");
119        let cancel = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 30.0));
120        ui.set_text(cancel, "Back");
121        // ANCHOR_END: shop_dialog
122
123        let mut selected = None;
124        // ANCHOR: shop_events
125        move |ctx: &mut Ctx| {
126            // UI input/layout ran before this closure. Drain first, then mutate.
127            let mut clicked = None;
128            for event in ctx.ui_retained.events() {
129                if let UiEvent::Clicked(id) = event {
130                    clicked = Some(id);
131                }
132            }
133            if let Some(id) = clicked {
134                if let Some(index) = items.iter().position(|&item| item == id) {
135                    selected = Some(index);
136                    ctx.ui_retained.set_enabled(buy, true);
137                    ctx.ui_retained.set_text(buy, format!("Buy item {}", index + 1));
138                } else if id == buy {
139                    ctx.ui_retained
140                        .set_text(message, format!("Buy item {}?", selected.unwrap() + 1));
141                    ctx.ui_retained.push_screen(dialog, true);
142                } else if id == confirm || id == cancel {
143                    ctx.ui_retained.pop_screen();
144                    if id == confirm {
145                        ctx.ui_retained.set_text(title, "Purchase complete!");
146                    }
147                }
148            }
149            // Tab/arrows or d-pad navigate; Enter/South confirms; Escape/East
150            // pops the top screen. A modal screen also owns the outside pointer.
151            if !ctx.mouse_over_ui() { /* world pointer actions */ }
152            if !ctx.ui_wants_keyboard() { /* world keyboard/gamepad actions */ }
153        }
154        // ANCHOR_END: shop_events
155    });
156}
Source

pub fn parent(&self, id: UiId) -> Option<UiId>

The node’s parent, or None for a detached root and for stale IDs.

use ponko::*;
let mut ui = Ui::default();
let root = ui.add(UiWidget::Panel, ui_layout::Style::default());
let child = ui.add_child(root, UiWidget::Label, ui_layout::Style::default());
assert_eq!(ui.parent(child), Some(root));
assert_eq!(ui.parent(root), None);
Source

pub fn is_screen(&self, id: UiId) -> bool

Whether id is on the screen stack right now.

use ponko::*;
let mut ui = Ui::default();
let root = ui.add(UiWidget::Panel, ui_layout::Style::default());
assert!(!ui.is_screen(root));
ui.push_screen(root, false);
assert!(ui.is_screen(root));
Source

pub fn top_screen(&self) -> Option<UiId>

Top screen root.

use ponko::*;
assert_eq!(Ui::default().top_screen(), None);
Source

pub fn set_scale(&mut self, scale: f32)

User scale, multiplied with the window’s existing DPI handling.

use ponko::*;
let mut ui = Ui::default(); ui.set_scale(1.5);
Source

pub fn rect(&self, id: UiId) -> Option<Rect>

Resolved rectangle in logical window coordinates (including user scale).

use ponko::*;
let mut ui = Ui::default(); let id = ui.add(UiWidget::Panel, ui_layout::Style::default());
assert!(ui.rect(id).is_some());
Source

pub fn focus(&self) -> Option<UiId>

Current keyboard/gamepad focus.

use ponko::*;
assert_eq!(Ui::default().focus(), None);
Source

pub fn events(&mut self) -> impl Iterator<Item = UiEvent> + '_

Drain semantic events once. Unread events are cleared next frame.

use ponko::*;
assert_eq!(Ui::default().events().count(), 0);
Examples found in repository?
examples/ui_shop.rs (line 128)
15fn main() {
16    Game::new("Shop", 900, 700).run(|ctx| {
17        // ANCHOR: shop_tree
18        let ui = &mut ctx.ui_retained;
19        let root = ui.add(
20            UiWidget::Panel,
21            Style {
22                size: Size {
23                    width: percent(1.0),
24                    height: percent(1.0),
25                },
26                flex_direction: FlexDirection::Column,
27                padding: ui_layout::Rect::length(24.0),
28                gap: Size::length(12.0),
29                ..Default::default()
30            },
31        );
32        let title = ui.add_child(root, UiWidget::Label, fixed(400.0, 32.0));
33        ui.set_text(title, "General store — choose an item");
34        let search = ui.add_child(root, UiWidget::TextInput, fixed(360.0, 36.0));
35        ui.set_text(search, "Your order note");
36        let scroll = ui.add_child(
37            root,
38            UiWidget::Scroll,
39            Style {
40                size: Size {
41                    width: percent(1.0),
42                    height: length(360.0),
43                },
44                min_size: Size::length(0.0),
45                flex_shrink: 1.0,
46                ..Default::default()
47            },
48        );
49        let grid = ui.add_child(
50            scroll,
51            UiWidget::Panel,
52            Style {
53                display: Display::Grid,
54                size: Size {
55                    width: percent(0.95),
56                    height: auto(),
57                },
58                grid_template_columns: vec![fr(1.0), fr(1.0), fr(1.0)],
59                grid_auto_rows: vec![length(76.0)],
60                gap: Size::length(8.0),
61                flex_shrink: 0.0,
62                ..Default::default()
63            },
64        );
65        let mut items = Vec::new();
66        for i in 0..36 {
67            let item = ui.add_child(
68                grid,
69                UiWidget::Button,
70                Style {
71                    min_size: Size::length(0.0),
72                    padding: ui_layout::Rect::length(8.0),
73                    ..Default::default()
74                },
75            );
76            ui.set_text(item, format!("Item {}\n{} coins", i + 1, (i + 1) * 3));
77            ui.set_paint(
78                item,
79                UiPaint {
80                    wrap: true,
81                    border_width: 1.0,
82                    ..Default::default()
83                },
84            );
85            items.push(item);
86        }
87        let gift = ui.add_child(root, UiWidget::Toggle, fixed(220.0, 28.0));
88        ui.set_text(gift, "Gift wrap");
89        let buy = ui.add_child(root, UiWidget::Button, fixed(220.0, 40.0));
90        ui.set_text(buy, "Buy selected item");
91        ui.set_enabled(buy, false);
92        ui.push_screen(root, false);
93        // ANCHOR_END: shop_tree
94
95        // ANCHOR: shop_dialog
96        // A detached screen can be reused after it is popped.
97        let dialog = ui.add(
98            UiWidget::Panel,
99            Style {
100                size: Size {
101                    width: length(360.0),
102                    height: length(180.0),
103                },
104                margin: ui_layout::Rect {
105                    left: auto(),
106                    right: auto(),
107                    top: length(180.0),
108                    bottom: auto(),
109                },
110                padding: ui_layout::Rect::length(20.0),
111                flex_direction: FlexDirection::Column,
112                gap: Size::length(12.0),
113                ..Default::default()
114            },
115        );
116        let message = ui.add_child(dialog, UiWidget::Label, fixed(320.0, 40.0));
117        let confirm = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 36.0));
118        ui.set_text(confirm, "Confirm purchase");
119        let cancel = ui.add_child(dialog, UiWidget::Button, fixed(240.0, 30.0));
120        ui.set_text(cancel, "Back");
121        // ANCHOR_END: shop_dialog
122
123        let mut selected = None;
124        // ANCHOR: shop_events
125        move |ctx: &mut Ctx| {
126            // UI input/layout ran before this closure. Drain first, then mutate.
127            let mut clicked = None;
128            for event in ctx.ui_retained.events() {
129                if let UiEvent::Clicked(id) = event {
130                    clicked = Some(id);
131                }
132            }
133            if let Some(id) = clicked {
134                if let Some(index) = items.iter().position(|&item| item == id) {
135                    selected = Some(index);
136                    ctx.ui_retained.set_enabled(buy, true);
137                    ctx.ui_retained.set_text(buy, format!("Buy item {}", index + 1));
138                } else if id == buy {
139                    ctx.ui_retained
140                        .set_text(message, format!("Buy item {}?", selected.unwrap() + 1));
141                    ctx.ui_retained.push_screen(dialog, true);
142                } else if id == confirm || id == cancel {
143                    ctx.ui_retained.pop_screen();
144                    if id == confirm {
145                        ctx.ui_retained.set_text(title, "Purchase complete!");
146                    }
147                }
148            }
149            // Tab/arrows or d-pad navigate; Enter/South confirms; Escape/East
150            // pops the top screen. A modal screen also owns the outside pointer.
151            if !ctx.mouse_over_ui() { /* world pointer actions */ }
152            if !ctx.ui_wants_keyboard() { /* world keyboard/gamepad actions */ }
153        }
154        // ANCHOR_END: shop_events
155    });
156}
Source

pub fn next_event(&mut self) -> Option<UiEvent>

Take the oldest queued event, or None when none are left. Unlike Ui::events, dropping the result keeps the other events queued, so calls that read one event at a time (the C API does) lose none.

use ponko::*;
assert_eq!(Ui::default().next_event(), None);
Examples found in repository?
examples/ui_panel.rs (line 61)
53fn terminal(ctx: &mut Ctx) {
54    // Aim with the middle of the screen instead of the mouse (for a locked
55    // cursor, or a controller in VR).
56    let (eye, forward) = (ctx.camera3d.position, ctx.camera3d.forward());
57    ctx.set_ui_pointer_ray(Some((eye, forward)));
58    ctx.world
59        .query::<(&mut UiPanel3D, &Terminal)>()
60        .for_each(|(panel, terminal)| {
61            while let Some(event) = panel.ui.next_event() {
62                if event == UiEvent::Clicked(terminal.buy) {
63                    panel.ui.set_text(terminal.buy, "Bought!");
64                }
65            }
66        });
67}
Source

pub fn wants_pointer(&self) -> bool

Pointer or a captured drag belongs to UI; modal screens capture outside.

use ponko::*;
assert!(!Ui::default().wants_pointer());
Source

pub fn wants_keyboard(&self) -> bool

Keyboard/gamepad belongs to UI while a screen is open (including text focus). The game should suppress its own actions while this is true.

use ponko::*;
assert!(!Ui::default().wants_keyboard());
Source

pub fn hit_test(&self, point: Vec2) -> Option<UiId>

Hit test the top screen, respecting ancestor clips and disabled nodes.

use ponko::*;
assert_eq!(Ui::default().hit_test(Vec2::ZERO), None);
Source

pub fn layout(&mut self, viewport: Vec2)

Resolve all screens against the logical window size. Percent sizes, min/max, flex/grid and absolute offsets are Taffy styles.

use ponko::*;
let mut ui = Ui::default(); ui.layout(vec2(800.0,600.0));
Source

pub fn frame(&mut self, viewport: Vec2, input: &UiInput)

Layout and process one input snapshot. Call once per frame, before game logic reads events. Cancel consumes the frame, preventing click-through.

use ponko::*;
let mut ui = Ui::default(); ui.frame(vec2(800.0,600.0), &UiInput::default());

Trait Implementations§

Source§

impl Default for Ui

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl Send for Ui

Source§

impl Sync for Ui

Auto Trait Implementations§

§

impl Forget for Ui

§

impl Freeze for Ui

§

impl RefUnwindSafe for Ui

§

impl Unpin for Ui

§

impl UnsafeUnpin for Ui

§

impl UnwindSafe for Ui

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Component for T
where T: Send + Sync + 'static,

Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Converts Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Converts &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Converts &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> Downcast<T> for T

Source§

fn downcast(&self) -> &T

Source§

impl<T> DowncastSend for T
where T: Any + Send,

Source§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync> ⓘ

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Send + Sync>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync> ⓘ

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<F, T> IntoSample<T> for F
where T: FromSample<F>,

Source§

fn into_sample(self) -> T

Source§

impl<T> MaybeSync for T
where T: Sync + ?Sized,

Source§

impl<T> MaybeSync for T
where T: Sync + ?Sized,

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<R, P> ReadPrimitive<R> for P
where R: Read + ReadEndian<P>, P: Default,

Source§

fn read_from_little_endian(read: &mut R) -> Result<Self, Error>

Read this value from the supplied reader. Same as ReadEndian::read_from_little_endian().
Source§

fn read_from_big_endian(read: &mut R) -> Result<Self, Error>

Read this value from the supplied reader. Same as ReadEndian::read_from_big_endian().
Source§

fn read_from_native_endian(read: &mut R) -> Result<Self, Error>

Read this value from the supplied reader. Same as ReadEndian::read_from_native_endian().
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, S> SimdFrom<T, S> for T
where S: Simd,

Source§

fn simd_from(_simd: S, value: T) -> T

Source§

impl<F, T, S> SimdInto<T, S> for F
where T: SimdFrom<F, S>, S: Simd,

Source§

fn simd_into(self, simd: S) -> T

Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

Source§

fn to_sample_(self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> Upcast<T> for T

Source§

fn upcast(&self) -> Option<&T>

Source§

impl<T> WasmNotSend for T
where T: Send,

Source§

impl<T> WasmNotSendSync for T

Source§

impl<T> WasmNotSync for T
where T: Sync,

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more