fix(color): Invalid custom curves + layout updates (#2973)

* Layout updates and bug fixes for curve editor.
- Fix issue 2972 where an invalid curve could be created
- Use vertical layout or list of points
- Add border to preview to better show end points
- Fix issue where preview was redrawn very frequently
- Better organisation of curve edit classes

* Fix to refresh curve preview on input edit page when parameters changed.
This commit is contained in:
philmoz
2023-01-15 11:23:40 +10:00
committed by GitHub
parent dd8ab3037f
commit cb9f411036
9 changed files with 399 additions and 356 deletions
+51 -21
View File
@@ -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<int(int)> function, std::function<int()> 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<coord_t>(0,
width() / 2 + divRoundClosest(x * width() / 2, RESX),
width() - 1);
return dx + limit<coord_t>(0,
dw / 2 + divRoundClosest(x * dw / 2, RESX),
dw - 1);
}
coord_t Curve::getPointY(int y) const
{
return limit<coord_t>(0,
height() / 2 - divRoundClosest(y * height() / 2, RESX),
height() - 1);
return dy + limit<coord_t>(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();
}
+5 -20
View File
@@ -33,12 +33,7 @@ class Curve: public Window
friend class CurveEdit;
public:
Curve(Window * parent, const rect_t & rect, std::function<int(int)> function, std::function<int()> position=nullptr):
Window(parent, rect, OPAQUE),
function(std::move(function)),
position(std::move(position))
{
}
Curve(Window * parent, const rect_t & rect, std::function<int(int)> function, std::function<int()> 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<int(int)> function;
std::function<int()> position;
std::list<CurvePoint> points;
+6 -6
View File
@@ -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<void(int32_t)> 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));
+3 -1
View File
@@ -33,6 +33,7 @@ class CurveParam : public Window
{
// Curve
CurveRef* ref;
std::function<void(int32_t)> 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<void(int32_t)> setRefValue);
};
+274 -77
View File
@@ -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);
}
+39 -19
View File
@@ -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);
};
+20 -3
View File
@@ -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();
+1 -1
View File
@@ -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);
-208
View File
@@ -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)