yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
welcome_screen.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <chrono>
5#include <cmath>
6#include <filesystem>
7#include <fstream>
8#include <string_view>
9
10#include "absl/strings/str_format.h"
11#include "absl/time/clock.h"
12#include "absl/time/time.h"
13#include "app/gui/core/icons.h"
15#include "app/platform/timing.h"
16#include "core/project.h"
17#include "imgui/imgui.h"
18#include "imgui/imgui_internal.h"
19#include "util/file_util.h"
20
21#ifndef M_PI
22#define M_PI 3.14159265358979323846
23#endif
24
25namespace yaze {
26namespace editor {
27
28namespace {
29
30// Get Zelda-inspired colors from theme or use fallback
31ImVec4 GetThemedColor(const char* color_name, const ImVec4& fallback) {
32 auto& theme_mgr = gui::ThemeManager::Get();
33 const auto& theme = theme_mgr.GetCurrentTheme();
34
35 if (!color_name) {
36 return fallback;
37 }
38
39 const std::string_view name(color_name);
40 if (name == "triforce_gold") {
41 return gui::ConvertColorToImVec4(theme.accent);
42 }
43 if (name == "hyrule_green") {
44 return gui::ConvertColorToImVec4(theme.success);
45 }
46 if (name == "master_sword_blue") {
47 return gui::ConvertColorToImVec4(theme.info);
48 }
49 if (name == "ganon_purple") {
50 return gui::ConvertColorToImVec4(theme.secondary);
51 }
52 if (name == "heart_red") {
53 return gui::ConvertColorToImVec4(theme.error);
54 }
55 if (name == "spirit_orange") {
56 return gui::ConvertColorToImVec4(theme.warning);
57 }
58 if (name == "shadow_purple") {
59 return ImLerp(gui::ConvertColorToImVec4(theme.secondary),
60 gui::GetSurfaceVec4(), 0.4f);
61 }
62
63 return fallback;
64}
65
66// Zelda-inspired color palette (fallbacks)
67const ImVec4 kTriforceGoldFallback = ImVec4(1.0f, 0.843f, 0.0f, 1.0f);
68const ImVec4 kHyruleGreenFallback = ImVec4(0.133f, 0.545f, 0.133f, 1.0f);
69const ImVec4 kMasterSwordBlueFallback = ImVec4(0.196f, 0.6f, 0.8f, 1.0f);
70const ImVec4 kGanonPurpleFallback = ImVec4(0.502f, 0.0f, 0.502f, 1.0f);
71const ImVec4 kHeartRedFallback = ImVec4(0.863f, 0.078f, 0.235f, 1.0f);
72const ImVec4 kSpiritOrangeFallback = ImVec4(1.0f, 0.647f, 0.0f, 1.0f);
73const ImVec4 kShadowPurpleFallback = ImVec4(0.416f, 0.353f, 0.804f, 1.0f);
74
75constexpr float kRecentCardBaseWidth = 220.0f;
76constexpr float kRecentCardBaseHeight = 95.0f;
77constexpr float kRecentCardWidthMaxFactor = 1.25f;
78constexpr float kRecentCardHeightMaxFactor = 1.25f;
79
80// Active colors (updated each frame from theme)
88
90 const std::filesystem::file_time_type& ftime) {
91 auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
92 ftime - std::filesystem::file_time_type::clock::now() +
93 std::chrono::system_clock::now());
94 auto now = std::chrono::system_clock::now();
95 auto diff = std::chrono::duration_cast<std::chrono::hours>(now - sctp);
96
97 int hours = diff.count();
98 if (hours < 24) {
99 return "Today";
100 } else if (hours < 48) {
101 return "Yesterday";
102 } else if (hours < 168) {
103 int days = hours / 24;
104 return absl::StrFormat("%d days ago", days);
105 } else if (hours < 720) {
106 int weeks = hours / 168;
107 return absl::StrFormat("%d week%s ago", weeks, weeks > 1 ? "s" : "");
108 } else {
109 int months = hours / 720;
110 return absl::StrFormat("%d month%s ago", months, months > 1 ? "s" : "");
111 }
112}
113
114// Draw a pixelated triforce in the background (ALTTP style)
115void DrawTriforceBackground(ImDrawList* draw_list, ImVec2 pos, float size,
116 float alpha, float glow) {
117 // Make it pixelated - round size to nearest 4 pixels
118 size = std::round(size / 4.0f) * 4.0f;
119
120 // Calculate triangle points with pixel-perfect positioning
121 auto triangle = [&](ImVec2 center, float s, ImU32 color) {
122 // Round to pixel boundaries for crisp edges
123 float half_s = s / 2.0f;
124 float tri_h = s * 0.866f; // Height of equilateral triangle
125
126 // Fixed: Proper equilateral triangle with apex at top
127 ImVec2 p1(std::round(center.x),
128 std::round(center.y - tri_h / 2.0f)); // Top apex
129 ImVec2 p2(std::round(center.x - half_s),
130 std::round(center.y + tri_h / 2.0f)); // Bottom left
131 ImVec2 p3(std::round(center.x + half_s),
132 std::round(center.y + tri_h / 2.0f)); // Bottom right
133
134 draw_list->AddTriangleFilled(p1, p2, p3, color);
135 };
136
137 ImVec4 gold_color = kTriforceGold;
138 gold_color.w = alpha;
139 ImU32 gold = ImGui::GetColorU32(gold_color);
140
141 // Proper triforce layout with three triangles
142 float small_size = size / 2.0f;
143 float small_height = small_size * 0.866f;
144
145 // Top triangle (centered above)
146 triangle(ImVec2(pos.x, pos.y), small_size, gold);
147
148 // Bottom left triangle
149 triangle(ImVec2(pos.x - small_size / 2.0f, pos.y + small_height), small_size,
150 gold);
151
152 // Bottom right triangle
153 triangle(ImVec2(pos.x + small_size / 2.0f, pos.y + small_height), small_size,
154 gold);
155}
156
158 int columns = 1;
159 float item_width = 0.0f;
160 float item_height = 0.0f;
161 float spacing = 0.0f;
162 float row_start_x = 0.0f;
163};
164
165GridLayout ComputeGridLayout(float avail_width, float min_width,
166 float max_width, float min_height,
167 float max_height, float preferred_width,
168 float aspect_ratio, float spacing) {
169 GridLayout layout;
170 layout.spacing = spacing;
171 const auto width_for_columns = [avail_width, spacing](int columns) {
172 return (avail_width - spacing * static_cast<float>(columns - 1)) /
173 static_cast<float>(columns);
174 };
175
176 layout.columns = std::max(1, static_cast<int>((avail_width + spacing) /
177 (preferred_width + spacing)));
178
179 layout.item_width = width_for_columns(layout.columns);
180 while (layout.columns > 1 && layout.item_width < min_width) {
181 layout.columns -= 1;
182 layout.item_width = width_for_columns(layout.columns);
183 }
184
185 layout.item_width = std::min(layout.item_width, max_width);
186 layout.item_width = std::min(layout.item_width, avail_width);
187 layout.item_height =
188 std::clamp(layout.item_width * aspect_ratio, min_height, max_height);
189
190 const float row_width =
191 layout.item_width * static_cast<float>(layout.columns) +
192 spacing * static_cast<float>(layout.columns - 1);
193 layout.row_start_x = ImGui::GetCursorPosX();
194 if (row_width < avail_width) {
195 layout.row_start_x += (avail_width - row_width) * 0.5f;
196 }
197
198 return layout;
199}
200
201} // namespace
202
206
207bool WelcomeScreen::Show(bool* p_open) {
208 // Update theme colors each frame
209 kTriforceGold = GetThemedColor("triforce_gold", kTriforceGoldFallback);
210 kHyruleGreen = GetThemedColor("hyrule_green", kHyruleGreenFallback);
211 kMasterSwordBlue =
212 GetThemedColor("master_sword_blue", kMasterSwordBlueFallback);
213 kGanonPurple = GetThemedColor("ganon_purple", kGanonPurpleFallback);
214 kHeartRed = GetThemedColor("heart_red", kHeartRedFallback);
215 kSpiritOrange = GetThemedColor("spirit_orange", kSpiritOrangeFallback);
216
218
219 // Get mouse position for interactive triforce movement
220 ImVec2 mouse_pos = ImGui::GetMousePos();
221
222 bool action_taken = false;
223
224 // Center the window within the dockspace region (accounting for sidebars)
225 ImGuiViewport* viewport = ImGui::GetMainViewport();
226 ImVec2 viewport_size = viewport->WorkSize;
227
228 // Calculate the dockspace region (excluding sidebars)
229 float dockspace_x = viewport->WorkPos.x + left_offset_;
230 float dockspace_width = viewport_size.x - left_offset_ - right_offset_;
231 float dockspace_center_x = dockspace_x + dockspace_width / 2.0f;
232 float dockspace_center_y = viewport->WorkPos.y + viewport_size.y / 2.0f;
233 ImVec2 center(dockspace_center_x, dockspace_center_y);
234
235 // Size based on dockspace region, not full viewport
236 float width = std::min(dockspace_width * 0.85f, 1400.0f);
237 float height = std::min(viewport_size.y * 0.85f, 900.0f);
238
239 ImGui::SetNextWindowPos(center, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
240 ImGui::SetNextWindowSize(ImVec2(width, height), ImGuiCond_Always);
241
242 // CRITICAL: Override ImGui's saved window state from imgui.ini
243 // Without this, ImGui will restore the last saved state (hidden/collapsed)
244 // even when our logic says the window should be visible
246 ImGui::SetNextWindowCollapsed(false); // Force window to be expanded
247 // Don't steal focus - allow menu bar to remain clickable
248 first_show_attempt_ = false;
249 }
250
251 // Window flags: allow menu bar to be clickable by not bringing to front
252 ImGuiWindowFlags window_flags =
253 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize |
254 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus |
255 ImGuiWindowFlags_NoTitleBar;
256
257 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20, 20));
258
259 if (ImGui::Begin("##WelcomeScreen", p_open, window_flags)) {
260 ImDrawList* bg_draw_list = ImGui::GetWindowDrawList();
261 ImVec2 window_pos = ImGui::GetWindowPos();
262 ImVec2 window_size = ImGui::GetWindowSize();
263
264 // Interactive scattered triforces (react to mouse position)
265 struct TriforceConfig {
266 float x_pct, y_pct; // Base position (percentage of window)
267 float size;
268 float alpha;
269 float repel_distance; // How far they move away from mouse
270 };
271
272 TriforceConfig triforce_configs[] = {
273 {0.08f, 0.12f, 36.0f, 0.025f, 50.0f}, // Top left corner
274 {0.92f, 0.15f, 34.0f, 0.022f, 50.0f}, // Top right corner
275 {0.06f, 0.88f, 32.0f, 0.020f, 45.0f}, // Bottom left
276 {0.94f, 0.85f, 34.0f, 0.023f, 50.0f}, // Bottom right
277 {0.50f, 0.08f, 38.0f, 0.028f, 55.0f}, // Top center
278 {0.50f, 0.92f, 32.0f, 0.020f, 45.0f}, // Bottom center
279 };
280
281 // Initialize base positions on first frame
283 for (int i = 0; i < kNumTriforces; ++i) {
284 float x = window_pos.x + window_size.x * triforce_configs[i].x_pct;
285 float y = window_pos.y + window_size.y * triforce_configs[i].y_pct;
286 triforce_base_positions_[i] = ImVec2(x, y);
288 }
290 }
291
292 // Update triforce positions based on mouse interaction + floating animation
293 for (int i = 0; i < kNumTriforces; ++i) {
294 // Update base position in case window moved/resized
295 float base_x = window_pos.x + window_size.x * triforce_configs[i].x_pct;
296 float base_y = window_pos.y + window_size.y * triforce_configs[i].y_pct;
297 triforce_base_positions_[i] = ImVec2(base_x, base_y);
298
299 // Slow, subtle floating animation
300 float time_offset = i * 1.2f; // Offset each triforce's animation
301 float float_speed_x =
302 (0.15f + (i % 2) * 0.1f) * triforce_speed_multiplier_; // Very slow
303 float float_speed_y =
304 (0.12f + ((i + 1) % 2) * 0.08f) * triforce_speed_multiplier_;
305 float float_amount_x = (20.0f + (i % 2) * 10.0f) *
306 triforce_size_multiplier_; // Smaller amplitude
307 float float_amount_y =
308 (25.0f + ((i + 1) % 2) * 15.0f) * triforce_size_multiplier_;
309
310 // Create gentle orbital motion
311 float float_x = std::sin(animation_time_ * float_speed_x + time_offset) *
312 float_amount_x;
313 float float_y =
314 std::cos(animation_time_ * float_speed_y + time_offset * 1.2f) *
315 float_amount_y;
316
317 // Calculate distance from mouse
318 float dx = triforce_base_positions_[i].x - mouse_pos.x;
319 float dy = triforce_base_positions_[i].y - mouse_pos.y;
320 float dist = std::sqrt(dx * dx + dy * dy);
321
322 // Calculate repulsion offset with stronger effect
323 ImVec2 target_pos = triforce_base_positions_[i];
324 float repel_radius =
325 200.0f; // Larger radius for more visible interaction
326
327 // Add floating motion to base position
328 target_pos.x += float_x;
329 target_pos.y += float_y;
330
331 // Apply mouse repulsion if enabled
332 if (triforce_mouse_repel_enabled_ && dist < repel_radius && dist > 0.1f) {
333 // Normalize direction away from mouse
334 float dir_x = dx / dist;
335 float dir_y = dy / dist;
336
337 // Much stronger repulsion when closer with exponential falloff
338 float normalized_dist = dist / repel_radius;
339 float repel_strength = (1.0f - normalized_dist * normalized_dist) *
340 triforce_configs[i].repel_distance;
341
342 target_pos.x += dir_x * repel_strength;
343 target_pos.y += dir_y * repel_strength;
344 }
345
346 // Smooth interpolation to target position (faster response)
347 // Use TimingManager for accurate delta time
348 float lerp_speed = 8.0f * yaze::TimingManager::Get().GetDeltaTime();
349 triforce_positions_[i].x +=
350 (target_pos.x - triforce_positions_[i].x) * lerp_speed;
351 triforce_positions_[i].y +=
352 (target_pos.y - triforce_positions_[i].y) * lerp_speed;
353
354 // Draw at current position with alpha multiplier
355 float adjusted_alpha =
356 triforce_configs[i].alpha * triforce_alpha_multiplier_;
357 float adjusted_size =
358 triforce_configs[i].size * triforce_size_multiplier_;
359 DrawTriforceBackground(bg_draw_list, triforce_positions_[i],
360 adjusted_size, adjusted_alpha, 0.0f);
361 }
362
363 // Update and draw particle system
364 if (particles_enabled_) {
365 // Spawn new particles
366 static float spawn_accumulator = 0.0f;
367 spawn_accumulator += ImGui::GetIO().DeltaTime * particle_spawn_rate_;
368 while (spawn_accumulator >= 1.0f &&
370 // Find inactive particle slot
371 for (int i = 0; i < kMaxParticles; ++i) {
372 if (particles_[i].lifetime <= 0.0f) {
373 // Spawn from random triforce
374 int source_triforce = rand() % kNumTriforces;
375 particles_[i].position = triforce_positions_[source_triforce];
376
377 // Random direction and speed
378 float angle = (rand() % 360) * (M_PI / 180.0f);
379 float speed = 20.0f + (rand() % 40);
381 ImVec2(std::cos(angle) * speed, std::sin(angle) * speed);
382
383 particles_[i].size = 2.0f + (rand() % 4);
384 particles_[i].alpha = 0.4f + (rand() % 40) / 100.0f;
385 particles_[i].max_lifetime = 2.0f + (rand() % 30) / 10.0f;
388 break;
389 }
390 }
391 spawn_accumulator -= 1.0f;
392 }
393
394 // Update and draw particles
395 float dt = ImGui::GetIO().DeltaTime;
396 for (int i = 0; i < kMaxParticles; ++i) {
397 if (particles_[i].lifetime > 0.0f) {
398 // Update lifetime
399 particles_[i].lifetime -= dt;
400 if (particles_[i].lifetime <= 0.0f) {
402 continue;
403 }
404
405 // Update position
406 particles_[i].position.x += particles_[i].velocity.x * dt;
407 particles_[i].position.y += particles_[i].velocity.y * dt;
408
409 // Fade out near end of life
410 float life_ratio =
412 float alpha =
414
415 // Draw particle as small golden circle
416 ImU32 particle_color =
417 ImGui::GetColorU32(ImVec4(1.0f, 0.843f, 0.0f, alpha));
418 bg_draw_list->AddCircleFilled(particles_[i].position,
419 particles_[i].size, particle_color, 8);
420 }
421 }
422 }
423
424 DrawHeader();
425
426 ImGui::Spacing();
427 ImGui::Spacing();
428
429 // Main content area with subtle gradient separator
430 ImDrawList* draw_list = ImGui::GetWindowDrawList();
431 ImVec2 separator_start = ImGui::GetCursorScreenPos();
432 ImVec2 separator_end(separator_start.x + ImGui::GetContentRegionAvail().x,
433 separator_start.y + 1);
434 ImVec4 gold_faded = kTriforceGold;
435 gold_faded.w = 0.3f;
436 ImVec4 blue_faded = kMasterSwordBlue;
437 blue_faded.w = 0.3f;
438 draw_list->AddRectFilledMultiColor(
439 separator_start, separator_end, ImGui::GetColorU32(gold_faded),
440 ImGui::GetColorU32(blue_faded), ImGui::GetColorU32(blue_faded),
441 ImGui::GetColorU32(gold_faded));
442
443 ImGui::Dummy(ImVec2(0, 10));
444
445 ImGui::BeginChild("WelcomeContent", ImVec2(0, -60), false);
446 const float content_width = ImGui::GetContentRegionAvail().x;
447 const float content_height = ImGui::GetContentRegionAvail().y;
448 const bool narrow_layout = content_width < 900.0f;
449
450 if (narrow_layout) {
451 float left_height =
452 std::clamp(content_height * 0.45f, 260.0f, content_height);
453 ImGui::BeginChild("LeftPanel", ImVec2(0, left_height), true,
454 ImGuiWindowFlags_NoScrollbar);
456 ImGui::Spacing();
457
458 ImVec2 sep_start = ImGui::GetCursorScreenPos();
459 draw_list->AddLine(
460 sep_start,
461 ImVec2(sep_start.x + ImGui::GetContentRegionAvail().x, sep_start.y),
462 ImGui::GetColorU32(
463 ImVec4(kTriforceGold.x, kTriforceGold.y, kTriforceGold.z, 0.2f)),
464 1.0f);
465
466 ImGui::Dummy(ImVec2(0, 5));
468 ImGui::EndChild();
469
470 ImGui::Spacing();
471
472 ImGui::BeginChild("RightPanel", ImVec2(0, 0), true);
474 ImGui::Spacing();
475
476 sep_start = ImGui::GetCursorScreenPos();
477 draw_list->AddLine(
478 sep_start,
479 ImVec2(sep_start.x + ImGui::GetContentRegionAvail().x, sep_start.y),
480 ImGui::GetColorU32(ImVec4(kMasterSwordBlue.x, kMasterSwordBlue.y,
481 kMasterSwordBlue.z, 0.2f)),
482 1.0f);
483
484 ImGui::Dummy(ImVec2(0, 5));
485 DrawWhatsNew();
486 ImGui::EndChild();
487 } else {
488 ImGui::BeginChild("LeftPanel",
489 ImVec2(ImGui::GetContentRegionAvail().x * 0.3f, 0),
490 true, ImGuiWindowFlags_NoScrollbar);
492 ImGui::Spacing();
493
494 ImVec2 sep_start = ImGui::GetCursorScreenPos();
495 draw_list->AddLine(
496 sep_start,
497 ImVec2(sep_start.x + ImGui::GetContentRegionAvail().x, sep_start.y),
498 ImGui::GetColorU32(
499 ImVec4(kTriforceGold.x, kTriforceGold.y, kTriforceGold.z, 0.2f)),
500 1.0f);
501
502 ImGui::Dummy(ImVec2(0, 5));
504 ImGui::EndChild();
505
506 ImGui::SameLine();
507
508 ImGui::BeginChild("RightPanel", ImVec2(0, 0), true);
510 ImGui::Spacing();
511
512 sep_start = ImGui::GetCursorScreenPos();
513 draw_list->AddLine(
514 sep_start,
515 ImVec2(sep_start.x + ImGui::GetContentRegionAvail().x, sep_start.y),
516 ImGui::GetColorU32(ImVec4(kMasterSwordBlue.x, kMasterSwordBlue.y,
517 kMasterSwordBlue.z, 0.2f)),
518 1.0f);
519
520 ImGui::Dummy(ImVec2(0, 5));
521 DrawWhatsNew();
522 ImGui::EndChild();
523 }
524
525 ImGui::EndChild();
526
527 // Footer with subtle gradient
528 ImVec2 footer_start = ImGui::GetCursorScreenPos();
529 ImVec2 footer_end(footer_start.x + ImGui::GetContentRegionAvail().x,
530 footer_start.y + 1);
531 ImVec4 red_faded = kHeartRed;
532 red_faded.w = 0.3f;
533 ImVec4 green_faded = kHyruleGreen;
534 green_faded.w = 0.3f;
535 draw_list->AddRectFilledMultiColor(
536 footer_start, footer_end, ImGui::GetColorU32(red_faded),
537 ImGui::GetColorU32(green_faded), ImGui::GetColorU32(green_faded),
538 ImGui::GetColorU32(red_faded));
539
540 ImGui::Dummy(ImVec2(0, 5));
542 }
543 ImGui::End();
544
545 ImGui::PopStyleVar();
546
547 return action_taken;
548}
549
551 animation_time_ += ImGui::GetIO().DeltaTime;
552
553 // Update hover scale for cards (smooth interpolation)
554 for (int i = 0; i < 6; ++i) {
555 float target = (hovered_card_ == i) ? 1.03f : 1.0f;
557 (target - card_hover_scale_[i]) * ImGui::GetIO().DeltaTime * 10.0f;
558 }
559
560 // Note: Triforce positions and particles are updated in Show() based on mouse
561 // position
562}
563
565 recent_projects_.clear();
566
567 // Use the ProjectManager singleton to get recent files
569 auto recent_files = manager.GetRecentFiles(); // Copy to allow modification
570
571 std::vector<std::string> files_to_remove;
572
573 for (const auto& filepath : recent_files) {
575 break;
576
577 std::filesystem::path path(filepath);
578
579 // Skip and mark for removal if file doesn't exist
580 if (!std::filesystem::exists(path)) {
581 files_to_remove.push_back(filepath);
582 continue;
583 }
584
585 RecentProject project;
586 project.filepath = filepath;
587 project.name = path.filename().string();
588
589 // Get file modification time
590 try {
591 auto ftime = std::filesystem::last_write_time(path);
592 project.last_modified = GetRelativeTimeString(ftime);
593 project.rom_title = "ALTTP ROM";
594 } catch (const std::filesystem::filesystem_error&) {
595 // File became inaccessible between exists() check and last_write_time()
596 files_to_remove.push_back(filepath);
597 continue;
598 }
599
600 recent_projects_.push_back(project);
601 }
602
603 // Remove missing files from the recent files manager
604 for (const auto& missing_file : files_to_remove) {
605 manager.RemoveFile(missing_file);
606 }
607
608 // Save updated list if we removed any files
609 if (!files_to_remove.empty()) {
610 manager.Save();
611 }
612}
613
615 ImDrawList* draw_list = ImGui::GetWindowDrawList();
616
617 ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[2]); // Large font
618
619 // Simple centered title
620 const char* title = ICON_MD_CASTLE " yaze";
621 auto windowWidth = ImGui::GetWindowSize().x;
622 auto textWidth = ImGui::CalcTextSize(title).x;
623 float xPos = (windowWidth - textWidth) * 0.5f;
624
625 ImGui::SetCursorPosX(xPos);
626 ImVec2 text_pos = ImGui::GetCursorScreenPos();
627
628 // Subtle static glow behind text
629 float glow_size = 30.0f;
630 ImU32 glow_color = ImGui::GetColorU32(
631 ImVec4(kTriforceGold.x, kTriforceGold.y, kTriforceGold.z, 0.15f));
632 draw_list->AddCircleFilled(
633 ImVec2(text_pos.x + textWidth / 2, text_pos.y + 15), glow_size,
634 glow_color, 32);
635
636 // Simple gold color for title
637 ImGui::TextColored(kTriforceGold, "%s", title);
638 ImGui::PopFont();
639
640 // Static subtitle
641 const char* subtitle = "Yet Another Zelda3 Editor";
642 textWidth = ImGui::CalcTextSize(subtitle).x;
643 ImGui::SetCursorPosX((windowWidth - textWidth) * 0.5f);
644
645 ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "%s", subtitle);
646
647 const std::string version_line = absl::StrFormat(
648 "v%s - Multi-provider AI, web preview, and release polish",
650 textWidth = ImGui::CalcTextSize(version_line.c_str()).x;
651 ImGui::SetCursorPosX((windowWidth - textWidth) * 0.5f);
652 ImGui::TextColored(ImVec4(0.55f, 0.55f, 0.55f, 1.0f), "%s",
653 version_line.c_str());
654
655 // Small decorative triforces flanking the title (static, transparent)
656 // Positioned well away from text to avoid crowding
657 ImVec2 left_tri_pos(xPos - 80, text_pos.y + 20);
658 ImVec2 right_tri_pos(xPos + textWidth + 50, text_pos.y + 20);
659 DrawTriforceBackground(draw_list, left_tri_pos, 20, 0.12f, 0.0f);
660 DrawTriforceBackground(draw_list, right_tri_pos, 20, 0.12f, 0.0f);
661
662 ImGui::Spacing();
663}
664
666 ImGui::TextColored(kSpiritOrange, ICON_MD_BOLT " Quick Actions");
667 const ImVec4 text_secondary = gui::GetTextSecondaryVec4();
668 ImGui::PushStyleColor(ImGuiCol_Text, text_secondary);
669 ImGui::TextWrapped(
670 "New here? Start with Open ROM or New Project. For AI workflows, "
671 "configure a provider in Settings > Agent (Ollama/Gemini/OpenAI/"
672 "Anthropic).");
673 ImGui::PopStyleColor();
674 ImGui::Spacing();
675
676 float button_width = ImGui::GetContentRegionAvail().x;
677
678 // Animated button colors (compact height)
679 auto draw_action_button = [&](const char* icon, const char* text,
680 const ImVec4& color, bool enabled,
681 std::function<void()> callback) {
682 ImGui::PushStyleColor(
683 ImGuiCol_Button,
684 ImVec4(color.x * 0.6f, color.y * 0.6f, color.z * 0.6f, 0.8f));
685 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
686 ImVec4(color.x, color.y, color.z, 1.0f));
687 ImGui::PushStyleColor(
688 ImGuiCol_ButtonActive,
689 ImVec4(color.x * 1.2f, color.y * 1.2f, color.z * 1.2f, 1.0f));
690
691 if (!enabled)
692 ImGui::BeginDisabled();
693
694 bool clicked =
695 ImGui::Button(absl::StrFormat("%s %s", icon, text).c_str(),
696 ImVec2(button_width, 38)); // Reduced from 45 to 38
697
698 if (!enabled)
699 ImGui::EndDisabled();
700
701 ImGui::PopStyleColor(3);
702
703 if (clicked && enabled && callback) {
704 callback();
705 }
706
707 return clicked;
708 };
709
710 // Open ROM button - Green like finding an item
711 if (draw_action_button(ICON_MD_FOLDER_OPEN, "Open ROM", kHyruleGreen, true,
713 // Handled by callback
714 }
715 if (ImGui::IsItemHovered()) {
716 ImGui::SetTooltip(ICON_MD_INFO
717 " Open a clean, legally obtained ALttP (USA) ROM file");
718 }
719
720 ImGui::Spacing();
721
722 // New Project button - Gold like getting a treasure
723 if (draw_action_button(ICON_MD_ADD_CIRCLE, "New Project", kTriforceGold, true,
725 // Handled by callback
726 }
727 if (ImGui::IsItemHovered()) {
728 ImGui::SetTooltip(ICON_MD_INFO
729 " Create a new project from a ROM and template");
730 }
731
732 ImGui::Spacing();
733
734 // AI Agent button - Purple like magic
735 if (draw_action_button(ICON_MD_SMART_TOY, "AI Agent", kGanonPurple, true,
737 // Handled by callback
738 }
739 if (ImGui::IsItemHovered()) {
740 ImGui::SetTooltip(ICON_MD_INFO
741 " Ask the AI agent to guide edits in natural language "
742 "(requires Ollama, Gemini, OpenAI, or Anthropic)");
743 }
744}
745
747 ImGui::TextColored(kMasterSwordBlue, ICON_MD_HISTORY " Recent Projects");
748 ImGui::Spacing();
749
750 if (recent_projects_.empty()) {
751 // Simple empty state
752 const ImVec4 text_secondary = gui::GetTextSecondaryVec4();
753 ImGui::PushStyleColor(ImGuiCol_Text, text_secondary);
754
755 ImVec2 cursor = ImGui::GetCursorPos();
756 ImGui::SetCursorPosX(cursor.x + ImGui::GetContentRegionAvail().x * 0.3f);
757 ImGui::TextColored(
758 ImVec4(kTriforceGold.x, kTriforceGold.y, kTriforceGold.z, 0.8f),
760 ImGui::SetCursorPosX(cursor.x);
761
762 ImGui::TextWrapped(
763 "No recent projects yet.\nOpen a ROM or start a new project to begin "
764 "your adventure!");
765 ImGui::PopStyleColor();
766 return;
767 }
768
769 const float scale = ImGui::GetFontSize() / 16.0f;
770 const float min_width = kRecentCardBaseWidth * scale;
771 const float max_width =
772 kRecentCardBaseWidth * kRecentCardWidthMaxFactor * scale;
773 const float min_height = kRecentCardBaseHeight * scale;
774 const float max_height =
775 kRecentCardBaseHeight * kRecentCardHeightMaxFactor * scale;
776 const float spacing = ImGui::GetStyle().ItemSpacing.x;
777 const float aspect_ratio = min_height / std::max(min_width, 1.0f);
778
779 GridLayout layout = ComputeGridLayout(
780 ImGui::GetContentRegionAvail().x, min_width, max_width, min_height,
781 max_height, min_width, aspect_ratio, spacing);
782
783 int column = 0;
784 for (size_t i = 0; i < recent_projects_.size(); ++i) {
785 if (column == 0) {
786 ImGui::SetCursorPosX(layout.row_start_x);
787 }
788
789 DrawProjectPanel(recent_projects_[i], static_cast<int>(i),
790 ImVec2(layout.item_width, layout.item_height));
791
792 column += 1;
793 if (column < layout.columns) {
794 ImGui::SameLine(0.0f, layout.spacing);
795 } else {
796 column = 0;
797 ImGui::Spacing();
798 }
799 }
800
801 if (column != 0) {
802 ImGui::NewLine();
803 }
804}
805
806void WelcomeScreen::DrawProjectPanel(const RecentProject& project, int index,
807 const ImVec2& card_size) {
808 ImGui::BeginGroup();
809
810 const ImVec4 surface = gui::GetSurfaceVec4();
811 const ImVec4 surface_variant = gui::GetSurfaceVariantVec4();
812 const ImVec4 text_primary = gui::GetOnSurfaceVec4();
813 const ImVec4 text_secondary = gui::GetTextSecondaryVec4();
814 const ImVec4 text_disabled = gui::GetTextDisabledVec4();
815
816 ImVec2 resolved_card_size = card_size;
817 ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
818
819 // Subtle hover scale (only on actual hover, no animation)
820 float hover_scale = card_hover_scale_[index];
821 if (hover_scale != 1.0f) {
822 ImVec2 center(cursor_pos.x + resolved_card_size.x / 2,
823 cursor_pos.y + resolved_card_size.y / 2);
824 cursor_pos.x = center.x - (resolved_card_size.x * hover_scale) / 2;
825 cursor_pos.y = center.y - (resolved_card_size.y * hover_scale) / 2;
826 resolved_card_size.x *= hover_scale;
827 resolved_card_size.y *= hover_scale;
828 }
829
830 const float layout_scale = resolved_card_size.y / kRecentCardBaseHeight;
831 const float padding = 8.0f * layout_scale;
832 const float icon_radius = 15.0f * layout_scale;
833 const float icon_offset = 13.0f * layout_scale;
834 const float text_offset = 32.0f * layout_scale;
835 const float name_offset_y = 8.0f * layout_scale;
836 const float rom_offset_y = 35.0f * layout_scale;
837 const float path_offset_y = 58.0f * layout_scale;
838
839 // Draw card background with subtle gradient
840 ImDrawList* draw_list = ImGui::GetWindowDrawList();
841
842 // Gradient background
843 ImVec4 color_top = ImLerp(surface_variant, surface, 0.7f);
844 ImVec4 color_bottom = ImLerp(surface_variant, surface, 0.3f);
845 ImU32 color_top_u32 = ImGui::GetColorU32(color_top);
846 ImU32 color_bottom_u32 = ImGui::GetColorU32(color_bottom);
847 draw_list->AddRectFilledMultiColor(
848 cursor_pos,
849 ImVec2(cursor_pos.x + resolved_card_size.x,
850 cursor_pos.y + resolved_card_size.y),
851 color_top_u32, color_top_u32, color_bottom_u32, color_bottom_u32);
852
853 // Static themed border
854 ImVec4 border_color_base = (index % 3 == 0) ? kHyruleGreen
855 : (index % 3 == 1) ? kMasterSwordBlue
856 : kTriforceGold;
857 ImU32 border_color = ImGui::GetColorU32(ImVec4(
858 border_color_base.x, border_color_base.y, border_color_base.z, 0.5f));
859
860 draw_list->AddRect(cursor_pos,
861 ImVec2(cursor_pos.x + resolved_card_size.x,
862 cursor_pos.y + resolved_card_size.y),
863 border_color, 6.0f, 0, 2.0f);
864
865 // Make the card clickable
866 ImGui::SetCursorScreenPos(cursor_pos);
867 ImGui::InvisibleButton(absl::StrFormat("ProjectPanel_%d", index).c_str(),
868 resolved_card_size);
869 bool is_hovered = ImGui::IsItemHovered();
870 bool is_clicked = ImGui::IsItemClicked();
871
873 is_hovered ? index : (hovered_card_ == index ? -1 : hovered_card_);
874
875 // Subtle hover glow (no particles)
876 if (is_hovered) {
877 ImU32 hover_color = ImGui::GetColorU32(
878 ImVec4(kTriforceGold.x, kTriforceGold.y, kTriforceGold.z, 0.15f));
879 draw_list->AddRectFilled(cursor_pos,
880 ImVec2(cursor_pos.x + resolved_card_size.x,
881 cursor_pos.y + resolved_card_size.y),
882 hover_color, 6.0f);
883 }
884
885 // Draw content (tighter layout)
886 ImVec2 content_pos(cursor_pos.x + padding, cursor_pos.y + padding);
887
888 // Icon with colored background circle (compact)
889 ImVec2 icon_center(content_pos.x + icon_offset, content_pos.y + icon_offset);
890 ImU32 icon_bg = ImGui::GetColorU32(border_color_base);
891 draw_list->AddCircleFilled(icon_center, icon_radius, icon_bg, 24);
892
893 // Center the icon properly
894 ImVec2 icon_size = ImGui::CalcTextSize(ICON_MD_VIDEOGAME_ASSET);
895 ImGui::SetCursorScreenPos(
896 ImVec2(icon_center.x - icon_size.x / 2, icon_center.y - icon_size.y / 2));
897 ImGui::PushStyleColor(ImGuiCol_Text, text_primary);
898 ImGui::Text(ICON_MD_VIDEOGAME_ASSET);
899 ImGui::PopStyleColor();
900
901 // Project name (compact, shorten if too long)
902 ImGui::SetCursorScreenPos(
903 ImVec2(content_pos.x + text_offset, content_pos.y + name_offset_y));
904 ImGui::PushTextWrapPos(cursor_pos.x + resolved_card_size.x - padding);
905 ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[0]); // Default font
906 std::string short_name = project.name;
907 const float approx_char_width = ImGui::CalcTextSize("A").x;
908 const float name_max_width =
909 std::max(120.0f, resolved_card_size.x - text_offset - padding);
910 size_t max_chars = static_cast<size_t>(
911 std::max(12.0f, name_max_width / std::max(approx_char_width, 1.0f)));
912 if (short_name.length() > max_chars) {
913 short_name = short_name.substr(0, max_chars - 3) + "...";
914 }
915 ImGui::TextColored(kTriforceGold, "%s", short_name.c_str());
916 ImGui::PopFont();
917 ImGui::PopTextWrapPos();
918
919 // ROM title (compact)
920 ImGui::SetCursorScreenPos(
921 ImVec2(content_pos.x + padding * 0.5f, content_pos.y + rom_offset_y));
922 ImGui::PushStyleColor(ImGuiCol_Text, text_secondary);
923 ImGui::Text(ICON_MD_GAMEPAD " %s", project.rom_title.c_str());
924 ImGui::PopStyleColor();
925
926 // Path in card (compact)
927 ImGui::SetCursorScreenPos(
928 ImVec2(content_pos.x + padding * 0.5f, content_pos.y + path_offset_y));
929 ImGui::PushStyleColor(ImGuiCol_Text, text_disabled);
930 std::string short_path = project.filepath;
931 const float path_max_width =
932 std::max(120.0f, resolved_card_size.x - padding * 2.0f);
933 size_t max_path_chars = static_cast<size_t>(
934 std::max(18.0f, path_max_width / std::max(approx_char_width, 1.0f)));
935 if (short_path.length() > max_path_chars) {
936 short_path =
937 "..." + short_path.substr(short_path.length() - (max_path_chars - 3));
938 }
939 ImGui::Text(ICON_MD_FOLDER " %s", short_path.c_str());
940 ImGui::PopStyleColor();
941
942 // Tooltip
943 if (is_hovered) {
944 ImGui::BeginTooltip();
945 ImGui::TextColored(kMasterSwordBlue, ICON_MD_INFO " Project Details");
946 ImGui::Separator();
947 ImGui::Text("Name: %s", project.name.c_str());
948 ImGui::Text("ROM: %s", project.rom_title.c_str());
949 ImGui::Text("Path: %s", project.filepath.c_str());
950 ImGui::Separator();
951 ImGui::TextColored(kTriforceGold, ICON_MD_TOUCH_APP " Click to open");
952 ImGui::EndTooltip();
953 }
954
955 // Handle click
956 if (is_clicked && open_project_callback_) {
958 }
959
960 ImGui::EndGroup();
961}
962
964 // Header with visual settings button
965 float content_width = ImGui::GetContentRegionAvail().x;
966 ImGui::TextColored(kGanonPurple, ICON_MD_LAYERS " Project Templates");
967 ImGui::SameLine(content_width - 25);
968 if (ImGui::SmallButton(show_triforce_settings_ ? ICON_MD_CLOSE
969 : ICON_MD_TUNE)) {
971 }
972 if (ImGui::IsItemHovered()) {
973 ImGui::SetTooltip(ICON_MD_AUTO_AWESOME " Visual Effects Settings");
974 }
975
976 ImGui::Spacing();
977
978 // Visual effects settings panel (when opened)
980 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.18f, 0.15f, 0.22f, 0.4f));
981 ImGui::BeginChild("VisualSettingsCompact", ImVec2(0, 115), true,
982 ImGuiWindowFlags_NoScrollbar);
983 {
984 ImGui::TextColored(kGanonPurple, ICON_MD_AUTO_AWESOME " Visual Effects");
985 ImGui::Spacing();
986
987 ImGui::Text(ICON_MD_OPACITY " Visibility");
988 ImGui::SetNextItemWidth(-1);
989 ImGui::SliderFloat("##visibility", &triforce_alpha_multiplier_, 0.0f,
990 3.0f, "%.1fx");
991
992 ImGui::Text(ICON_MD_SPEED " Speed");
993 ImGui::SetNextItemWidth(-1);
994 ImGui::SliderFloat("##speed", &triforce_speed_multiplier_, 0.05f, 1.0f,
995 "%.2fx");
996
997 ImGui::Checkbox(ICON_MD_MOUSE " Mouse Interaction",
999 ImGui::SameLine();
1000 ImGui::Checkbox(ICON_MD_AUTO_FIX_HIGH " Particles", &particles_enabled_);
1001
1002 if (ImGui::SmallButton(ICON_MD_REFRESH " Reset")) {
1007 particles_enabled_ = true;
1008 particle_spawn_rate_ = 2.0f;
1009 }
1010 }
1011 ImGui::EndChild();
1012 ImGui::PopStyleColor();
1013 ImGui::Spacing();
1014 }
1015
1016 ImGui::Spacing();
1017
1018 struct Template {
1019 const char* icon;
1020 const char* name;
1021 const char* description;
1022 const char* template_id;
1023 ImVec4 color;
1024 };
1025
1026 Template templates[] = {
1027 {ICON_MD_COTTAGE, "Vanilla ROM Hack",
1028 "Standard editing without custom ASM", "Vanilla ROM Hack", kHyruleGreen},
1029 {ICON_MD_MAP, "ZSCustomOverworld v3", "Full overworld expansion features",
1030 "ZSCustomOverworld v3 (Recommended)", kMasterSwordBlue},
1031 {ICON_MD_LAYERS, "ZSCustomOverworld v2", "Basic overworld expansion",
1032 "ZSCustomOverworld v2", kShadowPurple},
1033 {ICON_MD_SHUFFLE, "Randomizer Compatible",
1034 "Minimal custom features for rando", "Randomizer Compatible",
1035 kSpiritOrange},
1036 };
1037
1038 for (int i = 0; i < 4; ++i) {
1039 bool is_selected = (selected_template_ == i);
1040
1041 // Subtle selection highlight (no animation)
1042 if (is_selected) {
1043 ImGui::PushStyleColor(
1044 ImGuiCol_Header,
1045 ImVec4(templates[i].color.x * 0.6f, templates[i].color.y * 0.6f,
1046 templates[i].color.z * 0.6f, 0.6f));
1047 }
1048
1049 if (ImGui::Selectable(
1050 absl::StrFormat("%s %s", templates[i].icon, templates[i].name)
1051 .c_str(),
1052 is_selected)) {
1054 }
1055
1056 if (is_selected) {
1057 ImGui::PopStyleColor();
1058 }
1059
1060 if (ImGui::IsItemHovered()) {
1061 ImGui::SetTooltip("%s %s\n%s", ICON_MD_INFO, templates[i].name,
1062 templates[i].description);
1063 }
1064 }
1065
1066 ImGui::Spacing();
1067
1068 // Use Template button - enabled and functional
1069 ImGui::PushStyleColor(ImGuiCol_Button,
1070 ImVec4(kSpiritOrange.x * 0.6f, kSpiritOrange.y * 0.6f,
1071 kSpiritOrange.z * 0.6f, 0.8f));
1072 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, kSpiritOrange);
1073 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
1074 ImVec4(kSpiritOrange.x * 1.2f, kSpiritOrange.y * 1.2f,
1075 kSpiritOrange.z * 1.2f, 1.0f));
1076
1077 if (ImGui::Button(
1078 absl::StrFormat("%s Use Template", ICON_MD_ROCKET_LAUNCH).c_str(),
1079 ImVec2(-1, 30))) {
1080 // Trigger template-based project creation
1083 templates[selected_template_].template_id);
1084 } else if (new_project_callback_) {
1085 // Fallback to regular new project if template callback not set
1087 }
1088 }
1089
1090 ImGui::PopStyleColor(3);
1091
1092 if (ImGui::IsItemHovered()) {
1093 ImGui::SetTooltip(
1094 "%s Create new project with '%s' template\nThis will "
1095 "open a ROM and apply the template settings.",
1096 ICON_MD_INFO, templates[selected_template_].name);
1097 }
1098}
1099
1101 // Static tip (or could rotate based on session start time rather than
1102 // animation)
1103 const char* tips[] = {
1104 "Open a ROM first, then save a copy before editing",
1105 "Press Ctrl+Shift+P for the command palette and F1 for help",
1106 "Use Ctrl+Shift+A for AI workflows (Ollama/Gemini/OpenAI/Anthropic)",
1107 "Try z3ed --tui for scripting and automation",
1108 "Enable Web/WASM collaboration for live syncing",
1109 "Keep multiple sessions for parallel ROM work (Ctrl+Shift+N)"};
1110 int tip_index = 0; // Show first tip, or could be random on screen open
1111
1112 ImGui::Text(ICON_MD_LIGHTBULB);
1113 ImGui::SameLine();
1114 ImGui::TextColored(kTriforceGold, "Tip:");
1115 ImGui::SameLine();
1116 ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.8f, 1.0f), "%s", tips[tip_index]);
1117
1118 ImGui::SameLine(ImGui::GetWindowWidth() - 220);
1119 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.3f, 0.3f, 0.5f));
1120 if (ImGui::SmallButton(
1121 absl::StrFormat("%s Don't show again", ICON_MD_CLOSE).c_str())) {
1122 manually_closed_ = true;
1123 }
1124 ImGui::PopStyleColor();
1125}
1126
1128 ImGui::TextColored(kHeartRed, ICON_MD_NEW_RELEASES " What's New");
1129 ImGui::Spacing();
1130
1131 // Version badge (no animation)
1132 ImGui::TextColored(kMasterSwordBlue, ICON_MD_VERIFIED "yaze v%s",
1134 ImGui::Spacing();
1135
1136 // Feature list with icons and colors
1137 struct Feature {
1138 const char* icon;
1139 const char* title;
1140 const char* desc;
1141 ImVec4 color;
1142 };
1143
1144 Feature features[] = {
1145 {ICON_MD_PSYCHOLOGY, "AI Agent Workflows",
1146 "Multi-provider AI (Ollama/Gemini/OpenAI/Anthropic) in app and z3ed",
1147 kGanonPurple},
1148 {ICON_MD_TERMINAL, "CLI + Automation",
1149 "z3ed CLI/TUI with test, doctor, and schema export tooling",
1150 kMasterSwordBlue},
1151 {ICON_MD_PUBLIC, "Web/WASM Preview",
1152 "Browser build with collaboration server hooks", kTriforceGold},
1153 {ICON_MD_SPEED, "Performance & Stability",
1154 "Faster asset loading and safer graphics lifetimes", kSpiritOrange},
1155 {ICON_MD_FACT_CHECK, "Release Quality",
1156 "Cross-platform packaging and CI/CD hardening for v0.5.0", kHyruleGreen},
1157 };
1158
1159 for (const auto& feature : features) {
1160 ImGui::Bullet();
1161 ImGui::SameLine();
1162 ImGui::TextColored(feature.color, "%s ", feature.icon);
1163 ImGui::SameLine();
1164 ImGui::TextColored(ImVec4(0.95f, 0.95f, 0.95f, 1.0f), "%s", feature.title);
1165
1166 ImGui::Indent(25);
1167 ImGui::TextColored(ImVec4(0.65f, 0.65f, 0.65f, 1.0f), "%s", feature.desc);
1168 ImGui::Unindent(25);
1169 ImGui::Spacing();
1170 }
1171
1172 ImGui::Spacing();
1173 ImGui::PushStyleColor(
1174 ImGuiCol_Button,
1175 ImVec4(kMasterSwordBlue.x * 0.6f, kMasterSwordBlue.y * 0.6f,
1176 kMasterSwordBlue.z * 0.6f, 0.8f));
1177 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, kMasterSwordBlue);
1178 if (ImGui::Button(
1179 absl::StrFormat("%s View Full Changelog", ICON_MD_OPEN_IN_NEW)
1180 .c_str())) {
1181 // Open changelog or GitHub releases
1182 }
1183 ImGui::PopStyleColor(2);
1184}
1185
1186} // namespace editor
1187} // namespace yaze
static TimingManager & Get()
Definition timing.h:20
float GetDeltaTime() const
Get the last frame's delta time in seconds.
Definition timing.h:60
std::function< void()> open_rom_callback_
std::function< void()> new_project_callback_
void RefreshRecentProjects()
Refresh recent projects list from the project manager.
static constexpr int kNumTriforces
std::vector< RecentProject > recent_projects_
void DrawProjectPanel(const RecentProject &project, int index, const ImVec2 &card_size)
ImVec2 triforce_base_positions_[kNumTriforces]
Particle particles_[kMaxParticles]
std::function< void(const std::string &) new_project_with_template_callback_)
void UpdateAnimations()
Update animation time for dynamic effects.
static constexpr int kMaxParticles
bool Show(bool *p_open)
Show the welcome screen.
ImVec2 triforce_positions_[kNumTriforces]
std::function< void(const std::string &) open_project_callback_)
static constexpr int kMaxRecentProjects
std::function< void()> open_agent_callback_
static ThemeManager & Get()
static RecentFilesManager & GetInstance()
Definition project.h:312
#define YAZE_VERSION_STRING
Definition yaze.h:43
#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_LIGHTBULB
Definition icons.h:1083
#define ICON_MD_NEW_RELEASES
Definition icons.h:1291
#define ICON_MD_TUNE
Definition icons.h:2022
#define ICON_MD_REFRESH
Definition icons.h:1572
#define ICON_MD_MAP
Definition icons.h:1173
#define ICON_MD_AUTO_AWESOME
Definition icons.h:214
#define ICON_MD_VIDEOGAME_ASSET
Definition icons.h:2076
#define ICON_MD_PUBLIC
Definition icons.h:1524
#define ICON_MD_SPEED
Definition icons.h:1817
#define ICON_MD_VERIFIED
Definition icons.h:2055
#define ICON_MD_CASTLE
Definition icons.h:380
#define ICON_MD_AUTO_FIX_HIGH
Definition icons.h:218
#define ICON_MD_FACT_CHECK
Definition icons.h:721
#define ICON_MD_LAYERS
Definition icons.h:1068
#define ICON_MD_PSYCHOLOGY
Definition icons.h:1523
#define ICON_MD_BOLT
Definition icons.h:282
#define ICON_MD_TERMINAL
Definition icons.h:1951
#define ICON_MD_TOUCH_APP
Definition icons.h:2000
#define ICON_MD_MOUSE
Definition icons.h:1251
#define ICON_MD_FOLDER
Definition icons.h:809
#define ICON_MD_OPEN_IN_NEW
Definition icons.h:1354
#define ICON_MD_CLOSE
Definition icons.h:418
#define ICON_MD_GAMEPAD
Definition icons.h:866
#define ICON_MD_COTTAGE
Definition icons.h:480
#define ICON_MD_ADD_CIRCLE
Definition icons.h:95
#define ICON_MD_SMART_TOY
Definition icons.h:1781
#define ICON_MD_OPACITY
Definition icons.h:1351
#define ICON_MD_HISTORY
Definition icons.h:946
#define ICON_MD_EXPLORE
Definition icons.h:705
ImVec4 GetThemedColor(const char *color_name, const ImVec4 &fallback)
std::string GetRelativeTimeString(const std::filesystem::file_time_type &ftime)
void DrawTriforceBackground(ImDrawList *draw_list, ImVec2 pos, float size, float alpha, float glow)
ImVec4 ConvertColorToImVec4(const Color &color)
Definition color.h:23
ImVec4 GetSurfaceVariantVec4()
ImVec4 GetSurfaceVec4()
ImVec4 GetTextDisabledVec4()
ImVec4 GetTextSecondaryVec4()
ImVec4 GetOnSurfaceVec4()
Information about a recently used project.
#define M_PI