backend-infra-engineer: Release v0.3.3 snapshot
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
#include "assembly_editor.h"
|
||||
#include "app/editor/system/editor_card_registry.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "util/file_util.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "app/editor/system/editor_card_registry.h"
|
||||
#include "app/gui/core/icons.h"
|
||||
#include "app/gui/core/ui_helpers.h"
|
||||
#include "app/gui/widgets/text_editor.h"
|
||||
#include "util/file_util.h"
|
||||
|
||||
namespace yaze::editor {
|
||||
|
||||
@@ -18,7 +18,7 @@ using util::FileDialogWrapper;
|
||||
|
||||
namespace {
|
||||
|
||||
static const char *const kKeywords[] = {
|
||||
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",
|
||||
@@ -29,7 +29,7 @@ static const char *const kKeywords[] = {
|
||||
"TCS", "TDC", "TRB", "TSB", "TSC", "TSX", "TXA", "TXS", "TXY", "TYA",
|
||||
"TYX", "WAI", "WDM", "XBA", "XCE", "ORG", "LOROM", "HIROM"};
|
||||
|
||||
static const char *const kIdentifiers[] = {
|
||||
static const char* const kIdentifiers[] = {
|
||||
"abort", "abs", "acos", "asin", "atan", "atexit",
|
||||
"atof", "atoi", "atol", "ceil", "clock", "cosh",
|
||||
"ctime", "div", "exit", "fabs", "floor", "fmod",
|
||||
@@ -42,9 +42,10 @@ static const char *const kIdentifiers[] = {
|
||||
|
||||
TextEditor::LanguageDefinition GetAssemblyLanguageDef() {
|
||||
TextEditor::LanguageDefinition language_65816;
|
||||
for (auto &k : kKeywords) language_65816.mKeywords.emplace(k);
|
||||
for (auto& k : kKeywords)
|
||||
language_65816.mKeywords.emplace(k);
|
||||
|
||||
for (auto &k : kIdentifiers) {
|
||||
for (auto& k : kIdentifiers) {
|
||||
TextEditor::Identifier id;
|
||||
id.mDeclaration = "Built-in function";
|
||||
language_65816.mIdentifiers.insert(std::make_pair(std::string(k), id));
|
||||
@@ -175,27 +176,37 @@ FolderItem LoadFolder(const std::string& folder) {
|
||||
|
||||
void AssemblyEditor::Initialize() {
|
||||
text_editor_.SetLanguageDefinition(GetAssemblyLanguageDef());
|
||||
|
||||
|
||||
// Register cards with EditorCardManager
|
||||
if (!dependencies_.card_registry) return;
|
||||
if (!dependencies_.card_registry)
|
||||
return;
|
||||
auto* card_registry = dependencies_.card_registry;
|
||||
card_registry->RegisterCard({.card_id = "assembly.editor", .display_name = "Assembly Editor",
|
||||
.icon = ICON_MD_CODE, .category = "Assembly",
|
||||
.shortcut_hint = "", .priority = 10});
|
||||
card_registry->RegisterCard({.card_id = "assembly.file_browser", .display_name = "File Browser",
|
||||
.icon = ICON_MD_FOLDER_OPEN, .category = "Assembly",
|
||||
.shortcut_hint = "", .priority = 20});
|
||||
|
||||
// Don't show by default - only show when user explicitly opens Assembly Editor
|
||||
card_registry->RegisterCard({.card_id = "assembly.editor",
|
||||
.display_name = "Assembly Editor",
|
||||
.icon = ICON_MD_CODE,
|
||||
.category = "Assembly",
|
||||
.shortcut_hint = "",
|
||||
.priority = 10});
|
||||
card_registry->RegisterCard({.card_id = "assembly.file_browser",
|
||||
.display_name = "File Browser",
|
||||
.icon = ICON_MD_FOLDER_OPEN,
|
||||
.category = "Assembly",
|
||||
.shortcut_hint = "",
|
||||
.priority = 20});
|
||||
|
||||
// Don't show by default - only show when user explicitly opens Assembly
|
||||
// Editor
|
||||
}
|
||||
|
||||
absl::Status AssemblyEditor::Load() {
|
||||
// Register cards with EditorCardRegistry (dependency injection)
|
||||
// Note: Assembly editor uses dynamic file tabs, so we register the main editor window
|
||||
if (!dependencies_.card_registry) return absl::OkStatus();
|
||||
// Note: Assembly editor uses dynamic file tabs, so we register the main
|
||||
// editor window
|
||||
if (!dependencies_.card_registry)
|
||||
return absl::OkStatus();
|
||||
auto* card_registry = dependencies_.card_registry;
|
||||
|
||||
return absl::OkStatus();
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void AssemblyEditor::OpenFolder(const std::string& folder_path) {
|
||||
@@ -239,7 +250,8 @@ void AssemblyEditor::UpdateCodeView() {
|
||||
gui::VerticalSpacing(2.0f);
|
||||
|
||||
// Create session-aware card (non-static for multi-session support)
|
||||
gui::EditorCard file_browser_card(MakeCardTitle("File Browser").c_str(), ICON_MD_FOLDER);
|
||||
gui::EditorCard file_browser_card(MakeCardTitle("File Browser").c_str(),
|
||||
ICON_MD_FOLDER);
|
||||
bool file_browser_open = true;
|
||||
if (file_browser_card.Begin(&file_browser_open)) {
|
||||
if (current_folder_.name != "") {
|
||||
@@ -259,22 +271,22 @@ void AssemblyEditor::UpdateCodeView() {
|
||||
|
||||
// Ensure we have a TextEditor instance for this file
|
||||
if (file_id >= open_files_.size()) {
|
||||
open_files_.resize(file_id + 1);
|
||||
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;
|
||||
// This can happen if a file was closed and its ID is being reused.
|
||||
// For now, we just skip it.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create session-aware card title for each file
|
||||
std::string card_name = MakeCardTitle(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());
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)) {
|
||||
active_file_id_ = file_id;
|
||||
}
|
||||
open_files_[file_id].Render(absl::StrCat("##", card_name).c_str());
|
||||
}
|
||||
file_card.End(); // ALWAYS call End after Begin
|
||||
|
||||
@@ -286,26 +298,26 @@ void AssemblyEditor::UpdateCodeView() {
|
||||
}
|
||||
|
||||
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.");
|
||||
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();
|
||||
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();
|
||||
}
|
||||
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();
|
||||
toolbar.End();
|
||||
}
|
||||
|
||||
void AssemblyEditor::DrawCurrentFolder() {
|
||||
@@ -358,7 +370,6 @@ void AssemblyEditor::DrawCurrentFolder() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AssemblyEditor::DrawFileMenu() {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("Open", "Ctrl+O")) {
|
||||
@@ -398,36 +409,36 @@ void AssemblyEditor::DrawEditMenu() {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
// 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);
|
||||
}
|
||||
// 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());
|
||||
}
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status AssemblyEditor::Cut() {
|
||||
@@ -455,6 +466,8 @@ absl::Status AssemblyEditor::Redo() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status AssemblyEditor::Update() { return absl::OkStatus(); }
|
||||
absl::Status AssemblyEditor::Update() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace yaze::editor
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/gui/widgets/text_editor.h"
|
||||
#include "app/gui/app/editor_layout.h"
|
||||
#include "app/gui/core/style.h"
|
||||
#include "app/gui/widgets/text_editor.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
@@ -31,11 +31,11 @@ class AssemblyEditor : public Editor {
|
||||
text_editor_.SetShowWhitespaces(false);
|
||||
type_ = EditorType::kAssembly;
|
||||
}
|
||||
void ChangeActiveFile(const std::string_view &filename);
|
||||
void ChangeActiveFile(const std::string_view& filename);
|
||||
|
||||
void Initialize() override;
|
||||
absl::Status Load() override;
|
||||
void Update(bool &is_loaded);
|
||||
void Update(bool& is_loaded);
|
||||
void InlineUpdate();
|
||||
|
||||
void UpdateCodeView();
|
||||
@@ -52,7 +52,7 @@ class AssemblyEditor : public Editor {
|
||||
|
||||
absl::Status Save() override;
|
||||
|
||||
void OpenFolder(const std::string &folder_path);
|
||||
void OpenFolder(const std::string& folder_path);
|
||||
|
||||
void set_rom(Rom* rom) { rom_ = rom; }
|
||||
Rom* rom() const { return rom_; }
|
||||
|
||||
@@ -12,14 +12,14 @@ void MemoryEditorWithDiffChecker::DrawToolbar() {
|
||||
// Modern compact toolbar with icon-only buttons
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6, 4));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 4));
|
||||
|
||||
|
||||
if (ImGui::Button(ICON_MD_LOCATION_SEARCHING " Jump")) {
|
||||
ImGui::OpenPopup("JumpToAddress");
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Jump to specific address");
|
||||
}
|
||||
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_MD_SEARCH " Search")) {
|
||||
ImGui::OpenPopup("SearchPattern");
|
||||
@@ -27,7 +27,7 @@ void MemoryEditorWithDiffChecker::DrawToolbar() {
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Search for hex pattern");
|
||||
}
|
||||
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_MD_BOOKMARK " Bookmarks")) {
|
||||
ImGui::OpenPopup("Bookmarks");
|
||||
@@ -35,35 +35,38 @@ void MemoryEditorWithDiffChecker::DrawToolbar() {
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Manage address bookmarks");
|
||||
}
|
||||
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(ICON_MD_MORE_VERT);
|
||||
ImGui::SameLine();
|
||||
|
||||
|
||||
// Show current address
|
||||
if (current_address_ != 0) {
|
||||
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f),
|
||||
ICON_MD_LOCATION_ON " 0x%06X", current_address_);
|
||||
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f),
|
||||
ICON_MD_LOCATION_ON " 0x%06X", current_address_);
|
||||
}
|
||||
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::Separator();
|
||||
|
||||
|
||||
DrawJumpToAddressPopup();
|
||||
DrawSearchPopup();
|
||||
DrawBookmarksPopup();
|
||||
}
|
||||
|
||||
void MemoryEditorWithDiffChecker::DrawJumpToAddressPopup() {
|
||||
if (ImGui::BeginPopupModal("JumpToAddress", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f),
|
||||
ICON_MD_LOCATION_SEARCHING " Jump to Address");
|
||||
if (ImGui::BeginPopupModal("JumpToAddress", nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f),
|
||||
ICON_MD_LOCATION_SEARCHING " Jump to Address");
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
|
||||
ImGui::SetNextItemWidth(200);
|
||||
if (ImGui::InputText("##jump_addr", jump_address_, IM_ARRAYSIZE(jump_address_),
|
||||
ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {
|
||||
if (ImGui::InputText("##jump_addr", jump_address_,
|
||||
IM_ARRAYSIZE(jump_address_),
|
||||
ImGuiInputTextFlags_CharsHexadecimal |
|
||||
ImGuiInputTextFlags_EnterReturnsTrue)) {
|
||||
// Parse and jump on Enter key
|
||||
unsigned int addr;
|
||||
if (sscanf(jump_address_, "%X", &addr) == 1) {
|
||||
@@ -72,11 +75,11 @@ void MemoryEditorWithDiffChecker::DrawJumpToAddressPopup() {
|
||||
}
|
||||
}
|
||||
ImGui::TextDisabled("Format: 0x1C800 or 1C800");
|
||||
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
|
||||
if (ImGui::Button(ICON_MD_CHECK " Go", ImVec2(120, 0))) {
|
||||
unsigned int addr;
|
||||
if (sscanf(jump_address_, "%X", &addr) == 1) {
|
||||
@@ -93,21 +96,23 @@ void MemoryEditorWithDiffChecker::DrawJumpToAddressPopup() {
|
||||
}
|
||||
|
||||
void MemoryEditorWithDiffChecker::DrawSearchPopup() {
|
||||
if (ImGui::BeginPopupModal("SearchPattern", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextColored(ImVec4(0.4f, 0.8f, 0.4f, 1.0f),
|
||||
ICON_MD_SEARCH " Search Hex Pattern");
|
||||
if (ImGui::BeginPopupModal("SearchPattern", nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextColored(ImVec4(0.4f, 0.8f, 0.4f, 1.0f),
|
||||
ICON_MD_SEARCH " Search Hex Pattern");
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
|
||||
ImGui::SetNextItemWidth(300);
|
||||
if (ImGui::InputText("##search_pattern", search_pattern_, IM_ARRAYSIZE(search_pattern_),
|
||||
ImGuiInputTextFlags_EnterReturnsTrue)) {
|
||||
if (ImGui::InputText("##search_pattern", search_pattern_,
|
||||
IM_ARRAYSIZE(search_pattern_),
|
||||
ImGuiInputTextFlags_EnterReturnsTrue)) {
|
||||
// TODO: Implement search
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::TextDisabled("Use ?? for wildcard (e.g. FF 00 ?? 12)");
|
||||
ImGui::Spacing();
|
||||
|
||||
|
||||
// Quick preset patterns
|
||||
ImGui::Text(ICON_MD_LIST " Quick Patterns:");
|
||||
if (ImGui::SmallButton("LDA")) {
|
||||
@@ -121,11 +126,11 @@ void MemoryEditorWithDiffChecker::DrawSearchPopup() {
|
||||
if (ImGui::SmallButton("JSR")) {
|
||||
snprintf(search_pattern_, sizeof(search_pattern_), "20 ?? ??");
|
||||
}
|
||||
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
|
||||
if (ImGui::Button(ICON_MD_SEARCH " Search", ImVec2(120, 0))) {
|
||||
// TODO: Implement search using hex-search handler
|
||||
ImGui::CloseCurrentPopup();
|
||||
@@ -139,64 +144,71 @@ void MemoryEditorWithDiffChecker::DrawSearchPopup() {
|
||||
}
|
||||
|
||||
void MemoryEditorWithDiffChecker::DrawBookmarksPopup() {
|
||||
if (ImGui::BeginPopupModal("Bookmarks", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f),
|
||||
ICON_MD_BOOKMARK " Memory Bookmarks");
|
||||
if (ImGui::BeginPopupModal("Bookmarks", nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f),
|
||||
ICON_MD_BOOKMARK " Memory Bookmarks");
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
|
||||
if (bookmarks_.empty()) {
|
||||
ImGui::TextDisabled(ICON_MD_INFO " No bookmarks yet");
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
|
||||
if (ImGui::Button(ICON_MD_ADD " Add Current Address", ImVec2(250, 0))) {
|
||||
Bookmark new_bookmark;
|
||||
new_bookmark.address = current_address_;
|
||||
new_bookmark.name = absl::StrFormat("Bookmark %zu", bookmarks_.size() + 1);
|
||||
new_bookmark.name =
|
||||
absl::StrFormat("Bookmark %zu", bookmarks_.size() + 1);
|
||||
new_bookmark.description = "User-defined bookmark";
|
||||
bookmarks_.push_back(new_bookmark);
|
||||
}
|
||||
} else {
|
||||
// Bookmarks table
|
||||
ImGui::BeginChild("##bookmarks_list", ImVec2(500, 300), true);
|
||||
if (ImGui::BeginTable("##bookmarks_table", 3,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_Resizable)) {
|
||||
if (ImGui::BeginTable("##bookmarks_table", 3,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_Resizable)) {
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 150);
|
||||
ImGui::TableSetupColumn("Address", ImGuiTableColumnFlags_WidthFixed, 100);
|
||||
ImGui::TableSetupColumn("Description", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("Address", ImGuiTableColumnFlags_WidthFixed,
|
||||
100);
|
||||
ImGui::TableSetupColumn("Description",
|
||||
ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
|
||||
for (size_t i = 0; i < bookmarks_.size(); ++i) {
|
||||
const auto& bm = bookmarks_[i];
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(bm.name.c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
if (ImGui::Selectable(bm.name.c_str(), false,
|
||||
ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
current_address_ = bm.address;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f), "0x%06X", bm.address);
|
||||
|
||||
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f), "0x%06X",
|
||||
bm.address);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextDisabled("%s", bm.description.c_str());
|
||||
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
|
||||
ImGui::Spacing();
|
||||
if (ImGui::Button(ICON_MD_ADD " Add Bookmark", ImVec2(150, 0))) {
|
||||
Bookmark new_bookmark;
|
||||
new_bookmark.address = current_address_;
|
||||
new_bookmark.name = absl::StrFormat("Bookmark %zu", bookmarks_.size() + 1);
|
||||
new_bookmark.name =
|
||||
absl::StrFormat("Bookmark %zu", bookmarks_.size() + 1);
|
||||
new_bookmark.description = "User-defined bookmark";
|
||||
bookmarks_.push_back(new_bookmark);
|
||||
}
|
||||
@@ -205,15 +217,15 @@ void MemoryEditorWithDiffChecker::DrawBookmarksPopup() {
|
||||
bookmarks_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
|
||||
if (ImGui::Button(ICON_MD_CLOSE " Close", ImVec2(250, 0))) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#ifndef YAZE_APP_EDITOR_CODE_MEMORY_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_CODE_MEMORY_EDITOR_H
|
||||
|
||||
#include "util/file_util.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/gui/core/input.h"
|
||||
@@ -9,6 +8,7 @@
|
||||
#include "app/snes.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/macro.h"
|
||||
|
||||
namespace yaze {
|
||||
@@ -19,8 +19,8 @@ using ImGui::Text;
|
||||
|
||||
struct MemoryEditorWithDiffChecker {
|
||||
explicit MemoryEditorWithDiffChecker(Rom* rom = nullptr) : rom_(rom) {}
|
||||
|
||||
void Update(bool &show_memory_editor) {
|
||||
|
||||
void Update(bool& show_memory_editor) {
|
||||
DrawToolbar();
|
||||
ImGui::Separator();
|
||||
static MemoryEditor mem_edit;
|
||||
@@ -35,7 +35,7 @@ struct MemoryEditorWithDiffChecker {
|
||||
}
|
||||
|
||||
static uint64_t convert_address = 0;
|
||||
gui::InputHex("SNES to PC", (int *)&convert_address, 6, 200.f);
|
||||
gui::InputHex("SNES to PC", (int*)&convert_address, 6, 200.f);
|
||||
SameLine();
|
||||
Text("%x", SnesToPc(convert_address));
|
||||
|
||||
@@ -46,15 +46,15 @@ struct MemoryEditorWithDiffChecker {
|
||||
|
||||
NEXT_COLUMN()
|
||||
Text("%s", rom()->filename().data());
|
||||
mem_edit.DrawContents((void *)&(*rom()), rom()->size());
|
||||
mem_edit.DrawContents((void*)&(*rom()), rom()->size());
|
||||
|
||||
NEXT_COLUMN()
|
||||
if (show_compare_rom) {
|
||||
comp_edit.SetComparisonData((void *)&(*rom()));
|
||||
comp_edit.SetComparisonData((void*)&(*rom()));
|
||||
ImGui::BeginGroup();
|
||||
ImGui::BeginChild("Comparison ROM");
|
||||
Text("%s", comparison_rom.filename().data());
|
||||
comp_edit.DrawContents((void *)&(comparison_rom), comparison_rom.size());
|
||||
comp_edit.DrawContents((void*)&(comparison_rom), comparison_rom.size());
|
||||
ImGui::EndChild();
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
@@ -65,7 +65,7 @@ struct MemoryEditorWithDiffChecker {
|
||||
|
||||
// Set the ROM pointer
|
||||
void set_rom(Rom* rom) { rom_ = rom; }
|
||||
|
||||
|
||||
// Get the ROM pointer
|
||||
Rom* rom() const { return rom_; }
|
||||
|
||||
@@ -74,14 +74,14 @@ struct MemoryEditorWithDiffChecker {
|
||||
void DrawJumpToAddressPopup();
|
||||
void DrawSearchPopup();
|
||||
void DrawBookmarksPopup();
|
||||
|
||||
|
||||
Rom* rom_;
|
||||
|
||||
|
||||
// Toolbar state
|
||||
char jump_address_[16] = "0x000000";
|
||||
char search_pattern_[256] = "";
|
||||
uint32_t current_address_ = 0;
|
||||
|
||||
|
||||
struct Bookmark {
|
||||
uint32_t address;
|
||||
std::string name;
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "core/project.h"
|
||||
#include "util/file_util.h"
|
||||
#include "app/editor/system/toast_manager.h"
|
||||
#include "app/gui/core/icons.h"
|
||||
#include "core/project.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "util/file_util.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
@@ -22,38 +22,43 @@ ProjectFileEditor::ProjectFileEditor() {
|
||||
}
|
||||
|
||||
void ProjectFileEditor::Draw() {
|
||||
if (!active_) return;
|
||||
|
||||
if (!active_)
|
||||
return;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(900, 700), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin(absl::StrFormat("%s Project Editor###ProjectFileEditor",
|
||||
ICON_MD_DESCRIPTION).c_str(),
|
||||
&active_)) {
|
||||
ICON_MD_DESCRIPTION)
|
||||
.c_str(),
|
||||
&active_)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Toolbar
|
||||
if (ImGui::BeginTable("ProjectEditorToolbar", 8, ImGuiTableFlags_SizingFixedFit)) {
|
||||
if (ImGui::BeginTable("ProjectEditorToolbar", 8,
|
||||
ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(absl::StrFormat("%s New", ICON_MD_NOTE_ADD).c_str())) {
|
||||
NewFile();
|
||||
}
|
||||
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(absl::StrFormat("%s Open", ICON_MD_FOLDER_OPEN).c_str())) {
|
||||
if (ImGui::Button(
|
||||
absl::StrFormat("%s Open", ICON_MD_FOLDER_OPEN).c_str())) {
|
||||
auto file = util::FileDialogWrapper::ShowOpenFileDialog();
|
||||
if (!file.empty()) {
|
||||
auto status = LoadFile(file);
|
||||
if (!status.ok() && toast_manager_) {
|
||||
toast_manager_->Show(std::string(status.message()),
|
||||
ToastType::kError);
|
||||
toast_manager_->Show(std::string(status.message()),
|
||||
ToastType::kError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
bool can_save = !filepath_.empty() && IsModified();
|
||||
if (!can_save) ImGui::BeginDisabled();
|
||||
if (!can_save)
|
||||
ImGui::BeginDisabled();
|
||||
if (ImGui::Button(absl::StrFormat("%s Save", ICON_MD_SAVE).c_str())) {
|
||||
auto status = SaveFile();
|
||||
if (status.ok() && toast_manager_) {
|
||||
@@ -62,8 +67,9 @@ void ProjectFileEditor::Draw() {
|
||||
toast_manager_->Show(std::string(status.message()), ToastType::kError);
|
||||
}
|
||||
}
|
||||
if (!can_save) ImGui::EndDisabled();
|
||||
|
||||
if (!can_save)
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(absl::StrFormat("%s Save As", ICON_MD_SAVE_AS).c_str())) {
|
||||
auto file = util::FileDialogWrapper::ShowSaveFileDialog(
|
||||
@@ -73,41 +79,43 @@ void ProjectFileEditor::Draw() {
|
||||
if (status.ok() && toast_manager_) {
|
||||
toast_manager_->Show("Project file saved", ToastType::kSuccess);
|
||||
} else if (!status.ok() && toast_manager_) {
|
||||
toast_manager_->Show(std::string(status.message()), ToastType::kError);
|
||||
toast_manager_->Show(std::string(status.message()),
|
||||
ToastType::kError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("|");
|
||||
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(absl::StrFormat("%s Validate", ICON_MD_CHECK_CIRCLE).c_str())) {
|
||||
if (ImGui::Button(
|
||||
absl::StrFormat("%s Validate", ICON_MD_CHECK_CIRCLE).c_str())) {
|
||||
ValidateContent();
|
||||
show_validation_ = true;
|
||||
}
|
||||
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Checkbox("Show Validation", &show_validation_);
|
||||
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (!filepath_.empty()) {
|
||||
ImGui::TextDisabled("%s", filepath_.c_str());
|
||||
} else {
|
||||
ImGui::TextDisabled("No file loaded");
|
||||
}
|
||||
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
|
||||
// Validation errors panel
|
||||
if (show_validation_ && !validation_errors_.empty()) {
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.3f, 0.2f, 0.2f, 0.5f));
|
||||
if (ImGui::BeginChild("ValidationErrors", ImVec2(0, 100), true)) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f),
|
||||
"%s Validation Errors:", ICON_MD_ERROR);
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f),
|
||||
"%s Validation Errors:", ICON_MD_ERROR);
|
||||
for (const auto& error : validation_errors_) {
|
||||
ImGui::BulletText("%s", error.c_str());
|
||||
}
|
||||
@@ -115,11 +123,11 @@ void ProjectFileEditor::Draw() {
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
|
||||
// Main editor
|
||||
ImVec2 editor_size = ImGui::GetContentRegionAvail();
|
||||
text_editor_.Render("##ProjectEditor", editor_size);
|
||||
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
@@ -129,17 +137,17 @@ absl::Status ProjectFileEditor::LoadFile(const std::string& filepath) {
|
||||
return absl::InvalidArgumentError(
|
||||
absl::StrFormat("Cannot open file: %s", filepath));
|
||||
}
|
||||
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
file.close();
|
||||
|
||||
|
||||
text_editor_.SetText(buffer.str());
|
||||
filepath_ = filepath;
|
||||
modified_ = false;
|
||||
|
||||
|
||||
ValidateContent();
|
||||
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -147,7 +155,7 @@ absl::Status ProjectFileEditor::SaveFile() {
|
||||
if (filepath_.empty()) {
|
||||
return absl::InvalidArgumentError("No file path specified");
|
||||
}
|
||||
|
||||
|
||||
return SaveFileAs(filepath_);
|
||||
}
|
||||
|
||||
@@ -157,24 +165,24 @@ absl::Status ProjectFileEditor::SaveFileAs(const std::string& filepath) {
|
||||
if (!absl::EndsWith(final_path, ".yaze")) {
|
||||
final_path += ".yaze";
|
||||
}
|
||||
|
||||
|
||||
std::ofstream file(final_path);
|
||||
if (!file.is_open()) {
|
||||
return absl::InvalidArgumentError(
|
||||
absl::StrFormat("Cannot create file: %s", final_path));
|
||||
}
|
||||
|
||||
|
||||
file << text_editor_.GetText();
|
||||
file.close();
|
||||
|
||||
|
||||
filepath_ = final_path;
|
||||
modified_ = false;
|
||||
|
||||
|
||||
// Add to recent files
|
||||
auto& recent_mgr = project::RecentFilesManager::GetInstance();
|
||||
recent_mgr.AddFile(filepath_);
|
||||
recent_mgr.Save();
|
||||
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -217,7 +225,7 @@ autosave_enabled=true
|
||||
autosave_interval_secs=300
|
||||
theme=dark
|
||||
)";
|
||||
|
||||
|
||||
text_editor_.SetText(template_content);
|
||||
filepath_.clear();
|
||||
modified_ = true;
|
||||
@@ -231,54 +239,54 @@ void ProjectFileEditor::ApplySyntaxHighlighting() {
|
||||
|
||||
void ProjectFileEditor::ValidateContent() {
|
||||
validation_errors_.clear();
|
||||
|
||||
|
||||
std::string content = text_editor_.GetText();
|
||||
std::vector<std::string> lines = absl::StrSplit(content, '\n');
|
||||
|
||||
|
||||
std::string current_section;
|
||||
int line_num = 0;
|
||||
|
||||
|
||||
for (const auto& line : lines) {
|
||||
line_num++;
|
||||
std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
|
||||
|
||||
|
||||
// Skip empty lines and comments
|
||||
if (trimmed.empty() || trimmed[0] == '#') continue;
|
||||
|
||||
if (trimmed.empty() || trimmed[0] == '#')
|
||||
continue;
|
||||
|
||||
// Check for section headers
|
||||
if (trimmed[0] == '[' && trimmed[trimmed.size() - 1] == ']') {
|
||||
current_section = trimmed.substr(1, trimmed.size() - 2);
|
||||
|
||||
|
||||
// Validate known sections
|
||||
if (current_section != "project" &&
|
||||
current_section != "files" &&
|
||||
if (current_section != "project" && current_section != "files" &&
|
||||
current_section != "feature_flags" &&
|
||||
current_section != "workspace_settings" &&
|
||||
current_section != "build_settings") {
|
||||
validation_errors_.push_back(
|
||||
absl::StrFormat("Line %d: Unknown section [%s]",
|
||||
line_num, current_section));
|
||||
validation_errors_.push_back(absl::StrFormat(
|
||||
"Line %d: Unknown section [%s]", line_num, current_section));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Check for key=value pairs
|
||||
size_t equals_pos = trimmed.find('=');
|
||||
if (equals_pos == std::string::npos) {
|
||||
validation_errors_.push_back(
|
||||
absl::StrFormat("Line %d: Invalid format, expected key=value", line_num));
|
||||
validation_errors_.push_back(absl::StrFormat(
|
||||
"Line %d: Invalid format, expected key=value", line_num));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (validation_errors_.empty() && show_validation_ && toast_manager_) {
|
||||
toast_manager_->Show("Project file validation passed", ToastType::kSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectFileEditor::ShowValidationErrors() {
|
||||
if (validation_errors_.empty()) return;
|
||||
|
||||
if (validation_errors_.empty())
|
||||
return;
|
||||
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Validation Errors:");
|
||||
for (const auto& error : validation_errors_) {
|
||||
ImGui::BulletText("%s", error.c_str());
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include <string>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "core/project.h"
|
||||
#include "app/gui/widgets/text_editor.h"
|
||||
#include "core/project.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
@@ -15,7 +15,7 @@ class ToastManager;
|
||||
/**
|
||||
* @class ProjectFileEditor
|
||||
* @brief Editor for .yaze project files with syntax highlighting and validation
|
||||
*
|
||||
*
|
||||
* Provides a rich text editing experience for yaze project files with:
|
||||
* - Syntax highlighting for INI-style format
|
||||
* - Real-time validation
|
||||
@@ -25,61 +25,61 @@ class ToastManager;
|
||||
class ProjectFileEditor {
|
||||
public:
|
||||
ProjectFileEditor();
|
||||
|
||||
|
||||
void Draw();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Load a project file into the editor
|
||||
*/
|
||||
absl::Status LoadFile(const std::string& filepath);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Save the current editor contents to disk
|
||||
*/
|
||||
absl::Status SaveFile();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Save to a new file path
|
||||
*/
|
||||
absl::Status SaveFileAs(const std::string& filepath);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get whether the file has unsaved changes
|
||||
*/
|
||||
bool IsModified() const { return text_editor_.IsTextChanged() || modified_; }
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the current filepath
|
||||
*/
|
||||
const std::string& filepath() const { return filepath_; }
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set whether the editor window is active
|
||||
*/
|
||||
void set_active(bool active) { active_ = active; }
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get pointer to active state for ImGui
|
||||
*/
|
||||
bool* active() { return &active_; }
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set toast manager for notifications
|
||||
*/
|
||||
void SetToastManager(ToastManager* toast_manager) {
|
||||
toast_manager_ = toast_manager;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a new empty project file
|
||||
*/
|
||||
void NewFile();
|
||||
|
||||
|
||||
private:
|
||||
void ApplySyntaxHighlighting();
|
||||
void ValidateContent();
|
||||
void ShowValidationErrors();
|
||||
|
||||
|
||||
TextEditor text_editor_;
|
||||
std::string filepath_;
|
||||
bool active_ = false;
|
||||
|
||||
Reference in New Issue
Block a user