mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-12 00:59:41 +01:00
Merge pull request #17823 from hrydgard/more-achievement-ui-work
More achievement UI work
This commit is contained in:
15 files changed
+316
-255
No files matched your search
+21
-54
@@ -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,17 +160,19 @@ 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.
|
||||
entry.text = trackerText ? trackerText : "";
|
||||
// Bump the end-time, in case it was fading out.
|
||||
entry.endTime = now + forever_s;
|
||||
} else {
|
||||
// Keep the current text, hide and eventually delete it.
|
||||
entry.endTime = now + (double)FadeoutTime();
|
||||
@@ -218,7 +196,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 +213,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 +272,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-11
@@ -21,6 +21,10 @@ enum class OSDType {
|
||||
ACHIEVEMENT_CHALLENGE_INDICATOR, // Achievement icon ONLY, no auto-hide
|
||||
|
||||
LEADERBOARD_TRACKER,
|
||||
|
||||
PROGRESS_BAR,
|
||||
|
||||
VALUE_COUNT,
|
||||
};
|
||||
|
||||
// Data holder for on-screen messages.
|
||||
@@ -67,31 +71,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;
|
||||
|
||||
+6
-7
@@ -25,18 +25,18 @@ static constexpr Size ITEM_HEIGHT = 64.f;
|
||||
static constexpr float MIN_TEXT_SCALE = 0.8f;
|
||||
static constexpr float MAX_ITEM_SIZE = 65535.0f;
|
||||
|
||||
void MeasureBySpec(Size sz, float contentWidth, MeasureSpec spec, float *measured) {
|
||||
void MeasureBySpec(Size sz, float contentDim, MeasureSpec spec, float *measured) {
|
||||
if (sz == WRAP_CONTENT) {
|
||||
if (spec.type == UNSPECIFIED)
|
||||
*measured = contentWidth;
|
||||
*measured = contentDim;
|
||||
else if (spec.type == AT_MOST)
|
||||
*measured = contentWidth < spec.size ? contentWidth : spec.size;
|
||||
*measured = contentDim < spec.size ? contentDim : spec.size;
|
||||
else if (spec.type == EXACTLY)
|
||||
*measured = spec.size;
|
||||
} else if (sz == FILL_PARENT) {
|
||||
// UNSPECIFIED may have a minimum size of the parent. Let's use it to fill.
|
||||
if (spec.type == UNSPECIFIED)
|
||||
*measured = std::max(spec.size, contentWidth);
|
||||
*measured = std::max(spec.size, contentDim);
|
||||
else
|
||||
*measured = spec.size;
|
||||
} else if (spec.type == EXACTLY || (spec.type == AT_MOST && *measured > spec.size)) {
|
||||
@@ -46,7 +46,7 @@ void MeasureBySpec(Size sz, float contentWidth, MeasureSpec spec, float *measure
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyBoundBySpec(float &bound, MeasureSpec spec) {
|
||||
static void ApplyBoundBySpec(float &bound, MeasureSpec spec) {
|
||||
switch (spec.type) {
|
||||
case AT_MOST:
|
||||
bound = bound < spec.size ? bound : spec.size;
|
||||
@@ -122,9 +122,8 @@ void View::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert) {
|
||||
MeasureBySpec(layoutParams_->height, contentH, vert, &measuredHeight_);
|
||||
}
|
||||
|
||||
// Default values
|
||||
|
||||
void View::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
|
||||
// Default values
|
||||
w = 10.0f;
|
||||
h = 10.0f;
|
||||
}
|
||||
|
||||
+47
-45
@@ -824,55 +824,57 @@ void AnchorLayout::MeasureViews(const UIContext &dc, MeasureSpec horiz, MeasureS
|
||||
}
|
||||
}
|
||||
|
||||
static void ApplyAnchorLayoutParams(float measuredWidth, float measuredHeight, const Bounds &container, const AnchorLayoutParams *params, Bounds *vBounds) {
|
||||
vBounds->w = measuredWidth;
|
||||
vBounds->h = measuredHeight;
|
||||
|
||||
// Clamp width/height to our own
|
||||
if (vBounds->w > container.w) vBounds->w = container.w;
|
||||
if (vBounds->h > container.h) vBounds->h = container.h;
|
||||
|
||||
float left = 0, top = 0, right = 0, bottom = 0;
|
||||
bool center = false;
|
||||
if (params) {
|
||||
left = params->left;
|
||||
top = params->top;
|
||||
right = params->right;
|
||||
bottom = params->bottom;
|
||||
center = params->center;
|
||||
}
|
||||
|
||||
if (left > NONE) {
|
||||
vBounds->x = container.x + left;
|
||||
if (center)
|
||||
vBounds->x -= vBounds->w * 0.5f;
|
||||
} else if (right > NONE) {
|
||||
vBounds->x = container.x2() - right - vBounds->w;
|
||||
if (center) {
|
||||
vBounds->x += vBounds->w * 0.5f;
|
||||
}
|
||||
} else {
|
||||
// Both left and right are NONE. Center.
|
||||
vBounds->x = (container.w - vBounds->w) / 2.0f + container.x;
|
||||
}
|
||||
|
||||
if (top > NONE) {
|
||||
vBounds->y = container.y + top;
|
||||
if (center)
|
||||
vBounds->y -= vBounds->h * 0.5f;
|
||||
} else if (bottom > NONE) {
|
||||
vBounds->y = container.y2() - bottom - vBounds->h;
|
||||
if (center)
|
||||
vBounds->y += vBounds->h * 0.5f;
|
||||
} else {
|
||||
// Both top and bottom are NONE. Center.
|
||||
vBounds->y = (container.h - vBounds->h) / 2.0f + container.y;
|
||||
}
|
||||
}
|
||||
|
||||
void AnchorLayout::Layout() {
|
||||
for (size_t i = 0; i < views_.size(); i++) {
|
||||
const AnchorLayoutParams *params = views_[i]->GetLayoutParams()->As<AnchorLayoutParams>();
|
||||
|
||||
Bounds vBounds;
|
||||
vBounds.w = views_[i]->GetMeasuredWidth();
|
||||
vBounds.h = views_[i]->GetMeasuredHeight();
|
||||
|
||||
// Clamp width/height to our own
|
||||
if (vBounds.w > bounds_.w) vBounds.w = bounds_.w;
|
||||
if (vBounds.h > bounds_.h) vBounds.h = bounds_.h;
|
||||
|
||||
float left = 0, top = 0, right = 0, bottom = 0;
|
||||
bool center = false;
|
||||
if (params) {
|
||||
left = params->left;
|
||||
top = params->top;
|
||||
right = params->right;
|
||||
bottom = params->bottom;
|
||||
center = params->center;
|
||||
}
|
||||
|
||||
if (left > NONE) {
|
||||
vBounds.x = bounds_.x + left;
|
||||
if (center)
|
||||
vBounds.x -= vBounds.w * 0.5f;
|
||||
} else if (right > NONE) {
|
||||
vBounds.x = bounds_.x2() - right - vBounds.w;
|
||||
if (center) {
|
||||
vBounds.x += vBounds.w * 0.5f;
|
||||
}
|
||||
} else {
|
||||
// Both left and right are NONE. Center.
|
||||
vBounds.x = (bounds_.w - vBounds.w) / 2.0f + bounds_.x;
|
||||
}
|
||||
|
||||
if (top > NONE) {
|
||||
vBounds.y = bounds_.y + top;
|
||||
if (center)
|
||||
vBounds.y -= vBounds.h * 0.5f;
|
||||
} else if (bottom > NONE) {
|
||||
vBounds.y = bounds_.y2() - bottom - vBounds.h;
|
||||
if (center)
|
||||
vBounds.y += vBounds.h * 0.5f;
|
||||
} else {
|
||||
// Both top and bottom are NONE. Center.
|
||||
vBounds.y = (bounds_.h - vBounds.h) / 2.0f + bounds_.y;
|
||||
}
|
||||
|
||||
ApplyAnchorLayoutParams(views_[i]->GetMeasuredWidth(), views_[i]->GetMeasuredHeight(), bounds_, params, &vBounds);
|
||||
views_[i]->SetBounds(vBounds);
|
||||
views_[i]->Layout();
|
||||
}
|
||||
|
||||
+5
-2
@@ -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"
|
||||
@@ -282,6 +283,8 @@ static const ConfigSetting achievementSettings[] = {
|
||||
ConfigSetting("AchievementsSoundEffects", &g_Config.bAchievementsSoundEffects, true, CfgFlag::DEFAULT),
|
||||
ConfigSetting("AchievementsUnlockAudioFile", &g_Config.sAchievementsUnlockAudioFile, "", CfgFlag::DEFAULT),
|
||||
ConfigSetting("AchievementsLeaderboardSubmitAudioFile", &g_Config.sAchievementsLeaderboardSubmitAudioFile, "", CfgFlag::DEFAULT),
|
||||
|
||||
ConfigSetting("AchievementsLeaderboardTrackerPos", &g_Config.iAchievementsLeaderboardTrackerPos, (int)ScreenEdgePosition::TOP_LEFT, CfgFlag::DEFAULT),
|
||||
};
|
||||
|
||||
static const ConfigSetting cpuSettings[] = {
|
||||
@@ -789,8 +792,8 @@ static const ConfigSetting networkSettings[] = {
|
||||
ConfigSetting("UPnPUseOriginalPort", &g_Config.bUPnPUseOriginalPort, false, CfgFlag::PER_GAME),
|
||||
|
||||
ConfigSetting("EnableNetworkChat", &g_Config.bEnableNetworkChat, false, CfgFlag::PER_GAME),
|
||||
ConfigSetting("ChatButtonPosition",&g_Config.iChatButtonPosition,BOTTOM_LEFT, CfgFlag::PER_GAME),
|
||||
ConfigSetting("ChatScreenPosition",&g_Config.iChatScreenPosition,BOTTOM_LEFT, CfgFlag::PER_GAME),
|
||||
ConfigSetting("ChatButtonPosition", &g_Config.iChatButtonPosition, (int)ScreenEdgePosition::BOTTOM_LEFT, CfgFlag::PER_GAME),
|
||||
ConfigSetting("ChatScreenPosition", &g_Config.iChatScreenPosition, (int)ScreenEdgePosition::BOTTOM_LEFT, CfgFlag::PER_GAME),
|
||||
ConfigSetting("EnableQuickChat", &g_Config.bEnableQuickChat, true, CfgFlag::PER_GAME),
|
||||
ConfigSetting("QuickChat1", &g_Config.sQuickChat0, "Quick Chat 1", CfgFlag::PER_GAME),
|
||||
ConfigSetting("QuickChat2", &g_Config.sQuickChat1, "Quick Chat 2", CfgFlag::PER_GAME),
|
||||
|
||||
+3
-11
@@ -29,17 +29,6 @@
|
||||
|
||||
extern const char *PPSSPP_GIT_VERSION;
|
||||
|
||||
enum ChatPositions {
|
||||
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;
|
||||
@@ -497,6 +486,9 @@ public:
|
||||
bool bAchievementsSoundEffects;
|
||||
bool bAchievementsLogBadMemReads;
|
||||
|
||||
// Positioning of the various notifications
|
||||
int iAchievementsLeaderboardTrackerPos;
|
||||
|
||||
// Customizations
|
||||
std::string sAchievementsUnlockAudioFile;
|
||||
std::string sAchievementsLeaderboardSubmitAudioFile;
|
||||
|
||||
@@ -163,3 +163,15 @@ enum TiltTypes {
|
||||
TILT_ACTION_BUTTON,
|
||||
TILT_TRIGGER_BUTTONS,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
@@ -106,7 +106,7 @@ int DrawEngineCommon::ComputeNumVertsToDecode() const {
|
||||
void DrawEngineCommon::DecodeVerts(u8 *dest) {
|
||||
int decodeCounter = decodeCounter_;
|
||||
for (; decodeCounter < numDrawCalls_; decodeCounter++) {
|
||||
DecodeVertsStep(dest, decodeCounter, decodedVerts_, &drawCalls_[decodeCounter].uvScale); // NOTE! DecodeVertsStep can modify decodeCounter_!
|
||||
DecodeVertsStep(dest, decodeCounter, decodedVerts_, &drawCalls_[decodeCounter].uvScale); // NOTE! DecodeVertsStep can modify the decodeCounter parameter!
|
||||
}
|
||||
decodeCounter_ = decodeCounter;
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -852,6 +857,13 @@ void SystemInfoScreen::CreateTabs() {
|
||||
g_OSD.ShowLeaderboardTracker(1, nullptr, false);
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
|
||||
static const char *positions[] = { "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right", "None" };
|
||||
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
|
||||
|
||||
internals->Add(new ItemHeader(ac->T("Notifications")));
|
||||
internals->Add(new PopupMultiChoice(&g_Config.iAchievementsLeaderboardTrackerPos, ac->T("Leaderboard tracker"), positions, 0, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bAchievementsEnable);
|
||||
|
||||
#if PPSSPP_PLATFORM(ANDROID)
|
||||
internals->Add(new Choice(si->T("Exception")))->OnClick.Add([&](UI::EventParams &) {
|
||||
System_Notify(SystemNotification::TEST_JAVA_EXCEPTION);
|
||||
|
||||
@@ -113,6 +113,8 @@ public:
|
||||
|
||||
void CreateTabs() override;
|
||||
|
||||
void update() override;
|
||||
|
||||
protected:
|
||||
bool ShowSearchControls() const override { return false; }
|
||||
};
|
||||
|
||||
+9
-9
@@ -894,15 +894,15 @@ protected:
|
||||
// TODO: Shouldn't actually need bounds for this, Anchor can center too.
|
||||
static UI::AnchorLayoutParams *AnchorInCorner(const Bounds &bounds, int corner, float xOffset, float yOffset) {
|
||||
using namespace UI;
|
||||
switch (g_Config.iChatButtonPosition) {
|
||||
case 0: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, xOffset, NONE, NONE, yOffset, true);
|
||||
case 1: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, bounds.centerX(), NONE, NONE, yOffset, true);
|
||||
case 2: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, NONE, NONE, xOffset, yOffset, true);
|
||||
case 3: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, xOffset, yOffset, NONE, NONE, true);
|
||||
case 4: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, bounds.centerX(), yOffset, NONE, NONE, true);
|
||||
case 5: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, NONE, yOffset, xOffset, NONE, true);
|
||||
case 6: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, xOffset, bounds.centerY(), NONE, NONE, true);
|
||||
case 7: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, NONE, bounds.centerY(), xOffset, NONE, true);
|
||||
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::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);
|
||||
case ScreenEdgePosition::CENTER_LEFT: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, xOffset, bounds.centerY(), NONE, NONE, true);
|
||||
case ScreenEdgePosition::CENTER_RIGHT: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, NONE, bounds.centerY(), xOffset, NONE, true);
|
||||
default: return new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, xOffset, NONE, NONE, yOffset, true);
|
||||
}
|
||||
}
|
||||
|
||||
+183
-113
@@ -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,213 @@ 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;
|
||||
// If we have newlines, we may be looking at ASCII debug output. But let's verify.
|
||||
if (entry.text.find('\n') != 0) {
|
||||
if (!UTF8StringHasNonASCII(entry.text.c_str()))
|
||||
align |= FLAG_DYNAMIC_ASCII;
|
||||
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)g_Config.iAchievementsLeaderboardTrackerPos;
|
||||
|
||||
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];
|
||||
if (pos == ScreenEdgePosition::VALUE_COUNT) {
|
||||
// NONE.
|
||||
continue;
|
||||
}
|
||||
|
||||
float tw, th = 0.0f, h1 = 0.0f;
|
||||
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') != std::string::npos) {
|
||||
if (!UTF8StringHasNonASCII(entry.text.c_str()))
|
||||
measuredEntry.align |= FLAG_DYNAMIC_ASCII;
|
||||
}
|
||||
if (entry.text2.find('\n') != std::string::npos) {
|
||||
if (!UTF8StringHasNonASCII(entry.text2.c_str()))
|
||||
measuredEntry.align2 |= FLAG_DYNAMIC_ASCII;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -366,6 +366,11 @@ void RetroAchievementsSettingsScreen::CreateCustomizeTab(UI::ViewGroup *viewGrou
|
||||
viewGroup->Add(new ItemHeader(ac->T("Sound effects")));
|
||||
viewGroup->Add(new AudioFileChooser(&g_Config.sAchievementsUnlockAudioFile, ac->T("Achievement unlocked"), UISound::ACHIEVEMENT_UNLOCKED));
|
||||
viewGroup->Add(new AudioFileChooser(&g_Config.sAchievementsLeaderboardSubmitAudioFile, ac->T("Leaderboard score submission"), UISound::LEADERBOARD_SUBMITTED));
|
||||
|
||||
static const char *positions[] = { "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right", "None" };
|
||||
|
||||
viewGroup->Add(new ItemHeader(ac->T("Notifications")));
|
||||
viewGroup->Add(new PopupMultiChoice(&g_Config.iAchievementsLeaderboardTrackerPos, ac->T("Leaderboard tracker"), positions, 0, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bAchievementsEnable);
|
||||
}
|
||||
|
||||
void RetroAchievementsSettingsScreen::CreateDeveloperToolsTab(UI::ViewGroup *viewGroup) {
|
||||
|
||||
@@ -42,6 +42,7 @@ In Encore mode - listings may be wrong below = In Encore mode - listings may be
|
||||
Leaderboard score submission = Leaderboard score submission
|
||||
Leaderboard submission is enabled = Leaderboard submission is enabled
|
||||
Leaderboards = Leaderboards
|
||||
Leaderboard tracker = Leaderboard tracker
|
||||
Links = Links
|
||||
Locked achievements = Locked achievements
|
||||
Log bad memory accesses = Log bad memory accesses
|
||||
|
||||
@@ -10,7 +10,7 @@ Can't log in to RetroAchievements right now = Can't log in to RetroAchievements
|
||||
Challenge Mode = Utmanings-läge
|
||||
Challenge Mode (no savestates) = Utmanings-läge (inga sparade state)
|
||||
Contacting RetroAchievements server... = Contacting RetroAchievements server...
|
||||
Customize = Inställningar
|
||||
Customize = Anpassa
|
||||
Earned = Du har tjänat %d av %d achievements, och %d of %d poäng
|
||||
Encore Mode = Encore-läge (kan ta achievements igen)
|
||||
How to use RetroAchievements = Hur man använder RetroAchievements
|
||||
|
||||
Reference in new issue
Block a user