yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
palette_controls_panel.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <string>
5
6#include "absl/strings/str_format.h"
11#include "app/gui/core/color.h"
13#include "imgui/imgui.h"
14#include "util/i18n/tr.h"
15#include "zelda3/game_data.h"
16
17namespace yaze::editor {
18namespace {
19
21 const char* name;
22 const char* label;
23};
24constexpr PreviewGroup kGroups[] = {{"ow_main", "Overworld"},
25 {"ow_aux", "Overworld auxiliary"},
26 {"ow_animated", "Overworld animated"},
27 {"hud", "HUD"},
28 {"global_sprites", "Global sprites"},
29 {"armors", "Link"},
30 {"swords", "Swords"},
31 {"shields", "Shields"},
32 {"sprites_aux1", "Sprites"},
33 {"sprites_aux2", "Sprites auxiliary 2"},
34 {"sprites_aux3", "Sprites auxiliary 3"},
35 {"dungeon_main", "Dungeon"},
36 {"grass", "Grass"},
37 {"3d_object", "3D objects"},
38 {"ow_mini_map", "Overworld map"}};
39
40const char* GroupLabel(const std::string& name) {
41 for (const auto& group : kGroups) {
42 if (name == group.name)
43 return group.label;
44 }
45 return name.c_str();
46}
47
48bool CanPreview(const GraphicsEditorState& state) {
49 return state.game_data &&
51}
52
54 std::optional<GraphicsPaletteSelection> selection,
58 SetGraphicsPreviewPaletteForAllSheets(state, selection).IgnoreError();
59 } else {
60 SetGraphicsPreviewPalette(state, state.current_sheet_id, selection)
61 .IgnoreError();
62 }
63}
64
68 return state.browser_palette ? &*state.browser_palette : nullptr;
69 }
70 return state.PreviewPalette(state.current_sheet_id);
71}
72
73} // namespace
74
78 const bool inline_controls = layout == GraphicsPaletteSelectorLayout::kInline;
79 const float font = ImGui::GetFontSize();
80 const float right =
81 ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x;
82 const auto next_control = [inline_controls, right](float width) {
83 if (inline_controls &&
84 ImGui::GetItemRectMax().x + ImGui::GetStyle().ItemSpacing.x + width <=
85 right) {
86 ImGui::SameLine();
87 }
88 ImGui::SetNextItemWidth(inline_controls ? width : -1);
89 };
90 const auto* selected = Selected(state, scope);
91 const std::string selected_group = selected ? selected->group : "";
92 const char* label =
93 selected ? GroupLabel(selected->group) : tr("Loaded colors");
94 if (!inline_controls)
95 ImGui::TextUnformatted(tr("Preview palette"));
96 ImGui::BeginDisabled(!CanPreview(state));
97 ImGui::SetNextItemWidth(inline_controls ? 11 * font : -1);
98 const bool open = ImGui::BeginCombo("##PreviewPalette", label);
99 gui::AutoRegisterLastItem("combo", "Preview palette");
100 if (open) {
101 if (ImGui::Selectable(tr("Loaded colors"), !selected))
102 Choose(state, std::nullopt, scope);
103 gui::AutoRegisterLastItem("selectable", "Loaded colors");
104 for (const auto& group : kGroups) {
105 const auto* palettes =
106 state.game_data
107 ? state.game_data->palette_groups.get_group(group.name)
108 : nullptr;
109 if (!palettes || palettes->empty())
110 continue;
111 if (ImGui::Selectable(tr(group.label), selected_group == group.name)) {
112 Choose(state, GraphicsPaletteSelection{group.name}, scope);
113 }
115 "selectable", absl::StrFormat("Preview group %s", group.name));
116 }
117 ImGui::EndCombo();
118 }
119 ImGui::EndDisabled();
120 // A choice above can erase or replace the selected entry.
121 selected = Selected(state, scope);
122 if (!selected || !CanPreview(state))
123 return;
124 auto choice = *selected;
125 const auto resolved = ResolveGraphicsPreviewPalette(*state.game_data, choice);
126 if (!resolved.ok()) {
127 ImGui::TextWrapped("%s",
128 tr("Preview unavailable; choose another palette."));
129 return;
130 }
131 if (resolved->palette_count > 1) {
132 int index = choice.palette;
133 next_control(6 * font);
134 if (ImGui::InputInt("##PreviewPaletteIndex", &index)) {
135 choice.palette = std::clamp(index, 0, resolved->palette_count - 1);
136 choice.row = 0;
137 Choose(state, choice, scope);
138 }
139 // InputInt's step buttons form a group with ID zero while idle. Register
140 // the visible group explicitly so automation can discover it before focus.
141 const auto min = ImGui::GetItemRectMin();
142 const auto max = ImGui::GetItemRectMax();
144 metadata.label = "Preview palette index";
145 metadata.visible = ImGui::IsItemVisible();
146 metadata.bounds = {min.x, min.y, max.x, max.y, true};
148 "input:preview_palette_index", "input",
149 ImGui::GetID("##PreviewPaletteIndex"), "", metadata);
151 "Palette index in the selected group; changes only this preview");
152 }
153 if (resolved->row_count > 1) {
154 const auto row_label = absl::StrFormat("%s %d/%d", tr("Row"),
155 choice.row + 1, resolved->row_count);
156 next_control(
157 ImGui::CalcTextSize(row_label.c_str()).x +
158 2 * (ImGui::GetFrameHeight() + ImGui::GetStyle().ItemSpacing.x));
159 ImGui::BeginGroup();
160 ImGui::BeginDisabled(choice.row == 0);
161 const bool previous = ImGui::ArrowButton("##RowDown", ImGuiDir_Left);
162 gui::AutoRegisterLastItem("button", "Previous palette row");
163 ImGui::EndDisabled();
164 ImGui::SameLine();
165 ImGui::TextUnformatted(row_label.c_str());
166 ImGui::SameLine();
167 ImGui::BeginDisabled(choice.row + 1 >= resolved->row_count);
168 const bool next = ImGui::ArrowButton("##RowUp", ImGuiDir_Right);
169 gui::AutoRegisterLastItem("button", "Next palette row");
170 ImGui::EndDisabled();
171 if (previous || next) {
172 choice.row += next ? 1 : -1;
173 Choose(state, choice, scope);
174 }
175 ImGui::EndGroup();
176 }
177}
178
179void PaletteControlsPanel::Draw(bool* p_open) {
180 Update().IgnoreError();
181}
182
184 if (!CanPreview(*state_)) {
185 ImGui::TextDisabled(tr("Load this ROM's graphics to preview palettes"));
186 return absl::OkStatus();
187 }
188 if (state_->SourceTarget()) {
189 ImGui::TextWrapped("%s", tr("The source editor controls these colors."));
190 if (ImGui::Button(tr("Use manual palette")))
192 else
193 return absl::OkStatus();
194 }
195 DrawPresets();
196 ImGui::Separator();
198 ImGui::Separator();
200 ImGui::Separator();
202 return absl::OkStatus();
203}
204
206 constexpr PreviewGroup presets[] = {{"ow_main", "Overworld"},
207 {"dungeon_main", "Dungeon"},
208 {"sprites_aux1", "Sprites"},
209 {"armors", "Link"},
210 {"hud", "HUD"}};
211 for (size_t i = 0; i < std::size(presets); ++i) {
212 if (i != 0 && i != 3)
213 ImGui::SameLine();
214 const auto& preset = presets[i];
215 const auto* group =
217 ImGui::BeginDisabled(!group || group->empty());
218 if (ImGui::Button(tr(preset.label))) {
219 Choose(*state_, GraphicsPaletteSelection{preset.name});
220 }
222 absl::StrFormat("%s preview", preset.label));
223 ImGui::EndDisabled();
224 HOVER_HINT("Apply this palette to the current sheet preview");
225 }
226}
227
231
233 const auto* selected = state_->PreviewPalette(state_->current_sheet_id);
234 if (!selected) {
235 ImGui::TextWrapped("%s", tr("Showing this sheet's loaded colors. Choose a "
236 "group to preview another palette."));
237 return;
238 }
239 const auto resolved =
241 if (!resolved.ok())
242 return;
243 auto choice = *selected;
244 const auto& palette =
246 ->palette_ref(choice.palette);
247 const auto layout =
248 internal::GetPaletteRowLayout(choice.group, palette.size());
249 ImGui::TextDisabled(tr("Preview only; source colors are unchanged"));
250 for (size_t i = 0; i < palette.size(); ++i) {
251 if (i % layout.colors_per_row != 0)
252 ImGui::SameLine();
253 ImGui::PushID(static_cast<int>(i));
254 if (ImGui::ColorButton("##PaletteColor",
256 ImGuiColorEditFlags_NoTooltip, ImVec2(18, 18))) {
257 choice.row = static_cast<int>(i) / layout.colors_per_row;
258 Choose(*state_, choice);
259 }
260 if (ImGui::IsItemHovered()) {
261 ImGui::SetTooltip(
262 tr("Row %d, slot %d: $%04X. Click to preview this row."),
263 static_cast<int>(i) / layout.colors_per_row + 1,
264 static_cast<int>(i) % layout.colors_per_row, palette[i].snes());
265 }
266 ImGui::PopID();
267 }
268}
269
271 if (ImGui::Button(tr("Restore loaded colors")))
272 Choose(*state_, std::nullopt);
273 gui::AutoRegisterLastItem("button", "Restore loaded colors");
274 const auto* selected = state_->PreviewPalette(state_->current_sheet_id);
275 if (!selected)
276 return;
277 const auto choice = *selected;
278 if (!state_->selected_sheets.empty() &&
279 ImGui::Button(tr("Preview on selected sheets"))) {
280 for (uint16_t sheet : state_->selected_sheets) {
281 SetGraphicsPreviewPalette(*state_, sheet, choice).IgnoreError();
282 }
283 }
284 if (ImGui::Button(tr("Preview on all sheets"))) {
286 }
287}
288
289} // namespace yaze::editor
Shared state between GraphicsEditor panel components.
std::optional< GraphicsPaletteSelection > browser_palette
const GraphicsEditTarget * SourceTarget() const
const GraphicsPaletteSelection * PreviewPalette(uint16_t sheet) const
absl::Status Update()
Legacy Update method for backward compatibility.
void DrawPresets()
Draw quick preset buttons.
void Draw(bool *p_open) override
Draw the palette controls UI (WindowContent interface)
void DrawPaletteDisplay()
Draw the current palette display.
void DrawPaletteGroupSelector()
Draw palette group selection.
const void * gfx_sheets_owner() const
Definition arena.h:216
static Arena & Get()
Definition arena.cc:24
void RegisterWidget(const std::string &full_path, const std::string &type, ImGuiID imgui_id, const std::string &description="", const WidgetMetadata &metadata=WidgetMetadata())
static WidgetIdRegistry & Instance()
#define HOVER_HINT(string)
Definition macro.h:24
const GraphicsPaletteSelection * Selected(const GraphicsEditorState &state, GraphicsPaletteSelectorScope scope)
void Choose(GraphicsEditorState &state, std::optional< GraphicsPaletteSelection > selection, GraphicsPaletteSelectorScope scope=GraphicsPaletteSelectorScope::kCurrentSheet)
PaletteRowLayout GetPaletteRowLayout(std::string_view group_name, std::size_t palette_size)
Editors are the view controllers for the application.
absl::Status SetGraphicsPreviewPalette(GraphicsEditorState &state, uint16_t sheet, std::optional< GraphicsPaletteSelection > selection)
absl::Status SetGraphicsPreviewPaletteForAllSheets(GraphicsEditorState &state, std::optional< GraphicsPaletteSelection > selection)
void DrawGraphicsPreviewPaletteSelector(GraphicsEditorState &state, GraphicsPaletteSelectorLayout layout, GraphicsPaletteSelectorScope scope)
absl::StatusOr< GraphicsPalettePreview > ResolveGraphicsPreviewPalette(zelda3::GameData &data, const GraphicsPaletteSelection &selection)
void AutoRegisterLastItem(const std::string &widget_type, const std::string &explicit_label, const std::string &description)
Automatically register the last ImGui item.
ImVec4 ConvertSnesColorToImVec4(const gfx::SnesColor &color)
Convert SnesColor to standard ImVec4 for display.
Definition color.cc:24
PaletteGroup * get_group(const std::string &group_name)
const SnesPalette & palette_ref(int i) const
gfx::PaletteGroupMap palette_groups
Definition game_data.h:95
Automatic widget registration helpers for ImGui Test Engine integration.