6#include "absl/status/status.h"
7#include "absl/strings/str_cat.h"
8#include "absl/strings/str_format.h"
23#include "imgui/imgui.h"
30using ImGui::AcceptDragDropPayload;
31using ImGui::BeginChild;
32using ImGui::BeginDragDropTarget;
33using ImGui::BeginGroup;
34using ImGui::BeginPopup;
35using ImGui::BeginPopupContextItem;
37using ImGui::ColorButton;
38using ImGui::ColorPicker4;
40using ImGui::EndDragDropTarget;
44using ImGui::OpenPopup;
48using ImGui::Selectable;
49using ImGui::Separator;
50using ImGui::SetClipboardText;
56 ImGuiTableFlags_Reorderable | ImGuiTableFlags_Resizable |
57 ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Hideable;
59constexpr ImGuiColorEditFlags
kPalNoAlpha = ImGuiColorEditFlags_NoAlpha;
62 ImGuiColorEditFlags_NoPicker |
63 ImGuiColorEditFlags_NoTooltip;
66 ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha |
67 ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV |
68 ImGuiColorEditFlags_DisplayHex;
73 ImGui::GetForegroundDrawList()->AddRect(
79 if (!color || !BeginDragDropTarget()) {
84 constexpr ImGuiDragDropFlags kFlags =
85 ImGuiDragDropFlags_AcceptBeforeDelivery |
86 ImGuiDragDropFlags_AcceptNoDrawDefaultRect |
87 ImGuiDragDropFlags_AcceptNoPreviewTooltip;
88 auto accept = [&](
const char* type,
size_t components) {
92 if (
const ImGuiPayload* payload = AcceptDragDropPayload(type, kFlags)) {
94 if (payload->IsDelivery()) {
95 ImVec4 dropped(0, 0, 0, 1);
96 std::memcpy(&dropped.x, payload->Data,
sizeof(
float) * components);
97 if (components == 3) {
106 accept(IMGUI_PAYLOAD_TYPE_COLOR_3F, 3);
107 accept(IMGUI_PAYLOAD_TYPE_COLOR_4F, 4);
115#ifdef IMGUI_USE_STB_SPRINTF
116 int w = stbsp_vsnprintf(buf, (
int)buf_size, fmt, args);
118 int w = vsnprintf(buf, buf_size, fmt, args);
123 if (w == -1 || w >= (
int)buf_size)
124 w = (int)buf_size - 1;
129static inline float color_saturate(
float f) {
130 return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f;
133#define F32_TO_INT8_SAT(_VAL) \
134 ((int)(color_saturate(_VAL) * 255.0f + \
156 static ImVec4 color = ImVec4(0, 0, 0, 1.0f);
157 static ImVec4 current_palette[256] = {};
158 static int current_palette_count = 0;
159 ImGuiColorEditFlags misc_flags = ImGuiColorEditFlags_AlphaPreview |
160 ImGuiColorEditFlags_NoDragDrop |
161 ImGuiColorEditFlags_NoOptions;
165 current_palette_count =
166 std::min<int>(
static_cast<int>(palette.
size()),
167 static_cast<int>(IM_ARRAYSIZE(current_palette)));
169 for (
int n = 0; n < current_palette_count; ++n) {
170 const auto palette_color = palette[
static_cast<size_t>(n)];
171 current_palette[n].x = palette_color.rgb().x / 255.0f;
172 current_palette[n].y = palette_color.rgb().y / 255.0f;
173 current_palette[n].z = palette_color.rgb().z / 255.0f;
174 current_palette[n].w = 1.0f;
176 for (
int n = current_palette_count;
177 n < static_cast<int>(IM_ARRAYSIZE(current_palette)); ++n) {
178 current_palette[n] = ImVec4(0, 0, 0, 1.0f);
181 current_palette_count = 0;
184 static ImVec4 backup_color;
185 bool open_popup = ColorButton(
"MyColor##3b", color, misc_flags);
186 SameLine(0, GetStyle().ItemInnerSpacing.x);
187 open_popup |= Button(tr(
"Palette"));
192 backup_color = color;
199 ColorPicker4(
"##picker", (
float*)&color,
200 misc_flags | ImGuiColorEditFlags_NoSidePreview |
201 ImGuiColorEditFlags_NoSmallPreview);
205 Text(tr(
"Current ==>"));
207 Text(tr(
"Previous"));
209 if (Button(tr(
"Update Map Palette"))) {}
213 ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
218 "##previous", backup_color,
219 ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
221 color = backup_color;
226 if (current_palette_count <= 0) {
227 ImGui::TextDisabled(tr(
"No palette entries loaded."));
229 for (
int n = 0; n < current_palette_count; n++) {
232 SameLine(0.0f, GetStyle().ItemSpacing.y);
236 color = ImVec4(current_palette[n].x, current_palette[n].y,
237 current_palette[n].z, color.w);
239 if (BeginDragDropTarget()) {
240 if (
const ImGuiPayload* payload =
241 AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
242 memcpy((
float*)¤t_palette[n], payload->Data,
sizeof(
float) * 3);
243 if (
const ImGuiPayload* payload =
244 AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
245 memcpy((
float*)¤t_palette[n], payload->Data,
sizeof(
float) * 4);
255 return absl::OkStatus();
266 window_manager->RegisterPanel(
267 {.card_id =
"palette.control_panel",
268 .display_name =
"Palette Controls",
269 .window_title =
" Palette Controls",
271 .category =
"Palette",
272 .shortcut_hint =
"Ctrl+Shift+P",
276 .disabled_tooltip =
"Load a ROM first"});
278 window_manager->RegisterPanel(
279 {.card_id =
"palette.ow_main",
280 .display_name =
"Overworld Main",
281 .window_title =
" Overworld Main",
283 .category =
"Palette",
284 .shortcut_hint =
"Ctrl+Alt+1",
288 .disabled_tooltip =
"Load a ROM first"});
290 window_manager->RegisterPanel(
291 {.card_id =
"palette.ow_animated",
292 .display_name =
"Overworld Animated",
293 .window_title =
" Overworld Animated",
295 .category =
"Palette",
296 .shortcut_hint =
"Ctrl+Alt+2",
300 .disabled_tooltip =
"Load a ROM first"});
302 window_manager->RegisterPanel(
303 {.card_id =
"palette.dungeon_main",
304 .display_name =
"Dungeon Main",
305 .window_title =
" Dungeon Main",
307 .category =
"Palette",
308 .shortcut_hint =
"Ctrl+Alt+3",
312 .disabled_tooltip =
"Load a ROM first"});
314 window_manager->RegisterPanel(
315 {.card_id =
"palette.sprites",
316 .display_name =
"Global Sprite Palettes",
317 .window_title =
" SNES Palette",
319 .category =
"Palette",
320 .shortcut_hint =
"Ctrl+Alt+4",
324 .disabled_tooltip =
"Load a ROM first"});
326 window_manager->RegisterPanel(
327 {.card_id =
"palette.sprites_aux1",
328 .display_name =
"Sprites Aux 1",
329 .window_title =
" Sprites Aux 1",
331 .category =
"Palette",
332 .shortcut_hint =
"Ctrl+Alt+7",
336 .disabled_tooltip =
"Load a ROM first"});
338 window_manager->RegisterPanel(
339 {.card_id =
"palette.sprites_aux2",
340 .display_name =
"Sprites Aux 2",
341 .window_title =
" Sprites Aux 2",
343 .category =
"Palette",
344 .shortcut_hint =
"Ctrl+Alt+8",
348 .disabled_tooltip =
"Load a ROM first"});
350 window_manager->RegisterPanel(
351 {.card_id =
"palette.sprites_aux3",
352 .display_name =
"Sprites Aux 3",
353 .window_title =
" Sprites Aux 3",
355 .category =
"Palette",
356 .shortcut_hint =
"Ctrl+Alt+9",
360 .disabled_tooltip =
"Load a ROM first"});
362 window_manager->RegisterPanel(
363 {.card_id =
"palette.equipment",
364 .display_name =
"Equipment Palettes",
365 .window_title =
" Equipment Palettes",
367 .category =
"Palette",
368 .shortcut_hint =
"Ctrl+Alt+5",
372 .disabled_tooltip =
"Load a ROM first"});
374 window_manager->RegisterPanel(
375 {.card_id =
"palette.quick_access",
376 .display_name =
"Quick Access",
377 .window_title =
" Color Harmony",
379 .category =
"Palette",
380 .shortcut_hint =
"Ctrl+Alt+Q",
384 .disabled_tooltip =
"Load a ROM first"});
386 window_manager->RegisterPanel(
387 {.card_id =
"palette.custom",
388 .display_name =
"Custom Palette",
389 .window_title =
" Palette Editor",
391 .category =
"Palette",
392 .shortcut_hint =
"Ctrl+Alt+C",
396 .disabled_tooltip =
"Load a ROM first"});
399 window_manager->OpenWindow(session_id,
"palette.control_panel");
411 std::string
GetId()
const override {
return "palette.control_panel"; }
418 void Draw(
bool* p_open)
override {
419 if (p_open && !*p_open)
434 std::string
GetId()
const override {
return "palette.quick_access"; }
441 void Draw(
bool* p_open)
override {
442 if (p_open && !*p_open)
457 std::string
GetId()
const override {
return "palette.custom"; }
464 void Draw(
bool* p_open)
override {
465 if (p_open && !*p_open)
478 if (!
rom() || !
rom()->is_loaded()) {
479 return absl::NotFoundError(
"ROM not open, no palettes to display");
485 "Palette Group Name", std::to_string(i),
486 std::string(kPaletteGroupNames[i]));
507 std::make_unique<OverworldMainPalettePanel>(
rom_,
game_data());
509 window_manager->RegisterWindowContent(std::move(ow_main));
513 std::make_unique<OverworldAnimatedPalettePanel>(
rom_,
game_data());
515 window_manager->RegisterWindowContent(std::move(ow_anim));
519 std::make_unique<DungeonMainPalettePanel>(
rom_,
game_data());
521 window_manager->RegisterWindowContent(std::move(dungeon_main));
527 window_manager->RegisterWindowContent(std::move(sprite_global));
531 std::make_unique<SpritesAux1PalettePanel>(
rom_,
game_data());
533 window_manager->RegisterWindowContent(std::move(sprite_aux1));
537 std::make_unique<SpritesAux2PalettePanel>(
rom_,
game_data());
539 window_manager->RegisterWindowContent(std::move(sprite_aux2));
543 std::make_unique<SpritesAux3PalettePanel>(
rom_,
game_data());
545 window_manager->RegisterWindowContent(std::move(sprite_aux3));
548 auto equipment = std::make_unique<EquipmentPalettePanel>(
rom_,
game_data());
550 window_manager->RegisterWindowContent(std::move(equipment));
566 window_manager->RegisterWindowContent(std::make_unique<PaletteControlPanel>(
568 window_manager->RegisterWindowContent(
569 std::make_unique<QuickAccessPalettePanel>(
571 window_manager->RegisterWindowContent(std::make_unique<CustomPalettePanel>(
575 return absl::OkStatus();
580 return absl::FailedPreconditionError(
"ROM not loaded");
583 return absl::FailedPreconditionError(
584 "Cannot save palettes from an inactive ROM session");
593 return absl::OkStatus();
598 return absl::FailedPreconditionError(
599 "Cannot undo palettes from an inactive ROM session");
603 return absl::OkStatus();
608 return absl::FailedPreconditionError(
609 "Cannot redo palettes from an inactive ROM session");
613 return absl::OkStatus();
620 return absl::OkStatus();
624 BeginChild(
"QuickAccessPalettes", ImVec2(0, 0),
true);
626 Text(tr(
"Custom Palette"));
633 Text(tr(
"Current Color"));
643 CustomFormatString(buf, IM_ARRAYSIZE(buf),
"RGB: %d, %d, %d", cr, cg, cb);
646 CustomFormatString(buf, IM_ARRAYSIZE(buf),
"SNES: $%04X",
650 if (Button(tr(
"Copy to Clipboard"))) {
651 SetClipboardText(buf);
658 Text(tr(
"Recently Used Colors"));
663 ImVec4 displayColor =
665 if (ImGui::ColorButton(
"##recent", displayColor)) {
691 if (BeginChild(
"ColorPalette", ImVec2(0, 40), ImGuiChildFlags_None,
692 ImGuiWindowFlags_HorizontalScrollbar)) {
696 SameLine(0.0f, GetStyle().ItemSpacing.y);
700 bool open_color_picker = ImGui::ColorButton(
701 absl::StrFormat(
"##customPal%d", i).c_str(), displayColor);
702 const ImVec2 swatch_min = ImGui::GetItemRectMin();
703 const ImVec2 swatch_max = ImGui::GetItemRectMax();
704 if (ImGui::IsItemHovered() &&
705 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
706 open_color_picker =
true;
709 if (open_color_picker) {
713 "CustomPaletteColorEdit")
717 if (BeginPopupContextItem()) {
726 if (Button(tr(
"Delete"), ImVec2(-1, 0))) {
731 if (AcceptImGuiColorDrop(&
custom_palette_[i], swatch_min, swatch_max)) {
739 if (ImGui::Button(
"+")) {
744 if (ImGui::Button(tr(
"Clear"))) {
749 if (ImGui::Button(tr(
"Export"))) {
750 std::string clipboard;
752 clipboard += absl::StrFormat(
"$%04X,", color.snes());
754 SetClipboardText(clipboard.c_str());
760 if (ImGui::BeginPopup(
780 return absl::NotFoundError(
"ROM not open, no palettes to display");
783 auto palette_group_name = kPaletteGroupNames[category];
786 const auto size = palette_group->
size();
788 for (
int j = 0; j < size; j++) {
790 auto pal_size = palette->
size();
797 false, palette_group_name.data(), std::to_string(j),
801 for (
int n = 0; n < pal_size; n++) {
803 if (n > 0 && n % 8 != 0)
804 SameLine(0.0f, 2.0f);
807 absl::StrCat(kPaletteCategoryNames[category].data(), j,
"_", n);
810 if (ImGui::ColorButton(popup_id.c_str(), displayColor)) {
814 const ImVec2 swatch_min = ImGui::GetItemRectMin();
815 const ImVec2 swatch_max = ImGui::GetItemRectMax();
816 if (ImGui::IsItemHovered() &&
817 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
820 OpenPopup(popup_id.c_str());
823 const SnesColor original_color = (*palette)[n];
824 if (AcceptImGuiColorDrop(&(*palette)[n], swatch_min, swatch_max)) {
826 std::string(kPaletteGroupNames[category]),
827 j, n, original_color,
829 (*palette)[n].set_modified(
true);
834 if (BeginPopupContextItem(popup_id.c_str())) {
846 return absl::OkStatus();
851 auto it = std::find_if(
853 [&color](
const SnesColor& c) { return c.snes() == color.snes(); });
872 auto original_color = palette[n];
877 original_color, palette[n]);
878 palette[n].set_modified(
true);
890 Text(tr(
"RGB: %d, %d, %d"), cr, cg, cb);
891 Text(tr(
"SNES: $%04X"), palette[n].snes());
895 if (Button(tr(
"Copy as.."), ImVec2(-1, 0)))
902 CustomFormatString(buf, IM_ARRAYSIZE(buf),
"(%.3ff, %.3ff, %.3ff)", col[0],
905 SetClipboardText(buf);
907 CustomFormatString(buf, IM_ARRAYSIZE(buf),
"(%d,%d,%d)", cr, cg, cb);
909 SetClipboardText(buf);
911 CustomFormatString(buf, IM_ARRAYSIZE(buf),
"#%02X%02X%02X", cr, cg, cb);
913 SetClipboardText(buf);
916 CustomFormatString(buf, IM_ARRAYSIZE(buf),
"$%04X",
919 SetClipboardText(buf);
925 if (Button(tr(
"Add to Custom Palette"), ImVec2(-1, 0))) {
930 return absl::OkStatus();
935 if (index >= palette.
size()) {
936 return absl::InvalidArgumentError(
"Index out of bounds");
940 auto color = palette[index];
941 auto currentColor = color.rgb();
942 if (ColorPicker4(
"Color Picker", (
float*)&palette[index])) {
949 return absl::OkStatus();
955 if (index >= palette.
size() || index >= originalPalette.
size()) {
956 return absl::InvalidArgumentError(
"Index out of bounds");
958 auto color = originalPalette[index];
959 auto originalColor = color.rgb();
961 return absl::OkStatus();
976 tr(
"Palette controls are unavailable for an inactive ROM session."));
992 bool any_modified =
false;
995 ImGui::BulletText(tr(
"Overworld Main"));
999 ImGui::BulletText(tr(
"Overworld Animated"));
1000 any_modified =
true;
1003 ImGui::BulletText(tr(
"Dungeon Main"));
1004 any_modified =
true;
1007 ImGui::BulletText(tr(
"Global Sprite Palettes"));
1008 any_modified =
true;
1011 ImGui::BulletText(tr(
"Sprites Aux 1"));
1012 any_modified =
true;
1015 ImGui::BulletText(tr(
"Sprites Aux 2"));
1016 any_modified =
true;
1019 ImGui::BulletText(tr(
"Sprites Aux 3"));
1020 any_modified =
true;
1023 ImGui::BulletText(tr(
"Equipment Palettes"));
1024 any_modified =
true;
1027 if (!any_modified) {
1028 ImGui::TextDisabled(tr(
"No unsaved changes"));
1034 ImGui::Text(tr(
"Quick Actions:"));
1040 ImGui::BeginDisabled(!has_unsaved);
1042 absl::StrFormat(
ICON_MD_SAVE " Save All (%zu colors)", modified_count)
1049 absl::StrFormat(
"Failed to save palettes: %s", status.message()),
1054 ImGui::EndDisabled();
1056 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
1058 ImGui::SetTooltip(tr(
"Save all modified colors to ROM"));
1060 ImGui::SetTooltip(tr(
"No unsaved changes"));
1065 ImGui::BeginDisabled(!has_unsaved);
1074 ImGui::EndDisabled();
1076 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
1079 tr(
"Preview palette changes in other editors without saving to ROM"));
1081 ImGui::SetTooltip(tr(
"No changes to preview"));
1085 ImGui::BeginDisabled(!has_unsaved);
1086 if (ImGui::Button(
ICON_MD_UNDO " Discard All Changes", ImVec2(-1, 0))) {
1091 ImGui::EndDisabled();
1093 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
1095 ImGui::SetTooltip(tr(
"Discard all unsaved changes"));
1097 ImGui::SetTooltip(tr(
"No changes to discard"));
1102 if (ImGui::BeginPopupModal(
1106 nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
1107 ImGui::Text(tr(
"Discard all unsaved changes?"));
1109 tr(
"This will revert %zu modified colors."),
1113 if (ImGui::Button(tr(
"Discard"), ImVec2(120, 0))) {
1115 ImGui::CloseCurrentPopup();
1118 if (ImGui::Button(tr(
"Cancel"), ImVec2(120, 0))) {
1119 ImGui::CloseCurrentPopup();
1128 nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
1130 ImGui::Text(tr(
"An error occurred while saving to ROM."));
1133 if (ImGui::Button(tr(
"OK"), ImVec2(120, 0))) {
1134 ImGui::CloseCurrentPopup();
1146 ImGui::BeginGroup();
1147 ImGui::Text(tr(
"Current Color"));
1157 CustomFormatString(buf, IM_ARRAYSIZE(buf),
"RGB: %d, %d, %d", cr, cg, cb);
1158 ImGui::Text(
"%s", buf);
1160 CustomFormatString(buf, IM_ARRAYSIZE(buf),
"SNES: $%04X",
1162 ImGui::Text(
"%s", buf);
1164 if (ImGui::Button(tr(
"Copy to Clipboard"), ImVec2(-1, 0))) {
1165 SetClipboardText(buf);
1172 ImGui::Text(tr(
"Recently Used Colors"));
1174 ImGui::TextDisabled(tr(
"No recently used colors yet"));
1180 ImVec4 displayColor =
1187 if (ImGui::IsItemHovered()) {
1196 ImGui::TextWrapped(tr(
1197 "Create your own custom color palette for reference. "
1198 "Colors can be added from any palette group or created from scratch."));
1204 ImGui::TextDisabled(tr(
"Your custom palette is empty."));
1205 ImGui::Text(tr(
"Click + to add colors or drag colors from any palette."));
1209 if (i > 0 && i % 16 != 0)
1210 SameLine(0.0f, 2.0f);
1214 bool open_color_picker =
1215 ImGui::ColorButton(absl::StrFormat(
"##customPal%d", i).c_str(),
1217 const ImVec2 swatch_min = ImGui::GetItemRectMin();
1218 const ImVec2 swatch_max = ImGui::GetItemRectMax();
1219 if (ImGui::IsItemHovered() &&
1220 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
1221 open_color_picker =
true;
1224 if (open_color_picker) {
1228 "PanelCustomPaletteColorEdit")
1232 if (BeginPopupContextItem()) {
1241 if (ImGui::Button(tr(
"Delete"), ImVec2(-1, 0))) {
1243 ImGui::CloseCurrentPopup();
1248 if (AcceptImGuiColorDrop(&
custom_palette_[i], swatch_min, swatch_max)) {
1270 std::string clipboard;
1272 clipboard += absl::StrFormat(
"$%04X,", color.snes());
1274 if (!clipboard.empty()) {
1275 clipboard.pop_back();
1277 SetClipboardText(clipboard.c_str());
1279 if (ImGui::IsItemHovered()) {
1280 ImGui::SetTooltip(tr(
"Copy palette as comma-separated SNES values"));
1285 "PanelCustomPaletteColorEdit")
1302 int palette_index) {
1310 if (group_name ==
"ow_main") {
1311 window_manager->OpenWindow(session_id,
"palette.ow_main");
1316 }
else if (group_name ==
"ow_animated") {
1317 window_manager->OpenWindow(session_id,
"palette.ow_animated");
1322 }
else if (group_name ==
"dungeon_main") {
1323 window_manager->OpenWindow(session_id,
"palette.dungeon_main");
1328 }
else if (group_name ==
"global_sprites") {
1329 window_manager->OpenWindow(session_id,
"palette.sprites");
1334 }
else if (group_name ==
"sprites_aux1") {
1335 window_manager->OpenWindow(session_id,
"palette.sprites_aux1");
1340 }
else if (group_name ==
"sprites_aux2") {
1341 window_manager->OpenWindow(session_id,
"palette.sprites_aux2");
1346 }
else if (group_name ==
"sprites_aux3") {
1347 window_manager->OpenWindow(session_id,
"palette.sprites_aux3");
1352 }
else if (group_name ==
"armors") {
1353 window_manager->OpenWindow(session_id,
"palette.equipment");
1361 window_manager->OpenWindow(session_id,
"palette.control_panel");
1369 ImGui::SetNextItemWidth(-1);
1370 if (ImGui::InputTextWithHint(
"##PaletteSearch",
1387 if (group_name ==
"ow_main")
1389 if (group_name ==
"ow_animated")
1391 if (group_name ==
"dungeon_main")
1393 if (group_name ==
"global_sprites")
1395 if (group_name ==
"sprites_aux1")
1397 if (group_name ==
"sprites_aux2")
1399 if (group_name ==
"sprites_aux3")
1401 if (group_name ==
"armors")
1413 for (
size_t cat_idx = 0; cat_idx < categories.size(); cat_idx++) {
1414 const auto& cat = categories[cat_idx];
1417 bool has_visible_items =
false;
1418 for (
const auto& group_name : cat.group_names) {
1420 has_visible_items =
true;
1425 if (!has_visible_items)
1428 ImGui::PushID(
static_cast<int>(cat_idx));
1431 std::string header_text =
1432 absl::StrFormat(
"%s %s", cat.icon, cat.display_name);
1433 bool open = ImGui::CollapsingHeader(header_text.c_str(),
1434 ImGuiTreeNodeFlags_DefaultOpen);
1437 ImGui::Indent(10.0f);
1438 for (
const auto& group_name : cat.group_names) {
1452 std::optional<gui::StyleColorGuard> mod_guard;
1457 ImGui::Checkbox(label.c_str(), show_flag);
1461 ImGui::Unindent(10.0f);
1469 ImGui::Text(tr(
"Utilities:"));
1470 ImGui::Indent(10.0f);
1473 ImGui::Unindent(10.0f);
#define F32_TO_INT8_SAT(_VAL)
project::ResourceLabelManager * resource_label()
void set_dirty(bool dirty)
std::function< void()> draw_callback_
float GetPreferredWidth() const override
Get preferred width for this panel (optional)
std::string GetId() const override
Unique identifier for this panel.
std::string GetDisplayName() const override
Human-readable name shown in menus and title bars.
void Draw(bool *p_open) override
Draw the panel content.
CustomPalettePanel(std::function< void()> draw_callback)
std::string GetEditorCategory() const override
Editor category this panel belongs to.
int GetPriority() const override
Get display priority for menu ordering.
std::string GetIcon() const override
Material Design icon for this panel.
zelda3::GameData * game_data() const
EditorDependencies dependencies_
std::string GetIcon() const override
Material Design icon for this panel.
std::function< void()> draw_callback_
float GetPreferredWidth() const override
Get preferred width for this panel (optional)
std::string GetId() const override
Unique identifier for this panel.
void Draw(bool *p_open) override
Draw the panel content.
int GetPriority() const override
Get display priority for menu ordering.
std::string GetEditorCategory() const override
Editor category this panel belongs to.
PaletteControlPanel(std::function< void()> draw_callback)
std::string GetDisplayName() const override
Human-readable name shown in menus and title bars.
void DrawCategorizedPaletteList()
void Initialize() override
absl::Status DrawPaletteGroup(int category, bool right_side=false)
void DrawCustomPalettePanel()
bool * GetShowFlagForGroup(const std::string &group_name)
OverworldAnimatedPalettePanel * ow_anim_panel_
absl::Status ResetColorToOriginal(gfx::SnesPalette &palette, int index, const gfx::SnesPalette &originalPalette)
std::vector< gfx::SnesColor > custom_palette_
SpritesAux1PalettePanel * sprite_aux1_panel_
void DrawCustomPalette()
Draw custom palette editor with enhanced ROM hacking features.
bool show_sprites_aux3_panel_
bool show_sprites_aux1_panel_
void AddRecentlyUsedColor(const gfx::SnesColor &color)
absl::Status Update() override
bool PassesSearchFilter(const std::string &group_name) const
absl::Status Undo() override
bool show_dungeon_main_panel_
bool show_equipment_panel_
EquipmentPalettePanel * equipment_panel_
bool show_custom_palette_
SpritePalettePanel * sprite_global_panel_
absl::Status HandleColorPopup(gfx::SnesPalette &palette, int i, int j, int n)
bool show_ow_animated_panel_
gfx::SnesColor current_color_
void DrawQuickAccessPanel()
SpritesAux2PalettePanel * sprite_aux2_panel_
void DrawQuickAccessTab()
std::vector< gfx::SnesColor > recently_used_colors_
palette_internal::PaletteEditorHistory history_
absl::Status Save() override
bool show_sprites_aux2_panel_
absl::Status Load() override
void JumpToPalette(const std::string &group_name, int palette_index)
Jump to a specific palette by group and index.
DungeonMainPalettePanel * dungeon_main_panel_
SpritesAux3PalettePanel * sprite_aux3_panel_
OverworldMainPalettePanel * ow_main_panel_
absl::Status Redo() override
absl::Status EditColorInPalette(gfx::SnesPalette &palette, int index)
bool HasUnsavedChanges() const
void SetSelectedPaletteIndex(int index)
void SetToastManager(ToastManager *toast_manager)
QuickAccessPalettePanel(std::function< void()> draw_callback)
std::string GetId() const override
Unique identifier for this panel.
void Draw(bool *p_open) override
Draw the panel content.
std::string GetIcon() const override
Material Design icon for this panel.
std::string GetEditorCategory() const override
Editor category this panel belongs to.
int GetPriority() const override
Get display priority for menu ordering.
std::function< void()> draw_callback_
std::string GetDisplayName() const override
Human-readable name shown in menus and title bars.
float GetPreferredWidth() const override
Get preferred width for this panel (optional)
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
Base interface for all logical window content components.
void RecordChange(const std::string &group_name, size_t palette_index, size_t color_index, const gfx::SnesColor &original_color, const gfx::SnesColor &new_color)
bool HasUnsavedChanges() const
Check if there are ANY unsaved changes.
bool IsGroupModified(const std::string &group_name) const
Check if a specific palette group has modifications.
void Undo()
Undo the most recent change.
void Initialize(zelda3::GameData *game_data)
Initialize the palette manager with GameData.
void DiscardAllChanges()
Discard ALL unsaved changes.
size_t GetModifiedColorCount() const
Get count of modified colors across all groups.
static PaletteManager & Get()
Get the singleton instance.
absl::Status SaveAllToRom()
Save ALL modified palettes to ROM.
absl::Status ApplyPreviewChanges()
Apply preview changes to other editors without saving to ROM.
void Redo()
Redo the most recently undone change.
RAII timer for automatic timing management.
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).
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
#define ICON_MD_LANDSCAPE
#define ICON_MD_VISIBILITY
#define ICON_MD_CONTENT_COPY
#define ICON_MD_COLOR_LENS
#define TEXT_WITH_SEPARATOR(text)
bool AcceptImGuiColorDrop(SnesColor *color, ImVec2 min, ImVec2 max)
int CustomFormatString(char *buf, size_t buf_size, const char *fmt,...)
void DrawColorDropTargetOutline(ImVec2 min, ImVec2 max)
constexpr ImGuiTableFlags kPaletteTableFlags
constexpr ImGuiColorEditFlags kPalNoAlpha
const std::vector< PaletteCategoryInfo > & GetPaletteCategories()
Get all palette categories with their associated groups.
absl::Status DisplayPalette(gfx::SnesPalette &palette, bool loaded)
Display SNES palette with enhanced ROM hacking features.
constexpr ImGuiColorEditFlags kColorPopupFlags
constexpr ImGuiColorEditFlags kPalButtonFlags
constexpr int kNumPalettes
std::string GetGroupDisplayName(const std::string &group_name)
Get display name for a palette group.
uint16_t ConvertRgbToSnes(const snes_color &color)
Convert RGB (0-255) to SNES 15-bit color.
std::array< float, 4 > ToFloatArray(const SnesColor &color)
constexpr const char * kPalette
ImVec4 ConvertColorToImVec4(const Color &color)
bool FuzzyMatch(const std::string &pattern, const std::string &str)
Simple fuzzy match - returns true if all chars in pattern appear in str in order.
ImVec4 ConvertSnesColorToImVec4(const gfx::SnesColor &color)
Convert SnesColor to standard ImVec4 for display.
IMGUI_API bool SnesColorEdit4(absl::string_view label, gfx::SnesColor *color, ImGuiColorEditFlags flags)
std::string MakePopupId(size_t session_id, const std::string &editor_name, const std::string &popup_name)
Generate session-aware popup IDs to prevent conflicts in multi-editor layouts.
gfx::SnesColor ConvertImVec4ToSnesColor(const ImVec4 &color)
Convert standard ImVec4 to SnesColor.
#define RETURN_IF_ERROR(expr)
ToastManager * toast_manager
WorkspaceWindowManager * window_manager
PaletteGroup * get_group(const std::string &group_name)
Represents a group of palettes.
auto mutable_palette(int i)
std::string CreateOrGetLabel(const std::string &type, const std::string &key, const std::string &defaultValue)
void SelectableLabelWithNameEdit(bool selected, const std::string &type, const std::string &key, const std::string &defaultValue)
gfx::PaletteGroupMap palette_groups