1use macroquad::prelude::*;
2
3use macroquad::ui::widgets::{ProgressBar, Slider};
4use macroquad::ui::{
5 hash, root_ui,
6 widgets::{self, Group},
7 Drag, Ui,
8};
9
10pub struct Slot {
11 id: u64,
12 item: Option<String>,
13}
14impl Slot {
15 fn new(id: u64) -> Slot {
16 Slot { id, item: None }
17 }
18}
19
20pub enum FittingCommand {
21 Unfit { target_slot: u64 },
23 Fit { target_slot: u64, item: String },
25 Refit { target_slot: u64, origin_slot: u64 },
27}
28
29pub struct Data {
30 inventory: Vec<String>,
31 item_dragging: bool,
32 slots: Vec<(&'static str, Slot)>,
33 fit_command: Option<FittingCommand>,
34}
35impl Data {
36 pub fn new() -> Data {
37 Data {
38 inventory: vec![],
39 item_dragging: false,
40 slots: vec![
41 ("Left Mouse Button", Slot::new(hash!())),
42 ("Right Mouse Button", Slot::new(hash!())),
43 ("Middle Mouse Button", Slot::new(hash!())),
44 ("Space", Slot::new(hash!())),
45 ("\"1\"", Slot::new(hash!())),
46 ("\"2\"", Slot::new(hash!())),
47 ("\"3\"", Slot::new(hash!())),
48 ],
49 fit_command: None,
50 }
51 }
52
53 fn slots(&mut self, ui: &mut Ui) {
54 let item_dragging = &mut self.item_dragging;
55
56 let fit_command = &mut self.fit_command;
57 for (label, slot) in self.slots.iter_mut() {
58 Group::new(hash!("grp", slot.id, &label), Vec2::new(210., 55.)).ui(ui, |ui| {
59 let drag = Group::new(slot.id, Vec2::new(50., 50.))
60 .draggable(slot.item.is_some())
62 .hoverable(*item_dragging)
64 .highlight(*item_dragging)
66 .ui(ui, |ui| {
67 if let Some(ref item) = slot.item {
68 ui.label(Vec2::new(5., 10.), &item);
69 }
70 });
71
72 match drag {
73 Drag::Dropped(_, Some(id)) if slot.item.is_some() => {
75 *fit_command = Some(FittingCommand::Refit {
76 target_slot: id,
77 origin_slot: slot.id,
78 });
79 }
80 Drag::Dropped(_, None) if slot.item.is_some() => {
82 *fit_command = Some(FittingCommand::Unfit {
83 target_slot: slot.id,
84 });
85 }
86 Drag::Dropped(_, _) => unreachable!(),
89 Drag::Dragging(pos, id) => {
90 debug!("slots: pos: {:?}, id {:?}", pos, id);
91 *item_dragging = true;
92 }
93 Drag::No => {}
94 }
95 ui.label(Vec2::new(60., 20.), label);
96 });
97 }
98 }
99
100 fn inventory(&mut self, ui: &mut Ui) {
101 let item_dragging = &mut self.item_dragging;
102 for (n, item) in self.inventory.iter().enumerate() {
103 let drag = Group::new(hash!("inventory", n), Vec2::new(50., 50.))
104 .draggable(true)
105 .ui(ui, |ui| {
106 ui.label(Vec2::new(5., 10.), &item);
107 });
108
109 match drag {
110 Drag::Dropped(_, Some(id)) => {
111 self.fit_command = Some(FittingCommand::Fit {
112 target_slot: id,
113 item: item.clone(),
114 });
115 *item_dragging = false;
116 }
117 Drag::Dropped(_, _) => {
118 *item_dragging = false;
119 }
120 Drag::Dragging(pos, id) => {
121 debug!("inventory: pos: {:?}, id {:?}", pos, id);
122 *item_dragging = true;
123 }
124 _ => {}
125 }
126 }
127 }
128
129 fn set_item(&mut self, id: u64, item: Option<String>) {
130 if let Some(slot) = self.slots.iter_mut().find(|(_, slot)| slot.id == id) {
131 slot.1.item = item;
132 }
133 }
134}
135
136#[macroquad::main("UI showcase")]
137async fn main() {
138 let mut data = Data::new();
139
140 let mut data0 = String::new();
141 let mut data1 = String::new();
142
143 let mut text0 = String::new();
144 let mut text1 = String::new();
145
146 let mut number0 = 0.;
147 let mut number1 = 0.;
148 let mut number2 = 0.;
149
150 let texture: Texture2D = load_texture("examples/ferris.png").await.unwrap();
151
152 loop {
153 clear_background(WHITE);
154
155 widgets::Window::new(hash!(), vec2(400., 200.), vec2(320., 400.))
156 .label("Shop")
157 .titlebar(true)
158 .ui(&mut *root_ui(), |ui| {
159 for i in 0..30 {
160 Group::new(hash!("shop", i), Vec2::new(300., 80.)).ui(ui, |ui| {
161 ui.label(Vec2::new(10., 10.), &format!("Item N {i}"));
162 ui.label(Vec2::new(260., 40.), "10/10");
163 ui.label(Vec2::new(200., 58.), &format!("{} kr", 800));
164 if ui.button(Vec2::new(260., 55.), "buy") {
165 data.inventory.push(format!("Item {i}"));
166 }
167 });
168 }
169 });
170
171 widgets::Window::new(hash!(), vec2(100., 220.), vec2(542., 430.))
172 .label("Fitting window")
173 .titlebar(true)
174 .ui(&mut *root_ui(), |ui| {
175 Group::new(hash!(), Vec2::new(230., 400.)).ui(ui, |ui| {
176 data.slots(ui);
177 });
178 Group::new(hash!(), Vec2::new(280., 400.)).ui(ui, |ui| {
179 data.inventory(ui);
180 });
181 });
182
183 widgets::Window::new(hash!(), vec2(470., 50.), vec2(300., 300.))
184 .label("Megaui Showcase Window")
185 .ui(&mut *root_ui(), |ui| {
186 ui.tree_node(hash!(), "input", |ui| {
187 ui.label(None, "Some random text");
188 if ui.button(None, "click me") {
189 println!("hi");
190 }
191
192 ui.separator();
193
194 ui.label(None, "Some other random text");
195 if ui.button(None, "other button") {
196 println!("hi2");
197 }
198
199 ui.separator();
200
201 ui.input_text(hash!(), "<- input text 1", &mut data0);
202 ui.input_text(hash!(), "<- input text 2", &mut data1);
203 ui.label(None, &format!("Text entered: \"{data0}\" and \"{data1}\""));
204
205 ui.separator();
206 });
207 ui.tree_node(hash!(), "buttons", |ui| {
208 widgets::Button::new(texture.clone())
209 .size(vec2(120., 70.))
210 .ui(ui);
211 ui.same_line(0.);
212 widgets::Button::new("Button").size(vec2(120., 70.)).ui(ui);
213 widgets::Button::new("Button").size(vec2(120., 70.)).ui(ui);
214 ui.same_line(0.);
215 widgets::Button::new(texture.clone())
216 .size(vec2(120., 70.))
217 .ui(ui);
218 });
219 ui.tree_node(hash!(), "sliders and bars", |ui| {
220 let range0 = -10f32..10f32;
221 ui.slider(hash!(), "[-10 .. 10]", range0.clone(), &mut number0);
222 let progress0 = number0.remap(range0.start, range0.end, 0., 1.);
223 ProgressBar::new().label("first bar").ui(
224 ui,
225 progress0,
226 format!("{:.0}%", progress0 * 100.).as_str(),
227 );
228
229 let range1 = 0f32..100f32;
230 ui.slider(hash!(), "[0 .. 100]", range1.clone(), &mut number1);
231 let progress1 = number1.remap(range1.start, range1.end, 0., 1.);
232 ProgressBar::new().label("second bar").ui(
233 ui,
234 progress1,
235 format!("{:.1}/{:.0}", number1, range1.end).as_str(),
236 );
237
238 let range2 = 0f32..1f32;
239 Slider::new(hash!(), range2.clone())
240 .label_width(200.)
241 .label("slider with a long label")
242 .ui(ui, &mut number2);
243 let progress2 = number2;
244 ProgressBar::new()
245 .label("bar with a really long label")
246 .label_width(240.)
247 .ui(ui, progress2, "");
248 });
249 ui.tree_node(hash!(), "editbox 1", |ui| {
250 ui.label(None, "This is editbox!");
251 ui.editbox(hash!(), vec2(285., 165.), &mut text0);
252 });
253 ui.tree_node(hash!(), "editbox 2", |ui| {
254 ui.label(None, "This is editbox!");
255 ui.editbox(hash!(), vec2(285., 165.), &mut text1);
256 });
257 });
258
259 match data.fit_command.take() {
260 Some(FittingCommand::Unfit { target_slot }) => data.set_item(target_slot, None),
261 Some(FittingCommand::Fit { target_slot, item }) => {
262 data.set_item(target_slot, Some(item));
263 }
264 Some(FittingCommand::Refit {
265 target_slot,
266 origin_slot,
267 }) => {
268 let origin_item = data
269 .slots
270 .iter()
271 .find_map(|(_, slot)| {
272 if slot.id == origin_slot {
273 Some(slot.item.clone())
274 } else {
275 None
276 }
277 })
278 .flatten();
279 data.set_item(target_slot, origin_item);
280 data.set_item(origin_slot, None);
281 }
282 None => {}
283 };
284
285 next_frame().await;
286 }
287}