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.
383 lines
13 KiB
383 lines
13 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 <memory.h>
|
||
|
|
||
|
#include <eez/core/os.h>
|
||
|
#include <eez/core/util.h>
|
||
|
|
||
|
#include <eez/gui/gui.h>
|
||
|
#include <eez/gui/widgets/bar_graph.h>
|
||
|
|
||
|
namespace eez {
|
||
|
namespace gui {
|
||
|
|
||
|
#define BAR_GRAPH_ORIENTATION_LEFT_RIGHT 1
|
||
|
#define BAR_GRAPH_ORIENTATION_RIGHT_LEFT 2
|
||
|
#define BAR_GRAPH_ORIENTATION_TOP_BOTTOM 3
|
||
|
#define BAR_GRAPH_ORIENTATION_BOTTOM_TOP 4
|
||
|
#define BAR_GRAPH_ORIENTATION_MASK 0x0F
|
||
|
#define BAR_GRAPH_DO_NOT_DISPLAY_VALUE (1 << 4)
|
||
|
|
||
|
int calcValuePosInBarGraphWidget(Value &value, float min, float max, int d) {
|
||
|
return (int)roundf((value.getFloat() - min) * d / (max - min));
|
||
|
}
|
||
|
|
||
|
int calcValuePosInBarGraphWidgetWithClamp(Value &value, float min, float max, int d) {
|
||
|
int p = calcValuePosInBarGraphWidget(value, min, max, d);
|
||
|
|
||
|
if (p < 0) {
|
||
|
p = 0;
|
||
|
} else if (p >= d) {
|
||
|
p = d - 1;
|
||
|
}
|
||
|
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
void drawLineInBarGraphWidget(const BarGraphWidget *barGraphWidget, int p, uint16_t lineStyleID, int x, int y, int w, int h) {
|
||
|
const Style *style = getStyle(lineStyleID);
|
||
|
|
||
|
display::setColor(style->color);
|
||
|
if ((barGraphWidget->orientation & BAR_GRAPH_ORIENTATION_MASK) == BAR_GRAPH_ORIENTATION_LEFT_RIGHT) {
|
||
|
display::drawVLine(x + p, y, h - 1);
|
||
|
} else if ((barGraphWidget->orientation & BAR_GRAPH_ORIENTATION_MASK) == BAR_GRAPH_ORIENTATION_RIGHT_LEFT) {
|
||
|
display::drawVLine(x - p, y, h - 1);
|
||
|
} else if ((barGraphWidget->orientation & BAR_GRAPH_ORIENTATION_MASK) == BAR_GRAPH_ORIENTATION_TOP_BOTTOM) {
|
||
|
display::drawHLine(x, y + p, w - 1);
|
||
|
} else {
|
||
|
display::drawHLine(x, y - p, w - 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool BarGraphWidgetState::updateState() {
|
||
|
WIDGET_STATE_START(BarGraphWidget);
|
||
|
|
||
|
const Style* style = getStyle(g_hooks.overrideStyle(widgetCursor, widget->style));
|
||
|
|
||
|
WIDGET_STATE(flags.active, g_isActiveWidget);
|
||
|
WIDGET_STATE(flags.blinking, g_isBlinkTime && isBlinking(widgetCursor, widget->data));
|
||
|
WIDGET_STATE(data, get(widgetCursor, widget->data));
|
||
|
WIDGET_STATE(color, getColor(widgetCursor, widget->data, style));
|
||
|
WIDGET_STATE(backgroundColor, getBackgroundColor(widgetCursor, widget->data, style));
|
||
|
WIDGET_STATE(activeColor, getActiveColor(widgetCursor, widget->data, style));
|
||
|
WIDGET_STATE(activeBackgroundColor, getActiveBackgroundColor(widgetCursor, widget->data, style));
|
||
|
WIDGET_STATE(line1Data, get(widgetCursor, widget->line1Data));
|
||
|
WIDGET_STATE(line2Data, get(widgetCursor, widget->line2Data));
|
||
|
|
||
|
bool refreshTextData = true;
|
||
|
auto currentTime = millis();
|
||
|
if (hasPreviousState && textData != data) {
|
||
|
uint32_t refreshRate = getTextRefreshRate(widgetCursor, widget->data);
|
||
|
if (refreshRate != 0 && currentTime - textDataRefreshLastTime < refreshRate) {
|
||
|
refreshTextData = false;
|
||
|
}
|
||
|
}
|
||
|
if (refreshTextData) {
|
||
|
WIDGET_STATE(textData, data);
|
||
|
textDataRefreshLastTime = currentTime;
|
||
|
}
|
||
|
|
||
|
WIDGET_STATE_END()
|
||
|
}
|
||
|
|
||
|
void BarGraphWidgetState::render() {
|
||
|
const WidgetCursor &widgetCursor = g_widgetCursor;
|
||
|
|
||
|
auto widget = (const BarGraphWidget *)widgetCursor.widget;
|
||
|
const Style* style = getStyle(g_hooks.overrideStyle(widgetCursor, widget->style));
|
||
|
|
||
|
int x = widgetCursor.x;
|
||
|
int y = widgetCursor.y;
|
||
|
const int w = widgetCursor.w;
|
||
|
const int h = widgetCursor.h;
|
||
|
|
||
|
float min = getMin(widgetCursor, widget->data).getFloat();
|
||
|
|
||
|
float max;
|
||
|
Value displayValueRange = getDisplayValueRange(widgetCursor, widget->data);
|
||
|
if (displayValueRange.getType() == VALUE_TYPE_FLOAT) {
|
||
|
max = displayValueRange.getFloat();
|
||
|
} else {
|
||
|
max = getMax(widgetCursor, widget->data).getFloat();
|
||
|
}
|
||
|
|
||
|
bool horizontal =
|
||
|
(widget->orientation & BAR_GRAPH_ORIENTATION_MASK) == BAR_GRAPH_ORIENTATION_LEFT_RIGHT ||
|
||
|
(widget->orientation & BAR_GRAPH_ORIENTATION_MASK) == BAR_GRAPH_ORIENTATION_RIGHT_LEFT;
|
||
|
|
||
|
int d = horizontal ? w : h;
|
||
|
|
||
|
// calc bar position (monitored value)
|
||
|
int pValue = calcValuePosInBarGraphWidgetWithClamp(data, min, max, d);
|
||
|
|
||
|
// calc line 1 position (set value)
|
||
|
int pLine1 = calcValuePosInBarGraphWidget(line1Data, min, max, d);
|
||
|
bool drawLine1 = pLine1 >= 0 && pLine1 < d;
|
||
|
|
||
|
// calc line 2 position (limit value)
|
||
|
int pLine2 = calcValuePosInBarGraphWidget(line2Data, min, max, d);
|
||
|
bool drawLine2 = pLine2 >= 0 && pLine2 < d;
|
||
|
|
||
|
if (drawLine1 && drawLine2) {
|
||
|
// make sure line positions don't overlap
|
||
|
if (pLine1 == pLine2) {
|
||
|
pLine1 = pLine2 - 1;
|
||
|
}
|
||
|
|
||
|
// make sure all lines are visible
|
||
|
if (pLine1 < 0) {
|
||
|
pLine2 -= pLine1;
|
||
|
pLine1 = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Style textStyle;
|
||
|
memcpy(&textStyle, widget->textStyle ? getStyle(widget->textStyle) : style, sizeof(Style));
|
||
|
if (style->color != color) {
|
||
|
textStyle.color = flags.active || flags.blinking ? activeColor : color;
|
||
|
}
|
||
|
|
||
|
uint16_t fg = flags.active || flags.blinking ? activeColor : color;
|
||
|
uint16_t bg = flags.active || flags.blinking ? activeBackgroundColor : backgroundColor;
|
||
|
|
||
|
if (horizontal) {
|
||
|
// calc text position
|
||
|
char valueText[64];
|
||
|
int pText = 0;
|
||
|
int wText = 0;
|
||
|
if (!(widget->orientation & BAR_GRAPH_DO_NOT_DISPLAY_VALUE)) {
|
||
|
if (widget->textStyle) {
|
||
|
font::Font font = styleGetFont(&textStyle);
|
||
|
|
||
|
textData.toText(valueText, sizeof(valueText));
|
||
|
wText = display::measureStr(valueText, -1, font, w);
|
||
|
|
||
|
int padding = textStyle.paddingLeft;
|
||
|
wText += padding;
|
||
|
|
||
|
if (pValue + wText <= d) {
|
||
|
textStyle.backgroundColor = bg;
|
||
|
pText = pValue;
|
||
|
} else {
|
||
|
textStyle.backgroundColor = fg;
|
||
|
textStyle.color = textStyle.activeColor;
|
||
|
wText += padding;
|
||
|
pText = MAX(pValue - wText, 0);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
pText = pValue;
|
||
|
}
|
||
|
|
||
|
if ((widget->orientation & BAR_GRAPH_ORIENTATION_MASK) == BAR_GRAPH_ORIENTATION_LEFT_RIGHT) {
|
||
|
// draw bar
|
||
|
if (pText > 0) {
|
||
|
display::setColor(fg);
|
||
|
display::fillRect(x, y, x + pText - 1, y + h - 1);
|
||
|
}
|
||
|
|
||
|
if (!(widget->orientation & BAR_GRAPH_DO_NOT_DISPLAY_VALUE)) {
|
||
|
drawText(valueText, -1, x + pText, y, wText, h, &textStyle);
|
||
|
}
|
||
|
|
||
|
// draw background, but do not draw over line 1 and line 2
|
||
|
display::setColor(bg);
|
||
|
|
||
|
int pBackground = pText + wText;
|
||
|
|
||
|
if (drawLine1) {
|
||
|
if (pBackground <= pLine1) {
|
||
|
if (pBackground < pLine1) {
|
||
|
display::fillRect(x + pBackground, y, x + pLine1 - 1, y + h - 1);
|
||
|
}
|
||
|
pBackground = pLine1 + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (drawLine2) {
|
||
|
if (pBackground <= pLine2) {
|
||
|
if (pBackground < pLine2) {
|
||
|
display::fillRect(x + pBackground, y, x + pLine2 - 1, y + h - 1);
|
||
|
}
|
||
|
pBackground = pLine2 + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pBackground < d) {
|
||
|
display::fillRect(x + pBackground, y, x + d - 1, y + h - 1);
|
||
|
}
|
||
|
} else {
|
||
|
x += w - 1;
|
||
|
|
||
|
// draw bar
|
||
|
if (pText > 0) {
|
||
|
display::setColor(fg);
|
||
|
display::fillRect(x - (pText - 1), y, x, y + h - 1);
|
||
|
}
|
||
|
|
||
|
if (!(widget->orientation & BAR_GRAPH_DO_NOT_DISPLAY_VALUE)) {
|
||
|
drawText(valueText, -1, x - (pText + wText - 1), y, wText, h, &textStyle);
|
||
|
}
|
||
|
|
||
|
// draw background, but do not draw over line 1 and line 2
|
||
|
display::setColor(bg);
|
||
|
|
||
|
int pBackground = pText + wText;
|
||
|
|
||
|
if (drawLine1) {
|
||
|
if (pBackground <= pLine1) {
|
||
|
if (pBackground < pLine1) {
|
||
|
display::fillRect(x - (pLine1 - 1), y, x - pBackground, y + h - 1);
|
||
|
}
|
||
|
pBackground = pLine1 + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (drawLine2) {
|
||
|
if (pBackground <= pLine2) {
|
||
|
if (pBackground < pLine2) {
|
||
|
display::fillRect(x - (pLine2 - 1), y, x - pBackground, y + h - 1);
|
||
|
}
|
||
|
pBackground = pLine2 + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pBackground < d) {
|
||
|
display::fillRect(x - (d - 1), y, x - pBackground, y + h - 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (drawLine1) {
|
||
|
drawLineInBarGraphWidget(widget, pLine1, widget->line1Style, x, y, w, h);
|
||
|
}
|
||
|
if (drawLine2) {
|
||
|
drawLineInBarGraphWidget(widget, pLine2, widget->line2Style, x, y, w, h);
|
||
|
}
|
||
|
} else {
|
||
|
// calc text position
|
||
|
char valueText[64];
|
||
|
int pText = 0;
|
||
|
int hText = 0;
|
||
|
if (widget->textStyle) {
|
||
|
font::Font font = styleGetFont(&textStyle);
|
||
|
|
||
|
textData.toText(valueText, sizeof(valueText));
|
||
|
hText = font.getHeight();
|
||
|
|
||
|
int padding = textStyle.paddingTop;
|
||
|
hText += padding;
|
||
|
|
||
|
if (pValue + hText <= d) {
|
||
|
textStyle.backgroundColor = bg;
|
||
|
pText = pValue;
|
||
|
} else {
|
||
|
textStyle.backgroundColor = fg;
|
||
|
textStyle.color = textStyle.activeColor;
|
||
|
hText += padding;
|
||
|
pText = MAX(pValue - hText, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((widget->orientation & BAR_GRAPH_ORIENTATION_MASK) == BAR_GRAPH_ORIENTATION_TOP_BOTTOM) {
|
||
|
// draw bar
|
||
|
if (pText > 0) {
|
||
|
display::setColor(fg);
|
||
|
display::fillRect(x, y, x + w - 1, y + pText - 1);
|
||
|
}
|
||
|
|
||
|
drawText(valueText, -1, x, y + pText, w, hText, &textStyle);
|
||
|
|
||
|
// draw background, but do not draw over line 1 and line 2
|
||
|
display::setColor(bg);
|
||
|
|
||
|
int pBackground = pText + hText;
|
||
|
|
||
|
if (drawLine1) {
|
||
|
if (pBackground <= pLine1) {
|
||
|
if (pBackground < pLine1) {
|
||
|
display::fillRect(x, y + pBackground, x + w - 1, y + pLine1 - 1);
|
||
|
}
|
||
|
pBackground = pLine1 + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (drawLine2) {
|
||
|
if (pBackground <= pLine2) {
|
||
|
if (pBackground < pLine2) {
|
||
|
display::fillRect(x, y + pBackground, x + w - 1, y + pLine2 - 1);
|
||
|
}
|
||
|
pBackground = pLine2 + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pBackground < d) {
|
||
|
display::fillRect(x, y + pBackground, x + w - 1, y + d - 1);
|
||
|
}
|
||
|
} else {
|
||
|
y += h - 1;
|
||
|
|
||
|
// draw bar
|
||
|
if (pText > 0) {
|
||
|
display::setColor(fg);
|
||
|
display::fillRect(x, y - (pText - 1), x + w - 1, y);
|
||
|
}
|
||
|
|
||
|
drawText(valueText, -1, x, y - (pText + hText - 1), w, hText, &textStyle);
|
||
|
|
||
|
// draw background, but do not draw over line 1 and line 2
|
||
|
display::setColor(bg);
|
||
|
|
||
|
int pBackground = pText + hText;
|
||
|
|
||
|
if (drawLine1) {
|
||
|
if (pBackground <= pLine1) {
|
||
|
if (pBackground < pLine1) {
|
||
|
display::fillRect(x, y - (pLine1 - 1), x + w - 1, y - pBackground);
|
||
|
}
|
||
|
pBackground = pLine1 + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (drawLine2) {
|
||
|
if (pBackground <= pLine2) {
|
||
|
if (pBackground < pLine2) {
|
||
|
display::fillRect(x, y - (pLine2 - 1), x + w - 1, y - (pBackground));
|
||
|
}
|
||
|
pBackground = pLine2 + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pBackground < d) {
|
||
|
display::fillRect(x, y - (d - 1), x + w - 1, y - pBackground);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (drawLine1) {
|
||
|
drawLineInBarGraphWidget(widget, pLine1, widget->line1Style, x, y, w, h);
|
||
|
}
|
||
|
if (drawLine2) {
|
||
|
drawLineInBarGraphWidget(widget, pLine2, widget->line2Style, x, y, w, h);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace gui
|
||
|
} // namespace eez
|