diff --git a/radio/src/gui/colorlcd/curve.cpp b/radio/src/gui/colorlcd/curve.cpp index 5aa6b64c5..e4de41e40 100644 --- a/radio/src/gui/colorlcd/curve.cpp +++ b/radio/src/gui/colorlcd/curve.cpp @@ -42,53 +42,60 @@ IMPL_LZ4_BITMAP(LBM_CURVE_POINT); IMPL_LZ4_BITMAP(LBM_CURVE_POINT_CENTER); IMPL_LZ4_BITMAP(LBM_CURVE_COORD_SHADOW); +Curve::Curve(Window * parent, const rect_t & rect, std::function function, std::function position): + Window(parent, rect, OPAQUE), + function(std::move(function)), + position(std::move(position)) +{ +} + void Curve::drawBackground(BitmapBuffer * dc) { dc->clear(COLOR_THEME_PRIMARY2); // Axis - dc->drawSolidHorizontalLine(0, height()/2, width(), COLOR_THEME_SECONDARY2); - dc->drawSolidVerticalLine(width()/2, 0, height(), COLOR_THEME_SECONDARY2); + dc->drawSolidHorizontalLine(dx, dy+dh/2, dw, COLOR_THEME_SECONDARY2); + dc->drawSolidVerticalLine(dx+dw/2, dy, dh, COLOR_THEME_SECONDARY2); // Extra lines - dc->drawVerticalLine(width()/4, 0, height(), STASHED, COLOR_THEME_SECONDARY2); - dc->drawVerticalLine(width()*3/4, 0, height(), STASHED, COLOR_THEME_SECONDARY2); - dc->drawHorizontalLine(0, height()/4, width(), STASHED, COLOR_THEME_SECONDARY2); - dc->drawHorizontalLine(0, height()*3/4, width(), STASHED, COLOR_THEME_SECONDARY2); + dc->drawVerticalLine(dx+dw/4, dy, dh, STASHED, COLOR_THEME_SECONDARY2); + dc->drawVerticalLine(dx+dw*3/4, dy, dh, STASHED, COLOR_THEME_SECONDARY2); + dc->drawHorizontalLine(dx, dy+dh/4, dw, STASHED, COLOR_THEME_SECONDARY2); + dc->drawHorizontalLine(dx, dy+dh*3/4, dw, STASHED, COLOR_THEME_SECONDARY2); // Outside border - dc->drawSolidRect(0, 0, width(), height(), 1, COLOR_THEME_SECONDARY2); + dc->drawSolidRect(dx, dy, dw, dh, 1, COLOR_THEME_SECONDARY2); } coord_t Curve::getPointX(int x) const { - return limit(0, - width() / 2 + divRoundClosest(x * width() / 2, RESX), - width() - 1); + return dx + limit(0, + dw / 2 + divRoundClosest(x * dw / 2, RESX), + dw - 1); } coord_t Curve::getPointY(int y) const { - return limit(0, - height() / 2 - divRoundClosest(y * height() / 2, RESX), - height() - 1); + return dy + limit(0, + dh / 2 - divRoundClosest(y * dh / 2, RESX), + dh - 1); } void Curve::drawCurve(BitmapBuffer * dc) { auto prev = (coord_t) -1; - for (int x = 0; x < width(); x++) { - coord_t y = getPointY(function(divRoundClosest((x - width() / 2) * RESX, width() / 2))); + for (int x = 0; x < dw; x++) { + coord_t y = getPointY(function(divRoundClosest((x - dw / 2) * RESX, dw / 2))); if (prev >= 0) { if (prev < y) { for (int tmp = prev; tmp <= y; tmp++) { - dc->drawBitmapPattern(x - 2, tmp - 2, LBM_POINT, COLOR_THEME_SECONDARY1); + dc->drawBitmapPattern(dx + x - 2, tmp - 2, LBM_POINT, COLOR_THEME_SECONDARY1); } } else { for (int tmp = y; tmp <= prev; tmp++) { - dc->drawBitmapPattern(x - 2, tmp - 2, LBM_POINT, COLOR_THEME_SECONDARY1); + dc->drawBitmapPattern(dx + x - 2, tmp - 2, LBM_POINT, COLOR_THEME_SECONDARY1); } } } @@ -105,8 +112,8 @@ void Curve::drawPosition(BitmapBuffer * dc) coord_t y = getPointY(valueY); // the 2 lines - dc->drawSolidHorizontalLine(0, y, width(), COLOR_THEME_ACTIVE); - dc->drawSolidVerticalLine(x, 0, height(), COLOR_THEME_ACTIVE); + dc->drawSolidHorizontalLine(dx, y, dw, COLOR_THEME_ACTIVE); + dc->drawSolidVerticalLine(x, dy, dh, COLOR_THEME_ACTIVE); // the point (white inside) dc->drawBitmapPattern(x-4, y-4, LBM_CURVE_POINT, COLOR_THEME_ACTIVE); @@ -114,8 +121,8 @@ void Curve::drawPosition(BitmapBuffer * dc) char coords[16]; strAppendSigned(strAppend(strAppendSigned(coords, calcRESXto100(valueX)), ","), calcRESXto100(valueY)); - dc->drawSolidFilledRect(10, 11, 1 + getTextWidth(coords, 0, FONT(XS)), 17, COLOR_THEME_ACTIVE); - dc->drawText(11, 10, coords, FONT(XS) | COLOR_THEME_PRIMARY2); + dc->drawSolidFilledRect(10, 11, 2 + getTextWidth(coords, 0, FONT(XS)), 17, COLOR_THEME_ACTIVE); + dc->drawText(11, 10, coords, FONT(XS) | COLOR_THEME_PRIMARY1); } void Curve::drawPoint(BitmapBuffer * dc, const CurvePoint & point) @@ -129,6 +136,16 @@ void Curve::drawPoint(BitmapBuffer * dc, const CurvePoint & point) void Curve::paint(BitmapBuffer * dc) { + if (points.size() > 0) { + dx = 4; + dy = 4; + } else { + dx = 2; + dy = 2; + } + dw = width() - dx * 2; + dh = height() - dy * 2; + drawBackground(dc); drawCurve(dc); for (auto point: points) { @@ -150,3 +167,16 @@ void Curve::clearPoints() points.clear(); invalidate(); } + +void Curve::checkEvents() +{ + if (position) { + int pos = position(); + if (pos != lastPos) { + lastPos = pos; + invalidate(); + } + } + + Window::checkEvents(); +} diff --git a/radio/src/gui/colorlcd/curve.h b/radio/src/gui/colorlcd/curve.h index e63588e76..2c10bc4be 100644 --- a/radio/src/gui/colorlcd/curve.h +++ b/radio/src/gui/colorlcd/curve.h @@ -33,12 +33,7 @@ class Curve: public Window friend class CurveEdit; public: - Curve(Window * parent, const rect_t & rect, std::function function, std::function position=nullptr): - Window(parent, rect, OPAQUE), - function(std::move(function)), - position(std::move(position)) - { - } + Curve(Window * parent, const rect_t & rect, std::function function, std::function position=nullptr); #if defined(DEBUG_WINDOWS) std::string getName() const override @@ -47,19 +42,7 @@ class Curve: public Window } #endif - void checkEvents() override - { - // will always force a full window refresh - if (position) { - // int pos = position(); - // if (pos != lastPos) { - // lastPos = pos; - invalidate(); - // } - } - - Window::checkEvents(); - } + void checkEvents() override; void addPoint(const point_t & point, LcdFlags flags); @@ -68,7 +51,9 @@ class Curve: public Window void paint(BitmapBuffer * dc) override; protected: - // int lastPos = 0; + // Drawing rectangle position & size + uint8_t dx, dy, dw, dh; + int lastPos = 0; std::function function; std::function position; std::list points; diff --git a/radio/src/gui/colorlcd/curve_param.cpp b/radio/src/gui/colorlcd/curve_param.cpp index 023f3f10b..8af6a5f4f 100644 --- a/radio/src/gui/colorlcd/curve_param.cpp +++ b/radio/src/gui/colorlcd/curve_param.cpp @@ -46,8 +46,9 @@ void CurveParam::value_changed(lv_event_t* e) param->update(); } -CurveParam::CurveParam(Window* parent, const rect_t& rect, CurveRef* ref) : - Window(parent, rect), ref(ref) +CurveParam::CurveParam(Window* parent, const rect_t& rect, CurveRef* ref, + std::function setRefValue) : + Window(parent, rect), ref(ref), setRefValue(setRefValue) { lv_obj_set_flex_flow(lvobj, LV_FLEX_FLOW_ROW_WRAP); lv_obj_set_style_pad_column(lvobj, lv_dpx(4), 0); @@ -66,17 +67,16 @@ CurveParam::CurveParam(Window* parent, const rect_t& rect, CurveRef* ref) : // CURVE_REF_DIFF // CURVE_REF_EXPO - value_edit = new GVarNumberEdit(this, rect_t{}, -100, 100, - GET_SET_DEFAULT(ref->value)); + value_edit = new GVarNumberEdit(this, rect_t{}, -100, 100, GET_DEFAULT(ref->value), setRefValue); value_edit->setSuffix("%"); // CURVE_REF_FUNC func_choice = new Choice(this, rect_t{}, STR_VCURVEFUNC, 0, CURVE_BASE - 1, - GET_SET_DEFAULT(ref->value)); + GET_DEFAULT(ref->value), setRefValue); // CURVE_REF_CUSTOM cust_choice = new ChoiceEx(this, rect_t{}, -MAX_CURVES, MAX_CURVES, - GET_SET_DEFAULT(ref->value)); + GET_DEFAULT(ref->value), setRefValue); cust_choice->setTextHandler([](int value) { return getCurveString(value); }); cust_choice->set_lv_LongPressHandler(LongPressHandler, &(ref->value)); diff --git a/radio/src/gui/colorlcd/curve_param.h b/radio/src/gui/colorlcd/curve_param.h index e58b288f8..e94344b7e 100644 --- a/radio/src/gui/colorlcd/curve_param.h +++ b/radio/src/gui/colorlcd/curve_param.h @@ -33,6 +33,7 @@ class CurveParam : public Window { // Curve CurveRef* ref; + std::function setRefValue; // Controls GVarNumberEdit* value_edit; @@ -46,5 +47,6 @@ class CurveParam : public Window static void LongPressHandler(void* data); public: - CurveParam(Window* parent, const rect_t& rect, CurveRef* ref); + CurveParam(Window* parent, const rect_t& rect, CurveRef* ref, + std::function setRefValue); }; diff --git a/radio/src/gui/colorlcd/curveedit.cpp b/radio/src/gui/colorlcd/curveedit.cpp index f3412b4a0..9c4be3db4 100644 --- a/radio/src/gui/colorlcd/curveedit.cpp +++ b/radio/src/gui/colorlcd/curveedit.cpp @@ -26,100 +26,150 @@ #define SET_DIRTY() storageDirty(EE_MODEL) -CurveDataEdit::CurveDataEdit(Window * parent, const rect_t & rect, uint8_t index, CurveEdit * curveEdit) : - FormGroup(parent, rect, FORM_FORWARD_FOCUS), - index(index), - curveEdit(curveEdit) +static const lv_coord_t default_col_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; +static const lv_coord_t default_row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; + +CurveDataEdit::CurveDataEdit(Window * parent, const rect_t & rect, uint8_t index) : + Window(parent, rect, FORM_FORWARD_FOCUS), + index(index) { - update(); +} + +#if LCD_W > LCD_H + #define NUM_BTN_WIDTH 44 +#else + #define NUM_BTN_WIDTH 48 +#endif + +void CurveDataEdit::curvePointsRow(FormGroup::Line* parent, int start, int count, int curvePointsCount, bool isCustom) +{ + static const lv_coord_t points_col_dsc[] = {11, LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST}; + + auto form = new FormWindow(parent, rect_t{}); + form->padAll(0); + form->setFlexLayout(); + + FlexGridLayout grid(points_col_dsc, default_row_dsc, 4); + + auto line = form->newLine(&grid); + line->padAll(0); + line->padLeft(4); + line->padRight(4); + lv_obj_set_grid_align(line->getLvObj(), LV_GRID_ALIGN_SPACE_BETWEEN, LV_GRID_ALIGN_SPACE_BETWEEN); + + new StaticText(line, rect_t{}, "", CENTERED|COLOR_THEME_PRIMARY1); + + // Point number + for (int i = 0; i < count; i++) { + auto txt = new StaticText(line, rect_t{}, std::to_string(i + start + 1), 0, FONT(XS) | CENTERED | COLOR_THEME_PRIMARY1); + } + + line = form->newLine(&grid); + line->padAll(2); + line->padLeft(4); + line->padRight(4); + lv_obj_set_grid_align(line->getLvObj(), LV_GRID_ALIGN_SPACE_BETWEEN, LV_GRID_ALIGN_SPACE_BETWEEN); + + new StaticText(line, rect_t{}, "X", 0, CENTERED|COLOR_THEME_PRIMARY1); + + int8_t* points = curveAddress(index); + + // x value + if (isCustom) { + uint8_t i = 0; + uint8_t c = count; + if (start == 0) { + new StaticText(line, rect_t{}, "-100", 0, CENTERED | COLOR_THEME_SECONDARY1); + i += 1; + } + if ((start + count) == curvePointsCount) { + c -= 1; + } + // Adjustable points for custom curves + for (; i < c; i++) { + uint8_t px = i + start - 1; + numEditX[px] = new NumberEdit( + line, rect_t{ 0, 0, NUM_BTN_WIDTH, 0 }, + (px == 0) ? -100 : points[curvePointsCount + px - 1], + (px == curvePointsCount - 3) ? 100 : points[curvePointsCount + px + 1], + GET_VALUE(points[curvePointsCount + px]), + [=](int32_t newValue) { + points[curvePointsCount + px] = newValue; + if (px > 0) { + numEditX[px-1]->setMax(newValue); + } + if (px < curvePointsCount - 3) { + numEditX[px+1]->setMin(newValue); + } + SET_DIRTY(); + curveEdit->updatePreview(); + }, + 0, RIGHT); + lv_obj_set_grid_cell(numEditX[px]->getLvObj(), LV_GRID_ALIGN_CENTER, i+1, 1, LV_GRID_ALIGN_CENTER, 0, 1); + } + if ((start + count) == curvePointsCount) { + new StaticText(line, rect_t{}, "100", 0, CENTERED | COLOR_THEME_SECONDARY1); + } + } else { + for (uint8_t i = 0; i < count; i++) { + new StaticText(line, rect_t{}, + std::to_string(-100 + (200 * (i + start)) / (curvePointsCount - 1)), + 0, CENTERED | COLOR_THEME_SECONDARY1); + } + } + + line = form->newLine(&grid); + line->padAll(2); + line->padLeft(4); + line->padRight(4); + lv_obj_set_grid_align(line->getLvObj(), LV_GRID_ALIGN_SPACE_BETWEEN, LV_GRID_ALIGN_SPACE_BETWEEN); + + new StaticText(line, rect_t{}, "Y", 0, CENTERED|COLOR_THEME_PRIMARY1); + + // y value + for (uint8_t i = 0; i < count; i++) { + auto numedit = new NumberEdit( + line, rect_t{ 0, 0, NUM_BTN_WIDTH, 0 }, + -100, 100, GET_VALUE(points[i+start]), + [=](int32_t newValue) { + points[i+start] = newValue; + SET_DIRTY(); + curveEdit->updatePreview(); + }, + 0, RIGHT); + lv_obj_set_grid_cell(numedit->getLvObj(), LV_GRID_ALIGN_CENTER, i+1, 1, LV_GRID_ALIGN_CENTER, 0, 1); + } } void CurveDataEdit::update() { clear(); - FormGridLayout grid; - grid.setLabelWidth(0); - grid.setMarginRight(parent->width() - rect.w + 5); + memset(numEditX, 0, sizeof(numEditX)); - coord_t boxWidth = rect.w / 5; - coord_t boxHeight = PAGE_LINE_HEIGHT; + auto form = new FormWindow(this, rect_t{}); + form->padAll(0); + form->padBottom(4); + form->setFlexLayout(); + + FlexGridLayout grid(default_col_dsc, default_row_dsc, 4); CurveHeader & curve = g_model.curves[index]; uint8_t curvePointsCount = 5 + curve.points; - // Point number - for (int i = 0; i < curvePointsCount; i++) { - auto txt = new StaticText( - this, - {i * boxWidth + PAGE_PADDING, 10, boxWidth - PAGE_PADDING, boxHeight}, - std::to_string(i + 1) + " ", 0, RIGHT | COLOR_THEME_SECONDARY1); - txt->setBackgroundColor(COLOR_THEME_SECONDARY2); + for (int i = 0; i < curvePointsCount; i += 5) { + int count = 5; + if (i + count > curvePointsCount) count = curvePointsCount - i; + auto line = form->newLine(&grid); + line->padAll(0); + lv_obj_set_grid_align(line->getLvObj(), LV_GRID_ALIGN_SPACE_BETWEEN, LV_GRID_ALIGN_SPACE_BETWEEN); + curvePointsRow(line, i, count, curvePointsCount, curve.type == CURVE_TYPE_CUSTOM); } - grid.spacer(boxHeight + 10 + PAGE_PADDING); - - // x value - if (curve.type == CURVE_TYPE_CUSTOM) { - // Adjustable points for custom curves - for (int i = 0; i < curvePointsCount; i++) { - int8_t* points = curveAddress(index); - auto pointEdit = new NumberEdit( - this, - {coord_t(PAGE_LINE_SPACING + 1 + i * boxWidth), - grid.getWindowHeight(), coord_t(boxWidth - PAGE_LINE_SPACING), - boxHeight}, - i <= 1 ? -100 : points[curvePointsCount + i - 2], - i >= curvePointsCount - 2 ? 100 : points[curvePointsCount + i], - GET_VALUE(i == 0 ? -100 - : i == curvePointsCount - 1 - ? 100 - : points[curvePointsCount + i - 1]), - [=](int32_t newValue) { - if (i == 0 || i == curvePointsCount - 1) return; - points[curvePointsCount + i - 1] = newValue; - SET_DIRTY(); - curveEdit->updatePreview(); - }, - 0, RIGHT); - - if (i == 0 || i == curvePointsCount - 1) { - pointEdit->disable(); - } - } - } else { - for (uint8_t i = 0; i < curvePointsCount; i++) { - new StaticText(this, {i * boxWidth, grid.getWindowHeight(), boxWidth, boxHeight}, - std::to_string(-100 + 200 * i / (5 + curve.points - 1)), 0, - RIGHT | COLOR_THEME_SECONDARY1); - } - } - grid.spacer(boxHeight + PAGE_PADDING); - - // y value - for (uint8_t i = 0; i < curvePointsCount; i++) { - int8_t* points = curveAddress(index); - new NumberEdit( - this, - {coord_t(PAGE_LINE_SPACING + 1 + i * boxWidth), - grid.getWindowHeight(), - coord_t(boxWidth - PAGE_LINE_SPACING), - boxHeight}, - -100, 100, GET_VALUE(points[i]), - [=](int32_t newValue) { - points[i] = newValue; - SET_DIRTY(); - curveEdit->updatePreview(); - }, - 0, RIGHT); - } - } void CurveDataEdit::paint(BitmapBuffer* dc) { dc->clear(COLOR_THEME_SECONDARY3); - // dc->drawSolidHorizontalLine(0, rect.h / 3, getInnerWidth(), 0); - // dc->drawSolidHorizontalLine(0, 2 * rect.h / 3, getInnerWidth(), 0); } void CurveEdit::SetCurrentSource(uint32_t source) @@ -297,7 +347,6 @@ void CurveEdit::onEvent(event_t event) } } - void CurveEdit::checkEvents() { if (!lockSource) { @@ -314,3 +363,151 @@ void CurveEdit::checkEvents() } FormField::checkEvents(); } + +CurveEditWindow::CurveEditWindow(uint8_t index): + Page(ICON_MODEL_CURVES), + index(index) +{ + buildBody(&body); + buildHeader(&header); +} + +void CurveEditWindow::buildHeader(Window * window) +{ + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MENUCURVE, 0, COLOR_THEME_PRIMARY2); + char s[16]; + strAppendStringWithIndex(s, STR_CV, index + 1); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + s, 0, COLOR_THEME_PRIMARY2); +} + +void CurveEditWindow::buildBody(FormWindow * window) +{ + CurveHeader & curve = g_model.curves[index]; + int8_t * points = curveAddress(index); + + auto form = new FormWindow(window, rect_t{}); + form->padAll(0); + form->setFlexLayout(); + + FlexGridLayout grid(default_col_dsc, default_row_dsc, 0); + + auto line = form->newLine(&grid); + line->padAll(0); + lv_obj_set_grid_align(line->getLvObj(), LV_GRID_ALIGN_SPACE_BETWEEN, LV_GRID_ALIGN_SPACE_BETWEEN); + +#if LCD_H > LCD_W // portrait + lv_obj_set_flex_flow(line->getLvObj(), LV_FLEX_FLOW_COLUMN); + coord_t curveWidth = window->width() - 88; + coord_t boxWidth = window->width(); + coord_t boxHeight = window->height() - curveWidth; +#else + lv_obj_set_flex_flow(line->getLvObj(), LV_FLEX_FLOW_ROW); + coord_t curveWidth = window->height() - 2 * PAGE_PADDING; + coord_t boxWidth = window->width() - curveWidth; + coord_t boxHeight = window->height(); +#endif + + auto box = new Window(line, rect_t{}); + box->setWidth(boxWidth); + box->setHeight(boxHeight); + box->padAll(0); + + static const lv_coord_t controls_col_dsc[] = {LV_GRID_FR(5), LV_GRID_FR(8), LV_GRID_FR(5), LV_GRID_TEMPLATE_LAST}; + + form = new FormWindow(box, rect_t{}); + form->padAll(0); + form->setFlexLayout(); + + FlexGridLayout iGrid(controls_col_dsc, default_row_dsc, 0); + + auto iLine = form->newLine(&iGrid); + iLine->padAll(4); + iLine->padBottom(0); + lv_obj_set_grid_align(iLine->getLvObj(), LV_GRID_ALIGN_SPACE_BETWEEN, LV_GRID_ALIGN_SPACE_BETWEEN); + + // Name + new StaticText(iLine, rect_t{}, STR_NAME, 0, COLOR_THEME_PRIMARY1); + new ModelTextEdit(iLine, rect_t{0, 0, 100, 0}, curve.name, sizeof(curve.name)); + + // Smooth + auto smooth = new TextButton(iLine, rect_t{}, STR_SMOOTH, [=]() { + g_model.curves[index].smooth = !g_model.curves[index].smooth; + curveEdit->updatePreview(); + return g_model.curves[index].smooth; + }); + smooth->check(g_model.curves[index].smooth); + smooth->padAll(2); + smooth->setHeight(26); + + iLine = form->newLine(&iGrid); + iLine->padAll(4); + iLine->padBottom(0); + lv_obj_set_grid_align(iLine->getLvObj(), LV_GRID_ALIGN_SPACE_BETWEEN, LV_GRID_ALIGN_SPACE_BETWEEN); + + // Type + new StaticText(iLine, rect_t{}, STR_TYPE, 0, COLOR_THEME_PRIMARY1); + new Choice(iLine, rect_t{0, 0, 100, 0}, STR_CURVE_TYPES, 0, 1, GET_DEFAULT(g_model.curves[index].type), + [=](int32_t newValue) { + CurveHeader &curve = g_model.curves[index]; + if (newValue != curve.type) { + for (int i = 1; i < 4 + curve.points; i++) { + points[i] = calcRESXto100(applyCustomCurve(calc100toRESX(-100 + i * 200 / (4 + curve.points)), index)); + } + if (moveCurve(index, newValue == CURVE_TYPE_CUSTOM ? 3 + curve.points : -3 - curve.points)) { + if (newValue == CURVE_TYPE_CUSTOM) { + resetCustomCurveX(points, 5 + curve.points); + } + curve.type = newValue; + } + SET_DIRTY(); + curveEdit->updatePreview(); + if (curveDataEdit) { + curveDataEdit->update(); + } + } + }); + + // Points count + auto edit = new NumberEdit(iLine, rect_t{}, 2, 17, GET_DEFAULT(g_model.curves[index].points + 5), + [=](int32_t newValue) { + newValue -= 5; + CurveHeader &curve = g_model.curves[index]; + int newPoints[MAX_POINTS_PER_CURVE]; + newPoints[0] = points[0]; + newPoints[4 + newValue] = points[4 + curve.points]; + for (int i = 1; i < 4 + newValue; i++) + newPoints[i] = calcRESXto100(applyCustomCurve(-RESX + (i * 2 * RESX) / (4 + newValue), index)); + if (moveCurve(index, (newValue - curve.points) * (curve.type == CURVE_TYPE_CUSTOM ? 2 : 1))) { + for (int i = 0; i < 5 + newValue; i++) { + points[i] = newPoints[i]; + if (curve.type == CURVE_TYPE_CUSTOM && i != 0 && i != 4 + newValue) + points[5 + newValue + i - 1] = -100 + (i * 200) / (4 + newValue); + } + curve.points = newValue; + SET_DIRTY(); + curveEdit->updatePreview(); + if (curveDataEdit) { + curveDataEdit->update(); + } + } + }); + edit->setSuffix(STR_PTS); + + iLine = form->newLine(&iGrid); + iLine->padAll(0); + lv_obj_set_grid_align(iLine->getLvObj(), LV_GRID_ALIGN_SPACE_BETWEEN, LV_GRID_ALIGN_SPACE_BETWEEN); + + curveDataEdit = new CurveDataEdit(iLine, rect_t{ 0, 0, box->width(), box->height() - 67 }, index); + + // Curve editor + lv_obj_set_flex_align(line->getLvObj(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_AROUND); + curveEdit = new CurveEdit(line, { 0, 0, curveWidth, curveWidth }, index); + + curveDataEdit->setCurveEdit(curveEdit); +} diff --git a/radio/src/gui/colorlcd/curveedit.h b/radio/src/gui/colorlcd/curveedit.h index 5f4a632b8..774a31b77 100644 --- a/radio/src/gui/colorlcd/curveedit.h +++ b/radio/src/gui/colorlcd/curveedit.h @@ -21,31 +21,14 @@ #pragma once +#include "page.h" #include "form.h" #include "curve.h" -class CurveEdit; - -class CurveDataEdit : public FormGroup -{ - friend class CurveEdit; - - public: - CurveDataEdit(Window * parent, const rect_t & rect, uint8_t index, CurveEdit * curveEdit); - - void paint(BitmapBuffer * dc) override; - - void update(); - - protected: - uint8_t index; - CurveEdit * curveEdit; -}; +class NumberEdit; class CurveEdit: public FormField { - friend class CurveDataEdit; - public: CurveEdit(Window * parent, const rect_t & rect, uint8_t index); static void SetCurrentSource(uint32_t source); @@ -84,3 +67,40 @@ class CurveEdit: public FormField void left(); bool isCustomCurve() const; }; + +class CurveDataEdit : public Window +{ + public: + CurveDataEdit(Window * parent, const rect_t & rect, uint8_t index); + + void setCurveEdit(CurveEdit* _curveEdit) + { + curveEdit = _curveEdit; + update(); + } + + void paint(BitmapBuffer * dc) override; + + void update(); + + protected: + uint8_t index; + CurveEdit * curveEdit; + NumberEdit* numEditX[16]; + + void curvePointsRow(FormGroup::Line* parent, int start, int count, int curvePointsCount, bool isCustom); +}; + +class CurveEditWindow : public Page +{ + public: + CurveEditWindow(uint8_t index); + + protected: + uint8_t index; + CurveEdit * curveEdit = nullptr; + CurveDataEdit * curveDataEdit = nullptr; + + void buildHeader(Window * window); + void buildBody(FormWindow * window); +}; diff --git a/radio/src/gui/colorlcd/input_edit.cpp b/radio/src/gui/colorlcd/input_edit.cpp index d6bae17c7..df3421284 100644 --- a/radio/src/gui/colorlcd/input_edit.cpp +++ b/radio/src/gui/colorlcd/input_edit.cpp @@ -116,13 +116,25 @@ void InputEditWindow::buildBody(FormWindow* form) // Weight line = form->newLine(&grid); new StaticText(line, rect_t{}, STR_WEIGHT, 0, COLOR_THEME_PRIMARY1); - auto gvar = new GVarNumberEdit(line, rect_t{}, -100, 100, GET_SET_DEFAULT(input->weight)); + auto gvar = new GVarNumberEdit(line, rect_t{}, -100, 100, + GET_DEFAULT(input->weight), + [=](int32_t newValue) { + input->weight = newValue; + preview->invalidate(); + SET_DIRTY(); + }); gvar->setSuffix("%"); // Offset line = form->newLine(&grid); new StaticText(line, rect_t{}, STR_OFFSET, 0, COLOR_THEME_PRIMARY1); - gvar = new GVarNumberEdit(line, rect_t{}, -100, 100, GET_SET_DEFAULT(input->offset)); + gvar = new GVarNumberEdit(line, rect_t{}, -100, 100, + GET_DEFAULT(input->offset), + [=](int32_t newValue) { + input->offset = newValue; + preview->invalidate(); + SET_DIRTY(); + }); gvar->setSuffix("%"); // Switch @@ -134,7 +146,12 @@ void InputEditWindow::buildBody(FormWindow* form) // Curve line = form->newLine(&grid); new StaticText(line, rect_t{}, STR_CURVE, 0, COLOR_THEME_PRIMARY1); - auto param = new CurveParam(line, rect_t{}, &input->curve); + auto param = new CurveParam(line, rect_t{}, &input->curve, + [=](int32_t newValue) { + input->curve.value = newValue; + preview->invalidate(); + SET_DIRTY(); + }); lv_obj_set_style_grid_cell_x_align(param->getLvObj(), LV_GRID_ALIGN_STRETCH, 0); line = form->newLine(); diff --git a/radio/src/gui/colorlcd/mixer_edit.cpp b/radio/src/gui/colorlcd/mixer_edit.cpp index 81da81005..ef37c0057 100644 --- a/radio/src/gui/colorlcd/mixer_edit.cpp +++ b/radio/src/gui/colorlcd/mixer_edit.cpp @@ -136,7 +136,7 @@ void MixEditWindow::buildBody(FormWindow* form) // Curve new StaticText(line, rect_t{}, STR_CURVE, 0, COLOR_THEME_PRIMARY1); - new CurveParam(line, rect_t{}, &mix->curve); + new CurveParam(line, rect_t{}, &mix->curve, SET_DEFAULT(mix->curve.value)); line = form->newLine(); lv_obj_set_style_pad_all(line->getLvObj(), lv_dpx(8), 0); diff --git a/radio/src/gui/colorlcd/model_curves.cpp b/radio/src/gui/colorlcd/model_curves.cpp index 0f44ba919..d488ebd18 100644 --- a/radio/src/gui/colorlcd/model_curves.cpp +++ b/radio/src/gui/colorlcd/model_curves.cpp @@ -25,7 +25,6 @@ #define SET_DIRTY() storageDirty(EE_MODEL) - // initialize a new curves points to the default for a 5 point // curve. void initPoints(const CurveHeader &curve, int8_t *points) @@ -37,212 +36,6 @@ void initPoints(const CurveHeader &curve, int8_t *points) } } - -class CurveEditWindow : public Page -{ - public: - explicit CurveEditWindow(uint8_t index): - Page(ICON_MODEL_CURVES), - index(index) - { - buildBody(&body); - buildHeader(&header); - } - - protected: - uint8_t index; - CurveEdit * curveEdit = nullptr; - CurveDataEdit * curveDataEdit = nullptr; - - void buildHeader(Window * window) - { - new StaticText(window, - {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, - PAGE_LINE_HEIGHT}, - STR_MENUCURVE, 0, COLOR_THEME_PRIMARY2); - char s[16]; - strAppendStringWithIndex(s, STR_CV, index + 1); - new StaticText(window, - {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, - LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, - s, 0, COLOR_THEME_PRIMARY2); - } - -#if LCD_W > LCD_H - void buildBody(FormWindow * window) - { - coord_t curveWidth = window->height() - 2 * PAGE_PADDING; - - CurveHeader & curve = g_model.curves[index]; - int8_t * points = curveAddress(index); - - // Curve editor - curveEdit = new CurveEdit(window, { coord_t(LCD_W - curveWidth - PAGE_PADDING), PAGE_PADDING, curveWidth, curveWidth}, index); - - FormGridLayout grid; - grid.setLabelWidth(PAGE_PADDING); - grid.setMarginRight(curveWidth + 2 * PAGE_PADDING); - grid.spacer(PAGE_PADDING); - - // Name - new StaticText(window, grid.getFieldSlot(), STR_NAME, 0, COLOR_THEME_PRIMARY1); - grid.nextLine(); - new ModelTextEdit(window, grid.getFieldSlot(), curve.name, sizeof(curve.name)); - grid.nextLine(); - - // Type - new StaticText(window, grid.getFieldSlot(), STR_TYPE, 0, COLOR_THEME_PRIMARY1); - grid.nextLine(); - new Choice(window, grid.getFieldSlot(2, 0), STR_CURVE_TYPES, 0, 1, GET_DEFAULT(g_model.curves[index].type), - [=](int32_t newValue) { - CurveHeader &curve = g_model.curves[index]; - if (newValue != curve.type) { - for (int i = 1; i < 4 + curve.points; i++) { - points[i] = calcRESXto100(applyCustomCurve(calc100toRESX(-100 + i * 200 / (4 + curve.points)), index)); - } - if (moveCurve(index, newValue == CURVE_TYPE_CUSTOM ? 3 + curve.points : -3 - curve.points)) { - if (newValue == CURVE_TYPE_CUSTOM) { - resetCustomCurveX(points, 5 + curve.points); - } - curve.type = newValue; - } - SET_DIRTY(); - curveEdit->updatePreview(); - if (curveDataEdit) { - curveDataEdit->clear(); - curveDataEdit->update(); - } - } - }); - - // Points count - auto edit = new NumberEdit(window, grid.getFieldSlot(2, 1), 2, 17, GET_DEFAULT(g_model.curves[index].points + 5), - [=](int32_t newValue) { - newValue -= 5; - CurveHeader &curve = g_model.curves[index]; - int newPoints[MAX_POINTS_PER_CURVE]; - newPoints[0] = points[0]; - newPoints[4 + newValue] = points[4 + curve.points]; - for (int i = 1; i < 4 + newValue; i++) - newPoints[i] = calcRESXto100(applyCustomCurve(-RESX + (i * 2 * RESX) / (4 + newValue), index)); - if (moveCurve(index, (newValue - curve.points) * (curve.type == CURVE_TYPE_CUSTOM ? 2 : 1))) { - for (int i = 0; i < 5 + newValue; i++) { - points[i] = newPoints[i]; - if (curve.type == CURVE_TYPE_CUSTOM && i != 0 && i != 4 + newValue) - points[5 + newValue + i - 1] = -100 + (i * 200) / (4 + newValue); - } - curve.points = newValue; - SET_DIRTY(); - curveEdit->updatePreview(); - if (curveDataEdit) { - curveDataEdit->clear(); - curveDataEdit->update(); - } - } - }); - edit->setSuffix(STR_PTS); - grid.nextLine(); - - // Smooth - new StaticText(window, grid.getFieldSlot(2, 0), STR_SMOOTH, 0, COLOR_THEME_PRIMARY1); - new CheckBox(window, grid.getFieldSlot(2, 1), GET_DEFAULT(g_model.curves[index].smooth), - [=](int32_t newValue) { - g_model.curves[index].smooth = newValue; - SET_DIRTY(); - curveEdit->updatePreview(); - }); - grid.nextLine(); - - curveDataEdit = new CurveDataEdit(window, {0, grid.getWindowHeight(), coord_t(LCD_W - curveWidth - PAGE_PADDING - 1), window->height() - grid.getWindowHeight() - PAGE_PADDING}, index, curveEdit); - } -#else - void buildBody(FormWindow * window) - { - CurveHeader & curve = g_model.curves[index]; - int8_t * points = curveAddress(index); - - FormGridLayout grid; - grid.setMarginLeft(PAGE_PADDING); - grid.setMarginRight(PAGE_PADDING); - grid.setLabelWidth(100); - grid.spacer(PAGE_PADDING); - - // Curve editor - curveEdit = new CurveEdit(window, { 42, grid.getWindowHeight(), LCD_W - 80, LCD_W - 80}, index); - grid.spacer(curveEdit->height() + 15); - - // Name - new StaticText(window, grid.getLabelSlot(), STR_NAME, 0, COLOR_THEME_PRIMARY1); - new ModelTextEdit(window, grid.getFieldSlot(), curve.name, sizeof(curve.name)); - grid.nextLine(); - - // Type - new StaticText(window, grid.getLabelSlot(), STR_TYPE, 0, COLOR_THEME_PRIMARY1); - new Choice(window, grid.getFieldSlot(2, 0), STR_CURVE_TYPES, 0, 1, GET_DEFAULT(g_model.curves[index].type), - [=](int32_t newValue) { - CurveHeader &curve = g_model.curves[index]; - if (newValue != curve.type) { - for (int i = 1; i < 4 + curve.points; i++) { - points[i] = calcRESXto100(applyCustomCurve(calc100toRESX(-100 + i * 200 / (4 + curve.points)), index)); - } - if (moveCurve(index, newValue == CURVE_TYPE_CUSTOM ? 3 + curve.points : -3 - curve.points)) { - if (newValue == CURVE_TYPE_CUSTOM) { - resetCustomCurveX(points, 5 + curve.points); - } - curve.type = newValue; - } - SET_DIRTY(); - curveEdit->updatePreview(); - if(curveDataEdit) { - curveDataEdit->clear(); - curveDataEdit->update(); - } - } - }); - - // Points count - auto edit = new NumberEdit(window, grid.getFieldSlot(2, 1), 2, 17, GET_DEFAULT(g_model.curves[index].points + 5), - [=](int32_t newValue) { - newValue -= 5; - CurveHeader &curve = g_model.curves[index]; - int newPoints[MAX_POINTS_PER_CURVE]; - newPoints[0] = points[0]; - newPoints[4 + newValue] = points[4 + curve.points]; - for (int i = 1; i < 4 + newValue; i++) - newPoints[i] = calcRESXto100(applyCustomCurve(-RESX + (i * 2 * RESX) / (4 + newValue), index)); - if (moveCurve(index, (newValue - curve.points) * (curve.type == CURVE_TYPE_CUSTOM ? 2 : 1))) { - for (int i = 0; i < 5 + newValue; i++) { - points[i] = newPoints[i]; - if (curve.type == CURVE_TYPE_CUSTOM && i != 0 && i != 4 + newValue) - points[5 + newValue + i - 1] = -100 + (i * 200) / (4 + newValue); - } - curve.points = newValue; - SET_DIRTY(); - curveEdit->updatePreview(); - if(curveDataEdit) { - curveDataEdit->clear(); - curveDataEdit->update(); - } - } - }); - edit->setSuffix(STR_PTS); - grid.nextLine(); - - // Smooth - new StaticText(window, grid.getLabelSlot(), STR_SMOOTH, 0, COLOR_THEME_PRIMARY1); - new CheckBox(window, grid.getFieldSlot(), GET_DEFAULT(g_model.curves[index].smooth), - [=](int32_t newValue) { - g_model.curves[index].smooth = newValue; - SET_DIRTY(); - curveEdit->updatePreview(); - }); - grid.nextLine(); - - curveDataEdit = new CurveDataEdit(window, {PAGE_PADDING, grid.getWindowHeight(), coord_t(LCD_W - PAGE_PADDING - 1), window->height() - grid.getWindowHeight() - PAGE_PADDING}, index, curveEdit); - } -#endif -}; - class CurveButton : public Button { public: CurveButton(FormGroup * parent, const rect_t &rect, uint8_t index) : @@ -288,7 +81,6 @@ ModelCurvesPage::ModelCurvesPage() : { } - // can be called from any other screen to edit a curve. // currently called from model_mixes.cpp on longpress. void ModelCurvesPage::pushEditCurve(int index)