Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New UI view: Notice #17674

Merged
merged 2 commits into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Common/UI/Root.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,6 @@ void UpdateViewHierarchy(ViewGroup *root) {
}
root->SubviewFocused(GetFocusedView());
} else {
INFO_LOG(SCECTRL, "Processing focus moves.");
for (size_t i = 0; i < focusMoves.size(); i++) {
switch (focusMoves[i]) {
case NKCODE_DPAD_LEFT: MoveFocus(root, FOCUS_LEFT); break;
Expand All @@ -432,7 +431,6 @@ void UpdateViewHierarchy(ViewGroup *root) {
}
}
}
INFO_LOG(SCECTRL, "Clearing focus moves.");
focusMoves.clear();
}

Expand Down
1 change: 1 addition & 0 deletions Common/UI/View.h
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,7 @@ class Spinner : public InertView {
};

void MeasureBySpec(Size sz, float contentWidth, MeasureSpec spec, float *measured);
void ApplyBoundsBySpec(Bounds &bounds, MeasureSpec horiz, MeasureSpec vert);

bool IsDPadKey(const KeyInput &key);
bool IsAcceptKey(const KeyInput &key);
Expand Down
4 changes: 3 additions & 1 deletion GPU/D3D11/DrawEngineD3D11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ void DrawEngineD3D11::NotifyConfigChanged() {
}

void DrawEngineD3D11::DestroyDeviceObjects() {
draw_->SetInvalidationCallback(InvalidationCallback());
if (draw_) {
draw_->SetInvalidationCallback(InvalidationCallback());
}

ClearTrackedVertexArrays();
ClearInputLayoutMap();
Expand Down
3 changes: 2 additions & 1 deletion UI/ControlMappingScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include "UI/ControlMappingScreen.h"
#include "UI/GameSettingsScreen.h"
#include "UI/JoystickHistoryView.h"
#include "UI/OnScreenDisplay.h"

#if PPSSPP_PLATFORM(ANDROID)
#include "android/jni/app-android.h"
Expand Down Expand Up @@ -334,7 +335,7 @@ void KeyMappingNewKeyDialog::CreatePopupContents(UI::ViewGroup *parent) {
parent->Add(new TextView(std::string(km->T("Map a new key for")) + " " + mc->T(pspButtonName), new LinearLayoutParams(Margins(10, 0))));
parent->Add(new TextView(std::string(mapping_.ToVisualString()), new LinearLayoutParams(Margins(10, 0))));

comboMappingsNotEnabled_ = parent->Add(new TextView(km->T("Combo mappings are not enabled"), new LinearLayoutParams(Margins(10, 0))));
comboMappingsNotEnabled_ = parent->Add(new NoticeView(NoticeLevel::WARN, km->T("Combo mappings are not enabled"), "", new LinearLayoutParams(Margins(10, 0))));
comboMappingsNotEnabled_->SetVisibility(UI::V_GONE);

SetVRAppMode(VRAppMode::VR_CONTROLLER_MAPPING_MODE);
Expand Down
119 changes: 76 additions & 43 deletions UI/OnScreenDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,63 +18,62 @@
#include "Common/Net/HTTPClient.h"
#include "Core/Config.h"

static uint32_t GetOSDBackgroundColor(OSDType type) {
static const float g_atlasIconSize = 36.0f;
static const float extraTextScale = 0.7f;

static uint32_t GetNoticeBackgroundColor(NoticeLevel type) {
// Colors from Infima
switch (type) {
case OSDType::MESSAGE_ERROR:
case OSDType::MESSAGE_ERROR_DUMP: return 0x3530d5; // danger-darker
case OSDType::MESSAGE_WARNING: return 0x009ed9; // warning-darker
case OSDType::MESSAGE_INFO: return 0x706760; // gray-700
case OSDType::MESSAGE_SUCCESS: return 0x008b00;
case NoticeLevel::ERROR: return 0x3530d5; // danger-darker
case NoticeLevel::WARN: return 0x009ed9; // warning-darker
case NoticeLevel::INFO: return 0x706760; // gray-700
case NoticeLevel::SUCCESS: return 0x008b00; // nice green
default: return 0x606770;
}
}

ImageID GetOSDIcon(OSDType type) {
switch (type) {
case OSDType::MESSAGE_INFO: return ImageID::invalid(); // return ImageID("I_INFO");
case OSDType::MESSAGE_ERROR: return ImageID("I_CROSS");
case OSDType::MESSAGE_WARNING: return ImageID("I_WARNING");
case OSDType::MESSAGE_SUCCESS: return ImageID("I_CHECKEDBOX");
static ImageID GetOSDIcon(NoticeLevel level) {
switch (level) {
case NoticeLevel::INFO: return ImageID::invalid(); // return ImageID("I_INFO");
case NoticeLevel::ERROR: return ImageID("I_CROSS");
case NoticeLevel::WARN: return ImageID("I_WARNING");
case NoticeLevel::SUCCESS: return ImageID("I_CHECKEDBOX");
default: return ImageID::invalid();
}
}

static const float g_atlasIconSize = 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, float *height1) {
if (entry.type == OSDType::ACHIEVEMENT_UNLOCKED) {
const Achievements::Achievement *achievement = Achievements::GetAchievementByID(entry.numericID);
MeasureAchievement(dc, *achievement, width, height);
*width = 550.0f;
*height1 = *height;
return;
static NoticeLevel GetNoticeLevel(OSDType type) {
switch (type) {
case OSDType::MESSAGE_INFO: return NoticeLevel::INFO;
case OSDType::MESSAGE_ERROR:
case OSDType::MESSAGE_ERROR_DUMP: return NoticeLevel::ERROR;
case OSDType::MESSAGE_WARNING: return NoticeLevel::WARN;
case OSDType::MESSAGE_SUCCESS: return NoticeLevel::SUCCESS;
default: return NoticeLevel::SUCCESS;
}
}

dc.SetFontStyle(dc.theme->uiFont);
dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, entry.text.c_str(), width, height, align);
static void MeasureNotice(const UIContext &dc, NoticeLevel level, const std::string &text, const std::string &details, const std::string &iconName, int align, float *width, float *height, float *height1) {
dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, 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);
if (!details.empty()) {
dc.MeasureText(dc.theme->uiFont, extraTextScale, extraTextScale, details.c_str(), &width2, &height2, align);
*width = std::max(*width, width2);
*height += 5.0f + height2;
}

float iconSize = 0.0f;

if (!entry.iconName.empty()) {
if (!iconName.empty()) {
// Normal entry but with a cached icon.
int iconWidth, iconHeight;
if (g_iconCache.GetDimensions(entry.iconName, &iconWidth, &iconHeight)) {
if (g_iconCache.GetDimensions(iconName, &iconWidth, &iconHeight)) {
*width += 5.0f + iconWidth;
iconSize = iconWidth + 5.0f;
}
} else if (!GetOSDIcon(entry.type).isInvalid()) {
} else if (!GetOSDIcon(level).isInvalid()) {
// Atlas icon.
iconSize = g_atlasIconSize + 5.0f;
}
Expand All @@ -83,14 +82,20 @@ 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, float height1, int align, float alpha) {
// Align only matters here for the ASCII-only flag.
static void MeasureOSDEntry(const UIContext &dc, const OnScreenDisplay::Entry &entry, int align, float *width, float *height, float *height1) {
if (entry.type == OSDType::ACHIEVEMENT_UNLOCKED) {
const Achievements::Achievement *achievement = Achievements::GetAchievementByID(entry.numericID);
RenderAchievement(dc, *achievement, AchievementRenderStyle::UNLOCKED, bounds, alpha, entry.startTime, time_now_d());
return;
MeasureAchievement(dc, *achievement, width, height);
*width = 550.0f;
*height1 = *height;
} else {
MeasureNotice(dc, GetNoticeLevel(entry.type), entry.text, entry.text2, entry.iconName, align, width, height, height1);
}
}

UI::Drawable background = UI::Drawable(colorAlpha(GetOSDBackgroundColor(entry.type), alpha));
static void RenderNotice(UIContext &dc, Bounds bounds, float height1, NoticeLevel level, const std::string &text, const std::string &details, const std::string &iconName, int align, float alpha) {
UI::Drawable background = UI::Drawable(colorAlpha(GetNoticeBackgroundColor(level), alpha));

uint32_t foreGround = whiteAlpha(alpha);

Expand All @@ -100,13 +105,13 @@ static void RenderOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry, B

dc.FillRect(background, bounds);

ImageID iconID = GetOSDIcon(entry.type);
ImageID iconID = GetOSDIcon(level);

float iconSize = 0.0f;
if (!entry.iconName.empty()) {
if (!iconName.empty()) {
dc.Flush();
// Normal entry but with a cached icon.
Draw::Texture *texture = g_iconCache.BindIconTexture(&dc, entry.iconName);
Draw::Texture *texture = g_iconCache.BindIconTexture(&dc, iconName);
if (texture) {
iconSize = texture->Width();
dc.Draw()->DrawTexRect(Bounds(bounds.x + 2.5f, bounds.y + 2.5f, iconSize, iconSize), 0.0f, 0.0f, 1.0f, 1.0f, foreGround);
Expand All @@ -124,19 +129,29 @@ static void RenderOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry, B
bounds.x += iconSize + 5.0f;
bounds.w -= iconSize + 5.0f;

dc.DrawTextShadowRect(entry.text.c_str(), bounds.Inset(0.0f, 1.0f, 0.0f, 0.0f), foreGround, (align & FLAG_DYNAMIC_ASCII));
dc.DrawTextShadowRect(text.c_str(), bounds.Inset(0.0f, 1.0f, 0.0f, 0.0f), foreGround, (align & FLAG_DYNAMIC_ASCII));

if (!entry.text2.empty()) {
if (!details.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));
UI::Drawable backgroundDark = UI::Drawable(colorAlpha(darkenColor(GetNoticeBackgroundColor(level)), alpha));
dc.FillRect(backgroundDark, bottomTextBounds);
dc.SetFontScale(extraTextScale, extraTextScale);
dc.DrawTextRect(entry.text2.c_str(), bottomTextBounds, foreGround, (align & FLAG_DYNAMIC_ASCII) | ALIGN_LEFT);
dc.DrawTextRect(details.c_str(), bottomTextBounds, foreGround, (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) {
static void RenderOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry, Bounds bounds, float height1, int align, float alpha) {
if (entry.type == OSDType::ACHIEVEMENT_UNLOCKED) {
const Achievements::Achievement *achievement = Achievements::GetAchievementByID(entry.numericID);
RenderAchievement(dc, *achievement, AchievementRenderStyle::UNLOCKED, bounds, alpha, entry.startTime, time_now_d());
return;
}

RenderNotice(dc, bounds, height1, GetNoticeLevel(entry.type), entry.text, entry.text2, entry.iconName, align, alpha);
}

static void MeasureOSDProgressBar(const UIContext &dc, const OnScreenDisplay::ProgressBar &bar, float *width, float *height) {
*height = 36;
*width = 450.0f;
}
Expand Down Expand Up @@ -289,3 +304,21 @@ void OSDOverlayScreen::CreateViews() {
root_->SetTag("OSDOverlayScreen");
root_->Add(new OnScreenMessagesView(new UI::AnchorLayoutParams(0.0f, 0.0f, 0.0f, 0.0f)));
}

void NoticeView::GetContentDimensionsBySpec(const UIContext &dc, UI::MeasureSpec horiz, UI::MeasureSpec vert, float &w, float &h) const {
Bounds bounds(0, 0, layoutParams_->width, layoutParams_->height);
if (bounds.w < 0) {
// If there's no size, let's grow as big as we want.
bounds.w = horiz.size;
}
if (bounds.h < 0) {
bounds.h = vert.size;
}

ApplyBoundsBySpec(bounds, horiz, vert);
MeasureNotice(dc, level_, text_, detailsText_, iconName_, 0, &w, &h, &height1_);
}

void NoticeView::Draw(UIContext &dc) {
RenderNotice(dc, bounds_, height1_, level_, text_, detailsText_, iconName_, 0, 1.0f);
}
31 changes: 31 additions & 0 deletions UI/OnScreenDisplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#include "Common/UI/UIScreen.h"
#include "Common/System/System.h"

#ifdef ERROR
#undef ERROR
#endif

class DrawBuffer;

// Infrastructure for rendering overlays.
Expand All @@ -25,3 +29,30 @@ class OSDOverlayScreen : public UIScreen {
const char *tag() const override { return "OSDOverlayScreen"; }
void CreateViews() override;
};

enum class NoticeLevel {
SUCCESS,
INFO,
WARN,
ERROR,
};

class NoticeView : public UI::InertView {
public:
NoticeView(NoticeLevel level, const std::string &text, const std::string &detailsText, UI::LayoutParams *layoutParams = 0)
: InertView(layoutParams), level_(level), text_(text), detailsText_(detailsText), iconName_("") {}

void SetIconName(const std::string &name) {
iconName_ = name;
}

void GetContentDimensionsBySpec(const UIContext &dc, UI::MeasureSpec horiz, UI::MeasureSpec vert, float &w, float &h) const override;
void Draw(UIContext &dc) override;

private:
std::string text_;
std::string detailsText_;
std::string iconName_;
NoticeLevel level_;
mutable float height1_ = 0.0f;
};