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.
615 lines
22 KiB
615 lines
22 KiB
/* |
|
* 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
|
|
|