300 const ImGuiStyle& style = ImGui::GetStyle();
301 const float avail_width = ImGui::GetContentRegionAvail().x;
302 const float min_width = kDashboardRecentBaseWidth;
303 const float max_width =
304 kDashboardRecentBaseWidth * kDashboardRecentWidthMaxFactor;
308 const float height = ImGui::GetFrameHeight() + 10.0f * DensityFactor();
309 const float spacing = style.ItemSpacing.x;
310 const bool stack_items = avail_width < min_width * 1.6f;
311 FlowLayout row_layout{};
313 row_layout.columns = 1;
314 row_layout.item_width = avail_width;
315 row_layout.item_height = height;
316 row_layout.spacing = spacing;
318 row_layout = ComputeFlowLayout(avail_width, min_width, max_width, height,
319 height, height / std::max(min_width, 1.0f),
320 spacing, kDashboardMaxRecentColumns,
324 ImGuiTableFlags table_flags =
325 ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoPadOuterX;
326 const ImVec2 cell_padding(row_layout.spacing * 0.5f,
327 style.ItemSpacing.y * 0.4f);
329 if (ImGui::BeginTable(
"DashboardRecentGrid", row_layout.columns,
333 auto it = std::find_if(
335 [type](
const EditorInfo& info) { return info.type == type; });
341 ImGui::TableNextColumn();
343 const bool enabled =
has_rom_ || !it->requires_rom;
344 const float alpha = enabled ? 1.0f : 0.35f;
345 const ImVec4 base_color = GetEditorAccentColor(it->type, theme);
347 {{ImGuiCol_Button, ScaleColor(base_color, 0.5f, 0.7f * alpha)},
348 {ImGuiCol_ButtonHovered, ScaleColor(base_color, 0.7f, 0.9f * alpha)},
349 {ImGuiCol_ButtonActive, WithAlpha(base_color, 1.0f * alpha)}});
352 stack_items ? avail_width : row_layout.item_width,
353 row_layout.item_height > 0.0f ? row_layout.item_height : height);
355 ImGui::BeginDisabled();
357 if (ImGui::Button(absl::StrCat(it->icon,
" ", it->name).c_str(),
367 ImGui::EndDisabled();
370 if (ImGui::IsItemHovered()) {
372 ImGui::SetTooltip(tr(
"Load a ROM to open %s"), it->name.c_str());
374 ImGui::SetTooltip(
"%s", it->description.c_str());
386 const ImGuiStyle& style = ImGui::GetStyle();
387 const float avail_width = ImGui::GetContentRegionAvail().x;
388 const float scale = ImGui::GetFontSize() / 16.0f;
391 const float compact_scale =
392 (avail_width < 620.0f ? 0.85f : 1.0f) * DensityFactor();
393 const float min_width = kDashboardCardBaseWidth *
394 kDashboardCardMinWidthFactor * scale * compact_scale;
395 const float max_width = kDashboardCardBaseWidth *
396 kDashboardCardWidthMaxFactor * scale * compact_scale;
397 const float min_height =
398 std::max(kDashboardCardBaseHeight * kDashboardCardMinHeightFactor *
399 scale * compact_scale,
400 ImGui::GetFrameHeight() * 3.2f);
401 const float max_height = kDashboardCardBaseHeight *
402 kDashboardCardHeightMaxFactor * scale *
404 const float aspect_ratio =
405 kDashboardCardBaseHeight / std::max(kDashboardCardBaseWidth, 1.0f);
406 const float spacing = style.ItemSpacing.x;
408 FlowLayout layout = ComputeFlowLayout(
409 avail_width, min_width, max_width, min_height, max_height, aspect_ratio,
410 spacing, kDashboardMaxColumns,
static_cast<int>(
editors_.size()));
412 ImGuiTableFlags table_flags =
413 ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoPadOuterX;
414 const ImVec2 cell_padding(layout.spacing * 0.5f, style.ItemSpacing.y * 0.5f);
416 if (ImGui::BeginTable(
"DashboardEditorGrid", layout.columns, table_flags)) {
417 for (
size_t i = 0; i <
editors_.size(); ++i) {
418 ImGui::TableNextColumn();
421 ImVec2(layout.item_width, layout.item_height), enabled);
428 const ImVec2& card_size,
bool enabled) {
429 ImGui::PushID(index);
432 const float disabled_alpha = enabled ? 1.0f : 0.35f;
433 const ImVec4 base_color = GetEditorAccentColor(info.
type, theme);
437 text_primary.w *= enabled ? 1.0f : 0.5f;
438 text_secondary.w *= enabled ? 1.0f : 0.5f;
439 ImFont* text_font = ImGui::GetFont();
440 const float text_font_size = ImGui::GetFontSize();
442 const ImGuiStyle& style = ImGui::GetStyle();
443 const float line_height = ImGui::GetTextLineHeight();
444 const float padding_x = std::max(style.FramePadding.x, card_size.x * 0.06f);
445 const float padding_y = std::max(style.FramePadding.y, card_size.y * 0.08f);
447 const float footer_height = info.
shortcut.empty() ? 0.0f : line_height;
448 const float footer_spacing =
449 info.
shortcut.empty() ? 0.0f : style.ItemSpacing.y;
450 const float available_icon_height = card_size.y - padding_y * 2.0f -
451 line_height - footer_height -
453 const float min_icon_radius = line_height * 0.9f;
454 float max_icon_radius = card_size.y * 0.24f;
455 max_icon_radius = std::max(max_icon_radius, min_icon_radius);
456 const float icon_radius = std::clamp(available_icon_height * 0.5f,
457 min_icon_radius, max_icon_radius);
459 const ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
460 ImDrawList* draw_list = ImGui::GetWindowDrawList();
461 const ImVec2 icon_center(cursor_pos.x + card_size.x * 0.5f,
462 cursor_pos.y + padding_y + icon_radius);
463 float title_y = icon_center.y + icon_radius + style.ItemSpacing.y;
464 const float footer_y = cursor_pos.y + card_size.y - padding_y - footer_height;
465 if (title_y + line_height > footer_y - style.ItemSpacing.y) {
466 title_y = footer_y - line_height - style.ItemSpacing.y;
474 ImGui::GetColorU32(ScaleColor(base_color, 0.4f, 0.85f * disabled_alpha));
476 ImGui::GetColorU32(ScaleColor(base_color, 0.2f, 0.9f * disabled_alpha));
479 draw_list->AddRectFilledMultiColor(
481 ImVec2(cursor_pos.x + card_size.x, cursor_pos.y + card_size.y), color_top,
482 color_top, color_bottom, color_bottom);
487 ? ImGui::GetColorU32(WithAlpha(base_color, 1.0f * disabled_alpha))
488 : ImGui::GetColorU32(
489 ScaleColor(base_color, 0.6f, 0.7f * disabled_alpha));
490 const float rounding = std::max(style.FrameRounding, card_size.y * 0.05f);
491 const float border_thickness =
492 is_recent ? std::max(2.0f, style.FrameBorderSize + 1.0f)
493 : std::max(1.0f, style.FrameBorderSize);
496 ImVec2(cursor_pos.x + card_size.x, cursor_pos.y + card_size.y),
497 border_color, rounding, 0, border_thickness);
501 const float badge_radius =
502 std::clamp(line_height * 0.6f, line_height * 0.4f, line_height);
503 ImVec2 badge_pos(cursor_pos.x + card_size.x - padding_x - badge_radius,
504 cursor_pos.y + padding_y + badge_radius);
505 draw_list->AddCircleFilled(badge_pos, badge_radius,
506 ImGui::GetColorU32(base_color), 16);
507 const ImU32 star_color = ImGui::GetColorU32(text_primary);
508 const ImVec2 star_size =
509 text_font->CalcTextSizeA(text_font_size, FLT_MAX, 0.0f,
ICON_MD_STAR);
510 const ImVec2 star_pos(badge_pos.x - star_size.x * 0.5f,
511 badge_pos.y - star_size.y * 0.5f);
512 draw_list->AddText(text_font, text_font_size, star_pos, star_color,
517 ImVec4 button_bg = ImGui::GetStyleColorVec4(ImGuiCol_Button);
520 {{ImGuiCol_Button, button_bg},
521 {ImGuiCol_ButtonHovered,
522 ScaleColor(base_color, 0.3f, enabled ? 0.5f : 0.2f)},
523 {ImGuiCol_ButtonActive,
524 ScaleColor(base_color, 0.5f, enabled ? 0.7f : 0.2f)}});
527 ImGui::BeginDisabled();
530 ImGui::Button(absl::StrCat(
"##", info.
name).c_str(), card_size);
532 ImGui::EndDisabled();
534 bool is_hovered = ImGui::IsItemHovered();
538 ImGui::GetColorU32(WithAlpha(base_color, 1.0f * disabled_alpha));
539 draw_list->AddCircleFilled(icon_center, icon_radius, icon_bg, 32);
542 ImFont* icon_font = ImGui::GetFont();
543 if (ImGui::GetIO().Fonts->Fonts.size() > 2 &&
544 card_size.y >= kDashboardCardBaseHeight) {
545 icon_font = ImGui::GetIO().Fonts->Fonts[2];
546 }
else if (ImGui::GetIO().Fonts->Fonts.size() > 1) {
547 icon_font = ImGui::GetIO().Fonts->Fonts[1];
549 ImGui::PushFont(icon_font);
550 const float icon_font_size = ImGui::GetFontSize();
551 const ImVec2 icon_size = icon_font->CalcTextSizeA(icon_font_size, FLT_MAX,
552 0.0f, info.
icon.c_str());
554 const ImVec2 icon_text_pos(icon_center.x - icon_size.x * 0.5f,
555 icon_center.y - icon_size.y * 0.5f);
556 draw_list->AddText(icon_font, icon_font_size, icon_text_pos,
557 ImGui::GetColorU32(text_primary), info.
icon.c_str());
560 const ImVec2 name_size = text_font->CalcTextSizeA(text_font_size, FLT_MAX,
561 0.0f, info.
name.c_str());
562 float name_x = cursor_pos.x + (card_size.x - name_size.x) * 0.5f;
563 const float name_min_x = cursor_pos.x + padding_x;
564 const float name_max_x = cursor_pos.x + card_size.x - padding_x;
565 name_x = std::clamp(name_x, name_min_x, name_max_x);
566 const ImVec2 name_pos(name_x, title_y);
567 const ImVec4 name_clip(name_min_x, cursor_pos.y + padding_y, name_max_x,
569 draw_list->AddText(text_font, text_font_size, name_pos,
570 ImGui::GetColorU32(WithAlpha(base_color, disabled_alpha)),
571 info.
name.c_str(),
nullptr, 0.0f, &name_clip);
575 const ImVec2 shortcut_pos(cursor_pos.x + padding_x, footer_y);
576 const ImVec4 shortcut_clip(cursor_pos.x + padding_x, footer_y,
577 cursor_pos.x + card_size.x - padding_x,
578 cursor_pos.y + card_size.y - padding_y);
579 draw_list->AddText(text_font, text_font_size, shortcut_pos,
580 ImGui::GetColorU32(text_secondary),
581 info.
shortcut.c_str(),
nullptr, 0.0f, &shortcut_clip);
586 ImU32 glow_color = ImGui::GetColorU32(
587 ScaleColor(base_color, 1.0f, enabled ? 0.18f : 0.08f));
588 draw_list->AddRectFilled(
590 ImVec2(cursor_pos.x + card_size.x, cursor_pos.y + card_size.y),
591 glow_color, rounding);
596 const float tooltip_width = std::clamp(card_size.x * 1.4f, 240.0f, 340.0f);
597 ImGui::SetNextWindowSize(ImVec2(tooltip_width, 0), ImGuiCond_Always);
598 ImGui::BeginTooltip();
599 ImGui::PushFont(
nullptr, HeadingFontSize(1.15f));
600 ImGui::TextColored(WithAlpha(base_color, disabled_alpha),
"%s %s",
601 info.
icon.c_str(), info.
name.c_str());
604 ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + tooltip_width - 20.0f);
606 ImGui::TextWrapped(tr(
"Load a ROM to open this editor."));
608 ImGui::TextWrapped(
"%s", info.
description.c_str());
610 ImGui::PopTextWrapPos();
611 if (enabled && !info.
shortcut.empty()) {
613 ImGui::TextColored(WithAlpha(base_color, disabled_alpha),
618 ImGui::TextColored(accent,
ICON_MD_STAR " Recently used");
623 if (clicked && enabled) {