refactor(editor): streamline ImGui card management across various editors

- Refactored multiple editor classes to ensure that ImGui::End() is always called after ImGui::Begin(), enhancing state management and preventing potential rendering issues.
- Updated the DungeonEditor, GraphicsEditor, ScreenEditor, MessageEditor, MusicEditor, and SpriteEditor to follow this pattern, improving code consistency and reliability.

Benefits:
- Improves the stability of the editor UI by ensuring proper handling of ImGui state.
- Enhances code readability and maintainability by standardizing the usage of ImGui functions across different editor components.
This commit is contained in:
scawful
2025-10-13 15:14:01 -04:00
parent 27aba01864
commit c95e5ac7ef
8 changed files with 168 additions and 165 deletions

View File

@@ -443,58 +443,56 @@ void DungeonEditorV2::DrawRoomsListCard() {
if (selector_card.Begin()) {
if (!rom_ || !rom_->is_loaded()) {
ImGui::Text("ROM not loaded");
selector_card.End();
return;
}
// Add text filter
static char room_filter[256] = "";
ImGui::SetNextItemWidth(-1);
if (ImGui::InputTextWithHint("##RoomFilter", ICON_MD_SEARCH " Filter rooms...",
room_filter, sizeof(room_filter))) {
// Filter updated
}
ImGui::Separator();
// Scrollable room list - simple and reliable
if (ImGui::BeginChild("##RoomsList", ImVec2(0, 0), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
std::string filter_str = room_filter;
std::transform(filter_str.begin(), filter_str.end(), filter_str.begin(), ::tolower);
} else {
// Add text filter
static char room_filter[256] = "";
ImGui::SetNextItemWidth(-1);
if (ImGui::InputTextWithHint("##RoomFilter", ICON_MD_SEARCH " Filter rooms...",
room_filter, sizeof(room_filter))) {
// Filter updated
}
for (int i = 0; i < zelda3::NumberOfRooms; i++) {
// Get room name
std::string room_name;
if (i < static_cast<int>(std::size(zelda3::kRoomNames))) {
room_name = std::string(zelda3::kRoomNames[i]);
} else {
room_name = absl::StrFormat("Room %03X", i);
}
ImGui::Separator();
// Scrollable room list - simple and reliable
if (ImGui::BeginChild("##RoomsList", ImVec2(0, 0), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
std::string filter_str = room_filter;
std::transform(filter_str.begin(), filter_str.end(), filter_str.begin(), ::tolower);
// Apply filter
if (!filter_str.empty()) {
std::string name_lower = room_name;
std::transform(name_lower.begin(), name_lower.end(),
name_lower.begin(), ::tolower);
if (name_lower.find(filter_str) == std::string::npos) {
continue;
for (int i = 0; i < zelda3::NumberOfRooms; i++) {
// Get room name
std::string room_name;
if (i < static_cast<int>(std::size(zelda3::kRoomNames))) {
room_name = std::string(zelda3::kRoomNames[i]);
} else {
room_name = absl::StrFormat("Room %03X", i);
}
// Apply filter
if (!filter_str.empty()) {
std::string name_lower = room_name;
std::transform(name_lower.begin(), name_lower.end(),
name_lower.begin(), ::tolower);
if (name_lower.find(filter_str) == std::string::npos) {
continue;
}
}
// Simple selectable with room ID and name
std::string label = absl::StrFormat("[%03X] %s", i, room_name.c_str());
bool is_selected = (current_room_id_ == i);
if (ImGui::Selectable(label.c_str(), is_selected)) {
OnRoomSelected(i);
}
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) {
OnRoomSelected(i);
}
}
// Simple selectable with room ID and name
std::string label = absl::StrFormat("[%03X] %s", i, room_name.c_str());
bool is_selected = (current_room_id_ == i);
if (ImGui::Selectable(label.c_str(), is_selected)) {
OnRoomSelected(i);
}
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) {
OnRoomSelected(i);
}
ImGui::EndChild();
}
ImGui::EndChild();
}
}
selector_card.End();
@@ -510,92 +508,90 @@ void DungeonEditorV2::DrawEntrancesListCard() {
if (entrances_card.Begin()) {
if (!rom_ || !rom_->is_loaded()) {
ImGui::Text("ROM not loaded");
entrances_card.End();
return;
}
// Full entrance configuration UI (matching dungeon_room_selector layout)
auto& current_entrance = entrances_[current_entrance_id_];
gui::InputHexWord("Entrance ID", &current_entrance.entrance_id_);
gui::InputHexWord("Room ID", reinterpret_cast<uint16_t*>(&current_entrance.room_));
ImGui::SameLine();
gui::InputHexByte("Dungeon ID", &current_entrance.dungeon_id_, 50.f, true);
gui::InputHexByte("Blockset", &current_entrance.blockset_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("Music", &current_entrance.music_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("Floor", &current_entrance.floor_);
ImGui::Separator();
gui::InputHexWord("Player X ", &current_entrance.x_position_);
ImGui::SameLine();
gui::InputHexWord("Player Y ", &current_entrance.y_position_);
gui::InputHexWord("Camera X", &current_entrance.camera_trigger_x_);
ImGui::SameLine();
gui::InputHexWord("Camera Y", &current_entrance.camera_trigger_y_);
gui::InputHexWord("Scroll X ", &current_entrance.camera_x_);
ImGui::SameLine();
gui::InputHexWord("Scroll Y ", &current_entrance.camera_y_);
gui::InputHexWord("Exit", reinterpret_cast<uint16_t*>(&current_entrance.exit_), 50.f, true);
ImGui::Separator();
ImGui::Text("Camera Boundaries");
ImGui::Separator();
ImGui::Text("\t\t\t\t\tNorth East South West");
gui::InputHexByte("Quadrant", &current_entrance.camera_boundary_qn_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("##QE", &current_entrance.camera_boundary_qe_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("##QS", &current_entrance.camera_boundary_qs_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("##QW", &current_entrance.camera_boundary_qw_, 50.f, true);
gui::InputHexByte("Full room", &current_entrance.camera_boundary_fn_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("##FE", &current_entrance.camera_boundary_fe_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("##FS", &current_entrance.camera_boundary_fs_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("##FW", &current_entrance.camera_boundary_fw_, 50.f, true);
ImGui::Separator();
// Entrance list - simple and reliable
if (ImGui::BeginChild("##EntrancesList", ImVec2(0, 0), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
for (int i = 0; i < 0x8C; i++) {
// The last seven are spawn points
std::string entrance_name;
if (i < 0x85) {
entrance_name = std::string(zelda3::kEntranceNames[i]);
} else {
entrance_name = absl::StrFormat("Spawn Point %d", i - 0x85);
}
// Get associated room name
int room_id = entrances_[i].room_;
std::string room_name = "Unknown";
if (room_id >= 0 && room_id < static_cast<int>(std::size(zelda3::kRoomNames))) {
room_name = std::string(zelda3::kRoomNames[room_id]);
}
std::string label = absl::StrFormat("[%02X] %s -> %s",
i, entrance_name.c_str(), room_name.c_str());
bool is_selected = (current_entrance_id_ == i);
if (ImGui::Selectable(label.c_str(), is_selected)) {
current_entrance_id_ = i;
OnEntranceSelected(i);
} else {
// Full entrance configuration UI (matching dungeon_room_selector layout)
auto& current_entrance = entrances_[current_entrance_id_];
gui::InputHexWord("Entrance ID", &current_entrance.entrance_id_);
gui::InputHexWord("Room ID", reinterpret_cast<uint16_t*>(&current_entrance.room_));
ImGui::SameLine();
gui::InputHexByte("Dungeon ID", &current_entrance.dungeon_id_, 50.f, true);
gui::InputHexByte("Blockset", &current_entrance.blockset_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("Music", &current_entrance.music_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("Floor", &current_entrance.floor_);
ImGui::Separator();
gui::InputHexWord("Player X ", &current_entrance.x_position_);
ImGui::SameLine();
gui::InputHexWord("Player Y ", &current_entrance.y_position_);
gui::InputHexWord("Camera X", &current_entrance.camera_trigger_x_);
ImGui::SameLine();
gui::InputHexWord("Camera Y", &current_entrance.camera_trigger_y_);
gui::InputHexWord("Scroll X ", &current_entrance.camera_x_);
ImGui::SameLine();
gui::InputHexWord("Scroll Y ", &current_entrance.camera_y_);
gui::InputHexWord("Exit", reinterpret_cast<uint16_t*>(&current_entrance.exit_), 50.f, true);
ImGui::Separator();
ImGui::Text("Camera Boundaries");
ImGui::Separator();
ImGui::Text("\t\t\t\t\tNorth East South West");
gui::InputHexByte("Quadrant", &current_entrance.camera_boundary_qn_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("##QE", &current_entrance.camera_boundary_qe_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("##QS", &current_entrance.camera_boundary_qs_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("##QW", &current_entrance.camera_boundary_qw_, 50.f, true);
gui::InputHexByte("Full room", &current_entrance.camera_boundary_fn_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("##FE", &current_entrance.camera_boundary_fe_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("##FS", &current_entrance.camera_boundary_fs_, 50.f, true);
ImGui::SameLine();
gui::InputHexByte("##FW", &current_entrance.camera_boundary_fw_, 50.f, true);
ImGui::Separator();
// Entrance list - simple and reliable
if (ImGui::BeginChild("##EntrancesList", ImVec2(0, 0), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
for (int i = 0; i < 0x8C; i++) {
// The last seven are spawn points
std::string entrance_name;
if (i < 0x85) {
entrance_name = std::string(zelda3::kEntranceNames[i]);
} else {
entrance_name = absl::StrFormat("Spawn Point %d", i - 0x85);
}
// Get associated room name
int room_id = entrances_[i].room_;
std::string room_name = "Unknown";
if (room_id >= 0 && room_id < static_cast<int>(std::size(zelda3::kRoomNames))) {
room_name = std::string(zelda3::kRoomNames[room_id]);
}
std::string label = absl::StrFormat("[%02X] %s -> %s",
i, entrance_name.c_str(), room_name.c_str());
bool is_selected = (current_entrance_id_ == i);
if (ImGui::Selectable(label.c_str(), is_selected)) {
current_entrance_id_ = i;
OnEntranceSelected(i);
}
}
ImGui::EndChild();
}
ImGui::EndChild();
}
}
entrances_card.End();
@@ -757,12 +753,8 @@ void DungeonEditorV2::DrawRoomGraphicsCard() {
if (graphics_card.Begin()) {
if (!rom_ || !rom_->is_loaded()) {
ImGui::Text("ROM not loaded");
graphics_card.End();
return;
}
// Show graphics for current room
if (current_room_id_ >= 0 && current_room_id_ < static_cast<int>(rooms_.size())) {
} else if (current_room_id_ >= 0 && current_room_id_ < static_cast<int>(rooms_.size())) {
// Show graphics for current room
auto& room = rooms_[current_room_id_];
ImGui::Text("Room %03X Graphics", current_room_id_);

View File

@@ -112,28 +112,29 @@ absl::Status GraphicsEditor::Update() {
prototype_card.SetDefaultSize(600, 500);
// Get visibility flags from card manager and pass to Begin()
// Always call End() after Begin() - End() handles ImGui state safely
if (sheet_editor_card.Begin(card_manager.GetVisibilityFlag("graphics.sheet_editor"))) {
status_ = UpdateGfxEdit();
sheet_editor_card.End();
}
sheet_editor_card.End();
if (sheet_browser_card.Begin(card_manager.GetVisibilityFlag("graphics.sheet_browser"))) {
if (asset_browser_.Initialized == false) {
asset_browser_.Initialize(gfx::Arena::Get().gfx_sheets());
}
asset_browser_.Draw(gfx::Arena::Get().gfx_sheets());
sheet_browser_card.End();
}
sheet_browser_card.End();
if (player_anims_card.Begin(card_manager.GetVisibilityFlag("graphics.player_animations"))) {
status_ = UpdateLinkGfxView();
player_anims_card.End();
}
player_anims_card.End();
if (prototype_card.Begin(card_manager.GetVisibilityFlag("graphics.prototype_viewer"))) {
status_ = UpdateScadView();
prototype_card.End();
}
prototype_card.End();
CLEAR_AND_RETURN_STATUS(status_)
return absl::OkStatus();

View File

@@ -109,30 +109,29 @@ absl::Status ScreenEditor::Update() {
naming_screen_card.SetDefaultSize(500, 400);
// Get visibility flags from card manager and pass to Begin()
// Always call End() after Begin() - End() handles ImGui state safely
if (dungeon_maps_card.Begin(card_manager.GetVisibilityFlag("screen.dungeon_maps"))) {
DrawDungeonMapsEditor();
}
dungeon_maps_card.End();
if (inventory_menu_card.Begin(card_manager.GetVisibilityFlag("screen.inventory_menu"))) {
DrawInventoryMenuEditor();
}
inventory_menu_card.End();
if (overworld_map_card.Begin(card_manager.GetVisibilityFlag("screen.overworld_map"))) {
DrawOverworldMapEditor();
}
overworld_map_card.End();
if (title_screen_card.Begin(card_manager.GetVisibilityFlag("screen.title_screen"))) {
DrawTitleScreenEditor();
}
title_screen_card.End();
if (naming_screen_card.Begin(card_manager.GetVisibilityFlag("screen.naming_screen"))) {
DrawNamingScreenEditor();
DrawNamingScreenEditor();
}
naming_screen_card.End();

View File

@@ -151,8 +151,8 @@ absl::Status MessageEditor::Update() {
list_card.SetDefaultSize(400, 600);
if (list_card.Begin()) {
DrawMessageList();
list_card.End();
}
list_card.End();
}
// Message Editor Card
@@ -161,8 +161,8 @@ absl::Status MessageEditor::Update() {
editor_card.SetDefaultSize(500, 600);
if (editor_card.Begin()) {
DrawCurrentMessage();
editor_card.End();
}
editor_card.End();
}
// Font Atlas Card
@@ -172,8 +172,8 @@ absl::Status MessageEditor::Update() {
if (font_card.Begin()) {
DrawFontAtlas();
DrawExpandedMessageSettings();
font_card.End();
}
font_card.End();
}
// Dictionary Card
@@ -184,8 +184,8 @@ absl::Status MessageEditor::Update() {
DrawTextCommands();
DrawSpecialCharacters();
DrawDictionary();
dict_card.End();
}
dict_card.End();
}
return absl::OkStatus();

View File

@@ -48,20 +48,20 @@ absl::Status MusicEditor::Update() {
// Music Tracker Card
if (tracker_card.Begin(card_manager.GetVisibilityFlag("music.tracker"))) {
DrawTrackerView();
tracker_card.End();
}
tracker_card.End();
// Instrument Editor Card
if (instrument_card.Begin(card_manager.GetVisibilityFlag("music.instrument_editor"))) {
DrawInstrumentEditor();
instrument_card.End();
}
instrument_card.End();
// Assembly View Card
if (assembly_card.Begin(card_manager.GetVisibilityFlag("music.assembly"))) {
assembly_editor_.InlineUpdate();
assembly_card.End();
}
assembly_card.End();
return absl::OkStatus();
}

View File

@@ -57,15 +57,16 @@ absl::Status SpriteEditor::Update() {
custom_card.SetDefaultSize(800, 600);
// Get visibility flags from card manager and pass to Begin()
// Always call End() after Begin() - End() handles ImGui state safely
if (vanilla_card.Begin(card_manager.GetVisibilityFlag("sprite.vanilla_editor"))) {
DrawVanillaSpriteEditor();
vanilla_card.End();
}
vanilla_card.End();
if (custom_card.Begin(card_manager.GetVisibilityFlag("sprite.custom_editor"))) {
DrawCustomSprites();
custom_card.End();
}
custom_card.End();
return status_.ok() ? absl::OkStatus() : status_;
}