Implementation of Theme Switching and Persistence (#839)

* initial support for color theming using yml files in the THEME directory

* initial theming support using yml files (without yml support)

* fixed issues with themes actually working on radio.

* fix another issue with drive access

* more theme changes.  Added author, and info to display.  Also display an image of  the theme if it is present.

* theme files now support both RGB() and 0x syntax

* quick fix for possible variable overflow.

* more tweaks after JimB tested

* fixed layout issues with NV14 and also added word wrap correctly.

* added edgetx headers to new string functions.

* Whitespace / unused code cleanup

Co-authored-by: kkoenig1@optonline.com <kkoenig1@optonline.com>
Co-authored-by: Peter Feerick <peter.feerick@gmail.com>
This commit is contained in:
Kevin Koenig
2021-10-04 23:27:08 +11:00
committed by GitHub
co-authored by kkoenig1@optonline.com <kkoenig1@optonline.com> Peter Feerick
parent ba03d02b7a
commit 9cb6d73947
9 changed files with 806 additions and 50 deletions
+2
View File
@@ -11,6 +11,7 @@ file(GLOB WIDGETS_SRC RELATIVE ${RADIO_SRC_DIR}/gui/colorlcd ${RADIO_SRC_DIR}/gu
set(GUI_SRC
${GUI_SRC}
str_functions.cpp
colors.cpp
lcd.cpp
splash.cpp
@@ -18,6 +19,7 @@ set(GUI_SRC
curves.cpp
bitmaps.cpp
theme.cpp
theme_manager.cpp
tabsgroup.cpp
page.cpp
fab_button.cpp
+66
View File
@@ -0,0 +1,66 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#pragma once
#include "libopenui.h"
class FilePreview : public Window
{
public:
FilePreview(Window *parent, const rect_t &rect) :
Window(parent, rect, NO_SCROLLBAR)
{
}
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "FilePreview";
}
#endif
void setFile(const char *filename)
{
delete bitmap;
const char *ext = getFileExtension(filename);
if (ext && isExtensionMatching(ext, BITMAPS_EXT)) {
bitmap = BitmapBuffer::loadBitmap(filename);
} else {
bitmap = nullptr;
}
invalidate();
}
void paint(BitmapBuffer *dc) override
{
coord_t y = parent->getScrollPositionY() + 2;
coord_t h = MENU_BODY_HEIGHT - 4;
if (bitmap) {
coord_t bitmapHeight = min<coord_t>(h, bitmap->height());
coord_t bitmapWidth = min<coord_t>(width(), bitmap->width());
dc->drawScaledBitmap(bitmap, (width() - bitmapWidth) / 2, y + (h - bitmapHeight) / 2, bitmapWidth,
bitmapHeight);
}
}
protected:
BitmapBuffer *bitmap = nullptr;
};
+1 -42
View File
@@ -28,6 +28,7 @@
#include "standalone_lua.h"
#include "sdcard.h"
#include "view_text.h"
#include "file_preview.h"
class FileNameEditWindow : public Page
{
@@ -128,48 +129,6 @@ char * getCurrentPath()
return path;
}
class FilePreview : public Window
{
public:
FilePreview(Window *parent, const rect_t &rect) :
Window(parent, rect, NO_SCROLLBAR)
{
}
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "FilePreview";
}
#endif
void setFile(const char *filename)
{
delete bitmap;
const char *ext = getFileExtension(filename);
if (ext && isExtensionMatching(ext, BITMAPS_EXT)) {
bitmap = BitmapBuffer::loadBitmap(filename);
} else {
bitmap = nullptr;
}
invalidate();
}
void paint(BitmapBuffer *dc) override
{
coord_t y = parent->getScrollPositionY() + 2;
coord_t h = MENU_BODY_HEIGHT - 4;
if (bitmap) {
coord_t bitmapHeight = min<coord_t>(h, bitmap->height());
coord_t bitmapWidth = min<coord_t>(width(), bitmap->width());
dc->drawScaledBitmap(bitmap, (width() - bitmapWidth) / 2, y + (h - bitmapHeight) / 2, bitmapWidth,
bitmapHeight);
}
}
protected:
BitmapBuffer *bitmap = nullptr;
};
template <class T>
class FlashDialog: public FullScreenDialog
+57 -8
View File
@@ -27,6 +27,8 @@
#include "topbar.h"
#include "libopenui.h"
#include "layouts/layout_factory_impl.h"
#include "theme_manager.h"
#include "file_preview.h"
#define SET_DIRTY() storageDirty(EE_MODEL)
@@ -265,14 +267,7 @@ ScreenUserInterfacePage::ScreenUserInterfacePage(ScreenMenu* menu):
void ScreenUserInterfacePage::build(FormWindow * window)
{
FormGridLayout grid;
// Theme choice
new StaticText(window, grid.getLabelSlot(), STR_THEME, 0, COLOR_THEME_PRIMARY1);
// TODO: Theme picklist
grid.nextLine();
// Theme options ?
FormGridLayout grid(LCD_W, 10);
// Top Bar
new StaticText(window, grid.getLabelSlot(), STR_TOP_BAR, 0, COLOR_THEME_PRIMARY1);
@@ -283,6 +278,60 @@ void ScreenUserInterfacePage::build(FormWindow * window)
new SetupTopBarWidgetsPage(menu);
return 0;
});
grid.nextLine();
grid.spacer(8);
// Theme choice
new StaticText(window, grid.getLabelSlot(), STR_THEME, 0, COLOR_THEME_PRIMARY1);
auto tp = ThemePersistance::instance();
tp->refresh();
std::vector<std::string> names = tp->getNames();
new Choice(window, grid.getFieldSlot(), names, 0, names.size() - 1,
[=] () {
return tp->getThemeIndex();
},
[=] (int value) {
tp->setThemeIndex(value);
tp->applyTheme(value);
tp->setDefaultTheme(value);
window->clear();
build(window);
});
bool bNarrowScreen = LCD_W < LCD_H;
if (bNarrowScreen)
grid.setLabelWidth(LCD_W);
grid.nextLine();
auto theme = tp->getCurrentTheme();
auto themeImage = theme->getThemeImageFileName();
grid.spacer(8);
new StaticText(window, grid.getLabelSlot(), "Author", 0, COLOR_THEME_PRIMARY1 | FONT(BOLD));
grid.nextLine();
new StaticText(window, grid.getLabelSlot(), theme->getAuthor(), 0, COLOR_THEME_PRIMARY1);
grid.nextLine();
new StaticText(window, grid.getLabelSlot(), "Description", 0, COLOR_THEME_PRIMARY1 | FONT(BOLD));
grid.nextLine();
int charBreak = bNarrowScreen ? 40 : 30;
auto info = wrap(theme->getInfo(), charBreak);
rect_t r = grid.getLabelSlot();
r.h += 50;
new StaticText(window, r, info, 0, COLOR_THEME_PRIMARY1);
rect_t previewRect = bNarrowScreen ?
rect_t {0, r.x + r.h, LCD_W- 12, window->height()} :
rect_t {LCD_W / 2 + 6, 30, LCD_W / 2 - 12, window->height()};
auto preview = new FilePreview(window, previewRect);
preview->setFile(themeImage.c_str());
window->setInnerHeight(grid.getWindowHeight());
}
ScreenAddPage::ScreenAddPage(ScreenMenu * menu, uint8_t pageIndex):
+132
View File
@@ -0,0 +1,132 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include <stdlib.h>
#include <string.h>
#include <vector>
#include <list>
#include "str_functions.h"
static char splitChars[] = { ' ', '-', '\t' };
char *trim(char *str)
{
char *end;
// Trim leading space
while(isspace((unsigned char)*str)) str++;
if(*str == 0) // All spaces?
return str;
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace((unsigned char)*end)) end--;
// Write new null terminator character
end[1] = '\0';
return str;
}
std::string trim_start(std::string str)
{
uint32_t i;
for (i = 0; i < str.length(); i++)
if (!iswspace(str[i]))
break;
return str.substr(i);
}
std::vector<std::string> Explode(std::string str, char *splitChars)
{
std::vector<std::string> parts;
int startIndex = 0;
while (true)
{
size_t index = str.find_first_of(splitChars, startIndex);
if (index == std::string::npos)
{
parts.emplace_back(str.substr(startIndex));
return parts;
}
std::string word = str.substr(startIndex, index - startIndex);
char nextChar = str.substr(index, 1)[0];
// Dashes and the likes should stick to the word occuring before it. Whitespace doesn't have to.
if (iswspace(nextChar))
{
parts.emplace_back(word);
parts.emplace_back(std::string(&nextChar, 1));
}
else
{
parts.emplace_back(word + nextChar);
}
startIndex = index + 1;
}
}
std::string wrap(std::string str, uint32_t width)
{
std::vector<std::string> words = Explode(str, splitChars);
uint32_t curLineLength = 0;
std::ostringstream strBuilder;
for(uint32_t i = 0; i < words.size(); i++)
{
std::string word = words[i];
// If adding the new word to the current line would be too long,
// then put it on a new line (and split it up if it's too long).
if (curLineLength + word.length() > width)
{
// Only move down to a new line if we have text on the current line.
// Avoids situation where wrapped whitespace causes emptylines in text.
if (curLineLength > 0)
{
strBuilder << "\n";
curLineLength = 0;
}
// If the current word is too long to fit on a line even on it's own then
// split the word up.
while (word.length() > width)
{
strBuilder << word.substr(0, width - 1) + "-";
word = word.substr(width - 1);
strBuilder << "\n";
}
// Remove leading whitespace from the word so the new line starts flush to the left.
word = trim_start(word);
}
strBuilder << word;
curLineLength += word.length();
}
return strBuilder.str();
}
+29
View File
@@ -0,0 +1,29 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#pragma once
#include <wctype.h>
#include <string>
#include <iostream>
#include <sstream>
extern char *trim(char *str);
extern std::string trim_start(std::string str);
extern std::string wrap(std::string str, uint32_t width);
+383
View File
@@ -0,0 +1,383 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include "theme_manager.h"
ThemePersistance themePersistance;
#define COLOR_COUNT 12
static const char *conversionArray[COLOR_COUNT] = {
"DEFAULT", "PRIMARY1", "PRIMARY2", "PRIMARY3",
"SECONDARY1", "SECONDARY2", "SECONDARY3", "FOCUS",
"EDIT", "ACTIVE", "WARNING", "DISABLED",
};
constexpr const char *RGBSTRING = "RGB(";
constexpr const char *THEMES = "THEMES";
char *getWorkingDirectory()
{
static char path[FF_MAX_LFN + 1]; // TODO optimize that!
f_getcwd((TCHAR *)path, FF_MAX_LFN);
if (path[strlen(path) - 1] != '/') strncat(path, "/", FF_MAX_LFN);
strncat(path, THEMES, FF_MAX_LFN);
return path;
}
std::string ThemeFile::getThemeImageFileName()
{
char fullPath[FF_MAX_LFN + 1];
strncpy(fullPath, getWorkingDirectory(), FF_MAX_LFN);
if (fullPath[strlen(fullPath) - 1] != '/')
strncat(fullPath, "/", FF_MAX_LFN);
auto found = path.find('.');
if (found != std::string::npos) {
auto baseFileName(fullPath + path.substr(0, found) + ".png");
return baseFileName;
}
return "";
}
void ThemeFile::scanFile()
{
char line[256 + 1];
char fullPath[FF_MAX_LFN + 1];
ScanState scanState = none;
strncpy(fullPath, getWorkingDirectory(), FF_MAX_LFN);
if (fullPath[strlen(fullPath) - 1] != '/') strncat(fullPath, "/", FF_MAX_LFN);
strncat(fullPath, path.c_str(), FF_MAX_LFN);
FRESULT result = f_open(&file, fullPath, FA_OPEN_EXISTING | FA_READ);
if (result != FR_OK) return;
int lineNo = 1;
while (readNextLine(line, 256)) {
int len = strlen(line);
if (len == 0) continue;
if (lineNo == 1 && len != 3 && strcmp(line, "---") != 0) {
TRACE("invalid yml file at line %d", lineNo);
return;
} else if (lineNo != 1) {
if (line[0] != ' ' && line[0] != '\t') {
char *pline = trim(line);
if (line[strlen(line) - 1] != ':') {
TRACE("invalid yml file at line %d", lineNo);
return;
}
if (strcmp(pline, "colors:") == 0) {
scanState = colors;
continue;
} else if (strcmp(pline, "summary:") == 0) {
scanState = summary;
continue;
}
}
const char *ptr = strchr(line, ':');
if (!ptr) continue;
char lvalue[64];
char rvalue[64];
char *plvalue;
char *prvalue;
strncpy(lvalue, line, min((int)(ptr - line), 63));
lvalue[ptr - line] = '\0';
strncpy(rvalue, ptr + 1, 63);
plvalue = trim(lvalue);
prvalue = trim(rvalue);
switch (scanState) {
case colors: {
int colorIndex = findColorIndex(plvalue);
if (colorIndex >= 0) {
uint32_t color;
if (convertRGB(prvalue, color))
colorList.emplace_back(ColorEntry{(LcdColorIndex)colorIndex, color});
else
TRACE("Theme: Could not convert color value");
}
} break;
case summary: {
if (strcmp(plvalue, "name") == 0)
name = prvalue;
else if (strcmp(plvalue, "author") == 0)
author = prvalue;
else if (strcmp(plvalue, "info") == 0)
info = prvalue;
} break;
}
}
lineNo++;
}
f_close(&file);
}
#define HEX_COLOR_VALUE_LEN 8
bool ThemeFile::convertRGB(char *pColorRGB, uint32_t &color)
{
if (strlen(pColorRGB) < strlen(RGBSTRING)) return false;
if (strncmp(pColorRGB, RGBSTRING, strlen(RGBSTRING)) == 0) {
if (pColorRGB[strlen(pColorRGB) - 1] != ')') return false;
pColorRGB[strlen(pColorRGB) - 1] = '\0';
pColorRGB += strlen(RGBSTRING);
if (strlen(pColorRGB)) {
char *token = nullptr;
int numTokens = 0;
uint32_t tokens[3];
token = strtok(pColorRGB, ",");
while (token != nullptr) {
if (numTokens < 3) {
tokens[numTokens] = strtol(token, nullptr, 0);
numTokens++;
} else
break;
token = strtok(nullptr, ",");
}
if (numTokens != 3) return false;
color = RGB(tokens[0], tokens[1], tokens[2]);
return true;
}
} else if (pColorRGB[0] == '0' && (pColorRGB[1] == 'x' || pColorRGB[1] == 'X')) {
if (strlen(pColorRGB) != HEX_COLOR_VALUE_LEN) return false;
pColorRGB += 2;
uint32_t tokens[3];
char hexVal[3];
for (int i = 0; i < 3; i++) {
strncpy(hexVal, pColorRGB, 2);
hexVal[2] = '\0';
tokens[i] = strtol(hexVal, nullptr, 16);
pColorRGB += 2;
}
color = RGB(tokens[0], tokens[1], tokens[2]);
return true;
}
TRACE("Theme: Invalid color value");
return false;
}
LcdColorIndex ThemeFile::findColorIndex(const char *name)
{
int i;
for (i = 0; i < COLOR_COUNT; i++) {
if (strcmp(name, conversionArray[i]) == 0) break;
}
if (i >= COLOR_COUNT) return DEFAULT_COLOR_INDEX;
return (LcdColorIndex)i;
}
bool ThemeFile::readNextLine(char *line, int maxlen)
{
if (f_gets(line, maxlen, &file) != NULL) {
int curlen = strlen(line) - 1;
if (line[curlen] ==
'\n') { // remove unwanted chars if file was edited using windows
if (line[curlen - 1] == '\r') {
line[curlen - 1] = 0;
} else {
line[curlen] = 0;
}
}
return true;
}
return false;
}
void ThemeFile::setColor(LcdColorIndex colorIndex, uint32_t color)
{
if (colorIndex >= DEFAULT_COLOR_INDEX && colorIndex < LCD_COLOR_COUNT) {
colorList.emplace_back(ColorEntry {colorIndex, color});
}
}
void ThemeFile::applyTheme()
{
for (auto color: colorList) {
lcdColorTable[color.colorNumber] = color.colorValue;
}
OpenTxTheme::instance()->update(false);
}
void ThemePersistance::scanForThemes()
{
TRACE("in scanForThemes");
themes.clear();
DIR dir;
FILINFO fno;
char fullPath[FF_MAX_LFN + 1];
strcpy(fullPath, "./");
strcat(fullPath, THEMES);
TRACE("opening directory: %s", fullPath);
FRESULT res = f_opendir(&dir, fullPath); // Open the directory
if (res == FR_OK) {
TRACE("scanForThemes: open successful");
// read all entries
bool firstTime = true;
for (;;) {
res = sdReadDir(&dir, &fno, firstTime);
if (res != FR_OK || fno.fname[0] == 0)
break; // Break on error or end of dir
if (strlen((const char *)fno.fname) > SD_SCREEN_FILE_LENGTH) continue;
if (fno.fattrib & AM_DIR) continue;
TRACE("scanForThemes: found file %s", fno.fname);
std::string fname(fno.fname);
auto found = fname.find('.');
if (found != std::string::npos) {
if (fname.substr(found) != ".yml") continue;
}
themes.emplace_back(new ThemeFile(fno.fname));
}
f_closedir(&dir);
}
}
void ThemePersistance::loadDefaultTheme()
{
FIL file;
char fullPath[128];
strcpy(fullPath, getWorkingDirectory());
if (fullPath[strlen(fullPath) - 1] != '/')
strcat(fullPath, "/");
strcat(fullPath, "selectedtheme.txt");
FRESULT status = f_open(&file, fullPath, FA_READ);
if (status != FR_OK) return;
char line[256];
uint len;
status = f_read(&file, line, 256, &len);
if (status == FR_OK) {
refresh();
line[len] = '\0';
int index = 0;
for (auto theme : themes) {
if (theme->getPath() == std::string(line)) {
applyTheme(index);
setThemeIndex(index);
}
index++;
}
}
f_close(&file);
}
void ThemePersistance::deleteDefaultTheme()
{
char fullPath[128];
strcpy(fullPath, getWorkingDirectory());
if (fullPath[strlen(fullPath) - 1] != '/')
strcat(fullPath, "/");
strcat(fullPath, "selectedtheme.txt");
FIL file;
FRESULT status = f_open(&file, fullPath, FA_CREATE_ALWAYS | FA_WRITE);
if (status == FR_OK) f_close(&file);
}
void ThemePersistance::setDefaultTheme(int index)
{
char fullPath[128];
strcpy(fullPath, getWorkingDirectory());
if (fullPath[strlen(fullPath) - 1] != '/')
strcat(fullPath, "/");
strcat(fullPath, "selectedtheme.txt");
FIL file;
auto theme = themes[index];
FRESULT status = f_open(&file, fullPath, FA_CREATE_ALWAYS | FA_WRITE);
if (status != FR_OK) return;
f_printf(&file, theme->getPath().c_str());
f_close(&file);
}
class DefaultEdgeTxTheme : public ThemeFile
{
public:
DefaultEdgeTxTheme() : ThemeFile("")
{
setName("EdgeTX Default");
setAuthor("EdgeTX Team");
setInfo("Default EdgeTX Color Scheme");
}
void applyTheme() override
{
lcdColorTable[DEFAULT_COLOR_INDEX] = RGB(18, 94, 153);
lcdColorTable[COLOR_THEME_PRIMARY1_INDEX] = RGB(0, 0, 0);
lcdColorTable[COLOR_THEME_PRIMARY2_INDEX] = RGB(255, 255, 255);
lcdColorTable[COLOR_THEME_PRIMARY3_INDEX] = RGB(12, 63, 102);
lcdColorTable[COLOR_THEME_SECONDARY1_INDEX] = RGB(18, 94, 153);
lcdColorTable[COLOR_THEME_SECONDARY2_INDEX] = RGB(182, 224, 242);
lcdColorTable[COLOR_THEME_SECONDARY3_INDEX] = RGB(228, 238, 242);
lcdColorTable[COLOR_THEME_FOCUS_INDEX] = RGB(20, 161, 229);
lcdColorTable[COLOR_THEME_EDIT_INDEX] = RGB(0, 153, 9);
lcdColorTable[COLOR_THEME_ACTIVE_INDEX] = RGB(255, 222, 0);
lcdColorTable[COLOR_THEME_WARNING_INDEX] = RGB(224, 0, 0);
lcdColorTable[COLOR_THEME_DISABLED_INDEX] = RGB(140, 140, 140);
lcdColorTable[CUSTOM_COLOR_INDEX] = RGB(170, 85, 0);
OpenTxTheme::instance()->update(false);
}
std::string getThemeImageFileName() override
{
return "/THEMES/EdgeTX.png";
}
};
void ThemePersistance::insertDefaultTheme()
{
auto themeFile = new DefaultEdgeTxTheme();
themes.insert(themes.begin(), themeFile);
}
+134
View File
@@ -0,0 +1,134 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#pragma once
#include <stdlib.h>
#include <list>
#include "sdcard.h"
#include "colors.h"
#include "str_functions.h"
class ThemePersistance;
extern char * getWorkingDirectory();
extern ThemePersistance themePersistance;
struct ColorEntry
{
LcdColorIndex colorNumber;
uint32_t colorValue;
};
class ThemeFile
{
public:
ThemeFile(std::string themePath) :
path(themePath)
{
if (themePath.size()) {
scanFile();
}
}
std::string getPath() { return path; }
std::string getName() { return name; }
std::string getAuthor() { return author; }
std::string getInfo() { return info; }
void setName(std::string name) { this->name = name; }
void setAuthor(std::string author) { this->author = author; }
void setInfo(std::string info) { this->info = info; }
std::vector<ColorEntry> getColorList() { return colorList; }
void setColor(LcdColorIndex colorIndex, uint32_t color);
virtual std::string getThemeImageFileName();
virtual void applyTheme();
protected:
FIL file;
std::string path;
std::string name;
std::string author;
std::string info;
std::vector<ColorEntry> colorList;
enum ScanState
{
none,
summary,
colors
};
void scanFile();
bool convertRGB(char *pColorRGB, uint32_t &color);
LcdColorIndex findColorIndex(const char *name);
bool readNextLine(char * line, int maxlen);
};
class ThemePersistance
{
public:
ThemePersistance()
{
}
static ThemePersistance *instance() {
return &themePersistance;
}
void loadDefaultTheme();
void setDefaultTheme(int index);
void deleteDefaultTheme();
std::vector<std::string> getNames()
{
std::vector<std::string> names;
for (auto theme:themes) {
names.emplace_back(theme->getName());
}
return names;
}
void applyTheme(int index)
{
auto theme = themes[index];
theme->applyTheme();
}
inline int getThemeIndex() {return currentTheme;}
inline void setThemeIndex(int index) { currentTheme = index;}
inline ThemeFile* getCurrentTheme() { return themes[currentTheme]; }
void refresh()
{
scanForThemes();
insertDefaultTheme();
}
protected:
std::vector<ThemeFile *> themes;
int currentTheme = 0;
void scanForThemes();
void insertDefaultTheme();
};
@@ -22,6 +22,7 @@
#include "opentx.h"
#include "tabsgroup.h"
#include "480_bitmaps.h"
#include "theme_manager.h"
const ZoneOption OPTIONS_THEME_DEFAULT[] = {
{ STR_BACKGROUND_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(WHITE) },
@@ -281,6 +282,7 @@ class Theme480: public OpenTxTheme
void load() const override
{
loadColors();
ThemePersistance::instance()->loadDefaultTheme();
OpenTxTheme::load();
if (!backgroundBitmap) {
backgroundBitmap = BitmapBuffer::loadBitmap(getFilePath("background.png"));