9#include "absl/status/status.h"
10#include "absl/strings/str_format.h"
27#include "imgui/imgui.h"
72 if (BeginPopup(
"##Tile16EditorContextMenu")) {
73 if (BeginMenu(tr(
"View"))) {
77 if (MenuItem(tr(
"Show Collision IDs"),
nullptr,
87 if (BeginMenu(tr(
"Edit"))) {
88 if (MenuItem(tr(
"Copy Current Tile16"),
"Ctrl+C")) {
91 if (MenuItem(tr(
"Paste to Current Tile16"),
"Ctrl+V")) {
95 if (MenuItem(tr(
"Flip Horizontal"),
"H")) {
98 if (MenuItem(tr(
"Flip Vertical"),
"V")) {
101 if (MenuItem(tr(
"Rotate"),
"R")) {
104 if (MenuItem(tr(
"Clear"),
"Delete")) {
110 if (BeginMenu(tr(
"Scratch Space"))) {
111 for (
int i = 0; i < 4; i++) {
112 std::string slot_name =
"Slot " + std::to_string(i + 1);
114 if (MenuItem((slot_name +
" (Load)").c_str())) {
117 if (MenuItem((slot_name +
" (Save)").c_str())) {
120 if (MenuItem((slot_name +
" (Clear)").c_str())) {
124 if (MenuItem((slot_name +
" (Save)").c_str())) {
138 absl::Status draw_status;
139 static bool show_advanced_controls =
false;
140 static bool show_debug_info =
false;
143 {{ImGuiStyleVar_FramePadding, ImVec2(8, 4)},
144 {ImGuiStyleVar_ItemSpacing, ImVec2(8, 4)}});
151 const int total_tiles =
153 const float layout_width = ImGui::GetContentRegionAvail().x;
154 const bool compact_layout = layout_width < 900.0f;
155 const int layout_columns = compact_layout ? 1 : 2;
157 if (ImGui::BeginTable(
158 "##Tile16EditLayout", layout_columns,
159 ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_SizingStretchProp)) {
160 if (compact_layout) {
161 ImGui::TableSetupColumn(
"Tile16 Workbench",
162 ImGuiTableColumnFlags_WidthStretch, 1.0f);
164 ImGui::TableSetupColumn(
"Tile8 Source",
165 ImGuiTableColumnFlags_WidthStretch, 0.58f);
166 ImGui::TableSetupColumn(
"Editor", ImGuiTableColumnFlags_WidthStretch,
170 if (compact_layout) {
171 ImGui::TableNextRow();
172 ImGui::TableNextColumn();
177 show_debug_info, show_advanced_controls));
180 ImGui::TableNextRow();
181 ImGui::TableNextColumn();
187 ImGui::TableNextRow();
188 ImGui::TableNextColumn();
194 ImGui::TableNextColumn();
199 show_debug_info, show_advanced_controls));
222 std::max(0, total_tiles - 1));
225 ImGui::SetNextItemWidth(80);
235 if (ImGui::IsItemHovered()) {
236 ImGui::SetTooltip(tr(
"Tile ID (0-%d) - navigates as you type"),
246 if (ImGui::Button(
"<<")) {
250 if (ImGui::IsItemHovered())
251 ImGui::SetTooltip(tr(
"First page"));
254 if (ImGui::Button(
"<")) {
255 int new_tile = std::max(
260 if (ImGui::IsItemHovered())
261 ImGui::SetTooltip(tr(
"Previous page (PageUp)"));
268 if (ImGui::Button(
">")) {
270 std::min(total_tiles - 1,
275 if (ImGui::IsItemHovered())
276 ImGui::SetTooltip(tr(
"Next page (PageDown)"));
279 if (ImGui::Button(
">>")) {
283 if (ImGui::IsItemHovered())
284 ImGui::SetTooltip(tr(
"Last page"));
286 if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)) {
287 if (ImGui::IsKeyPressed(ImGuiKey_PageUp)) {
288 int new_tile = std::max(
293 if (ImGui::IsKeyPressed(ImGuiKey_PageDown)) {
300 if (ImGui::IsKeyPressed(ImGuiKey_Home)) {
304 if (ImGui::IsKeyPressed(ImGuiKey_End)) {
309 if (!ImGui::GetIO().KeyCtrl) {
310 if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow) &&
315 if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) &&
320 if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) &&
326 if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) &&
336 return absl::OkStatus();
340 bool show_debug_info,
bool show_advanced_controls) {
341 absl::Status draw_status;
344 if (ImGui::BeginChild(
345 "##Tile16FixedCanvas", ImVec2(preview_size + 26, preview_size + 26),
347 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
350 tile16_edit_frame_opts.
canvas_size = ImVec2(preview_size, preview_size);
360 auto tile16_edit_rt =
389 preview_data.begin());
403 if (display_palette && !display_palette->
empty()) {
414 for (
int y = 0; y < 8; ++y) {
415 for (
int x = 0; x < 4; ++x) {
416 std::swap(data[y * 8 + x], data[y * 8 + (7 - x)]);
422 for (
int y = 0; y < 4; ++y) {
423 for (
int x = 0; x < 8; ++x) {
424 std::swap(data[y * 8 + x], data[(7 - y) * 8 + x]);
435 const auto preview_command =
444 if (tile16_edit_rt.hovered &&
447 const auto mouse = ImGui::GetIO().MousePos;
452 draw_status.Update(preview_status);
453 if (preview_status.ok()) {
464 const bool left_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Left);
465 const bool right_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Right);
467 if (left_clicked || right_clicked) {
468 const ImGuiIO& io = ImGui::GetIO();
471 ImVec2(io.MousePos.x - canvas_pos.x, io.MousePos.y - canvas_pos.y);
475 const ImVec2 tile_position =
477 int tile_x =
static_cast<int>(tile_position.x);
478 int tile_y =
static_cast<int>(tile_position.y);
481 tile_x = std::max(0, std::min(15, tile_x));
482 tile_y = std::max(0, std::min(15, tile_y));
485 "Tile16 canvas click: (%.2f, %.2f) -> Tile16: (%d, %d), mode=%s",
486 mouse_pos.x, mouse_pos.y, tile_x, tile_y,
490 ImVec2(tile_x, tile_y), left_clicked, right_clicked));
510 ImGui::Image((ImTextureID)(intptr_t)tile8_texture, ImVec2(24, 24));
515 ImGui::TextDisabled(tr(
"G%d"), sheet_idx);
516 if (ImGui::IsItemHovered()) {
517 ImGui::BeginTooltip();
518 ImGui::Text(tr(
"Graphics chunk: %d"), sheet_idx);
544 "1x: paint one quadrant\n2x: fill current tile16 from a 2x2 tile8 "
545 "block\n4x: stamp a 2x2 tile16 patch from a 4x4 tile8 block");
547 Text(tr(
"Edit Mode:"));
548 if (ImGui::RadioButton(tr(
"Paint (P)"),
553 if (ImGui::RadioButton(tr(
"Pick (I)"),
558 if (ImGui::RadioButton(tr(
"Usage (U)"),
564 "Paint: left-click places Tile8 into Tile16.\n"
565 "Pick: left-click samples Tile8 from Tile16.\n"
566 "Usage: keeps usage overlay visible and samples on click.\n"
567 "Right-click on Tile16 preview always samples.");
571 if (ImGui::CollapsingHeader(tr(
"Usage Navigator"),
572 ImGuiTreeNodeFlags_DefaultOpen)) {
573 const float nav_height = 220.0f;
574 if (BeginChild(
"##UsageBlocksetNav", ImVec2(0, nav_height),
true,
575 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
590 if (result.selection_changed) {
610 if (show_advanced_controls) {
612 Text(tr(
"Advanced:"));
614 if (Button(tr(
"Palette Settings"), ImVec2(-1, 0))) {
619 if (Button(tr(
"Analyze Data"), ImVec2(-1, 0))) {
622 HOVER_HINT(
"Analyze tile8 source data format and palette state");
624 if (Button(tr(
"Manual Edit"), ImVec2(-1, 0))) {
625 ImGui::OpenPopup(
"ManualTile8Editor");
628 if (Button(tr(
"Refresh Blockset"), ImVec2(-1, 0))) {
633 Text(tr(
"Scratch:"));
641 if (show_debug_info) {
651 ImGui::TextDisabled(tr(
"Sheet:%d Slot:%d"), sheet_index, actual_slot);
655 if (ImGui::CollapsingHeader(tr(
"Palette Map"),
656 ImGuiTreeNodeFlags_DefaultOpen)) {
657 ImGui::BeginChild(
"##PaletteMappingScroll", ImVec2(0, 120),
true);
658 if (ImGui::BeginTable(
"##PalMap", 2,
659 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
660 ImGuiTableFlags_SizingFixedFit)) {
661 ImGui::TableSetupColumn(
"Btn", ImGuiTableColumnFlags_WidthFixed, 30);
662 ImGui::TableSetupColumn(
"CGRAM", ImGuiTableColumnFlags_WidthFixed, 70);
663 ImGui::TableHeadersRow();
665 for (
int i = 0; i < 8; ++i) {
666 ImGui::TableNextRow();
667 ImGui::TableNextColumn();
668 ImGui::Text(
"%d", i);
669 ImGui::TableNextColumn();
678 if (ImGui::CollapsingHeader(tr(
"Colors"))) {
681 ImGui::Text(tr(
"Slot %d:"), actual_slot);
684 i < 8 && (actual_slot + i) <
687 int color_index = actual_slot + i;
689 ImVec4 display_color = color.rgb();
691 ImGui::ColorButton(absl::StrFormat(
"##c%d", i).c_str(), display_color,
692 ImGuiColorEditFlags_NoTooltip, ImVec2(20, 20));
693 if (ImGui::IsItemHovered()) {
694 ImGui::SetTooltip(tr(
"%d:0x%04X"), color_index, color.snes());
697 if ((i + 1) % 4 != 0)
708 bool* show_debug_info,
bool* show_advanced_controls) {
709 const float available_width = ImGui::GetContentRegionAvail().x;
710 const int action_count = 4;
713 const float row_height = ImGui::GetTextLineHeightWithSpacing() +
714 rows * ImGui::GetFrameHeightWithSpacing() +
715 ImGui::GetStyle().FramePadding.y * 2.0f;
716 absl::Status action_status = absl::OkStatus();
718 if (ImGui::BeginChild(
719 "##Tile16CompactStatus", ImVec2(0, row_height),
false,
720 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
723 ImGui::TextDisabled(
"| %s | Live edits",
725 if (ImGui::IsItemHovered()) {
727 "Edits update every use of this Tile16 in the document.\n"
728 "Use Undo/Redo to revise edits and Save to write the ROM.");
731 if (ImGui::BeginTable(
"##Tile16CompactActions", columns,
732 ImGuiTableFlags_SizingStretchProp)) {
733 int action_index = 0;
734 auto next_action_cell = [&]() {
735 if (action_index % columns == 0) {
736 ImGui::TableNextRow();
738 ImGui::TableNextColumn();
744 ImGui::BeginDisabled();
746 if (ImGui::Button(tr(
"Undo"),
747 ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
748 action_status =
Undo();
751 ImGui::EndDisabled();
756 ImGui::BeginDisabled();
758 if (ImGui::Button(tr(
"Redo"),
759 ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
760 action_status =
Redo();
763 ImGui::EndDisabled();
767 const char* advanced_label = *show_advanced_controls
768 ?
"Advanced*##Tile16AdvancedToggle"
769 :
"Advanced##Tile16AdvancedToggle";
770 if (ImGui::Button(advanced_label,
771 ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
772 *show_advanced_controls = !*show_advanced_controls;
776 const char* debug_label = *show_debug_info ?
"Debug*##Tile16DebugToggle"
777 :
"Debug##Tile16DebugToggle";
778 if (ImGui::Button(debug_label,
779 ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
780 *show_debug_info = !*show_debug_info;
787 return action_status;
791 bool show_debug_info) {
792 absl::Status draw_status;
794 Text(tr(
"Brush Palette:"));
795 if (show_debug_info) {
798 ImGui::TextDisabled(tr(
"(Slot %d)"), actual_slot);
800 ImGui::TextDisabled(tr(
"Used for new tile8 placements"));
804 float available_width = ImGui::GetContentRegionAvail().x;
814 for (
int row = 0; row < 2; ++row) {
815 for (
int col = 0; col < 4; ++col) {
819 int i = row * 4 + col;
826 is_current ? selected_button_colors : default_button_colors;
827 const ImVec4 palette_button_border =
831 {{ImGuiCol_Button, palette_button_colors.
button},
832 {ImGuiCol_ButtonHovered, palette_button_colors.
hovered},
833 {ImGuiCol_ButtonActive, palette_button_colors.
active},
834 {ImGuiCol_Border, palette_button_border}});
838 if (ImGui::Button(absl::StrFormat(
"%d", i).c_str(),
839 ImVec2(button_size, button_size))) {
845 status.message().data());
847 util::logf(
"Palette successfully changed to %d",
858 std::vector<gui::MenuItemSpec> items;
859 items.emplace_back(
"Use Palette",
ICON_MD_BRUSH, [
this, i]() {
867 status.message().data());
872 [
this, i, &draw_status]() {
876 items.back().tooltip =
"Sets all four quadrants to this palette.";
877 items.back().separator_after =
true;
883 c < slot + 16 && c < static_cast<int>(ow_palette.size()); ++c) {
884 colors += absl::StrFormat(
"%s$%04X", colors.empty() ?
"" :
",",
885 ow_palette[c].snes());
889 copy_item.enabled_condition = [has = !colors.empty()]() {
892 items.push_back(std::move(copy_item));
897 if (ImGui::IsItemHovered()) {
898 ImGui::BeginTooltip();
899 if (show_debug_info) {
900 ImGui::Text(tr(
"Palette %d -> CGRAM 0x%02X"), i,
903 ImGui::Text(tr(
"Brush Palette %d"), i);
904 ImGui::TextDisabled(tr(
"Applied to new tile8 placements"));
905 ImGui::TextDisabled(tr(
"Right-click: palette actions"));
906 ImGui::TextDisabled(tr(
"Quadrant metadata is shown in strip below"));
908 tr(
"Hotkeys: Ctrl+1..8 palette, 1..4 quadrant focus"));
921 Text(tr(
"Quadrant Focus:"));
923 ImGui::TextDisabled(
"1-4");
924 static constexpr std::array<const char*, 4> kQuadrantLabels = {
"TL",
"TR",
926 const float quadrant_button_width = std::max(58.0f, button_size + 24.0f);
929 for (
int q = 0; q < 4; ++q) {
935 const uint8_t quadrant_palette = info.
palette_;
937 const bool matches_brush =
940 ImGui::PushID(100 + q);
942 is_active_quadrant ? active_quadrant_colors
943 : (matches_brush ? selected_button_colors
944 : default_button_colors);
945 const ImVec4 quadrant_border =
951 {{ImGuiCol_Button, quadrant_button_colors.
button},
952 {ImGuiCol_ButtonHovered, quadrant_button_colors.
hovered},
953 {ImGuiCol_ButtonActive, quadrant_button_colors.
active},
954 {ImGuiCol_Border, quadrant_border}});
956 is_active_quadrant ? 2.0f : 1.0f);
958 if (ImGui::Button(absl::StrFormat(
"%d %s:%d", q + 1, kQuadrantLabels[q],
961 ImVec2(quadrant_button_width, 0))) {
968 status.message().data());
973 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
979 if (ImGui::IsItemHovered()) {
980 ImGui::BeginTooltip();
981 ImGui::Text(tr(
"Quadrant %s metadata"), kQuadrantLabels[q]);
983 ImGui::Text(tr(
"Tile8: %02X"), info.
id_);
984 ImGui::Text(tr(
"Palette: %d"), quadrant_palette);
987 ImGui::Text(tr(
"Priority: %s"), info.
over_ ?
"Y" :
"N");
988 ImGui::TextDisabled(tr(
"LMB: set brush palette from this quadrant"));
990 tr(
"RMB: apply current brush palette to this quadrant"));
991 ImGui::TextDisabled(tr(
"Keys 1..4: focus TL/TR/BL/BR"));
1000 ImGui::TextDisabled(tr(
"Active %s: Tile8 %02X | P%d | H:%s V:%s | Pri:%s"),
1005 active_info.
over_ ?
"Y" :
"N");
1007 if (Button(tr(
"Apply Brush to Active Quadrant"), ImVec2(-1, 0))) {
1012 "Copy the Brush Palette into the selected quadrant metadata.\n"
1013 "Use keys 1..4 to change active quadrant quickly.");
1017 if (Button(tr(
"Apply Brush to All Quadrants"), ImVec2(-1, 0))) {
1021 "Copy the Brush Palette into Tile Palette metadata for all 4 "
1023 "Tip: right-click any brush palette button above for a one-step apply.");
1028 absl::Status draw_status;
1031 ImGui::Text(tr(
"Tile8 Source"));
1033 ImGui::Checkbox(tr(
"Grid##Tile16GridToggle"),
1036 if (ImGui::Checkbox(tr(
"IDs##Tile16CollisionIds"),
1042 int source_bitmap_height = 0;
1062 ImGui::GetContentRegionAvail().y, source_bitmap_height,
1065 if (BeginChild(
"##Tile8SourceScrollable", ImVec2(0, panel_height),
true,
1066 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
1081 const bool left_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Left);
1082 const bool right_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Right);
1086 ImGui::IsItemHovered(), ImGui::IsMouseDown(ImGuiMouseButton_Right));
1091 if (left_clicked || right_clicked) {
1109 float display_scale) {
1110 const ImGuiIO& io = ImGui::GetIO();
1113 ImVec2(io.MousePos.x - canvas_pos.x, io.MousePos.y - canvas_pos.y);
1120 return absl::OkStatus();
1125 if (right_clicked) {
1130 return absl::OkStatus();
1135 if (Button(tr(
"Clear"), ImVec2(-1, 0))) {
1139 if (Button(tr(
"Copy"), ImVec2(-1, 0))) {
1143 if (Button(tr(
"Paste"), ImVec2(-1, 0))) {
1147 return absl::OkStatus();
1158 if (!cache_status.ok()) {
1159 util::logf(
"Tile8 usage cache rebuild failed: %s",
1160 cache_status.message().data());
1170 ImDrawList* draw_list = ImGui::GetWindowDrawList();
1173 const float tile16_display = 32.0f * scale;
1174 const float quadrant_display = 16.0f * scale;
1175 const int tiles_per_row =
1178 for (
const auto& hit : hits) {
1179 const int tile_x = hit.tile16_id % tiles_per_row;
1180 const int tile_y = hit.tile16_id / tiles_per_row;
1181 const int quad_x = hit.quadrant % 2;
1182 const int quad_y = hit.quadrant / 2;
1185 canvas_pos.x + (tile_x * tile16_display) + (quad_x * quadrant_display),
1186 canvas_pos.y + (tile_y * tile16_display) + (quad_y * quadrant_display));
1187 const ImVec2 max(min.x + quadrant_display, min.y + quadrant_display);
1190 draw_list->AddRectFilled(min, max, IM_COL32(150, 0, 210, 80));
1191 draw_list->AddRect(min, max, IM_COL32(215, 170, 255, 180));
1197 if (Begin(
"Advanced Palette Settings",
1199 Text(tr(
"Pixel Normalization & Color Correction:"));
1202 if (SliderInt(tr(
"Normalization Mask"), &mask_value, 1, 255,
"0x%02X")) {
1204 static_cast<uint8_t
>(mask_value);
1207 Checkbox(tr(
"Auto Normalize Pixels"),
1210 if (Button(tr(
"Apply to All Graphics"))) {
1212 if (!reload_result.ok()) {
1213 Text(tr(
"Error: %s"), reload_result.message().data());
1218 if (Button(tr(
"Reset Defaults"))) {
1222 (void)reload_result;
1226 Text(tr(
"Current State:"));
1227 static constexpr std::array<const char*, 7> palette_group_names = {
1228 "OW Main",
"OW Aux",
"OW Anim",
"Dungeon",
1229 "Sprites",
"Armor",
"Sword"};
1237 Text(tr(
"Sheet-Specific Fixes:"));
1240 static bool fix_sheet_0 =
true;
1241 static bool fix_sprite_sheets =
true;
1242 static bool use_transparent_for_terrain =
false;
1244 if (Checkbox(tr(
"Fix Sheet 0 (Trees)"), &fix_sheet_0)) {
1246 if (!reload_result.ok()) {
1247 Text(tr(
"Error reloading: %s"), reload_result.message().data());
1251 "Use direct palette for sheet 0 instead of transparent palette");
1253 if (Checkbox(tr(
"Fix Sprite Sheets"), &fix_sprite_sheets)) {
1255 if (!reload_result.ok()) {
1256 Text(tr(
"Error reloading: %s"), reload_result.message().data());
1259 HOVER_HINT(
"Use direct palette for sprite graphics sheets");
1261 if (Checkbox(tr(
"Transparent for Terrain"),
1262 &use_transparent_for_terrain)) {
1264 if (!reload_result.ok()) {
1265 Text(tr(
"Error reloading: %s"), reload_result.message().data());
1268 HOVER_HINT(
"Force transparent palette for terrain graphics");
1271 Text(tr(
"Color Analysis:"));
1276 Text(tr(
"Selected Tile8 Analysis:"));
1277 const auto& tile_data =
1279 std::map<uint8_t, int> pixel_counts;
1280 for (uint8_t pixel : tile_data) {
1281 pixel_counts[pixel & 0x0F]++;
1284 Text(tr(
"Pixel Value Distribution:"));
1285 for (
const auto& pair : pixel_counts) {
1286 int value = pair.first;
1287 int count = pair.second;
1288 Text(tr(
" Value %d (0x%X): %d pixels"), value, value, count);
1291 Text(tr(
"Palette Colors Used:"));
1294 if (analysis_palette ==
nullptr || analysis_palette->
empty()) {
1299 for (
const auto& pair : pixel_counts) {
1300 int value = pair.first;
1301 int count = pair.second;
1302 if (value <
static_cast<int>(analysis_palette->
size())) {
1303 auto color = (*analysis_palette)[value];
1304 ImVec4 display_color = color.rgb();
1305 ImGui::ColorButton((
"##analysis" + std::to_string(value)).c_str(),
1306 display_color, ImGuiColorEditFlags_NoTooltip,
1308 if (ImGui::IsItemHovered()) {
1309 ImGui::SetTooltip(tr(
"Index %d: 0x%04X (%d pixels)"), value,
1310 color.snes(), count);
1320 if (CollapsingHeader(tr(
"ROM Palette Manager")) &&
session_.
rom()) {
1321 Text(tr(
"Experimental ROM Palette Selection:"));
1323 "Use ROM palettes to experiment with different color schemes");
1325 if (Button(tr(
"Open Enhanced Palette Editor"))) {
1329 if (Button(tr(
"Show Color Analysis"))) {
1334 static int quick_group = 0;
1335 static int quick_index = 0;
1337 SliderInt(tr(
"ROM Group"), &quick_group, 0, 6);
1338 SliderInt(tr(
"Palette Index"), &quick_index, 0, 7);
1340 if (Button(tr(
"Apply to Tile8 Source"))) {
1342 util::logf(
"Applied ROM palette group %d, index %d to Tile8 source",
1343 quick_group, quick_index);
1347 if (Button(tr(
"Apply to Tile16 Editor"))) {
1350 "Applied ROM palette group %d, index %d to Tile16 editor",
1351 quick_group, quick_index);
1361 Text(tr(
"Layout Scratch:"));
1362 for (
int i = 0; i < 4; ++i) {
1364 std::string slot_name =
"S" + std::to_string(i + 1);
1366 if (Button((slot_name +
" Save").c_str(), ImVec2(70, 20))) {
1373 ImGui::BeginDisabled();
1375 if (Button((slot_name +
" Load").c_str(), ImVec2(70, 20)) && can_load) {
1379 ImGui::EndDisabled();
1388 if (ImGui::BeginPopupModal(
"ManualTile8Editor",
nullptr,
1389 ImGuiWindowFlags_AlwaysAutoResize)) {
1390 ImGui::Text(
"Tile16 0x%03X — quadrant properties",
1393 bool changed =
false;
1394 const char* names[] = {
"Top left",
"Top right",
"Bottom left",
1396 for (
int q = 0; q < 4; ++q) {
1398 ImGui::SeparatorText(names[q]);
1399 auto& info = TileInfoForQuadrant(&edited, q);
1401 if (ImGui::InputInt(
"Tile8", &
id)) {
1402 info.id_ = std::clamp(
id, 0, 1023);
1405 int palette = info.palette_;
1406 if (ImGui::SliderInt(
"Palette", &palette, 0, 7)) {
1407 info.palette_ = palette;
1410 changed |= ImGui::Checkbox(
"X Flip", &info.horizontal_mirror_);
1412 changed |= ImGui::Checkbox(
"Y Flip", &info.vertical_mirror_);
1414 changed |= ImGui::Checkbox(
"Priority", &info.over_);
1419 if (ImGui::Button(
"Close"))
1420 ImGui::CloseCurrentPopup();
void set_tile8_stamp_size(int size)
int current_tile8() const
void set_scroll_to_current(bool scroll)
gfx::Bitmap * tile16_blockset_bmp()
bool * mutable_show_tile_grid()
gfx::Tilemap * tile16_blockset()
int current_tile16() const
float tile8_source_display_scale() const
uint8_t * mutable_palette_normalization_mask()
int current_palette_group() const
float * mutable_tile8_source_display_scale()
const gfx::SnesPalette & overworld_palette() const
static constexpr int kTilesPerRow
bool * mutable_show_tile_collision_ids()
Tile16ScratchData & scratch_slot(int i)
absl::Status BuildStampPreview(Tile16LocalPos pos, gfx::Bitmap *output) const
void set_current_palette(int palette)
bool tile8_usage_cache_dirty() const
void set_tile8_usage_cache_dirty(bool dirty)
bool * mutable_highlight_tile8_usage()
gfx::Tile16 & current_tile16_data()
bool * mutable_auto_normalize_pixels()
gfx::Bitmap * current_gfx_bmp()
int current_palette() const
absl::Status RebuildTile8UsageCache()
Tile16EditMode edit_mode() const
int active_quadrant() const
void set_current_tile8(int id)
Tile16LayoutScratch & layout_scratch(int i)
void set_current_page(int page)
bool * mutable_priority_tile()
gfx::Bitmap & mutable_tile8_preview_bitmap()
bool highlight_tile8_usage() const
uint8_t palette_normalization_mask() const
int * mutable_jump_to_tile_id()
bool * mutable_show_palette_settings()
std::vector< zelda3::Tile8PixelData > & mutable_current_gfx_individual()
int jump_to_tile_id() const
bool show_palette_settings() const
const gfx::SnesPalette & palette() const
bool show_tile_collision_ids() const
bool show_tile_grid() const
const gfx::SnesPalette * ResolveDisplayPalette() const
static constexpr int kTilesPerPage
int tile8_stamp_size() const
const zelda3::Tile8UsageIndex & tile8_usage_cache() const
void set_active_quadrant(int quadrant)
gfx::Bitmap & current_tile16_bmp()
void set_edit_mode(Tile16EditMode mode)
absl::Status SaveTile16ToScratchSpace(int slot)
absl::Status LoadLayoutFromScratch(int slot)
static ImVec2 Tile16PreviewDisplayPixelToTilePosition(const ImVec2 &display_position)
absl::Status DrawTile16EditorWorkbenchColumn(bool show_debug_info, bool show_advanced_controls)
absl::Status HandleTile16CanvasClick(const ImVec2 &tile_position, bool left_click, bool right_click)
absl::Status ClearTile16()
absl::Status ReplaceCurrentTile(const gfx::Tile16 &data)
void AnalyzeTile8SourceData() const
absl::Status ApplyPaletteToAll(uint8_t palette_id)
int GetActualPaletteSlotForCurrentTile16() const
absl::Status HandleTile8SourceSelection(bool right_clicked, float display_scale)
absl::Status PasteTile16FromClipboard()
gui::TileSelectorWidget blockset_selector_
absl::Status SaveLayoutToScratch(int slot)
absl::Status LoadTile16FromScratchSpace(int slot)
gfx::SnesPalette CreateRemappedPaletteForViewing(const gfx::SnesPalette &source, int target_row) const
int GetActualPaletteSlot(int palette_button, int sheet_index) const
absl::Status FlipTile16Horizontal()
absl::Status UpdateTile16Edit()
absl::Status DrawBrushAndTilePaletteControls(bool show_debug_info)
void DrawTile8UsageOverlay()
gfx::Bitmap stamp_preview_bitmap_
absl::Status DrawTile8SourcePanel(float preferred_height=0.0f)
absl::Status UpdateTile8Palette(int tile8_id)
absl::Status RefreshTile16Blockset()
void DrawManualTile8Inputs()
absl::Status RefreshAllPalettes()
void DrawPaletteSettings()
absl::Status ApplyPaletteToQuadrant(int quadrant, uint8_t palette_id)
absl::Status DrawTile16NavigationHeader(int total_tiles)
absl::Status RotateTile16()
gui::Canvas tile16_edit_canvas_
gui::Canvas blockset_canvas_
gui::Canvas tile8_source_canvas_
gfx::Tile16 * GetCurrentTile16Data()
absl::Status DrawCompactActionStatusRow(bool *show_debug_info, bool *show_advanced_controls)
void RequestTileSwitch(int target_tile_id)
absl::Status CopyTile16ToClipboard(int tile_id)
absl::Status DrawPrimaryActionControls()
absl::Status ClearScratchSpace(int slot)
int GetSheetIndexForTile8(int tile8_id) const
absl::Status FlipTile16Vertical()
Tile16EditSession session_
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
const SnesPalette & palette() const
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
TextureHandle texture() const
void UpdateSurfacePixels()
Update SDL surface with current pixel data from data_ vector Call this after modifying pixel data via...
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
std::vector< uint8_t > & mutable_data()
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
Tile composition of four 8x8 tiles.
SNES 16-bit tile metadata container.
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
void ShowScalingControls()
void ShowAdvancedCanvasProperties()
bool DrawTileSelector(int size, int size_y=0)
auto custom_labels_enabled()
void SetCanvasSize(ImVec2 canvas_size)
void set_draggable(bool draggable)
float GetGlobalScale() const
bool ApplyROMPalette(int group_index, int palette_index)
static void SectionHeader(const char *label)
RAII guard for ImGui style colors.
RAII guard for ImGui style vars.
#define ICON_MD_FORMAT_COLOR_FILL
#define HOVER_HINT(string)
const char * EditModeLabel(Tile16EditMode mode)
float ComputeTile8SourcePanelHeight(float available_height_px, int source_bitmap_height_px, float display_scale, float preferred_height_px=0.0f, float min_height_px=220.0f, float max_height_px=520.0f)
constexpr int kTile8PixelCount
int ComputeTile16CompactActionColumnCount(float available_width_px)
constexpr float kTile8DisplayScale
float ComputeTile16PaletteButtonSize(float available_width_px)
bool ComputeTile8UsageHighlight(bool source_hovered, bool right_mouse_down)
int ComputeTile16ActionRowCount(int action_count, int column_count)
constexpr int kTile16Size
int ComputeTile8IndexFromCanvasMouse(float mouse_x, float mouse_y, int source_bitmap_width_px, int max_tile_count, float display_scale)
float ComputeTile8SourceDisplayScale(float available_width_px, int source_bitmap_width_px, float min_scale=1.0f, float max_scale=4.0f)
void EndCanvas(Canvas &canvas)
void BeginCanvas(Canvas &canvas, ImVec2 child_size)
bool ItemContextMenu(const char *id, const std::function< std::vector< MenuItemSpec >()> &build, ImGuiPopupFlags flags)
Right-click menu for the last submitted ImGui item.
ButtonColorSet GetSuccessButtonColors()
MenuItemSpec CopyToClipboardItem(const std::string &label, const std::string &text)
Color GetThemeColor(const std::string &color_name)
void ThemedText(const char *text, SemanticColor color)
ButtonColorSet GetPrimaryButtonColors()
void logf(const absl::FormatSpec< Args... > &format, Args &&... args)
gfx::TileInfo & MutableTile16QuadrantInfo(gfx::Tile16 &tile, int quadrant)
void SyncTile16TilesInfo(gfx::Tile16 *tile)
int ComputeTile16Count(const gfx::Tilemap *tile16_blockset)
const gfx::TileInfo & Tile16QuadrantInfo(const gfx::Tile16 &tile, int quadrant)
constexpr int kMaxTile8UsageId
#define RETURN_IF_ERROR(expr)
std::optional< float > grid_step