yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
project_file_editor.cc
Go to the documentation of this file.
2
3#include <fstream>
4#include <sstream>
5
6#include "absl/strings/match.h"
7#include "absl/strings/str_format.h"
8#include "absl/strings/str_split.h"
9#include "core/project.h"
10#include "util/file_util.h"
12#include "app/gui/core/icons.h"
13#include "imgui/imgui.h"
14
15namespace yaze {
16namespace editor {
17
23
25 if (!active_) return;
26
27 ImGui::SetNextWindowSize(ImVec2(900, 700), ImGuiCond_FirstUseEver);
28 if (!ImGui::Begin(absl::StrFormat("%s Project Editor###ProjectFileEditor",
29 ICON_MD_DESCRIPTION).c_str(),
30 &active_)) {
31 ImGui::End();
32 return;
33 }
34
35 // Toolbar
36 if (ImGui::BeginTable("ProjectEditorToolbar", 8, ImGuiTableFlags_SizingFixedFit)) {
37 ImGui::TableNextColumn();
38 if (ImGui::Button(absl::StrFormat("%s New", ICON_MD_NOTE_ADD).c_str())) {
39 NewFile();
40 }
41
42 ImGui::TableNextColumn();
43 if (ImGui::Button(absl::StrFormat("%s Open", ICON_MD_FOLDER_OPEN).c_str())) {
45 if (!file.empty()) {
46 auto status = LoadFile(file);
47 if (!status.ok() && toast_manager_) {
48 toast_manager_->Show(std::string(status.message()),
50 }
51 }
52 }
53
54 ImGui::TableNextColumn();
55 bool can_save = !filepath_.empty() && IsModified();
56 if (!can_save) ImGui::BeginDisabled();
57 if (ImGui::Button(absl::StrFormat("%s Save", ICON_MD_SAVE).c_str())) {
58 auto status = SaveFile();
59 if (status.ok() && toast_manager_) {
60 toast_manager_->Show("Project file saved", ToastType::kSuccess);
61 } else if (!status.ok() && toast_manager_) {
62 toast_manager_->Show(std::string(status.message()), ToastType::kError);
63 }
64 }
65 if (!can_save) ImGui::EndDisabled();
66
67 ImGui::TableNextColumn();
68 if (ImGui::Button(absl::StrFormat("%s Save As", ICON_MD_SAVE_AS).c_str())) {
70 filepath_.empty() ? "project" : filepath_, "yaze");
71 if (!file.empty()) {
72 auto status = SaveFileAs(file);
73 if (status.ok() && toast_manager_) {
74 toast_manager_->Show("Project file saved", ToastType::kSuccess);
75 } else if (!status.ok() && toast_manager_) {
76 toast_manager_->Show(std::string(status.message()), ToastType::kError);
77 }
78 }
79 }
80
81 ImGui::TableNextColumn();
82 ImGui::Text("|");
83
84 ImGui::TableNextColumn();
85 if (ImGui::Button(absl::StrFormat("%s Validate", ICON_MD_CHECK_CIRCLE).c_str())) {
87 show_validation_ = true;
88 }
89
90 ImGui::TableNextColumn();
91 ImGui::Checkbox("Show Validation", &show_validation_);
92
93 ImGui::TableNextColumn();
94 if (!filepath_.empty()) {
95 ImGui::TextDisabled("%s", filepath_.c_str());
96 } else {
97 ImGui::TextDisabled("No file loaded");
98 }
99
100 ImGui::EndTable();
101 }
102
103 ImGui::Separator();
104
105 // Validation errors panel
106 if (show_validation_ && !validation_errors_.empty()) {
107 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.3f, 0.2f, 0.2f, 0.5f));
108 if (ImGui::BeginChild("ValidationErrors", ImVec2(0, 100), true)) {
109 ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f),
110 "%s Validation Errors:", ICON_MD_ERROR);
111 for (const auto& error : validation_errors_) {
112 ImGui::BulletText("%s", error.c_str());
113 }
114 }
115 ImGui::EndChild();
116 ImGui::PopStyleColor();
117 }
118
119 // Main editor
120 ImVec2 editor_size = ImGui::GetContentRegionAvail();
121 text_editor_.Render("##ProjectEditor", editor_size);
122
123 ImGui::End();
124}
125
126absl::Status ProjectFileEditor::LoadFile(const std::string& filepath) {
127 std::ifstream file(filepath);
128 if (!file.is_open()) {
129 return absl::InvalidArgumentError(
130 absl::StrFormat("Cannot open file: %s", filepath));
131 }
132
133 std::stringstream buffer;
134 buffer << file.rdbuf();
135 file.close();
136
137 text_editor_.SetText(buffer.str());
139 modified_ = false;
140
142
143 return absl::OkStatus();
144}
145
147 if (filepath_.empty()) {
148 return absl::InvalidArgumentError("No file path specified");
149 }
150
151 return SaveFileAs(filepath_);
152}
153
154absl::Status ProjectFileEditor::SaveFileAs(const std::string& filepath) {
155 // Ensure .yaze extension
156 std::string final_path = filepath;
157 if (!absl::EndsWith(final_path, ".yaze")) {
158 final_path += ".yaze";
159 }
160
161 std::ofstream file(final_path);
162 if (!file.is_open()) {
163 return absl::InvalidArgumentError(
164 absl::StrFormat("Cannot create file: %s", final_path));
165 }
166
167 file << text_editor_.GetText();
168 file.close();
169
170 filepath_ = final_path;
171 modified_ = false;
172
173 // Add to recent files
174 auto& recent_mgr = project::RecentFilesManager::GetInstance();
175 recent_mgr.AddFile(filepath_);
176 recent_mgr.Save();
177
178 return absl::OkStatus();
179}
180
182 // Create a template project file
183 const char* template_content = R"(# yaze Project File
184# Format Version: 2.0
185
186[project]
187name=New Project
188description=
189author=
190license=
191version=1.0
192created_date=
193last_modified=
194yaze_version=0.4.0
195tags=
196
197[files]
198rom_filename=
199rom_backup_folder=backups
200code_folder=asm
201assets_folder=assets
202patches_folder=patches
203labels_filename=labels.txt
204symbols_filename=symbols.txt
205output_folder=build
206additional_roms=
207
208[feature_flags]
209# REMOVED: kLogInstructions - DisassemblyViewer is always active
210kSaveDungeonMaps=true
211kSaveGraphicsSheet=true
212kLoadCustomOverworld=false
213
214[workspace_settings]
215font_global_scale=1.0
216autosave_enabled=true
217autosave_interval_secs=300
218theme=dark
219)";
220
221 text_editor_.SetText(template_content);
222 filepath_.clear();
223 modified_ = true;
224 validation_errors_.clear();
225}
226
228 // TODO: Implement custom syntax highlighting for INI format
229 // For now, use C language definition which provides some basic highlighting
230}
231
233 validation_errors_.clear();
234
235 std::string content = text_editor_.GetText();
236 std::vector<std::string> lines = absl::StrSplit(content, '\n');
237
238 std::string current_section;
239 int line_num = 0;
240
241 for (const auto& line : lines) {
242 line_num++;
243 std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
244
245 // Skip empty lines and comments
246 if (trimmed.empty() || trimmed[0] == '#') continue;
247
248 // Check for section headers
249 if (trimmed[0] == '[' && trimmed[trimmed.size() - 1] == ']') {
250 current_section = trimmed.substr(1, trimmed.size() - 2);
251
252 // Validate known sections
253 if (current_section != "project" &&
254 current_section != "files" &&
255 current_section != "feature_flags" &&
256 current_section != "workspace_settings" &&
257 current_section != "build_settings") {
258 validation_errors_.push_back(
259 absl::StrFormat("Line %d: Unknown section [%s]",
260 line_num, current_section));
261 }
262 continue;
263 }
264
265 // Check for key=value pairs
266 size_t equals_pos = trimmed.find('=');
267 if (equals_pos == std::string::npos) {
268 validation_errors_.push_back(
269 absl::StrFormat("Line %d: Invalid format, expected key=value", line_num));
270 continue;
271 }
272 }
273
275 toast_manager_->Show("Project file validation passed", ToastType::kSuccess);
276 }
277}
278
280 if (validation_errors_.empty()) return;
281
282 ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Validation Errors:");
283 for (const auto& error : validation_errors_) {
284 ImGui::BulletText("%s", error.c_str());
285 }
286}
287
288} // namespace editor
289} // namespace yaze
void SetShowWhitespaces(bool aValue)
std::string GetText() const
void Render(const char *aTitle, const ImVec2 &aSize=ImVec2(), bool aBorder=false)
void SetText(const std::string &aText)
void SetTabSize(int aValue)
void SetLanguageDefinition(const LanguageDefinition &aLanguageDef)
absl::Status SaveFileAs(const std::string &filepath)
Save to a new file path.
absl::Status LoadFile(const std::string &filepath)
Load a project file into the editor.
void NewFile()
Create a new empty project file.
bool IsModified() const
Get whether the file has unsaved changes.
std::vector< std::string > validation_errors_
absl::Status SaveFile()
Save the current editor contents to disk.
const std::string & filepath() const
Get the current filepath.
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
static RecentFilesManager & GetInstance()
Definition project.h:244
static std::string ShowSaveFileDialog(const std::string &default_name="", const std::string &default_extension="")
ShowSaveFileDialog opens a save file dialog and returns the selected filepath. Uses global feature fl...
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
#define ICON_MD_FOLDER_OPEN
Definition icons.h:811
#define ICON_MD_NOTE_ADD
Definition icons.h:1328
#define ICON_MD_SAVE_AS
Definition icons.h:1644
#define ICON_MD_ERROR
Definition icons.h:684
#define ICON_MD_CHECK_CIRCLE
Definition icons.h:398
#define ICON_MD_DESCRIPTION
Definition icons.h:537
#define ICON_MD_SAVE
Definition icons.h:1642
Main namespace for the application.
Definition controller.cc:20
static const LanguageDefinition & C()