You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
616 lines
22 KiB
616 lines
22 KiB
2 years ago
|
/*
|
||
|
* EEZ Modular Firmware
|
||
|
* Copyright (C) 2015-present, Envox d.o.o.
|
||
|
*
|
||
|
* This program is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation, either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
|
||
|
* 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.
|
||
|
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include <math.h>
|
||
|
#include <limits.h>
|
||
|
|
||
|
#include <eez/conf.h>
|
||
|
#include <eez/core/util.h>
|
||
|
|
||
|
#include <eez/gui/gui.h>
|
||
|
#include <eez/gui/widgets/yt_graph.h>
|
||
|
|
||
|
#define CONF_GUI_YT_GRAPH_BLANK_PIXELS_AFTER_CURSOR 10
|
||
|
|
||
|
namespace eez {
|
||
|
namespace gui {
|
||
|
|
||
|
// used for YT_GRAPH_UPDATE_METHOD_SCROLL and YT_GRAPH_UPDATE_METHOD_SCAN_LINE
|
||
|
struct YTGraphDrawHelper {
|
||
|
const WidgetCursor &widgetCursor;
|
||
|
const Widget *widget;
|
||
|
|
||
|
float min[2];
|
||
|
float max[2];
|
||
|
|
||
|
uint16_t color16;
|
||
|
uint16_t dataColor16[2];
|
||
|
|
||
|
uint32_t numPositions;
|
||
|
uint32_t position;
|
||
|
|
||
|
int x;
|
||
|
|
||
|
int yPrev[2];
|
||
|
int y[2];
|
||
|
|
||
|
Value::YtDataGetValueFunctionPointer ytDataGetValue;
|
||
|
|
||
|
YTGraphDrawHelper(const WidgetCursor &widgetCursor_) : widgetCursor(widgetCursor_), widget(widgetCursor.widget) {
|
||
|
min[0] = ytDataGetMin(widgetCursor, widget->data, 0).getFloat();
|
||
|
max[0] = ytDataGetMax(widgetCursor, widget->data, 0).getFloat();
|
||
|
|
||
|
min[1] = ytDataGetMin(widgetCursor, widget->data, 1).getFloat();
|
||
|
max[1] = ytDataGetMax(widgetCursor, widget->data, 1).getFloat();
|
||
|
|
||
|
const Style* y1Style = ytDataGetStyle(widgetCursor, widget->data, 0);
|
||
|
const Style* y2Style = ytDataGetStyle(widgetCursor, widget->data, 1);
|
||
|
dataColor16[0] = display::getColor16FromIndex(y1Style->color);
|
||
|
dataColor16[1] = display::getColor16FromIndex(y2Style->color);
|
||
|
|
||
|
ytDataGetValue = ytDataGetGetValueFunc(widgetCursor, widget->data);
|
||
|
}
|
||
|
|
||
|
int getYValue(int valueIndex, uint32_t position) {
|
||
|
if (position >= numPositions) {
|
||
|
return INT_MIN;
|
||
|
}
|
||
|
|
||
|
float value = ytDataGetValue(position, valueIndex, nullptr);
|
||
|
|
||
|
if (isNaN(value)) {
|
||
|
return INT_MIN;
|
||
|
}
|
||
|
|
||
|
int y = (int)round((widgetCursor.h - 1) * (value - min[valueIndex]) / (max[valueIndex] - min[valueIndex]));
|
||
|
return widgetCursor.h - 1 - y;
|
||
|
}
|
||
|
|
||
|
void drawValue(int valueIndex) {
|
||
|
int yPrevValue = yPrev[valueIndex];
|
||
|
int yValue = y[valueIndex];
|
||
|
|
||
|
if (yValue == INT_MIN) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// clipping
|
||
|
if (yPrevValue >= 0 && yValue < 0) {
|
||
|
yValue = 0;
|
||
|
} else if (yPrevValue < 0 && yValue >= 0) {
|
||
|
yPrevValue = 0;
|
||
|
} else if (yPrevValue < widgetCursor.h && yValue >= widgetCursor.h) {
|
||
|
yValue = widgetCursor.h - 1;
|
||
|
} else if (yPrevValue >= widgetCursor.h && yValue < widgetCursor.h) {
|
||
|
yPrevValue = widgetCursor.h - 1;
|
||
|
}
|
||
|
|
||
|
if (yValue < 0 || yPrevValue < 0 || yValue >= widgetCursor.h || yPrevValue >= widgetCursor.h) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
display::setColor16(dataColor16[valueIndex]);
|
||
|
|
||
|
if (yPrevValue == INT_MIN || abs(yPrevValue - yValue) <= 1) {
|
||
|
display::drawPixel(x, widgetCursor.y + yValue);
|
||
|
} else {
|
||
|
int y1;
|
||
|
int y2;
|
||
|
if (yPrevValue < yValue) {
|
||
|
//display::drawVLine(x, widgetCursor.y + yPrevValue + 1, yValue - yPrevValue - 1);
|
||
|
y1 = widgetCursor.y + yPrevValue + 1;
|
||
|
y2 = y1 + yValue - yPrevValue;
|
||
|
} else {
|
||
|
//display::drawVLine(x, widgetCursor.y + yValue, yPrevValue - yValue - 1);
|
||
|
y1 = widgetCursor.y + yValue;
|
||
|
y2 = y1 + yPrevValue - yValue;
|
||
|
}
|
||
|
for (int y = y1; y < y2; y++) {
|
||
|
display::drawPixel(x, y);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void drawStep() {
|
||
|
if (y[0] != INT_MIN && y[1] != INT_MIN && abs(yPrev[0] - y[0]) <= 1 && abs(yPrev[1] - y[1]) <= 1 && y[0] == y[1]) {
|
||
|
if (y[0] >= 0 && y[0] < widgetCursor.h) {
|
||
|
display::setColor16(position % 2 ? dataColor16[1] : dataColor16[0]);
|
||
|
display::drawPixel(x, widgetCursor.y + y[0]);
|
||
|
}
|
||
|
} else {
|
||
|
drawValue(0);
|
||
|
drawValue(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void drawScanLine(uint32_t startPosition, uint32_t endPosition, uint16_t graphWidth) {
|
||
|
numPositions = endPosition;
|
||
|
|
||
|
int x1 = widgetCursor.x + startPosition % graphWidth;
|
||
|
int x2 = widgetCursor.x + (endPosition - 1) % graphWidth;
|
||
|
display::setColor16(color16);
|
||
|
if (x1 <= x2) {
|
||
|
display::fillRect(x1, widgetCursor.y, x2, widgetCursor.y + widgetCursor.h - 1);
|
||
|
} else {
|
||
|
display::fillRect(x1, widgetCursor.y, widgetCursor.x + widgetCursor.w - 1, widgetCursor.y + widgetCursor.h - 1);
|
||
|
display::fillRect(widgetCursor.x, widgetCursor.y, x2, widgetCursor.y + widgetCursor.h - 1);
|
||
|
}
|
||
|
|
||
|
display::startPixelsDraw();
|
||
|
for (position = startPosition; position < endPosition; ++position) {
|
||
|
x = widgetCursor.x + position % graphWidth;
|
||
|
|
||
|
y[0] = getYValue(0, position);
|
||
|
yPrev[0] = getYValue(0, position == 0 ? position : position - 1);
|
||
|
|
||
|
y[1] = getYValue(1, position);
|
||
|
yPrev[1] = getYValue(1, position == 0 ? position : position - 1);
|
||
|
|
||
|
drawStep();
|
||
|
}
|
||
|
display::endPixelsDraw();
|
||
|
}
|
||
|
|
||
|
void drawScrolling(uint32_t previousHistoryValuePosition, uint32_t currentHistoryValuePosition, uint32_t numPositions_, uint16_t graphWidth) {
|
||
|
uint32_t numPointsToDraw = currentHistoryValuePosition - previousHistoryValuePosition;
|
||
|
if (numPointsToDraw > graphWidth) {
|
||
|
numPointsToDraw = graphWidth;
|
||
|
}
|
||
|
|
||
|
if (numPointsToDraw < graphWidth) {
|
||
|
display::bitBlt(
|
||
|
widgetCursor.x + numPointsToDraw,
|
||
|
widgetCursor.y,
|
||
|
widgetCursor.x + graphWidth - 1,
|
||
|
widgetCursor.y + widgetCursor.h - 1,
|
||
|
widgetCursor.x,
|
||
|
widgetCursor.y);
|
||
|
}
|
||
|
|
||
|
int endX = widgetCursor.x + graphWidth;
|
||
|
int startX = endX - numPointsToDraw;
|
||
|
|
||
|
position = previousHistoryValuePosition + 1;
|
||
|
|
||
|
numPositions = position + numPointsToDraw;
|
||
|
|
||
|
yPrev[0] = getYValue(0, previousHistoryValuePosition);
|
||
|
yPrev[1] = getYValue(1, previousHistoryValuePosition);
|
||
|
|
||
|
display::setColor16(color16);
|
||
|
display::fillRect(startX, widgetCursor.y, endX - 1, widgetCursor.y + widgetCursor.h - 1);
|
||
|
|
||
|
display::startPixelsDraw();
|
||
|
for (x = startX; x < endX; x++, position++) {
|
||
|
y[0] = getYValue(0, position);
|
||
|
y[1] = getYValue(1, position);
|
||
|
|
||
|
drawStep();
|
||
|
|
||
|
yPrev[0] = y[0];
|
||
|
yPrev[1] = y[1];
|
||
|
}
|
||
|
display::endPixelsDraw();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// used for YT_GRAPH_UPDATE_METHOD_STATIC
|
||
|
struct YTGraphStaticDrawHelper {
|
||
|
YTGraphWidgetState *widgetState;
|
||
|
const WidgetCursor &widgetCursor;
|
||
|
const Widget *widget;
|
||
|
|
||
|
Style* style;
|
||
|
|
||
|
uint16_t dataColor16;
|
||
|
|
||
|
uint32_t numPositions;
|
||
|
uint32_t position;
|
||
|
|
||
|
float offset;
|
||
|
float scale;
|
||
|
|
||
|
int m_valueIndex;
|
||
|
|
||
|
int x;
|
||
|
|
||
|
int yPrevMin;
|
||
|
int yPrevMax;
|
||
|
int yMin;
|
||
|
int yMax;
|
||
|
|
||
|
Value::YtDataGetValueFunctionPointer ytDataGetValue;
|
||
|
|
||
|
int xLabels[MAX_NUM_OF_Y_VALUES];
|
||
|
int yLabels[MAX_NUM_OF_Y_VALUES];
|
||
|
|
||
|
YTGraphStaticDrawHelper(YTGraphWidgetState *widgetState_, const WidgetCursor &widgetCursor_)
|
||
|
: widgetState(widgetState_), widgetCursor(widgetCursor_), widget(widgetCursor.widget)
|
||
|
{
|
||
|
ytDataGetValue = ytDataGetGetValueFunc(widgetCursor, widget->data);
|
||
|
}
|
||
|
|
||
|
void getYValue(uint32_t position, int &min, int &max) {
|
||
|
if (position >= numPositions) {
|
||
|
max = INT_MIN;
|
||
|
min = INT_MIN;
|
||
|
} else {
|
||
|
float fMax;
|
||
|
float fMin = ytDataGetValue(position, m_valueIndex, &fMax);
|
||
|
|
||
|
if (isNaN(fMin)) {
|
||
|
max = INT_MIN;
|
||
|
} else {
|
||
|
max = widgetCursor.h - 1 - (int)floor(widgetCursor.h / 2.0f + (fMin + offset) * scale);
|
||
|
}
|
||
|
|
||
|
if (isNaN(fMax)) {
|
||
|
min = INT_MIN;
|
||
|
} else {
|
||
|
min = widgetCursor.h - 1 - (int)floor(widgetCursor.h / 2.0f + (fMax + offset) * scale);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void drawValue() {
|
||
|
if (yMin == INT_MIN) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
display::setColor16(dataColor16);
|
||
|
|
||
|
int yFrom;
|
||
|
int yTo;
|
||
|
|
||
|
if (yPrevMax == INT_MIN) {
|
||
|
yFrom = yMin;
|
||
|
yTo = yMax;
|
||
|
} else {
|
||
|
if (yPrevMax < yMin) {
|
||
|
yFrom = yPrevMax + 1;
|
||
|
yTo = yMax;
|
||
|
} else if (yMax < yPrevMin) {
|
||
|
yFrom = yMin;
|
||
|
yTo = yPrevMin - 1;
|
||
|
} else {
|
||
|
yFrom = yMin;
|
||
|
yTo = yMax;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((yFrom < 0 && yTo < 0) || (yFrom >= widgetCursor.h && yTo >= widgetCursor.h)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (yFrom < 0) {
|
||
|
yFrom = 0;
|
||
|
}
|
||
|
|
||
|
if (yTo >= widgetCursor.h) {
|
||
|
yTo = widgetCursor.h - 1;
|
||
|
}
|
||
|
|
||
|
if (yFrom == yTo) {
|
||
|
display::drawPixel(x, widgetCursor.y + yFrom);
|
||
|
} else {
|
||
|
// display::drawVLine(x, widgetCursor.y + yFrom, yTo - yFrom);
|
||
|
int y1 = widgetCursor.y + yFrom;
|
||
|
int y2 = y1 + yTo - yFrom + 1;
|
||
|
for (int y = y1; y < y2; y++) {
|
||
|
display::drawPixel(x, y);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void getMinMax(int *yLabels, int n, int &yMin, int &yMax) {
|
||
|
yMin = INT_MAX;
|
||
|
yMax = INT_MIN;
|
||
|
for (int i = 0; i < n; i++) {
|
||
|
if (yLabels[i] < yMin) {
|
||
|
yMin = yLabels[i];
|
||
|
}
|
||
|
if (yLabels[i] > yMax) {
|
||
|
yMax = yLabels[i];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void repositionLabels(int labelHeight) {
|
||
|
for (int valueIndex = 0; valueIndex < MAX_NUM_OF_Y_VALUES; valueIndex++) {
|
||
|
if (yLabels[valueIndex] != INT_MIN) {
|
||
|
yLabels[valueIndex] -= labelHeight / 2;
|
||
|
if (yLabels[valueIndex] < widgetCursor.y) {
|
||
|
yLabels[valueIndex] = widgetCursor.y;
|
||
|
} else if (yLabels[valueIndex] > widgetCursor.y + widgetCursor.h - labelHeight) {
|
||
|
yLabels[valueIndex] = widgetCursor.y + widgetCursor.h - labelHeight;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void drawGraph(uint32_t currentHistoryValuePosition, int startX, int endX, int vertDivisions) {
|
||
|
//xLabels[m_valueIndex] = INT_MIN;
|
||
|
yLabels[m_valueIndex] = INT_MIN;
|
||
|
|
||
|
if (ytDataDataValueIsVisible(widgetCursor, widget->data, m_valueIndex)) {
|
||
|
position = currentHistoryValuePosition;
|
||
|
|
||
|
scale = (widgetCursor.h - 1) / widgetState->valueDiv[m_valueIndex] / vertDivisions;
|
||
|
offset = widgetState->valueOffset[m_valueIndex];
|
||
|
|
||
|
const Style* style = ytDataGetStyle(widgetCursor, widget->data, m_valueIndex);
|
||
|
dataColor16 = display::getColor16FromIndex(style->color);
|
||
|
|
||
|
getYValue(position > 0 ? position - 1 : 0, yPrevMin, yPrevMax);
|
||
|
|
||
|
for (x = startX; x < endX; x++, position++) {
|
||
|
getYValue(position, yMin, yMax);
|
||
|
drawValue();
|
||
|
yPrevMin = yMin;
|
||
|
yPrevMax = yMax;
|
||
|
|
||
|
if (yMin != INT_MIN) {
|
||
|
//xLabels[m_valueIndex] = x;
|
||
|
yLabels[m_valueIndex] = widgetCursor.y + yMin;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
xLabels[m_valueIndex] = endX;
|
||
|
}
|
||
|
|
||
|
void drawLabel(font::Font &font, bool transparent) {
|
||
|
if (yLabels[m_valueIndex] != INT_MIN) {
|
||
|
const Style *labelStyle = ytDataGetStyle(widgetCursor, widget->data, m_valueIndex);
|
||
|
|
||
|
char labelText[64];
|
||
|
ytDataGetLabel(widgetCursor, widget->data, m_valueIndex, labelText, sizeof(labelText));
|
||
|
int labelWidth = display::measureStr(labelText, -1, font, widgetCursor.w);
|
||
|
|
||
|
int xLabel = xLabels[m_valueIndex];
|
||
|
if (xLabel < widgetCursor.x) {
|
||
|
xLabel = widgetCursor.x;
|
||
|
} else if (xLabel > widgetCursor.x + widgetCursor.w - labelWidth) {
|
||
|
xLabel = widgetCursor.x + widgetCursor.w - labelWidth;
|
||
|
}
|
||
|
|
||
|
if (!transparent) {
|
||
|
display::setColor(labelStyle->backgroundColor);
|
||
|
display::fillRect(xLabel, yLabels[m_valueIndex], xLabel + labelWidth - 1, yLabels[m_valueIndex] + font.getHeight() - 1);
|
||
|
}
|
||
|
|
||
|
display::setColor(labelStyle->color);
|
||
|
display::drawStr(labelText, -1, xLabel, yLabels[m_valueIndex], widgetCursor.x, widgetCursor.y, widgetCursor.x + widgetCursor.w - 1, widgetCursor.y + widgetCursor.h - 1, font, -1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void drawStatic(uint32_t previousHistoryValuePosition, uint32_t currentHistoryValuePosition, uint32_t numPositions_, uint16_t graphWidth, bool showLabels, int selectedValueIndex) {
|
||
|
// draw background
|
||
|
const Style* style = getStyle(widget->style);
|
||
|
display::setColor(style->backgroundColor);
|
||
|
display::fillRect(widgetCursor.x, widgetCursor.y, widgetCursor.x + (int)widgetCursor.w - 1, widgetCursor.y + (int)widgetCursor.h - 1);
|
||
|
|
||
|
numPositions = numPositions_;
|
||
|
|
||
|
int startX = widgetCursor.x;
|
||
|
int endX = startX + graphWidth;
|
||
|
|
||
|
int horzDivisions = ytDataGetHorzDivisions(widgetCursor, widget->data);
|
||
|
int vertDivisions = ytDataGetVertDivisions(widgetCursor, widget->data);
|
||
|
|
||
|
// draw grid
|
||
|
display::setColor(style->borderColor);
|
||
|
for (int x = 1; x < horzDivisions; x++) {
|
||
|
display::drawVLine(widgetCursor.x + x * widgetCursor.w / horzDivisions, widgetCursor.y, widgetCursor.h - 1);
|
||
|
}
|
||
|
for (int y = 1; y < vertDivisions; y++) {
|
||
|
display::drawHLine(widgetCursor.x, widgetCursor.y + y * widgetCursor.h / vertDivisions, widgetCursor.w - 1);
|
||
|
}
|
||
|
|
||
|
// draw bookmarks
|
||
|
if (widgetState->bookmarks) {
|
||
|
for (int x = 0; x < widgetCursor.w; x++) {
|
||
|
if (widgetState->bookmarks[x]) {
|
||
|
display::setColor(COLOR_ID_BOOKMARK);
|
||
|
display::drawVLine(startX + x, widgetCursor.y, widgetCursor.h - 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// draw graphs
|
||
|
display::startPixelsDraw();
|
||
|
for (m_valueIndex = 0; m_valueIndex < MAX_NUM_OF_Y_VALUES; m_valueIndex++) {
|
||
|
if (m_valueIndex != selectedValueIndex) {
|
||
|
drawGraph(currentHistoryValuePosition, startX, endX, vertDivisions);
|
||
|
}
|
||
|
}
|
||
|
if (selectedValueIndex != -1) {
|
||
|
m_valueIndex = selectedValueIndex;
|
||
|
drawGraph(currentHistoryValuePosition, startX, endX, vertDivisions);
|
||
|
}
|
||
|
display::endPixelsDraw();
|
||
|
|
||
|
// draw cursor
|
||
|
if (ytDataIsCursorVisible(widgetCursor, widgetCursor.widget->data)) {
|
||
|
display::setColor(style->color);
|
||
|
display::drawVLine(startX + widgetState->cursorPosition - currentHistoryValuePosition, widgetCursor.y, widgetCursor.h - 1);
|
||
|
|
||
|
char text[64];
|
||
|
ytDataGetCursorXValue(widgetCursor, widgetCursor.widget->data).toText(text, sizeof(text));
|
||
|
|
||
|
font::Font font = styleGetFont(style);
|
||
|
int MIN_CURSOR_TEXT_WIDTH = 80;
|
||
|
int cursorTextWidth = MAX(display::measureStr(text, -1, font), MIN_CURSOR_TEXT_WIDTH);
|
||
|
int cursorTextHeight = font.getHeight();
|
||
|
const int PADDING = 0;
|
||
|
int xCursorText = widgetCursor.x + widgetState->cursorPosition - currentHistoryValuePosition - cursorTextWidth / 2;
|
||
|
if (xCursorText < widgetCursor.x + PADDING) {
|
||
|
xCursorText = widgetCursor.x + PADDING;
|
||
|
} else if (xCursorText + cursorTextWidth > widgetCursor.x + widgetCursor.w - PADDING) {
|
||
|
xCursorText = widgetCursor.x + widgetCursor.w - PADDING - cursorTextWidth;
|
||
|
}
|
||
|
int yCursorText = widgetCursor.y + widgetCursor.h - cursorTextHeight - PADDING;
|
||
|
|
||
|
drawText(
|
||
|
text, -1,
|
||
|
xCursorText, yCursorText, cursorTextWidth, cursorTextHeight,
|
||
|
style,
|
||
|
widgetState->flags.focused
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// draw labels
|
||
|
if (showLabels) {
|
||
|
font::Font font = styleGetFont(style);
|
||
|
int labelHeight = font.getHeight();
|
||
|
|
||
|
repositionLabels(labelHeight);
|
||
|
|
||
|
for (m_valueIndex = 0; m_valueIndex < MAX_NUM_OF_Y_VALUES; m_valueIndex++) {
|
||
|
if (m_valueIndex != selectedValueIndex) {
|
||
|
drawLabel(font, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (selectedValueIndex != -1) {
|
||
|
m_valueIndex = selectedValueIndex;
|
||
|
drawLabel(font, false);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
bool YTGraphWidgetState::updateState() {
|
||
|
WIDGET_STATE_START(YTGraphWidget);
|
||
|
|
||
|
refreshBackground = !hasPreviousState;
|
||
|
|
||
|
WIDGET_STATE(flags.focused, isFocusWidget(widgetCursor));
|
||
|
|
||
|
WIDGET_STATE(refreshCounter, ytDataGetRefreshCounter(widgetCursor, widget->data));
|
||
|
WIDGET_STATE(iChannel, widgetCursor.cursor);
|
||
|
WIDGET_STATE(ytGraphUpdateMethod, ytDataGetGraphUpdateMethod(widgetCursor, widget->data));
|
||
|
|
||
|
auto new_historyValuePosition = ytDataGetPosition(widgetCursor, widget->data);
|
||
|
|
||
|
if (hasPreviousState) {
|
||
|
previousHistoryValuePosition = historyValuePosition;
|
||
|
} else {
|
||
|
uint16_t graphWidth = (uint16_t)widgetCursor.w;
|
||
|
previousHistoryValuePosition = new_historyValuePosition - graphWidth;
|
||
|
hasPreviousState = false;
|
||
|
}
|
||
|
|
||
|
WIDGET_STATE(historyValuePosition, new_historyValuePosition);
|
||
|
WIDGET_STATE(cursorPosition, historyValuePosition + ytDataGetCursorOffset(widgetCursor, widget->data));
|
||
|
|
||
|
WIDGET_STATE(numHistoryValues, ytDataGetSize(widgetCursor, widget->data));
|
||
|
WIDGET_STATE(bookmarks, ytDataGetBookmarks(widgetCursor, widget->data));
|
||
|
WIDGET_STATE(showLabels, ytDataGetShowLabels(widgetCursor, widget->data));
|
||
|
WIDGET_STATE(selectedValueIndex, ytDataGetSelectedValueIndex(widgetCursor, widget->data));
|
||
|
|
||
|
if (ytGraphUpdateMethod == YT_GRAPH_UPDATE_METHOD_STATIC) {
|
||
|
for (int valueIndex = 0; valueIndex < MAX_NUM_OF_Y_VALUES; valueIndex++) {
|
||
|
WIDGET_STATE(valueIsVisible[valueIndex], ytDataDataValueIsVisible(widgetCursor, widget->data, valueIndex));
|
||
|
WIDGET_STATE(valueDiv[valueIndex], ytDataGetDiv(widgetCursor, widget->data, valueIndex));
|
||
|
WIDGET_STATE(valueOffset[valueIndex], ytDataGetOffset(widgetCursor, widget->data, valueIndex));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
WIDGET_STATE_END()
|
||
|
}
|
||
|
|
||
|
void YTGraphWidgetState::render() {
|
||
|
const WidgetCursor &widgetCursor = g_widgetCursor;
|
||
|
|
||
|
auto widget = (const YTGraphWidget *)widgetCursor.widget;
|
||
|
|
||
|
uint16_t graphWidth = (uint16_t)widgetCursor.w;
|
||
|
|
||
|
if (ytGraphUpdateMethod == YT_GRAPH_UPDATE_METHOD_STATIC) {
|
||
|
YTGraphStaticDrawHelper drawHelper(this, widgetCursor);
|
||
|
drawHelper.drawStatic(previousHistoryValuePosition, historyValuePosition, numHistoryValues, graphWidth, showLabels, selectedValueIndex);
|
||
|
} else {
|
||
|
const Style* style = getStyle(widget->style);
|
||
|
|
||
|
if (refreshBackground) {
|
||
|
display::setColor(style->backgroundColor);
|
||
|
display::fillRect(widgetCursor.x, widgetCursor.y, widgetCursor.x + (int)widgetCursor.w - 1, widgetCursor.y + (int)widgetCursor.h - 1);
|
||
|
}
|
||
|
|
||
|
YTGraphDrawHelper drawHelper(widgetCursor);
|
||
|
drawHelper.color16 = display::getColor16FromIndex(flags.active ? style->color : style->backgroundColor);
|
||
|
if (ytGraphUpdateMethod == YT_GRAPH_UPDATE_METHOD_SCAN_LINE) {
|
||
|
drawHelper.drawScanLine(previousHistoryValuePosition, historyValuePosition, graphWidth);
|
||
|
|
||
|
int x = widgetCursor.x;
|
||
|
|
||
|
// draw cursor
|
||
|
display::setColor(style->color);
|
||
|
display::drawVLine(x + historyValuePosition % graphWidth, widgetCursor.y, (int)widgetCursor.h - 1);
|
||
|
display::drawVLine(x + (historyValuePosition + 1) % graphWidth, widgetCursor.y, (int)widgetCursor.h - 1);
|
||
|
|
||
|
// draw blank lines
|
||
|
int x1 = x + (historyValuePosition + 2) % graphWidth;
|
||
|
int x2 = x + (historyValuePosition + CONF_GUI_YT_GRAPH_BLANK_PIXELS_AFTER_CURSOR) % graphWidth;
|
||
|
|
||
|
display::setColor(style->backgroundColor);
|
||
|
if (x1 < x2) {
|
||
|
display::fillRect(x1, widgetCursor.y, x2, widgetCursor.y + (int)widgetCursor.h - 1);
|
||
|
} else {
|
||
|
display::fillRect(x1, widgetCursor.y, x + graphWidth - 1, widgetCursor.y + (int)widgetCursor.h - 1);
|
||
|
display::fillRect(x, widgetCursor.y, x2, widgetCursor.y + (int)widgetCursor.h - 1);
|
||
|
}
|
||
|
} else if (ytGraphUpdateMethod == YT_GRAPH_UPDATE_METHOD_SCROLL) {
|
||
|
drawHelper.drawScrolling(previousHistoryValuePosition, historyValuePosition, numHistoryValues, graphWidth);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool YTGraphWidgetState::hasOnTouch() {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void YTGraphWidgetState::onTouch(const WidgetCursor &widgetCursor, Event &touchEvent) {
|
||
|
if (ytDataGetGraphUpdateMethod(widgetCursor, widgetCursor.widget->data) == YT_GRAPH_UPDATE_METHOD_STATIC) {
|
||
|
if (touchEvent.type == EVENT_TYPE_TOUCH_DOWN || touchEvent.type == EVENT_TYPE_TOUCH_MOVE) {
|
||
|
TouchDrag touchDrag = {
|
||
|
widgetCursor,
|
||
|
touchEvent.type,
|
||
|
touchEvent.x - widgetCursor.x,
|
||
|
touchEvent.y - widgetCursor.y
|
||
|
};
|
||
|
ytDataTouchDrag(widgetCursor, widgetCursor.widget->data, &touchDrag);
|
||
|
}
|
||
|
} else {
|
||
|
if (touchEvent.type == EVENT_TYPE_TOUCH_DOWN) {
|
||
|
if (widgetCursor.appContext->isWidgetActionEnabled(widgetCursor)) {
|
||
|
auto action = getWidgetAction(widgetCursor);
|
||
|
executeAction(widgetCursor, action);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace gui
|
||
|
} // namespace eez
|