Files
yaze/src/app/editor/graphics/palette_controls_panel.cc

302 lines
9.3 KiB
C++

#include "app/editor/graphics/palette_controls_panel.h"
#include "absl/strings/str_format.h"
#include "app/gfx/resource/arena.h"
#include "app/gfx/types/snes_palette.h"
#include "app/gui/core/icons.h"
#include "app/gui/core/style.h"
#include "imgui/imgui.h"
namespace yaze {
namespace editor {
using gfx::kPaletteGroupAddressesKeys;
void PaletteControlsPanel::Initialize() {
// Initialize with default palette group
state_->palette_group_index = 0;
state_->palette_index = 0;
state_->sub_palette_index = 0;
}
void PaletteControlsPanel::Draw(bool* p_open) {
// EditorPanel interface - delegate to existing Update() logic
if (!rom_ || !rom_->is_loaded()) {
ImGui::TextDisabled("Load a ROM to manage palettes");
return;
}
DrawPresets();
ImGui::Separator();
DrawPaletteGroupSelector();
ImGui::Separator();
DrawPaletteDisplay();
ImGui::Separator();
DrawApplyButtons();
}
absl::Status PaletteControlsPanel::Update() {
if (!rom_ || !rom_->is_loaded()) {
ImGui::TextDisabled("Load a ROM to manage palettes");
return absl::OkStatus();
}
DrawPresets();
ImGui::Separator();
DrawPaletteGroupSelector();
ImGui::Separator();
DrawPaletteDisplay();
ImGui::Separator();
DrawApplyButtons();
return absl::OkStatus();
}
void PaletteControlsPanel::DrawPresets() {
gui::TextWithSeparators("Quick Presets");
if (ImGui::Button(ICON_MD_LANDSCAPE " Overworld")) {
state_->palette_group_index = 0; // Dungeon Main (used for overworld too)
state_->palette_index = 0;
state_->refresh_graphics = true;
}
HOVER_HINT("Standard overworld palette");
ImGui::SameLine();
if (ImGui::Button(ICON_MD_CASTLE " Dungeon")) {
state_->palette_group_index = 0; // Dungeon Main
state_->palette_index = 1;
state_->refresh_graphics = true;
}
HOVER_HINT("Standard dungeon palette");
ImGui::SameLine();
if (ImGui::Button(ICON_MD_PERSON " Sprites")) {
state_->palette_group_index = 4; // Sprites Aux1
state_->palette_index = 0;
state_->refresh_graphics = true;
}
HOVER_HINT("Sprite/enemy palette");
if (ImGui::Button(ICON_MD_ACCOUNT_BOX " Link")) {
state_->palette_group_index = 3; // Sprite Aux3 (Link's palettes)
state_->palette_index = 0;
state_->refresh_graphics = true;
}
HOVER_HINT("Link's palette");
ImGui::SameLine();
if (ImGui::Button(ICON_MD_MENU " HUD")) {
state_->palette_group_index = 6; // HUD palettes
state_->palette_index = 0;
state_->refresh_graphics = true;
}
HOVER_HINT("HUD/menu palette");
}
void PaletteControlsPanel::DrawPaletteGroupSelector() {
gui::TextWithSeparators("Palette Selection");
// Palette group combo
ImGui::SetNextItemWidth(160);
if (ImGui::Combo("Group", reinterpret_cast<int*>(&state_->palette_group_index),
kPaletteGroupAddressesKeys,
IM_ARRAYSIZE(kPaletteGroupAddressesKeys))) {
state_->refresh_graphics = true;
}
// Palette index within group
ImGui::SetNextItemWidth(100);
int palette_idx = static_cast<int>(state_->palette_index);
if (ImGui::InputInt("Palette", &palette_idx)) {
state_->palette_index = static_cast<uint64_t>(std::max(0, palette_idx));
state_->refresh_graphics = true;
}
HOVER_HINT("Palette index within the group");
// Sub-palette index (for multi-row palettes)
ImGui::SetNextItemWidth(100);
int sub_idx = static_cast<int>(state_->sub_palette_index);
if (ImGui::InputInt("Sub-Palette", &sub_idx)) {
state_->sub_palette_index = static_cast<uint64_t>(std::max(0, sub_idx));
state_->refresh_graphics = true;
}
HOVER_HINT("Sub-palette row (0-7 for SNES 128-color palettes)");
}
void PaletteControlsPanel::DrawPaletteDisplay() {
gui::TextWithSeparators("Current Palette");
// Get the current palette from GameData
if (!game_data_) return;
auto palette_group_result = game_data_->palette_groups.get_group(
kPaletteGroupAddressesKeys[state_->palette_group_index]);
if (!palette_group_result) {
ImGui::TextDisabled("Invalid palette group");
return;
}
auto palette_group = *palette_group_result;
if (state_->palette_index >= palette_group.size()) {
ImGui::TextDisabled("Invalid palette index");
return;
}
auto palette = palette_group.palette(state_->palette_index);
// Display palette colors in rows of 16
int colors_per_row = 16;
int total_colors = static_cast<int>(palette.size());
int num_rows = (total_colors + colors_per_row - 1) / colors_per_row;
for (int row = 0; row < num_rows; row++) {
for (int col = 0; col < colors_per_row; col++) {
int idx = row * colors_per_row + col;
if (idx >= total_colors) break;
if (col > 0) ImGui::SameLine();
auto& color = palette[idx];
ImVec4 im_color(color.rgb().x / 255.0f, color.rgb().y / 255.0f,
color.rgb().z / 255.0f, 1.0f);
// Highlight current sub-palette row
bool in_sub_palette =
(row == static_cast<int>(state_->sub_palette_index));
if (in_sub_palette) {
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 2.0f);
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 1.0f, 0.0f, 1.0f));
}
std::string id = absl::StrFormat("##PalColor%d", idx);
if (ImGui::ColorButton(id.c_str(), im_color,
ImGuiColorEditFlags_NoTooltip, ImVec2(18, 18))) {
// Clicking a color in a row selects that sub-palette
state_->sub_palette_index = static_cast<uint64_t>(row);
state_->refresh_graphics = true;
}
if (in_sub_palette) {
ImGui::PopStyleColor();
ImGui::PopStyleVar();
}
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::Text("Index: %d (Row %d, Col %d)", idx, row, col);
ImGui::Text("SNES: $%04X", color.snes());
ImGui::Text("RGB: %d, %d, %d", static_cast<int>(color.rgb().x),
static_cast<int>(color.rgb().y),
static_cast<int>(color.rgb().z));
ImGui::EndTooltip();
}
}
}
// Row selection buttons
ImGui::Text("Sub-palette Row:");
for (int i = 0; i < std::min(8, num_rows); i++) {
if (i > 0) ImGui::SameLine();
bool selected = (state_->sub_palette_index == static_cast<uint64_t>(i));
if (selected) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.5f, 0.8f, 1.0f));
}
if (ImGui::SmallButton(absl::StrFormat("%d", i).c_str())) {
state_->sub_palette_index = static_cast<uint64_t>(i);
state_->refresh_graphics = true;
}
if (selected) {
ImGui::PopStyleColor();
}
}
}
void PaletteControlsPanel::DrawApplyButtons() {
gui::TextWithSeparators("Apply Palette");
// Apply to current sheet
ImGui::BeginDisabled(state_->open_sheets.empty());
if (ImGui::Button(ICON_MD_BRUSH " Apply to Current Sheet")) {
ApplyPaletteToSheet(state_->current_sheet_id);
}
ImGui::EndDisabled();
HOVER_HINT("Apply palette to the currently selected sheet");
ImGui::SameLine();
// Apply to all sheets
if (ImGui::Button(ICON_MD_FORMAT_PAINT " Apply to All Sheets")) {
ApplyPaletteToAllSheets();
}
HOVER_HINT("Apply palette to all active graphics sheets");
// Apply to selected sheets (multi-select)
if (!state_->selected_sheets.empty()) {
if (ImGui::Button(
absl::StrFormat(ICON_MD_CHECKLIST " Apply to %zu Selected",
state_->selected_sheets.size())
.c_str())) {
for (uint16_t sheet_id : state_->selected_sheets) {
ApplyPaletteToSheet(sheet_id);
}
}
HOVER_HINT("Apply palette to all selected sheets in browser");
}
// Refresh button
ImGui::Separator();
if (ImGui::Button(ICON_MD_REFRESH " Refresh Graphics")) {
state_->refresh_graphics = true;
if (!state_->open_sheets.empty()) {
ApplyPaletteToSheet(state_->current_sheet_id);
}
}
HOVER_HINT("Force refresh of current sheet graphics");
}
void PaletteControlsPanel::ApplyPaletteToSheet(uint16_t sheet_id) {
if (!rom_ || !rom_->is_loaded() || !game_data_) return;
auto palette_group_result = game_data_->palette_groups.get_group(
kPaletteGroupAddressesKeys[state_->palette_group_index]);
if (!palette_group_result) return;
auto palette_group = *palette_group_result;
if (state_->palette_index >= palette_group.size()) return;
auto palette = palette_group.palette(state_->palette_index);
auto& sheet = gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id);
if (sheet.is_active() && sheet.surface()) {
sheet.SetPaletteWithTransparent(palette, state_->sub_palette_index);
gfx::Arena::Get().NotifySheetModified(sheet_id);
}
}
void PaletteControlsPanel::ApplyPaletteToAllSheets() {
if (!rom_ || !rom_->is_loaded() || !game_data_) return;
auto palette_group_result = game_data_->palette_groups.get_group(
kPaletteGroupAddressesKeys[state_->palette_group_index]);
if (!palette_group_result) return;
auto palette_group = *palette_group_result;
if (state_->palette_index >= palette_group.size()) return;
auto palette = palette_group.palette(state_->palette_index);
for (int i = 0; i < zelda3::kNumGfxSheets; i++) {
auto& sheet = gfx::Arena::Get().mutable_gfx_sheets()->data()[i];
if (sheet.is_active() && sheet.surface()) {
sheet.SetPaletteWithTransparent(palette, state_->sub_palette_index);
gfx::Arena::Get().NotifySheetModified(i);
}
}
}
} // namespace editor
} // namespace yaze