ArcdpsExtension
 
Loading...
Searching...
No Matches
Widgets.h
Go to the documentation of this file.
1#pragma once
2
3#include "arcdps_structs.h"
5
6#include <algorithm>
7#include <cfloat>
8#include <concepts>
9#include <cstdint>
10#include <cstring>
11#include <format>
12#include <functional>
13#include <imgui/imgui.h>
14#include <imgui/imgui_internal.h>
15#include <initializer_list>
16#include <map>
17#include <optional>
18#include <ranges>
19#include <string>
20#include <string_view>
21#include <tuple>
22#include <type_traits>
23#include <utility>
24#include <vector>
25#include <windows.h>
26
27namespace ImGuiEx {
28 bool Spinner(const char* label, float radius, float thickness, const ImU32& color);
29 bool SpinnerAligned(const char* label, float radius, float thickness, const ImU32& color, Alignment alignment);
30 void AlignedTextColumn(Alignment alignment, const char* text, ...);
31 void TableHeader(const char* label, bool show_label, ImTextureID texture, Alignment alignment = Alignment::Left);
32 void AlignedProgressBar(float fraction, const ImVec2& size_arg, const char* overlay, Alignment alignment);
33 bool BeginMenu(const char* label, bool enabled, bool& hovered);
34 void BeginMenuChild(const char* child_str_id, const char* menu_label, std::function<void()> draw_func);
35 void BeginMenu(const char* menu_label, std::function<void()> draw_func);
36 bool BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags, ImGuiHoveredFlags hovered_flags);
37 void MenuItemTableColumnVisibility(ImGuiTable* table, int columnIdx);
38 bool TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags, void* icon);
39
45 bool BeginTable(const char* str_id, int column, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0.0f, 0.0f), float inner_width = 0.0f, ImGuiWindowFlags child_window_flags = 0);
46
56 // returns true if the window moved
57 bool WindowReposition(ImGuiWindow* window, Position position, const ImVec2& cornerVector, CornerPosition cornerPosition, ImGuiID fromWindowID, CornerPosition anchorPanelCornerPosition, CornerPosition selfPanelCornerPosition);
58
62 template<typename E>
63 requires std::is_enum_v<E>
64 bool Selectable(E& storage, E value) {
65 const bool selected = ImGui::Selectable(to_string(value).data());
66 if (selected) {
67 storage = value;
68 }
69 return selected;
70 }
71
75 template<typename E, std::ranges::range R>
76 requires(std::is_enum_v<E> && std::same_as<std::ranges::range_value_t<R>, E>)
77 bool EnumCombo(const char* label, E& storage, const R& values, const std::map<E, std::function<std::string()>>& pPopupText = {}) {
78 if (ImGui::BeginCombo(label, to_string(storage).data())) {
79 bool selected = false;
80 for (const E& val : values) {
81 if (ImGuiEx::Selectable(storage, val)) {
82 selected = true;
83 }
84
85 if (ImGui::IsItemHovered()) {
86 if (const auto& iterator = pPopupText.find(val); iterator != pPopupText.end()) {
87 const auto& second = iterator->second();
88 ImGui::SetTooltip("%s", second.c_str());
89 }
90 }
91 }
92
93 ImGui::EndCombo();
94
95 return selected;
96 }
97
98 return false;
99 }
100
101 template<typename E>
102 requires(std::is_enum_v<E>)
103 bool EnumCombo(const char* label, E& storage, const std::initializer_list<E>& values, const std::map<E, std::function<std::string()>>& pPopupText = {}) {
104 if (ImGui::BeginCombo(label, to_string(storage).data())) {
105 bool selected = false;
106 for (const E& val : values) {
107 if (ImGuiEx::Selectable(storage, val)) {
108 selected = true;
109 }
110
111 if (ImGui::IsItemHovered()) {
112 if (const auto& iterator = pPopupText.find(val); iterator != pPopupText.end()) {
113 ImGui::SetTooltip("%s", iterator->second().c_str());
114 }
115 }
116 }
117
118 ImGui::EndCombo();
119
120 return selected;
121 }
122
123 return false;
124 }
125
132 template<typename E>
133 requires std::is_enum_v<E>
134 bool EnumCombo(const char* label, E& storage, E lastElement, const std::map<uint64_t, std::function<std::string()>>& pPopupText = {}) {
135 if (ImGui::BeginCombo(label, to_string(storage).data())) {
136 bool selected = false;
137 for (uint64_t i = 0; i < static_cast<uint64_t>(lastElement); ++i) {
138 if (ImGuiEx::Selectable(storage, static_cast<E>(i))) {
139 selected = true;
140 }
141
142 if (ImGui::IsItemHovered()) {
143 if (const auto& iterator = pPopupText.find(i); iterator != pPopupText.end()) {
144 ImGui::SetTooltip("%s", iterator->second().c_str());
145 }
146 }
147 }
148
149 ImGui::EndCombo();
150
151 return selected;
152 }
153 return false;
154 }
155
156 template<typename E>
157 requires std::is_enum_v<E>
158 bool EnumRadioButton(int& buttonStorage, E value) {
159 return ImGui::RadioButton(to_string(value).data(), &buttonStorage, static_cast<int>(value));
160 }
161
162 template<typename E>
163 requires std::is_enum_v<E>
164 bool EnumRadioButton(int& buttonStorage, E value, E& storage) {
165 bool res = EnumRadioButton(buttonStorage, value);
166 if (res) {
167 storage = value;
168 }
169
170 return res;
171 }
172
173 // FIXME: This would work nicely if it was a public template, e.g. 'template<T> RadioButton(const char* label, T* v, T v_button)', but I'm not sure how we would expose it..
174 // I have fixed it, don't know what the problem is with this ... --knox
175 template<typename T>
176 bool RadioButton(const char* label, T& v, T v_button) {
177 const bool pressed = ImGui::RadioButton(label, v == v_button);
178 if (pressed)
179 v = v_button;
180 return pressed;
181 }
182
183 template<class... Args>
184 void TextColored(const ImVec4& col, std::string_view fmt, Args&&... args) {
185 ImGui::PushStyleColor(ImGuiCol_Text, col);
186 ImGui::TextEx(std::vformat(fmt, std::make_format_args(args...)).c_str(), NULL, ImGuiTextFlags_NoWidthForLargeClippedText); // Skip formatting
187 ImGui::PopStyleColor();
188 }
189
190#ifdef _WIN32
202 void KeyInput(const char* label, const char* id, char* buffer, size_t bufSize, WPARAM& keyContainer, const char* notSetText);
203#endif
204
214 template<typename T>
215 void OptionalSetting(std::optional<T>& setting, const char* title, const char* checkboxLabel, std::function<T()> constructValue, std::function<void()> children) {
216 ImGui::TextUnformatted(title);
217 ImGui::SameLine();
218 bool settingActive = setting.has_value();
219
220 if (ImGui::Checkbox(checkboxLabel, &settingActive)) {
221 if (settingActive) {
222 setting = constructValue();
223 } else {
224 setting.reset();
225 }
226 }
227 ImGui::SameLine();
228
229 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, !settingActive);
230
231 children();
232
233 // Pop ImGuiItemFlags_Disabled
234 ImGui::PopItemFlag();
235 }
236
237
238 // This code is derived from an issue in the ImGui github repo: https://github.com/ocornut/imgui/issues/1658#issuecomment-886171438
239 // Therefore the original code is licensed under the same license as ImGui (MIT)
240 //
241 // returns if the value was changed.
242 // `pPopupOpen` returns if the popup is open
243 template<std::ranges::viewable_range T, typename ValueType = std::ranges::views::all_t<T>>
244 // template<std::ranges::common_range T, typename ValueType = std::ranges::range_value_t<T>>
245 bool FilteredCombo(const char* pLabel, const T& pContainer, ValueType& pCurrent, bool* pPopupOpen = nullptr) {
246 // this is breaking the overloaded to_string functions (in theory it should work, but it doesn't :( )
247 // using std::to_string;
248
249 ImGuiContext& g = *GImGui;
250
251 ImGuiWindow* window = ImGui::GetCurrentWindow();
252 if (window->SkipItems)
253 return false;
254
255 static char searchInputBuffer[256] = {0};
256
257 std::string popupName = std::format("###FilteredCombo_popup_name_{}", pLabel);
258
259 // Display items
260 // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed)
261 bool valueChanged = false;
262
263 const float expectedWidth = ImGui::CalcItemWidth();
264 bool isNewOpen = false;
265 float frameHeight = ImGui::GetFrameHeight();
266 ImVec2 size(frameHeight, frameHeight);
267 ImVec2 CursorPos = window->DC.CursorPos;
268 ImVec2 pos = CursorPos + ImVec2(expectedWidth - frameHeight, 0);
269 const ImRect bb(pos, pos + size);
270
271 float ButtonTextAlignX = g.Style.ButtonTextAlign.x;
272 g.Style.ButtonTextAlign.x = 0;
273 if (ImGui::Button(std::format("{}###FilteredCombo_button_label_{}", to_string(pCurrent), pLabel).c_str(), ImVec2(expectedWidth, 0))) {
274 ImGui::OpenPopup(popupName.c_str());
275 isNewOpen = true;
276 memset(searchInputBuffer, 0, sizeof searchInputBuffer);
277 }
278 g.Style.ButtonTextAlign.x = ButtonTextAlignX;
279 // bool hovered = ImGui::IsItemHovered();
280 // bool active = ImGui::IsItemActivated();
281 // bool pressed = ImGui::IsItemClicked();
282
283 // Render
284 //const ImU32 bg_col = GetColorU32((active && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
285 //RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding);
286 const ImU32 textColor = ImGui::GetColorU32(ImGuiCol_Text);
287 ImGui::RenderArrow(window->DrawList, bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), textColor, ImGuiDir_Down);
288
289 ImVec2 item_max = ImGui::GetItemRectMax();
290 ImGui::SetNextWindowPos({CursorPos.x, item_max.y});
291 ImGui::SetNextWindowSize({ImGui::GetItemRectSize().x, 0});
292
293 if (ImGui::BeginPopup(popupName.c_str())) {
294 using CacheVectorType = std::tuple<double, ValueType>;
295 static std::vector<CacheVectorType> valueCache;
296
297 if (pPopupOpen != nullptr) {
298 *pPopupOpen = true;
299 }
300
301 ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4) ImColor(240, 240, 240, 255));
302 ImGui::PushStyleColor(ImGuiCol_Text, (ImVec4) ImColor(0, 0, 0, 255));
303 ImGui::PushItemWidth(-FLT_MIN);
304
305 // Filter input
306 if (isNewOpen) {
307 ImGui::SetKeyboardFocusHere();
308 for (const auto& val : pContainer) {
309 valueCache.emplace_back(1, val);
310 }
311 }
312 if (ImGui::InputText("###FilteredCombo_InputText", searchInputBuffer, sizeof searchInputBuffer)) {
313 // input changed, recalculate fuzzy values
314 valueCache.clear();
315 if (searchInputBuffer[0] == '\0') {
316 for (const auto& val : pContainer) {
317 valueCache.emplace_back(1, val);
318 }
319 } else {
320 for (const auto& val : pContainer) {
321 const auto& valStr = to_string(val);
322 double ratio = rapidfuzz::fuzz::ratio(valStr, searchInputBuffer);
323 if (ratio >= 0.5)
324 valueCache.emplace_back(ratio, val);
325 }
326
327 std::ranges::sort(valueCache, [](const CacheVectorType& val1, const CacheVectorType& val2) {
328 return std::get<0>(val1) > std::get<0>(val2);
329 });
330 }
331 }
332
333 // Search Icon, you can use it if you load IconsFontAwesome5 https://github.com/juliettef/IconFontCppHeaders
334 //const ImVec2 label_size = CalcTextSize(ICON_FA_SEARCH, NULL, true);
335 //const ImVec2 search_icon_pos(ImGui::GetItemRectMax().x - label_size.x - style.ItemInnerSpacing.x * 2, window->DC.CursorPos.y + style.FramePadding.y + g.FontSize * 0.1f);
336 //RenderText(search_icon_pos, ICON_FA_SEARCH);
337
338 ImGui::PopStyleColor(2);
339
340 if (ImGui::ListBoxHeader("###FilteredCombo_ItemList")) {
341 for (const auto& value : valueCache) {
342 const auto& val = std::get<1>(value);
343 const bool itemSelected = (val == pCurrent);
344 if (ImGui::Selectable(to_string(val).c_str(), itemSelected)) {
345 valueChanged = true;
346 pCurrent = val;
347 ImGui::CloseCurrentPopup();
348 }
349 if (itemSelected)
350 ImGui::SetItemDefaultFocus();
351 }
352 ImGui::ListBoxFooter();
353 }
354 ImGui::PopItemWidth();
355 ImGui::EndPopup();
356 }
357
358
359 if (valueChanged)
360 ImGui::MarkItemEdited(g.CurrentWindow->DC.LastItemId);
361
362 return valueChanged;
363 }
364
365 // template<typename T, typename ValueType>
366 // requires std::ranges::common_range<T> && (!std::ranges::viewable_range<T>)
367 // bool test(const char* pLabel, const T& pContainer, ValueType& pCurrent, bool* pPopupOpen = nullptr) {
368 // return FilteredCombo(pLabel, std::ranges::views::all(pContainer), pCurrent, pPopupOpen);
369 // }
370 template<std::ranges::common_range T, typename ValueType = std::ranges::range_value_t<T>>
371 requires(!std::ranges::viewable_range<T>)
372 bool FilteredCombo(const char* pLabel, const T& pContainer, ValueType& pCurrent, bool* pPopupOpen = nullptr) {
373 return FilteredCombo(pLabel, std::ranges::views::all(pContainer), pCurrent, pPopupOpen);
374 }
375} // namespace ImGuiEx
std::string_view to_string(Alignment alignment)
Definition arcdps_structs.cpp:7
CornerPosition
Definition arcdps_structs.h:81
Position
Definition arcdps_structs.h:73
Alignment
Definition arcdps_structs.h:64
double ratio(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, double score_cutoff=0)
calculates a simple ratio between two strings
Definition rapidfuzz_amalgamated.hpp:4383
Definition KeyInput.cpp:55
void KeyInput(const char *label, const char *id, char *buffer, size_t bufSize, WPARAM &keyContainer, const char *notSetText)
Definition Widgets.cpp:1199
void TableHeader(const char *label, bool show_label, ImTextureID texture, Alignment alignment)
Definition Widgets.cpp:130
void BeginMenuChild(const char *child_str_id, const char *menu_label, std::function< void()> draw_func)
Definition Widgets.cpp:558
bool BeginTable(const char *str_id, int columns_count, ImGuiTableFlags flags, const ImVec2 &outer_size, float inner_width, ImGuiWindowFlags child_window_flags)
Definition Widgets.cpp:1191
bool Selectable(E &storage, E value)
Definition Widgets.h:64
bool SpinnerAligned(const char *label, float radius, float thickness, const ImU32 &color, Alignment alignment)
Definition Widgets.cpp:59
bool TableIsMouseHoveringCurrentRow()
Definition Widgets.cpp:620
bool FilteredCombo(const char *pLabel, const T &pContainer, ValueType &pCurrent, bool *pPopupOpen=nullptr)
Definition Widgets.h:245
bool EnumRadioButton(int &buttonStorage, E value)
Definition Widgets.h:158
bool EnumCombo(const char *label, E &storage, const R &values, const std::map< E, std::function< std::string()> > &pPopupText={})
Definition Widgets.h:77
bool RadioButton(const char *label, T &v, T v_button)
Definition Widgets.h:176
bool BeginMenu(const char *label, bool enabled, bool &hoveredPar)
Definition Widgets.cpp:403
bool WindowReposition(ImGuiWindow *window, Position position, const ImVec2 &cornerVector, CornerPosition cornerPosition, ImGuiID fromWindowID, CornerPosition anchorPanelCornerPosition, CornerPosition selfPanelCornerPosition)
Definition Widgets.cpp:626
void AlignedTextColumn(Alignment alignment, const char *text,...)
Definition Widgets.cpp:88
void OptionalSetting(std::optional< T > &setting, const char *title, const char *checkboxLabel, std::function< T()> constructValue, std::function< void()> children)
Definition Widgets.h:215
ImRect TableGetCurrentRowRect()
Definition Widgets.cpp:612
void AlignedProgressBar(float fraction, const ImVec2 &size_arg, const char *overlay, Alignment alignment)
Definition Widgets.cpp:325
bool TreeNodeEx(const char *label, ImGuiTreeNodeFlags flags, void *icon)
Definition Widgets.cpp:923
bool Spinner(const char *label, float radius, float thickness, const ImU32 &color)
Definition Widgets.cpp:21
void TextColored(const ImVec4 &col, std::string_view fmt, Args &&... args)
Definition Widgets.h:184
void MenuItemTableColumnVisibility(ImGuiTable *table, int columnIdx)
Definition Widgets.cpp:601
bool BeginPopupContextWindow(const char *str_id, ImGuiPopupFlags popup_flags, ImGuiHoveredFlags hovered_flags)
Definition Widgets.cpp:588
IMGUI_API bool InputText(const char *label, std::string *str, ImGuiInputTextFlags flags=0, ImGuiInputTextCallback callback=nullptr, void *user_data=nullptr)
Definition imgui_stdlib.cpp:38