add screensaver and optimize performance

Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
2025-06-19 23:21:43 +02:00
parent 3ac9565007
commit d3dd96c93a
20 changed files with 402 additions and 21 deletions

View File

@@ -1,5 +1,6 @@
if (DEFINED ENV{IDF_PATH})
idf_component_register(SRCS
src/common/InactivityTracker.cpp
src/common/Menu.cpp
src/common/ScrollBar.cpp
src/common/Widget.cpp
@@ -7,6 +8,7 @@ if (DEFINED ENV{IDF_PATH})
src/ui/LightMenu.cpp
src/ui/LightSettingsMenu.cpp
src/ui/MainMenu.cpp
src/ui/ScreenSaver.cpp
src/ui/SettingsMenu.cpp
src/ui/SplashScreen.cpp
INCLUDE_DIRS "include"
@@ -21,6 +23,7 @@ cmake_minimum_required(VERSION 3.30)
project(insa)
add_library(${PROJECT_NAME} STATIC
src/common/InactivityTracker.cpp
src/common/Menu.cpp
src/common/ScrollBar.cpp
src/common/Widget.cpp
@@ -28,6 +31,7 @@ add_library(${PROJECT_NAME} STATIC
src/ui/LightMenu.cpp
src/ui/LightSettingsMenu.cpp
src/ui/MainMenu.cpp
src/ui/ScreenSaver.cpp
src/ui/SettingsMenu.cpp
src/ui/SplashScreen.cpp
)

View File

@@ -0,0 +1,20 @@
#pragma once
#include <cstdint>
#include <functional>
class InactivityTracker
{
public:
InactivityTracker(uint64_t timeoutMs, std::function<void()> onTimeout);
void update(uint64_t dt);
void reset();
void setEnabled(bool enabled);
private:
uint64_t m_timeoutMs;
uint64_t m_elapsedTime;
bool m_enabled;
std::function<void()> m_onTimeout;
};

View File

@@ -72,6 +72,8 @@ class Menu : public Widget
*/
void addText(uint8_t id, const std::string &text);
void addTextCounter(uint8_t id, const std::string &text, const uint8_t value);
/**
* @brief Adds a selection menu item (dropdown/list selection) to the menu
* @param id Unique identifier for this menu item (must be unique within the menu)

View File

@@ -0,0 +1,37 @@
#pragma once
#include "MenuOptions.h"
#include "common/Widget.h"
#include <cstdlib>
#include <vector>
class ScreenSaver final : public Widget
{
public:
explicit ScreenSaver(menu_options_t *options);
void update(uint64_t dt) override;
void render() override;
void onButtonClicked(ButtonType button) override;
private:
struct Star
{
float x;
float y;
float z;
float speed;
};
menu_options_t *m_options;
uint64_t m_animationCounter;
std::vector<Star> m_stars;
static constexpr int NUM_STARS = 10;
static constexpr float SPEED_MULTIPLIER = 0.02f;
static constexpr float Z_NEAR = 0.1f;
static constexpr float Z_FAR = 10.0f;
void initStars();
void resetStar(Star &star);
};

View File

@@ -0,0 +1,35 @@
#include "common/InactivityTracker.h"
InactivityTracker::InactivityTracker(uint64_t timeoutMs, std::function<void()> onTimeout)
: m_timeoutMs(timeoutMs), m_elapsedTime(0), m_enabled(true), m_onTimeout(onTimeout)
{
}
void InactivityTracker::update(uint64_t dt)
{
if (!m_enabled)
return;
m_elapsedTime += dt;
if (m_elapsedTime >= m_timeoutMs && m_onTimeout)
{
m_onTimeout();
m_enabled = false;
}
}
void InactivityTracker::reset()
{
m_elapsedTime = 0;
m_enabled = true;
}
void InactivityTracker::setEnabled(bool enabled)
{
m_enabled = enabled;
if (enabled)
{
reset();
}
}

View File

@@ -9,7 +9,8 @@ namespace MenuItemTypes
constexpr uint8_t TEXT = 0;
constexpr uint8_t SELECTION = 1;
constexpr uint8_t TOGGLE = 2;
}
constexpr uint8_t TEXT_COUNTER = 3;
} // namespace MenuItemTypes
// UI layout constants
namespace UIConstants
@@ -23,14 +24,12 @@ constexpr int SELECTION_MARGIN = 10;
constexpr int CORNER_RADIUS = 3;
constexpr int LINE_SPACING = 14;
constexpr int BOTTOM_OFFSET = 10;
}
} // namespace UIConstants
Menu::Menu(menu_options_t *options) : Widget(options->u8g2), m_options(options)
{
// Set up button callback using lambda to forward to member function
m_options->onButtonClicked = [this](const ButtonType button) {
onButtonClicked(button);
};
m_options->onButtonClicked = [this](const ButtonType button) { onButtonClicked(button); };
}
Menu::~Menu()
@@ -183,6 +182,13 @@ void Menu::renderWidget(const MenuItem *item, const uint8_t *font, const int x,
break;
}
case MenuItemTypes::TEXT_COUNTER: {
const std::string formattedValue = "(" + item->getValue() + ") >";
const u8g2_uint_t textWidth = u8g2_GetStrWidth(u8g2, formattedValue.c_str());
u8g2_DrawStr(u8g2, u8g2->width - textWidth - UIConstants::SELECTION_MARGIN, y, formattedValue.c_str());
break;
}
case MenuItemTypes::SELECTION: {
// Format selection value with angle brackets
const std::string formattedValue = "< " + item->getValue() + " >";
@@ -308,11 +314,23 @@ void Menu::onPressedBack() const
}
void Menu::addText(uint8_t id, const std::string &text)
{
addTextCounter(id, text, 0);
}
void Menu::addTextCounter(uint8_t id, const std::string &text, const uint8_t value)
{
auto callback = [this](const MenuItem &menuItem, const ButtonType button) -> void {
onButtonPressed(menuItem, button);
};
m_items.emplace_back(id, MenuItemTypes::TEXT, text, callback);
if (value > 0)
{
m_items.emplace_back(id, MenuItemTypes::TEXT_COUNTER, text, std::to_string(value), callback);
}
else
{
m_items.emplace_back(id, MenuItemTypes::TEXT, text, callback);
}
}
void Menu::addSelection(uint8_t id, const std::string &text, const std::vector<std::string> &values, const int index)

View File

@@ -14,7 +14,7 @@ constexpr uint8_t SETTINGS = 2;
MainMenu::MainMenu(menu_options_t *options) : Menu(options), m_options(options)
{
addText(MainMenuItem::LIGHT, "Lichtsteuerung");
addText(MainMenuItem::EXTERNAL_DEVICES, "Externe Geraete");
addTextCounter(MainMenuItem::EXTERNAL_DEVICES, "ext. Geraete", 0);
addText(MainMenuItem::SETTINGS, "Einstellungen");
}

View File

@@ -0,0 +1,93 @@
#include "ui/ScreenSaver.h"
#include <cmath>
ScreenSaver::ScreenSaver(menu_options_t *options) : Widget(options->u8g2), m_options(options), m_animationCounter(0)
{
initStars();
}
void ScreenSaver::initStars()
{
m_stars.resize(NUM_STARS);
for (auto &star : m_stars)
{
resetStar(star);
star.z = Z_NEAR + (static_cast<float>(rand()) / RAND_MAX) * (Z_FAR - Z_NEAR);
}
}
void ScreenSaver::resetStar(Star &star)
{
star.x = (static_cast<float>(rand()) / RAND_MAX - 0.5f) * 2.0f;
star.y = (static_cast<float>(rand()) / RAND_MAX - 0.5f) * 2.0f;
star.z = Z_FAR;
star.speed = 0.5f + (static_cast<float>(rand()) / RAND_MAX) * 1.5f;
}
void ScreenSaver::update(const uint64_t dt)
{
m_animationCounter += dt;
if (m_animationCounter > 8)
{
m_animationCounter = 0;
for (auto &star : m_stars)
{
star.z -= star.speed * SPEED_MULTIPLIER;
if (star.z < Z_NEAR)
{
resetStar(star);
}
}
}
}
void ScreenSaver::render()
{
// Verwende Page-Buffer Mode statt Full-Buffer für bessere Performance
// Schwarzer Hintergrund
u8g2_SetDrawColor(u8g2, 0);
u8g2_DrawBox(u8g2, 0, 0, u8g2->width, u8g2->height);
u8g2_SetDrawColor(u8g2, 1);
const int centerX = u8g2->width / 2;
const int centerY = u8g2->height / 2;
// Zeichne nur sichtbare Sterne (Clipping)
for (const auto &star : m_stars)
{
// 3D zu 2D Projektion
int screenX = centerX + static_cast<int>((star.x / star.z) * centerX);
int screenY = centerY + static_cast<int>((star.y / star.z) * centerY);
// Frühe Prüfung für Performance
if (screenX < -5 || screenX >= u8g2->width + 5 || screenY < -5 || screenY >= u8g2->height + 5)
{
continue;
}
// Vereinfachte Sterndarstellung für bessere Performance
int size = static_cast<int>((1.0f - (star.z - Z_NEAR) / (Z_FAR - Z_NEAR)) * 2.0f);
if (size <= 0)
{
u8g2_DrawPixel(u8g2, screenX, screenY);
}
else
{
// Verwende u8g2_DrawCircle für größere Sterne (schneller)
u8g2_DrawCircle(u8g2, screenX, screenY, size, U8G2_DRAW_ALL);
}
}
}
void ScreenSaver::onButtonClicked(ButtonType button)
{
if (m_options && m_options->popScreen)
{
m_options->popScreen();
}
}

View File

@@ -17,7 +17,7 @@ void SplashScreen::update(const uint64_t dt)
{
counter += dt;
#ifndef ESP32
if (counter > 20)
if (counter > 3000)
#else
if (counter > 10)
#endif