model select: simpler implementation
Use `LV_FLEX_FLOW_ROW_WRAP` to get all the buttons laid out properly.
This commit is contained in:
committed by
Raphael Coeffic
parent
a013fccc78
commit
c222d66855
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user