yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
new_project_dialog.cc
Go to the documentation of this file.
2
3#include <cstdio>
4#include <cstring>
5#include <filesystem>
6
7#include "absl/strings/str_format.h"
11#include "imgui/imgui.h"
12#include "util/file_util.h"
13
14namespace yaze {
15namespace editor {
16
17namespace {
18
20 const char* id;
21 const char* icon;
22 const char* name;
23 const char* use_when;
24 const char* what_changes;
25 int skill_level; // 1 beginner, 2 intermediate, 3 advanced
26};
27
28// Mirror of the catalog the welcome screen's template section uses. Kept
29// inline here so the dialog doesn't depend on welcome_screen internals; both
30// lists are short and stable, duplication is cheaper than plumbing.
32 {"Vanilla ROM Hack", ICON_MD_COTTAGE, "Vanilla ROM Hack",
33 "Edit rooms, sprites, or graphics without custom code.",
34 "Creates a project on top of the ROM. The ROM itself is only modified "
35 "when you save edits inside the editors.",
36 1},
37 {"ZSCustomOverworld v3", ICON_MD_TERRAIN, "ZSCustomOverworld v3",
38 "Resize overworld areas and add custom map features.",
39 "Applies the ZSO3 patch: expanded overworld tables, extended palette "
40 "and GFX storage, and custom entrances/exits.",
41 2},
42 {"ZSCustomOverworld v2", ICON_MD_MAP, "ZSCustomOverworld v2",
43 "Port an older hack that already uses ZSO v2.",
44 "Applies the legacy ZSO2 patch. Smaller feature set than v3.", 2},
45 {"Randomizer Compatible", ICON_MD_SHUFFLE, "Randomizer Compatible",
46 "Build a ROM that has to work with ALTTPR or similar.",
47 "Keeps edits inside the surface randomizers patch over. Skips ASM "
48 "hooks and overworld remapping.",
49 3},
50};
51constexpr int kTemplateCount = sizeof(kTemplates) / sizeof(kTemplates[0]);
52
53int FindTemplateIndex(const std::string& name) {
54 if (name.empty())
55 return 0;
56 for (int i = 0; i < kTemplateCount; ++i) {
57 if (name == kTemplates[i].id)
58 return i;
59 }
60 return 0;
61}
62
63} // namespace
64
65void NewProjectDialog::Open(const std::string& initial_template) {
66 selected_template_ = FindTemplateIndex(initial_template);
67 // Seed the project name with the template name so users who don't care
68 // about naming get something reasonable by default — they can still edit.
69 std::snprintf(project_name_buffer_, sizeof(project_name_buffer_), "%s",
70 kTemplates[selected_template_].name);
71 rom_path_buffer_[0] = '\0';
72 status_message_.clear();
73 open_requested_ = true;
74 just_opened_ = true;
75}
76
78 open_requested_ = false;
79 just_opened_ = false;
80 status_message_.clear();
81}
82
83void NewProjectDialog::ApplyTemplateSelection(const std::string& name) {
84 const int new_index = FindTemplateIndex(name);
85 selected_template_ = new_index;
86}
87
89 if (!open_requested_)
90 return false;
91
92 constexpr const char* kPopupId = "##new_project_dialog";
93 if (just_opened_) {
94 ImGui::OpenPopup(kPopupId);
95 just_opened_ = false;
96 }
97
98 ImGui::SetNextWindowSize(ImVec2(560, 0), ImGuiCond_Appearing);
99 if (!ImGui::BeginPopupModal(kPopupId, nullptr,
100 ImGuiWindowFlags_AlwaysAutoResize |
101 ImGuiWindowFlags_NoSavedSettings)) {
102 // Popup closed externally (escape key inside ImGui's own machinery, etc.).
103 Reset();
104 return false;
105 }
106
107 const ImVec4 text_secondary = gui::GetTextSecondaryVec4();
108
109 ImGui::TextUnformatted(ICON_MD_ROCKET_LAUNCH " Start a new project");
110 {
111 gui::StyleColorGuard text_guard(ImGuiCol_Text, text_secondary);
112 ImGui::TextWrapped(
113 "Pick a template, point at the ROM it should build on, and give the "
114 "project a name. The project file will be saved in your configured "
115 "projects folder.");
116 }
117 ImGui::Separator();
118 ImGui::Spacing();
119
120 // --- Template picker ------------------------------------------------------
121 ImGui::TextUnformatted("Template");
122 const char* combo_preview = kTemplates[selected_template_].name;
123 ImGui::SetNextItemWidth(-1);
124 if (ImGui::BeginCombo("##template_combo", combo_preview)) {
125 for (int i = 0; i < kTemplateCount; ++i) {
126 const bool selected = (i == selected_template_);
127 const std::string label =
128 absl::StrFormat("%s %s", kTemplates[i].icon, kTemplates[i].name);
129 if (ImGui::Selectable(label.c_str(), selected)) {
131 }
132 if (selected)
133 ImGui::SetItemDefaultFocus();
134 }
135 ImGui::EndCombo();
136 }
137
138 // Template description block — gives the user an at-a-glance sense of what
139 // the checkbox actually does, same copy as the welcome screen details.
140 const TemplateDescriptor& tmpl = kTemplates[selected_template_];
141 {
142 gui::StyleColorGuard text_guard(ImGuiCol_Text, text_secondary);
143 ImGui::TextWrapped("%s Use when: %s", ICON_MD_LIGHTBULB, tmpl.use_when);
144 ImGui::TextWrapped("%s Effect: %s", ICON_MD_EDIT, tmpl.what_changes);
145 const char* skill_tag = tmpl.skill_level == 1 ? "Beginner"
146 : tmpl.skill_level == 2 ? "Intermediate"
147 : "Advanced";
148 ImGui::TextWrapped("%s Skill: %s", ICON_MD_INFO, skill_tag);
149 }
150
151 ImGui::Spacing();
152
153 // --- ROM picker -----------------------------------------------------------
154 ImGui::TextUnformatted("Source ROM");
155 const float browse_width = ImGui::CalcTextSize(" Browse…").x +
156 ImGui::CalcTextSize(ICON_MD_FOLDER_OPEN).x +
157 ImGui::GetStyle().FramePadding.x * 2.0f + 8.0f;
158 ImGui::SetNextItemWidth(-browse_width - ImGui::GetStyle().ItemSpacing.x);
159 ImGui::InputText("##rom_path", rom_path_buffer_, sizeof(rom_path_buffer_));
160 ImGui::SameLine();
161 if (ImGui::Button(
162 absl::StrFormat("%s Browse…", ICON_MD_FOLDER_OPEN).c_str())) {
164 if (!picked.empty()) {
165 std::snprintf(rom_path_buffer_, sizeof(rom_path_buffer_), "%s",
166 picked.c_str());
167 status_message_.clear();
168 }
169 }
170
171 ImGui::Spacing();
172
173 // --- Project name ---------------------------------------------------------
174 ImGui::TextUnformatted("Project name");
175 ImGui::SetNextItemWidth(-1);
176 ImGui::InputText("##project_name", project_name_buffer_,
177 sizeof(project_name_buffer_));
178 {
179 gui::StyleColorGuard text_guard(ImGuiCol_Text, text_secondary);
180 ImGui::TextWrapped(
181 "Used as the project display name and to derive the project filename. "
182 "Spaces are replaced with underscores.");
183 }
184
185 ImGui::Spacing();
186 ImGui::Separator();
187 ImGui::Spacing();
188
189 // --- Actions --------------------------------------------------------------
190 const std::string rom_path(rom_path_buffer_);
191 const std::string project_name(project_name_buffer_);
192 const bool rom_valid = !rom_path.empty() && std::filesystem::exists(rom_path);
193 const bool name_valid = !project_name.empty();
194 const bool can_create = rom_valid && name_valid;
195
196 if (!can_create)
197 ImGui::BeginDisabled();
198 if (ImGui::Button(ICON_MD_ROCKET_LAUNCH " Create project", ImVec2(180, 0))) {
199 if (create_callback_) {
200 create_callback_(kTemplates[selected_template_].id, rom_path,
201 project_name);
202 }
203 ImGui::CloseCurrentPopup();
204 Reset();
205 ImGui::EndPopup();
206 return false;
207 }
208 if (!can_create)
209 ImGui::EndDisabled();
210
211 ImGui::SameLine();
212 if (ImGui::Button(ICON_MD_CLOSE " Cancel", ImVec2(100, 0)) ||
213 ImGui::IsKeyPressed(ImGuiKey_Escape, /*repeat=*/false)) {
214 ImGui::CloseCurrentPopup();
215 Reset();
216 ImGui::EndPopup();
217 return false;
218 }
219
220 // Inline validation hint — more discoverable than a disabled button alone.
221 if (!can_create) {
222 ImGui::SameLine();
223 const ImVec4 warn = gui::ConvertColorToImVec4(
224 gui::ThemeManager::Get().GetCurrentTheme().warning);
225 gui::StyleColorGuard warn_guard(ImGuiCol_Text, warn);
226 if (!rom_valid && rom_path.empty()) {
227 ImGui::TextUnformatted(ICON_MD_WARNING " Pick a ROM to continue");
228 } else if (!rom_valid) {
229 ImGui::TextUnformatted(ICON_MD_WARNING " ROM path does not exist");
230 } else if (!name_valid) {
231 ImGui::TextUnformatted(ICON_MD_WARNING " Give the project a name");
232 }
233 } else if (!status_message_.empty()) {
234 ImGui::SameLine();
235 ImGui::TextUnformatted(status_message_.c_str());
236 }
237
238 ImGui::EndPopup();
239 return true;
240}
241
242} // namespace editor
243} // namespace yaze
void Open(const std::string &initial_template="")
void ApplyTemplateSelection(const std::string &name)
RAII guard for ImGui style colors.
Definition style_guard.h:27
static ThemeManager & Get()
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
#define ICON_MD_ROCKET_LAUNCH
Definition icons.h:1612
#define ICON_MD_SHUFFLE
Definition icons.h:1738
#define ICON_MD_FOLDER_OPEN
Definition icons.h:813
#define ICON_MD_INFO
Definition icons.h:993
#define ICON_MD_WARNING
Definition icons.h:2123
#define ICON_MD_LIGHTBULB
Definition icons.h:1083
#define ICON_MD_TERRAIN
Definition icons.h:1952
#define ICON_MD_MAP
Definition icons.h:1173
#define ICON_MD_EDIT
Definition icons.h:645
#define ICON_MD_CLOSE
Definition icons.h:418
#define ICON_MD_COTTAGE
Definition icons.h:480
ImVec4 ConvertColorToImVec4(const Color &color)
Definition color.h:134
ImVec4 GetTextSecondaryVec4()