Add support for displaying progress bars

This commit is contained in:
Henrik Rydgård 2023-06-20 21:26:42 +02:00
parent 9a3b2add27
commit d2c3a7c978
4 changed files with 125 additions and 2 deletions

View file

@ -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,6 +35,11 @@ std::vector<OnScreenDisplay::Entry> OnScreenDisplay::Entries() {
return entries_; // makes a copy.
}
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, float duration_s, const char *id) {
// Automatic duration based on type.
if (duration_s <= 0.0f) {
@ -76,6 +89,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) {
_dbg_assert_(minValue == bar.minValue);
_dbg_assert_(maxValue == bar.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";

View file

@ -236,18 +236,34 @@ 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;
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_;
};

View file

@ -815,6 +815,20 @@ 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("Clear")))->OnClick.Add([&](UI::EventParams &) {
g_OSD.RemoveProgressBar("testprogress", 0.25f);
return UI::EVENT_DONE;
});
}
void AddressPromptScreen::CreatePopupContents(UI::ViewGroup *parent) {

View file

@ -77,19 +77,63 @@ static void RenderOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry, B
dc.DrawTextShadowRect(entry.text.c_str(), bounds, colorAlpha(0xFFFFFFFF, alpha), (align & FLAG_DYNAMIC_ASCII) | ALIGN_CENTER);
}
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) {
UI::Drawable background = UI::Drawable(colorAlpha(0x806050, alpha));
UI::Drawable progressBackground = UI::Drawable(colorAlpha(0xa08070, 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);
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);
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) {
if (!g_Config.bShowOnScreenMessages) {
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.