model select: simpler implementation

Use `LV_FLEX_FLOW_ROW_WRAP` to get all the buttons laid out properly.
This commit is contained in:
Raphael Coeffic
2022-06-11 15:38:08 +02:00
committed by Raphael Coeffic
parent a013fccc78
commit c222d66855
+149 -196
View File
@@ -37,8 +37,8 @@ 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;
// constexpr coord_t MODEL_IMAGE_WIDTH = MODEL_SELECT_CELL_WIDTH;
// constexpr coord_t MODEL_IMAGE_HEIGHT = 72;
constexpr size_t LEN_INFO_TEXT = 300;
constexpr size_t LEN_PATH = sizeof(TEMPLATES_PATH) + TEXT_FILENAME_MAXLEN;
constexpr size_t LEN_BUFFER = sizeof(TEMPLATES_PATH) + 2 * TEXT_FILENAME_MAXLEN + 1;
@@ -87,7 +87,7 @@ class TemplatePage : public Page
unsigned int count = 0;
static std::function<void(void)> update;
void paint(BitmapBuffer *dc)
void paint(BitmapBuffer *dc) override
{
Page::paint(dc);
rect_t rect = body.getRect();
@@ -321,6 +321,9 @@ class ModelButton : public Button
ModelButton(FormGroup *parent, const rect_t &rect, ModelCell *modelCell) :
Button(parent, rect), modelCell(modelCell)
{
setWidth(MODEL_SELECT_CELL_WIDTH);
setHeight(MODEL_SELECT_CELL_HEIGHT);
load();
}
@@ -439,229 +442,181 @@ class ModelButton : public Button
}
}
const char *modelFilename() { return modelCell->modelFilename; }
const char* modelFilename() { return modelCell->modelFilename; }
ModelCell* getModelCell() const { return modelCell; }
protected:
ModelCell *modelCell;
BitmapBuffer *buffer = nullptr;
};
static void _saveTemplate(ModelCell* model)
{
storageDirty(EE_MODEL);
storageCheck(true);
constexpr size_t size = sizeof(model->modelName) + sizeof(YAML_EXT);
char modelName[size];
snprintf(modelName, size, "%s%s", model->modelName, YAML_EXT);
char templatePath[FF_MAX_LFN];
snprintf(templatePath, FF_MAX_LFN, "%s%c%s", PERS_TEMPL_PATH, '/', modelName);
sdCheckAndCreateDirectory(TEMPLATES_PATH);
sdCheckAndCreateDirectory(PERS_TEMPL_PATH);
if (isFileAvailable(templatePath)) {
new ConfirmDialog(Layer::back(), STR_FILE_EXISTS, STR_ASK_OVERWRITE, [=] {
sdCopyFile(model->modelFilename, MODELS_PATH, modelName, PERS_TEMPL_PATH);
});
} else {
sdCopyFile(model->modelFilename, MODELS_PATH, modelName, PERS_TEMPL_PATH);
}
}
class ModelCategoryPageBody : public FormWindow
{
public:
ModelCategoryPageBody(FormWindow *parent, const rect_t &rect,
ModelsCategory *category) :
FormWindow(parent, rect//, FORM_FORWARD_FOCUS
), category(category)
ModelCategoryPageBody(Window* parent, ModelsCategory* category) :
FormWindow(parent, rect_t{}), category(category)
{
update();
setFlexLayout(LV_FLEX_FLOW_ROW_WRAP, MODEL_CELL_PADDING);
lv_obj_set_style_pad_row(lvobj, MODEL_CELL_PADDING, 0);
lv_obj_set_style_pad_all(lvobj, MODEL_CELL_PADDING, 0);
for (auto &model : *category)
addModelButton(model);
}
void update(int selected = -1)
protected:
ModelsCategory *category;
void addModelButton(ModelCell *model)
{
clear();
auto btn = new ModelButton(this, rect_t{}, model);
btn->setPressHandler([=]() -> uint8_t {
modelMenu(btn);
return 0;
});
}
if (selected < 0) {
auto model = modelslist.getCurrentModel();
selected = category->getModelIndex(model);
if (selected < 0) selected = 0;
void modelMenu(ModelButton* btn)
{
Menu *menu = new Menu(parent);
ModelCell* model = btn->getModelCell();
if (model != modelslist.getCurrentModel()) {
menu->addLine(STR_SELECT_MODEL, [=]() { selectModel(btn); });
}
menu->addLine(STR_CREATE_MODEL, [=]() { createModel(); });
menu->addLine(STR_DUPLICATE_MODEL, [=]() { duplicateModel(model); });
menu->addLine(STR_SAVE_TEMPLATE, std::bind(_saveTemplate, model));
if (model != modelslist.getCurrentModel()) {
// Move
if(modelslist.getCategories().size() > 1) {
menu->addLine(STR_MOVE_MODEL, [=]() { moveModel(btn); });
}
menu->addLine(STR_DELETE_MODEL, [=]() { deleteModel(btn); });
}
}
void selectModel(ModelButton* btn)
{
bool modelConnected =
TELEMETRY_STREAMING() && !g_eeGeneral.disableRssiPoweroffAlarm;
if (modelConnected) {
AUDIO_ERROR_MESSAGE(AU_MODEL_STILL_POWERED);
if (!confirmationDialog(STR_MODEL_STILL_POWERED, nullptr, false, []() {
tmr10ms_t startTime = getTicks();
while (!TELEMETRY_STREAMING()) {
if (getTicks() - startTime > TELEMETRY_CHECK_DELAY10ms) break;
}
return !TELEMETRY_STREAMING() ||
g_eeGeneral.disableRssiPoweroffAlarm;
})) {
return; // stop if connected but not confirmed
}
}
int index = 0;
coord_t y = MODEL_CELL_PADDING;
coord_t x = MODEL_CELL_PADDING;
// store changes (if any) and load selected model
storageFlushCurrentModel();
storageCheck(true);
memcpy(g_eeGeneral.currModelFilename, btn->modelFilename(),
LEN_MODEL_FILENAME);
ModelButton* selectButton = 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 {
Menu *menu = new Menu(parent);
if (model != modelslist.getCurrentModel()) {
menu->addLine(STR_SELECT_MODEL, [=]() {
bool modelConnected = TELEMETRY_STREAMING() &&
!g_eeGeneral.disableRssiPoweroffAlarm;
if (modelConnected) {
AUDIO_ERROR_MESSAGE(AU_MODEL_STILL_POWERED);
if (!confirmationDialog(
STR_MODEL_STILL_POWERED, nullptr, false, []() {
tmr10ms_t startTime = getTicks();
while (!TELEMETRY_STREAMING()) {
if (getTicks() - startTime > TELEMETRY_CHECK_DELAY10ms)
break;
}
return !TELEMETRY_STREAMING() ||
g_eeGeneral.disableRssiPoweroffAlarm;
})) {
return; // stop if connected but not confirmed
}
}
loadModel(g_eeGeneral.currModelFilename, false);
storageDirty(EE_GENERAL);
storageCheck(true);
// store changes (if any) and load selected model
storageFlushCurrentModel();
storageCheck(true);
memcpy(g_eeGeneral.currModelFilename, model->modelFilename,
LEN_MODEL_FILENAME);
modelslist.setCurrentModel(btn->getModelCell());
modelslist.setCurrentCategory(category);
checkAll();
loadModel(g_eeGeneral.currModelFilename, false);
storageDirty(EE_GENERAL);
storageCheck(true);
// Exit to main view
auto w = Layer::back();
if (w) w->onCancel();
}
modelslist.setCurrentModel(model);
modelslist.setCurrentCategory(category);
menu->deleteLater();
onEvent(EVT_KEY_FIRST(KEY_EXIT));
checkAll();
});
}
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_SAVE_TEMPLATE, [=]() {
storageDirty(EE_MODEL);
storageCheck(true);
constexpr size_t size = sizeof(model->modelName) + sizeof(YAML_EXT);
char modelName[size];
snprintf(modelName, size, "%s%s", model->modelName, YAML_EXT);
char templatePath[FF_MAX_LFN];
snprintf(templatePath, FF_MAX_LFN, "%s%c%s", PERS_TEMPL_PATH, '/', modelName);
sdCheckAndCreateDirectory(TEMPLATES_PATH);
sdCheckAndCreateDirectory(PERS_TEMPL_PATH);
if (isFileAvailable(templatePath)) {
new ConfirmDialog(parent, STR_FILE_EXISTS, STR_ASK_OVERWRITE,
[=] {
sdCopyFile(model->modelFilename, MODELS_PATH, modelName, PERS_TEMPL_PATH);
});
} else {
sdCopyFile(model->modelFilename, MODELS_PATH, modelName, PERS_TEMPL_PATH);
}
});
if (model != modelslist.getCurrentModel()) {
// Move
if(modelslist.getCategories().size() > 1) {
menu->addLine(STR_MOVE_MODEL, [=]() {
auto moveToMenu = new Menu(parent);
moveToMenu->setTitle(STR_MOVE_MODEL);
for (auto newcategory: modelslist.getCategories()) {
if(category != newcategory) {
moveToMenu->addLine(std::string(newcategory->name, sizeof(newcategory->name)), [=]() {
modelslist.moveModel(model, category, newcategory);
update(index < (int)category->size() - 1 ? index : index - 1);
modelslist.save();
});
}
}
});
}
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 < (int)category->size() - 1 ? index : index - 1);
});
});
}
return 1;
void createModel()
{
storageCheck(true);
auto model = modelslist.addModel(category, ::createModel(), false);
modelslist.setCurrentModel(model);
new SelectTemplateFolder([=]() {
model->setModelName(g_model.header.name);
modelslist.save();
addModelButton(model);
});
}
if (selected == index) {
selectButton = button;
}
index++;
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;
}
void duplicateModel(ModelCell* 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);
auto new_model = modelslist.addModel(category, duplicatedFilename);
addModelButton(new_model);
} else {
POPUP_WARNING("Invalid File"); // TODO: translation
}
}
if (index % MODEL_CELLS_PER_LINE != 0) {
y += MODEL_SELECT_CELL_HEIGHT + MODEL_CELL_PADDING;
}
void deleteModel(ModelButton* btn)
{
ModelCell* model = btn->getModelCell();
new ConfirmDialog(
parent, STR_DELETE_MODEL,
std::string(model->modelName, sizeof(model->modelName)).c_str(), [=] {
modelslist.removeModel(category, model);
btn->deleteLater();
});
}
// if (category->empty()) {
// setFocus();
// } else
if (selectButton) {
lv_group_focus_obj(selectButton->getLvObj());
// selectButton->setFocus();
void moveModel(ModelButton* btn)
{
auto moveToMenu = new Menu(parent);
moveToMenu->setTitle(STR_MOVE_MODEL);
auto model = btn->getModelCell();
for (auto newcategory : modelslist.getCategories()) {
if (category != newcategory) {
moveToMenu->addLine(
std::string(newcategory->name, sizeof(newcategory->name)), [=]() {
modelslist.moveModel(model, category, newcategory);
modelslist.save();
btn->deleteLater();
});
}
}
}
void addFirstModel() {
Menu *menu = new Menu(this);
menu->addLine(STR_CREATE_MODEL, getCreateModelAction());
menu->addLine(STR_CREATE_MODEL, [=]() { createModel(); });
}
#if defined(HARDWARE_KEYS)
void onEvent(event_t event) override
void onClicked() override
{
if (event == EVT_KEY_BREAK(KEY_ENTER)) {
addFirstModel();
} else {
FormWindow::onEvent(event);
}
}
#endif
#if defined(HARDWARE_TOUCH)
bool onTouchEnd(coord_t x, coord_t y) override
{
if(category->size() == 0)
addFirstModel();
else
FormWindow::onTouchEnd(x,y);
return true;
}
#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);
// }
// }
protected:
ModelsCategory *category;
std::function<void(void)> getCreateModelAction()
{
return [=]() {
storageCheck(true);
auto model = modelslist.addModel(category, createModel(), false);
modelslist.setCurrentModel(model);
new SelectTemplateFolder([=]() {
model->setModelName(g_model.header.name);
modelslist.save();
update(category->size() - 1);
});
};
if (category->size() == 0) addFirstModel();
}
};
@@ -678,9 +633,7 @@ class ModelCategoryPage : public PageTab
void build(FormWindow *window) override
{
new ModelCategoryPageBody(
window, {0, 0, LCD_W, window->height()},
category);
new ModelCategoryPageBody(window, category);
}
};