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
impl Ui
Sourcepub fn add(&mut self, widget: UiWidget, style: Style) -> UiId
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?
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
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}Sourcepub fn add_child(
&mut self,
parent: UiId,
widget: UiWidget,
style: Style,
) -> UiId
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?
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
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}Sourcepub fn remove(&mut self, id: UiId)
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());Sourcepub fn set_layout(&mut self, id: UiId, style: Style)
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());Sourcepub fn layout_style(&self, id: UiId) -> Option<&Style>
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());Sourcepub fn set_paint(&mut self, id: UiId, paint: UiPaint)
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?
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}Sourcepub fn set_text(&mut self, id: UiId, text: impl AsRef<str>)
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?
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
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}Sourcepub fn text(&self, id: UiId) -> Option<&str>
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(""));Sourcepub fn set_enabled(&mut self, id: UiId, enabled: bool)
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?
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}Sourcepub fn set_value(&mut self, id: UiId, value: f32)
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));Sourcepub fn value(&self, id: UiId) -> Option<f32>
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));Sourcepub fn set_scroll(&mut self, id: UiId, offset: Vec2)
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));Sourcepub fn scroll(&self, id: UiId) -> Option<Vec2>
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));Sourcepub fn push_screen(&mut self, root: UiId, modal: bool)
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?
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
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}Sourcepub fn pop_screen(&mut self) -> Option<UiId>
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?
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}Sourcepub fn parent(&self, id: UiId) -> Option<UiId>
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);Sourcepub fn is_screen(&self, id: UiId) -> bool
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));Sourcepub fn top_screen(&self) -> Option<UiId>
pub fn top_screen(&self) -> Option<UiId>
Top screen root.
use ponko::*;
assert_eq!(Ui::default().top_screen(), None);Sourcepub fn set_scale(&mut self, scale: f32)
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);Sourcepub fn rect(&self, id: UiId) -> Option<Rect>
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());Sourcepub fn focus(&self) -> Option<UiId>
pub fn focus(&self) -> Option<UiId>
Current keyboard/gamepad focus.
use ponko::*;
assert_eq!(Ui::default().focus(), None);Sourcepub fn events(&mut self) -> impl Iterator<Item = UiEvent> + '_
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?
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}Sourcepub fn next_event(&mut self) -> Option<UiEvent>
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?
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}Sourcepub fn wants_pointer(&self) -> bool
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());Sourcepub fn wants_keyboard(&self) -> bool
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());Sourcepub fn hit_test(&self, point: Vec2) -> Option<UiId>
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);Trait Implementations§
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> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<T> Component for T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
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>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
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)
fn as_any(&self) -> &(dyn Any + 'static)
&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)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&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 Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
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>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
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)
fn as_any(&self) -> &(dyn Any + 'static)
&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)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&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> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<S, T> Duplex<S> for Twhere
T: FromSample<S> + ToSample<S>,
Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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 moreSource§impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
fn into_sample(self) -> T
impl<T> MaybeSync for T
impl<T> MaybeSync for T
Source§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<R, P> ReadPrimitive<R> for P
impl<R, P> ReadPrimitive<R> for P
Source§fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
ReadEndian::read_from_little_endian().Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.