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:
@@ -3,8 +3,7 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <KlubhausCore.h>
|
#include <KlubhausCore.h>
|
||||||
#include <TFT_eSPI.h>
|
#include <TFT_eSPI.h>
|
||||||
|
#include <Style.h>
|
||||||
extern DisplayManager display;
|
|
||||||
|
|
||||||
// ── Fonts ───────────────────────────────────────────────────
|
// ── Fonts ───────────────────────────────────────────────────
|
||||||
// TFT_eSPI built-in fonts for 320x480 display (scaled from 800x480)
|
// 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.setCursor(dispW - wifiW - 10, 20);
|
||||||
_tft.print(wifiText);
|
_tft.print(wifiText);
|
||||||
|
|
||||||
// Get tile layouts from library helper
|
// Calculate tile layouts using TileLayoutHelper
|
||||||
int tileCount = display.calculateDashboardLayouts(STYLE_HEADER_HEIGHT, STYLE_TILE_GAP);
|
TileLayout layouts[DASHBOARD_TILE_COUNT];
|
||||||
display.setHeaderHeight(STYLE_HEADER_HEIGHT);
|
int gridCols, gridRows;
|
||||||
const TileLayout* layouts = display.getTileLayouts();
|
int tileCount = TileLayoutHelper::calculateLayouts(
|
||||||
|
dispW, dispH, STYLE_HEADER_HEIGHT, STYLE_TILE_GAP, layouts, &gridCols, &gridRows);
|
||||||
|
|
||||||
const char* tileLabels[] = { "Alert", "Silent", "Status", "Reboot" };
|
const char* tileLabels[] = { "Alert", "Silent", "Status", "Reboot" };
|
||||||
const uint16_t tileColors[] = { 0x0280, 0x0400, 0x0440, 0x0100 };
|
const uint16_t tileColors[] = { 0x0280, 0x0400, 0x0440, 0x0100 };
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <KlubhausCore.h>
|
#include <KlubhausCore.h>
|
||||||
|
#include <Style.h>
|
||||||
|
|
||||||
// ── Globals ──
|
// ── Globals ──
|
||||||
static LGFX* _gfx = nullptr;
|
static LGFX* _gfx = nullptr;
|
||||||
extern DisplayManager display;
|
|
||||||
|
|
||||||
// ── Forward declarations ──
|
// ── Forward declarations ──
|
||||||
static void initDisplay();
|
static void initDisplay();
|
||||||
@@ -443,10 +443,11 @@ void DisplayDriverGFX::drawDashboard(const ScreenState& state) {
|
|||||||
},
|
},
|
||||||
safeWifi.w);
|
safeWifi.w);
|
||||||
|
|
||||||
// Get tile layouts from library helper
|
// Calculate tile layouts using TileLayoutHelper
|
||||||
int tileCount = display.calculateDashboardLayouts(STYLE_HEADER_HEIGHT, STYLE_TILE_GAP);
|
TileLayout layouts[DASHBOARD_TILE_COUNT];
|
||||||
display.setHeaderHeight(STYLE_HEADER_HEIGHT);
|
int gridCols, gridRows;
|
||||||
const TileLayout* layouts = display.getTileLayouts();
|
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 char* tileLabels[] = { "Alert", "Silent", "Status", "Reboot" };
|
||||||
const uint16_t tileColors[] = { 0x0280, 0x0400, 0x0440, 0x0100 };
|
const uint16_t tileColors[] = { 0x0280, 0x0400, 0x0440, 0x0100 };
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FQBN="esp32:esp32:waveshare_esp32_s3_touch_lcd_43:PSRAM=enabled,FlashSize=16M,USBMode=hwcdc,PartitionScheme=app3M_fat9M_16MB"
|
FQBN="esp32:esp32:waveshare_esp32_s3_touch_lcd_43:PSRAM=enabled,FlashSize=16M,USBMode=hwcdc,PartitionScheme=app3M_fat9M_16MB"
|
||||||
PORT="/dev/ttyUSB0"
|
PORT="/dev/ttyUSB0"
|
||||||
LIBS="--library ./vendor/esp32-s3-lcd-43/LovyanGFX"
|
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"
|
||||||
|
|||||||
@@ -38,4 +38,7 @@ void loop() {
|
|||||||
|
|
||||||
// Serial console
|
// Serial console
|
||||||
logic.processSerial();
|
logic.processSerial();
|
||||||
|
|
||||||
|
// Yield to WiFi/BT stack
|
||||||
|
delay(LOOP_YIELD_MS);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,125 +4,6 @@
|
|||||||
#include "ScreenState.h"
|
#include "ScreenState.h"
|
||||||
#include "Style.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 {
|
class DisplayManager {
|
||||||
public:
|
public:
|
||||||
DisplayManager(IDisplayDriver* drv)
|
DisplayManager(IDisplayDriver* drv)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
struct Layout {
|
struct Layout {
|
||||||
uint16_t x, y, w, h;
|
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); }
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user