From 14781b8c076f23fbad3c01fb8c51edb02770e196 Mon Sep 17 00:00:00 2001 From: David Gwilliam Date: Fri, 29 May 2026 20:17:15 -0700 Subject: [PATCH] refactor: move TileLayoutHelper from DisplayManager.h to Style.h - Pure static helper class moved from DisplayManager.h to Style.h - Drivers now use TileLayoutHelper::calculateLayouts() directly - Remove extern DisplayManager references from display drivers - Drop LGFX_USE_V1 build flag (no longer needed) - Add delay(LOOP_YIELD_MS) in S3 loop to prevent watchdog --- boards/esp32-32e-4/DisplayDriverTFT.cpp | 12 +- boards/esp32-s3-lcd-43/DisplayDriverGFX.cpp | 11 +- boards/esp32-s3-lcd-43/board-config.sh | 2 +- boards/esp32-s3-lcd-43/esp32-s3-lcd-43.ino | 3 + libraries/KlubhausCore/src/DisplayManager.h | 119 -------------------- libraries/KlubhausCore/src/Style.h | 118 +++++++++++++++++++ 6 files changed, 134 insertions(+), 131 deletions(-) diff --git a/boards/esp32-32e-4/DisplayDriverTFT.cpp b/boards/esp32-32e-4/DisplayDriverTFT.cpp index 740909b..3548ddb 100644 --- a/boards/esp32-32e-4/DisplayDriverTFT.cpp +++ b/boards/esp32-32e-4/DisplayDriverTFT.cpp @@ -3,8 +3,7 @@ #include #include #include - -extern DisplayManager display; +#include // ── Fonts ─────────────────────────────────────────────────── // TFT_eSPI built-in fonts for 320x480 display (scaled from 800x480) @@ -257,10 +256,11 @@ void DisplayDriverTFT::drawDashboard(const ScreenState& st) { _tft.setCursor(dispW - wifiW - 10, 20); _tft.print(wifiText); - // Get tile layouts from library helper - int tileCount = display.calculateDashboardLayouts(STYLE_HEADER_HEIGHT, STYLE_TILE_GAP); - display.setHeaderHeight(STYLE_HEADER_HEIGHT); - const TileLayout* layouts = display.getTileLayouts(); + // Calculate tile layouts using TileLayoutHelper + TileLayout layouts[DASHBOARD_TILE_COUNT]; + int gridCols, gridRows; + int tileCount = TileLayoutHelper::calculateLayouts( + dispW, dispH, STYLE_HEADER_HEIGHT, STYLE_TILE_GAP, layouts, &gridCols, &gridRows); const char* tileLabels[] = { "Alert", "Silent", "Status", "Reboot" }; const uint16_t tileColors[] = { 0x0280, 0x0400, 0x0440, 0x0100 }; diff --git a/boards/esp32-s3-lcd-43/DisplayDriverGFX.cpp b/boards/esp32-s3-lcd-43/DisplayDriverGFX.cpp index 729fe91..b8a2372 100644 --- a/boards/esp32-s3-lcd-43/DisplayDriverGFX.cpp +++ b/boards/esp32-s3-lcd-43/DisplayDriverGFX.cpp @@ -6,10 +6,10 @@ #include #include +#include // ── Globals ── static LGFX* _gfx = nullptr; -extern DisplayManager display; // ── Forward declarations ── static void initDisplay(); @@ -443,10 +443,11 @@ void DisplayDriverGFX::drawDashboard(const ScreenState& state) { }, safeWifi.w); - // Get tile layouts from library helper - int tileCount = display.calculateDashboardLayouts(STYLE_HEADER_HEIGHT, STYLE_TILE_GAP); - display.setHeaderHeight(STYLE_HEADER_HEIGHT); - const TileLayout* layouts = display.getTileLayouts(); + // Calculate tile layouts using TileLayoutHelper + TileLayout layouts[DASHBOARD_TILE_COUNT]; + int gridCols, gridRows; + int tileCount = TileLayoutHelper::calculateLayouts( + DISP_W, DISP_H, STYLE_HEADER_HEIGHT, STYLE_TILE_GAP, layouts, &gridCols, &gridRows); const char* tileLabels[] = { "Alert", "Silent", "Status", "Reboot" }; const uint16_t tileColors[] = { 0x0280, 0x0400, 0x0440, 0x0100 }; diff --git a/boards/esp32-s3-lcd-43/board-config.sh b/boards/esp32-s3-lcd-43/board-config.sh index f1e3c3b..6dac840 100644 --- a/boards/esp32-s3-lcd-43/board-config.sh +++ b/boards/esp32-s3-lcd-43/board-config.sh @@ -1,4 +1,4 @@ FQBN="esp32:esp32:waveshare_esp32_s3_touch_lcd_43:PSRAM=enabled,FlashSize=16M,USBMode=hwcdc,PartitionScheme=app3M_fat9M_16MB" PORT="/dev/ttyUSB0" LIBS="--library ./vendor/esp32-s3-lcd-43/LovyanGFX" -OPTS="-DDEBUG_MODE -DBOARD_HAS_PSRAM -DLGFX_USE_V1 -DLOCAL_SECRETS" +OPTS="-DDEBUG_MODE -DBOARD_HAS_PSRAM -DLOCAL_SECRETS" diff --git a/boards/esp32-s3-lcd-43/esp32-s3-lcd-43.ino b/boards/esp32-s3-lcd-43/esp32-s3-lcd-43.ino index 7afaa23..52551ca 100644 --- a/boards/esp32-s3-lcd-43/esp32-s3-lcd-43.ino +++ b/boards/esp32-s3-lcd-43/esp32-s3-lcd-43.ino @@ -38,4 +38,7 @@ void loop() { // Serial console logic.processSerial(); + + // Yield to WiFi/BT stack + delay(LOOP_YIELD_MS); } diff --git a/libraries/KlubhausCore/src/DisplayManager.h b/libraries/KlubhausCore/src/DisplayManager.h index a9a7ce7..0512a63 100644 --- a/libraries/KlubhausCore/src/DisplayManager.h +++ b/libraries/KlubhausCore/src/DisplayManager.h @@ -4,125 +4,6 @@ #include "ScreenState.h" #include "Style.h" -#include - -/// Layout helper for dashboard tiles - computes positions based on constraints -class TileLayoutHelper { -public: - /// Calculate tile layouts for the given display dimensions - /// Returns array of TileLayout (must have capacity for DASHBOARD_TILE_COUNT) - /// Returns the number of columns and rows used - static int calculateLayouts(int displayW, int displayH, int headerH, int margin, - TileLayout* outLayouts, int* outCols, int* outRows) { - int contentH = displayH - headerH; - int tileCount = DASHBOARD_TILE_COUNT; - - // Determine base grid based on display aspect ratio - int cols, rows; - calculateGrid(tileCount, displayW, contentH, &cols, &rows); - - // Calculate base cell sizes - int cellW = displayW / cols; - int cellH = contentH / rows; - - // Simple first-fit: place tiles in order, respecting min sizes - // For more complex layouts, tiles could specify preferred positions - for(int i = 0; i < tileCount; i++) { - const DashboardTile& tile = DASHBOARD_TILES[i]; - int tileCols = tile.constraint.minCols; - int tileRows = tile.constraint.minRows; - - // Find next available position - int col = 0, row = 0; - findNextPosition(outLayouts, i, cols, rows, &col, &row); - - // Ensure tile fits within grid - if(col + tileCols > cols) - tileCols = cols - col; - if(row + tileRows > rows) - tileRows = rows - row; - - // Calculate pixel position - int x = col * cellW + margin; - int y = headerH + row * cellH + margin; - int w = tileCols * cellW - 2 * margin; - int h = tileRows * cellH - 2 * margin; - - outLayouts[i] = { x, y, w, h, col, row, tileCols, tileRows }; - } - - *outCols = cols; - *outRows = rows; - return tileCount; - } - -private: - /// Calculate optimal grid dimensions based on display and tile constraints - static void calculateGrid( - int tileCount, int displayW, int contentH, int* outCols, int* outRows) { - // Use 2x2 grid for 4 tiles regardless of aspect ratio - // This provides better visual balance than a single row - if(tileCount == 4) { - *outCols = 2; - *outRows = 2; - return; - } - - // Calculate aspect ratio to determine preferred layout - float aspectRatio = (float)displayW / contentH; - - // Start with simple square-ish grid - int cols = (int)std::sqrt(tileCount * aspectRatio); - if(cols < 1) - cols = 1; - if(cols > tileCount) - cols = tileCount; - - int rows = (tileCount + cols - 1) / cols; - - // For wide displays (landscape), prefer more columns - if(aspectRatio > 1.5f && tileCount <= 6) { - cols = tileCount; - rows = 1; - } - // For tall displays (portrait), prefer more rows - else if(aspectRatio < 0.8f && tileCount <= 6) { - rows = tileCount; - cols = 1; - } - - *outCols = cols; - *outRows = rows; - } - - /// Find next available grid position - static void findNextPosition(const TileLayout* layouts, int count, int gridCols, int gridRows, - int* outCol, int* outRow) { - // Simple: find first empty cell - // Could be enhanced to pack tightly based on tile sizes - for(int r = 0; r < gridRows; r++) { - for(int c = 0; c < gridCols; c++) { - bool occupied = false; - for(int i = 0; i < count; i++) { - if(layouts[i].col <= c && c < layouts[i].col + layouts[i].cols - && layouts[i].row <= r && r < layouts[i].row + layouts[i].rows) { - occupied = true; - break; - } - } - if(!occupied) { - *outCol = c; - *outRow = r; - return; - } - } - } - // Fallback: just use count position - *outCol = count % gridCols; - *outRow = count / gridCols; - } -}; - class DisplayManager { public: DisplayManager(IDisplayDriver* drv) diff --git a/libraries/KlubhausCore/src/Style.h b/libraries/KlubhausCore/src/Style.h index 9d67159..79ab1d2 100644 --- a/libraries/KlubhausCore/src/Style.h +++ b/libraries/KlubhausCore/src/Style.h @@ -2,6 +2,7 @@ #include #include +#include struct Layout { uint16_t x, y, w, h; @@ -227,3 +228,120 @@ public: static Rect icon(int16_t x, int16_t y, int16_t size) { return Rect(x, y, size, size); } }; + +/// Layout helper for dashboard tiles - computes positions based on constraints +class TileLayoutHelper { +public: + /// Calculate tile layouts for the given display dimensions + /// Returns array of TileLayout (must have capacity for DASHBOARD_TILE_COUNT) + /// Returns the number of columns and rows used + static int calculateLayouts(int displayW, int displayH, int headerH, int margin, + TileLayout* outLayouts, int* outCols, int* outRows) { + int contentH = displayH - headerH; + int tileCount = DASHBOARD_TILE_COUNT; + + // Determine base grid based on display aspect ratio + int cols, rows; + calculateGrid(tileCount, displayW, contentH, &cols, &rows); + + // Calculate base cell sizes + int cellW = displayW / cols; + int cellH = contentH / rows; + + // Simple first-fit: place tiles in order, respecting min sizes + // For more complex layouts, tiles could specify preferred positions + for(int i = 0; i < tileCount; i++) { + const DashboardTile& tile = DASHBOARD_TILES[i]; + int tileCols = tile.constraint.minCols; + int tileRows = tile.constraint.minRows; + + // Find next available position + int col = 0, row = 0; + findNextPosition(outLayouts, i, cols, rows, &col, &row); + + // Ensure tile fits within grid + if(col + tileCols > cols) + tileCols = cols - col; + if(row + tileRows > rows) + tileRows = rows - row; + + // Calculate pixel position + int x = col * cellW + margin; + int y = headerH + row * cellH + margin; + int w = tileCols * cellW - 2 * margin; + int h = tileRows * cellH - 2 * margin; + + outLayouts[i] = { x, y, w, h, col, row, tileCols, tileRows }; + } + + *outCols = cols; + *outRows = rows; + return tileCount; + } + +private: + /// Calculate optimal grid dimensions based on display and tile constraints + static void calculateGrid( + int tileCount, int displayW, int contentH, int* outCols, int* outRows) { + // Use 2x2 grid for 4 tiles regardless of aspect ratio + // This provides better visual balance than a single row + if(tileCount == 4) { + *outCols = 2; + *outRows = 2; + return; + } + + // Calculate aspect ratio to determine preferred layout + float aspectRatio = (float)displayW / contentH; + + // Start with simple square-ish grid + int cols = (int)std::sqrt(tileCount * aspectRatio); + if(cols < 1) + cols = 1; + if(cols > tileCount) + cols = tileCount; + + int rows = (tileCount + cols - 1) / cols; + + // For wide displays (landscape), prefer more columns + if(aspectRatio > 1.5f && tileCount <= 6) { + cols = tileCount; + rows = 1; + } + // For tall displays (portrait), prefer more rows + else if(aspectRatio < 0.8f && tileCount <= 6) { + rows = tileCount; + cols = 1; + } + + *outCols = cols; + *outRows = rows; + } + + /// Find next available grid position + static void findNextPosition(const TileLayout* layouts, int count, int gridCols, int gridRows, + int* outCol, int* outRow) { + // Simple: find first empty cell + // Could be enhanced to pack tightly based on tile sizes + for(int r = 0; r < gridRows; r++) { + for(int c = 0; c < gridCols; c++) { + bool occupied = false; + for(int i = 0; i < count; i++) { + if(layouts[i].col <= c && c < layouts[i].col + layouts[i].cols + && layouts[i].row <= r && r < layouts[i].row + layouts[i].rows) { + occupied = true; + break; + } + } + if(!occupied) { + *outCol = c; + *outRow = r; + return; + } + } + } + // Fallback: just use count position + *outCol = count % gridCols; + *outRow = count / gridCols; + } +};