8#include "absl/strings/str_cat.h"
9#include "absl/strings/str_format.h"
10#include "absl/strings/str_split.h"
11#include "absl/strings/strip.h"
19#include "imgui/imgui.h"
35 token = std::string(absl::StripAsciiWhitespace(token));
37 return absl::InvalidArgumentError(
"Empty color token");
40 if (token[0] ==
'$') {
42 }
else if (token.size() > 2 && (token.rfind(
"0x", 0) == 0 ||
43 token.rfind(
"0X", 0) == 0)) {
48 return absl::InvalidArgumentError(
"Color token is missing hex digits");
51 for (
char ch : token) {
52 if (!std::isxdigit(
static_cast<unsigned char>(ch))) {
53 return absl::InvalidArgumentError(
54 absl::StrCat(
"Invalid hex digit in color token: ", token));
58 if (token.size() > 4) {
59 return absl::InvalidArgumentError(
60 absl::StrCat(
"Color token is too long for SNES color: ", token));
65 value =
static_cast<uint32_t
>(std::stoul(token,
nullptr, 16));
66 }
catch (
const std::exception&) {
67 return absl::InvalidArgumentError(
68 absl::StrCat(
"Failed to parse color token: ", token));
72 return absl::InvalidArgumentError(
73 absl::StrCat(
"SNES color out of range (0x0000-0x7FFF): ", token));
76 return static_cast<uint16_t
>(value);
80 const std::string& clipboard) {
81 std::vector<uint16_t> colors;
82 for (
const auto& raw_token : absl::StrSplit(
83 clipboard, absl::ByAnyChar(
", \n\r\t"), absl::SkipEmpty())) {
84 const std::string token = std::string(absl::StripAsciiWhitespace(raw_token));
90 return color_or.status();
92 colors.push_back(*color_or);
96 return absl::InvalidArgumentError(
"No colors found in clipboard data");
102#if defined(YAZE_WITH_JSON)
103absl::StatusOr<uint16_t> ParseSnesColorJson(
const yaze::Json& value) {
105 return ParseSnesHexToken(value.
get<std::string>());
107 if (value.is_number_integer()) {
108 int parsed = value.
get<
int>();
109 if (parsed < 0 || parsed > 0x7FFF) {
110 return absl::InvalidArgumentError(
111 absl::StrFormat(
"SNES color out of range: %d", parsed));
113 return static_cast<uint16_t
>(parsed);
115 if (value.is_number_unsigned()) {
116 uint32_t parsed = value.
get<uint32_t>();
117 if (parsed > 0x7FFF) {
118 return absl::InvalidArgumentError(
119 absl::StrFormat(
"SNES color out of range: %u", parsed));
121 return static_cast<uint16_t
>(parsed);
123 return absl::InvalidArgumentError(
124 "Invalid color value type (expected string or number)");
131 const std::string& display_name,
Rom* rom,
133 : group_name_(group_name),
134 display_name_(display_name),
136 game_data_(game_data) {
157 if (ImGui::BeginTable(
158 "##PalettePanelLayout", 2,
159 ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) {
160 ImGui::TableSetupColumn(
"Grid", ImGuiTableColumnFlags_WidthStretch, 0.6f);
161 ImGui::TableSetupColumn(
"Editor", ImGuiTableColumnFlags_WidthStretch, 0.4f);
163 ImGui::TableNextRow();
164 ImGui::TableNextColumn();
171 ImGui::TableNextColumn();
181 ImGui::TextDisabled(
"Select a color to edit");
201 ImGui::BeginDisabled(!has_changes);
213 ImGui::EndDisabled();
215 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
216 ImGui::SetTooltip(
"No palette changes to save");
222 ImGui::BeginDisabled(!has_changes);
226 ImGui::EndDisabled();
228 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
229 ImGui::SetTooltip(
"No palette changes to discard");
236 size_t modified_count = 0;
239 for (
int p = 0; p < group->size(); p++) {
245 ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f),
"%s %zu modified",
250 ImGui::Dummy(ImVec2(20, 0));
255 ImGui::BeginDisabled(!can_undo);
259 ImGui::EndDisabled();
261 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
262 ImGui::SetTooltip(
"Nothing to undo");
267 ImGui::BeginDisabled(!can_redo);
271 ImGui::EndDisabled();
273 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
274 ImGui::SetTooltip(
"Nothing to redo");
278 ImGui::Dummy(ImVec2(20, 0));
293 ImGui::OpenPopup(
"BatchOperations");
305 int num_palettes = palette_group->size();
307 ImGui::Text(
"Palette:");
311 if (ImGui::BeginCombo(
314 const float item_height = ImGui::GetTextLineHeightWithSpacing();
315 ImGuiListClipper clipper;
316 clipper.Begin(num_palettes, item_height);
321 while (clipper.Step()) {
322 for (
int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i) {
326 std::string label = absl::StrFormat(
"Palette %d", i);
331 if (ImGui::Selectable(label.c_str(), is_selected)) {
336 ImGui::SetItemDefaultFocus();
349 ImGui::EndDisabled();
367 if (ImGui::ColorPicker4(
"##picker", &col.x,
368 ImGuiColorEditFlags_NoAlpha |
369 ImGuiColorEditFlags_PickerHueWheel |
370 ImGuiColorEditFlags_DisplayRGB |
371 ImGuiColorEditFlags_DisplayHSV)) {
378 ImGui::Text(
"Current vs Original");
380 ImGui::ColorButton(
"##current", col,
381 ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
389 if (ImGui::ColorButton(
390 "##original", orig_col,
391 ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
398 if (ImGui::IsItemHovered()) {
399 ImGui::SetTooltip(
"Click to restore original color");
408 ImGui::EndDisabled();
418 int r =
static_cast<int>(col.x);
419 int g =
static_cast<int>(col.y);
420 int b =
static_cast<int>(col.z);
423 ImGui::Text(
"RGB (0-255): (%d, %d, %d)", r, g, b);
424 if (ImGui::IsItemClicked()) {
425 ImGui::SetClipboardText(absl::StrFormat(
"(%d, %d, %d)", r, g, b).c_str());
431 if (ImGui::IsItemClicked()) {
432 ImGui::SetClipboardText(
439 ImGui::Text(
"Hex: #%02X%02X%02X", r, g, b);
440 if (ImGui::IsItemClicked()) {
441 ImGui::SetClipboardText(
442 absl::StrFormat(
"#%02X%02X%02X", r, g, b).c_str());
446 ImGui::TextDisabled(
"Click any value to copy");
459 ImGui::Text(
"Palette ID: %d", pal_meta.palette_id);
462 if (!pal_meta.name.empty()) {
463 ImGui::Text(
"Name: %s", pal_meta.name.c_str());
467 if (!pal_meta.description.empty()) {
468 ImGui::TextWrapped(
"%s", pal_meta.description.c_str());
474 ImGui::Text(
"Dimensions: %d colors (%dx%d)", metadata.colors_per_palette,
475 metadata.colors_per_row,
476 (metadata.colors_per_palette + metadata.colors_per_row - 1) /
477 metadata.colors_per_row);
479 ImGui::Text(
"Color Depth: %d BPP (4-bit SNES)", 4);
480 ImGui::TextDisabled(
"(16 colors per palette possible)");
485 ImGui::Text(
"ROM Address: $%06X", pal_meta.rom_address);
486 if (ImGui::IsItemClicked()) {
487 ImGui::SetClipboardText(
488 absl::StrFormat(
"$%06X", pal_meta.rom_address).c_str());
490 if (ImGui::IsItemHovered()) {
491 ImGui::SetTooltip(
"Click to copy address");
495 if (pal_meta.vram_address > 0) {
496 ImGui::Text(
"VRAM Address: $%04X", pal_meta.vram_address);
497 if (ImGui::IsItemClicked()) {
498 ImGui::SetClipboardText(
499 absl::StrFormat(
"$%04X", pal_meta.vram_address).c_str());
501 if (ImGui::IsItemHovered()) {
502 ImGui::SetTooltip(
"Click to copy VRAM address");
507 if (!pal_meta.usage_notes.empty()) {
509 ImGui::TextDisabled(
"Usage Notes:");
510 ImGui::TextWrapped(
"%s", pal_meta.usage_notes.c_str());
515 if (ImGui::BeginPopup(
"BatchOperations")) {
518 if (
ThemedButton(
"Copy Current Palette", ImVec2(-1, 0))) {
520 ImGui::CloseCurrentPopup();
523 if (
ThemedButton(
"Paste to Current Palette", ImVec2(-1, 0))) {
525 ImGui::CloseCurrentPopup();
530 if (
ThemedButton(
"Reset All Palettes", ImVec2(-1, 0))) {
532 ImGui::CloseCurrentPopup();
545 color_index, new_color);
549 absl::StrFormat(
"Failed to set color: %s", status.message()),
611 int color_index)
const {
636 if (!palette_group || index < 0 || index >= palette_group->size()) {
639 return palette_group->mutable_palette(index);
643 int color_index)
const {
662#if defined(YAZE_WITH_JSON)
664 if (!palette_group) {
674 for (
size_t palette_index = 0; palette_index < palette_group->size();
676 const auto& palette =
677 palette_group->palette_ref(
static_cast<int>(palette_index));
679 palette_json[
"index"] =
static_cast<int>(palette_index);
682 for (
size_t color_index = 0; color_index < palette.size(); color_index++) {
683 palette_json[
"colors"].push_back(
684 absl::StrFormat(
"$%04X", palette[color_index].snes()));
687 root[
"palettes"].push_back(palette_json);
697#if !defined(YAZE_WITH_JSON)
698 return absl::UnimplementedError(
"JSON support is disabled");
701 if (!palette_group) {
702 return absl::FailedPreconditionError(
"Palette group is unavailable");
708 }
catch (
const std::exception& e) {
709 return absl::InvalidArgumentError(
710 absl::StrCat(
"Failed to parse palette JSON: ", e.what()));
714 return absl::InvalidArgumentError(
"Palette JSON must be an object");
718 const auto& version_value = root[
"version"];
719 if (!version_value.is_number_integer() &&
720 !version_value.is_number_unsigned()) {
721 return absl::InvalidArgumentError(
722 "Palette JSON 'version' must be an integer");
724 int version = version_value.get<
int>();
726 return absl::InvalidArgumentError(
727 absl::StrFormat(
"Unsupported palette JSON version: %d", version));
732 const auto& group_value = root[
"group"];
733 if (!group_value.is_string()) {
734 return absl::InvalidArgumentError(
735 "Palette JSON 'group' must be a string");
737 const std::string group = group_value.get<std::string>();
739 return absl::InvalidArgumentError(absl::StrFormat(
740 "Palette JSON group '%s' does not match '%s'", group,
group_name_));
745 return absl::InvalidArgumentError(
746 "Palette JSON must contain a 'palettes' array");
749 struct PaletteImport {
751 std::vector<uint16_t> colors;
754 std::vector<PaletteImport> imports;
755 for (
const auto& palette_json : root[
"palettes"]) {
756 if (!palette_json.is_object()) {
757 return absl::InvalidArgumentError(
758 "Palette entry must be a JSON object");
761 if (!palette_json.contains(
"index") ||
762 !palette_json[
"index"].is_number_integer()) {
763 return absl::InvalidArgumentError(
764 "Palette entry is missing integer 'index'");
767 int palette_index = palette_json[
"index"].get<
int>();
768 if (palette_index < 0 || palette_index >= palette_group->size()) {
769 return absl::InvalidArgumentError(absl::StrFormat(
770 "Palette index %d out of range [0, %d)", palette_index,
771 static_cast<int>(palette_group->size())));
774 if (!palette_json.contains(
"colors") ||
775 !palette_json[
"colors"].is_array()) {
776 return absl::InvalidArgumentError(
777 "Palette entry is missing 'colors' array");
780 std::vector<uint16_t> colors;
781 colors.reserve(palette_json[
"colors"].size());
782 for (
const auto& color_json : palette_json[
"colors"]) {
783 auto color_or = ParseSnesColorJson(color_json);
784 if (!color_or.ok()) {
785 return color_or.status();
787 colors.push_back(*color_or);
790 const auto& palette = palette_group->palette_ref(palette_index);
791 if (colors.size() != palette.size()) {
792 return absl::InvalidArgumentError(absl::StrFormat(
793 "Palette %d expects %d colors but received %d", palette_index,
794 static_cast<int>(palette.size()),
795 static_cast<int>(colors.size())));
798 imports.push_back({palette_index, std::move(colors)});
802 manager.BeginBatch();
803 for (
const auto&
import : imports) {
804 for (
size_t color_index = 0; color_index <
import.colors.size();
806 auto status = manager.SetColor(
807 group_name_,
import.index,
static_cast<int>(color_index),
824 return absl::OkStatus();
837 for (
size_t i = 0; i < palette.size(); i++) {
838 result += absl::StrFormat(
"$%04X", palette[i].snes());
839 if (i < palette.size() - 1) {
844 ImGui::SetClipboardText(result.c_str());
851 return absl::FailedPreconditionError(
"No palette selected");
854 const char* clipboard = ImGui::GetClipboardText();
855 if (!clipboard || clipboard[0] ==
'\0') {
856 return absl::InvalidArgumentError(
"Clipboard is empty");
859 auto colors_or = ParseClipboardColors(clipboard);
860 if (!colors_or.ok()) {
861 return colors_or.status();
864 const auto& colors = *colors_or;
865 if (colors.size() != palette->size()) {
866 return absl::InvalidArgumentError(absl::StrFormat(
867 "Clipboard contains %d colors but palette expects %d",
868 static_cast<int>(colors.size()),
869 static_cast<int>(palette->size())));
873 manager.BeginBatch();
874 for (
size_t color_index = 0; color_index < colors.size(); color_index++) {
875 auto status = manager.SetColor(
889 return absl::OkStatus();
915 for (
int i = 0; i < 6; i++) {
918 pal.
name = absl::StrFormat(
"Overworld Main %02d", i);
919 pal.
description =
"BG main palette set (35 colors = 5x7, transparent slots are implicit)";
923 "Loaded by PaletteLoad_OWBGMain to CGRAM $0042 (rows 2-6, cols 1-7).";
940 ->palette_groups.get_group(
"ow_main");
948 const float button_size = 32.0f;
951 for (
int i = 0; i < palette->size(); i++) {
958 (*palette)[i], is_selected, is_modified,
959 ImVec2(button_size, button_size))) {
967 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
992 for (
int i = 0; i < 14; i++) {
995 pal.
name = absl::StrFormat(
"OW Anim %02d", i);
996 pal.
description =
"Animated overlay palette (7 colors, transparent slot is implicit)";
1000 "Loaded by PaletteLoad_OWBG3 to CGRAM $00E2 (row 7, cols 1-7).";
1018 ->palette_groups.get_group(
"ow_animated");
1026 const float button_size = 32.0f;
1029 for (
int i = 0; i < palette->size(); i++) {
1036 (*palette)[i], is_selected, is_modified,
1037 ImVec2(button_size, button_size))) {
1044 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1068 const char* dungeon_names[] = {
1069 "Sewers",
"Hyrule Castle",
"Eastern Palace",
"Desert Palace",
1070 "Agahnim's Tower",
"Swamp Palace",
"Palace of Darkness",
"Misery Mire",
1071 "Skull Woods",
"Ice Palace",
"Tower of Hera",
"Thieves' Town",
1072 "Turtle Rock",
"Ganon's Tower",
"Generic 1",
"Generic 2",
1073 "Generic 3",
"Generic 4",
"Generic 5",
"Generic 6"};
1075 for (
int i = 0; i < 20; i++) {
1078 pal.
name = dungeon_names[i];
1079 pal.
description = absl::StrFormat(
"Dungeon palette %d", i);
1083 "90 colors = 6 CGRAM banks x 15 colors (transparent slot per bank is implicit).";
1100 ->palette_groups.get_group(
"dungeon_main");
1108 const float button_size = 28.0f;
1111 for (
int i = 0; i < palette->size(); i++) {
1118 (*palette)[i], is_selected, is_modified,
1119 ImVec2(button_size, button_size))) {
1126 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1148 const char* sprite_names[] = {
"Global Sprites (Light World)",
1149 "Global Sprites (Dark World)"};
1151 for (
int i = 0; i < 2; i++) {
1154 pal.
name = sprite_names[i];
1156 "60 colors = 4 sprite banks x 15 colors (transparent slots are implicit)";
1161 "Loaded into CGRAM rows 9-12, cols 1-15 (row col0 is transparent).";
1178 ->palette_groups.get_group(
"global_sprites");
1186 const float button_size = 28.0f;
1189 for (
int i = 0; i < palette->size(); i++) {
1196 (*palette)[i], is_selected, is_modified,
1197 ImVec2(button_size, button_size))) {
1204 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1213 "Global sprite palettes are stored in ROM as 4 banks of 15 colors "
1214 "(transparent is implicit) and loaded to PPU CGRAM rows 9-12, cols 1-15.");
1215 ImGui::TextDisabled(
"Note: Palettes live in CGRAM, not VRAM.");
1234 const char* armor_names[] = {
"Green Mail",
"Blue Mail",
"Red Mail",
"Bunny",
1237 for (
int i = 0; i < 5; i++) {
1240 pal.
name = armor_names[i];
1241 pal.
description = absl::StrFormat(
"Link appearance: %s", armor_names[i]);
1245 "15 colors per set (transparent slot is implicit when loaded into CGRAM).";
1262 ->palette_groups.get_group(
"armors");
1270 const float button_size = 32.0f;
1273 for (
int i = 0; i < palette->size(); i++) {
1280 (*palette)[i], is_selected, is_modified,
1281 ImVec2(button_size, button_size))) {
1288 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1310 for (
int i = 0; i < 12; i++) {
1313 pal.
name = absl::StrFormat(
"Sprites Aux1 %02d", i);
1314 pal.
description =
"Auxiliary sprite palette (7 colors, transparent is implicit)";
1318 "Loaded into CGRAM with an implicit transparent slot at index 0 of the bank.";
1335 ->palette_groups.get_group(
"sprites_aux1");
1343 const float button_size = 32.0f;
1346 for (
int i = 0; i < palette->size(); i++) {
1353 (*palette)[i], is_selected, is_modified,
1354 ImVec2(button_size, button_size))) {
1361 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1383 for (
int i = 0; i < 11; i++) {
1386 pal.
name = absl::StrFormat(
"Sprites Aux2 %02d", i);
1387 pal.
description =
"Auxiliary sprite palette (7 colors, transparent is implicit)";
1391 "Loaded into CGRAM with an implicit transparent slot at index 0 of the bank.";
1408 ->palette_groups.get_group(
"sprites_aux2");
1416 const float button_size = 32.0f;
1419 for (
int i = 0; i < palette->size(); i++) {
1426 (*palette)[i], is_selected, is_modified,
1427 ImVec2(button_size, button_size))) {
1434 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1456 for (
int i = 0; i < 24; i++) {
1459 pal.
name = absl::StrFormat(
"Sprites Aux3 %02d", i);
1460 pal.
description =
"Auxiliary sprite palette (7 colors, transparent is implicit)";
1464 "Loaded into CGRAM with an implicit transparent slot at index 0 of the bank.";
1481 ->palette_groups.get_group(
"sprites_aux3");
1489 const float button_size = 32.0f;
1492 for (
int i = 0; i < palette->size(); i++) {
1500 ImGui::BeginGroup();
1502 (*palette)[i], is_selected, is_modified,
1503 ImVec2(button_size, button_size))) {
1508 ImVec2 pos = ImGui::GetItemRectMin();
1509 ImGui::GetWindowDrawList()->AddText(
1510 ImVec2(pos.x + button_size / 2 - 4, pos.y + button_size / 2 - 8),
1511 IM_COL32(255, 255, 255, 200),
"T");
1515 (*palette)[i], is_selected, is_modified,
1516 ImVec2(button_size, button_size))) {
1524 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
static Json parse(const std::string &)
std::string dump(int=-1, char=' ', bool=false, int=0) const
bool contains(const std::string &) const
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::Status WriteColor(uint32_t address, const gfx::SnesColor &color)
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
static const PaletteGroupMetadata metadata_
DungeonMainPalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
static PaletteGroupMetadata InitializeMetadata()
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
EquipmentPalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
static PaletteGroupMetadata InitializeMetadata()
static const PaletteGroupMetadata metadata_
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
OverworldAnimatedPalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
static PaletteGroupMetadata InitializeMetadata()
static const PaletteGroupMetadata metadata_
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
static PaletteGroupMetadata InitializeMetadata()
OverworldMainPalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
static const PaletteGroupMetadata metadata_
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
Base class for palette group editing cards.
gfx::SnesColor editing_color_
virtual gfx::PaletteGroup * GetPaletteGroup()=0
Get the palette group for this card.
void ResetPalette(int palette_index)
Reset a specific palette to original ROM values.
virtual void DrawCustomToolbarButtons()
Draw additional toolbar buttons (called after standard buttons)
void DrawPaletteSelector()
Draw palette selector dropdown.
void ResetColor(int palette_index, int color_index)
Reset a specific color to original ROM value.
gfx::SnesColor GetOriginalColor(int palette_index, int color_index) const
Get original color from ROM (for reset/comparison)
gfx::SnesPalette * GetMutablePalette(int index)
Get mutable palette by index.
absl::Status ImportFromClipboard()
void DiscardChanges()
Discard all unsaved changes.
std::string ExportToJson() const
void DrawToolbar()
Draw standard toolbar with save/discard/undo/redo.
bool HasUnsavedChanges() const
void DrawBatchOperationsPopup()
Draw batch operations popup.
void DrawMetadataInfo()
Draw palette metadata info panel.
void SetColor(int palette_index, int color_index, const gfx::SnesColor &new_color)
Set a color value (records change for undo)
virtual const PaletteGroupMetadata & GetMetadata() const =0
Get metadata for this palette group.
absl::Status SaveToRom()
Save all modified palettes to ROM.
absl::Status WriteColorToRom(int palette_index, int color_index, const gfx::SnesColor &color)
Write a single color to ROM.
ToastManager * toast_manager_
virtual void DrawCustomPanels()
Draw additional panels (called after main content)
void DrawColorPicker()
Draw color picker for selected color.
bool IsPaletteModified(int palette_index) const
std::string display_name_
PaletteGroupPanel(const std::string &group_name, const std::string &display_name, Rom *rom, zelda3::GameData *game_data=nullptr)
Construct a new Palette Group Panel.
zelda3::GameData * game_data_
bool IsColorModified(int palette_index, int color_index) const
void Draw(bool *p_open) override
Draw the card's ImGui UI.
void DrawColorInfo()
Draw color info panel with RGB/SNES/Hex values.
absl::Status ImportFromJson(const std::string &json)
std::string ExportToClipboard() const
virtual void DrawPaletteGrid()=0
Draw the palette grid specific to this palette type.
static PaletteGroupMetadata InitializeMetadata()
static const PaletteGroupMetadata metadata_
SpritePalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
void DrawCustomPanels() override
Draw additional panels (called after main content)
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
static PaletteGroupMetadata InitializeMetadata()
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
SpritesAux1PalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
static const PaletteGroupMetadata metadata_
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
static const PaletteGroupMetadata metadata_
static PaletteGroupMetadata InitializeMetadata()
SpritesAux2PalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
static PaletteGroupMetadata InitializeMetadata()
int GetColorsPerRow() const override
Get the number of colors per row for grid layout.
SpritesAux3PalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
void DrawPaletteGrid() override
Draw the palette grid specific to this palette type.
static const PaletteGroupMetadata metadata_
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
absl::Status SetColor(const std::string &group_name, int palette_index, int color_index, const SnesColor &new_color)
Set a color in a palette (records change for undo)
bool IsGroupModified(const std::string &group_name) const
Check if a specific palette group has modifications.
absl::Status SaveGroup(const std::string &group_name)
Save a specific palette group to ROM.
void Undo()
Undo the most recent change.
void ClearHistory()
Clear undo/redo history.
bool CanRedo() const
Check if redo is available.
bool CanUndo() const
Check if undo is available.
absl::Status ResetColor(const std::string &group_name, int palette_index, int color_index)
Reset a single color to its original ROM value.
absl::Status ResetPalette(const std::string &group_name, int palette_index)
Reset an entire palette to original ROM values.
bool IsColorModified(const std::string &group_name, int palette_index, int color_index) const
Check if a specific color is modified.
void DiscardGroup(const std::string &group_name)
Discard changes for a specific group.
static PaletteManager & Get()
Get the singleton instance.
SnesColor GetColor(const std::string &group_name, int palette_index, int color_index) const
Get a color from a palette.
bool IsPaletteModified(const std::string &group_name, int palette_index) const
Check if a specific palette is modified.
void Redo()
Redo the most recently undone change.
constexpr ImVec4 rgb() const
Get RGB values (WARNING: stored as 0-255 in ImVec4)
constexpr uint16_t snes() const
Get SNES 15-bit color.
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
static void HelpMarker(const char *desc)
static float GetStandardInputWidth()
#define ICON_MD_MORE_VERT
#define ICON_MD_FILE_DOWNLOAD
#define ICON_MD_FILE_UPLOAD
absl::StatusOr< uint16_t > ParseSnesHexToken(std::string token)
absl::StatusOr< std::vector< uint16_t > > ParseClipboardColors(const std::string &clipboard)
constexpr int kArmorPalettes
constexpr int kOverworldPaletteAnimated
constexpr int kOverworldPaletteMain
constexpr int kGlobalSpritesLW
constexpr int kGlobalSpritePalettesDW
constexpr int kDungeonMainPalettes
uint32_t GetPaletteAddress(const std::string &group_name, size_t palette_index, size_t color_index)
Graphical User Interface (GUI) components for the application.
bool ThemedIconButton(const char *icon, const char *tooltip, const ImVec2 &size, bool is_active, bool is_disabled, const char *panel_id, const char *anim_id)
Draw a standard icon button with theme-aware colors.
bool PrimaryButton(const char *label, const ImVec2 &size, const char *panel_id, const char *anim_id)
Draw a primary action button (accented color).
bool ThemedButton(const char *label, const ImVec2 &size, const char *panel_id, const char *anim_id)
Draw a standard text button with theme colors.
bool DangerButton(const char *label, const ImVec2 &size, const char *panel_id, const char *anim_id)
Draw a danger action button (error color).
void SectionHeader(const char *icon, const char *label, const ImVec4 &color)
IMGUI_API bool PaletteColorButton(const char *id, const gfx::SnesColor &color, bool is_selected, bool is_modified, const ImVec2 &size, ImGuiColorEditFlags flags)
ImVec4 ConvertSnesColorToImVec4(const gfx::SnesColor &color)
Convert SnesColor to standard ImVec4 for display.
gfx::SnesColor ConvertImVec4ToSnesColor(const ImVec4 &color)
Convert standard ImVec4 to SnesColor.
PaletteGroup * get_group(const std::string &group_name)
Represents a group of palettes.
gfx::PaletteGroupMap palette_groups