olcPixelGameEngine v2.28
The official distribution of olcPixelGameEngine, a tool used in javidx9's YouTube videos and projects
Loading...
Searching...
No Matches
olcPGEX_PopUpMenu.h
Go to the documentation of this file.
1/*
2 olcPGEX_PopUp.h
3
4 +-------------------------------------------------------------+
5 | OneLoneCoder Pixel Game Engine Extension |
6 | Retro PopUp Menu 1.0 |
7 +-------------------------------------------------------------+
8
9 What is this?
10 ~~~~~~~~~~~~~
11 This is an extension to the olcPixelGameEngine, which provides
12 a quick and easy to use, flexible, skinnable context pop-up
13 menu system.
14
15 License (OLC-3)
16 ~~~~~~~~~~~~~~~
17
18 Copyright 2018 - 2020 OneLoneCoder.com
19
20 Redistribution and use in source and binary forms, with or without
21 modification, are permitted provided that the following conditions
22 are met:
23
24 1. Redistributions or derivations of source code must retain the above
25 copyright notice, this list of conditions and the following disclaimer.
26
27 2. Redistributions or derivative works in binary form must reproduce
28 the above copyright notice. This list of conditions and the following
29 disclaimer must be reproduced in the documentation and/or other
30 materials provided with the distribution.
31
32 3. Neither the name of the copyright holder nor the names of its
33 contributors may be used to endorse or promote products derived
34 from this software without specific prior written permission.
35
36 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
37 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
38 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
39 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
40 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
43 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
45 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
46 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47
48 Links
49 ~~~~~
50 YouTube: https://www.youtube.com/javidx9
51 Discord: https://discord.gg/WhwHUMV
52 Twitter: https://www.twitter.com/javidx9
53 Twitch: https://www.twitch.tv/javidx9
54 GitHub: https://www.github.com/onelonecoder
55 Homepage: https://www.onelonecoder.com
56
57 Author
58 ~~~~~~
59 David Barr, aka javidx9, ©OneLoneCoder 2019, 2020
60*/
61
62
63/*
64 Example
65 ~~~~~~~
66
67 #define OLC_PGEX_POPUPMENU
68 #include "olcPGEX_PopUpMenu.h"
69
70 NOTE: Requires a 9-patch sprite, by default each patch is
71 8x8 pixels, patches are as follows:
72
73 | PANEL TL | PANEL T | PANEL TR | SCROLL UP | CURSOR TL | CURSOR TR |
74 | PANEL L | PANEL M | PANEL R | SUBMENU | CURSOR BL | CURSOR BR |
75 | PANEL BL | PANEL B | PANEL BR | SCROLL DOWN | UNUSED | UNUSED |
76
77 You can find an example sprite here:
78 https://github.com/OneLoneCoder/olcPixelGameEngine/blob/master/Videos/RetroMenu.png
79
80 Constructing A Menu
81 ~~~~~~~~~~~~~~~~~~~
82
83 // Declaration (presumably inside class)
84 olc::popup::Menu m;
85
86 // Construction (root menu is a 1x5 table)
87 m.SetTable(1, 5);
88
89 // Add first item to root menu (A 1x5 submenu)
90 m["Menu1"].SetTable(1, 5);
91
92 // Add items to first item
93 m["Menu1"]["Item1"];
94 m["Menu1"]["Item2"];
95
96 // Add a 4x3 submenu
97 m["Menu1"]["Item3"].SetTable(4, 3);
98 m["Menu1"]["Item3"]["Option1"];
99 m["Menu1"]["Item3"]["Option2"];
100
101 // Set properties of specific item
102 m["Menu1"]["Item3"]["Option3"].Enable(false);
103 m["Menu1"]["Item3"]["Option4"];
104 m["Menu1"]["Item3"]["Option5"];
105 m["Menu1"]["Item4"];
106
107 // Add second item to root menu
108 m["Menu2"].SetTable(3, 3);
109 m["Menu2"]["Item1"];
110 m["Menu2"]["Item2"].SetID(1001).Enable(true);
111 m["Menu2"]["Item3"];
112
113 // Construct the menu structure
114 m.Build();
115
116
117 Displaying a Menu
118 ~~~~~~~~~~~~~~~~~
119
120 // Declaration of menu manager (presumably inside class)
121 olc::popup::Manager man;
122
123 // Set the Menu object to the MenuManager (call once per pop)
124 man.Open(&m);
125
126 // Draw Menu at position (30, 30), using "patch sprite"
127 man.Draw(sprGFX, { 30,30 });
128
129
130 Interacting with menu
131 ~~~~~~~~~~~~~~~~~~~~~
132
133 // Send key events to menu
134 if (GetKey(olc::Key::UP).bPressed) man.OnUp();
135 if (GetKey(olc::Key::DOWN).bPressed) man.OnDown();
136 if (GetKey(olc::Key::LEFT).bPressed) man.OnLeft();
137 if (GetKey(olc::Key::RIGHT).bPressed) man.OnRight();
138 if (GetKey(olc::Key::Z).bPressed) man.OnBack();
139
140 // "Confirm/Action" Key does something, if it returns non-null
141 // then a menu item has been selected. The specific item will
142 // be returned
143 olc::popup::Menu* command = nullptr;
144 if (GetKey(olc::Key::SPACE).bPressed) command = man.OnConfirm();
145 if (command != nullptr)
146 {
147 std::string sLastAction =
148 "Selected: " + command->GetName() +
149 " ID: " + std::to_string(command->GetID());
150
151 // Optionally close menu?
152 man.Close();
153 }
154
155*/
156
157#ifndef OLC_PGEX_POPUPMENU_H
158#define OLC_PGEX_POPUPMENU_H
159
160#include <cstdint>
161
162#include "olcPixelGameEngine.h"
163
164namespace olc
165{
166 namespace popup
167 {
168 constexpr int32_t nPatch = 8;
169
170 class Menu
171 {
172 public:
174 Menu(const std::string n);
175
176 Menu& SetTable(int32_t nColumns, int32_t nRows);
177 Menu& SetID(int32_t id);
178 Menu& Enable(bool b);
179
180 int32_t GetID();
181 std::string& GetName();
182 bool Enabled();
186 Menu& operator[](const std::string& name);
187 void Build();
188 void DrawSelf(olc::PixelGameEngine& pge, olc::Sprite* sprGFX, olc::vi2d vScreenOffset);
190 void OnUp();
191 void OnDown();
192 void OnLeft();
193 void OnRight();
196
197 protected:
198 int32_t nID = -1;
200 std::unordered_map<std::string, size_t> itemPointer;
201 std::vector<olc::popup::Menu> items;
206 int32_t nCursorItem = 0;
207 int32_t nTopVisibleRow = 0;
208 int32_t nTotalRows = 0;
210 std::string sName;
212 bool bEnabled = true;
213 };
214
215 class Manager : public olc::PGEX
216 {
217 public:
219 void Open(Menu* mo);
220 void Close();
221 void OnUp();
222 void OnDown();
223 void OnLeft();
224 void OnRight();
225 void OnBack();
227 void Draw(olc::Sprite* sprGFX, olc::vi2d vScreenOffset);
228
229 private:
230 std::list<Menu*> panels;
231 };
232
233 }
234};
235
236
237
238
239#ifdef OLC_PGEX_POPUPMENU
240#undef OLC_PGEX_POPUPMENU
241
242namespace olc
243{
244 namespace popup
245 {
246 Menu::Menu()
247 {
248 }
249
250 Menu::Menu(const std::string n)
251 {
252 sName = n;
253 }
254
255
256 Menu& Menu::SetTable(int32_t nColumns, int32_t nRows)
257 {
258 vCellTable = { nColumns, nRows };
259 return *this;
260 }
261
262 Menu& Menu::SetID(int32_t id)
263 {
264 nID = id;
265 return *this;
266 }
267
268 Menu& Menu::Enable(bool b)
269 {
270 bEnabled = b;
271 return *this;
272 }
273
274 int32_t Menu::GetID()
275 {
276 return nID;
277 }
278
279 std::string& Menu::GetName()
280 {
281 return sName;
282 }
283
284 bool Menu::Enabled()
285 {
286 return bEnabled;
287 }
288
289 bool Menu::HasChildren()
290 {
291 return !items.empty();
292 }
293
295 {
296 return { int32_t(sName.size()), 1 };
297 }
298
300 {
301 return vCursorPos;
302 }
303
304 Menu& Menu::operator[](const std::string& name)
305 {
306 if (itemPointer.count(name) == 0)
307 {
308 itemPointer[name] = items.size();
309 items.push_back(Menu(name));
310 }
311
312 return items[itemPointer[name]];
313 }
314
315 void Menu::Build()
316 {
317 // Recursively build all children, so they can determine their size, use
318 // that size to indicate cell sizes if this object contains more than
319 // one item
320 for (auto& m : items)
321 {
322 if (m.HasChildren())
323 {
324 m.Build();
325 }
326
327 // Longest child name determines cell width
328 vCellSize.x = std::max(m.GetSize().x, vCellSize.x);
329 vCellSize.y = std::max(m.GetSize().y, vCellSize.y);
330 }
331
332 // Adjust size of this object (in patches) if it were rendered as a panel
335
336 // Calculate how many rows this item has to hold
337 nTotalRows = (items.size() / vCellTable.x) + (((items.size() % vCellTable.x) > 0) ? 1 : 0);
338 }
339
340 void Menu::DrawSelf(olc::PixelGameEngine& pge, olc::Sprite* sprGFX, olc::vi2d vScreenOffset)
341 {
342 // === Draw Panel
343
344 // Record current pixel mode user is using
345 olc::Pixel::Mode currentPixelMode = pge.GetPixelMode();
347
348 // Draw Panel & Border
349 olc::vi2d vPatchPos = { 0,0 };
350 for (vPatchPos.x = 0; vPatchPos.x < vSizeInPatches.x; vPatchPos.x++)
351 {
352 for (vPatchPos.y = 0; vPatchPos.y < vSizeInPatches.y; vPatchPos.y++)
353 {
354 // Determine position in screen space
355 olc::vi2d vScreenLocation = vPatchPos * nPatch + vScreenOffset;
356
357 // Calculate which patch is needed
358 olc::vi2d vSourcePatch = { 0, 0 };
359 if (vPatchPos.x > 0) vSourcePatch.x = 1;
360 if (vPatchPos.x == vSizeInPatches.x - 1) vSourcePatch.x = 2;
361 if (vPatchPos.y > 0) vSourcePatch.y = 1;
362 if (vPatchPos.y == vSizeInPatches.y - 1) vSourcePatch.y = 2;
363
364 // Draw Actual Patch
365 pge.DrawPartialSprite(vScreenLocation, sprGFX, vSourcePatch * nPatch, vPatchSize);
366 }
367 }
368
369 // === Draw Panel Contents
370 olc::vi2d vCell = { 0,0 };
371 vPatchPos = { 1,1 };
372
373 // Work out visible items
374 int32_t nTopLeftItem = nTopVisibleRow * vCellTable.x;
375 int32_t nBottomRightItem = vCellTable.y * vCellTable.x + nTopLeftItem;
376
377 // Clamp to size of child item vector
378 nBottomRightItem = std::min(int32_t(items.size()), nBottomRightItem);
379 int32_t nVisibleItems = nBottomRightItem - nTopLeftItem;
380
381 // Draw Scroll Markers (if required)
382 if (nTopVisibleRow > 0)
383 {
384 vPatchPos = { vSizeInPatches.x - 2, 0 };
385 olc::vi2d vScreenLocation = vPatchPos * nPatch + vScreenOffset;
386 olc::vi2d vSourcePatch = { 3, 0 };
387 pge.DrawPartialSprite(vScreenLocation, sprGFX, vSourcePatch * nPatch, vPatchSize);
388 }
389
391 {
392 vPatchPos = { vSizeInPatches.x - 2, vSizeInPatches.y - 1 };
393 olc::vi2d vScreenLocation = vPatchPos * nPatch + vScreenOffset;
394 olc::vi2d vSourcePatch = { 3, 2 };
395 pge.DrawPartialSprite(vScreenLocation, sprGFX, vSourcePatch * nPatch, vPatchSize);
396 }
397
398 // Draw Visible Items
399 for (int32_t i = 0; i < nVisibleItems; i++)
400 {
401 // Cell location
402 vCell.x = i % vCellTable.x;
403 vCell.y = i / vCellTable.x;
404
405 // Patch location (including border offset and padding)
406 vPatchPos.x = vCell.x * (vCellSize.x + vCellPadding.x) + 1;
407 vPatchPos.y = vCell.y * (vCellSize.y + vCellPadding.y) + 1;
408
409 // Actual screen location in pixels
410 olc::vi2d vScreenLocation = vPatchPos * nPatch + vScreenOffset;
411
412 // Display Item Header
413 pge.DrawString(vScreenLocation, items[nTopLeftItem + i].sName, items[nTopLeftItem + i].bEnabled ? olc::WHITE : olc::DARK_GREY);
414
415 if (items[nTopLeftItem + i].HasChildren())
416 {
417 // Display Indicator that panel has a sub panel
418 vPatchPos.x = vCell.x * (vCellSize.x + vCellPadding.x) + 1 + vCellSize.x;
419 vPatchPos.y = vCell.y * (vCellSize.y + vCellPadding.y) + 1;
420 olc::vi2d vSourcePatch = { 3, 1 };
421 vScreenLocation = vPatchPos * nPatch + vScreenOffset;
422 pge.DrawPartialSprite(vScreenLocation, sprGFX, vSourcePatch * nPatch, vPatchSize);
423 }
424 }
425
426 // Calculate cursor position in screen space in case system draws it
427 vCursorPos.x = (vCellCursor.x * (vCellSize.x + vCellPadding.x)) * nPatch + vScreenOffset.x - nPatch;
428 vCursorPos.y = ((vCellCursor.y - nTopVisibleRow) * (vCellSize.y + vCellPadding.y)) * nPatch + vScreenOffset.y + nPatch;
429 }
430
431 void Menu::ClampCursor()
432 {
433 // Find item in children
435
436 // Clamp Cursor
437 if (nCursorItem >= int32_t(items.size()))
438 {
439 vCellCursor.y = (items.size() / vCellTable.x);
440 vCellCursor.x = (items.size() % vCellTable.x) - 1;
441 nCursorItem = items.size() - 1;
442 }
443 }
444
445 void Menu::OnUp()
446 {
447 vCellCursor.y--;
448 if (vCellCursor.y < 0) vCellCursor.y = 0;
449
451 {
453 if (nTopVisibleRow < 0) nTopVisibleRow = 0;
454 }
455
456 ClampCursor();
457 }
458
459 void Menu::OnDown()
460 {
461 vCellCursor.y++;
463
465 {
469 }
470
471 ClampCursor();
472 }
473
474 void Menu::OnLeft()
475 {
476 vCellCursor.x--;
477 if (vCellCursor.x < 0) vCellCursor.x = 0;
478 ClampCursor();
479 }
480
481 void Menu::OnRight()
482 {
483 vCellCursor.x++;
485 ClampCursor();
486 }
487
488 Menu* Menu::OnConfirm()
489 {
490 // Check if selected item has children
492 {
493 return &items[nCursorItem];
494 }
495 else
496 return this;
497 }
498
500 {
501 return &items[nCursorItem];
502 }
503
504 // =====================================================================
505
507 {
508 }
509
510 void Manager::Open(Menu* mo)
511 {
512 Close();
513 panels.push_back(mo);
514 }
515
516 void Manager::Close()
517 {
518 panels.clear();
519 }
520
521 void Manager::OnUp()
522 {
523 if (!panels.empty()) panels.back()->OnUp();
524 }
525
526 void Manager::OnDown()
527 {
528 if (!panels.empty()) panels.back()->OnDown();
529 }
530
531 void Manager::OnLeft()
532 {
533 if (!panels.empty()) panels.back()->OnLeft();
534 }
535
536 void Manager::OnRight()
537 {
538 if (!panels.empty()) panels.back()->OnRight();
539 }
540
541 void Manager::OnBack()
542 {
543 if (!panels.empty()) panels.pop_back();
544 }
545
546 Menu* Manager::OnConfirm()
547 {
548 if (panels.empty()) return nullptr;
549
550 Menu* next = panels.back()->OnConfirm();
551 if (next == panels.back())
552 {
553 if (panels.back()->GetSelectedItem()->Enabled())
554 return panels.back()->GetSelectedItem();
555 }
556 else
557 {
558 if (next->Enabled())
559 panels.push_back(next);
560 }
561
562 return nullptr;
563 }
564
565 void Manager::Draw(olc::Sprite* sprGFX, olc::vi2d vScreenOffset)
566 {
567 if (panels.empty()) return;
568
569 // Draw Visible Menu System
570 for (auto& p : panels)
571 {
572 p->DrawSelf(*pge, sprGFX, vScreenOffset);
573 vScreenOffset += {10, 10};
574 }
575
576 // Draw Cursor
577 olc::Pixel::Mode currentPixelMode = pge->GetPixelMode();
579 pge->DrawPartialSprite(panels.back()->GetCursorPosition(), sprGFX, olc::vi2d(4, 0) * nPatch, { nPatch * 2, nPatch * 2 });
580 pge->SetPixelMode(currentPixelMode);
581 }
582 }
583};
584
585
586#endif
587#endif
Definition olcPixelGameEngine.h:1615
static PixelGameEngine * pge
Definition olcPixelGameEngine.h:1627
Definition olcPixelGameEngine.h:1225
void DrawPartialSprite(int32_t x, int32_t y, Sprite *sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, uint32_t scale=1, uint8_t flip=olc::Sprite::NONE)
void DrawString(int32_t x, int32_t y, const std::string &sText, Pixel col=olc::WHITE, uint32_t scale=1)
Pixel::Mode GetPixelMode()
void SetPixelMode(Pixel::Mode m)
Definition olcPixelGameEngine.h:1047
Definition olcPGEX_PopUpMenu.h:216
void Draw(olc::Sprite *sprGFX, olc::vi2d vScreenOffset)
void Open(Menu *mo)
Definition olcPGEX_PopUpMenu.h:171
olc::vi2d & GetCursorPosition()
Menu * OnConfirm()
int32_t nTopVisibleRow
Definition olcPGEX_PopUpMenu.h:207
olc::vi2d vCursorPos
Definition olcPGEX_PopUpMenu.h:211
int32_t nTotalRows
Definition olcPGEX_PopUpMenu.h:208
std::vector< olc::popup::Menu > items
Definition olcPGEX_PopUpMenu.h:201
Menu & SetTable(int32_t nColumns, int32_t nRows)
std::unordered_map< std::string, size_t > itemPointer
Definition olcPGEX_PopUpMenu.h:200
int32_t nCursorItem
Definition olcPGEX_PopUpMenu.h:206
void DrawSelf(olc::PixelGameEngine &pge, olc::Sprite *sprGFX, olc::vi2d vScreenOffset)
Menu & operator[](const std::string &name)
olc::vi2d vCellTable
Definition olcPGEX_PopUpMenu.h:199
std::string sName
Definition olcPGEX_PopUpMenu.h:210
std::string & GetName()
Menu(const std::string n)
Menu * GetSelectedItem()
olc::vi2d vCellSize
Definition olcPGEX_PopUpMenu.h:203
Menu & Enable(bool b)
olc::vi2d vSizeInPatches
Definition olcPGEX_PopUpMenu.h:202
olc::vi2d vCellPadding
Definition olcPGEX_PopUpMenu.h:204
bool bEnabled
Definition olcPGEX_PopUpMenu.h:212
const olc::vi2d vPatchSize
Definition olcPGEX_PopUpMenu.h:209
olc::vi2d GetSize()
Menu & SetID(int32_t id)
int32_t nID
Definition olcPGEX_PopUpMenu.h:198
olc::vi2d vCellCursor
Definition olcPGEX_PopUpMenu.h:205
constexpr int32_t nPatch
Definition olcPGEX_PopUpMenu.h:168
Definition olcPixelGameEngine.h:593
static const Pixel DARK_GREY(128, 128, 128)
static const Pixel WHITE(255, 255, 255)
Mode
Definition olcPixelGameEngine.h:931
@ MASK
Definition olcPixelGameEngine.h:931
@ ALPHA
Definition olcPixelGameEngine.h:931
T x
Definition olcPixelGameEngine.h:604
T y
Definition olcPixelGameEngine.h:606