VoltMod
C++23 framework for CS2 server plugins
Loading...
Searching...
No Matches
PanoramaMenuLayout.cpp
Go to the documentation of this file.
2#include <cstddef>
3#include <format>
4#include <utility>
5
6namespace VoltMod
7{
8
10 std::size_t rows, std::span<const std::string_view> iconNames)
11 : _screens(screens, std::string(screen)),
12 _root(screen),
13 _subtitle(std::format("{}_subtitle", screen)),
14 _close(std::format("{}_close", screen)),
15 _empty(std::format("{}_empty", screen)),
16 _prompt(std::format("{}_prompt", screen)),
17 _cancel(std::format("{}_cancel", screen)),
18 _back(std::format("{}_back", screen)),
19 _page(std::format("{}_page", screen)),
20 _pagePrevious(std::format("{}_page_previous", screen)),
21 _pageNext(std::format("{}_page_next", screen)),
22 _iconNames(iconNames)
23{
24 for (std::size_t index = 0; index < tabs; ++index)
25 {
26 _tabs.push_back(TabIds{
27 .Id = std::format("{}_tab{}", screen, index),
28 .Icon = std::format("{}_tab{}_icon", screen, index),
29 .LabelVar = std::format("tab{}", index),
30 });
31 }
32
33 for (std::size_t index = 0; index < rows; ++index)
34 {
35 _rows.push_back(RowIds{
36 .Id = std::format("{}_row{}", screen, index),
37 .Button = std::format("{}_row{}_button", screen, index),
38 .Decrease = std::format("{}_row{}_decrease", screen, index),
39 .Increase = std::format("{}_row{}_increase", screen, index),
40 .LabelVar = std::format("row{}_label", index),
41 .HintVar = std::format("row{}_hint", index),
42 .ValueVar = std::format("row{}_value", index),
43 });
44 }
45}
46
48{
49 Screen& screen = _screens.For(slot);
50 if (!screen.EnsureSpawned(slot))
51 {
52 return false;
53 }
54
55 screen.SetHidden(slot, _root, false);
56 screen.ShowCursor(slot, true);
57 return true;
58}
59
61{
62 Screen* screen = _screens.Find(slot);
63 if (!screen || !*screen)
64 {
65 return;
66 }
67
68 // Clients draw only their first custom_hud_layout, so a hidden one would block the next menu.
69 screen->ShowCursor(slot, false);
70 screen->Remove();
71}
72
74{
75 Screen& screen = _screens.For(slot);
76 screen.SetText(slot, "brand", header.Brand);
77 screen.SetText(slot, "brand_subtitle", header.BrandSubtitle);
78 screen.SetText(slot, "breadcrumb", header.Breadcrumb);
79 screen.SetText(slot, "title", header.Title);
80 screen.SetText(slot, "subtitle", header.Subtitle);
81 screen.SetHidden(slot, _subtitle, header.Subtitle.empty());
82
83 for (const ScreenText& text : _texts)
84 {
85 screen.SetText(slot, text.Variable, text.Value(slot));
86 }
87}
88
90{
91 _screens.For(slot).SetClass(slot, _root, "screen--no-sidebar", !visible);
92}
93
95{
96 _screens.For(slot).SetClass(slot, _root, "screen--home", visible);
97}
98
99void PanoramaMenuLayout::SetTab(int slot, int index, const MenuTab* tab)
100{
101 const TabIds& ids = _tabs[static_cast<std::size_t>(index)];
102 Screen& screen = _screens.For(slot);
103
104 screen.SetHidden(slot, ids.Id, !tab);
105 if (!tab)
106 {
107 return;
108 }
109
110 screen.SetText(slot, ids.LabelVar, tab->Label);
111 screen.SetClass(slot, ids.Id, "tab--selected", tab->Selected);
112
113 screen.SetModifier(slot, ids.Icon, "icon-set", _iconNames, tab->Icon);
114}
115
116void PanoramaMenuLayout::SetRow(int slot, int index, const MenuRow* row, std::string_view pendingHint)
117{
118 const RowIds& ids = _rows[static_cast<std::size_t>(index)];
119 Screen& screen = _screens.For(slot);
120
121 screen.SetHidden(slot, ids.Id, !row);
122 if (!row)
123 {
124 return;
125 }
126
127 const bool toggle = row->Kind == MenuRowKind::Toggle;
128 screen.SetText(slot, ids.LabelVar, row->Label);
129 screen.SetText(slot, ids.ValueVar, row->Value);
130 screen.SetClass(slot, ids.Id, "row--value", !row->Value.empty());
131 screen.SetClass(slot, ids.Id, "row--disabled", !row->Enabled);
132 screen.SetClass(slot, ids.Id, "row--on", row->State.value_or(false));
133 screen.SetClass(slot, ids.Id, "row--toggle", toggle);
134 screen.SetClass(slot, ids.Id, "row--chevron", row->Kind == MenuRowKind::Submenu || row->Kind == MenuRowKind::Input);
135 // An inert row still ships a live button, so without this it hovers and clicks like any other.
136 screen.SetClass(slot, ids.Id, "row--static", !row->Selectable);
137 screen.SetClass(slot, ids.Id, "row--steppers", row->Steppable && row->Enabled && !toggle);
138 screen.SetClass(slot, ids.Id, "row--pending", row->Pending);
139 if (row->Pending)
140 {
141 screen.SetText(slot, ids.HintVar, pendingHint);
142 }
143}
144
145void PanoramaMenuLayout::SetEmpty(int slot, std::string_view text)
146{
147 Screen& screen = _screens.For(slot);
148 screen.SetText(slot, "empty", text);
149 screen.SetHidden(slot, _empty, text.empty());
150}
151
152void PanoramaMenuLayout::SetPager(int slot, std::string_view text)
153{
154 Screen& screen = _screens.For(slot);
155 screen.SetText(slot, "page", text);
156 screen.SetHidden(slot, _page, text.empty());
157}
158
159void PanoramaMenuLayout::SetPrompt(int slot, std::string_view text, std::string_view hint)
160{
161 Screen& screen = _screens.For(slot);
162 screen.SetText(slot, "prompt_text", text);
163 screen.SetText(slot, "prompt_hint", hint);
164 screen.SetHidden(slot, _prompt, text.empty());
165 screen.SetClass(slot, _root, "screen--prompting", !text.empty());
166}
167
168void PanoramaMenuLayout::SetFooter(int slot, std::string_view back, std::string_view cancel)
169{
170 Screen& screen = _screens.For(slot);
171 screen.SetText(slot, "back", back);
172 screen.SetHidden(slot, _back, back.empty());
173 screen.SetText(slot, "cancel", cancel);
174}
175
176void PanoramaMenuLayout::AddText(std::string_view variable, std::function<std::string(int slot)> text)
177{
178 _texts.push_back(ScreenText{.Variable = std::string{variable}, .Value = std::move(text)});
179}
180
181std::optional<MenuButton> PanoramaMenuLayout::ButtonFor(std::string_view id) const
182{
183 if (id == _cancel)
184 {
186 }
187 if (id == _back)
188 {
190 }
191 if (id == _close)
192 {
194 }
195 if (id == _pagePrevious)
196 {
198 }
199 if (id == _pageNext)
200 {
202 }
203
204 for (int index = 0; index < TabCount(); ++index)
205 {
206 if (id == _tabs[static_cast<std::size_t>(index)].Id)
207 {
208 return MenuButton{MenuButtonKind::Tab, index};
209 }
210 }
211
212 for (int index = 0; index < RowCount(); ++index)
213 {
214 const RowIds& row = _rows[static_cast<std::size_t>(index)];
215 if (id == row.Button)
216 {
217 return MenuButton{MenuButtonKind::Row, index};
218 }
219 if (id == row.Decrease)
220 {
222 }
223 if (id == row.Increase)
224 {
225 return MenuButton{MenuButtonKind::StepUp, index};
226 }
227 }
228
229 return std::nullopt;
230}
231
232} // namespace VoltMod
void SetEmpty(int slot, std::string_view text)
std::optional< MenuButton > ButtonFor(std::string_view id) const
void SetHomeVisible(int slot, bool visible)
PanoramaMenuLayout(ScreenManager &screens, std::string_view screen, std::size_t tabs, std::size_t rows, std::span< const std::string_view > iconNames)
void AddText(std::string_view variable, std::function< std::string(int slot)> text)
void SetFooter(int slot, std::string_view back, std::string_view cancel)
void SetPrompt(int slot, std::string_view text, std::string_view hint)
void SetHeader(int slot, const MenuHeader &header)
void SetRow(int slot, int index, const MenuRow *row, std::string_view pendingHint)
void SetPager(int slot, std::string_view text)
void SetTab(int slot, int index, const MenuTab *tab)
void SetSidebarVisible(int slot, bool visible)
Screen & For(int slot)
Screen * Find(int slot)
Create Screen objects and report their button presses.
A spawned Panorama layout created by ScreenManager.
Definition Screen.hpp:23
void ShowCursor(int slot, bool shown)
Definition Screen.cpp:62
void SetHidden(int slot, std::string_view elementId, bool hidden)
Definition Screen.cpp:46
void SetClass(int slot, std::string_view elementId, std::string_view className, bool on)
Definition Screen.cpp:38
void SetText(int slot, std::string_view variable, std::string_view value)
Definition Screen.cpp:30
@ Input
Carries a value the player types in chat.
@ Submenu
Opens another menu.
@ Toggle
Carries an on/off value.
static std::string ReadFile(const std::filesystem::path &path)
Definition Loader.cpp:56