VoltMod
C++23 framework for CS2 server plugins
Loading...
Searching...
No Matches
MenuStack.hpp
Go to the documentation of this file.
1#pragma once
2
10#include <cstdint>
11#include <memory>
12#include <string>
13#include <string_view>
14#include <vector>
15
16namespace VoltMod
17{
18
19/**
20 * @brief One player's open menus, and the row behaviour every surface shares.
21 *
22 * The half of a menu session that has nothing to do with how it is drawn: the stack, the
23 * breadcrumb, how a row describes itself, what activating a row does, and how a stepped value is
24 * held back so a burst of presses is one action. @ref CenterHtmlMenu draws this as center HTML; a
25 * plugin drawing its own Panorama screen holds one too, so the same @ref Menu behaves the same
26 * way whichever surface a player is on.
27 *
28 * What stays outside: the cursor, page shape, key or click routing, freezing, and prompts. Those
29 * differ per surface, and a surface that has no cursor should not be made to carry one.
30 *
31 * SDK-free. A surface hands it the @ref Scheduler; the tests hand it a @ref PendingCommit::Timer
32 * they fire by hand.
33 */
35{
36public:
37 /** @p surface is what a row's Activate callback is handed: the surface owning this stack. A
38 * slot's stack is dropped when @p slots reports it changed hands. Every reference must
39 * outlive this instance. */
41
42 /** The same, with the held commit's delay driven by @p timer instead of a Scheduler. */
44
45 /** Top menu, or null when none is open. */
46 Menu* Current(int slot);
47
48 /** Bottom menu - the one the session was opened with - or null. */
49 Menu* Root(int slot);
50
51 int Depth(int slot) const;
52 bool IsOpen(int slot) const;
53 bool AnyOpen() const;
54
55 /** Parent menu titles joined. Valid until the stack changes. */
56 std::string_view Breadcrumb(int slot) const;
57
58 void Push(int slot, std::shared_ptr<Menu> menu);
59
60 /** Pop the top menu, applying whatever a stepped row was left showing. Ask @ref IsOpen after
61 * it: an empty stack is the caller's cue to unfreeze and take the menu off screen. */
62 void Pop(int slot);
63
64 /** Clear the whole stack, applying whatever a stepped row was left showing. */
65 void Clear(int slot);
66
67 /** Pop every menu but the root without closing the session, for a surface where entering a
68 * branch is a jump rather than a push. Does nothing when nothing is open. */
69 void PopToRoot(int slot);
70
71 /** Row @p index as it describes itself, with @ref MenuRow::Pending and @ref MenuRow::Changed
72 * filled in and a Toggle's on/off word spelled. An index with no row behind it describes as
73 * a disabled, unselectable line. */
74 MenuRow Describe(int slot, int index);
75
76 /** Run row @p index, as if it had been selected and confirmed. Ignores rows that are disabled,
77 * unselectable, or out of range.
78 *
79 * Runs a commit held for another row first, and cancels one held for *this* row: a row whose
80 * activation is its own commit would otherwise apply the value twice. */
81 void Activate(int slot, int index);
82
83 /** Step row @p index's value by @p direction (-1 or +1). True when the row consumed it, which
84 * is what tells a keyboard surface to page instead. A row with a @ref MenuItem::Commit is
85 * stepped, not applied: the commit is held so a burst of presses runs one action. */
86 bool Step(int slot, int index, int direction);
87
88 /** Apply whatever a stepped row was left showing, and forget it. Every way out of a row that
89 * is not another step goes through here. */
90 void ApplyPending(int slot);
91
92 /** True while @p slot is holding a stepped value for row @p index. */
93 bool IsPending(int slot, int index) const;
94
95 /** A held commit was applied. A surface that redraws every frame can ignore this; one that
96 * draws on demand needs it, because the commit lands on a timer rather than on a press. */
97 Event<int /*slot*/> Committed;
98
99private:
100 /** How long a stepped value waits before it is applied. */
101 static constexpr int64_t ChangedMs = 150;
102
103 /** What a row last showed, so a value that moves under it can flash as changed. */
104 struct RowMemory
105 {
106 std::string Value;
107 /** Monotonic milliseconds of the last change; 0 until one happens. */
108 int64_t ChangedAt = 0;
109 /** False until the row has described itself once: arriving on screen is not a change. */
110 bool Drawn = false;
111 };
112
113 struct State
114 {
115 std::vector<std::shared_ptr<Menu>> Menus;
116 /** One entry per row of the current menu, rebuilt when the menu changes. */
117 std::vector<RowMemory> Rows;
118 std::string Breadcrumb;
119 };
120
121 /** Re-derive what depends on which menu is on top. */
122 void Rebuild(int slot);
123
124 MenuSurface& _surface;
125 Translations& _translations;
126 PerSlot<State> _states;
127 PendingCommit _pending;
128};
129
130} // namespace VoltMod
A multicast signal with a fixed handler signature: the one way to subscribe in VoltMod.
Definition Event.hpp:54
One player's open menus, and the row behaviour every surface shares.
Definition MenuStack.hpp:35
bool IsPending(int slot, int index) const
bool IsOpen(int slot) const
Definition MenuStack.cpp:48
void Pop(int slot)
Definition MenuStack.cpp:81
Menu * Current(int slot)
Definition MenuStack.cpp:25
void Activate(int slot, int index)
std::string_view Breadcrumb(int slot) const
Definition MenuStack.cpp:65
void Push(int slot, std::shared_ptr< Menu > menu)
Definition MenuStack.cpp:70
MenuRow Describe(int slot, int index)
bool AnyOpen() const
Definition MenuStack.cpp:53
void PopToRoot(int slot)
void ApplyPending(int slot)
Event< int > Committed
Definition MenuStack.hpp:97
void Clear(int slot)
int Depth(int slot) const
Definition MenuStack.cpp:43
Menu * Root(int slot)
Definition MenuStack.cpp:34
bool Step(int slot, int index, int direction)
std::function< Subscription(int64_t delayMs, std::function< void()> callback)> Timer
One-shot delays and repeating timers, run on the game thread.
Definition Scheduler.hpp:18
"The occupant of this slot changed" - raised by PlayerManager, consumed below it.
Localization system. Loads one JSON file per language; nested objects flatten into dotted keys (categ...
static std::string ReadFile(const std::filesystem::path &path)
Definition Loader.cpp:56