Inputs rework
This commit is contained in:
committed by
Raphael Coeffic
parent
95463540c0
commit
9aaca6595e
@@ -167,6 +167,10 @@ add_gui_src(curve.cpp)
|
||||
add_gui_src(sourcechoice.cpp)
|
||||
add_gui_src(switchchoice.cpp)
|
||||
add_gui_src(curveedit.cpp)
|
||||
add_gui_src(input_edit.cpp)
|
||||
add_gui_src(input_mix_group.cpp)
|
||||
add_gui_src(input_mix_button.cpp)
|
||||
add_gui_src(lvgl_widgets/input_mix_line.c)
|
||||
|
||||
if(HARDWARE_TOUCH)
|
||||
add_gui_src(keyboard_curve.cpp)
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* 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 "input_edit.h"
|
||||
#include "model_curves.h"
|
||||
|
||||
#include "opentx.h"
|
||||
#include "gvar_numberedit.h"
|
||||
|
||||
#define SET_DIRTY() storageDirty(EE_MODEL)
|
||||
|
||||
class SensorValue : Window
|
||||
{
|
||||
public:
|
||||
SensorValue(Window *parent, const rect_t &rect, ExpoData *expoData) :
|
||||
Window(parent, rect), expoData(expoData)
|
||||
{
|
||||
}
|
||||
void paint(BitmapBuffer *dc) override
|
||||
{
|
||||
if (isTelemetryValue()) {
|
||||
uint8_t sensorIndex = (expoData->srcRaw - MIXSRC_FIRST_TELEM) / 3;
|
||||
#if defined(SIMU)
|
||||
if (true) {
|
||||
#else
|
||||
TelemetryItem &telemetryItem = telemetryItems[sensorIndex];
|
||||
if (telemetryItem.isAvailable()) {
|
||||
#endif
|
||||
LcdFlags flags = LEFT | COLOR_THEME_PRIMARY1;
|
||||
drawSensorCustomValue(dc, 3, 2, sensorIndex, lastSensorVal, flags);
|
||||
} else {
|
||||
dc->drawText(3, 2, "---", COLOR_THEME_PRIMARY1);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isTelemetryValue()
|
||||
{
|
||||
return expoData->srcRaw >= MIXSRC_FIRST_TELEM &&
|
||||
expoData->srcRaw <= MIXSRC_LAST_TELEM;
|
||||
}
|
||||
void checkEvents() override
|
||||
{
|
||||
getvalue_t sensorVal = -1;
|
||||
if (isTelemetryValue()) {
|
||||
sensorVal = getValue(expoData->srcRaw);
|
||||
}
|
||||
if (lastSensorVal != sensorVal) {
|
||||
lastSensorVal = sensorVal;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
LcdFlags getSensorPrec()
|
||||
{
|
||||
LcdFlags prec = 0;
|
||||
if (isTelemetryValue()) {
|
||||
uint8_t sensorIndex = (expoData->srcRaw - MIXSRC_FIRST_TELEM) / 3;
|
||||
TelemetrySensor sensor = g_model.telemetrySensors[sensorIndex];
|
||||
if (sensor.prec > 0) {
|
||||
prec |= (sensor.prec == 1 ? PREC1 : PREC2);
|
||||
}
|
||||
}
|
||||
return prec;
|
||||
}
|
||||
|
||||
protected:
|
||||
getvalue_t lastSensorVal;
|
||||
ExpoData *expoData;
|
||||
};
|
||||
|
||||
InputEditWindow::InputEditWindow(int8_t input, uint8_t index) :
|
||||
Page(ICON_MODEL_INPUTS),
|
||||
input(input),
|
||||
index(index),
|
||||
preview(
|
||||
this,
|
||||
{INPUT_EDIT_CURVE_LEFT, INPUT_EDIT_CURVE_TOP, INPUT_EDIT_CURVE_WIDTH,
|
||||
INPUT_EDIT_CURVE_HEIGHT},
|
||||
[=](int x) -> int {
|
||||
ExpoData *line = expoAddress(index);
|
||||
int16_t anas[MAX_INPUTS] = {0};
|
||||
applyExpos(anas, e_perout_mode_inactive_flight_mode, line->srcRaw, x);
|
||||
return anas[line->chn];
|
||||
},
|
||||
[=]() -> int { return getValue(expoAddress(index)->srcRaw); })
|
||||
{
|
||||
#if LCD_W > LCD_H
|
||||
auto BODY_WIDTH = LCD_W - preview.width() - PAGE_PADDING;
|
||||
body.setWidth(BODY_WIDTH);
|
||||
body.setLeft(preview.width() + PAGE_PADDING);
|
||||
#else
|
||||
body.setRect({0, INPUT_EDIT_CURVE_TOP + INPUT_EDIT_CURVE_HEIGHT, LCD_W,
|
||||
LCD_H - INPUT_EDIT_CURVE_TOP - INPUT_EDIT_CURVE_HEIGHT});
|
||||
#endif
|
||||
buildBody(&body);
|
||||
buildHeader(&header);
|
||||
}
|
||||
|
||||
void InputEditWindow::deleteLater(bool detach, bool trash)
|
||||
{
|
||||
if (_deleted) return;
|
||||
|
||||
preview.deleteLater(true, false);
|
||||
|
||||
Page::deleteLater(detach, trash);
|
||||
}
|
||||
|
||||
void InputEditWindow::buildHeader(Window *window)
|
||||
{
|
||||
new StaticText(window,
|
||||
{PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT,
|
||||
PAGE_LINE_HEIGHT},
|
||||
STR_MENUINPUTS, 0, COLOR_THEME_PRIMARY2);
|
||||
new StaticText(window,
|
||||
{PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT,
|
||||
LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT},
|
||||
getSourceString(MIXSRC_FIRST_INPUT + input), 0,
|
||||
COLOR_THEME_PRIMARY2);
|
||||
}
|
||||
|
||||
// TODO share this code with MIXER
|
||||
void InputEditWindow::updateCurveParamField(ExpoData *line)
|
||||
{
|
||||
curveParamField->clear();
|
||||
|
||||
const rect_t rect = {0, 0, curveParamField->width(),
|
||||
curveParamField->height()};
|
||||
|
||||
switch (line->curve.type) {
|
||||
case CURVE_REF_DIFF:
|
||||
case CURVE_REF_EXPO: {
|
||||
GVarNumberEdit *edit = new GVarNumberEdit(
|
||||
curveParamField, rect, -100, 100, GET_SET_DEFAULT(line->curve.value));
|
||||
edit->setSuffix("%");
|
||||
break;
|
||||
}
|
||||
|
||||
case CURVE_REF_FUNC:
|
||||
new Choice(curveParamField, rect, STR_VCURVEFUNC, 0, CURVE_BASE - 1,
|
||||
GET_SET_DEFAULT(line->curve.value));
|
||||
break;
|
||||
|
||||
case CURVE_REF_CUSTOM: {
|
||||
auto choice = new ChoiceEx(curveParamField, rect, -MAX_CURVES, MAX_CURVES,
|
||||
GET_SET_DEFAULT(line->curve.value));
|
||||
choice->setTextHandler([](int value) { return getCurveString(value); });
|
||||
choice->setLongPressHandler([this](event_t event) {
|
||||
ExpoData *line = expoAddress(index);
|
||||
|
||||
// if no curve is specified then dont link to curve page
|
||||
if (line->curve.value != 0)
|
||||
ModelCurvesPage::pushEditCurve(abs(line->curve.value) - 1);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputEditWindow::buildBody(FormWindow *window)
|
||||
{
|
||||
FormGridLayout grid;
|
||||
grid.setLabelWidth(INPUT_EDIT_LABELS_WIDTH);
|
||||
grid.spacer(PAGE_PADDING);
|
||||
|
||||
ExpoData *line = expoAddress(index);
|
||||
|
||||
#if LCD_W > LCD_H
|
||||
grid.setMarginRight(180);
|
||||
#endif
|
||||
|
||||
// Input Name
|
||||
new StaticText(window, grid.getLabelSlot(), STR_INPUTNAME, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
new ModelTextEdit(window, grid.getFieldSlot(), g_model.inputNames[line->chn],
|
||||
sizeof(g_model.inputNames[line->chn]));
|
||||
grid.nextLine();
|
||||
|
||||
// Switch
|
||||
new StaticText(window, grid.getLabelSlot(), STR_SWITCH, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST_IN_MIXES,
|
||||
SWSRC_LAST_IN_MIXES, GET_SET_DEFAULT(line->swtch));
|
||||
grid.nextLine();
|
||||
|
||||
// Side
|
||||
new StaticText(window, grid.getLabelSlot(), STR_SIDE, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
new Choice(
|
||||
window, grid.getFieldSlot(), STR_VCURVEFUNC, 1, 3,
|
||||
[=]() -> int16_t { return 4 - line->mode; },
|
||||
[=](int16_t newValue) {
|
||||
line->mode = 4 - newValue;
|
||||
SET_DIRTY();
|
||||
});
|
||||
grid.nextLine();
|
||||
|
||||
// Name
|
||||
new StaticText(window, grid.getLabelSlot(), STR_EXPONAME, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
new ModelTextEdit(window, grid.getFieldSlot(), line->name,
|
||||
sizeof(line->name));
|
||||
grid.nextLine();
|
||||
|
||||
// Source
|
||||
new StaticText(window, grid.getLabelSlot(), STR_SOURCE, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
new SourceChoice(
|
||||
window, grid.getFieldSlot(2, 0), INPUTSRC_FIRST, INPUTSRC_LAST,
|
||||
GET_DEFAULT(line->srcRaw), [=](int32_t newValue) {
|
||||
line->srcRaw = newValue;
|
||||
if (line->srcRaw > MIXSRC_Ail && line->carryTrim == TRIM_ON) {
|
||||
line->carryTrim = TRIM_OFF;
|
||||
trimChoice->invalidate();
|
||||
}
|
||||
window->clear();
|
||||
buildBody(window);
|
||||
SET_DIRTY();
|
||||
});
|
||||
|
||||
SensorValue *sensor = nullptr;
|
||||
if (line->srcRaw >= MIXSRC_FIRST_TELEM) {
|
||||
sensor = new SensorValue(window, grid.getFieldSlot(2, 1), line);
|
||||
|
||||
grid.nextLine();
|
||||
// Scale
|
||||
new StaticText(window, grid.getLabelSlot(), STR_SCALE, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
new NumberEdit(window, grid.getFieldSlot(), 0,
|
||||
maxTelemValue(line->srcRaw - MIXSRC_FIRST_TELEM + 1),
|
||||
GET_SET_DEFAULT(line->scale), 0, sensor->getSensorPrec());
|
||||
adjustHeight();
|
||||
}
|
||||
grid.nextLine();
|
||||
|
||||
// Weight
|
||||
new StaticText(window, grid.getLabelSlot(), STR_WEIGHT, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
auto gvar = new GVarNumberEdit(window, grid.getFieldSlot(), -100, 100,
|
||||
GET_SET_DEFAULT(line->weight));
|
||||
gvar->setSuffix("%");
|
||||
grid.nextLine();
|
||||
|
||||
// Offset
|
||||
new StaticText(window, grid.getLabelSlot(), STR_OFFSET, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
gvar = new GVarNumberEdit(window, grid.getFieldSlot(), -100, 100,
|
||||
GET_SET_DEFAULT(line->offset));
|
||||
gvar->setSuffix("%");
|
||||
grid.nextLine();
|
||||
|
||||
// Trim
|
||||
new StaticText(window, grid.getLabelSlot(), STR_TRIM, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
trimChoice = new Choice(window, grid.getFieldSlot(), STR_VMIXTRIMS, -TRIM_OFF,
|
||||
-TRIM_LAST, GET_VALUE(-line->carryTrim),
|
||||
SET_VALUE(line->carryTrim, -newValue));
|
||||
trimChoice->setAvailableHandler([=](int value) {
|
||||
return value != TRIM_ON || line->srcRaw <= MIXSRC_Ail;
|
||||
});
|
||||
grid.nextLine();
|
||||
|
||||
// Curve
|
||||
new StaticText(&body, grid.getLabelSlot(), STR_CURVE, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
new Choice(&body, grid.getFieldSlot(2, 0), "\004DiffExpoFuncCstm", 0,
|
||||
CURVE_REF_CUSTOM, GET_DEFAULT(line->curve.type),
|
||||
[=](int32_t newValue) {
|
||||
line->curve.type = newValue;
|
||||
line->curve.value = 0;
|
||||
SET_DIRTY();
|
||||
updateCurveParamField(line);
|
||||
});
|
||||
curveParamField = new FormGroup(&body, grid.getFieldSlot(2, 1)
|
||||
// , FORM_FORWARD_FOCUS
|
||||
);
|
||||
updateCurveParamField(line);
|
||||
grid.nextLine();
|
||||
|
||||
#if defined(FLIGHT_MODES)
|
||||
// Flight modes
|
||||
new StaticText(window, grid.getLabelSlot(), STR_FLMODE, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
for (uint32_t i = 0; i < MAX_FLIGHT_MODES; i++) {
|
||||
char fm[2] = {char('0' + i), '\0'};
|
||||
if (i > 0 && (i % 4) == 0) grid.nextLine();
|
||||
new TextButton(
|
||||
window, grid.getFieldSlot(4, i % 4), fm,
|
||||
[=]() -> uint32_t {
|
||||
BFBIT_FLIP(line->flightModes, bfBit<uint32_t>(i));
|
||||
SET_DIRTY();
|
||||
return !(bfSingleBitGet(line->flightModes, i));
|
||||
},
|
||||
OPAQUE | (bfSingleBitGet(line->flightModes, i) ? 0 : BUTTON_CHECKED));
|
||||
}
|
||||
grid.nextLine();
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 "page.h"
|
||||
#include "curve.h"
|
||||
#include "choice.h"
|
||||
|
||||
struct ExpoData;
|
||||
|
||||
class InputEditWindow : public Page
|
||||
{
|
||||
public:
|
||||
InputEditWindow(int8_t input, uint8_t index);
|
||||
void deleteLater(bool detach = true, bool trash = true) override;
|
||||
|
||||
protected:
|
||||
uint8_t input;
|
||||
uint8_t index;
|
||||
Curve preview;
|
||||
Choice *trimChoice = nullptr;
|
||||
FormGroup *curveParamField = nullptr;
|
||||
|
||||
void buildHeader(Window *window);
|
||||
void updateCurveParamField(ExpoData *line);
|
||||
void buildBody(FormWindow *window);
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 "input_mix_button.h"
|
||||
#include "opentx.h"
|
||||
|
||||
#include "lvgl_widgets/input_mix_line.h"
|
||||
|
||||
InputMixButton::InputMixButton(Window* parent, const rect_t& rect, uint8_t index) :
|
||||
Button(parent, rect, nullptr, 0, COLOR_THEME_PRIMARY1,
|
||||
input_mix_line_create),
|
||||
index(index)
|
||||
{
|
||||
}
|
||||
|
||||
void InputMixButton::checkEvents()
|
||||
{
|
||||
check(isActive());
|
||||
Button::checkEvents();
|
||||
}
|
||||
|
||||
void InputMixButton::drawFlightModes(BitmapBuffer *dc, FlightModesType value,
|
||||
LcdFlags textColor, coord_t x, coord_t y)
|
||||
{
|
||||
dc->drawMask(x, y + 2, mixerSetupFlightmodeIcon, textColor);
|
||||
x += 20;
|
||||
|
||||
for (int i = 0; i < MAX_FLIGHT_MODES; i++) {
|
||||
char s[] = " ";
|
||||
s[0] = '0' + i;
|
||||
if (value & (1 << i)) {
|
||||
dc->drawText(x, y + 2, s, FONT(XS) | COLOR_THEME_DISABLED);
|
||||
} else {
|
||||
dc->drawSolidFilledRect(x, y + 2, 8, 3, COLOR_THEME_FOCUS);
|
||||
dc->drawText(x, y + 2, s, FONT(XS) | textColor);
|
||||
}
|
||||
x += 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 "button.h"
|
||||
|
||||
class InputMixButton : public Button
|
||||
{
|
||||
public:
|
||||
InputMixButton(Window* parent, const rect_t& rect, uint8_t index);
|
||||
|
||||
uint8_t getIndex() const { return index; }
|
||||
void setIndex(uint8_t i) { index = i; }
|
||||
|
||||
void checkEvents() override;
|
||||
|
||||
void drawFlightModes(BitmapBuffer *dc, FlightModesType value,
|
||||
LcdFlags textColor, coord_t x, coord_t y);
|
||||
|
||||
// TODO: generic "getHeight()" and recalc on changes
|
||||
|
||||
protected:
|
||||
uint8_t index;
|
||||
bool active = false;
|
||||
|
||||
virtual bool isActive() const = 0;
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 "input_mix_group.h"
|
||||
#include "opentx.h"
|
||||
|
||||
static const lv_coord_t col_dsc[] = {
|
||||
LV_DPI_DEF / 2, LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST,
|
||||
};
|
||||
|
||||
static const lv_coord_t row_dsc[] = {
|
||||
LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST,
|
||||
};
|
||||
|
||||
InputMixGroup::InputMixGroup(Window* parent, mixsrc_t idx) :
|
||||
Window(parent, rect_t{}), idx(idx)
|
||||
{
|
||||
lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, 0);
|
||||
lv_obj_set_style_radius(lvobj, lv_dpx(16), 0);
|
||||
lv_obj_set_style_pad_hor(lvobj, lv_dpx(8), 0);
|
||||
lv_obj_set_style_pad_ver(lvobj, lv_dpx(8), 0);
|
||||
|
||||
lv_obj_set_width(lvobj, lv_pct(100));
|
||||
lv_obj_set_height(lvobj, LV_SIZE_CONTENT);
|
||||
|
||||
lv_obj_set_layout(lvobj, LV_LAYOUT_GRID);
|
||||
lv_obj_set_grid_dsc_array(lvobj, col_dsc, row_dsc);
|
||||
|
||||
label = lv_label_create(lvobj);
|
||||
lv_label_set_text(label, getSourceString(idx));
|
||||
lv_obj_set_grid_cell(label,
|
||||
LV_GRID_ALIGN_START, 0, 1,
|
||||
LV_GRID_ALIGN_START, 0, 1);
|
||||
|
||||
auto box = window_create(lvobj);
|
||||
lv_obj_set_size(box, lv_pct(100), LV_SIZE_CONTENT);
|
||||
lv_obj_set_grid_cell(box,
|
||||
LV_GRID_ALIGN_STRETCH, 1, 1,
|
||||
LV_GRID_ALIGN_START, 0, 1);
|
||||
|
||||
line_container = window_create(box);
|
||||
lv_obj_set_size(line_container, lv_pct(100), LV_SIZE_CONTENT);
|
||||
lv_obj_set_flex_flow(line_container, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_style_pad_row(line_container, lv_dpx(8), LV_PART_MAIN);
|
||||
}
|
||||
|
||||
void InputMixGroup::addLine(Window* line, const uint8_t* symbol)
|
||||
{
|
||||
lines.emplace_back(line, symbol);
|
||||
lv_obj_t* line_obj = line->getLvObj();
|
||||
lv_obj_set_parent(line_obj, line_container);
|
||||
}
|
||||
|
||||
bool InputMixGroup::removeLine(Window* line)
|
||||
{
|
||||
auto l = std::find_if(lines.begin(), lines.begin(), [=](const Line& l) -> bool {
|
||||
return l.win == line;
|
||||
});
|
||||
|
||||
if (l != lines.end()) {
|
||||
lines.erase(l);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 "window.h"
|
||||
#include "opentx_types.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
class InputMixGroup : public Window
|
||||
{
|
||||
struct Line {
|
||||
Window* win;
|
||||
const uint8_t* symbol;
|
||||
|
||||
Line(Window* w, const uint8_t* s) :
|
||||
win(w), symbol(s)
|
||||
{}
|
||||
};
|
||||
|
||||
mixsrc_t idx;
|
||||
std::list<Line> lines;
|
||||
|
||||
lv_obj_t* label;
|
||||
lv_obj_t* line_container;
|
||||
|
||||
public:
|
||||
InputMixGroup(Window* parent, mixsrc_t idx);
|
||||
|
||||
mixsrc_t getMixSrc() { return idx; }
|
||||
size_t getLineCount() { return lines.size(); }
|
||||
|
||||
void addLine(Window* line, const uint8_t* symbol = nullptr);
|
||||
bool removeLine(Window* line);
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* 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 "input_mix_line.h"
|
||||
|
||||
#define MY_CLASS &input_mix_line_class
|
||||
|
||||
const lv_obj_class_t input_mix_line_class = {
|
||||
.width_def = LV_PCT(100),
|
||||
.height_def = LV_DPI_DEF / 4,
|
||||
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
|
||||
.instance_size = sizeof(lv_btn_t),
|
||||
.base_class = &lv_btn_class,
|
||||
};
|
||||
|
||||
lv_obj_t* input_mix_line_create(lv_obj_t* parent)
|
||||
{
|
||||
LV_LOG_INFO("begin");
|
||||
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
|
||||
lv_obj_class_init_obj(obj);
|
||||
return obj;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef INPUT_MIX_LINE_H
|
||||
#define INPUT_MIX_LINE_H
|
||||
|
||||
#include <lvgl/lvgl.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const lv_obj_class_t input_mix_line_class;
|
||||
lv_obj_t* input_mix_line_create(lv_obj_t* parent);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -25,12 +25,12 @@
|
||||
#include "libopenui.h"
|
||||
#include "choiceex.h"
|
||||
#include "model_curves.h"
|
||||
#include "input_edit.h"
|
||||
#include "input_mix_group.h"
|
||||
#include "input_mix_button.h"
|
||||
|
||||
#define SET_DIRTY() storageDirty(EE_MODEL)
|
||||
|
||||
#define PASTE_BEFORE -2
|
||||
#define PASTE_AFTER -1
|
||||
|
||||
uint8_t getExposCount()
|
||||
{
|
||||
uint8_t count = 0;
|
||||
@@ -54,27 +54,19 @@ bool reachExposLimit()
|
||||
return false;
|
||||
}
|
||||
|
||||
void copyExpo(uint8_t source, uint8_t dest, int8_t input)
|
||||
// TODO: these functions need to be added to the generic API
|
||||
// used by all radios, and be removed from UI code
|
||||
//
|
||||
void copyExpo(uint8_t source, uint8_t dest, uint8_t input)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
ExpoData sourceExpo;
|
||||
memcpy(&sourceExpo, expoAddress(source), sizeof(ExpoData));
|
||||
ExpoData *expo = expoAddress(dest);
|
||||
size_t trailingExpos = MAX_EXPOS - (dest + 1);
|
||||
if (input == PASTE_AFTER) {
|
||||
trailingExpos--;
|
||||
memmove(expo + 2, expo + 1, trailingExpos * sizeof(ExpoData));
|
||||
memcpy(expo + 1, &sourceExpo, sizeof(ExpoData));
|
||||
(expo + 1)->chn = (expo)->chn;
|
||||
} else if (input == PASTE_BEFORE) {
|
||||
memmove(expo + 1, expo, trailingExpos * sizeof(ExpoData));
|
||||
memcpy(expo, &sourceExpo, sizeof(ExpoData));
|
||||
expo->chn = (expo + 1)->chn;
|
||||
} else {
|
||||
memmove(expo + 1, expo, trailingExpos * sizeof(ExpoData));
|
||||
memcpy(expo, &sourceExpo, sizeof(ExpoData));
|
||||
expo->chn = input;
|
||||
}
|
||||
memmove(expo + 1, expo, trailingExpos * sizeof(ExpoData));
|
||||
memcpy(expo, &sourceExpo, sizeof(ExpoData));
|
||||
expo->chn = input;
|
||||
resumeMixerCalculations();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
@@ -93,559 +85,6 @@ void deleteExpo(uint8_t idx)
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
class SensorValue : Window
|
||||
{
|
||||
public:
|
||||
SensorValue(Window *parent, const rect_t &rect, ExpoData *expoData) :
|
||||
Window(parent, rect), expoData(expoData)
|
||||
{
|
||||
}
|
||||
void paint(BitmapBuffer *dc) override
|
||||
{
|
||||
if (isTelemetryValue()) {
|
||||
uint8_t sensorIndex = (expoData->srcRaw - MIXSRC_FIRST_TELEM) / 3;
|
||||
#if defined(SIMU)
|
||||
if(true) {
|
||||
#else
|
||||
TelemetryItem &telemetryItem = telemetryItems[sensorIndex];
|
||||
if (telemetryItem.isAvailable()) {
|
||||
#endif
|
||||
LcdFlags flags = LEFT | COLOR_THEME_PRIMARY1;
|
||||
drawSensorCustomValue(dc, 3, 2, sensorIndex, lastSensorVal, flags);
|
||||
} else {
|
||||
dc->drawText(3, 2, "---", COLOR_THEME_PRIMARY1);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isTelemetryValue()
|
||||
{
|
||||
return expoData->srcRaw >= MIXSRC_FIRST_TELEM &&
|
||||
expoData->srcRaw <= MIXSRC_LAST_TELEM;
|
||||
}
|
||||
void checkEvents() override
|
||||
{
|
||||
getvalue_t sensorVal = -1;
|
||||
if (isTelemetryValue()) {
|
||||
sensorVal = getValue(expoData->srcRaw);
|
||||
}
|
||||
if (lastSensorVal != sensorVal) {
|
||||
lastSensorVal = sensorVal;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
LcdFlags getSensorPrec()
|
||||
{
|
||||
LcdFlags prec = 0;
|
||||
if (isTelemetryValue()) {
|
||||
uint8_t sensorIndex = (expoData->srcRaw - MIXSRC_FIRST_TELEM) / 3;
|
||||
TelemetrySensor sensor = g_model.telemetrySensors[sensorIndex];
|
||||
if (sensor.prec > 0) {
|
||||
prec |= (sensor.prec == 1 ? PREC1 : PREC2);
|
||||
}
|
||||
}
|
||||
return prec;
|
||||
}
|
||||
|
||||
protected:
|
||||
getvalue_t lastSensorVal;
|
||||
ExpoData *expoData;
|
||||
};
|
||||
|
||||
class InputEditWindow : public Page
|
||||
{
|
||||
public:
|
||||
InputEditWindow(int8_t input, uint8_t index) :
|
||||
Page(ICON_MODEL_INPUTS),
|
||||
input(input),
|
||||
index(index),
|
||||
preview(
|
||||
this,
|
||||
{INPUT_EDIT_CURVE_LEFT, INPUT_EDIT_CURVE_TOP, INPUT_EDIT_CURVE_WIDTH,
|
||||
INPUT_EDIT_CURVE_HEIGHT},
|
||||
[=](int x) -> int {
|
||||
ExpoData *line = expoAddress(index);
|
||||
int16_t anas[MAX_INPUTS] = {0};
|
||||
applyExpos(anas, e_perout_mode_inactive_flight_mode, line->srcRaw,
|
||||
x);
|
||||
return anas[line->chn];
|
||||
},
|
||||
[=]() -> int { return getValue(expoAddress(index)->srcRaw); })
|
||||
{
|
||||
#if LCD_W > LCD_H
|
||||
auto BODY_WIDTH = LCD_W - preview.width() - PAGE_PADDING;
|
||||
body.setWidth(BODY_WIDTH);
|
||||
body.setLeft(preview.width() + PAGE_PADDING);
|
||||
#else
|
||||
body.setRect({0, INPUT_EDIT_CURVE_TOP + INPUT_EDIT_CURVE_HEIGHT, LCD_W,
|
||||
LCD_H - INPUT_EDIT_CURVE_TOP - INPUT_EDIT_CURVE_HEIGHT});
|
||||
#endif
|
||||
buildBody(&body);
|
||||
buildHeader(&header);
|
||||
}
|
||||
|
||||
void deleteLater(bool detach = true, bool trash = true) override
|
||||
{
|
||||
if (_deleted) return;
|
||||
|
||||
preview.deleteLater(true, false);
|
||||
|
||||
Page::deleteLater(detach, trash);
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t input;
|
||||
uint8_t index;
|
||||
Curve preview;
|
||||
Choice *trimChoice = nullptr;
|
||||
FormGroup *curveParamField = nullptr;
|
||||
|
||||
void buildHeader(Window *window)
|
||||
{
|
||||
new StaticText(window,
|
||||
{PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT,
|
||||
PAGE_LINE_HEIGHT},
|
||||
STR_MENUINPUTS, 0, COLOR_THEME_PRIMARY2);
|
||||
new StaticText(window,
|
||||
{PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT,
|
||||
LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT},
|
||||
getSourceString(MIXSRC_FIRST_INPUT + input), 0, COLOR_THEME_PRIMARY2);
|
||||
}
|
||||
|
||||
// TODO share this code with MIXER
|
||||
void updateCurveParamField(ExpoData *line)
|
||||
{
|
||||
curveParamField->clear();
|
||||
|
||||
const rect_t rect = {0, 0, curveParamField->width(),
|
||||
curveParamField->height()};
|
||||
|
||||
switch (line->curve.type) {
|
||||
case CURVE_REF_DIFF:
|
||||
case CURVE_REF_EXPO: {
|
||||
GVarNumberEdit *edit =
|
||||
new GVarNumberEdit(curveParamField, rect, -100, 100,
|
||||
GET_SET_DEFAULT(line->curve.value));
|
||||
edit->setSuffix("%");
|
||||
break;
|
||||
}
|
||||
|
||||
case CURVE_REF_FUNC:
|
||||
new Choice(curveParamField, rect, STR_VCURVEFUNC, 0, CURVE_BASE - 1,
|
||||
GET_SET_DEFAULT(line->curve.value));
|
||||
break;
|
||||
|
||||
case CURVE_REF_CUSTOM: {
|
||||
auto choice = new ChoiceEx(curveParamField, rect, -MAX_CURVES, MAX_CURVES,
|
||||
GET_SET_DEFAULT(line->curve.value));
|
||||
choice->setTextHandler([](int value) { return getCurveString(value); });
|
||||
choice->setLongPressHandler([this](event_t event) {
|
||||
ExpoData *line = expoAddress(index);
|
||||
|
||||
// if no curve is specified then dont link to curve page
|
||||
if (line->curve.value != 0)
|
||||
ModelCurvesPage::pushEditCurve(abs(line->curve.value) - 1);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void buildBody(FormWindow *window)
|
||||
{
|
||||
FormGridLayout grid;
|
||||
grid.setLabelWidth(INPUT_EDIT_LABELS_WIDTH);
|
||||
grid.spacer(PAGE_PADDING);
|
||||
|
||||
ExpoData *line = expoAddress(index);
|
||||
|
||||
#if LCD_W > LCD_H
|
||||
grid.setMarginRight(180);
|
||||
#endif
|
||||
|
||||
// Input Name
|
||||
new StaticText(window, grid.getLabelSlot(), STR_INPUTNAME, 0, COLOR_THEME_PRIMARY1);
|
||||
new ModelTextEdit(window, grid.getFieldSlot(), g_model.inputNames[line->chn], sizeof(g_model.inputNames[line->chn]));
|
||||
grid.nextLine();
|
||||
|
||||
// Switch
|
||||
new StaticText(window, grid.getLabelSlot(), STR_SWITCH, 0, COLOR_THEME_PRIMARY1);
|
||||
new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, GET_SET_DEFAULT(line->swtch));
|
||||
grid.nextLine();
|
||||
|
||||
// Side
|
||||
new StaticText(window, grid.getLabelSlot(), STR_SIDE, 0, COLOR_THEME_PRIMARY1);
|
||||
new Choice(window, grid.getFieldSlot(), STR_VCURVEFUNC, 1, 3,
|
||||
[=]() -> int16_t {
|
||||
return 4 - line->mode;
|
||||
},
|
||||
[=](int16_t newValue) {
|
||||
line->mode = 4 - newValue;
|
||||
SET_DIRTY();
|
||||
});
|
||||
grid.nextLine();
|
||||
|
||||
// Name
|
||||
new StaticText(window, grid.getLabelSlot(), STR_EXPONAME, 0, COLOR_THEME_PRIMARY1);
|
||||
new ModelTextEdit(window, grid.getFieldSlot(), line->name, sizeof(line->name));
|
||||
grid.nextLine();
|
||||
|
||||
// Source
|
||||
new StaticText(window, grid.getLabelSlot(), STR_SOURCE, 0, COLOR_THEME_PRIMARY1);
|
||||
new SourceChoice(window, grid.getFieldSlot(2, 0), INPUTSRC_FIRST, INPUTSRC_LAST,
|
||||
GET_DEFAULT(line->srcRaw),
|
||||
[=] (int32_t newValue) {
|
||||
line->srcRaw = newValue;
|
||||
if (line->srcRaw > MIXSRC_Ail && line->carryTrim == TRIM_ON) {
|
||||
line->carryTrim = TRIM_OFF;
|
||||
trimChoice->invalidate();
|
||||
}
|
||||
window->clear();
|
||||
buildBody(window);
|
||||
SET_DIRTY();
|
||||
}
|
||||
);
|
||||
|
||||
SensorValue *sensor = nullptr;
|
||||
if (line->srcRaw >= MIXSRC_FIRST_TELEM) {
|
||||
sensor = new SensorValue(window, grid.getFieldSlot(2, 1), line);
|
||||
|
||||
grid.nextLine();
|
||||
// Scale
|
||||
new StaticText(window, grid.getLabelSlot(), STR_SCALE, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
new NumberEdit(window, grid.getFieldSlot(), 0,
|
||||
maxTelemValue(line->srcRaw - MIXSRC_FIRST_TELEM + 1),
|
||||
GET_SET_DEFAULT(line->scale), 0,
|
||||
sensor->getSensorPrec());
|
||||
adjustHeight();
|
||||
}
|
||||
grid.nextLine();
|
||||
|
||||
// Weight
|
||||
new StaticText(window, grid.getLabelSlot(), STR_WEIGHT, 0, COLOR_THEME_PRIMARY1);
|
||||
auto gvar = new GVarNumberEdit(window, grid.getFieldSlot(), -100, 100, GET_SET_DEFAULT(line->weight));
|
||||
gvar->setSuffix("%");
|
||||
grid.nextLine();
|
||||
|
||||
// Offset
|
||||
new StaticText(window, grid.getLabelSlot(), STR_OFFSET, 0, COLOR_THEME_PRIMARY1);
|
||||
gvar = new GVarNumberEdit(window, grid.getFieldSlot(), -100, 100, GET_SET_DEFAULT(line->offset));
|
||||
gvar->setSuffix("%");
|
||||
grid.nextLine();
|
||||
|
||||
// Trim
|
||||
new StaticText(window, grid.getLabelSlot(), STR_TRIM, 0, COLOR_THEME_PRIMARY1);
|
||||
trimChoice = new Choice(window, grid.getFieldSlot(), STR_VMIXTRIMS, -TRIM_OFF, -TRIM_LAST,
|
||||
GET_VALUE(-line->carryTrim),
|
||||
SET_VALUE(line->carryTrim, -newValue));
|
||||
trimChoice->setAvailableHandler([=](int value) {
|
||||
return value != TRIM_ON || line->srcRaw <= MIXSRC_Ail;
|
||||
});
|
||||
grid.nextLine();
|
||||
|
||||
// Curve
|
||||
new StaticText(&body, grid.getLabelSlot(), STR_CURVE, 0, COLOR_THEME_PRIMARY1);
|
||||
new Choice(&body, grid.getFieldSlot(2, 0), "\004DiffExpoFuncCstm", 0, CURVE_REF_CUSTOM,
|
||||
GET_DEFAULT(line->curve.type),
|
||||
[=](int32_t newValue) {
|
||||
line->curve.type = newValue;
|
||||
line->curve.value = 0;
|
||||
SET_DIRTY();
|
||||
updateCurveParamField(line);
|
||||
});
|
||||
curveParamField = new FormGroup(&body, grid.getFieldSlot(2, 1)
|
||||
// , FORM_FORWARD_FOCUS
|
||||
);
|
||||
updateCurveParamField(line);
|
||||
grid.nextLine();
|
||||
|
||||
#if defined(FLIGHT_MODES)
|
||||
// Flight modes
|
||||
new StaticText(window, grid.getLabelSlot(), STR_FLMODE, 0, COLOR_THEME_PRIMARY1);
|
||||
for (uint32_t i=0; i<MAX_FLIGHT_MODES; i++) {
|
||||
char fm[2] = { char('0' + i), '\0'};
|
||||
if (i > 0 && (i % 4) == 0)
|
||||
grid.nextLine();
|
||||
new TextButton(window, grid.getFieldSlot(4, i % 4), fm,
|
||||
[=]() -> uint32_t {
|
||||
BFBIT_FLIP(line->flightModes, bfBit<uint32_t>(i));
|
||||
SET_DIRTY();
|
||||
return !(bfSingleBitGet(line->flightModes, i));
|
||||
},
|
||||
OPAQUE | (bfSingleBitGet(line->flightModes, i) ? 0 : BUTTON_CHECKED));
|
||||
}
|
||||
grid.nextLine();
|
||||
#endif
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
void CommonInputOrMixButton::checkEvents()
|
||||
{
|
||||
if (active != isActive()) {
|
||||
invalidate();
|
||||
active = !active;
|
||||
}
|
||||
|
||||
Button::checkEvents();
|
||||
}
|
||||
|
||||
void CommonInputOrMixButton::drawFlightModes(BitmapBuffer *dc,
|
||||
FlightModesType value,
|
||||
LcdFlags textColor)
|
||||
{
|
||||
dc->drawMask(146, 2 + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP,
|
||||
mixerSetupFlightmodeIcon, textColor);
|
||||
coord_t x = 166;
|
||||
for (int i = 0; i < MAX_FLIGHT_MODES; i++) {
|
||||
char s[] = " ";
|
||||
s[0] = '0' + i;
|
||||
if (value & (1 << i)) {
|
||||
dc->drawText(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, s,
|
||||
FONT(XS) | COLOR_THEME_DISABLED);
|
||||
} else {
|
||||
dc->drawSolidFilledRect(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, 8, 3,
|
||||
COLOR_THEME_FOCUS);
|
||||
dc->drawText(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, s, FONT(XS) | textColor);
|
||||
}
|
||||
x += 8;
|
||||
}
|
||||
}
|
||||
|
||||
void CommonInputOrMixButton::paint(BitmapBuffer * dc)
|
||||
{
|
||||
dc->drawSolidFilledRect(0, 0, width(), height(),
|
||||
isActive() ? COLOR_THEME_ACTIVE : COLOR_THEME_PRIMARY2);
|
||||
paintBody(dc);
|
||||
|
||||
if (!hasFocus())
|
||||
dc->drawSolidRect(0, 0, rect.w, rect.h, 1, COLOR_THEME_SECONDARY2);
|
||||
else
|
||||
dc->drawSolidRect(0, 0, rect.w, rect.h, 2, COLOR_THEME_FOCUS);
|
||||
}
|
||||
|
||||
class InputLineButton : public CommonInputOrMixButton
|
||||
{
|
||||
public:
|
||||
InputLineButton(FormGroup *parent, const rect_t &rect, uint8_t index) :
|
||||
CommonInputOrMixButton(parent, rect, index)
|
||||
{
|
||||
const ExpoData &line = g_model.expoData[index];
|
||||
if (line.swtch || line.curve.value != 0 || line.flightModes) {
|
||||
setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP);
|
||||
}
|
||||
}
|
||||
|
||||
bool isActive() const override { return isExpoActive(index); }
|
||||
|
||||
void paintBody(BitmapBuffer *dc) override
|
||||
{
|
||||
const ExpoData &line = g_model.expoData[index];
|
||||
|
||||
LcdFlags textColor = COLOR_THEME_SECONDARY1;
|
||||
// if (hasFocus())
|
||||
// textColor = COLOR_THEME_PRIMARY2;
|
||||
|
||||
// first line ...
|
||||
drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight,
|
||||
-100, 100, textColor);
|
||||
drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw, textColor);
|
||||
|
||||
if (line.name[0]) {
|
||||
dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, textColor);
|
||||
dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name),
|
||||
textColor);
|
||||
}
|
||||
|
||||
// second line ...
|
||||
if (line.swtch) {
|
||||
dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP,
|
||||
mixerSetupSwitchIcon, textColor);
|
||||
drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch,
|
||||
textColor);
|
||||
}
|
||||
|
||||
if (line.curve.value != 0) {
|
||||
dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP,
|
||||
mixerSetupCurveIcon, textColor);
|
||||
drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve,
|
||||
textColor);
|
||||
}
|
||||
|
||||
if (line.flightModes) {
|
||||
drawFlightModes(dc, line.flightModes, textColor);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
ModelInputsPage::ModelInputsPage():
|
||||
PageTab(STR_MENUINPUTS, ICON_MODEL_INPUTS)
|
||||
{
|
||||
setOnSetVisibleHandler([]() {
|
||||
// reset clipboard
|
||||
s_copyMode = 0;
|
||||
});
|
||||
}
|
||||
|
||||
void ModelInputsPage::rebuild(FormWindow * window, int8_t focusIndex)
|
||||
{
|
||||
auto scroll_y = lv_obj_get_scroll_y(window->getLvObj());
|
||||
window->clear();
|
||||
build(window, focusIndex);
|
||||
lv_obj_scroll_to_y(window->getLvObj(), scroll_y, LV_ANIM_OFF);
|
||||
}
|
||||
|
||||
void ModelInputsPage::editInput(FormWindow * window, uint8_t input, uint8_t index)
|
||||
{
|
||||
// Window::clearFocus();
|
||||
Window * editWindow = new InputEditWindow(input, index);
|
||||
editWindow->setCloseHandler([=]() {
|
||||
rebuild(window, index);
|
||||
});
|
||||
}
|
||||
|
||||
void ModelInputsPage::build(FormWindow *window, int8_t focusIndex)
|
||||
{
|
||||
FormGridLayout grid;
|
||||
grid.spacer(PAGE_PADDING);
|
||||
grid.setLabelWidth(66);
|
||||
|
||||
int inputIndex = 0;
|
||||
ExpoData *line = g_model.expoData;
|
||||
for (uint8_t input = 0; input < MAX_INPUTS; input++) {
|
||||
if (inputIndex < MAX_EXPOS && line->chn == input && EXPO_VALID(line)) {
|
||||
coord_t h = grid.getWindowHeight();
|
||||
auto txt = new StaticText(window, grid.getLabelSlot(),
|
||||
getSourceString(MIXSRC_FIRST_INPUT + input),
|
||||
BUTTON_BACKGROUND, CENTERED | COLOR_THEME_PRIMARY1);
|
||||
while (inputIndex < MAX_EXPOS && line->chn == input && EXPO_VALID(line)) {
|
||||
Button *button =
|
||||
new InputLineButton(window, grid.getFieldSlot(), inputIndex);
|
||||
button->setPressHandler([=]() -> uint8_t {
|
||||
button->bringToTop();
|
||||
Menu *menu = new Menu(window);
|
||||
menu->addLine(STR_EDIT,
|
||||
[=]() { editInput(window, input, inputIndex); });
|
||||
if (!reachExposLimit()) {
|
||||
menu->addLine(STR_INSERT_BEFORE, [=]() {
|
||||
insertExpo(inputIndex, input);
|
||||
editInput(window, input, inputIndex);
|
||||
});
|
||||
menu->addLine(STR_INSERT_AFTER, [=]() {
|
||||
insertExpo(inputIndex + 1, input);
|
||||
editInput(window, input, inputIndex + 1);
|
||||
});
|
||||
menu->addLine(STR_COPY, [=]() {
|
||||
s_copyMode = COPY_MODE;
|
||||
s_copySrcIdx = inputIndex;
|
||||
});
|
||||
if (s_copyMode != 0) {
|
||||
menu->addLine(STR_PASTE_BEFORE, [=]() {
|
||||
copyExpo(s_copySrcIdx, inputIndex, PASTE_BEFORE);
|
||||
if (s_copyMode == MOVE_MODE) {
|
||||
deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx + 1
|
||||
: s_copySrcIdx);
|
||||
s_copyMode = 0;
|
||||
}
|
||||
rebuild(window, inputIndex);
|
||||
});
|
||||
menu->addLine(STR_PASTE_AFTER, [=]() {
|
||||
copyExpo(s_copySrcIdx, inputIndex, PASTE_AFTER);
|
||||
if (s_copyMode == MOVE_MODE) {
|
||||
deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx + 1
|
||||
: s_copySrcIdx);
|
||||
s_copyMode = 0;
|
||||
}
|
||||
rebuild(window, inputIndex + 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
menu->addLine(STR_MOVE, [=]() {
|
||||
s_copyMode = MOVE_MODE;
|
||||
s_copySrcIdx = inputIndex;
|
||||
});
|
||||
menu->addLine(STR_DELETE, [=]() {
|
||||
deleteExpo(inputIndex);
|
||||
rebuild(window, inputIndex);
|
||||
});
|
||||
return 0;
|
||||
});
|
||||
button->setFocusHandler([=](bool focus) {
|
||||
if (focus) {
|
||||
txt->setBackgroundColor(COLOR_THEME_FOCUS);
|
||||
txt->setTextFlags(COLOR_THEME_PRIMARY2 | LEFT);
|
||||
} else {
|
||||
txt->setBackgroundColor(COLOR_THEME_SECONDARY2);
|
||||
txt->setTextFlags(COLOR_THEME_PRIMARY1 | LEFT);
|
||||
}
|
||||
txt->invalidate();
|
||||
if (focus) button->bringToTop();
|
||||
});
|
||||
|
||||
// if (focusIndex == inputIndex) {
|
||||
// button->setFocus(SET_FOCUS_DEFAULT);
|
||||
// txt->setBackgroundColor(COLOR_THEME_FOCUS);
|
||||
// txt->setTextFlags(COLOR_THEME_PRIMARY2 | LEFT);
|
||||
// txt->invalidate();
|
||||
// }
|
||||
grid.spacer(button->height() - 1);
|
||||
++inputIndex;
|
||||
++line;
|
||||
}
|
||||
|
||||
h = grid.getWindowHeight() - h + 1;
|
||||
txt->setHeight(h);
|
||||
|
||||
grid.spacer(7);
|
||||
} else {
|
||||
auto button = new TextButton(window, grid.getLabelSlot(),
|
||||
getSourceString(MIXSRC_FIRST_INPUT + input),
|
||||
nullptr, BUTTON_BACKGROUND,
|
||||
COLOR_THEME_PRIMARY1 | RIGHT);
|
||||
button->setPressHandler([=]() -> uint8_t {
|
||||
button->bringToTop();
|
||||
Menu *menu = new Menu(window);
|
||||
menu->addLine(STR_EDIT, [=]() {
|
||||
insertExpo(inputIndex, input);
|
||||
editInput(window, input, inputIndex);
|
||||
return 0;
|
||||
});
|
||||
if (!reachExposLimit()) {
|
||||
if (s_copyMode != 0) {
|
||||
menu->addLine(STR_PASTE, [=]() {
|
||||
copyExpo(s_copySrcIdx, inputIndex, input);
|
||||
if (s_copyMode == MOVE_MODE) {
|
||||
int sourceInd = s_copySrcIdx >= inputIndex ? s_copySrcIdx + 1
|
||||
: s_copySrcIdx;
|
||||
memcpy(g_model.inputNames[expoAddress(inputIndex)->chn],
|
||||
g_model.inputNames[expoAddress(sourceInd)->chn],
|
||||
LEN_INPUT_NAME);
|
||||
deleteExpo(sourceInd);
|
||||
s_copyMode = 0;
|
||||
}
|
||||
rebuild(window, inputIndex);
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
grid.spacer(button->height() + 5);
|
||||
}
|
||||
}
|
||||
|
||||
// Window *focus = Window::getFocus();
|
||||
// if (focus) {
|
||||
// focus->bringToTop();
|
||||
// }
|
||||
|
||||
grid.nextLine();
|
||||
|
||||
}
|
||||
|
||||
// TODO port: avoid global s_currCh on ARM boards (as done here)...
|
||||
int8_t s_currCh;
|
||||
uint8_t s_copyMode;
|
||||
@@ -665,3 +104,371 @@ void insertExpo(uint8_t idx, uint8_t input)
|
||||
resumeMixerCalculations();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
static bool getFreeInput(uint8_t& input, uint8_t& index)
|
||||
{
|
||||
uint8_t chn = 0;
|
||||
ExpoData* line = g_model.expoData;
|
||||
for (uint8_t i = 0; i < MAX_EXPOS; i++) {
|
||||
if (!EXPO_VALID(line) || (line->chn > chn)) {
|
||||
if (i >= MAX_EXPOS) break;
|
||||
if (chn >= MAX_INPUTS) break;
|
||||
index = i;
|
||||
input = chn;
|
||||
return true;
|
||||
}
|
||||
chn = line->chn + 1;
|
||||
++line;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
class InputLineButton : public InputMixButton
|
||||
{
|
||||
public:
|
||||
InputLineButton(Window* parent, uint8_t index) :
|
||||
InputMixButton(parent, rect_t{}, index)
|
||||
{
|
||||
auto h = getFontHeight(FONT(STD));
|
||||
h += 2 * lv_obj_get_style_border_width(lvobj, LV_PART_MAIN);
|
||||
h += lv_obj_get_style_pad_top(lvobj, LV_PART_MAIN);
|
||||
h += lv_obj_get_style_pad_bottom(lvobj, LV_PART_MAIN);
|
||||
|
||||
const ExpoData &line = g_model.expoData[index];
|
||||
if (line.swtch || line.curve.value != 0 || line.flightModes) {
|
||||
h += lv_obj_get_style_text_line_space(lvobj, LV_PART_MAIN);
|
||||
h += getFontHeight(FONT(STD));
|
||||
}
|
||||
setHeight(h);
|
||||
}
|
||||
|
||||
bool isActive() const override { return isExpoActive(index); }
|
||||
|
||||
void paint(BitmapBuffer *dc) override
|
||||
{
|
||||
const ExpoData &line = g_model.expoData[index];
|
||||
LcdFlags textColor = COLOR_THEME_SECONDARY1;
|
||||
|
||||
// first line ...
|
||||
coord_t y = 0;
|
||||
y += lv_obj_get_style_border_width(lvobj, LV_PART_MAIN);
|
||||
y += lv_obj_get_style_pad_top(lvobj, LV_PART_MAIN);
|
||||
|
||||
coord_t x = 0;
|
||||
x += lv_obj_get_style_border_width(lvobj, LV_PART_MAIN);
|
||||
x += lv_obj_get_style_pad_left(lvobj, LV_PART_MAIN);
|
||||
|
||||
drawValueOrGVar(dc, x, y, line.weight, -100, 100, textColor);
|
||||
drawSource(dc, x + 65, y, line.srcRaw, textColor);
|
||||
|
||||
if (line.name[0]) {
|
||||
dc->drawMask(146, y, mixerSetupLabelIcon, textColor);
|
||||
dc->drawSizedText(166, y, line.name, sizeof(line.name), textColor);
|
||||
}
|
||||
|
||||
// second line ...
|
||||
y += lv_obj_get_style_text_line_space(lvobj, LV_PART_MAIN);
|
||||
y += getFontHeight(FONT(STD));
|
||||
|
||||
if (line.swtch) {
|
||||
dc->drawMask(x, y, mixerSetupSwitchIcon, textColor);
|
||||
drawSwitch(dc, x + 21, y, line.swtch, textColor);
|
||||
}
|
||||
|
||||
if (line.curve.value != 0) {
|
||||
dc->drawMask(x + 65, y, mixerSetupCurveIcon, textColor);
|
||||
drawCurveRef(dc, x + 85, y, line.curve, textColor);
|
||||
}
|
||||
|
||||
if (line.flightModes) {
|
||||
drawFlightModes(dc, line.flightModes, textColor, 146, y);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ModelInputsPage::ModelInputsPage():
|
||||
PageTab(STR_MENUINPUTS, ICON_MODEL_INPUTS)
|
||||
{
|
||||
setOnSetVisibleHandler([=]() {
|
||||
// reset clipboard
|
||||
_copyMode = 0;
|
||||
});
|
||||
}
|
||||
|
||||
InputMixGroup* ModelInputsPage::getGroupBySrc(mixsrc_t src)
|
||||
{
|
||||
auto g =
|
||||
std::find_if(groups.begin(), groups.end(), [=](InputMixGroup* g) -> bool {
|
||||
return g->getMixSrc() == src;
|
||||
});
|
||||
|
||||
if (g != groups.end()) return *g;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InputMixGroup* ModelInputsPage::getGroupByIndex(uint8_t index)
|
||||
{
|
||||
ExpoData* expo = expoAddress(index);
|
||||
if (!EXPO_VALID(expo)) return nullptr;
|
||||
|
||||
int input = expo->chn;
|
||||
return getGroupBySrc(MIXSRC_FIRST_INPUT + input);
|
||||
}
|
||||
|
||||
InputMixButton* ModelInputsPage::getLineByIndex(uint8_t index)
|
||||
{
|
||||
auto l = std::find_if(lines.begin(), lines.end(), [=](InputMixButton* l) {
|
||||
return l->getIndex() == index;
|
||||
});
|
||||
|
||||
if (l != lines.end()) return *l;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ModelInputsPage::removeGroup(InputMixGroup* g)
|
||||
{
|
||||
auto group = std::find_if(groups.begin(), groups.end(),
|
||||
[=](InputMixGroup* lh) -> bool { return lh == g; });
|
||||
if (group != groups.end()) groups.erase(group);
|
||||
}
|
||||
|
||||
void ModelInputsPage::removeLine(InputMixButton* l)
|
||||
{
|
||||
auto line = std::find_if(lines.begin(), lines.end(),
|
||||
[=](InputMixButton* lh) -> bool { return lh == l; });
|
||||
if (line == lines.end()) return;
|
||||
|
||||
line = lines.erase(line);
|
||||
while (line != lines.end()) {
|
||||
(*line)->setIndex((*line)->getIndex() - 1);
|
||||
++line;
|
||||
}
|
||||
}
|
||||
|
||||
InputMixButton* ModelInputsPage::createLineButton(InputMixGroup *group,
|
||||
uint8_t index)
|
||||
{
|
||||
auto button = new InputLineButton(group, index);
|
||||
lines.emplace_back(button);
|
||||
group->addLine(button);
|
||||
|
||||
uint8_t input = group->getMixSrc() - MIXSRC_FIRST_INPUT;
|
||||
button->setPressHandler([=]() -> uint8_t {
|
||||
Menu *menu = new Menu(form);
|
||||
menu->addLine(STR_EDIT, [=]() { editInput(input, index); });
|
||||
if (!reachExposLimit()) {
|
||||
menu->addLine(STR_INSERT_BEFORE,
|
||||
[=]() {
|
||||
uint8_t idx = button->getIndex();
|
||||
insertInput(input, idx);
|
||||
_copyMode = 0;
|
||||
});
|
||||
menu->addLine(STR_INSERT_AFTER,
|
||||
[=]() {
|
||||
uint8_t idx = button->getIndex();
|
||||
insertInput(input, idx + 1);
|
||||
_copyMode = 0;
|
||||
});
|
||||
menu->addLine(STR_COPY, [=]() {
|
||||
_copyMode = COPY_MODE;
|
||||
_copySrc = button;
|
||||
});
|
||||
if (_copyMode != 0) {
|
||||
menu->addLine(STR_PASTE_BEFORE, [=]() {
|
||||
uint8_t idx = button->getIndex();
|
||||
pasteInputBefore(idx);
|
||||
_copyMode = 0;
|
||||
});
|
||||
menu->addLine(STR_PASTE_AFTER, [=]() {
|
||||
uint8_t idx = button->getIndex();
|
||||
pasteInputAfter(idx);
|
||||
_copyMode = 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
menu->addLine(STR_MOVE, [=]() {
|
||||
_copyMode = MOVE_MODE;
|
||||
_copySrc = button;
|
||||
});
|
||||
menu->addLine(STR_DELETE, [=]() {
|
||||
uint8_t idx = button->getIndex();
|
||||
deleteInput(idx);
|
||||
_copyMode = 0;
|
||||
});
|
||||
return 0;
|
||||
});
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
void ModelInputsPage::addLineButton(uint8_t index)
|
||||
{
|
||||
ExpoData* expo = expoAddress(index);
|
||||
if (!EXPO_VALID(expo)) return;
|
||||
int input = expo->chn;
|
||||
|
||||
addLineButton(MIXSRC_FIRST_INPUT + input, index);
|
||||
}
|
||||
|
||||
void ModelInputsPage::addLineButton(mixsrc_t src, uint8_t index)
|
||||
{
|
||||
InputMixGroup* group_w = getGroupBySrc(src);
|
||||
if (!group_w) {
|
||||
group_w = new InputMixGroup(form, src);
|
||||
// insertion sort
|
||||
groups.emplace_back(group_w);
|
||||
auto g = groups.rbegin();
|
||||
if (g != groups.rend()) {
|
||||
auto g_prev = g; ++g_prev;
|
||||
while (g_prev != groups.rend()) {
|
||||
if ((*g_prev)->getMixSrc() < (*g)->getMixSrc()) break;
|
||||
lv_obj_swap((*g)->getLvObj(), (*g_prev)->getLvObj());
|
||||
std::swap(*g, *g_prev);
|
||||
++g; ++g_prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create new line button
|
||||
auto btn = createLineButton(group_w, index);
|
||||
lv_group_focus_obj(btn->getLvObj());
|
||||
|
||||
// insertion sort for the focus group
|
||||
auto l = lines.rbegin();
|
||||
if (l != lines.rend()) {
|
||||
auto l_prev = l; ++l_prev;
|
||||
while (l_prev != lines.rend()) {
|
||||
if ((*l_prev)->getIndex() < (*l)->getIndex()) break;
|
||||
// Swap elements (focus + line list)
|
||||
lv_obj_t* obj1 = (*l)->getLvObj();
|
||||
lv_obj_t* obj2 = (*l_prev)->getLvObj();
|
||||
if (lv_obj_get_parent(obj1) == lv_obj_get_parent(obj2)) {
|
||||
// same input group: swap obj + focus group
|
||||
lv_obj_swap(obj1, obj2);
|
||||
} else {
|
||||
// different input group: swap only focus group
|
||||
lv_group_swap_obj(obj1, obj2);
|
||||
}
|
||||
std::swap(*l, *l_prev);
|
||||
// Inc index of elements after
|
||||
(*l)->setIndex((*l)->getIndex() + 1);
|
||||
++l; ++l_prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModelInputsPage::editInput(uint8_t input, uint8_t index)
|
||||
{
|
||||
new InputEditWindow(input, index);
|
||||
}
|
||||
|
||||
void ModelInputsPage::insertInput(uint8_t input, uint8_t index)
|
||||
{
|
||||
::insertExpo(index, input);
|
||||
addLineButton(MIXSRC_FIRST_INPUT + input, index);
|
||||
editInput(input, index);
|
||||
}
|
||||
|
||||
void ModelInputsPage::deleteInput(uint8_t index)
|
||||
{
|
||||
auto group = getGroupByIndex(index);
|
||||
if (!group) return;
|
||||
|
||||
auto line = getLineByIndex(index);
|
||||
if (!line) return;
|
||||
|
||||
group->removeLine(line);
|
||||
if (group->getLineCount() == 0) {
|
||||
group->deleteLater();
|
||||
removeGroup(group);
|
||||
removeLine(line);
|
||||
} else {
|
||||
line->deleteLater();
|
||||
removeLine(line);
|
||||
}
|
||||
|
||||
::deleteExpo(index);
|
||||
}
|
||||
|
||||
void ModelInputsPage::pasteInput(uint8_t dst_idx, uint8_t input)
|
||||
{
|
||||
if (!_copyMode || !_copySrc) return;
|
||||
uint8_t src_idx = _copySrc->getIndex();
|
||||
|
||||
::copyExpo(src_idx, dst_idx, input);
|
||||
addLineButton(dst_idx);
|
||||
|
||||
if (_copyMode == MOVE_MODE) {
|
||||
deleteInput(src_idx);
|
||||
}
|
||||
}
|
||||
|
||||
static int _inputChnFromIndex(uint8_t index)
|
||||
{
|
||||
ExpoData* expo = expoAddress(index);
|
||||
if (!EXPO_VALID(expo)) return -1;
|
||||
return expo->chn;
|
||||
}
|
||||
|
||||
void ModelInputsPage::pasteInputBefore(uint8_t dst_idx)
|
||||
{
|
||||
int input = _inputChnFromIndex(dst_idx);
|
||||
pasteInput(dst_idx, input);
|
||||
}
|
||||
|
||||
void ModelInputsPage::pasteInputAfter(uint8_t dst_idx)
|
||||
{
|
||||
int input = _inputChnFromIndex(dst_idx);
|
||||
pasteInput(dst_idx + 1, input);
|
||||
}
|
||||
|
||||
void ModelInputsPage::build(FormWindow *window)
|
||||
{
|
||||
window->setFlexLayout(LV_FLEX_FLOW_COLUMN, 8);
|
||||
lv_obj_set_style_pad_all(window->getLvObj(), 8, 0);
|
||||
|
||||
form = new FormGroup(window, rect_t{});
|
||||
form->setFlexLayout(LV_FLEX_FLOW_COLUMN, 8);
|
||||
|
||||
auto form_obj = form->getLvObj();
|
||||
lv_obj_set_width(form_obj, lv_pct(100));
|
||||
|
||||
auto btn = new TextButton(window, rect_t{}, LV_SYMBOL_PLUS, [=]() {
|
||||
uint8_t input, index;
|
||||
if (getFreeInput(input, index)) insertInput(input, index);
|
||||
return 0;
|
||||
});
|
||||
auto btn_obj = btn->getLvObj();
|
||||
lv_obj_set_width(btn_obj, lv_pct(100));
|
||||
|
||||
groups.clear();
|
||||
lines.clear();
|
||||
|
||||
uint8_t index = 0;
|
||||
ExpoData* line = g_model.expoData;
|
||||
for (uint8_t input = 0; input < MAX_INPUTS; input++) {
|
||||
|
||||
if (index >= MAX_EXPOS) break;
|
||||
|
||||
if (line->chn == input && EXPO_VALID(line)) {
|
||||
// one group for the complete input channel
|
||||
auto group = new InputMixGroup(form, MIXSRC_FIRST_INPUT + input);
|
||||
groups.emplace_back(group);
|
||||
while (index < MAX_EXPOS && line->chn == input && EXPO_VALID(line)) {
|
||||
// one button per input line
|
||||
createLineButton(group, index);
|
||||
++index;
|
||||
++line;
|
||||
}
|
||||
} else if (line->chn > input && EXPO_VALID(line)) {
|
||||
TRACE("missing input for channel #%d", input);
|
||||
} else if (!EXPO_VALID(line)) {
|
||||
TRACE("invalid line #%d", index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,54 +19,43 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _MODEL_INPUTS_H
|
||||
#define _MODEL_INPUTS_H
|
||||
#pragma once
|
||||
|
||||
#include "tabsgroup.h"
|
||||
|
||||
class ModelInputsPage: public PageTab {
|
||||
public:
|
||||
ModelInputsPage();
|
||||
class InputMixGroup;
|
||||
class InputMixButton;
|
||||
|
||||
void build(FormWindow * window) override
|
||||
{
|
||||
build(window, 0);
|
||||
}
|
||||
class ModelInputsPage : public PageTab
|
||||
{
|
||||
public:
|
||||
ModelInputsPage();
|
||||
|
||||
protected:
|
||||
void build(FormWindow * window, int8_t focusIndex);
|
||||
void rebuild(FormWindow * window, int8_t focusIndex);
|
||||
void editInput(FormWindow * window, uint8_t channel, uint8_t index);
|
||||
uint8_t s_copySrcIdx;
|
||||
void build(FormWindow *window) override;
|
||||
|
||||
protected:
|
||||
FormGroup* form = nullptr;
|
||||
std::list<InputMixGroup*> groups;
|
||||
std::list<InputMixButton*> lines;
|
||||
InputMixButton* _copySrc = nullptr;
|
||||
uint8_t _copyMode = 0;
|
||||
|
||||
InputMixGroup* getGroupBySrc(mixsrc_t src);
|
||||
virtual InputMixGroup* getGroupByIndex(uint8_t index);
|
||||
InputMixButton* getLineByIndex(uint8_t index);
|
||||
|
||||
void removeGroup(InputMixGroup* g);
|
||||
void removeLine(InputMixButton* l);
|
||||
|
||||
virtual void addLineButton(uint8_t index);
|
||||
virtual void addLineButton(mixsrc_t src, uint8_t index);
|
||||
virtual InputMixButton* createLineButton(InputMixGroup *group, uint8_t index);
|
||||
|
||||
void editInput(uint8_t input, uint8_t index);
|
||||
void insertInput(uint8_t input, uint8_t index);
|
||||
void deleteInput(uint8_t index);
|
||||
|
||||
void pasteInput(uint8_t dst_idx, uint8_t input);
|
||||
void pasteInputBefore(uint8_t dst_idx);
|
||||
void pasteInputAfter(uint8_t dst_idx);
|
||||
};
|
||||
|
||||
class CommonInputOrMixButton : public Button {
|
||||
public:
|
||||
CommonInputOrMixButton(FormGroup * parent, const rect_t & rect, uint8_t index):
|
||||
Button(parent, rect, nullptr, 0, COLOR_THEME_PRIMARY1),
|
||||
index(index)
|
||||
{
|
||||
setFocusHandler([=](bool active) {
|
||||
if (active) {
|
||||
bringToTop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
virtual bool isActive() const = 0;
|
||||
|
||||
void checkEvents() override;
|
||||
|
||||
void drawFlightModes(BitmapBuffer *dc, FlightModesType value, LcdFlags textColor);
|
||||
|
||||
void paint(BitmapBuffer * dc) override;
|
||||
|
||||
virtual void paintBody(BitmapBuffer * dc) = 0;
|
||||
|
||||
protected:
|
||||
uint8_t index;
|
||||
bool active = false;
|
||||
};
|
||||
|
||||
|
||||
#endif //_MODEL_INPUTS_H
|
||||
|
||||
@@ -515,13 +515,6 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex)
|
||||
if (focus) button->bringToTop();
|
||||
});
|
||||
|
||||
// if (focusMixIndex == mixIndex) {
|
||||
// button->setFocus(SET_FOCUS_DEFAULT);
|
||||
// txt->setBackgroundColor(COLOR_THEME_FOCUS);
|
||||
// txt->setTextFlags(COLOR_THEME_PRIMARY2 | CENTERED);
|
||||
// txt->invalidate();
|
||||
// }
|
||||
|
||||
grid.spacer(button->height() - 1);
|
||||
++mixIndex;
|
||||
++mix;
|
||||
@@ -537,7 +530,6 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex)
|
||||
else {
|
||||
auto button = new TextButton(window, grid.getLabelSlot(),
|
||||
getSourceString(MIXSRC_CH1 + ch));
|
||||
// if (focusMixIndex == mixIndex) button->setFocus(SET_FOCUS_DEFAULT);
|
||||
button->setPressHandler([=]() -> uint8_t {
|
||||
button->bringToTop();
|
||||
Menu *menu = new Menu(window);
|
||||
@@ -567,13 +559,7 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex)
|
||||
}
|
||||
}
|
||||
|
||||
// Window * focus = Window::getFocus();
|
||||
// if (focus) {
|
||||
// focus->bringToTop();
|
||||
// }
|
||||
|
||||
grid.nextLine();
|
||||
|
||||
}
|
||||
|
||||
void deleteMix(uint8_t idx)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*********************/
|
||||
#include "etx_lv_theme.h"
|
||||
#include "../colors.h"
|
||||
#include "lvgl_widgets/input_mix_line.h"
|
||||
|
||||
extern lv_color_t makeLvColor(uint32_t colorFlags);
|
||||
|
||||
@@ -44,6 +45,7 @@ typedef struct {
|
||||
lv_style_t scrollbar_scrolled;
|
||||
lv_style_t card;
|
||||
lv_style_t btn;
|
||||
lv_style_t line_btn;
|
||||
|
||||
/*Utility*/
|
||||
lv_style_t bg_color_primary;
|
||||
@@ -294,6 +296,20 @@ static void style_init(void)
|
||||
lv_style_set_pad_column(&styles.btn, lv_disp_dpx(theme.disp, 5));
|
||||
lv_style_set_pad_row(&styles.btn, lv_disp_dpx(theme.disp, 5));
|
||||
|
||||
style_init_reset(&styles.line_btn);
|
||||
lv_style_set_radius(
|
||||
&styles.line_btn,
|
||||
(disp_size == DISP_LARGE ? lv_disp_dpx(theme.disp, 16)
|
||||
: disp_size == DISP_MEDIUM ? lv_disp_dpx(theme.disp, 12)
|
||||
: lv_disp_dpx(theme.disp, 8)));
|
||||
|
||||
lv_style_set_bg_opa(&styles.line_btn, LV_OPA_COVER);
|
||||
lv_style_set_bg_color(&styles.line_btn, makeLvColor(COLOR_THEME_PRIMARY2));
|
||||
|
||||
lv_style_set_border_opa(&styles.line_btn, LV_OPA_COVER);
|
||||
lv_style_set_border_width(&styles.line_btn, BORDER_WIDTH);
|
||||
lv_style_set_border_color(&styles.line_btn, makeLvColor(COLOR_THEME_SECONDARY2));
|
||||
|
||||
static lv_color_filter_dsc_t dark_filter;
|
||||
lv_color_filter_dsc_init(&dark_filter, dark_color_filter_cb);
|
||||
|
||||
@@ -779,6 +795,12 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if(lv_obj_check_type(obj, &input_mix_line_class)) {
|
||||
lv_obj_add_style(obj, &styles.line_btn, 0);
|
||||
lv_obj_add_style(obj, &styles.pad_tiny, 0);
|
||||
lv_obj_add_style(obj, &styles.bg_color_active, LV_STATE_CHECKED);
|
||||
lv_obj_add_style(obj, &styles.focus_border, LV_STATE_FOCUSED);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LV_USE_LINE
|
||||
|
||||
Reference in New Issue
Block a user