209 kTriforceGold = GetThemedColor(
"triforce_gold", kTriforceGoldFallback);
210 kHyruleGreen = GetThemedColor(
"hyrule_green", kHyruleGreenFallback);
212 GetThemedColor(
"master_sword_blue", kMasterSwordBlueFallback);
213 kGanonPurple = GetThemedColor(
"ganon_purple", kGanonPurpleFallback);
214 kHeartRed = GetThemedColor(
"heart_red", kHeartRedFallback);
215 kSpiritOrange = GetThemedColor(
"spirit_orange", kSpiritOrangeFallback);
220 ImVec2 mouse_pos = ImGui::GetMousePos();
222 bool action_taken =
false;
225 ImGuiViewport* viewport = ImGui::GetMainViewport();
226 ImVec2 viewport_size = viewport->WorkSize;
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);
236 float width = std::min(dockspace_width * 0.85f, 1400.0f);
237 float height = std::min(viewport_size.y * 0.85f, 900.0f);
239 ImGui::SetNextWindowPos(center, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
240 ImGui::SetNextWindowSize(ImVec2(width, height), ImGuiCond_Always);
246 ImGui::SetNextWindowCollapsed(
false);
252 ImGuiWindowFlags window_flags =
253 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize |
254 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus |
255 ImGuiWindowFlags_NoTitleBar;
257 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20, 20));
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();
265 struct TriforceConfig {
269 float repel_distance;
272 TriforceConfig triforce_configs[] = {
273 {0.08f, 0.12f, 36.0f, 0.025f, 50.0f},
274 {0.92f, 0.15f, 34.0f, 0.022f, 50.0f},
275 {0.06f, 0.88f, 32.0f, 0.020f, 45.0f},
276 {0.94f, 0.85f, 34.0f, 0.023f, 50.0f},
277 {0.50f, 0.08f, 38.0f, 0.028f, 55.0f},
278 {0.50f, 0.92f, 32.0f, 0.020f, 45.0f},
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;
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;
300 float time_offset = i * 1.2f;
301 float float_speed_x =
303 float float_speed_y =
305 float float_amount_x = (20.0f + (i % 2) * 10.0f) *
307 float float_amount_y =
311 float float_x = std::sin(
animation_time_ * float_speed_x + time_offset) *
320 float dist = std::sqrt(dx * dx + dy * dy);
328 target_pos.x += float_x;
329 target_pos.y += float_y;
334 float dir_x = dx / dist;
335 float dir_y = dy / dist;
338 float normalized_dist = dist / repel_radius;
339 float repel_strength = (1.0f - normalized_dist * normalized_dist) *
340 triforce_configs[i].repel_distance;
342 target_pos.x += dir_x * repel_strength;
343 target_pos.y += dir_y * repel_strength;
355 float adjusted_alpha =
357 float adjusted_size =
360 adjusted_size, adjusted_alpha, 0.0f);
366 static float spawn_accumulator = 0.0f;
368 while (spawn_accumulator >= 1.0f &&
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);
391 spawn_accumulator -= 1.0f;
395 float dt = ImGui::GetIO().DeltaTime;
416 ImU32 particle_color =
417 ImGui::GetColorU32(ImVec4(1.0f, 0.843f, 0.0f, alpha));
418 bg_draw_list->AddCircleFilled(
particles_[i].position,
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;
436 ImVec4 blue_faded = kMasterSwordBlue;
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));
443 ImGui::Dummy(ImVec2(0, 10));
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;
452 std::clamp(content_height * 0.45f, 260.0f, content_height);
453 ImGui::BeginChild(
"LeftPanel", ImVec2(0, left_height),
true,
454 ImGuiWindowFlags_NoScrollbar);
458 ImVec2 sep_start = ImGui::GetCursorScreenPos();
461 ImVec2(sep_start.x + ImGui::GetContentRegionAvail().x, sep_start.y),
463 ImVec4(kTriforceGold.x, kTriforceGold.y, kTriforceGold.z, 0.2f)),
466 ImGui::Dummy(ImVec2(0, 5));
472 ImGui::BeginChild(
"RightPanel", ImVec2(0, 0),
true);
476 sep_start = ImGui::GetCursorScreenPos();
479 ImVec2(sep_start.x + ImGui::GetContentRegionAvail().x, sep_start.y),
480 ImGui::GetColorU32(ImVec4(kMasterSwordBlue.x, kMasterSwordBlue.y,
481 kMasterSwordBlue.z, 0.2f)),
484 ImGui::Dummy(ImVec2(0, 5));
488 ImGui::BeginChild(
"LeftPanel",
489 ImVec2(ImGui::GetContentRegionAvail().x * 0.3f, 0),
490 true, ImGuiWindowFlags_NoScrollbar);
494 ImVec2 sep_start = ImGui::GetCursorScreenPos();
497 ImVec2(sep_start.x + ImGui::GetContentRegionAvail().x, sep_start.y),
499 ImVec4(kTriforceGold.x, kTriforceGold.y, kTriforceGold.z, 0.2f)),
502 ImGui::Dummy(ImVec2(0, 5));
508 ImGui::BeginChild(
"RightPanel", ImVec2(0, 0),
true);
512 sep_start = ImGui::GetCursorScreenPos();
515 ImVec2(sep_start.x + ImGui::GetContentRegionAvail().x, sep_start.y),
516 ImGui::GetColorU32(ImVec4(kMasterSwordBlue.x, kMasterSwordBlue.y,
517 kMasterSwordBlue.z, 0.2f)),
520 ImGui::Dummy(ImVec2(0, 5));
528 ImVec2 footer_start = ImGui::GetCursorScreenPos();
529 ImVec2 footer_end(footer_start.x + ImGui::GetContentRegionAvail().x,
531 ImVec4 red_faded = kHeartRed;
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));
540 ImGui::Dummy(ImVec2(0, 5));
545 ImGui::PopStyleVar();
615 ImDrawList* draw_list = ImGui::GetWindowDrawList();
617 ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[2]);
621 auto windowWidth = ImGui::GetWindowSize().x;
622 auto textWidth = ImGui::CalcTextSize(title).x;
623 float xPos = (windowWidth - textWidth) * 0.5f;
625 ImGui::SetCursorPosX(xPos);
626 ImVec2 text_pos = ImGui::GetCursorScreenPos();
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,
637 ImGui::TextColored(kTriforceGold,
"%s", title);
641 const char* subtitle =
"Yet Another Zelda3 Editor";
642 textWidth = ImGui::CalcTextSize(subtitle).x;
643 ImGui::SetCursorPosX((windowWidth - textWidth) * 0.5f);
645 ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f),
"%s", subtitle);
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());
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);
807 const ImVec2& card_size) {
816 ImVec2 resolved_card_size = card_size;
817 ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
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;
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;
840 ImDrawList* draw_list = ImGui::GetWindowDrawList();
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(
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);
854 ImVec4 border_color_base = (index % 3 == 0) ? kHyruleGreen
855 : (index % 3 == 1) ? kMasterSwordBlue
857 ImU32 border_color = ImGui::GetColorU32(ImVec4(
858 border_color_base.x, border_color_base.y, border_color_base.z, 0.5f));
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);
866 ImGui::SetCursorScreenPos(cursor_pos);
867 ImGui::InvisibleButton(absl::StrFormat(
"ProjectPanel_%d", index).c_str(),
869 bool is_hovered = ImGui::IsItemHovered();
870 bool is_clicked = ImGui::IsItemClicked();
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),
886 ImVec2 content_pos(cursor_pos.x + padding, cursor_pos.y + padding);
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);
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);
899 ImGui::PopStyleColor();
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]);
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) +
"...";
915 ImGui::TextColored(kTriforceGold,
"%s", short_name.c_str());
917 ImGui::PopTextWrapPos();
920 ImGui::SetCursorScreenPos(
921 ImVec2(content_pos.x + padding * 0.5f, content_pos.y + rom_offset_y));
922 ImGui::PushStyleColor(ImGuiCol_Text, text_secondary);
924 ImGui::PopStyleColor();
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) {
937 "..." + short_path.substr(short_path.length() - (max_path_chars - 3));
940 ImGui::PopStyleColor();
944 ImGui::BeginTooltip();
945 ImGui::TextColored(kMasterSwordBlue,
ICON_MD_INFO " Project Details");
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());