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
This commit is contained in:
2026-05-29 20:17:15 -07:00
parent 3d17e7bab9
commit 14781b8c07
6 changed files with 134 additions and 131 deletions
-119
View File
@@ -4,125 +4,6 @@
#include "ScreenState.h"
#include "Style.h"
#include <cmath>
/// 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)
+118
View File
@@ -2,6 +2,7 @@
#include <Arduino.h>
#include <cstdint>
#include <cmath>
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;
}
};