/**************************************************************************** * 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: . * ****************************************************************************/ /********************** 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::onTouchStart, \ className::onTouchHeld, \ className::onTouchEnd \ } #define GET_METHOD(type, method) reinterpret_cast(pgm_read_word_near(&functionTable[type].method##_ptr)) #define SCREEN_TABLE PROGMEM const ScreenRef::table_t ScreenRef::functionTable[] = #define SCREEN_TABLE_POST const uint8_t ScreenRef::functionTableSize = sizeof(ScreenRef::functionTable)/sizeof(ScreenRef::functionTable[0]); class ScreenRef { protected: typedef void onStartup_func_t(void); typedef void onEntry_func_t(void); typedef void onExit_func_t(void); typedef void onIdle_func_t(void); typedef void onRefresh_func_t(void); 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; 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[]; static const uint8_t functionTableSize; public: uint8_t getType() {return type;} void setType(uint8_t t) { type = t; } uint8_t lookupScreen(onEntry_func_t onRefresh_ptr) { for(uint8_t type = 0; type < functionTableSize; type++) { if(GET_METHOD(type, onRefresh) == onRefresh_ptr) { return type; } } #if defined(UI_FRAMEWORK_DEBUG) #if defined(SERIAL_PROTOCOLLNPAIR) SERIAL_PROTOCOLLNPAIR("Screen not found: ", (uint16_t) onRefresh_ptr); #else Serial.print("Screen not found: "); Serial.println((uint16_t) onRefresh_ptr, HEX); #endif #endif return 0xFF; } void setScreen(onEntry_func_t onRefresh_ptr) { uint8_t type = lookupScreen(onRefresh_ptr); if(type != 0xFF) { setType(type); #if defined(UI_FRAMEWORK_DEBUG) #if defined(SERIAL_PROTOCOLLNPAIR) SERIAL_PROTOCOLLNPAIR("New screen: ",type); #else Serial.print("New screen: "); Serial.println(type); #endif #endif return; } #if defined(UI_FRAMEWORK_DEBUG) #if defined(SERIAL_PROTOCOLLNPAIR) SERIAL_PROTOCOLLNPAIR("Screen not found: ", (uint16_t) onRefresh_ptr); #else Serial.print("Screen not found: "); Serial.println((uint16_t) onRefresh_ptr, HEX); #endif #endif } 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)();} 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() { for(uint8_t type = 0; type < functionTableSize; type++) { GET_METHOD(type, onStartup)(); } } }; /********************** 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() { initializeAll(); onEntry(); } void push() { stack[3] = stack[2]; stack[2] = stack[1]; stack[1] = stack[0]; stack[0] = getType(); } void pop() { setType(stack[0]); forget(); } void forget() { stack[0] = stack[1]; stack[1] = stack[2]; stack[2] = stack[3]; stack[3] = 0; } void goTo(onEntry_func_t s) { push(); onExit(); setScreen(s); onEntry(); } void goBack() { pop(); onEntry(); } uint8_t getScreen() { return getType(); } } current_screen; /********************** BASE SCREEN CLASSS ******************************/ /* 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 false;} static bool onTouchHeld(uint8_t) {return false;} static bool onTouchEnd(uint8_t) {return false;} }; #define GOTO_SCREEN(screen) current_screen.goTo(screen::onRefresh); #define GOTO_PREVIOUS() current_screen.goBack();