yaze 0.2.0
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 <functional>
4#include <optional>
5#include <string>
6
7#include "ImGuiFileDialog/ImGuiFileDialog.h"
8#include "absl/strings/string_view.h"
9#include "app/gfx/bitmap.h"
11#include "app/gfx/snes_tile.h"
12#include "app/gui/canvas.h"
13#include "app/gui/color.h"
14#include "imgui/imgui.h"
15#include "imgui/imgui_internal.h"
16#include "imgui/misc/cpp/imgui_stdlib.h"
17
18namespace ImGui {
19
20static inline ImGuiInputTextFlags InputScalar_DefaultCharsFilter(
21 ImGuiDataType data_type, const char* format) {
22 if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
23 return ImGuiInputTextFlags_CharsScientific;
24 const char format_last_char = format[0] ? format[strlen(format) - 1] : 0;
25 return (format_last_char == 'x' || format_last_char == 'X')
26 ? ImGuiInputTextFlags_CharsHexadecimal
27 : ImGuiInputTextFlags_CharsDecimal;
28}
29bool InputScalarLeft(const char* label, ImGuiDataType data_type, void* p_data,
30 const void* p_step, const void* p_step_fast,
31 const char* format, float input_width,
32 ImGuiInputTextFlags flags, bool no_step = false) {
33 ImGuiWindow* window = ImGui::GetCurrentWindow();
34 if (window->SkipItems) return false;
35
36 ImGuiContext& g = *GImGui;
37 ImGuiStyle& style = g.Style;
38
39 if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt;
40
41 char buf[64];
42 DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format);
43
44 if (g.ActiveId == 0 && (flags & (ImGuiInputTextFlags_CharsDecimal |
45 ImGuiInputTextFlags_CharsHexadecimal |
46 ImGuiInputTextFlags_CharsScientific)) == 0)
47 flags |= InputScalar_DefaultCharsFilter(data_type, format);
48 flags |= ImGuiInputTextFlags_AutoSelectAll;
49
50 bool value_changed = false;
51 // if (p_step == NULL) {
52 // ImGui::SetNextItemWidth(input_width);
53 // if (InputText("", buf, IM_ARRAYSIZE(buf), flags))
54 // value_changed = DataTypeApplyFromText(buf, data_type, p_data, format);
55 // } else {
56 const float button_size = GetFrameHeight();
57 AlignTextToFramePadding();
58 Text("%s", label);
59 SameLine();
60 BeginGroup(); // The only purpose of the group here is to allow the caller
61 // to query item data e.g. IsItemActive()
62 PushID(label);
63 SetNextItemWidth(ImMax(
64 1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2));
65
66 // Place the label on the left of the input field
67 PushStyleVar(ImGuiStyleVar_ItemSpacing,
68 ImVec2{style.ItemSpacing.x, style.ItemSpacing.y});
69 PushStyleVar(ImGuiStyleVar_FramePadding,
70 ImVec2{style.FramePadding.x, style.FramePadding.y});
71
72 SetNextItemWidth(input_width);
73 if (InputText("", buf, IM_ARRAYSIZE(buf),
74 flags)) // PushId(label) + "" gives us the expected ID
75 // from outside point of view
76 value_changed = DataTypeApplyFromText(buf, data_type, p_data, format);
77 IMGUI_TEST_ENGINE_ITEM_INFO(
78 g.LastItemData.ID, label,
79 g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable);
80
81 // Mouse wheel support
82 if (IsItemHovered() && g.IO.MouseWheel != 0.0f) {
83 float scroll_amount = g.IO.MouseWheel;
84 float scroll_speed = 0.25f; // Adjust the scroll speed as needed
85
86 if (g.IO.KeyCtrl && p_step_fast)
87 scroll_amount *= *(const float*)p_step_fast;
88 else
89 scroll_amount *= *(const float*)p_step;
90
91 if (scroll_amount > 0.0f) {
92 scroll_amount *= scroll_speed; // Adjust the scroll speed as needed
93 DataTypeApplyOp(data_type, '+', p_data, p_data, &scroll_amount);
94 value_changed = true;
95 } else if (scroll_amount < 0.0f) {
96 scroll_amount *= -scroll_speed; // Adjust the scroll speed as needed
97 DataTypeApplyOp(data_type, '-', p_data, p_data, &scroll_amount);
98 value_changed = true;
99 }
100 }
101
102 // Step buttons
103 if (!no_step) {
104 const ImVec2 backup_frame_padding = style.FramePadding;
105 style.FramePadding.x = style.FramePadding.y;
106 ImGuiButtonFlags button_flags = ImGuiButtonFlags_PressedOnClick;
107 if (flags & ImGuiInputTextFlags_ReadOnly) BeginDisabled();
108 SameLine(0, style.ItemInnerSpacing.x);
109 if (ButtonEx("-", ImVec2(button_size, button_size), button_flags)) {
110 DataTypeApplyOp(data_type, '-', p_data, p_data,
111 g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);
112 value_changed = true;
113 }
114 SameLine(0, style.ItemInnerSpacing.x);
115 if (ButtonEx("+", ImVec2(button_size, button_size), button_flags)) {
116 DataTypeApplyOp(data_type, '+', p_data, p_data,
117 g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);
118 value_changed = true;
119 }
120
121 if (flags & ImGuiInputTextFlags_ReadOnly) EndDisabled();
122
123 style.FramePadding = backup_frame_padding;
124 }
125 PopID();
126 EndGroup();
127 ImGui::PopStyleVar(2);
128
129 if (value_changed) MarkItemEdited(g.LastItemData.ID);
130
131 return value_changed;
132}
133} // namespace ImGui
134
135namespace yaze {
136namespace app {
137namespace gui {
138
139const int kStepOneHex = 0x01;
140const int kStepFastHex = 0x0F;
141
142bool InputHex(const char* label, uint64_t* data) {
143 return ImGui::InputScalar(label, ImGuiDataType_U64, data, &kStepOneHex,
144 &kStepFastHex, "%06X",
145 ImGuiInputTextFlags_CharsHexadecimal);
146}
147
148bool InputHex(const char* label, int* data, int num_digits, float input_width) {
149 const std::string format = "%0" + std::to_string(num_digits) + "X";
150 return ImGui::InputScalarLeft(label, ImGuiDataType_S32, data, &kStepOneHex,
151 &kStepFastHex, format.c_str(), input_width,
152 ImGuiInputTextFlags_CharsHexadecimal);
153}
154
155bool InputHexShort(const char* label, uint32_t* data) {
156 return ImGui::InputScalar(label, ImGuiDataType_U32, data, &kStepOneHex,
157 &kStepFastHex, "%06X",
158 ImGuiInputTextFlags_CharsHexadecimal);
159}
160
161bool InputHexWord(const char* label, uint16_t* data, float input_width,
162 bool no_step) {
163 return ImGui::InputScalarLeft(label, ImGuiDataType_U16, data, &kStepOneHex,
164 &kStepFastHex, "%04X", input_width,
165 ImGuiInputTextFlags_CharsHexadecimal, no_step);
166}
167
168bool InputHexWord(const char* label, int16_t* data, float input_width,
169 bool no_step) {
170 return ImGui::InputScalarLeft(label, ImGuiDataType_S16, data, &kStepOneHex,
171 &kStepFastHex, "%04X", input_width,
172 ImGuiInputTextFlags_CharsHexadecimal, no_step);
173}
174
175bool InputHexByte(const char* label, uint8_t* data, float input_width,
176 bool no_step) {
177 return ImGui::InputScalarLeft(label, ImGuiDataType_U8, data, &kStepOneHex,
178 &kStepFastHex, "%02X", input_width,
179 ImGuiInputTextFlags_CharsHexadecimal, no_step);
180}
181
182bool InputHexByte(const char* label, uint8_t* data, uint8_t max_value,
183 float input_width, bool no_step) {
184 if (ImGui::InputScalarLeft(label, ImGuiDataType_U8, data, &kStepOneHex,
185 &kStepFastHex, "%02X", input_width,
186 ImGuiInputTextFlags_CharsHexadecimal, no_step)) {
187 if (*data > max_value) {
188 *data = max_value;
189 }
190 return true;
191 }
192 return false;
193}
194
195void ItemLabel(absl::string_view title, ItemLabelFlags flags) {
196 ImGuiWindow* window = ImGui::GetCurrentWindow();
197 const ImVec2 lineStart = ImGui::GetCursorScreenPos();
198 const ImGuiStyle& style = ImGui::GetStyle();
199 float fullWidth = ImGui::GetContentRegionAvail().x;
200 float itemWidth = ImGui::CalcItemWidth() + style.ItemSpacing.x;
201 ImVec2 textSize = ImGui::CalcTextSize(title.begin(), title.end());
202 ImRect textRect;
203 textRect.Min = ImGui::GetCursorScreenPos();
204 if (flags & ItemLabelFlag::Right) textRect.Min.x = textRect.Min.x + itemWidth;
205 textRect.Max = textRect.Min;
206 textRect.Max.x += fullWidth - itemWidth;
207 textRect.Max.y += textSize.y;
208
209 ImGui::SetCursorScreenPos(textRect.Min);
210
211 ImGui::AlignTextToFramePadding();
212 // Adjust text rect manually because we render it directly into a drawlist
213 // instead of using public functions.
214 textRect.Min.y += window->DC.CurrLineTextBaseOffset;
215 textRect.Max.y += window->DC.CurrLineTextBaseOffset;
216
217 ImGui::ItemSize(textRect);
218 if (ImGui::ItemAdd(
219 textRect, window->GetID(title.data(), title.data() + title.size()))) {
220 ImGui::RenderTextEllipsis(
221 ImGui::GetWindowDrawList(), textRect.Min, textRect.Max, textRect.Max.x,
222 textRect.Max.x, title.data(), title.data() + title.size(), &textSize);
223
224 if (textRect.GetWidth() < textSize.x && ImGui::IsItemHovered())
225 ImGui::SetTooltip("%.*s", (int)title.size(), title.data());
226 }
227 if (flags & ItemLabelFlag::Left) {
228 ImVec2 result;
229 auto other = ImVec2{0, textSize.y + window->DC.CurrLineTextBaseOffset};
230 result.x = textRect.Max.x - other.x;
231 result.y = textRect.Max.y - other.y;
232 ImGui::SetCursorScreenPos(result);
233 ImGui::SameLine();
234 } else if (flags & ItemLabelFlag::Right)
235 ImGui::SetCursorScreenPos(lineStart);
236}
237
238bool ListBox(const char* label, int* current_item,
239 const std::vector<std::string>& items, int height_in_items) {
240 std::vector<const char*> items_ptr;
241 items_ptr.reserve(items.size());
242 for (const auto& item : items) {
243 items_ptr.push_back(item.c_str());
244 }
245 int items_count = static_cast<int>(items.size());
246 return ImGui::ListBox(label, current_item, items_ptr.data(), items_count,
247 height_in_items);
248}
249
250bool InputTileInfo(const char* label, gfx::TileInfo* tile_info) {
251 ImGui::PushID(label);
252 ImGui::BeginGroup();
253 bool changed = false;
254 changed |= InputHexWord(label, &tile_info->id_);
255 changed |= InputHexByte("Palette", &tile_info->palette_);
256 changed |= ImGui::Checkbox("Priority", &tile_info->over_);
257 changed |= ImGui::Checkbox("Vertical Flip", &tile_info->vertical_mirror_);
258 changed |= ImGui::Checkbox("Horizontal Flip", &tile_info->horizontal_mirror_);
259 ImGui::EndGroup();
260 ImGui::PopID();
261 return changed;
262}
263
264ImGuiID GetID(const std::string& id) { return ImGui::GetID(id.c_str()); }
265
266void FileDialogPipeline(absl::string_view display_key,
267 absl::string_view file_extensions,
268 std::optional<absl::string_view> button_text,
269 std::function<void()> callback) {
270 if (button_text.has_value() && ImGui::Button(button_text->data())) {
271 ImGuiFileDialog::Instance()->OpenDialog(display_key.data(), "Choose File",
272 file_extensions.data(), ".");
273 }
274
275 if (ImGuiFileDialog::Instance()->Display(
276 display_key.data(), ImGuiWindowFlags_NoCollapse, ImVec2(600, 400))) {
277 if (ImGuiFileDialog::Instance()->IsOk()) {
278 callback();
279 }
280 ImGuiFileDialog::Instance()->Close();
281 }
282}
283
284} // namespace gui
285} // namespace app
286} // namespace yaze
SNES 16-bit tile metadata container.
Definition snes_tile.h:50
Definition input.cc:18
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:29
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:175
void ItemLabel(absl::string_view title, ItemLabelFlags flags)
Definition input.cc:195
const int kStepFastHex
Definition input.cc:140
bool InputHex(const char *label, uint64_t *data)
Definition input.cc:142
bool InputTileInfo(const char *label, gfx::TileInfo *tile_info)
Definition input.cc:250
void FileDialogPipeline(absl::string_view display_key, absl::string_view file_extensions, std::optional< absl::string_view > button_text, std::function< void()> callback)
Definition input.cc:266
bool InputHexShort(const char *label, uint32_t *data)
Definition input.cc:155
ImGuiID GetID(const std::string &id)
Definition input.cc:264
const int kStepOneHex
Definition input.cc:139
enum ItemLabelFlag { Left=1u<< 0u, Right=1u<< 1u, Default=Left, } ItemLabelFlags
Definition input.h:51
bool ListBox(const char *label, int *current_item, const std::vector< std::string > &items, int height_in_items)
Definition input.cc:238
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
Definition input.cc:161
Definition common.cc:22