From fd17b7725df7fb0c7491ee48264df20b5afa904f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 27 Oct 2026 11:55:40 +0100 Subject: [PATCH 1/3] Add a "firmware" start screen, for testing the firmware screen Co-Authored-By: Claude Opus 5.5 --- UI/NativeApp.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 40f037a2dd..7f3a66736a 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -141,6 +141,7 @@ #include "UI/DevScreens.h" #include "UI/DiscordIntegration.h" #include "UI/EmuScreen.h" +#include "UI/FirmwareScreen.h" #include "UI/GameInfoCache.h" #include "UI/GameSettingsScreen.h" #include "UI/GPUDriverTestScreen.h" @@ -900,6 +901,9 @@ void NativeInit(int argc, const char *argv[], const CommandLineOptions &cmdLineO GameSettingsScreen *settings = new GameSettingsScreen(Path()); settings->OpenDeveloperTools(); g_screenManager->push(settings); + } else if (equals(cmdLineOptions.startScreen.value(), "firmware")) { + g_screenManager->switchScreen(new MainScreen()); + g_screenManager->push(new FirmwareScreen(Path())); } else { WARN_LOG(Log::System, "Unknown start screen '%s'", cmdLineOptions.startScreen.value().c_str()); g_screenManager->switchScreen(new LogoScreen(AfterLogoScreen::DEFAULT)); From a941ad634dc489649b8263c26f2add517c4d0f97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 27 Oct 2026 11:55:40 +0100 Subject: [PATCH 2/3] NoticeView: Round corners like a Choice, pad the contents, fill the width RenderNotice and MeasureNotice take padding (added inside the background) and the corners to round. The drop shadow moves out of RenderNotice to the floating OSD notices, since views don't have one. Without layout params, a NoticeView is FILL_PARENT wide like a Choice. Co-Authored-By: Claude Opus 5.5 --- Common/UI/Notice.cpp | 12 ++++++------ Common/UI/Notice.h | 10 ++++++---- UI/OnScreenDisplay.cpp | 18 ++++++++++++++++-- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Common/UI/Notice.cpp b/Common/UI/Notice.cpp index a5ee6c88eb..e0b117220f 100644 --- a/Common/UI/Notice.cpp +++ b/Common/UI/Notice.cpp @@ -47,7 +47,7 @@ NoticeLevel GetNoticeLevel(OSDType type) { constexpr float m = 4.0f; // Align only matters here for the ASCII-only flag. -void MeasureNotice(const UIContext &dc, NoticeLevel level, std::string_view text, std::string_view details, std::string_view iconName, int align, float maxWidth, float *width, float *height, float *height1) { +void MeasureNotice(const UIContext &dc, NoticeLevel level, std::string_view text, std::string_view details, std::string_view iconName, int align, float maxWidth, float *width, float *height, float *height1, float padding) { float iconW = 0.0f; float iconH = 0.0f; if (!iconName.empty() && !startsWith(iconName, "I_")) { // Check for atlas image. Bit hacky, but we choose prefixes for icon IDs anyway in a way that this is safe. @@ -64,7 +64,7 @@ void MeasureNotice(const UIContext &dc, NoticeLevel level, std::string_view text } } - float chromeWidth = iconW + m * 2.0f + 12.0f; + float chromeWidth = iconW + m * 2.0f + 12.0f + padding * 2.0f; float availableWidth = maxWidth - chromeWidth; // OK, now that we have figured out how much space we have for the text, we can measure it (with wrapping if needed). @@ -89,10 +89,10 @@ void MeasureNotice(const UIContext &dc, NoticeLevel level, std::string_view text } float heightSum = *height1 + (height2 > 0.0f ? height2 + 8.0f : 0.0f); - *height = std::max(heightSum, iconH + m); + *height = std::max(heightSum, iconH + m) + padding * 2.0f; } -void RenderNotice(UIContext &dc, Bounds bounds, float height1, NoticeLevel level, std::string_view text, std::string_view details, std::string_view iconName, int align, float alpha, OSDMessageFlags flags, float timeVal) { +void RenderNotice(UIContext &dc, Bounds bounds, float height1, NoticeLevel level, std::string_view text, std::string_view details, std::string_view iconName, int align, float alpha, OSDMessageFlags flags, float timeVal, float padding, RoundCorners corners) { UI::Drawable background = UI::Drawable(colorAlpha(GetNoticeBackgroundColor(level), alpha)); dc.SetFontStyle(dc.GetTheme().uiFont); @@ -100,9 +100,9 @@ void RenderNotice(UIContext &dc, Bounds bounds, float height1, NoticeLevel level uint32_t foreGround = whiteAlpha(alpha); if (!(flags & OSDMessageFlags::Transparent)) { - dc.DrawRectDropShadow(bounds, 12.0f, 0.7f * alpha); - dc.FillRect(background, bounds); + dc.FillRoundRect(background, bounds, dc.GetTheme().choiceRoundRadius, corners); } + bounds = bounds.Expand(-padding); float iconW = 0.0f; float iconH = 0.0f; diff --git a/Common/UI/Notice.h b/Common/UI/Notice.h index 5962e77502..fee57922de 100644 --- a/Common/UI/Notice.h +++ b/Common/UI/Notice.h @@ -4,6 +4,7 @@ #include #include +#include "Common/Render/DrawEnum.h" #include "Common/UI/View.h" #include "Common/System/OSD.h" @@ -18,8 +19,7 @@ enum class NoticeLevel { class NoticeView : public UI::InertView { public: - NoticeView(NoticeLevel level, std::string_view text, std::string_view detailsText, UI::LayoutParams *layoutParams = 0) - : InertView(layoutParams), level_(level), text_(text), detailsText_(detailsText) {} + NoticeView(NoticeLevel level, std::string_view text, std::string_view detailsText, UI::LayoutParams *layoutParams = nullptr); void SetIconName(std::string_view name) { iconName_ = name; @@ -56,5 +56,7 @@ private: ImageID GetOSDIcon(NoticeLevel level); NoticeLevel GetNoticeLevel(OSDType type); uint32_t GetNoticeBackgroundColor(NoticeLevel type); -void MeasureNotice(const UIContext &dc, NoticeLevel level, std::string_view text, std::string_view details, std::string_view iconName, int align, float maxWidth, float *width, float *height, float *height1); -void RenderNotice(UIContext &dc, Bounds bounds, float height1, NoticeLevel level, std::string_view text, std::string_view details, std::string_view iconName, int align, float alpha, OSDMessageFlags flags, float timeVal); +// padding is added around the contents, inside the background. Pass the same to both. +void MeasureNotice(const UIContext &dc, NoticeLevel level, std::string_view text, std::string_view details, std::string_view iconName, int align, float maxWidth, float *width, float *height, float *height1, float padding = 0.0f); +// Doesn't draw a drop shadow, floating notices draw their own. +void RenderNotice(UIContext &dc, Bounds bounds, float height1, NoticeLevel level, std::string_view text, std::string_view details, std::string_view iconName, int align, float alpha, OSDMessageFlags flags, float timeVal, float padding = 0.0f, RoundCorners corners = RoundCorners::None); diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index c0af73f7a8..616b200aba 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -46,6 +46,9 @@ static void RenderOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry, B } return; } else { + if (!(entry.flags & OSDMessageFlags::Transparent)) { + dc.DrawRectDropShadow(bounds, 12.0f, 0.7f * alpha); + } RenderNotice(dc, bounds, height1, GetNoticeLevel(entry.type), entry.text, entry.text2, entry.iconName, align, alpha, entry.flags, now - entry.startTime); } } @@ -424,6 +427,17 @@ ViewLayoutMode OSDOverlayScreen::LayoutMode() const { return ViewLayoutMode::ApplyInsets; } +// Inside the background, like the text padding of a Choice. +static const float g_noticeViewPadding = 8.0f; + +NoticeView::NoticeView(NoticeLevel level, std::string_view text, std::string_view detailsText, UI::LayoutParams *layoutParams) + : InertView(layoutParams), level_(level), text_(text), detailsText_(detailsText) { + if (!layoutParams) { + layoutParams_->width = UI::FILL_PARENT; + layoutParams_->height = UI::WRAP_CONTENT; + } +} + void NoticeView::GetContentDimensionsBySpec(const UIContext &dc, UI::MeasureSpec horiz, UI::MeasureSpec vert, float &w, float &h) const { float layoutWidth = layoutParams_->width; if (layoutWidth < 0) { @@ -432,12 +446,12 @@ void NoticeView::GetContentDimensionsBySpec(const UIContext &dc, UI::MeasureSpec } ApplyBoundBySpec(layoutWidth, horiz); const int align = wrapText_ ? FLAG_WRAP_TEXT : 0; - MeasureNotice(dc, level_, text_, detailsText_, iconName_, align, layoutWidth, &w, &h, &height1_); + MeasureNotice(dc, level_, text_, detailsText_, iconName_, align, layoutWidth, &w, &h, &height1_, g_noticeViewPadding); } void NoticeView::Draw(UIContext &dc) { dc.PushScissor(bounds_); const int align = wrapText_ ? FLAG_WRAP_TEXT : 0; - RenderNotice(dc, bounds_, height1_, level_, text_, detailsText_, iconName_, align, 1.0f, OSDMessageFlags::None, 0.0f); + RenderNotice(dc, bounds_, height1_, level_, text_, detailsText_, iconName_, align, 1.0f, OSDMessageFlags::None, 0.0f, g_noticeViewPadding, roundCorners_); dc.PopScissor(); } From adf905ce7ae6bb0dfcab05164599873455bb13a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 27 Oct 2026 13:29:01 +0100 Subject: [PATCH 3/3] NoticeView: Take the padding from the theme's corner radius Co-Authored-By: Claude Opus 5.5 --- UI/OnScreenDisplay.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index 616b200aba..314cb1dfda 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -427,8 +427,10 @@ ViewLayoutMode OSDOverlayScreen::LayoutMode() const { return ViewLayoutMode::ApplyInsets; } -// Inside the background, like the text padding of a Choice. -static const float g_noticeViewPadding = 8.0f; +// Inside the background, enough to keep the contents clear of the rounded corners. +static float NoticeViewPadding(const UIContext &dc) { + return dc.GetTheme().choiceRoundRadius * 0.5f; +} NoticeView::NoticeView(NoticeLevel level, std::string_view text, std::string_view detailsText, UI::LayoutParams *layoutParams) : InertView(layoutParams), level_(level), text_(text), detailsText_(detailsText) { @@ -446,12 +448,12 @@ void NoticeView::GetContentDimensionsBySpec(const UIContext &dc, UI::MeasureSpec } ApplyBoundBySpec(layoutWidth, horiz); const int align = wrapText_ ? FLAG_WRAP_TEXT : 0; - MeasureNotice(dc, level_, text_, detailsText_, iconName_, align, layoutWidth, &w, &h, &height1_, g_noticeViewPadding); + MeasureNotice(dc, level_, text_, detailsText_, iconName_, align, layoutWidth, &w, &h, &height1_, NoticeViewPadding(dc)); } void NoticeView::Draw(UIContext &dc) { dc.PushScissor(bounds_); const int align = wrapText_ ? FLAG_WRAP_TEXT : 0; - RenderNotice(dc, bounds_, height1_, level_, text_, detailsText_, iconName_, align, 1.0f, OSDMessageFlags::None, 0.0f, g_noticeViewPadding, roundCorners_); + RenderNotice(dc, bounds_, height1_, level_, text_, detailsText_, iconName_, align, 1.0f, OSDMessageFlags::None, 0.0f, NoticeViewPadding(dc), roundCorners_); dc.PopScissor(); }