reorganized deletion and creation of tabs

This commit is contained in:
KlausMu 2024-04-21 17:53:28 +02:00 committed by Klaus Musch
parent 84e8895102
commit 5a420c381a
17 changed files with 469 additions and 342 deletions

View file

@ -4,8 +4,9 @@
Preferences preferences;
std::string currentScene;
std::string currentGUIname;
std::string activeScene;
std::string activeGUIname;
int activeGUIlist;
void init_preferences_HAL(void) {
// Restore settings from internal flash memory
@ -17,10 +18,11 @@ void init_preferences_HAL(void) {
// from tft.h
set_backlightBrightness_HAL(preferences.getUChar("blBrightness"));
// from here
currentScene = std::string(preferences.getString("currentScene").c_str());
currentGUIname = std::string(preferences.getString("currentGUIname").c_str());
activeScene = std::string(preferences.getString("currentScene").c_str());
activeGUIname = std::string(preferences.getString("currentGUIname").c_str());
activeGUIlist =(preferences.getInt("currentGUIlist"));
// Serial.printf("Preferences restored: brightness %d, GUI %s, scene %s\r\n", get_backlightBrightness_HAL(), get_currentGUIname().c_str(), get_currentScene().c_str());
// Serial.printf("Preferences restored: brightness %d, GUI %s, scene %s\r\n", get_backlightBrightness_HAL(), get_activeGUIname().c_str(), get_activeScene().c_str());
} else {
// Serial.printf("No preferences to restore\r\n");
}
@ -35,23 +37,30 @@ void save_preferences_HAL(void) {
preferences.putUInt("slpTimeout", get_sleepTimeout_HAL());
preferences.putUChar("blBrightness", get_backlightBrightness_HAL());
// from here
preferences.putString("currentScene", currentScene.c_str());
preferences.putString("currentGUIname", currentGUIname.c_str());
preferences.putString("currentScene", activeScene.c_str());
preferences.putString("currentGUIname", activeGUIname.c_str());
preferences.putInt("currentGUIlist", activeGUIlist);
if (!preferences.getBool("alreadySetUp")) {
preferences.putBool("alreadySetUp", true);
}
preferences.end();
}
std::string get_currentScene_HAL() {
return currentScene;
std::string get_activeScene_HAL() {
return activeScene;
}
void set_currentScene_HAL(std::string aCurrentScene) {
currentScene = aCurrentScene;
void set_activeScene_HAL(std::string anActiveScene) {
activeScene = anActiveScene;
}
std::string get_currentGUIname_HAL(){
return currentGUIname;
std::string get_activeGUIname_HAL(){
return activeGUIname;
}
void set_currentGUIname_HAL(std::string aCurrentGUIname) {
currentGUIname = aCurrentGUIname;
void set_activeGUIname_HAL(std::string anActiveGUIname) {
activeGUIname = anActiveGUIname;
}
int get_activeGUIlist_HAL() {
return activeGUIlist;
}
void set_activeGUIlist_HAL(int anActiveGUIlist) {
activeGUIlist = anActiveGUIlist;
}

View file

@ -5,7 +5,9 @@
void init_preferences_HAL(void);
void save_preferences_HAL(void);
std::string get_currentScene_HAL();
void set_currentScene_HAL(std::string aCurrentScene);
std::string get_currentGUIname_HAL();
void set_currentGUIname_HAL(std::string aCurrentGUIname);
std::string get_activeScene_HAL();
void set_activeScene_HAL(std::string anActiveScene);
std::string get_activeGUIname_HAL();
void set_activeGUIname_HAL(std::string anActiveGUIname);
int get_activeGUIlist_HAL();
void set_activeGUIlist_HAL(int anActiveGUIlist);

View file

@ -1,31 +1,50 @@
#include <string>
std::string currentScene;
std::string currentGUIname;
enum GUIlists {
// MAIN_GUI_LIST: we are in the main_gui_list (with the scene selector as first gui), either if a scene is active or not
// SCENE_GUI_LIST: a scene is active and we are not in the main_gui_list. In that case, we try to use the scene specific gui list, if the scene defined one.
MAIN_GUI_LIST,
SCENE_GUI_LIST
};
/*
Possible example:
main_gui_list : "Scene selection", "Smart Home", "Settings", "IR Receiver"};
"Off" :
"TV" : "Numpad"
"Fire TV" : "Numpad", "Settings"
"Chromecast" :
"Apple TV" : "Apple TV", "Settings", "IR Receiver"
*/
std::string activeScene;
std::string activeGUIname;
int activeGUIlist;
void init_preferences_HAL(void) {
// set some values for tests
activeScene = ""; // "Off", "TV", "Fire TV", "Chromecast", "Apple TV";
activeGUIname = ""; // "Scene selection", "Smart Home", "Settings", "IR Receiver" // "Numpad", "Apple TV"
activeGUIlist = MAIN_GUI_LIST; // // MAIN_GUI_LIST, SCENE_GUI_LIST;
}
void save_preferences_HAL(void) {
}
std::string get_currentScene_HAL() {
// if (currentScene == "") {
// // set here something if you need it for a test at startup
// return "Apple TV";
// } else
{return currentScene;}
std::string get_activeScene_HAL() {
return activeScene;
}
void set_currentScene_HAL(std::string aCurrentScene) {
currentScene = aCurrentScene;
void set_activeScene_HAL(std::string anActiveScene) {
activeScene = anActiveScene;
}
std::string get_currentGUIname_HAL(){
// if (currentGUIname == "") {
// // set here something if you need it for a test at startup
// return "IR Receiver"; // "Numpad"; // "Apple TV";
// } else
{return currentGUIname;}
std::string get_activeGUIname_HAL(){
return activeGUIname;
}
void set_currentGUIname_HAL(std::string aCurrentGUIname) {
currentGUIname = aCurrentGUIname;
void set_activeGUIname_HAL(std::string anActiveGUIname) {
activeGUIname = anActiveGUIname;
}
int get_activeGUIlist_HAL() {
return activeGUIlist;
}
void set_activeGUIlist_HAL(int anActiveGUIlist) {
activeGUIlist = anActiveGUIlist;
}

View file

@ -5,7 +5,9 @@
void init_preferences_HAL(void);
void save_preferences_HAL(void);
std::string get_currentScene_HAL();
void set_currentScene_HAL(std::string aCurrentScene);
std::string get_currentGUIname_HAL();
void set_currentGUIname_HAL(std::string aCurrentGUIname);
std::string get_activeScene_HAL();
void set_activeScene_HAL(std::string anActiveScene);
std::string get_activeGUIname_HAL();
void set_activeGUIname_HAL(std::string anActiveGUIname);
int get_activeGUIlist_HAL();
void set_activeGUIlist_HAL(int anActiveGUIlist);

View file

@ -13,8 +13,6 @@ lv_obj_t* BluetoothLabel = NULL;
lv_obj_t* BattPercentageLabel = NULL;
lv_obj_t* BattIconLabel = NULL;
lv_obj_t* SceneLabel = NULL;
uint32_t currentTabID = -1; // id of the current tab
uint32_t oldTabID = -1;
lv_obj_t* tabview = NULL;
// page indicator
@ -28,8 +26,8 @@ lv_style_t panel_style;
lv_style_t style_red_border;
#endif
void guis_doTabCreationAtStartup();
void guis_doAfterSliding(int oldTabID, int newTabID, bool newGuiList);
void guis_doTabCreationOnStartup();
void guis_doTabCreationAfterSliding(int newTabID);
// Helper Functions -----------------------------------------------------------------------------------------------------------------------
@ -87,28 +85,28 @@ void tabview_content_is_scrolling_event_cb(lv_event_t* e){
// -----------------------
static bool waitBeforeActionAfterSlidingAnimationEnded = false;
static unsigned long waitBeforeActionAfterSlidingAnimationEnded_timerStart;
static int newTabID_forLateTabCreation;
// This is the callback when the animation of the tab sliding ended
static void tabview_animation_ready_cb(lv_anim_t* a) {
// Unfortunately, the animation has not completely ended here. We cannot do the recreation of the tabs here.
// We have to wait some more milliseconds or at least one cycle of lv_timer_handler();
// calling lv_timer_handler(); here does not help
// lv_timer_handler();
// guis_doAfterSliding(oldTabID, currentTabID, false);
// guis_doTabCreationAfterSliding(newTabID_forLateTabCreation);
waitBeforeActionAfterSlidingAnimationEnded = true;
waitBeforeActionAfterSlidingAnimationEnded_timerStart = millis();
}
// Update currentTabID when a new tab is selected
// Update gui when a new tab is selected
// this is a callback if the tabview is changed (LV_EVENT_VALUE_CHANGED)
// Sent when a new tab is selected by sliding or clicking the tab button. lv_tabview_get_tab_act(tabview) returns the zero based index of the current tab.
void tabview_tab_changed_event_cb(lv_event_t* e) {
if (lv_event_get_code(e) == LV_EVENT_VALUE_CHANGED) {
oldTabID = currentTabID;
currentTabID = lv_tabview_get_tab_act(lv_event_get_target(e));
int newTabID = lv_tabview_get_tab_active((lv_obj_t*)lv_event_get_target(e));
// Wait until the animation ended, then call "guis_doAfterSliding(oldTabID, currentTabID, false);"
// Wait until the animation ended, then call "guis_doTabCreationAfterSliding(newTabID);"
// https://forum.lvgl.io/t/delete-a-tab-after-the-tabview-scroll-animation-is-complete/3155/4
lv_obj_t* myTabview = lv_event_get_target(e);
lv_obj_t* tabContainer = lv_tabview_get_content(myTabview);
@ -116,11 +114,12 @@ void tabview_tab_changed_event_cb(lv_event_t* e) {
if(anim) {
// Swipe is not yet complete. User released the touch screen, an animation will bring it to the end.
// That's the normal (and only?) case for the tft touchscreen
newTabID_forLateTabCreation = newTabID;
lv_anim_set_ready_cb(anim, tabview_animation_ready_cb);
} else {
// Swipe is complete, no additional animation is needed. Most likely only possible in simulator
Serial.println("Change of tab detected, without animation at the end. Will directly do my job after sliding.");
guis_doAfterSliding(oldTabID, currentTabID, false);
guis_doTabCreationAfterSliding(newTabID);
}
}
}
@ -154,8 +153,8 @@ void init_gui(void) {
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_black(), LV_PART_MAIN);
// set default height and position of main widgets
setMainWidgetsHeightAndPosition();
// At startup, set current GUI according to get_currentGUIname(), and create the content of that tab (and the previous and the next) for the first time
guis_doTabCreationAtStartup();
// On startup, set current GUIname and GUIlist according to last state before going to sleep
guis_doTabCreationOnStartup();
// memoryUsage bar
init_gui_memoryUsage_bar();
// status bar
@ -288,12 +287,12 @@ void gui_loop(void) {
waitBeforeActionAfterSlidingAnimationEnded = false;
} else if (waitOneLoop) {
waitOneLoop = false;
guis_doAfterSliding(oldTabID, currentTabID, false);
guis_doTabCreationAfterSliding(newTabID_forLateTabCreation);
};
// // as alternative, we could wait some milliseconds. But one cycle of gui_loop() works well.
// if (waitBeforeActionAfterSlidingAnimationEnded) {
// if (millis() - waitBeforeActionAfterSlidingAnimationEnded_timerStart >= 5) {
// guis_doAfterSliding(oldTabID, currentTabID, false);
// guis_doTabCreationAfterSliding(newTabID_forLateTabCreation);
// waitBeforeActionAfterSlidingAnimationEnded = false;
// }
// }
@ -301,19 +300,25 @@ void gui_loop(void) {
lv_timer_handler();
}
void guis_doTabCreationAtStartup() {
gui_memoryOptimizer_prepare_startup();
guis_doAfterSliding(-1, -1, false);
}
void guis_doAfterSliding(int oldTabID, int newTabID, bool newGuiList) {
// With parameter newGuiList it is signaled that we are changing from a scene specific list to the main list or vice versa
// In that case, we have to do special treatment because we are not simply sliding to the left or to the right, but we start newly with a different gui list.
gui_memoryOptimizer_doAfterSliding_deletionAndCreation(&tabview, oldTabID, newTabID, newGuiList, &panel, &img1, &img2);
// ------------------------------------------------------------------------------------------------------------
// There are several reasons why the tabs could get recreated. All are going through these functions in "guiBase.cpp", which are calling functions in "guiMemoryOptimizer.cpp"
// 1. tab creation on startup (called by init_gui())
void guis_doTabCreationOnStartup() {
Serial.printf("Startup: try to resume at scene \"%s\" with GUI \"%s\"\r\n", gui_memoryOptimizer_getActiveSceneName().c_str(), gui_memoryOptimizer_getActiveGUIname().c_str());
gui_memoryOptimizer_onStartup(&tabview, &panel, &img1, &img2);
doLogMemoryUsage();
}
// 2. tab creation after sliding (called by tabview_tab_changed_event_cb())
void guis_doTabCreationAfterSliding(int newTabID) {
gui_memoryOptimizer_afterSliding(&tabview, &panel, &img1, &img2, newTabID);
doLogMemoryUsage();
}
// 3. after gui list has changed (called by handleScene()), when switching between main_gui_list and scene specific list
void guis_doTabCreationAfterGUIlistChanged(GUIlists newGUIlist) {
gui_memoryOptimizer_afterGUIlistChanged(&tabview, &panel, &img1, &img2, newGUIlist);
doLogMemoryUsage();
}
// ------------------------------------------------------------------------------------------------------------
void setActiveTab(uint32_t index, lv_anim_enable_t anim_en, bool send_tab_changed_event) {
// unsigned long startTime = millis();

View file

@ -1,6 +1,7 @@
#pragma once
#include <lvgl.h>
#include "applicationInternal/gui/guiMemoryOptimizer.h"
// used by memoryUsage.cpp
extern lv_obj_t* MemoryUsageLabel;
@ -15,7 +16,6 @@ extern lv_style_t panel_style;
extern int tabviewTop;
extern int tabviewHeight;
extern int panelHeight;
extern uint32_t currentTabID;
// used by almost all gui_*.cpp
extern lv_color_t color_primary;
@ -32,10 +32,9 @@ void tabview_content_is_scrolling_event_cb(lv_event_t* e);
void tabview_tab_changed_event_cb(lv_event_t* e);
void sceneLabel_or_pageIndicator_event_cb(lv_event_t* e);
void pageIndicator_navigate_event_cb(lv_event_t* e);
void guis_doTabCreationAfterGUIlistChanged(GUIlists newGUIlist);
void setActiveTab(uint32_t index, lv_anim_enable_t anim_en, bool send_tab_changed_event = false);
// used by memoryUsage.cpp
void showMemoryUsageBar(bool showBar);
// used by commandHandler to show WiFi status
void showWiFiConnected(bool connected);
void guis_doAfterSliding(int oldTabID, int newTabID, bool newGuiList);

View file

@ -1,47 +1,90 @@
#include <lvgl.h>
#include "applicationInternal/gui/guiBase.h"
#include "applicationInternal/gui/guiMemoryOptimizer.h"
#include "applicationInternal/gui/guiRegistry.h"
#include "applicationInternal/scenes/sceneRegistry.h"
#include "applicationInternal/hardware/hardwarePresenter.h"
#include "applicationInternal/scenes/sceneRegistry.h"
struct tab_in_memory {
struct t_gui_on_tab {
lv_obj_t* tab;
int listIndex;
std::string guiName;
std::string GUIname;
int gui_list_index;
int gui_list_index_previous;
};
tab_in_memory tabs_in_memory[3] = {{NULL, -1, ""}, {NULL, -1, ""}, {NULL, -1, ""}};
// holds the ids of the tabs we had in memory before, so that we can determine the next or previous id
int tabs_in_memory_previous_listIndex[3]= {-1 , -1, -1};
struct t_gui_state {
// the next three are saved in the preferenceStorage every time they change
std::string activeScene_internalDontUse;
std::string activeGUIname_internalDontUse;
GUIlists activeGUIlist_internalDontUse;
// ---
int activeTabID = -1; // id of the active tab (one of 0,1,2)
int oldTabID = -1; // id of the tab before swiping (one of 0,1,2)
t_gui_on_tab gui_on_tab[3] = {{NULL, "", -1, -1}, {NULL, "", -1, -1}, {NULL, "", -1, -1}};
};
t_gui_state gui_state;
// Both the gui_state and the preferenceStorage should know at any time the current state (scene, GUIname, and GUIlist)
// preferenceStorage should know it because when going to sleep, it should persist the state in NVM.
// So whenever values change, it should be done through these functions.
// On startup, the gui_state is set by gui_memoryOptimizer_onStartup()
std::string gui_memoryOptimizer_getActiveSceneName() {
gui_state.activeScene_internalDontUse = get_activeScene();
return gui_state.activeScene_internalDontUse;
}
void gui_memoryOptimizer_setActiveSceneName(std::string aSceneName) {
gui_state.activeScene_internalDontUse = aSceneName;
set_activeScene(aSceneName);
}
std::string gui_memoryOptimizer_getActiveGUIname() {
gui_state.activeGUIname_internalDontUse = get_activeGUIname();
return gui_state.activeGUIname_internalDontUse;
}
void gui_memoryOptimizer_setActiveGUIname(std::string aGUIname) {
gui_state.activeGUIname_internalDontUse = aGUIname;
set_activeGUIname(aGUIname);
}
GUIlists gui_memoryOptimizer_getActiveGUIlist() {
gui_state.activeGUIlist_internalDontUse = (GUIlists)get_activeGUIlist();
return gui_state.activeGUIlist_internalDontUse;
}
void gui_memoryOptimizer_setActiveGUIlist(GUIlists aGUIlist) {
gui_state.activeGUIlist_internalDontUse = aGUIlist;
set_activeGUIlist(aGUIlist);
}
int gui_memoryOptimizer_getActiveTabID() {
return gui_state.activeTabID;
}
bool gui_memoryOptimizer_isTabIDInMemory(int tabID) {
// range check
if ((tabID < 0) || (tabID >= 3)) {
return false;
}
return (tabs_in_memory[tabID].listIndex != -1);
return (gui_state.gui_on_tab[tabID].gui_list_index != -1);
}
bool gui_memoryOptimizer_isGUInameInMemory(std::string guiName) {
bool gui_memoryOptimizer_isGUInameInMemory(std::string GUIname) {
for (uint8_t index=0; index <= 2; index++) {
if (tabs_in_memory[index].guiName == guiName) {
if (gui_state.gui_on_tab[index].GUIname == GUIname) {
return true;
}
}
return false;
}
void notify_active_tabs_before_delete() {
void notify_active_tabs_before_delete(t_gui_state *gui_state) {
Serial.printf(" Will notify tabs about deletion\r\n");
std::string nameOfTab;
for (int index=0; index <= 2; index++) {
if (tabs_in_memory[index].listIndex == -1) {
if (gui_state->gui_on_tab[index].gui_list_index == -1) {
Serial.printf(" Will not notify tab %d about deletion because it does not exist\r\n", index);
continue;
}
// For deletion, do not use the listIndex, but the name of the gui.
// For deletion, do not use the gui_list_index, but the name of the gui.
// The gui_list might have changed (when switching from a scene specific list to the main list or vice versa), so index could have changed as well.
nameOfTab = tabs_in_memory[index].guiName;
nameOfTab = gui_state->gui_on_tab[index].GUIname;
if (nameOfTab == "") {
Serial.printf(" Will not notify tab %d about deletion because it is not set\r\n", index);
} else if (registered_guis_byName_map.count(nameOfTab) == 0) {
@ -53,7 +96,7 @@ void notify_active_tabs_before_delete() {
}
}
void clear_tabview(lv_obj_t* tabview) {
void clear_tabview(lv_obj_t* tabview, t_gui_state *gui_state) {
if (tabview != NULL) {
// first remove events for the tabview
lv_obj_remove_event_cb(tabview, tabview_tab_changed_event_cb);
@ -63,10 +106,10 @@ void clear_tabview(lv_obj_t* tabview) {
tabview = NULL;
}
// set struct tabs_in_memory to NULL
tabs_in_memory[0] = {NULL, -1, ""};
tabs_in_memory[1] = {NULL, -1, ""};
tabs_in_memory[2] = {NULL, -1, ""};
// the gui_list_index_previous is needed for setGUIlistIndicesToBeShown_afterSlide();
gui_state->gui_on_tab[0] = {NULL, "", -1, gui_state->gui_on_tab[0].gui_list_index};
gui_state->gui_on_tab[1] = {NULL, "", -1, gui_state->gui_on_tab[1].gui_list_index};
gui_state->gui_on_tab[2] = {NULL, "", -1, gui_state->gui_on_tab[2].gui_list_index};
}
@ -114,162 +157,161 @@ lv_obj_t* create_panel() {
std::string get_name_of_gui_to_be_shown(int index) {
if (index == -1) {
return "";
} else if (index <= get_gui_list(get_currentScene())->size() -1) {
return get_gui_list(get_currentScene())->at(index);
} else if (index <= get_gui_list_active()->size() -1) {
return get_gui_list_active()->at(index);
} else {
return "";
}
}
void create_new_tab(lv_obj_t* tabview, uint32_t tabs_in_memory_index) {
std::string nameOfTab = get_name_of_gui_to_be_shown(tabs_in_memory[tabs_in_memory_index].listIndex);
void create_new_tab(lv_obj_t* tabview, t_gui_on_tab *gui_on_tab) {
std::string nameOfTab = get_name_of_gui_to_be_shown(gui_on_tab->gui_list_index);
if (nameOfTab == "") {
Serial.printf(" Will not create new tab at index %d because no name was provided\r\n", tabs_in_memory_index);
Serial.printf(" Will not create new tab because no name was provided\r\n");
} else if (registered_guis_byName_map.count(nameOfTab) == 0) {
Serial.printf(" Will not create new tab at index %d because name %s was not found in registry\r\n", tabs_in_memory_index, nameOfTab.c_str());
Serial.printf(" Will not create new tab because name %s was not found in registry\r\n", nameOfTab.c_str());
} else {
Serial.printf(" Will create tab with name \"%s\" at index %d\r\n", nameOfTab.c_str(), tabs_in_memory_index);
Serial.printf(" Will create tab with name \"%s\" \r\n", nameOfTab.c_str());
// save name of tab for deletion later
tabs_in_memory[tabs_in_memory_index].guiName = nameOfTab;
// create tab and save pointer to tab in tabs_in_memory
tabs_in_memory[tabs_in_memory_index].tab = lv_tabview_add_tab(tabview, nameOfTab.c_str());
gui_on_tab->GUIname = nameOfTab;
// create tab and save pointer to tab in gui_on_tab
gui_on_tab->tab = lv_tabview_add_tab(tabview, nameOfTab.c_str());
// let the gui create it's content
registered_guis_byName_map.at(nameOfTab).this_create_tab_content(tabs_in_memory[tabs_in_memory_index].tab);
registered_guis_byName_map.at(nameOfTab).this_create_tab_content(gui_on_tab->tab);
}
}
void doTabCreation_strategyMax3(lv_obj_t* tabview, uint32_t oldTabID, uint32_t newTabID) {
// create up to three tabs and the content of the tabs
/*
example: gui_list: 0 1 2 3 4
in memory active
0 1 -1 0 <- first state, special case - also the initial state
0 1 2 1
1 2 3 1
2 3 4 1
3 4 -1 1 <- last state, special case
*/
// create up to three tabs and the content of the tabs
/*
example: gui_list: 0 1 2 3 4
in memory active
0 1 -1 0 <- first state, special case - also the initial state
0 1 2 1
1 2 3 1
2 3 4 1
3 4 -1 1 <- last state, special case
*/
void setGUIlistIndicesToBeShown_forSpecificGUIlistIndex(int gui_list_index, t_gui_state *gui_state) {
// Set the gui_list_indeces to be shown for a specific gui_list_index
if (gui_list_index == 0) {
// first state
Serial.printf(" GUIlistIndices: will resume at specific index with \"first state\"\r\n");
gui_state->gui_on_tab[0] = {NULL, "", 0};
// take care if there is only one gui in list
gui_state->gui_on_tab[1] = {NULL, "", get_gui_list_active()->size() >= 2 ? 1 : -1};
gui_state->gui_on_tab[2] = {NULL, "", -1};
gui_state->activeTabID = 0;
} else if (gui_list_index == get_gui_list_active()->size() -1) {
// last state
Serial.printf(" GUIlistIndices: will resume at specific index with \"last state\"\r\n");
gui_state->gui_on_tab[0] = {NULL, "", gui_list_index -1};
gui_state->gui_on_tab[1] = {NULL, "", gui_list_index};
gui_state->gui_on_tab[2] = {NULL, "", -1};
gui_state->activeTabID = 1;
} else {
// any other state
Serial.printf(" GUIlistIndices: will resume at specific index with \"state between\"\r\n");
gui_state->gui_on_tab[0] = {NULL, "", gui_list_index -1};
gui_state->gui_on_tab[1] = {NULL, "", gui_list_index};
gui_state->gui_on_tab[2] = {NULL, "", gui_list_index +1};
gui_state->activeTabID = 1;
}
}
int tabToBeActivated = -1;
void setGUIlistIndicesToBeShown_forFirstGUIinGUIlist(t_gui_state *gui_state) {
Serial.printf(" GUIlistIndices: will show the first gui from \"gui_list\" as initial state\r\n");
// take care if there is no gui in list
gui_state->gui_on_tab[0] = {NULL, "", get_gui_list_active()->size() != 0 ? 0 : -1};
// take care if there is only one gui in list
gui_state->gui_on_tab[1] = {NULL, "", get_gui_list_active()->size() >= 2 ? 1 : -1};
gui_state->gui_on_tab[2] = {NULL, "", -1};
gui_state->activeTabID = 0;
}
void setGUIlistIndicesToBeShown_afterSlide(t_gui_state *gui_state) {
int oldListIndex = -1;
if ((oldTabID == -1) && (newTabID == -1)) {
// This is the initialization after the ESP32 has booted.
if ((tabs_in_memory_previous_listIndex[0] < get_gui_list(get_currentScene())->size()) && (tabs_in_memory_previous_listIndex[0] != -1)) {
// In gui_memoryOptimizer_prepare_startup, the index of get_currentGUIname() in gui_list was saved, if found.
// We can resume at this old state.
oldListIndex = tabs_in_memory_previous_listIndex[0] ;
if (oldListIndex == 0) {
// first state
Serial.printf(" Startup: will resume where we went to sleep with \"first state\"\r\n");
tabs_in_memory[0] = {NULL, 0};
// take care if there is only one gui in list
tabs_in_memory[1] = {NULL, get_gui_list(get_currentScene())->size() >= 2 ? 1 : -1};
tabs_in_memory[2] = {NULL, -1};
tabToBeActivated = 0;
} else if (oldListIndex == get_gui_list(get_currentScene())->size() -1) {
// last state
Serial.printf(" Startup: will resume where we went to sleep with \"last state\"\r\n");
tabs_in_memory[0] = {NULL, oldListIndex -1};
tabs_in_memory[1] = {NULL, oldListIndex};
tabs_in_memory[2] = {NULL, -1};
tabToBeActivated = 1;
} else {
// any other state
Serial.printf(" Startup: will resume where we went to sleep with \"state between\"\r\n");
tabs_in_memory[0] = {NULL, oldListIndex -1};
tabs_in_memory[1] = {NULL, oldListIndex};
tabs_in_memory[2] = {NULL, oldListIndex +1};
tabToBeActivated = 1;
}
} else {
Serial.printf(" Startup: cannot resume old state, so we will show the first tabs from \"gui_list\" as initial state\r\n");
// take care if there is no gui in list
tabs_in_memory[0] = {NULL, get_gui_list(get_currentScene())->size() != 0 ? 0 : -1};
// take care if there is only one gui in list
tabs_in_memory[1] = {NULL, get_gui_list(get_currentScene())->size() >= 2 ? 1 : -1};
tabs_in_memory[2] = {NULL, -1};
tabToBeActivated = 0;
}
} else if (oldTabID > newTabID) {
if (gui_state->oldTabID > gui_state->activeTabID) {
// swipe to previous item in list
Serial.printf(" Will swipe to previous item in list\r\n");
oldListIndex = tabs_in_memory_previous_listIndex[1];
oldListIndex = gui_state->gui_on_tab[1].gui_list_index_previous;
if ((oldListIndex == 1)) {
// next state is the "first state"
tabs_in_memory[0] = {NULL, 0};
tabs_in_memory[1] = {NULL, 1};
tabs_in_memory[2] = {NULL, -1};
tabToBeActivated = 0;
gui_state->gui_on_tab[0] = {NULL, "", 0};
gui_state->gui_on_tab[1] = {NULL, "", 1};
gui_state->gui_on_tab[2] = {NULL, "", -1};
gui_state->activeTabID = 0;
} else {
tabs_in_memory[0] = {NULL, oldListIndex -2};
tabs_in_memory[1] = {NULL, oldListIndex -1};
tabs_in_memory[2] = {NULL, oldListIndex};
tabToBeActivated = 1;
gui_state->gui_on_tab[0] = {NULL, "", oldListIndex -2};
gui_state->gui_on_tab[1] = {NULL, "", oldListIndex -1};
gui_state->gui_on_tab[2] = {NULL, "", oldListIndex};
gui_state->activeTabID = 1;
}
} else {
// swipe to next item in list
Serial.printf(" Will swipe to next item in list\r\n");
if (tabs_in_memory_previous_listIndex[2] == -1) {
if (gui_state->gui_on_tab[2].gui_list_index_previous == -1) {
// last state was the first state
oldListIndex = tabs_in_memory_previous_listIndex[0]; // is always 0
oldListIndex = gui_state->gui_on_tab[0].gui_list_index_previous; // is always 0
} else {
oldListIndex = tabs_in_memory_previous_listIndex[1];
oldListIndex = gui_state->gui_on_tab[1].gui_list_index_previous;
}
if (oldListIndex == get_gui_list(get_currentScene())->size() -2) {
if (oldListIndex == get_gui_list_active()->size() -2) {
// next state is the "last state"
tabs_in_memory[0] = {NULL, oldListIndex};
tabs_in_memory[1] = {NULL, oldListIndex +1};
tabs_in_memory[2] = {NULL, -1};
tabToBeActivated = 1;
gui_state->gui_on_tab[0] = {NULL, "", oldListIndex};
gui_state->gui_on_tab[1] = {NULL, "", oldListIndex +1};
gui_state->gui_on_tab[2] = {NULL, "", -1};
gui_state->activeTabID = 1;
} else {
tabs_in_memory[0] = {NULL, oldListIndex};
tabs_in_memory[1] = {NULL, oldListIndex +1};
tabs_in_memory[2] = {NULL, oldListIndex +2};
tabToBeActivated = 1;
gui_state->gui_on_tab[0] = {NULL, "", oldListIndex};
gui_state->gui_on_tab[1] = {NULL, "", oldListIndex +1};
gui_state->gui_on_tab[2] = {NULL, "", oldListIndex +2};
gui_state->activeTabID = 1;
}
}
}
void doTabCreation_strategyMax3(lv_obj_t* tabview, t_gui_state *gui_state) {
// create the tabs
Serial.printf(" Will create tabs. List indices of the three tabs are %d, %d, %d, tab nr %d will be activated\r\n", tabs_in_memory[0].listIndex, tabs_in_memory[1].listIndex, tabs_in_memory[2].listIndex, tabToBeActivated);
Serial.printf(" Will create tabs. List indices of the three tabs are %d, %d, %d, tab nr %d will be activated\r\n", gui_state->gui_on_tab[0].gui_list_index, gui_state->gui_on_tab[1].gui_list_index, gui_state->gui_on_tab[2].gui_list_index, gui_state->activeTabID);
for (int i=0; i<3; i++) {
create_new_tab(tabview, i);
create_new_tab(tabview, &gui_state->gui_on_tab[i]);
}
if (get_gui_list(get_currentScene())->size() > 0) {
std::string nameOfNewActiveTab = get_gui_list(get_currentScene())->at(tabs_in_memory[tabToBeActivated].listIndex);
if (get_gui_list_active()->size() > 0) {
std::string nameOfNewActiveTab = get_gui_list_active()->at(gui_state->gui_on_tab[gui_state->activeTabID].gui_list_index);
Serial.printf(" New visible tab is \"%s\"\r\n", nameOfNewActiveTab.c_str());
// set the tab we swiped to as active
setActiveTab(tabToBeActivated, LV_ANIM_OFF);
set_currentGUIname(nameOfNewActiveTab);
currentTabID = tabToBeActivated;
// set active tab
setActiveTab(gui_state->activeTabID, LV_ANIM_OFF);
gui_memoryOptimizer_setActiveGUIname(nameOfNewActiveTab);
}
}
LV_IMG_DECLARE(gradientLeft);
LV_IMG_DECLARE(gradientRight);
void getBreadcrumpPosition(uint8_t* breadcrumpPosition, std::string nameOfTab) {
void getBreadcrumpPosition(uint8_t* breadcrumpPosition, std::string nameOfGUI) {
*breadcrumpPosition = 0;
gui_list currentGUIlist = get_gui_list(get_currentScene());
gui_list gui_list_active = get_gui_list_active();
uint8_t counter = 0;
for (std::vector<std::string>::iterator it = currentGUIlist->begin() ; it != currentGUIlist->end(); ++it) {
for (std::vector<std::string>::iterator it = gui_list_active->begin() ; it != gui_list_active->end(); ++it) {
counter++;
if (*it == nameOfTab) {
if (*it == nameOfGUI) {
*breadcrumpPosition = counter;
return;
}
}
}
void fillPanelWithPageIndicator_strategyMax3(lv_obj_t* panel, lv_obj_t* img1, lv_obj_t* img2) {
void fillPanelWithPageIndicator_strategyMax3(lv_obj_t* panel, lv_obj_t* img1, lv_obj_t* img2, t_gui_state *gui_state) {
Serial.printf(" Will fill panel with page indicators\r\n");
if (get_gui_list(get_currentScene())->size() == 0) {
if (get_gui_list_active()->size() == 0) {
Serial.printf(" no tab available, so no page indicators\r\n");
// at least add the style
lv_obj_add_style(panel, &panel_style, 0);
@ -301,33 +343,33 @@ void fillPanelWithPageIndicator_strategyMax3(lv_obj_t* panel, lv_obj_t* img1, lv
btn = lv_btn_create(panel);
lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(btn, 150, lv_pct(100));
if (tabs_in_memory[0].listIndex == 0) {
if (gui_state->gui_on_tab[0].gui_list_index == 0) {
lv_obj_set_style_bg_color(btn, lv_color_black(), LV_PART_MAIN);
} else {
lv_obj_set_style_bg_color(btn, color_primary, LV_PART_MAIN);
}
uint8_t breadcrumpLength = get_gui_list(get_currentScene())->size();
uint8_t breadcrumpLength = get_gui_list_active()->size();
uint8_t breadcrumpDotSize = 8; // should be an even number
uint8_t breadcrumpDotDistance = 2; // should be an even number
int8_t breadcrumpStartPositionX = (-1) * (breadcrumpLength -1) * (breadcrumpDotSize + breadcrumpDotDistance) / 2;
// create the panel content for the three guis (or less) which are currently in memory
std::string nameOfTab;
std::string nameOfGUI;
uint8_t breadcrumpPosition;
for (int i=0; i<3; i++) {
if (tabs_in_memory[i].listIndex != -1) {
nameOfTab = tabs_in_memory[i].guiName;
getBreadcrumpPosition(&breadcrumpPosition, nameOfTab);
if (gui_state->gui_on_tab[i].gui_list_index != -1) {
nameOfGUI = gui_state->gui_on_tab[i].GUIname;
getBreadcrumpPosition(&breadcrumpPosition, nameOfGUI);
// Create actual buttons for every tab
lv_obj_t* btn = lv_btn_create(panel);
// only if this is the button for the currently active tab, make it clickable to get to scene selection gui
if (nameOfTab == get_currentGUIname()) {
if (nameOfGUI == gui_memoryOptimizer_getActiveGUIname()) {
lv_obj_add_flag(btn, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_event_cb(btn, sceneLabel_or_pageIndicator_event_cb, LV_EVENT_CLICKED, NULL);
} else if ((i==0 || i==1) && (tabs_in_memory[i+1].listIndex != -1)) {
} else if ((i==0 || i==1) && (gui_state->gui_on_tab[i+1].gui_list_index != -1)) {
// this is the button on the previous tab, which can be seen on the active tab
// activate click to prev tab
lv_obj_add_flag(btn, LV_OBJ_FLAG_CLICKABLE);
@ -367,7 +409,7 @@ void fillPanelWithPageIndicator_strategyMax3(lv_obj_t* panel, lv_obj_t* img1, lv
}
lv_obj_t* label = lv_label_create(btn);
lv_obj_set_style_text_font(label, &lv_font_montserrat_10, LV_PART_MAIN);
lv_label_set_text_fmt(label, "%s", nameOfTab.c_str());
lv_label_set_text_fmt(label, "%s", nameOfGUI.c_str());
lv_obj_align(label, LV_ALIGN_BOTTOM_MID, 0, 6);
}
@ -377,8 +419,8 @@ void fillPanelWithPageIndicator_strategyMax3(lv_obj_t* panel, lv_obj_t* img1, lv
btn = lv_btn_create(panel);
lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(btn, 150, lv_pct(100));
// 4 at last position 4 at middle position only one tab available overall
if ((tabs_in_memory[2].listIndex == get_gui_list(get_currentScene())->size()-1) || (tabs_in_memory[1].listIndex == get_gui_list(get_currentScene())->size()-1) || (tabs_in_memory[1].listIndex == -1)) {
// 4 at last position 4 at middle position only one tab available overall
if ((gui_state->gui_on_tab[2].gui_list_index == get_gui_list_active()->size()-1) || (gui_state->gui_on_tab[1].gui_list_index == get_gui_list_active()->size()-1) || (gui_state->gui_on_tab[1].gui_list_index == -1)) {
lv_obj_set_style_bg_color(btn, lv_color_black(), LV_PART_MAIN);
} else {
lv_obj_set_style_bg_color(btn, color_primary, LV_PART_MAIN);
@ -419,71 +461,76 @@ void fillPanelWithPageIndicator_strategyMax3(lv_obj_t* panel, lv_obj_t* img1, lv
}
void gui_memoryOptimizer_prepare_startup() {
// find index of get_currentGUIname() in gui_list
for (int i=0; i<get_gui_list(get_currentScene())->size(); i++) {
if (get_gui_list(get_currentScene())->at(i) == get_currentGUIname()) {
Serial.printf("Startup: found GUI with name \"%s\" in \"gui_list\" at position %d\r\n", get_currentGUIname().c_str(), i);
// save position so that "guis_doAfterSliding" can use it
tabs_in_memory[0].listIndex = i;
}
}
void gui_memoryOptimizer_notifyAndClear(lv_obj_t** tabview, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2, t_gui_state *gui_state) {
// if the gui was not found in main_gui_list, try to find it in scene specific list
if (tabs_in_memory[0].listIndex == -1) {
useSceneGUIlist = true;
for (int i=0; i<get_gui_list(get_currentScene())->size(); i++) {
if (get_gui_list(get_currentScene())->at(i) == get_currentGUIname()) {
Serial.printf("Startup: found GUI with name \"%s\" in scene specific \"gui_list\" at position %d\r\n", get_currentGUIname().c_str(), i);
// save position so that "guis_doAfterSliding" can use it
tabs_in_memory[0].listIndex = i;
}
}
}
// if the gui was still not found, reset useSceneGUIlist
if (tabs_in_memory[0].listIndex == -1) {
useSceneGUIlist = false;
}
// 1. notify old guis that they will be deleted so that they can persist their state if needed
notify_active_tabs_before_delete(gui_state);
// 2. clear current tabview and save gui_list_index_previous (needed for swipe)
clear_tabview(*tabview, gui_state);
// 3. clear current panel for page indicator
clear_panel(*panel, *img1, *img2);
}
void gui_memoryOptimizer_doAfterSliding_deletionAndCreation(lv_obj_t** tabview, int oldTabID, int newTabID, bool newGuiList, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2) {
void gui_memoryOptimizer_doContentCreation(lv_obj_t** tabview, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2, t_gui_state *gui_state);
// find the position of the current GUI in the gui list which was active last (both were automatically saved in the preferences)
void gui_memoryOptimizer_onStartup(lv_obj_t** tabview, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2) {
// Get last state from preferences and save it in gui_state
// So it is ok to call them without using the return values.
gui_memoryOptimizer_getActiveSceneName();
gui_memoryOptimizer_getActiveGUIname();
gui_memoryOptimizer_getActiveGUIlist();
// 1. find last used gui
int gui_list_index = -1;
// find index of gui_memoryOptimizer_getActiveGUIname() in gui_list_active
for (int i=0; i<get_gui_list_active()->size(); i++) {
if (get_gui_list_active()->at(i) == gui_memoryOptimizer_getActiveGUIname()) {
Serial.printf("Startup: found GUI with name \"%s\" in \"gui_list_active\" at position %d\r\n", gui_memoryOptimizer_getActiveGUIname().c_str(), i);
gui_list_index = i;
break;
}
}
// 2. set gui_list_indices and the tab to be activated
if ((gui_list_index >= 0) && (gui_list_index < get_gui_list_active()->size())) {
// gui was found
setGUIlistIndicesToBeShown_forSpecificGUIlistIndex(gui_list_index, &gui_state);
} else {
// gui was not found
Serial.printf("Startup: GUI with name \"%s\" was not found. Will start with first GUI of main_gui_list\r\n", gui_memoryOptimizer_getActiveGUIname().c_str());
gui_memoryOptimizer_setActiveGUIlist(MAIN_GUI_LIST);
setGUIlistIndicesToBeShown_forFirstGUIinGUIlist(&gui_state);
}
// 3. create content
gui_memoryOptimizer_doContentCreation(tabview, panel, img1, img2, &gui_state);
}
void gui_memoryOptimizer_afterSliding(lv_obj_t** tabview, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2, int newTabID) {
// Here the magic for dynamic creation and deletion of lvgl objects happens to keep memory usage low.
// The next and previous tab must always be available in the tabview, because they can already been seen during the animation.
// And you always need 3 tabs, otherwise you even could not slide to the next or previous tab.
// So we always have 3 tabs.
// After the animation, the tabview and hence all tabs are deleted and recreated.
// With parameter newGuiList it is signaled that we are changing from a scene specific list to the main list or vice versa
// In that case, we have to do special treatment because we are not simply sliding to the left or to the right, but we start newly with a different gui list.
// only called by guis_doTabCreationAtStartup():
// oldTabID = -1, newTabID = -1, newGuiList = false
// called by handleScene()
// oldTabID = -1, newTabID = -1, newGuiList = true
Serial.printf("--- Start of tab deletion and creation\r\n");
bool isInitialization = ((oldTabID == -1) && (newTabID == -1));
if (isInitialization) {
Serial.printf("Startup: will initially create the tabs to be shown\r\n");
} else {
Serial.printf("Changing from oldTabID %d \"%s\" to newTabID %d \"%s\"\r\n",
oldTabID, get_gui_list(get_currentScene())->at(tabs_in_memory[oldTabID].listIndex).c_str(),
newTabID, get_gui_list(get_currentScene())->at(tabs_in_memory[newTabID].listIndex).c_str());
}
gui_state.oldTabID = gui_state.activeTabID;
gui_state.activeTabID = newTabID;
// Save the ids of the tabs we had in memory before. This is only used by doTabCreation_strategyMax3() to know where we come from and where we have to go to.
for (int i=0; i<3; i++) {
tabs_in_memory_previous_listIndex[i] = tabs_in_memory[i].listIndex;
}
Serial.printf("Changing from oldTabID %d \"%s\" to newTabID %d \"%s\"\r\n",
gui_state.oldTabID, gui_state.gui_on_tab[gui_state.oldTabID].GUIname.c_str(),
gui_state.activeTabID, gui_state.gui_on_tab[gui_state.activeTabID].GUIname.c_str());
// the old tabs need to be notified that they will be deleted so that they can persist their state if needed
notify_active_tabs_before_delete();
// clear current tabview
clear_tabview(*tabview);
// clear current panel for page indicator
clear_panel(*panel, *img1, *img2);
// 1. notify old guis and clear tabview and panel
gui_memoryOptimizer_notifyAndClear(tabview, panel, img1, img2, &gui_state);
// only optional: delete and create the whole screen. Not necessary.
// Only used for a test. init_gui_status_bar() would need to be called again at a suitable place, because the status bar would also be deleted.
@ -494,25 +541,44 @@ void gui_memoryOptimizer_doAfterSliding_deletionAndCreation(lv_obj_t** tabview,
// lv_scr_load(newscr);
// lv_obj_del(oldscr);
if (newGuiList) {
// If we are switching to a new gui list, then we need to set tabs_in_memory_previous_listIndex[0] = -1;
// Doing so, doTabCreation_strategyMax3() knows that we cannot resume an old state.
tabs_in_memory_previous_listIndex[0] = -1;
}
// 2. set gui_list_indices and the tab to be activated
setGUIlistIndicesToBeShown_afterSlide(&gui_state);
// 3. create content
gui_memoryOptimizer_doContentCreation(tabview, panel, img1, img2, &gui_state);
}
void gui_memoryOptimizer_afterGUIlistChanged(lv_obj_t** tabview, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2, GUIlists newGUIlist) {
Serial.printf("--- Will change to new gui_list\r\n");
// 1. notify old guis and clear tabview and panel
gui_memoryOptimizer_notifyAndClear(tabview, panel, img1, img2, &gui_state);
// 2. set gui_list_indices and the tab to be activated
gui_memoryOptimizer_setActiveGUIlist(newGUIlist);
setGUIlistIndicesToBeShown_forFirstGUIinGUIlist(&gui_state);
// 3. create content
gui_memoryOptimizer_doContentCreation(tabview, panel, img1, img2, &gui_state);
}
void gui_memoryOptimizer_doContentCreation(lv_obj_t** tabview, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2, t_gui_state *gui_state) {
// recreate the tabview
lv_obj_t* newTabview = create_tabview();
*tabview = newTabview;
// Create the tabs. Use strategy "3 tabs at maximum" to keep memory usage low.
// Set the tab we swiped to as active
doTabCreation_strategyMax3(*tabview, oldTabID, newTabID);
doTabCreation_strategyMax3(*tabview, gui_state);
// Create the panel for the page indicator. Panel itself takes about 2136 bytes for three tabs.
lv_obj_t* newPanel = create_panel();
*panel = newPanel;
*img1 = lv_img_create(lv_scr_act());
*img2 = lv_img_create(lv_scr_act());
fillPanelWithPageIndicator_strategyMax3(*panel, *img1, *img2);
fillPanelWithPageIndicator_strategyMax3(*panel, *img1, *img2, gui_state);
// now, as the correct tab is active, register again the events for the tabview
lv_obj_add_event_cb(*tabview, tabview_tab_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);

View file

@ -1,6 +1,26 @@
#pragma once
#include <string>
#include <lvgl.h>
void gui_memoryOptimizer_prepare_startup();
void gui_memoryOptimizer_doAfterSliding_deletionAndCreation(lv_obj_t** tabview, int oldTabID, int newTabID, bool newGuiList, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2);
enum GUIlists {
// MAIN_GUI_LIST: we are in the main_gui_list (with the scene selector as first gui), either if a scene is active or not
// SCENE_GUI_LIST: a scene is active and we are not in the main_gui_list. In that case, we try to use the scene specific gui list, if the scene defined one.
MAIN_GUI_LIST,
SCENE_GUI_LIST
};
void gui_memoryOptimizer_onStartup(lv_obj_t** tabview, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2);
void gui_memoryOptimizer_afterSliding(lv_obj_t** tabview, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2, int newTabID);
void gui_memoryOptimizer_afterGUIlistChanged(lv_obj_t** tabview, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2, GUIlists newGUIlist);
int gui_memoryOptimizer_getActiveTabID();
bool gui_memoryOptimizer_isTabIDInMemory(int tabID);
bool gui_memoryOptimizer_isGUInameInMemory(std::string guiName);
bool gui_memoryOptimizer_isGUInameInMemory(std::string GUIname);
std::string gui_memoryOptimizer_getActiveSceneName();
void gui_memoryOptimizer_setActiveSceneName(std::string aSceneName);
std::string gui_memoryOptimizer_getActiveGUIname();
void gui_memoryOptimizer_setActiveGUIname(std::string aGUIname);
GUIlists gui_memoryOptimizer_getActiveGUIlist();
void gui_memoryOptimizer_setActiveGUIlist(GUIlists aGUIlist);

View file

@ -26,17 +26,23 @@ void init_preferences(void) {
void save_preferences(void) {
save_preferences_HAL();
};
std::string get_currentScene() {
return get_currentScene_HAL();
std::string get_activeScene() {
return get_activeScene_HAL();
}
void set_currentScene(std::string aCurrentScene) {
set_currentScene_HAL(aCurrentScene);
void set_activeScene(std::string anActiveScene) {
set_activeScene_HAL(anActiveScene);
}
std::string get_currentGUIname() {
return get_currentGUIname_HAL();
std::string get_activeGUIname() {
return get_activeGUIname_HAL();
}
void set_currentGUIname(std::string aCurrentGUIname) {
set_currentGUIname_HAL(aCurrentGUIname);
void set_activeGUIname(std::string anActiveGUIname) {
set_activeGUIname_HAL(anActiveGUIname);
}
int get_activeGUIlist() {
return get_activeGUIlist_HAL();
}
void set_activeGUIlist(int anActiveGUIlist) {
set_activeGUIlist_HAL(anActiveGUIlist);
}
// --- user led ---------------------------------------------------------------

View file

@ -10,10 +10,12 @@ void init_hardware_general(void);
// --- preferences ------------------------------------------------------------
void init_preferences(void);
void save_preferences(void);
std::string get_currentScene();
void set_currentScene(std::string aCurrentScene);
std::string get_currentGUIname();
void set_currentGUIname(std::string aCurrentGUIname);
std::string get_activeScene();
void set_activeScene(std::string anActiveScene);
std::string get_activeGUIname();
void set_activeGUIname(std::string anActiveGUIname);
int get_activeGUIlist();
void set_activeGUIlist(int anActiveGUIlist);
// --- user led ---------------------------------------------------------------
void init_userled(void);

View file

@ -1,7 +1,8 @@
#include <string>
#include "applicationInternal/gui/guiMemoryOptimizer.h"
#include "applicationInternal/hardware/hardwarePresenter.h"
#include "applicationInternal/scenes/sceneRegistry.h"
#include "applicationInternal/commandHandler.h"
#include "applicationInternal/hardware/hardwarePresenter.h"
const uint8_t ROWS = 5; //five rows
const uint8_t COLS = 5; //five columns
@ -33,7 +34,7 @@ void doShortPress(char keyChar, int keyCode){
if ((currentMillis - lastTimeSent[keyCode/ROWS][keyCode%ROWS]) > repeatRate) {
lastTimeSent[keyCode/ROWS][keyCode%ROWS] = currentMillis;
uint16_t command = get_command_short(get_currentScene(), keyChar);
uint16_t command = get_command_short(gui_memoryOptimizer_getActiveSceneName(), keyChar);
if (command != COMMAND_UNKNOWN) {
Serial.printf("key: key '%c', will use command '%u'\r\n", keyChar, command);
executeCommand(command);
@ -44,7 +45,7 @@ void doShortPress(char keyChar, int keyCode){
}
void doLongPress(char keyChar, int keyCode){
uint16_t command = get_command_long(get_currentScene(), keyChar);
uint16_t command = get_command_long(gui_memoryOptimizer_getActiveSceneName(), keyChar);
if (command != COMMAND_UNKNOWN) {
Serial.printf("key: key '%c' (long press), will use command '%u'\r\n", keyChar, command);
executeCommand(command);
@ -70,11 +71,11 @@ void keypad_loop(void) {
if (keypad_keys[i].kstate == PRESSED) {
// Serial.println("pressed");
if ((get_key_repeatMode(get_currentScene(), keyChar) == SHORT) && (lastKeyState[keyCode/ROWS][keyCode%ROWS] != PRESSED)) {
if ((get_key_repeatMode(gui_memoryOptimizer_getActiveSceneName(), keyChar) == SHORT) && (lastKeyState[keyCode/ROWS][keyCode%ROWS] != PRESSED)) {
// Serial.printf("key: PRESSED of SHORT key %c (%d)\r\n", keyChar, keyCode);
doShortPress(keyChar, keyCode);
} else if ((get_key_repeatMode(get_currentScene(), keyChar) == SHORT_REPEATED) && (lastKeyState[keyCode/ROWS][keyCode%ROWS] != PRESSED)) { // here do not repeat it too early, do the repeat only in HOLD
} else if ((get_key_repeatMode(gui_memoryOptimizer_getActiveSceneName(), keyChar) == SHORT_REPEATED) && (lastKeyState[keyCode/ROWS][keyCode%ROWS] != PRESSED)) { // here do not repeat it too early, do the repeat only in HOLD
// Serial.printf("key: PRESSED of SHORT_REPEATED key %c (%d)\r\n", keyChar, keyCode);
doShortPress(keyChar, keyCode);
@ -84,13 +85,13 @@ void keypad_loop(void) {
} else if (keypad_keys[i].kstate == HOLD) {
// Serial.println("hold");
if ((get_key_repeatMode(get_currentScene(), keyChar) == SHORTorLONG) && (lastKeyState[keyCode/ROWS][keyCode%ROWS] != HOLD)) {
if ((get_key_repeatMode(gui_memoryOptimizer_getActiveSceneName(), keyChar) == SHORTorLONG) && (lastKeyState[keyCode/ROWS][keyCode%ROWS] != HOLD)) {
// Serial.printf("key: HOLD of SHORTorLONG key %c (%d)\r\n", keyChar, keyCode);
// Serial.printf("will set keyIsHold to TRUE for keycode %d\r\n", keyCode);
keyIsHold[keyCode/ROWS][keyCode%ROWS] = true;
doLongPress(keyChar, keyCode);
} else if (get_key_repeatMode(get_currentScene(), keyChar) == SHORT_REPEATED) { // this is the only case where we do not check the lastKeyState, because here it is intended to repeat the action
} else if (get_key_repeatMode(gui_memoryOptimizer_getActiveSceneName(), keyChar) == SHORT_REPEATED) { // this is the only case where we do not check the lastKeyState, because here it is intended to repeat the action
// Serial.printf("key: HOLD of SHORT_REPEATED key %c (%d)\r\n", keyChar, keyCode);
doShortPress(keyChar, keyCode);
@ -99,7 +100,7 @@ void keypad_loop(void) {
} else if (keypad_keys[i].kstate == RELEASED) {
// Serial.println("released");
if ((get_key_repeatMode(get_currentScene(), keyChar) == SHORTorLONG) && !keyIsHold[keyCode/ROWS][keyCode%ROWS] && (lastKeyState[keyCode/ROWS][keyCode%ROWS] != RELEASED)) {
if ((get_key_repeatMode(gui_memoryOptimizer_getActiveSceneName(), keyChar) == SHORTorLONG) && !keyIsHold[keyCode/ROWS][keyCode%ROWS] && (lastKeyState[keyCode/ROWS][keyCode%ROWS] != RELEASED)) {
// Serial.printf("value of keyIsHold for keycode %d is %d\r\n", keyCode, keyIsHold[keyCode/ROWS][keyCode%ROWS]);
// Serial.printf("key: RELEASED of SHORTorLONG key %c (%d)\r\n", keyChar, keyCode);
doShortPress(keyChar, keyCode);

View file

@ -7,6 +7,12 @@
#include "applicationInternal/commandHandler.h"
#include "scenes/scene__default.h"
void setLabelActiveScene() {
if ((SceneLabel != NULL) && sceneExists(gui_memoryOptimizer_getActiveSceneName())) {
lv_label_set_text(SceneLabel, gui_memoryOptimizer_getActiveSceneName().c_str());
}
}
void handleScene(uint16_t command, commandData commandData, std::string additionalPayload = "") {
// FORCE can be either as second payload in commandData
@ -25,27 +31,26 @@ void handleScene(uint16_t command, commandData commandData, std::string addition
// do not really switch scene, but show sceneSelection gui. From that on, we are in the main_gui_list.
if (scene_name == scene_name_selection) {
useSceneGUIlist = false;
guis_doAfterSliding(-1, -1, true);
guis_doTabCreationAfterGUIlistChanged(MAIN_GUI_LIST);
return;
}
// do not switch scene, but navigate to the prev or next gui in the currently active list of guis
if ((scene_name == scene_gui_next) || (scene_name == scene_gui_prev)) {
if (scene_name == scene_gui_prev) {
if (currentTabID == 0) {
if (gui_memoryOptimizer_getActiveTabID() == 0) {
Serial.println("scene: cannot navigate to prev gui, because there is none");
} else {
Serial.println("scene: will navigate to prev gui");
setActiveTab(currentTabID -1, LV_ANIM_ON, true);
setActiveTab(gui_memoryOptimizer_getActiveTabID() -1, LV_ANIM_ON, true);
}
} else if (scene_name == scene_gui_next) {
if (!gui_memoryOptimizer_isTabIDInMemory(currentTabID +1)) {
if (!gui_memoryOptimizer_isTabIDInMemory(gui_memoryOptimizer_getActiveTabID() +1)) {
Serial.println("scene: cannot navigate to next gui, because there is none");
} else {
Serial.println("scene: will navigate to next gui");
setActiveTab(currentTabID +1, LV_ANIM_ON, true);
setActiveTab(gui_memoryOptimizer_getActiveTabID() +1, LV_ANIM_ON, true);
}
}
@ -57,15 +62,15 @@ void handleScene(uint16_t command, commandData commandData, std::string addition
Serial.printf("scene: cannot start scene %s, because it is unknown\r\n", scene_name.c_str());
return;
} else {
Serial.printf("scene: will switch from old scene %s to new scene %s\r\n", get_currentScene().c_str(), scene_name.c_str());
Serial.printf("scene: will switch from old scene %s to new scene %s\r\n", gui_memoryOptimizer_getActiveSceneName().c_str(), scene_name.c_str());
}
// do not activate the same scene again, only when forced to do so (e.g. by long press on the gui or when selected by hardware key)
bool callEndAndStartSequences;
if ((scene_name == get_currentScene()) && ((isForcePayload != "FORCE") && (additionalPayload != "FORCE"))) {
if ((scene_name == gui_memoryOptimizer_getActiveSceneName()) && ((isForcePayload != "FORCE") && (additionalPayload != "FORCE"))) {
Serial.printf("scene: will not start scene again, because it is already active\r\n");
callEndAndStartSequences = false;
} else if ((scene_name == get_currentScene()) && ((isForcePayload == "FORCE") || (additionalPayload == "FORCE"))) {
} else if ((scene_name == gui_memoryOptimizer_getActiveSceneName()) && ((isForcePayload == "FORCE") || (additionalPayload == "FORCE"))) {
Serial.printf("scene: scene is already active, but FORCE was set, so start scene again\r\n");
callEndAndStartSequences = true;
} else {
@ -78,13 +83,13 @@ void handleScene(uint16_t command, commandData commandData, std::string addition
if (callEndAndStartSequences) {
// end old scene
if (!sceneExists(get_currentScene()) && (get_currentScene() != "")) {
Serial.printf("scene: WARNING: cannot end scene %s, because it is unknown\r\n", get_currentScene().c_str());
if (!sceneExists(gui_memoryOptimizer_getActiveSceneName()) && (gui_memoryOptimizer_getActiveSceneName() != "")) {
Serial.printf("scene: WARNING: cannot end scene %s, because it is unknown\r\n", gui_memoryOptimizer_getActiveSceneName().c_str());
} else {
if (get_currentScene() != "") {
Serial.printf("scene: will call end sequence for scene %s\r\n", get_currentScene().c_str());
scene_end_sequence_from_registry(get_currentScene());
if (gui_memoryOptimizer_getActiveSceneName() != "") {
Serial.printf("scene: will call end sequence for scene %s\r\n", gui_memoryOptimizer_getActiveSceneName().c_str());
scene_end_sequence_from_registry(gui_memoryOptimizer_getActiveSceneName());
}
}
@ -94,19 +99,12 @@ void handleScene(uint16_t command, commandData commandData, std::string addition
scene_start_sequence_from_registry(scene_name);
}
set_currentScene(scene_name);
gui_memoryOptimizer_setActiveSceneName(scene_name);
if (SceneLabel != NULL) {lv_label_set_text(SceneLabel, get_currentScene().c_str());}
if (SceneLabel != NULL) {lv_label_set_text(SceneLabel, gui_memoryOptimizer_getActiveSceneName().c_str());}
Serial.printf("scene: scene handling finished, new scene %s is active\r\n", get_currentScene().c_str());
Serial.printf("scene: scene handling finished, new scene %s is active\r\n", gui_memoryOptimizer_getActiveSceneName().c_str());
useSceneGUIlist = true;
// recreate the gui based on the current scene
guis_doAfterSliding(-1, -1, true);
guis_doTabCreationAfterGUIlistChanged(SCENE_GUI_LIST);
}
void setLabelCurrentScene() {
if ((SceneLabel != NULL) && sceneExists(get_currentScene())) {
lv_label_set_text(SceneLabel, get_currentScene().c_str());
}
}

View file

@ -3,5 +3,5 @@
#include <string>
#include "applicationInternal/commandHandler.h"
void setLabelActiveScene();
void handleScene(uint16_t command, commandData commandData, std::string additionalPayload = "");
void setLabelCurrentScene();

View file

@ -1,16 +1,12 @@
#include <map>
#include <stdexcept>
#include "applicationInternal/commandHandler.h"
#include "applicationInternal/scenes/sceneRegistry.h"
#include "applicationInternal/gui/guiMemoryOptimizer.h"
#include "applicationInternal/hardware/hardwarePresenter.h"
#include "applicationInternal/scenes/sceneRegistry.h"
#include "applicationInternal/commandHandler.h"
// scenes
#include "scenes/scene__default.h"
// If useSceneGUIlist == true, then a scene is active and we are not in the main_gui_list (with the scene selector as first gui).
// In that case, we try to use the scene specific gui list, if the scene defined one.
// If useSceneGUIlist == false, then we are in the main_gui_list, either if a scene is active or not.
bool useSceneGUIlist = false;
// https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work
struct scene_definition {
scene_setKeys this_scene_setKeys;
@ -90,7 +86,7 @@ void scene_end_sequence_from_registry(std::string sceneName) {
repeatModes get_key_repeatMode(std::string sceneName, char keyChar) {
try {
// look if the map of the current scene has a definition for it
// look if the map of the active scene has a definition for it
if ((registered_scenes.count(sceneName) > 0) && (registered_scenes.at(sceneName).this_key_repeatModes->count(keyChar) > 0)) {
// Serial.printf("get_key_repeatMode: will use key from scene %s\r\n", sceneName.c_str());
return registered_scenes.at(sceneName).this_key_repeatModes->at(keyChar);
@ -114,7 +110,7 @@ repeatModes get_key_repeatMode(std::string sceneName, char keyChar) {
uint16_t get_command_short(std::string sceneName, char keyChar) {
try {
// look if the map of the current scene has a definition for it
// look if the map of the active scene has a definition for it
if ((registered_scenes.count(sceneName) > 0) && (registered_scenes.at(sceneName).this_key_commands_short->count(keyChar) > 0)) {
// Serial.printf("get_command_short: will use key from scene %s\r\n", sceneName.c_str());
return registered_scenes.at(sceneName).this_key_commands_short->at(keyChar);
@ -139,7 +135,7 @@ uint16_t get_command_short(std::string sceneName, char keyChar) {
uint16_t get_command_long(std::string sceneName, char keyChar) {
try {
// look if the map of the current scene has a definition for it
// look if the map of the active scene has a definition for it
if ((registered_scenes.count(sceneName) > 0) && (registered_scenes.at(sceneName).this_key_commands_long->count(keyChar) > 0)) {
// Serial.printf("get_command_long: will use key from scene %s\r\n", sceneName.c_str());
return registered_scenes.at(sceneName).this_key_commands_long->at(keyChar);
@ -162,40 +158,44 @@ uint16_t get_command_long(std::string sceneName, char keyChar) {
}
gui_list get_gui_list(std::string sceneName) {
gui_list get_gui_list(GUIlists gui_list) {
try {
// If useSceneGUIlist == true, then a scene is active and we are not in the main_gui_list (with the scene selector as first gui).
// If gui_list == MAIN_GUI_LIST, then we are in the main_gui_list, either if a scene is active or not.
// If gui_list == SCENE_GUI_LIST, then a scene is active and we are not in the main_gui_list (with the scene selector as first gui).
// In that case, we try to use the scene specific gui list, if the scene defined one.
// If useSceneGUIlist == false, then we are in the main_gui_list, either if a scene is active or not.
#if (USE_SCENE_SPECIFIC_GUI_LIST != 0)
// look if the current scene has a definition for a gui list
if (useSceneGUIlist &&
(registered_scenes.count(sceneName) > 0) && (registered_scenes.at(sceneName).this_gui_list != NULL)) {
// Serial.printf("get_gui_list: will use gui_list from scene %s\r\n", sceneName.c_str());
return registered_scenes.at(sceneName).this_gui_list;
// if there is no scene specific gui list, simply return the main_gui_list
} else {
// Serial.printf("get_gui_list: will use main_gui_list\r\n");
if (gui_list == MAIN_GUI_LIST) {
return &main_gui_list;
}
#else
// never use scene specific gui list
return &main_gui_list;
#endif
} else {
#if (USE_SCENE_SPECIFIC_GUI_LIST != 0)
// look if the active scene has a definition for a gui list
if ((registered_scenes.count(gui_memoryOptimizer_getActiveSceneName()) > 0) && (registered_scenes.at(gui_memoryOptimizer_getActiveSceneName()).this_gui_list != NULL)) {
// Serial.printf("get_gui_list: will use gui_list from scene %s\r\n", sceneName.c_str());
return registered_scenes.at(gui_memoryOptimizer_getActiveSceneName()).this_gui_list;
} else {
// no scene specific gui list was defined
return &main_gui_list;
}
#else
// never use scene specific gui list
return &main_gui_list;
#endif
}
}
catch (const std::out_of_range& oor) {
Serial.printf("get_gui_list: internal error, sceneName not registered\r\n");
return NULL;
}
}
gui_list get_gui_list_active() {
return get_gui_list(gui_memoryOptimizer_getActiveGUIlist());
}
uint16_t get_activate_scene_command(std::string sceneName) {
try {
// look if the current scene is known
// look if the active scene is known
if ((registered_scenes.count(sceneName) > 0)) {
// Serial.printf("get_activate_scene_command: will use activate_scene_command from scene %s\r\n", sceneName.c_str());
return registered_scenes.at(sceneName).this_activate_scene_command;

View file

@ -6,8 +6,6 @@
#include <vector>
#include "applicationInternal/keys.h"
extern bool useSceneGUIlist;
typedef std::vector<std::string> t_gui_list;
typedef std::vector<std::string> t_scene_list;
@ -37,7 +35,7 @@ void scene_end_sequence_from_registry(std::string sceneName);
repeatModes get_key_repeatMode(std::string sceneName, char keyChar);
uint16_t get_command_short(std::string sceneName, char keyChar);
uint16_t get_command_long(std::string sceneName, char keyChar);
gui_list get_gui_list(std::string sceneName);
gui_list get_gui_list_active();
uint16_t get_activate_scene_command(std::string sceneName);
scene_list get_scenes_on_sceneSelectionGUI();
void set_scenes_on_sceneSelectionGUI(t_scene_list a_scene_list);

View file

@ -18,12 +18,12 @@ static void virtualKeypad_event_cb(lv_event_t* e) {
int user_data = (intptr_t)(target->user_data);
// send corrensponding number
if (get_currentScene() == scene_name_TV) {
if (gui_memoryOptimizer_getActiveSceneName() == scene_name_TV) {
uint16_t virtualKeyMapTVNumbers[10] = {SAMSUNG_NUM_1, SAMSUNG_NUM_2, SAMSUNG_NUM_3, SAMSUNG_NUM_4, SAMSUNG_NUM_5, SAMSUNG_NUM_6, SAMSUNG_NUM_7, SAMSUNG_NUM_8, SAMSUNG_NUM_9, SAMSUNG_NUM_0};
uint16_t command = virtualKeyMapTVNumbers[user_data];
executeCommand(command);
} else if (get_currentScene() == scene_name_fireTV) {
} else if (gui_memoryOptimizer_getActiveSceneName() == scene_name_fireTV) {
int virtualKeyMapFireTVNumbers[10] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0};
int number = virtualKeyMapFireTVNumbers[user_data];
std::string numberStr = std::to_string(number);

View file

@ -125,7 +125,7 @@ int main(int argc, char *argv[]) {
#endif
// init GUI - will initialize tft, touch and lvgl
init_gui();
setLabelCurrentScene();
setLabelActiveScene();
gui_loop(); // Run the LVGL UI once before the loop takes over
// setup the Inertial Measurement Unit (IMU) for motion detection. Has to be after init_gui(), otherwise I2C will not work