144 void Draw(
bool* p_open)
override {
149 ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows);
154 constexpr int kRoomsPerRow = 16;
155 constexpr int kRoomsPerCol = 19;
156 constexpr int kTotalRooms = 0x128;
157 constexpr float kCellSpacing = 1.0f;
164 float preview_height = 0.0f;
168 const float height = std::max(1.0f, ImGui::GetContentRegionAvail().y);
169 preview_height = std::min(
171 std::max(0.0f, height - 64.0f - ImGui::GetStyle().ItemSpacing.y));
172 const float grid_height = std::max(
173 1.0f, height - preview_height - ImGui::GetStyle().ItemSpacing.y);
174 if (!ImGui::BeginChild(
"##RoomMatrixGrid", ImVec2(0, grid_height),
182 const ImVec2 avail = ImGui::GetContentRegionAvail();
183 const float panel_width = std::max(1.0f, avail.x);
184 const float panel_height =
185 std::max(1.0f, avail.y - ImGui::GetStyle().ItemSpacing.y);
186 const float cell_size_by_width =
187 (panel_width - kCellSpacing * (kRoomsPerRow - 1)) / kRoomsPerRow;
188 const float cell_size_by_height =
189 (panel_height - kCellSpacing * (kRoomsPerCol - 1)) / kRoomsPerCol;
190 const float cell_size =
192 std::floor(std::min({cell_size_by_width, cell_size_by_height,
193 24.0f * ImGui::GetFontSize() / 13.0f})));
195 const float grid_width =
196 kRoomsPerRow * cell_size + kCellSpacing * (kRoomsPerRow - 1);
197 const float grid_height =
198 kRoomsPerCol * cell_size + kCellSpacing * (kRoomsPerCol - 1);
199 const float x_offset = std::max(0.0f, (panel_width - grid_width) * 0.5f);
200 const float y_offset = std::max(0.0f, (panel_height - grid_height) * 0.5f);
202 ImDrawList* draw_list = ImGui::GetWindowDrawList();
203 const ImVec2 grid_origin = ImGui::GetCursorScreenPos();
204 ImVec2 canvas_pos = grid_origin;
205 canvas_pos.x += x_offset;
206 canvas_pos.y += y_offset;
209 for (
int row = 0; row < kRoomsPerCol; row++) {
210 for (
int col = 0; col < kRoomsPerRow; col++) {
211 int room_id = room_index;
212 bool is_valid_room = (room_id < kTotalRooms);
213 const bool matches_filter =
218 ImVec2(canvas_pos.x + col * (cell_size + kCellSpacing),
219 canvas_pos.y + row * (cell_size + kCellSpacing));
221 ImVec2(cell_min.x + cell_size, cell_min.y + cell_size);
227 if (!matches_filter) {
229 bg_color, ImGui::ColorConvertFloat4ToU32(theme.panel_bg_darker),
242 if ((is_open || is_current) && matches_filter) {
243 ImVec4 color = ImGui::ColorConvertU32ToFloat4(bg_color);
245 ImGui::ColorConvertRGBtoHSV(color.x, color.y, color.z, h, s, v);
246 ImGui::ColorConvertHSVtoRGB(h, s, v + (1.0f - v) * 0.22f, color.x,
248 bg_color = ImGui::ColorConvertFloat4ToU32(color);
250 draw_list->AddRectFilled(cell_min, cell_max, bg_color);
252 if (is_current || is_open) {
253 const ImU32 outline =
254 is_current ? ImGui::ColorConvertFloat4ToU32(
255 theme.dungeon_selection_primary)
256 : IM_COL32(255, 255, 255, matches_filter ? 130 : 45);
257 draw_list->AddRect(ImVec2(cell_min.x + 1, cell_min.y + 1),
258 ImVec2(cell_max.x - 1, cell_max.y - 1), outline,
259 0, 0, is_current ? 2.0f : 1.0f);
264 matches_filter, theme);
269 if (cell_size >= 14.0f) {
270 const std::string label = absl::StrFormat(
"%03X", room_id);
271 const ImVec2 normal_size = ImGui::CalcTextSize(label.c_str());
273 std::min(1.0f, (cell_size - 2.0f) / normal_size.x);
274 const ImVec2 text_pos(
275 cell_min.x + (cell_size - normal_size.x * scale) * 0.5f,
276 cell_min.y + (cell_size - normal_size.y * scale) * 0.5f);
277 const ImVec4 color = ImGui::ColorConvertU32ToFloat4(bg_color);
278 const float luminance =
279 color.x * 0.2126f + color.y * 0.7152f + color.z * 0.0722f;
280 const ImU32 ink = luminance > 0.55f ? IM_COL32(15, 15, 15, 255)
281 : IM_COL32(245, 245, 245, 255);
282 draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize() * scale,
283 text_pos, ink, label.c_str());
287 ImGui::SetCursorScreenPos(cell_min);
289 snprintf(btn_id,
sizeof(btn_id),
"##room%d", room_id);
290 ImGui::InvisibleButton(btn_id, ImVec2(cell_size, cell_size));
292 absl::StrFormat(
"Room 0x%03X", room_id));
294 const bool cell_hovered = ImGui::IsItemHovered();
298 if (ImGui::IsItemClicked()) {
299 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
312 const bool can_swap =
316 std::vector<gui::MenuItemSpec> items;
317 items.emplace_back(is_open ?
"Focus Room" :
"Open in Workbench",
336 items.back().separator_after =
true;
339 tr(
"Copy Room ID"), absl::StrFormat(
"0x%03X", room_id)));
342 items.back().separator_after =
true;
345 tr(
"Swap With Current Room"),
349 [can_swap]() {
return can_swap; }));
356 ImGui::BeginTooltip();
359 ImGui::TextDisabled(tr(
"Room 0x%02X"), room_id);
367 ImGui::TextColored(theme.dungeon_selection_primary,
369 }
else if (is_open) {
370 ImGui::TextColored(theme.status_success,
376 if (room !=
nullptr) {
378 "Palette: 0x%02X | Blockset: 0x%02X | Spriteset: 0x%02X",
381 "Objects: %zu | Sprites: %zu | Doors: %zu | Chests: %zu",
389 if (preview_bitmap.is_active() &&
390 preview_bitmap.texture() != 0) {
392 constexpr float kThumbnailSize = 144.0f;
393 ImGui::Image((ImTextureID)(intptr_t)preview_bitmap.texture(),
394 ImVec2(kThumbnailSize, kThumbnailSize));
403 draw_list->AddRectFilled(
405 ImGui::ColorConvertFloat4ToU32(theme.panel_bg_darker));
414 ImGui::SetCursorScreenPos(grid_origin);
415 ImGui::Dummy(ImVec2(panel_width, grid_height + y_offset));
436 const bool open = ImGui::BeginChild(
437 "##RoomMatrixFixedPreview", ImVec2(0, height),
false,
438 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
440 const std::string caption = absl::StrFormat(
442 const ImVec2 position = ImGui::GetCursorScreenPos();
443 const ImVec2 label_size(ImGui::GetContentRegionAvail().x,
444 ImGui::GetTextLineHeight());
445 auto* draw_list = ImGui::GetWindowDrawList();
446 draw_list->PushClipRect(
448 ImVec2(position.x + label_size.x, position.y + label_size.y),
true);
449 draw_list->AddText(position, ImGui::GetColorU32(ImGuiCol_TextDisabled),
451 draw_list->PopClipRect();
452 ImGui::Dummy(label_size);
453 const ImVec2 available = ImGui::GetContentRegionAvail();
454 const float size = std::max(0.0f, std::min(available.x, available.y));
461 if (bitmap.is_active() && bitmap.texture() != 0) {
462 ImGui::SetCursorPosX(ImGui::GetCursorPosX() +
463 (available.x - size) * 0.5f);
464 ImGui::Image((ImTextureID)(intptr_t)bitmap.texture(),
583 const ImVec2& cell_min,
const ImVec2& cell_max,
586 const ImU32 dim_target =
590 ImVec2(cell_min.x - 1.5f, cell_min.y - 1.5f),
591 ImVec2(cell_max.x + 1.5f, cell_max.y + 1.5f),
596 ImU32 color = *outline;
597 if (!matches_filter) {
600 draw_list->AddRect(ImVec2(cell_min.x + 1.0f, cell_min.y + 1.0f),
601 ImVec2(cell_max.x - 1.0f, cell_max.y - 1.0f), color,
605 const float corner = std::max(4.0f, (cell_max.x - cell_min.x) * 0.3f);
607 if (!matches_filter) {
610 draw_list->AddTriangleFilled(ImVec2(cell_max.x - corner, cell_min.y),
611 ImVec2(cell_max.x, cell_min.y),
612 ImVec2(cell_max.x, cell_min.y + corner),