227 const ImGuiStyle& style = ImGui::GetStyle();
228 const float avail_width = ImGui::GetContentRegionAvail().x;
229 const float min_width = kDashboardRecentBaseWidth;
230 const float max_width =
231 kDashboardRecentBaseWidth * kDashboardRecentWidthMaxFactor;
233 std::max(kDashboardRecentBaseHeight, ImGui::GetFrameHeight());
234 const float spacing = style.ItemSpacing.x;
235 const bool stack_items = avail_width < min_width * 1.6f;
236 FlowLayout row_layout{};
238 row_layout.columns = 1;
239 row_layout.item_width = avail_width;
240 row_layout.item_height = height;
241 row_layout.spacing = spacing;
243 row_layout = ComputeFlowLayout(avail_width, min_width, max_width, height,
244 height, height / std::max(min_width, 1.0f),
245 spacing, kDashboardMaxRecentColumns,
249 ImGuiTableFlags table_flags =
250 ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoPadOuterX;
251 const ImVec2 cell_padding(row_layout.spacing * 0.5f,
252 style.ItemSpacing.y * 0.4f);
253 ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cell_padding);
254 if (ImGui::BeginTable(
"DashboardRecentGrid", row_layout.columns,
258 auto it = std::find_if(
260 [type](
const EditorInfo& info) { return info.type == type; });
266 ImGui::TableNextColumn();
268 const ImVec4 base_color = GetEditorAccentColor(it->type, theme);
269 ImGui::PushStyleColor(ImGuiCol_Button,
270 ScaleColor(base_color, 0.5f, 0.7f));
271 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
272 ScaleColor(base_color, 0.7f, 0.9f));
273 ImGui::PushStyleColor(ImGuiCol_ButtonActive, WithAlpha(base_color, 1.0f));
276 stack_items ? avail_width : row_layout.item_width,
277 row_layout.item_height > 0.0f ? row_layout.item_height : height);
278 if (ImGui::Button(absl::StrCat(it->icon,
" ", it->name).c_str(),
287 ImGui::PopStyleColor(3);
289 if (ImGui::IsItemHovered()) {
290 ImGui::SetTooltip(
"%s", it->description.c_str());
295 ImGui::PopStyleVar();
302 const ImGuiStyle& style = ImGui::GetStyle();
303 const float avail_width = ImGui::GetContentRegionAvail().x;
304 const float min_width =
305 kDashboardCardBaseWidth * kDashboardCardMinWidthFactor;
306 const float max_width =
307 kDashboardCardBaseWidth * kDashboardCardWidthMaxFactor;
308 const float min_height =
309 std::max(kDashboardCardBaseHeight * kDashboardCardMinHeightFactor,
310 ImGui::GetFrameHeight() * 3.2f);
311 const float max_height =
312 kDashboardCardBaseHeight * kDashboardCardHeightMaxFactor;
313 const float aspect_ratio =
314 kDashboardCardBaseHeight / std::max(kDashboardCardBaseWidth, 1.0f);
315 const float spacing = style.ItemSpacing.x;
317 FlowLayout layout = ComputeFlowLayout(
318 avail_width, min_width, max_width, min_height, max_height, aspect_ratio,
319 spacing, kDashboardMaxColumns,
static_cast<int>(
editors_.size()));
321 ImGuiTableFlags table_flags =
322 ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoPadOuterX;
323 const ImVec2 cell_padding(layout.spacing * 0.5f, style.ItemSpacing.y * 0.5f);
324 ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cell_padding);
325 if (ImGui::BeginTable(
"DashboardEditorGrid", layout.columns, table_flags)) {
326 for (
size_t i = 0; i <
editors_.size(); ++i) {
327 ImGui::TableNextColumn();
329 ImVec2(layout.item_width, layout.item_height));
333 ImGui::PopStyleVar();
337 const ImVec2& card_size) {
338 ImGui::PushID(index);
341 const ImVec4 base_color = GetEditorAccentColor(info.
type, theme);
345 ImFont* text_font = ImGui::GetFont();
346 const float text_font_size = ImGui::GetFontSize();
348 const ImGuiStyle& style = ImGui::GetStyle();
349 const float line_height = ImGui::GetTextLineHeight();
350 const float padding_x = std::max(style.FramePadding.x, card_size.x * 0.06f);
351 const float padding_y = std::max(style.FramePadding.y, card_size.y * 0.08f);
353 const float footer_height = info.
shortcut.empty() ? 0.0f : line_height;
354 const float footer_spacing =
355 info.
shortcut.empty() ? 0.0f : style.ItemSpacing.y;
356 const float available_icon_height = card_size.y - padding_y * 2.0f -
357 line_height - footer_height -
359 const float min_icon_radius = line_height * 0.9f;
360 float max_icon_radius = card_size.y * 0.24f;
361 max_icon_radius = std::max(max_icon_radius, min_icon_radius);
362 const float icon_radius = std::clamp(available_icon_height * 0.5f,
363 min_icon_radius, max_icon_radius);
365 const ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
366 ImDrawList* draw_list = ImGui::GetWindowDrawList();
367 const ImVec2 icon_center(cursor_pos.x + card_size.x * 0.5f,
368 cursor_pos.y + padding_y + icon_radius);
369 float title_y = icon_center.y + icon_radius + style.ItemSpacing.y;
370 const float footer_y = cursor_pos.y + card_size.y - padding_y - footer_height;
371 if (title_y + line_height > footer_y - style.ItemSpacing.y) {
372 title_y = footer_y - line_height - style.ItemSpacing.y;
379 ImU32 color_top = ImGui::GetColorU32(ScaleColor(base_color, 0.4f, 0.85f));
380 ImU32 color_bottom = ImGui::GetColorU32(ScaleColor(base_color, 0.2f, 0.9f));
383 draw_list->AddRectFilledMultiColor(
385 ImVec2(cursor_pos.x + card_size.x, cursor_pos.y + card_size.y), color_top,
386 color_top, color_bottom, color_bottom);
390 is_recent ? ImGui::GetColorU32(WithAlpha(base_color, 1.0f))
391 : ImGui::GetColorU32(ScaleColor(base_color, 0.6f, 0.7f));
392 const float rounding = std::max(style.FrameRounding, card_size.y * 0.05f);
393 const float border_thickness =
394 is_recent ? std::max(2.0f, style.FrameBorderSize + 1.0f)
395 : std::max(1.0f, style.FrameBorderSize);
398 ImVec2(cursor_pos.x + card_size.x, cursor_pos.y + card_size.y),
399 border_color, rounding, 0, border_thickness);
403 const float badge_radius =
404 std::clamp(line_height * 0.6f, line_height * 0.4f, line_height);
405 ImVec2 badge_pos(cursor_pos.x + card_size.x - padding_x - badge_radius,
406 cursor_pos.y + padding_y + badge_radius);
407 draw_list->AddCircleFilled(badge_pos, badge_radius,
408 ImGui::GetColorU32(base_color), 16);
409 const ImU32 star_color = ImGui::GetColorU32(text_primary);
410 const ImVec2 star_size =
411 text_font->CalcTextSizeA(text_font_size, FLT_MAX, 0.0f,
ICON_MD_STAR);
412 const ImVec2 star_pos(badge_pos.x - star_size.x * 0.5f,
413 badge_pos.y - star_size.y * 0.5f);
414 draw_list->AddText(text_font, text_font_size, star_pos, star_color,
419 ImVec4 button_bg = ImGui::GetStyleColorVec4(ImGuiCol_Button);
421 ImGui::PushStyleColor(ImGuiCol_Button, button_bg);
422 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
423 ScaleColor(base_color, 0.3f, 0.5f));
424 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
425 ScaleColor(base_color, 0.5f, 0.7f));
428 ImGui::Button(absl::StrCat(
"##", info.
name).c_str(), card_size);
429 bool is_hovered = ImGui::IsItemHovered();
431 ImGui::PopStyleColor(3);
434 ImU32 icon_bg = ImGui::GetColorU32(base_color);
435 draw_list->AddCircleFilled(icon_center, icon_radius, icon_bg, 32);
438 ImFont* icon_font = ImGui::GetFont();
439 if (ImGui::GetIO().Fonts->Fonts.size() > 2 &&
440 card_size.y >= kDashboardCardBaseHeight) {
441 icon_font = ImGui::GetIO().Fonts->Fonts[2];
442 }
else if (ImGui::GetIO().Fonts->Fonts.size() > 1) {
443 icon_font = ImGui::GetIO().Fonts->Fonts[1];
445 ImGui::PushFont(icon_font);
446 const float icon_font_size = ImGui::GetFontSize();
447 const ImVec2 icon_size = icon_font->CalcTextSizeA(icon_font_size, FLT_MAX,
448 0.0f, info.
icon.c_str());
450 const ImVec2 icon_text_pos(icon_center.x - icon_size.x * 0.5f,
451 icon_center.y - icon_size.y * 0.5f);
452 draw_list->AddText(icon_font, icon_font_size, icon_text_pos,
453 ImGui::GetColorU32(text_primary), info.
icon.c_str());
456 const ImVec2 name_size = text_font->CalcTextSizeA(text_font_size, FLT_MAX,
457 0.0f, info.
name.c_str());
458 float name_x = cursor_pos.x + (card_size.x - name_size.x) * 0.5f;
459 const float name_min_x = cursor_pos.x + padding_x;
460 const float name_max_x = cursor_pos.x + card_size.x - padding_x;
461 name_x = std::clamp(name_x, name_min_x, name_max_x);
462 const ImVec2 name_pos(name_x, title_y);
463 const ImVec4 name_clip(name_min_x, cursor_pos.y + padding_y, name_max_x,
465 draw_list->AddText(text_font, text_font_size, name_pos,
466 ImGui::GetColorU32(base_color), info.
name.c_str(),
nullptr,
471 const ImVec2 shortcut_pos(cursor_pos.x + padding_x, footer_y);
472 const ImVec4 shortcut_clip(cursor_pos.x + padding_x, footer_y,
473 cursor_pos.x + card_size.x - padding_x,
474 cursor_pos.y + card_size.y - padding_y);
475 draw_list->AddText(text_font, text_font_size, shortcut_pos,
476 ImGui::GetColorU32(text_secondary),
477 info.
shortcut.c_str(),
nullptr, 0.0f, &shortcut_clip);
482 ImU32 glow_color = ImGui::GetColorU32(ScaleColor(base_color, 1.0f, 0.18f));
483 draw_list->AddRectFilled(
485 ImVec2(cursor_pos.x + card_size.x, cursor_pos.y + card_size.y),
486 glow_color, rounding);
491 const float tooltip_width = std::clamp(card_size.x * 1.4f, 240.0f, 340.0f);
492 ImGui::SetNextWindowSize(ImVec2(tooltip_width, 0), ImGuiCond_Always);
493 ImGui::BeginTooltip();
494 ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
495 ImGui::TextColored(base_color,
"%s %s", info.
icon.c_str(),
499 ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + tooltip_width - 20.0f);
500 ImGui::TextWrapped(
"%s", info.
description.c_str());
501 ImGui::PopTextWrapPos();
509 ImGui::TextColored(accent,
ICON_MD_STAR " Recently used");