7#include "absl/status/status.h"
8#include "absl/strings/str_cat.h"
9#include "absl/strings/str_format.h"
29#include "imgui/imgui.h"
36using ImGui::AcceptDragDropPayload;
37using ImGui::BeginChild;
38using ImGui::BeginDragDropTarget;
39using ImGui::BeginGroup;
40using ImGui::BeginPopup;
41using ImGui::BeginPopupContextItem;
43using ImGui::ColorButton;
44using ImGui::ColorPicker4;
46using ImGui::EndDragDropTarget;
50using ImGui::OpenPopup;
54using ImGui::Selectable;
55using ImGui::Separator;
56using ImGui::SetClipboardText;
62 ImGuiTableFlags_Reorderable | ImGuiTableFlags_Resizable |
63 ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Hideable;
65constexpr ImGuiColorEditFlags
kPalNoAlpha = ImGuiColorEditFlags_NoAlpha;
68 ImGuiColorEditFlags_NoPicker |
69 ImGuiColorEditFlags_NoTooltip;
72 ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha |
73 ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV |
74 ImGuiColorEditFlags_DisplayHex;
79 ImGui::GetForegroundDrawList()->AddRect(
85 if (!color || !BeginDragDropTarget()) {
90 constexpr ImGuiDragDropFlags kFlags =
91 ImGuiDragDropFlags_AcceptBeforeDelivery |
92 ImGuiDragDropFlags_AcceptNoDrawDefaultRect |
93 ImGuiDragDropFlags_AcceptNoPreviewTooltip;
94 auto accept = [&](
const char* type,
size_t components) {
98 if (
const ImGuiPayload* payload = AcceptDragDropPayload(type, kFlags)) {
100 if (payload->IsDelivery()) {
101 ImVec4 dropped(0, 0, 0, 1);
102 std::memcpy(&dropped.x, payload->Data,
sizeof(
float) * components);
103 if (components == 3) {
112 accept(IMGUI_PAYLOAD_TYPE_COLOR_3F, 3);
113 accept(IMGUI_PAYLOAD_TYPE_COLOR_4F, 4);
121#ifdef IMGUI_USE_STB_SPRINTF
122 int w = stbsp_vsnprintf(buf, (
int)buf_size, fmt, args);
124 int w = vsnprintf(buf, buf_size, fmt, args);
129 if (w == -1 || w >= (
int)buf_size)
130 w = (int)buf_size - 1;
135static inline float color_saturate(
float f) {
136 return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f;
139#define F32_TO_INT8_SAT(_VAL) \
140 ((int)(color_saturate(_VAL) * 255.0f + \
146 std::vector<gfx::SnesColor>& colors,
int index,
148 if (!BeginPopupContextItem()) {
153 on_edited(colors[index]);
158 bool removed =
false;
161 [&removed]() { removed =
true; },
165 colors.erase(colors.begin() + index);
189 static ImVec4 color = ImVec4(0, 0, 0, 1.0f);
190 static ImVec4 current_palette[256] = {};
191 static int current_palette_count = 0;
192 ImGuiColorEditFlags misc_flags = ImGuiColorEditFlags_AlphaPreview |
193 ImGuiColorEditFlags_NoDragDrop |
194 ImGuiColorEditFlags_NoOptions;
198 current_palette_count =
199 std::min<int>(
static_cast<int>(palette.
size()),
200 static_cast<int>(IM_ARRAYSIZE(current_palette)));
202 for (
int n = 0; n < current_palette_count; ++n) {
203 const auto palette_color = palette[
static_cast<size_t>(n)];
204 current_palette[n].x = palette_color.rgb().x / 255.0f;
205 current_palette[n].y = palette_color.rgb().y / 255.0f;
206 current_palette[n].z = palette_color.rgb().z / 255.0f;
207 current_palette[n].w = 1.0f;
209 for (
int n = current_palette_count;
210 n < static_cast<int>(IM_ARRAYSIZE(current_palette)); ++n) {
211 current_palette[n] = ImVec4(0, 0, 0, 1.0f);
214 current_palette_count = 0;
217 static ImVec4 backup_color;
218 bool open_popup = ColorButton(
"MyColor##3b", color, misc_flags);
219 SameLine(0, GetStyle().ItemInnerSpacing.x);
220 open_popup |= Button(tr(
"Palette"));
225 backup_color = color;
232 ColorPicker4(
"##picker", (
float*)&color,
233 misc_flags | ImGuiColorEditFlags_NoSidePreview |
234 ImGuiColorEditFlags_NoSmallPreview);
238 Text(tr(
"Current ==>"));
240 Text(tr(
"Previous"));
242 if (Button(tr(
"Update Map Palette"))) {}
246 ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
251 "##previous", backup_color,
252 ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
254 color = backup_color;
259 if (current_palette_count <= 0) {
260 ImGui::TextDisabled(tr(
"No palette entries loaded."));
262 for (
int n = 0; n < current_palette_count; n++) {
265 SameLine(0.0f, GetStyle().ItemSpacing.y);
269 color = ImVec4(current_palette[n].x, current_palette[n].y,
270 current_palette[n].z, color.w);
272 if (BeginDragDropTarget()) {
273 if (
const ImGuiPayload* payload =
274 AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
275 memcpy((
float*)¤t_palette[n], payload->Data,
sizeof(
float) * 3);
276 if (
const ImGuiPayload* payload =
277 AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
278 memcpy((
float*)¤t_palette[n], payload->Data,
sizeof(
float) * 4);
288 return absl::OkStatus();
299 window_manager->RegisterPanel(
300 {.card_id =
"palette.control_panel",
301 .display_name =
"Palette",
302 .window_title =
" Palette",
304 .category =
"Palette",
306 .shortcut_hint =
"Ctrl+Shift+P",
310 .disabled_tooltip =
"Load a ROM first"});
312 window_manager->RegisterPanel(
313 {.card_id =
"palette.ow_main",
314 .display_name =
"Overworld Main",
315 .window_title =
" Overworld Main",
317 .category =
"Palette",
319 .shortcut_hint =
"Ctrl+Alt+1",
323 .disabled_tooltip =
"Load a ROM first"});
325 window_manager->RegisterPanel(
326 {.card_id =
"palette.ow_animated",
327 .display_name =
"Overworld Animated",
328 .window_title =
" Overworld Animated",
330 .category =
"Palette",
332 .shortcut_hint =
"Ctrl+Alt+2",
336 .disabled_tooltip =
"Load a ROM first"});
338 window_manager->RegisterPanel(
339 {.card_id =
"palette.dungeon_main",
340 .display_name =
"Dungeon Main",
341 .window_title =
" Dungeon Main",
343 .category =
"Palette",
345 .shortcut_hint =
"Ctrl+Alt+3",
349 .disabled_tooltip =
"Load a ROM first"});
351 window_manager->RegisterPanel(
352 {.card_id =
"palette.sprites",
353 .display_name =
"Global Sprite Palettes",
354 .window_title =
" SNES Palette",
356 .category =
"Palette",
358 .shortcut_hint =
"Ctrl+Alt+4",
362 .disabled_tooltip =
"Load a ROM first"});
364 window_manager->RegisterPanel(
365 {.card_id =
"palette.sprites_aux1",
366 .display_name =
"Sprites Aux 1",
367 .window_title =
" Sprites Aux 1",
369 .category =
"Palette",
371 .shortcut_hint =
"Ctrl+Alt+7",
375 .disabled_tooltip =
"Load a ROM first"});
377 window_manager->RegisterPanel(
378 {.card_id =
"palette.sprites_aux2",
379 .display_name =
"Sprites Aux 2",
380 .window_title =
" Sprites Aux 2",
382 .category =
"Palette",
384 .shortcut_hint =
"Ctrl+Alt+8",
388 .disabled_tooltip =
"Load a ROM first"});
390 window_manager->RegisterPanel(
391 {.card_id =
"palette.sprites_aux3",
392 .display_name =
"Sprites Aux 3",
393 .window_title =
" Sprites Aux 3",
395 .category =
"Palette",
397 .shortcut_hint =
"Ctrl+Alt+9",
401 .disabled_tooltip =
"Load a ROM first"});
403 window_manager->RegisterPanel(
404 {.card_id =
"palette.equipment",
405 .display_name =
"Equipment Palettes",
406 .window_title =
" Equipment Palettes",
408 .category =
"Palette",
410 .shortcut_hint =
"Ctrl+Alt+5",
414 .disabled_tooltip =
"Load a ROM first"});
416 window_manager->RegisterPanel(
417 {.card_id =
"palette.quick_access",
418 .display_name =
"Quick Access",
419 .window_title =
" Color Harmony",
421 .category =
"Palette",
423 .shortcut_hint =
"Ctrl+Alt+Q",
427 .disabled_tooltip =
"Load a ROM first"});
429 window_manager->RegisterPanel(
430 {.card_id =
"palette.custom",
431 .display_name =
"Custom Palette",
432 .window_title =
" Palette Editor",
434 .category =
"Palette",
436 .shortcut_hint =
"Ctrl+Alt+C",
440 .disabled_tooltip =
"Load a ROM first"});
443 window_manager->OpenWindow(session_id,
"palette.control_panel");
455 std::string
GetId()
const override {
return "palette.control_panel"; }
465 void Draw(
bool* p_open)
override {
466 if (p_open && !*p_open)
481 std::string
GetId()
const override {
return "palette.quick_access"; }
491 void Draw(
bool* p_open)
override {
492 if (p_open && !*p_open)
507 std::string
GetId()
const override {
return "palette.custom"; }
517 void Draw(
bool* p_open)
override {
518 if (p_open && !*p_open)
535template <
typename Panel>
537 size_t session_id,
const char* panel_id,
539 auto* panel =
dynamic_cast<Panel*
>(
541 return panel !=
nullptr && panel->IsOwnedByRom(rom) ? panel :
nullptr;
547 if (window_manager ==
nullptr) {
560 ow_main_panel_ = ResolveSessionPalettePanel<OverworldMainPalettePanel>(
561 window_manager, session_id,
"palette.ow_main",
rom_);
562 ow_anim_panel_ = ResolveSessionPalettePanel<OverworldAnimatedPalettePanel>(
563 window_manager, session_id,
"palette.ow_animated",
rom_);
565 window_manager, session_id,
"palette.dungeon_main",
rom_);
567 window_manager, session_id,
"palette.global_sprites",
rom_);
569 window_manager, session_id,
"palette.sprites_aux1",
rom_);
571 window_manager, session_id,
"palette.sprites_aux2",
rom_);
573 window_manager, session_id,
"palette.sprites_aux3",
rom_);
575 window_manager, session_id,
"palette.armors",
rom_);
583 if (panel ==
nullptr) {
602 if (!
rom() || !
rom()->is_loaded()) {
603 return absl::NotFoundError(
"ROM not open, no palettes to display");
609 "Palette Group Name", std::to_string(i),
610 std::string(kPaletteGroupNames[i]));
631 std::make_unique<OverworldMainPalettePanel>(
rom_,
game_data());
633 window_manager->RegisterWindowContent(std::move(ow_main));
637 std::make_unique<OverworldAnimatedPalettePanel>(
rom_,
game_data());
639 window_manager->RegisterWindowContent(std::move(ow_anim));
643 std::make_unique<DungeonMainPalettePanel>(
rom_,
game_data());
645 window_manager->RegisterWindowContent(std::move(dungeon_main));
651 window_manager->RegisterWindowContent(std::move(sprite_global));
655 std::make_unique<SpritesAux1PalettePanel>(
rom_,
game_data());
657 window_manager->RegisterWindowContent(std::move(sprite_aux1));
661 std::make_unique<SpritesAux2PalettePanel>(
rom_,
game_data());
663 window_manager->RegisterWindowContent(std::move(sprite_aux2));
667 std::make_unique<SpritesAux3PalettePanel>(
rom_,
game_data());
669 window_manager->RegisterWindowContent(std::move(sprite_aux3));
672 auto equipment = std::make_unique<EquipmentPalettePanel>(
rom_,
game_data());
674 window_manager->RegisterWindowContent(std::move(equipment));
679 window_manager->RegisterWindowContent(std::make_unique<PaletteControlPanel>(
681 window_manager->RegisterWindowContent(
682 std::make_unique<QuickAccessPalettePanel>(
684 window_manager->RegisterWindowContent(std::make_unique<CustomPalettePanel>(
688 return absl::OkStatus();
694 return absl::FailedPreconditionError(
695 "Cannot save palettes from an inactive ROM session");
697 return manager.BeginSaveTransaction();
710 return absl::FailedPreconditionError(
"ROM not loaded");
713 return absl::FailedPreconditionError(
714 "Cannot save palettes from an inactive ROM session");
721 palette_manager.GetModifiedColorWriteRanges(
game_data());
722 if (!ranges.empty()) {
736 return absl::OkStatus();
751 return absl::FailedPreconditionError(
752 "Cannot undo palettes from an inactive ROM session");
756 return absl::OkStatus();
761 return absl::FailedPreconditionError(
762 "Cannot redo palettes from an inactive ROM session");
766 return absl::OkStatus();
773 return absl::OkStatus();
777 BeginChild(
"QuickAccessPalettes", ImVec2(0, 0),
true);
779 Text(tr(
"Custom Palette"));
786 Text(tr(
"Current Color"));
796 CustomFormatString(buf, IM_ARRAYSIZE(buf),
"RGB: %d, %d, %d", cr, cg, cb);
799 CustomFormatString(buf, IM_ARRAYSIZE(buf),
"SNES: $%04X",
803 if (Button(tr(
"Copy to Clipboard"))) {
804 SetClipboardText(buf);
811 Text(tr(
"Recently Used Colors"));
816 ImVec4 displayColor =
818 if (ImGui::ColorButton(
"##recent", displayColor)) {
844 if (BeginChild(
"ColorPalette", ImVec2(0, 40), ImGuiChildFlags_None,
845 ImGuiWindowFlags_HorizontalScrollbar)) {
849 SameLine(0.0f, GetStyle().ItemSpacing.y);
853 bool open_color_picker = ImGui::ColorButton(
854 absl::StrFormat(
"##customPal%d", i).c_str(), displayColor);
855 const ImVec2 swatch_min = ImGui::GetItemRectMin();
856 const ImVec2 swatch_max = ImGui::GetItemRectMax();
857 if (ImGui::IsItemHovered() &&
858 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
859 open_color_picker =
true;
862 if (open_color_picker) {
866 "CustomPaletteColorEdit")
870 if (DrawCustomColorContextMenu(
877 if (AcceptImGuiColorDrop(&
custom_palette_[i], swatch_min, swatch_max)) {
885 if (ImGui::Button(
"+")) {
890 if (ImGui::Button(tr(
"Clear"))) {
895 if (ImGui::Button(tr(
"Export"))) {
896 std::string clipboard;
898 clipboard += absl::StrFormat(
"$%04X,", color.snes());
900 SetClipboardText(clipboard.c_str());
906 if (ImGui::BeginPopup(
926 return absl::NotFoundError(
"ROM not open, no palettes to display");
929 auto palette_group_name = kPaletteGroupNames[category];
932 const auto size = palette_group->
size();
934 for (
int j = 0; j < size; j++) {
936 auto pal_size = palette->
size();
943 false, palette_group_name.data(), std::to_string(j),
947 for (
int n = 0; n < pal_size; n++) {
949 if (n > 0 && n % 8 != 0)
950 SameLine(0.0f, 2.0f);
953 absl::StrCat(kPaletteCategoryNames[category].data(), j,
"_", n);
956 if (ImGui::ColorButton(popup_id.c_str(), displayColor)) {
960 const ImVec2 swatch_min = ImGui::GetItemRectMin();
961 const ImVec2 swatch_max = ImGui::GetItemRectMax();
962 if (ImGui::IsItemHovered() &&
963 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
966 OpenPopup(popup_id.c_str());
969 const SnesColor original_color = (*palette)[n];
970 if (AcceptImGuiColorDrop(&(*palette)[n], swatch_min, swatch_max)) {
972 std::string(kPaletteGroupNames[category]),
973 j, n, original_color,
975 (*palette)[n].set_modified(
true);
980 if (BeginPopupContextItem(popup_id.c_str())) {
992 return absl::OkStatus();
997 auto it = std::find_if(
999 [&color](
const SnesColor& c) { return c.snes() == color.snes(); });
1018 auto original_color = palette[n];
1023 original_color, palette[n]);
1024 palette[n].set_modified(
true);
1036 Text(tr(
"RGB: %d, %d, %d"), cr, cg, cb);
1037 Text(tr(
"SNES: $%04X"), palette[n].snes());
1043 CustomFormatString(buf, IM_ARRAYSIZE(buf),
"(%.3ff, %.3ff, %.3ff)", col[0],
1046 SetClipboardText(buf);
1052 if (ImGui::MenuItem(
ICON_MD_ADD " Add to Custom Palette")) {
1057 return absl::OkStatus();
1062 if (index >= palette.
size()) {
1063 return absl::InvalidArgumentError(
"Index out of bounds");
1067 auto color = palette[index];
1068 auto currentColor = color.rgb();
1069 if (ColorPicker4(
"Color Picker", (
float*)&palette[index])) {
1076 return absl::OkStatus();
1082 if (index >= palette.
size() || index >= originalPalette.
size()) {
1083 return absl::InvalidArgumentError(
"Index out of bounds");
1085 auto color = originalPalette[index];
1086 auto originalColor = color.rgb();
1088 return absl::OkStatus();
1098 ImGui::TextDisabled(
1099 tr(
"Palette controls are unavailable for an inactive ROM session."));
1104 const size_t modified_count = manager.GetModifiedColorCount();
1105 const bool pending = manager.HasUnsavedChanges() || (
rom_ &&
rom_->
dirty());
1107 const bool save = ImGui::Button(
ICON_MD_SAVE " Save ROM");
1111 ImGui::EndDisabled();
1112 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
1114 tr(
"Save through the application, including pending editor changes."));
1116 ImGui::BeginDisabled(!
CanUndo());
1120 ImGui::EndDisabled();
1122 ImGui::BeginDisabled(!
CanRedo());
1126 ImGui::EndDisabled();
1129 ImGui::OpenPopup(
"PaletteTools");
1131 bool discard_requested =
false;
1132 if (ImGui::BeginPopup(
"PaletteTools")) {
1133 if (ImGui::MenuItem(tr(
"Refresh editor previews"))) {
1134 const auto result = manager.ApplyPreviewChanges();
1139 if (ImGui::MenuItem(tr(
"Discard palette changes..."),
nullptr,
false,
1140 manager.HasUnsavedChanges()))
1141 discard_requested =
true;
1144 if (ImGui::MenuItem(tr(
"Color reference")))
1146 "palette.quick_access");
1147 if (ImGui::MenuItem(tr(
"Custom swatches")))
1152 if (discard_requested)
1153 ImGui::OpenPopup(
"Discard palette changes?###ConfirmPaletteDiscard");
1154 if (ImGui::BeginPopupModal(
"Discard palette changes?###ConfirmPaletteDiscard",
1155 nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
1156 ImGui::Text(tr(
"Discard changes to %zu colors?"), modified_count);
1157 if (ImGui::Button(tr(
"Discard"))) {
1158 manager.DiscardAllChanges();
1159 ImGui::CloseCurrentPopup();
1162 if (ImGui::Button(tr(
"Cancel")))
1163 ImGui::CloseCurrentPopup();
1169 ImGui::TextDisabled(tr(
"%zu edited colors"), modified_count);
1171 ImGui::TextDisabled(pending ? tr(
"ROM has unsaved changes")
1172 : tr(
"No pending palette edits"));
1186 const float nav_width =
1187 std::clamp(ImGui::GetContentRegionAvail().x * 0.22f,
1188 ImGui::GetFontSize() * 11, ImGui::GetFontSize() * 16);
1189 ImGui::BeginChild(
"PaletteGroups", ImVec2(nav_width, 0));
1193 ImGui::BeginChild(
"PaletteSelection", ImVec2(0, 0));
1202 panel->DrawEmbedded();
1210 ImGui::BeginGroup();
1211 ImGui::Text(tr(
"Current Color"));
1221 CustomFormatString(buf, IM_ARRAYSIZE(buf),
"RGB: %d, %d, %d", cr, cg, cb);
1222 ImGui::Text(
"%s", buf);
1224 CustomFormatString(buf, IM_ARRAYSIZE(buf),
"SNES: $%04X",
1226 ImGui::Text(
"%s", buf);
1228 if (ImGui::Button(tr(
"Copy to Clipboard"), ImVec2(-1, 0))) {
1229 SetClipboardText(buf);
1236 ImGui::Text(tr(
"Recently Used Colors"));
1238 ImGui::TextDisabled(tr(
"No recently used colors yet"));
1244 ImVec4 displayColor =
1251 if (ImGui::IsItemHovered()) {
1260 ImGui::TextWrapped(tr(
1261 "Create your own custom color palette for reference. "
1262 "Colors can be added from any palette group or created from scratch."));
1268 ImGui::TextDisabled(tr(
"Your custom palette is empty."));
1269 ImGui::Text(tr(
"Click + to add colors or drag colors from any palette."));
1273 if (i > 0 && i % 16 != 0)
1274 SameLine(0.0f, 2.0f);
1278 bool open_color_picker =
1279 ImGui::ColorButton(absl::StrFormat(
"##customPal%d", i).c_str(),
1281 const ImVec2 swatch_min = ImGui::GetItemRectMin();
1282 const ImVec2 swatch_max = ImGui::GetItemRectMax();
1283 if (ImGui::IsItemHovered() &&
1284 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
1285 open_color_picker =
true;
1288 if (open_color_picker) {
1292 "PanelCustomPaletteColorEdit")
1296 if (DrawCustomColorContextMenu(
1303 if (AcceptImGuiColorDrop(&
custom_palette_[i], swatch_min, swatch_max)) {
1325 std::string clipboard;
1327 clipboard += absl::StrFormat(
"$%04X,", color.snes());
1329 if (!clipboard.empty()) {
1330 clipboard.pop_back();
1332 SetClipboardText(clipboard.c_str());
1334 if (ImGui::IsItemHovered()) {
1335 ImGui::SetTooltip(tr(
"Copy palette as comma-separated SNES values"));
1340 "PanelCustomPaletteColorEdit")
1357 if (group ==
"ow_main")
1359 if (group ==
"ow_animated")
1361 if (group ==
"dungeon_main")
1363 if (group ==
"global_sprites")
1365 if (group ==
"sprites_aux1")
1367 if (group ==
"sprites_aux2")
1369 if (group ==
"sprites_aux3")
1371 if (group ==
"armors")
1377 int palette_index) {
1383 panel->SetSelectedPaletteIndex(palette_index);
1385 "palette.control_panel");
1394 ImGui::SetNextItemWidth(-1);
1395 if (ImGui::InputTextWithHint(
"##PaletteSearch",
1414 bool heading_drawn =
false;
1415 for (
const auto& group : category.group_names) {
1418 if (!heading_drawn) {
1420 ImGui::TextDisabled(
"%s", category.display_name.c_str());
1421 heading_drawn =
true;
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
project::ResourceLabelManager * resource_label()
void set_dirty(bool dirty)
bool loaded() const
Check if the manifest has been loaded.
WindowPresentationPolicy GetPresentationPolicy() const override
How this content is hosted and advertised by workspace chrome.
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.
virtual void SetDependencies(const EditorDependencies &deps)
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.
WindowPresentationPolicy GetPresentationPolicy() const override
How this content is hosted and advertised by workspace chrome.
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()
OverworldAnimatedPalettePanel * ow_anim_panel_
bool CanUndo() const override
absl::Status ResetColorToOriginal(gfx::SnesPalette &palette, int index, const gfx::SnesPalette &originalPalette)
std::vector< gfx::SnesColor > custom_palette_
SpritesAux1PalettePanel * sprite_aux1_panel_
void CommitSaveTransaction() override
void RollbackSaveTransaction() override
void DrawCustomPalette()
Draw custom palette editor with enhanced ROM hacking features.
bool show_sprites_aux3_panel_
absl::Status BeginSaveTransaction() override
bool show_sprites_aux1_panel_
void SetDependencies(const EditorDependencies &deps) override
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_
std::string selected_group_
void RefreshPanelReferences()
bool show_custom_palette_
void RefreshPaletteCatalogIfChanged()
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_
bool CanRedo() const override
void DrawQuickAccessTab()
std::vector< gfx::SnesColor > recently_used_colors_
palette_internal::PaletteEditorHistory history_
PaletteGroupPanel * GetPanelForGroup(const std::string &group_name)
absl::Status Save() override
bool show_sprites_aux2_panel_
absl::Status Load() override
void DrawPaletteCatalog()
void ApplyPanelDependencies()
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)
Base class for palette group editing cards.
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)
WindowPresentationPolicy GetPresentationPolicy() const override
How this content is hosted and advertised by workspace chrome.
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)
Base interface for all logical window content components.
Central registry for all editor cards with session awareness and dependency injection.
bool OpenWindow(size_t session_id, const std::string &base_window_id)
WindowContent * GetWindowContent(const std::string &window_id)
Get a WindowContent instance by ID.
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)
void RollbackSaveTransaction()
void Undo()
Undo the most recent change.
void CommitSaveTransaction()
void Initialize(zelda3::GameData *game_data)
Initialize the palette manager with GameData.
static PaletteManager & Get()
Get the singleton instance.
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_MORE_HORIZ
#define ICON_MD_OPEN_IN_NEW
#define ICON_MD_CONTENT_COPY
#define ICON_MD_COLOR_LENS
#define TEXT_WITH_SEPARATOR(text)
bool DrawCustomColorContextMenu(std::vector< gfx::SnesColor > &colors, int index, const std::function< void(const gfx::SnesColor &)> &on_edited)
bool AcceptImGuiColorDrop(SnesColor *color, ImVec2 min, ImVec2 max)
Panel * ResolveSessionPalettePanel(WorkspaceWindowManager *window_manager, size_t session_id, const char *panel_id, Rom *rom)
int CustomFormatString(char *buf, size_t buf_size, const char *fmt,...)
void DrawColorDropTargetOutline(ImVec2 min, ImVec2 max)
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 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
std::string GetGroupDisplayName(const std::string &group_name)
Get display name for a palette group.
std::array< float, 4 > ToFloatArray(const SnesColor &color)
constexpr int kNumPalettes
constexpr const char * kPalette
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.
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.
void AutoRegisterLastItem(const std::string &widget_type, const std::string &explicit_label, const std::string &description)
Automatically register the last ImGui item.
void SnesColorCopyMenuItems(const gfx::SnesColor &color)
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)
void RenderMenuItem(const CanvasMenuItem &item, std::function< void(const std::string &, std::function< void()>)> popup_opened_callback)
Render a single menu item.
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 F32_TO_INT8_SAT(_VAL)
#define RETURN_IF_ERROR(expr)
Unified dependency container for all editor types.
project::YazeProject * project
ToastManager * toast_manager
WorkspaceWindowManager * window_manager
static WindowPresentationPolicy CoreWorkspace()
static WindowPresentationPolicy OptionalPopOut()
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)
core::HackManifest hack_manifest
gfx::PaletteGroupMap palette_groups