yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
editor_layout.cc
Go to the documentation of this file.
1#define IMGUI_DEFINE_MATH_OPERATORS
2
4
5#include "absl/strings/str_format.h"
11#include "imgui/imgui.h"
12#include "imgui/imgui_internal.h"
13
14namespace yaze {
15namespace gui {
16
17// ============================================================================
18// Toolset Implementation
19// ============================================================================
20
22 // Ultra-compact toolbar with minimal padding
23 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4, 2));
24 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(3, 3));
25 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(6, 4));
26
27 // Don't use BeginGroup - it causes stretching. Just use direct layout.
28 in_toolbar_ = true;
29 button_count_ = 0;
31
32}
33
35 // End the current line
36 ImGui::NewLine();
37
38 ImGui::PopStyleVar(3);
39 ImGui::Separator();
40 in_toolbar_ = false;
42}
43
45 // Compact inline mode buttons without child window to avoid scroll issues
46 // Just use a simple colored background rect
47 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.15f, 0.17f, 0.5f));
48
49 // Use a frameless child with exact button height to avoid scrolling
50 const float button_size = 28.0f; // Smaller buttons to match toolbar height
51 const float padding = 4.0f;
52 const int num_buttons = 2;
53 const float item_spacing = ImGui::GetStyle().ItemSpacing.x;
54
55 float total_width = (num_buttons * button_size) +
56 ((num_buttons - 1) * item_spacing) +
57 (padding * 2);
58
59 ImGui::BeginChild("##ModeGroup", ImVec2(total_width, button_size + padding),
60 ImGuiChildFlags_AlwaysUseWindowPadding,
61 ImGuiWindowFlags_NoScrollbar);
62
63 // Store for button sizing
64 mode_group_button_size_ = button_size;
65}
66
67bool Toolset::ModeButton(const char* icon, bool selected, const char* tooltip) {
68 if (selected) {
69 ImGui::PushStyleColor(ImGuiCol_Button, GetAccentColor());
70 }
71
72 // Use smaller buttons that fit the toolbar height
73 float size = mode_group_button_size_ > 0 ? mode_group_button_size_ : 28.0f;
74 bool clicked = ImGui::Button(icon, ImVec2(size, size));
75
76 if (selected) {
77 ImGui::PopStyleColor();
78 }
79
80 if (tooltip && ImGui::IsItemHovered()) {
81 ImGui::SetTooltip("%s", tooltip);
82 }
83
84 ImGui::SameLine();
86
87 return clicked;
88}
89
91 ImGui::EndChild();
92 ImGui::PopStyleColor();
93 ImGui::SameLine();
95}
96
98 // Use a proper separator that doesn't stretch
99 ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
100 ImGui::SameLine();
101}
102
103void Toolset::AddRomBadge(uint8_t version, std::function<void()> on_upgrade) {
104 RomVersionBadge(version == 0xFF ? "Vanilla" :
105 absl::StrFormat("v%d", version).c_str(),
106 version == 0xFF);
107
108 if (on_upgrade && (version == 0xFF || version < 3)) {
109 ImGui::SameLine(0, 2); // Tighter spacing
110 if (ImGui::SmallButton(ICON_MD_UPGRADE)) {
111 on_upgrade();
112 }
113 if (ImGui::IsItemHovered()) {
114 ImGui::SetTooltip("Upgrade to ZSCustomOverworld v3");
115 }
116 }
117
118 ImGui::SameLine();
119}
120
121bool Toolset::AddProperty(const char* icon, const char* label,
122 uint8_t* value,
123 std::function<void()> on_change) {
124 ImGui::Text("%s", icon);
125 ImGui::SameLine();
126 ImGui::SetNextItemWidth(55);
127
128 bool changed = InputHexByte(label, value);
129 if (changed && on_change) {
130 on_change();
131 }
132
133 ImGui::SameLine();
134 return changed;
135}
136
137bool Toolset::AddProperty(const char* icon, const char* label,
138 uint16_t* value,
139 std::function<void()> on_change) {
140 ImGui::Text("%s", icon);
141 ImGui::SameLine();
142 ImGui::SetNextItemWidth(70);
143
144 bool changed = InputHexWord(label, value);
145 if (changed && on_change) {
146 on_change();
147 }
148
149 ImGui::SameLine();
150 return changed;
151}
152
153bool Toolset::AddCombo(const char* icon, int* current,
154 const char* const items[], int count) {
155 ImGui::Text("%s", icon);
156 ImGui::SameLine(0, 2); // Reduce spacing between icon and combo
157 ImGui::SetNextItemWidth(100); // Slightly narrower for better fit
158
159 bool changed = ImGui::Combo("##combo", current, items, count);
160 ImGui::SameLine();
161
162 return changed;
163}
164
165bool Toolset::AddToggle(const char* icon, bool* state, const char* tooltip) {
166 bool result = ToggleIconButton(icon, icon, state, tooltip);
167 ImGui::SameLine();
168 return result;
169}
170
171bool Toolset::AddAction(const char* icon, const char* tooltip) {
172 bool clicked = ImGui::SmallButton(icon);
173
174 // Register for test automation
175 if (ImGui::GetItemID() != 0 && tooltip) {
176 std::string button_path = absl::StrFormat("ToolbarAction:%s", tooltip);
178 button_path, "button", ImGui::GetItemID(), tooltip);
179 }
180
181 if (tooltip && ImGui::IsItemHovered()) {
182 ImGui::SetTooltip("%s", tooltip);
183 }
184
185 ImGui::SameLine();
186 return clicked;
187}
188
189bool Toolset::BeginCollapsibleSection(const char* label, bool* p_open) {
190 ImGui::NewLine(); // Start on new line
191 bool is_open = ImGui::CollapsingHeader(label, ImGuiTreeNodeFlags_None);
192 if (p_open) *p_open = is_open;
193 in_section_ = is_open;
194 return is_open;
195}
196
200
201void Toolset::AddV3StatusBadge(uint8_t version, std::function<void()> on_settings) {
202 if (version >= 3 && version != 0xFF) {
203 StatusBadge("v3 Active", ButtonType::Success);
204 ImGui::SameLine();
205 if (ImGui::SmallButton(ICON_MD_TUNE " Settings") && on_settings) {
206 on_settings();
207 }
208 } else {
209 StatusBadge("v3 Available", ButtonType::Default);
210 ImGui::SameLine();
211 if (ImGui::SmallButton(ICON_MD_UPGRADE " Upgrade")) {
212 ImGui::OpenPopup("UpgradeROMVersion");
213 }
214 }
215 ImGui::SameLine();
216}
217
218bool Toolset::AddUsageStatsButton(const char* tooltip) {
219 bool clicked = ImGui::SmallButton(ICON_MD_ANALYTICS " Usage");
220 if (tooltip && ImGui::IsItemHovered()) {
221 ImGui::SetTooltip("%s", tooltip);
222 }
223 ImGui::SameLine();
224 return clicked;
225}
226
227// ============================================================================
228// EditorCard Implementation
229// ============================================================================
230
231EditorCard::EditorCard(const char* title, const char* icon)
232 : title_(title), icon_(icon ? icon : ""), default_size_(400, 300) {
233 window_name_ = icon_.empty() ? title_ : icon_ + " " + title_;
234}
235
236EditorCard::EditorCard(const char* title, const char* icon, bool* p_open)
237 : title_(title), icon_(icon ? icon : ""), default_size_(400, 300) {
238 p_open_ = p_open;
239 window_name_ = icon_.empty() ? title_ : icon_ + " " + title_;
240}
241
242void EditorCard::SetDefaultSize(float width, float height) {
243 default_size_ = ImVec2(width, height);
244}
245
247 position_ = pos;
248}
249
250bool EditorCard::Begin(bool* p_open) {
251 // Check visibility flag first - if provided and false, don't show the card
252 if (p_open && !*p_open) {
253 imgui_begun_ = false;
254 return false;
255 }
256
257 // Handle icon-collapsed state
260 imgui_begun_ = false;
261 return false;
262 }
263
264 ImGuiWindowFlags flags = ImGuiWindowFlags_None;
265
266 // Apply headless mode
267 if (headless_) {
268 flags |= ImGuiWindowFlags_NoTitleBar;
269 flags |= ImGuiWindowFlags_NoCollapse;
270 }
271
272 // Control docking
273 if (!docking_allowed_) {
274 flags |= ImGuiWindowFlags_NoDocking;
275 }
276
277 // Set initial position based on position enum
278 if (first_draw_) {
279 float display_width = ImGui::GetIO().DisplaySize.x;
280 float display_height = ImGui::GetIO().DisplaySize.y;
281
282 switch (position_) {
283 case Position::Right:
284 ImGui::SetNextWindowPos(ImVec2(display_width - default_size_.x - 10, 30),
285 ImGuiCond_FirstUseEver);
286 break;
287 case Position::Left:
288 ImGui::SetNextWindowPos(ImVec2(10, 30), ImGuiCond_FirstUseEver);
289 break;
290 case Position::Bottom:
291 ImGui::SetNextWindowPos(
292 ImVec2(10, display_height - default_size_.y - 10),
293 ImGuiCond_FirstUseEver);
294 break;
296 case Position::Free:
297 ImGui::SetNextWindowPos(
298 ImVec2(display_width * 0.5f - default_size_.x * 0.5f,
299 display_height * 0.3f),
300 ImGuiCond_FirstUseEver);
301 break;
302 }
303
304 ImGui::SetNextWindowSize(default_size_, ImGuiCond_FirstUseEver);
305 first_draw_ = false;
306 }
307
308 // Create window title with icon
309 std::string window_title = icon_.empty() ? title_ : icon_ + " " + title_;
310
311 // Modern card styling
312 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 8.0f);
313 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10, 10));
314 ImGui::PushStyleColor(ImGuiCol_TitleBg, GetThemeColor(ImGuiCol_TitleBg));
315 ImGui::PushStyleColor(ImGuiCol_TitleBgActive, GetAccentColor());
316
317 // Use p_open parameter if provided, otherwise use stored p_open_
318 bool* actual_p_open = p_open ? p_open : p_open_;
319
320 // If closable is false, don't pass p_open (removes X button)
321 bool visible = ImGui::Begin(window_title.c_str(),
322 closable_ ? actual_p_open : nullptr,
323 flags);
324
325 // Mark that ImGui::Begin() was called - End() must always be called now
326 imgui_begun_ = true;
327
328 // Register card window for test automation
329 if (ImGui::GetCurrentWindow() && ImGui::GetCurrentWindow()->ID != 0) {
330 std::string card_path = absl::StrFormat("EditorCard:%s", title_.c_str());
332 card_path, "window", ImGui::GetCurrentWindow()->ID,
333 absl::StrFormat("Editor card: %s", title_.c_str()));
334 }
335
336 return visible;
337}
338
340 // Only call ImGui::End() and pop styles if ImGui::Begin() was called
341 if (imgui_begun_) {
342 // Check if window was focused this frame
343 focused_ = ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows);
344
345 ImGui::End();
346 ImGui::PopStyleColor(2);
347 ImGui::PopStyleVar(2);
348 imgui_begun_ = false;
349 }
350}
351
353 // Set window focus using ImGui's focus system
354 ImGui::SetWindowFocus(window_name_.c_str());
355 focused_ = true;
356}
357
359 // Draw a small floating button with the icon
360 ImGui::SetNextWindowPos(saved_icon_pos_, ImGuiCond_Always);
361 ImGui::SetNextWindowSize(ImVec2(50, 50));
362
363 ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar |
364 ImGuiWindowFlags_NoResize |
365 ImGuiWindowFlags_NoScrollbar |
366 ImGuiWindowFlags_NoCollapse;
367
368 std::string icon_window_name = window_name_ + "##IconCollapsed";
369
370 if (ImGui::Begin(icon_window_name.c_str(), nullptr, flags)) {
371 // Draw icon button
372 if (ImGui::Button(icon_.c_str(), ImVec2(40, 40))) {
373 collapsed_to_icon_ = false; // Expand back to full window
374 }
375
376 if (ImGui::IsItemHovered()) {
377 ImGui::SetTooltip("Expand %s", title_.c_str());
378 }
379
380 // Allow dragging the icon
381 if (ImGui::IsWindowHovered() && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
382 ImVec2 mouse_delta = ImGui::GetIO().MouseDelta;
383 saved_icon_pos_.x += mouse_delta.x;
384 saved_icon_pos_.y += mouse_delta.y;
385 }
386 }
387 ImGui::End();
388}
389
390// ============================================================================
391// EditorLayout Implementation
392// ============================================================================
393
395 toolbar_.Begin();
396 in_layout_ = true;
397}
398
400 toolbar_.End();
401 in_layout_ = false;
402}
403
405 // Main canvas takes remaining space
406 ImGui::BeginChild("##MainCanvas", ImVec2(0, 0), false);
407}
408
410 ImGui::EndChild();
411}
412
414 cards_.push_back(card);
415}
416
417} // namespace gui
418} // namespace yaze
419
Draggable, dockable card for editor sub-windows.
bool Begin(bool *p_open=nullptr)
void SetDefaultSize(float width, float height)
void SetPosition(Position pos)
EditorCard(const char *title, const char *icon=nullptr)
std::vector< EditorCard * > cards_
void RegisterCard(EditorCard *card)
bool ModeButton(const char *icon, bool selected, const char *tooltip)
bool AddUsageStatsButton(const char *tooltip)
bool BeginCollapsibleSection(const char *label, bool *p_open)
bool AddProperty(const char *icon, const char *label, uint8_t *value, std::function< void()> on_change=nullptr)
bool AddAction(const char *icon, const char *tooltip)
bool AddCombo(const char *icon, int *current, const char *const items[], int count)
void AddRomBadge(uint8_t version, std::function< void()> on_upgrade=nullptr)
void AddV3StatusBadge(uint8_t version, std::function< void()> on_settings)
bool AddToggle(const char *icon, bool *state, const char *tooltip)
void RegisterWidget(const std::string &full_path, const std::string &type, ImGuiID imgui_id, const std::string &description="", const WidgetMetadata &metadata=WidgetMetadata())
static WidgetIdRegistry & Instance()
#define ICON_MD_UPGRADE
Definition icons.h:2045
#define ICON_MD_TUNE
Definition icons.h:2020
#define ICON_MD_ANALYTICS
Definition icons.h:152
bool ToggleIconButton(const char *icon_on, const char *icon_off, bool *state, const char *tooltip)
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
Definition input.cc:175
void RomVersionBadge(const char *version, bool is_vanilla)
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:189
Color GetThemeColor(const std::string &color_name)
void StatusBadge(const char *text, ButtonType type)
ImVec4 GetAccentColor()
Definition ui_helpers.cc:41
Main namespace for the application.
Definition controller.cc:20