openMSX
ImGuiUtils.cc
Go to the documentation of this file.
1#include "ImGuiUtils.hh"
2
3#include "ImGuiCpp.hh"
4
5#include "BooleanSetting.hh"
6#include "EnumSetting.hh"
7#include "HotKey.hh"
8#include "IntegerSetting.hh"
9#include "FloatSetting.hh"
10#include "VideoSourceSetting.hh"
11
12#include "ranges.hh"
13
14#include <imgui.h>
15#include <imgui_stdlib.h>
16#include <SDL.h>
17
18#include <variant>
19
20namespace openmsx {
21
22void HelpMarker(std::string_view desc)
23{
24 ImGui::SameLine();
25 ImGui::TextDisabled("(?)");
26 simpleToolTip(desc);
27}
28
29void drawURL(std::string_view text, zstring_view url)
30{
31 auto pos = ImGui::GetCursorScreenPos();
32 auto color = ImGui::GetColorU32(ImGuiCol_ButtonHovered);
33 im::StyleColor(ImGuiCol_Text, color, [&]{
35 });
36
37 simpleToolTip(url);
38
39 if (ImGui::IsItemHovered()) { // underline
40 auto size = ImGui::CalcTextSize(text);
41 auto* drawList = ImGui::GetWindowDrawList();
42 ImVec2 p1{pos.x, pos.y + size.y};
43 ImVec2 p2{pos.x + size.x, pos.y + size.y};
44 drawList->AddLine(p1, p2, color);
45 }
46
47 if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
48 SDL_OpenURL(url.c_str());
49 }
50}
51
53{
54 return std::string(setting.getDescription());
55}
56
57template<std::invocable<const Setting&> GetTooltip = GetSettingDescription>
58static void settingStuff(Setting& setting, GetTooltip getTooltip = {})
59{
60 simpleToolTip([&] { return getTooltip(setting); });
62 auto defaultValue = setting.getDefaultValue();
63 auto defaultString = defaultValue.getString();
64 ImGui::StrCat("Default value: ", defaultString);
65 if (defaultString.empty()) {
66 ImGui::SameLine();
67 ImGui::TextDisabled("<empty>");
68 }
69 if (ImGui::Button("Restore default")) {
70 try {
71 setting.setValue(defaultValue);
72 } catch (MSXException&) {
73 // ignore
74 }
75 ImGui::CloseCurrentPopup();
76 }
77 });
78}
79
81{
82 std::string name(setting.getBaseName());
83 return Checkbox(hotKey, name.c_str(), setting);
84}
85bool Checkbox(const HotKey& hotKey, const char* label, BooleanSetting& setting, std::function<std::string(const Setting&)> getTooltip)
86{
87 bool value = setting.getBoolean();
88 bool changed = ImGui::Checkbox(label, &value);
89 try {
90 if (changed) setting.setBoolean(value);
91 } catch (MSXException&) {
92 // ignore
93 }
94 settingStuff(setting, getTooltip);
95
96 ImGui::SameLine();
97 auto shortCut = getShortCutForCommand(hotKey, strCat("toggle ", setting.getBaseName()));
98 auto spacing = std::max(0.0f, ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(shortCut).x);
99 ImGui::SameLine(0.0f, spacing);
100 ImGui::TextDisabled("%s", shortCut.c_str());
101
102 return changed;
103}
104
105bool SliderInt(IntegerSetting& setting, ImGuiSliderFlags flags)
106{
107 std::string name(setting.getBaseName());
108 return SliderInt(name.c_str(), setting, flags);
109}
110bool SliderInt(const char* label, IntegerSetting& setting, ImGuiSliderFlags flags)
111{
112 int value = setting.getInt();
113 int min = setting.getMinValue();
114 int max = setting.getMaxValue();
115 bool changed = ImGui::SliderInt(label, &value, min, max, "%d", flags);
116 try {
117 if (changed) setting.setInt(value);
118 } catch (MSXException&) {
119 // ignore
120 }
121 settingStuff(setting);
122 return changed;
123}
124
125bool SliderFloat(FloatSetting& setting, const char* format, ImGuiSliderFlags flags)
126{
127 std::string name(setting.getBaseName());
128 return SliderFloat(name.c_str(), setting, format, flags);
129}
130bool SliderFloat(const char* label, FloatSetting& setting, const char* format, ImGuiSliderFlags flags)
131{
132 float value = setting.getFloat();
133 float min = narrow_cast<float>(setting.getMinValue());
134 float max = narrow_cast<float>(setting.getMaxValue());
135 bool changed = ImGui::SliderFloat(label, &value, min, max, format, flags);
136 try {
137 if (changed) setting.setFloat(value);
138 } catch (MSXException&) {
139 // ignore
140 }
141 settingStuff(setting);
142 return changed;
143}
144
146{
147 std::string name(setting.getBaseName());
148 return InputText(name.c_str(), setting);
149}
150bool InputText(const char* label, Setting& setting)
151{
152 auto value = std::string(setting.getValue().getString());
153 bool changed = ImGui::InputText(label, &value, ImGuiInputTextFlags_EnterReturnsTrue);
154 try {
155 if (changed) setting.setValue(TclObject(value));
156 } catch (MSXException&) {
157 // ignore
158 }
159 settingStuff(setting);
160 return changed;
161}
162
163void ComboBox(const char* label, Setting& setting, std::function<std::string(const std::string&)> displayValue, EnumToolTips toolTips)
164{
165 auto* enumSetting = dynamic_cast<EnumSettingBase*>(&setting);
166 assert(enumSetting);
167 auto current = setting.getValue().getString();
168 im::Combo(label, current.c_str(), [&]{
169 for (const auto& entry : enumSetting->getMap()) {
170 bool selected = entry.name == current;
171 const auto& display = displayValue(entry.name);
172 if (ImGui::Selectable(display.c_str(), selected)) {
173 try {
174 setting.setValue(TclObject(entry.name));
175 } catch (MSXException&) {
176 // ignore
177 }
178 }
179 if (auto it = ranges::find(toolTips, entry.name, &EnumToolTip::value);
180 it != toolTips.end()) {
181 simpleToolTip(it->tip);
182 }
183 }
184 });
185 settingStuff(setting);
186}
187void ComboBox(const char* label, Setting& setting, EnumToolTips toolTips)
188{
189 ComboBox(label, setting, std::identity{}, toolTips);
190}
192{
193 std::string name(setting.getBaseName());
194 ComboBox(name.c_str(), setting, toolTips);
195}
196
197void ComboBox(VideoSourceSetting& setting) // TODO share code with EnumSetting?
198{
199 std::string name(setting.getBaseName());
200 ComboBox(name.c_str(), setting);
201}
202void ComboBox(const char* label, VideoSourceSetting& setting) // TODO share code with EnumSetting?
203{
204 std::string name(setting.getBaseName());
205 auto current = setting.getValue().getString();
206 im::Combo(label, current.c_str(), [&]{
207 for (const auto& value : setting.getPossibleValues()) {
208 bool selected = value == current;
209 if (ImGui::Selectable(std::string(value).c_str(), selected)) {
210 try {
211 setting.setValue(TclObject(value));
212 } catch (MSXException&) {
213 // ignore
214 }
215 }
216 }
217 });
218 settingStuff(setting);
219}
220
221const char* getComboString(int item, const char* itemsSeparatedByZeros)
222{
223 const char* p = itemsSeparatedByZeros;
224 while (true) {
225 assert(*p);
226 if (item == 0) return p;
227 --item;
228 while (*p) ++p;
229 ++p;
230 }
231}
232
233std::string formatTime(double time)
234{
235 assert(time >= 0.0);
236 int hours = int(time * (1.0 / 3600.0));
237 time -= double(hours * 3600);
238 int minutes = int(time * (1.0 / 60.0));
239 time -= double(minutes * 60);
240 int seconds = int(time);
241 time -= double(seconds);
242 int hundreds = int(100.0 * time);
243
244 std::string result = "00:00:00.00";
245 auto insert = [&](size_t pos, unsigned value) {
246 assert(value < 100);
247 result[pos + 0] = char('0' + (value / 10));
248 result[pos + 1] = char('0' + (value % 10));
249 };
250 insert(0, hours % 100);
251 insert(3, minutes);
252 insert(6, seconds);
253 insert(9, hundreds);
254 return result;
255}
256
257float calculateFade(float current, float target, float period)
258{
259 const auto& io = ImGui::GetIO();
260 auto step = io.DeltaTime / period;
261 if (target > current) {
262 return std::min(target, current + step);
263 } else {
264 return std::max(target, current - step);
265 }
266}
267
268std::string getShortCutForCommand(const HotKey& hotkey, std::string_view command)
269{
270 for (const auto& info : hotkey.getGlobalBindings()) {
271 if (info.command != command) continue;
272 if (const auto* keyDown = std::get_if<KeyDownEvent>(&info.event)) {
273 std::string result;
274 auto modifiers = keyDown->getModifiers();
275 if (modifiers & KMOD_CTRL) strAppend(result, "CTRL+");
276 if (modifiers & KMOD_SHIFT) strAppend(result, "SHIFT+");
277 if (modifiers & KMOD_ALT) strAppend(result, "ALT+");
278 if (modifiers & KMOD_GUI) strAppend(result, "GUI+");
279 strAppend(result, SDL_GetKeyName(keyDown->getKeyCode()));
280 return result;
281 }
282 }
283 return "";
284}
285
286void setColors(int style)
287{
288 // style: 0->dark, 1->light, 2->classic
289 bool light = style == 1;
290
291 // AABBGGRR
292 imColors[size_t(imColor::TRANSPARENT )] = 0x00000000;
293 imColors[size_t(imColor::BLACK )] = 0xff000000;
294 imColors[size_t(imColor::WHITE )] = 0xffffffff;
295 imColors[size_t(imColor::GRAY )] = 0xff808080;
296 imColors[size_t(imColor::YELLOW )] = 0xff00ffff;
297 imColors[size_t(imColor::RED_BG )] = 0x400000ff;
298 imColors[size_t(imColor::YELLOW_BG )] = 0x8000ffff;
299
300 imColors[size_t(imColor::TEXT )] = ImGui::GetColorU32(ImGuiCol_Text);
301 imColors[size_t(imColor::TEXT_DISABLED )] = ImGui::GetColorU32(ImGuiCol_TextDisabled);
302
303 imColors[size_t(imColor::ERROR )] = 0xff0000ff;
304 imColors[size_t(imColor::WARNING )] = 0xff33b3ff;
305
306 imColors[size_t(imColor::COMMENT )] = 0xff5cff5c;
307 imColors[size_t(imColor::VARIABLE )] = 0xffffff00;
308 imColors[size_t(imColor::LITERAL )] = light ? 0xff9c5d27 : 0xff00ffff;
309 imColors[size_t(imColor::PROC )] = 0xffcd00cd;
310 imColors[size_t(imColor::OPERATOR )] = 0xffcdcd00;
311
312 imColors[size_t(imColor::KEY_ACTIVE )] = 0xff1040ff;
313 imColors[size_t(imColor::KEY_NOT_ACTIVE)] = 0x80000000;
314}
315
316} // namespace openmsx
BaseSetting * setting
virtual void setValue(const TclObject &value)=0
Change the value of this setting to the given value.
virtual const TclObject & getValue() const =0
Get current value as a TclObject.
virtual TclObject getDefaultValue() const =0
Get the default value of this setting.
std::string_view getBaseName() const
Definition Setting.hh:38
A Setting with a floating point value.
const auto & getGlobalBindings() const
Definition HotKey.hh:76
A Setting with an integer value.
zstring_view getString() const
Definition TclObject.cc:142
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 PopupContextItem(const char *str_id, ImGuiPopupFlags popup_flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:439
void Combo(const char *label, const char *preview_value, ImGuiComboFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:307
void StyleColor(ImGuiCol idx1, ImVec4 col1, ImGuiCol idx2, ImVec4 col2, std::invocable<> auto next)
Definition ImGuiCpp.hh:156
void format(SectorAccessibleDisk &disk, MSXBootSectorType bootType)
Format the given disk (= a single partition).
This file implemented 3 utility functions:
Definition Autofire.cc:9
bool Checkbox(const HotKey &hotKey, BooleanSetting &setting)
Definition ImGuiUtils.cc:80
bool SliderFloat(FloatSetting &setting, const char *format, ImGuiSliderFlags flags)
std::string formatTime(double time)
std::span< const EnumToolTip > EnumToolTips
Definition ImGuiUtils.hh:64
bool SliderInt(IntegerSetting &setting, ImGuiSliderFlags flags)
void drawURL(std::string_view text, zstring_view url)
Definition ImGuiUtils.cc:29
void simpleToolTip(std::string_view desc)
Definition ImGuiUtils.hh:66
std::string getShortCutForCommand(const HotKey &hotkey, std::string_view command)
void ComboBox(const char *label, Setting &setting, std::function< std::string(const std::string &)> displayValue, EnumToolTips toolTips)
std::array< ImU32, size_t(imColor::NUM_COLORS)> imColors
void HelpMarker(std::string_view desc)
Definition ImGuiUtils.cc:22
bool InputText(Setting &setting)
const char * getComboString(int item, const char *itemsSeparatedByZeros)
void setColors(int style)
float calculateFade(float current, float target, float period)
std::string strCat()
Definition strCat.hh:703
void strAppend(std::string &result, Ts &&...ts)
Definition strCat.hh:752
std::string operator()(const Setting &setting) const
Definition ImGuiUtils.cc:52