Facelift round 3

This commit is contained in:
Raphael Coeffic
2021-06-14 06:34:57 +02:00
parent 85bc612f4a
commit cd815232ee
9 changed files with 243 additions and 283 deletions
+227 -269
View File
@@ -24,9 +24,23 @@
#include "storage/modelslist.h"
#include "libopenui.h"
#define CATEGORIES_WIDTH 120
#define MODELS_LEFT 123
#define MODELS_COLUMN_WIDTH 174
#if LCD_W > LCD_H
constexpr int MODEL_CELLS_PER_LINE = 3;
#else
constexpr int MODEL_CELLS_PER_LINE = 2;
#endif
constexpr coord_t MODEL_CELL_PADDING = 6;
constexpr coord_t MODEL_SELECT_CELL_WIDTH =
(LCD_W - (MODEL_CELLS_PER_LINE + 1) * MODEL_CELL_PADDING) /
MODEL_CELLS_PER_LINE;
constexpr coord_t MODEL_SELECT_CELL_HEIGHT = 92;
constexpr coord_t MODEL_IMAGE_WIDTH = MODEL_SELECT_CELL_WIDTH;
constexpr coord_t MODEL_IMAGE_HEIGHT = 72;
enum ModelSelectMode {
MODE_SELECT_MODEL,
@@ -66,310 +80,254 @@ void setCurrentCategory(unsigned int index)
}
#endif
class ModelButton: public Button {
public:
ModelButton(FormGroup * parent, const rect_t & rect, ModelCell * modelCell, Window * footer) :
Button(parent, rect),
modelCell(modelCell)
{
load();
}
class ModelButton : public Button
{
public:
ModelButton(FormGroup *parent, const rect_t &rect, ModelCell *modelCell) :
Button(parent, rect), modelCell(modelCell)
{
load();
}
void load()
{
uint8_t version;
void load()
{
uint8_t version;
PACK(struct {
ModelHeader header;
TimerData timers[MAX_TIMERS];
}) partialModel;
const char * error = nullptr;
PACK(struct {
ModelHeader header;
TimerData timers[MAX_TIMERS];
})
partialModel;
const char *error = nullptr;
if (strncmp(modelCell->modelFilename, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME) == 0) {
memcpy(&partialModel.header, &g_model.header, sizeof(partialModel));
}
else {
error = readModel(modelCell->modelFilename, (uint8_t *)&partialModel.header, sizeof(partialModel), &version);
if (!error) {
if (modelCell->modelName[0] == '\0'
&& partialModel.header.name[0] != '\0') {
modelCell->setModelName(partialModel.header.name);
}
if (strncmp(modelCell->modelFilename, g_eeGeneral.currModelFilename,
LEN_MODEL_FILENAME) == 0) {
memcpy(&partialModel.header, &g_model.header, sizeof(partialModel));
} else {
error =
readModel(modelCell->modelFilename, (uint8_t *)&partialModel.header,
sizeof(partialModel), &version);
if (!error) {
if (modelCell->modelName[0] == '\0' &&
partialModel.header.name[0] != '\0') {
modelCell->setModelName(partialModel.header.name);
}
}
buffer = new BitmapBuffer(BMP_RGB565, MODELCELL_WIDTH, MODELCELL_HEIGHT);
if (buffer == nullptr) {
return;
}
buffer->clear(DEFAULT_BGCOLOR);
if (error) {
buffer->drawText(5, 2, "(Invalid Model)", DEFAULT_COLOR);
buffer->drawBitmapPattern(5, 23, LBM_LIBRARY_SLOT, DEFAULT_COLOR);
}
else {
char timerName[LEN_TIMER_STRING];
buffer->drawSizedText(5, 2, modelCell->modelName, LEN_MODEL_NAME, FONT(XS) | DEFAULT_COLOR);
getTimerString(timerName, 0);
for (auto & timer : partialModel.timers) {
if (timer.mode > 0 && timer.persistent) {
getTimerString(timerName, timer.value);
break;
}
}
buffer->drawText(101, 40, timerName, DEFAULT_COLOR);
for (int i = 0; i < 4; i++) {
buffer->drawBitmapPattern(104+i*11, 25, LBM_SCORE0, TITLE_BGCOLOR);
}
GET_FILENAME(filename, BITMAPS_PATH, partialModel.header.bitmap, "");
const BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(filename);
if (bitmap) {
buffer->drawScaledBitmap(bitmap, 5, 24, 56, 32);
delete bitmap;
}
else {
buffer->drawBitmapPattern(5, 23, LBM_LIBRARY_SLOT, DEFAULT_COLOR);
}
}
buffer->drawSolidHorizontalLine(5, 19, 143, LINE_COLOR);
}
void paint(BitmapBuffer * dc) override
{
FormField::paint(dc);
dc->drawBitmap(10, 2, buffer);
if (modelCell == modelslist.getCurrentModel()) {
dc->drawBitmapPattern(112, 71, LBM_ACTIVE_MODEL, TITLE_BGCOLOR);
}
}
const char * modelFilename()
{
return modelCell->modelFilename;
buffer = new BitmapBuffer(BMP_RGB565, width(), height());
if (buffer == nullptr) {
return;
}
buffer->clear(FIELD_BGCOLOR);
if (error) {
buffer->drawText(width() / 2, 2, "(Invalid Model)",
DEFAULT_COLOR | CENTERED);
} else {
buffer->drawSizedText(width() / 2, 2, modelCell->modelName,
LEN_MODEL_NAME,
DEFAULT_COLOR | CENTERED);
GET_FILENAME(filename, BITMAPS_PATH, partialModel.header.bitmap, "");
const BitmapBuffer *bitmap = BitmapBuffer::loadBitmap(filename);
if (bitmap) {
buffer->drawScaledBitmap(bitmap, 0, 0, width(), height());
delete bitmap;
} else {
buffer->drawText(width() / 2, 56, "(No Picture)",
FONT(XXS) | DEFAULT_COLOR | CENTERED);
}
}
}
void paint(BitmapBuffer *dc) override
{
FormField::paint(dc);
dc->drawBitmap(0, 0, buffer);
if (modelCell == modelslist.getCurrentModel()) {
dc->drawSolidFilledRect(0, 0, width(), 20, HIGHLIGHT_COLOR);
dc->drawSizedText(width() / 2, 2, modelCell->modelName,
LEN_MODEL_NAME,
DEFAULT_COLOR | CENTERED);
} else if (hasFocus()) {
dc->drawFilledRect(0, 0, width(), 20, SOLID, FOCUS_BGCOLOR, 5);
dc->drawSizedText(width() / 2, 2, modelCell->modelName,
LEN_MODEL_NAME,
FOCUS_COLOR | CENTERED);
}
protected:
ModelCell * modelCell;
BitmapBuffer * buffer = nullptr;
if (!hasFocus()) {
dc->drawSolidRect(0, 0, width(), height(), 1, FIELD_FRAME_COLOR);
} else {
dc->drawSolidRect(0, 0, width(), height(), 2, FOCUS_BGCOLOR);
}
}
const char *modelFilename() { return modelCell->modelFilename; }
protected:
ModelCell *modelCell;
BitmapBuffer *buffer = nullptr;
};
class ModelSelectFooter: public Window {
public:
explicit ModelSelectFooter(Window * parent):
Window(parent, {0, parent->height() - MODEL_SELECT_FOOTER_HEIGHT, LCD_W, MODEL_SELECT_FOOTER_HEIGHT}, OPAQUE)
{
}
class ModelCategoryPageBody : public FormWindow
{
public:
ModelCategoryPageBody(FormWindow *parent, const rect_t &rect,
ModelsCategory *category) :
FormWindow(parent, rect, FORM_FORWARD_FOCUS), category(category)
{
update();
}
void setCurrentModel(ModelCell * model)
{
currentModel = model;
invalidate();
}
void update(int selected = 0)
{
clear();
void paint(BitmapBuffer * dc) override
{
dc->drawSolidFilledRect(0, 0, width(), height(), DISABLE_COLOR);
uint32_t size = sdGetSize() / 100;
coord_t x = 7;
if (modelselSdFreeBitmap) {
dc->drawMask(7, 4, modelselSdFreeBitmap, DEFAULT_COLOR);
x += modelselSdFreeBitmap->width() + 3;
}
x = dc->drawNumber(x, 3, size, PREC1|FONT(XS), 0, nullptr, "GB");
x += 20;
if (modelselModelQtyBitmap) {
dc->drawMask(x, 4, modelselModelQtyBitmap, DEFAULT_COLOR);
x += modelselModelQtyBitmap->width() + 3;
}
x = dc->drawNumber(x, 3, modelslist.getModelsCount(), FONT(XS));
if (currentModel) {
x += 20;
if (modelselModelNameBitmap) {
dc->drawMask(x, 4, modelselModelNameBitmap, DEFAULT_COLOR);
x += modelselModelNameBitmap->width() + 3;
}
dc->drawText(x, 3, currentModel->modelFilename, FONT(XS) | DEFAULT_COLOR);
}
}
int index = 0;
coord_t y = MODEL_CELL_PADDING;
coord_t x = MODEL_CELL_PADDING;
protected:
ModelCell * currentModel = nullptr;
};
for (auto &model : *category) {
auto button = new ModelButton(
this, {x, y, MODEL_SELECT_CELL_WIDTH, MODEL_SELECT_CELL_HEIGHT},
model);
button->setPressHandler([=]() -> uint8_t {
if (button->hasFocus()) {
Menu *menu = new Menu(parent);
if (model != modelslist.getCurrentModel()) {
menu->addLine(STR_SELECT_MODEL, [=]() {
// we store the latest changes if any
storageFlushCurrentModel();
storageCheck(true);
memcpy(g_eeGeneral.currModelFilename, model->modelFilename,
LEN_MODEL_FILENAME);
loadModel(g_eeGeneral.currModelFilename, false);
storageDirty(EE_GENERAL);
storageCheck(true);
#if LCD_W > LCD_H
constexpr coord_t MODEL_SELECT_CELL_WIDTH = (LCD_W - 3 * PAGE_PADDING) / 2;
#else
constexpr coord_t MODEL_SELECT_CELL_WIDTH = LCD_W - 2 * PAGE_PADDING;
#endif
constexpr coord_t MODEL_SELECT_CELL_HEIGHT = 94;
class ModelCategoryPageBody: public FormWindow {
public:
ModelCategoryPageBody(FormWindow * parent, const rect_t & rect, ModelsCategory * category, ModelSelectFooter * footer):
FormWindow(parent, rect, FORM_FORWARD_FOCUS),
category(category),
footer(footer)
{
update();
}
void update(int selected = 0)
{
clear();
int index = 0;
coord_t y = PAGE_PADDING;
coord_t x = PAGE_PADDING;
coord_t h = PAGE_PADDING;
for (auto & model: * category) {
auto button = new ModelButton(this, {x, y, MODEL_SELECT_CELL_WIDTH, MODEL_SELECT_CELL_HEIGHT}, model, nullptr);
button->setFocusHandler([=](bool active) {
if (active) {
footer->setCurrentModel(model);
modelslist.setCurrentModel(model);
update(); // modelslist.getModelIndex(modelCell));
});
}
});
button->setPressHandler([=]() -> uint8_t {
if (button->hasFocus()) {
Menu * menu = new Menu(parent);
if (model != modelslist.getCurrentModel()) {
menu->addLine(STR_SELECT_MODEL, [=]() {
// we store the latest changes if any
storageFlushCurrentModel();
storageCheck(true);
memcpy(g_eeGeneral.currModelFilename, model->modelFilename, LEN_MODEL_FILENAME);
loadModel(g_eeGeneral.currModelFilename, false);
storageDirty(EE_GENERAL);
storageCheck(true);
modelslist.setCurrentModel(model);
update(); // modelslist.getModelIndex(modelCell));
});
}
menu->addLine(STR_CREATE_MODEL, getCreateModelAction());
menu->addLine(STR_DUPLICATE_MODEL, [=]() {
char duplicatedFilename[LEN_MODEL_FILENAME + 1];
memcpy(duplicatedFilename, model->modelFilename, sizeof(duplicatedFilename));
if (findNextFileIndex(duplicatedFilename, LEN_MODEL_FILENAME, MODELS_PATH)) {
sdCopyFile(model->modelFilename, MODELS_PATH, duplicatedFilename, MODELS_PATH);
modelslist.addModel(category, duplicatedFilename);
update(index);
}
else {
POPUP_WARNING("Invalid File");
}
});
// menu->addLine(STR_MOVE_MODEL);
if (model != modelslist.getCurrentModel()) {
menu->addLine(STR_DELETE_MODEL, [=]() {
new ConfirmDialog(parent, STR_DELETE_MODEL, std::string(model->modelName, sizeof(model->modelName)).c_str(), [=] {
modelslist.removeModel(category, model);
update(index > 0 ? index - 1 : 0);
menu->addLine(STR_CREATE_MODEL, getCreateModelAction());
menu->addLine(STR_DUPLICATE_MODEL, [=]() {
char duplicatedFilename[LEN_MODEL_FILENAME + 1];
memcpy(duplicatedFilename, model->modelFilename,
sizeof(duplicatedFilename));
if (findNextFileIndex(duplicatedFilename, LEN_MODEL_FILENAME,
MODELS_PATH)) {
sdCopyFile(model->modelFilename, MODELS_PATH, duplicatedFilename,
MODELS_PATH);
modelslist.addModel(category, duplicatedFilename);
update(index);
} else {
POPUP_WARNING("Invalid File");
}
});
// menu->addLine(STR_MOVE_MODEL);
if (model != modelslist.getCurrentModel()) {
menu->addLine(STR_DELETE_MODEL, [=]() {
new ConfirmDialog(
parent, STR_DELETE_MODEL,
std::string(model->modelName, sizeof(model->modelName))
.c_str(),
[=] {
modelslist.removeModel(category, model);
update(index > 0 ? index - 1 : 0);
});
});
}
}
else {
button->setFocus(SET_FOCUS_DEFAULT);
}
return 1;
});
if (selected == index) {
});
}
} else {
button->setFocus(SET_FOCUS_DEFAULT);
footer->setCurrentModel(model);
}
return 1;
});
if (selected == index) {
button->setFocus(SET_FOCUS_DEFAULT);
}
index++;
#if LCD_W > LCD_H
if (index % 2 == 0) {
x = PAGE_PADDING + MODEL_SELECT_CELL_WIDTH + PAGE_PADDING;
h += MODEL_SELECT_CELL_HEIGHT + PAGE_PADDING;
}
else {
x = PAGE_PADDING;
y += MODEL_SELECT_CELL_HEIGHT + PAGE_PADDING;
}
if (index % MODEL_CELLS_PER_LINE == 0) {
x = MODEL_CELL_PADDING;
y += MODEL_SELECT_CELL_HEIGHT + MODEL_CELL_PADDING;
} else {
x += MODEL_CELL_PADDING + MODEL_SELECT_CELL_WIDTH;
}
#else
h += MODEL_SELECT_CELL_HEIGHT + PAGE_PADDING;
y += MODEL_SELECT_CELL_HEIGHT + PAGE_PADDING;
y += MODEL_SELECT_CELL_HEIGHT + MODEL_CELL_PADDING;
#endif
index++;
}
setInnerHeight(h);
if (category->empty()) {
setFocus();
}
}
y += MODEL_SELECT_CELL_HEIGHT + MODEL_CELL_PADDING;
setInnerHeight(y);
if (category->empty()) {
setFocus();
}
}
#if defined(HARDWARE_KEYS)
void onEvent(event_t event) override
{
if (event == EVT_KEY_BREAK(KEY_ENTER)) {
Menu * menu = new Menu(this);
menu->addLine(STR_CREATE_MODEL, getCreateModelAction());
//TODO: create category?
}
else {
FormWindow::onEvent(event);
}
void onEvent(event_t event) override
{
if (event == EVT_KEY_BREAK(KEY_ENTER)) {
Menu *menu = new Menu(this);
menu->addLine(STR_CREATE_MODEL, getCreateModelAction());
// TODO: create category?
} else {
FormWindow::onEvent(event);
}
}
#endif
void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override
{
if (category->empty()) {
// use Window::setFocus() to avoid forwarding focus to nowhere
// this crashes currently in libopenui
Window::setFocus(flag, from);
}
else {
FormWindow::setFocus(flag, from);
}
void setFocus(uint8_t flag = SET_FOCUS_DEFAULT,
Window *from = nullptr) override
{
if (category->empty()) {
// use Window::setFocus() to avoid forwarding focus to nowhere
// this crashes currently in libopenui
Window::setFocus(flag, from);
} else {
FormWindow::setFocus(flag, from);
}
}
protected:
ModelsCategory * category;
ModelSelectFooter * footer;
std::function<void(void)> getCreateModelAction()
{
return [=]() {
storageCheck(true);
modelslist.setCurrentModel(modelslist.addModel(category, createModel()));
#if defined(LUA)
// chainMenu(menuModelWizard);
#endif
update(category->size() - 1);
};
}
protected:
ModelsCategory *category;
std::function<void(void)> getCreateModelAction()
{
return [=]() {
storageCheck(true);
modelslist.setCurrentModel(modelslist.addModel(category, createModel()));
update(category->size() - 1);
};
}
};
class ModelCategoryPage: public PageTab {
public:
explicit ModelCategoryPage(ModelsCategory * category) :
PageTab(category->name, ICON_MODEL),
category(category)
{
}
class ModelCategoryPage : public PageTab
{
public:
explicit ModelCategoryPage(ModelsCategory *category) :
PageTab(category->name, ICON_MODEL), category(category)
{
}
protected:
ModelsCategory * category;
protected:
ModelsCategory *category;
void build(FormWindow * window) override
{
auto footer = new ModelSelectFooter(window);
new ModelCategoryPageBody(window, {0, 0, LCD_W, window->height() - MODEL_SELECT_FOOTER_HEIGHT}, category, footer);
}
void build(FormWindow *window) override
{
new ModelCategoryPageBody(
window, {0, 0, LCD_W, window->height()},
category);
}
};
ModelSelectMenu::ModelSelectMenu():
TabsGroup(ICON_MODEL)
{
+2 -2
View File
@@ -26,11 +26,11 @@ PageHeader::PageHeader(Page * parent, uint8_t icon):
FormGroup(parent, { 0, 0, LCD_W, MENU_HEADER_HEIGHT }, OPAQUE),
icon(icon)
#if defined(HARDWARE_TOUCH)
, back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, ICON_BACK,
, back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH },
[=]() -> uint8_t {
parent->deleteLater();
return 0;
}, NO_FOCUS)
}, NO_FOCUS | FORM_NO_BORDER)
#endif
{
}
+1 -1
View File
@@ -51,7 +51,7 @@ class PageHeader: public FormGroup
protected:
uint8_t icon;
#if defined(HARDWARE_TOUCH)
IconButton back;
Button back;
#endif
};
+4 -3
View File
@@ -164,14 +164,15 @@ void RadioSdManagerPage::build(FormWindow * window)
// sort directories and files
directories.sort(compare_nocase);
files.sort(compare_nocase);
for (auto name: directories) {
new TextButton(window, grid.getLabelSlot(), name, [=]() -> uint8_t {
auto b = new TextButton(window, grid.getLabelSlot(), name, [=]() -> uint8_t {
f_chdir(name.data());
window->clear();
build(window);
return 0;
}, 0);
}, OPAQUE);
b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; });
grid.nextLine();
}
+3 -2
View File
@@ -115,7 +115,7 @@ void RadioToolsPage::rebuild(FormWindow * window)
new StaticText(window, grid.getLabelSlot(), "lua", BUTTON_BACKGROUND, CENTERED);
std::string path_str(path);
new TextButton(
auto b = new TextButton(
window, grid.getFieldSlot(1), label,
[window, path_str]() -> uint8_t {
f_chdir("/SCRIPTS/TOOLS/");
@@ -124,7 +124,8 @@ void RadioToolsPage::rebuild(FormWindow * window)
StandaloneLuaWindow::instance()->attach(window);
return 0;
},
0);
OPAQUE);
b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; });
grid.nextLine();
}
}
+2 -2
View File
@@ -32,12 +32,12 @@
TabsGroupHeader::TabsGroupHeader(TabsGroup * parent, uint8_t icon):
FormGroup(parent, { 0, 0, LCD_W, MENU_BODY_TOP }, OPAQUE),
#if defined(HARDWARE_TOUCH)
back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, ICON_BACK,
back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH },
[=]() -> uint8_t {
parent->deleteLater();
ViewMain::instance()->setFocus((SET_FOCUS_DEFAULT));
return 1;
}, NO_FOCUS),
}, NO_FOCUS | FORM_NO_BORDER),
#endif
icon(icon),
carousel(this, parent)
+1 -1
View File
@@ -155,7 +155,7 @@ class TabsGroupHeader: public FormGroup {
protected:
#if defined(HARDWARE_TOUCH)
IconButton back;
Button back;
#endif
uint8_t icon;
TabsCarousel carousel;
+1 -1
View File
@@ -53,7 +53,7 @@ class TextWidget: public Widget
const ZoneOption TextWidget::options[] = {
{ STR_TEXT, ZoneOption::String, OPTION_VALUE_STRING(TEXT_WIDGET_DEFAULT_LABEL) },
{ STR_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(RED) },
{ STR_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(DEFAULT_COLOR>>16) },
{ STR_SIZE, ZoneOption::TextSize, OPTION_VALUE_UNSIGNED(0) },
{ STR_SHADOW, ZoneOption::Bool, OPTION_VALUE_BOOL(false) },
{ nullptr, ZoneOption::Bool }
+2 -2
View File
@@ -29,8 +29,8 @@
#include "sdcard_raw.h"
#endif
#define MODELCELL_WIDTH 172
#define MODELCELL_HEIGHT 59
// #define MODELCELL_WIDTH 172
// #define MODELCELL_HEIGHT 59
// modelXXXXXXX.bin F,FF F,3F,FF\r\n
#define LEN_MODELS_IDX_LINE (LEN_MODEL_FILENAME + sizeof(" F,FF F,3F,FF\r\n")-1)