This repository has been archived on 2026-06-05. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
PatchExtractor/patches/borealis-applet-frame-hints.patch
niklascfw 2cf3db2097
All checks were successful
Build NRO / build (push) Successful in 1m48s
Add Borealis GUI for patch extraction on Switch.
Replace the console UI with a Borealis-based flow, bundle ROMFS assets and
borealis as a submodule, and apply small upstream patches at build time.
Self-delete runs after romfsExit on quit so the NRO can be removed like the
old console build.
2026-05-28 22:22:44 +02:00

384 lines
12 KiB
Diff

diff --git a/library/include/borealis/views/applet_frame.hpp b/library/include/borealis/views/applet_frame.hpp
index 1402928..beacd1a 100644
--- a/library/include/borealis/views/applet_frame.hpp
+++ b/library/include/borealis/views/applet_frame.hpp
@@ -19,6 +19,7 @@
#include <borealis/core/bind.hpp>
#include <borealis/core/box.hpp>
+#include <borealis/core/event.hpp>
#include <borealis/views/image.hpp>
#include <borealis/views/label.hpp>
@@ -30,6 +31,7 @@ class AppletFrame : public Box
{
public:
AppletFrame();
+ ~AppletFrame() override;
void handleXMLElement(tinyxml2::XMLElement* element) override;
@@ -48,8 +50,20 @@ class AppletFrame : public Box
static View* create();
private:
+ void updateFooterHints();
+ void clearHintBox(Box* box);
+ void addHintItem(Box* box, const Action& action, float spacingAfter);
+
BRLS_BIND(Label, title, "brls/applet_frame/title_label");
BRLS_BIND(Image, icon, "brls/applet_frame/title_icon");
+ BRLS_BIND(Box, footerHintRight, "brls/applet_frame/footer_hint_right");
+ BRLS_BIND(Box, footerHintLeft, "brls/applet_frame/footer_hint_left");
+
+ GenericEvent::Subscription focusSubscription;
+ VoidEvent::Subscription hintsSubscription;
+
+ int switchFont = FONT_INVALID;
+ int regularFont = FONT_INVALID;
protected:
View* contentView = nullptr;
diff --git a/library/include/borealis/views/label.hpp b/library/include/borealis/views/label.hpp
index 3e1ccef..797d328 100644
--- a/library/include/borealis/views/label.hpp
+++ b/library/include/borealis/views/label.hpp
@@ -85,6 +85,7 @@ class Label : public View
void setVerticalAlign(VerticalAlign align);
void setFontSize(float value);
+ void setFontFace(int fontFace);
void setLineHeight(float value);
void setTextColor(NVGcolor color);
diff --git a/library/lib/views/applet_frame.cpp b/library/lib/views/applet_frame.cpp
index f9f47dc..11b8cd6 100644
--- a/library/lib/views/applet_frame.cpp
+++ b/library/lib/views/applet_frame.cpp
@@ -15,6 +15,11 @@
limitations under the License.
*/
+#include <algorithm>
+#include <set>
+
+#include <borealis/core/application.hpp>
+#include <borealis/core/font.hpp>
#include <borealis/core/logger.hpp>
#include <borealis/core/util.hpp>
#include <borealis/views/applet_frame.hpp>
@@ -22,6 +27,73 @@
namespace brls
{
+namespace
+{
+
+constexpr float HINT_FONT_SIZE = 22.0f;
+constexpr float HINT_SPACING = 30.0f;
+constexpr float HINT_ICON_GAP = 4.0f;
+
+const char* getButtonIcon(ControllerButton button)
+{
+ switch (button)
+ {
+ case BUTTON_A:
+ return "\uE0E0";
+ case BUTTON_B:
+ return "\uE0E1";
+ case BUTTON_X:
+ return "\uE0E2";
+ case BUTTON_Y:
+ return "\uE0E3";
+ case BUTTON_START:
+ return "\uE0EF";
+ case BUTTON_BACK:
+ return "\uE0F0";
+ case BUTTON_UP:
+ return "\uE0EB";
+ case BUTTON_DOWN:
+ return "\uE0EC";
+ case BUTTON_LEFT:
+ return "\uE0ED";
+ case BUTTON_RIGHT:
+ return "\uE0EE";
+ default:
+ return "\uE152";
+ }
+}
+
+std::string getButtonHintText(const Action& action)
+{
+ if (!action.hintText.empty())
+ return action.hintText;
+
+ switch (action.button)
+ {
+ case BUTTON_A:
+ return "Ok";
+ case BUTTON_B:
+ return "Zurück";
+ case BUTTON_START:
+ return "Beenden";
+ default:
+ return "";
+ }
+}
+
+bool actionsSort(const Action& a, const Action& b)
+{
+ if (a.button == BUTTON_START)
+ return true;
+ if (b.button == BUTTON_A)
+ return true;
+ if (b.button == BUTTON_B && a.button != BUTTON_A)
+ return true;
+ return false;
+}
+
+} // namespace
+
const std::string appletFrameXML = R"xml(
<brls:Box
width="auto"
@@ -36,9 +108,9 @@ const std::string appletFrameXML = R"xml(
axis="row"
paddingTop="@style/brls/applet_frame/header_padding_top_bottom"
paddingBottom="@style/brls/applet_frame/header_padding_top_bottom"
- paddingLeft="@style/brls/applet_frame/header_padding_sides"
+ paddingLeft="26px"
paddingRight="@style/brls/applet_frame/header_padding_sides"
- marginLeft="@style/brls/applet_frame/padding_sides"
+ marginLeft="22px"
marginRight="@style/brls/applet_frame/padding_sides"
lineColor="@theme/brls/applet_frame/separator"
lineBottom="1px">
@@ -61,16 +133,12 @@ const std::string appletFrameXML = R"xml(
<!-- Content will be injected here with grow="1.0" -->
- <!--
- Footer
- Direction inverted so that the bottom left text can be
- set to visibility="gone" without affecting the hint
- -->
+ <!-- Footer -->
<brls:Box
width="auto"
height="@style/brls/applet_frame/footer_height"
axis="row"
- direction="rightToLeft"
+ direction="leftToRight"
paddingLeft="@style/brls/applet_frame/footer_padding_sides"
paddingRight="@style/brls/applet_frame/footer_padding_sides"
paddingTop="@style/brls/applet_frame/footer_padding_top_bottom"
@@ -81,15 +149,25 @@ const std::string appletFrameXML = R"xml(
lineTop="1px"
justifyContent="spaceBetween" >
- <brls:Rectangle
- width="272px"
+ <brls:Box
+ id="brls/applet_frame/footer_hint_left"
+ width="auto"
height="auto"
- color="#FF0000" />
+ axis="row"
+ direction="leftToRight"
+ justifyContent="flexStart"
+ alignItems="center"
+ visibility="gone" />
- <brls:Rectangle
- width="75px"
+ <brls:Box
+ id="brls/applet_frame/footer_hint_right"
+ width="auto"
height="auto"
- color="#FF00FF" />
+ grow="1.0"
+ axis="row"
+ direction="leftToRight"
+ justifyContent="flexEnd"
+ alignItems="center" />
</brls:Box>
@@ -109,6 +187,141 @@ AppletFrame::AppletFrame()
});
this->forwardXMLAttribute("iconInterpolation", this->icon, "interpolation");
+
+ this->switchFont = Application::getFont(FONT_SWITCH_ICONS);
+ this->regularFont = Application::getFont(FONT_REGULAR);
+
+ this->focusSubscription = Application::getGlobalFocusChangeEvent()->subscribe([this](View*) {
+ this->updateFooterHints();
+ });
+
+ this->hintsSubscription = Application::getGlobalHintsUpdateEvent()->subscribe([this]() {
+ this->updateFooterHints();
+ });
+
+ this->updateFooterHints();
+}
+
+AppletFrame::~AppletFrame()
+{
+ Application::getGlobalFocusChangeEvent()->unsubscribe(this->focusSubscription);
+ Application::getGlobalHintsUpdateEvent()->unsubscribe(this->hintsSubscription);
+}
+
+void AppletFrame::clearHintBox(Box* box)
+{
+ if (!box)
+ return;
+
+ std::vector<View*> children = box->getChildren();
+ while (!children.empty())
+ {
+ View* child = children.back();
+ box->removeView(child);
+ children.pop_back();
+ }
+}
+
+void AppletFrame::addHintItem(Box* box, const Action& action, float spacingAfter)
+{
+ const std::string hintText = getButtonHintText(action);
+ if (hintText.empty())
+ return;
+
+ Box* item = new Box(Axis::ROW);
+ item->setDirection(Direction::LEFT_TO_RIGHT);
+ item->setAlignItems(AlignItems::CENTER);
+ if (spacingAfter > 0.0f)
+ item->setMarginRight(spacingAfter);
+
+ Label* icon = new Label();
+ icon->setText(getButtonIcon(action.button));
+ icon->setFontSize(HINT_FONT_SIZE);
+ icon->setSingleLine(true);
+ if (this->switchFont != FONT_INVALID)
+ icon->setFontFace(this->switchFont);
+
+ Label* text = new Label();
+ text->setText(hintText);
+ text->setFontSize(HINT_FONT_SIZE);
+ text->setSingleLine(true);
+ text->setMarginLeft(HINT_ICON_GAP);
+ if (this->regularFont != FONT_INVALID)
+ text->setFontFace(this->regularFont);
+
+ item->addView(icon);
+ item->addView(text);
+ box->addView(item);
+}
+
+void AppletFrame::updateFooterHints()
+{
+ this->clearHintBox(this->footerHintRight);
+ this->clearHintBox(this->footerHintLeft);
+
+ std::set<ControllerButton> addedKeys;
+ std::vector<Action> rightActions;
+ std::vector<Action> leftActions;
+
+ View* focusParent = Application::getCurrentFocus();
+ if (!focusParent)
+ focusParent = this->contentView;
+
+ while (focusParent)
+ {
+ for (const Action& action : focusParent->getActions())
+ {
+ if (action.hidden || !action.available)
+ continue;
+
+ if (addedKeys.find(action.button) != addedKeys.end())
+ continue;
+
+ addedKeys.insert(action.button);
+
+ if (action.button == BUTTON_START)
+ leftActions.push_back(action);
+ else
+ rightActions.push_back(action);
+ }
+
+ focusParent = focusParent->getParent();
+ }
+
+ std::stable_sort(rightActions.begin(), rightActions.end(), actionsSort);
+ std::stable_sort(leftActions.begin(), leftActions.end(), actionsSort);
+
+ for (size_t i = 0; i < rightActions.size(); i++)
+ {
+ float spacing = (i + 1 < rightActions.size()) ? HINT_SPACING : 0.0f;
+ this->addHintItem(this->footerHintRight, rightActions[i], spacing);
+ }
+
+ for (size_t i = 0; i < leftActions.size(); i++)
+ {
+ float spacing = (i + 1 < leftActions.size()) ? HINT_SPACING : 0.0f;
+ this->addHintItem(this->footerHintLeft, leftActions[i], spacing);
+ }
+
+ std::string* commonFooter = Application::getCommonFooter();
+ if (commonFooter && !commonFooter->empty())
+ {
+ Label* text = new Label();
+ text->setText(*commonFooter);
+ text->setFontSize(HINT_FONT_SIZE);
+ text->setSingleLine(true);
+ if (!leftActions.empty())
+ text->setMarginLeft(HINT_SPACING);
+ if (this->regularFont != FONT_INVALID)
+ text->setFontFace(this->regularFont);
+ this->footerHintLeft->addView(text);
+ }
+
+ const bool hasRight = !this->footerHintRight->getChildren().empty();
+ const bool hasLeft = !this->footerHintLeft->getChildren().empty();
+
+ this->footerHintRight->setVisibility(hasRight ? Visibility::VISIBLE : Visibility::GONE);
+ this->footerHintLeft->setVisibility(hasLeft ? Visibility::VISIBLE : Visibility::GONE);
}
void AppletFrame::setIconFromRes(std::string name)
@@ -132,7 +345,6 @@ void AppletFrame::setContentView(View* view)
{
if (this->contentView)
{
- // Remove the node
this->removeView(this->contentView);
this->contentView = nullptr;
}
@@ -146,6 +358,7 @@ void AppletFrame::setContentView(View* view)
this->contentView->setGrow(1.0f);
this->addView(this->contentView, 1);
+ this->updateFooterHints();
}
void AppletFrame::handleXMLElement(tinyxml2::XMLElement* element)
diff --git a/library/lib/views/label.cpp b/library/lib/views/label.cpp
index c31d904..25e4bdf 100644
--- a/library/lib/views/label.cpp
+++ b/library/lib/views/label.cpp
@@ -316,6 +316,14 @@ void Label::setFontSize(float value)
this->invalidate();
}
+void Label::setFontFace(int fontFace)
+{
+ if (fontFace != FONT_INVALID)
+ this->font = fontFace;
+
+ this->invalidate();
+}
+
void Label::setLineHeight(float value)
{
this->lineHeight = value;