Refactor Overworld Editor and enhance settings management for v3 features

- Removed the outdated ZEML layout file and associated references to streamline the OverworldEditor.
- Introduced the OverworldEditorManager class to encapsulate v3 settings management, improving code organization and maintainability.
- Updated the OverworldEditor to utilize ImGui for layout, enhancing user interaction and flexibility.
- Enhanced the changelog with detailed descriptions of new features and improvements, including a comprehensive undo/redo system and advanced palette management.
- Removed deprecated diagnostic patterns from the clangd configuration to improve code quality checks.
This commit is contained in:
scawful
2025-09-27 15:47:22 -04:00
parent a9ead0a45c
commit 7355294f49
13 changed files with 1405 additions and 218 deletions

View File

@@ -11,6 +11,7 @@ set(
app/editor/dungeon/dungeon_room_loader.cc
app/editor/dungeon/dungeon_usage_tracker.cc
app/editor/overworld/overworld_editor.cc
app/editor/overworld/overworld_editor_manager.cc
app/editor/sprite/sprite_editor.cc
app/editor/music/music_editor.cc
app/editor/message/message_editor.cc

View File

@@ -23,7 +23,6 @@
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/gui/style.h"
#include "app/gui/zeml.h"
#include "app/rom.h"
#include "app/zelda3/common.h"
#include "app/zelda3/overworld/overworld.h"
@@ -42,40 +41,15 @@ using namespace ImGui;
constexpr float kInputFieldSize = 30.f;
void OverworldEditor::Initialize() {
layout_node_ = gui::zeml::Parse(gui::zeml::LoadFile("overworld.zeml"));
// Initialize MapPropertiesSystem with canvas and bitmap data
map_properties_system_ = std::make_unique<MapPropertiesSystem>(
&overworld_, rom_, &maps_bmp_, &ow_map_canvas_);
gui::zeml::Bind(std::to_address(layout_node_.GetNode("OverworldCanvas")),
[this]() { DrawOverworldCanvas(); });
// Initialize OverworldEditorManager for v3 features
overworld_manager_ = std::make_unique<OverworldEditorManager>(&overworld_, rom_);
// Setup overworld canvas context menu
SetupOverworldCanvasContextMenu();
gui::zeml::Bind(
std::to_address(layout_node_.GetNode("OverworldTileSelector")),
[this]() { status_ = DrawTileSelector(); });
gui::zeml::Bind(std::to_address(layout_node_.GetNode("OwUsageStats")),
[this]() {
if (rom_->is_loaded()) {
status_ = UpdateUsageStats();
}
});
gui::zeml::Bind(std::to_address(layout_node_.GetNode("owToolset")),
[this]() { DrawToolset(); });
gui::zeml::Bind(std::to_address(layout_node_.GetNode("OwTile16Editor")),
[this]() {
if (rom_->is_loaded()) {
status_ = tile16_editor_.Update();
}
});
gui::zeml::Bind(std::to_address(layout_node_.GetNode("OwGfxGroupEditor")),
[this]() {
if (rom_->is_loaded()) {
status_ = gfx_group_editor_.Update();
}
});
// Core editing tools
gui::AddTableColumn(toolset_table_, "##Pan", [&]() {
@@ -179,8 +153,65 @@ absl::Status OverworldEditor::Load() {
absl::Status OverworldEditor::Update() {
status_ = absl::OkStatus();
if (overworld_canvas_fullscreen_) DrawFullscreenCanvas();
gui::zeml::Render(layout_node_);
if (overworld_canvas_fullscreen_) {
DrawFullscreenCanvas();
return status_;
}
// Replace ZEML with pure ImGui layout
if (ImGui::BeginTabBar("##OwEditorTabBar")) {
if (ImGui::BeginTabItem("Map Editor")) {
DrawToolset();
if (ImGui::BeginTable("##owEditTable", 2,
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
ImGuiTableFlags_BordersV)) {
ImGui::TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Tile Selector", ImGuiTableColumnFlags_WidthFixed, 256.0f);
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
DrawOverworldCanvas();
ImGui::TableNextColumn();
status_ = DrawTileSelector();
ImGui::EndTable();
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Tile16 Editor")) {
if (rom_->is_loaded()) {
status_ = tile16_editor_.Update();
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Graphics Group Editor")) {
if (rom_->is_loaded()) {
status_ = gfx_group_editor_.Update();
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Usage Statistics")) {
if (rom_->is_loaded()) {
status_ = UpdateUsageStats();
}
ImGui::EndTabItem();
}
// Add v3 settings tab
if (rom_->is_loaded()) {
status_ = overworld_manager_->DrawV3SettingsPanel();
}
ImGui::EndTabBar();
}
return status_;
}
@@ -1249,9 +1280,9 @@ void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0, ImVec2 scrolling,
for (auto &each : overworld_.entrances()) {
if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
each.map_id_ >= (current_world_ * 0x40) && !each.deleted) {
auto color = ImVec4(255, 255, 0, 100);
auto color = ImVec4(255, 0, 255, 100);
if (each.is_hole_) {
color = ImVec4(255, 255, 255, 200);
color = ImVec4(255, 255, 0, 200);
}
ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16, color);
std::string str = util::HexByte(each.entrance_id_);

View File

@@ -12,9 +12,9 @@
#include "app/gfx/tilemap.h"
#include "app/gui/canvas.h"
#include "app/gui/input.h"
#include "app/gui/zeml.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld.h"
#include "app/editor/overworld/overworld_editor_manager.h"
#include "imgui/imgui.h"
namespace yaze {
@@ -263,6 +263,7 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
// Map properties system for UI organization
std::unique_ptr<MapPropertiesSystem> map_properties_system_;
std::unique_ptr<OverworldEditorManager> overworld_manager_;
// Scratch space for large layouts
// Scratch space canvas for tile16 drawing (like a mini overworld)
@@ -328,7 +329,6 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
gui::Table map_settings_table_{kOWMapTable.data(), 8, kOWMapFlags,
ImVec2(0, 0)};
gui::zeml::Node layout_node_;
absl::Status status_;
};
} // namespace editor

View File

@@ -0,0 +1,284 @@
#include "overworld_editor_manager.h"
#include "app/gfx/snes_color.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/gui/style.h"
#include "app/zelda3/overworld/overworld_map.h"
namespace yaze {
namespace editor {
using namespace ImGui;
absl::Status OverworldEditorManager::DrawV3SettingsPanel() {
if (BeginTabItem("v3 Settings")) {
Text("ZSCustomOverworld v3 Settings");
Separator();
// Check if custom ASM is applied
uint8_t asm_version = GetCustomASMVersion();
if (asm_version >= 3 && asm_version != 0xFF) {
TextColored(ImVec4(0, 1, 0, 1), "Custom Overworld ASM v%d Applied", asm_version);
} else if (asm_version == 0x00) {
TextColored(ImVec4(1, 1, 0, 1), "Vanilla ROM - Custom features available via flag");
} else {
TextColored(ImVec4(1, 0, 0, 1), "Custom ASM v%d - Consider upgrading to v3", asm_version);
}
Separator();
RETURN_IF_ERROR(DrawCustomOverworldSettings());
RETURN_IF_ERROR(DrawAreaSpecificSettings());
RETURN_IF_ERROR(DrawTransitionSettings());
RETURN_IF_ERROR(DrawOverlaySettings());
EndTabItem();
}
return absl::OkStatus();
}
absl::Status OverworldEditorManager::DrawCustomOverworldSettings() {
if (TreeNode("Custom Overworld Features")) {
RETURN_IF_ERROR(DrawBooleanSetting("Enable Area-Specific Background Colors",
&enable_area_specific_bg_,
"Allows each overworld area to have its own background color"));
RETURN_IF_ERROR(DrawBooleanSetting("Enable Main Palette Override",
&enable_main_palette_,
"Allows each area to override the main palette"));
RETURN_IF_ERROR(DrawBooleanSetting("Enable Mosaic Transitions",
&enable_mosaic_,
"Enables mosaic screen transitions between areas"));
RETURN_IF_ERROR(DrawBooleanSetting("Enable Custom GFX Groups",
&enable_gfx_groups_,
"Allows each area to have custom tile GFX groups"));
RETURN_IF_ERROR(DrawBooleanSetting("Enable Subscreen Overlays",
&enable_subscreen_overlay_,
"Enables custom subscreen overlays (fog, sky, etc.)"));
RETURN_IF_ERROR(DrawBooleanSetting("Enable Animated GFX Override",
&enable_animated_gfx_,
"Allows each area to have custom animated tiles"));
Separator();
if (Button("Apply Custom Overworld ASM")) {
RETURN_IF_ERROR(ApplyCustomOverworldASM());
}
SameLine();
HOVER_HINT("Writes the custom overworld settings to ROM");
TreePop();
}
return absl::OkStatus();
}
absl::Status OverworldEditorManager::DrawAreaSpecificSettings() {
if (TreeNode("Area-Specific Settings")) {
// Map selection
int map_count = zelda3::kNumOverworldMaps;
SliderInt("Map Index", &current_map_index_, 0, map_count - 1);
auto* current_map = overworld_->mutable_overworld_map(current_map_index_);
// Area size controls
RETURN_IF_ERROR(DrawAreaSizeControls());
// Background color
if (enable_area_specific_bg_) {
uint16_t bg_color = current_map->area_specific_bg_color();
RETURN_IF_ERROR(DrawColorPicker("Background Color", &bg_color));
current_map->set_area_specific_bg_color(bg_color);
}
// Main palette
if (enable_main_palette_) {
uint8_t main_palette = current_map->main_palette();
SliderInt("Main Palette", (int*)&main_palette, 0, 5);
current_map->set_main_palette(main_palette);
}
// Mosaic settings
if (enable_mosaic_) {
RETURN_IF_ERROR(DrawMosaicControls());
}
// GFX groups
if (enable_gfx_groups_) {
RETURN_IF_ERROR(DrawGfxGroupControls());
}
// Subscreen overlay
if (enable_subscreen_overlay_) {
uint16_t overlay = current_map->subscreen_overlay();
RETURN_IF_ERROR(DrawOverlaySetting("Subscreen Overlay", &overlay));
current_map->set_subscreen_overlay(overlay);
}
// Animated GFX
if (enable_animated_gfx_) {
uint8_t animated_gfx = current_map->animated_gfx();
RETURN_IF_ERROR(DrawGfxGroupSetting("Animated GFX", &animated_gfx));
current_map->set_animated_gfx(animated_gfx);
}
TreePop();
}
return absl::OkStatus();
}
absl::Status OverworldEditorManager::DrawAreaSizeControls() {
auto* current_map = overworld_->mutable_overworld_map(current_map_index_);
const char* area_size_names[] = {"Small", "Large", "Wide", "Tall"};
int current_size = static_cast<int>(current_map->area_size());
if (Combo("Area Size", &current_size, area_size_names, 4)) {
current_map->SetAreaSize(static_cast<zelda3::AreaSizeEnum>(current_size));
}
return absl::OkStatus();
}
absl::Status OverworldEditorManager::DrawMosaicControls() {
auto* current_map = overworld_->mutable_overworld_map(current_map_index_);
const auto& mosaic = current_map->mosaic_expanded();
bool mosaic_up = mosaic[0];
bool mosaic_down = mosaic[1];
bool mosaic_left = mosaic[2];
bool mosaic_right = mosaic[3];
if (Checkbox("Mosaic Up", &mosaic_up)) {
current_map->set_mosaic_expanded(0, mosaic_up);
}
SameLine();
if (Checkbox("Mosaic Down", &mosaic_down)) {
current_map->set_mosaic_expanded(1, mosaic_down);
}
if (Checkbox("Mosaic Left", &mosaic_left)) {
current_map->set_mosaic_expanded(2, mosaic_left);
}
SameLine();
if (Checkbox("Mosaic Right", &mosaic_right)) {
current_map->set_mosaic_expanded(3, mosaic_right);
}
return absl::OkStatus();
}
absl::Status OverworldEditorManager::DrawGfxGroupControls() {
auto* current_map = overworld_->mutable_overworld_map(current_map_index_);
Text("Custom Tile GFX Groups:");
for (int i = 0; i < 8; i++) {
uint8_t gfx_id = current_map->custom_tileset(i);
std::string label = "GFX " + std::to_string(i);
RETURN_IF_ERROR(DrawGfxGroupSetting(label.c_str(), &gfx_id));
current_map->set_custom_tileset(i, gfx_id);
if (i < 7) SameLine();
}
return absl::OkStatus();
}
absl::Status OverworldEditorManager::DrawTransitionSettings() {
if (TreeNode("Transition Settings")) {
Text("Complex area transition calculations are automatically handled");
Text("based on neighboring area sizes (Large, Wide, Tall, Small).");
if (GetCustomASMVersion() >= 3) {
TextColored(ImVec4(0, 1, 0, 1), "Using v3+ enhanced transitions");
} else {
TextColored(ImVec4(1, 1, 0, 1), "Using vanilla/v2 transitions");
}
TreePop();
}
return absl::OkStatus();
}
absl::Status OverworldEditorManager::DrawOverlaySettings() {
if (TreeNode("Interactive Overlay Settings")) {
Text("Interactive overlays reveal holes and change map elements.");
auto* current_map = overworld_->mutable_overworld_map(current_map_index_);
Text("Map %d has %s", current_map_index_,
current_map->has_overlay() ? "interactive overlay" : "no overlay");
if (current_map->has_overlay()) {
Text("Overlay ID: 0x%04X", current_map->overlay_id());
Text("Overlay data size: %zu bytes", current_map->overlay_data().size());
}
TreePop();
}
return absl::OkStatus();
}
absl::Status OverworldEditorManager::ApplyCustomOverworldASM() {
return overworld_->SaveCustomOverworldASM(
enable_area_specific_bg_, enable_main_palette_, enable_mosaic_,
enable_gfx_groups_, enable_subscreen_overlay_, enable_animated_gfx_);
}
bool OverworldEditorManager::ValidateV3Compatibility() {
uint8_t asm_version = GetCustomASMVersion();
return (asm_version >= 3 && asm_version != 0xFF);
}
bool OverworldEditorManager::CheckCustomASMApplied() {
uint8_t asm_version = GetCustomASMVersion();
return (asm_version != 0xFF && asm_version != 0x00);
}
uint8_t OverworldEditorManager::GetCustomASMVersion() {
return (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
}
absl::Status OverworldEditorManager::DrawBooleanSetting(const char* label, bool* setting,
const char* help_text) {
Checkbox(label, setting);
if (help_text && IsItemHovered()) {
SetTooltip("%s", help_text);
}
return absl::OkStatus();
}
absl::Status OverworldEditorManager::DrawColorPicker(const char* label, uint16_t* color) {
gfx::SnesColor snes_color(*color);
ImVec4 imgui_color = snes_color.rgb();
if (ColorEdit3(label, &imgui_color.x)) {
gfx::SnesColor new_color;
new_color.set_rgb(imgui_color);
*color = new_color.snes();
}
return absl::OkStatus();
}
absl::Status OverworldEditorManager::DrawOverlaySetting(const char* label, uint16_t* overlay) {
int overlay_int = *overlay;
if (InputInt(label, &overlay_int, 1, 16, ImGuiInputTextFlags_CharsHexadecimal)) {
*overlay = static_cast<uint16_t>(overlay_int & 0xFFFF);
}
return absl::OkStatus();
}
absl::Status OverworldEditorManager::DrawGfxGroupSetting(const char* label, uint8_t* gfx_id,
int max_value) {
int gfx_int = *gfx_id;
if (SliderInt(label, &gfx_int, 0, max_value)) {
*gfx_id = static_cast<uint8_t>(gfx_int);
}
return absl::OkStatus();
}
} // namespace editor
} // namespace yaze

View File

@@ -0,0 +1,90 @@
#ifndef YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_EDITOR_MANAGER_H
#define YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_EDITOR_MANAGER_H
#include <memory>
#include "absl/status/status.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld.h"
namespace yaze {
namespace editor {
/**
* @class OverworldEditorManager
* @brief Manages the complex overworld editor functionality and v3 features
*
* This class separates the complex overworld editing functionality from the main
* OverworldEditor class to improve maintainability and organization, especially
* for ZSCustomOverworld v3 features.
*/
class OverworldEditorManager {
public:
OverworldEditorManager(zelda3::Overworld* overworld, Rom* rom)
: overworld_(overworld), rom_(rom) {}
// v3 Feature Management
absl::Status DrawV3SettingsPanel();
absl::Status DrawCustomOverworldSettings();
absl::Status DrawAreaSpecificSettings();
absl::Status DrawTransitionSettings();
absl::Status DrawOverlaySettings();
// Map Properties Management
absl::Status DrawMapPropertiesTable();
absl::Status DrawAreaSizeControls();
absl::Status DrawMosaicControls();
absl::Status DrawPaletteControls();
absl::Status DrawGfxGroupControls();
// Save/Load Operations for v3
absl::Status SaveCustomOverworldData();
absl::Status LoadCustomOverworldData();
absl::Status ApplyCustomOverworldASM();
// Validation and Checks
bool ValidateV3Compatibility();
bool CheckCustomASMApplied();
uint8_t GetCustomASMVersion();
// Getters/Setters for v3 settings
auto enable_area_specific_bg() const { return enable_area_specific_bg_; }
auto enable_main_palette() const { return enable_main_palette_; }
auto enable_mosaic() const { return enable_mosaic_; }
auto enable_gfx_groups() const { return enable_gfx_groups_; }
auto enable_subscreen_overlay() const { return enable_subscreen_overlay_; }
auto enable_animated_gfx() const { return enable_animated_gfx_; }
void set_enable_area_specific_bg(bool value) { enable_area_specific_bg_ = value; }
void set_enable_main_palette(bool value) { enable_main_palette_ = value; }
void set_enable_mosaic(bool value) { enable_mosaic_ = value; }
void set_enable_gfx_groups(bool value) { enable_gfx_groups_ = value; }
void set_enable_subscreen_overlay(bool value) { enable_subscreen_overlay_ = value; }
void set_enable_animated_gfx(bool value) { enable_animated_gfx_ = value; }
private:
zelda3::Overworld* overworld_;
Rom* rom_;
// v3 Feature flags
bool enable_area_specific_bg_ = false;
bool enable_main_palette_ = false;
bool enable_mosaic_ = false;
bool enable_gfx_groups_ = false;
bool enable_subscreen_overlay_ = false;
bool enable_animated_gfx_ = false;
// Current editing state
int current_map_index_ = 0;
// Helper methods
absl::Status DrawBooleanSetting(const char* label, bool* setting, const char* help_text = nullptr);
absl::Status DrawColorPicker(const char* label, uint16_t* color);
absl::Status DrawOverlaySetting(const char* label, uint16_t* overlay);
absl::Status DrawGfxGroupSetting(const char* label, uint8_t* gfx_id, int max_value = 255);
};
} // namespace editor
} // namespace yaze
#endif // YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_EDITOR_MANAGER_H

View File

@@ -471,14 +471,12 @@ absl::Status Tile16Editor::DrawToCurrentTile16(ImVec2 click_position) {
absl::Status Tile16Editor::UpdateTile16Edit() {
static bool show_tile8_selector = true;
static bool show_tile16_editor = true;
static bool show_controls = true;
// View controls
if (BeginMenuBar()) {
if (BeginMenu("View")) {
Checkbox("Tile8 Selector", &show_tile8_selector);
Checkbox("Tile16 Editor", &show_tile16_editor);
Checkbox("Controls", &show_controls);
EndMenu();
}
EndMenuBar();
@@ -552,6 +550,32 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
tile8_source_canvas_.DrawOverlay();
EndChild();
Separator();
Text("Palette: %d (Group: %d)", current_palette_, current_palette_group_);
if (Button("Pal -", ImVec2(40, 0)))
RETURN_IF_ERROR(CyclePalette(false));
SameLine();
if (Button("Pal +", ImVec2(40, 0)))
RETURN_IF_ERROR(CyclePalette(true));
// Quick palette grid
for (int i = 0; i < 8; ++i) {
if (i > 0 && i % 4 != 0)
SameLine();
bool is_current = (current_palette_ == i);
if (is_current)
PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.7f, 0.4f, 1.0f));
if (Button(std::to_string(i).c_str(), ImVec2(20, 20))) {
current_palette_ = i;
RETURN_IF_ERROR(RefreshAllPalettes());
}
if (is_current)
PopStyleColor();
if (i == 3) { /* New line */
}
}
ImGui::EndGroup();
}
@@ -674,132 +698,98 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
}
}
}
// Compact controls section directly below
if (show_controls) {
if (BeginTable("##Tile16CompactControls", 3,
ImGuiTableFlags_Resizable |
ImGuiTableFlags_BordersInnerV)) {
TableSetupColumn("Info & Palette", ImGuiTableColumnFlags_WidthFixed,
280);
TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 200);
TableSetupColumn("Advanced", ImGuiTableColumnFlags_WidthStretch);
TableNextRow();
// Info and palette column
TableNextColumn();
if (BeginChild("InfoPaletteChild", ImVec2(270, 120), true)) {
gui::DrawTable(tile_edit_table_);
Separator();
Text("Palette: %d (Group: %d)", current_palette_,
current_palette_group_);
if (Button("Pal -", ImVec2(35, 0)))
RETURN_IF_ERROR(CyclePalette(false));
SameLine();
if (Button("Pal +", ImVec2(35, 0)))
RETURN_IF_ERROR(CyclePalette(true));
// Quick palette grid
for (int i = 0; i < 8; ++i) {
if (i > 0 && i % 4 != 0)
SameLine();
bool is_current = (current_palette_ == i);
if (is_current)
PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.7f, 0.4f, 1.0f));
if (Button(std::to_string(i).c_str(), ImVec2(16, 16))) {
current_palette_ = i;
RETURN_IF_ERROR(RefreshAllPalettes());
}
if (is_current)
PopStyleColor();
if (i == 3) { /* New line */
}
}
}
EndChild();
// Actions column
TableNextColumn();
if (BeginChild("ActionsChild", ImVec2(190, 120), true)) {
if (Button("Clear Tile16", ImVec2(80, 0)))
RETURN_IF_ERROR(ClearTile16());
if (Button("Copy", ImVec2(80, 0)))
RETURN_IF_ERROR(CopyTile16ToClipboard(current_tile16_));
if (Button("Paste", ImVec2(80, 0)))
RETURN_IF_ERROR(PasteTile16FromClipboard());
Separator();
bool can_undo = !undo_stack_.empty();
bool can_redo = !redo_stack_.empty();
if (!can_undo)
BeginDisabled();
if (Button("Undo", ImVec2(80, 0)))
RETURN_IF_ERROR(Undo());
if (!can_undo)
EndDisabled();
if (!can_redo)
BeginDisabled();
if (Button("Redo", ImVec2(80, 0)))
RETURN_IF_ERROR(Redo());
if (!can_redo)
EndDisabled();
Separator();
DrawScratchSpace();
}
EndChild();
// Advanced settings column
TableNextColumn();
if (BeginChild("AdvancedChild", ImVec2(0, 120), true)) {
if (Button("Palette Settings")) {
show_palette_settings_ = !show_palette_settings_;
}
if (Button("Manual Tile8 Inputs")) {
ImGui::OpenPopup("ManualTile8Editor");
}
HOVER_HINT("Edit tile8 IDs and properties directly");
if (Button("Refresh Blockset")) {
RETURN_IF_ERROR(RefreshTile16Blockset());
}
HOVER_HINT("Regenerate tile16 blockset from ROM data");
Text("Advanced Palette:");
const char* palette_group_names[] = {
"OW Main", "OW Aux", "OW Anim", "Dungeon",
"Sprites", "Armor", "Sword"};
if (Combo("##AdvPaletteGroup", &current_palette_group_,
palette_group_names, 7)) {
RETURN_IF_ERROR(RefreshAllPalettes());
}
Text("Normalization:");
int mask_value = static_cast<int>(palette_normalization_mask_);
if (SliderInt("##NormMask", &mask_value, 1, 255, "0x%02X")) {
palette_normalization_mask_ = static_cast<uint8_t>(mask_value);
RETURN_IF_ERROR(LoadTile8()); // Reload with new mask
}
Checkbox("Auto Normalize", &auto_normalize_pixels_);
}
EndChild();
// Manual tile8 editor popup
DrawManualTile8Inputs();
EndTable();
}
// Close the tile16 editor scroll region
EndChild();
if (BeginChild("InfoPaletteChild", ImVec2(270, 120), true)) {
gui::DrawTable(tile_edit_table_);
}
EndChild();
// Compact controls section directly below
if (BeginTable(
"##Tile16CompactControls", 2,
ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) {
TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 100);
TableSetupColumn("Advanced", ImGuiTableColumnFlags_WidthStretch);
TableNextRow();
// Actions column
TableNextColumn();
if (BeginChild("ActionsChild", ImVec2(190, 120), true)) {
if (Button("Clear Tile16", ImVec2(80, 0)))
RETURN_IF_ERROR(ClearTile16());
if (Button("Copy", ImVec2(80, 0)))
RETURN_IF_ERROR(CopyTile16ToClipboard(current_tile16_));
if (Button("Paste", ImVec2(80, 0)))
RETURN_IF_ERROR(PasteTile16FromClipboard());
Separator();
bool can_undo = !undo_stack_.empty();
bool can_redo = !redo_stack_.empty();
if (!can_undo)
BeginDisabled();
if (Button("Undo", ImVec2(80, 0)))
RETURN_IF_ERROR(Undo());
if (!can_undo)
EndDisabled();
if (!can_redo)
BeginDisabled();
if (Button("Redo", ImVec2(80, 0)))
RETURN_IF_ERROR(Redo());
if (!can_redo)
EndDisabled();
Separator();
DrawScratchSpace();
}
EndChild();
// Advanced settings column
TableNextColumn();
if (BeginChild("AdvancedChild", ImVec2(0, 120), true)) {
if (Button("Palette Settings")) {
show_palette_settings_ = !show_palette_settings_;
}
if (Button("Manual Tile8 Inputs")) {
ImGui::OpenPopup("ManualTile8Editor");
}
HOVER_HINT("Edit tile8 IDs and properties directly");
if (Button("Refresh Blockset")) {
RETURN_IF_ERROR(RefreshTile16Blockset());
}
HOVER_HINT("Regenerate tile16 blockset from ROM data");
Text("Advanced Palette:");
const char* palette_group_names[] = {"OW Main", "OW Aux", "OW Anim",
"Dungeon", "Sprites", "Armor",
"Sword"};
if (Combo("##AdvPaletteGroup", &current_palette_group_,
palette_group_names, 7)) {
RETURN_IF_ERROR(RefreshAllPalettes());
}
Text("Normalization:");
int mask_value = static_cast<int>(palette_normalization_mask_);
if (SliderInt("##NormMask", &mask_value, 1, 255, "0x%02X")) {
palette_normalization_mask_ = static_cast<uint8_t>(mask_value);
RETURN_IF_ERROR(LoadTile8()); // Reload with new mask
}
Checkbox("Auto Normalize", &auto_normalize_pixels_);
}
EndChild();
// Manual tile8 editor popup
DrawManualTile8Inputs();
EndTable();
}
// Close the tile16 editor scroll region
EndChild();
EndTable();
}