Theme editor tweaks (#1066)
* 1. fixed color of lists to match menu popup as per JimB 2. added more controls to PreviewWindow() of theme editor * 1. fixed issue with touch. in theme list now if you touch, you must long press to get menu. Enter works on short press 2. changed directory structure to new format, as per Issue #897 3. sorted theme list alphabetically and not by random order that the directory comes back in * 1. make background image work correctly for new theme folder structure 2. sort theme names * 1. fixed issues with touching previewwindow 2. fixed processing exit button on ThemeEditPage() * fix focus issue in previewwindow * layout enhancements for nv14 * more updates to layout * fix nv14 issues. Still havnt done performance optimization yet. Lets merge first. Co-authored-by: kkoenig1@optonline.com <kkoenig1@optonline.com>
This commit is contained in:
co-authored by
kkoenig1@optonline.com <kkoenig1@optonline.com>
parent
9a795c4126
commit
a9abf028f7
@@ -80,17 +80,15 @@ void ListBase::drawLine(BitmapBuffer *dc, const rect_t &rect, uint32_t index, Lc
|
||||
void ListBase::paint(BitmapBuffer *dc)
|
||||
{
|
||||
dc->clear(COLOR_THEME_SECONDARY3);
|
||||
uint32_t bgColor = 0;
|
||||
bgColor = hasFocus() ? COLOR_THEME_FOCUS : COLOR_THEME_SECONDARY1;
|
||||
|
||||
int curY = 0;
|
||||
for (int n = 0; n < (int)names.size(); n++) {
|
||||
if (n == selected) {
|
||||
dc->drawSolidFilledRect(1, curY, rect.w - 2, lineHeight, bgColor);
|
||||
}
|
||||
//if (n == selected) {
|
||||
dc->drawSolidFilledRect(1, curY, rect.w - 2, lineHeight, n == selected ? COLOR_THEME_FOCUS : COLOR_THEME_PRIMARY2);
|
||||
//}
|
||||
|
||||
LcdFlags textColor =
|
||||
n == selected ? COLOR_THEME_PRIMARY2 : COLOR_THEME_PRIMARY1;
|
||||
n == selected ? COLOR_THEME_PRIMARY2 : COLOR_THEME_SECONDARY1;
|
||||
|
||||
auto fontHeight = getFontHeight(FONT(STD));
|
||||
drawLine(dc, { 8, curY + (lineHeight - fontHeight) / 2, rect.w, lineHeight}, n, textColor);
|
||||
|
||||
@@ -86,27 +86,43 @@ class ThemedTextEdit : public TextEdit
|
||||
class ThemedStaticText : public StaticText
|
||||
{
|
||||
public:
|
||||
using StaticText::StaticText;
|
||||
ThemedStaticText(FormGroup *window, const rect_t &rect, std::string text, LcdColorIndex colorIndex) :
|
||||
StaticText(window, rect, text, 0, COLOR(colorIndex)),
|
||||
_colorIndex(colorIndex)
|
||||
{
|
||||
}
|
||||
|
||||
void paint(BitmapBuffer *dc) override
|
||||
{
|
||||
colorMaintainer.applyColorValues();
|
||||
setTextFlags(COLOR_THEME_PRIMARY1);
|
||||
setTextFlags(COLOR(_colorIndex));
|
||||
StaticText::paint(dc);
|
||||
colorMaintainer.restoreColorValues();
|
||||
}
|
||||
|
||||
protected:
|
||||
LcdColorIndex _colorIndex;
|
||||
};
|
||||
|
||||
class ThemedCheckBox : public CheckBox
|
||||
{
|
||||
public:
|
||||
ThemedCheckBox(Window *parent, rect_t rect, bool checked) :
|
||||
CheckBox(
|
||||
parent, rect, [=]() { return checked; }, [](uint8_t value) {}),
|
||||
CheckBox(parent, rect, [=]() { return checked; }, [](uint8_t value) {}, NO_FOCUS),
|
||||
checked(checked)
|
||||
{
|
||||
enable(false);
|
||||
setFocusHandler([] (bool focus) {
|
||||
});
|
||||
}
|
||||
|
||||
#if defined(HARDWARE_KEYS)
|
||||
void onEvent(event_t event) override
|
||||
{
|
||||
return parent->onEvent(event);
|
||||
}
|
||||
#endif
|
||||
|
||||
void paint(BitmapBuffer *dc) override
|
||||
{
|
||||
colorMaintainer.applyColorValues();
|
||||
@@ -120,14 +136,14 @@ class ThemedCheckBox : public CheckBox
|
||||
|
||||
class ThemedMainViewHorizontalTrim : public MainViewHorizontalTrim
|
||||
{
|
||||
public:
|
||||
using MainViewHorizontalTrim::MainViewHorizontalTrim;
|
||||
void paint(BitmapBuffer *dc) override
|
||||
{
|
||||
colorMaintainer.applyColorValues();
|
||||
MainViewHorizontalTrim::paint(dc);
|
||||
colorMaintainer.restoreColorValues();
|
||||
}
|
||||
public:
|
||||
using MainViewHorizontalTrim::MainViewHorizontalTrim;
|
||||
void paint(BitmapBuffer *dc) override
|
||||
{
|
||||
colorMaintainer.applyColorValues();
|
||||
MainViewHorizontalTrim::paint(dc);
|
||||
colorMaintainer.restoreColorValues();
|
||||
}
|
||||
};
|
||||
|
||||
class ThemedMainViewHorizontalSlider : public MainViewHorizontalSlider
|
||||
@@ -142,6 +158,38 @@ class ThemedMainViewHorizontalSlider : public MainViewHorizontalSlider
|
||||
}
|
||||
};
|
||||
|
||||
class ThemedButton : public TextButton
|
||||
{
|
||||
public:
|
||||
ThemedButton(FormGroup *window, const rect_t &rect, std::string text, bool isChecked, WindowFlags windowFlags,
|
||||
LcdColorIndex colorIndex) :
|
||||
TextButton(window, rect, text, nullptr, windowFlags | NO_FOCUS, COLOR(colorIndex)),
|
||||
_colorIndex(colorIndex),
|
||||
_isChecked(isChecked)
|
||||
{
|
||||
setPressHandler([=] () { return _isChecked; });
|
||||
}
|
||||
|
||||
#if defined(HARDWARE_KEYS)
|
||||
void onEvent(event_t event) override
|
||||
{
|
||||
parent->onEvent(event);
|
||||
}
|
||||
#endif
|
||||
|
||||
void paint(BitmapBuffer *dc) override
|
||||
{
|
||||
colorMaintainer.applyColorValues();
|
||||
setTextFlags(COLOR(_colorIndex));
|
||||
TextButton::paint(dc);
|
||||
colorMaintainer.restoreColorValues();
|
||||
}
|
||||
protected:
|
||||
LcdColorIndex _colorIndex;
|
||||
bool _isChecked = true;
|
||||
};
|
||||
|
||||
|
||||
// display controls using the appropriate theme.
|
||||
class PreviewWindow : public FormGroup
|
||||
{
|
||||
@@ -150,14 +198,14 @@ class PreviewWindow : public FormGroup
|
||||
std::vector<ColorEntry> colorList) :
|
||||
FormGroup(window, rect, NO_FOCUS), _colorList(colorList)
|
||||
{
|
||||
new ThemedStaticText(this, {5, 40, 100, LINE_HEIGHT}, "Checkbox", 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
new ThemedStaticText(this, {5, 40, 100, LINE_HEIGHT}, "Checkbox", COLOR_THEME_PRIMARY1_INDEX);
|
||||
new ThemedCheckBox(this, {100 + 15, 40, 20, LINE_HEIGHT}, true);
|
||||
new ThemedCheckBox(this, {140 + 15, 40, 20, LINE_HEIGHT}, false);
|
||||
new ThemedMainViewHorizontalTrim(this,
|
||||
{5, 65, HORIZONTAL_SLIDERS_WIDTH, 20}, 0);
|
||||
new ThemedMainViewHorizontalSlider(
|
||||
this, {5, 87, HORIZONTAL_SLIDERS_WIDTH, 20}, 0);
|
||||
new ThemedMainViewHorizontalTrim(this, {5, 65, HORIZONTAL_SLIDERS_WIDTH, 20}, 0);
|
||||
new ThemedMainViewHorizontalSlider(this, {5, 87, HORIZONTAL_SLIDERS_WIDTH, 20}, 0);
|
||||
new ThemedButton(this, {5, 109, 100, LINE_HEIGHT + 10}, "Checked", true, BUTTON_CHECKED, COLOR_THEME_PRIMARY1_INDEX);
|
||||
new ThemedButton(this, {110, 109, 100, LINE_HEIGHT + 10}, "Regular", false, 0, COLOR_THEME_PRIMARY1_INDEX);
|
||||
new ThemedStaticText(this, {5, 145, 100, LINE_HEIGHT}, "Warning Text", COLOR_THEME_WARNING_INDEX);
|
||||
ticks = getTicks();
|
||||
}
|
||||
|
||||
@@ -214,26 +262,23 @@ class PreviewWindow : public FormGroup
|
||||
dc->clear(COLOR_THEME_SECONDARY3);
|
||||
|
||||
// top bar background
|
||||
dc->drawSolidFilledRect(0, 0, rect.w, TOPBAR_HEIGHT,
|
||||
COLOR_THEME_SECONDARY1);
|
||||
dc->drawSolidFilledRect(0, 0, rect.w, TOPBAR_HEIGHT, COLOR_THEME_SECONDARY1);
|
||||
|
||||
int width;
|
||||
int x = 5;
|
||||
// topbar icons
|
||||
auto bm = getBitmap(mask_menu_radio, COLOR_THEME_SECONDARY1,
|
||||
COLOR_THEME_PRIMARY2, &width);
|
||||
auto bm = getBitmap(mask_menu_radio, COLOR_THEME_SECONDARY1, COLOR_THEME_PRIMARY2, &width);
|
||||
dc->drawBitmap(x, 5, bm);
|
||||
x += MENU_HEADER_BUTTON_WIDTH + 2;
|
||||
delete bm;
|
||||
|
||||
bm = getBitmap(mask_radio_tools, COLOR_THEME_SECONDARY1,
|
||||
COLOR_THEME_PRIMARY2, &width);
|
||||
dc->drawSolidFilledRect(x - 2, 0, MENU_HEADER_BUTTON_WIDTH + 2, TOPBAR_HEIGHT, COLOR_THEME_FOCUS);
|
||||
bm = getBitmap(mask_radio_tools, COLOR_THEME_FOCUS, COLOR_THEME_PRIMARY2, &width);
|
||||
dc->drawBitmap(x, 5, bm);
|
||||
x += MENU_HEADER_BUTTON_WIDTH + 2;
|
||||
delete bm;
|
||||
|
||||
bm = getBitmap(mask_radio_setup, COLOR_THEME_SECONDARY1,
|
||||
COLOR_THEME_PRIMARY2, &width);
|
||||
bm = getBitmap(mask_radio_setup, COLOR_THEME_SECONDARY1, COLOR_THEME_PRIMARY2, &width);
|
||||
dc->drawBitmap(x, 5, bm);
|
||||
delete bm;
|
||||
|
||||
|
||||
@@ -40,11 +40,15 @@
|
||||
#define COLOR_PREVIEW_HEIGHT (LCD_H - TOPBAR_HEIGHT - 30)
|
||||
#define LEFT_LIST_WIDTH (LCD_W / 2) - COLOR_PREVIEW_WIDTH
|
||||
#define LEFT_LIST_HEIGHT (LCD_H - TOPBAR_HEIGHT - 37)
|
||||
#define COLOR_LIST_WIDTH ((LCD_W * 3)/10)
|
||||
#define COLOR_LIST_HEIGHT (LCD_H - TOPBAR_HEIGHT)
|
||||
#else
|
||||
#define COLOR_PREVIEW_WIDTH 18
|
||||
#define COLOR_PREVIEW_HEIGHT (LCD_H - TOPBAR_HEIGHT - 30)
|
||||
#define COLOR_PREVIEW_WIDTH LCD_W
|
||||
#define COLOR_PREVIEW_HEIGHT 18
|
||||
#define LEFT_LIST_WIDTH LCD_W
|
||||
#define LEFT_LIST_HEIGHT (LCD_H / 2 - 38)
|
||||
#define COLOR_LIST_WIDTH LCD_W
|
||||
#define COLOR_LIST_HEIGHT (LCD_H / 2 - 38)
|
||||
#endif
|
||||
|
||||
#define MARGIN_WIDTH 5
|
||||
@@ -60,8 +64,8 @@ constexpr int COLOR_BOX_LEFT = 3;
|
||||
constexpr int COLOR_BOX_WIDTH = 45;
|
||||
constexpr int COLOR_BOX_HEIGHT = 27;
|
||||
#else
|
||||
constexpr int BUTTON_HEIGHT = 25;
|
||||
constexpr int BUTTON_WIDTH = 50;
|
||||
constexpr int BUTTON_HEIGHT = 30;
|
||||
constexpr int BUTTON_WIDTH = 65;
|
||||
constexpr LcdFlags textFont = FONT(XS);
|
||||
constexpr rect_t detailsDialogRect = {5, 50, LCD_W - 10, 340};
|
||||
constexpr int labelWidth = 120;
|
||||
@@ -74,8 +78,6 @@ constexpr int COLOR_BUTTON_WIDTH = COLOR_BOX_WIDTH;
|
||||
constexpr int COLOR_BUTTON_HEIGHT = 20;
|
||||
#endif
|
||||
|
||||
#define COLOR_LIST_WIDTH ((LCD_W * 3)/10)
|
||||
#define COLOR_LIST_HEIGHT (LCD_H - TOPBAR_HEIGHT)
|
||||
|
||||
|
||||
class ThemeDetailsDialog: public Dialog
|
||||
@@ -227,7 +229,11 @@ protected:
|
||||
_hexBox = new StaticText(window, r, "", 0, COLOR_THEME_PRIMARY1 | FONT(L) | RIGHT);
|
||||
setHexStr(_hexBox, _theme.getColorEntryByIndex(_indexOfColor)->colorValue);
|
||||
|
||||
r = { COLOR_BOX_LEFT, COLOR_BOX_HEIGHT + 7, COLOR_LIST_WIDTH, window->height() - COLOR_BOX_HEIGHT - 7};
|
||||
if (LCD_W > LCD_H)
|
||||
r = { COLOR_BOX_LEFT, COLOR_BOX_HEIGHT + 7, COLOR_LIST_WIDTH, window->height() - COLOR_BOX_HEIGHT - 7};
|
||||
else
|
||||
r = { COLOR_BOX_LEFT, COLOR_BOX_HEIGHT + 7, COLOR_LIST_WIDTH, LCD_H / 2 - COLOR_BOX_HEIGHT - 20 };
|
||||
|
||||
_colorEditor = new ColorEditor(window, r, _theme.getColorEntryByIndex(_indexOfColor)->colorValue,
|
||||
[=](uint32_t rgb) {
|
||||
_theme.setColor(_indexOfColor, rgb);
|
||||
@@ -320,6 +326,7 @@ class ThemeEditPage : public Page
|
||||
focus->getNextField()->setFocus(SET_FOCUS_FORWARD, focus);
|
||||
}
|
||||
} else if (event == EVT_KEY_FIRST(KEY_EXIT)) {
|
||||
killEvents(event);
|
||||
deleteLater();
|
||||
} else {
|
||||
Window::onEvent(event);
|
||||
@@ -479,18 +486,15 @@ void ThemeSetupPage::displayThemeMenu(Window *window, ThemePersistance *tp)
|
||||
|
||||
new ThemeDetailsDialog(window, newTheme, [=] (ThemeFile theme) {
|
||||
if (strlen(theme.getName()) != 0) {
|
||||
char path[NAME_LENGTH + 20];
|
||||
strncpy(path, theme.getName(), NAME_LENGTH + 19);
|
||||
removeAllWhiteSpace(path);
|
||||
strncat(path, ".yml", NAME_LENGTH + 19);
|
||||
theme.setPath(path);
|
||||
|
||||
char name[NAME_LENGTH + 20];
|
||||
strncpy(name, theme.getName(), NAME_LENGTH + 19);
|
||||
removeAllWhiteSpace(name);
|
||||
// use the current themes color list to make the new theme
|
||||
auto curTheme = tp->getCurrentTheme();
|
||||
for (auto color : curTheme->getColorList())
|
||||
theme.setColor(color.colorNumber, color.colorValue);
|
||||
|
||||
theme.serialize();
|
||||
tp->createNewTheme(name, theme);
|
||||
tp->refresh();
|
||||
listBox->setNames(tp->getNames());
|
||||
listBox->setSelected(currentTheme);
|
||||
@@ -504,7 +508,7 @@ void ThemeSetupPage::displayThemeMenu(Window *window, ThemePersistance *tp)
|
||||
if (confirmationDialog("Delete Theme?", tp->getThemeByIndex(listBox->getSelected())->getName())) {
|
||||
tp->deleteThemeByIndex(listBox->getSelected());
|
||||
listBox->setNames(tp->getNames());
|
||||
listBox->setSelected(currentTheme);
|
||||
listBox->setSelected(min<int>(currentTheme, tp->getNames().size() - 1));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -530,7 +534,10 @@ void ThemeSetupPage::setupListbox(FormWindow *window, rect_t r, ThemePersistance
|
||||
listBox->setActiveIndex(tp->getThemeIndex());
|
||||
listBox->setTitle(STR_THEME + std::string("s"));
|
||||
listBox->setLongPressHandler([=] (event_t event) { displayThemeMenu(window, tp); });
|
||||
listBox->setPressHandler([=] (event_t event) { displayThemeMenu(window, tp); });
|
||||
listBox->setPressHandler([=] (event_t event) {
|
||||
if (event != 0)
|
||||
displayThemeMenu(window, tp);
|
||||
});
|
||||
}
|
||||
|
||||
void ThemeSetupPage::build(FormWindow *window)
|
||||
@@ -549,30 +556,37 @@ void ThemeSetupPage::build(FormWindow *window)
|
||||
rect_t r = { 2, 3, LEFT_LIST_WIDTH, LEFT_LIST_HEIGHT };
|
||||
setupListbox(window, r, tp);
|
||||
|
||||
rect_t colorPreviewRect;
|
||||
if (LCD_W > LCD_H) {
|
||||
r.x = LEFT_LIST_WIDTH + MARGIN_WIDTH;
|
||||
r.w = LCD_W - r.x;
|
||||
colorPreviewRect = {LEFT_LIST_WIDTH + 6, 0, COLOR_PREVIEW_WIDTH, COLOR_PREVIEW_HEIGHT};
|
||||
} else {
|
||||
r.y += LEFT_LIST_HEIGHT + 4;
|
||||
r.x = 0;
|
||||
r.w = LCD_W;
|
||||
colorPreviewRect = {0, LEFT_LIST_HEIGHT + 6, COLOR_PREVIEW_WIDTH, COLOR_PREVIEW_HEIGHT};
|
||||
}
|
||||
|
||||
// setup ThemeColorPreview()
|
||||
auto colorList = theme != nullptr ? theme->getColorList() : std::vector<ColorEntry>();
|
||||
rect_t colorPreviewRect = {LEFT_LIST_WIDTH + 6, 0, COLOR_PREVIEW_WIDTH, COLOR_PREVIEW_HEIGHT};
|
||||
themeColorPreview = new ThemeColorPreview(window, colorPreviewRect, colorList);
|
||||
|
||||
// setup FileCarosell()
|
||||
r.h = 130;
|
||||
r.w -= COLOR_PREVIEW_WIDTH;
|
||||
r.x += COLOR_PREVIEW_WIDTH;
|
||||
if (LCD_W > LCD_H) {
|
||||
r.w -= COLOR_PREVIEW_WIDTH;
|
||||
r.h = 130;
|
||||
r.x += COLOR_PREVIEW_WIDTH;
|
||||
} else {
|
||||
r.y += COLOR_PREVIEW_HEIGHT;
|
||||
r.h = LEFT_LIST_HEIGHT - COLOR_PREVIEW_HEIGHT - 50;
|
||||
}
|
||||
auto fileNames = theme != nullptr ? theme->getThemeImageFileNames() : std::vector<std::string>();
|
||||
fileCarosell = new FileCarosell(window, r, fileNames, listBox);
|
||||
|
||||
// author and name of theme on right side of screen
|
||||
r.x += 7;
|
||||
r.y += 130;
|
||||
r.y += 135;
|
||||
r.h = 20;
|
||||
nameText = new StaticText(window, r, theme != nullptr ? theme->getName() : "", 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
|
||||
@@ -402,26 +402,13 @@ void ScreenUserInterfacePage::build(FormWindow * window)
|
||||
build(window);
|
||||
});
|
||||
|
||||
std::function<uint8_t ()> launchEditor = [=]() {
|
||||
auto theme = tp->getCurrentTheme();
|
||||
auto te = new ThemeEditorPage(window, theme);
|
||||
te->setCloseHandler([=] () {
|
||||
window->clear();
|
||||
build(window);
|
||||
});
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
// new TextButton(window, grid.getFieldSlot(3, 2), STR_EDIT, launchEditor, BUTTON_BACKGROUND | OPAQUE, COLOR_THEME_PRIMARY2);
|
||||
|
||||
bool bNarrowScreen = LCD_W < LCD_H;
|
||||
if (bNarrowScreen)
|
||||
grid.setLabelWidth(LCD_W);
|
||||
|
||||
grid.nextLine();
|
||||
auto theme = tp->getCurrentTheme();
|
||||
auto themeImage = theme->getThemeImageFileName();
|
||||
auto themeImage = theme->getThemeImageFileNames();
|
||||
|
||||
grid.spacer(8);
|
||||
|
||||
@@ -440,10 +427,10 @@ void ScreenUserInterfacePage::build(FormWindow * window)
|
||||
new StaticText(window, r, info, 0, COLOR_THEME_PRIMARY1);
|
||||
|
||||
rect_t previewRect = bNarrowScreen ?
|
||||
rect_t {0, r.h, LCD_W- 12, window->height()} :
|
||||
rect_t {0, r.h, LCD_W, 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());
|
||||
preview->setFile(themeImage.size() > 0 ? themeImage[0].c_str() : "");
|
||||
|
||||
window->setInnerHeight(grid.getWindowHeight());
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include "zone.h"
|
||||
#include "ffconf.h"
|
||||
#include "thirdparty/libopenui/src/theme.h"
|
||||
|
||||
class BitmapBuffer;
|
||||
@@ -82,6 +83,12 @@ class OpenTxTheme: public Theme
|
||||
|
||||
ZoneOptionValue * getOptionValue(unsigned int index) const;
|
||||
|
||||
virtual void setBackgroundImageFileName(const char *fileName)
|
||||
{
|
||||
strncpy(backgroundImageFileName, fileName, FF_MAX_LFN);
|
||||
backgroundImageFileName[FF_MAX_LFN] = '\0'; // ensure string termination
|
||||
}
|
||||
|
||||
virtual void load() const;
|
||||
|
||||
virtual void drawBackground(BitmapBuffer * dc) const;
|
||||
@@ -112,6 +119,7 @@ class OpenTxTheme: public Theme
|
||||
const char * name;
|
||||
const ZoneOption * options;
|
||||
BitmapBuffer * thumb;
|
||||
char backgroundImageFileName[FF_MAX_LFN + 1];
|
||||
|
||||
public:
|
||||
static const BitmapBuffer * error;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
#include "theme_manager.h"
|
||||
|
||||
#define MAX_FILES 20
|
||||
#define MAX_FILES 9
|
||||
ThemePersistance themePersistance;
|
||||
|
||||
static const char *colorNames[COLOR_COUNT] = {
|
||||
@@ -33,6 +33,32 @@ static const char *colorNames[COLOR_COUNT] = {
|
||||
constexpr const char *RGBSTRING = "RGB(";
|
||||
constexpr const char* SELECTED_THEME_FILE = THEMES_PATH "/selectedtheme.txt";
|
||||
|
||||
ThemeFile::ThemeFile(std::string themePath) :
|
||||
path(themePath)
|
||||
{
|
||||
if (path.size()) {
|
||||
deSerialize();
|
||||
}
|
||||
|
||||
FIL file;
|
||||
auto found = path.rfind('/');
|
||||
if (found != std::string::npos) {
|
||||
int n = 0;
|
||||
while (n < MAX_FILES) {
|
||||
auto baseFileName(path.substr(0, found + 1) + (n != 0 ? "screenshot" + std::to_string(n) : "logo") + ".png");
|
||||
FRESULT result = f_open(&file, baseFileName.c_str(), FA_OPEN_EXISTING);
|
||||
if (result == FR_OK) {
|
||||
_imageFileNames.emplace_back(baseFileName);
|
||||
f_close(&file);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThemeFile& ThemeFile::operator= (const ThemeFile& theme)
|
||||
{
|
||||
if (this == &theme)
|
||||
@@ -43,58 +69,20 @@ ThemeFile& ThemeFile::operator= (const ThemeFile& theme)
|
||||
strncpy(author, theme.author, AUTHOR_LENGTH);
|
||||
strncpy(info, theme.info, INFO_LENGTH);
|
||||
colorList.assign(theme.colorList.begin(), theme.colorList.end());
|
||||
_imageFileNames.assign(theme._imageFileNames.begin(), theme._imageFileNames.end());
|
||||
|
||||
return *this;
|
||||
};
|
||||
|
||||
std::string ThemeFile::getThemeImageFileName()
|
||||
{
|
||||
char fullPath[FF_MAX_LFN + 1];
|
||||
strncpy(fullPath, THEMES_PATH "/", FF_MAX_LFN);
|
||||
|
||||
auto found = path.rfind('.');
|
||||
if (found != std::string::npos) {
|
||||
auto baseFileName(fullPath + path.substr(0, found) + ".png");
|
||||
return baseFileName;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::vector<std::string> ThemeFile::getThemeImageFileNames()
|
||||
{
|
||||
std::vector<std::string> fileNames;
|
||||
|
||||
char fullPath[FF_MAX_LFN + 1];
|
||||
strncpy(fullPath, THEMES_PATH "/", FF_MAX_LFN);
|
||||
auto found = path.rfind('.');
|
||||
FIL file;
|
||||
if (found != std::string::npos) {
|
||||
int n = 0;
|
||||
while (n < MAX_FILES) {
|
||||
auto baseFileName(fullPath + path.substr(0, found) + (n != 0 ? std::to_string(n) : "") + ".png");
|
||||
FRESULT result = f_open(&file, baseFileName.c_str(), FA_OPEN_EXISTING);
|
||||
if (result == FR_OK) {
|
||||
fileNames.emplace_back(baseFileName);
|
||||
f_close(&file);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
return fileNames;
|
||||
return _imageFileNames;
|
||||
}
|
||||
|
||||
void ThemeFile::serialize()
|
||||
{
|
||||
char fullPath[FF_MAX_LFN + 1];
|
||||
strncpy(fullPath, THEMES_PATH "/", FF_MAX_LFN);
|
||||
strncat(fullPath, path.c_str(), FF_MAX_LFN);
|
||||
|
||||
FIL file;
|
||||
FRESULT result = f_open(&file, fullPath, FA_CREATE_ALWAYS | FA_WRITE);
|
||||
FRESULT result = f_open(&file, path.c_str(), FA_CREATE_ALWAYS | FA_WRITE);
|
||||
if (result == FR_OK) {
|
||||
f_printf(&file, "---\n");
|
||||
f_printf(&file, "summary:\n");
|
||||
@@ -121,14 +109,10 @@ void ThemeFile::serialize()
|
||||
void ThemeFile::deSerialize()
|
||||
{
|
||||
char line[256 + 1];
|
||||
char fullPath[FF_MAX_LFN + 1];
|
||||
ScanState scanState = none;
|
||||
|
||||
strncpy(fullPath, THEMES_PATH "/", FF_MAX_LFN);
|
||||
strncat(fullPath, path.c_str(), FF_MAX_LFN);
|
||||
|
||||
FIL file;
|
||||
FRESULT result = f_open(&file, fullPath, FA_OPEN_EXISTING | FA_READ);
|
||||
FRESULT result = f_open(&file, path.c_str(), FA_OPEN_EXISTING | FA_READ);
|
||||
if (result != FR_OK) return;
|
||||
|
||||
int lineNo = 1;
|
||||
@@ -301,11 +285,31 @@ void ThemeFile::setColor(LcdColorIndex colorIndex, uint32_t color)
|
||||
}
|
||||
}
|
||||
|
||||
void ThemeFile::applyTheme()
|
||||
void ThemeFile::applyColors()
|
||||
{
|
||||
for (auto color: colorList) {
|
||||
lcdColorTable[color.colorNumber] = color.colorValue;
|
||||
}
|
||||
}
|
||||
|
||||
void ThemeFile::applyBackground()
|
||||
{
|
||||
auto instance = OpenTxTheme::instance();
|
||||
std::string backgroundImageFileName(getPath());
|
||||
auto pos = backgroundImageFileName.rfind('/');
|
||||
if (pos != std::string::npos) {
|
||||
auto rootDir = backgroundImageFileName.substr(0, pos + 1);
|
||||
rootDir = rootDir + "background_" + std::to_string(LCD_W) + "x" + std::to_string(LCD_H) + ".png";
|
||||
instance->setBackgroundImageFileName((char *)rootDir.c_str());
|
||||
} else {
|
||||
instance->setBackgroundImageFileName("");
|
||||
}
|
||||
}
|
||||
|
||||
void ThemeFile::applyTheme()
|
||||
{
|
||||
applyColors();
|
||||
applyBackground();
|
||||
OpenTxTheme::instance()->update(false);
|
||||
}
|
||||
|
||||
@@ -318,7 +322,16 @@ void ThemePersistance::clearThemes()
|
||||
themes.clear();
|
||||
}
|
||||
|
||||
#define MAX_SD_FILE_LENGTH 128
|
||||
void ThemePersistance::scanThemeFolder(char *fullPath)
|
||||
{
|
||||
FIL file;
|
||||
strncat(fullPath, "/theme.yml", FF_MAX_LFN);
|
||||
FRESULT result = f_open(&file, fullPath, FA_OPEN_EXISTING | FA_READ);
|
||||
if (result != FR_OK) return;
|
||||
|
||||
TRACE("scanForThemes: found file %s", fullPath);
|
||||
themes.emplace_back(new ThemeFile(fullPath));
|
||||
}
|
||||
|
||||
void ThemePersistance::scanForThemes()
|
||||
{
|
||||
@@ -345,20 +358,20 @@ void ThemePersistance::scanForThemes()
|
||||
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 (strcasecmp(fname.substr(found).c_str(), YAML_EXT) != 0) continue;
|
||||
if (fno.fattrib & AM_DIR) {
|
||||
char themePath[FF_MAX_LFN + 1];
|
||||
strncpy(themePath, fullPath, FF_MAX_LFN);
|
||||
strncat(themePath, "/", FF_MAX_LFN);
|
||||
strncat(themePath, fno.fname, FF_MAX_LFN);
|
||||
scanThemeFolder(themePath);
|
||||
}
|
||||
else continue;
|
||||
|
||||
themes.emplace_back(new ThemeFile(fno.fname));
|
||||
}
|
||||
|
||||
f_closedir(&dir);
|
||||
std::sort(themes.begin(), themes.end(),
|
||||
[](ThemeFile *a, ThemeFile *b) {
|
||||
return strcmp(a->getName(), b->getName()) < 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,16 +415,12 @@ bool ThemePersistance::deleteThemeByIndex(int index)
|
||||
if (index > 0 && index < (int) themes.size()) {
|
||||
ThemeFile* theme = themes[index];
|
||||
|
||||
char curFile[FF_MAX_LFN + 1];
|
||||
strncpy(curFile, THEMES_PATH "/", FF_MAX_LFN);
|
||||
strncat(curFile, theme->getPath().c_str(), FF_MAX_LFN);
|
||||
|
||||
char newFile[FF_MAX_LFN + 1];
|
||||
strncpy(newFile, curFile, FF_MAX_LFN);
|
||||
strncpy(newFile, theme->getPath().c_str(), FF_MAX_LFN);
|
||||
strcat(newFile, ".deleted");
|
||||
|
||||
// for now we are just renaming the file so we don't find it
|
||||
FRESULT status = f_rename(curFile, newFile);
|
||||
FRESULT status = f_rename(theme->getPath().c_str(), newFile);
|
||||
refresh();
|
||||
|
||||
// make sure currentTheme stays in bounds
|
||||
@@ -423,6 +432,23 @@ bool ThemePersistance::deleteThemeByIndex(int index)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ThemePersistance::createNewTheme(std::string name, ThemeFile &theme)
|
||||
{
|
||||
char fullPath[FF_MAX_LFN + 1];
|
||||
strncpy(fullPath, THEMES_PATH, FF_MAX_LFN);
|
||||
fullPath[FF_MAX_LFN] = '\0';
|
||||
strncat(fullPath, "/", FF_MAX_LFN);
|
||||
strncat(fullPath, name.c_str(), FF_MAX_LFN);
|
||||
|
||||
FRESULT result = f_mkdir(fullPath);
|
||||
if (result != FR_OK) return false;
|
||||
strncat(fullPath, "/", FF_MAX_LFN);
|
||||
strncat(fullPath, "theme.yml", FF_MAX_LFN);
|
||||
theme.setPath(fullPath);
|
||||
theme.serialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ThemePersistance::deleteDefaultTheme()
|
||||
{
|
||||
FIL file;
|
||||
@@ -467,20 +493,20 @@ class DefaultEdgeTxTheme : public ThemeFile
|
||||
colorList.emplace_back(ColorEntry { COLOR_THEME_DISABLED_INDEX, RGB(140, 140, 140) });
|
||||
}
|
||||
|
||||
void applyTheme() override
|
||||
void applyBackground() override
|
||||
{
|
||||
ThemeFile::applyTheme();
|
||||
}
|
||||
|
||||
std::string getThemeImageFileName() override
|
||||
{
|
||||
return "/THEMES/EdgeTX.png";
|
||||
auto instance = OpenTxTheme::instance();
|
||||
char fileName[FF_MAX_LFN + 1];
|
||||
fileName[FF_MAX_LFN] = '\0';
|
||||
strncpy(fileName, THEMES_PATH, FF_MAX_LFN);
|
||||
strcat(fileName, "/EdgeTX/background.png");
|
||||
instance->setBackgroundImageFileName(fileName);
|
||||
}
|
||||
|
||||
std::vector<std::string> getThemeImageFileNames() override
|
||||
{
|
||||
std::vector<std::string> fileNames;
|
||||
fileNames.emplace_back("/THEMES/EdgeTX.png");
|
||||
fileNames.emplace_back("/THEMES/EdgeTX/EdgeTX.png");
|
||||
return fileNames;
|
||||
}
|
||||
};
|
||||
@@ -490,3 +516,4 @@ void ThemePersistance::insertDefaultTheme()
|
||||
auto themeFile = new DefaultEdgeTxTheme();
|
||||
themes.insert(themes.begin(), themeFile);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,13 +55,7 @@ class ThemeFile
|
||||
info[0] = '\0';
|
||||
}
|
||||
|
||||
ThemeFile(std::string themePath) :
|
||||
path(themePath)
|
||||
{
|
||||
if (themePath.size()) {
|
||||
deSerialize();
|
||||
}
|
||||
}
|
||||
ThemeFile(std::string themePath);
|
||||
ThemeFile(const ThemeFile &theme)
|
||||
{
|
||||
path = theme.path;
|
||||
@@ -117,9 +111,8 @@ class ThemeFile
|
||||
void setColor(LcdColorIndex colorIndex, uint32_t color);
|
||||
void setColorByIndex(int index, uint32_t color);
|
||||
|
||||
virtual std::string getThemeImageFileName();
|
||||
virtual std::vector<std::string> getThemeImageFileNames();
|
||||
virtual void applyTheme();
|
||||
void applyTheme();
|
||||
|
||||
protected:
|
||||
std::string path;
|
||||
@@ -127,6 +120,9 @@ class ThemeFile
|
||||
char author[AUTHOR_LENGTH + 1];
|
||||
char info[INFO_LENGTH + 1];
|
||||
std::vector<ColorEntry> colorList;
|
||||
std::vector<std::string> _imageFileNames;
|
||||
void applyColors();
|
||||
virtual void applyBackground();
|
||||
|
||||
enum ScanState
|
||||
{
|
||||
@@ -158,6 +154,7 @@ class ThemePersistance
|
||||
void deleteDefaultTheme();
|
||||
static char **getColorNames();
|
||||
bool deleteThemeByIndex(int index);
|
||||
bool createNewTheme(std::string name, ThemeFile &theme);
|
||||
|
||||
std::vector<std::string> getNames()
|
||||
{
|
||||
@@ -206,4 +203,5 @@ class ThemePersistance
|
||||
void scanForThemes();
|
||||
void insertDefaultTheme();
|
||||
void clearThemes();
|
||||
void scanThemeFolder(char *fullPath);
|
||||
};
|
||||
|
||||
@@ -186,6 +186,15 @@ class Theme480: public OpenTxTheme
|
||||
delete dot;
|
||||
}
|
||||
|
||||
void setBackgroundImageFileName(const char *fileName) override
|
||||
{
|
||||
// ensure you delete old bitmap
|
||||
if (strcmp(backgroundImageFileName, fileName) != 0 && backgroundBitmap != nullptr)
|
||||
delete backgroundBitmap;
|
||||
OpenTxTheme::setBackgroundImageFileName(fileName); // set the filename
|
||||
backgroundBitmap = BitmapBuffer::loadBitmap(backgroundImageFileName);
|
||||
}
|
||||
|
||||
void loadThemeBitmaps() const
|
||||
{
|
||||
// Calibration screen
|
||||
|
||||
Reference in New Issue
Block a user