diff --git a/lib/GfxRenderer/Bitmap.cpp b/lib/GfxRenderer/Bitmap.cpp index 0b8a3e65..603954d2 100644 --- a/lib/GfxRenderer/Bitmap.cpp +++ b/lib/GfxRenderer/Bitmap.cpp @@ -1,8 +1,10 @@ #include "Bitmap.h" -#include "BitmapHelpers.h" + #include #include +#include "BitmapHelpers.h" + // ============================================================================ // IMAGE PROCESSING OPTIONS // ============================================================================ @@ -31,13 +33,12 @@ int Bitmap::readByte() const { return -1; } -size_t Bitmap::readBytes(void *buf, size_t count) const { +size_t Bitmap::readBytes(void* buf, size_t count) const { if (file && *file) { return file->read(buf, count); } else if (memoryBuffer) { size_t available = memorySize - bufferPos; - if (count > available) - count = available; + if (count > available) count = available; memcpy(buf, memoryBuffer + bufferPos, count); bufferPos += count; return count; @@ -74,8 +75,7 @@ bool Bitmap::seekCur(int32_t offset) const { uint16_t Bitmap::readLE16() { const int c0 = readByte(); const int c1 = readByte(); - return static_cast(c0 & 0xFF) | - (static_cast(c1 & 0xFF) << 8); + return static_cast(c0 & 0xFF) | (static_cast(c1 & 0xFF) << 8); } uint32_t Bitmap::readLE32() { @@ -83,64 +83,58 @@ uint32_t Bitmap::readLE32() { const int c1 = readByte(); const int c2 = readByte(); const int c3 = readByte(); - return static_cast(c0 & 0xFF) | - (static_cast(c1 & 0xFF) << 8) | - (static_cast(c2 & 0xFF) << 16) | - (static_cast(c3 & 0xFF) << 24); + return static_cast(c0 & 0xFF) | (static_cast(c1 & 0xFF) << 8) | + (static_cast(c2 & 0xFF) << 16) | (static_cast(c3 & 0xFF) << 24); } -const char *Bitmap::errorToString(BmpReaderError err) { +const char* Bitmap::errorToString(BmpReaderError err) { switch (err) { - case BmpReaderError::Ok: - return "Ok"; - case BmpReaderError::FileInvalid: - return "FileInvalid"; - case BmpReaderError::SeekStartFailed: - return "SeekStartFailed"; - case BmpReaderError::NotBMP: - return "NotBMP"; - case BmpReaderError::DIBTooSmall: - return "DIBTooSmall"; - case BmpReaderError::BadPlanes: - return "BadPlanes"; - case BmpReaderError::UnsupportedBpp: - return "UnsupportedBpp"; - case BmpReaderError::UnsupportedCompression: - return "UnsupportedCompression"; - case BmpReaderError::BadDimensions: - return "BadDimensions"; - case BmpReaderError::ImageTooLarge: - return "ImageTooLarge"; - case BmpReaderError::PaletteTooLarge: - return "PaletteTooLarge"; - case BmpReaderError::SeekPixelDataFailed: - return "SeekPixelDataFailed"; - case BmpReaderError::BufferTooSmall: - return "BufferTooSmall"; - case BmpReaderError::OomRowBuffer: - return "OomRowBuffer"; - case BmpReaderError::ShortReadRow: - return "ShortReadRow"; + case BmpReaderError::Ok: + return "Ok"; + case BmpReaderError::FileInvalid: + return "FileInvalid"; + case BmpReaderError::SeekStartFailed: + return "SeekStartFailed"; + case BmpReaderError::NotBMP: + return "NotBMP"; + case BmpReaderError::DIBTooSmall: + return "DIBTooSmall"; + case BmpReaderError::BadPlanes: + return "BadPlanes"; + case BmpReaderError::UnsupportedBpp: + return "UnsupportedBpp"; + case BmpReaderError::UnsupportedCompression: + return "UnsupportedCompression"; + case BmpReaderError::BadDimensions: + return "BadDimensions"; + case BmpReaderError::ImageTooLarge: + return "ImageTooLarge"; + case BmpReaderError::PaletteTooLarge: + return "PaletteTooLarge"; + case BmpReaderError::SeekPixelDataFailed: + return "SeekPixelDataFailed"; + case BmpReaderError::BufferTooSmall: + return "BufferTooSmall"; + case BmpReaderError::OomRowBuffer: + return "OomRowBuffer"; + case BmpReaderError::ShortReadRow: + return "ShortReadRow"; } return "Unknown"; } BmpReaderError Bitmap::parseHeaders() { - if (!file && !memoryBuffer) - return BmpReaderError::FileInvalid; - if (!seekSet(0)) - return BmpReaderError::SeekStartFailed; + if (!file && !memoryBuffer) return BmpReaderError::FileInvalid; + if (!seekSet(0)) return BmpReaderError::SeekStartFailed; const uint16_t bfType = readLE16(); - if (bfType != 0x4D42) - return BmpReaderError::NotBMP; + if (bfType != 0x4D42) return BmpReaderError::NotBMP; seekCur(8); bfOffBits = readLE32(); const uint32_t biSize = readLE32(); - if (biSize < 40) - return BmpReaderError::DIBTooSmall; + if (biSize < 40) return BmpReaderError::DIBTooSmall; width = static_cast(readLE32()); const auto rawHeight = static_cast(readLE32()); @@ -150,24 +144,18 @@ BmpReaderError Bitmap::parseHeaders() { const uint16_t planes = readLE16(); bpp = readLE16(); const uint32_t comp = readLE32(); - const bool validBpp = - bpp == 1 || bpp == 2 || bpp == 8 || bpp == 24 || bpp == 32; + const bool validBpp = bpp == 1 || bpp == 2 || bpp == 8 || bpp == 24 || bpp == 32; - if (planes != 1) - return BmpReaderError::BadPlanes; - if (!validBpp) - return BmpReaderError::UnsupportedBpp; - if (!(comp == 0 || (bpp == 32 && comp == 3))) - return BmpReaderError::UnsupportedCompression; + if (planes != 1) return BmpReaderError::BadPlanes; + if (!validBpp) return BmpReaderError::UnsupportedBpp; + if (!(comp == 0 || (bpp == 32 && comp == 3))) return BmpReaderError::UnsupportedCompression; seekCur(12); const uint32_t colorsUsed = readLE32(); - if (colorsUsed > 256u) - return BmpReaderError::PaletteTooLarge; + if (colorsUsed > 256u) return BmpReaderError::PaletteTooLarge; seekCur(4); - if (width <= 0 || height <= 0) - return BmpReaderError::BadDimensions; + if (width <= 0 || height <= 0) return BmpReaderError::BadDimensions; constexpr int MAX_IMAGE_WIDTH = 2048; constexpr int MAX_IMAGE_HEIGHT = 3072; @@ -177,8 +165,7 @@ BmpReaderError Bitmap::parseHeaders() { rowBytes = (width * bpp + 31) / 32 * 4; - for (int i = 0; i < 256; i++) - paletteLum[i] = static_cast(i); + for (int i = 0; i < 256; i++) paletteLum[i] = static_cast(i); if (colorsUsed > 0) { for (uint32_t i = 0; i < colorsUsed; i++) { uint8_t rgb[4]; @@ -187,8 +174,7 @@ BmpReaderError Bitmap::parseHeaders() { } } - if (!seekSet(bfOffBits)) - return BmpReaderError::SeekPixelDataFailed; + if (!seekSet(bfOffBits)) return BmpReaderError::SeekPixelDataFailed; if (bpp > 2 && dithering) { if (USE_ATKINSON) { @@ -201,12 +187,11 @@ BmpReaderError Bitmap::parseHeaders() { return BmpReaderError::Ok; } -BmpReaderError Bitmap::readNextRow(uint8_t *data, uint8_t *rowBuffer) const { - if (readBytes(rowBuffer, rowBytes) != (size_t)rowBytes) - return BmpReaderError::ShortReadRow; +BmpReaderError Bitmap::readNextRow(uint8_t* data, uint8_t* rowBuffer) const { + if (readBytes(rowBuffer, rowBytes) != (size_t)rowBytes) return BmpReaderError::ShortReadRow; prevRowY += 1; - uint8_t *outPtr = data; + uint8_t* outPtr = data; uint8_t currentOutByte = 0; int bitShift = 6; int currentX = 0; @@ -236,46 +221,44 @@ BmpReaderError Bitmap::readNextRow(uint8_t *data, uint8_t *rowBuffer) const { }; switch (bpp) { - case 32: { - const uint8_t *p = rowBuffer; - for (int x = 0; x < width; x++) { - uint8_t lum = (77u * p[2] + 150u * p[1] + 29u * p[0]) >> 8; - packPixel(lum); - p += 4; + case 32: { + const uint8_t* p = rowBuffer; + for (int x = 0; x < width; x++) { + uint8_t lum = (77u * p[2] + 150u * p[1] + 29u * p[0]) >> 8; + packPixel(lum); + p += 4; + } + break; } - break; - } - case 24: { - const uint8_t *p = rowBuffer; - for (int x = 0; x < width; x++) { - uint8_t lum = (77u * p[2] + 150u * p[1] + 29u * p[0]) >> 8; - packPixel(lum); - p += 3; + case 24: { + const uint8_t* p = rowBuffer; + for (int x = 0; x < width; x++) { + uint8_t lum = (77u * p[2] + 150u * p[1] + 29u * p[0]) >> 8; + packPixel(lum); + p += 3; + } + break; } - break; - } - case 8: { - for (int x = 0; x < width; x++) - packPixel(paletteLum[rowBuffer[x]]); - break; - } - case 2: { - for (int x = 0; x < width; x++) { - uint8_t lum = - paletteLum[(rowBuffer[x >> 2] >> (6 - ((x & 3) * 2))) & 0x03]; - packPixel(lum); + case 8: { + for (int x = 0; x < width; x++) packPixel(paletteLum[rowBuffer[x]]); + break; } - break; - } - case 1: { - for (int x = 0; x < width; x++) { - const uint8_t palIndex = (rowBuffer[x >> 3] & (0x80 >> (x & 7))) ? 1 : 0; - packPixel(paletteLum[palIndex]); + case 2: { + for (int x = 0; x < width; x++) { + uint8_t lum = paletteLum[(rowBuffer[x >> 2] >> (6 - ((x & 3) * 2))) & 0x03]; + packPixel(lum); + } + break; } - break; - } - default: - return BmpReaderError::UnsupportedBpp; + case 1: { + for (int x = 0; x < width; x++) { + const uint8_t palIndex = (rowBuffer[x >> 3] & (0x80 >> (x & 7))) ? 1 : 0; + packPixel(paletteLum[palIndex]); + } + break; + } + default: + return BmpReaderError::UnsupportedBpp; } if (atkinsonDitherer) @@ -283,17 +266,13 @@ BmpReaderError Bitmap::readNextRow(uint8_t *data, uint8_t *rowBuffer) const { else if (fsDitherer) fsDitherer->nextRow(); - if (bitShift != 6) - *outPtr = currentOutByte; + if (bitShift != 6) *outPtr = currentOutByte; return BmpReaderError::Ok; } BmpReaderError Bitmap::rewindToData() const { - if (!seekSet(bfOffBits)) - return BmpReaderError::SeekPixelDataFailed; - if (fsDitherer) - fsDitherer->reset(); - if (atkinsonDitherer) - atkinsonDitherer->reset(); + if (!seekSet(bfOffBits)) return BmpReaderError::SeekPixelDataFailed; + if (fsDitherer) fsDitherer->reset(); + if (atkinsonDitherer) atkinsonDitherer->reset(); return BmpReaderError::Ok; } diff --git a/lib/GfxRenderer/Bitmap.h b/lib/GfxRenderer/Bitmap.h index 595c8d30..41ee0509 100644 --- a/lib/GfxRenderer/Bitmap.h +++ b/lib/GfxRenderer/Bitmap.h @@ -29,18 +29,16 @@ enum class BmpReaderError : uint8_t { }; class Bitmap { -public: - static const char *errorToString(BmpReaderError err); + public: + static const char* errorToString(BmpReaderError err); - explicit Bitmap(FsFile &file, bool dithering = false) - : file(&file), dithering(dithering) {} - explicit Bitmap(const uint8_t *buffer, size_t size, bool dithering = false) - : file(nullptr), memoryBuffer(buffer), memorySize(size), - dithering(dithering) {} + explicit Bitmap(FsFile& file, bool dithering = false) : file(&file), dithering(dithering) {} + explicit Bitmap(const uint8_t* buffer, size_t size, bool dithering = false) + : file(nullptr), memoryBuffer(buffer), memorySize(size), dithering(dithering) {} ~Bitmap(); BmpReaderError parseHeaders(); - BmpReaderError readNextRow(uint8_t *data, uint8_t *rowBuffer) const; + BmpReaderError readNextRow(uint8_t* data, uint8_t* rowBuffer) const; BmpReaderError rewindToData() const; // Getters @@ -52,19 +50,19 @@ public: bool is1Bit() const { return bpp == 1; } uint16_t getBpp() const { return bpp; } -private: + private: // Internal IO helpers int readByte() const; - size_t readBytes(void *buf, size_t count) const; + size_t readBytes(void* buf, size_t count) const; bool seekSet(uint32_t pos) const; - bool seekCur(int32_t offset) const; // Only needed for skip? + bool seekCur(int32_t offset) const; // Only needed for skip? uint16_t readLE16(); uint32_t readLE32(); // Source (one is valid) - FsFile *file = nullptr; - const uint8_t *memoryBuffer = nullptr; + FsFile* file = nullptr; + const uint8_t* memoryBuffer = nullptr; size_t memorySize = 0; mutable size_t bufferPos = 0; @@ -78,10 +76,10 @@ private: uint8_t paletteLum[256] = {}; // Floyd-Steinberg dithering state (mutable for const methods) - mutable int16_t *errorCurRow = nullptr; - mutable int16_t *errorNextRow = nullptr; - mutable int prevRowY = -1; // Track row progression for error propagation + mutable int16_t* errorCurRow = nullptr; + mutable int16_t* errorNextRow = nullptr; + mutable int prevRowY = -1; // Track row progression for error propagation - mutable AtkinsonDitherer *atkinsonDitherer = nullptr; - mutable FloydSteinbergDitherer *fsDitherer = nullptr; + mutable AtkinsonDitherer* atkinsonDitherer = nullptr; + mutable FloydSteinbergDitherer* fsDitherer = nullptr; }; diff --git a/lib/ThemeEngine/include/BasicElements.h b/lib/ThemeEngine/include/BasicElements.h index e0032d0b..1c75c98e 100644 --- a/lib/ThemeEngine/include/BasicElements.h +++ b/lib/ThemeEngine/include/BasicElements.h @@ -1,43 +1,42 @@ #pragma once +#include +#include + +#include + #include "ThemeContext.h" #include "ThemeTypes.h" #include "UIElement.h" -#include -#include -#include namespace ThemeEngine { // --- Container --- class Container : public UIElement { -protected: - std::vector children; + protected: + std::vector children; Expression bgColorExpr; bool hasBg = false; bool border = false; - Expression borderExpr; // Dynamic border based on expression - int padding = 0; // Inner padding for children - int borderRadius = 0; // Corner radius (for future rounded rect support) + Expression borderExpr; // Dynamic border based on expression + int padding = 0; // Inner padding for children + int borderRadius = 0; // Corner radius (for future rounded rect support) -public: - Container(const std::string &id) : UIElement(id) { - bgColorExpr = Expression::parse("0xFF"); - } + public: + Container(const std::string& id) : UIElement(id) { bgColorExpr = Expression::parse("0xFF"); } virtual ~Container() { - for (auto child : children) - delete child; + for (auto child : children) delete child; } - Container *asContainer() override { return this; } + Container* asContainer() override { return this; } ElementType getType() const override { return ElementType::Container; } - void addChild(UIElement *child) { children.push_back(child); } + void addChild(UIElement* child) { children.push_back(child); } - const std::vector &getChildren() const { return children; } + const std::vector& getChildren() const { return children; } - void setBackgroundColorExpr(const std::string &expr) { + void setBackgroundColorExpr(const std::string& expr) { bgColorExpr = Expression::parse(expr); hasBg = true; markDirty(); @@ -48,29 +47,28 @@ public: markDirty(); } - void setBorderExpr(const std::string &expr) { + void setBorderExpr(const std::string& expr) { borderExpr = Expression::parse(expr); markDirty(); } bool hasBorderExpr() const { return !borderExpr.empty(); } - + void setPadding(int p) { padding = p; markDirty(); } - + int getPadding() const { return padding; } - + void setBorderRadius(int r) { borderRadius = r; markDirty(); } - + int getBorderRadius() const { return borderRadius; } - void layout(const ThemeContext &context, int parentX, int parentY, - int parentW, int parentH) override { + void layout(const ThemeContext& context, int parentX, int parentY, int parentW, int parentH) override { UIElement::layout(context, parentX, parentY, parentW, parentH); // Children are laid out with padding offset int childX = absX + padding; @@ -89,9 +87,8 @@ public: } } - void draw(const GfxRenderer &renderer, const ThemeContext &context) override { - if (!isVisible(context)) - return; + void draw(const GfxRenderer& renderer, const ThemeContext& context) override { + if (!isVisible(context)) return; if (hasBg) { std::string colStr = context.evaluatestring(bgColorExpr); @@ -144,13 +141,11 @@ public: // --- Rectangle --- class Rectangle : public UIElement { bool fill = false; - Expression fillExpr; // Dynamic fill based on expression + Expression fillExpr; // Dynamic fill based on expression Expression colorExpr; -public: - Rectangle(const std::string &id) : UIElement(id) { - colorExpr = Expression::parse("0x00"); - } + public: + Rectangle(const std::string& id) : UIElement(id) { colorExpr = Expression::parse("0x00"); } ElementType getType() const override { return ElementType::Rectangle; } void setFill(bool f) { @@ -158,19 +153,18 @@ public: markDirty(); } - void setFillExpr(const std::string &expr) { + void setFillExpr(const std::string& expr) { fillExpr = Expression::parse(expr); markDirty(); } - void setColorExpr(const std::string &c) { + void setColorExpr(const std::string& c) { colorExpr = Expression::parse(c); markDirty(); } - void draw(const GfxRenderer &renderer, const ThemeContext &context) override { - if (!isVisible(context)) - return; + void draw(const GfxRenderer& renderer, const ThemeContext& context) override { + if (!isVisible(context)) return; std::string colStr = context.evaluatestring(colorExpr); uint8_t color = Color::parse(colStr).value; @@ -193,24 +187,22 @@ public: // --- Label --- class Label : public UIElement { -public: + public: enum class Alignment { Left, Center, Right }; -private: + private: Expression textExpr; int fontId = 0; Alignment alignment = Alignment::Left; Expression colorExpr; - int maxLines = 1; // For multi-line support - bool ellipsis = true; // Truncate with ... if too long + int maxLines = 1; // For multi-line support + bool ellipsis = true; // Truncate with ... if too long -public: - Label(const std::string &id) : UIElement(id) { - colorExpr = Expression::parse("0x00"); - } + public: + Label(const std::string& id) : UIElement(id) { colorExpr = Expression::parse("0x00"); } ElementType getType() const override { return ElementType::Label; } - void setText(const std::string &expr) { + void setText(const std::string& expr) { textExpr = Expression::parse(expr); markDirty(); } @@ -226,7 +218,7 @@ public: alignment = c ? Alignment::Center : Alignment::Left; markDirty(); } - void setColorExpr(const std::string &c) { + void setColorExpr(const std::string& c) { colorExpr = Expression::parse(c); markDirty(); } @@ -239,9 +231,8 @@ public: markDirty(); } - void draw(const GfxRenderer &renderer, const ThemeContext &context) override { - if (!isVisible(context)) - return; + void draw(const GfxRenderer& renderer, const ThemeContext& context) override { + if (!isVisible(context)) return; std::string finalText = context.evaluatestring(textExpr); if (finalText.empty()) { @@ -288,13 +279,13 @@ class BitmapElement : public UIElement { bool scaleToFit = true; bool preserveAspect = true; -public: - BitmapElement(const std::string &id) : UIElement(id) { - cacheable = true; // Bitmaps benefit from caching + public: + BitmapElement(const std::string& id) : UIElement(id) { + cacheable = true; // Bitmaps benefit from caching } ElementType getType() const override { return ElementType::Bitmap; } - void setSrc(const std::string &src) { + void setSrc(const std::string& src) { srcExpr = Expression::parse(src); invalidateCache(); } @@ -309,41 +300,41 @@ public: invalidateCache(); } - void draw(const GfxRenderer &renderer, const ThemeContext &context) override; + void draw(const GfxRenderer& renderer, const ThemeContext& context) override; }; // --- ProgressBar --- class ProgressBar : public UIElement { - Expression valueExpr; // Current value (0-100 or 0-max) - Expression maxExpr; // Max value (default 100) - Expression fgColorExpr; // Foreground color - Expression bgColorExpr; // Background color + Expression valueExpr; // Current value (0-100 or 0-max) + Expression maxExpr; // Max value (default 100) + Expression fgColorExpr; // Foreground color + Expression bgColorExpr; // Background color bool showBorder = true; int borderWidth = 1; -public: - ProgressBar(const std::string &id) : UIElement(id) { + public: + ProgressBar(const std::string& id) : UIElement(id) { valueExpr = Expression::parse("0"); maxExpr = Expression::parse("100"); - fgColorExpr = Expression::parse("0x00"); // Black fill - bgColorExpr = Expression::parse("0xFF"); // White background + fgColorExpr = Expression::parse("0x00"); // Black fill + bgColorExpr = Expression::parse("0xFF"); // White background } ElementType getType() const override { return ElementType::ProgressBar; } - void setValue(const std::string &expr) { + void setValue(const std::string& expr) { valueExpr = Expression::parse(expr); markDirty(); } - void setMax(const std::string &expr) { + void setMax(const std::string& expr) { maxExpr = Expression::parse(expr); markDirty(); } - void setFgColor(const std::string &expr) { + void setFgColor(const std::string& expr) { fgColorExpr = Expression::parse(expr); markDirty(); } - void setBgColor(const std::string &expr) { + void setBgColor(const std::string& expr) { bgColorExpr = Expression::parse(expr); markDirty(); } @@ -352,23 +343,19 @@ public: markDirty(); } - void draw(const GfxRenderer &renderer, const ThemeContext &context) override { - if (!isVisible(context)) - return; + void draw(const GfxRenderer& renderer, const ThemeContext& context) override { + if (!isVisible(context)) return; std::string valStr = context.evaluatestring(valueExpr); std::string maxStr = context.evaluatestring(maxExpr); int value = valStr.empty() ? 0 : std::stoi(valStr); int maxVal = maxStr.empty() ? 100 : std::stoi(maxStr); - if (maxVal <= 0) - maxVal = 100; + if (maxVal <= 0) maxVal = 100; float ratio = static_cast(value) / static_cast(maxVal); - if (ratio < 0) - ratio = 0; - if (ratio > 1) - ratio = 1; + if (ratio < 0) ratio = 0; + if (ratio > 1) ratio = 1; // Draw background std::string bgStr = context.evaluatestring(bgColorExpr); @@ -398,14 +385,12 @@ class Divider : public UIElement { bool horizontal = true; int thickness = 1; -public: - Divider(const std::string &id) : UIElement(id) { - colorExpr = Expression::parse("0x00"); - } + public: + Divider(const std::string& id) : UIElement(id) { colorExpr = Expression::parse("0x00"); } ElementType getType() const override { return ElementType::Divider; } - void setColorExpr(const std::string &expr) { + void setColorExpr(const std::string& expr) { colorExpr = Expression::parse(expr); markDirty(); } @@ -418,9 +403,8 @@ public: markDirty(); } - void draw(const GfxRenderer &renderer, const ThemeContext &context) override { - if (!isVisible(context)) - return; + void draw(const GfxRenderer& renderer, const ThemeContext& context) override { + if (!isVisible(context)) return; std::string colStr = context.evaluatestring(colorExpr); uint8_t color = Color::parse(colStr).value; @@ -445,27 +429,26 @@ class BatteryIcon : public UIElement { Expression valueExpr; Expression colorExpr; -public: - BatteryIcon(const std::string &id) : UIElement(id) { + public: + BatteryIcon(const std::string& id) : UIElement(id) { valueExpr = Expression::parse("0"); - colorExpr = Expression::parse("0x00"); // Black by default + colorExpr = Expression::parse("0x00"); // Black by default } ElementType getType() const override { return ElementType::BatteryIcon; } - void setValue(const std::string &expr) { + void setValue(const std::string& expr) { valueExpr = Expression::parse(expr); markDirty(); } - void setColor(const std::string &expr) { + void setColor(const std::string& expr) { colorExpr = Expression::parse(expr); markDirty(); } - void draw(const GfxRenderer &renderer, const ThemeContext &context) override { - if (!isVisible(context)) - return; + void draw(const GfxRenderer& renderer, const ThemeContext& context) override { + if (!isVisible(context)) return; std::string valStr = context.evaluatestring(valueExpr); int percentage = valStr.empty() ? 0 : std::stoi(valStr); @@ -480,22 +463,17 @@ public: int x = absX; int y = absY; - if (absW > batteryWidth) - x += (absW - batteryWidth) / 2; - if (absH > batteryHeight) - y += (absH - batteryHeight) / 2; + if (absW > batteryWidth) x += (absW - batteryWidth) / 2; + if (absH > batteryHeight) y += (absH - batteryHeight) / 2; renderer.drawLine(x + 1, y, x + batteryWidth - 3, y, black); - renderer.drawLine(x + 1, y + batteryHeight - 1, x + batteryWidth - 3, - y + batteryHeight - 1, black); + renderer.drawLine(x + 1, y + batteryHeight - 1, x + batteryWidth - 3, y + batteryHeight - 1, black); renderer.drawLine(x, y + 1, x, y + batteryHeight - 2, black); - renderer.drawLine(x + batteryWidth - 2, y + 1, x + batteryWidth - 2, - y + batteryHeight - 2, black); + renderer.drawLine(x + batteryWidth - 2, y + 1, x + batteryWidth - 2, y + batteryHeight - 2, black); renderer.drawPixel(x + batteryWidth - 1, y + 3, black); renderer.drawPixel(x + batteryWidth - 1, y + batteryHeight - 4, black); - renderer.drawLine(x + batteryWidth - 0, y + 4, x + batteryWidth - 0, - y + batteryHeight - 5, black); + renderer.drawLine(x + batteryWidth - 0, y + 4, x + batteryWidth - 0, y + batteryHeight - 5, black); if (percentage > 0) { int filledWidth = percentage * (batteryWidth - 5) / 100 + 1; @@ -509,4 +487,4 @@ public: } }; -} // namespace ThemeEngine +} // namespace ThemeEngine diff --git a/lib/ThemeEngine/include/DefaultTheme.h b/lib/ThemeEngine/include/DefaultTheme.h index 2c5e263b..f1822582 100644 --- a/lib/ThemeEngine/include/DefaultTheme.h +++ b/lib/ThemeEngine/include/DefaultTheme.h @@ -6,8 +6,8 @@ namespace ThemeEngine { // Use static function for C++14 ODR compatibility -static const char *getDefaultThemeIni() { - static const char *theme = R"INI( +static const char* getDefaultThemeIni() { + static const char* theme = R"INI( ; ============================================ ; DEFAULT THEME - Original CrossPoint Reader ; ============================================ @@ -275,4 +275,4 @@ Align = center return theme; } -} // namespace ThemeEngine +} // namespace ThemeEngine diff --git a/lib/ThemeEngine/include/IniParser.h b/lib/ThemeEngine/include/IniParser.h index 5e27f6ed..3f7321b4 100644 --- a/lib/ThemeEngine/include/IniParser.h +++ b/lib/ThemeEngine/include/IniParser.h @@ -24,17 +24,15 @@ struct IniSection { }; class IniParser { -public: + public: // Parse a stream (File, Serial, etc.) - static std::map> - parse(Stream &stream); + static std::map> parse(Stream& stream); // Parse a string buffer (useful for testing) - static std::map> - parseString(const std::string &content); + static std::map> parseString(const std::string& content); -private: - static void trim(std::string &s); + private: + static void trim(std::string& s); }; -} // namespace ThemeEngine +} // namespace ThemeEngine diff --git a/lib/ThemeEngine/include/LayoutElements.h b/lib/ThemeEngine/include/LayoutElements.h index 337c3a00..050ef334 100644 --- a/lib/ThemeEngine/include/LayoutElements.h +++ b/lib/ThemeEngine/include/LayoutElements.h @@ -1,22 +1,23 @@ #pragma once +#include + #include "BasicElements.h" #include "ThemeContext.h" #include "ThemeTypes.h" #include "UIElement.h" -#include namespace ThemeEngine { // --- HStack: Horizontal Stack Layout --- // Children are arranged horizontally with optional spacing class HStack : public Container { - int spacing = 0; // Gap between children - int padding = 0; // Internal padding + int spacing = 0; // Gap between children + int padding = 0; // Internal padding bool centerVertical = false; -public: - HStack(const std::string &id) : Container(id) {} + public: + HStack(const std::string& id) : Container(id) {} ElementType getType() const override { return ElementType::HStack; } void setSpacing(int s) { @@ -32,8 +33,7 @@ public: markDirty(); } - void layout(const ThemeContext &context, int parentX, int parentY, - int parentW, int parentH) override { + void layout(const ThemeContext& context, int parentX, int parentY, int parentW, int parentH) override { UIElement::layout(context, parentX, parentY, parentW, parentH); int currentX = absX + padding; @@ -68,8 +68,8 @@ class VStack : public Container { int padding = 0; bool centerHorizontal = false; -public: - VStack(const std::string &id) : Container(id) {} + public: + VStack(const std::string& id) : Container(id) {} ElementType getType() const override { return ElementType::VStack; } void setSpacing(int s) { @@ -85,8 +85,7 @@ public: markDirty(); } - void layout(const ThemeContext &context, int parentX, int parentY, - int parentW, int parentH) override { + void layout(const ThemeContext& context, int parentX, int parentY, int parentW, int parentH) override { UIElement::layout(context, parentX, parentY, parentW, parentH); int currentY = absY + padding; @@ -120,8 +119,8 @@ class Grid : public Container { int colSpacing = 10; int padding = 0; -public: - Grid(const std::string &id) : Container(id) {} + public: + Grid(const std::string& id) : Container(id) {} ElementType getType() const override { return ElementType::Grid; } void setColumns(int c) { @@ -141,12 +140,10 @@ public: markDirty(); } - void layout(const ThemeContext &context, int parentX, int parentY, - int parentW, int parentH) override { + void layout(const ThemeContext& context, int parentX, int parentY, int parentW, int parentH) override { UIElement::layout(context, parentX, parentY, parentW, parentH); - if (children.empty()) - return; + if (children.empty()) return; int availableW = absW - 2 * padding - (columns - 1) * colSpacing; int cellW = availableW / columns; @@ -162,8 +159,7 @@ public: // Pass cell dimensions to avoid clamping issues child->layout(context, cellX, currentY, cellW, availableH); int childH = child->getAbsH(); - if (childH > maxRowHeight) - maxRowHeight = childH; + if (childH > maxRowHeight) maxRowHeight = childH; col++; if (col >= columns) { @@ -184,27 +180,27 @@ class Badge : public UIElement { Expression bgColorExpr; Expression fgColorExpr; int fontId = 0; - int paddingH = 8; // Horizontal padding - int paddingV = 4; // Vertical padding + int paddingH = 8; // Horizontal padding + int paddingV = 4; // Vertical padding int cornerRadius = 0; -public: - Badge(const std::string &id) : UIElement(id) { - bgColorExpr = Expression::parse("0x00"); // Black background - fgColorExpr = Expression::parse("0xFF"); // White text + public: + Badge(const std::string& id) : UIElement(id) { + bgColorExpr = Expression::parse("0x00"); // Black background + fgColorExpr = Expression::parse("0xFF"); // White text } ElementType getType() const override { return ElementType::Badge; } - void setText(const std::string &expr) { + void setText(const std::string& expr) { textExpr = Expression::parse(expr); markDirty(); } - void setBgColor(const std::string &expr) { + void setBgColor(const std::string& expr) { bgColorExpr = Expression::parse(expr); markDirty(); } - void setFgColor(const std::string &expr) { + void setFgColor(const std::string& expr) { fgColorExpr = Expression::parse(expr); markDirty(); } @@ -221,9 +217,8 @@ public: markDirty(); } - void draw(const GfxRenderer &renderer, const ThemeContext &context) override { - if (!isVisible(context)) - return; + void draw(const GfxRenderer& renderer, const ThemeContext& context) override { + if (!isVisible(context)) return; std::string text = context.evaluatestring(textExpr); if (text.empty()) { @@ -238,10 +233,8 @@ public: int badgeH = textH + 2 * paddingV; // Use absX, absY as position, but we may auto-size - if (absW == 0) - absW = badgeW; - if (absH == 0) - absH = badgeH; + if (absW == 0) absW = badgeW; + if (absH == 0) absH = badgeH; // Draw background std::string bgStr = context.evaluatestring(bgColorExpr); @@ -264,31 +257,31 @@ public: // --- Toggle: On/Off Switch --- class Toggle : public UIElement { - Expression valueExpr; // Boolean expression + Expression valueExpr; // Boolean expression Expression onColorExpr; Expression offColorExpr; int trackWidth = 44; int trackHeight = 24; int knobSize = 20; -public: - Toggle(const std::string &id) : UIElement(id) { + public: + Toggle(const std::string& id) : UIElement(id) { valueExpr = Expression::parse("false"); - onColorExpr = Expression::parse("0x00"); // Black when on - offColorExpr = Expression::parse("0xFF"); // White when off + onColorExpr = Expression::parse("0x00"); // Black when on + offColorExpr = Expression::parse("0xFF"); // White when off } ElementType getType() const override { return ElementType::Toggle; } - void setValue(const std::string &expr) { + void setValue(const std::string& expr) { valueExpr = Expression::parse(expr); markDirty(); } - void setOnColor(const std::string &expr) { + void setOnColor(const std::string& expr) { onColorExpr = Expression::parse(expr); markDirty(); } - void setOffColor(const std::string &expr) { + void setOffColor(const std::string& expr) { offColorExpr = Expression::parse(expr); markDirty(); } @@ -305,15 +298,13 @@ public: markDirty(); } - void draw(const GfxRenderer &renderer, const ThemeContext &context) override { - if (!isVisible(context)) - return; + void draw(const GfxRenderer& renderer, const ThemeContext& context) override { + if (!isVisible(context)) return; bool isOn = context.evaluateBool(valueExpr.rawExpr); // Get colors - std::string colorStr = - isOn ? context.evaluatestring(onColorExpr) : context.evaluatestring(offColorExpr); + std::string colorStr = isOn ? context.evaluatestring(onColorExpr) : context.evaluatestring(offColorExpr); uint8_t trackColor = Color::parse(colorStr).value; // Draw track @@ -324,8 +315,7 @@ public: // Draw knob int knobMargin = (trackHeight - knobSize) / 2; - int knobX = isOn ? (trackX + trackWidth - knobSize - knobMargin) - : (trackX + knobMargin); + int knobX = isOn ? (trackX + trackWidth - knobSize - knobMargin) : (trackX + knobMargin); int knobY = trackY + knobMargin; // Knob is opposite color of track @@ -338,17 +328,17 @@ public: // --- TabBar: Horizontal tab selection --- class TabBar : public Container { - Expression selectedExpr; // Currently selected tab index or name + Expression selectedExpr; // Currently selected tab index or name int tabSpacing = 0; int padding = 0; int indicatorHeight = 3; bool showIndicator = true; -public: - TabBar(const std::string &id) : Container(id) {} + public: + TabBar(const std::string& id) : Container(id) {} ElementType getType() const override { return ElementType::TabBar; } - void setSelected(const std::string &expr) { + void setSelected(const std::string& expr) { selectedExpr = Expression::parse(expr); markDirty(); } @@ -369,12 +359,10 @@ public: markDirty(); } - void layout(const ThemeContext &context, int parentX, int parentY, - int parentW, int parentH) override { + void layout(const ThemeContext& context, int parentX, int parentY, int parentW, int parentH) override { UIElement::layout(context, parentX, parentY, parentW, parentH); - if (children.empty()) - return; + if (children.empty()) return; // Distribute tabs evenly int numTabs = children.size(); @@ -389,9 +377,8 @@ public: } } - void draw(const GfxRenderer &renderer, const ThemeContext &context) override { - if (!isVisible(context)) - return; + void draw(const GfxRenderer& renderer, const ThemeContext& context) override { + if (!isVisible(context)) return; // Draw background if set if (hasBg) { @@ -419,7 +406,7 @@ public: } if (selectedIdx >= 0 && selectedIdx < static_cast(children.size())) { - UIElement *tab = children[selectedIdx]; + UIElement* tab = children[selectedIdx]; int indX = tab->getAbsX(); int indY = absY + absH - indicatorHeight; int indW = tab->getAbsW(); @@ -437,25 +424,25 @@ public: // --- Icon: Small symbolic image --- // Can be a built-in icon name or a path to a BMP class Icon : public UIElement { - Expression srcExpr; // Icon name or path + Expression srcExpr; // Icon name or path Expression colorExpr; int iconSize = 24; // Built-in icon names and their simple representations // In a real implementation, these would be actual bitmap data -public: - Icon(const std::string &id) : UIElement(id) { - colorExpr = Expression::parse("0x00"); // Black by default + public: + Icon(const std::string& id) : UIElement(id) { + colorExpr = Expression::parse("0x00"); // Black by default } ElementType getType() const override { return ElementType::Icon; } - void setSrc(const std::string &expr) { + void setSrc(const std::string& expr) { srcExpr = Expression::parse(expr); markDirty(); } - void setColorExpr(const std::string &expr) { + void setColorExpr(const std::string& expr) { colorExpr = Expression::parse(expr); markDirty(); } @@ -464,18 +451,18 @@ public: markDirty(); } - void draw(const GfxRenderer &renderer, const ThemeContext &context) override; + void draw(const GfxRenderer& renderer, const ThemeContext& context) override; }; // --- ScrollIndicator: Visual scroll position --- class ScrollIndicator : public UIElement { - Expression positionExpr; // 0.0 to 1.0 - Expression totalExpr; // Total items - Expression visibleExpr; // Visible items + Expression positionExpr; // 0.0 to 1.0 + Expression totalExpr; // Total items + Expression visibleExpr; // Visible items int trackWidth = 4; -public: - ScrollIndicator(const std::string &id) : UIElement(id) { + public: + ScrollIndicator(const std::string& id) : UIElement(id) { positionExpr = Expression::parse("0"); totalExpr = Expression::parse("1"); visibleExpr = Expression::parse("1"); @@ -483,15 +470,15 @@ public: ElementType getType() const override { return ElementType::ScrollIndicator; } - void setPosition(const std::string &expr) { + void setPosition(const std::string& expr) { positionExpr = Expression::parse(expr); markDirty(); } - void setTotal(const std::string &expr) { + void setTotal(const std::string& expr) { totalExpr = Expression::parse(expr); markDirty(); } - void setVisibleCount(const std::string &expr) { + void setVisibleCount(const std::string& expr) { visibleExpr = Expression::parse(expr); markDirty(); } @@ -500,9 +487,8 @@ public: markDirty(); } - void draw(const GfxRenderer &renderer, const ThemeContext &context) override { - if (!isVisible(context)) - return; + void draw(const GfxRenderer& renderer, const ThemeContext& context) override { + if (!isVisible(context)) return; // Get values std::string posStr = context.evaluatestring(positionExpr); @@ -526,8 +512,7 @@ public: // Calculate thumb size and position float ratio = static_cast(visible) / static_cast(total); int thumbH = static_cast(absH * ratio); - if (thumbH < 20) - thumbH = 20; // Minimum thumb size + if (thumbH < 20) thumbH = 20; // Minimum thumb size int maxScroll = total - visible; float scrollRatio = maxScroll > 0 ? position / maxScroll : 0; @@ -540,4 +525,4 @@ public: } }; -} // namespace ThemeEngine +} // namespace ThemeEngine diff --git a/lib/ThemeEngine/include/ListElement.h b/lib/ThemeEngine/include/ListElement.h index 2e9ea4cd..b17c1275 100644 --- a/lib/ThemeEngine/include/ListElement.h +++ b/lib/ThemeEngine/include/ListElement.h @@ -1,57 +1,58 @@ #pragma once +#include +#include + #include "BasicElements.h" #include "UIElement.h" -#include -#include namespace ThemeEngine { // --- List --- // Supports vertical, horizontal, and grid layouts class List : public Container { -public: + public: enum class Direction { Vertical, Horizontal }; enum class LayoutMode { List, Grid }; -private: - std::string source; // Data source name (e.g., "MainMenu", "FileList") - std::string itemTemplateId; // ID of the template element - int itemWidth = 0; // Explicit item width (0 = auto) - int itemHeight = 0; // Explicit item height (0 = auto from template) - int scrollOffset = 0; // Scroll position for long lists - int visibleItems = -1; // Max visible items (-1 = auto) - int spacing = 0; // Gap between items - int columns = 1; // Number of columns (for grid mode) + private: + std::string source; // Data source name (e.g., "MainMenu", "FileList") + std::string itemTemplateId; // ID of the template element + int itemWidth = 0; // Explicit item width (0 = auto) + int itemHeight = 0; // Explicit item height (0 = auto from template) + int scrollOffset = 0; // Scroll position for long lists + int visibleItems = -1; // Max visible items (-1 = auto) + int spacing = 0; // Gap between items + int columns = 1; // Number of columns (for grid mode) Direction direction = Direction::Vertical; LayoutMode layoutMode = LayoutMode::List; // Template element reference (resolved after loading) - UIElement *itemTemplate = nullptr; + UIElement* itemTemplate = nullptr; -public: - List(const std::string &id) : Container(id) {} + public: + List(const std::string& id) : Container(id) {} ElementType getType() const override { return ElementType::List; } - void setSource(const std::string &s) { + void setSource(const std::string& s) { source = s; markDirty(); } - const std::string &getSource() const { return source; } + const std::string& getSource() const { return source; } - void setItemTemplateId(const std::string &id) { + void setItemTemplateId(const std::string& id) { itemTemplateId = id; markDirty(); } - void setItemTemplate(UIElement *elem) { + void setItemTemplate(UIElement* elem) { itemTemplate = elem; markDirty(); } - UIElement *getItemTemplate() const { return itemTemplate; } + UIElement* getItemTemplate() const { return itemTemplate; } void setItemWidth(int w) { itemWidth = w; @@ -64,18 +65,14 @@ public: } int getItemHeight() const { - if (itemHeight > 0) - return itemHeight; - if (itemTemplate) - return itemTemplate->getAbsH() > 0 ? itemTemplate->getAbsH() : 45; + if (itemHeight > 0) return itemHeight; + if (itemTemplate) return itemTemplate->getAbsH() > 0 ? itemTemplate->getAbsH() : 45; return 45; } int getItemWidth() const { - if (itemWidth > 0) - return itemWidth; - if (itemTemplate) - return itemTemplate->getAbsW() > 0 ? itemTemplate->getAbsW() : 100; + if (itemWidth > 0) return itemWidth; + if (itemTemplate) return itemTemplate->getAbsW() > 0 ? itemTemplate->getAbsW() : 100; return 100; } @@ -98,8 +95,7 @@ public: void setColumns(int c) { columns = c > 0 ? c : 1; - if (columns > 1) - layoutMode = LayoutMode::Grid; + if (columns > 1) layoutMode = LayoutMode::Grid; markDirty(); } @@ -108,7 +104,7 @@ public: markDirty(); } - void setDirectionFromString(const std::string &dir) { + void setDirectionFromString(const std::string& dir) { if (dir == "Horizontal" || dir == "horizontal" || dir == "row") { direction = Direction::Horizontal; } else { @@ -123,14 +119,13 @@ public: } // Resolve template reference from element map - void resolveTemplate(const std::map &elements) { + void resolveTemplate(const std::map& elements) { if (elements.count(itemTemplateId)) { itemTemplate = elements.at(itemTemplateId); } } - void layout(const ThemeContext &context, int parentX, int parentY, - int parentW, int parentH) override { + void layout(const ThemeContext& context, int parentX, int parentY, int parentW, int parentH) override { // Layout self first (bounds) UIElement::layout(context, parentX, parentY, parentW, parentH); @@ -142,7 +137,7 @@ public: } // Draw is implemented in BasicElements.cpp - void draw(const GfxRenderer &renderer, const ThemeContext &context) override; + void draw(const GfxRenderer& renderer, const ThemeContext& context) override; }; -} // namespace ThemeEngine +} // namespace ThemeEngine diff --git a/lib/ThemeEngine/include/ThemeContext.h b/lib/ThemeEngine/include/ThemeContext.h index 42c40361..1f03e654 100644 --- a/lib/ThemeEngine/include/ThemeContext.h +++ b/lib/ThemeEngine/include/ThemeContext.h @@ -11,22 +11,21 @@ namespace ThemeEngine { struct ExpressionToken { enum Type { LITERAL, VARIABLE }; Type type; - std::string value; // Literal text or variable name + std::string value; // Literal text or variable name }; // Pre-parsed expression for efficient repeated evaluation struct Expression { std::vector tokens; - std::string rawExpr; // Original expression string for complex evaluation + std::string rawExpr; // Original expression string for complex evaluation bool empty() const { return tokens.empty() && rawExpr.empty(); } - static Expression parse(const std::string &str) { + static Expression parse(const std::string& str) { Expression expr; expr.rawExpr = str; - if (str.empty()) - return expr; + if (str.empty()) return expr; size_t start = 0; while (start < str.length()) { @@ -39,8 +38,7 @@ struct Expression { if (open > start) { // Literal before variable - expr.tokens.push_back( - {ExpressionToken::LITERAL, str.substr(start, open - start)}); + expr.tokens.push_back({ExpressionToken::LITERAL, str.substr(start, open - start)}); } size_t close = str.find('}', open); @@ -51,8 +49,7 @@ struct Expression { } // Variable - expr.tokens.push_back( - {ExpressionToken::VARIABLE, str.substr(open + 1, close - open - 1)}); + expr.tokens.push_back({ExpressionToken::VARIABLE, str.substr(open + 1, close - open - 1)}); start = close + 1; } return expr; @@ -60,115 +57,94 @@ struct Expression { }; class ThemeContext { -private: + private: std::map strings; std::map ints; std::map bools; - const ThemeContext *parent = nullptr; + const ThemeContext* parent = nullptr; // Helper to trim whitespace - static std::string trim(const std::string &s) { + static std::string trim(const std::string& s) { size_t start = s.find_first_not_of(" \t\n\r"); - if (start == std::string::npos) - return ""; + if (start == std::string::npos) return ""; size_t end = s.find_last_not_of(" \t\n\r"); return s.substr(start, end - start + 1); } // Helper to check if string is a number - static bool isNumber(const std::string &s) { - if (s.empty()) - return false; + static bool isNumber(const std::string& s) { + if (s.empty()) return false; size_t start = (s[0] == '-') ? 1 : 0; for (size_t i = start; i < s.length(); i++) { - if (!isdigit(s[i])) - return false; + if (!isdigit(s[i])) return false; } return start < s.length(); } -public: - ThemeContext(const ThemeContext *parent = nullptr) : parent(parent) {} + public: + ThemeContext(const ThemeContext* parent = nullptr) : parent(parent) {} - void setString(const std::string &key, const std::string &value) { - strings[key] = value; - } - void setInt(const std::string &key, int value) { ints[key] = value; } - void setBool(const std::string &key, bool value) { bools[key] = value; } + void setString(const std::string& key, const std::string& value) { strings[key] = value; } + void setInt(const std::string& key, int value) { ints[key] = value; } + void setBool(const std::string& key, bool value) { bools[key] = value; } - std::string getString(const std::string &key, - const std::string &defaultValue = "") const { + std::string getString(const std::string& key, const std::string& defaultValue = "") const { auto it = strings.find(key); - if (it != strings.end()) - return it->second; - if (parent) - return parent->getString(key, defaultValue); + if (it != strings.end()) return it->second; + if (parent) return parent->getString(key, defaultValue); return defaultValue; } - int getInt(const std::string &key, int defaultValue = 0) const { + int getInt(const std::string& key, int defaultValue = 0) const { auto it = ints.find(key); - if (it != ints.end()) - return it->second; - if (parent) - return parent->getInt(key, defaultValue); + if (it != ints.end()) return it->second; + if (parent) return parent->getInt(key, defaultValue); return defaultValue; } - bool getBool(const std::string &key, bool defaultValue = false) const { + bool getBool(const std::string& key, bool defaultValue = false) const { auto it = bools.find(key); - if (it != bools.end()) - return it->second; - if (parent) - return parent->getBool(key, defaultValue); + if (it != bools.end()) return it->second; + if (parent) return parent->getBool(key, defaultValue); return defaultValue; } - bool hasKey(const std::string &key) const { - if (strings.count(key) || ints.count(key) || bools.count(key)) - return true; - if (parent) - return parent->hasKey(key); + bool hasKey(const std::string& key) const { + if (strings.count(key) || ints.count(key) || bools.count(key)) return true; + if (parent) return parent->hasKey(key); return false; } // Get any value as string - std::string getAnyAsString(const std::string &key) const { + std::string getAnyAsString(const std::string& key) const { // Check strings first auto sit = strings.find(key); - if (sit != strings.end()) - return sit->second; + if (sit != strings.end()) return sit->second; // Check ints auto iit = ints.find(key); - if (iit != ints.end()) - return std::to_string(iit->second); + if (iit != ints.end()) return std::to_string(iit->second); // Check bools auto bit = bools.find(key); - if (bit != bools.end()) - return bit->second ? "true" : "false"; + if (bit != bools.end()) return bit->second ? "true" : "false"; // Check parent - if (parent) - return parent->getAnyAsString(key); + if (parent) return parent->getAnyAsString(key); return ""; } // Evaluate a complex boolean expression // Supports: !, &&, ||, ==, !=, <, >, <=, >=, parentheses - bool evaluateBool(const std::string &expression) const { + bool evaluateBool(const std::string& expression) const { std::string expr = trim(expression); - if (expr.empty()) - return false; + if (expr.empty()) return false; // Handle literal true/false - if (expr == "true" || expr == "1") - return true; - if (expr == "false" || expr == "0") - return false; + if (expr == "true" || expr == "1") return true; + if (expr == "false" || expr == "0") return false; // Handle {var} wrapper if (expr.size() > 2 && expr.front() == '{' && expr.back() == '}') { @@ -185,10 +161,8 @@ public: int depth = 1; size_t closePos = 1; while (closePos < expr.length() && depth > 0) { - if (expr[closePos] == '(') - depth++; - if (expr[closePos] == ')') - depth--; + if (expr[closePos] == '(') depth++; + if (expr[closePos] == ')') depth--; closePos++; } if (closePos <= expr.length()) { @@ -212,14 +186,11 @@ public: size_t orPos = expr.find("||"); // Process || first (lower precedence than &&) - if (orPos != std::string::npos && - (andPos == std::string::npos || orPos < andPos)) { - return evaluateBool(expr.substr(0, orPos)) || - evaluateBool(expr.substr(orPos + 2)); + if (orPos != std::string::npos && (andPos == std::string::npos || orPos < andPos)) { + return evaluateBool(expr.substr(0, orPos)) || evaluateBool(expr.substr(orPos + 2)); } if (andPos != std::string::npos) { - return evaluateBool(expr.substr(0, andPos)) && - evaluateBool(expr.substr(andPos + 2)); + return evaluateBool(expr.substr(0, andPos)) && evaluateBool(expr.substr(andPos + 2)); } // Handle comparisons @@ -270,7 +241,7 @@ public: } // Compare two values (handles variables, numbers, strings) - int compareValues(const std::string &left, const std::string &right) const { + int compareValues(const std::string& left, const std::string& right) const { std::string leftVal = resolveValue(left); std::string rightVal = resolveValue(right); @@ -286,7 +257,7 @@ public: } // Resolve a value (variable name -> value, or literal) - std::string resolveValue(const std::string &val) const { + std::string resolveValue(const std::string& val) const { std::string v = trim(val); // Remove quotes for string literals @@ -298,8 +269,7 @@ public: } // If it's a number, return as-is - if (isNumber(v)) - return v; + if (isNumber(v)) return v; // Check for hex color literals (0x00, 0xFF, etc.) if (v.size() > 2 && v[0] == '0' && (v[1] == 'x' || v[1] == 'X')) { @@ -326,12 +296,11 @@ public: } // Evaluate a string expression with variable substitution - std::string evaluatestring(const Expression &expr) const { - if (expr.empty()) - return ""; + std::string evaluatestring(const Expression& expr) const { + if (expr.empty()) return ""; std::string result; - for (const auto &token : expr.tokens) { + for (const auto& token : expr.tokens) { if (token.type == ExpressionToken::LITERAL) { result += token.value; } else { @@ -339,10 +308,8 @@ public: std::string varName = token.value; // If the variable contains comparison operators, evaluate as condition - if (varName.find("==") != std::string::npos || - varName.find("!=") != std::string::npos || - varName.find("&&") != std::string::npos || - varName.find("||") != std::string::npos) { + if (varName.find("==") != std::string::npos || varName.find("!=") != std::string::npos || + varName.find("&&") != std::string::npos || varName.find("||") != std::string::npos) { result += evaluateBool(varName) ? "true" : "false"; continue; } @@ -355,7 +322,7 @@ public: std::string condition = trim(varName.substr(0, qPos)); std::string trueVal = trim(varName.substr(qPos + 1, cPos - qPos - 1)); std::string falseVal = trim(varName.substr(cPos + 1)); - + bool condResult = evaluateBool(condition); result += resolveValue(condResult ? trueVal : falseVal); continue; @@ -371,12 +338,11 @@ public: } // Legacy method for backward compatibility - std::string evaluateString(const std::string &expression) const { - if (expression.empty()) - return ""; + std::string evaluateString(const std::string& expression) const { + if (expression.empty()) return ""; Expression expr = Expression::parse(expression); return evaluatestring(expr); } }; -} // namespace ThemeEngine +} // namespace ThemeEngine diff --git a/lib/ThemeEngine/include/ThemeManager.h b/lib/ThemeEngine/include/ThemeManager.h index 65c99f9b..1bee3566 100644 --- a/lib/ThemeEngine/include/ThemeManager.h +++ b/lib/ThemeEngine/include/ThemeManager.h @@ -1,12 +1,14 @@ #pragma once +#include + +#include +#include +#include + #include "BasicElements.h" #include "IniParser.h" #include "ThemeContext.h" -#include -#include -#include -#include namespace ThemeEngine { @@ -18,10 +20,10 @@ struct ProcessedAsset { // Screen render cache - stores full screen state for quick restore struct ScreenCache { - uint8_t *buffer = nullptr; + uint8_t* buffer = nullptr; size_t bufferSize = 0; std::string screenName; - uint32_t contextHash = 0; // Hash of context data to detect changes + uint32_t contextHash = 0; // Hash of context data to detect changes bool valid = false; ~ScreenCache() { @@ -35,8 +37,8 @@ struct ScreenCache { }; class ThemeManager { -private: - std::map elements; // All elements by ID + private: + std::map elements; // All elements by ID std::string currentThemeName; int navBookCount = 1; // Number of navigable book slots (from theme [Global] section) std::map fontMap; @@ -49,12 +51,11 @@ private: std::map elementDependsOnData; // Factory and property methods - UIElement *createElement(const std::string &id, const std::string &type); - void applyProperties(UIElement *elem, - const std::map &props); + UIElement* createElement(const std::string& id, const std::string& type); + void applyProperties(UIElement* elem, const std::map& props); -public: - static ThemeManager &get() { + public: + static ThemeManager& get() { static ThemeManager instance; return instance; } @@ -63,66 +64,59 @@ public: void begin(); // Register a font ID mapping (e.g. "UI_12" -> 0) - void registerFont(const std::string &name, int id); + void registerFont(const std::string& name, int id); // Theme loading - void loadTheme(const std::string &themeName); + void loadTheme(const std::string& themeName); void unloadTheme(); // Get current theme name - const std::string &getCurrentTheme() const { return currentThemeName; } + const std::string& getCurrentTheme() const { return currentThemeName; } // Get number of navigable book slots (from theme config, default 1) int getNavBookCount() const { return navBookCount; } // Render a screen - void renderScreen(const std::string &screenName, const GfxRenderer &renderer, - const ThemeContext &context); + void renderScreen(const std::string& screenName, const GfxRenderer& renderer, const ThemeContext& context); // Render with dirty tracking (only redraws changed regions) - void renderScreenOptimized(const std::string &screenName, - const GfxRenderer &renderer, - const ThemeContext &context, - const ThemeContext *prevContext = nullptr); + void renderScreenOptimized(const std::string& screenName, const GfxRenderer& renderer, const ThemeContext& context, + const ThemeContext* prevContext = nullptr); // Invalidate all caches (call when theme changes or screen switches) void invalidateAllCaches(); // Invalidate specific screen cache - void invalidateScreenCache(const std::string &screenName); + void invalidateScreenCache(const std::string& screenName); // Enable/disable caching void setCachingEnabled(bool enabled) { useCaching = enabled; } bool isCachingEnabled() const { return useCaching; } // Asset path resolution - std::string getAssetPath(const std::string &assetName); + std::string getAssetPath(const std::string& assetName); // Asset caching - const std::vector *getCachedAsset(const std::string &path); - const ProcessedAsset *getProcessedAsset(const std::string &path, - GfxRenderer::Orientation orientation, + const std::vector* getCachedAsset(const std::string& path); + const ProcessedAsset* getProcessedAsset(const std::string& path, GfxRenderer::Orientation orientation, int targetW = 0, int targetH = 0); - void cacheProcessedAsset(const std::string &path, - const ProcessedAsset &asset, - int targetW = 0, int targetH = 0); + void cacheProcessedAsset(const std::string& path, const ProcessedAsset& asset, int targetW = 0, int targetH = 0); // Clear asset caches (for memory management) void clearAssetCaches(); // Get element by ID (useful for direct manipulation) - UIElement *getElement(const std::string &id) { + UIElement* getElement(const std::string& id) { auto it = elements.find(id); return it != elements.end() ? it->second : nullptr; } -private: + private: std::map> assetCache; std::map processedCache; // Compute a simple hash of context data for cache invalidation - uint32_t computeContextHash(const ThemeContext &context, - const std::string &screenName); + uint32_t computeContextHash(const ThemeContext& context, const std::string& screenName); }; -} // namespace ThemeEngine +} // namespace ThemeEngine diff --git a/lib/ThemeEngine/include/ThemeTypes.h b/lib/ThemeEngine/include/ThemeTypes.h index 554c582f..d9d733f6 100644 --- a/lib/ThemeEngine/include/ThemeTypes.h +++ b/lib/ThemeEngine/include/ThemeTypes.h @@ -14,13 +14,11 @@ struct Dimension { Dimension(int v, DimensionUnit u) : value(v), unit(u) {} Dimension() : value(0), unit(DimensionUnit::PIXELS) {} - static Dimension parse(const std::string &str) { - if (str.empty()) - return Dimension(0, DimensionUnit::PIXELS); + static Dimension parse(const std::string& str) { + if (str.empty()) return Dimension(0, DimensionUnit::PIXELS); if (str.back() == '%') { - return Dimension(std::stoi(str.substr(0, str.length() - 1)), - DimensionUnit::PERCENT); + return Dimension(std::stoi(str.substr(0, str.length() - 1)), DimensionUnit::PERCENT); } return Dimension(std::stoi(str), DimensionUnit::PIXELS); } @@ -34,20 +32,16 @@ struct Dimension { }; struct Color { - uint8_t value; // For E-Ink: 0 (Black) to 255 (White), or simplified palette + uint8_t value; // For E-Ink: 0 (Black) to 255 (White), or simplified palette Color(uint8_t v) : value(v) {} Color() : value(0) {} - static Color parse(const std::string &str) { - if (str.empty()) - return Color(0); - if (str == "black") - return Color(0x00); - if (str == "white") - return Color(0xFF); - if (str == "gray" || str == "grey") - return Color(0x80); + static Color parse(const std::string& str) { + if (str.empty()) return Color(0); + if (str == "black") return Color(0x00); + if (str == "white") return Color(0xFF); + if (str == "gray" || str == "grey") return Color(0x80); if (str.size() > 2 && str.substr(0, 2) == "0x") { return Color((uint8_t)std::strtol(str.c_str(), nullptr, 16)); } @@ -64,16 +58,13 @@ struct Rect { bool isEmpty() const { return w <= 0 || h <= 0; } - bool intersects(const Rect &other) const { - return !(x + w <= other.x || other.x + other.w <= x || y + h <= other.y || - other.y + other.h <= y); + bool intersects(const Rect& other) const { + return !(x + w <= other.x || other.x + other.w <= x || y + h <= other.y || other.y + other.h <= y); } - Rect unite(const Rect &other) const { - if (isEmpty()) - return other; - if (other.isEmpty()) - return *this; + Rect unite(const Rect& other) const { + if (isEmpty()) return other; + if (other.isEmpty()) return *this; int nx = std::min(x, other.x); int ny = std::min(y, other.y); int nx2 = std::max(x + w, other.x + other.w); @@ -82,4 +73,4 @@ struct Rect { } }; -} // namespace ThemeEngine +} // namespace ThemeEngine diff --git a/lib/ThemeEngine/include/UIElement.h b/lib/ThemeEngine/include/UIElement.h index 306c4548..ac53f7ac 100644 --- a/lib/ThemeEngine/include/UIElement.h +++ b/lib/ThemeEngine/include/UIElement.h @@ -1,52 +1,51 @@ #pragma once -#include "ThemeContext.h" -#include "ThemeTypes.h" #include + #include #include +#include "ThemeContext.h" +#include "ThemeTypes.h" + namespace ThemeEngine { -class Container; // Forward declaration +class Container; // Forward declaration class UIElement { -public: + public: int getAbsX() const { return absX; } int getAbsY() const { return absY; } int getAbsW() const { return absW; } int getAbsH() const { return absH; } - const std::string &getId() const { return id; } + const std::string& getId() const { return id; } -protected: + protected: std::string id; Dimension x, y, width, height; Expression visibleExpr; - bool visibleExprIsStatic = true; // True if visibility doesn't depend on data + bool visibleExprIsStatic = true; // True if visibility doesn't depend on data // Recomputed every layout pass int absX = 0, absY = 0, absW = 0, absH = 0; // Caching support - bool cacheable = false; // Set true for expensive elements like bitmaps - bool cacheValid = false; // Is the cached render still valid? - uint8_t *cachedRender = nullptr; + bool cacheable = false; // Set true for expensive elements like bitmaps + bool cacheValid = false; // Is the cached render still valid? + uint8_t* cachedRender = nullptr; size_t cachedRenderSize = 0; int cachedX = 0, cachedY = 0, cachedW = 0, cachedH = 0; // Dirty tracking - bool dirty = true; // Needs redraw + bool dirty = true; // Needs redraw - bool isVisible(const ThemeContext &context) const { - if (visibleExpr.empty()) - return true; + bool isVisible(const ThemeContext& context) const { + if (visibleExpr.empty()) return true; return context.evaluateBool(visibleExpr.rawExpr); } -public: - UIElement(const std::string &id) : id(id) { - visibleExpr = Expression::parse("true"); - } + public: + UIElement(const std::string& id) : id(id) { visibleExpr = Expression::parse("true"); } virtual ~UIElement() { if (cachedRender) { @@ -71,11 +70,11 @@ public: height = val; markDirty(); } - void setVisibleExpr(const std::string &expr) { + void setVisibleExpr(const std::string& expr) { visibleExpr = Expression::parse(expr); // Check if expression contains variables - visibleExprIsStatic = (expr == "true" || expr == "false" || expr == "1" || - expr == "0" || expr.find('{') == std::string::npos); + visibleExprIsStatic = + (expr == "true" || expr == "false" || expr == "1" || expr == "0" || expr.find('{') == std::string::npos); markDirty(); } @@ -97,31 +96,24 @@ public: } // Calculate absolute position based on parent - virtual void layout(const ThemeContext &context, int parentX, int parentY, - int parentW, int parentH) { + virtual void layout(const ThemeContext& context, int parentX, int parentY, int parentW, int parentH) { int newX = parentX + x.resolve(parentW); int newY = parentY + y.resolve(parentH); int newW = width.resolve(parentW); int newH = height.resolve(parentH); // Clamp to parent bounds - if (newX >= parentX + parentW) - newX = parentX + parentW - 1; - if (newY >= parentY + parentH) - newY = parentY + parentH - 1; + if (newX >= parentX + parentW) newX = parentX + parentW - 1; + if (newY >= parentY + parentH) newY = parentY + parentH - 1; int maxX = parentX + parentW; int maxY = parentY + parentH; - if (newX + newW > maxX) - newW = maxX - newX; - if (newY + newH > maxY) - newH = maxY - newY; + if (newX + newW > maxX) newW = maxX - newX; + if (newY + newH > maxY) newH = maxY - newY; - if (newW < 0) - newW = 0; - if (newH < 0) - newH = 0; + if (newW < 0) newW = 0; + if (newH < 0) newH = 0; // Check if position changed if (newX != absX || newY != absY || newW != absW || newH != absH) { @@ -133,7 +125,7 @@ public: } } - virtual Container *asContainer() { return nullptr; } + virtual Container* asContainer() { return nullptr; } enum class ElementType { Base, @@ -166,12 +158,11 @@ public: Rect getBounds() const { return Rect(absX, absY, absW, absH); } // Main draw method - handles caching automatically - virtual void draw(const GfxRenderer &renderer, - const ThemeContext &context) = 0; + virtual void draw(const GfxRenderer& renderer, const ThemeContext& context) = 0; -protected: + protected: // Cache the rendered output - bool cacheRender(const GfxRenderer &renderer) { + bool cacheRender(const GfxRenderer& renderer) { if (cachedRender) { free(cachedRender); cachedRender = nullptr; @@ -190,16 +181,13 @@ protected: } // Restore from cache - bool restoreFromCache(const GfxRenderer &renderer) const { - if (!cacheValid || !cachedRender) - return false; - if (absX != cachedX || absY != cachedY || absW != cachedW || - absH != cachedH) - return false; + bool restoreFromCache(const GfxRenderer& renderer) const { + if (!cacheValid || !cachedRender) return false; + if (absX != cachedX || absY != cachedY || absW != cachedW || absH != cachedH) return false; renderer.restoreRegion(cachedRender, absX, absY, absW, absH); return true; } }; -} // namespace ThemeEngine +} // namespace ThemeEngine diff --git a/lib/ThemeEngine/src/BasicElements.cpp b/lib/ThemeEngine/src/BasicElements.cpp index 1f180076..8fed357a 100644 --- a/lib/ThemeEngine/src/BasicElements.cpp +++ b/lib/ThemeEngine/src/BasicElements.cpp @@ -1,4 +1,5 @@ #include "BasicElements.h" + #include "Bitmap.h" #include "ListElement.h" #include "ThemeManager.h" @@ -7,8 +8,7 @@ namespace ThemeEngine { // --- BitmapElement --- -void BitmapElement::draw(const GfxRenderer &renderer, - const ThemeContext &context) { +void BitmapElement::draw(const GfxRenderer& renderer, const ThemeContext& context) { if (!isVisible(context)) { markClean(); return; @@ -21,13 +21,12 @@ void BitmapElement::draw(const GfxRenderer &renderer, } // Check if we have a cached 1-bit render of this bitmap at this size - const ProcessedAsset *processed = - ThemeManager::get().getProcessedAsset(path, renderer.getOrientation(), absW, absH); + const ProcessedAsset* processed = ThemeManager::get().getProcessedAsset(path, renderer.getOrientation(), absW, absH); if (processed && processed->w == absW && processed->h == absH) { // Draw cached 1-bit data directly const int rowBytes = (absW + 7) / 8; for (int y = 0; y < absH; y++) { - const uint8_t *srcRow = processed->data.data() + y * rowBytes; + const uint8_t* srcRow = processed->data.data() + y * rowBytes; for (int x = 0; x < absW; x++) { bool isBlack = !(srcRow[x / 8] & (1 << (7 - (x % 8)))); if (isBlack) { @@ -40,7 +39,7 @@ void BitmapElement::draw(const GfxRenderer &renderer, } // Load raw asset from cache (file data cached in memory) - const std::vector *data = ThemeManager::get().getCachedAsset(path); + const std::vector* data = ThemeManager::get().getCachedAsset(path); if (!data || data->empty()) { markClean(); return; @@ -54,19 +53,19 @@ void BitmapElement::draw(const GfxRenderer &renderer, // Draw the bitmap (handles scaling internally) renderer.drawBitmap(bmp, absX, absY, absW, absH); - + // Cache the result as 1-bit packed data for next time ProcessedAsset asset; asset.w = absW; asset.h = absH; asset.orientation = renderer.getOrientation(); - + const int rowBytes = (absW + 7) / 8; - asset.data.resize(rowBytes * absH, 0xFF); // Initialize to white - + asset.data.resize(rowBytes * absH, 0xFF); // Initialize to white + // Capture pixels using renderer's coordinate system for (int y = 0; y < absH; y++) { - uint8_t *dstRow = asset.data.data() + y * rowBytes; + uint8_t* dstRow = asset.data.data() + y * rowBytes; for (int x = 0; x < absW; x++) { // Read pixel from framebuffer (this handles orientation) bool isBlack = renderer.readPixel(absX + x, absY + y); @@ -75,14 +74,14 @@ void BitmapElement::draw(const GfxRenderer &renderer, } } } - + ThemeManager::get().cacheProcessedAsset(path, asset, absW, absH); markClean(); } // --- List --- -void List::draw(const GfxRenderer &renderer, const ThemeContext &context) { +void List::draw(const GfxRenderer& renderer, const ThemeContext& context) { if (!isVisible(context)) { markClean(); return; @@ -148,8 +147,7 @@ void List::draw(const GfxRenderer &renderer, const ThemeContext &context) { currentX += itemW + spacing; continue; } - if (currentX > absX + absW) - break; + if (currentX > absX + absW) break; } else { // Grid mode if (currentY + itemH < absY) { @@ -162,8 +160,7 @@ void List::draw(const GfxRenderer &renderer, const ThemeContext &context) { currentX = absX + col * (itemW + spacing); continue; } - if (currentY > absY + absH) - break; + if (currentY > absY + absH) break; } // Layout and draw @@ -225,4 +222,4 @@ void List::draw(const GfxRenderer &renderer, const ThemeContext &context) { markClean(); } -} // namespace ThemeEngine +} // namespace ThemeEngine diff --git a/lib/ThemeEngine/src/IniParser.cpp b/lib/ThemeEngine/src/IniParser.cpp index 3d44cf54..022aa805 100644 --- a/lib/ThemeEngine/src/IniParser.cpp +++ b/lib/ThemeEngine/src/IniParser.cpp @@ -1,11 +1,11 @@ #include "IniParser.h" + #include namespace ThemeEngine { -void IniParser::trim(std::string &s) { - if (s.empty()) - return; +void IniParser::trim(std::string& s) { + if (s.empty()) return; // Trim left size_t first = s.find_first_not_of(" \t\n\r"); @@ -19,14 +19,13 @@ void IniParser::trim(std::string &s) { s = s.substr(first, (last - first + 1)); } -std::map> -IniParser::parse(Stream &stream) { +std::map> IniParser::parse(Stream& stream) { std::map> sections; // stream check not strictly possible like file, can rely on available() std::string currentSection = ""; - String line; // Use Arduino String for easy file reading, then convert to - // std::string + String line; // Use Arduino String for easy file reading, then convert to + // std::string while (stream.available()) { line = stream.readStringUntil('\n'); @@ -34,7 +33,7 @@ IniParser::parse(Stream &stream) { trim(sLine); if (sLine.empty() || sLine[0] == ';' || sLine[0] == '#') { - continue; // Skip comments and empty lines + continue; // Skip comments and empty lines } if (sLine.front() == '[' && sLine.back() == ']') { @@ -62,8 +61,7 @@ IniParser::parse(Stream &stream) { return sections; } -std::map> -IniParser::parseString(const std::string &content) { +std::map> IniParser::parseString(const std::string& content) { std::map> sections; std::stringstream ss(content); std::string line; @@ -101,4 +99,4 @@ IniParser::parseString(const std::string &content) { return sections; } -} // namespace ThemeEngine +} // namespace ThemeEngine diff --git a/lib/ThemeEngine/src/LayoutElements.cpp b/lib/ThemeEngine/src/LayoutElements.cpp index 5fd04d5a..5b08af92 100644 --- a/lib/ThemeEngine/src/LayoutElements.cpp +++ b/lib/ThemeEngine/src/LayoutElements.cpp @@ -1,12 +1,14 @@ #include "LayoutElements.h" -#include "ThemeManager.h" + #include +#include "ThemeManager.h" + namespace ThemeEngine { // Built-in icon drawing // These are simple geometric representations of common icons -void Icon::draw(const GfxRenderer &renderer, const ThemeContext &context) { +void Icon::draw(const GfxRenderer& renderer, const ThemeContext& context) { if (!isVisible(context)) { markClean(); return; @@ -29,15 +31,14 @@ void Icon::draw(const GfxRenderer &renderer, const ThemeContext &context) { int cy = absY + h / 2; // Check if it's a path to a BMP file - if (iconName.find('/') != std::string::npos || - iconName.find('.') != std::string::npos) { + if (iconName.find('/') != std::string::npos || iconName.find('.') != std::string::npos) { // Try to load as bitmap std::string path = iconName; if (path[0] != '/') { path = ThemeManager::get().getAssetPath(iconName); } - const std::vector *data = ThemeManager::get().getCachedAsset(path); + const std::vector* data = ThemeManager::get().getCachedAsset(path); if (data && !data->empty()) { Bitmap bmp(data->data(), data->size()); if (bmp.parseHeaders() == BmpReaderError::Ok) { @@ -170,4 +171,4 @@ void Icon::draw(const GfxRenderer &renderer, const ThemeContext &context) { markClean(); } -} // namespace ThemeEngine +} // namespace ThemeEngine diff --git a/lib/ThemeEngine/src/ThemeManager.cpp b/lib/ThemeEngine/src/ThemeManager.cpp index 24330167..6e706bc4 100644 --- a/lib/ThemeEngine/src/ThemeManager.cpp +++ b/lib/ThemeEngine/src/ThemeManager.cpp @@ -1,82 +1,63 @@ #include "ThemeManager.h" -#include "DefaultTheme.h" -#include "LayoutElements.h" -#include "ListElement.h" + #include + #include #include #include +#include "DefaultTheme.h" +#include "LayoutElements.h" +#include "ListElement.h" + namespace ThemeEngine { void ThemeManager::begin() { // Default fonts or setup } -void ThemeManager::registerFont(const std::string &name, int id) { - fontMap[name] = id; -} +void ThemeManager::registerFont(const std::string& name, int id) { fontMap[name] = id; } -std::string ThemeManager::getAssetPath(const std::string &assetName) { +std::string ThemeManager::getAssetPath(const std::string& assetName) { // Check if absolute path - if (!assetName.empty() && assetName[0] == '/') - return assetName; + if (!assetName.empty() && assetName[0] == '/') return assetName; // Otherwise relative to theme assets return "/themes/" + currentThemeName + "/assets/" + assetName; } -UIElement *ThemeManager::createElement(const std::string &id, - const std::string &type) { +UIElement* ThemeManager::createElement(const std::string& id, const std::string& type) { // Basic elements - if (type == "Container") - return new Container(id); - if (type == "Rectangle") - return new Rectangle(id); - if (type == "Label") - return new Label(id); - if (type == "Bitmap") - return new BitmapElement(id); - if (type == "List") - return new List(id); - if (type == "ProgressBar") - return new ProgressBar(id); - if (type == "Divider") - return new Divider(id); + if (type == "Container") return new Container(id); + if (type == "Rectangle") return new Rectangle(id); + if (type == "Label") return new Label(id); + if (type == "Bitmap") return new BitmapElement(id); + if (type == "List") return new List(id); + if (type == "ProgressBar") return new ProgressBar(id); + if (type == "Divider") return new Divider(id); // Layout elements - if (type == "HStack") - return new HStack(id); - if (type == "VStack") - return new VStack(id); - if (type == "Grid") - return new Grid(id); + if (type == "HStack") return new HStack(id); + if (type == "VStack") return new VStack(id); + if (type == "Grid") return new Grid(id); // Advanced elements - if (type == "Badge") - return new Badge(id); - if (type == "Toggle") - return new Toggle(id); - if (type == "TabBar") - return new TabBar(id); - if (type == "Icon") - return new Icon(id); - if (type == "ScrollIndicator") - return new ScrollIndicator(id); - if (type == "BatteryIcon") - return new BatteryIcon(id); + if (type == "Badge") return new Badge(id); + if (type == "Toggle") return new Toggle(id); + if (type == "TabBar") return new TabBar(id); + if (type == "Icon") return new Icon(id); + if (type == "ScrollIndicator") return new ScrollIndicator(id); + if (type == "BatteryIcon") return new BatteryIcon(id); return nullptr; } -void ThemeManager::applyProperties( - UIElement *elem, const std::map &props) { - +void ThemeManager::applyProperties(UIElement* elem, const std::map& props) { const auto elemType = elem->getType(); - - for (const auto &kv : props) { - const std::string &key = kv.first; - const std::string &val = kv.second; + + for (const auto& kv : props) { + const std::string& key = kv.first; + const std::string& val = kv.second; // ========== Common properties ========== if (key == "X") @@ -95,7 +76,7 @@ void ThemeManager::applyProperties( // ========== Rectangle properties ========== else if (key == "Fill") { if (elemType == UIElement::ElementType::Rectangle) { - auto rect = static_cast(elem); + auto rect = static_cast(elem); if (val.find('{') != std::string::npos) { rect->setFillExpr(val); } else { @@ -104,21 +85,19 @@ void ThemeManager::applyProperties( } } else if (key == "Color") { if (elemType == UIElement::ElementType::Rectangle) { - static_cast(elem)->setColorExpr(val); - } else if (elemType == UIElement::ElementType::Container || - elemType == UIElement::ElementType::HStack || - elemType == UIElement::ElementType::VStack || - elemType == UIElement::ElementType::Grid || + static_cast(elem)->setColorExpr(val); + } else if (elemType == UIElement::ElementType::Container || elemType == UIElement::ElementType::HStack || + elemType == UIElement::ElementType::VStack || elemType == UIElement::ElementType::Grid || elemType == UIElement::ElementType::TabBar) { - static_cast(elem)->setBackgroundColorExpr(val); + static_cast(elem)->setBackgroundColorExpr(val); } else if (elemType == UIElement::ElementType::Label) { - static_cast