Fixes formatting violations that were causing CI failures. Applied clang-format-14 to ensure consistent code formatting across the codebase. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
331 lines
11 KiB
C++
331 lines
11 KiB
C++
#include "object_editor_card.h"
|
|
|
|
#include "absl/strings/str_format.h"
|
|
#include "app/gfx/backend/irenderer.h"
|
|
#include "app/gui/core/icons.h"
|
|
#include "app/gui/core/ui_helpers.h"
|
|
#include "imgui/imgui.h"
|
|
|
|
namespace yaze::editor {
|
|
|
|
ObjectEditorCard::ObjectEditorCard(gfx::IRenderer* renderer, Rom* rom,
|
|
DungeonCanvasViewer* canvas_viewer)
|
|
: renderer_(renderer),
|
|
rom_(rom),
|
|
canvas_viewer_(canvas_viewer),
|
|
object_selector_(rom) {
|
|
emulator_preview_.Initialize(renderer, rom);
|
|
}
|
|
|
|
void ObjectEditorCard::Draw(bool* p_open) {
|
|
gui::EditorCard card("Object Editor", ICON_MD_CONSTRUCTION, p_open);
|
|
card.SetDefaultSize(450, 750);
|
|
card.SetPosition(gui::EditorCard::Position::Right);
|
|
|
|
if (card.Begin(p_open)) {
|
|
// Interaction mode controls at top (moved from tab)
|
|
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Mode:");
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::RadioButton("None",
|
|
interaction_mode_ == InteractionMode::None)) {
|
|
interaction_mode_ = InteractionMode::None;
|
|
canvas_viewer_->SetObjectInteractionEnabled(false);
|
|
canvas_viewer_->ClearPreviewObject();
|
|
}
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::RadioButton("Place",
|
|
interaction_mode_ == InteractionMode::Place)) {
|
|
interaction_mode_ = InteractionMode::Place;
|
|
canvas_viewer_->SetObjectInteractionEnabled(true);
|
|
if (has_preview_object_) {
|
|
canvas_viewer_->SetPreviewObject(preview_object_);
|
|
}
|
|
}
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::RadioButton("Select",
|
|
interaction_mode_ == InteractionMode::Select)) {
|
|
interaction_mode_ = InteractionMode::Select;
|
|
canvas_viewer_->SetObjectInteractionEnabled(true);
|
|
canvas_viewer_->ClearPreviewObject();
|
|
}
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::RadioButton("Delete",
|
|
interaction_mode_ == InteractionMode::Delete)) {
|
|
interaction_mode_ = InteractionMode::Delete;
|
|
canvas_viewer_->SetObjectInteractionEnabled(true);
|
|
canvas_viewer_->ClearPreviewObject();
|
|
}
|
|
|
|
// Current object info
|
|
DrawSelectedObjectInfo();
|
|
|
|
ImGui::Separator();
|
|
|
|
// Tabbed interface for Browser and Preview
|
|
if (ImGui::BeginTabBar("##ObjectEditorTabs", ImGuiTabBarFlags_None)) {
|
|
|
|
// Tab 1: Object Browser
|
|
if (ImGui::BeginTabItem(ICON_MD_LIST " Browser")) {
|
|
DrawObjectSelector();
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
// Tab 2: Emulator Preview (enhanced)
|
|
if (ImGui::BeginTabItem(ICON_MD_MONITOR " Preview")) {
|
|
DrawEmulatorPreview();
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
ImGui::EndTabBar();
|
|
}
|
|
}
|
|
card.End();
|
|
}
|
|
|
|
void ObjectEditorCard::DrawObjectSelector() {
|
|
ImGui::Text(ICON_MD_INFO " Select an object to place on the canvas");
|
|
ImGui::Separator();
|
|
|
|
// Text filter for objects
|
|
static char object_filter[256] = "";
|
|
ImGui::SetNextItemWidth(-1);
|
|
if (ImGui::InputTextWithHint("##ObjectFilter",
|
|
ICON_MD_SEARCH " Filter objects...",
|
|
object_filter, sizeof(object_filter))) {
|
|
// Filter updated
|
|
}
|
|
|
|
ImGui::Separator();
|
|
|
|
// Object list with categories
|
|
if (ImGui::BeginChild("##ObjectList", ImVec2(0, 0), true)) {
|
|
// Floor objects
|
|
if (ImGui::CollapsingHeader(ICON_MD_GRID_ON " Floor Objects",
|
|
ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
for (int i = 0; i < 0x100; i++) {
|
|
std::string filter_str = object_filter;
|
|
if (!filter_str.empty()) {
|
|
// Simple name-based filtering
|
|
std::string object_name = absl::StrFormat("Object %02X", i);
|
|
std::transform(filter_str.begin(), filter_str.end(),
|
|
filter_str.begin(), ::tolower);
|
|
std::transform(object_name.begin(), object_name.end(),
|
|
object_name.begin(), ::tolower);
|
|
if (object_name.find(filter_str) == std::string::npos) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Create preview icon with small canvas
|
|
ImGui::BeginGroup();
|
|
|
|
// Small preview canvas (32x32 pixels)
|
|
DrawObjectPreviewIcon(i, ImVec2(32, 32));
|
|
|
|
ImGui::SameLine();
|
|
|
|
// Object label and selection
|
|
std::string object_label = absl::StrFormat("%02X - Floor Object", i);
|
|
|
|
if (ImGui::Selectable(object_label.c_str(),
|
|
has_preview_object_ && preview_object_.id_ == i,
|
|
0, ImVec2(0, 32))) { // Match preview height
|
|
preview_object_ =
|
|
zelda3::RoomObject{static_cast<int16_t>(i), 0, 0, 0, 0};
|
|
has_preview_object_ = true;
|
|
canvas_viewer_->SetPreviewObject(preview_object_);
|
|
canvas_viewer_->SetObjectInteractionEnabled(true);
|
|
interaction_mode_ = InteractionMode::Place;
|
|
}
|
|
|
|
ImGui::EndGroup();
|
|
|
|
if (ImGui::IsItemHovered()) {
|
|
ImGui::BeginTooltip();
|
|
ImGui::Text("Object ID: 0x%02X", i);
|
|
ImGui::Text("Type: Floor Object");
|
|
ImGui::Text("Click to select for placement");
|
|
ImGui::EndTooltip();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Wall objects
|
|
if (ImGui::CollapsingHeader(ICON_MD_BORDER_ALL " Wall Objects")) {
|
|
for (int i = 0; i < 0x50; i++) {
|
|
std::string object_label = absl::StrFormat("%s %02X - Wall Object",
|
|
ICON_MD_BORDER_VERTICAL, i);
|
|
|
|
if (ImGui::Selectable(object_label.c_str())) {
|
|
// Wall objects have special handling
|
|
preview_object_ = zelda3::RoomObject{static_cast<int16_t>(i), 0, 0, 0,
|
|
1}; // layer=1 for walls
|
|
has_preview_object_ = true;
|
|
canvas_viewer_->SetPreviewObject(preview_object_);
|
|
canvas_viewer_->SetObjectInteractionEnabled(true);
|
|
interaction_mode_ = InteractionMode::Place;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Special objects
|
|
if (ImGui::CollapsingHeader(ICON_MD_STAR " Special Objects")) {
|
|
const char* special_objects[] = {"Stairs Down", "Stairs Up", "Chest",
|
|
"Door", "Pot", "Block",
|
|
"Switch", "Torch"};
|
|
|
|
for (int i = 0; i < IM_ARRAYSIZE(special_objects); i++) {
|
|
std::string object_label =
|
|
absl::StrFormat("%s %s", ICON_MD_STAR, special_objects[i]);
|
|
|
|
if (ImGui::Selectable(object_label.c_str())) {
|
|
// Special object IDs start at 0xF8
|
|
preview_object_ =
|
|
zelda3::RoomObject{static_cast<int16_t>(0xF8 + i), 0, 0, 0, 2};
|
|
has_preview_object_ = true;
|
|
canvas_viewer_->SetPreviewObject(preview_object_);
|
|
canvas_viewer_->SetObjectInteractionEnabled(true);
|
|
interaction_mode_ = InteractionMode::Place;
|
|
}
|
|
}
|
|
}
|
|
|
|
ImGui::EndChild();
|
|
}
|
|
|
|
// Quick actions at bottom
|
|
if (ImGui::Button(ICON_MD_CLEAR " Clear Selection", ImVec2(-1, 0))) {
|
|
has_preview_object_ = false;
|
|
canvas_viewer_->ClearPreviewObject();
|
|
canvas_viewer_->SetObjectInteractionEnabled(false);
|
|
interaction_mode_ = InteractionMode::None;
|
|
}
|
|
}
|
|
|
|
void ObjectEditorCard::DrawEmulatorPreview() {
|
|
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f),
|
|
ICON_MD_INFO " Real-time object rendering preview");
|
|
ImGui::Separator();
|
|
|
|
// Toggle emulator preview visibility
|
|
ImGui::Checkbox("Enable Preview", &show_emulator_preview_);
|
|
ImGui::SameLine();
|
|
gui::HelpMarker(
|
|
"Uses SNES emulation to render objects accurately.\n"
|
|
"May impact performance.");
|
|
|
|
if (show_emulator_preview_) {
|
|
ImGui::Separator();
|
|
|
|
// Embed the emulator preview with improved layout
|
|
ImGui::BeginChild("##EmulatorPreviewRegion", ImVec2(0, 0), true);
|
|
|
|
emulator_preview_.Render();
|
|
|
|
ImGui::EndChild();
|
|
} else {
|
|
ImGui::Separator();
|
|
ImGui::TextDisabled(ICON_MD_PREVIEW " Preview disabled for performance");
|
|
ImGui::TextWrapped(
|
|
"Enable to see accurate object rendering using "
|
|
"SNES emulation.");
|
|
}
|
|
}
|
|
|
|
// DrawInteractionControls removed - controls moved to top of card
|
|
|
|
void ObjectEditorCard::DrawObjectPreviewIcon(int object_id,
|
|
const ImVec2& size) {
|
|
// Create a small preview box for the object
|
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
|
ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
|
|
ImVec2 box_min = cursor_pos;
|
|
ImVec2 box_max = ImVec2(cursor_pos.x + size.x, cursor_pos.y + size.y);
|
|
|
|
// Draw background
|
|
draw_list->AddRectFilled(box_min, box_max, IM_COL32(40, 40, 45, 255));
|
|
draw_list->AddRect(box_min, box_max, IM_COL32(100, 100, 100, 255));
|
|
|
|
// Draw a simple representation based on object ID
|
|
// For now, use colored squares and icons as placeholders
|
|
// Later this can be replaced with actual object bitmaps
|
|
|
|
// Color based on object ID for visual variety
|
|
float hue = (object_id % 16) / 16.0f;
|
|
ImU32 obj_color = ImGui::ColorConvertFloat4ToU32(
|
|
ImVec4(0.5f + hue * 0.3f, 0.4f, 0.6f - hue * 0.2f, 1.0f));
|
|
|
|
// Draw inner colored square (16x16 in the center)
|
|
ImVec2 inner_min = ImVec2(cursor_pos.x + 8, cursor_pos.y + 8);
|
|
ImVec2 inner_max = ImVec2(cursor_pos.x + 24, cursor_pos.y + 24);
|
|
draw_list->AddRectFilled(inner_min, inner_max, obj_color);
|
|
draw_list->AddRect(inner_min, inner_max, IM_COL32(200, 200, 200, 255));
|
|
|
|
// Draw object ID text (very small)
|
|
std::string id_text = absl::StrFormat("%02X", object_id);
|
|
ImVec2 text_size = ImGui::CalcTextSize(id_text.c_str());
|
|
ImVec2 text_pos = ImVec2(cursor_pos.x + (size.x - text_size.x) * 0.5f,
|
|
cursor_pos.y + size.y - text_size.y - 2);
|
|
draw_list->AddText(text_pos, IM_COL32(180, 180, 180, 255), id_text.c_str());
|
|
|
|
// Advance cursor
|
|
ImGui::Dummy(size);
|
|
}
|
|
|
|
void ObjectEditorCard::DrawSelectedObjectInfo() {
|
|
ImGui::BeginGroup();
|
|
|
|
// Show current object for placement
|
|
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f), ICON_MD_INFO " Current:");
|
|
|
|
if (has_preview_object_) {
|
|
ImGui::SameLine();
|
|
ImGui::Text("ID: 0x%02X", preview_object_.id_);
|
|
ImGui::SameLine();
|
|
ImGui::Text("Layer: %s",
|
|
preview_object_.layer_ == zelda3::RoomObject::BG1 ? "BG1"
|
|
: preview_object_.layer_ == zelda3::RoomObject::BG2 ? "BG2"
|
|
: "BG3");
|
|
} else {
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled("None");
|
|
}
|
|
|
|
// Show selection count
|
|
auto& interaction = canvas_viewer_->object_interaction();
|
|
const auto& selected = interaction.GetSelectedObjectIndices();
|
|
|
|
ImGui::SameLine();
|
|
ImGui::Text("|");
|
|
ImGui::SameLine();
|
|
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.4f, 1.0f),
|
|
ICON_MD_CHECKLIST " Selected: %zu", selected.size());
|
|
|
|
ImGui::SameLine();
|
|
ImGui::Text("|");
|
|
ImGui::SameLine();
|
|
ImGui::Text("Mode: %s", interaction_mode_ == InteractionMode::Place
|
|
? ICON_MD_ADD_BOX " Place"
|
|
: interaction_mode_ == InteractionMode::Select
|
|
? ICON_MD_CHECK_BOX " Select"
|
|
: interaction_mode_ == InteractionMode::Delete
|
|
? ICON_MD_DELETE " Delete"
|
|
: "None");
|
|
|
|
// Show quick actions for selections
|
|
if (!selected.empty()) {
|
|
ImGui::SameLine();
|
|
if (ImGui::SmallButton(ICON_MD_CLEAR " Clear")) {
|
|
interaction.ClearSelection();
|
|
}
|
|
}
|
|
|
|
ImGui::EndGroup();
|
|
}
|
|
|
|
} // namespace yaze::editor
|