mirror of
https://github.com/mehah/otclient
synced 2026-08-15 16:29:06 -04:00
improve: UIGraph with new features by oen (#984)
UIGraph widget got massive update including: Multiple graphs in a single widget Showing value of a closest point on a line when widget is hovered Better performance due to A LOT of caching author: Oen
This commit is contained in:
parent
0700333f63
commit
5c5dc582a9
6 changed files with 477 additions and 93 deletions
|
|
@ -467,6 +467,7 @@ Beyond of it's flexibility with scripts, otclient comes with tons of other featu
|
|||
- Module Shop
|
||||
- Module Oufit
|
||||
- Placeholder
|
||||
- UIGraph
|
||||
|
||||
## <a name="themobileproject"><img height="32" src="https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/android/android.png" alt="Android"> The Mobile Project </a>
|
||||
The Mobile Project
|
||||
|
|
|
|||
|
|
@ -133,7 +133,12 @@ local toggle = function()
|
|||
end
|
||||
|
||||
local drawGraph = function(graph, value)
|
||||
graph:addValue(value)
|
||||
if graph:getGraphsCount() == 0 then
|
||||
graph:createGraph()
|
||||
graph:setLineWidth(1, 1)
|
||||
graph:setLineColor(1, "#FF0000")
|
||||
end
|
||||
graph:addValue(1, value)
|
||||
end
|
||||
|
||||
local toggleAnalyzer = function(window)
|
||||
|
|
|
|||
|
|
@ -226,8 +226,6 @@ AnalyzerLootItem < UIItem
|
|||
AnalyzerGraph < UIGraph
|
||||
height: 140
|
||||
capacity: 400
|
||||
line-width: 1
|
||||
color: red
|
||||
margin-top: 5
|
||||
margin-left: 5
|
||||
margin-right: 5
|
||||
|
|
|
|||
|
|
@ -1063,13 +1063,21 @@ void Client::registerLuaFunctions()
|
|||
g_lua.bindClassMemberFunction<UIProgressRect>("getPercent", &UIProgressRect::getPercent);
|
||||
|
||||
g_lua.registerClass<UIGraph, UIWidget>();
|
||||
g_lua.bindClassStaticFunction<UIGraph>("create", [] { return UIGraphPtr(new UIGraph); });
|
||||
g_lua.bindClassMemberFunction<UIGraph>("addValue", &UIGraph::addValue);
|
||||
g_lua.bindClassStaticFunction<UIGraph>("create", [] { return std::make_shared<UIGraph>(); });
|
||||
g_lua.bindClassMemberFunction<UIGraph>("clear", &UIGraph::clear);
|
||||
g_lua.bindClassMemberFunction<UIGraph>("setLineWidth", &UIGraph::setLineWidth);
|
||||
g_lua.bindClassMemberFunction<UIGraph>("createGraph", &UIGraph::createGraph);
|
||||
g_lua.bindClassMemberFunction<UIGraph>("getGraphsCount", &UIGraph::getGraphsCount);
|
||||
g_lua.bindClassMemberFunction<UIGraph>("addValue", &UIGraph::addValue);
|
||||
g_lua.bindClassMemberFunction<UIGraph>("setCapacity", &UIGraph::setCapacity);
|
||||
g_lua.bindClassMemberFunction<UIGraph>("setTitle", &UIGraph::setTitle);
|
||||
g_lua.bindClassMemberFunction<UIGraph>("setShowLabels", &UIGraph::setShowLabels);
|
||||
g_lua.bindClassMemberFunction<UIGraph>("setShowInfo", &UIGraph::setShowInfo);
|
||||
g_lua.bindClassMemberFunction<UIGraph>("setLineWidth", &UIGraph::setLineWidth);
|
||||
g_lua.bindClassMemberFunction<UIGraph>("setLineColor", &UIGraph::setLineColor);
|
||||
g_lua.bindClassMemberFunction<UIGraph>("setInfoText", &UIGraph::setInfoText);
|
||||
g_lua.bindClassMemberFunction<UIGraph>("setInfoLineColor", &UIGraph::setInfoLineColor);
|
||||
g_lua.bindClassMemberFunction<UIGraph>("setTextBackground", &UIGraph::setTextBackground);
|
||||
g_lua.bindClassMemberFunction<UIGraph>("setGraphVisible", &UIGraph::setGraphVisible);
|
||||
|
||||
g_lua.registerClass<UIMapAnchorLayout, UIAnchorLayout>();
|
||||
}
|
||||
|
|
@ -1,93 +1,428 @@
|
|||
#include "uigraph.h"
|
||||
#include <framework/graphics/fontmanager.h>
|
||||
#include <framework/core/eventdispatcher.h>
|
||||
#include <framework/graphics/drawpoolmanager.h>
|
||||
#include <framework/platform/platformwindow.h>
|
||||
|
||||
void UIGraph::drawSelf(DrawPoolType drawPane)
|
||||
{
|
||||
if (drawPane != DrawPoolType::FOREGROUND)
|
||||
return;
|
||||
if (drawPane != DrawPoolType::FOREGROUND)
|
||||
return;
|
||||
|
||||
UIWidget::drawSelf(drawPane);
|
||||
if (m_backgroundColor.aF() > Fw::MIN_ALPHA) {
|
||||
Rect backgroundDestRect = m_rect;
|
||||
backgroundDestRect.expand(-m_borderWidth.top, -m_borderWidth.right, -m_borderWidth.bottom, -m_borderWidth.left);
|
||||
drawBackground(m_rect);
|
||||
}
|
||||
|
||||
Rect dest = getPaddingRect();
|
||||
drawImage(m_rect);
|
||||
|
||||
float offsetX = dest.left();
|
||||
float offsetY = dest.top();
|
||||
size_t elements = std::min<size_t>(m_values.size(), dest.width() / (m_width * 2) - 1);
|
||||
size_t start = m_values.size() - elements;
|
||||
int minVal = 0xFFFFFF, maxVal = -0xFFFFFF;
|
||||
for (size_t i = start; i < m_values.size(); ++i) {
|
||||
if (minVal > m_values[i])
|
||||
minVal = m_values[i];
|
||||
if (maxVal < m_values[i])
|
||||
maxVal = m_values[i];
|
||||
}
|
||||
// round
|
||||
maxVal = (1 + maxVal / 10) * 10;
|
||||
minVal = (minVal / 10) * 10;
|
||||
float step = (float)(dest.height()) / std::max<float>(1, (maxVal - minVal));
|
||||
std::vector<Point> points;
|
||||
for (size_t i = start, j = 0; i < m_values.size(); ++i) {
|
||||
points.push_back(Point(offsetX + j * m_width, offsetY + 1 + (maxVal - m_values[i]) * step));
|
||||
j += 2;
|
||||
}
|
||||
if (m_needsUpdate) {
|
||||
cacheGraphs();
|
||||
}
|
||||
|
||||
if (elements > 0) {
|
||||
g_drawPool.addAction([points = std::move(points), width = m_width, color = m_color] {
|
||||
static std::vector<float> vertices(1024, 0);
|
||||
if (vertices.size() < points.size())
|
||||
vertices.resize(points.size());
|
||||
if (!m_graphs.empty()) {
|
||||
Rect dest = getPaddingRect();
|
||||
|
||||
int i = 0;
|
||||
for (const auto& point : points) {
|
||||
vertices[i++] = point.x;
|
||||
vertices[i++] = point.y;
|
||||
}
|
||||
// draw graph first
|
||||
for (auto& graph : m_graphs) {
|
||||
if (!graph.visible) continue;
|
||||
|
||||
g_painter->setColor(color);
|
||||
g_painter->drawLine(vertices, i / 2, width);
|
||||
});
|
||||
g_drawPool.addAction([points = graph.points, width = graph.width, color = graph.lineColor] {
|
||||
static std::vector<float> vertices;
|
||||
vertices.resize(points.size() * 2);
|
||||
int i = 0;
|
||||
for (const auto& point : points) {
|
||||
vertices[i++] = static_cast<float>(point.x);
|
||||
vertices[i++] = static_cast<float>(point.y);
|
||||
}
|
||||
g_painter->setColor(color);
|
||||
g_painter->drawLine(vertices, points.size(), width);
|
||||
});
|
||||
}
|
||||
|
||||
if (!m_title.empty())
|
||||
m_font->drawText(m_title, dest, Color::white, Fw::AlignTopCenter);
|
||||
if (m_showLabes) {
|
||||
m_font->drawText(std::to_string(m_values.back()), dest, Color::white, Fw::AlignTopRight);
|
||||
m_font->drawText(std::to_string(maxVal), dest, Color::white, Fw::AlignTopLeft);
|
||||
m_font->drawText(std::to_string(minVal), dest, Color::white, Fw::AlignBottomLeft);
|
||||
}
|
||||
}
|
||||
// then update if needed and draw vertical line if hovered
|
||||
bool updated = false;
|
||||
for (auto& graph : m_graphs) {
|
||||
if (!graph.visible) continue;
|
||||
|
||||
if (m_showInfo && isHovered()) {
|
||||
updateGraph(graph, updated);
|
||||
g_drawPool.addAction([line = graph.infoLine, color = graph.infoLineColor] {
|
||||
g_painter->setColor(color);
|
||||
std::vector<float> vertices = {
|
||||
static_cast<float>(line[0].x), static_cast<float>(line[0].y),
|
||||
static_cast<float>(line[1].x), static_cast<float>(line[1].y)
|
||||
};
|
||||
g_painter->drawLine(vertices, vertices.size() / 2, 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// reposition intersecting rects and keep them within rect bounds
|
||||
if (updated) {
|
||||
updateInfoBoxes();
|
||||
}
|
||||
|
||||
// now we draw indicators on the graph lines
|
||||
for (auto& graph : m_graphs) {
|
||||
if (!graph.visible) continue;
|
||||
|
||||
if (m_showInfo && isHovered()) {
|
||||
g_drawPool.addFilledRect(graph.infoIndicatorBg, Color::white);
|
||||
g_drawPool.addFilledRect(graph.infoIndicator, graph.lineColor);
|
||||
}
|
||||
}
|
||||
|
||||
// lastly we can draw info boxes with value
|
||||
for (auto& graph : m_graphs) {
|
||||
if (!graph.visible) continue;
|
||||
|
||||
if (m_showInfo && isHovered()) {
|
||||
g_drawPool.addFilledRect(graph.infoRectBg, graph.infoTextBg);
|
||||
g_drawPool.addFilledRect(graph.infoRectIcon, graph.lineColor);
|
||||
m_font->drawText(graph.infoValue, graph.infoRect, Color::black, Fw::AlignLeftCenter);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_title.empty())
|
||||
m_font->drawText(m_title, dest, Color::white, Fw::AlignTopCenter);
|
||||
if (m_showLabes) {
|
||||
m_font->drawText(m_lastValue, dest, Color::white, Fw::AlignTopRight);
|
||||
m_font->drawText(m_maxValue, dest, Color::white, Fw::AlignTopLeft);
|
||||
m_font->drawText(m_minValue, dest, Color::white, Fw::AlignBottomLeft);
|
||||
}
|
||||
}
|
||||
|
||||
drawBorder(m_rect);
|
||||
drawIcon(m_rect);
|
||||
drawText(m_rect);
|
||||
}
|
||||
|
||||
void UIGraph::clear()
|
||||
{
|
||||
m_values.clear();
|
||||
m_graphs.clear();
|
||||
}
|
||||
|
||||
void UIGraph::addValue(int value, bool ignoreSmallValues)
|
||||
void UIGraph::setLineWidth(size_t index, int width) {
|
||||
if (m_graphs.size() <= index - 1) {
|
||||
g_logger.warning(stdext::format("[UIGraph::setLineWidth (%s)] Graph of index %d out of bounds.", getId(), index));
|
||||
return;
|
||||
}
|
||||
|
||||
auto& graph = m_graphs[index - 1];
|
||||
graph.width = width;
|
||||
m_needsUpdate = true;
|
||||
}
|
||||
|
||||
size_t UIGraph::createGraph()
|
||||
{
|
||||
if (ignoreSmallValues) {
|
||||
if (!m_values.empty() && m_values.back() <= 2 && value <= 2 && ++m_ignores <= 10)
|
||||
return;
|
||||
m_ignores = 0;
|
||||
}
|
||||
m_values.push_back(value);
|
||||
while (m_values.size() > m_capacity)
|
||||
m_values.pop_front();
|
||||
auto graph = Graph();
|
||||
|
||||
graph.points = {};
|
||||
graph.values = {};
|
||||
|
||||
graph.infoLine[0] = Point();
|
||||
graph.infoLine[1] = Point();
|
||||
|
||||
graph.originalInfoRect = Rect();
|
||||
graph.infoRect = Rect();
|
||||
graph.infoRectBg = Rect();
|
||||
graph.infoRectIcon = Rect(0, 0, 10, 10);
|
||||
graph.infoIndicator = Rect(0, 0, 5, 5);
|
||||
graph.infoIndicatorBg = Rect(0, 0, 7, 7);
|
||||
|
||||
graph.infoText = "Value: ";
|
||||
|
||||
graph.infoLineColor = Color::white;
|
||||
graph.infoTextBg = Color(0, 0, 0, 100);
|
||||
graph.lineColor = Color::white;
|
||||
|
||||
graph.width = 1;
|
||||
graph.infoIndex = -1;
|
||||
|
||||
graph.visible = true;
|
||||
|
||||
m_graphs.push_back(graph);
|
||||
return m_graphs.size();
|
||||
}
|
||||
|
||||
void UIGraph::addValue(size_t index, int value, bool ignoreSmallValues)
|
||||
{
|
||||
if (m_graphs.size() <= index - 1) {
|
||||
g_logger.warning(stdext::format(
|
||||
"[UIGraph::addValue (%s)] Graph of index %d out of bounds. Use:\n"
|
||||
" if graph:getGraphsCount() == 0 then\n"
|
||||
" graph:createGraph()\n"
|
||||
" graph:setLineWidth(1, 1)\n"
|
||||
" graph:setLineColor(1, \"#FF0000\")\n"
|
||||
" end\n"
|
||||
" graph:addValue(1,value)",
|
||||
getId(), index));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto& graph = m_graphs[index - 1];
|
||||
|
||||
if (ignoreSmallValues) {
|
||||
if (!graph.values.empty() && graph.values.back() <= 2 && value <= 2 && ++m_ignores <= 10)
|
||||
return;
|
||||
m_ignores = 0;
|
||||
}
|
||||
|
||||
graph.values.push_back(value);
|
||||
while (graph.values.size() > m_capacity)
|
||||
graph.values.pop_front();
|
||||
|
||||
m_needsUpdate = true;
|
||||
}
|
||||
|
||||
void UIGraph::setLineColor(size_t index, const Color& color)
|
||||
{
|
||||
if (m_graphs.size() <= index - 1) {
|
||||
g_logger.warning(stdext::format("[UIGraph::setLineColor (%s)] Graph of index %d out of bounds.", getId(), index));
|
||||
return;
|
||||
}
|
||||
|
||||
auto& graph = m_graphs[index - 1];
|
||||
graph.lineColor = color;
|
||||
}
|
||||
|
||||
void UIGraph::setInfoText(size_t index, const std::string& text)
|
||||
{
|
||||
if (m_graphs.size() <= index - 1) {
|
||||
g_logger.warning(stdext::format("[UIGraph::setInfoText (%s)] Graph of index %d out of bounds.", getId(), index));
|
||||
return;
|
||||
}
|
||||
|
||||
auto& graph = m_graphs[index - 1];
|
||||
graph.infoText = text;
|
||||
}
|
||||
|
||||
void UIGraph::setGraphVisible(size_t index, bool visible)
|
||||
{
|
||||
if (m_graphs.size() <= index - 1) {
|
||||
g_logger.warning(stdext::format("[UIGraph::setGraphVisible (%s)] Graph of index %d out of bounds.", getId(), index));
|
||||
return;
|
||||
}
|
||||
|
||||
auto& graph = m_graphs[index - 1];
|
||||
graph.visible = visible;
|
||||
}
|
||||
|
||||
void UIGraph::setInfoLineColor(size_t index, const Color& color)
|
||||
{
|
||||
if (m_graphs.size() <= index - 1) {
|
||||
g_logger.warning(stdext::format("[UIGraph::setInfoLineColor (%s)] Graph of index %d out of bounds.", getId(), index));
|
||||
return;
|
||||
}
|
||||
|
||||
auto& graph = m_graphs[index - 1];
|
||||
graph.infoLineColor = color;
|
||||
}
|
||||
|
||||
void UIGraph::setTextBackground(size_t index, const Color& color)
|
||||
{
|
||||
if (m_graphs.size() <= index - 1) {
|
||||
g_logger.warning(stdext::format("[UIGraph::setTextBackground (%s)] Graph of index %d out of bounds.", getId(), index));
|
||||
return;
|
||||
}
|
||||
|
||||
auto& graph = m_graphs[index - 1];
|
||||
graph.infoTextBg = color;
|
||||
}
|
||||
|
||||
void UIGraph::cacheGraphs()
|
||||
{
|
||||
if (!m_needsUpdate)
|
||||
return;
|
||||
|
||||
if (!m_rect.isEmpty() && m_rect.isValid()) {
|
||||
if (!m_graphs.empty()) {
|
||||
Rect rect = getPaddingRect();
|
||||
|
||||
float paddingX = static_cast<float>(rect.x());
|
||||
float paddingY = static_cast<float>(rect.y());
|
||||
float graphWidth = static_cast<float>(rect.width());
|
||||
float graphHeight = static_cast<float>(rect.height());
|
||||
|
||||
float minValue = 0.0f;
|
||||
float maxValue = 0.0f;
|
||||
for (auto& graph : m_graphs) {
|
||||
if (graph.values.empty())
|
||||
continue;
|
||||
|
||||
graph.points.clear();
|
||||
|
||||
auto [minValueIter, maxValueIter] = std::minmax_element(graph.values.begin(), graph.values.end());
|
||||
minValue = *minValueIter;
|
||||
maxValue = *maxValueIter;
|
||||
float range = maxValue - minValue;
|
||||
if (range == 0.0f)
|
||||
range = 1.0f;
|
||||
|
||||
float pointSpacing = graphWidth / std::max<int>(static_cast<int>(graph.values.size()) - 1, 1);
|
||||
for (size_t i = 0; i < graph.values.size(); ++i) {
|
||||
float x = paddingX + i * pointSpacing;
|
||||
float y = paddingY + graphHeight - ((graph.values[i] - minValue) / range) * graphHeight;
|
||||
graph.points.push_back({ static_cast<int>(x), static_cast<int>(y) });
|
||||
}
|
||||
}
|
||||
|
||||
m_minValue = std::to_string(static_cast<int>(minValue));
|
||||
m_maxValue = std::to_string(static_cast<int>(maxValue));
|
||||
if (!m_graphs[0].values.empty())
|
||||
m_lastValue = std::to_string(m_graphs[0].values.back());
|
||||
else
|
||||
m_lastValue = "0";
|
||||
}
|
||||
|
||||
m_needsUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
void UIGraph::updateGraph(Graph& graph, bool& updated)
|
||||
{
|
||||
if (graph.values.empty())
|
||||
return;
|
||||
|
||||
auto dest = getPaddingRect();
|
||||
auto mousePos = g_window.getMousePosition();
|
||||
float graphWidth = static_cast<float>(dest.width());
|
||||
float graphHeight = static_cast<float>(dest.height());
|
||||
float pointSpacing = graphWidth / std::max<int>(static_cast<int>(graph.values.size()) - 1, 1);
|
||||
|
||||
int dataIndex = static_cast<int>((mousePos.x - dest.left()) / pointSpacing + 0.5f);
|
||||
dataIndex = std::clamp(dataIndex, 0, static_cast<int>(graph.values.size()) - 1);
|
||||
|
||||
if (graph.infoIndex != dataIndex) {
|
||||
graph.infoIndex = dataIndex;
|
||||
|
||||
float snappedX = dest.left() + dataIndex * pointSpacing;
|
||||
int value = graph.values[graph.infoIndex];
|
||||
|
||||
graph.infoLine[0] = Point(snappedX, dest.top());
|
||||
graph.infoLine[1] = Point(snappedX, dest.bottom());
|
||||
|
||||
graph.infoValue = stdext::format("%s %d", graph.infoText, value);
|
||||
|
||||
auto [minValueIter, maxValueIter] = std::minmax_element(graph.values.begin(), graph.values.end());
|
||||
float minValue = static_cast<float>(*minValueIter);
|
||||
float maxValue = static_cast<float>(*maxValueIter);
|
||||
float range = maxValue - minValue;
|
||||
if (range == 0.0f)
|
||||
range = 1.0f;
|
||||
|
||||
float pointY = dest.top() + graphHeight - ((value - minValue) / range) * graphHeight;
|
||||
|
||||
auto textSize = m_font->calculateTextRectSize(graph.infoValue);
|
||||
graph.infoRectBg.setWidth(textSize.width() + 16);
|
||||
graph.infoRectBg.setHeight(textSize.height());
|
||||
graph.infoRectBg.expand(4);
|
||||
graph.infoRectBg.moveTop(pointY - graph.infoRectBg.height() / 2.0);
|
||||
if (snappedX >= dest.horizontalCenter())
|
||||
graph.infoRectBg.moveRight(snappedX - 10);
|
||||
else
|
||||
graph.infoRectBg.moveLeft(snappedX + 10);
|
||||
|
||||
graph.infoRect.setWidth(textSize.width());
|
||||
graph.infoRect.setHeight(textSize.height());
|
||||
graph.infoRect.moveRight(graph.infoRectBg.right() - 4);
|
||||
graph.infoRect.moveVerticalCenter(graph.infoRectBg.verticalCenter());
|
||||
|
||||
int iconPadding = graph.infoRectBg.height() - graph.infoRectIcon.width();
|
||||
graph.infoRectIcon.moveLeft(graph.infoRectBg.left() + (iconPadding / 2.0));
|
||||
graph.infoRectIcon.moveVerticalCenter(graph.infoRectBg.verticalCenter());
|
||||
|
||||
graph.infoIndicator.moveLeft(snappedX - 3);
|
||||
graph.infoIndicator.moveTop(pointY - 3);
|
||||
graph.infoIndicatorBg.moveCenter(graph.infoIndicator.center());
|
||||
|
||||
graph.originalInfoRect = graph.infoRectBg;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
void UIGraph::updateInfoBoxes()
|
||||
{
|
||||
auto dest = getPaddingRect();
|
||||
std::vector<Rect> occupiedSpaces(m_graphs.size());
|
||||
for (size_t i = 0; i < m_graphs.size(); ++i) {
|
||||
auto& graph = m_graphs[i];
|
||||
|
||||
graph.infoRectBg = graph.originalInfoRect;
|
||||
graph.infoRect.moveVerticalCenter(graph.infoRectBg.verticalCenter());
|
||||
graph.infoRectIcon.moveVerticalCenter(graph.infoRectBg.verticalCenter());
|
||||
|
||||
occupiedSpaces[i] = graph.infoRectBg;
|
||||
|
||||
for (size_t j = 0; j < occupiedSpaces.size(); ++j) {
|
||||
if (i == j) continue; // graph's space, ignore
|
||||
|
||||
auto& space = occupiedSpaces[j];
|
||||
// first check if this graph occupies another graph's space and move it above the space
|
||||
if (space.intersects(graph.infoRectBg)) {
|
||||
graph.infoRectBg.moveBottom(space.top() - 2);
|
||||
graph.infoRect.moveVerticalCenter(graph.infoRectBg.verticalCenter());
|
||||
graph.infoRectIcon.moveVerticalCenter(graph.infoRectBg.verticalCenter());
|
||||
}
|
||||
|
||||
// lets make sure its within bounds of this widget
|
||||
if (graph.infoRectBg.top() < dest.top()) {
|
||||
graph.infoRectBg.moveTop(dest.top());
|
||||
graph.infoRect.moveVerticalCenter(graph.infoRectBg.verticalCenter());
|
||||
graph.infoRectIcon.moveVerticalCenter(graph.infoRectBg.verticalCenter());
|
||||
}
|
||||
|
||||
// if we just moved due to bounds check, we have to make sure we are not occuping another graph
|
||||
// this time move it below the occupied space
|
||||
if (space.intersects(graph.infoRectBg)) {
|
||||
graph.infoRectBg.moveTop(space.bottom() + 2);
|
||||
graph.infoRect.moveVerticalCenter(graph.infoRectBg.verticalCenter());
|
||||
graph.infoRectIcon.moveVerticalCenter(graph.infoRectBg.verticalCenter());
|
||||
}
|
||||
|
||||
// and check again if we are within bounds
|
||||
if (graph.infoRectBg.bottom() > dest.bottom()) {
|
||||
graph.infoRectBg.moveBottom(dest.bottom());
|
||||
graph.infoRect.moveVerticalCenter(graph.infoRectBg.verticalCenter());
|
||||
graph.infoRectIcon.moveVerticalCenter(graph.infoRectBg.verticalCenter());
|
||||
}
|
||||
}
|
||||
|
||||
occupiedSpaces[i] = graph.infoRectBg;
|
||||
}
|
||||
}
|
||||
|
||||
void UIGraph::onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode)
|
||||
{
|
||||
UIWidget::onStyleApply(styleName, styleNode);
|
||||
UIWidget::onStyleApply(styleName, styleNode);
|
||||
|
||||
for (const OTMLNodePtr& node : styleNode->children()) {
|
||||
if (node->tag() == "line-width")
|
||||
setLineWidth(node->value<int>());
|
||||
else if (node->tag() == "capacity")
|
||||
setCapacity(node->value<int>());
|
||||
else if (node->tag() == "title")
|
||||
setTitle(node->value<std::string>());
|
||||
else if (node->tag() == "show-labels")
|
||||
setShowLabels(node->value<bool>());
|
||||
}
|
||||
for (const OTMLNodePtr& node : styleNode->children()) {
|
||||
if (node->tag() == "capacity")
|
||||
setCapacity(node->value<int>());
|
||||
else if (node->tag() == "title")
|
||||
setTitle(node->value());
|
||||
else if (node->tag() == "show-labels")
|
||||
setShowLabels(node->value<bool>());
|
||||
else if (node->tag() == "show-info") // draw info (vertical line, labels with values) on mouse position
|
||||
setShowInfo(node->value<bool>());
|
||||
}
|
||||
}
|
||||
|
||||
void UIGraph::onGeometryChange(const Rect& oldRect, const Rect& newRect)
|
||||
{
|
||||
UIWidget::onGeometryChange(oldRect, newRect);
|
||||
m_needsUpdate = true;
|
||||
}
|
||||
|
||||
void UIGraph::onLayoutUpdate()
|
||||
{
|
||||
UIWidget::onLayoutUpdate();
|
||||
m_needsUpdate = true;
|
||||
}
|
||||
|
||||
void UIGraph::onVisibilityChange(bool visible)
|
||||
{
|
||||
UIWidget::onVisibilityChange(visible);
|
||||
m_needsUpdate = visible;
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2022 OTClient <https://github.com/edubart/otclient>
|
||||
* Copyright (c) 2010-2025 OTClient <https://github.com/edubart/otclient>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
@ -25,6 +25,27 @@
|
|||
#include "declarations.h"
|
||||
#include <framework/ui/uiwidget.h>
|
||||
|
||||
struct Graph
|
||||
{
|
||||
std::vector<Point> points;
|
||||
std::deque<int> values;
|
||||
Point infoLine[2];
|
||||
Rect originalInfoRect;
|
||||
Rect infoRect;
|
||||
Rect infoRectBg;
|
||||
Rect infoRectIcon;
|
||||
Rect infoIndicator;
|
||||
Rect infoIndicatorBg;
|
||||
std::string infoValue;
|
||||
std::string infoText;
|
||||
Color infoLineColor;
|
||||
Color infoTextBg;
|
||||
Color lineColor;
|
||||
int width;
|
||||
int infoIndex;
|
||||
bool visible;
|
||||
};
|
||||
|
||||
class UIGraph : public UIWidget
|
||||
{
|
||||
public:
|
||||
|
|
@ -33,32 +54,48 @@ public:
|
|||
void drawSelf(DrawPoolType drawPane);
|
||||
|
||||
void clear();
|
||||
void addValue(int value, bool ignoreSmallValues = false);
|
||||
void setLineWidth(int width)
|
||||
{
|
||||
m_width = width;
|
||||
}
|
||||
void setCapacity(int capacity)
|
||||
{
|
||||
size_t createGraph();
|
||||
size_t getGraphsCount() { return m_graphs.size(); }
|
||||
void addValue(size_t index, int value, bool ignoreSmallValues = false);
|
||||
|
||||
void setCapacity(int capacity) {
|
||||
m_capacity = capacity;
|
||||
m_needsUpdate = true;
|
||||
}
|
||||
void setTitle(const std::string& title)
|
||||
{
|
||||
m_title = title;
|
||||
}
|
||||
void setShowLabels(bool value)
|
||||
{
|
||||
m_showLabes = value;
|
||||
}
|
||||
void setTitle(const std::string& title) { m_title = title; }
|
||||
void setShowLabels(bool value) { m_showLabes = value; }
|
||||
void setShowInfo(bool value) { m_showInfo = value; }
|
||||
|
||||
void setLineWidth(size_t index, int width);
|
||||
void setLineColor(size_t index, const Color& color);
|
||||
void setInfoText(size_t index, const std::string& text);
|
||||
void setInfoLineColor(size_t index, const Color& color);
|
||||
void setTextBackground(size_t index, const Color& color);
|
||||
void setGraphVisible(size_t index, bool visible);
|
||||
|
||||
protected:
|
||||
void onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode);
|
||||
void onGeometryChange(const Rect& oldRect, const Rect& newRect);
|
||||
void onLayoutUpdate();
|
||||
void onVisibilityChange(bool visible);
|
||||
|
||||
void cacheGraphs();
|
||||
void updateGraph(Graph& graph, bool& updated);
|
||||
void updateInfoBoxes();
|
||||
|
||||
private:
|
||||
// cache
|
||||
std::string m_minValue;
|
||||
std::string m_maxValue;
|
||||
std::string m_lastValue;
|
||||
std::string m_title;
|
||||
|
||||
bool m_showLabes{ true };
|
||||
bool m_showInfo{ true };
|
||||
bool m_needsUpdate{false};
|
||||
|
||||
size_t m_capacity = 100;
|
||||
size_t m_ignores = 0;
|
||||
int m_width = 1;
|
||||
bool m_showLabes = true;
|
||||
std::deque<int> m_values;
|
||||
|
||||
std::vector<Graph> m_graphs;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue