Refactor the on-screen display to be more customizable

This commit is contained in:
Henrik Rydgård 2023-07-31 23:13:52 +02:00
parent 1475fcb065
commit 48d577014a
9 changed files with 226 additions and 190 deletions

View File

@ -21,22 +21,6 @@ void OnScreenDisplay::Update() {
iter++;
}
}
for (auto iter = sideEntries_.begin(); iter != sideEntries_.end(); ) {
if (now >= iter->endTime) {
iter = sideEntries_.erase(iter);
} else {
iter++;
}
}
for (auto iter = bars_.begin(); iter != bars_.end(); ) {
if (now >= iter->endTime) {
iter = bars_.erase(iter);
} else {
iter++;
}
}
}
std::vector<OnScreenDisplay::Entry> OnScreenDisplay::Entries() {
@ -44,16 +28,6 @@ std::vector<OnScreenDisplay::Entry> OnScreenDisplay::Entries() {
return entries_; // makes a copy.
}
std::vector<OnScreenDisplay::Entry> OnScreenDisplay::SideEntries() {
std::lock_guard<std::mutex> guard(mutex_);
return sideEntries_; // makes a copy.
}
std::vector<OnScreenDisplay::ProgressBar> OnScreenDisplay::ProgressBars() {
std::lock_guard<std::mutex> guard(mutex_);
return bars_; // makes a copy.
}
void OnScreenDisplay::NudgeSidebar() {
sideBarShowTime_ = time_now_d();
}
@ -89,7 +63,7 @@ void OnScreenDisplay::Show(OSDType type, const std::string &text, const std::str
std::lock_guard<std::mutex> guard(mutex_);
if (id) {
for (auto iter = entries_.begin(); iter != entries_.end(); ++iter) {
if (iter->id && !strcmp(iter->id, id)) {
if (iter->id == id) {
Entry msg = *iter;
msg.endTime = now + duration_s;
msg.text = text;
@ -111,7 +85,9 @@ void OnScreenDisplay::Show(OSDType type, const std::string &text, const std::str
msg.startTime = now;
msg.endTime = now + duration_s;
msg.type = type;
msg.id = id;
if (id) {
msg.id = id;
}
entries_.insert(entries_.begin(), msg);
}
@ -132,7 +108,7 @@ void OnScreenDisplay::ShowAchievementProgress(int achievementID, bool show) {
double now = time_now_d();
// There can only be one of these at a time.
for (auto &entry : sideEntries_) {
for (auto &entry : entries_) {
if (entry.type == OSDType::ACHIEVEMENT_PROGRESS) {
if (!show) {
// Hide and eventually delete it.
@ -158,13 +134,13 @@ void OnScreenDisplay::ShowAchievementProgress(int achievementID, bool show) {
entry.type = OSDType::ACHIEVEMENT_PROGRESS;
entry.startTime = now;
entry.endTime = now + forever_s;
sideEntries_.insert(sideEntries_.begin(), entry);
entries_.insert(entries_.begin(), entry);
}
void OnScreenDisplay::ShowChallengeIndicator(int achievementID, bool show) {
double now = time_now_d();
for (auto &entry : sideEntries_) {
for (auto &entry : entries_) {
if (entry.numericID == achievementID && entry.type == OSDType::ACHIEVEMENT_CHALLENGE_INDICATOR && !show) {
// Hide and eventually delete it.
entry.endTime = now + (double)FadeoutTime();
@ -184,13 +160,13 @@ void OnScreenDisplay::ShowChallengeIndicator(int achievementID, bool show) {
entry.type = OSDType::ACHIEVEMENT_CHALLENGE_INDICATOR;
entry.startTime = now;
entry.endTime = now + forever_s;
sideEntries_.insert(sideEntries_.begin(), entry);
entries_.insert(entries_.begin(), entry);
}
void OnScreenDisplay::ShowLeaderboardTracker(int leaderboardTrackerID, const char *trackerText, bool show) { // show=true is used both for create and update.
double now = time_now_d();
for (auto &entry : sideEntries_) {
for (auto &entry : entries_) {
if (entry.numericID == leaderboardTrackerID && entry.type == OSDType::LEADERBOARD_TRACKER) {
if (show) {
// Just an update.
@ -218,7 +194,7 @@ void OnScreenDisplay::ShowLeaderboardTracker(int leaderboardTrackerID, const cha
if (trackerText) {
entry.text = trackerText;
}
sideEntries_.insert(sideEntries_.begin(), entry);
entries_.insert(entries_.begin(), entry);
}
void OnScreenDisplay::ShowOnOff(const std::string &message, bool on, float duration_s) {
@ -235,32 +211,33 @@ void OnScreenDisplay::SetProgressBar(std::string id, std::string &&message, floa
bool found = false;
std::lock_guard<std::mutex> guard(mutex_);
for (auto &bar : bars_) {
if (bar.id == id) {
for (auto &bar : entries_) {
if (bar.type == OSDType::PROGRESS_BAR && bar.id == id) {
bar.minValue = minValue;
bar.maxValue = maxValue;
bar.progress = progress;
bar.message = message;
bar.text = message;
bar.endTime = now + 60.0; // Nudge the progress bar to keep it shown.
return;
}
}
ProgressBar bar;
Entry bar;
bar.id = id;
bar.message = std::move(message);
bar.type = OSDType::PROGRESS_BAR;
bar.text = std::move(message);
bar.minValue = minValue;
bar.maxValue = maxValue;
bar.progress = progress;
bar.startTime = now + delay;
bar.endTime = now + 60.0; // Show the progress bar for 60 seconds, then fade it out.
bars_.push_back(bar);
entries_.push_back(bar);
}
void OnScreenDisplay::RemoveProgressBar(std::string id, bool success, float delay_s) {
std::lock_guard<std::mutex> guard(mutex_);
for (auto iter = bars_.begin(); iter != bars_.end(); iter++) {
if (iter->id == id) {
for (auto iter = entries_.begin(); iter != entries_.end(); iter++) {
if (iter->type == OSDType::PROGRESS_BAR && iter->id == id) {
if (success) {
// Quickly shoot up to max, if we weren't there.
if (iter->maxValue != 0.0f) {
@ -293,16 +270,4 @@ void OnScreenDisplay::ClearAchievementStuff() {
break;
}
}
for (auto &iter : sideEntries_) {
switch (iter.type) {
case OSDType::ACHIEVEMENT_CHALLENGE_INDICATOR:
case OSDType::ACHIEVEMENT_UNLOCKED:
case OSDType::ACHIEVEMENT_PROGRESS:
case OSDType::LEADERBOARD_TRACKER:
iter.endTime = now;
break;
default:
break;
}
}
}

View File

@ -21,6 +21,22 @@ enum class OSDType {
ACHIEVEMENT_CHALLENGE_INDICATOR, // Achievement icon ONLY, no auto-hide
LEADERBOARD_TRACKER,
PROGRESS_BAR,
VALUE_COUNT,
};
enum class ScreenEdgePosition {
BOTTOM_LEFT = 0,
BOTTOM_CENTER = 1,
BOTTOM_RIGHT = 2,
TOP_LEFT = 3,
TOP_CENTER = 4,
TOP_RIGHT = 5,
CENTER_LEFT = 6,
CENTER_RIGHT = 7,
VALUE_COUNT,
};
// Data holder for on-screen messages.
@ -67,31 +83,23 @@ public:
std::string text2;
std::string iconName;
int numericID;
const char *id;
std::string id;
double startTime;
double endTime;
};
struct ProgressBar {
std::string id;
std::string message;
// Progress-bar-only data:
float minValue;
float maxValue;
float progress;
double startTime;
double endTime;
};
std::vector<Entry> Entries();
std::vector<Entry> SideEntries();
std::vector<ProgressBar> ProgressBars();
static float FadeinTime() { return 0.1f; }
static float FadeoutTime() { return 0.25f; }
private:
std::vector<Entry> entries_;
std::vector<Entry> sideEntries_;
std::vector<ProgressBar> bars_;
std::mutex mutex_;
double sideBarShowTime_ = 0.0;

View File

@ -47,6 +47,7 @@
#include "Common/Thread/ThreadUtil.h"
#include "Common/GPU/Vulkan/VulkanLoader.h"
#include "Common/VR/PPSSPPVR.h"
#include "Common/System/OSD.h"
#include "Core/Config.h"
#include "Core/ConfigSettings.h"
#include "Core/ConfigValues.h"

View File

@ -29,17 +29,6 @@
extern const char *PPSSPP_GIT_VERSION;
enum class ScreenEdgePosition {
BOTTOM_LEFT = 0,
BOTTOM_CENTER = 1,
BOTOM_RIGHT = 2,
TOP_LEFT = 3,
TOP_CENTER = 4,
TOP_RIGHT = 5,
CENTER_LEFT = 6,
CENTER_RIGHT = 7,
};
namespace http {
class Request;
class RequestManager;

View File

@ -459,6 +459,11 @@ const char *GetCompilerABI() {
#endif
}
void SystemInfoScreen::update() {
TabbedUIDialogScreenWithGameBackground::update();
g_OSD.NudgeSidebar();
}
void SystemInfoScreen::CreateTabs() {
using namespace Draw;
using namespace UI;

View File

@ -113,6 +113,8 @@ public:
void CreateTabs() override;
void update() override;
protected:
bool ShowSearchControls() const override { return false; }
};

View File

@ -897,7 +897,7 @@ static UI::AnchorLayoutParams *AnchorInCorner(const Bounds &bounds, int corner,
switch ((ScreenEdgePosition)g_Config.iChatButtonPosition) {
case ScreenEdgePosition::BOTTOM_LEFT: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, xOffset, NONE, NONE, yOffset, true);
case ScreenEdgePosition::BOTTOM_CENTER: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, bounds.centerX(), NONE, NONE, yOffset, true);
case ScreenEdgePosition::BOTOM_RIGHT: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, NONE, NONE, xOffset, yOffset, true);
case ScreenEdgePosition::BOTTOM_RIGHT: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, NONE, NONE, xOffset, yOffset, true);
case ScreenEdgePosition::TOP_LEFT: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, xOffset, yOffset, NONE, NONE, true);
case ScreenEdgePosition::TOP_CENTER: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, bounds.centerX(), yOffset, NONE, NONE, true);
case ScreenEdgePosition::TOP_RIGHT: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, NONE, yOffset, xOffset, NONE, true);

View File

@ -156,12 +156,12 @@ static void RenderOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry, B
}
}
static void MeasureOSDProgressBar(const UIContext &dc, const OnScreenDisplay::ProgressBar &bar, float *width, float *height) {
static void MeasureOSDProgressBar(const UIContext &dc, const OnScreenDisplay::Entry &bar, float *width, float *height) {
*height = 36;
*width = 450.0f;
}
static void RenderOSDProgressBar(UIContext &dc, const OnScreenDisplay::ProgressBar &entry, Bounds bounds, int align, float alpha) {
static void RenderOSDProgressBar(UIContext &dc, const OnScreenDisplay::Entry &entry, Bounds bounds, int align, float alpha) {
uint32_t foreGround = whiteAlpha(alpha);
dc.DrawRectDropShadow(bounds, 12.0f, 0.7f * alpha);
@ -197,7 +197,7 @@ static void RenderOSDProgressBar(UIContext &dc, const OnScreenDisplay::ProgressB
dc.SetFontStyle(dc.theme->uiFont);
dc.SetFontScale(1.0f, 1.0f);
dc.DrawTextShadowRect(entry.message.c_str(), bounds, colorAlpha(0xFFFFFFFF, alpha), (align & FLAG_DYNAMIC_ASCII) | ALIGN_CENTER);
dc.DrawTextShadowRect(entry.text.c_str(), bounds, colorAlpha(0xFFFFFFFF, alpha), (align & FLAG_DYNAMIC_ASCII) | ALIGN_CENTER);
}
static void MeasureLeaderboardTracker(UIContext &dc, const std::string &text, float *width, float *height) {
@ -226,143 +226,209 @@ void OnScreenMessagesView::Draw(UIContext &dc) {
double now = time_now_d();
float y = 10.0f;
const float padding = 5.0f;
const float fadeinCoef = 1.0f / OnScreenDisplay::FadeinTime();
const float fadeoutCoef = 1.0f / OnScreenDisplay::FadeoutTime();
float sidebarAlpha = g_OSD.SidebarAlpha();
if (sidebarAlpha > 0.0f) {
// Draw side entries. Top entries should apply on top of them if there's a collision, so drawing
// these first makes sense.
const std::vector<OnScreenDisplay::Entry> sideEntries = g_OSD.SideEntries();
for (auto &entry : sideEntries) {
float tw, th;
struct LayoutEdge {
float height;
float maxWidth;
float alpha;
};
const rc_client_achievement_t *achievement = nullptr;
AchievementRenderStyle style;
struct MeasuredEntry {
float w;
float h;
float h1;
float alpha;
int align;
int align2;
AchievementRenderStyle style;
};
switch (entry.type) {
case OSDType::ACHIEVEMENT_PROGRESS:
{
achievement = rc_client_get_achievement_info(Achievements::GetClient(), entry.numericID);
if (!achievement)
continue;
style = AchievementRenderStyle::PROGRESS_INDICATOR;
MeasureAchievement(dc, achievement, style, &tw, &th);
break;
}
case OSDType::ACHIEVEMENT_CHALLENGE_INDICATOR:
{
achievement = rc_client_get_achievement_info(Achievements::GetClient(), entry.numericID);
if (!achievement)
continue;
style = AchievementRenderStyle::CHALLENGE_INDICATOR;
MeasureAchievement(dc, achievement, style, &tw, &th);
break;
}
case OSDType::LEADERBOARD_TRACKER:
{
MeasureLeaderboardTracker(dc, entry.text, &tw, &th);
break;
}
default:
continue;
}
Bounds b(10.0f, y, tw, th);
// We don't multiply in sidebarAlpha here because it shouldn't drive the below animation.
float alpha = Clamp((float)(entry.endTime - now) * fadeoutCoef, 0.0f, 1.0f);
// OK, render the thing.
switch (entry.type) {
case OSDType::ACHIEVEMENT_PROGRESS:
case OSDType::ACHIEVEMENT_CHALLENGE_INDICATOR:
{
RenderAchievement(dc, achievement, style, b, alpha * sidebarAlpha, entry.startTime, now, false);
break;
}
case OSDType::LEADERBOARD_TRACKER:
RenderLeaderboardTracker(dc, b, entry.text, alpha * sidebarAlpha);
break;
default:
continue;
}
y += (b.h + 4.0f) * alpha; // including alpha here gets us smooth animations.
}
}
y = 10.0f;
// Draw the progress bars.
const std::vector<OnScreenDisplay::ProgressBar> bars = g_OSD.ProgressBars();
for (auto &bar : bars) {
float tw, th;
MeasureOSDProgressBar(dc, bar, &tw, &th);
Bounds b(0.0f, y, tw, th);
b.x = (bounds_.w - b.w) * 0.5f;
float enterAlpha = saturatef((float)(now - bar.startTime) * 4.0f);
float leaveAlpha = saturatef((float)(bar.endTime - now) * 4.0f);
float alpha = std::min(enterAlpha, leaveAlpha);
RenderOSDProgressBar(dc, bar, b, 0, alpha);
y += (b.h + 4.0f) * alpha; // including alpha here gets us smooth animations.
}
// Draw the rest of the top-center messages.
// Grab all the entries. Makes a copy so we can release the lock ASAP.
const std::vector<OnScreenDisplay::Entry> entries = g_OSD.Entries();
for (const auto &entry : entries) {
dc.SetFontScale(1.0f, 1.0f);
// Messages that are wider than the screen are left-aligned instead of centered.
int align = 0;
std::vector<MeasuredEntry> measuredEntries;
measuredEntries.resize(entries.size());
// Indexed by the enum ScreenEdgePosition.
LayoutEdge edges[(size_t)ScreenEdgePosition::VALUE_COUNT]{};
for (size_t i = 0; i < (size_t)ScreenEdgePosition::VALUE_COUNT; i++) {
edges[i].alpha = sidebarAlpha;
}
edges[(size_t)ScreenEdgePosition::TOP_CENTER].alpha = 1.0f;
ScreenEdgePosition typeEdges[(size_t)OSDType::VALUE_COUNT]{};
// Default to top.
for (int i = 0; i < (size_t)OSDType::VALUE_COUNT; i++) {
typeEdges[i] = ScreenEdgePosition::TOP_CENTER;
}
// TODO: Add ability to override these with g_Config settings.
typeEdges[(size_t)OSDType::ACHIEVEMENT_CHALLENGE_INDICATOR] = ScreenEdgePosition::TOP_LEFT;
typeEdges[(size_t)OSDType::ACHIEVEMENT_PROGRESS] = ScreenEdgePosition::TOP_LEFT;
typeEdges[(size_t)OSDType::LEADERBOARD_TRACKER] = ScreenEdgePosition::TOP_LEFT;
dc.SetFontScale(1.0f, 1.0f);
// First pass: Measure all the sides.
for (size_t i = 0; i < entries.size(); i++) {
const auto &entry = entries[i];
auto &measuredEntry = measuredEntries[i];
ScreenEdgePosition pos = typeEdges[(size_t)entry.type];
measuredEntry.align = 0;
measuredEntry.align2 = 0;
// If we have newlines, we may be looking at ASCII debug output. But let's verify.
if (entry.text.find('\n') != 0) {
if (entry.text.find('\n') != std::string::npos) {
if (!UTF8StringHasNonASCII(entry.text.c_str()))
align |= FLAG_DYNAMIC_ASCII;
measuredEntry.align |= FLAG_DYNAMIC_ASCII;
}
if (entry.text2.find('\n') != std::string::npos) {
if (!UTF8StringHasNonASCII(entry.text2.c_str()))
measuredEntry.align2 |= FLAG_DYNAMIC_ASCII;
}
float tw, th = 0.0f, h1 = 0.0f;
switch (entry.type) {
case OSDType::ACHIEVEMENT_PROGRESS:
{
const rc_client_achievement_t *achievement = rc_client_get_achievement_info(Achievements::GetClient(), entry.numericID);
if (!achievement)
continue;
measuredEntry.style = AchievementRenderStyle::PROGRESS_INDICATOR;
MeasureAchievement(dc, achievement, measuredEntry.style, &measuredEntry.w, &measuredEntry.h);
break;
}
case OSDType::ACHIEVEMENT_CHALLENGE_INDICATOR:
{
const rc_client_achievement_t *achievement = rc_client_get_achievement_info(Achievements::GetClient(), entry.numericID);
if (!achievement)
continue;
measuredEntry.style = AchievementRenderStyle::CHALLENGE_INDICATOR;
MeasureAchievement(dc, achievement, measuredEntry.style, &measuredEntry.w, &measuredEntry.h);
break;
}
case OSDType::LEADERBOARD_TRACKER:
{
MeasureLeaderboardTracker(dc, entry.text, &measuredEntry.w, &measuredEntry.h);
break;
}
case OSDType::ACHIEVEMENT_UNLOCKED:
{
const rc_client_achievement_t *achievement = rc_client_get_achievement_info(Achievements::GetClient(), entry.numericID);
if (achievement) {
MeasureAchievement(dc, achievement, AchievementRenderStyle::UNLOCKED, &tw, &th);
h1 = th;
}
tw = 550.0f;
if (!achievement)
continue;
measuredEntry.style = AchievementRenderStyle::UNLOCKED;
MeasureAchievement(dc, achievement, AchievementRenderStyle::UNLOCKED, &measuredEntry.w, &measuredEntry.h);
measuredEntry.h1 = measuredEntry.h;
measuredEntry.w = 550.0f;
break;
}
case OSDType::PROGRESS_BAR:
MeasureOSDProgressBar(dc, entry, &measuredEntry.w, &measuredEntry.h);
break;
default:
MeasureOSDEntry(dc, entry, align, &tw, &th, &h1);
MeasureOSDEntry(dc, entry, measuredEntry.align, &measuredEntry.w, &measuredEntry.h, &measuredEntry.h1);
break;
}
Bounds b(0.0f, y, tw, th);
float enterAlpha = saturatef((float)(now - entry.startTime) * fadeoutCoef);
float leaveAlpha = saturatef((float)(entry.endTime - now) * fadeoutCoef);
float alpha = std::min(enterAlpha, leaveAlpha);
measuredEntry.alpha = alpha;
if (tw > bounds_.w) {
// Left-aligned
b.x = 2;
} else {
// Centered
b.x = (bounds_.w - b.w) * 0.5f;
edges[(size_t)pos].height += (measuredEntry.h + 4.0f) * alpha;
edges[(size_t)pos].maxWidth = std::max(edges[(size_t)pos].maxWidth, measuredEntry.w);
}
// Now, perform layout for all 8 edges.
for (size_t i = 0; i < (size_t)ScreenEdgePosition::VALUE_COUNT; i++) {
if (edges[i].height == 0.0f) {
// Nothing on this side, ignore it entirely.
continue;
}
// Scale down if height doesn't fit.
float scale = 1.0f;
if (th > bounds_.h - y) {
// Scale down!
scale = std::max(0.15f, (bounds_.h - y) / th);
dc.SetFontScale(scale, scale);
b.w *= scale;
b.h *= scale;
// First, compute the start position.
float y = padding;
int horizAdj = 0;
int vertAdj = 0;
switch ((ScreenEdgePosition)i) {
case ScreenEdgePosition::TOP_LEFT: horizAdj = -1; vertAdj = -1; break;
case ScreenEdgePosition::CENTER_LEFT: horizAdj = -1; break;
case ScreenEdgePosition::BOTTOM_LEFT: horizAdj = -1; vertAdj = 1; break;
case ScreenEdgePosition::TOP_RIGHT: horizAdj = 1; vertAdj = -1; break;
case ScreenEdgePosition::CENTER_RIGHT: horizAdj = 1; break;
case ScreenEdgePosition::BOTTOM_RIGHT: horizAdj = 1; vertAdj = 1; break;
case ScreenEdgePosition::TOP_CENTER: vertAdj = -1; break;
case ScreenEdgePosition::BOTTOM_CENTER: vertAdj = 1; break;
default: break;
}
float alpha = Clamp((float)(entry.endTime - now) * 4.0f, 0.0f, 1.0f);
RenderOSDEntry(dc, entry, b, h1, align, alpha);
y += (b.h * scale + 4.0f) * alpha; // including alpha here gets us smooth animations.
if (vertAdj == 0) {
// Center vertically
y = (bounds_.h - edges[i].height) * 0.5f;
} else if (vertAdj == 1) {
y = (bounds_.h - edges[i].height);
}
// Then, loop through the entries and those belonging here, get rendered here.
for (size_t j = 0; j < (size_t)entries.size(); j++) {
auto &entry = entries[j];
if (typeEdges[(size_t)entry.type] != (ScreenEdgePosition)i) { // yes, i
continue;
}
auto &measuredEntry = measuredEntries[j];
float alpha = measuredEntry.alpha * edges[i].alpha;
Bounds b(padding, y, measuredEntry.w, measuredEntry.h);
if (horizAdj == 0) {
// Centered
b.x = (bounds_.w - b.w) * 0.5f;
} else if (horizAdj == 1) {
// Right-aligned
b.x = bounds_.w - (b.w + padding);
}
switch (entry.type) {
case OSDType::ACHIEVEMENT_PROGRESS:
case OSDType::ACHIEVEMENT_CHALLENGE_INDICATOR:
{
const rc_client_achievement_t *achievement = rc_client_get_achievement_info(Achievements::GetClient(), entry.numericID);
RenderAchievement(dc, achievement, measuredEntry.style, b, alpha, entry.startTime, now, false);
break;
}
case OSDType::LEADERBOARD_TRACKER:
RenderLeaderboardTracker(dc, b, entry.text, alpha);
break;
case OSDType::PROGRESS_BAR:
RenderOSDProgressBar(dc, entry, b, 0, alpha);
break;
default:
{
// Scale down if height doesn't fit.
float scale = 1.0f;
if (measuredEntry.h > bounds_.h - y) {
// Scale down!
scale = std::max(0.15f, (bounds_.h - y) / measuredEntry.h);
dc.SetFontScale(scale, scale);
b.w *= scale;
b.h *= scale;
}
float alpha = Clamp((float)(entry.endTime - now) * 4.0f, 0.0f, 1.0f);
RenderOSDEntry(dc, entry, b, measuredEntry.h1, measuredEntry.align, alpha);
break;
}
}
y += (measuredEntry.h + 4.0f) * measuredEntry.alpha;
}
}
}

View File

@ -275,7 +275,7 @@ void RetroAchievementsSettingsScreen::CreateAccountTab(UI::ViewGroup *viewGroup)
using namespace UI;
if (!g_Config.bAchievementsEnable) {
viewGroup->Add(new TextView(ac->T("Achievements are disabled")));
viewGroup->Add(new NoticeView(NoticeLevel::INFO, ac->T("Achievements are disabled"), "", new LinearLayoutParams(Margins(5))));
} else if (Achievements::IsLoggedIn()) {
const rc_client_user_t *info = rc_client_get_user_info(Achievements::GetClient());