fix(color): LVGLify themes page and fix several possible string handling issues (#3135)
* Convert theme select, theme edit and color edit pages to LVGL flex. Add 'Edit Color' string to language files (requires translations). Simplify theme edit 'onCancel' logic. Fix size of buttons in ConfirmDialog for portrait layout. Truncate item strings in ListBox to fit available space. Set window background color on Page and TabsGroup classes instead of using 'paint' function. Replace strncpy and strncat with strAppend - avoid possible unterminated strings and string overrun. Truncate display of long name and author strings in theme pages. * Revert code to truncate ListBox entry strings as it causes other issues. * Adjust layout for theme color editor to fit large hex string size. * Remove unnecessary copying of the ThemeFile object being edited. Refresh TabsGroup page background color when theme changes. * Set selected theme in list on long press as well as short press. Comment out unused 'title' in ListBox. * Handle default theme like custom themes on SD card: - show logo when selecting theme - support background_WxH.png names for background images Use f_stat( via isFileAvailable) instead of opening and closing files to check for existence. * Remove debug print. * Fix rebase.
This commit is contained in:
@@ -47,14 +47,22 @@ ConfirmDialog::ConfirmDialog(Window* parent, const char* title,
|
||||
onCancel();
|
||||
return 0;
|
||||
});
|
||||
#if LCD_W > LCD_H
|
||||
btn->setWidth(LV_DPI_DEF);
|
||||
#else
|
||||
btn->setWidth(LV_DPI_DEF * 3 / 4);
|
||||
#endif
|
||||
|
||||
btn = new TextButton(box, rect_t{}, STR_YES, [=]() -> int8_t {
|
||||
this->deleteLater();
|
||||
this->confirmHandler();
|
||||
return 0;
|
||||
});
|
||||
#if LCD_W > LCD_H
|
||||
btn->setWidth(LV_DPI_DEF);
|
||||
#else
|
||||
btn->setWidth(LV_DPI_DEF * 3 / 4);
|
||||
#endif
|
||||
|
||||
content->setWidth(LCD_W * 0.8);
|
||||
content->updateSize();
|
||||
|
||||
@@ -97,9 +97,9 @@ class ListBase : public TableField
|
||||
|
||||
class ListBox : public ListBase
|
||||
{
|
||||
std::string title;
|
||||
// std::string title;
|
||||
|
||||
public:
|
||||
using ListBase::ListBase;
|
||||
inline void setTitle(std::string title) { this->title = title; }
|
||||
// inline void setTitle(std::string title) { this->title = title; }
|
||||
};
|
||||
|
||||
@@ -58,6 +58,8 @@ Page::Page(unsigned icon):
|
||||
body(this, _get_body_rect(), FORM_FORWARD_FOCUS)
|
||||
{
|
||||
Layer::push(this);
|
||||
|
||||
lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY3), 0);
|
||||
}
|
||||
|
||||
void Page::deleteLater(bool detach, bool trash)
|
||||
@@ -69,15 +71,9 @@ void Page::deleteLater(bool detach, bool trash)
|
||||
Window::deleteLater(detach, trash);
|
||||
}
|
||||
|
||||
void Page::paint(BitmapBuffer * dc)
|
||||
{
|
||||
dc->clear(COLOR_THEME_SECONDARY3);
|
||||
}
|
||||
|
||||
void Page::onCancel()
|
||||
{
|
||||
if (canCancel())
|
||||
deleteLater();
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void Page::onClicked()
|
||||
|
||||
@@ -55,11 +55,8 @@ class Page : public Window
|
||||
void onCancel() override;
|
||||
void onClicked() override;
|
||||
|
||||
void paint(BitmapBuffer* dc) override;
|
||||
void deleteLater(bool detach = true, bool trash = true) override;
|
||||
|
||||
virtual bool canCancel() { return true; }
|
||||
|
||||
protected:
|
||||
PageHeader header;
|
||||
FormWindow body;
|
||||
|
||||
@@ -21,112 +21,98 @@
|
||||
#include "radio_theme.h"
|
||||
#include <algorithm>
|
||||
|
||||
#include "bitmapbuffer.h"
|
||||
#include "libopenui.h"
|
||||
#include "opentx.h"
|
||||
#include "file_preview.h"
|
||||
#include "theme_manager.h"
|
||||
#include "view_main.h"
|
||||
#include "widget_settings.h"
|
||||
#include "sliders.h"
|
||||
#include "color_list.h"
|
||||
#include "color_editor.h"
|
||||
#include "listbox.h"
|
||||
#include "preview_window.h"
|
||||
|
||||
#if (LCD_W > LCD_H)
|
||||
#define COLOR_PREVIEW_WIDTH 18
|
||||
#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 - 14)
|
||||
#define TOP_LIST_OFFSET 4
|
||||
#define LEFT_LIST_OFFSET 3
|
||||
constexpr int COLOR_PREVIEW_SIZE = 18;
|
||||
|
||||
#if LCD_W > LCD_H
|
||||
constexpr int LIST_WIDTH = ((LCD_W - 12) / 2 - COLOR_PREVIEW_SIZE);
|
||||
constexpr int COLOR_LIST_WIDTH = ((LCD_W * 3)/10);
|
||||
#else
|
||||
#define COLOR_PREVIEW_WIDTH LCD_W
|
||||
#define COLOR_PREVIEW_HEIGHT 18
|
||||
#define LEFT_LIST_WIDTH LCD_W - 6
|
||||
#define LEFT_LIST_HEIGHT (LCD_H / 2 - 38)
|
||||
#define COLOR_LIST_WIDTH LCD_W - 6
|
||||
#define COLOR_LIST_HEIGHT (LCD_H / 2 - 24)
|
||||
#define TOP_LIST_OFFSET 4
|
||||
#define LEFT_LIST_OFFSET 3
|
||||
constexpr int LIST_HEIGHT = (LCD_H / 2 - 38);
|
||||
constexpr int COLOR_LIST_HEIGHT = (LCD_H / 2 - 24);
|
||||
#endif
|
||||
|
||||
#define MARGIN_WIDTH 5
|
||||
|
||||
#if (LCD_W > LCD_H)
|
||||
constexpr int BUTTON_HEIGHT = 30;
|
||||
#if LCD_W > LCD_H
|
||||
constexpr int BUTTON_WIDTH = 75;
|
||||
constexpr rect_t detailsDialogRect = {30, 30, LCD_W - 60, LCD_H - 60};
|
||||
constexpr int labelWidth = 150;
|
||||
|
||||
constexpr int COLOR_BOX_LEFT = 5;
|
||||
constexpr int COLOR_BOX_WIDTH = 45;
|
||||
constexpr int COLOR_BOX_HEIGHT = 27;
|
||||
#else
|
||||
constexpr int BUTTON_HEIGHT = 30;
|
||||
constexpr int BUTTON_WIDTH = 65;
|
||||
constexpr rect_t detailsDialogRect = {10, 50, LCD_W - 20, 220};
|
||||
constexpr int labelWidth = 120;
|
||||
|
||||
constexpr int COLOR_BOX_LEFT = 3;
|
||||
constexpr int COLOR_BOX_WIDTH = 55;
|
||||
constexpr int COLOR_BOX_HEIGHT = 32;
|
||||
|
||||
constexpr int COLOR_BUTTON_WIDTH = COLOR_BOX_WIDTH;
|
||||
constexpr int COLOR_BUTTON_HEIGHT = 20;
|
||||
#endif
|
||||
|
||||
constexpr int BUTTON_HEIGHT = 30;
|
||||
constexpr int COLOR_BOX_HEIGHT = 30;
|
||||
|
||||
constexpr int BOX_MARGIN = 2;
|
||||
constexpr int MAX_BOX_WIDTH = 15;
|
||||
|
||||
|
||||
class ThemeColorPreview : public FormField
|
||||
class ThemeColorPreview : public FormWindow
|
||||
{
|
||||
public:
|
||||
ThemeColorPreview(Window *parent, const rect_t &rect, std::vector<ColorEntry> colorList) :
|
||||
FormField(parent, rect, NO_FOCUS),
|
||||
FormWindow(parent, rect, NO_FOCUS),
|
||||
colorList(colorList)
|
||||
{
|
||||
setBoxWidth();
|
||||
padAll(0);
|
||||
#if LCD_W > LCD_H
|
||||
setFlexLayout(LV_FLEX_FLOW_COLUMN, BOX_MARGIN);
|
||||
#else
|
||||
setFlexLayout(LV_FLEX_FLOW_ROW, BOX_MARGIN);
|
||||
#endif
|
||||
build();
|
||||
}
|
||||
~ThemeColorPreview()
|
||||
|
||||
void build()
|
||||
{
|
||||
clear();
|
||||
setBoxWidth();
|
||||
int size = (boxWidth + BOX_MARGIN) * colorList.size() - BOX_MARGIN;
|
||||
#if LCD_W > LCD_H
|
||||
padTop((height() - size) / 2);
|
||||
#else
|
||||
padLeft((width() - size) / 2);
|
||||
#endif
|
||||
for (auto color: colorList) {
|
||||
auto c = lv_obj_create(lvobj);
|
||||
lv_obj_set_size(c, boxWidth, boxWidth);
|
||||
lv_obj_set_style_border_width(c, 1, 0);
|
||||
lv_obj_set_style_border_color(c, makeLvColor(COLOR2FLAGS(BLACK)), 0);
|
||||
lv_obj_set_style_border_opa(c, LV_OPA_100, 0);
|
||||
lv_obj_set_style_bg_color(c, makeLvColor(COLOR2FLAGS(color.colorValue)), 0);
|
||||
lv_obj_set_style_bg_opa(c, LV_OPA_100, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void setColorList(std::vector<ColorEntry> colorList)
|
||||
{
|
||||
this->colorList.assign(colorList.begin(), colorList.end());
|
||||
setBoxWidth();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void paint(BitmapBuffer *dc) override
|
||||
{
|
||||
int totalNessessarySpace = colorList.size() * (boxWidth + 2);
|
||||
int axis = rect.w > rect.h ? rect.w : rect.h;
|
||||
axis = (axis - totalNessessarySpace) / 2;
|
||||
for (auto color: colorList) {
|
||||
if (rect.w > rect.h) {
|
||||
dc->drawSolidFilledRect(axis, 0, boxWidth, boxWidth, COLOR2FLAGS(color.colorValue));
|
||||
dc->drawSolidRect(axis, 0, boxWidth, boxWidth, 1, COLOR2FLAGS(BLACK));
|
||||
} else {
|
||||
dc->drawSolidFilledRect(0, axis, boxWidth, boxWidth, COLOR2FLAGS(color.colorValue));
|
||||
dc->drawSolidRect(0, axis, boxWidth, boxWidth, 1, COLOR2FLAGS(BLACK));
|
||||
}
|
||||
axis += boxWidth + BOX_MARGIN;
|
||||
}
|
||||
this->colorList = colorList;
|
||||
build();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<ColorEntry> colorList;
|
||||
int boxWidth = MAX_BOX_WIDTH;
|
||||
|
||||
void setBoxWidth()
|
||||
{
|
||||
auto winSize = rect.w > rect.h ? rect.w : rect.h;
|
||||
boxWidth = winSize / (colorList.size() + BOX_MARGIN);
|
||||
#if LCD_W > LCD_H
|
||||
boxWidth = (height() - (colorList.size() - 1) * BOX_MARGIN) / colorList.size();
|
||||
#else
|
||||
boxWidth = (width() - (colorList.size() - 1) * BOX_MARGIN) / colorList.size();
|
||||
#endif
|
||||
boxWidth = min(boxWidth, MAX_BOX_WIDTH);
|
||||
}
|
||||
};
|
||||
@@ -198,169 +184,179 @@ class ThemeDetailsDialog: public Dialog
|
||||
std::function<void(ThemeFile theme)> saveHandler = nullptr;
|
||||
};
|
||||
|
||||
class ColorSquare : public FormField
|
||||
class ColorSquare : public Window
|
||||
{
|
||||
public:
|
||||
ColorSquare(Window *window, const rect_t &rect, uint32_t color, WindowFlags windowFlags = 0)
|
||||
: FormField(window, rect, windowFlags | NO_FOCUS),
|
||||
ColorSquare(Window *window, const rect_t &rect, uint32_t color, WindowFlags windowFlags = 0) :
|
||||
Window(window, rect, windowFlags | NO_FOCUS),
|
||||
_color(color)
|
||||
{
|
||||
}
|
||||
|
||||
void paint (BitmapBuffer *dc) override
|
||||
{
|
||||
dc->drawSolidFilledRect(0, 0, rect.w, rect.h, COLOR2FLAGS(_color));
|
||||
dc->drawSolidRect(0, 0, rect.w, rect.h, 1, COLOR2FLAGS(BLACK));
|
||||
swatch = lv_obj_create(lvobj);
|
||||
lv_obj_set_size(swatch, width(), height());
|
||||
lv_obj_set_style_border_width(swatch, 1, 0);
|
||||
lv_obj_set_style_border_color(swatch, makeLvColor(COLOR2FLAGS(BLACK)), 0);
|
||||
lv_obj_set_style_border_opa(swatch, LV_OPA_100, 0);
|
||||
lv_obj_set_style_bg_color(swatch, makeLvColor(COLOR2FLAGS(_color)), 0);
|
||||
lv_obj_set_style_bg_opa(swatch, LV_OPA_100, 0);
|
||||
}
|
||||
|
||||
void setColorToEdit(uint32_t colorEntry)
|
||||
{
|
||||
_color = colorEntry;
|
||||
|
||||
invalidate();
|
||||
lv_obj_set_style_bg_color(swatch, makeLvColor(COLOR2FLAGS(_color)), 0);
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t _color;
|
||||
lv_obj_t *swatch;
|
||||
};
|
||||
|
||||
void setHexStr(StaticText* hexBox, uint32_t rgb)
|
||||
{
|
||||
auto r = GET_RED(rgb), g = GET_GREEN(rgb), b = GET_BLUE(rgb);
|
||||
char hexstr[8];
|
||||
snprintf(hexstr, sizeof(hexstr), "%02X%02X%02X",
|
||||
(uint16_t)r, (uint16_t)g, (uint16_t)b);
|
||||
hexBox->setText(hexstr);
|
||||
}
|
||||
|
||||
class ColorEditPage : public Page
|
||||
{
|
||||
public:
|
||||
ColorEditPage(ThemeFile theme, LcdColorIndex indexOfColor,
|
||||
std::function<void (uint32_t rgb)> setValue = nullptr) :
|
||||
Page(ICON_RADIO_EDIT_THEME),
|
||||
_setValue(std::move(setValue)),
|
||||
_indexOfColor(indexOfColor),
|
||||
_theme(theme)
|
||||
{
|
||||
buildBody(&body);
|
||||
buildHead(&header);
|
||||
}
|
||||
|
||||
void setActiveColorBar(int activeTab)
|
||||
{
|
||||
if (activeTab >= 0 && _activeTab <= (int)_tabs.size()) {
|
||||
_activeTab = activeTab;
|
||||
for (auto i = 0; i < (int) _tabs.size(); i++)
|
||||
_tabs[i]->check(i == _activeTab);
|
||||
_colorEditor->setColorEditorType(_activeTab == 1 ? HSV_COLOR_EDITOR : RGB_COLOR_EDITOR);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::function<void (uint32_t rgb)> _setValue;
|
||||
LcdColorIndex _indexOfColor;
|
||||
ThemeFile _theme;
|
||||
TextButton *_cancelButton;
|
||||
ColorEditor *_colorEditor;
|
||||
PreviewWindow *_previewWindow = nullptr;
|
||||
std::vector<Button *> _tabs;
|
||||
int _activeTab = 0;
|
||||
ColorSquare *_colorSquare = nullptr;
|
||||
StaticText *_hexBox = nullptr;
|
||||
|
||||
void buildBody(FormWindow* window)
|
||||
{
|
||||
rect_t r;
|
||||
|
||||
// hexBox
|
||||
r = { COLOR_BOX_LEFT + COLOR_BOX_WIDTH + 5, TOP_LIST_OFFSET, 90, 30 };
|
||||
_hexBox = new StaticText(window, r, "", 0, COLOR_THEME_PRIMARY1 | FONT(L) | RIGHT);
|
||||
setHexStr(_hexBox, _theme.getColorEntryByIndex(_indexOfColor)->colorValue);
|
||||
|
||||
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);
|
||||
if (_colorSquare != nullptr) {
|
||||
_colorSquare->setColorToEdit(rgb);
|
||||
}
|
||||
if (_previewWindow != nullptr) {
|
||||
_previewWindow->setColorList(_theme.getColorList());
|
||||
}
|
||||
if (_hexBox != nullptr) {
|
||||
setHexStr(_hexBox, rgb);
|
||||
}
|
||||
if (_setValue != nullptr)
|
||||
_setValue(rgb);
|
||||
});
|
||||
_colorEditor->setColorEditorType(HSV_COLOR_EDITOR);
|
||||
_activeTab = 1;
|
||||
|
||||
r = { COLOR_BOX_LEFT, 4, COLOR_BOX_WIDTH, COLOR_BOX_HEIGHT };
|
||||
_colorSquare = new ColorSquare(window, r, _theme.getColorEntryByIndex(_indexOfColor)->colorValue);
|
||||
|
||||
r = LCD_W > LCD_H ?
|
||||
rect_t {
|
||||
COLOR_LIST_WIDTH + MARGIN_WIDTH + LEFT_LIST_OFFSET,
|
||||
TOP_LIST_OFFSET,
|
||||
LCD_W - COLOR_LIST_WIDTH - LEFT_LIST_OFFSET - MARGIN_WIDTH * 2,
|
||||
COLOR_LIST_HEIGHT } :
|
||||
rect_t {
|
||||
LEFT_LIST_OFFSET,
|
||||
TOP_LIST_OFFSET + COLOR_LIST_HEIGHT + MARGIN_WIDTH,
|
||||
LEFT_LIST_WIDTH,
|
||||
COLOR_LIST_HEIGHT - (MARGIN_WIDTH * 2)
|
||||
};
|
||||
_previewWindow = new PreviewWindow(window, r, _theme.getColorList());
|
||||
}
|
||||
|
||||
void buildHead(PageHeader* window)
|
||||
{
|
||||
LcdFlags flags = 0;
|
||||
if (LCD_W < LCD_H) {
|
||||
flags = FONT(XS);
|
||||
public:
|
||||
ColorEditPage(ThemeFile *theme, LcdColorIndex indexOfColor,
|
||||
std::function<void ()> updateHandler = nullptr) :
|
||||
Page(ICON_RADIO_EDIT_THEME),
|
||||
_updateHandler(std::move(updateHandler)),
|
||||
_indexOfColor(indexOfColor),
|
||||
_theme(theme)
|
||||
{
|
||||
buildBody(&body);
|
||||
buildHead(&header);
|
||||
}
|
||||
|
||||
// page title
|
||||
new StaticText(window,
|
||||
{ PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT,
|
||||
PAGE_LINE_HEIGHT },
|
||||
"Edit Color", 0, COLOR_THEME_PRIMARY2 | flags);
|
||||
new StaticText(window,
|
||||
{ PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT,
|
||||
LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT },
|
||||
ThemePersistance::getColorNames()[(int)_indexOfColor], 0, COLOR_THEME_PRIMARY2 | flags);
|
||||
void setActiveColorBar(int activeTab)
|
||||
{
|
||||
if (activeTab >= 0 && _activeTab <= (int)_tabs.size()) {
|
||||
_activeTab = activeTab;
|
||||
for (auto i = 0; i < (int) _tabs.size(); i++)
|
||||
_tabs[i]->check(i == _activeTab);
|
||||
_colorEditor->setColorEditorType(_activeTab == 1 ? HSV_COLOR_EDITOR : RGB_COLOR_EDITOR);
|
||||
}
|
||||
}
|
||||
|
||||
// page tabs
|
||||
rect_t r = { LCD_W - 2*(BUTTON_WIDTH + 5), 6, BUTTON_WIDTH, BUTTON_HEIGHT };
|
||||
_tabs.emplace_back(
|
||||
new TextButton(window, r, "RGB",
|
||||
[=] () {
|
||||
setActiveColorBar(0);
|
||||
return 1;
|
||||
}));
|
||||
r.x += (BUTTON_WIDTH + 5);
|
||||
_tabs.emplace_back(
|
||||
new TextButton(window, r, "HSV",
|
||||
[=] () {
|
||||
setActiveColorBar(1);
|
||||
return 1;
|
||||
}));
|
||||
_tabs[1]->check(true);
|
||||
}
|
||||
protected:
|
||||
std::function<void ()> _updateHandler;
|
||||
LcdColorIndex _indexOfColor;
|
||||
ThemeFile *_theme;
|
||||
TextButton *_cancelButton;
|
||||
ColorEditor *_colorEditor;
|
||||
PreviewWindow *_previewWindow = nullptr;
|
||||
std::vector<Button *> _tabs;
|
||||
int _activeTab = 0;
|
||||
ColorSquare *_colorSquare = nullptr;
|
||||
StaticText *_hexBox = nullptr;
|
||||
|
||||
void deleteLater(bool detach = true, bool trash = true) override
|
||||
{
|
||||
if (_updateHandler != nullptr)
|
||||
_updateHandler();
|
||||
Page::deleteLater(detach, trash);
|
||||
}
|
||||
|
||||
void setHexStr(uint32_t rgb)
|
||||
{
|
||||
if (_hexBox) {
|
||||
auto r = GET_RED(rgb), g = GET_GREEN(rgb), b = GET_BLUE(rgb);
|
||||
char hexstr[8];
|
||||
snprintf(hexstr, sizeof(hexstr), "%02X%02X%02X",
|
||||
(uint16_t)r, (uint16_t)g, (uint16_t)b);
|
||||
_hexBox->setText(hexstr);
|
||||
}
|
||||
}
|
||||
|
||||
void buildBody(FormWindow* window)
|
||||
{
|
||||
window->padAll(4);
|
||||
FormWindow *form = new FormWindow(window, {0, 0, window->width() - 8, window->height() - 8});
|
||||
form->padAll(0);
|
||||
#if LCD_W > LCD_H
|
||||
form->setFlexLayout(LV_FLEX_FLOW_ROW, 4);
|
||||
rect_t r = {0, 0, COLOR_LIST_WIDTH, form->height()};
|
||||
#else
|
||||
form->setFlexLayout(LV_FLEX_FLOW_COLUMN, 4);
|
||||
rect_t r = {0, 0, form->width(), COLOR_LIST_HEIGHT};
|
||||
#endif
|
||||
|
||||
FormWindow *colForm = new FormWindow(form, r);
|
||||
colForm->padAll(0);
|
||||
colForm->setFlexLayout(LV_FLEX_FLOW_COLUMN, 4);
|
||||
colForm->setWidth(r.w);
|
||||
|
||||
#if LCD_W > LCD_H
|
||||
r.w = form->width() - COLOR_LIST_WIDTH - 4;
|
||||
#else
|
||||
r.h = form->height() - COLOR_LIST_HEIGHT - 4;
|
||||
#endif
|
||||
_previewWindow = new PreviewWindow(form, r, _theme->getColorList());
|
||||
|
||||
r.w = colForm->width();
|
||||
r.h = COLOR_BOX_HEIGHT;
|
||||
|
||||
FormWindow *colBoxForm = new FormWindow(colForm, r);
|
||||
colBoxForm->padAll(0);
|
||||
colBoxForm->setFlexLayout(LV_FLEX_FLOW_ROW, 2);
|
||||
|
||||
r.h = colForm->height() - COLOR_BOX_HEIGHT - 4;
|
||||
|
||||
_colorEditor = new ColorEditor(colForm, r, _theme->getColorEntryByIndex(_indexOfColor)->colorValue,
|
||||
[=](uint32_t rgb) {
|
||||
_theme->setColor(_indexOfColor, rgb);
|
||||
if (_colorSquare != nullptr) {
|
||||
_colorSquare->setColorToEdit(rgb);
|
||||
}
|
||||
if (_previewWindow != nullptr) {
|
||||
_previewWindow->setColorList(_theme->getColorList());
|
||||
}
|
||||
setHexStr(rgb);
|
||||
});
|
||||
_colorEditor->setColorEditorType(HSV_COLOR_EDITOR);
|
||||
_activeTab = 1;
|
||||
|
||||
r.w = COLOR_BOX_WIDTH;
|
||||
r.h = COLOR_BOX_HEIGHT;
|
||||
_colorSquare = new ColorSquare(colBoxForm, r, _theme->getColorEntryByIndex(_indexOfColor)->colorValue);
|
||||
|
||||
// hexBox
|
||||
r.w = 95;
|
||||
_hexBox = new StaticText(colBoxForm, r, "", 0, COLOR_THEME_PRIMARY1 | FONT(L) | RIGHT);
|
||||
setHexStr(_theme->getColorEntryByIndex(_indexOfColor)->colorValue);
|
||||
}
|
||||
|
||||
void buildHead(PageHeader* window)
|
||||
{
|
||||
// page title
|
||||
header.setTitle(STR_EDIT_COLOR);
|
||||
new StaticText(window,
|
||||
{ PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT,
|
||||
LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT },
|
||||
ThemePersistance::getColorNames()[(int)_indexOfColor], 0, COLOR_THEME_PRIMARY2);
|
||||
|
||||
// page tabs
|
||||
rect_t r = { LCD_W - 2*(BUTTON_WIDTH + 5), 6, BUTTON_WIDTH, BUTTON_HEIGHT };
|
||||
_tabs.emplace_back(
|
||||
new TextButton(window, r, "RGB",
|
||||
[=] () {
|
||||
setActiveColorBar(0);
|
||||
return 1;
|
||||
}));
|
||||
r.x += (BUTTON_WIDTH + 5);
|
||||
_tabs.emplace_back(
|
||||
new TextButton(window, r, "HSV",
|
||||
[=] () {
|
||||
setActiveColorBar(1);
|
||||
return 1;
|
||||
}));
|
||||
_tabs[1]->check(true);
|
||||
}
|
||||
};
|
||||
|
||||
class ThemeEditPage : public Page
|
||||
{
|
||||
public:
|
||||
explicit ThemeEditPage(ThemeFile theme, std::function<void (ThemeFile &theme)> saveHandler = nullptr) :
|
||||
explicit ThemeEditPage(ThemeFile *theme, std::function<void (ThemeFile &theme)> saveHandler = nullptr) :
|
||||
Page(ICON_RADIO_EDIT_THEME),
|
||||
_theme(theme),
|
||||
_theme(*theme),
|
||||
page(this),
|
||||
saveHandler(std::move(saveHandler))
|
||||
{
|
||||
@@ -368,7 +364,7 @@ class ThemeEditPage : public Page
|
||||
buildHeader(&header);
|
||||
}
|
||||
|
||||
bool canCancel() override
|
||||
void onCancel() override
|
||||
{
|
||||
if (_dirty) {
|
||||
new ConfirmDialog(
|
||||
@@ -382,40 +378,49 @@ class ThemeEditPage : public Page
|
||||
[=]() {
|
||||
deleteLater();
|
||||
});
|
||||
return false;
|
||||
} else {
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void checkEvents() override
|
||||
{
|
||||
if (!started && _themeName) {
|
||||
started = true;
|
||||
_themeName->setText(_theme.getName());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void editColorPage()
|
||||
{
|
||||
auto colorEntry = _cList->getSelectedColor();
|
||||
new ColorEditPage(_theme, colorEntry.colorNumber,
|
||||
[=] (uint32_t color) {
|
||||
_dirty = true;
|
||||
_theme.setColor(colorEntry.colorNumber, color);
|
||||
_cList->setColorList(_theme.getColorList());
|
||||
_previewWindow->setColorList(_theme.getColorList());
|
||||
});
|
||||
new ColorEditPage(&_theme, colorEntry.colorNumber,
|
||||
[=] () {
|
||||
_dirty = true;
|
||||
_cList->setColorList(_theme.getColorList());
|
||||
_previewWindow->setColorList(_theme.getColorList());
|
||||
});
|
||||
}
|
||||
|
||||
void buildHeader(FormGroup *window)
|
||||
{
|
||||
LcdFlags flags = 0;
|
||||
if (LCD_W < LCD_H) {
|
||||
flags = FONT(XS);
|
||||
}
|
||||
#if LCD_W > LCD_H
|
||||
LcdFlags flags = 0;
|
||||
#else
|
||||
LcdFlags flags = FONT(XS);
|
||||
#endif
|
||||
|
||||
// page title
|
||||
header.setTitle(STR_EDIT_THEME);
|
||||
_themeName = new StaticText(window,
|
||||
{PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT,
|
||||
LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT},
|
||||
_theme.getName(), 0, COLOR_THEME_PRIMARY2 | flags);
|
||||
"", 0, COLOR_THEME_PRIMARY2 | flags);
|
||||
lv_label_set_long_mode(_themeName->getLvObj(), LV_LABEL_LONG_DOT);
|
||||
|
||||
// save and cancel
|
||||
rect_t r = {LCD_W - (BUTTON_WIDTH + 5), 6, BUTTON_WIDTH, BUTTON_HEIGHT };
|
||||
_detailButton = new TextButton(window, r, STR_DETAILS, [=] () {
|
||||
new TextButton(window, r, STR_DETAILS, [=] () {
|
||||
new ThemeDetailsDialog(page, _theme, [=] (ThemeFile t) {
|
||||
_theme.setAuthor(t.getAuthor());
|
||||
_theme.setInfo(t.getInfo());
|
||||
@@ -431,53 +436,60 @@ class ThemeEditPage : public Page
|
||||
|
||||
void buildBody(FormGroup *window)
|
||||
{
|
||||
rect_t r = { LEFT_LIST_OFFSET, TOP_LIST_OFFSET, COLOR_LIST_WIDTH, COLOR_LIST_HEIGHT};
|
||||
_cList = new ColorList(window, r, _theme.getColorList());
|
||||
window->padAll(4);
|
||||
FormWindow *form = new FormWindow(window, {0, 0, window->width() - 8, window->height() - 8});
|
||||
form->padAll(0);
|
||||
#if LCD_W > LCD_H
|
||||
form->setFlexLayout(LV_FLEX_FLOW_ROW, 4);
|
||||
rect_t r = {0, 0, COLOR_LIST_WIDTH, form->height()};
|
||||
#else
|
||||
form->setFlexLayout(LV_FLEX_FLOW_COLUMN, 4);
|
||||
rect_t r = {0, 0, form->width(), COLOR_LIST_HEIGHT};
|
||||
#endif
|
||||
|
||||
_cList = new ColorList(form, r, _theme.getColorList());
|
||||
_cList->setLongPressHandler([=] () { editColorPage(); });
|
||||
_cList->setPressHandler([=] () { editColorPage(); });
|
||||
|
||||
if (LCD_W > LCD_H) {
|
||||
r = {
|
||||
COLOR_LIST_WIDTH + MARGIN_WIDTH + LEFT_LIST_OFFSET,
|
||||
TOP_LIST_OFFSET,
|
||||
LCD_W - COLOR_LIST_WIDTH - LEFT_LIST_OFFSET - MARGIN_WIDTH * 2,
|
||||
COLOR_LIST_HEIGHT
|
||||
};
|
||||
} else {
|
||||
r = {
|
||||
LEFT_LIST_OFFSET,
|
||||
TOP_LIST_OFFSET + COLOR_LIST_HEIGHT + MARGIN_WIDTH,
|
||||
LEFT_LIST_WIDTH,
|
||||
COLOR_LIST_HEIGHT - (MARGIN_WIDTH * 2)
|
||||
};
|
||||
}
|
||||
_previewWindow = new PreviewWindow(window, r, _theme.getColorList());
|
||||
#if LCD_W > LCD_H
|
||||
r.w = form->width() - COLOR_LIST_WIDTH - 4;
|
||||
#else
|
||||
r.h = form->height() - COLOR_LIST_HEIGHT - 4;
|
||||
#endif
|
||||
_previewWindow = new PreviewWindow(form, r, _theme.getColorList());
|
||||
}
|
||||
|
||||
protected:
|
||||
bool _dirty = false;
|
||||
bool started = false;
|
||||
ThemeFile _theme;
|
||||
Page *page;
|
||||
std::function<void(ThemeFile &theme)> saveHandler = nullptr;
|
||||
PreviewWindow *_previewWindow = nullptr;
|
||||
ColorList *_cList = nullptr;
|
||||
StaticText *_themeName = nullptr;
|
||||
TextButton *_saveButton;
|
||||
TextButton *_detailButton;
|
||||
};
|
||||
|
||||
ThemeSetupPage::ThemeSetupPage() : PageTab(STR_THEME_EDITOR, ICON_RADIO_EDIT_THEME) {}
|
||||
ThemeSetupPage::~ThemeSetupPage()
|
||||
{
|
||||
}
|
||||
|
||||
void ThemeSetupPage::setAuthor(ThemeFile *theme)
|
||||
{
|
||||
char author[256];
|
||||
strcpy(author, "By: ");
|
||||
strcat(author, theme->getAuthor());
|
||||
char author[256] = "";
|
||||
if (theme && (strlen(theme->getAuthor()) > 0)) {
|
||||
strcpy(author, "By: ");
|
||||
strcat(author, theme->getAuthor());
|
||||
}
|
||||
authorText->setText(author);
|
||||
}
|
||||
|
||||
void ThemeSetupPage::setName(ThemeFile *theme)
|
||||
{
|
||||
if (theme && (strlen(theme->getName()) > 0)) {
|
||||
nameText->setText(theme->getName());
|
||||
} else {
|
||||
nameText->setText("");
|
||||
}
|
||||
}
|
||||
|
||||
bool isTopWindow(Window *window)
|
||||
{
|
||||
@@ -493,7 +505,15 @@ void ThemeSetupPage::checkEvents()
|
||||
{
|
||||
PageTab::checkEvents();
|
||||
|
||||
fileCarosell->pause(!isTopWindow(pageWindow));
|
||||
if (fileCarosell)
|
||||
fileCarosell->pause(!isTopWindow(pageWindow));
|
||||
|
||||
if (!started && nameText && authorText) {
|
||||
auto theme = ThemePersistance::instance()->getCurrentTheme();
|
||||
started = true;
|
||||
setName(theme);
|
||||
setAuthor(theme);
|
||||
}
|
||||
}
|
||||
|
||||
void ThemeSetupPage::displayThemeMenu(Window *window, ThemePersistance *tp)
|
||||
@@ -509,6 +529,7 @@ void ThemeSetupPage::displayThemeMenu(Window *window, ThemePersistance *tp)
|
||||
nameText->setTextFlags(COLOR_THEME_PRIMARY1);
|
||||
authorText->setTextFlags(COLOR_THEME_PRIMARY1);
|
||||
listBox->setActiveItem(idx);
|
||||
TabsGroup::refreshTheme();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -521,25 +542,26 @@ void ThemeSetupPage::displayThemeMenu(Window *window, ThemePersistance *tp)
|
||||
auto theme = tp->getThemeByIndex(themeIdx);
|
||||
if (theme == nullptr) return;
|
||||
|
||||
new ThemeEditPage(*theme, [=](ThemeFile &theme) {
|
||||
theme.serialize();
|
||||
new ThemeEditPage(theme, [=](ThemeFile &editedTheme) {
|
||||
// update cached theme data
|
||||
*theme = editedTheme;
|
||||
|
||||
theme->serialize();
|
||||
|
||||
// if the theme info currently displayed
|
||||
// were changed, update the UI
|
||||
if (themeIdx == currentTheme) {
|
||||
setAuthor(&theme);
|
||||
nameText->setText(theme.getName());
|
||||
listBox->setName(currentTheme, theme.getName());
|
||||
themeColorPreview->setColorList(theme.getColorList());
|
||||
setAuthor(theme);
|
||||
setName(theme);
|
||||
listBox->setName(currentTheme, theme->getName());
|
||||
themeColorPreview->setColorList(theme->getColorList());
|
||||
}
|
||||
|
||||
// if the active theme changed, re-apply it
|
||||
if (themeIdx == tp->getThemeIndex()) theme.applyTheme();
|
||||
|
||||
// update cached theme data
|
||||
auto tp_theme = tp->getThemeByIndex(themeIdx);
|
||||
if (!tp_theme) return;
|
||||
*tp_theme = theme;
|
||||
if (themeIdx == tp->getThemeIndex()) {
|
||||
theme->applyTheme();
|
||||
TabsGroup::refreshTheme();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -586,33 +608,47 @@ void ThemeSetupPage::displayThemeMenu(Window *window, ThemePersistance *tp)
|
||||
}
|
||||
}
|
||||
|
||||
void ThemeSetupPage::setupListbox(FormWindow *window, rect_t r, ThemePersistance *tp)
|
||||
void ThemeSetupPage::setSelected(ThemePersistance *tp)
|
||||
{
|
||||
auto value = listBox->getSelected();
|
||||
if (themeColorPreview && authorText && nameText && fileCarosell) {
|
||||
ThemeFile *theme = tp->getThemeByIndex(value);
|
||||
if (theme) {
|
||||
themeColorPreview->setColorList(theme->getColorList());
|
||||
setAuthor(theme);
|
||||
setName(theme);
|
||||
fileCarosell->setFileNames(theme->getThemeImageFileNames());
|
||||
}
|
||||
currentTheme = value;
|
||||
}
|
||||
}
|
||||
|
||||
void ThemeSetupPage::setupListbox(Window *window, rect_t r, ThemePersistance *tp)
|
||||
{
|
||||
listBox = new ListBox(window, r, tp->getNames());
|
||||
listBox->setAutoEdit(true);
|
||||
listBox->setSelected(currentTheme);
|
||||
listBox->setActiveItem(tp->getThemeIndex());
|
||||
listBox->setTitle(STR_THEME + std::string("s")); // TODO: fix this!
|
||||
listBox->setLongPressHandler([=] () { displayThemeMenu(window, tp); });
|
||||
listBox->setLongPressHandler([=] () {
|
||||
setSelected(tp);
|
||||
displayThemeMenu(window, tp);
|
||||
});
|
||||
listBox->setPressHandler([=] () {
|
||||
auto value = listBox->getSelected();
|
||||
if (themeColorPreview && authorText && nameText && fileCarosell) {
|
||||
ThemeFile *theme = tp->getThemeByIndex(value);
|
||||
if (!theme) return;
|
||||
themeColorPreview->setColorList(theme->getColorList());
|
||||
setAuthor(theme);
|
||||
nameText->setText(theme->getName());
|
||||
fileCarosell->setFileNames(theme->getThemeImageFileNames());
|
||||
}
|
||||
currentTheme = value;
|
||||
setSelected(tp);
|
||||
});
|
||||
}
|
||||
|
||||
void ThemeSetupPage::build(FormWindow *window)
|
||||
{
|
||||
window->padAll(0);
|
||||
window->padAll(4);
|
||||
pageWindow = window;
|
||||
|
||||
#if LCD_W > LCD_H
|
||||
window->setFlexLayout(LV_FLEX_FLOW_ROW, 2);
|
||||
#else
|
||||
window->setFlexLayout(LV_FLEX_FLOW_COLUMN, 2);
|
||||
#endif
|
||||
|
||||
auto tp = ThemePersistance::instance();
|
||||
auto theme = tp->getCurrentTheme();
|
||||
currentTheme = tp->getThemeIndex();
|
||||
@@ -622,48 +658,50 @@ void ThemeSetupPage::build(FormWindow *window)
|
||||
fileCarosell = nullptr;
|
||||
nameText = nullptr;
|
||||
authorText = nullptr;
|
||||
|
||||
|
||||
// create listbox and setup menus
|
||||
rect_t r = { LEFT_LIST_OFFSET, TOP_LIST_OFFSET, LEFT_LIST_WIDTH, LEFT_LIST_HEIGHT };
|
||||
#if LCD_W > LCD_H
|
||||
rect_t r = { 0, 0, LIST_WIDTH, window->height() - 8};
|
||||
#else
|
||||
rect_t r = { 0, 0, window->width() - 8, LIST_HEIGHT };
|
||||
#endif
|
||||
setupListbox(window, r, tp);
|
||||
|
||||
rect_t colorPreviewRect;
|
||||
if (LCD_W > LCD_H) {
|
||||
r.x = LEFT_LIST_WIDTH + MARGIN_WIDTH + LEFT_LIST_OFFSET;
|
||||
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};
|
||||
}
|
||||
#if LCD_W > LCD_H
|
||||
r.w = COLOR_PREVIEW_SIZE;
|
||||
#else
|
||||
r.h = COLOR_PREVIEW_SIZE;
|
||||
#endif
|
||||
|
||||
// setup ThemeColorPreview()
|
||||
auto colorList = theme != nullptr ? theme->getColorList() : std::vector<ColorEntry>();
|
||||
themeColorPreview = new ThemeColorPreview(window, colorPreviewRect, colorList);
|
||||
|
||||
themeColorPreview = new ThemeColorPreview(window, r, colorList);
|
||||
themeColorPreview->setWidth(r.w);
|
||||
|
||||
#if LCD_W > LCD_H
|
||||
r.w = window->width() - LIST_WIDTH - COLOR_PREVIEW_SIZE - 12;
|
||||
r.h = window->height() - 8;
|
||||
#else
|
||||
r.w = window->width() - 8;
|
||||
r.h = window->height() - LIST_HEIGHT - COLOR_PREVIEW_SIZE - 12;
|
||||
#endif
|
||||
|
||||
auto rw = new FormWindow(window, r);
|
||||
rw->padAll(0);
|
||||
rw->setFlexLayout(LV_FLEX_FLOW_COLUMN, 2);
|
||||
rw->setWidth(r.w);
|
||||
|
||||
r.h -= 46;
|
||||
|
||||
// setup FileCarosell()
|
||||
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);
|
||||
fileCarosell = new FileCarosell(rw, r, fileNames);
|
||||
|
||||
r.h = 20;
|
||||
|
||||
// author and name of theme on right side of screen
|
||||
r.x += 7;
|
||||
r.y += 135;
|
||||
r.h = 20;
|
||||
nameText = new StaticText(window, r, theme != nullptr ? theme->getName() : "", 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
if (theme != nullptr && strlen(theme->getAuthor())) {
|
||||
r.y += 20;
|
||||
authorText = new StaticText(window, r, "", 0, COLOR_THEME_PRIMARY1);
|
||||
setAuthor(theme);
|
||||
}
|
||||
nameText = new StaticText(rw, r, "", 0, COLOR_THEME_PRIMARY1);
|
||||
lv_label_set_long_mode(nameText->getLvObj(), LV_LABEL_LONG_DOT);
|
||||
authorText = new StaticText(rw, r, "", 0, COLOR_THEME_PRIMARY1);
|
||||
lv_label_set_long_mode(authorText->getLvObj(), LV_LABEL_LONG_DOT);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ class ThemeColorPreview;
|
||||
class ThemeSetupPage: public PageTab {
|
||||
public:
|
||||
ThemeSetupPage();
|
||||
~ThemeSetupPage();
|
||||
|
||||
void build(FormWindow * window) override;
|
||||
void checkEvents() override;
|
||||
@@ -40,7 +39,11 @@ class ThemeSetupPage: public PageTab {
|
||||
StaticText *authorText = nullptr;
|
||||
StaticText *nameText = nullptr;
|
||||
int currentTheme = 0;
|
||||
void setupListbox(FormWindow *window, rect_t r, ThemePersistance *tp);
|
||||
bool started = false;
|
||||
|
||||
void setupListbox(Window *window, rect_t r, ThemePersistance *tp);
|
||||
void displayThemeMenu(Window *window, ThemePersistance *tp);
|
||||
void setAuthor(ThemeFile *theme);
|
||||
void setName(ThemeFile *theme);
|
||||
void setSelected(ThemePersistance *tp);
|
||||
};
|
||||
|
||||
@@ -139,21 +139,36 @@ static constexpr rect_t _get_body_rect()
|
||||
return { 0, MENU_BODY_TOP, LCD_W, MENU_BODY_HEIGHT };
|
||||
}
|
||||
|
||||
TabsGroup* TabsGroup::activeTabsGroup = nullptr;
|
||||
|
||||
TabsGroup::TabsGroup(uint8_t icon):
|
||||
Window(Layer::back(), { 0, 0, LCD_W, LCD_H }, OPAQUE),
|
||||
header(this, icon),
|
||||
body(this, _get_body_rect(), NO_FOCUS | FORM_FORWARD_FOCUS)
|
||||
{
|
||||
Layer::push(this);
|
||||
|
||||
lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY3), 0);
|
||||
|
||||
activeTabsGroup = this;
|
||||
}
|
||||
|
||||
TabsGroup::~TabsGroup()
|
||||
{
|
||||
if (activeTabsGroup == this)
|
||||
activeTabsGroup = nullptr;
|
||||
|
||||
for (auto tab: tabs) {
|
||||
delete tab;
|
||||
}
|
||||
}
|
||||
|
||||
void TabsGroup::refreshTheme()
|
||||
{
|
||||
if (activeTabsGroup)
|
||||
lv_obj_set_style_bg_color(activeTabsGroup->getLvObj(), makeLvColor(COLOR_THEME_SECONDARY3), 0);
|
||||
}
|
||||
|
||||
void TabsGroup::deleteLater(bool detach, bool trash)
|
||||
{
|
||||
if (_deleted)
|
||||
@@ -292,11 +307,6 @@ void TabsGroup::onCancel()
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void TabsGroup::paint(BitmapBuffer * dc)
|
||||
{
|
||||
dc->clear(COLOR_THEME_SECONDARY3);
|
||||
}
|
||||
|
||||
#if defined(HARDWARE_TOUCH)
|
||||
bool TabsGroup::onTouchEnd(coord_t x, coord_t y)
|
||||
{
|
||||
|
||||
@@ -213,18 +213,18 @@ class TabsGroup: public Window
|
||||
void onClicked() override;
|
||||
void onCancel() override;
|
||||
|
||||
void paint(BitmapBuffer * dc) override;
|
||||
|
||||
#if defined(HARDWARE_TOUCH)
|
||||
bool onTouchEnd(coord_t x, coord_t y) override;
|
||||
#endif
|
||||
|
||||
static void refreshTheme();
|
||||
|
||||
protected:
|
||||
TabsGroupHeader header;
|
||||
FormWindow body;
|
||||
std::vector<PageTab *> tabs;
|
||||
PageTab * currentTab = nullptr;
|
||||
static TabsGroup* activeTabsGroup;
|
||||
|
||||
void setVisibleTab(PageTab * tab);
|
||||
};
|
||||
|
||||
@@ -47,10 +47,8 @@ ThemeFile::ThemeFile(std::string themePath) :
|
||||
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) {
|
||||
if (isFileAvailable(baseFileName.c_str(), true)) {
|
||||
_imageFileNames.emplace_back(baseFileName);
|
||||
f_close(&file);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -66,9 +64,9 @@ ThemeFile& ThemeFile::operator= (const ThemeFile& theme)
|
||||
return *this;
|
||||
|
||||
path = theme.path;
|
||||
strncpy(name, theme.name, NAME_LENGTH);
|
||||
strncpy(author, theme.author, AUTHOR_LENGTH);
|
||||
strncpy(info, theme.info, INFO_LENGTH);
|
||||
strAppend(name, theme.name, NAME_LENGTH);
|
||||
strAppend(author, theme.author, AUTHOR_LENGTH);
|
||||
strAppend(info, theme.info, INFO_LENGTH);
|
||||
colorList.assign(theme.colorList.begin(), theme.colorList.end());
|
||||
_imageFileNames.assign(theme._imageFileNames.begin(), theme._imageFileNames.end());
|
||||
|
||||
@@ -149,9 +147,8 @@ void ThemeFile::deSerialize()
|
||||
char *plvalue;
|
||||
char *prvalue;
|
||||
|
||||
strncpy(lvalue, line, min((int)(ptr - line), 63));
|
||||
lvalue[ptr - line] = '\0';
|
||||
strncpy(rvalue, ptr + 1, 63);
|
||||
strAppend(lvalue, line, min((int)(ptr - line), 63));
|
||||
strAppend(rvalue, ptr + 1, 63);
|
||||
plvalue = trim(lvalue);
|
||||
prvalue = trim(rvalue);
|
||||
|
||||
@@ -169,14 +166,11 @@ void ThemeFile::deSerialize()
|
||||
|
||||
case summary: {
|
||||
if (strcmp(plvalue, "name") == 0) {
|
||||
strncpy(name, prvalue, NAME_LENGTH);
|
||||
name[NAME_LENGTH] = '\0';
|
||||
strAppend(name, prvalue, NAME_LENGTH);
|
||||
} else if (strcmp(plvalue, "author") == 0) {
|
||||
strncpy(author, prvalue, AUTHOR_LENGTH);
|
||||
author[AUTHOR_LENGTH] = '\0';
|
||||
strAppend(author, prvalue, AUTHOR_LENGTH);
|
||||
} else if (strcmp(plvalue, "info") == 0) {
|
||||
strncpy(info, prvalue, INFO_LENGTH);
|
||||
info[INFO_LENGTH] = '\0';
|
||||
strAppend(info, prvalue, INFO_LENGTH);
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -347,14 +341,11 @@ void ThemePersistance::clearThemes()
|
||||
|
||||
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;
|
||||
|
||||
f_close(&file);
|
||||
TRACE("scanForThemes: found file %s", fullPath);
|
||||
themes.emplace_back(new ThemeFile(fullPath));
|
||||
if (isFileAvailable(fullPath, true)) {
|
||||
TRACE("scanForThemes: found file %s", fullPath);
|
||||
themes.emplace_back(new ThemeFile(fullPath));
|
||||
}
|
||||
}
|
||||
|
||||
void ThemePersistance::scanForThemes()
|
||||
@@ -366,8 +357,7 @@ void ThemePersistance::scanForThemes()
|
||||
|
||||
char fullPath[FF_MAX_LFN + 1];
|
||||
|
||||
strncpy(fullPath, THEMES_PATH, FF_MAX_LFN);
|
||||
fullPath[FF_MAX_LFN] = '\0';
|
||||
strAppend(fullPath, THEMES_PATH, FF_MAX_LFN);
|
||||
|
||||
TRACE("opening directory: %s", fullPath);
|
||||
FRESULT res = f_opendir(&dir, fullPath); // Open the directory
|
||||
@@ -384,9 +374,9 @@ void ThemePersistance::scanForThemes()
|
||||
if (strlen((const char *)fno.fname) > SD_SCREEN_FILE_LENGTH) 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);
|
||||
char *s = strAppend(themePath, fullPath, FF_MAX_LFN);
|
||||
s = strAppend(s, "/", FF_MAX_LFN - (s - themePath));
|
||||
strAppend(s, fno.fname, FF_MAX_LFN - (s - themePath));
|
||||
scanThemeFolder(themePath);
|
||||
}
|
||||
}
|
||||
@@ -416,15 +406,23 @@ void ThemePersistance::loadDefaultTheme()
|
||||
line[len] = '\0';
|
||||
|
||||
int index = 0;
|
||||
bool found = false;
|
||||
for (auto theme : themes) {
|
||||
if (theme->getPath() == std::string(line)) {
|
||||
applyTheme(index);
|
||||
setThemeIndex(index);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
// Force default if no match for last selected theme
|
||||
if (!found)
|
||||
index = 0;
|
||||
|
||||
applyTheme(index);
|
||||
setThemeIndex(index);
|
||||
}
|
||||
|
||||
f_close(&file);
|
||||
}
|
||||
|
||||
@@ -439,8 +437,8 @@ bool ThemePersistance::deleteThemeByIndex(int index)
|
||||
if (index > 0 && index < (int) themes.size()) {
|
||||
ThemeFile* theme = themes[index];
|
||||
|
||||
char newFile[FF_MAX_LFN + 1];
|
||||
strncpy(newFile, theme->getPath().c_str(), FF_MAX_LFN);
|
||||
char newFile[FF_MAX_LFN + 10];
|
||||
strAppend(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
|
||||
@@ -459,27 +457,19 @@ bool ThemePersistance::deleteThemeByIndex(int index)
|
||||
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);
|
||||
char *s = strAppend(fullPath, THEMES_PATH, FF_MAX_LFN);
|
||||
s = strAppend(s, "/", FF_MAX_LFN - (s - fullPath));
|
||||
s = strAppend(s, name.c_str(), FF_MAX_LFN - (s - fullPath));
|
||||
|
||||
FRESULT result = f_mkdir(fullPath);
|
||||
if (result != FR_OK) return false;
|
||||
strncat(fullPath, "/", FF_MAX_LFN);
|
||||
strncat(fullPath, "theme.yml", FF_MAX_LFN);
|
||||
s = strAppend(s, "/", FF_MAX_LFN - (s - fullPath));
|
||||
strAppend(s, "theme.yml", FF_MAX_LFN - (s - fullPath));
|
||||
theme.setPath(fullPath);
|
||||
theme.serialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ThemePersistance::deleteDefaultTheme()
|
||||
{
|
||||
FIL file;
|
||||
FRESULT status = f_open(&file, SELECTED_THEME_FILE, FA_CREATE_ALWAYS | FA_WRITE);
|
||||
if (status == FR_OK) f_close(&file);
|
||||
}
|
||||
|
||||
void ThemePersistance::setDefaultTheme(int index)
|
||||
{
|
||||
FIL file;
|
||||
@@ -489,7 +479,8 @@ void ThemePersistance::setDefaultTheme(int index)
|
||||
if (status != FR_OK) return;
|
||||
|
||||
currentTheme = index;
|
||||
f_printf(&file, theme->getPath().c_str());
|
||||
if (index > 0)
|
||||
f_printf(&file, theme->getPath().c_str());
|
||||
f_close(&file);
|
||||
}
|
||||
}
|
||||
@@ -497,7 +488,7 @@ void ThemePersistance::setDefaultTheme(int index)
|
||||
class DefaultEdgeTxTheme : public ThemeFile
|
||||
{
|
||||
public:
|
||||
DefaultEdgeTxTheme() : ThemeFile("")
|
||||
DefaultEdgeTxTheme() : ThemeFile(THEMES_PATH"/EdgeTX/")
|
||||
{
|
||||
setName("EdgeTX Default");
|
||||
setAuthor("EdgeTX Team");
|
||||
@@ -516,23 +507,6 @@ class DefaultEdgeTxTheme : public ThemeFile
|
||||
colorList.emplace_back(ColorEntry { COLOR_THEME_WARNING_INDEX, RGB(224, 0, 0) });
|
||||
colorList.emplace_back(ColorEntry { COLOR_THEME_DISABLED_INDEX, RGB(140, 140, 140) });
|
||||
}
|
||||
|
||||
void applyBackground() override
|
||||
{
|
||||
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/EdgeTX.png");
|
||||
return fileNames;
|
||||
}
|
||||
};
|
||||
|
||||
void ThemePersistance::insertDefaultTheme()
|
||||
|
||||
@@ -61,12 +61,9 @@ class ThemeFile
|
||||
_imageFileNames(theme._imageFileNames)
|
||||
{
|
||||
path = theme.path;
|
||||
strncpy(name, theme.name, NAME_LENGTH);
|
||||
name[NAME_LENGTH] = '\0';
|
||||
strncpy(author, theme.author, AUTHOR_LENGTH);
|
||||
author[AUTHOR_LENGTH] = '\0';
|
||||
strncpy(info, theme.info, INFO_LENGTH);
|
||||
info[INFO_LENGTH] = '\0';
|
||||
strAppend(name, theme.name, NAME_LENGTH);
|
||||
strAppend(author, theme.author, AUTHOR_LENGTH);
|
||||
strAppend(info, theme.info, INFO_LENGTH);
|
||||
}
|
||||
virtual ~ThemeFile() {}
|
||||
|
||||
@@ -101,9 +98,9 @@ class ThemeFile
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setName(std::string name) { strncpy(this->name, name.c_str(), NAME_LENGTH); }
|
||||
void setAuthor(std::string author) { strncpy(this->author, author.c_str(), AUTHOR_LENGTH); }
|
||||
void setInfo(std::string info) { strncpy(this->info, info.c_str(), INFO_LENGTH); }
|
||||
void setName(std::string name) { strAppend(this->name, name.c_str(), NAME_LENGTH); }
|
||||
void setAuthor(std::string author) { strAppend(this->author, author.c_str(), AUTHOR_LENGTH); }
|
||||
void setInfo(std::string info) { strAppend(this->info, info.c_str(), INFO_LENGTH); }
|
||||
void setPath(std::string path) { this->path = path; }
|
||||
|
||||
std::vector<ColorEntry>& getColorList() { return colorList; }
|
||||
@@ -150,7 +147,6 @@ class ThemePersistance
|
||||
|
||||
void loadDefaultTheme();
|
||||
void setDefaultTheme(int index);
|
||||
void deleteDefaultTheme();
|
||||
static char **getColorNames();
|
||||
bool deleteThemeByIndex(int index);
|
||||
bool createNewTheme(std::string name, ThemeFile &theme);
|
||||
|
||||
@@ -723,6 +723,7 @@ const char STR_MODEL_QUICK_SELECT[] = TR_MODEL_QUICK_SELECT;
|
||||
const char STR_LOADING[] = TR_LOADING;
|
||||
const char STR_DELETE_THEME[] = TR_DELETE_THEME;
|
||||
const char STR_SAVE_THEME[] = TR_SAVE_THEME;
|
||||
const char STR_EDIT_COLOR[] = TR_EDIT_COLOR;
|
||||
#endif
|
||||
|
||||
#if !defined(COLORLCD)
|
||||
|
||||
@@ -744,6 +744,7 @@ extern const char STR_MODEL_QUICK_SELECT[];
|
||||
extern const char STR_LOADING[];
|
||||
extern const char STR_DELETE_THEME[];
|
||||
extern const char STR_SAVE_THEME[];
|
||||
extern const char STR_EDIT_COLOR[];
|
||||
#endif
|
||||
extern const char STR_EXECUTE_FILE[];
|
||||
extern const char STR_DELETE_FILE[];
|
||||
|
||||
@@ -742,6 +742,7 @@
|
||||
#define TR_LOADING "加载中..."
|
||||
#define TR_DELETE_THEME "删除主题?"
|
||||
#define TR_SAVE_THEME "保存主题?"
|
||||
#define TR_EDIT_COLOR "Edit Color"
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODEL_QUICK_SELECT "快速选择模型"
|
||||
|
||||
@@ -742,6 +742,7 @@
|
||||
#define TR_LOADING "Načítání..."
|
||||
#define TR_DELETE_THEME "Smazat motiv?"
|
||||
#define TR_SAVE_THEME "Uložit motiv?"
|
||||
#define TR_EDIT_COLOR "Edit Color"
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODEL_QUICK_SELECT "Rychlý výběr modelu"
|
||||
|
||||
@@ -748,6 +748,7 @@
|
||||
#define TR_LOADING "Indlæser..."
|
||||
#define TR_DELETE_THEME "Slet tema?"
|
||||
#define TR_SAVE_THEME "Gem tema?"
|
||||
#define TR_EDIT_COLOR "Edit Color"
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODEL_QUICK_SELECT "Hurtigvalg af model"
|
||||
|
||||
@@ -740,6 +740,7 @@
|
||||
#define TR_LOADING "Wird geladen..."
|
||||
#define TR_DELETE_THEME "Theme löschen?"
|
||||
#define TR_SAVE_THEME "Theme speichern?"
|
||||
#define TR_EDIT_COLOR "Edit Color"
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODEL_QUICK_SELECT "schnelle Modellauswahl"
|
||||
|
||||
@@ -741,6 +741,7 @@
|
||||
#define TR_LOADING "Loading..."
|
||||
#define TR_DELETE_THEME "Delete Theme?"
|
||||
#define TR_SAVE_THEME "Save Theme?"
|
||||
#define TR_EDIT_COLOR "Edit Color"
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODEL_QUICK_SELECT "Model quick select"
|
||||
|
||||
@@ -742,6 +742,7 @@
|
||||
#define TR_LOADING "Loading..."
|
||||
#define TR_DELETE_THEME "Delete Theme?"
|
||||
#define TR_SAVE_THEME "Save Theme?"
|
||||
#define TR_EDIT_COLOR "Edit Color"
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODEL_QUICK_SELECT "Model quick select"
|
||||
|
||||
@@ -754,6 +754,7 @@
|
||||
#define TR_LOADING "Loading..."
|
||||
#define TR_DELETE_THEME "Delete Theme?"
|
||||
#define TR_SAVE_THEME "Save Theme?"
|
||||
#define TR_EDIT_COLOR "Edit Color"
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODEL_QUICK_SELECT "Mallin pikavalinta"
|
||||
|
||||
@@ -751,6 +751,7 @@
|
||||
#define TR_LOADING "Loading..."
|
||||
#define TR_DELETE_THEME "Delete Theme?"
|
||||
#define TR_SAVE_THEME "Save Theme?"
|
||||
#define TR_EDIT_COLOR "Edit Color"
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODEL_QUICK_SELECT "Model quick select"
|
||||
|
||||
@@ -743,6 +743,7 @@
|
||||
#define TR_LOADING "Caricamento..."
|
||||
#define TR_DELETE_THEME "Cancello Tema?"
|
||||
#define TR_SAVE_THEME "Salvo Tema?"
|
||||
#define TR_EDIT_COLOR "Edit Color"
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODEL_QUICK_SELECT "Selezione veloce modello"
|
||||
|
||||
@@ -742,6 +742,7 @@
|
||||
#define TR_LOADING "読み込み中..."
|
||||
#define TR_DELETE_THEME "テーマを削除しますか?"
|
||||
#define TR_SAVE_THEME "テーマを保存しますか?"
|
||||
#define TR_EDIT_COLOR "Edit Color"
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODEL_QUICK_SELECT "モデル クイックセレクト"
|
||||
|
||||
@@ -746,6 +746,7 @@
|
||||
#define TR_LOADING "Loading..."
|
||||
#define TR_DELETE_THEME "Delete Theme?"
|
||||
#define TR_SAVE_THEME "Save Theme?"
|
||||
#define TR_EDIT_COLOR "Edit Color"
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODEL_QUICK_SELECT "Model quick select"
|
||||
|
||||
@@ -739,6 +739,7 @@
|
||||
#define TR_LOADING "Ładowanie..."
|
||||
#define TR_DELETE_THEME "Usunąć motyw?"
|
||||
#define TR_SAVE_THEME "Zapisać motyw?"
|
||||
#define TR_EDIT_COLOR "Edit Color"
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODEL_QUICK_SELECT "Szybki wybór modelu"
|
||||
|
||||
@@ -746,6 +746,7 @@
|
||||
#define TR_LOADING "Loading..."
|
||||
#define TR_DELETE_THEME "Delete Theme?"
|
||||
#define TR_SAVE_THEME "Save Theme?"
|
||||
#define TR_EDIT_COLOR "Edit Color"
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODEL_QUICK_SELECT "Model quick select"
|
||||
|
||||
@@ -763,6 +763,7 @@
|
||||
#define TR_LOADING "Laddar..."
|
||||
#define TR_DELETE_THEME "Radera tema?"
|
||||
#define TR_SAVE_THEME "Spara tema?"
|
||||
#define TR_EDIT_COLOR "Edit Color"
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODEL_QUICK_SELECT "Snabbval av modell"
|
||||
|
||||
@@ -741,6 +741,7 @@
|
||||
#define TR_LOADING "加載中..."
|
||||
#define TR_DELETE_THEME "刪除主題?"
|
||||
#define TR_SAVE_THEME "保存主題?"
|
||||
#define TR_EDIT_COLOR "Edit Color"
|
||||
|
||||
#if defined(COLORLCD)
|
||||
#define TR_MODEL_QUICK_SELECT "快速選擇模型"
|
||||
|
||||
Reference in New Issue
Block a user