1#define IMGUI_DEFINE_MATH_OPERATORS
11#include "absl/strings/str_cat.h"
12#include "absl/strings/str_format.h"
13#include "absl/strings/str_split.h"
14#include "absl/strings/strip.h"
24#include "imgui/imgui.h"
25#include "imgui/imgui_internal.h"
41 token = std::string(absl::StripAsciiWhitespace(token));
43 return absl::InvalidArgumentError(
"Empty color token");
46 if (token[0] ==
'$') {
48 }
else if (token.size() > 2 &&
49 (token.rfind(
"0x", 0) == 0 || token.rfind(
"0X", 0) == 0)) {
54 return absl::InvalidArgumentError(
"Color token is missing hex digits");
57 for (
char ch : token) {
58 if (!std::isxdigit(
static_cast<unsigned char>(ch))) {
59 return absl::InvalidArgumentError(
60 absl::StrCat(
"Invalid hex digit in color token: ", token));
64 if (token.size() > 4) {
65 return absl::InvalidArgumentError(
66 absl::StrCat(
"Color token is too long for SNES color: ", token));
71 value =
static_cast<uint32_t
>(std::stoul(token,
nullptr, 16));
72 }
catch (
const std::exception&) {
73 return absl::InvalidArgumentError(
74 absl::StrCat(
"Failed to parse color token: ", token));
78 return absl::InvalidArgumentError(
79 absl::StrCat(
"SNES color out of range (0x0000-0x7FFF): ", token));
82 return static_cast<uint16_t
>(value);
86 const std::string& clipboard) {
87 std::vector<uint16_t> colors;
88 for (
const auto& raw_token : absl::StrSplit(
89 clipboard, absl::ByAnyChar(
", \n\r\t"), absl::SkipEmpty())) {
90 const std::string token =
91 std::string(absl::StripAsciiWhitespace(raw_token));
97 return color_or.status();
99 colors.push_back(*color_or);
102 if (colors.empty()) {
103 return absl::InvalidArgumentError(
"No colors found in clipboard data");
109#if defined(YAZE_WITH_JSON)
110absl::StatusOr<uint16_t> ParseSnesColorJson(
const yaze::Json& value) {
112 return ParseSnesHexToken(value.
get<std::string>());
114 if (value.is_number_integer()) {
115 int parsed = value.
get<
int>();
116 if (parsed < 0 || parsed > 0x7FFF) {
117 return absl::InvalidArgumentError(
118 absl::StrFormat(
"SNES color out of range: %d", parsed));
120 return static_cast<uint16_t
>(parsed);
122 if (value.is_number_unsigned()) {
123 uint32_t parsed = value.
get<uint32_t>();
124 if (parsed > 0x7FFF) {
125 return absl::InvalidArgumentError(
126 absl::StrFormat(
"SNES color out of range: %u", parsed));
128 return static_cast<uint16_t
>(parsed);
130 return absl::InvalidArgumentError(
131 "Invalid color value type (expected string or number)");
138 const std::string& display_name,
Rom* rom,
140 : group_name_(group_name),
141 display_name_(display_name),
143 game_data_(game_data) {
160 group && group->size() > 0
161 ? std::clamp(index, 0,
static_cast<int>(group->size()) - 1)
172 bool batch_requested =
false;
176 ImGui::OpenPopup(
"SelectedPaletteTools");
177 if (ImGui::BeginPopup(
"SelectedPaletteTools")) {
178 if (ImGui::MenuItem(tr(
"Copy colors")))
180 if (ImGui::MenuItem(tr(
"Paste colors"))) {
186 if (ImGui::MenuItem(tr(
"Batch operations...")))
187 batch_requested =
true;
193 ImGui::GetContentRegionAvail().x >= ImGui::GetFontSize() * 44;
194 const auto draw_grid = [
this]() {
197 if (ImGui::CollapsingHeader(tr(
"Details"))) {
202 const auto draw_picker = [
this]() {
205 if (ImGui::CollapsingHeader(tr(
"Color values")))
208 ImGui::TextDisabled(tr(
"Select a color to edit"));
212 ImGui::BeginTable(
"PaletteContent", 2, ImGuiTableFlags_Resizable)) {
213 ImGui::TableSetupColumn(
"Colors", ImGuiTableColumnFlags_WidthStretch, 0.6f);
214 ImGui::TableSetupColumn(
"Edit", ImGuiTableColumnFlags_WidthStretch, 0.4f);
215 ImGui::TableNextColumn();
217 ImGui::TableNextColumn();
220 }
else if (!columns) {
226 ImGui::OpenPopup(
"BatchOperations");
235 const float spacing = ImGui::GetStyle().ItemSpacing.x;
236 const float cell = std::clamp(
237 (ImGui::GetContentRegionAvail().x - (columns - 1) * spacing) / columns,
238 12.0f, ImGui::GetFrameHeight() * 1.35f);
239 for (
int i = 0; i < palette->size(); ++i) {
247 if ((i + 1) % columns && i + 1 < palette->size())
257 ImGui::BeginDisabled(!has_changes);
259 absl::StrFormat(
"%s Apply to buffer",
ICON_MD_SAVE).c_str())) {
269 ImGui::EndDisabled();
271 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
272 ImGui::SetTooltip(tr(
"No palette changes to apply"));
278 ImGui::BeginDisabled(!has_changes);
282 ImGui::EndDisabled();
284 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
285 ImGui::SetTooltip(tr(
"No palette changes to discard"));
292 size_t modified_count = 0;
295 for (
int p = 0; p < group->size(); p++) {
301 ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), tr(
"%s %zu modified"),
306 ImGui::Dummy(ImVec2(20, 0));
311 ImGui::BeginDisabled(!can_undo);
315 ImGui::EndDisabled();
316 if (!can_undo && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
317 ImGui::SetTooltip(tr(
"Nothing to undo"));
322 ImGui::BeginDisabled(!can_redo);
326 ImGui::EndDisabled();
327 if (!can_redo && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
328 ImGui::SetTooltip(tr(
"Nothing to redo"));
332 ImGui::Dummy(ImVec2(20, 0));
347 ImGui::OpenPopup(
"BatchOperations");
359 int num_palettes = palette_group->size();
361 ImGui::Text(tr(
"Palette:"));
365 if (ImGui::BeginCombo(
368 const float item_height = ImGui::GetTextLineHeightWithSpacing();
369 ImGuiListClipper clipper;
370 clipper.Begin(num_palettes, item_height);
375 while (clipper.Step()) {
376 for (
int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i) {
380 std::string label = absl::StrFormat(
"Palette %d", i);
385 if (ImGui::Selectable(label.c_str(), is_selected)) {
390 ImGui::SetItemDefaultFocus();
403 ImGui::EndDisabled();
421 const ImGuiID picker_id = ImGui::GetID(
"##picker");
423 ImGui::IsKeyPressed(ImGuiKey_Escape,
false)) {
424 manager.CancelColorEdit(picker_id);
429 ImGui::SetNextItemWidth(
430 std::min(ImGui::GetContentRegionAvail().x, ImGui::GetFontSize() * 19));
433 const bool changed = ImGui::ColorPicker4(
435 ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_PickerHueWheel |
436 ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV);
437 const bool active = ImGui::IsItemActive();
441 picker_metadata.
label =
"Palette color picker";
442 picker_metadata.
window_name = ImGui::GetCurrentWindow()->Name;
443 picker_metadata.
visible = ImGui::IsItemVisible();
445 const auto min = ImGui::GetItemRectMin();
446 const auto max = ImGui::GetItemRectMax();
447 picker_metadata.
bounds = {min.x, min.y, max.x, max.y,
true};
449 absl::StrCat(
"Palette/picker:", picker_id),
"color", picker_id,
450 "Drag to preview; release commits one Undo step; Escape cancels",
452 ImGui::EndDisabled();
455 (
active_picker_ != picker_id || !manager.HasColorEdit(picker_id))) {
467 manager.CommitColorEdit(picker_id);
470 }
else if (
active_picker_ == picker_id && !ImGui::IsMouseDown(0)) {
478 absl::SNPrintF(snes,
sizeof(snes),
"%04X",
480 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 7);
481 if (ImGui::InputText(
"SNES", snes,
sizeof(snes),
482 ImGuiInputTextFlags_CharsHexadecimal |
483 ImGuiInputTextFlags_EnterReturnsTrue)) {
484 const auto parsed = ParseSnesHexToken(snes);
496 ImGui::Text(tr(
"Current vs Original"));
498 ImGui::ColorButton(
"##current", col,
499 ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
507 if (ImGui::ColorButton(
508 "##original", orig_col,
509 ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker,
515 if (ImGui::IsItemHovered()) {
516 ImGui::SetTooltip(tr(
"Click to restore original color"));
527 ImGui::EndDisabled();
540 auto col = color.rgb();
541 int r =
static_cast<int>(col.x);
542 int g =
static_cast<int>(col.y);
543 int b =
static_cast<int>(col.z);
546 ImGui::Text(tr(
"RGB (0-255): (%d, %d, %d)"), r, g, b);
547 if (ImGui::IsItemClicked()) {
548 ImGui::SetClipboardText(absl::StrFormat(
"(%d, %d, %d)", r, g, b).c_str());
553 ImGui::Text(tr(
"SNES BGR555: $%04X"), color.snes());
554 if (ImGui::IsItemClicked()) {
555 ImGui::SetClipboardText(absl::StrFormat(
"$%04X", color.snes()).c_str());
561 ImGui::Text(tr(
"Hex: #%02X%02X%02X"), r, g, b);
562 if (ImGui::IsItemClicked()) {
563 ImGui::SetClipboardText(
564 absl::StrFormat(
"#%02X%02X%02X", r, g, b).c_str());
568 ImGui::TextDisabled(tr(
"Click any value to copy"));
581 ImGui::Text(tr(
"Palette ID: %d"), pal_meta.palette_id);
584 if (!pal_meta.name.empty()) {
585 ImGui::Text(tr(
"Name: %s"), pal_meta.name.c_str());
589 if (!pal_meta.description.empty()) {
590 ImGui::TextWrapped(
"%s", pal_meta.description.c_str());
596 ImGui::Text(tr(
"Dimensions: %d colors (%dx%d)"), metadata.colors_per_palette,
597 metadata.colors_per_row,
598 (metadata.colors_per_palette + metadata.colors_per_row - 1) /
599 metadata.colors_per_row);
601 ImGui::Text(tr(
"Color Depth: %d BPP (4-bit SNES)"), 4);
602 ImGui::TextDisabled(tr(
"(16 colors per palette possible)"));
607 ImGui::Text(tr(
"ROM Address: $%06X"), pal_meta.rom_address);
608 if (ImGui::IsItemClicked()) {
609 ImGui::SetClipboardText(
610 absl::StrFormat(
"$%06X", pal_meta.rom_address).c_str());
612 if (ImGui::IsItemHovered()) {
613 ImGui::SetTooltip(tr(
"Click to copy address"));
617 if (pal_meta.vram_address > 0) {
618 ImGui::Text(tr(
"VRAM Address: $%04X"), pal_meta.vram_address);
619 if (ImGui::IsItemClicked()) {
620 ImGui::SetClipboardText(
621 absl::StrFormat(
"$%04X", pal_meta.vram_address).c_str());
623 if (ImGui::IsItemHovered()) {
624 ImGui::SetTooltip(tr(
"Click to copy VRAM address"));
629 if (!pal_meta.usage_notes.empty()) {
631 ImGui::TextDisabled(tr(
"Usage Notes:"));
632 ImGui::TextWrapped(
"%s", pal_meta.usage_notes.c_str());
637 if (ImGui::BeginPopup(
"BatchOperations")) {
640 if (
ThemedButton(
"Copy Current Palette", ImVec2(-1, 0))) {
642 ImGui::CloseCurrentPopup();
645 if (
ThemedButton(
"Paste to Current Palette", ImVec2(-1, 0))) {
647 ImGui::CloseCurrentPopup();
652 if (
ThemedButton(
"Reset All Palettes", ImVec2(-1, 0))) {
654 ImGui::CloseCurrentPopup();
675 color_index, new_color);
679 absl::StrFormat(
"Failed to set color: %s", status.message()),
688 return absl::FailedPreconditionError(
689 "Cannot save palettes from an inactive ROM session");
693 palette_manager.GetModifiedGroupColorWriteRanges(
group_name_);
695 return ranges.status();
698 if (!ranges->empty()) {
702 if (!preflight.ok()) {
778 int color_index)
const {
815 if (!palette_group || index < 0 || index >= palette_group->size()) {
818 return palette_group->mutable_palette(index);
822 int color_index)
const {
838#if defined(YAZE_WITH_JSON)
840 if (!palette_group) {
850 for (
size_t palette_index = 0; palette_index < palette_group->size();
852 const auto& palette =
853 palette_group->palette_ref(
static_cast<int>(palette_index));
855 palette_json[
"index"] =
static_cast<int>(palette_index);
858 for (
size_t color_index = 0; color_index < palette.size(); color_index++) {
859 palette_json[
"colors"].push_back(
860 absl::StrFormat(
"$%04X", palette[color_index].snes()));
863 root[
"palettes"].push_back(palette_json);
874 return absl::FailedPreconditionError(
875 "Cannot import palettes into an inactive ROM session");
877#if !defined(YAZE_WITH_JSON)
878 return absl::UnimplementedError(
"JSON support is disabled");
881 if (!palette_group) {
882 return absl::FailedPreconditionError(
"Palette group is unavailable");
888 }
catch (
const std::exception& e) {
889 return absl::InvalidArgumentError(
890 absl::StrCat(
"Failed to parse palette JSON: ", e.what()));
894 return absl::InvalidArgumentError(
"Palette JSON must be an object");
898 const auto& version_value = root[
"version"];
899 if (!version_value.is_number_integer() &&
900 !version_value.is_number_unsigned()) {
901 return absl::InvalidArgumentError(
902 "Palette JSON 'version' must be an integer");
904 int version = version_value.get<
int>();
906 return absl::InvalidArgumentError(
907 absl::StrFormat(
"Unsupported palette JSON version: %d", version));
912 const auto& group_value = root[
"group"];
913 if (!group_value.is_string()) {
914 return absl::InvalidArgumentError(
915 "Palette JSON 'group' must be a string");
917 const std::string group = group_value.get<std::string>();
919 return absl::InvalidArgumentError(absl::StrFormat(
920 "Palette JSON group '%s' does not match '%s'", group,
group_name_));
925 return absl::InvalidArgumentError(
926 "Palette JSON must contain a 'palettes' array");
929 struct PaletteImport {
931 std::vector<uint16_t> colors;
934 std::vector<PaletteImport> imports;
935 for (
const auto& palette_json : root[
"palettes"]) {
936 if (!palette_json.is_object()) {
937 return absl::InvalidArgumentError(
"Palette entry must be a JSON object");
940 if (!palette_json.contains(
"index") ||
941 !palette_json[
"index"].is_number_integer()) {
942 return absl::InvalidArgumentError(
943 "Palette entry is missing integer 'index'");
946 int palette_index = palette_json[
"index"].get<
int>();
947 if (palette_index < 0 || palette_index >= palette_group->size()) {
948 return absl::InvalidArgumentError(absl::StrFormat(
949 "Palette index %d out of range [0, %d)", palette_index,
950 static_cast<int>(palette_group->size())));
953 if (!palette_json.contains(
"colors") ||
954 !palette_json[
"colors"].is_array()) {
955 return absl::InvalidArgumentError(
956 "Palette entry is missing 'colors' array");
959 std::vector<uint16_t> colors;
960 colors.reserve(palette_json[
"colors"].size());
961 for (
const auto& color_json : palette_json[
"colors"]) {
962 auto color_or = ParseSnesColorJson(color_json);
963 if (!color_or.ok()) {
964 return color_or.status();
966 colors.push_back(*color_or);
969 const auto& palette = palette_group->palette_ref(palette_index);
970 if (colors.size() != palette.size()) {
971 return absl::InvalidArgumentError(absl::StrFormat(
972 "Palette %d expects %d colors but received %d", palette_index,
973 static_cast<int>(palette.size()),
static_cast<int>(colors.size())));
976 imports.push_back({palette_index, std::move(colors)});
980 manager.BeginBatch();
981 for (
const auto&
import : imports) {
982 for (
size_t color_index = 0; color_index <
import.colors.size();
984 auto status = manager.SetColor(
985 group_name_,
import.index,
static_cast<int>(color_index),
995 return absl::OkStatus();
1008 for (
size_t i = 0; i < palette.size(); i++) {
1009 result += absl::StrFormat(
"$%04X", palette[i].snes());
1010 if (i < palette.size() - 1) {
1015 ImGui::SetClipboardText(result.c_str());
1021 return absl::FailedPreconditionError(
1022 "Cannot import palettes into an inactive ROM session");
1026 return absl::FailedPreconditionError(
"No palette selected");
1029 const char* clipboard = ImGui::GetClipboardText();
1030 if (!clipboard || clipboard[0] ==
'\0') {
1031 return absl::InvalidArgumentError(
"Clipboard is empty");
1034 auto colors_or = ParseClipboardColors(clipboard);
1035 if (!colors_or.ok()) {
1036 return colors_or.status();
1039 const auto& colors = *colors_or;
1040 if (colors.size() != palette->size()) {
1041 return absl::InvalidArgumentError(absl::StrFormat(
1042 "Clipboard contains %d colors but palette expects %d",
1043 static_cast<int>(colors.size()),
static_cast<int>(palette->size())));
1047 manager.BeginBatch();
1048 for (
size_t color_index = 0; color_index < colors.size(); color_index++) {
1050 static_cast<int>(color_index),
1059 return absl::OkStatus();
1085 for (
int i = 0; i < 6; i++) {
1088 pal.
name = absl::StrFormat(
"Overworld Main %02d", i);
1090 "BG main palette set (35 colors = 5x7, transparent slots are implicit)";
1094 "Loaded by PaletteLoad_OWBGMain to CGRAM $0042 (rows 2-6, cols 1-7).";
1111 ->palette_groups.get_group(
"ow_main");
1133 for (
int i = 0; i < 14; i++) {
1136 pal.
name = absl::StrFormat(
"OW Anim %02d", i);
1138 "Animated overlay palette (7 colors, transparent slot is implicit)";
1142 "Loaded by PaletteLoad_OWBG3 to CGRAM $00E2 (row 7, cols 1-7).";
1160 ->palette_groups.get_group(
"ow_animated");
1181 const char* dungeon_names[] = {
1182 "Sewers",
"Hyrule Castle",
"Eastern Palace",
"Desert Palace",
1183 "Agahnim's Tower",
"Swamp Palace",
"Palace of Darkness",
"Misery Mire",
1184 "Skull Woods",
"Ice Palace",
"Tower of Hera",
"Thieves' Town",
1185 "Turtle Rock",
"Ganon's Tower",
"Generic 1",
"Generic 2",
1186 "Generic 3",
"Generic 4",
"Generic 5",
"Generic 6"};
1188 for (
int i = 0; i < 20; i++) {
1191 pal.
name = dungeon_names[i];
1192 pal.
description = absl::StrFormat(
"Dungeon palette %d", i);
1196 "90 colors = 6 CGRAM banks x 15 colors (transparent slot per bank is "
1214 ->palette_groups.get_group(
"dungeon_main");
1234 const char* sprite_names[] = {
"Global Sprites (Light World)",
1235 "Global Sprites (Dark World)"};
1237 for (
int i = 0; i < 2; i++) {
1240 pal.
name = sprite_names[i];
1242 "60 colors = 4 sprite banks x 15 colors (transparent slots are "
1248 "Loaded into CGRAM rows 9-12, cols 1-15 (row col0 is transparent).";
1265 ->palette_groups.get_group(
"global_sprites");
1271 tr(
"Global sprite palettes are stored in ROM as 4 banks of 15 colors "
1272 "(transparent is implicit) and loaded to PPU CGRAM rows 9-12, cols "
1274 ImGui::TextDisabled(tr(
"Note: Palettes live in CGRAM, not VRAM."));
1293 const char* armor_names[] = {
"Green Mail",
"Blue Mail",
"Red Mail",
"Bunny",
1296 for (
int i = 0; i < 5; i++) {
1299 pal.
name = armor_names[i];
1300 pal.
description = absl::StrFormat(
"Link appearance: %s", armor_names[i]);
1304 "15 colors per set (transparent slot is implicit when loaded into "
1322 ->palette_groups.get_group(
"armors");
1341 for (
int i = 0; i < 12; i++) {
1344 pal.
name = absl::StrFormat(
"Sprites Aux1 %02d", i);
1346 "Auxiliary sprite palette (7 colors, transparent is implicit)";
1350 "Loaded into CGRAM with an implicit transparent slot at index 0 of the "
1368 ->palette_groups.get_group(
"sprites_aux1");
1387 for (
int i = 0; i < 11; i++) {
1390 pal.
name = absl::StrFormat(
"Sprites Aux2 %02d", i);
1392 "Auxiliary sprite palette (7 colors, transparent is implicit)";
1396 "Loaded into CGRAM with an implicit transparent slot at index 0 of the "
1414 ->palette_groups.get_group(
"sprites_aux2");
1433 for (
int i = 0; i < 24; i++) {
1436 pal.
name = absl::StrFormat(
"Sprites Aux3 %02d", i);
1438 "Auxiliary sprite palette (7 colors, transparent is implicit)";
1442 "Loaded into CGRAM with an implicit transparent slot at index 0 of the "
1460 ->palette_groups.get_group(
"sprites_aux3");
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.
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.
EquipmentPalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
static PaletteGroupMetadata InitializeMetadata()
static const PaletteGroupMetadata metadata_
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)
static const PaletteGroupMetadata metadata_
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
Base class for palette group editing cards.
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.
void DrawPaletteGrid()
Draw the palette grid specific to this palette type.
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 SetSelectedPaletteIndex(int index)
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.
void DrawContent(bool embedded)
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 int GetColorsPerRow() const =0
Get the number of colors per row for grid layout.
static PaletteGroupMetadata InitializeMetadata()
static const PaletteGroupMetadata metadata_
SpritePalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
void DrawCustomPanels() override
Draw additional panels (called after main content)
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
static PaletteGroupMetadata InitializeMetadata()
SpritesAux1PalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
static const PaletteGroupMetadata metadata_
gfx::PaletteGroup * GetPaletteGroup() override
Get the palette group for this card.
static const PaletteGroupMetadata metadata_
static PaletteGroupMetadata InitializeMetadata()
SpritesAux2PalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
static PaletteGroupMetadata InitializeMetadata()
SpritesAux3PalettePanel(Rom *rom, zelda3::GameData *game_data=nullptr)
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, std::string action_label={}, std::function< void()> on_action=nullptr)
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 the complete redo entry matches current colors. Conflicting history is retained,...
bool CanUndo() const
Check if the complete undo entry matches current colors. Conflicting history is retained,...
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.
SnesColor GetOriginalColor(const std::string &group_name, int palette_index, int color_index) const
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.
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.
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
static float GetStandardInputWidth()
#define ICON_MD_MORE_VERT
#define ICON_MD_FILE_DOWNLOAD
#define ICON_MD_MORE_HORIZ
#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).
void AutoRegisterLastItem(const std::string &widget_type, const std::string &explicit_label, const std::string &description)
Automatically register the last ImGui item.
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