refactor(display): extract dashboard tile grid logic to DisplayManager
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "IDisplayDriver.h"
|
||||
#include "ScreenState.h"
|
||||
|
||||
class DisplayManager {
|
||||
public:
|
||||
@@ -24,8 +25,6 @@ public:
|
||||
|
||||
TouchEvent readTouch() { return _drv ? _drv->readTouch() : TouchEvent {}; }
|
||||
|
||||
int dashboardTouch(int x, int y) { return _drv ? _drv->dashboardTouch(x, y) : -1; }
|
||||
|
||||
HoldState updateHold(unsigned long ms) { return _drv ? _drv->updateHold(ms) : HoldState {}; }
|
||||
|
||||
void updateHint(int x, int y, bool active) {
|
||||
@@ -34,9 +33,114 @@ public:
|
||||
}
|
||||
|
||||
int width() { return _drv ? _drv->width() : 0; }
|
||||
|
||||
int height() { return _drv ? _drv->height() : 0; }
|
||||
|
||||
/// Auto-calculate grid dimensions for dashboard tiles
|
||||
/// Prefers landscape (more columns than rows) for wide displays
|
||||
void getTileGrid(int tileCount, int* outRows, int* outCols) const {
|
||||
if(tileCount <= 0) {
|
||||
*outRows = *outCols = 0;
|
||||
return;
|
||||
}
|
||||
// Calculate cols: try to make it wider than tall
|
||||
int cols = (int)sqrt(tileCount);
|
||||
if(cols * cols < tileCount) cols++;
|
||||
|
||||
// Make cols >= rows for landscape preference
|
||||
while(cols * ((tileCount + cols - 1) / cols) < tileCount) {
|
||||
cols++;
|
||||
}
|
||||
|
||||
int rows = (tileCount + cols - 1) / cols;
|
||||
|
||||
// If display is wider, prefer more columns
|
||||
if(_drv) {
|
||||
int w = _drv->width();
|
||||
int h = _drv->height();
|
||||
if(w > h) {
|
||||
// Landscape display - maximize columns
|
||||
cols = tileCount;
|
||||
rows = 1;
|
||||
}
|
||||
}
|
||||
|
||||
*outRows = rows;
|
||||
*outCols = cols;
|
||||
}
|
||||
|
||||
/// Render all dashboard tiles with auto-calculated grid
|
||||
void renderDashboard(const ScreenState& st) {
|
||||
if(!_drv) return;
|
||||
|
||||
int rows, cols;
|
||||
getTileGrid(DASHBOARD_TILE_COUNT, &rows, &cols);
|
||||
|
||||
int dispW = _drv->width();
|
||||
int dispH = _drv->height();
|
||||
|
||||
// Reserve space for header
|
||||
int headerH = 30;
|
||||
int contentH = dispH - headerH;
|
||||
|
||||
// Calculate tile sizes
|
||||
int tileW = dispW / cols;
|
||||
int tileH = contentH / rows;
|
||||
int margin = 8;
|
||||
|
||||
for(int i = 0; i < DASHBOARD_TILE_COUNT; i++) {
|
||||
int row = i / cols;
|
||||
int col = i % cols;
|
||||
|
||||
int x = col * tileW + margin;
|
||||
int y = headerH + row * tileH + margin;
|
||||
int w = tileW - 2 * margin;
|
||||
int h = tileH - 2 * margin;
|
||||
|
||||
_drv->drawTileAt(x, y, w, h, DASHBOARD_TILES[i].label, DASHBOARD_TILES[i].bgColor);
|
||||
}
|
||||
|
||||
// Store grid for touch calculations
|
||||
_gridRows = rows;
|
||||
_gridCols = cols;
|
||||
}
|
||||
|
||||
/// Handle dashboard touch - returns action for tapped tile, or NONE
|
||||
TileAction handleDashboardTouch(int x, int y) const {
|
||||
if(!_drv || _gridCols <= 0) return TileAction::NONE;
|
||||
|
||||
// Transform touch coordinates (handles rotated touch panels)
|
||||
_drv->transformTouch(&x, &y);
|
||||
|
||||
int dispW = _drv->width();
|
||||
int dispH = _drv->height();
|
||||
int headerH = 30;
|
||||
|
||||
// Check if in header area
|
||||
if(y < headerH) return TileAction::NONE;
|
||||
|
||||
// Calculate which tile was touched
|
||||
int tileW = dispW / _gridCols;
|
||||
int contentH = dispH - headerH;
|
||||
int tileH = contentH / _gridRows;
|
||||
|
||||
int col = x / tileW;
|
||||
int row = (y - headerH) / tileH;
|
||||
|
||||
// Bounds check
|
||||
if(col < 0 || col >= _gridCols || row < 0 || row >= _gridRows) {
|
||||
return TileAction::NONE;
|
||||
}
|
||||
|
||||
int index = row * _gridCols + col;
|
||||
if(index < 0 || index >= DASHBOARD_TILE_COUNT) {
|
||||
return TileAction::NONE;
|
||||
}
|
||||
|
||||
return DASHBOARD_TILES[index].action;
|
||||
}
|
||||
|
||||
private:
|
||||
IDisplayDriver* _drv;
|
||||
mutable int _gridRows = 0;
|
||||
mutable int _gridCols = 0;
|
||||
};
|
||||
|
||||
@@ -323,11 +323,30 @@ int DoorbellLogic::handleTouch(const TouchEvent& evt) {
|
||||
}
|
||||
|
||||
if(_state.screen == ScreenID::DASHBOARD) {
|
||||
int tile = _display->dashboardTouch(evt.x, evt.y);
|
||||
if(tile >= 0) {
|
||||
Serial.printf("[DASH] Tile %d tapped\n", tile);
|
||||
TileAction action = _display->handleDashboardTouch(evt.x, evt.y);
|
||||
if(action != TileAction::NONE) {
|
||||
Serial.printf("[DASH] Action: %d\n", (int)action);
|
||||
switch(action) {
|
||||
case TileAction::ALERT:
|
||||
onAlert("Manual Alert", "Tile tap");
|
||||
break;
|
||||
case TileAction::SILENCE:
|
||||
if(_state.deviceState == DeviceState::ALERTING)
|
||||
silenceAlert();
|
||||
break;
|
||||
case TileAction::STATUS:
|
||||
heartbeat();
|
||||
break;
|
||||
case TileAction::REBOOT:
|
||||
flushStatus("REBOOT (tile)");
|
||||
delay(500);
|
||||
ESP.restart();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return tile;
|
||||
return (int)action;
|
||||
}
|
||||
|
||||
if(_state.screen == ScreenID::ALERT) {
|
||||
|
||||
@@ -27,8 +27,6 @@ public:
|
||||
|
||||
// ── Touch ──
|
||||
virtual TouchEvent readTouch() = 0;
|
||||
/// Returns tile index at (x,y), or -1 if none.
|
||||
virtual int dashboardTouch(int x, int y) = 0;
|
||||
/// Track a long-press gesture; returns progress/completion.
|
||||
virtual HoldState updateHold(unsigned long holdMs) = 0;
|
||||
/// Idle hint animation (e.g. pulsing ring) while alert is showing.
|
||||
@@ -36,4 +34,10 @@ public:
|
||||
virtual void updateHint(int x, int y, bool active) = 0;
|
||||
virtual int width() = 0;
|
||||
virtual int height() = 0;
|
||||
|
||||
// ── Dashboard tiles ──
|
||||
/// Transform raw touch coordinates (for rotated touch panels)
|
||||
virtual void transformTouch(int* x, int* y) { /* default: no transform */ }
|
||||
/// Draw a tile at specified position (library handles grid math)
|
||||
virtual void drawTileAt(int x, int y, int w, int h, const char* label, uint16_t bgColor) = 0;
|
||||
};
|
||||
|
||||
@@ -7,6 +7,31 @@ enum class ScreenID { BOOT, OFF, ALERT, DASHBOARD };
|
||||
|
||||
enum class BootStage { SPLASH, INIT_DISPLAY, INIT_NETWORK, CONNECTING_WIFI, READY, DONE };
|
||||
|
||||
/// Dashboard tile action handlers
|
||||
enum class TileAction {
|
||||
NONE,
|
||||
ALERT, // Trigger alert
|
||||
SILENCE, // Silence alert
|
||||
STATUS, // Send heartbeat/status
|
||||
REBOOT, // Reboot device
|
||||
};
|
||||
|
||||
/// Dashboard tile definitions — shared across all boards
|
||||
struct DashboardTile {
|
||||
const char* label;
|
||||
uint16_t bgColor; // RGB565
|
||||
TileAction action;
|
||||
};
|
||||
|
||||
/// Standard dashboard tiles (auto-gridded based on count)
|
||||
static constexpr DashboardTile DASHBOARD_TILES[] = {
|
||||
{ "Alert", 0x0280, TileAction::ALERT },
|
||||
{ "Silent", 0x0400, TileAction::SILENCE },
|
||||
{ "Status", 0x0440, TileAction::STATUS },
|
||||
{ "Reboot", 0x0100, TileAction::REBOOT },
|
||||
};
|
||||
static constexpr int DASHBOARD_TILE_COUNT = sizeof(DASHBOARD_TILES) / sizeof(DASHBOARD_TILES[0]);
|
||||
|
||||
struct ScreenState {
|
||||
DeviceState deviceState = DeviceState::BOOTED;
|
||||
ScreenID screen = ScreenID::BOOT;
|
||||
|
||||
Reference in New Issue
Block a user