mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-12-03 11:11:25 +00:00
Merge pull request #17607 from hrydgard/overlay-progress-bars
Overlay: Support progress bars and "extra text" in messages
This commit is contained in:
commit
14586d6125
@ -72,6 +72,9 @@ struct Bounds {
|
||||
Bounds Offset(float xAmount, float yAmount) const {
|
||||
return Bounds(x + xAmount, y + yAmount, w, h);
|
||||
}
|
||||
Bounds Inset(float left, float top, float right, float bottom) {
|
||||
return Bounds(x + left, y + top, w - left - right, h - bottom - top);
|
||||
}
|
||||
|
||||
float x;
|
||||
float y;
|
||||
|
@ -20,6 +20,14 @@ void OnScreenDisplay::Update() {
|
||||
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() {
|
||||
@ -27,7 +35,12 @@ std::vector<OnScreenDisplay::Entry> OnScreenDisplay::Entries() {
|
||||
return entries_; // makes a copy.
|
||||
}
|
||||
|
||||
void OnScreenDisplay::Show(OSDType type, const std::string &text, float duration_s, const char *id) {
|
||||
std::vector<OnScreenDisplay::ProgressBar> OnScreenDisplay::ProgressBars() {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
return bars_; // makes a copy.
|
||||
}
|
||||
|
||||
void OnScreenDisplay::Show(OSDType type, const std::string &text, const std::string &text2, float duration_s, const char *id) {
|
||||
// Automatic duration based on type.
|
||||
if (duration_s <= 0.0f) {
|
||||
switch (type) {
|
||||
@ -55,7 +68,9 @@ void OnScreenDisplay::Show(OSDType type, const std::string &text, float duration
|
||||
Entry msg = *iter;
|
||||
msg.endTime = now + duration_s;
|
||||
msg.text = text;
|
||||
msg.text2 = text2;
|
||||
msg.type = type;
|
||||
// Move to top (should we? maybe not?)
|
||||
entries_.erase(iter);
|
||||
entries_.insert(entries_.begin(), msg);
|
||||
return;
|
||||
@ -65,6 +80,7 @@ void OnScreenDisplay::Show(OSDType type, const std::string &text, float duration
|
||||
|
||||
Entry msg;
|
||||
msg.text = text;
|
||||
msg.text2 = text2;
|
||||
msg.endTime = now + duration_s;
|
||||
msg.type = type;
|
||||
msg.id = id;
|
||||
@ -76,6 +92,42 @@ void OnScreenDisplay::ShowOnOff(const std::string &message, bool on, float durat
|
||||
Show(OSDType::MESSAGE_INFO, message + ": " + (on ? "on" : "off"), duration_s);
|
||||
}
|
||||
|
||||
void OnScreenDisplay::SetProgressBar(std::string id, std::string &&message, int minValue, int maxValue, int progress) {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
double now = time_now_d();
|
||||
bool found = false;
|
||||
for (auto &bar : bars_) {
|
||||
if (bar.id == id) {
|
||||
bar.minValue = minValue;
|
||||
bar.maxValue = maxValue;
|
||||
bar.progress = progress;
|
||||
bar.message = message;
|
||||
bar.endTime = now + 60.0; // Nudge the progress bar to keep it shown.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ProgressBar bar;
|
||||
bar.id = id;
|
||||
bar.message = std::move(message);
|
||||
bar.minValue = minValue;
|
||||
bar.maxValue = maxValue;
|
||||
bar.progress = progress;
|
||||
bar.endTime = now + 60.0; // Show the progress bar for 60 seconds, then fade it out.
|
||||
bars_.push_back(bar);
|
||||
}
|
||||
|
||||
void OnScreenDisplay::RemoveProgressBar(std::string id, float fadeout_s) {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
for (auto iter = bars_.begin(); iter != bars_.end(); iter++) {
|
||||
if (iter->id == id) {
|
||||
iter->progress = iter->maxValue;
|
||||
iter->endTime = time_now_d() + (double)fadeout_s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *RequestTypeAsString(SystemRequestType type) {
|
||||
switch (type) {
|
||||
case SystemRequestType::INPUT_TEXT_MODAL: return "INPUT_TEXT_MODAL";
|
||||
|
@ -228,7 +228,10 @@ enum class OSDType {
|
||||
class OnScreenDisplay {
|
||||
public:
|
||||
// If you specify 0.0f as duration, a duration will be chosen automatically depending on type.
|
||||
void Show(OSDType type, const std::string &message, float duration_s = 0.0f, const char *id = nullptr);
|
||||
void Show(OSDType type, const std::string &text, float duration_s = 0.0f, const char *id = nullptr) {
|
||||
Show(type, text, "", duration_s, id);
|
||||
}
|
||||
void Show(OSDType type, const std::string &text, const std::string &text2, float duration_s = 0.0f, const char *id = nullptr);
|
||||
void ShowOnOff(const std::string &message, bool on, float duration_s = 0.0f);
|
||||
|
||||
bool IsEmpty() const { return entries_.empty(); } // Shortcut to skip rendering.
|
||||
@ -236,18 +239,35 @@ public:
|
||||
// Call this every frame, cleans up old entries.
|
||||
void Update();
|
||||
|
||||
// Progress bar controls
|
||||
// Set is both create and update.
|
||||
void SetProgressBar(std::string id, std::string &&message, int minValue, int maxValue, int progress);
|
||||
void RemoveProgressBar(std::string id, float fadeout_s);
|
||||
|
||||
struct Entry {
|
||||
OSDType type;
|
||||
std::string text;
|
||||
std::string text2;
|
||||
const char *id;
|
||||
double endTime;
|
||||
double duration;
|
||||
float progress;
|
||||
};
|
||||
|
||||
struct ProgressBar {
|
||||
std::string id;
|
||||
std::string message;
|
||||
int minValue;
|
||||
int maxValue;
|
||||
int progress;
|
||||
double endTime;
|
||||
};
|
||||
|
||||
std::vector<Entry> Entries();
|
||||
std::vector<ProgressBar> ProgressBars();
|
||||
|
||||
private:
|
||||
std::vector<Entry> entries_;
|
||||
std::vector<ProgressBar> bars_;
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
|
@ -73,13 +73,13 @@ u32 BlockDevice::CalculateCRC(volatile bool *cancel) {
|
||||
void BlockDevice::NotifyReadError() {
|
||||
auto err = GetI18NCategory(I18NCat::ERRORS);
|
||||
if (!reportedError_) {
|
||||
g_OSD.Show(OSDType::MESSAGE_ERROR, err->T("Game disc read error - ISO corrupt"), 6.0f);
|
||||
g_OSD.Show(OSDType::MESSAGE_WARNING, err->T("Game disc read error - ISO corrupt"), fileLoader_->GetPath().ToVisualString(), 6.0f);
|
||||
reportedError_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
FileBlockDevice::FileBlockDevice(FileLoader *fileLoader)
|
||||
: fileLoader_(fileLoader) {
|
||||
: BlockDevice(fileLoader) {
|
||||
filesize_ = fileLoader->FileSize();
|
||||
}
|
||||
|
||||
@ -137,7 +137,7 @@ typedef struct ciso_header
|
||||
static const u32 CSO_READ_BUFFER_SIZE = 256 * 1024;
|
||||
|
||||
CISOFileBlockDevice::CISOFileBlockDevice(FileLoader *fileLoader)
|
||||
: fileLoader_(fileLoader)
|
||||
: BlockDevice(fileLoader)
|
||||
{
|
||||
// CISO format is fairly simple, but most tools do not write the header_size.
|
||||
|
||||
@ -382,7 +382,7 @@ bool CISOFileBlockDevice::ReadBlocks(u32 minBlock, int count, u8 *outPtr) {
|
||||
}
|
||||
|
||||
NPDRMDemoBlockDevice::NPDRMDemoBlockDevice(FileLoader *fileLoader)
|
||||
: fileLoader_(fileLoader)
|
||||
: BlockDevice(fileLoader)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
MAC_KEY mkey;
|
||||
|
@ -32,6 +32,7 @@ class FileLoader;
|
||||
|
||||
class BlockDevice {
|
||||
public:
|
||||
BlockDevice(FileLoader *fileLoader) : fileLoader_(fileLoader) {}
|
||||
virtual ~BlockDevice() {}
|
||||
virtual bool ReadBlock(int blockNumber, u8 *outPtr, bool uncached = false) = 0;
|
||||
virtual bool ReadBlocks(u32 minBlock, int count, u8 *outPtr) {
|
||||
@ -51,6 +52,7 @@ public:
|
||||
void NotifyReadError();
|
||||
|
||||
protected:
|
||||
FileLoader *fileLoader_;
|
||||
bool reportedError_ = false;
|
||||
};
|
||||
|
||||
@ -64,7 +66,6 @@ public:
|
||||
bool IsDisc() override { return true; }
|
||||
|
||||
private:
|
||||
FileLoader *fileLoader_;
|
||||
u32 *index;
|
||||
u8 *readBuffer;
|
||||
u8 *zlibBuffer;
|
||||
@ -88,7 +89,6 @@ public:
|
||||
bool IsDisc() override { return true; }
|
||||
|
||||
private:
|
||||
FileLoader *fileLoader_;
|
||||
u64 filesize_;
|
||||
};
|
||||
|
||||
@ -113,7 +113,6 @@ public:
|
||||
bool IsDisc() override { return false; }
|
||||
|
||||
private:
|
||||
FileLoader *fileLoader_;
|
||||
static std::mutex mutex_;
|
||||
u32 lbaSize;
|
||||
|
||||
|
@ -804,7 +804,7 @@ void SystemInfoScreen::CreateTabs() {
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
internals->Add(new Choice(si->T("Warning")))->OnClick.Add([&](UI::EventParams &) {
|
||||
g_OSD.Show(OSDType::MESSAGE_WARNING, si->T("Warning"));
|
||||
g_OSD.Show(OSDType::MESSAGE_WARNING, si->T("Warning"), "Some\nAdditional\nDetail");
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
internals->Add(new Choice(si->T("Info")))->OnClick.Add([&](UI::EventParams &) {
|
||||
@ -815,6 +815,24 @@ void SystemInfoScreen::CreateTabs() {
|
||||
g_OSD.Show(OSDType::MESSAGE_SUCCESS, si->T("Success"));
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
internals->Add(new ItemHeader(si->T("Progress tests")));
|
||||
internals->Add(new Choice(si->T("30%")))->OnClick.Add([&](UI::EventParams &) {
|
||||
g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 30);
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
internals->Add(new Choice(si->T("100%")))->OnClick.Add([&](UI::EventParams &) {
|
||||
g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 100);
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
internals->Add(new Choice(si->T("N/A%")))->OnClick.Add([&](UI::EventParams &) {
|
||||
g_OSD.SetProgressBar("testprogress", "Test Progress", 0, 0, 0);
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
internals->Add(new Choice(si->T("Clear")))->OnClick.Add([&](UI::EventParams &) {
|
||||
g_OSD.RemoveProgressBar("testprogress", 0.25f);
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void AddressPromptScreen::CreatePopupContents(UI::ViewGroup *parent) {
|
||||
|
@ -229,6 +229,8 @@ bool EmuScreen::bootAllowStorage(const Path &filename) {
|
||||
}
|
||||
|
||||
void EmuScreen::bootGame(const Path &filename) {
|
||||
auto sc = GetI18NCategory(I18NCat::SCREEN);
|
||||
|
||||
if (PSP_IsRebooting())
|
||||
return;
|
||||
if (PSP_IsInited()) {
|
||||
@ -259,8 +261,6 @@ void EmuScreen::bootGame(const Path &filename) {
|
||||
if (!bootAllowStorage(filename))
|
||||
return;
|
||||
|
||||
auto sc = GetI18NCategory(I18NCat::SCREEN);
|
||||
|
||||
invalid_ = true;
|
||||
|
||||
// We don't want to boot with the wrong game specific config, so wait until info is ready.
|
||||
|
@ -40,9 +40,19 @@ ImageID GetOSDIcon(OSDType type) {
|
||||
|
||||
static const float iconSize = 36.0f;
|
||||
|
||||
static const float extraTextScale = 0.7f;
|
||||
|
||||
// Align only matters here for the ASCII-only flag.
|
||||
static void MeasureOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry, int align, float *width, float *height) {
|
||||
static void MeasureOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry, int align, float *width, float *height, float *height1) {
|
||||
dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, entry.text.c_str(), width, height, align);
|
||||
*height1 = *height;
|
||||
|
||||
float width2 = 0.0f, height2 = 0.0f;
|
||||
if (!entry.text2.empty()) {
|
||||
dc.MeasureText(dc.theme->uiFont, extraTextScale, extraTextScale, entry.text2.c_str(), &width2, &height2, align);
|
||||
*width = std::max(*width, width2);
|
||||
*height += 5.0f + height2;
|
||||
}
|
||||
|
||||
if (!GetOSDIcon(entry.type).isInvalid()) {
|
||||
*width += iconSize + 5.0f;
|
||||
@ -52,7 +62,7 @@ static void MeasureOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry,
|
||||
*height = std::max(*height, iconSize + 5.0f);
|
||||
}
|
||||
|
||||
static void RenderOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry, Bounds bounds, int align, float alpha) {
|
||||
static void RenderOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry, Bounds bounds, float height1, int align, float alpha) {
|
||||
UI::Drawable background = UI::Drawable(colorAlpha(GetOSDBackgroundColor(entry.type), alpha));
|
||||
|
||||
uint32_t foreGround = whiteAlpha(alpha);
|
||||
@ -74,7 +84,61 @@ static void RenderOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry, B
|
||||
bounds.w -= iconSize + 5.0f;
|
||||
}
|
||||
|
||||
dc.DrawTextShadowRect(entry.text.c_str(), bounds, colorAlpha(0xFFFFFFFF, alpha), (align & FLAG_DYNAMIC_ASCII) | ALIGN_CENTER);
|
||||
dc.DrawTextShadowRect(entry.text.c_str(), bounds.Inset(0.0f, 1.0f, 0.0f, 0.0f), colorAlpha(0xFFFFFFFF, alpha), (align & FLAG_DYNAMIC_ASCII));
|
||||
|
||||
if (!entry.text2.empty()) {
|
||||
Bounds bottomTextBounds = bounds.Inset(3.0f, height1 + 5.0f, 3.0f, 3.0f);
|
||||
UI::Drawable backgroundDark = UI::Drawable(colorAlpha(darkenColor(GetOSDBackgroundColor(entry.type)), alpha));
|
||||
dc.FillRect(backgroundDark, bottomTextBounds);
|
||||
dc.SetFontScale(extraTextScale, extraTextScale);
|
||||
dc.DrawTextRect(entry.text2.c_str(), bottomTextBounds, colorAlpha(0xFFFFFFFF, alpha), (align & FLAG_DYNAMIC_ASCII) | ALIGN_LEFT);
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
static void MeasureOSDProgressBar(UIContext &dc, const OnScreenDisplay::ProgressBar &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) {
|
||||
uint32_t foreGround = whiteAlpha(alpha);
|
||||
|
||||
Bounds shadowBounds = bounds.Expand(10.0f);
|
||||
|
||||
dc.Draw()->DrawImage4Grid(dc.theme->dropShadow4Grid, shadowBounds.x, shadowBounds.y + 4.0f, shadowBounds.x2(), shadowBounds.y2(), alphaMul(0xFF000000, 0.9f * alpha), 1.0f);
|
||||
|
||||
uint32_t backgroundColor = colorAlpha(0x806050, alpha);
|
||||
uint32_t progressBackgroundColor = colorAlpha(0xa08070, alpha);
|
||||
|
||||
if (entry.maxValue > entry.minValue) {
|
||||
// Normal progress bar
|
||||
|
||||
UI::Drawable background = UI::Drawable(backgroundColor);
|
||||
UI::Drawable progressBackground = UI::Drawable(progressBackgroundColor);
|
||||
|
||||
float ratio = (float)(entry.progress - entry.minValue) / (float)entry.maxValue;
|
||||
|
||||
Bounds boundLeft = bounds;
|
||||
Bounds boundRight = bounds;
|
||||
|
||||
boundLeft.w *= ratio;
|
||||
boundRight.x += ratio * boundRight.w;
|
||||
boundRight.w *= (1.0f - ratio);
|
||||
|
||||
dc.FillRect(progressBackground, boundLeft);
|
||||
dc.FillRect(background, boundRight);
|
||||
} else {
|
||||
// Indeterminate spinner
|
||||
float alpha = cos(time_now_d() * 5.0) * 0.5f + 0.5f;
|
||||
uint32_t pulse = colorBlend(backgroundColor, progressBackgroundColor, alpha);
|
||||
UI::Drawable background = UI::Drawable(pulse);
|
||||
dc.FillRect(background, bounds);
|
||||
}
|
||||
|
||||
dc.SetFontStyle(dc.theme->uiFont);
|
||||
|
||||
dc.DrawTextShadowRect(entry.message.c_str(), bounds, colorAlpha(0xFFFFFFFF, alpha), (align & FLAG_DYNAMIC_ASCII) | ALIGN_CENTER);
|
||||
}
|
||||
|
||||
void OnScreenMessagesView::Draw(UIContext &dc) {
|
||||
@ -82,14 +146,27 @@ void OnScreenMessagesView::Draw(UIContext &dc) {
|
||||
return;
|
||||
}
|
||||
|
||||
double now = time_now_d();
|
||||
|
||||
// Get height
|
||||
float w, h;
|
||||
dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, "Wg", &w, &h);
|
||||
|
||||
float y = 10.0f;
|
||||
// Then draw them all.
|
||||
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 alpha = Clamp((float)(bar.endTime - now) * 4.0f, 0.0f, 1.0f);
|
||||
RenderOSDProgressBar(dc, bar, b, 0, alpha);
|
||||
y += (b.h + 4.0f) * alpha; // including alpha here gets us smooth animations.
|
||||
}
|
||||
|
||||
const std::vector<OnScreenDisplay::Entry> entries = g_OSD.Entries();
|
||||
double now = time_now_d();
|
||||
for (auto iter = entries.begin(); iter != entries.end(); ++iter) {
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
// Messages that are wider than the screen are left-aligned instead of centered.
|
||||
@ -101,8 +178,8 @@ void OnScreenMessagesView::Draw(UIContext &dc) {
|
||||
align |= FLAG_DYNAMIC_ASCII;
|
||||
}
|
||||
|
||||
float tw, th;
|
||||
MeasureOSDEntry(dc, *iter, align, &tw, &th);
|
||||
float tw, th, h1;
|
||||
MeasureOSDEntry(dc, *iter, align, &tw, &th, &h1);
|
||||
|
||||
Bounds b(0.0f, y, tw, th);
|
||||
|
||||
@ -125,7 +202,7 @@ void OnScreenMessagesView::Draw(UIContext &dc) {
|
||||
}
|
||||
|
||||
float alpha = Clamp((float)(iter->endTime - now) * 4.0f, 0.0f, 1.0f);
|
||||
RenderOSDEntry(dc, *iter, b, align, alpha);
|
||||
RenderOSDEntry(dc, *iter, b, h1, align, alpha);
|
||||
y += (b.h * scale + 4.0f) * alpha; // including alpha here gets us smooth animations.
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user