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