openMSX
ImGuiUtils.hh
Go to the documentation of this file.
1#ifndef IMGUI_UTILS_HH
2#define IMGUI_UTILS_HH
3
4#include "ImGuiCpp.hh"
5
6#include "Reactor.hh"
7
8#include "function_ref.hh"
9#include "ranges.hh"
10#include "strCat.hh"
11#include "StringOp.hh"
12
13#include <imgui.h>
14
15#include <algorithm>
16#include <concepts>
17#include <span>
18#include <string>
19#include <string_view>
20#include <utility>
21
22namespace ImGui {
23
24inline void TextUnformatted(const std::string& str)
25{
26 const char* begin = str.data();
27 const char* end = begin + str.size();
29}
30inline void TextUnformatted(std::string_view str)
31{
32 const char* begin = str.data();
33 const char* end = begin + str.size();
35}
36
37inline auto CalcTextSize(std::string_view str)
38{
39 return ImGui::CalcTextSize(str.data(), str.data() + str.size());
40}
41
42template<typename... Ts>
43void StrCat(Ts&& ...ts)
44{
45 auto s = tmpStrCat(std::forward<Ts>(ts)...);
46 TextUnformatted(std::string_view(s));
47}
48
49inline void RightAlignText(std::string_view text, std::string_view maxWidthText)
50{
51 auto maxWidth = ImGui::CalcTextSize(maxWidthText).x;
52 auto actualWidth = ImGui::CalcTextSize(text).x;
53 if (auto spacing = maxWidth - actualWidth; spacing > 0.0f) {
54 auto pos = ImGui::GetCursorPosX();
55 ImGui::SetCursorPosX(pos + spacing);
56 }
58}
59
60} // namespace ImGui
61
62namespace openmsx {
63
64class BooleanSetting;
65class FloatSetting;
66class HotKey;
67class IntegerSetting;
68class Setting;
69class VideoSourceSetting;
70
72 std::string_view value;
73 std::string_view tip;
74};
75using EnumToolTips = std::span<const EnumToolTip>;
76
77inline void simpleToolTip(std::string_view desc)
78{
79 if (desc.empty()) return;
81 im::TextWrapPos(ImGui::GetFontSize() * 35.0f, [&]{
83 });
84 });
85}
86
87void simpleToolTip(std::invocable<> auto descFunc)
88{
89 if (!ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) return;
90 auto desc = std::invoke(descFunc);
91 if (desc.empty()) return;
92 im::Tooltip([&]{
93 im::TextWrapPos(ImGui::GetFontSize() * 35.0f, [&]{
95 });
96 });
97}
98
99void HelpMarker(std::string_view desc);
100
102{
103 static constexpr gl::vec2 center{0.5f, 0.5f};
104 gl::vec2 windowPos = ImGui::GetWindowPos();
105 gl::vec2 windowSize = ImGui::GetWindowSize();
106 auto windowCenter = windowPos + center * windowSize;
107 ImGui::SetNextWindowPos(windowCenter, ImGuiCond_Appearing, center);
108}
109
110void drawURL(std::string_view text, zstring_view url);
111
113 std::string operator()(const Setting& setting) const;
114};
115
116bool Checkbox(const HotKey& hotkey, BooleanSetting& setting);
117bool Checkbox(const HotKey& hotkey, const char* label, BooleanSetting& setting, function_ref<std::string(const Setting&)> getTooltip = GetSettingDescription{});
118bool SliderInt(IntegerSetting& setting, ImGuiSliderFlags flags = 0);
119bool SliderInt(const char* label, IntegerSetting& setting, ImGuiSliderFlags flags = 0);
120bool SliderFloat(FloatSetting& setting, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
121bool SliderFloat(const char* label, FloatSetting& setting, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
122bool InputText(Setting& setting);
123bool InputText(const char* label, Setting& setting);
124void ComboBox(Setting& setting, EnumToolTips toolTips = {}); // must be an EnumSetting
125void ComboBox(const char* label, Setting& setting, EnumToolTips toolTips = {}); // must be an EnumSetting
126void ComboBox(const char* label, Setting& setting, function_ref<std::string(const std::string&)> displayValue, EnumToolTips toolTips = {});
127void ComboBox(VideoSourceSetting& setting);
128void ComboBox(const char* label, VideoSourceSetting& setting);
129
130const char* getComboString(int item, const char* itemsSeparatedByZeros);
131
132std::string formatTime(std::optional<double> time);
133float calculateFade(float current, float target, float period);
134
135template<int HexDigits>
136void comboHexSequence(const char* label, int* value, int mult, int offset = 0) {
137 assert(offset < mult);
138 *value &= ~(mult - 1);
139 // only apply offset in display, not in the actual value
140 auto preview = tmpStrCat("0x", hex_string<HexDigits>(*value | offset));
141 im::Combo(label, preview.c_str(), [&]{
142 for (int addr = 0; addr < 0x1ffff; addr += mult) {
143 if (auto str = tmpStrCat("0x", hex_string<HexDigits>(addr | offset));
144 ImGui::Selectable(str.c_str(), *value == addr)) {
145 *value = addr;
146 }
147 if (*value == addr) {
148 ImGui::SetItemDefaultFocus();
149 }
150 }
151 });
152};
153
154template<typename Range, typename Projection>
155void sortUpDown_T(Range& range, const ImGuiTableSortSpecs* sortSpecs, Projection proj) {
156 if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Descending) {
157 ranges::stable_sort(range, std::greater<>{}, proj);
158 } else {
159 ranges::stable_sort(range, std::less<>{}, proj);
160 }
161};
162template<typename Range, typename Projection>
163void sortUpDown_String(Range& range, const ImGuiTableSortSpecs* sortSpecs, Projection proj) {
164 if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Descending) {
166 } else {
168 }
169};
170
171
172[[nodiscard]] inline const std::string* getOptionalDictValue(
173 const std::vector<std::pair<std::string, std::string>>& info,
174 std::string_view key)
175{
176 auto it = ranges::find_if(info, [&](const auto& p) { return p.first == key; });
177 if (it == info.end()) return {};
178 return &it->second;
179}
180
181template<typename T> // 'MachineInfo' or 'ExtensionInfo', both have a 'configInfo' member
182[[nodiscard]] std::vector<std::string> getAllValuesFor(std::string_view key, const std::vector<T>& items)
183{
184 std::vector<std::string> result;
185 for (const auto& item : items) {
186 if (const auto* value = getOptionalDictValue(item.configInfo, key)) {
187 if (!contains(result, *value)) { // O(N^2), but that's fine
188 result.emplace_back(*value);
189 }
190 }
191 }
193 return result;
194}
195
196template<typename T>
197void displayFilterCombo(std::string& selection, zstring_view key, const std::vector<T>& items)
198{
199 im::Combo(key.c_str(), selection.empty() ? "--all--" : selection.c_str(), [&]{
200 if (ImGui::Selectable("--all--")) {
201 selection.clear();
202 }
203 for (const auto& type : getAllValuesFor(key, items)) {
204 if (ImGui::Selectable(type.c_str())) {
205 selection = type;
206 }
207 }
208 });
209}
210
211template<typename T>
212void applyComboFilter(std::string_view key, std::string_view value, const std::vector<T>& items, std::vector<size_t>& indices)
213{
214 if (value.empty()) return;
215 std::erase_if(indices, [&](auto idx) {
216 const auto& info = items[idx].configInfo;
217 const auto* val = getOptionalDictValue(info, key);
218 if (!val) return true; // remove items that don't have the key
219 return *val != value;
220 });
221}
222
223template<std::invocable<size_t> GetName>
224void filterIndices(std::string_view filterString, GetName getName, std::vector<size_t>& indices)
225{
226 if (filterString.empty()) return;
227 std::erase_if(indices, [&](auto idx) {
228 const auto& name = getName(idx);
229 return !ranges::all_of(StringOp::split_view<StringOp::EmptyParts::REMOVE>(filterString, ' '),
230 [&](auto part) { return StringOp::containsCaseInsensitive(name, part); });
231 });
232}
233
234template<typename T>
235void applyDisplayNameFilter(std::string_view filterString, const std::vector<T>& items, std::vector<size_t>& indices)
236{
237 filterIndices(filterString, [&](size_t idx) { return items[idx].displayName; }, indices);
238}
239
240// Similar to c++23 chunk_by(). Main difference is internal vs external iteration.
241template<typename Range, typename BinaryPred, typename Action>
242static void chunk_by(Range&& range, BinaryPred pred, Action action)
243{
244 auto it = std::begin(range);
245 auto last = std::end(range);
246 while (it != last) {
247 auto start = it;
248 auto prev = it++;
249 while (it != last && pred(*prev, *it)) {
250 prev = it++;
251 }
252 action(start, it);
253 }
254}
255
256std::string getShortCutForCommand(const HotKey& hotkey, std::string_view command);
257
258std::string getKeyChordName(ImGuiKeyChord keyChord);
259std::optional<ImGuiKeyChord> parseKeyChord(std::string_view name);
260
261// Read from VRAM-table, including mirroring behavior
262// shared between ImGuiCharacter, ImGuiSpriteViewer
264public:
265 VramTable(std::span<const uint8_t> vram_, bool planar_ = false)
266 : vram(vram_), planar(planar_) {}
267
268 void setRegister(unsigned value, unsigned extraLsbBits) {
269 registerMask = (value << extraLsbBits) | ~(~0u << extraLsbBits);
270 }
271 void setIndexSize(unsigned bits) {
272 indexMask = ~0u << bits;
273 }
274
275 [[nodiscard]] uint8_t operator[](unsigned index) const {
276 auto addr = registerMask & (indexMask | index);
277 if (planar) {
278 addr = ((addr << 16) | (addr >> 1)) & 0x1'FFFF;
279 }
280 return vram[addr];
281 }
282private:
283 std::span<const uint8_t> vram;
284 unsigned registerMask = 0;
285 unsigned indexMask = 0;
286 bool planar = false;
287};
288
289enum class imColor : unsigned {
291 BLACK,
292 WHITE,
293 GRAY,
294 YELLOW,
295 RED_BG, // red background (transparent)
296 YELLOW_BG, // yellow background (transparent)
297
298 TEXT,
300
301 ERROR,
302 WARNING,
303
304 COMMENT, // syntax highlighting in the console
305 VARIABLE,
306 LITERAL,
307 PROC,
308 OPERATOR,
309
310 KEY_ACTIVE, // virtual keyboard
312
314};
315inline std::array<ImU32, size_t(imColor::NUM_COLORS)> imColors;
316
317void setColors(int style);
318
319inline ImU32 getColor(imColor col) {
320 assert(col < imColor::NUM_COLORS);
321 return imColors[size_t(col)];
322}
323
324} // namespace openmsx
325
326#endif
BaseSetting * setting
void setRegister(unsigned value, unsigned extraLsbBits)
VramTable(std::span< const uint8_t > vram_, bool planar_=false)
uint8_t operator[](unsigned index) const
void setIndexSize(unsigned bits)
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
constexpr const char * c_str() const
void StrCat(Ts &&...ts)
Definition ImGuiUtils.hh:43
auto CalcTextSize(std::string_view str)
Definition ImGuiUtils.hh:37
void TextUnformatted(const std::string &str)
Definition ImGuiUtils.hh:24
void RightAlignText(std::string_view text, std::string_view maxWidthText)
Definition ImGuiUtils.hh:49
bool containsCaseInsensitive(std::string_view haystack, std::string_view needle)
Definition StringOp.hh:181
void Combo(const char *label, const char *preview_value, ImGuiComboFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:289
void TextWrapPos(float wrap_local_pos_x, std::invocable<> auto next)
Definition ImGuiCpp.hh:212
void Tooltip(std::invocable<> auto next)
Definition ImGuiCpp.hh:374
void ItemTooltip(std::invocable<> auto next)
Definition ImGuiCpp.hh:382
This file implemented 3 utility functions:
Definition Autofire.cc:11
bool Checkbox(const HotKey &hotKey, BooleanSetting &setting)
Definition ImGuiUtils.cc:81
bool SliderFloat(FloatSetting &setting, const char *format, ImGuiSliderFlags flags)
std::span< const EnumToolTip > EnumToolTips
Definition ImGuiUtils.hh:75
void centerNextWindowOverCurrent()
bool SliderInt(IntegerSetting &setting, ImGuiSliderFlags flags)
void drawURL(std::string_view text, zstring_view url)
Definition ImGuiUtils.cc:30
void filterIndices(std::string_view filterString, GetName getName, std::vector< size_t > &indices)
void ComboBox(const char *label, Setting &setting, function_ref< std::string(const std::string &)> displayValue, EnumToolTips toolTips)
void applyComboFilter(std::string_view key, std::string_view value, const std::vector< T > &items, std::vector< size_t > &indices)
void sortUpDown_String(Range &range, const ImGuiTableSortSpecs *sortSpecs, Projection proj)
void simpleToolTip(std::string_view desc)
Definition ImGuiUtils.hh:77
std::vector< std::string > getAllValuesFor(std::string_view key, const std::vector< T > &items)
std::string getShortCutForCommand(const HotKey &hotkey, std::string_view command)
std::array< ImU32, size_t(imColor::NUM_COLORS)> imColors
void sortUpDown_T(Range &range, const ImGuiTableSortSpecs *sortSpecs, Projection proj)
void displayFilterCombo(std::string &selection, zstring_view key, const std::vector< T > &items)
void HelpMarker(std::string_view desc)
Definition ImGuiUtils.cc:23
const std::string * getOptionalDictValue(const std::vector< std::pair< std::string, std::string > > &info, std::string_view key)
bool InputText(Setting &setting)
ImU32 getColor(imColor col)
std::optional< ImGuiKeyChord > parseKeyChord(std::string_view name)
const char * getComboString(int item, const char *itemsSeparatedByZeros)
void setColors(int style)
void comboHexSequence(const char *label, int *value, int mult, int offset=0)
std::string getKeyChordName(ImGuiKeyChord keyChord)
void applyDisplayNameFilter(std::string_view filterString, const std::vector< T > &items, std::vector< size_t > &indices)
std::string formatTime(std::optional< double > time)
float calculateFade(float current, float target, float period)
constexpr bool all_of(InputRange &&range, UnaryPredicate pred)
Definition ranges.hh:188
auto find_if(InputRange &&range, UnaryPredicate pred)
Definition ranges.hh:175
void stable_sort(RandomAccessRange &&range)
Definition ranges.hh:78
constexpr void sort(RandomAccessRange &&range)
Definition ranges.hh:51
constexpr bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition stl.hh:32
TemporaryString tmpStrCat(Ts &&... ts)
Definition strCat.hh:742
std::string_view value
Definition ImGuiUtils.hh:72
std::string_view tip
Definition ImGuiUtils.hh:73
std::string operator()(const Setting &setting) const
Definition ImGuiUtils.cc:53
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)