yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
selection_properties_panel.cc
Go to the documentation of this file.
2#include "util/i18n/tr.h"
3
4#include <cstdio>
5
6#include "absl/strings/str_cat.h"
7#include "absl/strings/str_format.h"
15#include "imgui/imgui.h"
16#include "rom/rom.h"
17#include "zelda3/dungeon/room.h"
20
21namespace yaze {
22namespace editor {
23
25 switch (type) {
27 return "None";
29 return "Dungeon Room";
31 return "Dungeon Object";
33 return "Dungeon Sprite";
35 return "Dungeon Entrance";
37 return "Overworld Map";
39 return "Overworld Tile";
41 return "Overworld Sprite";
43 return "Overworld Entrance";
45 return "Overworld Exit";
47 return "Overworld Item";
49 return "Graphics Sheet";
51 return "Palette";
52 default:
53 return "Unknown";
54 }
55}
56
60
64
68 ImGui::Spacing();
69 }
70
71 switch (selection_.type) {
74 break;
77 break;
80 break;
83 break;
86 break;
89 break;
92 break;
95 break;
98 break;
101 break;
104 break;
107 break;
110 break;
111 }
112
113 // Advanced options toggle
114 ImGui::Spacing();
115 ImGui::Separator();
116 ImGui::Spacing();
117 ImGui::Checkbox(tr("Show Advanced"), &show_advanced_);
118 ImGui::SameLine();
119 ImGui::Checkbox(tr("Raw Data"), &show_raw_data_);
120}
121
125
127 const char* title) {
128 std::string full_title = absl::StrFormat("%s %s", icon, title);
129 if (!selection_.display_name.empty()) {
130 absl::StrAppend(&full_title, " - ", selection_.display_name);
131 }
132 if (selection_.read_only) {
133 absl::StrAppend(&full_title, " (Read Only)");
134 }
135
136 gui::SectionHeader(full_title.c_str());
137 ImGui::Spacing();
138}
139
142 ImGui::Separator();
143
144 ImGui::Text(tr("Type: %s"), GetSelectionTypeName(selection_.type));
145 if (!selection_.display_name.empty()) {
146 ImGui::Text(tr("Name: %s"), selection_.display_name.c_str());
147 }
148 if (selection_.id >= 0) {
149 ImGui::Text(tr("ID: %d (0x%X)"), selection_.id, selection_.id);
150 }
151 if (selection_.secondary_id >= 0) {
152 ImGui::Text(tr("Secondary: %d (0x%X)"), selection_.secondary_id,
154 }
155 if (selection_.read_only) {
156 ImGui::TextDisabled(tr("Read Only"));
157 }
158
159 ImGui::Spacing();
160 bool wrote_action = false;
161 if (selection_.id >= 0) {
162 if (gui::ThemedButton("Copy ID")) {
163 char buffer[32];
164 std::snprintf(buffer, sizeof(buffer), "%d", selection_.id);
165 ImGui::SetClipboardText(buffer);
166 }
167 wrote_action = true;
168 }
169 if (selection_.id >= 0) {
170 if (wrote_action)
171 ImGui::SameLine();
172 if (gui::ThemedButton("Copy Hex")) {
173 char buffer[32];
174 std::snprintf(buffer, sizeof(buffer), "0x%X", selection_.id);
175 ImGui::SetClipboardText(buffer);
176 }
177 wrote_action = true;
178 }
179 if (!selection_.display_name.empty()) {
180 if (wrote_action)
181 ImGui::SameLine();
182 if (gui::ThemedButton("Copy Name")) {
183 ImGui::SetClipboardText(selection_.display_name.c_str());
184 }
185 wrote_action = true;
186 }
187
189
190 if (show_raw_data_) {
191 ImGui::Spacing();
192 ImGui::TextDisabled(tr("Data Ptr: %p"), selection_.data);
193 }
194}
195
197 if (!send_to_agent_) {
198 return;
199 }
200
201 ImGui::Spacing();
202 ImGui::Separator();
203 ImGui::Spacing();
204
205 ImGui::TextDisabled(tr("%s Agent Actions"), ICON_MD_SMART_TOY);
206 bool no_selection = (selection_.type == SelectionType::kNone);
207 ImGui::BeginDisabled(no_selection);
208 if (gui::ThemedButton("Explain")) {
209 SendAgentPrompt("Explain this selection and how to edit it safely.");
210 }
211 ImGui::SameLine();
212 if (gui::ThemedButton("Suggest Fixes")) {
213 SendAgentPrompt("Suggest improvements or checks for this selection.");
214 }
215 ImGui::SameLine();
216 if (gui::ThemedButton("Audit")) {
217 SendAgentPrompt("Audit this selection for possible issues or conflicts.");
218 }
219 ImGui::EndDisabled();
220 if (no_selection &&
221 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
222 ImGui::SetTooltip(tr("Select an object first to use agent actions"));
223 }
224}
225
227 std::string context = absl::StrFormat("Selection Type: %s",
229 if (!selection_.display_name.empty()) {
230 context += absl::StrFormat("\nName: %s", selection_.display_name);
231 }
232 if (selection_.id >= 0) {
233 context += absl::StrFormat("\nID: 0x%X", selection_.id);
234 }
235 if (selection_.secondary_id >= 0) {
236 context += absl::StrFormat("\nSecondary ID: 0x%X", selection_.secondary_id);
237 }
238 if (selection_.read_only) {
239 context += "\nRead Only: true";
240 }
241 return context;
242}
243
245 const char* intent) const {
246 std::string prompt = intent ? intent : "Review this selection.";
247 prompt += "\n\n";
248 prompt += BuildSelectionContext();
249 prompt += "\n\nProvide actionable steps inside Yaze where possible.";
250 return prompt;
251}
252
254 if (!send_to_agent_) {
255 return;
256 }
258 if (focus_agent_panel_) {
260 }
261}
262
263bool SelectionPropertiesPanel::DrawPositionEditor(const char* label, int* x,
264 int* y, int min_val,
265 int max_val) {
266 bool changed = false;
267 ImGui::PushID(label);
268
269 ImGui::Text("%s", label);
270 ImGui::PushItemWidth(80);
271
272 ImGui::Text(tr("X:"));
273 ImGui::SameLine();
274 if (ImGui::InputInt("##X", x, 1, 8)) {
275 *x = std::clamp(*x, min_val, max_val);
276 changed = true;
277 }
278
279 ImGui::SameLine();
280 ImGui::Text(tr("Y:"));
281 ImGui::SameLine();
282 if (ImGui::InputInt("##Y", y, 1, 8)) {
283 *y = std::clamp(*y, min_val, max_val);
284 changed = true;
285 }
286
287 ImGui::PopItemWidth();
288 ImGui::PopID();
289
290 return changed;
291}
292
293bool SelectionPropertiesPanel::DrawSizeEditor(const char* label, int* width,
294 int* height) {
295 bool changed = false;
296 ImGui::PushID(label);
297
298 ImGui::Text("%s", label);
299 ImGui::PushItemWidth(80);
300
301 ImGui::Text(tr("W:"));
302 ImGui::SameLine();
303 if (ImGui::InputInt("##W", width, 1, 8)) {
304 *width = std::max(1, *width);
305 changed = true;
306 }
307
308 ImGui::SameLine();
309 ImGui::Text(tr("H:"));
310 ImGui::SameLine();
311 if (ImGui::InputInt("##H", height, 1, 8)) {
312 *height = std::max(1, *height);
313 changed = true;
314 }
315
316 ImGui::PopItemWidth();
317 ImGui::PopID();
318
319 return changed;
320}
321
323 uint8_t* value,
324 const char* tooltip) {
325 bool changed = false;
326 int val = *value;
327
328 ImGui::PushItemWidth(80);
329 if (ImGui::InputInt(label, &val, 1, 16)) {
330 *value = static_cast<uint8_t>(std::clamp(val, 0, 255));
331 changed = true;
332 }
333 ImGui::PopItemWidth();
334
335 if (tooltip && ImGui::IsItemHovered()) {
336 ImGui::SetTooltip("%s", tooltip);
337 }
338
339 return changed;
340}
341
343 uint16_t* value,
344 const char* tooltip) {
345 bool changed = false;
346 int val = *value;
347
348 ImGui::PushItemWidth(100);
349 if (ImGui::InputInt(label, &val, 1, 256)) {
350 *value = static_cast<uint16_t>(std::clamp(val, 0, 65535));
351 changed = true;
352 }
353 ImGui::PopItemWidth();
354
355 if (tooltip && ImGui::IsItemHovered()) {
356 ImGui::SetTooltip("%s", tooltip);
357 }
358
359 return changed;
360}
361
363 int* current_item,
364 const char* const items[],
365 int items_count) {
366 return ImGui::Combo(label, current_item, items, items_count);
367}
368
370 uint8_t* flags,
371 const char* const flag_names[],
372 int flag_count) {
373 bool changed = false;
374
375 if (ImGui::TreeNode(label)) {
376 for (int i = 0; i < flag_count && i < 8; ++i) {
377 bool bit_set = (*flags >> i) & 1;
378 if (ImGui::Checkbox(flag_names[i], &bit_set)) {
379 if (bit_set) {
380 *flags |= (1 << i);
381 } else {
382 *flags &= ~(1 << i);
383 }
384 changed = true;
385 }
386 }
387 ImGui::TreePop();
388 }
389
390 return changed;
391}
392
394 const char* value) {
395 ImGui::Text("%s:", label);
396 ImGui::SameLine();
397 ImGui::TextDisabled("%s", value);
398}
399
401 uint32_t value, int digits) {
402 ImGui::Text("%s:", label);
403 ImGui::SameLine();
404 char fmt[16];
405 snprintf(fmt, sizeof(fmt), "0x%%0%dX", digits);
406 ImGui::TextDisabled(fmt, value);
407}
408
414
415// ============================================================================
416// Type-specific property editors
417// ============================================================================
418
420 DrawPropertyHeader(ICON_MD_GRID_VIEW, "Dungeon Room");
421
422 if (!selection_.data) {
423 ImGui::TextDisabled(tr("No room data available."));
424 return;
425 }
426
427 auto* room = static_cast<zelda3::Room*>(selection_.data);
428
429 if (ImGui::CollapsingHeader(tr("Identity"), ImGuiTreeNodeFlags_DefaultOpen)) {
430 DrawReadOnlyHex("Room ID", selection_.id, 4);
432 }
433
434 if (ImGui::CollapsingHeader(tr("Graphics & Layout"),
435 ImGuiTreeNodeFlags_DefaultOpen)) {
436 uint8_t blockset = room->blockset();
437 if (DrawByteProperty("Blockset", &blockset,
438 "Tile graphics and layout definition")) {
439 room->SetBlockset(blockset);
440 NotifyChange();
441 }
442
443 uint8_t palette = room->palette();
444 if (DrawByteProperty("Palette", &palette, "Room color scheme")) {
445 room->SetPalette(palette);
446 NotifyChange();
447 }
448
449 uint8_t spriteset = room->spriteset();
450 if (DrawByteProperty("Spriteset", &spriteset,
451 "Enemy graphics and behavior")) {
452 room->SetSpriteset(spriteset);
453 NotifyChange();
454 }
455 }
456
457 if (show_advanced_ &&
458 ImGui::CollapsingHeader(tr("Advanced Settings"),
459 ImGuiTreeNodeFlags_DefaultOpen)) {
460
461 // Room Tags with manifest integration
462 auto draw_tag_combo = [&](const char* label, zelda3::TagKey current,
463 std::function<void(zelda3::TagKey)> setter) {
464 std::string current_label =
465 zelda3::GetRoomTagLabel(static_cast<int>(current));
466 if (ImGui::BeginCombo(label, current_label.c_str())) {
467 const auto& vanilla_tags = zelda3::Zelda3Labels::GetRoomTagNames();
468 for (int i = 0; i < static_cast<int>(vanilla_tags.size()); ++i) {
469 std::string item_label = zelda3::GetRoomTagLabel(i);
470 if (ImGui::Selectable(item_label.c_str(),
471 static_cast<int>(current) == i)) {
472 setter(static_cast<zelda3::TagKey>(i));
473 NotifyChange();
474 }
475 }
476 ImGui::EndCombo();
477 }
478 };
479
480 draw_tag_combo("Tag 1", room->tag1(),
481 [&](zelda3::TagKey val) { room->SetTag1(val); });
482 draw_tag_combo("Tag 2", room->tag2(),
483 [&](zelda3::TagKey val) { room->SetTag2(val); });
484
485 // Room Effect
486 int effect = static_cast<int>(room->effect());
487 const auto& effects = zelda3::Zelda3Labels::GetRoomEffectNames();
488 std::vector<const char*> effect_ptrs;
489 for (const auto& s : effects)
490 effect_ptrs.push_back(s.c_str());
491
492 if (ImGui::Combo(tr("Effect"), &effect, effect_ptrs.data(),
493 static_cast<int>(effect_ptrs.size()))) {
494 room->SetEffect(static_cast<zelda3::EffectKey>(effect));
495 NotifyChange();
496 }
497 }
498}
499
501 DrawPropertyHeader(ICON_MD_CATEGORY, "Dungeon Object");
502
503 if (ImGui::CollapsingHeader(tr("Transform"),
504 ImGuiTreeNodeFlags_DefaultOpen)) {
505 // Placeholder for actual object data
506 int x = 0, y = 0;
507 if (DrawPositionEditor("Position", &x, &y, 0, 63)) {
508 NotifyChange();
509 }
510
511 int w = 1, h = 1;
512 if (DrawSizeEditor("Size", &w, &h)) {
513 NotifyChange();
514 }
515 }
516
517 if (ImGui::CollapsingHeader(tr("Object Type"),
518 ImGuiTreeNodeFlags_DefaultOpen)) {
519 ImGui::TextDisabled(tr("Object ID: --"));
520 ImGui::TextDisabled(tr("Subtype: --"));
521 ImGui::TextDisabled(tr("Layer: --"));
522 }
523
524 if (show_raw_data_ &&
525 ImGui::CollapsingHeader(tr("Raw Data"), ImGuiTreeNodeFlags_None)) {
526 ImGui::TextDisabled(tr("Byte 1: 0x00"));
527 ImGui::TextDisabled(tr("Byte 2: 0x00"));
528 ImGui::TextDisabled(tr("Byte 3: 0x00"));
529 }
530}
531
533 DrawPropertyHeader(ICON_MD_PEST_CONTROL, "Dungeon Sprite");
534
535 if (ImGui::CollapsingHeader(tr("Position"), ImGuiTreeNodeFlags_DefaultOpen)) {
536 int x = 0, y = 0;
537 if (DrawPositionEditor("Position", &x, &y, 0, 255)) {
538 NotifyChange();
539 }
540 }
541
542 if (ImGui::CollapsingHeader(tr("Sprite Data"),
543 ImGuiTreeNodeFlags_DefaultOpen)) {
544 ImGui::TextDisabled(tr("Sprite ID: --"));
545 ImGui::TextDisabled(tr("Subtype: --"));
546 ImGui::TextDisabled(tr("Overlord: No"));
547 }
548}
549
551 DrawPropertyHeader(ICON_MD_DOOR_FRONT, "Dungeon Entrance");
552
553 if (ImGui::CollapsingHeader(tr("Target"), ImGuiTreeNodeFlags_DefaultOpen)) {
554 ImGui::TextDisabled(tr("Target Room: --"));
555 ImGui::TextDisabled(tr("Entry Position: --"));
556 }
557
558 if (ImGui::CollapsingHeader(tr("Properties"),
559 ImGuiTreeNodeFlags_DefaultOpen)) {
560 ImGui::TextDisabled(tr("Door Type: --"));
561 ImGui::TextDisabled(tr("Direction: --"));
562 }
563}
564
566 DrawPropertyHeader(ICON_MD_MAP, "Overworld Map");
567
568 if (ImGui::CollapsingHeader(tr("Identity"), ImGuiTreeNodeFlags_DefaultOpen)) {
569 DrawReadOnlyHex("Map ID", selection_.id, 2);
571 }
572
573 if (ImGui::CollapsingHeader(tr("Graphics"), ImGuiTreeNodeFlags_DefaultOpen)) {
574 ImGui::TextDisabled(tr("GFX Set: --"));
575 ImGui::TextDisabled(tr("Palette: --"));
576 ImGui::TextDisabled(tr("Sprite GFX: --"));
577 ImGui::TextDisabled(tr("Sprite Palette: --"));
578 }
579
580 if (ImGui::CollapsingHeader(tr("Properties"),
581 ImGuiTreeNodeFlags_DefaultOpen)) {
582 ImGui::TextDisabled(tr("Large Map: No"));
583 ImGui::TextDisabled(tr("Area Size: 1x1"));
584 ImGui::TextDisabled(tr("Parent ID: --"));
585 }
586}
587
589 DrawPropertyHeader(ICON_MD_GRID_ON, "Overworld Tile");
590
591 if (ImGui::CollapsingHeader(tr("Tile Info"),
592 ImGuiTreeNodeFlags_DefaultOpen)) {
593 DrawReadOnlyHex("Tile16 ID", selection_.id, 4);
594 ImGui::TextDisabled(tr("Position: --"));
595 }
596
597 if (show_advanced_ &&
598 ImGui::CollapsingHeader(tr("Tile16 Data"), ImGuiTreeNodeFlags_None)) {
599 ImGui::TextDisabled(tr("TL: 0x0000"));
600 ImGui::TextDisabled(tr("TR: 0x0000"));
601 ImGui::TextDisabled(tr("BL: 0x0000"));
602 ImGui::TextDisabled(tr("BR: 0x0000"));
603 }
604}
605
607 DrawPropertyHeader(ICON_MD_PEST_CONTROL, "Overworld Sprite");
608
609 if (ImGui::CollapsingHeader(tr("Position"), ImGuiTreeNodeFlags_DefaultOpen)) {
610 int x = 0, y = 0;
611 if (DrawPositionEditor("Position", &x, &y, 0, 8191)) {
612 NotifyChange();
613 }
614 }
615
616 if (ImGui::CollapsingHeader(tr("Sprite"), ImGuiTreeNodeFlags_DefaultOpen)) {
617 ImGui::TextDisabled(tr("Sprite ID: --"));
618 ImGui::TextDisabled(tr("Map ID: --"));
619 }
620}
621
623 DrawPropertyHeader(ICON_MD_DOOR_FRONT, "Overworld Entrance");
624
625 if (ImGui::CollapsingHeader(tr("Position"), ImGuiTreeNodeFlags_DefaultOpen)) {
626 int x = 0, y = 0;
627 if (DrawPositionEditor("Position", &x, &y)) {
628 NotifyChange();
629 }
630 }
631
632 if (ImGui::CollapsingHeader(tr("Target"), ImGuiTreeNodeFlags_DefaultOpen)) {
633 ImGui::TextDisabled(tr("Entrance ID: --"));
634 ImGui::TextDisabled(tr("Target Room: --"));
635 }
636}
637
639 DrawPropertyHeader(ICON_MD_EXIT_TO_APP, "Overworld Exit");
640
641 if (ImGui::CollapsingHeader(tr("Exit Point"),
642 ImGuiTreeNodeFlags_DefaultOpen)) {
643 ImGui::TextDisabled(tr("Exit ID: --"));
644 int x = 0, y = 0;
645 if (DrawPositionEditor("Position", &x, &y)) {
646 NotifyChange();
647 }
648 }
649
650 if (ImGui::CollapsingHeader(tr("Target Map"),
651 ImGuiTreeNodeFlags_DefaultOpen)) {
652 ImGui::TextDisabled(tr("Room ID: --"));
653 ImGui::TextDisabled(tr("Target Map: --"));
654 }
655}
656
658 DrawPropertyHeader(ICON_MD_STAR, "Overworld Item");
659
660 if (ImGui::CollapsingHeader(tr("Position"), ImGuiTreeNodeFlags_DefaultOpen)) {
661 int x = 0, y = 0;
662 if (DrawPositionEditor("Position", &x, &y)) {
663 NotifyChange();
664 }
665 }
666
667 if (ImGui::CollapsingHeader(tr("Item Data"),
668 ImGuiTreeNodeFlags_DefaultOpen)) {
669 ImGui::TextDisabled(tr("Item ID: --"));
670 ImGui::TextDisabled(tr("Map ID: --"));
671 }
672}
673
675 DrawPropertyHeader(ICON_MD_IMAGE, "Graphics Sheet");
676
677 if (ImGui::CollapsingHeader(tr("Sheet Info"),
678 ImGuiTreeNodeFlags_DefaultOpen)) {
679 DrawReadOnlyHex("Sheet ID", selection_.id, 2);
680 ImGui::TextDisabled(tr("Size: 128x32"));
681 ImGui::TextDisabled(tr("BPP: 4"));
682 }
683
684 if (show_advanced_ &&
685 ImGui::CollapsingHeader(tr("ROM Location"), ImGuiTreeNodeFlags_None)) {
686 ImGui::TextDisabled(tr("Address: --"));
687 ImGui::TextDisabled(tr("Compressed: Yes"));
688 ImGui::TextDisabled(tr("Original Size: --"));
689 }
690}
691
694
695 if (ImGui::CollapsingHeader(tr("Palette Info"),
696 ImGuiTreeNodeFlags_DefaultOpen)) {
697 DrawReadOnlyHex("Palette ID", selection_.id, 2);
698 ImGui::TextDisabled(tr("Colors: 16"));
699 }
700
701 if (ImGui::CollapsingHeader(tr("Colors"), ImGuiTreeNodeFlags_DefaultOpen)) {
702 // Would show color swatches in actual implementation
703 ImGui::TextDisabled(tr("Color editing not yet implemented"));
704 }
705}
706
707} // namespace editor
708} // namespace yaze
bool DrawByteProperty(const char *label, uint8_t *value, const char *tooltip=nullptr)
bool DrawFlagsProperty(const char *label, uint8_t *flags, const char *const flag_names[], int flag_count)
std::string BuildAgentPrompt(const char *intent) const
bool DrawPositionEditor(const char *label, int *x, int *y, int min_val=0, int max_val=512)
std::function< void(const std::string &) send_to_agent_)
void DrawReadOnlyText(const char *label, const char *value)
bool DrawSizeEditor(const char *label, int *width, int *height)
bool DrawWordProperty(const char *label, uint16_t *value, const char *tooltip=nullptr)
bool DrawComboProperty(const char *label, int *current_item, const char *const items[], int items_count)
void ClearSelection()
Clear the current selection.
void Draw()
Draw the properties panel content.
void DrawReadOnlyHex(const char *label, uint32_t value, int digits=4)
void DrawPropertyHeader(const char *icon, const char *title)
void SetSelection(const SelectionContext &context)
Set the current selection to display/edit.
#define ICON_MD_GRID_VIEW
Definition icons.h:897
#define ICON_MD_EXIT_TO_APP
Definition icons.h:699
#define ICON_MD_INFO
Definition icons.h:993
#define ICON_MD_STAR
Definition icons.h:1848
#define ICON_MD_MAP
Definition icons.h:1173
#define ICON_MD_GRID_ON
Definition icons.h:896
#define ICON_MD_DOOR_FRONT
Definition icons.h:613
#define ICON_MD_IMAGE
Definition icons.h:982
#define ICON_MD_PEST_CONTROL
Definition icons.h:1429
#define ICON_MD_PALETTE
Definition icons.h:1370
#define ICON_MD_CATEGORY
Definition icons.h:382
#define ICON_MD_SMART_TOY
Definition icons.h:1781
const char * GetSelectionTypeName(SelectionType type)
Get a human-readable name for a selection type.
SelectionType
Types of entities that can be selected and edited.
bool ThemedButton(const char *label, const ImVec2 &size, const char *panel_id, const char *anim_id)
Draw a standard text button with theme colors.
ImVec4 GetPrimaryVec4()
void SectionHeader(const char *icon, const char *label, const ImVec4 &color)
void ColoredTextF(const ImVec4 &color, const char *fmt,...)
bool DrawEmptyState(const EmptyStateOptions &options)
Draw a centered empty-state block.
EmptyStateOptions EmptyNoSelection(bool compact)
std::string GetRoomTagLabel(int id)
Convenience function to get a room tag label.
Holds information about the current selection.
static const std::vector< std::string > & GetRoomEffectNames()
static const std::vector< std::string > & GetRoomTagNames()