diff --git a/src/app/editor/code/assembly_editor.cc b/src/app/editor/code/assembly_editor.cc index 6f1e3888..401a5fc7 100644 --- a/src/app/editor/code/assembly_editor.cc +++ b/src/app/editor/code/assembly_editor.cc @@ -8,6 +8,7 @@ #include "absl/strings/match.h" #include "util/file_util.h" #include "app/gui/icons.h" +#include "app/gui/ui_helpers.h" #include "app/gui/modules/text_editor.h" namespace yaze::editor { @@ -16,6 +17,82 @@ using util::FileDialogWrapper; namespace { +static const char *const kKeywords[] = { + "ADC", "AND", "ASL", "BCC", "BCS", "BEQ", "BIT", "BMI", "BNE", "BPL", + "BRA", "BRL", "BVC", "BVS", "CLC", "CLD", "CLI", "CLV", "CMP", "CPX", + "CPY", "DEC", "DEX", "DEY", "EOR", "INC", "INX", "INY", "JMP", "JSR", + "JSL", "LDA", "LDX", "LDY", "LSR", "MVN", "NOP", "ORA", "PEA", "PER", + "PHA", "PHB", "PHD", "PHP", "PHX", "PHY", "PLA", "PLB", "PLD", "PLP", + "PLX", "PLY", "REP", "ROL", "ROR", "RTI", "RTL", "RTS", "SBC", "SEC", + "SEI", "SEP", "STA", "STP", "STX", "STY", "STZ", "TAX", "TAY", "TCD", + "TCS", "TDC", "TRB", "TSB", "TSC", "TSX", "TXA", "TXS", "TXY", "TYA", + "TYX", "WAI", "WDM", "XBA", "XCE", "ORG", "LOROM", "HIROM"}; + +static const char *const kIdentifiers[] = { + "abort", "abs", "acos", "asin", "atan", "atexit", + "atof", "atoi", "atol", "ceil", "clock", "cosh", + "ctime", "div", "exit", "fabs", "floor", "fmod", + "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph", + "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", + "log", "memcmp", "modf", "pow", "putchar", "putenv", + "puts", "rand", "remove", "rename", "sinh", "sqrt", + "srand", "strcat", "strcmp", "strerror", "time", "tolower", + "toupper"}; + +TextEditor::LanguageDefinition GetAssemblyLanguageDef() { + TextEditor::LanguageDefinition language_65816; + for (auto &k : kKeywords) language_65816.mKeywords.emplace(k); + + for (auto &k : kIdentifiers) { + TextEditor::Identifier id; + id.mDeclaration = "Built-in function"; + language_65816.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } + + language_65816.mTokenRegexStrings.push_back( + std::make_pair( + "[ \\t]*#[ \\t]*[a-zA-Z_]+", TextEditor::PaletteIndex::Preprocessor)); + language_65816.mTokenRegexStrings.push_back( + std::make_pair( + "L?\\\"(\\\\.|[^\\\"])*\\\"", TextEditor::PaletteIndex::String)); + language_65816.mTokenRegexStrings.push_back( + std::make_pair( + "\\'\\\\?[^\\']\\'", TextEditor::PaletteIndex::CharLiteral)); + language_65816.mTokenRegexStrings.push_back( + std::make_pair( + "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", + TextEditor::PaletteIndex::Number)); + language_65816.mTokenRegexStrings.push_back( + std::make_pair( + "[+-]?[0-9]+[Uu]?[lL]?[lL]?", TextEditor::PaletteIndex::Number)); + language_65816.mTokenRegexStrings.push_back( + std::make_pair( + "0[0-7]+[Uu]?[lL]?[lL]?", TextEditor::PaletteIndex::Number)); + language_65816.mTokenRegexStrings.push_back( + std::make_pair( + "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", + TextEditor::PaletteIndex::Number)); + language_65816.mTokenRegexStrings.push_back( + std::make_pair( + "[a-zA-Z_][a-zA-Z0-9_]*", TextEditor::PaletteIndex::Identifier)); + language_65816.mTokenRegexStrings.push_back( + std::make_pair( + "[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/" + "\\;\\,\\.]", + TextEditor::PaletteIndex::Punctuation)); + + language_65816.mCommentStart = "/*"; + language_65816.mCommentEnd = "*/"; + language_65816.mSingleLineComment = ";"; + + language_65816.mCaseSensitive = false; + language_65816.mAutoIndentation = true; + + language_65816.mName = "65816"; + + return language_65816; +} + std::vector RemoveIgnoredFiles( const std::vector& files, const std::vector& ignored_files) { @@ -96,7 +173,7 @@ FolderItem LoadFolder(const std::string& folder) { } // namespace void AssemblyEditor::Initialize() { - // Set the language definition + text_editor_.SetLanguageDefinition(GetAssemblyLanguageDef()); } absl::Status AssemblyEditor::Load() { return absl::OkStatus(); } @@ -114,7 +191,6 @@ void AssemblyEditor::Update(bool& is_loaded) { } auto cpos = text_editor_.GetCursorPosition(); - SetEditorText(); ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s | %s", cpos.mLine + 1, cpos.mColumn + 1, text_editor_.GetTotalLines(), text_editor_.IsOverwrite() ? "Ovr" : "Ins", @@ -128,7 +204,6 @@ void AssemblyEditor::Update(bool& is_loaded) { void AssemblyEditor::InlineUpdate() { auto cpos = text_editor_.GetCursorPosition(); - SetEditorText(); ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s | %s", cpos.mLine + 1, cpos.mColumn + 1, text_editor_.GetTotalLines(), text_editor_.IsOverwrite() ? "Ovr" : "Ins", @@ -140,41 +215,75 @@ void AssemblyEditor::InlineUpdate() { } void AssemblyEditor::UpdateCodeView() { - ImGui::BeginTable("##table_view", 2, - ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | - ImGuiTableFlags_Resizable); + DrawToolset(); + gui::VerticalSpacing(2.0f); - // Table headers - ImGui::TableSetupColumn("Files", ImGuiTableColumnFlags_WidthFixed, 256.0f); - ImGui::TableSetupColumn("Editor", ImGuiTableColumnFlags_WidthStretch); - - ImGui::TableHeadersRow(); - - // Table data - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (current_folder_.name != "") { - DrawCurrentFolder(); - } else { - if (ImGui::Button("Open Folder")) { - current_folder_ = LoadFolder(FileDialogWrapper::ShowOpenFolderDialog()); + static gui::EditorCard file_browser_card("File Browser", ICON_MD_FOLDER); + bool file_browser_open = true; + if (file_browser_card.Begin(&file_browser_open)) { + if (current_folder_.name != "") { + DrawCurrentFolder(); + } else { + if (ImGui::Button("Open Folder")) { + current_folder_ = LoadFolder(FileDialogWrapper::ShowOpenFolderDialog()); + } } + file_browser_card.End(); } - ImGui::TableNextColumn(); + // Draw open files as individual, dockable EditorCards + for (int i = 0; i < active_files_.Size; i++) { + int file_id = active_files_[i]; + bool open = true; - auto cpos = text_editor_.GetCursorPosition(); - SetEditorText(); - ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s | %s", cpos.mLine + 1, - cpos.mColumn + 1, text_editor_.GetTotalLines(), - text_editor_.IsOverwrite() ? "Ovr" : "Ins", - text_editor_.CanUndo() ? "*" : " ", - text_editor_.GetLanguageDefinition().mName.c_str(), - current_file_.c_str()); + // Ensure we have a TextEditor instance for this file + if (file_id >= open_files_.size()) { + open_files_.resize(file_id + 1); + } + if (file_id >= files_.size()) { + // This can happen if a file was closed and its ID is being reused. + // For now, we just skip it. + continue; + } - text_editor_.Render("##asm_editor"); + std::string card_name = files_[file_id]; + gui::EditorCard file_card(card_name.c_str(), ICON_MD_DESCRIPTION, &open); + if (file_card.Begin()) { + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)) { + active_file_id_ = file_id; + } + open_files_[file_id].Render(absl::StrCat("##", card_name).c_str()); + file_card.End(); + } - ImGui::EndTable(); + if (!open) { + active_files_.erase(active_files_.Data + i); + i--; + } + } +} + +absl::Status AssemblyEditor::Save() { + if (active_file_id_ != -1 && active_file_id_ < open_files_.size()) { + std::string content = open_files_[active_file_id_].GetText(); + util::SaveFile(files_[active_file_id_], content); + return absl::OkStatus(); + } + return absl::FailedPreconditionError("No active file to save."); +} + +void AssemblyEditor::DrawToolset() { + static gui::Toolset toolbar; + toolbar.Begin(); + + if (toolbar.AddAction(ICON_MD_FOLDER_OPEN, "Open Folder")) { + current_folder_ = LoadFolder(FileDialogWrapper::ShowOpenFolderDialog()); + } + if (toolbar.AddAction(ICON_MD_SAVE, "Save File")) { + Save(); + } + + toolbar.End(); } void AssemblyEditor::DrawCurrentFolder() { @@ -227,58 +336,6 @@ void AssemblyEditor::DrawCurrentFolder() { } } -void AssemblyEditor::DrawFileTabView() { - static int next_tab_id = 0; - - if (ImGui::BeginTabBar("AssemblyFileTabBar", ImGuiTabBarFlags_None)) { - if (ImGui::TabItemButton(ICON_MD_ADD, ImGuiTabItemFlags_None)) { - if (std::ranges::find(active_files_, current_file_id_) != - active_files_.end()) { - // Room is already open - next_tab_id++; - } - active_files_.push_back(next_tab_id++); // Add new tab - } - - // Submit our regular tabs - for (int n = 0; n < active_files_.Size;) { - bool open = true; - - if (ImGui::BeginTabItem(files_[active_files_[n]].data(), &open, - ImGuiTabItemFlags_None)) { - auto cpos = text_editor_.GetCursorPosition(); - { - std::ifstream t(current_file_); - if (t.good()) { - std::string str((std::istreambuf_iterator(t)), - std::istreambuf_iterator()); - text_editor_.SetText(str); - } else { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Error opening file: %s\n", current_file_.c_str()); - } - } - ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s | %s", cpos.mLine + 1, - cpos.mColumn + 1, text_editor_.GetTotalLines(), - text_editor_.IsOverwrite() ? "Ovr" : "Ins", - text_editor_.CanUndo() ? "*" : " ", - text_editor_.GetLanguageDefinition().mName.c_str(), - current_file_.c_str()); - - open_files_[active_files_[n]].Render("##asm_editor"); - ImGui::EndTabItem(); - } - - if (!open) - active_files_.erase(active_files_.Data + n); - else - n++; - } - - ImGui::EndTabBar(); - } - ImGui::Separator(); -} void AssemblyEditor::DrawFileMenu() { if (ImGui::BeginMenu("File")) { @@ -319,19 +376,36 @@ void AssemblyEditor::DrawEditMenu() { } } -void AssemblyEditor::SetEditorText() { - if (!file_is_loaded_) { - std::ifstream t(current_file_); - if (t.good()) { - std::string str((std::istreambuf_iterator(t)), - std::istreambuf_iterator()); - text_editor_.SetText(str); - } else { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error opening file: %s\n", - current_file_.c_str()); +void AssemblyEditor::ChangeActiveFile(const std::string_view &filename) { + // Check if file is already open + for (int i = 0; i < active_files_.Size; ++i) { + int file_id = active_files_[i]; + if (files_[file_id] == filename) { + // Optional: Focus window + return; + } + } + + // Add new file + int new_file_id = files_.size(); + files_.push_back(std::string(filename)); + active_files_.push_back(new_file_id); + + // Resize open_files_ if needed + if (new_file_id >= open_files_.size()) { + open_files_.resize(new_file_id + 1); + } + + // Load file content using utility + std::string content = util::LoadFile(std::string(filename)); + if (!content.empty()) { + open_files_[new_file_id].SetText(content); + open_files_[new_file_id].SetLanguageDefinition(GetAssemblyLanguageDef()); + open_files_[new_file_id].SetPalette(TextEditor::GetDarkPalette()); + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error opening file: %s\n", + std::string(filename).c_str()); } - file_is_loaded_ = true; - } } absl::Status AssemblyEditor::Cut() { diff --git a/src/app/editor/code/assembly_editor.h b/src/app/editor/code/assembly_editor.h index a8bc47ac..06eab770 100644 --- a/src/app/editor/code/assembly_editor.h +++ b/src/app/editor/code/assembly_editor.h @@ -5,6 +5,7 @@ #include "app/editor/editor.h" #include "app/gui/modules/text_editor.h" +#include "app/gui/editor_layout.h" #include "app/gui/style.h" #include "app/rom.h" @@ -24,15 +25,11 @@ struct FolderItem { class AssemblyEditor : public Editor { public: explicit AssemblyEditor(Rom* rom = nullptr) : rom_(rom) { - text_editor_.SetLanguageDefinition(gui::GetAssemblyLanguageDef()); text_editor_.SetPalette(TextEditor::GetDarkPalette()); text_editor_.SetShowWhitespaces(false); type_ = EditorType::kAssembly; } - void ChangeActiveFile(const std::string_view &filename) { - current_file_ = filename; - file_is_loaded_ = false; - } + void ChangeActiveFile(const std::string_view &filename); void Initialize() override; absl::Status Load() override; @@ -51,7 +48,7 @@ class AssemblyEditor : public Editor { absl::Status Update() override; - absl::Status Save() override { return absl::UnimplementedError("Save"); } + absl::Status Save() override; void OpenFolder(const std::string &folder_path); @@ -61,12 +58,13 @@ class AssemblyEditor : public Editor { private: void DrawFileMenu(); void DrawEditMenu(); - void SetEditorText(); void DrawCurrentFolder(); void DrawFileTabView(); + void DrawToolset(); bool file_is_loaded_ = false; int current_file_id_ = 0; + int active_file_id_ = -1; std::vector files_; std::vector open_files_; diff --git a/src/app/editor/dungeon/dungeon_editor_v2.cc b/src/app/editor/dungeon/dungeon_editor_v2.cc index 084a9be1..4251c518 100644 --- a/src/app/editor/dungeon/dungeon_editor_v2.cc +++ b/src/app/editor/dungeon/dungeon_editor_v2.cc @@ -6,16 +6,13 @@ #include "app/gfx/snes_palette.h" #include "app/zelda3/dungeon/room.h" #include "app/gui/icons.h" +#include "app/gui/ui_helpers.h" #include "imgui/imgui.h" namespace yaze::editor { using ImGui::BeginTable; -using ImGui::BeginTabBar; -using ImGui::BeginTabItem; using ImGui::EndTable; -using ImGui::EndTabBar; -using ImGui::EndTabItem; using ImGui::TableHeadersRow; using ImGui::TableNextColumn; using ImGui::TableNextRow; @@ -65,6 +62,9 @@ absl::Status DungeonEditorV2::Update() { return absl::OkStatus(); } + DrawToolset(); + gui::VerticalSpacing(2.0f); + DrawLayout(); return absl::OkStatus(); } @@ -86,6 +86,17 @@ absl::Status DungeonEditorV2::Save() { return absl::OkStatus(); } +void DungeonEditorV2::DrawToolset() { + static gui::Toolset toolbar; + toolbar.Begin(); + + if (toolbar.AddAction(ICON_MD_ADD, "Open Room")) { + OnRoomSelected(room_selector_.current_room_id()); + } + + toolbar.End(); +} + void DungeonEditorV2::DrawLayout() { // Simple 3-column layout as designed if (BeginTable("##DungeonEditTable", 3, @@ -101,40 +112,9 @@ void DungeonEditorV2::DrawLayout() { TableNextColumn(); room_selector_.Draw(); - // Column 2: Canvas (fully delegated) + // Column 2: Canvas area for active room cards TableNextColumn(); - if (BeginTabBar("##RoomTabs")) { - for (int i = 0; i < active_rooms_.Size; i++) { - int room_id = active_rooms_[i]; - bool open = true; - - std::string tab_name_str; - const char* tab_name; - if (room_id >= 0 && static_cast(room_id) < std::size(zelda3::kRoomNames)) { - tab_name = zelda3::kRoomNames[room_id].data(); - } else { - tab_name_str = absl::StrFormat("Room %03X", room_id); - tab_name = tab_name_str.c_str(); - } - - if (BeginTabItem(tab_name, &open)) { - DrawRoomTab(room_id); - EndTabItem(); - } - - if (!open) { - active_rooms_.erase(active_rooms_.Data + i); - i--; - } - } - - // Add tab button - if (ImGui::TabItemButton(ICON_MD_ADD, ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip)) { - OnRoomSelected(room_selector_.current_room_id()); - } - - EndTabBar(); - } + // This column is now just a docking space. The cards themselves are independent windows. // Column 3: Object Selector (fully delegated) TableNextColumn(); @@ -142,6 +122,32 @@ void DungeonEditorV2::DrawLayout() { EndTable(); } + + // Draw active rooms as individual, dockable EditorCards + for (int i = 0; i < active_rooms_.Size; i++) { + int room_id = active_rooms_[i]; + bool open = true; + + std::string card_name_str; + const char* card_name; + if (room_id >= 0 && static_cast(room_id) < std::size(zelda3::kRoomNames)) { + card_name_str = absl::StrFormat("%s###RoomCard%d", zelda3::kRoomNames[room_id].data(), room_id); + } else { + card_name_str = absl::StrFormat("Room %03X###RoomCard%d", room_id, room_id); + } + card_name = card_name_str.c_str(); + + gui::EditorCard room_card(card_name, ICON_MD_GRID_ON, &open); + if (room_card.Begin()) { + DrawRoomTab(room_id); + room_card.End(); + } + + if (!open) { + active_rooms_.erase(active_rooms_.Data + i); + i--; + } + } } void DungeonEditorV2::DrawRoomTab(int room_id) { @@ -188,11 +194,12 @@ void DungeonEditorV2::OnRoomSelected(int room_id) { // Check if already open for (int i = 0; i < active_rooms_.Size; i++) { if (active_rooms_[i] == room_id) { - return; // Already open + // Optional: Focus the existing window if possible. For now, do nothing. + return; } } - // Add new tab + // Add new room to be opened as a card active_rooms_.push_back(room_id); room_selector_.set_active_rooms(active_rooms_); } diff --git a/src/app/editor/dungeon/dungeon_editor_v2.h b/src/app/editor/dungeon/dungeon_editor_v2.h index e373a2dc..bcaba475 100644 --- a/src/app/editor/dungeon/dungeon_editor_v2.h +++ b/src/app/editor/dungeon/dungeon_editor_v2.h @@ -12,6 +12,7 @@ #include "dungeon_room_loader.h" #include "app/zelda3/dungeon/room.h" #include "app/zelda3/dungeon/room_entrance.h" +#include "app/gui/editor_layout.h" #include "imgui/imgui.h" namespace yaze { @@ -79,6 +80,7 @@ class DungeonEditorV2 : public Editor { // Simple UI layout void DrawLayout(); void DrawRoomTab(int room_id); + void DrawToolset(); // Room selection callback void OnRoomSelected(int room_id); diff --git a/src/app/editor/graphics/graphics_editor.cc b/src/app/editor/graphics/graphics_editor.cc index aa3fb998..20eba53d 100644 --- a/src/app/editor/graphics/graphics_editor.cc +++ b/src/app/editor/graphics/graphics_editor.cc @@ -5,6 +5,7 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" +#include "gui/ui_helpers.h" #include "util/file_util.h" #include "app/core/window.h" #include "app/gfx/arena.h" @@ -47,25 +48,63 @@ void GraphicsEditor::Initialize() {} absl::Status GraphicsEditor::Load() { return absl::OkStatus(); } absl::Status GraphicsEditor::Update() { - if (ImGui::BeginTabBar("##TabBar")) { + DrawToolset(); + gui::VerticalSpacing(2.0f); + + static gui::EditorCard sheet_editor_card("Sheet Editor", ICON_MD_EDIT); + static gui::EditorCard sheet_browser_card("Sheet Browser", ICON_MD_VIEW_LIST); + static gui::EditorCard player_anims_card("Player Animations", ICON_MD_PERSON); + static gui::EditorCard prototype_card("Prototype Viewer", ICON_MD_CONSTRUCTION); + + if (show_sheet_editor_ && sheet_editor_card.Begin(&show_sheet_editor_)) { status_ = UpdateGfxEdit(); - if (ImGui::BeginTabItem("Sheet Browser")) { - if (asset_browser_.Initialized == false) { - asset_browser_.Initialize(gfx::Arena::Get().gfx_sheets()); - } - asset_browser_.Draw(gfx::Arena::Get().gfx_sheets()); - ImGui::EndTabItem(); - } - status_ = UpdateScadView(); - status_ = UpdateLinkGfxView(); - ImGui::EndTabBar(); + sheet_editor_card.End(); } + + if (show_sheet_browser_ && sheet_browser_card.Begin(&show_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(); + } + + if (show_player_animations_ && player_anims_card.Begin(&show_player_animations_)) { + status_ = UpdateLinkGfxView(); + player_anims_card.End(); + } + + if (show_prototype_viewer_ && prototype_card.Begin(&show_prototype_viewer_)) { + status_ = UpdateScadView(); + prototype_card.End(); + } + CLEAR_AND_RETURN_STATUS(status_) return absl::OkStatus(); } +void GraphicsEditor::DrawToolset() { + static gui::Toolset toolbar; + toolbar.Begin(); + + if (toolbar.AddAction(ICON_MD_EDIT, "Sheet Editor")) { + show_sheet_editor_ = !show_sheet_editor_; + } + if (toolbar.AddAction(ICON_MD_VIEW_LIST, "Sheet Browser")) { + show_sheet_browser_ = !show_sheet_browser_; + } + if (toolbar.AddAction(ICON_MD_PERSON, "Player Animations")) { + show_player_animations_ = !show_player_animations_; + } + if (toolbar.AddAction(ICON_MD_CONSTRUCTION, "Prototype Viewer")) { + show_prototype_viewer_ = !show_prototype_viewer_; + } + + toolbar.End(); +} + + absl::Status GraphicsEditor::UpdateGfxEdit() { - if (ImGui::BeginTabItem("Sheet Editor")) { if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags, ImVec2(0, 0))) { for (const auto& name : @@ -89,8 +128,6 @@ absl::Status GraphicsEditor::UpdateGfxEdit() { } ImGui::EndTable(); - ImGui::EndTabItem(); - } return absl::OkStatus(); } @@ -405,8 +442,6 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() { } absl::Status GraphicsEditor::UpdateLinkGfxView() { - TAB_ITEM("Player Animations") - if (ImGui::BeginTable("##PlayerAnimationTable", 3, kGfxEditTableFlags, ImVec2(0, 0))) { for (const auto& name : {"Canvas", "Animation Steps", "Properties"}) @@ -448,14 +483,11 @@ absl::Status GraphicsEditor::UpdateLinkGfxView() { } ImGui::EndTable(); - END_TAB_ITEM() return absl::OkStatus(); } absl::Status GraphicsEditor::UpdateScadView() { - TAB_ITEM("Prototype") - - RETURN_IF_ERROR(DrawToolset()) + DrawToolset(); if (open_memory_editor_) { ImGui::Begin("Memory Editor", &open_memory_editor_); @@ -513,31 +545,6 @@ absl::Status GraphicsEditor::UpdateScadView() { } END_TABLE() - END_TAB_ITEM() - return absl::OkStatus(); -} - -absl::Status GraphicsEditor::DrawToolset() { - static constexpr absl::string_view kGfxToolsetColumnNames[] = { - "#memoryEditor", - }; - - if (ImGui::BeginTable("GraphicsToolset", 1, ImGuiTableFlags_SizingFixedFit, - ImVec2(0, 0))) { - for (const auto& name : kGfxToolsetColumnNames) - ImGui::TableSetupColumn(name.data()); - - TableNextColumn(); - if (Button(absl::StrCat(ICON_MD_MEMORY, "Open Memory Editor").c_str())) { - if (!open_memory_editor_) { - open_memory_editor_ = true; - } else { - open_memory_editor_ = false; - } - } - - ImGui::EndTable(); - } return absl::OkStatus(); } diff --git a/src/app/editor/graphics/graphics_editor.h b/src/app/editor/graphics/graphics_editor.h index 253ba452..35cb3291 100644 --- a/src/app/editor/graphics/graphics_editor.h +++ b/src/app/editor/graphics/graphics_editor.h @@ -9,6 +9,7 @@ #include "app/gfx/bitmap.h" #include "app/gfx/snes_tile.h" #include "app/gui/canvas.h" +#include "app/gui/editor_layout.h" #include "app/gui/modules/asset_browser.h" #include "app/rom.h" #include "app/zelda3/overworld/overworld.h" @@ -105,7 +106,7 @@ class GraphicsEditor : public Editor { absl::Status DrawTilemapImport(); // Other Functions - absl::Status DrawToolset(); + void DrawToolset(); absl::Status DrawPaletteControls(); absl::Status DrawClipboardImport(); absl::Status DrawExperimentalFeatures(); @@ -115,6 +116,12 @@ class GraphicsEditor : public Editor { absl::Status DecompressSuperDonkey(); // Member Variables + // Card visibility + bool show_sheet_editor_ = true; + bool show_sheet_browser_ = false; + bool show_player_animations_ = false; + bool show_prototype_viewer_ = false; + ImVec4 current_color_; uint16_t current_sheet_ = 0; uint8_t tile_size_ = 0x01; diff --git a/src/app/editor/graphics/screen_editor.cc b/src/app/editor/graphics/screen_editor.cc index 5399599d..3dcf4bed 100644 --- a/src/app/editor/graphics/screen_editor.cc +++ b/src/app/editor/graphics/screen_editor.cc @@ -18,6 +18,7 @@ #include "app/gui/color.h" #include "app/gui/icons.h" #include "app/gui/input.h" +#include "app/gui/ui_helpers.h" #include "imgui/imgui.h" #include "util/hex.h" #include "util/macro.h" @@ -64,22 +65,63 @@ absl::Status ScreenEditor::Load() { } absl::Status ScreenEditor::Update() { - if (ImGui::BeginTabBar("##ScreenEditorTabBar")) { - if (ImGui::BeginTabItem("Dungeon Maps")) { - DrawDungeonMapsEditor(); - ImGui::EndTabItem(); - } - DrawInventoryMenuEditor(); - DrawOverworldMapEditor(); - DrawTitleScreenEditor(); - DrawNamingScreenEditor(); - ImGui::EndTabBar(); + DrawToolset(); + gui::VerticalSpacing(2.0f); + + static gui::EditorCard dungeon_maps_card("Dungeon Maps", ICON_MD_MAP); + static gui::EditorCard inventory_menu_card("Inventory Menu", ICON_MD_INVENTORY); + static gui::EditorCard overworld_map_card("Overworld Map", ICON_MD_PUBLIC); + static gui::EditorCard title_screen_card("Title Screen", ICON_MD_TITLE); + static gui::EditorCard naming_screen_card("Naming Screen", ICON_MD_EDIT_ATTRIBUTES); + + if (show_dungeon_maps_ && dungeon_maps_card.Begin(&show_dungeon_maps_)) { + DrawDungeonMapsEditor(); + dungeon_maps_card.End(); } + if (show_inventory_menu_ && inventory_menu_card.Begin(&show_inventory_menu_)) { + DrawInventoryMenuEditor(); + inventory_menu_card.End(); + } + if (show_overworld_map_ && overworld_map_card.Begin(&show_overworld_map_)) { + DrawOverworldMapEditor(); + overworld_map_card.End(); + } + if (show_title_screen_ && title_screen_card.Begin(&show_title_screen_)) { + DrawTitleScreenEditor(); + title_screen_card.End(); + } + if (show_naming_screen_ && naming_screen_card.Begin(&show_naming_screen_)) { + DrawNamingScreenEditor(); + naming_screen_card.End(); + } + return status_; } +void ScreenEditor::DrawToolset() { + static gui::Toolset toolbar; + toolbar.Begin(); + + if (toolbar.AddAction(ICON_MD_MAP, "Dungeon Maps")) { + show_dungeon_maps_ = !show_dungeon_maps_; + } + if (toolbar.AddAction(ICON_MD_INVENTORY, "Inventory Menu")) { + show_inventory_menu_ = !show_inventory_menu_; + } + if (toolbar.AddAction(ICON_MD_PUBLIC, "Overworld Map")) { + show_overworld_map_ = !show_overworld_map_; + } + if (toolbar.AddAction(ICON_MD_TITLE, "Title Screen")) { + show_title_screen_ = !show_title_screen_; + } + if (toolbar.AddAction(ICON_MD_EDIT_ATTRIBUTES, "Naming Screen")) { + show_naming_screen_ = !show_naming_screen_; + } + + toolbar.End(); +} + void ScreenEditor::DrawInventoryMenuEditor() { - if (ImGui::BeginTabItem("Inventory Menu")) { static bool create = false; if (!create && rom()->is_loaded()) { status_ = inventory_.Create(); @@ -91,7 +133,7 @@ void ScreenEditor::DrawInventoryMenuEditor() { if (ImGui::BeginTable("InventoryScreen", 3, ImGuiTableFlags_Resizable)) { ImGui::TableSetupColumn("Canvas"); - ImGui::TableSetupColumn("Tiles"); + ImGui::TableSetupColumn("Tilesheet"); ImGui::TableSetupColumn("Palette"); ImGui::TableHeadersRow(); @@ -115,8 +157,6 @@ void ScreenEditor::DrawInventoryMenuEditor() { ImGui::EndTable(); } ImGui::Separator(); - ImGui::EndTabItem(); - } } void ScreenEditor::DrawInventoryToolset() { @@ -504,24 +544,15 @@ void ScreenEditor::LoadBinaryGfx() { } void ScreenEditor::DrawTitleScreenEditor() { - if (ImGui::BeginTabItem("Title Screen")) { - ImGui::EndTabItem(); - } } void ScreenEditor::DrawNamingScreenEditor() { - if (ImGui::BeginTabItem("Naming Screen")) { - ImGui::EndTabItem(); - } } void ScreenEditor::DrawOverworldMapEditor() { - if (ImGui::BeginTabItem("Overworld Map")) { - ImGui::EndTabItem(); - } } -void ScreenEditor::DrawToolset() { +void ScreenEditor::DrawDungeonMapToolset() { static bool show_bg1 = true; static bool show_bg2 = true; static bool show_bg3 = true; diff --git a/src/app/editor/graphics/screen_editor.h b/src/app/editor/graphics/screen_editor.h index 1fe0edc3..4dcc5e46 100644 --- a/src/app/editor/graphics/screen_editor.h +++ b/src/app/editor/graphics/screen_editor.h @@ -12,6 +12,7 @@ #include "app/rom.h" #include "app/zelda3/screen/dungeon_map.h" #include "app/zelda3/screen/inventory.h" +#include "app/gui/editor_layout.h" #include "imgui/imgui.h" namespace yaze { @@ -58,6 +59,7 @@ class ScreenEditor : public Editor { void DrawInventoryMenuEditor(); void DrawToolset(); + void DrawDungeonMapToolset(); void DrawInventoryToolset(); absl::Status LoadDungeonMapTile16(const std::vector& gfx_data, @@ -75,6 +77,13 @@ class ScreenEditor : public Editor { EditingMode current_mode_ = EditingMode::DRAW; + // Card visibility + bool show_dungeon_maps_ = true; + bool show_inventory_menu_ = false; + bool show_overworld_map_ = false; + bool show_title_screen_ = false; + bool show_naming_screen_ = false; + bool binary_gfx_loaded_ = false; uint8_t selected_room = 0; diff --git a/src/app/editor/sprite/sprite_editor.cc b/src/app/editor/sprite/sprite_editor.cc index de7f0195..d2314289 100644 --- a/src/app/editor/sprite/sprite_editor.cc +++ b/src/app/editor/sprite/sprite_editor.cc @@ -1,6 +1,7 @@ #include "sprite_editor.h" #include "app/gfx/performance_profiler.h" +#include "gui/ui_helpers.h" #include "util/file_util.h" #include "app/editor/sprite/zsprite.h" #include "app/gfx/arena.h" @@ -36,21 +37,40 @@ absl::Status SpriteEditor::Update() { sheets_loaded_ = true; } - if (ImGui::BeginTabBar("##SpriteEditorTabs")) { - if (ImGui::BeginTabItem("Vanilla")) { - DrawVanillaSpriteEditor(); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Custom")) { - DrawCustomSprites(); - ImGui::EndTabItem(); - } - ImGui::EndTabBar(); + DrawToolset(); + gui::VerticalSpacing(2.0f); + + static gui::EditorCard vanilla_card("Vanilla Sprites", ICON_MD_PEST_CONTROL_RODENT); + static gui::EditorCard custom_card("Custom Sprites", ICON_MD_ADD_MODERATOR); + + if (show_vanilla_editor_ && vanilla_card.Begin(&show_vanilla_editor_)) { + DrawVanillaSpriteEditor(); + vanilla_card.End(); + } + + if (show_custom_editor_ && custom_card.Begin(&show_custom_editor_)) { + DrawCustomSprites(); + custom_card.End(); } return status_.ok() ? absl::OkStatus() : status_; } +void SpriteEditor::DrawToolset() { + static gui::Toolset toolbar; + toolbar.Begin(); + + if (toolbar.AddAction(ICON_MD_PEST_CONTROL_RODENT, "Vanilla Sprites")) { + show_vanilla_editor_ = !show_vanilla_editor_; + } + if (toolbar.AddAction(ICON_MD_ADD_MODERATOR, "Custom Sprites")) { + show_custom_editor_ = !show_custom_editor_; + } + + toolbar.End(); +} + + void SpriteEditor::DrawVanillaSpriteEditor() { if (ImGui::BeginTable("##SpriteCanvasTable", 3, ImGuiTableFlags_Resizable, ImVec2(0, 0))) { diff --git a/src/app/editor/sprite/sprite_editor.h b/src/app/editor/sprite/sprite_editor.h index 58e72182..f58c7912 100644 --- a/src/app/editor/sprite/sprite_editor.h +++ b/src/app/editor/sprite/sprite_editor.h @@ -8,6 +8,7 @@ #include "app/editor/editor.h" #include "app/editor/sprite/zsprite.h" #include "app/gui/canvas.h" +#include "app/gui/editor_layout.h" #include "app/rom.h" namespace yaze { @@ -80,6 +81,11 @@ class SpriteEditor : public Editor { * @brief Draws the animation frames manager. */ void DrawAnimationFrames(); + void DrawToolset(); + + // Card visibility + bool show_vanilla_editor_ = true; + bool show_custom_editor_ = false; ImVector active_sprites_; /**< Active sprites. */