Skip to content

Commit

Permalink
feat(color): Quick Model Select, keep active model in view when chang…
Browse files Browse the repository at this point in the history
…ing labels (#2895)

* Fix rebase merge issue.

* Incorporate solution from @ParkerEde in PR #2626

* Fix selection logic for models.

* Select model on first press.

* Fix quick model selection when using scroll wheel & enter button.
Add setting to enable quick model selection.

* German translation.

* Fix behaviour when 'model quick select' is off to work like existing 2.8 logic.

* Add translations.

* Prioritise active model as selected model when the set of visible models changes.

* Fix selection ordering when using scroll wheel.
Minor code cleanup.
  • Loading branch information
philmoz authored and pfeerick committed Jan 25, 2023
1 parent ec32bf9 commit ffed9f8
Show file tree
Hide file tree
Showing 20 changed files with 184 additions and 55 deletions.
4 changes: 4 additions & 0 deletions companion/src/firmwares/edgetx/yaml_generalsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ Node convert<GeneralSettings>::encode(const GeneralSettings& rhs)
node["varioRange"] = rhs.varioRange * 15;
node["varioRepeat"] = rhs.varioRepeat;
node["backgroundVolume"] = rhs.backgroundVolume + 2;
if (Boards::getCapability(getCurrentFirmware()->getBoard(), Board::HasColorLcd)) {
node["modelQuickSelect"] = (int)rhs.modelQuickSelect;
}

Node serialPort;
for (int i = 0; i < GeneralSettings::SP_COUNT; i++) {
Expand Down Expand Up @@ -408,6 +411,7 @@ bool convert<GeneralSettings>::decode(const Node& node, GeneralSettings& rhs)
node["varioRange"] >> ifactor<int>(rhs.varioRange, 15);
node["varioRepeat"] >> rhs.varioRepeat;
node["backgroundVolume"] >> ioffset_int(rhs.backgroundVolume, 2);
node["modelQuickSelect"] >> rhs.modelQuickSelect;

// depreciated v2.7 replaced by serialPort
if (node["auxSerialMode"]) {
Expand Down
1 change: 1 addition & 0 deletions companion/src/firmwares/generalsettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ class GeneralSettings {
bool serialPower[SP_COUNT];
int antennaMode;
unsigned int backlightColor;
bool modelQuickSelect;
CustomFunctionData customFn[CPN_MAX_SPECIAL_FUNCTIONS];
char switchName[CPN_MAX_SWITCHES][HARDWARE_NAME_LEN + 1];
unsigned int switchConfig[CPN_MAX_SWITCHES];
Expand Down
13 changes: 13 additions & 0 deletions companion/src/generaledit/generalsetup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,13 @@ void GeneralSetupPanel::setValues()

// TODO: only if ACCESS available??
ui->registrationId->setText(generalSettings.registrationId);

if (Boards::getCapability(firmware->getBoard(), Board::HasColorLcd)) {
ui->modelQuickSelect_CB->setChecked(generalSettings.modelQuickSelect);
} else {
ui->label_modelQuickSelect->hide();
ui->modelQuickSelect_CB->hide();
}
}

void GeneralSetupPanel::on_faimode_CB_stateChanged(int)
Expand Down Expand Up @@ -799,3 +806,9 @@ void GeneralSetupPanel::stickReverseEdited()
generalSettings.stickReverse = ((int)ui->stickReverse1->isChecked()) | ((int)ui->stickReverse2->isChecked()<<1) | ((int)ui->stickReverse3->isChecked()<<2) | ((int)ui->stickReverse4->isChecked()<<3);
emit modified();
}

void GeneralSetupPanel::on_modelQuickSelect_CB_stateChanged(int)
{
generalSettings.modelQuickSelect = ui->modelQuickSelect_CB->isChecked();
emit modified();
}
2 changes: 2 additions & 0 deletions companion/src/generaledit/generalsetup.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ class GeneralSetupPanel : public GeneralPanel
void on_pwrOnDelay_valueChanged(int);
void on_pwrOffDelay_valueChanged(int);

void on_modelQuickSelect_CB_stateChanged(int);

private:
Ui::GeneralSetup *ui;

Expand Down
19 changes: 18 additions & 1 deletion companion/src/generaledit/generalsetup.ui
Original file line number Diff line number Diff line change
Expand Up @@ -810,7 +810,7 @@
</property>
</widget>
</item>
<item row="22" column="0">
<item row="23" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
Expand Down Expand Up @@ -1155,6 +1155,23 @@ p, li { white-space: pre-wrap; }
<item row="21" column="1">
<widget class="QComboBox" name="rotEncMode_CB"/>
</item>
<item row="22" column="0">
<widget class="QLabel" name="label_modelQuickSelect">
<property name="text">
<string>Model quick select</string>
</property>
</widget>
</item>
<item row="22" column="1">
<widget class="QCheckBox" name="modelQuickSelect_CB">
<property name="toolTip">
<string>Enable this to quickly change model on the model select page (a long press can then be used to open the model edit menu).</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0" colspan="2">
Expand Down
2 changes: 1 addition & 1 deletion radio/src/datastructs_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ PACK(struct TrainerData {
NOBACKUP(char switchNames[STORAGE_NUM_SWITCHES][LEN_SWITCH_NAME] SKIP); \
NOBACKUP(char anaNames[NUM_STICKS + STORAGE_NUM_POTS + STORAGE_NUM_SLIDERS][LEN_ANA_NAME] SKIP); \
NOBACKUP(char currModelFilename[LEN_MODEL_FILENAME+1]); \
NOBACKUP(uint8_t spare5:1 SKIP); \
NOBACKUP(uint8_t modelQuickSelect:1); \
NOBACKUP(uint8_t blOffBright:7); \
NOBACKUP(char bluetoothName[LEN_BLUETOOTH_NAME]);
#else
Expand Down
149 changes: 100 additions & 49 deletions radio/src/gui/colorlcd/model_select.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,11 @@ class ButtonHolder : public FormWindow
class ModelButton : public Button
{
public:
ModelButton(FormGroup *parent, const rect_t &rect, ModelCell *modelCell) :
ModelButton(FormGroup *parent, const rect_t &rect, ModelCell *modelCell, std::function<void()> setSelected) :
Button(parent, rect), modelCell(modelCell)
{
m_setSelected = std::move(setSelected);

lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
setWidth(MODEL_SELECT_CELL_WIDTH);
setHeight(MODEL_SELECT_CELL_HEIGHT);
Expand Down Expand Up @@ -298,39 +300,38 @@ class ModelButton : public Button

if (modelCell == modelslist.getCurrentModel()) {
dc->drawSolidFilledRect(0, 0, width(), 20, COLOR_THEME_ACTIVE);
dc->drawSizedText(width() / 2, 2, modelCell->modelName, LEN_MODEL_NAME,
COLOR_THEME_SECONDARY1 | CENTERED);
} else {
LcdFlags textColor;
dc->drawFilledRect(0, 0, width(), 20, SOLID, COLOR_THEME_PRIMARY2);

textColor = COLOR_THEME_SECONDARY1;

dc->drawSizedText(width() / 2, 2, modelCell->modelName, LEN_MODEL_NAME,
textColor | CENTERED);
}
dc->drawSizedText(width() / 2, 2, modelCell->modelName, LEN_MODEL_NAME,
COLOR_THEME_SECONDARY1 | CENTERED);

if (!hasFocus()) {
dc->drawSolidRect(0, 0, width(), height(), 1, COLOR_THEME_SECONDARY2);
} else {
dc->drawSolidRect(0, 0, width(), height(), 2, COLOR_THEME_FOCUS);
if (m_setSelected) m_setSelected();
}
}

const char *modelFilename() { return modelCell->modelFilename; }
ModelCell *getModelCell() const { return modelCell; }

void setFocused() {
if (!lv_obj_has_state(lvobj, LV_STATE_FOCUSED)) {
lv_group_focus_obj(lvobj);
}
}

protected:
bool loaded = false;
ModelCell *modelCell;
BitmapBuffer *buffer = nullptr;
std::function<void()> m_setSelected = nullptr;

void onClicked() override {
if (!lv_obj_has_state(lvobj, LV_STATE_FOCUSED)) {
lv_group_focus_obj(lvobj);
} else {
Button::onClicked();
}
setFocused();
Button::onClicked();
}
};

Expand Down Expand Up @@ -364,41 +365,46 @@ ModelsPageBody::ModelsPageBody(Window *parent, const rect_t &rect) :
{
setFlexLayout(LV_FLEX_FLOW_ROW_WRAP, MODEL_CELL_PADDING);
padRow(MODEL_CELL_PADDING);
update();
}

void ModelsPageBody::selectModel(ModelCell *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
// Don't need to check connection to receiver if re-selecting the active model
if (model != modelslist.getCurrentModel()) {
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
}
}
}

// Exit to main view
auto w = Layer::back();
if (w) w->onCancel();

// store changes (if any) and load selected model
storageFlushCurrentModel();
storageCheck(true);
memcpy(g_eeGeneral.currModelFilename, model->modelFilename,
LEN_MODEL_FILENAME);
// Skip reloading model if re-selecting the active model
if (model != modelslist.getCurrentModel()) {
// store changes (if any) and load selected model
storageFlushCurrentModel();
storageCheck(true);
memcpy(g_eeGeneral.currModelFilename, model->modelFilename,
LEN_MODEL_FILENAME);

loadModel(g_eeGeneral.currModelFilename, true);
modelslist.setCurrentModel(model);
loadModel(g_eeGeneral.currModelFilename, true);
modelslist.setCurrentModel(model);

storageDirty(EE_GENERAL);
storageCheck(true);
storageDirty(EE_GENERAL);
storageCheck(true);
}
}

void ModelsPageBody::duplicateModel(ModelCell *model)
Expand Down Expand Up @@ -500,7 +506,22 @@ void ModelsPageBody::editLabels(ModelCell* model)
}
}

void ModelsPageBody::update(int selected)
void ModelsPageBody::openMenu()
{
Menu *menu = new Menu(this);
menu->setTitle(focusedModel->modelName);
if (g_eeGeneral.modelQuickSelect || focusedModel != modelslist.getCurrentModel()) {
menu->addLine(STR_SELECT_MODEL, [=]() { selectModel(focusedModel); });
}
menu->addLine(STR_DUPLICATE_MODEL, [=]() { duplicateModel(focusedModel); });
menu->addLine(STR_EDIT_LABELS, [=]() { editLabels(focusedModel); });
menu->addLine(STR_SAVE_TEMPLATE, [=]() { saveAsTemplate(focusedModel);});
if (focusedModel != modelslist.getCurrentModel()) {
menu->addLine(STR_DELETE_MODEL, [=]() { deleteModel(focusedModel); });
}
}

void ModelsPageBody::update()
{
clear();

Expand All @@ -511,25 +532,55 @@ void ModelsPageBody::update(int selected)
models = modelslabels.getAllModels();
}

// Used to work out which button to set focus to.
// Priority -
// current active model
// previously selected model
// first model in the list
ModelButton* firstButton = nullptr;
ModelButton* focusedButton = nullptr;

for (auto &model : models) {
auto button = new ModelButton(this, rect_t{}, model);
auto button = new ModelButton(this, rect_t{}, model, [=]() {
focusedModel = model;
});

// Long Press Handler for Models
if (!firstButton)
firstButton = button;
if (model == modelslist.getCurrentModel())
focusedButton = button;
if (model == focusedModel && !focusedButton)
focusedButton = button;

// Press Handler for Models
button->setPressHandler([=]() -> uint8_t {
Menu *menu = new Menu(this);
menu->setTitle(model->modelName);
if (model != modelslist.getCurrentModel()) {
menu->addLine(STR_SELECT_MODEL, [=]() { selectModel(model); });
if (model == focusedModel) {
if (g_eeGeneral.modelQuickSelect)
selectModel(model);
else
openMenu();
} else {
focusedModel = model;
}
menu->addLine(STR_DUPLICATE_MODEL, [=]() { duplicateModel(model); });
if (model != modelslist.getCurrentModel()) {
menu->addLine(STR_DELETE_MODEL, [=]() { deleteModel(model); });
return 0;
});

// Long Press Handler for Models
button->setLongPressHandler([=]() -> uint8_t {
if (model == focusedModel) {
openMenu();
}
menu->addLine(STR_EDIT_LABELS, [=]() { editLabels(model); });
menu->addLine(STR_SAVE_TEMPLATE, [=]() { saveAsTemplate(model);});
return 0;
});
}

if (!focusedButton)
focusedButton = firstButton;

if (focusedButton) {
focusedButton->setFocused();
focusedModel = focusedButton->getModelCell();
}
}

//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -589,8 +640,8 @@ class LabelDialog : public Dialog

ModelLabelsWindow::ModelLabelsWindow() : Page(ICON_MODEL)
{
buildBody(&body);
buildHead(&header);
buildBody(&body);

// find the first label of the current model and make that label active
auto currentModel = modelslist.getCurrentModel();
Expand Down
5 changes: 4 additions & 1 deletion radio/src/gui/colorlcd/model_select.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ class ModelsPageBody : public FormWindow
public:
ModelsPageBody(Window *parent, const rect_t &rect);

void update();

void setLabels(LabelsVector labels)
{
selectedLabels = labels;
update();
}
void update(int selected = -1);

inline void setSortOrder(ModelsSortBy sortOrder)
{
Expand All @@ -61,8 +62,10 @@ class ModelsPageBody : public FormWindow
bool refresh = false;
std::string selectedLabel;
LabelsVector selectedLabels;
ModelCell *focusedModel = nullptr;
std::function<void()> refreshLabels = nullptr;

void openMenu();
void selectModel(ModelCell* model);
void duplicateModel(ModelCell* model);
void deleteModel(ModelCell* model);
Expand Down
6 changes: 6 additions & 0 deletions radio/src/gui/colorlcd/radio_setup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -735,4 +735,10 @@ void RadioSetupPage::build(FormWindow * window)
std::string(
&getSourceString(MIXSRC_Rud + modn12x3[4 * value + 1])[1]);
});
grid.setColSpan(1);

// Model quick select
line = window->newLine(&grid);
new StaticText(line, rect_t{}, STR_MODEL_QUICK_SELECT, 0, COLOR_THEME_PRIMARY1);
new CheckBox(line, rect_t{}, GET_SET_DEFAULT(g_eeGeneral.modelQuickSelect));
}
2 changes: 1 addition & 1 deletion radio/src/storage/yaml/yaml_datastructs_nv14.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ static const struct YamlNode struct_RadioData[] = {
YAML_PADDING( 192 ),
YAML_PADDING( 216 ),
YAML_STRING("currModelFilename", 17),
YAML_PADDING( 1 ),
YAML_UNSIGNED( "modelQuickSelect", 1 ),
YAML_UNSIGNED( "blOffBright", 7 ),
YAML_STRING("bluetoothName", 10),
YAML_STRING("themeName", 8),
Expand Down
2 changes: 1 addition & 1 deletion radio/src/storage/yaml/yaml_datastructs_x10.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ static const struct YamlNode struct_RadioData[] = {
YAML_PADDING( 240 ),
YAML_PADDING( 360 ),
YAML_STRING("currModelFilename", 17),
YAML_PADDING( 1 ),
YAML_UNSIGNED( "modelQuickSelect", 1 ),
YAML_UNSIGNED( "blOffBright", 7 ),
YAML_STRING("bluetoothName", 10),
YAML_STRING("themeName", 8),
Expand Down
2 changes: 1 addition & 1 deletion radio/src/storage/yaml/yaml_datastructs_x12s.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ static const struct YamlNode struct_RadioData[] = {
YAML_PADDING( 240 ),
YAML_PADDING( 312 ),
YAML_STRING("currModelFilename", 17),
YAML_PADDING( 1 ),
YAML_UNSIGNED( "modelQuickSelect", 1 ),
YAML_UNSIGNED( "blOffBright", 7 ),
YAML_STRING("bluetoothName", 10),
YAML_STRING("themeName", 8),
Expand Down
Loading

0 comments on commit ffed9f8

Please sign in to comment.