9#include "absl/strings/str_cat.h"
10#include "absl/strings/str_format.h"
11#include "absl/strings/str_split.h"
12#include "absl/strings/strip.h"
21#include "imgui/imgui.h"
37 token = std::string(absl::StripAsciiWhitespace(token));
39 return absl::InvalidArgumentError(
"Empty color token");
42 if (token[0] ==
'$') {
44 }
else if (token.size() > 2 &&
45 (token.rfind(
"0x", 0) == 0 || token.rfind(
"0X", 0) == 0)) {
50 return absl::InvalidArgumentError(
"Color token is missing hex digits");
53 for (
char ch : token) {
54 if (!std::isxdigit(
static_cast<unsigned char>(ch))) {
55 return absl::InvalidArgumentError(
56 absl::StrCat(
"Invalid hex digit in color token: ", token));
60 if (token.size() > 4) {
61 return absl::InvalidArgumentError(
62 absl::StrCat(
"Color token is too long for SNES color: ", token));
67 value =
static_cast<uint32_t
>(std::stoul(token,
nullptr, 16));
68 }
catch (
const std::exception&) {
69 return absl::InvalidArgumentError(
70 absl::StrCat(
"Failed to parse color token: ", token));
74 return absl::InvalidArgumentError(
75 absl::StrCat(
"SNES color out of range (0x0000-0x7FFF): ", token));
78 return static_cast<uint16_t
>(value);
82 const std::string& clipboard) {
83 std::vector<uint16_t> colors;
84 for (
const auto& raw_token : absl::StrSplit(
85 clipboard, absl::ByAnyChar(
", \n\r\t"), absl::SkipEmpty())) {
86 const std::string token =
87 std::string(absl::StripAsciiWhitespace(raw_token));
93 return color_or.status();
95 colors.push_back(*color_or);
99 return absl::InvalidArgumentError(
"No colors found in clipboard data");
105#if defined(YAZE_WITH_JSON)
106absl::StatusOr<uint16_t> ParseSnesColorJson(
const yaze::Json& value) {
108 return ParseSnesHexToken(value.
get<std::string>());
110 if (value.is_number_integer()) {
111 int parsed = value.
get<
int>();
112 if (parsed < 0 || parsed > 0x7FFF) {
113 return absl::InvalidArgumentError(
114 absl::StrFormat(
"SNES color out of range: %d", parsed));
116 return static_cast<uint16_t
>(parsed);
118 if (value.is_number_unsigned()) {
119 uint32_t parsed = value.
get<uint32_t>();
120 if (parsed > 0x7FFF) {
121 return absl::InvalidArgumentError(
122 absl::StrFormat(
"SNES color out of range: %u", parsed));
124 return static_cast<uint16_t
>(parsed);
126 return absl::InvalidArgumentError(
127 "Invalid color value type (expected string or number)");
134 const std::string& display_name,
Rom* rom,
136 : group_name_(group_name),
137 display_name_(display_name),
139 game_data_(game_data) {
148 tr(
"Palette controls are unavailable for an inactive ROM session."));
165 if (ImGui::BeginTable(
166 "##PalettePanelLayout", 2,
167 ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) {
168 ImGui::TableSetupColumn(
"Grid", ImGuiTableColumnFlags_WidthStretch, 0.6f);
169 ImGui::TableSetupColumn(
"Editor", ImGuiTableColumnFlags_WidthStretch, 0.4f);
171 ImGui::TableNextRow();
172 ImGui::TableNextColumn();
179 ImGui::TableNextColumn();
189 ImGui::TextDisabled(tr(
"Select a color to edit"));
209 ImGui::BeginDisabled(!has_changes);
220 ImGui::EndDisabled();
222 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
223 ImGui::SetTooltip(tr(
"No palette changes to save"));
229 ImGui::BeginDisabled(!has_changes);
233 ImGui::EndDisabled();
235 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
236 ImGui::SetTooltip(tr(
"No palette changes to discard"));
243 size_t modified_count = 0;
246 for (
int p = 0; p < group->size(); p++) {
252 ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), tr(
"%s %zu modified"),
257 ImGui::Dummy(ImVec2(20, 0));
262 ImGui::BeginDisabled(!can_undo);
266 ImGui::EndDisabled();
267 if (!can_undo && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
268 ImGui::SetTooltip(tr(
"Nothing to undo"));
273 ImGui::BeginDisabled(!can_redo);
277 ImGui::EndDisabled();
278 if (!can_redo && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
279 ImGui::SetTooltip(tr(
"Nothing to redo"));
283 ImGui::Dummy(ImVec2(20, 0));
298 ImGui::OpenPopup(
"BatchOperations");
310 int num_palettes = palette_group->size();
312 ImGui::Text(tr(
"Palette:"));
316 if (ImGui::BeginCombo(
319 const float item_height = ImGui::GetTextLineHeightWithSpacing();
320 ImGuiListClipper clipper;
321 clipper.Begin(num_palettes, item_height);
326 while (clipper.Step()) {
327 for (
int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i) {
331 std::string label = absl::StrFormat(
"Palette %d", i);
336 if (ImGui::Selectable(label.c_str(), is_selected)) {
341 ImGui::SetItemDefaultFocus();
354 ImGui::EndDisabled();
372 if (ImGui::ColorPicker4(
"##picker", &col.x,
373 ImGuiColorEditFlags_NoAlpha |
374 ImGuiColorEditFlags_PickerHueWheel |
375 ImGuiColorEditFlags_DisplayRGB |
376 ImGuiColorEditFlags_DisplayHSV)) {
383 ImGui::Text(tr(
"Current vs Original"));
385 ImGui::ColorButton(
"##current", col,
386 ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
394 if (ImGui::ColorButton(
395 "##original", orig_col,
396 ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
403 if (ImGui::IsItemHovered()) {
404 ImGui::SetTooltip(tr(
"Click to restore original color"));
413 ImGui::EndDisabled();
423 int r =
static_cast<int>(col.x);
424 int g =
static_cast<int>(col.y);
425 int b =
static_cast<int>(col.z);
428 ImGui::Text(tr(
"RGB (0-255): (%d, %d, %d)"), r, g, b);
429 if (ImGui::IsItemClicked()) {
430 ImGui::SetClipboardText(absl::StrFormat(
"(%d, %d, %d)", r, g, b).c_str());
436 if (ImGui::IsItemClicked()) {
437 ImGui::SetClipboardText(
444 ImGui::Text(tr(
"Hex: #%02X%02X%02X"), r, g, b);
445 if (ImGui::IsItemClicked()) {
446 ImGui::SetClipboardText(
447 absl::StrFormat(
"#%02X%02X%02X", r, g, b).c_str());
451 ImGui::TextDisabled(tr(
"Click any value to copy"));
464 ImGui::Text(tr(
"Palette ID: %d"), pal_meta.palette_id);
467 if (!pal_meta.name.empty()) {
468 ImGui::Text(tr(
"Name: %s"), pal_meta.name.c_str());
472 if (!pal_meta.description.empty()) {
473 ImGui::TextWrapped(
"%s", pal_meta.description.c_str());
479 ImGui::Text(tr(
"Dimensions: %d colors (%dx%d)"), metadata.colors_per_palette,
480 metadata.colors_per_row,
481 (metadata.colors_per_palette + metadata.colors_per_row - 1) /
482 metadata.colors_per_row);
484 ImGui::Text(tr(
"Color Depth: %d BPP (4-bit SNES)"), 4);
485 ImGui::TextDisabled(tr(
"(16 colors per palette possible)"));
490 ImGui::Text(tr(
"ROM Address: $%06X"), pal_meta.rom_address);
491 if (ImGui::IsItemClicked()) {
492 ImGui::SetClipboardText(
493 absl::StrFormat(
"$%06X", pal_meta.rom_address).c_str());
495 if (ImGui::IsItemHovered()) {
496 ImGui::SetTooltip(tr(
"Click to copy address"));
500 if (pal_meta.vram_address > 0) {
501 ImGui::Text(tr(
"VRAM Address: $%04X"), pal_meta.vram_address);
502 if (ImGui::IsItemClicked()) {
503 ImGui::SetClipboardText(
504 absl::StrFormat(
"$%04X", pal_meta.vram_address).c_str());
506 if (ImGui::IsItemHovered()) {
507 ImGui::SetTooltip(tr(
"Click to copy VRAM address"));
512 if (!pal_meta.usage_notes.empty()) {
514 ImGui::TextDisabled(tr(
"Usage Notes:"));
515 ImGui::TextWrapped(
"%s", pal_meta.usage_notes.c_str());
520 if (ImGui::BeginPopup(
"BatchOperations")) {
523 if (
ThemedButton(
"Copy Current Palette", ImVec2(-1, 0))) {
525 ImGui::CloseCurrentPopup();
528 if (
ThemedButton(
"Paste to Current Palette", ImVec2(-1, 0))) {
530 ImGui::CloseCurrentPopup();
535 if (
ThemedButton(
"Reset All Palettes", ImVec2(-1, 0))) {
537 ImGui::CloseCurrentPopup();
558 color_index, new_color);
562 absl::StrFormat(
"Failed to set color: %s", status.message()),
571 return absl::FailedPreconditionError(
572 "Cannot save palettes from an inactive ROM session");
577 palette_manager.GetModifiedGroupColorWriteRanges(
group_name_);
578 if (!ranges.empty()) {
582 if (!preflight.ok()) {
658 int color_index)
const {
695 if (!palette_group || index < 0 || index >= palette_group->size()) {
698 return palette_group->mutable_palette(index);
702 int color_index)
const {
718#if defined(YAZE_WITH_JSON)
720 if (!palette_group) {
730 for (
size_t palette_index = 0; palette_index < palette_group->size();
732 const auto& palette =
733 palette_group->palette_ref(
static_cast<int>(palette_index));
735 palette_json[
"index"] =
static_cast<int>(palette_index);
738 for (
size_t color_index = 0; color_index < palette.size(); color_index++) {
739 palette_json[
"colors"].push_back(
740 absl::StrFormat(
"$%04X", palette[color_index].snes()));
743 root[
"palettes"].push_back(palette_json);
754 return absl::FailedPreconditionError(
755 "Cannot import palettes into an inactive ROM session");
757#if !defined(YAZE_WITH_JSON)
758 return absl::UnimplementedError(
"JSON support is disabled");
761 if (!palette_group) {
762 return absl::FailedPreconditionError(
"Palette group is unavailable");
768 }
catch (
const std::exception& e) {
769 return absl::InvalidArgumentError(
770 absl::StrCat(
"Failed to parse palette JSON: ", e.what()));
774 return absl::InvalidArgumentError(
"Palette JSON must be an object");
778 const auto& version_value = root[
"version"];
779 if (!version_value.is_number_integer() &&
780 !version_value.is_number_unsigned()) {
781 return absl::InvalidArgumentError(
782 "Palette JSON 'version' must be an integer");
784 int version = version_value.get<
int>();
786 return absl::InvalidArgumentError(
787 absl::StrFormat(
"Unsupported palette JSON version: %d", version));
792 const auto& group_value = root[
"group"];
793 if (!group_value.is_string()) {
794 return absl::InvalidArgumentError(
795 "Palette JSON 'group' must be a string");
797 const std::string group = group_value.get<std::string>();
799 return absl::InvalidArgumentError(absl::StrFormat(
800 "Palette JSON group '%s' does not match '%s'", group,
group_name_));
805 return absl::InvalidArgumentError(
806 "Palette JSON must contain a 'palettes' array");
809 struct PaletteImport {
811 std::vector<uint16_t> colors;
814 std::vector<PaletteImport> imports;
815 for (
const auto& palette_json : root[
"palettes"]) {
816 if (!palette_json.is_object()) {
817 return absl::InvalidArgumentError(
"Palette entry must be a JSON object");
820 if (!palette_json.contains(
"index") ||
821 !palette_json[
"index"].is_number_integer()) {
822 return absl::InvalidArgumentError(
823 "Palette entry is missing integer 'index'");
826 int palette_index = palette_json[
"index"].get<
int>();
827 if (palette_index < 0 || palette_index >= palette_group->size()) {
828 return absl::InvalidArgumentError(absl::StrFormat(
829 "Palette index %d out of range [0, %d)", palette_index,
830 static_cast<int>(palette_group->size())));
833 if (!palette_json.contains(
"colors") ||
834 !palette_json[
"colors"].is_array()) {
835 return absl::InvalidArgumentError(
836 "Palette entry is missing 'colors' array");
839 std::vector<uint16_t> colors;
840 colors.reserve(palette_json[
"colors"].size());
841 for (
const auto& color_json : palette_json[
"colors"]) {
842 auto color_or = ParseSnesColorJson(color_json);
843 if (!color_or.ok()) {
844 return color_or.status();
846 colors.push_back(*color_or);
849 const auto& palette = palette_group->palette_ref(palette_index);
850 if (colors.size() != palette.size()) {
851 return absl::InvalidArgumentError(absl::StrFormat(
852 "Palette %d expects %d colors but received %d", palette_index,
853 static_cast<int>(palette.size()),
static_cast<int>(colors.size())));
856 imports.push_back({palette_index, std::move(colors)});
860 manager.BeginBatch();
861 for (
const auto&
import : imports) {
862 for (
size_t color_index = 0; color_index <
import.colors.size();
864 auto status = manager.SetColor(
865 group_name_,
import.index,
static_cast<int>(color_index),
882 return absl::OkStatus();
895 for (
size_t i = 0; i < palette.size(); i++) {
896 result += absl::StrFormat(
"$%04X", palette[i].snes());
897 if (i < palette.size() - 1) {
902 ImGui::SetClipboardText(result.c_str());
908 return absl::FailedPreconditionError(
909 "Cannot import palettes into an inactive ROM session");
913 return absl::FailedPreconditionError(
"No palette selected");
916 const char* clipboard = ImGui::GetClipboardText();
917 if (!clipboard || clipboard[0] ==
'\0') {
918 return absl::InvalidArgumentError(
"Clipboard is empty");
921 auto colors_or = ParseClipboardColors(clipboard);
922 if (!colors_or.ok()) {
923 return colors_or.status();
926 const auto& colors = *colors_or;
927 if (colors.size() != palette->size()) {
928 return absl::InvalidArgumentError(absl::StrFormat(
929 "Clipboard contains %d colors but palette expects %d",
930 static_cast<int>(colors.size()),
static_cast<int>(palette->size())));
934 manager.BeginBatch();
935 for (
size_t color_index = 0; color_index < colors.size(); color_index++) {
937 static_cast<int>(color_index),
950 return absl::OkStatus();
976 for (
int i = 0; i < 6; i++) {
979 pal.
name = absl::StrFormat(
"Overworld Main %02d", i);
981 "BG main palette set (35 colors = 5x7, transparent slots are implicit)";
985 "Loaded by PaletteLoad_OWBGMain to CGRAM $0042 (rows 2-6, cols 1-7).";
1002 ->palette_groups.get_group(
"ow_main");
1010 const float button_size = 32.0f;
1013 for (
int i = 0; i < palette->size(); i++) {
1020 (*palette)[i], is_selected, is_modified,
1021 ImVec2(button_size, button_size))) {
1029 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1054 for (
int i = 0; i < 14; i++) {
1057 pal.
name = absl::StrFormat(
"OW Anim %02d", i);
1059 "Animated overlay palette (7 colors, transparent slot is implicit)";
1063 "Loaded by PaletteLoad_OWBG3 to CGRAM $00E2 (row 7, cols 1-7).";
1081 ->palette_groups.get_group(
"ow_animated");
1089 const float button_size = 32.0f;
1092 for (
int i = 0; i < palette->size(); i++) {
1099 (*palette)[i], is_selected, is_modified,
1100 ImVec2(button_size, button_size))) {
1107 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1131 const char* dungeon_names[] = {
1132 "Sewers",
"Hyrule Castle",
"Eastern Palace",
"Desert Palace",
1133 "Agahnim's Tower",
"Swamp Palace",
"Palace of Darkness",
"Misery Mire",
1134 "Skull Woods",
"Ice Palace",
"Tower of Hera",
"Thieves' Town",
1135 "Turtle Rock",
"Ganon's Tower",
"Generic 1",
"Generic 2",
1136 "Generic 3",
"Generic 4",
"Generic 5",
"Generic 6"};
1138 for (
int i = 0; i < 20; i++) {
1141 pal.
name = dungeon_names[i];
1142 pal.
description = absl::StrFormat(
"Dungeon palette %d", i);
1146 "90 colors = 6 CGRAM banks x 15 colors (transparent slot per bank is "
1164 ->palette_groups.get_group(
"dungeon_main");
1172 const float button_size = 28.0f;
1175 for (
int i = 0; i < palette->size(); i++) {
1182 (*palette)[i], is_selected, is_modified,
1183 ImVec2(button_size, button_size))) {
1190 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1213 const char* sprite_names[] = {
"Global Sprites (Light World)",
1214 "Global Sprites (Dark World)"};
1216 for (
int i = 0; i < 2; i++) {
1219 pal.
name = sprite_names[i];
1221 "60 colors = 4 sprite banks x 15 colors (transparent slots are "
1227 "Loaded into CGRAM rows 9-12, cols 1-15 (row col0 is transparent).";
1244 ->palette_groups.get_group(
"global_sprites");
1252 const float button_size = 28.0f;
1255 for (
int i = 0; i < palette->size(); i++) {
1262 (*palette)[i], is_selected, is_modified,
1263 ImVec2(button_size, button_size))) {
1270 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1279 tr(
"Global sprite palettes are stored in ROM as 4 banks of 15 colors "
1280 "(transparent is implicit) and loaded to PPU CGRAM rows 9-12, cols "
1282 ImGui::TextDisabled(tr(
"Note: Palettes live in CGRAM, not VRAM."));
1301 const char* armor_names[] = {
"Green Mail",
"Blue Mail",
"Red Mail",
"Bunny",
1304 for (
int i = 0; i < 5; i++) {
1307 pal.
name = armor_names[i];
1308 pal.
description = absl::StrFormat(
"Link appearance: %s", armor_names[i]);
1312 "15 colors per set (transparent slot is implicit when loaded into "
1330 ->palette_groups.get_group(
"armors");
1338 const float button_size = 32.0f;
1341 for (
int i = 0; i < palette->size(); i++) {
1348 (*palette)[i], is_selected, is_modified,
1349 ImVec2(button_size, button_size))) {
1356 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1378 for (
int i = 0; i < 12; i++) {
1381 pal.
name = absl::StrFormat(
"Sprites Aux1 %02d", i);
1383 "Auxiliary sprite palette (7 colors, transparent is implicit)";
1387 "Loaded into CGRAM with an implicit transparent slot at index 0 of the "
1405 ->palette_groups.get_group(
"sprites_aux1");
1413 const float button_size = 32.0f;
1416 for (
int i = 0; i < palette->size(); i++) {
1423 (*palette)[i], is_selected, is_modified,
1424 ImVec2(button_size, button_size))) {
1431 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1453 for (
int i = 0; i < 11; i++) {
1456 pal.
name = absl::StrFormat(
"Sprites Aux2 %02d", i);
1458 "Auxiliary sprite palette (7 colors, transparent is implicit)";
1462 "Loaded into CGRAM with an implicit transparent slot at index 0 of the "
1480 ->palette_groups.get_group(
"sprites_aux2");
1488 const float button_size = 32.0f;
1491 for (
int i = 0; i < palette->size(); i++) {
1498 (*palette)[i], is_selected, is_modified,
1499 ImVec2(button_size, button_size))) {
1506 if ((i + 1) % colors_per_row != 0 && i + 1 < palette->size()) {
1528 for (
int i = 0; i < 24; i++) {
1531 pal.
name = absl::StrFormat(
"Sprites Aux3 %02d", i);
1533 "Auxiliary sprite palette (7 colors, transparent is implicit)";
1537 "Loaded into CGRAM with an implicit transparent slot at index 0 of the "
1555 ->palette_groups.get_group(
"sprites_aux3");
1563 const float button_size = 32.0f;
1566 for (
int i = 0; i < palette->size(); i++) {
1574 ImGui::BeginGroup();
1576 (*palette)[i], is_selected, is_modified,
1577 ImVec2(button_size, button_size))) {
1582 ImVec2 pos = ImGui::GetItemRectMin();
1583 ImGui::GetWindowDrawList()->AddText(
1584 ImVec2(pos.x + button_size / 2 - 4, pos.y + button_size / 2 - 8),
1585 IM_COL32(255, 255, 255, 200),
"T");
1589 (*palette)[i], is_selected, is_modified,
1590 ImVec2(button_size, button_size))) {
1598 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...
bool loaded() const
Check if the manifest has been loaded.
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.
const project::YazeProject * project_
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.
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
bool IsManagedSession() const
Return true only while PaletteManager is bound to this panel's session.
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.
void Undo()
Undo the most recent change.
void ClearHistory()
Clear undo/redo history.
bool IsManaging(const zelda3::GameData *game_data) const
Check whether the manager is bound to this exact GameData and ROM.
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)
absl::Status ValidateHackManifestSaveConflicts(const core::HackManifest &manifest, project::RomWritePolicy write_policy, const std::vector< std::pair< uint32_t, uint32_t > > &ranges, absl::string_view save_scope, const char *log_tag, ToastManager *toast_manager)
constexpr int kArmorPalettes
constexpr int kOverworldPaletteAnimated
constexpr int kOverworldPaletteMain
constexpr int kGlobalSpritesLW
constexpr int kGlobalSpritePalettesDW
constexpr int kDungeonMainPalettes
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.
core::HackManifest hack_manifest
gfx::PaletteGroupMap palette_groups