feat(color): New label based Model Selector (#1374)
* Model Labels Selector * Portrait Layout * Receiver ID Check * Fix Labels .yml format * Companion Initial Commit commit 967ddf3979b4954402fe62c858faba4b7a416bba Author: dlktdr <cblackburn320@gmail.com> Date: Mon Aug 29 01:49:15 2022 -0700 Fix Labels .yml format commit 534221cad9e01146c47a9ac1080a655e04f1f6c8 Author: dlktdr <cblackburn320@gmail.com> Date: Mon Aug 29 01:20:57 2022 -0700 Receiver ID Check commit f32c0939d44e9ae60bc0705a9176354c23fec198 Author: dlktdr <cblackburn320@gmail.com> Date: Sun Aug 28 23:13:25 2022 -0700 Portrait Layout commit 961181217c7841ff3f21299ac6aa419b810f80e9 Author: dlktdr <cblackburn320@gmail.com> Date: Sat Dec 11 16:56:13 2021 -0800 Model Labels Selector commitdc99dd40d2Author: David PHAM-VAN <1387855+DavBfr@users.noreply.github.com> Date: Fri Aug 26 03:04:34 2022 -0300 feat: Allow Lua widget to override built-in widget with same name (#2208) * Allow CPP Widgets Override with LUA * Reset a widget option it the type differs commit4b747caff9Author: eshifri <46511024+eshifri@users.noreply.github.com> Date: Thu Aug 25 19:47:53 2022 -0700 fix: Write model name into models.yml when duplicating model (#2224) * Update model name in the models.yml file (#2214) * Fix the length of the name. commite97f23a305Author: eshifri <46511024+eshifri@users.noreply.github.com> Date: Wed Aug 24 21:19:52 2022 -0700 fix(cpn): Fix full path to model notes for Simulator (#2223) * Correct Highlighted Column * labels .yml indentation * Update da.h New defines translated to Danish * Update dataconstants.h Omit warnings due to difference of LEN_MODEL_FILENAME and LEN_MODEL_NAME in modellist.cpp * Update modelslist.cpp Wonder if this are the right file in PR #1374 (?), seems a lot are changed to the better in #1374 * Update dataconstants.h Restore #define for LEN_MODEL_FILENAME to 16 * Rebase, Correct Issues Delete now deletes instead of select New model stores correct information Duplicate save all information in cell Rework items to use cell over button Reword function variables * CMPN: Add context menu Fix output not functional. Removed Yaml::null at some point :) Fix some compiler warnings * CMPN: Compiler warning, replace ,->_ in list * FW: Various Changes Change view to a grid Don't allow comma in label name Add scroll indicators Save sort order Co-authored-by: HThuren <99370924+HThuren@users.noreply.github.com>
This commit is contained in:
@@ -67,6 +67,7 @@ set(GUI_SRC
|
||||
model_outputs.cpp
|
||||
model_curves.cpp
|
||||
model_gvars.cpp
|
||||
model_templates.cpp
|
||||
model_logical_switches.cpp
|
||||
special_functions.cpp
|
||||
model_telemetry.cpp
|
||||
|
||||
@@ -595,8 +595,8 @@ void drawTextLines(BitmapBuffer * dc, coord_t left, coord_t top, coord_t width,
|
||||
case '(':
|
||||
case '{':
|
||||
case '[':
|
||||
case ' ':
|
||||
nxt++;
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\0':
|
||||
done = true;
|
||||
|
||||
@@ -107,6 +107,18 @@ void ListBase::setSelected(int selected)
|
||||
select(selected, 0);
|
||||
}
|
||||
|
||||
void ListBase::setSelected(std::set<uint32_t> selected)
|
||||
{
|
||||
if(!multiSelect) return;
|
||||
|
||||
for(int i=0; i < getRowCount(); i++) {
|
||||
if(selected.find(i) != selected.end())
|
||||
lv_table_add_cell_ctrl(lvobj, i, 0, LV_TABLE_CELL_CTRL_CUSTOM_1);
|
||||
else
|
||||
lv_table_clear_cell_ctrl(lvobj, i, 0, LV_TABLE_CELL_CTRL_CUSTOM_1);
|
||||
}
|
||||
}
|
||||
|
||||
int ListBase::getSelected() const
|
||||
{
|
||||
uint16_t row, col;
|
||||
@@ -115,6 +127,17 @@ int ListBase::getSelected() const
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::set<uint32_t> ListBase::getSelection()
|
||||
{
|
||||
if(!multiSelect) return std::set<uint32_t>();
|
||||
std::set<uint32_t> selectedIndexes;
|
||||
for(int i=0; i < getRowCount(); i++) {
|
||||
if(lv_table_has_cell_ctrl(lvobj, i, 0, LV_TABLE_CELL_CTRL_CUSTOM_1))
|
||||
selectedIndexes.insert(i);
|
||||
}
|
||||
return selectedIndexes;
|
||||
}
|
||||
|
||||
void ListBase::setActiveItem(int item)
|
||||
{
|
||||
if (item != activeItem) {
|
||||
@@ -133,7 +156,20 @@ void ListBase::onPress(uint16_t row, uint16_t col)
|
||||
if (row == LV_TABLE_CELL_NONE) return;
|
||||
|
||||
TRACE("SHORT_PRESS");
|
||||
if (pressHandler) { pressHandler(); }
|
||||
|
||||
if(multiSelect && row < getRowCount()) {
|
||||
std::set<uint32_t> lastSelection = getSelection();
|
||||
|
||||
bool chk = lv_table_has_cell_ctrl(lvobj, row, 0, LV_TABLE_CELL_CTRL_CUSTOM_1);
|
||||
if(chk) lv_table_clear_cell_ctrl(lvobj, row, 0, LV_TABLE_CELL_CTRL_CUSTOM_1);
|
||||
else lv_table_add_cell_ctrl(lvobj, row, 0, LV_TABLE_CELL_CTRL_CUSTOM_1);
|
||||
|
||||
if(_multiSelectHandler) {
|
||||
_multiSelectHandler(getSelection(),lastSelection);
|
||||
}
|
||||
} else {
|
||||
if (pressHandler) { pressHandler(); }
|
||||
}
|
||||
}
|
||||
|
||||
void ListBase::onLongPressed()
|
||||
@@ -147,6 +183,7 @@ void ListBase::onClicked()
|
||||
{
|
||||
if (!getAutoEdit()) {
|
||||
lv_group_set_editing((lv_group_t*)lv_obj_get_group(lvobj), true);
|
||||
|
||||
} else {
|
||||
TableField::onClicked();
|
||||
}
|
||||
@@ -164,7 +201,9 @@ void ListBase::onCancel()
|
||||
|
||||
void ListBase::onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc)
|
||||
{
|
||||
if (row != activeItem) return;
|
||||
if ((multiSelect == false && row != activeItem) ||
|
||||
(multiSelect == true && !lv_table_has_cell_ctrl(lvobj, dsc->id, 0, LV_TABLE_CELL_CTRL_CUSTOM_1)))
|
||||
return;
|
||||
|
||||
lv_area_t coords;
|
||||
lv_coord_t area_h = lv_area_get_height(dsc->draw_area);
|
||||
|
||||
@@ -35,6 +35,7 @@ class ListBase : public TableField
|
||||
{
|
||||
std::function<void()> longPressHandler = nullptr;
|
||||
std::function<void()> pressHandler = nullptr;
|
||||
std::function<void(std::set<uint32_t>, std::set<uint32_t>)> _multiSelectHandler = nullptr;
|
||||
bool autoEdit = false;
|
||||
|
||||
public:
|
||||
@@ -49,11 +50,23 @@ class ListBase : public TableField
|
||||
void setLineHeight(uint8_t height);
|
||||
|
||||
virtual void setSelected(int selected);
|
||||
virtual void setSelected(std::set<uint32_t> selected);
|
||||
|
||||
int getSelected() const;
|
||||
std::set<uint32_t> getSelection();
|
||||
|
||||
void setMultiSelect(bool mode) {
|
||||
multiSelect = mode;
|
||||
}
|
||||
|
||||
virtual void setActiveItem(int item);
|
||||
int getActiveItem() const;
|
||||
|
||||
|
||||
void setMultiSelectHandler(std::function<void(std::set<uint32_t>, std::set<uint32_t>)> handler)
|
||||
{
|
||||
_multiSelectHandler = std::move(handler);
|
||||
}
|
||||
|
||||
void setLongPressHandler(std::function<void()> handler)
|
||||
{
|
||||
longPressHandler = std::move(handler);
|
||||
@@ -71,6 +84,7 @@ class ListBase : public TableField
|
||||
protected:
|
||||
static void event_cb(lv_event_t* e);
|
||||
int activeItem = -1;
|
||||
bool multiSelect = false;
|
||||
|
||||
void onPress(uint16_t row, uint16_t col) override;
|
||||
void onLongPressed();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,12 +22,100 @@
|
||||
#ifndef _MODEL_SELECT_H_
|
||||
#define _MODEL_SELECT_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include "libopenui.h"
|
||||
#include "listbox.h"
|
||||
#include "storage/modelslist.h"
|
||||
#include "tabsgroup.h"
|
||||
|
||||
class ModelSelectMenu: public TabsGroup {
|
||||
public:
|
||||
ModelSelectMenu();
|
||||
void build(int index=-1);
|
||||
constexpr int MAX_LABEL_SIZE = 20;
|
||||
|
||||
class ModelButton;
|
||||
|
||||
class ModelsPageBody : public FormWindow
|
||||
{
|
||||
public:
|
||||
ModelsPageBody(Window *parent, const rect_t &rect);
|
||||
|
||||
void setLabels(LabelsVector labels)
|
||||
{
|
||||
selectedLabels = labels;
|
||||
update();
|
||||
}
|
||||
void update(int selected = -1);
|
||||
|
||||
inline void setSortOrder(ModelsSortBy sortOrder)
|
||||
{
|
||||
modelslabels.setSortOrder(sortOrder);
|
||||
update();
|
||||
}
|
||||
|
||||
void setLblRefreshFunc(std::function<void()> fnc)
|
||||
{
|
||||
refreshLabels = std::move(fnc);
|
||||
}
|
||||
|
||||
protected:
|
||||
ModelsSortBy _sortOrder;
|
||||
bool isDirty = false;
|
||||
bool refresh = false;
|
||||
std::string selectedLabel;
|
||||
LabelsVector selectedLabels;
|
||||
std::function<void()> refreshLabels = nullptr;
|
||||
|
||||
void selectModel(ModelCell* model);
|
||||
void duplicateModel(ModelCell* model);
|
||||
void deleteModel(ModelCell* model);
|
||||
void editLabels(ModelCell* model);
|
||||
};
|
||||
|
||||
#endif // _MODEL_SELECT_H_
|
||||
class ModelLabelsWindow : public Page
|
||||
{
|
||||
public:
|
||||
ModelLabelsWindow();
|
||||
|
||||
protected:
|
||||
ModelsSortBy sort = DEFAULT_MODEL_SORT;
|
||||
char tmpLabel[MAX_LABEL_SIZE + 1] = "\0";
|
||||
ListBox *lblselector;
|
||||
ModelsPageBody *mdlselector;
|
||||
std::string currentLabel;
|
||||
|
||||
LabelsVector getLabels()
|
||||
{
|
||||
auto labels = modelslabels.getLabels();
|
||||
if (modelslabels.getUnlabeledModels().size() > 0)
|
||||
labels.emplace_back(STR_UNLABELEDMODEL);
|
||||
return labels;
|
||||
}
|
||||
|
||||
void newModel();
|
||||
void buildHead(PageHeader *window);
|
||||
void buildBody(FormWindow *window);
|
||||
void updateFilteredLabels(std::set<uint32_t> selected, bool setdirty = true);
|
||||
void labelRefreshRequest();
|
||||
};
|
||||
|
||||
class ProgressDialog : public Dialog
|
||||
{
|
||||
uint32_t lastUpdate = 0;
|
||||
Progress *progress;
|
||||
|
||||
std::function<void()> onClose;
|
||||
|
||||
public:
|
||||
ProgressDialog(Window *parent, std::string title,
|
||||
std::function<void()> onClose);
|
||||
|
||||
void updateProgress(const char *filename, int percentage);
|
||||
|
||||
// disable keys
|
||||
void onEvent(event_t) override;
|
||||
|
||||
protected:
|
||||
std::string _title;
|
||||
};
|
||||
|
||||
#endif // _MODEL_SELECT_H_
|
||||
|
||||
@@ -45,11 +45,9 @@ ModelSetupPage::ModelSetupPage() :
|
||||
|
||||
static void onModelNameChanged()
|
||||
{
|
||||
modelslist.load();
|
||||
auto model = modelslist.getCurrentModel();
|
||||
if (model) {
|
||||
model->setModelName(g_model.header.name);
|
||||
modelslist.save();
|
||||
}
|
||||
SET_DIRTY();
|
||||
}
|
||||
@@ -233,6 +231,31 @@ void ModelSetupPage::build(FormWindow * window)
|
||||
new StaticText(line, rect_t{}, STR_MODELNAME, 0, COLOR_THEME_PRIMARY1);
|
||||
new ModelNameEdit(line, rect_t{});
|
||||
|
||||
// Model labels
|
||||
line = window->newLine(&grid);
|
||||
new StaticText(line, rect_t{}, STR_LABELS, 0, COLOR_THEME_PRIMARY1);
|
||||
auto curmod = modelslist.getCurrentModel();
|
||||
labelTextButton =
|
||||
new TextButton(line, rect_t{}, modelslabels.getLabelString(curmod ,STR_UNLABELEDMODEL), [=] () {
|
||||
Menu *menu = new Menu(window, true);
|
||||
menu->setTitle(STR_LABELS);
|
||||
for (auto &label: modelslabels.getLabels()) {
|
||||
menu->addLine(label,
|
||||
[=] () {
|
||||
if (!modelslabels.isLabelSelected(label, curmod))
|
||||
modelslabels.addLabelToModel(label, curmod);
|
||||
else
|
||||
modelslabels.removeLabelFromModel(label, curmod);
|
||||
labelTextButton->setText(modelslabels.getLabelString(curmod,STR_UNLABELEDMODEL));
|
||||
strcpy(g_model.header.labels, modelslabels.getLabelString(curmod,STR_UNLABELEDMODEL).c_str());
|
||||
SET_DIRTY();
|
||||
}, [=] () {
|
||||
return modelslabels.isLabelSelected(label, curmod);
|
||||
});
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
// Bitmap
|
||||
line = window->newLine(&grid);
|
||||
new StaticText(line, rect_t{}, STR_BITMAP, 0, COLOR_THEME_PRIMARY1);
|
||||
|
||||
@@ -26,4 +26,6 @@ class ModelSetupPage: public PageTab {
|
||||
ModelSetupPage();
|
||||
|
||||
void build(FormWindow * window) override;
|
||||
private:
|
||||
TextButton *labelTextButton = nullptr;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "model_templates.h"
|
||||
#include "standalone_lua.h"
|
||||
#include "str_functions.h"
|
||||
|
||||
char TemplatePage::path[LEN_PATH + 1];
|
||||
std::function<void(void)> TemplatePage::update = nullptr;
|
||||
|
||||
|
||||
void TemplatePage::updateInfo()
|
||||
{
|
||||
FIL fp;
|
||||
FRESULT res = f_open (&fp, buffer, FA_READ);
|
||||
unsigned int bytesRead = 0;
|
||||
if (res == FR_OK) {
|
||||
f_read (&fp, infoText, LEN_INFO_TEXT, &bytesRead);
|
||||
f_close(&fp);
|
||||
}
|
||||
infoText[bytesRead] = '\0';
|
||||
invalidate();
|
||||
}
|
||||
|
||||
#if defined(HARDWARE_KEYS)
|
||||
void TemplatePage::onEvent(event_t event)
|
||||
{
|
||||
if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) {
|
||||
killEvents(event);
|
||||
deleteLater();
|
||||
} else {
|
||||
Page::onEvent(event);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void TemplatePage::paint(BitmapBuffer *dc)
|
||||
{
|
||||
Page::paint(dc);
|
||||
rect_t rect = body.getRect();
|
||||
coord_t x = LCD_W / 2 + PAGE_PADDING;
|
||||
coord_t y = rect.y + PAGE_PADDING;
|
||||
coord_t w = LCD_W / 2 - 2 * PAGE_PADDING;
|
||||
coord_t h = rect.h - 2 * PAGE_PADDING;
|
||||
if (count > 0) {
|
||||
if (infoText[0] == '\0')
|
||||
drawTextLines(dc, x, y, w, h, STR_NO_INFORMATION, COLOR_THEME_DISABLED);
|
||||
else
|
||||
drawTextLines(dc, x, y, w, h, infoText, COLOR_THEME_PRIMARY1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SelectTemplate::SelectTemplate(TemplatePage* tp)
|
||||
: templateFolderPage(tp)
|
||||
{
|
||||
rect_t rect = {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + 10, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT};
|
||||
new StaticText(&header, rect, STR_SELECT_TEMPLATE, 0, COLOR_THEME_PRIMARY2);
|
||||
|
||||
FormGridLayout grid;
|
||||
grid.spacer(PAGE_PADDING);
|
||||
std::list<std::string> files;
|
||||
FILINFO fno;
|
||||
DIR dir;
|
||||
FRESULT res = f_opendir(&dir, path);
|
||||
|
||||
if (res == FR_OK) {
|
||||
// read all entries
|
||||
bool firstTime = true;
|
||||
for (;;) {
|
||||
res = sdReadDir(&dir, &fno, firstTime);
|
||||
firstTime = false;
|
||||
if (res != FR_OK || fno.fname[0] == 0)
|
||||
break; // Break on error or end of dir
|
||||
if (strlen((const char*)fno.fname) > SD_SCREEN_FILE_LENGTH)
|
||||
continue;
|
||||
if (fno.fname[0] == '.')
|
||||
continue;
|
||||
if (!(fno.fattrib & AM_DIR)) {
|
||||
const char *ext = getFileExtension(fno.fname);
|
||||
if(ext && !strcasecmp(ext, YAML_EXT)) {
|
||||
int len = ext - fno.fname;
|
||||
if (len < FF_MAX_LFN) {
|
||||
char name[FF_MAX_LFN] = { 0 };
|
||||
strncpy(name, fno.fname, len);
|
||||
files.push_back(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
files.sort(compare_nocase);
|
||||
|
||||
for (auto name: files) {
|
||||
auto tb = new TemplateButton(&body, grid.getLabelSlot(), name, [=]() -> uint8_t {
|
||||
// Read model template
|
||||
loadModelTemplate((name + YAML_EXT).c_str(), path);
|
||||
storageDirty(EE_MODEL);
|
||||
storageCheck(true);
|
||||
// Dismiss template pages
|
||||
deleteLater();
|
||||
templateFolderPage->deleteLater();
|
||||
#if defined(LUA)
|
||||
// If there is a wizard Lua script, fire it up
|
||||
snprintf(buffer, LEN_BUFFER, "%s%c%s%s", path, '/', name.c_str(), SCRIPT_EXT);
|
||||
if (f_stat(buffer, 0) == FR_OK) {
|
||||
luaExec(buffer);
|
||||
// Need to update() the ModelCategoryPageBody before attaching StandaloneLuaWindow to not mess up focus
|
||||
update();
|
||||
update = nullptr;
|
||||
StandaloneLuaWindow::instance()->attach();
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
});
|
||||
tb->setFocusHandler([=](bool active) {
|
||||
if (active) {
|
||||
snprintf(buffer, LEN_BUFFER, "%s%c%s%s", path, '/', name.c_str(), TEXT_EXT);
|
||||
updateInfo();
|
||||
}
|
||||
});
|
||||
tb->setHeight(PAGE_LINE_HEIGHT * 2);
|
||||
grid.spacer(tb->height() + 5);
|
||||
}
|
||||
}
|
||||
|
||||
f_closedir(&dir);
|
||||
count = files.size();
|
||||
if (count == 0) {
|
||||
rect_t rect = body.getRect();
|
||||
rect.x = PAGE_PADDING;
|
||||
rect.y = PAGE_PADDING;
|
||||
rect.w = rect.w - (2 * PAGE_PADDING);
|
||||
|
||||
#if LCD_W > LCD_H
|
||||
rect.h = PAGE_LINE_HEIGHT;
|
||||
int charBreak = 60;
|
||||
#else
|
||||
rect.h = PAGE_LINE_HEIGHT * 2;
|
||||
int charBreak = 40;
|
||||
#endif
|
||||
new StaticText(&body, rect, wrap(STR_NO_TEMPLATES, charBreak), 0, COLOR_THEME_PRIMARY1);
|
||||
|
||||
// The following button is needed because the EXIT key does not work without...
|
||||
rect = body.getRect();
|
||||
rect.x = rect.w - PAGE_PADDING - 100;
|
||||
rect.y = rect.h - PAGE_PADDING - (PAGE_LINE_HEIGHT * 2);
|
||||
rect.w = 100;
|
||||
rect.h = PAGE_LINE_HEIGHT * 2;
|
||||
new TextButton(&body, rect, STR_EXIT, [=]() -> uint8_t { deleteLater(); return 0; });
|
||||
} else {
|
||||
snprintf(buffer, LEN_BUFFER, "%s%c%s%s", path, '/', files.front().c_str(), TEXT_EXT);
|
||||
updateInfo();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SelectTemplateFolder::SelectTemplateFolder(std::function<void(void)> update)
|
||||
{
|
||||
TemplatePage::update = update;
|
||||
rect_t rect = {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + 10, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT};
|
||||
new StaticText(&header, rect, STR_SELECT_TEMPLATE_FOLDER, 0, COLOR_THEME_PRIMARY2);
|
||||
|
||||
FormGridLayout grid;
|
||||
grid.spacer(PAGE_PADDING);
|
||||
|
||||
auto tfb = new TemplateButton(&body, grid.getLabelSlot(), STR_BLANK_MODEL, [=]() -> uint8_t {
|
||||
deleteLater();
|
||||
return 0;
|
||||
});
|
||||
snprintf(infoText, LEN_INFO_TEXT, "%s", STR_BLANK_MODEL_INFO);
|
||||
tfb->setFocusHandler([=](bool active) {
|
||||
if (active) {
|
||||
snprintf(infoText, LEN_INFO_TEXT, "%s", STR_BLANK_MODEL_INFO);
|
||||
invalidate();
|
||||
}
|
||||
});
|
||||
tfb->setHeight(PAGE_LINE_HEIGHT * 2);
|
||||
grid.spacer(tfb->height() + 5);
|
||||
|
||||
std::list<std::string> directories;
|
||||
FILINFO fno;
|
||||
DIR dir;
|
||||
FRESULT res = f_opendir(&dir, TEMPLATES_PATH);
|
||||
|
||||
if (res == FR_OK) {
|
||||
// read all entries
|
||||
bool firstTime = true;
|
||||
for (;;) {
|
||||
res = sdReadDir(&dir, &fno, firstTime);
|
||||
firstTime = false;
|
||||
if (res != FR_OK || fno.fname[0] == 0)
|
||||
break; // Break on error or end of dir
|
||||
if (strlen((const char*)fno.fname) > SD_SCREEN_FILE_LENGTH)
|
||||
continue;
|
||||
if (fno.fname[0] == '.')
|
||||
continue;
|
||||
if (fno.fattrib & AM_DIR)
|
||||
directories.push_back((char*)fno.fname);
|
||||
}
|
||||
|
||||
directories.sort(compare_nocase);
|
||||
|
||||
for (auto name: directories) {
|
||||
#if not defined(LUA)
|
||||
// Don't show wizards dir if no lua
|
||||
if (!strcasecmp(name.c_str(), "WIZARD") == 0) {
|
||||
#endif
|
||||
auto tfb = new TemplateButton(&body, grid.getLabelSlot(), name,
|
||||
[=]() -> uint8_t {
|
||||
snprintf(path, LEN_PATH, "%s%c%s", TEMPLATES_PATH, '/', name.c_str());
|
||||
new SelectTemplate(this);
|
||||
return 0;
|
||||
});
|
||||
tfb->setFocusHandler([=](bool active) {
|
||||
if (active) {
|
||||
snprintf(buffer, LEN_BUFFER, "%s%c%s%c%s%s", TEMPLATES_PATH, '/', name.c_str(), '/', "about", TEXT_EXT);
|
||||
updateInfo();
|
||||
}
|
||||
});
|
||||
tfb->setHeight(PAGE_LINE_HEIGHT * 2);
|
||||
grid.spacer(tfb->height() + 5);
|
||||
}
|
||||
#if not defined(LUA)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
f_closedir(&dir);
|
||||
count = directories.size();
|
||||
if (count == 0) {
|
||||
rect_t rect = grid.getLabelSlot();
|
||||
rect.w = body.getRect().w - 2 * PAGE_PADDING;
|
||||
new StaticText(&body, rect, STR_NO_TEMPLATES, 0, COLOR_THEME_PRIMARY1);
|
||||
}
|
||||
}
|
||||
|
||||
SelectTemplateFolder::~SelectTemplateFolder()
|
||||
{
|
||||
if (update)
|
||||
update();
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include "opentx.h"
|
||||
|
||||
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;
|
||||
|
||||
class TemplatePage : public Page
|
||||
{
|
||||
public:
|
||||
|
||||
TemplatePage() : Page(ICON_MODEL_SELECT)
|
||||
{ }
|
||||
|
||||
void updateInfo();
|
||||
|
||||
#if defined(HARDWARE_KEYS)
|
||||
void onEvent(event_t event) override;
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG_WINDOWS)
|
||||
std::string getName() const { return "TemplatePage"; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
static char path[LEN_PATH + 1];
|
||||
char buffer[LEN_BUFFER + 1];
|
||||
char infoText[LEN_INFO_TEXT + 1] = { 0 };
|
||||
unsigned int count = 0;
|
||||
static std::function<void(void)> update;
|
||||
|
||||
void paint(BitmapBuffer *dc) override;
|
||||
};
|
||||
|
||||
class TemplateButton : public TextButton
|
||||
{
|
||||
public:
|
||||
TemplateButton(FormGroup* parent, const rect_t& rect, std::string name, std::function<uint8_t(void)> pressHandler = nullptr)
|
||||
: TextButton(parent, rect, name, pressHandler)
|
||||
{ }
|
||||
};
|
||||
|
||||
class SelectTemplate : public TemplatePage
|
||||
{
|
||||
public:
|
||||
SelectTemplate(TemplatePage* tp);
|
||||
|
||||
protected:
|
||||
TemplatePage* templateFolderPage;
|
||||
};
|
||||
|
||||
class SelectTemplateFolder : public TemplatePage
|
||||
{
|
||||
public:
|
||||
SelectTemplateFolder(std::function<void(void)> update);
|
||||
~SelectTemplateFolder();
|
||||
};
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "custom_failsafe.h"
|
||||
#include "ppm_settings.h"
|
||||
#include "channel_range.h"
|
||||
#include "storage/modelslist.h"
|
||||
|
||||
#if defined(PXX2)
|
||||
#include "access_settings.h"
|
||||
@@ -70,7 +71,7 @@ class ModuleWindow : public FormGroup
|
||||
void updateSubType();
|
||||
|
||||
uint8_t getModuleIdx() const { return moduleIdx; }
|
||||
|
||||
|
||||
protected:
|
||||
uint8_t moduleIdx;
|
||||
|
||||
@@ -82,8 +83,11 @@ class ModuleWindow : public FormGroup
|
||||
TextButton *registerButton = nullptr;
|
||||
Window *fsLine = nullptr;
|
||||
Choice *rfPower = nullptr;
|
||||
StaticText *idUnique = nullptr;
|
||||
|
||||
void startRSSIDialog(std::function<void()> closeHandler = nullptr);
|
||||
|
||||
void updateIDStaticText(int mdIdx);
|
||||
};
|
||||
|
||||
struct FailsafeChoice : public FormGroup {
|
||||
@@ -138,6 +142,22 @@ ModuleWindow::ModuleWindow(Window* parent, uint8_t moduleIdx) :
|
||||
updateModule();
|
||||
}
|
||||
|
||||
void ModuleWindow::updateIDStaticText(int mdIdx)
|
||||
{
|
||||
if(idUnique == nullptr)
|
||||
return;
|
||||
char buffer[50];
|
||||
std::string idStr = STR_MODELIDUNIQUE;
|
||||
if(!modelslist.isModelIdUnique(mdIdx, buffer, sizeof(buffer))) {
|
||||
idStr = STR_MODELIDUSED;
|
||||
idStr = idStr + buffer;
|
||||
idUnique->setTextFlags(COLOR_THEME_WARNING);
|
||||
} else {
|
||||
idUnique->setTextFlags(COLOR_THEME_PRIMARY1);
|
||||
}
|
||||
idUnique->setText(idStr);
|
||||
}
|
||||
|
||||
void ModuleWindow::updateModule()
|
||||
{
|
||||
FlexGridLayout grid(col_dsc, row_dsc, 2);
|
||||
@@ -194,6 +214,15 @@ void ModuleWindow::updateModule()
|
||||
// Bind and Range buttons
|
||||
if (!isModuleRFAccess(moduleIdx) && (isModuleModelIndexAvailable(moduleIdx) ||
|
||||
isModuleBindRangeAvailable(moduleIdx))) {
|
||||
// Is Reciever ID Unique
|
||||
if(isModuleModelIndexAvailable(moduleIdx)) {
|
||||
auto line = newLine(&grid);
|
||||
new StaticText(line, rect_t{},"");
|
||||
auto box = new FormGroup(line, rect_t{});
|
||||
box->setFlexLayout(LV_FLEX_FLOW_ROW, lv_dpx(8));
|
||||
idUnique = new StaticText(box, rect_t{}, "", 0, 0);
|
||||
updateIDStaticText(moduleIdx);
|
||||
}
|
||||
|
||||
auto line = newLine(&grid);
|
||||
new StaticText(line, rect_t{}, STR_RECEIVER, 0, COLOR_THEME_PRIMARY1);
|
||||
@@ -207,6 +236,8 @@ void ModuleWindow::updateModule()
|
||||
GET_DEFAULT(*modelId), [=](int32_t newValue) {
|
||||
if (newValue != *modelId) {
|
||||
*modelId = newValue;
|
||||
modelslist.updateCurrentModelCell();
|
||||
updateIDStaticText(moduleIdx);
|
||||
#if defined(CROSSFIRE)
|
||||
if (isModuleCrossfire(moduleIdx)) {
|
||||
moduleState[moduleIdx].counter =
|
||||
@@ -217,6 +248,7 @@ void ModuleWindow::updateModule()
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (isModuleBindRangeAvailable(moduleIdx)) {
|
||||
bindButton = new TextButton(box, rect_t{},STR_MODULE_BIND);
|
||||
bindButton->setPressHandler([=]() -> uint8_t {
|
||||
@@ -303,7 +335,7 @@ void ModuleWindow::updateModule()
|
||||
#if defined(PXX2)
|
||||
else if (isModuleRFAccess(moduleIdx)) {
|
||||
|
||||
// Register and Range buttons
|
||||
// Register and Range buttons
|
||||
auto line = newLine(&grid);
|
||||
new StaticText(line, rect_t{}, STR_MODULE, 0, COLOR_THEME_PRIMARY1);
|
||||
|
||||
@@ -517,7 +549,7 @@ void ModuleSubTypeChoice::update()
|
||||
setValues(STR_R9M_REGION);
|
||||
setGetValueHandler(GET_DEFAULT(md->subType));
|
||||
setSetValueHandler(SET_DEFAULT(md->subType));
|
||||
setAvailableHandler(nullptr);
|
||||
setAvailableHandler(nullptr);
|
||||
}
|
||||
#if defined(PXX2)
|
||||
else if (isModulePXX2(moduleIdx)) {
|
||||
@@ -526,7 +558,7 @@ void ModuleSubTypeChoice::update()
|
||||
setValues(STR_ISRM_RF_PROTOCOLS);
|
||||
setGetValueHandler(GET_DEFAULT(md->subType));
|
||||
setSetValueHandler(SET_DEFAULT(md->subType));
|
||||
setAvailableHandler(nullptr);
|
||||
setAvailableHandler(nullptr);
|
||||
}
|
||||
#endif
|
||||
#if defined(AFHDS2) || defined(AFHDS3)
|
||||
@@ -590,9 +622,9 @@ void ModuleSubTypeChoice::update()
|
||||
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_HIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_HIDDEN);
|
||||
|
||||
|
||||
// update choice value
|
||||
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
|
||||
}
|
||||
@@ -669,7 +701,7 @@ ModulePage::ModulePage(uint8_t moduleIdx) : Page(ICON_MODEL_SETUP)
|
||||
: isExternalModuleAvailable(moduleType);
|
||||
});
|
||||
|
||||
auto subTypeChoice = new ModuleSubTypeChoice(box, moduleIdx);
|
||||
auto subTypeChoice = new ModuleSubTypeChoice(box, moduleIdx);
|
||||
auto moduleWindow = new ModuleWindow(form, moduleIdx);
|
||||
|
||||
// This needs to be after moduleWindow has been created
|
||||
|
||||
@@ -240,7 +240,7 @@ void ViewMain::onEvent(event_t event)
|
||||
break;
|
||||
|
||||
case EVT_KEY_LONG(KEY_MODEL):
|
||||
new ModelSelectMenu();
|
||||
new ModelLabelsWindow();
|
||||
break;
|
||||
|
||||
// TODO:
|
||||
|
||||
@@ -46,7 +46,7 @@ ViewMainMenu::ViewMainMenu(Window* parent) :
|
||||
auto carousel = new SelectFabCarousel(this);
|
||||
carousel->addButton(ICON_MODEL_SELECT, STR_MAIN_MENU_SELECT_MODEL, [=]() -> uint8_t {
|
||||
deleteLater();
|
||||
new ModelSelectMenu();
|
||||
new ModelLabelsWindow();
|
||||
return 0;
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user