Skip to content

Commit 5cd5853

Browse files
committed
Achievements: Add type badges to list and wrap long titles
1 parent 02a36c7 commit 5cd5853

File tree

1 file changed

+135
-105
lines changed

1 file changed

+135
-105
lines changed

src/core/fullscreenui_achievements.cpp

Lines changed: 135 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,14 +1086,6 @@ void FullscreenUI::DrawAchievementsWindow()
10861086
}
10871087
EndFullscreenWindow();
10881088

1089-
SetFullscreenStatusText(std::array{
1090-
std::make_pair(ICON_PF_ACHIEVEMENTS_MISSABLE, TRANSLATE_SV("Achievements", "Missable")),
1091-
std::make_pair(ICON_PF_ACHIEVEMENTS_PROGRESSION, TRANSLATE_SV("Achievements", "Progression")),
1092-
std::make_pair(ICON_PF_ACHIEVEMENTS_WIN, TRANSLATE_SV("Achievements", "Win Condition")),
1093-
std::make_pair(ICON_FA_LOCK, TRANSLATE_SV("Achievements", "Locked")),
1094-
std::make_pair(ICON_FA_UNLOCK, TRANSLATE_SV("Achievements", "Unlocked")),
1095-
});
1096-
10971089
if (IsGamepadInputSource())
10981090
{
10991091
if (s_achievements_locals.open_subset)
@@ -1137,159 +1129,197 @@ void FullscreenUI::DrawAchievementsWindow()
11371129

11381130
void FullscreenUI::DrawAchievement(const rc_client_achievement_t* cheevo)
11391131
{
1140-
static constexpr float progress_height_unscaled = 20.0f;
1141-
static constexpr float progress_spacing_unscaled = 5.0f;
1142-
static constexpr float progress_rounding_unscaled = 5.0f;
1132+
static constexpr const float progress_height_unscaled = 20.0f;
1133+
static constexpr const float progress_rounding_unscaled = 5.0f;
1134+
1135+
static constexpr const float& title_font_size = UIStyle.LargeFontSize;
1136+
static constexpr const float& title_font_weight = UIStyle.BoldFontWeight;
1137+
static constexpr const float& subtitle_font_size = UIStyle.MediumFontSize;
1138+
static constexpr const float& subtitle_font_weight = UIStyle.NormalFontWeight;
1139+
static constexpr const float& type_badge_font_size = UIStyle.MediumSmallFontSize;
1140+
static constexpr const float& type_badge_font_weight = UIStyle.BoldFontWeight;
1141+
1142+
const std::string_view title(cheevo->title);
1143+
const std::string_view description = cheevo->description ? std::string_view(cheevo->description) : std::string_view();
1144+
const std::string_view measured_progress(cheevo->measured_progress);
1145+
const bool is_unlocked = (cheevo->state == RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED);
1146+
const bool is_measured = (!is_unlocked && !measured_progress.empty());
1147+
1148+
ImVec2 type_badge_padding;
1149+
ImVec2 type_badge_size;
1150+
float type_badge_spacing = 0.0f;
1151+
float type_badge_rounding = 0.0f;
1152+
ImU32 type_badge_bg_color = 0;
1153+
TinyString type_badge_text;
1154+
switch (cheevo->type)
1155+
{
1156+
case RC_CLIENT_ACHIEVEMENT_TYPE_MISSABLE:
1157+
type_badge_text.format(ICON_PF_ACHIEVEMENTS_MISSABLE " {}", TRANSLATE_SV("Achievements", "Missable"));
1158+
type_badge_bg_color = IM_COL32(205, 45, 32, 255);
1159+
break;
11431160

1144-
const float spacing = LayoutScale(LAYOUT_MENU_ITEM_TITLE_SUMMARY_SPACING);
1145-
const u32 text_color = ImGui::GetColorU32(UIStyle.SecondaryTextColor);
1146-
const u32 summary_color = ImGui::GetColorU32(DarkerColor(UIStyle.SecondaryTextColor));
1147-
const u32 rarity_color = ImGui::GetColorU32(DarkerColor(DarkerColor(UIStyle.SecondaryTextColor)));
1161+
case RC_CLIENT_ACHIEVEMENT_TYPE_PROGRESSION:
1162+
type_badge_text.format(ICON_PF_ACHIEVEMENTS_PROGRESSION " {}", TRANSLATE_SV("Achievements", "Progression"));
1163+
type_badge_bg_color = IM_COL32(13, 71, 161, 255);
1164+
break;
1165+
1166+
case RC_CLIENT_ACHIEVEMENT_TYPE_WIN:
1167+
type_badge_text.format(ICON_PF_ACHIEVEMENTS_PROGRESSION " {}", TRANSLATE_SV("Achievements", "Win Condition"));
1168+
type_badge_bg_color = IM_COL32(50, 110, 30, 255);
1169+
break;
1170+
}
1171+
if (!type_badge_text.empty())
1172+
{
1173+
type_badge_padding = LayoutScale(5.0f, 3.0f);
1174+
type_badge_spacing = LayoutScale(10.0f);
1175+
type_badge_rounding = LayoutScale(3.0f);
1176+
type_badge_size = UIStyle.Font->CalcTextSizeA(type_badge_font_size, type_badge_font_weight, FLT_MAX, 0.0f,
1177+
IMSTR_START_END(type_badge_text));
1178+
type_badge_size += type_badge_padding * 2.0f;
1179+
}
11481180

11491181
const ImVec2 image_size = LayoutScale(50.0f, 50.0f);
1150-
const bool is_unlocked = (cheevo->state == RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED);
1151-
const std::string_view measured_progress(cheevo->measured_progress);
1152-
const bool is_measured = !is_unlocked && !measured_progress.empty();
1153-
const float unlock_rarity_height = spacing + UIStyle.MediumFontSize;
1154-
const ImVec2 points_template_size = UIStyle.Font->CalcTextSizeA(
1155-
UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX, 0.0f, TRANSLATE("Achievements", "XXX points"));
1182+
const float image_right_padding = LayoutScale(15.0f);
11561183
const float avail_width = GetMenuButtonAvailableWidth();
1157-
const size_t summary_length = std::strlen(cheevo->description);
1158-
const float summary_wrap_width = (avail_width - (image_size.x + spacing + spacing) - points_template_size.x);
1159-
const ImVec2 summary_text_size =
1160-
UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX, summary_wrap_width,
1161-
cheevo->description, cheevo->description + summary_length);
1162-
1163-
const float content_height = UIStyle.LargeFontSize + spacing + summary_text_size.y + unlock_rarity_height +
1164-
LayoutScale(is_measured ? progress_height_unscaled : 0.0f) +
1184+
const float spacing = LayoutScale(4.0f);
1185+
const ImVec2 right_side_size = UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX,
1186+
0.0f, TRANSLATE("Achievements", "XXX points"));
1187+
const float max_text_width = avail_width - (image_size.x + image_right_padding + spacing + right_side_size.x);
1188+
const float max_title_width =
1189+
max_text_width - (type_badge_text.empty() ? 0.0f : type_badge_size.x + type_badge_spacing);
1190+
const ImVec2 title_size = UIStyle.Font->CalcTextSizeA(UIStyle.LargeFontSize, UIStyle.BoldFontWeight, FLT_MAX,
1191+
max_title_width, IMSTR_START_END(title));
1192+
const ImVec2 description_size = description.empty() ?
1193+
ImVec2() :
1194+
UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight,
1195+
FLT_MAX, max_text_width, IMSTR_START_END(description));
1196+
const float content_height = (title_size.y + spacing + description_size.y + spacing + UIStyle.MediumFontSize) +
1197+
(is_measured ? (spacing + LayoutScale(progress_height_unscaled)) : 0.0f) +
11651198
LayoutScale(LAYOUT_MENU_ITEM_EXTRA_HEIGHT);
1199+
1200+
SmallString text;
1201+
text.format("chv_{}", cheevo->id);
1202+
11661203
ImRect bb;
11671204
bool visible, hovered;
1168-
const bool clicked =
1169-
MenuButtonFrame(TinyString::from_format("chv_{}", cheevo->id), content_height, true, &bb, &visible, &hovered);
1205+
const bool clicked = MenuButtonFrame(text, content_height, true, &bb, &visible, &hovered);
11701206
if (!visible)
11711207
return;
11721208

1173-
const std::string& badge_path =
1174-
GetCachedAchievementBadgePath(cheevo, cheevo->state != RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED);
1209+
ImDrawList* const dl = ImGui::GetWindowDrawList();
11751210

1176-
if (!badge_path.empty())
1211+
if (const std::string& badge_path = GetCachedAchievementBadgePath(cheevo, !is_unlocked); !badge_path.empty())
11771212
{
11781213
GPUTexture* badge = GetCachedTextureAsync(badge_path);
11791214
if (badge)
11801215
{
11811216
const ImRect image_bb = CenterImage(ImRect(bb.Min, bb.Min + image_size), badge);
1182-
ImGui::GetWindowDrawList()->AddImage(badge, image_bb.Min, image_bb.Max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f),
1183-
IM_COL32(255, 255, 255, 255));
1217+
dl->AddImage(badge, image_bb.Min, image_bb.Max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f),
1218+
IM_COL32(255, 255, 255, 255));
11841219
}
11851220
}
11861221

1187-
SmallString text;
1222+
// make it easier to compute bounding boxes...
1223+
ImVec2 current_pos = ImVec2(bb.Min.x + image_size.x + image_right_padding, bb.Min.y);
11881224

1189-
const float midpoint = bb.Min.y + UIStyle.LargeFontSize + spacing;
1190-
text = TRANSLATE_PLURAL_SSTR("Achievements", "%n points", "Achievement points", cheevo->points);
1191-
const ImVec2 points_size =
1192-
UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX, 0.0f, IMSTR_START_END(text));
1193-
const float points_template_start = bb.Max.x - points_template_size.x;
1194-
const float points_start = points_template_start + ((points_template_size.x - points_size.x) * 0.5f);
1225+
// -- Title --
1226+
const ImRect title_bb(current_pos, current_pos + title_size);
1227+
const u32 text_color = ImGui::GetColorU32(UIStyle.SecondaryTextColor);
1228+
RenderShadowedTextClipped(dl, UIStyle.Font, title_font_size, title_font_weight, title_bb.Min, title_bb.Max,
1229+
text_color, title, &title_size, ImVec2(0.0f, 0.0f), max_title_width, &title_bb);
1230+
current_pos.y += title_size.y + spacing;
11951231

1196-
std::string_view right_icon_text;
1197-
switch (cheevo->type)
1232+
// -- Type Badge --
1233+
if (!type_badge_text.empty())
11981234
{
1199-
case RC_CLIENT_ACHIEVEMENT_TYPE_MISSABLE:
1200-
right_icon_text = ICON_PF_ACHIEVEMENTS_MISSABLE; // Missable
1201-
break;
1202-
1203-
case RC_CLIENT_ACHIEVEMENT_TYPE_PROGRESSION:
1204-
right_icon_text = ICON_PF_ACHIEVEMENTS_PROGRESSION; // Progression
1205-
break;
1206-
1207-
case RC_CLIENT_ACHIEVEMENT_TYPE_WIN:
1208-
right_icon_text = ICON_PF_ACHIEVEMENTS_WIN; // Win Condition
1209-
break;
1210-
1211-
// Just use the lock for standard achievements.
1212-
case RC_CLIENT_ACHIEVEMENT_TYPE_STANDARD:
1213-
default:
1214-
right_icon_text = is_unlocked ? ICON_FA_UNLOCK : ICON_FA_LOCK;
1215-
break;
1235+
const ImVec2 type_badge_pos(title_bb.Min.x + title_size.x + type_badge_spacing,
1236+
ImFloor(title_bb.Min.y + (title_font_size - type_badge_size.y) * 0.5f));
1237+
dl->AddRectFilled(type_badge_pos, type_badge_pos + type_badge_size, type_badge_bg_color, type_badge_rounding);
1238+
1239+
const ImVec2 type_badge_text_pos = type_badge_pos + type_badge_padding;
1240+
const ImVec4 type_badge_text_clip = ImVec4(type_badge_pos.x, type_badge_pos.y, type_badge_pos.x + type_badge_size.x,
1241+
type_badge_pos.y + type_badge_size.y);
1242+
dl->AddText(UIStyle.Font, type_badge_font_size, type_badge_font_weight, type_badge_text_pos,
1243+
IM_COL32(255, 255, 255, 255), IMSTR_START_END(type_badge_text), 0.0f, &type_badge_text_clip);
12161244
}
12171245

1218-
const ImVec2 right_icon_size = UIStyle.Font->CalcTextSizeA(UIStyle.LargeFontSize, UIStyle.BoldFontWeight, FLT_MAX,
1219-
0.0f, IMSTR_START_END(right_icon_text));
1220-
1221-
const float text_start_x = bb.Min.x + image_size.x + LayoutScale(15.0f);
1222-
const ImRect title_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(points_start, midpoint));
1223-
const ImRect summary_bb(ImVec2(text_start_x, midpoint), ImVec2(points_start, midpoint + summary_text_size.y));
1224-
const ImRect unlock_rarity_bb(summary_bb.Min.x, summary_bb.Max.y + spacing, summary_bb.Max.x,
1225-
summary_bb.Max.y + unlock_rarity_height);
1226-
const ImRect points_bb(ImVec2(points_start, midpoint), bb.Max);
1227-
const ImRect lock_bb(ImVec2(points_template_start + ((points_template_size.x - right_icon_size.x) * 0.5f), bb.Min.y),
1228-
ImVec2(bb.Max.x, midpoint));
1229-
1230-
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, title_bb.Min, title_bb.Max,
1231-
text_color, cheevo->title, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &title_bb);
1232-
RenderShadowedTextClipped(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, lock_bb.Min, lock_bb.Max,
1233-
text_color, right_icon_text, &right_icon_size, ImVec2(0.0f, 0.0f), 0.0f, &lock_bb);
1234-
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, points_bb.Min,
1235-
points_bb.Max, summary_color, text, &points_size, ImVec2(0.0f, 0.0f), 0.0f, &points_bb);
1236-
1237-
if (cheevo->description && summary_length > 0)
1246+
// -- Description --
1247+
const u32 description_color = ImGui::GetColorU32(DarkerColor(UIStyle.SecondaryTextColor));
1248+
if (!description.empty())
12381249
{
1239-
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, summary_bb.Min,
1240-
summary_bb.Max, summary_color, std::string_view(cheevo->description, summary_length),
1241-
&summary_text_size, ImVec2(0.0f, 0.0f), summary_wrap_width, &summary_bb);
1250+
const ImRect description_bb(current_pos, current_pos + description_size);
1251+
RenderShadowedTextClipped(dl, UIStyle.Font, subtitle_font_size, subtitle_font_weight, description_bb.Min,
1252+
description_bb.Max, description_color, description, &description_size, ImVec2(0.0f, 0.0f),
1253+
max_text_width, &description_bb);
1254+
current_pos.y += description_size.y + spacing;
12421255
}
12431256

1257+
// -- Rarity --
12441258
// display hc if hc is active
12451259
const float rarity_to_display =
12461260
rc_client_get_hardcore_enabled(Achievements::GetClient()) ? cheevo->rarity_hardcore : cheevo->rarity;
1247-
1261+
const ImRect rarity_bb(current_pos, ImVec2(current_pos.x + max_text_width, current_pos.y + UIStyle.MediumFontSize));
1262+
const u32 rarity_color = ImGui::GetColorU32(DarkerColor(DarkerColor(UIStyle.SecondaryTextColor)));
12481263
if (is_unlocked)
12491264
{
12501265
const std::string date =
12511266
Host::FormatNumber(Host::NumberFormatType::LongDateTime, static_cast<s64>(cheevo->unlock_time));
12521267
text.format(TRANSLATE_FS("Achievements", "Unlocked: {} | {:.1f}% of players have this achievement"), date,
12531268
rarity_to_display);
12541269

1255-
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, unlock_rarity_bb.Min,
1256-
unlock_rarity_bb.Max, rarity_color, text, nullptr, ImVec2(0.0f, 0.0f), 0.0f,
1257-
&unlock_rarity_bb);
1270+
RenderShadowedTextClipped(dl, UIStyle.Font, subtitle_font_size, subtitle_font_weight, rarity_bb.Min, rarity_bb.Max,
1271+
rarity_color, text, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &rarity_bb);
12581272
}
12591273
else
12601274
{
12611275
text.format(TRANSLATE_FS("Achievements", "{:.1f}% of players have this achievement"), rarity_to_display);
1262-
RenderShadowedTextClipped(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, unlock_rarity_bb.Min,
1263-
unlock_rarity_bb.Max, rarity_color, text, nullptr, ImVec2(0.0f, 0.0f), 0.0f,
1264-
&unlock_rarity_bb);
1276+
RenderShadowedTextClipped(dl, UIStyle.Font, subtitle_font_size, subtitle_font_weight, rarity_bb.Min, rarity_bb.Max,
1277+
rarity_color, text, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &rarity_bb);
12651278
}
1279+
current_pos.y += UIStyle.MediumFontSize + spacing;
12661280

1267-
if (!is_unlocked && is_measured)
1281+
if (is_measured)
12681282
{
1269-
ImDrawList* dl = ImGui::GetWindowDrawList();
1270-
const float progress_height = LayoutScale(progress_height_unscaled);
1271-
const float progress_spacing = LayoutScale(progress_spacing_unscaled);
12721283
const float progress_rounding = LayoutScale(progress_rounding_unscaled);
1273-
const ImRect progress_bb(summary_bb.Min.x, unlock_rarity_bb.Max.y + progress_spacing,
1274-
summary_bb.Max.x - progress_spacing,
1275-
unlock_rarity_bb.Max.y + progress_spacing + progress_height);
1284+
const ImRect progress_bb(current_pos,
1285+
ImVec2(max_text_width, current_pos.y + LayoutScale(progress_height_unscaled)));
12761286
const float fraction = cheevo->measured_percent * 0.01f;
12771287
dl->AddRectFilled(progress_bb.Min, progress_bb.Max, ImGui::GetColorU32(UIStyle.PrimaryDarkColor),
12781288
progress_rounding);
12791289
ImGui::RenderRectFilledRangeH(dl, progress_bb, ImGui::GetColorU32(UIStyle.SecondaryColor), 0.0f, fraction,
12801290
progress_rounding);
12811291

1282-
const ImVec2 text_size = UIStyle.Font->CalcTextSizeA(UIStyle.MediumFontSize, UIStyle.NormalFontWeight, FLT_MAX,
1283-
0.0f, IMSTR_START_END(measured_progress));
1284-
const ImVec2 text_pos(progress_bb.Min.x + ((progress_bb.Max.x - progress_bb.Min.x) / 2.0f) - (text_size.x / 2.0f),
1285-
progress_bb.Min.y + ((progress_bb.Max.y - progress_bb.Min.y) / 2.0f) - (text_size.y / 2.0f));
1286-
dl->AddText(UIStyle.Font, UIStyle.MediumFontSize, UIStyle.NormalFontWeight, text_pos,
1292+
const ImVec2 text_size = UIStyle.Font->CalcTextSizeA(subtitle_font_size, subtitle_font_weight, FLT_MAX, 0.0f,
1293+
IMSTR_START_END(measured_progress));
1294+
const ImVec2 text_pos =
1295+
ImFloor(ImVec2(progress_bb.Min.x + ((progress_bb.Max.x - progress_bb.Min.x) / 2.0f) - (text_size.x / 2.0f),
1296+
progress_bb.Min.y + ((progress_bb.Max.y - progress_bb.Min.y) / 2.0f) - (text_size.y / 2.0f)));
1297+
dl->AddText(UIStyle.Font, subtitle_font_size, subtitle_font_weight, text_pos,
12871298
ImGui::GetColorU32(UIStyle.PrimaryTextColor), IMSTR_START_END(measured_progress));
12881299
}
12891300

1301+
// right side items
1302+
current_pos = ImVec2(bb.Max.x - right_side_size.x, bb.Min.y);
1303+
1304+
// -- Lock Icon and Points --
1305+
const std::string_view lock_text = is_unlocked ? ICON_EMOJI_UNLOCKED : ICON_FA_LOCK;
1306+
const ImVec2 lock_size =
1307+
UIStyle.Font->CalcTextSizeA(title_font_size, 0.0f, FLT_MAX, 0.0f, IMSTR_START_END(lock_text));
1308+
const ImRect lock_bb(current_pos, ImVec2(bb.Max.x, current_pos.y + lock_size.y));
1309+
RenderShadowedTextClipped(dl, UIStyle.Font, title_font_size, 0.0f, lock_bb.Min, lock_bb.Max, text_color, lock_text,
1310+
&lock_size, ImVec2(0.5f, 0.0f), 0.0f, &lock_bb);
1311+
current_pos.y += lock_size.y + spacing;
1312+
1313+
text = TRANSLATE_PLURAL_SSTR("Achievements", "%n points", "Achievement points", cheevo->points);
1314+
const ImVec2 points_size =
1315+
UIStyle.Font->CalcTextSizeA(subtitle_font_size, subtitle_font_weight, FLT_MAX, 0.0f, IMSTR_START_END(text));
1316+
const ImRect points_bb(current_pos, ImVec2(bb.Max.x, current_pos.y + points_size.y));
1317+
RenderShadowedTextClipped(dl, UIStyle.Font, subtitle_font_size, subtitle_font_weight, points_bb.Min, points_bb.Max,
1318+
description_color, text, &points_size, ImVec2(0.5f, 0.0f), 0.0f, &points_bb);
1319+
12901320
if (clicked)
12911321
{
1292-
const SmallString url = SmallString::from_format(fmt::runtime(ACHEIVEMENT_DETAILS_URL_TEMPLATE), cheevo->id);
1322+
const std::string url = fmt::format(fmt::runtime(ACHEIVEMENT_DETAILS_URL_TEMPLATE), cheevo->id);
12931323
INFO_LOG("Opening achievement details: {}", url);
12941324
Host::OpenURL(url);
12951325
}

0 commit comments

Comments
 (0)