Mixers rework

This commit is contained in:
Raphael Coeffic
2022-06-11 15:38:08 +02:00
committed by Raphael Coeffic
parent 9aaca6595e
commit 86c72e3a2a
12 changed files with 853 additions and 559 deletions
+1
View File
@@ -168,6 +168,7 @@ add_gui_src(sourcechoice.cpp)
add_gui_src(switchchoice.cpp)
add_gui_src(curveedit.cpp)
add_gui_src(input_edit.cpp)
add_gui_src(mixer_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)
-15
View File
@@ -116,15 +116,6 @@ static const uint8_t mask_monitor_lockch[] = {
static const uint8_t mask_monitor_logsw[] = {
#include "mask_monitor_logsw.lbm"
};
static const uint8_t mask_mplex_add[] = {
#include "mask_mplex_add.lbm"
};
static const uint8_t mask_mplex_multi[] = {
#include "mask_mplex_multi.lbm"
};
static const uint8_t mask_mplex_replace[] = {
#include "mask_mplex_replace.lbm"
};
static const uint8_t mask_edgetx[] = {
#include "mask_edgetx.lbm"
};
@@ -256,9 +247,6 @@ BitmapBuffer * chanMonInvertedBitmap = nullptr;
BitmapBuffer * mixerSetupMixerBitmap = nullptr;
BitmapBuffer * mixerSetupToBitmap = nullptr;
BitmapBuffer * mixerSetupOutputBitmap = nullptr;
BitmapBuffer * mixerSetupAddBitmap = nullptr;
BitmapBuffer * mixerSetupMultiBitmap = nullptr;
BitmapBuffer * mixerSetupReplaceBitmap = nullptr;
BitmapBuffer * mixerSetupCurveIcon = nullptr;
BitmapBuffer * mixerSetupSwitchIcon = nullptr;
BitmapBuffer * mixerSetupLabelIcon = nullptr;
@@ -295,9 +283,6 @@ static const _BuiltinBitmap _builtinBitmaps[] = {
{BMP_8BIT, mask_sbar_to, &mixerSetupToBitmap},
{BMP_8BIT, mask_sbar_output, &mixerSetupOutputBitmap},
{BMP_8BIT, mask_mplex_add, &mixerSetupAddBitmap},
{BMP_8BIT, mask_mplex_multi, &mixerSetupMultiBitmap},
{BMP_8BIT, mask_mplex_replace, &mixerSetupReplaceBitmap},
{BMP_8BIT, mask_textline_label, &mixerSetupLabelIcon},
{BMP_8BIT, mask_textline_curve, &mixerSetupCurveIcon},
+11
View File
@@ -21,6 +21,7 @@
#pragma once
#include "definitions.h"
#include "lz4_bitmaps.h"
DEFINE_LZ4_BITMAP(LBM_POINT);
@@ -59,3 +60,13 @@ extern BitmapBuffer * mixerSetupDelaySlowIcon;
void loadBuiltinBitmaps();
const uint8_t* getBuiltinIcon(MenuIcons id);
PACK(struct _bitmap_mask {
uint16_t w;
uint16_t h;
uint8_t mask[0];
});
#define MASK_WIDTH(m) (((_bitmap_mask*)m)->w)
#define MASK_HEIGHT(m) (((_bitmap_mask*)m)->h)
#define MASK_DATA(m) (((_bitmap_mask*)m)->mask)
+36 -2
View File
@@ -24,11 +24,30 @@
#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,
void InputMixButton::self_size(lv_event_t* e)
{
auto obj = lv_event_get_target(e);
auto p = (lv_point_t*)lv_event_get_param(e);
auto btn = (InputMixButton*)lv_obj_get_user_data(obj);
if (!btn) return;
p->y = btn->calcHeight();
}
void InputMixButton::value_changed(lv_event_t* e)
{
auto obj = lv_event_get_target(e);
lv_obj_refresh_self_size(obj);
}
InputMixButton::InputMixButton(Window* parent, uint8_t index) :
Button(parent, rect_t{}, nullptr, 0, COLOR_THEME_PRIMARY1,
input_mix_line_create),
index(index)
{
lv_obj_add_event_cb(lvobj, InputMixButton::self_size, LV_EVENT_GET_SELF_SIZE, nullptr);
lv_obj_add_event_cb(lvobj, InputMixButton::value_changed, LV_EVENT_VALUE_CHANGED, nullptr);
}
void InputMixButton::checkEvents()
@@ -56,4 +75,19 @@ void InputMixButton::drawFlightModes(BitmapBuffer *dc, FlightModesType value,
}
}
lv_coord_t InputMixButton::calcHeight() const
{
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);
size_t lines = getLines();
if (lines > 1) {
lines -= 1;
h += lines * lv_obj_get_style_text_line_space(lvobj, LV_PART_MAIN);
h += lines * getFontHeight(FONT(STD));
}
return h;
}
+6 -4
View File
@@ -26,7 +26,7 @@
class InputMixButton : public Button
{
public:
InputMixButton(Window* parent, const rect_t& rect, uint8_t index);
InputMixButton(Window* parent, uint8_t index);
uint8_t getIndex() const { return index; }
void setIndex(uint8_t i) { index = i; }
@@ -36,11 +36,13 @@ class InputMixButton : public Button
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;
static void self_size(lv_event_t* e);
static void value_changed(lv_event_t* e);
lv_coord_t calcHeight() const;
virtual size_t getLines() const = 0;
virtual bool isActive() const = 0;
};
@@ -19,7 +19,7 @@
const lv_obj_class_t input_mix_line_class = {
.width_def = LV_PCT(100),
.height_def = LV_DPI_DEF / 4,
.height_def = LV_SIZE_CONTENT,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.instance_size = sizeof(lv_btn_t),
.base_class = &lv_btn_class,
+262
View File
@@ -0,0 +1,262 @@
/*
* 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 "mixer_edit.h"
#include "channel_bar.h"
#include "gvar_numberedit.h"
#include "model_curves.h"
#include "opentx.h"
#define SET_DIRTY() storageDirty(EE_MODEL)
#if (LCD_W > LCD_H)
#define MIX_STATUS_BAR_WIDTH 250
#define MIX_STATUS_BAR_MARGIN 3
#define MIX_RIGHT_MARGIN 0
#else
#define MIX_STATUS_BAR_WIDTH 180
#define MIX_STATUS_BAR_MARGIN 0
#define MIX_RIGHT_MARGIN 3
#endif
class MixerEditStatusBar : public Window
{
public:
MixerEditStatusBar(Window *parent, const rect_t &rect, int8_t channel) :
Window(parent, rect),
_channel(channel)
{
channelBar = new ComboChannelBar(this, {MIX_STATUS_BAR_MARGIN, 0, rect.w - (MIX_STATUS_BAR_MARGIN * 2), rect.h}, channel);
channelBar->setLeftMargin(0);
channelBar->setTextColor(COLOR_THEME_PRIMARY2);
channelBar->setOutputChannelBarLimitColor(COLOR_THEME_EDIT);
}
void paint(BitmapBuffer *dc) override
{
// dc->clear(COLOR_THEME_SECONDARY2);
}
protected:
ComboChannelBar *channelBar;
int8_t _channel;
};
MixEditWindow::MixEditWindow(int8_t channel, uint8_t mixIndex) :
Page(ICON_MODEL_MIXER), channel(channel), mixIndex(mixIndex)
{
buildBody(&body);
buildHeader(&header);
}
void MixEditWindow::buildHeader(Window *window)
{
new StaticText(window,
{PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT,
PAGE_LINE_HEIGHT},
STR_MIXES, 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_CH1 + channel), 0,
COLOR_THEME_PRIMARY2);
statusBar = new MixerEditStatusBar(
window,
{window->getRect().w - MIX_STATUS_BAR_WIDTH - MIX_RIGHT_MARGIN, 0,
MIX_STATUS_BAR_WIDTH, MENU_HEADER_HEIGHT + 3},
channel);
}
void MixEditWindow::buildBody(FormWindow *window)
{
FormGridLayout grid;
grid.spacer(8);
MixData *mix = mixAddress(mixIndex);
// Mix name
new StaticText(window, grid.getLabelSlot(), STR_MIXNAME, 0,
COLOR_THEME_PRIMARY1);
new ModelTextEdit(window, grid.getFieldSlot(), mix->name, sizeof(mix->name));
grid.nextLine();
// Source
new StaticText(window, grid.getLabelSlot(), STR_SOURCE, 0,
COLOR_THEME_PRIMARY1);
new SourceChoice(window, grid.getFieldSlot(), 0, MIXSRC_LAST,
GET_SET_DEFAULT(mix->srcRaw));
grid.nextLine();
// Weight
new StaticText(window, grid.getLabelSlot(), STR_WEIGHT, 0,
COLOR_THEME_PRIMARY1);
auto gvar = new GVarNumberEdit(window, grid.getFieldSlot(), MIX_WEIGHT_MIN,
MIX_WEIGHT_MAX, GET_SET_DEFAULT(mix->weight));
gvar->setSuffix("%");
grid.nextLine();
// Offset
new StaticText(window, grid.getLabelSlot(), STR_OFFSET, 0,
COLOR_THEME_PRIMARY1);
gvar = new GVarNumberEdit(window, grid.getFieldSlot(), MIX_OFFSET_MIN,
MIX_OFFSET_MAX, GET_SET_DEFAULT(mix->offset));
gvar->setSuffix("%");
grid.nextLine();
// Trim
new StaticText(window, grid.getLabelSlot(), STR_TRIM, 0,
COLOR_THEME_PRIMARY1);
new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(mix->carryTrim));
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(mix->curve.type),
[=](int32_t newValue) {
mix->curve.type = newValue;
mix->curve.value = 0;
SET_DIRTY();
updateCurveParamField(mix);
});
curveParamField =
new FormGroup(&body, grid.getFieldSlot(2, 1) // , FORM_FORWARD_FOCUS
);
updateCurveParamField(mix);
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(mix->flightModes, bfBit<uint32_t>(i));
SET_DIRTY();
return !(bfSingleBitGet(mix->flightModes, i));
},
OPAQUE | (bfSingleBitGet(mix->flightModes, i) ? 0 : BUTTON_CHECKED));
}
grid.nextLine();
#endif
// 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(mix->swtch));
grid.nextLine();
// Warning
new StaticText(window, grid.getLabelSlot(), STR_MIXWARNING, 0,
COLOR_THEME_PRIMARY1);
auto edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, 3,
GET_SET_DEFAULT(mix->mixWarn));
edit->setZeroText(STR_OFF);
grid.nextLine();
// Multiplex
new StaticText(window, grid.getLabelSlot(), STR_MULTPX, 0,
COLOR_THEME_PRIMARY1);
new Choice(window, grid.getFieldSlot(), STR_VMLTPX, 0, 2,
GET_SET_DEFAULT(mix->mltpx));
grid.nextLine();
// Delay up
new StaticText(window, grid.getLabelSlot(), STR_DELAYUP, 0,
COLOR_THEME_PRIMARY1);
edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX,
GET_DEFAULT(mix->delayUp),
SET_VALUE(mix->delayUp, newValue), 0, PREC1);
edit->setSuffix("s");
grid.nextLine();
// Delay down
new StaticText(window, grid.getLabelSlot(), STR_DELAYDOWN, 0,
COLOR_THEME_PRIMARY1);
edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX,
GET_DEFAULT(mix->delayDown),
SET_VALUE(mix->delayDown, newValue), 0, PREC1);
edit->setSuffix("s");
grid.nextLine();
// Slow up
new StaticText(window, grid.getLabelSlot(), STR_SLOWUP, 0,
COLOR_THEME_PRIMARY1);
edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX,
GET_DEFAULT(mix->speedUp),
SET_VALUE(mix->speedUp, newValue), 0, PREC1);
edit->setSuffix("s");
grid.nextLine();
// Slow down
new StaticText(window, grid.getLabelSlot(), STR_SLOWDOWN, 0,
COLOR_THEME_PRIMARY1);
edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX,
GET_DEFAULT(mix->speedDown),
SET_VALUE(mix->speedDown, newValue), 0, PREC1);
edit->setSuffix("s");
grid.nextLine();
}
// TODO share this code with INPUT
void MixEditWindow::updateCurveParamField(MixData *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) {
MixData *mix = mixAddress(mixIndex);
// if no curve is specified then dont link to curve page
if (mix->curve.value != 0)
ModelCurvesPage::pushEditCurve(abs(mix->curve.value) - 1);
});
break;
}
}
}
+48
View File
@@ -0,0 +1,48 @@
/*
* 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 "form.h"
#include "curve.h"
struct MixData;
class FormGroup;
class MixerEditStatusBar;
class MixEditWindow : public Page
{
public:
MixEditWindow(int8_t channel, uint8_t mixIndex);
protected:
uint8_t channel;
uint8_t mixIndex;
FormGroup *curveParamField = nullptr;
MixerEditStatusBar *statusBar = nullptr;
void buildHeader(Window *window);
void buildBody(FormWindow *window);
// TODO share this code with INPUT
void updateCurveParamField(MixData *line);
};
+20 -18
View File
@@ -127,23 +127,7 @@ static bool getFreeInput(uint8_t& input, uint8_t& index)
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); }
using InputMixButton::InputMixButton;
void paint(BitmapBuffer *dc) override
{
@@ -185,6 +169,17 @@ class InputLineButton : public InputMixButton
drawFlightModes(dc, line.flightModes, textColor, 146, y);
}
}
protected:
bool isActive() const override { return isExpoActive(index); }
size_t getLines() const override {
const ExpoData* line = expoAddress(index);
if (line->swtch || line->curve.value != 0 || line->flightModes) {
return 2;
} else {
return 1;
}
}
};
ModelInputsPage::ModelInputsPage():
@@ -362,7 +357,14 @@ void ModelInputsPage::addLineButton(mixsrc_t src, uint8_t index)
void ModelInputsPage::editInput(uint8_t input, uint8_t index)
{
new InputEditWindow(input, index);
auto line = getLineByIndex(index);
if (!line) return;
auto line_obj = line->getLvObj();
auto edit = new InputEditWindow(input, index);
edit->setCloseHandler([=]() {
lv_event_send(line_obj, LV_EVENT_VALUE_CHANGED, nullptr);
});
}
void ModelInputsPage::insertInput(uint8_t input, uint8_t index)
+438 -501
View File
@@ -26,15 +26,30 @@
#include "bitfield.h"
#include "model_inputs.h"
#include "gvar_numberedit.h"
#include "model_curves.h"
#include "dataconstants.h"
#include "channel_bar.h"
#include "input_mix_group.h"
#include "input_mix_button.h"
#include "mixer_edit.h"
#define SET_DIRTY() storageDirty(EE_MODEL)
#define PASTE_BEFORE -2
#define PASTE_AFTER -1
static const uint8_t _mask_mplex_add[] = {
#include "mask_mplex_add.lbm"
};
STATIC_LZ4_BITMAP(mask_mplex_add);
static const uint8_t _mask_mplex_multi[] = {
#include "mask_mplex_multi.lbm"
};
STATIC_LZ4_BITMAP(mask_mplex_multi);
static const uint8_t _mask_mplex_replace[] = {
#include "mask_mplex_replace.lbm"
};
STATIC_LZ4_BITMAP(mask_mplex_replace);
uint8_t getMixesCount()
{
uint8_t count = 0;
@@ -58,298 +73,6 @@ bool reachMixesLimit()
return false;
}
#if (LCD_W > LCD_H)
#define MIX_STATUS_BAR_WIDTH 250
#define MIX_STATUS_BAR_MARGIN 3
#define MIX_RIGHT_MARGIN 0
#else
#define MIX_STATUS_BAR_WIDTH 180
#define MIX_STATUS_BAR_MARGIN 0
#define MIX_RIGHT_MARGIN 3
#endif
class MixerEditStatusBar : public Window
{
public:
MixerEditStatusBar(Window *parent, const rect_t &rect, int8_t channel) :
Window(parent, rect),
_channel(channel)
{
channelBar = new ComboChannelBar(this, {MIX_STATUS_BAR_MARGIN, 0, rect.w - (MIX_STATUS_BAR_MARGIN * 2), rect.h}, channel);
channelBar->setLeftMargin(0);
channelBar->setTextColor(COLOR_THEME_PRIMARY2);
channelBar->setOutputChannelBarLimitColor(COLOR_THEME_EDIT);
}
void paint(BitmapBuffer *dc) override
{
// dc->clear(COLOR_THEME_SECONDARY2);
}
protected:
ComboChannelBar *channelBar;
int8_t _channel;
};
class MixEditWindow : public Page
{
public:
MixEditWindow(int8_t channel, uint8_t mixIndex) :
Page(ICON_MODEL_MIXER), channel(channel), mixIndex(mixIndex)
{
buildBody(&body);
buildHeader(&header);
}
protected:
uint8_t channel;
uint8_t mixIndex;
FormGroup *curveParamField = nullptr;
MixerEditStatusBar *statusBar = nullptr;
void buildHeader(Window *window)
{
new StaticText(window,
{PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT,
PAGE_LINE_HEIGHT},
STR_MIXES, 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_CH1 + channel), 0, COLOR_THEME_PRIMARY2);
statusBar = new MixerEditStatusBar(
window, {window->getRect().w - MIX_STATUS_BAR_WIDTH - MIX_RIGHT_MARGIN, 0, MIX_STATUS_BAR_WIDTH, MENU_HEADER_HEIGHT + 3},
channel);
}
void buildBody(FormWindow *window)
{
FormGridLayout grid;
grid.spacer(8);
MixData *mix = mixAddress(mixIndex);
// Mix name
new StaticText(window, grid.getLabelSlot(), STR_MIXNAME, 0, COLOR_THEME_PRIMARY1);
new ModelTextEdit(window, grid.getFieldSlot(), mix->name,
sizeof(mix->name));
grid.nextLine();
// Source
new StaticText(window, grid.getLabelSlot(), STR_SOURCE, 0, COLOR_THEME_PRIMARY1);
new SourceChoice(window, grid.getFieldSlot(), 0, MIXSRC_LAST,
GET_SET_DEFAULT(mix->srcRaw));
grid.nextLine();
// Weight
new StaticText(window, grid.getLabelSlot(), STR_WEIGHT, 0, COLOR_THEME_PRIMARY1);
auto gvar =
new GVarNumberEdit(window, grid.getFieldSlot(), MIX_WEIGHT_MIN,
MIX_WEIGHT_MAX, GET_SET_DEFAULT(mix->weight));
gvar->setSuffix("%");
grid.nextLine();
// Offset
new StaticText(window, grid.getLabelSlot(), STR_OFFSET, 0, COLOR_THEME_PRIMARY1);
gvar = new GVarNumberEdit(window, grid.getFieldSlot(), MIX_OFFSET_MIN,
MIX_OFFSET_MAX, GET_SET_DEFAULT(mix->offset));
gvar->setSuffix("%");
grid.nextLine();
// Trim
new StaticText(window, grid.getLabelSlot(), STR_TRIM, 0, COLOR_THEME_PRIMARY1);
new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(mix->carryTrim));
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(mix->curve.type),
[=](int32_t newValue) {
mix->curve.type = newValue;
mix->curve.value = 0;
SET_DIRTY();
updateCurveParamField(mix);
});
curveParamField =
new FormGroup(&body, grid.getFieldSlot(2, 1)// , FORM_FORWARD_FOCUS
);
updateCurveParamField(mix);
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(mix->flightModes, bfBit<uint32_t>(i));
SET_DIRTY();
return !(bfSingleBitGet(mix->flightModes, i));
},
OPAQUE | (bfSingleBitGet(mix->flightModes, i) ? 0 : BUTTON_CHECKED));
}
grid.nextLine();
#endif
// 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(mix->swtch));
grid.nextLine();
// Warning
new StaticText(window, grid.getLabelSlot(), STR_MIXWARNING, 0, COLOR_THEME_PRIMARY1);
auto edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, 3,
GET_SET_DEFAULT(mix->mixWarn));
edit->setZeroText(STR_OFF);
grid.nextLine();
// Multiplex
new StaticText(window, grid.getLabelSlot(), STR_MULTPX, 0, COLOR_THEME_PRIMARY1);
new Choice(window, grid.getFieldSlot(), STR_VMLTPX, 0, 2,
GET_SET_DEFAULT(mix->mltpx));
grid.nextLine();
// Delay up
new StaticText(window, grid.getLabelSlot(), STR_DELAYUP, 0, COLOR_THEME_PRIMARY1);
edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX,
GET_DEFAULT(mix->delayUp),
SET_VALUE(mix->delayUp, newValue), 0, PREC1);
edit->setSuffix("s");
grid.nextLine();
// Delay down
new StaticText(window, grid.getLabelSlot(), STR_DELAYDOWN, 0, COLOR_THEME_PRIMARY1);
edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX,
GET_DEFAULT(mix->delayDown),
SET_VALUE(mix->delayDown, newValue), 0, PREC1);
edit->setSuffix("s");
grid.nextLine();
// Slow up
new StaticText(window, grid.getLabelSlot(), STR_SLOWUP, 0, COLOR_THEME_PRIMARY1);
edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX,
GET_DEFAULT(mix->speedUp),
SET_VALUE(mix->speedUp, newValue), 0, PREC1);
edit->setSuffix("s");
grid.nextLine();
// Slow down
new StaticText(window, grid.getLabelSlot(), STR_SLOWDOWN, 0, COLOR_THEME_PRIMARY1);
edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX,
GET_DEFAULT(mix->speedDown),
SET_VALUE(mix->speedDown, newValue), 0, PREC1);
edit->setSuffix("s");
grid.nextLine();
}
// TODO share this code with INPUT
void updateCurveParamField(MixData *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) {
MixData *mix = mixAddress(mixIndex);
// if no curve is specified then dont link to curve page
if (mix->curve.value != 0)
ModelCurvesPage::pushEditCurve(abs(mix->curve.value) - 1);
});
break;
}
}
}
};
class MixLineButton : public CommonInputOrMixButton
{
public:
MixLineButton(FormGroup *parent, const rect_t &rect, uint8_t index) :
CommonInputOrMixButton(parent, rect, index)
{
const MixData &mix = g_model.mixData[index];
if (mix.swtch || mix.curve.value != 0 || mix.flightModes) {
setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP);
}
}
bool isActive() const override { return isMixActive(index); }
void paintBody(BitmapBuffer *dc) override
{
const MixData &line = g_model.mixData[index];
LcdFlags textColor = COLOR_THEME_SECONDARY1;
// first line ...
drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight,
MIX_WEIGHT_MIN, MIX_WEIGHT_MAX, 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) {
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);
}
// Put this icon on the first line, since we have more space there
uint8_t delayslow = 0;
if (line.speedDown || line.speedUp) delayslow = 1;
if (line.delayUp || line.delayDown) delayslow += 2;
if (delayslow) {
BitmapBuffer *delayslowbmp[] = {mixerSetupSlowIcon, mixerSetupDelayIcon, mixerSetupDelaySlowIcon};
dc->drawMask(width() - 16, FIELD_PADDING_TOP, delayslowbmp[delayslow - 1], textColor);
}
}
};
void insertMix(uint8_t idx, uint8_t channel)
{
pauseMixerCalculations();
@@ -370,198 +93,6 @@ void insertMix(uint8_t idx, uint8_t channel)
storageDirty(EE_MODEL);
}
ModelMixesPage::ModelMixesPage() :
PageTab(STR_MIXES, ICON_MODEL_MIXER)
{
setOnSetVisibleHandler([]() {
// reset clipboard
s_copyMode = 0;
});
}
void ModelMixesPage::rebuild(FormWindow * window, int8_t focusMixIndex)
{
auto scroll_y = lv_obj_get_scroll_y(window->getLvObj());
window->clear();
build(window, focusMixIndex);
lv_obj_scroll_to_y(window->getLvObj(), scroll_y, LV_ANIM_OFF);
}
void ModelMixesPage::editMix(FormWindow * window, uint8_t channel, uint8_t mixIndex)
{
// Window::clearFocus();
Window *editWindow = new MixEditWindow(channel, mixIndex);
editWindow->setCloseHandler([=]() {
int8_t newIndex = mixIndex;
if (is_memclear(&g_model.mixData[mixIndex], sizeof(MixData))) {
deleteMix(mixIndex);
newIndex--;
}
rebuild(window, newIndex);
});
}
class MixLineTitle : public StaticText
{
public:
using StaticText::StaticText;
void setTextFlags(LcdFlags flags)
{
textFlags = flags;
for (auto child : children) {
auto bitmap = dynamic_cast<StaticBitmap*>(child);
if (bitmap) bitmap->setMaskColor(flags & 0xFFFF0000);
}
}
};
void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex)
{
FormGridLayout grid;
grid.spacer(PAGE_PADDING);
grid.setLabelWidth(66);
const BitmapBuffer * const mixerMultiplexBitmap[] = {
mixerSetupAddBitmap,
mixerSetupMultiBitmap,
mixerSetupReplaceBitmap
};
int mixIndex = 0;
MixData * mix = g_model.mixData;
for (uint8_t ch = 0; ch < MAX_OUTPUT_CHANNELS; ch++) {
bool skip_mix = (ch == 0 && is_memclear(mix, sizeof(MixData)));
if (mixIndex < MAX_MIXERS && mix->destCh == ch && !skip_mix) {
coord_t h = grid.getWindowHeight();
auto txt = new MixLineTitle(window, grid.getLabelSlot(),
getSourceString(MIXSRC_CH1 + ch),
BUTTON_BACKGROUND, COLOR_THEME_PRIMARY1 | CENTERED);
uint8_t count = 0;
while (mixIndex < MAX_MIXERS && mix->destCh == ch && !skip_mix) {
Button * button = new MixLineButton(window, grid.getFieldSlot(), mixIndex);
button->setPressHandler([=]() -> uint8_t {
button->bringToTop();
Menu * menu = new Menu(window);
menu->addLine(STR_EDIT, [=]() {
editMix(window, ch, mixIndex);
});
if (!reachMixesLimit()) {
menu->addLine(STR_INSERT_BEFORE, [=]() {
insertMix(mixIndex, ch);
editMix(window, ch, mixIndex);
});
menu->addLine(STR_INSERT_AFTER, [=]() {
insertMix(mixIndex + 1, ch);
editMix(window, ch, mixIndex + 1);
});
menu->addLine(STR_COPY, [=]() {
s_copyMode = COPY_MODE;
s_copySrcIdx =mixIndex;
});
if (s_copyMode != 0) {
menu->addLine(STR_PASTE_BEFORE, [=]() {
copyMix(s_copySrcIdx, mixIndex, PASTE_BEFORE);
if(s_copyMode == MOVE_MODE) {
deleteMix((s_copySrcIdx > mixIndex) ? s_copySrcIdx+1 : s_copySrcIdx);
s_copyMode = 0;
}
rebuild(window, mixIndex);
});
menu->addLine(STR_PASTE_AFTER, [=]() {
copyMix(s_copySrcIdx, mixIndex, PASTE_AFTER);
if(s_copyMode == MOVE_MODE) {
deleteMix((s_copySrcIdx > mixIndex) ? s_copySrcIdx+1 : s_copySrcIdx);
s_copyMode = 0;
}
rebuild(window, mixIndex+1);
});
}
}
menu->addLine(STR_MOVE, [=]() {
s_copyMode = MOVE_MODE;
s_copySrcIdx = mixIndex;
});
menu->addLine(STR_DELETE, [=]() {
deleteMix(mixIndex);
rebuild(window, -1);
});
return 0;
});
if (count++ > 0) {
new StaticBitmap(
txt,
{35 - txt->left(),
button->top() - txt->top() + (button->height() - 18) / 2, 25,
17},
mixerMultiplexBitmap[mix->mltpx], COLOR_THEME_SECONDARY1);
}
button->setFocusHandler([=](bool focus) {
if (focus) {
txt->setBackgroundColor(COLOR_THEME_FOCUS);
txt->setTextFlags(COLOR_THEME_PRIMARY2 | CENTERED);
} else {
txt->setBackgroundColor(COLOR_THEME_SECONDARY2);
txt->setTextFlags(COLOR_THEME_PRIMARY1 | CENTERED);
}
txt->invalidate();
if (focus) button->bringToTop();
});
grid.spacer(button->height() - 1);
++mixIndex;
++mix;
skip_mix = (ch == 0 && is_memclear(mix, sizeof(MixData)));
}
h = grid.getWindowHeight() - h + 1;
txt->setHeight(h);
grid.spacer(7);
}
else {
auto button = new TextButton(window, grid.getLabelSlot(),
getSourceString(MIXSRC_CH1 + ch));
button->setPressHandler([=]() -> uint8_t {
button->bringToTop();
Menu *menu = new Menu(window);
menu->addLine(STR_EDIT, [=]() {
insertMix(mixIndex, ch);
editMix(window, ch, mixIndex);
return 0;
});
if (!reachMixesLimit()) {
if (s_copyMode != 0) {
menu->addLine(STR_PASTE, [=]() {
copyMix(s_copySrcIdx, mixIndex, ch);
if (s_copyMode == MOVE_MODE) {
deleteMix((s_copySrcIdx >= mixIndex) ? s_copySrcIdx + 1
: s_copySrcIdx);
s_copyMode = 0;
}
rebuild(window, -1);
return 0;
});
}
}
return 0;
});
grid.spacer(button->height() + 5);
}
}
grid.nextLine();
}
void deleteMix(uint8_t idx)
{
pauseMixerCalculations();
@@ -589,20 +120,9 @@ void copyMix(uint8_t source, uint8_t dest, int8_t ch)
memcpy(&sourceMix, mixAddress(source), sizeof(MixData));
MixData *mix = mixAddress(dest);
size_t trailingMixes = MAX_MIXERS - (dest + 1);
if (ch == PASTE_AFTER) {
trailingMixes--;
memmove(mix + 2, mix + 1, trailingMixes * sizeof(MixData));
memcpy(mix + 1, &sourceMix, sizeof(MixData));
(mix + 1)->destCh = (mix)->destCh;
} else if (ch == PASTE_BEFORE) {
memmove(mix + 1, mix, trailingMixes * sizeof(MixData));
memcpy(mix, &sourceMix, sizeof(MixData));
mix->destCh = (mix + 1)->destCh;
} else {
memmove(mix + 1, mix, trailingMixes * sizeof(MixData));
memcpy(mix, &sourceMix, sizeof(MixData));
mix->destCh = ch;
}
memmove(mix + 1, mix, trailingMixes * sizeof(MixData));
memcpy(mix, &sourceMix, sizeof(MixData));
mix->destCh = ch;
resumeMixerCalculations();
storageDirty(EE_MODEL);
}
@@ -649,3 +169,420 @@ bool swapMixes(uint8_t &idx, uint8_t up)
idx = tgt_idx;
return true;
}
class MixLineButton : public InputMixButton
{
public:
MixLineButton(Window* parent, uint8_t index);
void paint(BitmapBuffer* dc) override;
protected:
size_t getLines() const override;
bool isActive() const override { return isMixActive(index); }
};
static void mix_draw_mplex(lv_event_t* e)
{
auto obj = (lv_obj_t*)lv_event_get_user_data(e);
if (!obj || (lv_obj_get_index(obj) == 0)) return;
auto btn = (MixLineButton*)lv_obj_get_user_data(obj);
if (!btn) return;
lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e);
if (dsc->part != LV_PART_MAIN) return;
MixData* mix = mixAddress(btn->getIndex());
const uint8_t* mask_map = nullptr;
if (mix->mltpx == MLTPX_ADD) {
mask_map = mask_mplex_add;
} else if (mix->mltpx == MLTPX_MUL) {
mask_map = mask_mplex_multi;
} else if (mix->mltpx == MLTPX_REPL) {
mask_map = mask_mplex_replace;
} else {
return;
}
lv_area_t coords;
lv_coord_t area_h = lv_area_get_height(&obj->coords);
lv_coord_t mask_w = MASK_WIDTH(mask_map);
lv_coord_t mask_h = MASK_HEIGHT(mask_map);
lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
coords.x1 = obj->coords.x1 - mask_w - pad_left;
coords.x2 = coords.x1 + mask_w - 1;
coords.y1 = obj->coords.y1 + (area_h - mask_h) / 2;
coords.y2 = coords.y1 + mask_h - 1;
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.bg_opa = LV_OPA_COVER;
rect_dsc.bg_color = makeLvColor(COLOR_THEME_SECONDARY1);
lv_draw_mask_map_param_t m;
int16_t mask_id;
lv_draw_mask_map_init(&m, &coords, MASK_DATA(mask_map));
mask_id = lv_draw_mask_add(&m, NULL);
// draw masked symbol
lv_draw_rect(dsc->draw_ctx, &rect_dsc, &coords);
// free ressources
lv_draw_mask_free_param(&m);
lv_draw_mask_remove_id(mask_id);
}
MixLineButton::MixLineButton(Window* parent, uint8_t index) :
InputMixButton(parent, index)
{
lv_obj_t* p_obj = parent->getLvObj();
lv_obj_add_event_cb(p_obj, mix_draw_mplex, LV_EVENT_DRAW_PART_END, lvobj);
}
size_t MixLineButton::getLines() const
{
const MixData* mix = mixAddress(index);
if (mix->swtch || mix->curve.value != 0 || mix->flightModes) {
return 2;
} else {
return 1;
}
}
void MixLineButton::paint(BitmapBuffer* dc)
{
const MixData& line = g_model.mixData[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, MIX_WEIGHT_MIN, MIX_WEIGHT_MAX,
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) {
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);
}
// Put this icon on the first line, since we have more space there
uint8_t delayslow = 0;
if (line.speedDown || line.speedUp) delayslow = 1;
if (line.delayUp || line.delayDown) delayslow += 2;
if (delayslow) {
BitmapBuffer* delayslowbmp[] = {mixerSetupSlowIcon, mixerSetupDelayIcon,
mixerSetupDelaySlowIcon};
dc->drawMask(width() - 16, y, delayslowbmp[delayslow - 1], textColor);
}
}
ModelMixesPage::ModelMixesPage() :
ModelInputsPage()
{
setTitle(STR_MIXES);
setIcon(ICON_MODEL_MIXER);
}
InputMixGroup* ModelMixesPage::getGroupByIndex(uint8_t index)
{
MixData* mix = mixAddress(index);
if (is_memclear(mix, sizeof(MixData))) return nullptr;
int ch = mix->destCh;
return getGroupBySrc(MIXSRC_CH1 + ch);
}
InputMixButton* ModelMixesPage::createLineButton(InputMixGroup *group, uint8_t index)
{
auto button = new MixLineButton(group, index);
lines.emplace_back(button);
group->addLine(button);
uint8_t ch = group->getMixSrc() - MIXSRC_CH1;
button->setPressHandler([=]() -> uint8_t {
Menu *menu = new Menu(form);
menu->addLine(STR_EDIT, [=]() { editMix(ch, index); });
if (!reachMixesLimit()) {
menu->addLine(STR_INSERT_BEFORE, [=]() {
uint8_t idx = button->getIndex();
insertMix(ch, idx);
_copyMode = 0;
});
menu->addLine(STR_INSERT_AFTER, [=]() {
uint8_t idx = button->getIndex();
insertMix(ch, idx + 1);
_copyMode = 0;
});
menu->addLine(STR_COPY, [=]() {
_copyMode = COPY_MODE;
_copySrc = button;
});
if (s_copyMode != 0) {
menu->addLine(STR_PASTE_BEFORE, [=]() {
uint8_t idx = button->getIndex();
pasteMixBefore(idx);
_copyMode = 0;
});
menu->addLine(STR_PASTE_AFTER, [=]() {
uint8_t idx = button->getIndex();
pasteMixAfter(idx);
_copyMode = 0;
});
}
}
menu->addLine(STR_MOVE, [=]() {
_copyMode = MOVE_MODE;
_copySrc = button;
});
menu->addLine(STR_DELETE, [=]() {
uint8_t idx = button->getIndex();
deleteMix(idx);
_copyMode = 0;
});
return 0;
});
return button;
}
void ModelMixesPage::addLineButton(mixsrc_t src, uint8_t index)
{
ModelInputsPage::addLineButton(src, index);
}
void ModelMixesPage::addLineButton(uint8_t index)
{
MixData* mix = mixAddress(index);
if (is_memclear(mix, sizeof(MixData))) return;
int channel = mix->destCh;
addLineButton(MIXSRC_CH1 + channel, index);
}
void ModelMixesPage::newMix()
{
Menu* menu = new Menu(Layer::back());
menu->setTitle(STR_MENU_CHANNELS);
uint8_t index = 0;
MixData* line = mixAddress(0);
// search for unused channels
for (uint8_t ch = 0; ch < MAX_OUTPUT_CHANNELS; ch++) {
if (index >= MAX_MIXERS) break;
bool skip_mix = (ch == 0 && is_memclear(line, sizeof(MixData)));
if (line->destCh == ch && !skip_mix) {
while (index < MAX_MIXERS && (line->destCh == ch) && !skip_mix) {
++index;
++line;
skip_mix = (ch == 0 && is_memclear(line, sizeof(MixData)));
}
} else {
std::string ch_name(getSourceString(MIXSRC_CH1 + ch));
menu->addLine(ch_name.c_str(), [=]() { insertMix(ch, index); });
}
}
}
void ModelMixesPage::editMix(uint8_t channel, uint8_t index)
{
auto line = getLineByIndex(index);
if (!line) return;
auto line_obj = line->getLvObj();
auto edit = new MixEditWindow(channel, index);
edit->setCloseHandler([=]() {
MixData* mix = mixAddress(index);
if (is_memclear(mix, sizeof(MixData))) {
deleteMix(index);
} else {
lv_event_send(line_obj, LV_EVENT_VALUE_CHANGED, nullptr);
}
});
}
void ModelMixesPage::insertMix(uint8_t channel, uint8_t index)
{
::insertMix(index, channel);
addLineButton(MIXSRC_CH1 + channel, index);
editMix(channel, index);
}
void ModelMixesPage::deleteMix(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);
}
::deleteMix(index);
}
void ModelMixesPage::pasteMix(uint8_t dst_idx, uint8_t channel)
{
if (!_copyMode || !_copySrc) return;
uint8_t src_idx = _copySrc->getIndex();
::copyMix(src_idx, dst_idx, channel);
addLineButton(dst_idx);
if (_copyMode == MOVE_MODE) {
deleteInput(src_idx);
}
}
static int _mixChnFromIndex(uint8_t index)
{
MixData* mix = mixAddress(index);
if (is_memclear(mix, sizeof(MixData))) return -1;
return mix->destCh;
}
void ModelMixesPage::pasteMixBefore(uint8_t dst_idx)
{
int channel = _mixChnFromIndex(dst_idx);
pasteInput(dst_idx, channel);
}
void ModelMixesPage::pasteMixAfter(uint8_t dst_idx)
{
int channel = _mixChnFromIndex(dst_idx);
pasteInput(dst_idx + 1, channel);
}
void ModelMixesPage::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, [=]() {
newMix();
return 0;
});
auto btn_obj = btn->getLvObj();
lv_obj_set_width(btn_obj, lv_pct(100));
groups.clear();
lines.clear();
uint8_t index = 0;
MixData* line = g_model.mixData;
for (uint8_t ch = 0; ch < MAX_OUTPUT_CHANNELS; ch++) {
if (index >= MAX_MIXERS) break;
bool skip_mix = (ch == 0 && is_memclear(line, sizeof(MixData)));
if (line->destCh == ch && !skip_mix) {
// one group for the complete mixer channel
auto group = new InputMixGroup(form, MIXSRC_CH1 + ch);
groups.emplace_back(group);
while (index < MAX_MIXERS && (line->destCh == ch) && !skip_mix) {
// one button per input line
createLineButton(group, index);
++index;
++line;
skip_mix = (ch == 0 && is_memclear(line, sizeof(MixData)));
}
}
}
// int mixIndex = 0;
// MixData * mix = g_model.mixData;
// for (uint8_t ch = 0; ch < MAX_OUTPUT_CHANNELS; ch++) {
// bool skip_mix = (ch == 0 && is_memclear(mix, sizeof(MixData)));
// if (mixIndex < MAX_MIXERS && mix->destCh == ch && !skip_mix) {
// coord_t h = grid.getWindowHeight();
// auto txt = new MixLineTitle(window, grid.getLabelSlot(),
// getSourceString(MIXSRC_CH1 + ch),
// BUTTON_BACKGROUND, COLOR_THEME_PRIMARY1 | CENTERED);
// uint8_t count = 0;
// while (mixIndex < MAX_MIXERS && mix->destCh == ch && !skip_mix) {
// TODO:
// if (count++ > 0) {
// new StaticBitmap(
// txt,
// {35 - txt->left(),
// button->top() - txt->top() + (button->height() - 18) / 2, 25,
// 17},
// mixerMultiplexBitmap[mix->mltpx], COLOR_THEME_SECONDARY1);
// }
// button->setFocusHandler([=](bool focus) {
// if (focus) {
// txt->setBackgroundColor(COLOR_THEME_FOCUS);
// txt->setTextFlags(COLOR_THEME_PRIMARY2 | CENTERED);
// } else {
// txt->setBackgroundColor(COLOR_THEME_SECONDARY2);
// txt->setTextFlags(COLOR_THEME_PRIMARY1 | CENTERED);
// }
// txt->invalidate();
// if (focus) button->bringToTop();
// });
// grid.spacer(button->height() - 1);
// ++mixIndex;
// ++mix;
// skip_mix = (ch == 0 && is_memclear(mix, sizeof(MixData)));
// }
// h = grid.getWindowHeight() - h + 1;
// txt->setHeight(h);
// grid.spacer(7);
// }
// }
// grid.nextLine();
}
+24 -17
View File
@@ -19,25 +19,32 @@
* GNU General Public License for more details.
*/
#ifndef _MODEL_MIXES_H_
#define _MODEL_MIXES_H_
#pragma once
#include "tabsgroup.h"
#include "model_inputs.h"
class ModelMixesPage: public PageTab {
public:
ModelMixesPage();
void build(FormWindow * window) override
{
build(window, 0);
}
class ModelMixesPage : public ModelInputsPage
{
public:
ModelMixesPage();
protected:
void build(FormWindow * window, int8_t focusMixIndex);
void rebuild(FormWindow * window, int8_t focusMixIndex);
void editMix(FormWindow * window, uint8_t channel, uint8_t mixIndex);
uint8_t s_copySrcIdx;
void build(FormWindow* window) override;
protected:
InputMixGroup* getGroupByIndex(uint8_t index) override;
void addLineButton(uint8_t index) override;
void addLineButton(mixsrc_t src, uint8_t index) override;
InputMixButton* createLineButton(InputMixGroup* group,
uint8_t index) override;
void newMix();
void editMix(uint8_t input, uint8_t index);
void insertMix(uint8_t input, uint8_t index);
void deleteMix(uint8_t index);
void pasteMix(uint8_t dst_idx, uint8_t channel);
void pasteMixBefore(uint8_t dst_idx);
void pasteMixAfter(uint8_t dst_idx);
};
#endif // _MODEL_MIXES_H_
+6 -1
View File
@@ -662,7 +662,12 @@ char *getSourceString(char (&dest)[L], mixsrc_t idx)
strAppendStringWithIndex(dest, STR_PPM_TRAINER,
idx - MIXSRC_FIRST_TRAINER + 1);
} else if (idx <= MIXSRC_LAST_CH) {
strAppendStringWithIndex(dest, STR_CH, idx - MIXSRC_CH1 + 1);
auto ch = idx - MIXSRC_CH1;
if (g_model.limitData[ch].name[0] != '\0') {
copyToTerminated(dest, g_model.limitData[ch].name);
} else {
strAppendStringWithIndex(dest, STR_CH, ch + 1);
}
} else if (idx <= MIXSRC_LAST_GVAR) {
strAppendStringWithIndex(dest, STR_GV, idx - MIXSRC_GVAR1 + 1);
} else if (idx < MIXSRC_FIRST_TIMER) {