All checks were successful
Build NRO / build (push) Successful in 1m48s
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.
384 lines
12 KiB
Diff
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;
|