refactor: Enhance Assembly Editor with Language Definition and Toolset Integration
- Introduced a new language definition for the 65816 assembly language, improving syntax highlighting and code editing capabilities. - Updated the AssemblyEditor to utilize the new language definition and integrated a toolset for file management actions, enhancing user experience. - Refactored the file handling logic to support dynamic file opening and saving, ensuring better resource management within the editor. - Removed deprecated tab view code, transitioning to a more modular card-based layout for active files, improving UI responsiveness and organization.
This commit is contained in:
@@ -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<std::string, TextEditor::PaletteIndex>(
|
||||
"[ \\t]*#[ \\t]*[a-zA-Z_]+", TextEditor::PaletteIndex::Preprocessor));
|
||||
language_65816.mTokenRegexStrings.push_back(
|
||||
std::make_pair<std::string, TextEditor::PaletteIndex>(
|
||||
"L?\\\"(\\\\.|[^\\\"])*\\\"", TextEditor::PaletteIndex::String));
|
||||
language_65816.mTokenRegexStrings.push_back(
|
||||
std::make_pair<std::string, TextEditor::PaletteIndex>(
|
||||
"\\'\\\\?[^\\']\\'", TextEditor::PaletteIndex::CharLiteral));
|
||||
language_65816.mTokenRegexStrings.push_back(
|
||||
std::make_pair<std::string, TextEditor::PaletteIndex>(
|
||||
"[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",
|
||||
TextEditor::PaletteIndex::Number));
|
||||
language_65816.mTokenRegexStrings.push_back(
|
||||
std::make_pair<std::string, TextEditor::PaletteIndex>(
|
||||
"[+-]?[0-9]+[Uu]?[lL]?[lL]?", TextEditor::PaletteIndex::Number));
|
||||
language_65816.mTokenRegexStrings.push_back(
|
||||
std::make_pair<std::string, TextEditor::PaletteIndex>(
|
||||
"0[0-7]+[Uu]?[lL]?[lL]?", TextEditor::PaletteIndex::Number));
|
||||
language_65816.mTokenRegexStrings.push_back(
|
||||
std::make_pair<std::string, TextEditor::PaletteIndex>(
|
||||
"0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?",
|
||||
TextEditor::PaletteIndex::Number));
|
||||
language_65816.mTokenRegexStrings.push_back(
|
||||
std::make_pair<std::string, TextEditor::PaletteIndex>(
|
||||
"[a-zA-Z_][a-zA-Z0-9_]*", TextEditor::PaletteIndex::Identifier));
|
||||
language_65816.mTokenRegexStrings.push_back(
|
||||
std::make_pair<std::string, TextEditor::PaletteIndex>(
|
||||
"[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/"
|
||||
"\\;\\,\\.]",
|
||||
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<std::string> RemoveIgnoredFiles(
|
||||
const std::vector<std::string>& files,
|
||||
const std::vector<std::string>& 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<char>(t)),
|
||||
std::istreambuf_iterator<char>());
|
||||
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<char>(t)),
|
||||
std::istreambuf_iterator<char>());
|
||||
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() {
|
||||
|
||||
@@ -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<std::string> files_;
|
||||
std::vector<TextEditor> open_files_;
|
||||
|
||||
@@ -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<size_t>(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<size_t>(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_);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<uint8_t>& 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;
|
||||
|
||||
@@ -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))) {
|
||||
|
||||
@@ -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<int> active_sprites_; /**< Active sprites. */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user