Achievements: Don't use visibility for leaderboard paging

Fixes leaderboards not being scrollable with controller only navigation.
This commit is contained in:
Stenzek
2026-01-16 00:37:05 +10:00
parent f372a92246
commit d02df7cf4b
3 changed files with 93 additions and 37 deletions

View File

@@ -128,9 +128,10 @@ static bool OpenLeaderboardById(u32 leaderboard_id);
static void FetchNextLeaderboardEntries();
static void CloseLeaderboard();
static void DrawLeaderboardListEntry(const rc_client_leaderboard_t* lboard);
static void DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& entry, u32 index, bool is_self,
static bool DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& entry, u32 index, bool is_self,
float rank_column_width, float name_column_width, float time_column_width,
float column_spacing, std::time_t current_time, const std::tm& current_tm);
static void DrawLeaderboardLoadingIndicator(float pos_y, float avail_height, bool short_text);
static SmallString FormatRelativeTimestamp(std::time_t timestamp, std::time_t current_time, const std::tm& current_tm);
namespace {
@@ -425,23 +426,8 @@ void FullscreenUI::DrawNotifications(NotificationLayout& layout)
if (notif.note == Achievements::NOTIFICATION_SPINNER_NOTE)
{
// based on https://github.com/ocornut/imgui/issues/1901
static constexpr u32 num_segments = 30;
const float radius = ImFloor(note_size.x * 0.5f);
const float thickness = LayoutScale(4.0f);
const ImVec2 pos = ImVec2((box_min.x + box_width) - horizontal_padding - note_size.x + radius,
box_min.y + vertical_padding + radius);
const float start =
std::abs(ImSin(static_cast<float>(GImGui->Time * 1.8f)) * static_cast<float>(num_segments - 5));
const float a_min = IM_PI * 2.0f * start / static_cast<float>(num_segments);
const float a_max = IM_PI * 2.0f * (static_cast<float>(num_segments - 3) / static_cast<float>(num_segments));
for (u32 i = 0; i < num_segments; i++)
{
const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
dl->PathLineTo(ImVec2(pos.x + ImCos(static_cast<float>(a + GImGui->Time * 8.0f)) * radius,
pos.y + ImSin(static_cast<float>(a + GImGui->Time * 8.0f)) * radius));
}
dl->PathStroke(title_col, false, thickness);
DrawSpinner(dl, ImVec2((box_min.x + box_width) - horizontal_padding - note_size.x, box_min.y + vertical_padding),
title_col, note_size.x, LayoutScale(4.0f));
}
else if (!notif.note.empty())
{
@@ -2372,26 +2358,29 @@ void FullscreenUI::DrawLeaderboardsWindow()
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f));
ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 0.0f);
u32 total_count = 0;
u32 last_visible_count = 0;
if (!s_achievements_locals.is_showing_all_leaderboard_entries)
{
if (s_achievements_locals.leaderboard_nearby_entries)
{
for (u32 i = 0; i < s_achievements_locals.leaderboard_nearby_entries->num_entries; i++)
{
DrawLeaderboardEntry(s_achievements_locals.leaderboard_nearby_entries->entries[i], i,
static_cast<s32>(i) == s_achievements_locals.leaderboard_nearby_entries->user_index,
rank_column_width, name_column_width, time_column_width, column_spacing, current_time,
current_tm.value());
total_count++;
if (DrawLeaderboardEntry(s_achievements_locals.leaderboard_nearby_entries->entries[i], i,
static_cast<s32>(i) ==
s_achievements_locals.leaderboard_nearby_entries->user_index,
rank_column_width, name_column_width, time_column_width, column_spacing,
current_time, current_tm.value()))
{
last_visible_count = total_count;
}
}
}
else
{
const ImVec2 pos_min(0.0f, heading_height);
const ImVec2 pos_max(display_size.x, display_size.y);
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumLargeFontSize, UIStyle.BoldFontWeight, pos_min, pos_max,
text_color,
TRANSLATE_SV("Achievements", "Downloading leaderboard data, please wait..."),
nullptr, ImVec2(0.5f, 0.5f), 0.0f);
DrawLeaderboardLoadingIndicator(heading_height, display_size.y - heading_height, false);
}
}
else
@@ -2400,19 +2389,29 @@ void FullscreenUI::DrawLeaderboardsWindow()
{
for (u32 i = 0; i < list->num_entries; i++)
{
DrawLeaderboardEntry(list->entries[i], i, static_cast<s32>(i) == list->user_index, rank_column_width,
name_column_width, time_column_width, column_spacing, current_time,
current_tm.value());
total_count++;
if (DrawLeaderboardEntry(list->entries[i], i, static_cast<s32>(i) == list->user_index, rank_column_width,
name_column_width, time_column_width, column_spacing, current_time,
current_tm.value()))
{
last_visible_count = total_count;
}
}
}
if (!s_achievements_locals.has_fetched_all_leaderboard_entries)
{
bool visible;
text.format(ICON_FA_HOURGLASS_HALF " {}", TRANSLATE_SV("Achievements", "Loading..."));
MenuButtonWithVisibilityQuery(text, text, {}, {}, &visible, false);
if (visible && !s_achievements_locals.leaderboard_fetch_handle)
// if showing the last few, fetch the next batch
if ((total_count - last_visible_count) <= 3 && !s_achievements_locals.leaderboard_fetch_handle)
FetchNextLeaderboardEntries();
// show the loading indicator in the bottom-right
if (s_achievements_locals.leaderboard_fetch_handle)
{
const ImRect& win_rc = ImGui::GetCurrentWindow()->InnerRect;
DrawLeaderboardLoadingIndicator(win_rc.Min.y, win_rc.GetHeight(), true);
}
}
}
@@ -2449,7 +2448,7 @@ void FullscreenUI::DrawLeaderboardsWindow()
}
}
void FullscreenUI::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& entry, u32 index, bool is_self,
bool FullscreenUI::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& entry, u32 index, bool is_self,
float rank_column_width, float name_column_width, float time_column_width,
float column_spacing, std::time_t current_time, const std::tm& current_tm)
{
@@ -2457,7 +2456,7 @@ void FullscreenUI::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& ent
bool visible, hovered;
bool pressed = MenuButtonFrame(entry.user, UIStyle.MediumLargeFontSize, true, &bb, &visible, &hovered);
if (!visible)
return;
return false;
const float midpoint = bb.Min.y + UIStyle.MediumLargeFontSize + LayoutScale(4.0f);
float text_start_x = bb.Min.x;
@@ -2536,6 +2535,44 @@ void FullscreenUI::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& ent
INFO_LOG("Opening profile details: {}", url);
Host::OpenURL(url);
}
return true;
}
void FullscreenUI::DrawLeaderboardLoadingIndicator(float pos_y, float avail_height, bool short_text)
{
static constexpr const float& font_weight = UIStyle.BoldFontWeight;
const float font_size = short_text ? UIStyle.MediumFontSize : UIStyle.MediumLargeFontSize;
const u32 color = ImGui::GetColorU32(DarkerColor(ImGui::GetStyle().Colors[ImGuiCol_Text], 0.9f));
const std::string_view text = short_text ?
TRANSLATE_SV("Achievements", "Loading...") :
TRANSLATE_SV("Achievements", "Downloading leaderboard data, please wait...");
const ImVec2 text_size = UIStyle.Font->CalcTextSizeA(font_size, font_weight, FLT_MAX, 0.0f, IMSTR_START_END(text));
const float spinner_size = font_size;
const float spinner_spacing = short_text ? LayoutScale(12.0f) : LayoutScale(16.0f);
const float total_width = spinner_size + spinner_spacing + text_size.x;
const float display_width = ImGui::GetIO().DisplaySize.x;
// position in right side of screen if short text, center otherwise
const ImVec2 pos = short_text ?
ImVec2((display_width - total_width) - LayoutScale(25.0f),
pos_y + avail_height - font_size - LayoutScale(10.0f)) :
ImVec2((display_width - total_width) * 0.5f, pos_y + (avail_height - font_size) * 0.5f);
// for short text, draw a background box
if (short_text)
{
const ImVec2 padding = ImVec2(LayoutScale(10.0f), LayoutScale(6.0f));
ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x - padding.x, pos.y - padding.y),
ImVec2(pos.x + total_width + padding.x, pos.y + font_size + padding.y),
ImGui::GetColorU32(ModAlpha(UIStyle.PopupBackgroundColor, 0.8f)),
LayoutScale(LAYOUT_MENU_ITEM_BORDER_ROUNDING));
}
DrawSpinner(ImGui::GetWindowDrawList(), pos, color, spinner_size, LayoutScale(3.0f));
const ImVec2 text_pos = ImVec2(pos.x + spinner_size + spinner_spacing, pos.y);
RenderShadowedTextClipped(UIStyle.Font, font_size, UIStyle.BoldFontWeight, text_pos, text_pos + text_size, color,
text, &text_size);
}
SmallString FullscreenUI::FormatRelativeTimestamp(time_t timestamp, time_t current_time, const std::tm& current_tm)

View File

@@ -1351,6 +1351,24 @@ void FullscreenUI::DrawRoundedGradientRect(ImDrawList* const dl, const ImVec2& p
ImDrawFlags_RoundCornersRight);
}
void FullscreenUI::DrawSpinner(ImDrawList* const dl, const ImVec2& pos, ImU32 color, float size, float thickness)
{
// based off https://github.com/ocornut/imgui/issues/1901
static constexpr u32 num_segments = 30;
const float radius = ImFloor(size * 0.5f);
const ImVec2 center = ImVec2(pos.x + radius, pos.y + radius);
const float start = std::abs(ImSin(static_cast<float>(GImGui->Time * 1.8f)) * static_cast<float>(num_segments - 5));
const float a_min = IM_PI * 2.0f * start / static_cast<float>(num_segments);
const float a_max = IM_PI * 2.0f * (static_cast<float>(num_segments - 3) / static_cast<float>(num_segments));
for (u32 i = 0; i < num_segments; i++)
{
const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
dl->PathLineTo(ImVec2(center.x + ImCos(static_cast<float>(a + GImGui->Time * 8.0f)) * radius,
center.y + ImSin(static_cast<float>(a + GImGui->Time * 8.0f)) * radius));
}
dl->PathStroke(color, false, thickness);
}
bool FullscreenUI::BeginFullscreenColumns(const char* title, float pos_y, bool expand_to_screen_width, bool footer)
{
ImGui::SetNextWindowPos(ImVec2(expand_to_screen_width ? 0.0f : UIStyle.LayoutPaddingLeft, pos_y));

View File

@@ -320,6 +320,7 @@ void PopPrimaryColor();
void DrawRoundedGradientRect(ImDrawList* const dl, const ImVec2& pos_min, const ImVec2& pos_max, ImU32 col_left,
ImU32 col_right, float rounding);
void DrawSpinner(ImDrawList* const dl, const ImVec2& pos, ImU32 color, float size, float thickness);
void DrawWindowTitle(std::string_view title);