yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
input.cc
Go to the documentation of this file.
1#include "input.h"
2#include "util/i18n/tr.h"
3
4#include <algorithm>
5#include <functional>
6#include <limits>
7#include <string>
8#include <variant>
9
10#include "absl/strings/string_view.h"
13#include "imgui/imgui.h"
14#include "imgui/imgui_internal.h"
15
16template <class... Ts>
17struct overloaded : Ts... {
18 using Ts::operator()...;
19};
20template <class... Ts>
21overloaded(Ts...) -> overloaded<Ts...>;
22
23namespace ImGui {
24
25static inline ImGuiInputTextFlags InputScalar_DefaultCharsFilter(
26 ImGuiDataType data_type, const char* format) {
27 if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
28 return ImGuiInputTextFlags_CharsScientific;
29 const char format_last_char = format[0] ? format[strlen(format) - 1] : 0;
30 return (format_last_char == 'x' || format_last_char == 'X')
31 ? ImGuiInputTextFlags_CharsHexadecimal
32 : ImGuiInputTextFlags_CharsDecimal;
33}
34
35// Helper: returns true if label is "invisible" (starts with "##")
36static inline bool IsInvisibleLabel(const char* label) {
37 return label && label[0] == '#' && label[1] == '#';
38}
39
40// Result struct for extended input functions
42 bool changed; // Any change occurred
43 bool immediate; // Change was from button/wheel (apply immediately)
44 bool text_changed; // Change was from text input
45 bool text_committed; // Text input was committed (deactivated after edit)
46};
47
48bool InputScalarLeft(const char* label, ImGuiDataType data_type, void* p_data,
49 const void* p_step, const void* p_step_fast,
50 const char* format, float input_width,
51 ImGuiInputTextFlags flags, bool no_step = false) {
52 InputScalarResult result = {};
53 // Call extended version and return simple bool
54 // (implementation below handles both)
55
56 ImGuiWindow* window = ImGui::GetCurrentWindow();
57 if (window->SkipItems)
58 return false;
59
60 ImGuiContext& g = *GImGui;
61 ImGuiStyle& style = g.Style;
62
63 if (format == NULL)
64 format = DataTypeGetInfo(data_type)->PrintFmt;
65
66 char buf[64];
67 DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format);
68
69 if (g.ActiveId == 0 && (flags & (ImGuiInputTextFlags_CharsDecimal |
70 ImGuiInputTextFlags_CharsHexadecimal |
71 ImGuiInputTextFlags_CharsScientific)) == 0)
72 flags |= InputScalar_DefaultCharsFilter(data_type, format);
73 flags |= ImGuiInputTextFlags_AutoSelectAll;
74
75 bool value_changed = false;
76 const float button_size = GetFrameHeight();
77
78 // Support invisible labels (##) by not rendering the label, but still using
79 // it for ID
80 bool invisible_label = IsInvisibleLabel(label);
81
82 if (!invisible_label) {
83 AlignTextToFramePadding();
84 Text("%s", label);
85 SameLine();
86 }
87
88 BeginGroup(); // The only purpose of the group here is to allow the caller
89 // to query item data e.g. IsItemActive()
90 PushID(label);
91 SetNextItemWidth(ImMax(
92 1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2));
93
94 // Place the label on the left of the input field, unless invisible
95 PushStyleVar(ImGuiStyleVar_ItemSpacing,
96 ImVec2{style.ItemSpacing.x, style.ItemSpacing.y});
97 PushStyleVar(ImGuiStyleVar_FramePadding,
98 ImVec2{style.FramePadding.x, style.FramePadding.y});
99
100 SetNextItemWidth(input_width);
101 if (InputText("", buf, IM_ARRAYSIZE(buf),
102 flags)) // PushId(label) + "" gives us the expected ID
103 // from outside point of view
104 value_changed = DataTypeApplyFromText(buf, data_type, p_data, format);
105 IMGUI_TEST_ENGINE_ITEM_INFO(
106 g.LastItemData.ID, label,
107 g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable);
108
109 // Mouse wheel support
110 if (IsItemHovered() && g.IO.MouseWheel != 0.0f) {
111 float scroll_amount = g.IO.MouseWheel;
112 float scroll_speed = 0.25f; // Adjust the scroll speed as needed
113
114 if (g.IO.KeyCtrl && p_step_fast)
115 scroll_amount *= *(const float*)p_step_fast;
116 else
117 scroll_amount *= *(const float*)p_step;
118
119 if (scroll_amount > 0.0f) {
120 scroll_amount *= scroll_speed; // Adjust the scroll speed as needed
121 DataTypeApplyOp(data_type, '+', p_data, p_data, &scroll_amount);
122 value_changed = true;
123 } else if (scroll_amount < 0.0f) {
124 scroll_amount *= -scroll_speed; // Adjust the scroll speed as needed
125 DataTypeApplyOp(data_type, '-', p_data, p_data, &scroll_amount);
126 value_changed = true;
127 }
128 }
129
130 // Step buttons
131 if (!no_step) {
132 const ImVec2 backup_frame_padding = style.FramePadding;
133 style.FramePadding.x = style.FramePadding.y;
134 ImGuiButtonFlags button_flags = ImGuiButtonFlags_PressedOnClick;
135 if (flags & ImGuiInputTextFlags_ReadOnly)
136 BeginDisabled();
137 SameLine(0, style.ItemInnerSpacing.x);
138 if (ButtonEx("-", ImVec2(button_size, button_size), button_flags)) {
139 DataTypeApplyOp(data_type, '-', p_data, p_data,
140 g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);
141 value_changed = true;
142 }
143 SameLine(0, style.ItemInnerSpacing.x);
144 if (ButtonEx("+", ImVec2(button_size, button_size), button_flags)) {
145 DataTypeApplyOp(data_type, '+', p_data, p_data,
146 g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);
147 value_changed = true;
148 }
149
150 if (flags & ImGuiInputTextFlags_ReadOnly)
151 EndDisabled();
152
153 style.FramePadding = backup_frame_padding;
154 }
155 PopID();
156 EndGroup();
157 ImGui::PopStyleVar(2);
158
159 if (value_changed)
160 MarkItemEdited(g.LastItemData.ID);
161
162 return value_changed;
163}
164
165// Extended version that tracks change source
166InputScalarResult InputScalarLeftEx(const char* label, ImGuiDataType data_type,
167 void* p_data, const void* p_step,
168 const void* p_step_fast, const char* format,
169 float input_width,
170 ImGuiInputTextFlags flags,
171 bool no_step = false) {
172 InputScalarResult result = {false, false, false, false};
173
174 ImGuiWindow* window = ImGui::GetCurrentWindow();
175 if (window->SkipItems)
176 return result;
177
178 ImGuiContext& g = *GImGui;
179 ImGuiStyle& style = g.Style;
180
181 if (format == NULL)
182 format = DataTypeGetInfo(data_type)->PrintFmt;
183
184 char buf[64];
185 DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format);
186
187 if (g.ActiveId == 0 && (flags & (ImGuiInputTextFlags_CharsDecimal |
188 ImGuiInputTextFlags_CharsHexadecimal |
189 ImGuiInputTextFlags_CharsScientific)) == 0)
190 flags |= InputScalar_DefaultCharsFilter(data_type, format);
191 flags |= ImGuiInputTextFlags_AutoSelectAll;
192
193 const float button_size = GetFrameHeight();
194
195 // Support invisible labels (##) by not rendering the label, but still using
196 // it for ID
197 bool invisible_label = IsInvisibleLabel(label);
198
199 if (!invisible_label) {
200 AlignTextToFramePadding();
201 Text("%s", label);
202 SameLine();
203 }
204
205 BeginGroup();
206 PushID(label);
207 SetNextItemWidth(ImMax(
208 1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2));
209
210 PushStyleVar(ImGuiStyleVar_ItemSpacing,
211 ImVec2{style.ItemSpacing.x, style.ItemSpacing.y});
212 PushStyleVar(ImGuiStyleVar_FramePadding,
213 ImVec2{style.FramePadding.x, style.FramePadding.y});
214
215 SetNextItemWidth(input_width);
216 if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) {
217 if (DataTypeApplyFromText(buf, data_type, p_data, format)) {
218 result.text_changed = true;
219 result.changed = true;
220 }
221 }
222
223 // Check if text input was committed (deactivated after edit)
224 if (IsItemDeactivatedAfterEdit()) {
225 result.text_committed = true;
226 }
227
228 IMGUI_TEST_ENGINE_ITEM_INFO(
229 g.LastItemData.ID, label,
230 g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable);
231
232 // Mouse wheel support - immediate change
233 if (IsItemHovered() && g.IO.MouseWheel != 0.0f) {
234 float scroll_amount = g.IO.MouseWheel;
235 float scroll_speed = 0.25f;
236
237 if (g.IO.KeyCtrl && p_step_fast)
238 scroll_amount *= *(const float*)p_step_fast;
239 else
240 scroll_amount *= *(const float*)p_step;
241
242 if (scroll_amount > 0.0f) {
243 scroll_amount *= scroll_speed;
244 DataTypeApplyOp(data_type, '+', p_data, p_data, &scroll_amount);
245 result.changed = true;
246 result.immediate = true;
247 } else if (scroll_amount < 0.0f) {
248 scroll_amount *= -scroll_speed;
249 DataTypeApplyOp(data_type, '-', p_data, p_data, &scroll_amount);
250 result.changed = true;
251 result.immediate = true;
252 }
253 }
254
255 // Step buttons - immediate change
256 if (!no_step) {
257 const ImVec2 backup_frame_padding = style.FramePadding;
258 style.FramePadding.x = style.FramePadding.y;
259 ImGuiButtonFlags button_flags = ImGuiButtonFlags_PressedOnClick;
260 if (flags & ImGuiInputTextFlags_ReadOnly)
261 BeginDisabled();
262 SameLine(0, style.ItemInnerSpacing.x);
263 if (ButtonEx("-", ImVec2(button_size, button_size), button_flags)) {
264 DataTypeApplyOp(data_type, '-', p_data, p_data,
265 g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);
266 result.changed = true;
267 result.immediate = true;
268 }
269 SameLine(0, style.ItemInnerSpacing.x);
270 if (ButtonEx("+", ImVec2(button_size, button_size), button_flags)) {
271 DataTypeApplyOp(data_type, '+', p_data, p_data,
272 g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);
273 result.changed = true;
274 result.immediate = true;
275 }
276
277 if (flags & ImGuiInputTextFlags_ReadOnly)
278 EndDisabled();
279
280 style.FramePadding = backup_frame_padding;
281 }
282 PopID();
283 EndGroup();
284 ImGui::PopStyleVar(2);
285
286 if (result.changed)
287 MarkItemEdited(g.LastItemData.ID);
288
289 return result;
290}
291} // namespace ImGui
292
293namespace yaze {
294namespace gui {
295
296namespace {
297
299 if (!ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) {
300 return false;
301 }
302
303 const ImGuiIO& io = ImGui::GetIO();
304 const bool platform_primary_held = io.KeyCtrl || io.KeySuper;
305 return ImGui::IsItemActive() || platform_primary_held;
306}
307
308template <typename T>
309bool ApplyHexMouseWheel(T* data, T min_value, T max_value) {
311 return false;
312 }
313
314 const float wheel = ImGui::GetIO().MouseWheel;
315 if (wheel == 0.0f) {
316 return false;
317 }
318
319 using Numeric = long long;
320 Numeric new_value = static_cast<Numeric>(*data) + (wheel > 0.0f ? 1 : -1);
321 new_value = std::clamp(new_value, static_cast<Numeric>(min_value),
322 static_cast<Numeric>(max_value));
323 if (static_cast<T>(new_value) != *data) {
324 *data = static_cast<T>(new_value);
325 ImGui::ClearActiveID();
326 return true;
327 }
328 return false;
329}
330
331} // namespace
332
333const int kStepOneHex = 0x01;
334const int kStepFastHex = 0x0F;
335
336bool InputHex(const char* label, uint64_t* data) {
337 return ImGui::InputScalar(label, ImGuiDataType_U64, data, &kStepOneHex,
338 &kStepFastHex, "%06X",
339 ImGuiInputTextFlags_CharsHexadecimal);
340}
341
342bool InputHex(const char* label, int* data, int num_digits, float input_width) {
343 const std::string format = "%0" + std::to_string(num_digits) + "X";
344 return ImGui::InputScalarLeft(label, ImGuiDataType_S32, data, &kStepOneHex,
345 &kStepFastHex, format.c_str(), input_width,
346 ImGuiInputTextFlags_CharsHexadecimal);
347}
348
349bool InputHexShort(const char* label, uint32_t* data) {
350 return ImGui::InputScalar(label, ImGuiDataType_U32, data, &kStepOneHex,
351 &kStepFastHex, "%06X",
352 ImGuiInputTextFlags_CharsHexadecimal);
353}
354
355bool InputHexWord(const char* label, uint16_t* data, float input_width,
356 bool no_step) {
357 bool changed = ImGui::InputScalarLeft(
358 label, ImGuiDataType_U16, data, &kStepOneHex, &kStepFastHex, "%04X",
359 input_width, ImGuiInputTextFlags_CharsHexadecimal, no_step);
360 bool wheel_changed = ApplyHexMouseWheel<uint16_t>(
361 data, 0u, std::numeric_limits<uint16_t>::max());
362 return changed || wheel_changed;
363}
364
365bool InputHexWord(const char* label, int16_t* data, float input_width,
366 bool no_step) {
367 bool changed = ImGui::InputScalarLeft(
368 label, ImGuiDataType_S16, data, &kStepOneHex, &kStepFastHex, "%04X",
369 input_width, ImGuiInputTextFlags_CharsHexadecimal, no_step);
370 bool wheel_changed =
371 ApplyHexMouseWheel<int16_t>(data, std::numeric_limits<int16_t>::min(),
372 std::numeric_limits<int16_t>::max());
373 return changed || wheel_changed;
374}
375
376bool InputHexByte(const char* label, uint8_t* data, float input_width,
377 bool no_step) {
378 bool changed = ImGui::InputScalarLeft(
379 label, ImGuiDataType_U8, data, &kStepOneHex, &kStepFastHex, "%02X",
380 input_width, ImGuiInputTextFlags_CharsHexadecimal, no_step);
381 bool wheel_changed = ApplyHexMouseWheel<uint8_t>(
382 data, 0u, std::numeric_limits<uint8_t>::max());
383 return changed || wheel_changed;
384}
385
386bool InputHexByte(const char* label, uint8_t* data, uint8_t max_value,
387 float input_width, bool no_step) {
388 bool changed = ImGui::InputScalarLeft(
389 label, ImGuiDataType_U8, data, &kStepOneHex, &kStepFastHex, "%02X",
390 input_width, ImGuiInputTextFlags_CharsHexadecimal, no_step);
391 if (changed && *data > max_value) {
392 *data = max_value;
393 }
394 bool wheel_changed = ApplyHexMouseWheel<uint8_t>(data, 0u, max_value);
395 return changed || wheel_changed;
396}
397
398// Extended versions that properly track change source
399InputHexResult InputHexByteEx(const char* label, uint8_t* data,
400 float input_width, bool no_step) {
401 auto result = ImGui::InputScalarLeftEx(
402 label, ImGuiDataType_U8, data, &kStepOneHex, &kStepFastHex, "%02X",
403 input_width, ImGuiInputTextFlags_CharsHexadecimal, no_step);
404 InputHexResult hex_result;
405 hex_result.changed = result.changed;
406 hex_result.immediate = result.immediate;
407 hex_result.text_committed = result.text_committed;
408 return hex_result;
409}
410
411InputHexResult InputHexByteEx(const char* label, uint8_t* data,
412 uint8_t max_value, float input_width,
413 bool no_step) {
414 auto result = ImGui::InputScalarLeftEx(
415 label, ImGuiDataType_U8, data, &kStepOneHex, &kStepFastHex, "%02X",
416 input_width, ImGuiInputTextFlags_CharsHexadecimal, no_step);
417 if (result.changed && *data > max_value) {
418 *data = max_value;
419 }
420 InputHexResult hex_result;
421 hex_result.changed = result.changed;
422 hex_result.immediate = result.immediate;
423 hex_result.text_committed = result.text_committed;
424 return hex_result;
425}
426
427InputHexResult InputHexWordEx(const char* label, uint16_t* data,
428 float input_width, bool no_step) {
429 auto result = ImGui::InputScalarLeftEx(
430 label, ImGuiDataType_U16, data, &kStepOneHex, &kStepFastHex, "%04X",
431 input_width, ImGuiInputTextFlags_CharsHexadecimal, no_step);
432 InputHexResult hex_result;
433 hex_result.changed = result.changed;
434 hex_result.immediate = result.immediate;
435 hex_result.text_committed = result.text_committed;
436 return hex_result;
437}
438
439void Paragraph(const std::string& text) {
440 ImGui::TextWrapped("%s", text.c_str());
441}
442
443// TODO: Setup themes and text/clickable colors
444bool ClickableText(const std::string& text) {
445 ImGui::BeginGroup();
446 ImGui::PushID(text.c_str());
447
448 // Calculate text size
449 ImVec2 text_size = ImGui::CalcTextSize(text.c_str());
450
451 // Get cursor position for hover detection
452 ImVec2 pos = ImGui::GetCursorScreenPos();
453 ImRect bb(pos, ImVec2(pos.x + text_size.x, pos.y + text_size.y));
454
455 // Add item
456 const ImGuiID id = ImGui::GetID(text.c_str());
457 bool result = false;
458 if (ImGui::ItemAdd(bb, id)) {
459 bool hovered = ImGui::IsItemHovered();
460 bool clicked = ImGui::IsItemClicked();
461
462 // Render text with high-contrast appropriate color
463 ImVec4 link_color = ImGui::GetStyleColorVec4(ImGuiCol_TextLink);
464 ImVec4 bg_color = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
465
466 // Ensure good contrast against background
467 float contrast_factor =
468 (bg_color.x + bg_color.y + bg_color.z) < 1.5f ? 1.0f : 0.3f;
469
470 ImVec4 color;
471 if (hovered) {
472 // Brighter color on hover for better visibility
473 color = ImVec4(std::min(1.0f, link_color.x + 0.3f),
474 std::min(1.0f, link_color.y + 0.3f),
475 std::min(1.0f, link_color.z + 0.3f), 1.0f);
476 } else {
477 // Ensure link color has good contrast
478 color = ImVec4(std::max(contrast_factor, link_color.x),
479 std::max(contrast_factor, link_color.y),
480 std::max(contrast_factor, link_color.z), 1.0f);
481 }
482
483 ImGui::GetWindowDrawList()->AddText(
484 pos, ImGui::ColorConvertFloat4ToU32(color), text.c_str());
485
486 result = clicked;
487 }
488
489 ImGui::PopID();
490
491 // Advance cursor past the text
492 ImGui::Dummy(text_size);
493 ImGui::EndGroup();
494
495 return result;
496}
497
498void ItemLabel(absl::string_view title, ItemLabelFlags flags) {
499 ImGuiWindow* window = ImGui::GetCurrentWindow();
500 const ImVec2 lineStart = ImGui::GetCursorScreenPos();
501 const ImGuiStyle& style = ImGui::GetStyle();
502 float fullWidth = ImGui::GetContentRegionAvail().x;
503 float itemWidth = ImGui::CalcItemWidth() + style.ItemSpacing.x;
504 ImVec2 textSize =
505 ImGui::CalcTextSize(title.data(), title.data() + title.size());
506 ImRect textRect;
507 textRect.Min = ImGui::GetCursorScreenPos();
508 if (flags & ItemLabelFlag::Right)
509 textRect.Min.x = textRect.Min.x + itemWidth;
510 textRect.Max = textRect.Min;
511 textRect.Max.x += fullWidth - itemWidth;
512 textRect.Max.y += textSize.y;
513
514 ImGui::SetCursorScreenPos(textRect.Min);
515
516 ImGui::AlignTextToFramePadding();
517 // Adjust text rect manually because we render it directly into a drawlist
518 // instead of using public functions.
519 textRect.Min.y += window->DC.CurrLineTextBaseOffset;
520 textRect.Max.y += window->DC.CurrLineTextBaseOffset;
521
522 ImGui::ItemSize(textRect);
523 if (ImGui::ItemAdd(
524 textRect, window->GetID(title.data(), title.data() + title.size()))) {
525 ImGui::RenderTextEllipsis(ImGui::GetWindowDrawList(), textRect.Min,
526 textRect.Max, textRect.Max.x, title.data(),
527 title.data() + title.size(), &textSize);
528
529 if (textRect.GetWidth() < textSize.x && ImGui::IsItemHovered())
530 ImGui::SetTooltip("%.*s", (int)title.size(), title.data());
531 }
532 if (flags & ItemLabelFlag::Left) {
533 ImVec2 result;
534 auto other = ImVec2{0, textSize.y + window->DC.CurrLineTextBaseOffset};
535 result.x = textRect.Max.x - other.x;
536 result.y = textRect.Max.y - other.y;
537 ImGui::SetCursorScreenPos(result);
538 ImGui::SameLine();
539 } else if (flags & ItemLabelFlag::Right)
540 ImGui::SetCursorScreenPos(lineStart);
541}
542
543bool ListBox(const char* label, int* current_item,
544 const std::vector<std::string>& items, int height_in_items) {
545 std::vector<const char*> items_ptr;
546 items_ptr.reserve(items.size());
547 for (const auto& item : items) {
548 items_ptr.push_back(item.c_str());
549 }
550 int items_count = static_cast<int>(items.size());
551 return ImGui::ListBox(label, current_item, items_ptr.data(), items_count,
552 height_in_items);
553}
554
555bool InputTileInfo(const char* label, gfx::TileInfo* tile_info) {
556 ImGui::PushID(label);
557 ImGui::BeginGroup();
558 bool changed = false;
559 changed |= InputHexWord(label, &tile_info->id_);
560 changed |= InputHexByte("Palette", &tile_info->palette_);
561 changed |= ImGui::Checkbox(tr("Priority"), &tile_info->over_);
562 changed |= ImGui::Checkbox(tr("Vertical Flip"), &tile_info->vertical_mirror_);
563 changed |=
564 ImGui::Checkbox(tr("Horizontal Flip"), &tile_info->horizontal_mirror_);
565 ImGui::EndGroup();
566 ImGui::PopID();
567 return changed;
568}
569
570ImGuiID GetID(const std::string& id) {
571 return ImGui::GetID(id.c_str());
572}
573
574ImGuiKey MapKeyToImGuiKey(char key) {
575 switch (key) {
576 case 'A':
577 return ImGuiKey_A;
578 case 'B':
579 return ImGuiKey_B;
580 case 'C':
581 return ImGuiKey_C;
582 case 'D':
583 return ImGuiKey_D;
584 case 'E':
585 return ImGuiKey_E;
586 case 'F':
587 return ImGuiKey_F;
588 case 'G':
589 return ImGuiKey_G;
590 case 'H':
591 return ImGuiKey_H;
592 case 'I':
593 return ImGuiKey_I;
594 case 'J':
595 return ImGuiKey_J;
596 case 'K':
597 return ImGuiKey_K;
598 case 'L':
599 return ImGuiKey_L;
600 case 'M':
601 return ImGuiKey_M;
602 case 'N':
603 return ImGuiKey_N;
604 case 'O':
605 return ImGuiKey_O;
606 case 'P':
607 return ImGuiKey_P;
608 case 'Q':
609 return ImGuiKey_Q;
610 case 'R':
611 return ImGuiKey_R;
612 case 'S':
613 return ImGuiKey_S;
614 case 'T':
615 return ImGuiKey_T;
616 case 'U':
617 return ImGuiKey_U;
618 case 'V':
619 return ImGuiKey_V;
620 case 'W':
621 return ImGuiKey_W;
622 case 'X':
623 return ImGuiKey_X;
624 case 'Y':
625 return ImGuiKey_Y;
626 case 'Z':
627 return ImGuiKey_Z;
628 case '/':
629 return ImGuiKey_Slash;
630 case '-':
631 return ImGuiKey_Minus;
632 default:
633 return ImGuiKey_COUNT;
634 }
635}
636
637void AddTableColumn(Table& table, const std::string& label,
638 GuiElement element) {
639 table.column_labels.push_back(label);
640 table.column_contents.push_back(element);
641}
642
643void DrawTable(Table& params) {
644 if (ImGui::BeginTable(params.id, params.num_columns, params.flags,
645 params.size)) {
646 for (int i = 0; i < params.num_columns; ++i)
647 ImGui::TableSetupColumn(params.column_labels[i].c_str());
648
649 for (int i = 0; i < params.num_columns; ++i) {
650 ImGui::TableNextColumn();
651 switch (params.column_contents[i].index()) {
652 case 0:
653 std::get<0>(params.column_contents[i])();
654 break;
655 case 1:
656 ImGui::Text("%s", std::get<1>(params.column_contents[i]).c_str());
657 break;
658 }
659 }
660 ImGui::EndTable();
661 }
662}
663
664bool OpenUrl(const std::string& url) {
665 // if iOS
666#ifdef __APPLE__
667#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
668 // no system call on iOS
669 return false;
670#else
671 return system(("open " + url).c_str()) == 0;
672#endif
673#endif
674
675#ifdef __linux__
676 return system(("xdg-open " + url).c_str()) == 0;
677#endif
678
679#ifdef __windows__
680 return system(("start " + url).c_str()) == 0;
681#endif
682
683 return false;
684}
685
686void MemoryEditorPopup(const std::string& label, std::span<uint8_t> memory) {
687 static bool open = false;
688 static yaze::gui::MemoryEditorWidget editor;
689 if (ImGui::Button(tr("View Data"))) {
690 open = true;
691 }
692 if (open) {
693 ImGui::Begin(label.c_str(), &open);
694 editor.DrawContents(memory.data(), memory.size());
695 ImGui::End();
696 }
697}
698
699// Custom hex input functions that properly respect width
700bool InputHexByteCustom(const char* label, uint8_t* data, float input_width) {
701 ImGui::PushID(label);
702
703 // Create a simple hex input that respects width
704 char buf[8];
705 snprintf(buf, sizeof(buf), "%02X", *data);
706
707 ImGui::SetNextItemWidth(input_width);
708 bool changed = ImGui::InputText(
709 label, buf, sizeof(buf),
710 ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_AutoSelectAll);
711
712 if (changed) {
713 unsigned int temp;
714 if (sscanf(buf, "%X", &temp) == 1) {
715 *data = static_cast<uint8_t>(temp & 0xFF);
716 }
717 }
718
719 ImGui::PopID();
720 return changed;
721}
722
723bool InputHexWordCustom(const char* label, uint16_t* data, float input_width) {
724 ImGui::PushID(label);
725
726 // Create a simple hex input that respects width
727 char buf[8];
728 snprintf(buf, sizeof(buf), "%04X", *data);
729
730 ImGui::SetNextItemWidth(input_width);
731 bool changed = ImGui::InputText(
732 label, buf, sizeof(buf),
733 ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_AutoSelectAll);
734
735 if (changed) {
736 unsigned int temp;
737 if (sscanf(buf, "%X", &temp) == 1) {
738 *data = static_cast<uint16_t>(temp & 0xFFFF);
739 }
740 }
741
742 ImGui::PopID();
743 return changed;
744}
745
746bool SliderFloatWheel(const char* label, float* v, float v_min, float v_max,
747 const char* format, float wheel_step,
748 ImGuiSliderFlags flags) {
749 bool changed = ImGui::SliderFloat(label, v, v_min, v_max, format, flags);
750
751 // Require active focus or the platform primary modifier so hovering while
752 // scrolling a panel doesn't unexpectedly change the value.
753 if (IsValueWheelAdjustmentAllowedForCurrentItem()) {
754 float wheel = ImGui::GetIO().MouseWheel;
755 if (wheel != 0.0f) {
756 *v = std::clamp(*v + wheel * wheel_step, v_min, v_max);
757 changed = true;
758 }
759 }
760 return changed;
761}
762
763bool SliderIntWheel(const char* label, int* v, int v_min, int v_max,
764 const char* format, int wheel_step,
765 ImGuiSliderFlags flags) {
766 bool changed = ImGui::SliderInt(label, v, v_min, v_max, format, flags);
767
768 if (IsValueWheelAdjustmentAllowedForCurrentItem()) {
769 float wheel = ImGui::GetIO().MouseWheel;
770 if (wheel != 0.0f) {
771 int delta = static_cast<int>(wheel) * wheel_step;
772 *v = std::clamp(*v + delta, v_min, v_max);
773 changed = true;
774 }
775 }
776 return changed;
777}
778
779} // namespace gui
780} // namespace yaze
SNES 16-bit tile metadata container.
Definition snes_tile.h:52
overloaded(Ts...) -> overloaded< Ts... >
Definition input.cc:23
InputScalarResult InputScalarLeftEx(const char *label, ImGuiDataType data_type, void *p_data, const void *p_step, const void *p_step_fast, const char *format, float input_width, ImGuiInputTextFlags flags, bool no_step=false)
Definition input.cc:166
bool InputScalarLeft(const char *label, ImGuiDataType data_type, void *p_data, const void *p_step, const void *p_step_fast, const char *format, float input_width, ImGuiInputTextFlags flags, bool no_step=false)
Definition input.cc:48
bool ApplyHexMouseWheel(T *data, T min_value, T max_value)
Definition input.cc:309
bool InputHexByteCustom(const char *label, uint8_t *data, float input_width)
Definition input.cc:700
bool ClickableText(const std::string &text)
Definition input.cc:444
bool SliderIntWheel(const char *label, int *v, int v_min, int v_max, const char *format, int wheel_step, ImGuiSliderFlags flags)
Definition input.cc:763
void Paragraph(const std::string &text)
Definition input.cc:439
void ItemLabel(absl::string_view title, ItemLabelFlags flags)
Definition input.cc:498
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
Definition input.cc:355
bool ListBox(const char *label, int *current_item, const std::vector< std::string > &items, int height_in_items)
Definition input.cc:543
bool SliderFloatWheel(const char *label, float *v, float v_min, float v_max, const char *format, float wheel_step, ImGuiSliderFlags flags)
Definition input.cc:746
bool InputHexShort(const char *label, uint32_t *data)
Definition input.cc:349
void AddTableColumn(Table &table, const std::string &label, GuiElement element)
Definition input.cc:637
enum ItemLabelFlag { Left=1u<< 0u, Right=1u<< 1u, Default=Left, } ItemLabelFlags
Definition input.h:82
void MemoryEditorPopup(const std::string &label, std::span< uint8_t > memory)
Definition input.cc:686
const int kStepOneHex
Definition input.cc:333
void DrawTable(Table &params)
Definition input.cc:643
bool OpenUrl(const std::string &url)
Definition input.cc:664
bool InputHexWordCustom(const char *label, uint16_t *data, float input_width)
Definition input.cc:723
bool InputHex(const char *label, uint64_t *data)
Definition input.cc:336
bool InputTileInfo(const char *label, gfx::TileInfo *tile_info)
Definition input.cc:555
std::variant< std::function< void()>, std::string > GuiElement
Definition input.h:94
InputHexResult InputHexByteEx(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:399
const int kStepFastHex
Definition input.cc:334
ImGuiID GetID(const std::string &id)
Definition input.cc:570
ImGuiKey MapKeyToImGuiKey(char key)
Definition input.cc:574
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:376
InputHexResult InputHexWordEx(const char *label, uint16_t *data, float input_width, bool no_step)
Definition input.cc:427
void DrawContents(void *mem_data_void, size_t mem_size, size_t base_display_addr=0x0000)
std::vector< std::string > column_labels
Definition input.h:101
std::vector< GuiElement > column_contents
Definition input.h:102
ImVec2 size
Definition input.h:100
int num_columns
Definition input.h:98
const char * id
Definition input.h:97
ImGuiTableFlags flags
Definition input.h:99