yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
room_matrix_content.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_DUNGEON_WORKSPACE_ROOM_MATRIX_CONTENT_H_
2#define YAZE_APP_EDITOR_DUNGEON_WORKSPACE_ROOM_MATRIX_CONTENT_H_
3
4#include <array>
5#include <cctype>
6#include <cmath>
7#include <cstdint>
8#include <functional>
9#include <optional>
10#include <string>
11#include <unordered_map>
12#include <vector>
13#include "util/i18n/tr.h"
14
22#include "app/gui/core/icons.h"
23#include "imgui/imgui.h"
24#include "zelda3/dungeon/room.h"
27
28namespace yaze {
29namespace editor {
30
46 public:
54 RoomMatrixContent(int* current_room_id, ImVector<int>* active_rooms,
55 std::function<void(int)> on_room_selected,
56 std::function<void(int, int)> on_room_swap = nullptr,
57 DungeonRoomStore* rooms = nullptr)
58 : current_room_id_(current_room_id),
59 active_rooms_(active_rooms),
60 rooms_(rooms),
61 on_room_selected_(std::move(on_room_selected)),
62 on_room_swap_(std::move(on_room_swap)) {}
63
64 // ==========================================================================
65 // WindowContent Identity
66 // ==========================================================================
67
68 std::string GetId() const override { return "dungeon.room_matrix"; }
69 std::string GetDisplayName() const override { return "Room Matrix"; }
70 std::string GetIcon() const override { return ICON_MD_GRID_VIEW; }
71 std::string GetEditorCategory() const override { return "Dungeon"; }
72 int GetPriority() const override { return 30; }
73 std::string GetWorkflowGroup() const override { return "Core"; }
74 float GetPreferredWidth() const override { return 440.0f; }
75
77 std::function<void(int, RoomSelectionIntent)> callback) {
78 on_room_intent_ = std::move(callback);
79 }
80
81 // ==========================================================================
82 // WindowContent Drawing
83 // ==========================================================================
84
85 void Draw(bool* p_open) override {
87 return;
88
89 const auto& theme = AgentUI::GetTheme();
90
91 // 16 wide x 19 tall = 304 cells (296 rooms + 8 empty)
92 constexpr int kRoomsPerRow = 16;
93 constexpr int kRoomsPerCol = 19;
94 constexpr int kTotalRooms = 0x128; // 296 rooms (0x00-0x127)
95 constexpr float kCellSpacing = 1.0f;
96
97 DrawMatrixSummary(theme, kTotalRooms);
98
99 ImGui::PushID("RoomMatrixFilter");
100 ImGui::SetNextItemWidth(
101 std::max(140.0f, ImGui::GetContentRegionAvail().x - 82.0f));
102 ImGui::InputTextWithHint("##Search", ICON_MD_SEARCH " Filter room id/name",
103 search_filter_, IM_ARRAYSIZE(search_filter_));
104 ImGui::SameLine();
105 const bool has_filter = search_filter_[0] != '\0';
106 if (!has_filter) {
107 ImGui::BeginDisabled();
108 }
109 if (ImGui::Button(ICON_MD_CLOSE "##ClearFilter")) {
110 search_filter_[0] = '\0';
111 }
112 if (!has_filter) {
113 ImGui::EndDisabled();
114 }
115 if (ImGui::IsItemHovered()) {
116 ImGui::SetTooltip(tr("Clear filter"));
117 }
118 ImGui::PopID();
119
120 DrawMatrixLegend(theme);
121 ImGui::Spacing();
122
123 const ImVec2 avail = ImGui::GetContentRegionAvail();
124 const float panel_width = std::max(1.0f, avail.x);
125 const float panel_height = std::max(1.0f, avail.y);
126 const float cell_size_by_width =
127 (panel_width - kCellSpacing * (kRoomsPerRow - 1)) / kRoomsPerRow;
128 const float cell_size_by_height =
129 (panel_height - kCellSpacing * (kRoomsPerCol - 1)) / kRoomsPerCol;
130 const float cell_size = std::clamp(
131 std::min(cell_size_by_width, cell_size_by_height), 12.0f, 24.0f);
132
133 const float grid_width =
134 kRoomsPerRow * cell_size + kCellSpacing * (kRoomsPerRow - 1);
135 const float grid_height =
136 kRoomsPerCol * cell_size + kCellSpacing * (kRoomsPerCol - 1);
137 const float x_offset = std::max(0.0f, (panel_width - grid_width) * 0.5f);
138 const float y_offset = std::max(0.0f, (panel_height - grid_height) * 0.5f);
139
140 ImDrawList* draw_list = ImGui::GetWindowDrawList();
141 ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
142 canvas_pos.x += x_offset;
143 canvas_pos.y += y_offset;
144
145 int room_index = 0;
146 for (int row = 0; row < kRoomsPerCol; row++) {
147 for (int col = 0; col < kRoomsPerRow; col++) {
148 int room_id = room_index;
149 bool is_valid_room = (room_id < kTotalRooms);
150 const bool matches_filter = MatchesSearchFilter(room_id);
151
152 ImVec2 cell_min =
153 ImVec2(canvas_pos.x + col * (cell_size + kCellSpacing),
154 canvas_pos.y + row * (cell_size + kCellSpacing));
155 ImVec2 cell_max =
156 ImVec2(cell_min.x + cell_size, cell_min.y + cell_size);
157
158 if (is_valid_room) {
159 // Get color based on room palette if available, else use algorithmic
160 ImU32 bg_color = GetRoomColor(room_id, theme);
161 if (!matches_filter) {
162 bg_color = BlendRoomColor(
163 bg_color, ImGui::ColorConvertFloat4ToU32(theme.panel_bg_darker),
164 0.72f);
165 }
166
167 bool is_current = (*current_room_id_ == room_id);
168 bool is_open = false;
169 for (int i = 0; i < active_rooms_->Size; i++) {
170 if ((*active_rooms_)[i] == room_id) {
171 is_open = true;
172 break;
173 }
174 }
175
176 // Draw cell background
177 draw_list->AddRectFilled(cell_min, cell_max, bg_color);
178
179 // Draw outline based on state using theme colors
180 if (is_current) {
181 // Add glow effect for current room (outer glow layers)
182 ImVec4 glow_color = theme.dungeon_selection_primary;
183 glow_color.w = matches_filter ? 0.3f : 0.18f;
184 ImVec2 glow_min(cell_min.x - 2, cell_min.y - 2);
185 ImVec2 glow_max(cell_max.x + 2, cell_max.y + 2);
186 draw_list->AddRect(glow_min, glow_max,
187 ImGui::ColorConvertFloat4ToU32(glow_color), 0.0f,
188 0, 3.0f);
189
190 // Inner bright border
191 ImU32 sel_color =
192 ImGui::ColorConvertFloat4ToU32(theme.dungeon_selection_primary);
193 draw_list->AddRect(cell_min, cell_max, sel_color, 0.0f, 0, 2.5f);
194 } else if (is_open) {
195 ImU32 open_color = ImGui::ColorConvertFloat4ToU32(
196 theme.dungeon_grid_cell_selected);
197 draw_list->AddRect(cell_min, cell_max, open_color, 0.0f, 0, 2.0f);
198 } else {
199 ImU32 border_color =
200 ImGui::ColorConvertFloat4ToU32(theme.dungeon_grid_cell_border);
201 if (!matches_filter) {
202 border_color = BlendRoomColor(
203 border_color,
204 ImGui::ColorConvertFloat4ToU32(theme.panel_bg_darker), 0.5f);
205 }
206 draw_list->AddRect(cell_min, cell_max, border_color, 0.0f, 0, 1.0f);
207 }
208
209 // Draw room ID (only if cell is large enough)
210 if (cell_size >= 18.0f) {
211 char label[8];
212 snprintf(label, sizeof(label), "%02X", room_id);
213 ImVec2 text_size = ImGui::CalcTextSize(label);
214 ImVec2 text_pos =
215 ImVec2(cell_min.x + (cell_size - text_size.x) * 0.5f,
216 cell_min.y + (cell_size - text_size.y) * 0.5f);
217 ImVec4 text_color_vec = theme.dungeon_grid_text;
218 if (!matches_filter) {
219 text_color_vec.w *= 0.55f;
220 }
221 ImU32 text_color = ImGui::ColorConvertFloat4ToU32(text_color_vec);
222 draw_list->AddText(text_pos, text_color, label);
223 }
224
225 // Handle clicks
226 ImGui::SetCursorScreenPos(cell_min);
227 char btn_id[32];
228 snprintf(btn_id, sizeof(btn_id), "##room%d", room_id);
229 ImGui::InvisibleButton(btn_id, ImVec2(cell_size, cell_size));
230
231 if (ImGui::IsItemClicked()) {
232 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
233 // Double-click: open as standalone panel
234 if (on_room_intent_) {
236 } else if (on_room_selected_) {
237 on_room_selected_(room_id);
238 }
239 } else if (on_room_selected_) {
240 on_room_selected_(room_id);
241 }
242 }
243
244 if (ImGui::BeginPopupContextItem()) {
245 const bool can_swap =
247 *current_room_id_ < kTotalRooms && *current_room_id_ != room_id;
248
249 std::string open_label =
250 is_open ? "Focus Room" : "Open in Workbench";
251 if (ImGui::MenuItem(open_label.c_str())) {
252 if (on_room_intent_) {
253 on_room_intent_(room_id,
255 } else if (on_room_selected_) {
256 on_room_selected_(room_id);
257 }
258 }
259
260 if (ImGui::MenuItem(tr("Open as Panel"))) {
261 if (on_room_intent_) {
263 } else if (on_room_selected_) {
264 on_room_selected_(room_id);
265 }
266 }
267
268 if (ImGui::MenuItem(tr("Swap With Current Room"), nullptr, false,
269 can_swap)) {
271 }
272
273 ImGui::Separator();
274
275 char id_buf[16];
276 snprintf(id_buf, sizeof(id_buf), "0x%02X", room_id);
277 if (ImGui::MenuItem(tr("Copy Room ID"))) {
278 ImGui::SetClipboardText(id_buf);
279 }
280
281 const std::string& room_label = zelda3::GetRoomLabel(room_id);
282 if (ImGui::MenuItem(tr("Copy Room Name"))) {
283 ImGui::SetClipboardText(room_label.c_str());
284 }
285
286 ImGui::EndPopup();
287 }
288
289 // Tooltip with room info and thumbnail preview
290 if (ImGui::IsItemHovered()) {
291 ImGui::BeginTooltip();
292 // Use unified ResourceLabelProvider for room names
293 ImGui::Text("%s", zelda3::GetRoomLabel(room_id).c_str());
294 ImGui::TextDisabled(tr("Room 0x%02X"), room_id);
295
296 if (is_current) {
297 ImGui::TextColored(theme.dungeon_selection_primary,
298 ICON_MD_MY_LOCATION " Current workbench room");
299 } else if (is_open) {
300 ImGui::TextColored(theme.status_success,
301 ICON_MD_TAB " Open in editor");
302 }
303
304 if (rooms_) {
305 auto* loaded_room = rooms_->GetIfLoaded(room_id);
306 if (loaded_room != nullptr) {
307 // Show palette info
308 ImGui::TextDisabled(tr("Palette: %d | Blockset: %d"),
309 loaded_room->palette(),
310 loaded_room->blockset());
311
312 // Show thumbnail preview of the room
313 auto& preview_bitmap = PrepareCanonicalRoomComposite(
314 *loaded_room, tooltip_composite_output_);
317 if (preview_bitmap.is_active() &&
318 preview_bitmap.texture() != 0) {
319 ImGui::Separator();
320 // Render at thumbnail size (80x80 from 512x512)
321 constexpr float kThumbnailSize = 80.0f;
322 ImGui::Image((ImTextureID)(intptr_t)preview_bitmap.texture(),
323 ImVec2(kThumbnailSize, kThumbnailSize));
324 }
325 }
326 }
327
328 ImGui::Separator();
329 ImGui::TextDisabled(tr("Click to %s"), is_open ? "focus" : "open");
330 ImGui::TextDisabled(tr("Double-click to open as panel"));
331 ImGui::TextDisabled(tr("Right-click for actions"));
332 ImGui::EndTooltip();
333 }
334 } else {
335 // Empty cell
336 draw_list->AddRectFilled(
337 cell_min, cell_max,
338 ImGui::ColorConvertFloat4ToU32(theme.panel_bg_darker));
339 }
340
341 room_index++;
342 }
343 }
344
345 // Advance cursor past the grid
346 ImGui::Dummy(
347 ImVec2(panel_width, std::max(grid_height + y_offset, panel_height)));
348 }
349
351 if (rooms_ != rooms) {
352 rooms_ = rooms;
353 room_color_cache_.clear();
356 }
357 }
358
359 private:
360 void DrawMatrixSummary(const AgentUITheme& theme, int total_rooms) const {
361 const int open_rooms = active_rooms_ ? active_rooms_->Size : 0;
362 const int current_room = current_room_id_ ? *current_room_id_ : -1;
363 const std::string current_label = current_room >= 0
364 ? zelda3::GetRoomLabel(current_room)
365 : "No room selected";
366
367 ImGui::SeparatorText(tr("Navigator"));
368 ImGui::Text("%s", current_label.c_str());
369 ImGui::TextDisabled(tr("Current: 0x%02X"), std::max(0, current_room));
370 ImGui::SameLine();
371 ImGui::TextDisabled(tr("%d open"), open_rooms);
372
373 if (current_room >= 0) {
374 if (auto room_meta = GetRoomMetadata(current_room);
375 room_meta.has_value()) {
376 ImGui::SameLine();
377 ImGui::TextColored(theme.status_success, tr("Pal %d / Blockset %d"),
378 room_meta->first, room_meta->second);
379 }
380 }
381 }
382
383 void DrawMatrixLegend(const AgentUITheme& theme) const {
384 ImGui::SeparatorText(tr("Legend"));
386 ICON_MD_MY_LOCATION " Current");
387 ImGui::SameLine();
389 ImGui::SameLine();
391 ICON_MD_GRID_VIEW " Other");
392 }
393
394 void DrawLegendSwatch(const ImVec4& color, const char* label) const {
395 ImDrawList* draw_list = ImGui::GetWindowDrawList();
396 const ImVec2 pos = ImGui::GetCursorScreenPos();
397 const float size = ImGui::GetFrameHeight() - 6.0f;
398 const ImVec2 min = ImVec2(pos.x, pos.y + 3.0f);
399 const ImVec2 max = ImVec2(pos.x + size, pos.y + size + 3.0f);
400 draw_list->AddRectFilled(min, max, ImGui::ColorConvertFloat4ToU32(color),
401 3.0f);
402 draw_list->AddRect(min, max, ImGui::GetColorU32(ImGuiCol_Border), 3.0f);
403 ImGui::Dummy(ImVec2(size + 6.0f, size + 6.0f));
404 ImGui::SameLine(0.0f, 6.0f);
405 ImGui::TextUnformatted(label);
406 }
407
408 bool MatchesSearchFilter(int room_id) const {
409 if (search_filter_[0] == '\0') {
410 return true;
411 }
412
413 const std::string filter = NormalizeForSearch(search_filter_);
414 char id_buf[16];
415 snprintf(id_buf, sizeof(id_buf), "%02X", room_id);
416 char hex_buf[16];
417 snprintf(hex_buf, sizeof(hex_buf), "0x%02X", room_id);
418
419 const std::string room_label =
421 const std::string id_text = NormalizeForSearch(id_buf);
422 const std::string hex_text = NormalizeForSearch(hex_buf);
423 return room_label.find(filter) != std::string::npos ||
424 id_text.find(filter) != std::string::npos ||
425 hex_text.find(filter) != std::string::npos;
426 }
427
428 static std::string NormalizeForSearch(const std::string& value) {
429 std::string lowered;
430 lowered.reserve(value.size());
431 for (unsigned char ch : value) {
432 lowered.push_back(static_cast<char>(std::tolower(ch)));
433 }
434 return lowered;
435 }
436
437 static ImU32 BlendRoomColor(ImU32 source, ImU32 target, float blend) {
438 ImVec4 src = ImGui::ColorConvertU32ToFloat4(source);
439 ImVec4 dst = ImGui::ColorConvertU32ToFloat4(target);
440 src.x = (src.x * (1.0f - blend)) + (dst.x * blend);
441 src.y = (src.y * (1.0f - blend)) + (dst.y * blend);
442 src.z = (src.z * (1.0f - blend)) + (dst.z * blend);
443 src.w = 1.0f;
444 return ImGui::ColorConvertFloat4ToU32(src);
445 }
446
447 std::optional<std::pair<int, int>> GetRoomMetadata(int room_id) const {
448 if (!rooms_ || room_id < 0 ||
449 room_id >= static_cast<int>(DungeonRoomStore::kRoomCount)) {
450 return std::nullopt;
451 }
452
453 if (const auto* room = rooms_->GetIfMaterialized(room_id);
454 room != nullptr) {
455 return std::make_pair(static_cast<int>(room->palette()),
456 static_cast<int>(room->blockset()));
457 }
458
459 Rom* rom = rooms_->rom();
460 if (rom == nullptr || !rom->is_loaded()) {
461 return std::nullopt;
462 }
463
464 zelda3::Room header_room = zelda3::LoadRoomHeaderFromRom(rom, room_id);
465 return std::make_pair(static_cast<int>(header_room.palette()),
466 static_cast<int>(header_room.blockset()));
467 }
468
472 ImU32 GetRoomColor(int room_id, const AgentUITheme& theme) {
473 auto sample_dominant_color =
474 [](const gfx::Bitmap& bitmap) -> std::optional<ImU32> {
475 if (!bitmap.is_active() || bitmap.width() <= 0 || bitmap.height() <= 0 ||
476 bitmap.data() == nullptr || bitmap.surface() == nullptr ||
477 bitmap.surface()->format == nullptr ||
478 bitmap.surface()->format->palette == nullptr) {
479 return std::nullopt;
480 }
481
482 constexpr int kSampleStep = 16;
483 std::array<uint32_t, 256> histogram{};
484 SDL_Palette* palette = bitmap.surface()->format->palette;
485 const uint8_t* pixels = bitmap.data();
486 const int width = bitmap.width();
487 const int height = bitmap.height();
488
489 for (int y = 0; y < height; y += kSampleStep) {
490 for (int x = 0; x < width; x += kSampleStep) {
491 const uint8_t idx = pixels[(y * width) + x];
492 if (idx == 255) {
493 continue;
494 }
495 histogram[idx]++;
496 }
497 }
498
499 uint32_t best_count = 0;
500 uint8_t best_index = 0;
501 for (int i = 0; i < palette->ncolors && i < 256; ++i) {
502 if (histogram[static_cast<size_t>(i)] > best_count) {
503 best_count = histogram[static_cast<size_t>(i)];
504 best_index = static_cast<uint8_t>(i);
505 }
506 }
507
508 if (best_count == 0) {
509 return std::nullopt;
510 }
511 const SDL_Color& c = palette->colors[best_index];
512 return IM_COL32(c.r, c.g, c.b, 255);
513 };
514
515 auto soften_color = [&](ImU32 color, float blend = 0.32f) -> ImU32 {
516 ImVec4 src = ImGui::ColorConvertU32ToFloat4(color);
517 const ImVec4 bg = theme.panel_bg_darker;
518 src.x = (src.x * (1.0f - blend)) + (bg.x * blend);
519 src.y = (src.y * (1.0f - blend)) + (bg.y * blend);
520 src.z = (src.z * (1.0f - blend)) + (bg.z * blend);
521 src.w = 1.0f;
522 return ImGui::ColorConvertFloat4ToU32(src);
523 };
524
525 auto palette_fallback_color = [&](int palette_id,
526 int blockset_id) -> ImU32 {
527 const ImVec4 bg = theme.panel_bg_darker;
528 const float palette_mix =
529 0.26f + (static_cast<float>(palette_id & 0x07) * 0.055f);
530 const float blockset_mix =
531 0.08f + (static_cast<float>(blockset_id & 0x0F) * 0.018f);
532 ImVec4 tint = theme.dungeon_selection_primary;
533 tint.x = std::clamp(tint.x + blockset_mix, 0.0f, 1.0f);
534 tint.y = std::clamp(tint.y + (palette_mix * 0.35f), 0.0f, 1.0f);
535 tint.z = std::clamp(tint.z - (blockset_mix * 0.2f), 0.0f, 1.0f);
536
537 ImVec4 mixed;
538 mixed.x = std::clamp(
539 (bg.x * (1.0f - palette_mix)) + (tint.x * palette_mix), 0.0f, 1.0f);
540 mixed.y = std::clamp(
541 (bg.y * (1.0f - palette_mix)) + (tint.y * palette_mix), 0.0f, 1.0f);
542 mixed.z = std::clamp(
543 (bg.z * (1.0f - palette_mix)) + (tint.z * palette_mix), 0.0f, 1.0f);
544 mixed.w = 1.0f;
545 return ImGui::ColorConvertFloat4ToU32(mixed);
546 };
547
548 // If room data is available and loaded, sample the actual room bitmap and
549 // choose its most frequent indexed color.
550 if (rooms_) {
551 auto* room = rooms_->GetIfLoaded(room_id);
552 if (room != nullptr) {
553 room->PrepareForRender();
554 const uint64_t source_revision = room->composite_source_revision();
555 auto cache_it = room_color_cache_.find(room_id);
556 if (cache_it == room_color_cache_.end() ||
557 cache_it->second.source_revision != source_revision) {
558 auto& composite = PrepareCanonicalRoomComposite(
560 cache_it = room_color_cache_
561 .insert_or_assign(
562 room_id,
563 CachedRoomColor{room->composite_source_revision(),
564 sample_dominant_color(composite)})
565 .first;
566 }
567 if (cache_it->second.color.has_value()) {
568 return soften_color(cache_it->second.color.value());
569 }
570
571 if (auto bg1_color = sample_dominant_color(room->bg1_buffer().bitmap());
572 bg1_color.has_value()) {
573 return soften_color(bg1_color.value());
574 }
575
576 return palette_fallback_color(room->palette(), room->blockset());
577 }
578
579 if (auto room_meta = GetRoomMetadata(room_id); room_meta.has_value()) {
580 return palette_fallback_color(room_meta->first, room_meta->second);
581 }
582 }
583
584 // Fallback: neutral deterministic color buckets (no rainbow hue wheel).
585 const auto clamp01 = [](float v) {
586 return (v < 0.0f) ? 0.0f : (v > 1.0f ? 1.0f : v);
587 };
588
589 const ImVec4 dark = theme.panel_bg_darker;
590 const ImVec4 mid = theme.panel_bg_color;
591 const float group_mix =
592 0.16f + (static_cast<float>((room_id >> 4) & 0x03) * 0.08f);
593 const float step = static_cast<float>(room_id & 0x07) * 0.0125f;
594
595 ImVec4 fallback;
596 fallback.x = clamp01(dark.x + (mid.x - dark.x) * group_mix + step);
597 fallback.y = clamp01(dark.y + (mid.y - dark.y) * group_mix + step);
598 fallback.z = clamp01(dark.z + (mid.z - dark.z) * group_mix + step);
599 fallback.w = 1.0f;
600 return ImGui::ColorConvertFloat4ToU32(fallback);
601 }
602
603 int* current_room_id_ = nullptr;
604 ImVector<int>* active_rooms_ = nullptr;
606 std::function<void(int)> on_room_selected_;
607 std::function<void(int, int)> on_room_swap_;
608 std::function<void(int, RoomSelectionIntent)> on_room_intent_;
610 uint64_t source_revision = 0;
611 std::optional<ImU32> color;
612 };
613 std::unordered_map<int, CachedRoomColor> room_color_cache_;
616 char search_filter_[64] = "";
617};
618
619} // namespace editor
620} // namespace yaze
621
622#endif // YAZE_APP_EDITOR_DUNGEON_WORKSPACE_ROOM_MATRIX_CONTENT_H_
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:28
bool is_loaded() const
Definition rom.h:155
zelda3::Room * GetIfMaterialized(int room_id)
static constexpr size_t kRoomCount
zelda3::Room * GetIfLoaded(int room_id)
WindowContent for displaying a visual 16x19 grid of all dungeon rooms.
std::function< void(int)> on_room_selected_
void DrawMatrixLegend(const AgentUITheme &theme) const
static std::string NormalizeForSearch(const std::string &value)
void DrawMatrixSummary(const AgentUITheme &theme, int total_rooms) const
std::function< void(int, RoomSelectionIntent)> on_room_intent_
static ImU32 BlendRoomColor(ImU32 source, ImU32 target, float blend)
std::string GetIcon() const override
Material Design icon for this panel.
std::optional< std::pair< int, int > > GetRoomMetadata(int room_id) const
void SetRooms(DungeonRoomStore *rooms)
std::unordered_map< int, CachedRoomColor > room_color_cache_
std::string GetEditorCategory() const override
Editor category this panel belongs to.
float GetPreferredWidth() const override
Get preferred width for this panel (optional)
std::string GetWorkflowGroup() const override
Optional workflow group for hack-centric actions.
RoomCompositeOutput color_sample_composite_output_
int GetPriority() const override
Get display priority for menu ordering.
std::string GetDisplayName() const override
Human-readable name shown in menus and title bars.
RoomMatrixContent(int *current_room_id, ImVector< int > *active_rooms, std::function< void(int)> on_room_selected, std::function< void(int, int)> on_room_swap=nullptr, DungeonRoomStore *rooms=nullptr)
Construct a room matrix panel.
void Draw(bool *p_open) override
Draw the panel content.
bool MatchesSearchFilter(int room_id) const
std::string GetId() const override
Unique identifier for this panel.
std::function< void(int, int)> on_room_swap_
RoomCompositeOutput tooltip_composite_output_
void DrawLegendSwatch(const ImVec4 &color, const char *label) const
void SetRoomIntentCallback(std::function< void(int, RoomSelectionIntent)> callback)
ImU32 GetRoomColor(int room_id, const AgentUITheme &theme)
Get color for a room from dominant preview color, with fallback.
Base interface for all logical window content components.
void ProcessTextureQueue(IRenderer *renderer)
Definition arena.cc:211
static Arena & Get()
Definition arena.cc:24
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:69
uint8_t blockset() const
Definition room.h:951
uint8_t palette() const
Definition room.h:954
void PrepareForRender(std::optional< uint8_t > entrance_blockset=std::nullopt)
Definition room.cc:980
#define ICON_MD_GRID_VIEW
Definition icons.h:897
#define ICON_MD_MY_LOCATION
Definition icons.h:1270
#define ICON_MD_SEARCH
Definition icons.h:1673
#define ICON_MD_TAB
Definition icons.h:1930
#define ICON_MD_CLOSE
Definition icons.h:418
const AgentUITheme & GetTheme()
gfx::Bitmap & PrepareCanonicalRoomComposite(zelda3::Room &room, RoomCompositeOutput &output)
RoomSelectionIntent
Intent for room selection in the dungeon editor.
void EnsureCompositeBitmapTextureQueued(Bitmap &composite)
Room LoadRoomHeaderFromRom(Rom *rom, int room_id)
Definition room.cc:673
std::string GetRoomLabel(int id)
Convenience function to get a room label.
Centralized theme colors for Agent UI components.
Definition agent_theme.h:19