You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
243 lines
8.0 KiB
243 lines
8.0 KiB
/********************
|
|
* screen_types.cpp *
|
|
********************/
|
|
|
|
/****************************************************************************
|
|
* Written By Marcio Teixeira 2018 - Aleph Objects, Inc. *
|
|
* *
|
|
* This program is free software: you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation, either version 3 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* To view a copy of the GNU General Public License, go to the following *
|
|
* location: <https://www.gnu.org/licenses/>. *
|
|
****************************************************************************/
|
|
|
|
#pragma once
|
|
|
|
typedef enum {
|
|
BACKGROUND = 1,
|
|
FOREGROUND = 2,
|
|
BOTH = 3
|
|
} draw_mode_t;
|
|
|
|
/********************** VIRTUAL DISPATCH DATA TYPE ******************************/
|
|
|
|
// True virtual classes are extremely expensive on the Arduino
|
|
// as the compiler stores the virtual function tables in RAM.
|
|
// We invent a data type called ScreenRef that gives us
|
|
// polymorphism by mapping an ID to virtual methods on various
|
|
// classes. This works by keeping a table in PROGMEM of pointers
|
|
// to static methods.
|
|
|
|
#define DECL_SCREEN(className) { \
|
|
className::onStartup, \
|
|
className::onEntry, \
|
|
className::onExit, \
|
|
className::onIdle, \
|
|
className::onRefresh, \
|
|
className::onRedraw, \
|
|
className::onTouchStart, \
|
|
className::onTouchHeld, \
|
|
className::onTouchEnd \
|
|
}
|
|
|
|
#define GET_METHOD(type, method) reinterpret_cast<method##_func_t*>(pgm_read_ptr_far(&functionTable[type].method##_ptr))
|
|
#define SCREEN_TABLE PROGMEM const ScreenRef::table_t ScreenRef::functionTable[] =
|
|
#define SCREEN_TABLE_POST size_t ScreenRef::tableSize() { \
|
|
constexpr size_t siz = sizeof(functionTable)/sizeof(functionTable[0]); \
|
|
static_assert(siz > 0, "The screen table is empty!"); \
|
|
return siz; \
|
|
}
|
|
|
|
class ScreenRef {
|
|
protected:
|
|
typedef void onStartup_func_t();
|
|
typedef void onEntry_func_t();
|
|
typedef void onExit_func_t();
|
|
typedef void onIdle_func_t();
|
|
typedef void onRefresh_func_t();
|
|
typedef void onRedraw_func_t(draw_mode_t);
|
|
typedef bool onTouchStart_func_t(uint8_t);
|
|
typedef bool onTouchHeld_func_t(uint8_t);
|
|
typedef bool onTouchEnd_func_t(uint8_t);
|
|
|
|
private:
|
|
typedef struct {
|
|
onStartup_func_t *onStartup_ptr;
|
|
onEntry_func_t *onEntry_ptr;
|
|
onExit_func_t *onExit_ptr;
|
|
onIdle_func_t *onIdle_ptr;
|
|
onRefresh_func_t *onRefresh_ptr;
|
|
onRedraw_func_t *onRedraw_ptr;
|
|
onTouchStart_func_t *onTouchStart_ptr;
|
|
onTouchHeld_func_t *onTouchHeld_ptr;
|
|
onTouchEnd_func_t *onTouchEnd_ptr;
|
|
} table_t;
|
|
|
|
uint8_t type = 0;
|
|
static PROGMEM const table_t functionTable[];
|
|
|
|
public:
|
|
static size_t tableSize();
|
|
|
|
uint8_t getType() {return type;}
|
|
void setType(uint8_t t) {type = t;}
|
|
|
|
uint8_t lookupScreen(onRedraw_func_t onRedraw_ptr);
|
|
|
|
void setScreen(onRedraw_func_t onRedraw_ptr);
|
|
|
|
void onStartup() {GET_METHOD(type, onStartup)();}
|
|
void onEntry() {GET_METHOD(type, onEntry)();}
|
|
void onExit() {GET_METHOD(type, onExit)();}
|
|
void onIdle() {GET_METHOD(type, onIdle)();}
|
|
void onRefresh() {GET_METHOD(type, onRefresh)();}
|
|
void onRedraw(draw_mode_t dm) {GET_METHOD(type, onRedraw)(dm);}
|
|
bool onTouchStart(uint8_t tag) {return GET_METHOD(type, onTouchStart)(tag);}
|
|
bool onTouchHeld(uint8_t tag) {return GET_METHOD(type, onTouchHeld)(tag);}
|
|
bool onTouchEnd(uint8_t tag) {return GET_METHOD(type, onTouchEnd)(tag);}
|
|
|
|
void initializeAll();
|
|
};
|
|
|
|
/********************** SCREEN STACK ******************************/
|
|
|
|
// To conserve dynamic memory, the screen stack is hard-coded to
|
|
// have four values, allowing a menu of up to four levels.
|
|
|
|
class ScreenStack : public ScreenRef {
|
|
private:
|
|
uint8_t stack[4];
|
|
|
|
public:
|
|
void start();
|
|
void push(onRedraw_func_t);
|
|
void push();
|
|
void pop();
|
|
void forget();
|
|
void goTo(onRedraw_func_t);
|
|
void goBack();
|
|
|
|
uint8_t peek() {return stack[0];}
|
|
uint8_t getScreen() {return getType();}
|
|
};
|
|
|
|
extern ScreenStack current_screen;
|
|
|
|
/********************** BASE SCREEN CLASS ******************************/
|
|
|
|
/* UIScreen is the base class for all user interface screens.
|
|
*/
|
|
class UIScreen {
|
|
public:
|
|
static void onStartup() {}
|
|
static void onEntry() {current_screen.onRefresh();}
|
|
static void onExit() {}
|
|
static void onIdle() {}
|
|
static bool onTouchStart(uint8_t) {return true;}
|
|
static bool onTouchHeld(uint8_t) {return false;}
|
|
static bool onTouchEnd(uint8_t) {return true;}
|
|
};
|
|
|
|
#define PUSH_SCREEN(screen) current_screen.push(screen::onRedraw)
|
|
#define GOTO_SCREEN(screen) current_screen.goTo(screen::onRedraw)
|
|
#define GOTO_PREVIOUS() current_screen.goBack();
|
|
#define AT_SCREEN(screen) (current_screen.getType() == current_screen.lookupScreen(screen::onRedraw))
|
|
#define IS_PARENT_SCREEN(screen) (current_screen.peek() == current_screen.lookupScreen(screen::onRedraw))
|
|
|
|
/************************** CACHED VS UNCACHED SCREENS ***************************/
|
|
|
|
class UncachedScreen {
|
|
public:
|
|
static void onRefresh() {
|
|
using namespace FTDI;
|
|
CommandProcessor cmd;
|
|
cmd.cmd(CMD_DLSTART);
|
|
#if ENABLED(TOUCH_UI_USE_UTF8)
|
|
load_utf8_bitmaps(cmd);
|
|
#endif
|
|
|
|
current_screen.onRedraw(BOTH);
|
|
|
|
cmd.cmd(DL::DL_DISPLAY);
|
|
cmd.cmd(CMD_SWAP);
|
|
cmd.execute();
|
|
}
|
|
};
|
|
|
|
template<uint8_t DL_SLOT,uint32_t DL_SIZE = 0>
|
|
class CachedScreen {
|
|
protected:
|
|
static void gfxError() {
|
|
using namespace FTDI;
|
|
CommandProcessor cmd;
|
|
cmd.cmd(CMD_DLSTART)
|
|
.cmd(CLEAR(true,true,true))
|
|
.font(30)
|
|
.text(0, 0, display_width, display_height, F("GFX MEM FULL"));
|
|
}
|
|
|
|
static bool storeBackground() {
|
|
DLCache dlcache(DL_SLOT);
|
|
if (!dlcache.store(DL_SIZE)) {
|
|
SERIAL_ECHO_MSG("CachedScreen::storeBackground() failed: not enough DL cache space");
|
|
gfxError(); // Try to cache a shorter error message instead.
|
|
dlcache.store(DL_SIZE);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void repaintBackground() {
|
|
using namespace FTDI;
|
|
DLCache dlcache(DL_SLOT);
|
|
CommandProcessor cmd;
|
|
|
|
cmd.cmd(CMD_DLSTART);
|
|
#if ENABLED(TOUCH_UI_USE_UTF8)
|
|
load_utf8_bitmaps(cmd);
|
|
#endif
|
|
current_screen.onRedraw(BACKGROUND);
|
|
|
|
dlcache.store(DL_SIZE);
|
|
}
|
|
|
|
public:
|
|
static void onRefresh() {
|
|
#if ENABLED(TOUCH_UI_DEBUG)
|
|
const uint32_t start_time = millis();
|
|
#endif
|
|
using namespace FTDI;
|
|
DLCache dlcache(DL_SLOT);
|
|
CommandProcessor cmd;
|
|
|
|
cmd.cmd(CMD_DLSTART);
|
|
|
|
if (dlcache.has_data()) {
|
|
dlcache.append();
|
|
}
|
|
else {
|
|
#if ENABLED(TOUCH_UI_USE_UTF8)
|
|
load_utf8_bitmaps(cmd);
|
|
#endif
|
|
current_screen.onRedraw(BACKGROUND);
|
|
dlcache.store(DL_SIZE);
|
|
}
|
|
|
|
current_screen.onRedraw(FOREGROUND);
|
|
|
|
cmd.cmd(DL::DL_DISPLAY);
|
|
cmd.cmd(CMD_SWAP);
|
|
cmd.execute();
|
|
#if ENABLED(TOUCH_UI_DEBUG)
|
|
SERIAL_ECHOLNPAIR("Time to draw screen (ms): ", millis() - start_time);
|
|
#endif
|
|
}
|
|
};
|
|
|