mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-04-10 19:15:13 +00:00
Added a system status component
This commit is contained in:
parent
eeff59773d
commit
86a554d1b2
|
@ -18,6 +18,7 @@
|
|||
#include "MameNames.h"
|
||||
#include "Scripting.h"
|
||||
#include "SystemData.h"
|
||||
#include "SystemStatus.h"
|
||||
#include "UIModeController.h"
|
||||
#include "Window.h"
|
||||
#include "utils/FileSystemUtil.h"
|
||||
|
@ -2103,10 +2104,12 @@ returnValue = Utils::Platform::launchGameUnix(command, startDirectory, runInBack
|
|||
}
|
||||
|
||||
// Unless we're running in the background while the game is launched, re-enable the text
|
||||
// scrolling that was disabled in ViewController.
|
||||
// scrolling that was disabled in ViewController. Also poll the system status immediately
|
||||
// in case something changed while the game was running.
|
||||
if (!runInBackground) {
|
||||
window->setAllowTextScrolling(true);
|
||||
window->setAllowFileAnimation(true);
|
||||
SystemStatus::getInstance().pollImmediately();
|
||||
}
|
||||
|
||||
// Update number of times the game has been launched.
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "FileFilterIndex.h"
|
||||
#include "Settings.h"
|
||||
#include "SystemData.h"
|
||||
#include "SystemStatus.h"
|
||||
#include "Window.h"
|
||||
#include "components/HelpComponent.h"
|
||||
#include "guis/GuiTextEditKeyboardPopup.h"
|
||||
|
@ -36,6 +37,7 @@ GuiSettings::GuiSettings(std::string title)
|
|||
, mNeedsGoToStart {false}
|
||||
, mNeedsGoToSystem {false}
|
||||
, mNeedsGoToGroupedCollections {false}
|
||||
, mNeedsUpdateStatusComponents {false}
|
||||
, mInvalidateCachedBackground {false}
|
||||
{
|
||||
addChild(&mMenu);
|
||||
|
@ -143,6 +145,15 @@ void GuiSettings::save()
|
|||
ViewController::getInstance()->goToSystem(SystemData::sSystemVector.front(), false);
|
||||
}
|
||||
|
||||
if (mNeedsUpdateStatusComponents) {
|
||||
SystemStatus::getInstance().setCheckFlags();
|
||||
SystemStatus::getInstance().pollImmediately();
|
||||
// If we're not done within this time window it's not the end of the world,
|
||||
// the indicators will still get updated during the next poll.
|
||||
SDL_Delay(100);
|
||||
mWindow->updateSystemStatusComponents();
|
||||
}
|
||||
|
||||
if (mNeedsCollectionsUpdate) {
|
||||
auto state = ViewController::getInstance()->getState();
|
||||
// If we're in any view other than the grouped custom collections, always jump to the
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
mGoToSystem = goToSystem;
|
||||
};
|
||||
void setNeedsGoToGroupedCollections() { mNeedsGoToGroupedCollections = true; }
|
||||
void setNeedsUpdateStatusComponents() { mNeedsUpdateStatusComponents = true; }
|
||||
void setNeedsCloseMenu(std::function<void()> closeFunction)
|
||||
{
|
||||
mCloseMenuFunction = closeFunction;
|
||||
|
@ -84,6 +85,7 @@ private:
|
|||
bool mNeedsGoToStart;
|
||||
bool mNeedsGoToSystem;
|
||||
bool mNeedsGoToGroupedCollections;
|
||||
bool mNeedsUpdateStatusComponents;
|
||||
bool mInvalidateCachedBackground;
|
||||
};
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "components/LottieAnimComponent.h"
|
||||
#include "components/RatingComponent.h"
|
||||
#include "components/ScrollableContainer.h"
|
||||
#include "components/SystemStatusComponent.h"
|
||||
#include "components/TextComponent.h"
|
||||
#include "components/VideoFFmpegComponent.h"
|
||||
#include "components/primary/CarouselComponent.h"
|
||||
|
|
|
@ -92,10 +92,14 @@ void GamelistView::onShow()
|
|||
video->stopVideoPlayer();
|
||||
|
||||
mWindow->passClockComponents(&mClockComponents);
|
||||
mWindow->passSystemStatusComponents(&mSystemStatusComponents);
|
||||
|
||||
for (auto& clock : mClockComponents)
|
||||
clock->update(500);
|
||||
|
||||
for (auto& systemstatus : mSystemStatusComponents)
|
||||
systemstatus->update(SystemStatus::updateTime);
|
||||
|
||||
mLastUpdated = nullptr;
|
||||
GuiComponent::onShow();
|
||||
|
||||
|
@ -365,6 +369,11 @@ void GamelistView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
|||
mClockComponents.emplace_back(std::make_unique<DateTimeComponent>());
|
||||
mClockComponents.back()->applyTheme(theme, "gamelist", element.first, ALL);
|
||||
}
|
||||
else if (element.second.type == "systemstatus") {
|
||||
mSystemStatusComponents.emplace_back(std::make_unique<SystemStatusComponent>());
|
||||
mSystemStatusComponents.back()->applyTheme(theme, "gamelist", element.first, ALL);
|
||||
mSystemStatusComponents.back()->updateGrid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -375,6 +384,14 @@ void GamelistView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
|||
mClockComponents.back()->update(1000);
|
||||
}
|
||||
|
||||
if (mSystemStatusComponents.empty()) {
|
||||
// Apply a default systemstatus if the theme does not contain any configuration for it.
|
||||
mSystemStatusComponents.emplace_back(std::make_unique<SystemStatusComponent>());
|
||||
mSystemStatusComponents.back()->applyTheme(theme, "gamelist", "systemstatus_default",
|
||||
ThemeFlags::ALL);
|
||||
mSystemStatusComponents.back()->updateGrid();
|
||||
}
|
||||
|
||||
if (mPrimary == nullptr) {
|
||||
mTextList = std::make_unique<TextListComponent<FileData*>>();
|
||||
mPrimary = mTextList.get();
|
||||
|
|
|
@ -138,6 +138,7 @@ private:
|
|||
std::vector<std::unique_ptr<TextComponent>> mGamelistInfoComponents;
|
||||
std::vector<std::unique_ptr<HelpComponent>> mHelpComponents;
|
||||
std::vector<std::unique_ptr<DateTimeComponent>> mClockComponents;
|
||||
std::vector<std::unique_ptr<SystemStatusComponent>> mSystemStatusComponents;
|
||||
};
|
||||
|
||||
#endif // ES_APP_VIEWS_GAMELIST_VIEW_H
|
||||
|
|
|
@ -248,6 +248,8 @@ void SystemView::onCursorChanged(const CursorState& state)
|
|||
{
|
||||
mWindow->passHelpComponents(nullptr);
|
||||
mWindow->passClockComponents(&mSystemElements[mPrimary->getCursor()].clockComponents);
|
||||
mWindow->passSystemStatusComponents(
|
||||
&mSystemElements[mPrimary->getCursor()].systemStatusComponents);
|
||||
|
||||
if (Settings::getInstance()->getBool("CustomEventScripts") &&
|
||||
Settings::getInstance()->getBool("CustomEventScriptsBrowsing")) {
|
||||
|
@ -260,6 +262,9 @@ void SystemView::onCursorChanged(const CursorState& state)
|
|||
for (auto& clock : mSystemElements[mPrimary->getCursor()].clockComponents)
|
||||
clock->update(1000);
|
||||
|
||||
for (auto& systemstatus : mSystemElements[mPrimary->getCursor()].systemStatusComponents)
|
||||
systemstatus->update(SystemStatus::updateTime);
|
||||
|
||||
// Reset horizontally scrolling text.
|
||||
for (auto& text : mSystemElements[mPrimary->getCursor()].gameCountComponents)
|
||||
text->resetComponent();
|
||||
|
@ -745,6 +750,13 @@ void SystemView::populate()
|
|||
elements.clockComponents.back()->applyTheme(theme, "system", element.first,
|
||||
ThemeFlags::ALL);
|
||||
}
|
||||
else if (element.second.type == "systemstatus") {
|
||||
elements.systemStatusComponents.emplace_back(
|
||||
std::make_unique<SystemStatusComponent>());
|
||||
elements.systemStatusComponents.back()->applyTheme(
|
||||
theme, "system", element.first, ThemeFlags::ALL);
|
||||
elements.systemStatusComponents.back()->updateGrid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -756,6 +768,14 @@ void SystemView::populate()
|
|||
elements.clockComponents.back()->update(1000);
|
||||
}
|
||||
|
||||
if (elements.systemStatusComponents.empty()) {
|
||||
// Apply a default systemstatus if the theme does not contain any configuration for it.
|
||||
elements.systemStatusComponents.emplace_back(std::make_unique<SystemStatusComponent>());
|
||||
elements.systemStatusComponents.back()->applyTheme(
|
||||
theme, "system", "systemstatus_default", ThemeFlags::ALL);
|
||||
elements.systemStatusComponents.back()->updateGrid();
|
||||
}
|
||||
|
||||
std::stable_sort(
|
||||
elements.children.begin(), elements.children.end(),
|
||||
[](GuiComponent* a, GuiComponent* b) { return b->getZIndex() > a->getZIndex(); });
|
||||
|
@ -921,6 +941,8 @@ void SystemView::populate()
|
|||
mWindow->passHelpComponents(&mSystemElements[mPrimary->getCursor()].helpComponents);
|
||||
|
||||
mWindow->passClockComponents(&mSystemElements[mPrimary->getCursor()].clockComponents);
|
||||
mWindow->passSystemStatusComponents(
|
||||
&mSystemElements[mPrimary->getCursor()].systemStatusComponents);
|
||||
|
||||
mFadeTransitions = (static_cast<ViewTransitionAnimation>(Settings::getInstance()->getInt(
|
||||
"TransitionsSystemToSystem")) == ViewTransitionAnimation::FADE);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "components/LottieAnimComponent.h"
|
||||
#include "components/RatingComponent.h"
|
||||
#include "components/ScrollableContainer.h"
|
||||
#include "components/SystemStatusComponent.h"
|
||||
#include "components/TextComponent.h"
|
||||
#include "components/VideoFFmpegComponent.h"
|
||||
#include "components/primary/CarouselComponent.h"
|
||||
|
@ -139,6 +140,7 @@ private:
|
|||
std::vector<std::unique_ptr<RatingComponent>> ratingComponents;
|
||||
std::vector<std::unique_ptr<HelpComponent>> helpComponents;
|
||||
std::vector<std::unique_ptr<DateTimeComponent>> clockComponents;
|
||||
std::vector<std::unique_ptr<SystemStatusComponent>> systemStatusComponents;
|
||||
};
|
||||
|
||||
Renderer* mRenderer;
|
||||
|
|
|
@ -63,6 +63,7 @@ set(CORE_HEADERS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ScrollIndicatorComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/SliderComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/SystemStatusComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextEditComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoComponent.h
|
||||
|
@ -139,6 +140,7 @@ set(CORE_SOURCES
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/ScrollableContainer.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/SliderComponent.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/SystemStatusComponent.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextEditComponent.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoComponent.cpp
|
||||
|
|
|
@ -95,6 +95,15 @@ void SystemStatus::setCheckFlags()
|
|||
mCheckWifi = Settings::getInstance()->getBool("SystemStatusWifi");
|
||||
mCheckCellular = Settings::getInstance()->getBool("SystemStatusCellular");
|
||||
mCheckBattery = Settings::getInstance()->getBool("SystemStatusBattery");
|
||||
|
||||
if (!mCheckBluetooth)
|
||||
mHasBluetooth = false;
|
||||
if (!mCheckWifi)
|
||||
mHasWifi = false;
|
||||
if (!mCheckCellular)
|
||||
mHasCellular = false;
|
||||
if (!mCheckBattery)
|
||||
mHasBattery = false;
|
||||
}
|
||||
|
||||
void SystemStatus::setPolling(const bool state)
|
||||
|
@ -116,13 +125,33 @@ void SystemStatus::setPolling(const bool state)
|
|||
}
|
||||
}
|
||||
|
||||
SystemStatus::Status SystemStatus::getStatus()
|
||||
SystemStatus::Status SystemStatus::getStatus(const bool update)
|
||||
{
|
||||
#if defined(__ANDROID__)
|
||||
getStatusBluetooth();
|
||||
getStatusWifi();
|
||||
getStatusCellular();
|
||||
getStatusBattery();
|
||||
if (update) {
|
||||
getStatusBluetooth();
|
||||
getStatusWifi();
|
||||
getStatusCellular();
|
||||
getStatusBattery();
|
||||
#if (DEBUG_SYSTEM_STATUS)
|
||||
std::string status {"Bluetooth "};
|
||||
status.append(mHasBluetooth ? "enabled" : "disabled")
|
||||
.append(", Wi-Fi ")
|
||||
.append(mHasWifi ? "enabled" : "disabled")
|
||||
.append(", cellular ")
|
||||
.append(mHasCellular ? "enabled" : "disabled")
|
||||
.append(", battery ")
|
||||
.append(mHasBattery ? "enabled" : "disabled");
|
||||
if (mHasBattery) {
|
||||
status.append(" (")
|
||||
.append(mBatteryCharging ? "charging" : "not charging")
|
||||
.append(" and at ")
|
||||
.append(std::to_string(mBatteryCapacity))
|
||||
.append("% capacity)");
|
||||
}
|
||||
LOG(LogDebug) << "SystemStatus::getStatus(): " << status;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
mStatus.hasBluetooth = mHasBluetooth;
|
||||
|
@ -166,7 +195,7 @@ void SystemStatus::pollStatus()
|
|||
#endif
|
||||
|
||||
int delayValue {0};
|
||||
while (!mPollImmediately && !mExitPolling && delayValue < 3000) {
|
||||
while (!mPollImmediately && !mExitPolling && delayValue < pollingTime) {
|
||||
delayValue += 100;
|
||||
SDL_Delay(100);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ public:
|
|||
void setCheckFlags();
|
||||
void setPolling(const bool state);
|
||||
void pollImmediately() { mPollImmediately = true; }
|
||||
const bool getPollImmediately() { return mPollImmediately; }
|
||||
|
||||
struct Status {
|
||||
bool hasBluetooth;
|
||||
|
@ -42,7 +43,10 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
Status getStatus();
|
||||
Status getStatus(const bool update = true);
|
||||
|
||||
static constexpr int updateTime {300};
|
||||
static constexpr int pollingTime {2500};
|
||||
|
||||
private:
|
||||
SystemStatus() noexcept;
|
||||
|
|
|
@ -134,6 +134,8 @@ std::map<std::string, std::map<std::string, std::string>> ThemeData::sPropertyAt
|
|||
{"customControllerIcon", "controller"}}},
|
||||
{"helpsystem",
|
||||
{{"customButtonIcon", "button"}}},
|
||||
{"systemstatus",
|
||||
{{"customIcon", "icon"}}},
|
||||
};
|
||||
|
||||
std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
|
||||
|
@ -589,6 +591,22 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
|
|||
{"lineSpacing", FLOAT},
|
||||
{"format", STRING},
|
||||
{"opacity", FLOAT}}},
|
||||
{"systemstatus",
|
||||
{{"pos", NORMALIZED_PAIR},
|
||||
{"size", NORMALIZED_PAIR},
|
||||
{"origin", NORMALIZED_PAIR},
|
||||
{"fontPath", PATH},
|
||||
{"textRelativeScale", FLOAT},
|
||||
{"color", COLOR},
|
||||
{"backgroundColor", COLOR},
|
||||
{"backgroundColorEnd", COLOR},
|
||||
{"backgroundGradientType", STRING},
|
||||
{"backgroundPadding", NORMALIZED_PAIR},
|
||||
{"backgroundCornerRadius", FLOAT},
|
||||
{"entries", STRING},
|
||||
{"entrySpacing", FLOAT},
|
||||
{"customIcon", PATH},
|
||||
{"opacity", FLOAT}}},
|
||||
{"sound",
|
||||
{{"path", PATH}}}};
|
||||
// clang-format on
|
||||
|
|
|
@ -32,6 +32,7 @@ Window::Window() noexcept
|
|||
: mRenderer {Renderer::getInstance()}
|
||||
, mHelpComponents {nullptr}
|
||||
, mClockComponents {nullptr}
|
||||
, mSystemStatusComponents {nullptr}
|
||||
, mSplashTextPositions {0.0f, 0.0f, 0.0f, 0.0f}
|
||||
, mBackgroundOverlayOpacity {1.0f}
|
||||
, mScreensaver {nullptr}
|
||||
|
@ -465,6 +466,11 @@ void Window::update(int deltaTime)
|
|||
clockComponent->update(deltaTime);
|
||||
}
|
||||
|
||||
if (mSystemStatusComponents != nullptr) {
|
||||
for (auto& systemStatusComponent : *mSystemStatusComponents)
|
||||
systemStatusComponent->update(deltaTime);
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__) || defined(__IOS__)
|
||||
if (Settings::getInstance()->getBool("InputTouchOverlay"))
|
||||
InputOverlay::getInstance().update(deltaTime);
|
||||
|
@ -692,6 +698,11 @@ void Window::render()
|
|||
clockComponent->render(trans);
|
||||
}
|
||||
|
||||
if (mSystemStatusComponents != nullptr) {
|
||||
for (auto& systemStatusComponent : *mSystemStatusComponents)
|
||||
systemStatusComponent->render(trans);
|
||||
}
|
||||
|
||||
if (mInfoPopup)
|
||||
mInfoPopup->render(trans);
|
||||
|
||||
|
|
|
@ -14,9 +14,11 @@
|
|||
#include "HelpPrompt.h"
|
||||
#include "InputConfig.h"
|
||||
#include "Settings.h"
|
||||
#include "SystemStatus.h"
|
||||
#include "components/DateTimeComponent.h"
|
||||
#include "components/HelpComponent.h"
|
||||
#include "components/ImageComponent.h"
|
||||
#include "components/SystemStatusComponent.h"
|
||||
#include "components/TextComponent.h"
|
||||
#include "guis/GuiInfoPopup.h"
|
||||
#include "resources/Font.h"
|
||||
|
@ -179,6 +181,20 @@ public:
|
|||
mClockComponents = clockComponents;
|
||||
}
|
||||
|
||||
void passSystemStatusComponents(
|
||||
std::vector<std::unique_ptr<SystemStatusComponent>>* systemstatusComponents)
|
||||
{
|
||||
mSystemStatusComponents = systemstatusComponents;
|
||||
}
|
||||
|
||||
void updateSystemStatusComponents()
|
||||
{
|
||||
if (mSystemStatusComponents != nullptr) {
|
||||
for (auto& systemStatusComponent : *mSystemStatusComponents)
|
||||
systemStatusComponent->update(SystemStatus::pollingTime);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Window() noexcept;
|
||||
~Window();
|
||||
|
@ -198,6 +214,7 @@ private:
|
|||
std::vector<std::unique_ptr<HelpComponent>>* mHelpComponents;
|
||||
std::unique_ptr<HelpComponent> mHelp;
|
||||
std::vector<std::unique_ptr<DateTimeComponent>>* mClockComponents;
|
||||
std::vector<std::unique_ptr<SystemStatusComponent>>* mSystemStatusComponents;
|
||||
std::unique_ptr<ImageComponent> mBackgroundOverlay;
|
||||
std::unique_ptr<ImageComponent> mSplash;
|
||||
std::unique_ptr<TextComponent> mSplashTextScanning;
|
||||
|
|
391
es-core/src/components/SystemStatusComponent.cpp
Normal file
391
es-core/src/components/SystemStatusComponent.cpp
Normal file
|
@ -0,0 +1,391 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// ES-DE Frontend
|
||||
// SystemStatusComponent.cpp
|
||||
//
|
||||
// Displays system status information (Bluetooth, Wi-Fi, cellular and battery).
|
||||
//
|
||||
|
||||
#include "components/SystemStatusComponent.h"
|
||||
|
||||
#include "SystemStatus.h"
|
||||
#include "Window.h"
|
||||
#include "utils/FileSystemUtil.h"
|
||||
|
||||
#define PREFIX "icon_"
|
||||
|
||||
SystemStatusComponent::SystemStatusComponent()
|
||||
: mRenderer {Renderer::getInstance()}
|
||||
, mHasBluetooth {false}
|
||||
, mHasWifi {false}
|
||||
, mHasCellular {false}
|
||||
, mHasBattery {false}
|
||||
, mBatteryCharging {false}
|
||||
, mBatteryCapacity {100}
|
||||
, mEntries {sAllowedEntries}
|
||||
, mColorShift {0xFFFFFFFF}
|
||||
, mBackgroundColor {0x00000000}
|
||||
, mBackgroundColorEnd {0x00000000}
|
||||
, mAccumulator {0}
|
||||
, mAccumulatorAndroid {0}
|
||||
, mBackgroundPadding {0.0f, 0.0f}
|
||||
, mBackgroundCornerRadius {0.0f}
|
||||
, mColorGradientHorizontal {true}
|
||||
, mEntrySpacing {0.005f * mRenderer->getScreenWidth()}
|
||||
{
|
||||
}
|
||||
|
||||
void SystemStatusComponent::updateGrid()
|
||||
{
|
||||
mGrid.reset();
|
||||
|
||||
if (Settings::getInstance()->getBool("SystemStatusDisplayAll")) {
|
||||
mHasBluetooth = true;
|
||||
mHasWifi = true;
|
||||
mHasCellular = true;
|
||||
mHasBattery = true;
|
||||
}
|
||||
|
||||
mDisplayEntries.clear();
|
||||
|
||||
if (mHasBluetooth && Settings::getInstance()->getBool("SystemStatusBluetooth") &&
|
||||
std::find(mEntries.cbegin(), mEntries.cend(), "bluetooth") != mEntries.cend())
|
||||
mDisplayEntries.emplace_back("bluetooth");
|
||||
if (mHasWifi && Settings::getInstance()->getBool("SystemStatusWifi") &&
|
||||
std::find(mEntries.cbegin(), mEntries.cend(), "wifi") != mEntries.cend())
|
||||
mDisplayEntries.emplace_back("wifi");
|
||||
if (mHasCellular && Settings::getInstance()->getBool("SystemStatusCellular") &&
|
||||
std::find(mEntries.cbegin(), mEntries.cend(), "cellular") != mEntries.cend())
|
||||
mDisplayEntries.emplace_back("cellular");
|
||||
if (mHasBattery && Settings::getInstance()->getBool("SystemStatusBattery") &&
|
||||
std::find(mEntries.cbegin(), mEntries.cend(), "battery") != mEntries.cend())
|
||||
mDisplayEntries.emplace_back("battery");
|
||||
|
||||
if (mDisplayEntries.empty())
|
||||
return;
|
||||
|
||||
const bool batteryText {Settings::getInstance()->getBool("SystemStatusBattery") &&
|
||||
Settings::getInstance()->getBool("SystemStatusBatteryPercentage")};
|
||||
|
||||
int numEntries {static_cast<int>(mDisplayEntries.size())};
|
||||
if (mEntrySpacing != 0.0f)
|
||||
numEntries += numEntries - 1;
|
||||
if (mHasBattery && batteryText)
|
||||
++numEntries;
|
||||
|
||||
mGrid = std::make_shared<ComponentGrid>(glm::ivec2 {numEntries, 1});
|
||||
mEntryMap.clear();
|
||||
|
||||
float width {0.0f};
|
||||
int i {0};
|
||||
|
||||
for (auto it = mDisplayEntries.cbegin(); it != mDisplayEntries.cend(); ++it) {
|
||||
if (*it == "battery") {
|
||||
mBattery = std::make_shared<ImageComponent>(false, true);
|
||||
mBattery->setImage(mIconPathMap["battery_full"]);
|
||||
mBattery->setColorShift(mColorShift);
|
||||
mBattery->setResize(0, mSize.y);
|
||||
mBattery->setOpacity(mThemeOpacity);
|
||||
width += std::round(mBattery->getSize().x);
|
||||
mGrid->setEntry(mBattery, glm::ivec2 {i, 0}, false, false);
|
||||
}
|
||||
else {
|
||||
std::shared_ptr<ImageComponent> icon {std::make_shared<ImageComponent>(false, true)};
|
||||
icon->setImage(mIconPathMap[*it]);
|
||||
icon->setColorShift(mColorShift);
|
||||
icon->setResize(0, mSize.y);
|
||||
icon->setOpacity(mThemeOpacity);
|
||||
width += std::round(icon->getSize().x);
|
||||
mGrid->setEntry(icon, glm::ivec2 {i, 0}, false, false);
|
||||
}
|
||||
|
||||
mEntryMap[*it] = i;
|
||||
++i;
|
||||
|
||||
if (mEntrySpacing != 0.0f && *it != mDisplayEntries.back()) {
|
||||
++i;
|
||||
width += mEntrySpacing;
|
||||
mGrid->setEntry(std::make_shared<GuiComponent>(), glm::ivec2 {i, 0}, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (mHasBattery && batteryText) {
|
||||
// We set the initial value to "100%" to calculate the cell size based on this, as this
|
||||
// will be the longest text that will ever be displayed for the battery capacity.
|
||||
mBatteryPercentage = std::make_shared<TextComponent>(
|
||||
"100%", mFont, 0xFFFFFFFF, ALIGN_LEFT, ALIGN_CENTER, glm::ivec2 {1, 0},
|
||||
glm::vec3 {0.0f, 0.0f, 0.0f}, glm::vec2 {0.0f, 0.0f}, 0x00000000, 1.0f);
|
||||
mBatteryPercentage->setColor(mColorShift);
|
||||
mBatteryPercentage->setOpacity(mThemeOpacity);
|
||||
width += mBatteryPercentage->getSize().x;
|
||||
mEntryMap["batteryText"] = i;
|
||||
mGrid->setEntry(mBatteryPercentage, glm::ivec2 {i, 0}, false, false);
|
||||
}
|
||||
|
||||
for (int i {0}; i < static_cast<int>(mGrid->getChildCount()); ++i) {
|
||||
mGrid->setColWidthPerc(i, mGrid->getChild(i)->getSize().x / width);
|
||||
if (mHasBattery && batteryText && i == static_cast<int>(mGrid->getChildCount()) - 2)
|
||||
continue;
|
||||
|
||||
if (mEntrySpacing != 0.0f && i != static_cast<int>(mGrid->getChildCount()) - 1) {
|
||||
++i;
|
||||
mGrid->setColWidthPerc(i, mEntrySpacing / width);
|
||||
}
|
||||
}
|
||||
|
||||
mGrid->setSize(width, mSize.y);
|
||||
mGrid->setOrigin(mOrigin);
|
||||
mSize.x = width;
|
||||
}
|
||||
|
||||
void SystemStatusComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||
const std::string& view,
|
||||
const std::string& element,
|
||||
unsigned int properties)
|
||||
{
|
||||
// Apply default settings as the theme may not define any configuration.
|
||||
const glm::vec2 scale {glm::vec2 {Renderer::getScreenWidth(), Renderer::getScreenHeight()}};
|
||||
mPosition = glm::vec3 {0.984f * scale.x, 0.016f * scale.y, 0.0f};
|
||||
mSize = glm::vec2 {0.0f, 0.035f} * scale;
|
||||
mOrigin = glm::vec2 {1.0f, 0.0f};
|
||||
mColor = 0xFFFFFFFF;
|
||||
|
||||
mIconPathMap.clear();
|
||||
mIconPathMap["bluetooth"] = ":/graphics/systemstatus/bluetooth.svg";
|
||||
mIconPathMap["wifi"] = ":/graphics/systemstatus/wifi.svg";
|
||||
mIconPathMap["cellular"] = ":/graphics/systemstatus/cellular.svg";
|
||||
mIconPathMap["battery_charging"] = ":/graphics/systemstatus/battery_charging.svg";
|
||||
mIconPathMap["battery_low"] = ":/graphics/systemstatus/battery_low.svg";
|
||||
mIconPathMap["battery_medium"] = ":/graphics/systemstatus/battery_medium.svg";
|
||||
mIconPathMap["battery_high"] = ":/graphics/systemstatus/battery_high.svg";
|
||||
mIconPathMap["battery_full"] = ":/graphics/systemstatus/battery_full.svg";
|
||||
|
||||
GuiComponent::applyTheme(theme, view, element, properties);
|
||||
|
||||
const ThemeData::ThemeElement* elem {theme->getElement(view, element, "systemstatus")};
|
||||
|
||||
float textRelativeScale {0.9f};
|
||||
|
||||
if (!elem) {
|
||||
mSize = glm::round(mSize);
|
||||
mFont = {Font::get(mSize.y * textRelativeScale, FONT_PATH_LIGHT)};
|
||||
return;
|
||||
}
|
||||
|
||||
mSize.x = 0.0f;
|
||||
mSize.y = glm::clamp(mSize.y, 0.01f * scale.y, 0.5f * scale.y);
|
||||
mSize = glm::round(mSize);
|
||||
|
||||
if (elem->has("textRelativeScale"))
|
||||
textRelativeScale = glm::clamp(elem->get<float>("textRelativeScale"), 0.5f, 1.0f);
|
||||
|
||||
if (elem->has("fontPath"))
|
||||
mFont = {Font::get(mSize.y * textRelativeScale, elem->get<std::string>("fontPath"))};
|
||||
else
|
||||
mFont = {Font::get(mSize.y * textRelativeScale, FONT_PATH_LIGHT)};
|
||||
|
||||
if (elem->has("color"))
|
||||
mColorShift = elem->get<unsigned int>("color");
|
||||
|
||||
if (elem->has("colorEnd"))
|
||||
mColorShiftEnd = elem->get<unsigned int>("colorEnd");
|
||||
else
|
||||
mColorShiftEnd = mColorShift;
|
||||
|
||||
if (elem->has("backgroundColor")) {
|
||||
mBackgroundColor = elem->get<unsigned int>("backgroundColor");
|
||||
|
||||
if (elem->has("backgroundColorEnd"))
|
||||
mBackgroundColorEnd = elem->get<unsigned int>("backgroundColorEnd");
|
||||
else
|
||||
mBackgroundColorEnd = mBackgroundColor;
|
||||
|
||||
if (elem->has("backgroundGradientType")) {
|
||||
const std::string& backgroundGradientType {
|
||||
elem->get<std::string>("backgroundGradientType")};
|
||||
if (backgroundGradientType == "horizontal") {
|
||||
mColorGradientHorizontal = true;
|
||||
}
|
||||
else if (backgroundGradientType == "vertical") {
|
||||
mColorGradientHorizontal = false;
|
||||
}
|
||||
else {
|
||||
mColorGradientHorizontal = true;
|
||||
LOG(LogWarning) << "SystemStatusComponent: Invalid theme configuration, property "
|
||||
"\"backgroundGradientType\" for element \""
|
||||
<< element.substr(13) << "\" defined as \""
|
||||
<< backgroundGradientType << "\"";
|
||||
}
|
||||
}
|
||||
|
||||
if (elem->has("backgroundPadding")) {
|
||||
const glm::vec2 backgroundPadding {
|
||||
glm::clamp(elem->get<glm::vec2>("backgroundPadding"), 0.0f, 0.2f)};
|
||||
mBackgroundPadding.x = backgroundPadding.x * mRenderer->getScreenWidth();
|
||||
mBackgroundPadding.y = backgroundPadding.y * mRenderer->getScreenHeight();
|
||||
}
|
||||
|
||||
if (elem->has("backgroundCornerRadius")) {
|
||||
mBackgroundCornerRadius =
|
||||
glm::clamp(elem->get<float>("backgroundCornerRadius"), 0.0f, 0.5f) *
|
||||
mRenderer->getScreenWidth();
|
||||
}
|
||||
}
|
||||
|
||||
if (elem->has("entries")) {
|
||||
// Replace possible whitespace separators with commas.
|
||||
std::string entriesTag {Utils::String::toLower(elem->get<std::string>("entries"))};
|
||||
for (auto& character : entriesTag) {
|
||||
if (std::isspace(character))
|
||||
character = ',';
|
||||
}
|
||||
entriesTag = Utils::String::replace(entriesTag, ",,", ",");
|
||||
std::vector<std::string> entries {Utils::String::delimitedStringToVector(entriesTag, ",")};
|
||||
|
||||
// If the "all" value has been set then leave mEntries fully populated.
|
||||
if (std::find(entries.begin(), entries.end(), "all") == entries.end()) {
|
||||
mEntries.clear();
|
||||
for (auto& allowedEntry : sAllowedEntries) {
|
||||
if (std::find(entries.cbegin(), entries.cend(), allowedEntry) != entries.cend())
|
||||
mEntries.emplace_back(allowedEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (elem->has("entrySpacing")) {
|
||||
mEntrySpacing = std::round(glm::clamp(elem->get<float>("entrySpacing"), 0.0f, 0.04f) *
|
||||
mRenderer->getScreenWidth());
|
||||
}
|
||||
|
||||
// Custom entry icons.
|
||||
// The names may look a bit strange when combined with the PREFIX string "icon_" but it's
|
||||
// because ThemeData adds this prefix to avoid name collisions when using XML attributes.
|
||||
if (elem->has(PREFIX "icon_wifi"))
|
||||
mIconPathMap["wifi"] = elem->get<std::string>(PREFIX "icon_wifi");
|
||||
if (elem->has(PREFIX "icon_bluetooth"))
|
||||
mIconPathMap["bluetooth"] = elem->get<std::string>(PREFIX "icon_bluetooth");
|
||||
if (elem->has(PREFIX "icon_cellular"))
|
||||
mIconPathMap["cellular"] = elem->get<std::string>(PREFIX "icon_cellular");
|
||||
if (elem->has(PREFIX "icon_battery_charging"))
|
||||
mIconPathMap["battery_charging"] = elem->get<std::string>(PREFIX "icon_battery_charging");
|
||||
if (elem->has(PREFIX "icon_battery_low"))
|
||||
mIconPathMap["icon_battery_low"] = elem->get<std::string>(PREFIX "icon_battery_low");
|
||||
if (elem->has(PREFIX "icon_battery_medium"))
|
||||
mIconPathMap["battery_medium"] = elem->get<std::string>(PREFIX "icon_battery_medium");
|
||||
if (elem->has(PREFIX "icon_battery_high"))
|
||||
mIconPathMap["battery_high"] = elem->get<std::string>(PREFIX "icon_battery_high");
|
||||
if (elem->has(PREFIX "icon_battery_full"))
|
||||
mIconPathMap["battery_full"] = elem->get<std::string>(PREFIX "icon_battery_full");
|
||||
}
|
||||
|
||||
void SystemStatusComponent::update(int deltaTime)
|
||||
{
|
||||
if (mEntries.empty())
|
||||
return;
|
||||
|
||||
mAccumulator += deltaTime;
|
||||
mAccumulatorAndroid += deltaTime;
|
||||
|
||||
if (mAccumulator >= SystemStatus::updateTime) {
|
||||
#if defined(__ANDROID__)
|
||||
// For Android we poll on the main thread instead of in a separate thread.
|
||||
SystemStatus::Status status;
|
||||
if (mAccumulatorAndroid >= SystemStatus::pollingTime ||
|
||||
SystemStatus::getInstance().getPollImmediately()) {
|
||||
status = SystemStatus::getInstance().getStatus(true);
|
||||
mAccumulatorAndroid = 0;
|
||||
}
|
||||
else {
|
||||
status = SystemStatus::getInstance().getStatus(false);
|
||||
}
|
||||
#else
|
||||
SystemStatus::Status status {SystemStatus::getInstance().getStatus()};
|
||||
#endif
|
||||
mAccumulator = 0;
|
||||
|
||||
bool statusChanged {false};
|
||||
bool batteryStatusChanged {false};
|
||||
|
||||
if (mHasBluetooth != status.hasBluetooth) {
|
||||
mHasBluetooth = status.hasBluetooth;
|
||||
statusChanged = true;
|
||||
}
|
||||
if (mHasWifi != status.hasWifi) {
|
||||
mHasWifi = status.hasWifi;
|
||||
statusChanged = true;
|
||||
}
|
||||
if (mHasCellular != status.hasCellular) {
|
||||
mHasCellular = status.hasCellular;
|
||||
statusChanged = true;
|
||||
}
|
||||
if (mHasBattery != status.hasBattery) {
|
||||
mHasBattery = status.hasBattery;
|
||||
statusChanged = true;
|
||||
batteryStatusChanged = true;
|
||||
}
|
||||
if (mHasBattery && mBatteryCharging != status.batteryCharging) {
|
||||
mBatteryCharging = status.batteryCharging;
|
||||
batteryStatusChanged = true;
|
||||
}
|
||||
if (mHasBattery && mBatteryCapacity != status.batteryCapacity) {
|
||||
mBatteryCapacity = status.batteryCapacity;
|
||||
batteryStatusChanged = true;
|
||||
}
|
||||
|
||||
if (statusChanged)
|
||||
updateGrid();
|
||||
|
||||
if (mHasBattery && batteryStatusChanged) {
|
||||
if (mBatteryPercentage != nullptr)
|
||||
mBatteryPercentage->setValue(std::to_string(mBatteryCapacity) + "%");
|
||||
|
||||
if (mBatteryCharging)
|
||||
mBattery->setImage(mIconPathMap["battery_charging"]);
|
||||
else if (mBatteryCapacity >= 0 && mBatteryCapacity <= 25)
|
||||
mBattery->setImage(mIconPathMap["battery_low"]);
|
||||
else if (mBatteryCapacity >= 26 && mBatteryCapacity <= 60)
|
||||
mBattery->setImage(mIconPathMap["battery_medium"]);
|
||||
else if (mBatteryCapacity >= 61 && mBatteryCapacity <= 90)
|
||||
mBattery->setImage(mIconPathMap["battery_high"]);
|
||||
else if (mBatteryCapacity > 90)
|
||||
mBattery->setImage(mIconPathMap["battery_full"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SystemStatusComponent::render(const glm::mat4& parentTrans)
|
||||
{
|
||||
if (mDisplayEntries.empty())
|
||||
return;
|
||||
|
||||
if (mBackgroundColor != 0x00000000) {
|
||||
const glm::vec3 positionTemp {mPosition};
|
||||
mPosition.x -= mBackgroundPadding.x / 2.0f;
|
||||
mPosition.y -= mBackgroundPadding.y / 2.0f;
|
||||
|
||||
const glm::mat4 trans {parentTrans * getTransform()};
|
||||
mRenderer->setMatrix(trans);
|
||||
|
||||
mRenderer->drawRect(0.0f, 0.0f, mSize.x + mBackgroundPadding.x,
|
||||
mSize.y + mBackgroundPadding.y, mBackgroundColor, mBackgroundColorEnd,
|
||||
mColorGradientHorizontal, mThemeOpacity, 1.0f,
|
||||
Renderer::BlendFactor::SRC_ALPHA,
|
||||
Renderer::BlendFactor::ONE_MINUS_SRC_ALPHA, mBackgroundCornerRadius);
|
||||
|
||||
mPosition = positionTemp;
|
||||
}
|
||||
|
||||
if (mGrid) {
|
||||
mGrid->setPosition(mPosition);
|
||||
mGrid->setRotationOrigin(mRotationOrigin);
|
||||
mGrid->setRotation(mRotation);
|
||||
|
||||
if (Settings::getInstance()->getBool("DebugImage")) {
|
||||
const glm::mat4 trans {parentTrans * getTransform()};
|
||||
mRenderer->setMatrix(trans);
|
||||
mRenderer->drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033);
|
||||
}
|
||||
|
||||
mGrid->render(parentTrans);
|
||||
}
|
||||
}
|
67
es-core/src/components/SystemStatusComponent.h
Normal file
67
es-core/src/components/SystemStatusComponent.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// ES-DE Frontend
|
||||
// SystemStatusComponent.h
|
||||
//
|
||||
// Displays system status information (Bluetooth, Wi-Fi, cellular and battery).
|
||||
//
|
||||
|
||||
#ifndef ES_CORE_COMPONENTS_SYSTEM_STATUS_COMPONENT_H
|
||||
#define ES_CORE_COMPONENTS_SYSTEM_STATUS_COMPONENT_H
|
||||
|
||||
#include "GuiComponent.h"
|
||||
#include "components/ComponentGrid.h"
|
||||
#include "components/ImageComponent.h"
|
||||
#include "components/TextComponent.h"
|
||||
#include "renderers/Renderer.h"
|
||||
#include "resources/Font.h"
|
||||
|
||||
class SystemStatusComponent : public GuiComponent
|
||||
{
|
||||
public:
|
||||
SystemStatusComponent();
|
||||
void updateGrid();
|
||||
|
||||
void applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||
const std::string& view,
|
||||
const std::string& element,
|
||||
unsigned int properties) override;
|
||||
|
||||
void update(int deltaTime) override;
|
||||
void render(const glm::mat4& parent) override;
|
||||
|
||||
private:
|
||||
Renderer* mRenderer;
|
||||
std::shared_ptr<ComponentGrid> mGrid;
|
||||
std::shared_ptr<Font> mFont;
|
||||
|
||||
std::shared_ptr<ImageComponent> mBattery;
|
||||
std::shared_ptr<TextComponent> mBatteryPercentage;
|
||||
|
||||
bool mHasBluetooth;
|
||||
bool mHasWifi;
|
||||
bool mHasCellular;
|
||||
bool mHasBattery;
|
||||
bool mBatteryCharging;
|
||||
int mBatteryCapacity;
|
||||
|
||||
std::vector<std::string> mEntries;
|
||||
std::vector<std::string> mDisplayEntries;
|
||||
std::map<std::string, int> mEntryMap;
|
||||
std::map<std::string, std::string> mIconPathMap;
|
||||
|
||||
static inline std::vector<std::string> sAllowedEntries {"bluetooth", "wifi", "cellular",
|
||||
"battery"};
|
||||
|
||||
unsigned int mColorShift;
|
||||
unsigned int mBackgroundColor;
|
||||
unsigned int mBackgroundColorEnd;
|
||||
int mAccumulator;
|
||||
int mAccumulatorAndroid;
|
||||
glm::vec2 mBackgroundPadding;
|
||||
float mBackgroundCornerRadius;
|
||||
bool mColorGradientHorizontal;
|
||||
float mEntrySpacing;
|
||||
};
|
||||
|
||||
#endif // ES_CORE_COMPONENTS_SYSTEM_STATUS_COMPONENT_H
|
Loading…
Reference in a new issue