From f6f278bf658a4e44b53910c334a6438b73383374 Mon Sep 17 00:00:00 2001 From: scawful Date: Mon, 29 Sep 2025 18:56:41 -0400 Subject: [PATCH] Refactor OverworldEditor and Tile16Editor for enhanced functionality and user experience - Removed unnecessary build flags for Yaze minimal builds in release.yml. - Updated version number to 0.3.2 in yaze.h to reflect recent changes. - Enhanced OverworldEditor with new method to scroll the blockset canvas to the currently selected tile, improving navigation. - Implemented logic to handle area size checks for ZSCustomOverworld v3, ensuring compatibility with different ROM versions. - Refactored tile selection and drawing logic in Tile16Editor for improved accuracy and user interaction. - Updated context menu in OverworldEditor to include a refresh option for map changes, enhancing usability. --- .github/workflows/release.yml | 3 - incl/yaze.h | 9 +- src/app/editor/overworld/overworld_editor.cc | 335 +++++++++-- src/app/editor/overworld/overworld_editor.h | 5 + src/app/editor/overworld/tile16_editor.cc | 559 ++++++++++--------- src/app/editor/overworld/tile16_editor.h | 2 +- src/app/gui/canvas.cc | 2 +- 7 files changed, 577 insertions(+), 338 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6481017d..7bb5d378 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -313,9 +313,6 @@ jobs: if ($useVcpkg) { $args += "-DCMAKE_TOOLCHAIN_FILE=$toolchain" - $args += '-DYAZE_MINIMAL_BUILD=OFF' - } else { - $args += '-DYAZE_MINIMAL_BUILD=ON' } cmake @args diff --git a/incl/yaze.h b/incl/yaze.h index 3b7bf310..465818b9 100644 --- a/incl/yaze.h +++ b/incl/yaze.h @@ -28,18 +28,11 @@ extern "C" { * @{ */ -/** Major version number */ -#define YAZE_VERSION_MAJOR 0 -/** Minor version number */ -#define YAZE_VERSION_MINOR 3 -/** Patch version number */ -#define YAZE_VERSION_PATCH 1 - /** Combined version as a string */ #define YAZE_VERSION_STRING "0.3.2" /** Combined version as a number (major * 10000 + minor * 100 + patch) */ -#define YAZE_VERSION_NUMBER 301 +#define YAZE_VERSION_NUMBER 302 /** @} */ diff --git a/src/app/editor/overworld/overworld_editor.cc b/src/app/editor/overworld/overworld_editor.cc index b6399ea2..ebdf10fa 100644 --- a/src/app/editor/overworld/overworld_editor.cc +++ b/src/app/editor/overworld/overworld_editor.cc @@ -1008,6 +1008,9 @@ void OverworldEditor::CheckForSelectRectangle() { current_tile16_ = overworld_.GetTileFromPosition(ow_map_canvas_.selected_tile_pos()); ow_map_canvas_.set_selected_tile_pos(ImVec2(-1, -1)); + + // Scroll blockset canvas to show the selected tile + ScrollBlocksetCanvasToCurrentTile(); } static std::vector tile16_ids; @@ -1160,35 +1163,95 @@ absl::Status OverworldEditor::CheckForCurrentMap() { const int current_highlighted_map = current_map_; - if (overworld_.overworld_map(current_map_)->is_large_map() || - overworld_.overworld_map(current_map_)->large_index() != 0) { + // Check if ZSCustomOverworld v3 is present + uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + bool use_v3_area_sizes = (asm_version >= 3 && asm_version != 0xFF); + + // Get area size for v3+ ROMs, otherwise use legacy logic + if (use_v3_area_sizes) { + using zelda3::AreaSizeEnum; + auto area_size = overworld_.overworld_map(current_map_)->area_size(); const int highlight_parent = overworld_.overworld_map(current_highlighted_map)->parent(); - const int parent_map_x = highlight_parent % 8; - const int parent_map_y = highlight_parent / 8; - ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize, - parent_map_y * kOverworldMapSize, large_map_size, - large_map_size); - } else { - // Calculate map coordinates accounting for world offset - int current_map_x, current_map_y; + + // Calculate parent map coordinates accounting for world offset + int parent_map_x; + int parent_map_y; if (current_world_ == 0) { // Light World (0x00-0x3F) - current_map_x = current_highlighted_map % 8; - current_map_y = current_highlighted_map / 8; + parent_map_x = highlight_parent % 8; + parent_map_y = highlight_parent / 8; } else if (current_world_ == 1) { // Dark World (0x40-0x7F) - current_map_x = (current_highlighted_map - 0x40) % 8; - current_map_y = (current_highlighted_map - 0x40) / 8; + parent_map_x = (highlight_parent - 0x40) % 8; + parent_map_y = (highlight_parent - 0x40) / 8; } else { - // Special World (0x80-0x9F) - use display coordinates based on current_world_ - // The special world maps are displayed in the same 8x8 grid as LW/DW - current_map_x = (current_highlighted_map - 0x80) % 8; - current_map_y = (current_highlighted_map - 0x80) / 8; + // Special World (0x80-0x9F) + parent_map_x = (highlight_parent - 0x80) % 8; + parent_map_y = (highlight_parent - 0x80) / 8; + } + + // Draw outline based on area size + switch (area_size) { + case AreaSizeEnum::LargeArea: + // 2x2 grid (1024x1024) + ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize, + parent_map_y * kOverworldMapSize, + large_map_size, large_map_size); + break; + case AreaSizeEnum::WideArea: + // 2x1 grid (1024x512) - horizontal + ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize, + parent_map_y * kOverworldMapSize, + large_map_size, kOverworldMapSize); + break; + case AreaSizeEnum::TallArea: + // 1x2 grid (512x1024) - vertical + ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize, + parent_map_y * kOverworldMapSize, + kOverworldMapSize, large_map_size); + break; + case AreaSizeEnum::SmallArea: + default: + // 1x1 grid (512x512) + ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize, + parent_map_y * kOverworldMapSize, + kOverworldMapSize, kOverworldMapSize); + break; + } + } else { + // Legacy logic for vanilla and v2 ROMs + if (overworld_.overworld_map(current_map_)->is_large_map() || + overworld_.overworld_map(current_map_)->large_index() != 0) { + const int highlight_parent = + overworld_.overworld_map(current_highlighted_map)->parent(); + const int parent_map_x = highlight_parent % 8; + const int parent_map_y = highlight_parent / 8; + ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize, + parent_map_y * kOverworldMapSize, large_map_size, + large_map_size); + } else { + // Calculate map coordinates accounting for world offset + int current_map_x; + int current_map_y; + if (current_world_ == 0) { + // Light World (0x00-0x3F) + current_map_x = current_highlighted_map % 8; + current_map_y = current_highlighted_map / 8; + } else if (current_world_ == 1) { + // Dark World (0x40-0x7F) + current_map_x = (current_highlighted_map - 0x40) % 8; + current_map_y = (current_highlighted_map - 0x40) / 8; + } else { + // Special World (0x80-0x9F) - use display coordinates based on current_world_ + // The special world maps are displayed in the same 8x8 grid as LW/DW + current_map_x = (current_highlighted_map - 0x80) % 8; + current_map_y = (current_highlighted_map - 0x80) / 8; + } + ow_map_canvas_.DrawOutline(current_map_x * kOverworldMapSize, + current_map_y * kOverworldMapSize, + kOverworldMapSize, kOverworldMapSize); } - ow_map_canvas_.DrawOutline(current_map_x * kOverworldMapSize, - current_map_y * kOverworldMapSize, - kOverworldMapSize, kOverworldMapSize); } // Ensure current map has texture created for rendering @@ -1341,6 +1404,9 @@ absl::Status OverworldEditor::DrawTile16Selector() { if (id != current_tile16_ && id >= 0 && id < 512) { current_tile16_ = id; RETURN_IF_ERROR(tile16_editor_.SetCurrentTile(id)); + + // Scroll blockset canvas to show the selected tile + ScrollBlocksetCanvasToCurrentTile(); } } @@ -2053,7 +2119,19 @@ void OverworldEditor::RefreshChildMapOnDemand(int map_index) { } // Handle multi-area maps (large, wide, tall) with safe coordination - RefreshMultiAreaMapsSafely(map_index, map); + // Check if ZSCustomOverworld v3 is present + uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + bool use_v3_area_sizes = (asm_version >= 3 && asm_version != 0xFF); + + if (use_v3_area_sizes) { + // Use v3 multi-area coordination + RefreshMultiAreaMapsSafely(map_index, map); + } else { + // Legacy logic: only handle large maps for vanilla/v2 + if (map->is_large_map()) { + RefreshMultiAreaMapsSafely(map_index, map); + } + } } /** @@ -2166,17 +2244,22 @@ void OverworldEditor::RefreshMultiAreaMapsSafely(int map_index, status = sibling_map->BuildTiles16Gfx(*overworld_.mutable_tiles16(), overworld_.tiles16().size()); if (status.ok()) { - status = sibling_map->BuildBitmap( - overworld_.GetMapTiles(current_world_)); + // Load palette for the sibling map + status = sibling_map->LoadPalette(); if (status.ok()) { - maps_bmp_[sibling].set_data(sibling_map->bitmap_data()); - maps_bmp_[sibling].set_modified(false); + status = sibling_map->BuildBitmap( + overworld_.GetMapTiles(current_world_)); + if (status.ok()) { + maps_bmp_[sibling].set_data(sibling_map->bitmap_data()); + maps_bmp_[sibling].SetPalette(overworld_.current_area_palette()); + maps_bmp_[sibling].set_modified(false); - // Update texture if it exists - if (maps_bmp_[sibling].texture()) { - core::Renderer::Get().UpdateBitmap(&maps_bmp_[sibling]); - } else { - EnsureMapTexture(sibling); + // Update texture if it exists + if (maps_bmp_[sibling].texture()) { + core::Renderer::Get().UpdateBitmap(&maps_bmp_[sibling]); + } else { + EnsureMapTexture(sibling); + } } } } @@ -2206,39 +2289,136 @@ absl::Status OverworldEditor::RefreshMapPalette() { overworld_.mutable_overworld_map(current_map_)->LoadPalette()); const auto current_map_palette = overworld_.current_area_palette(); - if (overworld_.overworld_map(current_map_)->is_large_map()) { - // We need to update the map and its siblings if it's a large map - for (int i = 1; i < 4; i++) { - int sibling_index = overworld_.overworld_map(current_map_)->parent() + i; - if (i >= 2) - sibling_index += 6; - RETURN_IF_ERROR( - overworld_.mutable_overworld_map(sibling_index)->LoadPalette()); - maps_bmp_[sibling_index].SetPalette(current_map_palette); + // Check if ZSCustomOverworld v3 is present + uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + bool use_v3_area_sizes = (asm_version >= 3 && asm_version != 0xFF); + + if (use_v3_area_sizes) { + // Use v3 area size system + using zelda3::AreaSizeEnum; + auto area_size = overworld_.overworld_map(current_map_)->area_size(); + + if (area_size != AreaSizeEnum::SmallArea) { + // Get all sibling maps that need palette updates + std::vector sibling_maps; + int parent_id = overworld_.overworld_map(current_map_)->parent(); + + switch (area_size) { + case AreaSizeEnum::LargeArea: + // 2x2 grid: parent, parent+1, parent+8, parent+9 + sibling_maps = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9}; + break; + case AreaSizeEnum::WideArea: + // 2x1 grid: parent, parent+1 + sibling_maps = {parent_id, parent_id + 1}; + break; + case AreaSizeEnum::TallArea: + // 1x2 grid: parent, parent+8 + sibling_maps = {parent_id, parent_id + 8}; + break; + default: + break; + } + + // Update palette for all siblings + for (int sibling_index : sibling_maps) { + if (sibling_index < 0 || sibling_index >= zelda3::kNumOverworldMaps) { + continue; + } + RETURN_IF_ERROR( + overworld_.mutable_overworld_map(sibling_index)->LoadPalette()); + maps_bmp_[sibling_index].SetPalette(current_map_palette); + } + } else { + // Small area - only update current map + maps_bmp_[current_map_].SetPalette(current_map_palette); } + } else { + // Legacy logic for vanilla and v2 ROMs + if (overworld_.overworld_map(current_map_)->is_large_map()) { + // We need to update the map and its siblings if it's a large map + for (int i = 1; i < 4; i++) { + int sibling_index = overworld_.overworld_map(current_map_)->parent() + i; + if (i >= 2) + sibling_index += 6; + RETURN_IF_ERROR( + overworld_.mutable_overworld_map(sibling_index)->LoadPalette()); + maps_bmp_[sibling_index].SetPalette(current_map_palette); + } + } + maps_bmp_[current_map_].SetPalette(current_map_palette); } - maps_bmp_[current_map_].SetPalette(current_map_palette); return absl::OkStatus(); } void OverworldEditor::RefreshMapProperties() { const auto& current_ow_map = *overworld_.mutable_overworld_map(current_map_); - if (current_ow_map.is_large_map()) { - // We need to copy the properties from the parent map to the children - for (int i = 1; i < 4; i++) { - int sibling_index = current_ow_map.parent() + i; - if (i >= 2) { - sibling_index += 6; + + // Check if ZSCustomOverworld v3 is present + uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; + bool use_v3_area_sizes = (asm_version >= 3 && asm_version != 0xFF); + + if (use_v3_area_sizes) { + // Use v3 area size system + using zelda3::AreaSizeEnum; + auto area_size = current_ow_map.area_size(); + + if (area_size != AreaSizeEnum::SmallArea) { + // Get all sibling maps that need property updates + std::vector sibling_maps; + int parent_id = current_ow_map.parent(); + + switch (area_size) { + case AreaSizeEnum::LargeArea: + // 2x2 grid: parent+1, parent+8, parent+9 (skip parent itself) + sibling_maps = {parent_id + 1, parent_id + 8, parent_id + 9}; + break; + case AreaSizeEnum::WideArea: + // 2x1 grid: parent+1 (skip parent itself) + sibling_maps = {parent_id + 1}; + break; + case AreaSizeEnum::TallArea: + // 1x2 grid: parent+8 (skip parent itself) + sibling_maps = {parent_id + 8}; + break; + default: + break; + } + + // Copy properties from parent map to all siblings + for (int sibling_index : sibling_maps) { + if (sibling_index < 0 || sibling_index >= zelda3::kNumOverworldMaps) { + continue; + } + auto& map = *overworld_.mutable_overworld_map(sibling_index); + map.set_area_graphics(current_ow_map.area_graphics()); + map.set_area_palette(current_ow_map.area_palette()); + map.set_sprite_graphics(game_state_, + current_ow_map.sprite_graphics(game_state_)); + map.set_sprite_palette(game_state_, + current_ow_map.sprite_palette(game_state_)); + map.set_message_id(current_ow_map.message_id()); + } + } + } else { + // Legacy logic for vanilla and v2 ROMs + if (current_ow_map.is_large_map()) { + // We need to copy the properties from the parent map to the children + for (int i = 1; i < 4; i++) { + int sibling_index = current_ow_map.parent() + i; + if (i >= 2) { + sibling_index += 6; + } + auto& map = *overworld_.mutable_overworld_map(sibling_index); + map.set_area_graphics(current_ow_map.area_graphics()); + map.set_area_palette(current_ow_map.area_palette()); + map.set_sprite_graphics(game_state_, + current_ow_map.sprite_graphics(game_state_)); + map.set_sprite_palette(game_state_, + current_ow_map.sprite_palette(game_state_)); + map.set_message_id(current_ow_map.message_id()); } - auto& map = *overworld_.mutable_overworld_map(sibling_index); - map.set_area_graphics(current_ow_map.area_graphics()); - map.set_area_palette(current_ow_map.area_palette()); - map.set_sprite_graphics(game_state_, - current_ow_map.sprite_graphics(game_state_)); - map.set_sprite_palette(game_state_, - current_ow_map.sprite_palette(game_state_)); - map.set_message_id(current_ow_map.message_id()); } } } @@ -3011,6 +3191,18 @@ void OverworldEditor::SetupOverworldCanvasContextMenu() { ow_map_canvas_.AddContextMenuItem(overlay_item); } + // Map editing controls + gui::Canvas::ContextMenuItem refresh_map_item; + refresh_map_item.label = "Refresh Map Changes"; + refresh_map_item.callback = [this]() { + RefreshOverworldMap(); + auto status = RefreshTile16Blockset(); + if (!status.ok()) { + util::logf("Failed to refresh tile16 blockset: %s", status.message().data()); + } + }; + ow_map_canvas_.AddContextMenuItem(refresh_map_item); + // Canvas controls gui::Canvas::ContextMenuItem reset_pos_item; reset_pos_item.label = "Reset Canvas Position"; @@ -3028,6 +3220,39 @@ void OverworldEditor::SetupOverworldCanvasContextMenu() { ow_map_canvas_.AddContextMenuItem(zoom_fit_item); } +void OverworldEditor::ScrollBlocksetCanvasToCurrentTile() { + // Calculate the position of the current tile in the blockset canvas + // Blockset is arranged in an 8-tile-per-row grid, each tile is 16x16 pixels + constexpr int kTilesPerRow = 8; + constexpr int kTileDisplaySize = 32; // Each tile displayed at 32x32 (16x16 at 2x scale) + + // Calculate tile position in canvas coordinates (absolute position in the grid) + int tile_col = current_tile16_ % kTilesPerRow; + int tile_row = current_tile16_ / kTilesPerRow; + float tile_x = static_cast(tile_col * kTileDisplaySize); + float tile_y = static_cast(tile_row * kTileDisplaySize); + + // Get the canvas dimensions + ImVec2 canvas_size = blockset_canvas_.canvas_size(); + + // Calculate the scroll position to center the tile in the viewport + float scroll_x = tile_x - (canvas_size.x / 2.0F) + (kTileDisplaySize / 2.0F); + float scroll_y = tile_y - (canvas_size.y / 2.0F) + (kTileDisplaySize / 2.0F); + + // Clamp scroll to valid ranges (don't scroll beyond bounds) + if (scroll_x < 0) scroll_x = 0; + if (scroll_y < 0) scroll_y = 0; + + // Update the blockset canvas scrolling position first + blockset_canvas_.set_scrolling(ImVec2(-1, -scroll_y)); + + // Set the points to draw the white outline box around the current tile + // Points are in canvas coordinates (not screen coordinates) + blockset_canvas_.mutable_points()->clear(); + blockset_canvas_.mutable_points()->push_back(ImVec2(tile_x, tile_y)); + blockset_canvas_.mutable_points()->push_back(ImVec2(tile_x + kTileDisplaySize, tile_y + kTileDisplaySize)); +} + void OverworldEditor::DrawOverworldProperties() { static bool init_properties = false; diff --git a/src/app/editor/overworld/overworld_editor.h b/src/app/editor/overworld/overworld_editor.h index 398dc153..ecb6ee25 100644 --- a/src/app/editor/overworld/overworld_editor.h +++ b/src/app/editor/overworld/overworld_editor.h @@ -211,6 +211,11 @@ class OverworldEditor : public Editor, public gfx::GfxContext { void HandleMapInteraction(); void SetupOverworldCanvasContextMenu(); + /** + * @brief Scroll the blockset canvas to show the current selected tile16 + */ + void ScrollBlocksetCanvasToCurrentTile(); + // Scratch space canvas methods absl::Status DrawScratchSpace(); absl::Status SaveCurrentSelectionToScratch(int slot); diff --git a/src/app/editor/overworld/tile16_editor.cc b/src/app/editor/overworld/tile16_editor.cc index 47461b3b..9b3142b3 100644 --- a/src/app/editor/overworld/tile16_editor.cc +++ b/src/app/editor/overworld/tile16_editor.cc @@ -9,6 +9,7 @@ #include "app/gfx/performance_profiler.h" #include "app/gfx/snes_palette.h" #include "app/gui/canvas.h" +#include "app/gui/icons.h" #include "app/gui/input.h" #include "app/gui/style.h" #include "app/rom.h" @@ -251,22 +252,8 @@ absl::Status Tile16Editor::Update() { } void Tile16Editor::DrawTile16Editor() { - if (BeginTable("#Tile16EditorTable", 2, ImGuiTableFlags_Resizable)) { - TableSetupColumn("Blockset", ImGuiTableColumnFlags_WidthFixed, 280); - TableSetupColumn("Editor", ImGuiTableColumnFlags_WidthStretch); - TableHeadersRow(); - TableNextRow(); - - // Blockset selection column - TableNextColumn(); - status_ = UpdateBlockset(); - - // Editor column - TableNextColumn(); - status_ = UpdateTile16Edit(); - - EndTable(); - } + // REFACTORED: Single unified table layout in UpdateTile16Edit + status_ = UpdateTile16Edit(); } absl::Status Tile16Editor::UpdateBlockset() { @@ -308,7 +295,8 @@ absl::Status Tile16Editor::UpdateBlockset() { selected_tile, grid_x, grid_y); } } - blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 0, true, 2.0f); + blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 0, true, + blockset_canvas_.GetGlobalScale()); blockset_canvas_.DrawGrid(); blockset_canvas_.DrawOverlay(); EndChild(); @@ -690,7 +678,7 @@ absl::Status Tile16Editor::UpdateTile16Edit() { // Modern header with improved styling ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8, 4)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8, 4)); - + // Header section with better visual hierarchy ImGui::BeginGroup(); ImGui::TextColored(ImVec4(0.8f, 0.9f, 1.0f, 1.0f), "Tile16 Editor"); @@ -700,16 +688,16 @@ absl::Status Tile16Editor::UpdateTile16Edit() { ImGui::TextDisabled("|"); ImGui::SameLine(); ImGui::TextDisabled("Palette: %d", current_palette_); - + // Show actual palette slot for debugging if (show_debug_info) { ImGui::SameLine(); int actual_slot = GetActualPaletteSlotForCurrentTile16(); ImGui::TextDisabled("(Slot: %d)", actual_slot); } - + ImGui::EndGroup(); - + // Modern button styling for controls ImGui::SameLine(ImGui::GetContentRegionAvail().x - 180); if (ImGui::Button("Debug Info", ImVec2(80, 0))) { @@ -719,49 +707,84 @@ absl::Status Tile16Editor::UpdateTile16Edit() { if (ImGui::Button("Advanced", ImVec2(80, 0))) { show_advanced_controls = !show_advanced_controls; } - + ImGui::PopStyleVar(2); - + ImGui::Separator(); - // Modern 3-column layout with improved spacing + // REFACTORED: Improved 3-column layout with better space utilization if (ImGui::BeginTable("##Tile16EditLayout", 3, - ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV | - ImGuiTableFlags_SizingFixedFit)) { - ImGui::TableSetupColumn("Tile8 Source", ImGuiTableColumnFlags_WidthStretch, 0.5f); - ImGui::TableSetupColumn("Tile16 Editor", ImGuiTableColumnFlags_WidthFixed, 120.0f); - ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthStretch, 0.3f); - + ImGuiTableFlags_Resizable | + ImGuiTableFlags_BordersInnerV | + ImGuiTableFlags_SizingStretchProp)) { + ImGui::TableSetupColumn("Tile16 Blockset", + ImGuiTableColumnFlags_WidthStretch, 0.35f); + ImGui::TableSetupColumn("Tile8 Source", ImGuiTableColumnFlags_WidthStretch, + 0.35f); + ImGui::TableSetupColumn("Editor & Controls", + ImGuiTableColumnFlags_WidthStretch, 0.30f); + ImGui::TableHeadersRow(); ImGui::TableNextRow(); - // Tile8 selector column - modern design + // ========== COLUMN 1: Tile16 Blockset ========== ImGui::TableNextColumn(); ImGui::BeginGroup(); - - ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.9f, 1.0f), "Tile8 Source"); - - // Modern palette group selector with better styling - const char* palette_group_names[] = { - "OW Main", "OW Aux", "OW Anim", "Dungeon", "Sprites", "Armor", "Sword"}; - - ImGui::SetNextItemWidth(120); - ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.2f, 0.2f, 0.25f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.25f, 0.25f, 0.3f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.3f, 0.3f, 0.35f, 1.0f)); - - if (ImGui::Combo("##PaletteGroup", ¤t_palette_group_, palette_group_names, 7)) { - RETURN_IF_ERROR(RefreshAllPalettes()); - } - - ImGui::PopStyleColor(3); - // CRITICAL FIX: Use proper scrollable child window with fixed height + ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.9f, 1.0f), "Tile16 Blockset"); + + // Blockset canvas with scrolling + if (BeginChild("##BlocksetScrollable", + ImVec2(0, ImGui::GetContentRegionAvail().y), true, + ImGuiWindowFlags_AlwaysVerticalScrollbar)) { + + blockset_canvas_.DrawBackground(); + blockset_canvas_.DrawContextMenu(); + + // Handle tile selection from blockset + bool tile_selected = false; + blockset_canvas_.DrawTileSelector(32.0f); + + if (ImGui::IsItemClicked(ImGuiMouseButton_Left) && + blockset_canvas_.IsMouseHovering()) { + tile_selected = true; + } + + if (tile_selected) { + const ImGuiIO& io = ImGui::GetIO(); + ImVec2 canvas_pos = blockset_canvas_.zero_point(); + ImVec2 mouse_pos = + ImVec2(io.MousePos.x - canvas_pos.x, io.MousePos.y - canvas_pos.y); + + int grid_x = static_cast(mouse_pos.x / + (32 * blockset_canvas_.GetGlobalScale())); + int grid_y = static_cast(mouse_pos.y / + (32 * blockset_canvas_.GetGlobalScale())); + int selected_tile = grid_x + grid_y * 8; + + if (selected_tile != current_tile16_ && selected_tile >= 0) { + RETURN_IF_ERROR(SetCurrentTile(selected_tile)); + util::logf("Selected Tile16 from blockset: %d", selected_tile); + } + } + + blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 0, true, 2); + blockset_canvas_.DrawGrid(); + blockset_canvas_.DrawOverlay(); + } + EndChild(); + ImGui::EndGroup(); + + // ========== COLUMN 2: Tile8 Source ========== + ImGui::TableNextColumn(); + ImGui::BeginGroup(); + + ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.9f, 1.0f), "Tile8 Source"); + tile8_source_canvas_.set_draggable(false); - // Use direct ImGui child window for proper scrolling - if (BeginChild("##Tile8SourceScrollable", - ImVec2(0, 32 * 8 * 4), true, + // Scrollable tile8 source + if (BeginChild("##Tile8SourceScrollable", ImVec2(0, 0), true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { tile8_source_canvas_.DrawBackground(); @@ -777,19 +800,18 @@ absl::Status Tile16Editor::UpdateTile16Edit() { } if (tile8_selected) { - // Get mouse position relative to canvas more accurately const ImGuiIO& io = ImGui::GetIO(); ImVec2 canvas_pos = tile8_source_canvas_.zero_point(); ImVec2 mouse_pos = ImVec2(io.MousePos.x - canvas_pos.x, io.MousePos.y - canvas_pos.y); - // Account for the 4x scale when calculating tile position + // Account for dynamic zoom when calculating tile position int tile_x = static_cast( mouse_pos.x / (8 * 4)); // 8 pixel tile * 4x scale = 32 pixels per tile int tile_y = static_cast(mouse_pos.y / (8 * 4)); - // Calculate tiles per row based on bitmap width (should be 16 for 128px wide bitmap) + // Calculate tiles per row based on bitmap width int tiles_per_row = current_gfx_bmp_.width() / 8; int new_tile8 = tile_x + (tile_y * tiles_per_row); @@ -802,15 +824,16 @@ absl::Status Tile16Editor::UpdateTile16Edit() { } } - tile8_source_canvas_.DrawBitmap(current_gfx_bmp_, 2, 2, 4.0F); + tile8_source_canvas_.DrawBitmap(current_gfx_bmp_, 2, 2, 4); tile8_source_canvas_.DrawGrid(); tile8_source_canvas_.DrawOverlay(); - - } // End tile8 source scrollable child + } EndChild(); ImGui::EndGroup(); - // Tile16 editor column - compact and focused + + // ========== COLUMN 3: Tile16 Editor + Controls ========== TableNextColumn(); + ImGui::BeginGroup(); // Fixed size container to prevent canvas expansion if (ImGui::BeginChild("##Tile16FixedCanvas", ImVec2(90, 90), true, @@ -820,9 +843,9 @@ absl::Status Tile16Editor::UpdateTile16Edit() { tile16_edit_canvas_.DrawBackground(ImVec2(64, 64)); tile16_edit_canvas_.DrawContextMenu(); - // Draw current tile16 bitmap at 4x scale for clarity (16x16 pixels -> 64x64 display) + // Draw current tile16 bitmap with dynamic zoom if (current_tile16_bmp_.is_active()) { - tile16_edit_canvas_.DrawBitmap(current_tile16_bmp_, 2, 2, 4.0F); + tile16_edit_canvas_.DrawBitmap(current_tile16_bmp_, 2, 2, 4); } // Handle tile8 painting with improved hover preview @@ -832,21 +855,23 @@ absl::Status Tile16Editor::UpdateTile16Edit() { // Create a display tile that shows the current palette selection gfx::Bitmap display_tile; - + // Get the original pixel data (already has sheet offsets from ProcessGraphicsBuffer) - std::vector tile_data = current_gfx_individual_[current_tile8_].vector(); - + std::vector tile_data = + current_gfx_individual_[current_tile8_].vector(); + // The pixel data already contains the correct indices for the 256-color palette // We don't need to remap - just use it as-is display_tile.Create(8, 8, 8, tile_data); - + // Apply the complete 256-color palette if (overworld_palette_.size() >= 256) { display_tile.SetPalette(overworld_palette_); } else { - display_tile.SetPalette(current_gfx_individual_[current_tile8_].palette()); + display_tile.SetPalette( + current_gfx_individual_[current_tile8_].palette()); } - + // Apply flips if needed if (x_flip || y_flip) { auto& data = display_tile.mutable_data(); @@ -867,26 +892,27 @@ absl::Status Tile16Editor::UpdateTile16Edit() { } } } - + // Render the display tile core::Renderer::Get().RenderBitmap(&display_tile); // CRITICAL FIX: Handle tile painting with simple click instead of click+drag // Draw the preview first - tile16_edit_canvas_.DrawTilePainter(display_tile, 8, 4.0F); + tile16_edit_canvas_.DrawTilePainter( + display_tile, 8, 4); // Check for simple click to paint tile8 to tile16 if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { - // Get mouse position relative to tile16 canvas const ImGuiIO& io = ImGui::GetIO(); ImVec2 canvas_pos = tile16_edit_canvas_.zero_point(); ImVec2 mouse_pos = ImVec2(io.MousePos.x - canvas_pos.x, io.MousePos.y - canvas_pos.y); - // Convert canvas coordinates to tile16 coordinates (0-15 range) - // The canvas is 64x64 display pixels showing a 16x16 tile at 4x scale - int tile_x = static_cast(mouse_pos.x / 4.0F); - int tile_y = static_cast(mouse_pos.y / 4.0F); + // Convert canvas coordinates to tile16 coordinates with dynamic zoom + int tile_x = static_cast(mouse_pos.x / + 4); + int tile_y = static_cast(mouse_pos.y / + 4); // Clamp to valid range tile_x = std::max(0, std::min(15, tile_x)); @@ -895,22 +921,22 @@ absl::Status Tile16Editor::UpdateTile16Edit() { util::logf("Tile16 canvas click: (%.2f, %.2f) -> Tile16: (%d, %d)", mouse_pos.x, mouse_pos.y, tile_x, tile_y); - // Pass the display tile to draw RETURN_IF_ERROR( DrawToCurrentTile16(ImVec2(tile_x, tile_y), &display_tile)); } // CRITICAL FIX: Right-click to pick tile8 from tile16 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { - // Get mouse position relative to tile16 canvas const ImGuiIO& io = ImGui::GetIO(); ImVec2 canvas_pos = tile16_edit_canvas_.zero_point(); ImVec2 mouse_pos = ImVec2(io.MousePos.x - canvas_pos.x, io.MousePos.y - canvas_pos.y); - // Convert canvas coordinates to tile16 coordinates (0-15 range) - int tile_x = static_cast(mouse_pos.x / 4.0F); - int tile_y = static_cast(mouse_pos.y / 4.0F); + // Convert with dynamic zoom + int tile_x = static_cast(mouse_pos.x / + 4); + int tile_y = static_cast(mouse_pos.y / + 4); // Clamp to valid range tile_x = std::max(0, std::min(15, tile_x)); @@ -922,164 +948,156 @@ absl::Status Tile16Editor::UpdateTile16Edit() { } } - tile16_edit_canvas_.DrawGrid(8.0F); // 8x8 grid + tile16_edit_canvas_.DrawGrid(8.0F); // Scale grid with zoom tile16_edit_canvas_.DrawOverlay(); - - } // End fixed canvas child window + } ImGui::EndChild(); - // Compact preview below canvas - if (current_tile16_bmp_.is_active()) { - auto* texture = current_tile16_bmp_.texture(); - if (texture) { - Text("Preview:"); - ImGui::Image((ImTextureID)(intptr_t)texture, ImVec2(32.0F, 32.0F)); - } - } - - // Controls column - clean and organized - TableNextColumn(); - Text("Controls"); - - // Essential tile8 controls at the top - Text("Tile8 Options:"); - Checkbox("X Flip", &x_flip); - SameLine(); - Checkbox("Y Flip", &y_flip); - Checkbox("Priority", &priority_tile); - - // Show current tile8 selection - if (current_tile8_ >= 0 && - current_tile8_ < static_cast(current_gfx_individual_.size()) && - current_gfx_individual_[current_tile8_].is_active()) { - Text("Tile8: %d", current_tile8_); - SameLine(); - auto* tile8_texture = current_gfx_individual_[current_tile8_].texture(); - if (tile8_texture) { - ImGui::Image((ImTextureID)(intptr_t)tile8_texture, ImVec2(16, 16)); - } - } - Separator(); - // Modern palette selector with enhanced UX - ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.9f, 1.0f), "Palette Selection"); - - // Show debug info if enabled - if (show_debug_info) { - int actual_slot = GetActualPaletteSlotForCurrentTile16(); - ImGui::TextDisabled("Button %d → Slot %d", current_palette_, actual_slot); + // === Compact Controls Section === + + // Tile8 info and preview + if (current_tile8_ >= 0 && + current_tile8_ < static_cast(current_gfx_individual_.size()) && + current_gfx_individual_[current_tile8_].is_active()) { + Text("Tile8: %02X", current_tile8_); + SameLine(); + auto* tile8_texture = current_gfx_individual_[current_tile8_].texture(); + if (tile8_texture) { + ImGui::Image((ImTextureID)(intptr_t)tile8_texture, ImVec2(24, 24)); + } } - // Modern palette grid with improved styling + // Tile8 transform options in compact form + Checkbox("X Flip", &x_flip); + SameLine(); + Checkbox("Y Flip", &y_flip); + SameLine(); + Checkbox("Priority", &priority_tile); + + Separator(); + + // Palette selector - more compact + Text("Palette:"); + if (show_debug_info) { + SameLine(); + int actual_slot = GetActualPaletteSlotForCurrentTile16(); + ImGui::TextDisabled("(Slot %d)", actual_slot); + } + + // Compact palette grid ImGui::BeginGroup(); - - // Calculate optimal button size based on available width float available_width = ImGui::GetContentRegionAvail().x; - float button_size = std::min(28.0f, (available_width - 24.0f) / 4.0f); - + float button_size = std::min(32.0f, (available_width - 16.0f) / 4.0f); + for (int row = 0; row < 2; ++row) { for (int col = 0; col < 4; ++col) { - if (col > 0) ImGui::SameLine(); - + if (col > 0) + ImGui::SameLine(); + int i = row * 4 + col; bool is_current = (current_palette_ == i); - + // Modern button styling with better visual hierarchy ImGui::PushID(i); - + if (is_current) { - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.7f, 0.3f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.8f, 0.4f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.1f, 0.6f, 0.2f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button, + ImVec4(0.2f, 0.7f, 0.3f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, + ImVec4(0.3f, 0.8f, 0.4f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, + ImVec4(0.1f, 0.6f, 0.2f, 1.0f)); } else { - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.3f, 0.35f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.4f, 0.4f, 0.45f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.25f, 0.25f, 0.3f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button, + ImVec4(0.3f, 0.3f, 0.35f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, + ImVec4(0.4f, 0.4f, 0.45f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, + ImVec4(0.25f, 0.25f, 0.3f, 1.0f)); } - + // Add border for better definition ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); - ImGui::PushStyleColor(ImGuiCol_Border, is_current ? - ImVec4(0.4f, 0.9f, 0.5f, 1.0f) : ImVec4(0.5f, 0.5f, 0.5f, 0.3f)); - - if (ImGui::Button(absl::StrFormat("%d", i).c_str(), ImVec2(button_size, button_size))) { + ImGui::PushStyleColor(ImGuiCol_Border, + is_current ? ImVec4(0.4f, 0.9f, 0.5f, 1.0f) + : ImVec4(0.5f, 0.5f, 0.5f, 0.3f)); + + if (ImGui::Button(absl::StrFormat("%d", i).c_str(), + ImVec2(button_size, button_size))) { if (current_palette_ != i) { current_palette_ = i; auto status = RefreshAllPalettes(); if (!status.ok()) { - util::logf("Failed to refresh palettes: %s", status.message().data()); + util::logf("Failed to refresh palettes: %s", + status.message().data()); } else { - util::logf("Palette successfully changed to %d", current_palette_); + util::logf("Palette successfully changed to %d", + current_palette_); } } } - + ImGui::PopStyleColor(4); // 3 button colors + 1 border color ImGui::PopStyleVar(1); // border size ImGui::PopID(); - - // Enhanced tooltip with debug information + + // Simplified tooltip if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - ImGui::Text("Palette Button %d", i); - if (show_debug_info) { - // Show what actual palette slot this maps to for different sheets - ImGui::Separator(); - ImGui::Text("Sheet Mappings:"); - ImGui::Text(" Sheet 0,3,4: Slot %d", GetActualPaletteSlot(i, 0)); - ImGui::Text(" Sheet 1,2: Slot %d", GetActualPaletteSlot(i, 1)); - ImGui::Text(" Sheet 5,6: Slot %d", GetActualPaletteSlot(i, 5)); - ImGui::Text(" Sheet 7: Slot %d", GetActualPaletteSlot(i, 7)); + ImGui::Text("Palette %d → Slots:", i); + ImGui::Text(" S0,3,4: %d", GetActualPaletteSlot(i, 0)); + ImGui::Text(" S1,2: %d", GetActualPaletteSlot(i, 1)); + ImGui::Text(" S5,6: %d", GetActualPaletteSlot(i, 5)); + ImGui::Text(" S7: %d", GetActualPaletteSlot(i, 7)); } else { + ImGui::Text("Palette %d", i); if (is_current) { - ImGui::TextColored(ImVec4(0.3f, 0.8f, 0.3f, 1.0f), "✓ Active"); - } else { - ImGui::Text("Click to activate"); + ImGui::TextColored(ImVec4(0.3f, 0.8f, 0.3f, 1.0f), "Active"); } } ImGui::EndTooltip(); } } } - ImGui::EndGroup(); Separator(); - // Essential actions - Text("Actions:"); - if (Button("Clear", ImVec2(50, 0))) { + // Compact action buttons + if (Button("Clear", ImVec2(-1, 0))) { RETURN_IF_ERROR(ClearTile16()); } - SameLine(); - if (Button("Copy", ImVec2(50, 0))) { + + if (Button("Copy", ImVec2(-1, 0))) { RETURN_IF_ERROR(CopyTile16ToClipboard(current_tile16_)); } - if (Button("Paste", ImVec2(50, 0))) { + + if (Button("Paste", ImVec2(-1, 0))) { RETURN_IF_ERROR(PasteTile16FromClipboard()); } Separator(); - // Save/Discard changes - Text("Changes:"); - if (Button("Save", ImVec2(50, 0))) { + // Save/Discard - full width buttons + if (Button("Save Changes", ImVec2(-1, 0))) { RETURN_IF_ERROR(CommitChangesToOverworld()); } HOVER_HINT("Apply changes to overworld and regenerate blockset"); - SameLine(); - if (Button("Discard", ImVec2(50, 0))) { + + if (Button("Discard Changes", ImVec2(-1, 0))) { RETURN_IF_ERROR(DiscardChanges()); } HOVER_HINT("Reload tile16 from ROM, discarding local changes"); - bool can_undo = !undo_stack_.empty(); + Separator(); + bool can_undo = !undo_stack_.empty(); if (!can_undo) BeginDisabled(); - if (Button("Undo", ImVec2(50, 0))) { + if (Button("Undo", ImVec2(-1, 0))) { RETURN_IF_ERROR(Undo()); } if (!can_undo) @@ -1087,61 +1105,55 @@ absl::Status Tile16Editor::UpdateTile16Edit() { // Advanced controls (collapsible) if (show_advanced_controls) { - ImGui::Separator(); - ImGui::TextColored(ImVec4(0.8f, 0.9f, 1.0f, 1.0f), "Advanced Controls"); - - ImGui::BeginGroup(); - if (ImGui::Button("Palette Settings", ImVec2(120, 0))) { + Separator(); + Text("Advanced:"); + + if (Button("Palette Settings", ImVec2(-1, 0))) { show_palette_settings_ = !show_palette_settings_; } - - if (ImGui::Button("Manual Edit", ImVec2(120, 0))) { + + if (Button("Manual Edit", ImVec2(-1, 0))) { ImGui::OpenPopup("ManualTile8Editor"); } - - if (ImGui::Button("Refresh Blockset", ImVec2(120, 0))) { + + if (Button("Refresh Blockset", ImVec2(-1, 0))) { RETURN_IF_ERROR(RefreshTile16Blockset()); } - ImGui::EndGroup(); // Scratch space in compact form - ImGui::Text("Scratch Space:"); + Text("Scratch:"); DrawScratchSpace(); // Manual tile8 editor popup DrawManualTile8Inputs(); } - - // Debug information panel + + // Compact debug information panel if (show_debug_info) { - ImGui::Separator(); - ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.6f, 1.0f), "Debug Information"); - - // Current tile16 info - ImGui::BeginGroup(); - ImGui::Text("Current Tile16: %d (0x%02X)", current_tile16_, current_tile16_); - ImGui::Text("Current Tile8: %d", current_tile8_); - ImGui::Text("Selected Palette Button: %d", current_palette_); - + Separator(); + Text("Debug:"); + ImGui::TextDisabled("T16:%02X T8:%d Pal:%d", current_tile16_, + current_tile8_, current_palette_); + if (current_tile8_ >= 0) { int sheet_index = GetSheetIndexForTile8(current_tile8_); int actual_slot = GetActualPaletteSlot(current_palette_, sheet_index); - ImGui::Text("Tile8 Sheet Index: %d", sheet_index); - ImGui::Text("Actual Palette Slot: %d", actual_slot); + ImGui::TextDisabled("Sheet:%d Slot:%d", sheet_index, actual_slot); } - ImGui::EndGroup(); - - // Palette mapping table - if (ImGui::CollapsingHeader("Palette Mapping Reference")) { - if (ImGui::BeginTable("##PaletteMapping", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { - ImGui::TableSetupColumn("Button"); - ImGui::TableSetupColumn("Sheet 0,3,4"); - ImGui::TableSetupColumn("Sheet 1,2"); - ImGui::TableSetupColumn("Sheet 5,6"); - ImGui::TableSetupColumn("Sheet 7"); - ImGui::TableSetupColumn("Default"); + + // Compact palette mapping table + if (ImGui::CollapsingHeader("Palette Map", + ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::BeginChild("##PaletteMappingScroll", ImVec2(0, 120), true); + if (ImGui::BeginTable("##PalMap", 3, + ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | + ImGuiTableFlags_SizingFixedFit)) { + ImGui::TableSetupColumn("Btn", ImGuiTableColumnFlags_WidthFixed, 30); + ImGui::TableSetupColumn("S0,3-4", ImGuiTableColumnFlags_WidthFixed, + 50); + ImGui::TableSetupColumn("S1-2", ImGuiTableColumnFlags_WidthFixed, 50); ImGui::TableHeadersRow(); - + for (int i = 0; i < 8; ++i) { ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -1150,53 +1162,55 @@ absl::Status Tile16Editor::UpdateTile16Edit() { ImGui::Text("%d", GetActualPaletteSlot(i, 0)); ImGui::TableNextColumn(); ImGui::Text("%d", GetActualPaletteSlot(i, 1)); - ImGui::TableNextColumn(); - ImGui::Text("%d", GetActualPaletteSlot(i, 5)); - ImGui::TableNextColumn(); - ImGui::Text("%d", GetActualPaletteSlot(i, 7)); - ImGui::TableNextColumn(); - ImGui::Text("%d", GetActualPaletteSlot(i, 0)); } ImGui::EndTable(); } + ImGui::EndChild(); } - - // Color comparison section - if (ImGui::CollapsingHeader("Color Comparison")) { + + // Color preview - compact + if (ImGui::CollapsingHeader("Colors")) { if (overworld_palette_.size() >= 256) { - ImGui::Text("Overworld Palette Size: %zu colors", overworld_palette_.size()); - - // Show first few colors of current palette slot int actual_slot = GetActualPaletteSlotForCurrentTile16(); - ImGui::Text("Current Palette Slot Colors (starting at slot %d):", actual_slot); - - ImGui::BeginGroup(); - for (int i = 0; i < 16 && (actual_slot + i) < static_cast(overworld_palette_.size()); ++i) { + ImGui::Text("Slot %d:", actual_slot); + + for (int i = 0; + i < 8 && + (actual_slot + i) < static_cast(overworld_palette_.size()); + ++i) { int color_index = actual_slot + i; auto color = overworld_palette_[color_index]; ImVec4 display_color = color.rgb(); - - ImGui::ColorButton(absl::StrFormat("##color%d", i).c_str(), - display_color, ImGuiColorEditFlags_NoTooltip, ImVec2(16, 16)); + + ImGui::ColorButton(absl::StrFormat("##c%d", i).c_str(), + display_color, ImGuiColorEditFlags_NoTooltip, + ImVec2(20, 20)); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Slot %d: 0x%04X", color_index, color.snes()); + ImGui::SetTooltip("%d:0x%04X", color_index, color.snes()); } - - if ((i + 1) % 8 != 0) ImGui::SameLine(); + + if ((i + 1) % 4 != 0) + ImGui::SameLine(); } - ImGui::EndGroup(); - } else { - ImGui::TextDisabled("Overworld palette not available"); } } } + ImGui::EndGroup(); EndTable(); } - // Draw palette settings if enabled + // Draw palette settings and canvas popups DrawPaletteSettings(); + // Show canvas popup windows if opened from context menu + blockset_canvas_.ShowAdvancedCanvasProperties(); + blockset_canvas_.ShowScalingControls(); + tile8_source_canvas_.ShowAdvancedCanvasProperties(); + tile8_source_canvas_.ShowScalingControls(); + tile16_edit_canvas_.ShowAdvancedCanvasProperties(); + tile16_edit_canvas_.ShowScalingControls(); + return absl::OkStatus(); } @@ -1249,7 +1263,7 @@ absl::Status Tile16Editor::LoadTile8() { try { tile_bitmap.Create(8, 8, 8, tile_data); - + // Set default palette using the same system as overworld if (overworld_palette_.size() >= 256) { // Use complete 256-color palette (same as overworld system) @@ -1948,37 +1962,38 @@ int Tile16Editor::GetPaletteSlotForSheet(int sheet_index) const { } // NEW: Get the actual palette slot for a given palette button and sheet index -int Tile16Editor::GetActualPaletteSlot(int palette_button, int sheet_index) const { +int Tile16Editor::GetActualPaletteSlot(int palette_button, + int sheet_index) const { // Map palette buttons 0-7 to actual 256-color palette slots based on sheet type // Based on the correct 256-color palette structure from SetColorsPalette() // The 256-color palette is organized as a 16x16 grid (16 colors per row) - + switch (sheet_index) { - case 0: // Main blockset -> AUX1 region (right side, rows 2-4, cols 9-15) - case 3: - case 4: + case 0: // Main blockset -> AUX1 region (right side, rows 2-4, cols 9-15) + case 3: + case 4: // AUX1 palette: Row 2-4, cols 9-15 = slots 41-47, 57-63, 73-79 // Use row 2, col 9 + palette_button offset return 41 + palette_button; // Row 2, col 9 = slot 41 - - case 5: - case 6: // Area graphics -> AUX2 region (right side, rows 5-7, cols 9-15) + + case 5: + case 6: // Area graphics -> AUX2 region (right side, rows 5-7, cols 9-15) // AUX2 palette: Row 5-7, cols 9-15 = slots 89-95, 105-111, 121-127 - // Use row 5, col 9 + palette_button offset + // Use row 5, col 9 + palette_button offset return 89 + palette_button; // Row 5, col 9 = slot 89 - - case 1: - case 2: // Main graphics -> MAIN region (left side, rows 2-6, cols 1-7) + + case 1: + case 2: // Main graphics -> MAIN region (left side, rows 2-6, cols 1-7) // MAIN palette: Row 2-6, cols 1-7 = slots 33-39, 49-55, 65-71, 81-87, 97-103 // Use row 2, col 1 + palette_button offset return 33 + palette_button; // Row 2, col 1 = slot 33 - - case 7: // Animated tiles -> ANIMATED region (row 7, cols 1-7) + + case 7: // Animated tiles -> ANIMATED region (row 7, cols 1-7) // ANIMATED palette: Row 7, cols 1-7 = slots 113-119 return 113 + palette_button; // Row 7, col 1 = slot 113 - - default: - return 33 + palette_button; // Default to MAIN region + + default: + return 33 + palette_button; // Default to MAIN region } } @@ -1986,10 +2001,10 @@ int Tile16Editor::GetActualPaletteSlot(int palette_button, int sheet_index) cons int Tile16Editor::GetSheetIndexForTile8(int tile8_id) const { // Determine which graphics sheet a tile8 belongs to based on its position // This is based on the 256-tile per sheet organization - + constexpr int kTilesPerSheet = 256; // 16x16 tiles per sheet int sheet_index = tile8_id / kTilesPerSheet; - + // Clamp to valid sheet range (0-7) return std::min(7, std::max(0, sheet_index)); } @@ -1998,17 +2013,17 @@ int Tile16Editor::GetSheetIndexForTile8(int tile8_id) const { int Tile16Editor::GetActualPaletteSlotForCurrentTile16() const { // For the current tile16, we need to determine which sheet the tile8s belong to // and use the most appropriate palette region - - if (current_tile8_ >= 0 && current_tile8_ < static_cast(current_gfx_individual_.size())) { + + if (current_tile8_ >= 0 && + current_tile8_ < static_cast(current_gfx_individual_.size())) { int sheet_index = GetSheetIndexForTile8(current_tile8_); return GetActualPaletteSlot(current_palette_, sheet_index); } - + // Default to sheet 0 (main blockset) if no tile8 selected return GetActualPaletteSlot(current_palette_, 0); } - // Helper methods for palette management absl::Status Tile16Editor::UpdateTile8Palette(int tile8_id) { if (tile8_id < 0 || @@ -2046,15 +2061,16 @@ absl::Status Tile16Editor::UpdateTile8Palette(int tile8_id) { current_palette_ = 0; } - // Use the same palette system as the overworld (complete 256-color palette) - if (display_palette.size() >= 256) { - // Apply complete 256-color palette (same as overworld system) - // The pixel data already contains correct color indices for the 256-color palette - current_gfx_individual_[tile8_id].SetPalette(display_palette); - } else { - // For smaller palettes, use SetPaletteWithTransparent with current palette - current_gfx_individual_[tile8_id].SetPaletteWithTransparent(display_palette, current_palette_); - } + // // Use the same palette system as the overworld (complete 256-color palette) + // if (display_palette.size() >= 256) { + // // Apply complete 256-color palette (same as overworld system) + // // The pixel data already contains correct color indices for the 256-color palette + // current_gfx_individual_[tile8_id].SetPalette(display_palette); + // } else { + // For smaller palettes, use SetPaletteWithTransparent with current palette + current_gfx_individual_[tile8_id].SetPaletteWithTransparent(display_palette, + current_palette_); + // } current_gfx_individual_[tile8_id].set_modified(true); Renderer::Get().UpdateBitmap(¤t_gfx_individual_[tile8_id]); @@ -2104,13 +2120,15 @@ absl::Status Tile16Editor::RefreshAllPalettes() { // CRITICAL FIX: Use the same palette system as the overworld // The overworld system applies the complete 256-color palette to the main graphics bitmap // Individual tile8 graphics use the same palette but with proper color mapping - + if (current_gfx_bmp_.is_active()) { // Apply the complete 256-color palette to the source bitmap (same as overworld) current_gfx_bmp_.SetPalette(display_palette); current_gfx_bmp_.set_modified(true); core::Renderer::Get().UpdateBitmap(¤t_gfx_bmp_); - util::logf("Applied complete 256-color palette to source bitmap (same as overworld)"); + util::logf( + "Applied complete 256-color palette to source bitmap (same as " + "overworld)"); } // Update current tile16 being edited with complete 256-color palette @@ -2133,7 +2151,8 @@ absl::Status Tile16Editor::RefreshAllPalettes() { } util::logf( - "Successfully refreshed all palettes in tile16 editor using complete 256-color palette " + "Successfully refreshed all palettes in tile16 editor using complete " + "256-color palette " "(same as overworld system)"); return absl::OkStatus(); } diff --git a/src/app/editor/overworld/tile16_editor.h b/src/app/editor/overworld/tile16_editor.h index aab0c95b..2defad0c 100644 --- a/src/app/editor/overworld/tile16_editor.h +++ b/src/app/editor/overworld/tile16_editor.h @@ -246,7 +246,7 @@ class Tile16Editor : public gfx::GfxContext { // Canvas for editing the selected tile - optimized for 2x2 grid of 8x8 tiles (16x16 total) gui::Canvas tile16_edit_canvas_{"Tile16EditCanvas", ImVec2(64, 64), // Fixed 64x64 display size (16x16 pixels at 4x scale) - gui::CanvasGridSize::k8x8, 4.0F}; // 8x8 grid with 4x scale for clarity + gui::CanvasGridSize::k8x8, 8.0F}; // 8x8 grid with 4x scale for clarity gfx::Bitmap current_tile16_bmp_; // Tile8 canvas to get the tile to drawing in the tile16_edit_canvas_ diff --git a/src/app/gui/canvas.cc b/src/app/gui/canvas.cc index 1358d0f4..ec316cf9 100644 --- a/src/app/gui/canvas.cc +++ b/src/app/gui/canvas.cc @@ -384,8 +384,8 @@ void Canvas::DrawContextMenu() { refresh_graphics_ = false; util::logf("Applied interactive palette changes to canvas"); } - ImGui::EndChild(); } + ImGui::EndChild(); } else { ImGui::Text("Load ROM palettes first using Enhanced Palette Manager"); }