From a7a58acd1d123b00aaf4d78ccee5637f9aa36fda Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Sun, 21 Jan 2018 19:37:44 +0300 Subject: [PATCH] #31 [Gui] Big update for arbitrary values viewer. It is now viable and can be used to inspect user metrics. Currently arbitrary values viewer is built into blocks list widget. --- easy_profiler_core/chunk_allocator.h | 14 +- easy_profiler_core/thread_storage.h | 3 + profiler_gui/arbitrary_value_inspector.cpp | 756 ++++++++++++++------- profiler_gui/arbitrary_value_inspector.h | 113 ++- profiler_gui/blocks_graphics_view.cpp | 188 +++-- profiler_gui/blocks_graphics_view.h | 33 +- profiler_gui/common_functions.h | 11 + profiler_gui/common_types.h | 14 + profiler_gui/descriptors_tree_widget.cpp | 28 +- profiler_gui/descriptors_tree_widget.h | 27 +- profiler_gui/easy_graphics_scrollbar.cpp | 89 +-- profiler_gui/easy_graphics_scrollbar.h | 3 +- profiler_gui/globals.cpp | 3 +- profiler_gui/globals.h | 15 +- profiler_gui/globals_qobjects.h | 14 +- profiler_gui/graphics_image_item.cpp | 6 +- profiler_gui/graphics_image_item.h | 2 +- profiler_gui/graphics_slider_area.cpp | 95 ++- profiler_gui/graphics_slider_area.h | 19 +- profiler_gui/main_window.cpp | 32 +- profiler_gui/themes/default.css | 6 + profiler_gui/themes/default.scss | 8 + 22 files changed, 951 insertions(+), 528 deletions(-) diff --git a/easy_profiler_core/chunk_allocator.h b/easy_profiler_core/chunk_allocator.h index 47e0788..7c643fd 100644 --- a/easy_profiler_core/chunk_allocator.h +++ b/easy_profiler_core/chunk_allocator.h @@ -303,9 +303,11 @@ EASY_FORCE_INLINE T unaligned_load64(const void* ptr, T* val) ////////////////////////////////////////////////////////////////////////// -template +template class chunk_allocator { + static_assert(N != 0, "chunk_allocator N must be a positive value"); + struct chunk { EASY_ALIGNED(char, data[N], EASY_ALIGNMENT_SIZE); chunk* prev = nullptr; }; struct chunk_list @@ -380,8 +382,8 @@ class chunk_allocator }; // Used in serialize(): workaround for no constexpr support in MSVC 2013. - EASY_STATIC_CONSTEXPR int_fast32_t MAX_CHUNK_OFFSET = N - sizeof(uint16_t); - EASY_STATIC_CONSTEXPR uint16_t N_MINUS_ONE = N - 1; + EASY_STATIC_CONSTEXPR int_fast32_t MaxChunkOffset = N - sizeof(uint16_t); + EASY_STATIC_CONSTEXPR uint16_t OneBeforeN = static_cast(N - 1); chunk_list m_chunks; ///< List of chunks. chunk* m_markedChunk; ///< Chunk marked by last closed frame @@ -421,7 +423,7 @@ public: // If there is enough space for at least another payload size, // set it to zero. - if (chunkOffset < N_MINUS_ONE) + if (chunkOffset < OneBeforeN) unaligned_zero16(data + n); return data; @@ -503,7 +505,7 @@ public: isMarked = (current == m_markedChunk); const char* data = current->data; - const int_fast32_t maxOffset = isMarked ? m_markedChunkOffset : MAX_CHUNK_OFFSET; + const int_fast32_t maxOffset = isMarked ? m_markedChunkOffset : MaxChunkOffset; int_fast32_t chunkOffset = 0; // signed int so overflow is not checked. auto payloadSize = unaligned_load16(data); @@ -555,7 +557,7 @@ public: // If there is enough space for at least another payload size, // set it to zero. - if (chunkOffset < N_MINUS_ONE) + if (chunkOffset < OneBeforeN) unaligned_zero16(data + n); if (marked == m_chunks.last && chunkOffset > m_chunkOffset) diff --git a/easy_profiler_core/thread_storage.h b/easy_profiler_core/thread_storage.h index 4635855..abdf936 100644 --- a/easy_profiler_core/thread_storage.h +++ b/easy_profiler_core/thread_storage.h @@ -95,6 +95,9 @@ public: EASY_CONSTEXPR uint16_t SIZEOF_BLOCK = sizeof(profiler::BaseBlockData) + 1 + sizeof(uint16_t); // SerializedBlock stores BaseBlockData + at least 1 character for name ('\0') + 2 bytes for size of serialized data EASY_CONSTEXPR uint16_t SIZEOF_CSWITCH = sizeof(profiler::CSwitchEvent) + 1 + sizeof(uint16_t); // SerializedCSwitch also stores additional 4 bytes to be able to save 64-bit thread_id +static_assert((int)SIZEOF_BLOCK * 128 < 65536, "Chunk size for profiler::Block must be less than 65536"); +static_assert((int)SIZEOF_CSWITCH * 128 < 65536, "Chunk size for CSwitchBlock must be less than 65536"); + struct ThreadStorage EASY_FINAL { StackBuffer nonscopedBlocks; diff --git a/profiler_gui/arbitrary_value_inspector.cpp b/profiler_gui/arbitrary_value_inspector.cpp index 2655827..1cef5cc 100644 --- a/profiler_gui/arbitrary_value_inspector.cpp +++ b/profiler_gui/arbitrary_value_inspector.cpp @@ -57,8 +57,10 @@ #include #include #include +#include #include #include +#include #include #include "arbitrary_value_inspector.h" #include "treeview_first_column_delegate.h" @@ -387,269 +389,504 @@ QPointF ArbitraryValuesCollection::point(const profiler::ArbitraryValue& _value) ////////////////////////////////////////////////////////////////////////// -EasyArbitraryValuesChartItem::EasyArbitraryValuesChartItem() - : Parent(nullptr) +ArbitraryValuesChartItem::ArbitraryValuesChartItem() + : Parent() + , m_workerMaxValue(0) + , m_workerMinValue(0) { } -EasyArbitraryValuesChartItem::~EasyArbitraryValuesChartItem() +ArbitraryValuesChartItem::~ArbitraryValuesChartItem() { } -void EasyArbitraryValuesChartItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) +void ArbitraryValuesChartItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) { - if (m_collections.empty()) - return; - - const auto& chart = *reinterpret_cast(scene()->parent()); - const auto scale = chart.xscale(); - - qreal minValue = 1e300, maxValue = -1e300; - for (const auto& c : m_collections) - { - const auto& collection = *c.ptr; - if (minValue > collection.minValue()) - minValue = collection.minValue(); - if (maxValue < collection.maxValue()) - maxValue = collection.maxValue(); - } - - const qreal height = std::max(maxValue - minValue, 1.); - - auto r = scene()->sceneRect(); - + const auto widget = static_cast(scene()->parent()); + const auto currentScale = widget->getWindowScale(); + const bool bindMode = widget->bindMode(); + const auto bottom = m_boundingRect.bottom(); + const auto width = m_boundingRect.width() * currentScale; _painter->save(); + _painter->setTransform(QTransform::fromScale(1.0 / currentScale, 1), true); - for (const auto& c : m_collections) + if (!bindMode) + paintImage(_painter); + else + paintImage(_painter, currentScale, widget->minimum(), widget->maximum(), widget->value(), widget->sliderWidth()); + + const auto font_h = widget->fontHeight(); + QRectF rect(0, m_boundingRect.top() - widget->margin(), width - 3, m_boundingRect.height() + widget->margins()); + _painter->setPen(profiler_gui::TEXT_COLOR); + _painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, bindMode ? " Mode: Zoom" : " Mode: Overview"); + + if (!EASY_GLOBALS.scene.empty) { - const auto& points = c.ptr->points(); - if (points.empty()) - continue; - - if (c.selected) - { - auto pen = _painter->pen(); - pen.setColor(QColor::fromRgba(c.color)); - pen.setWidth(3); - _painter->setPen(pen); - } - else - { - _painter->setPen(QColor::fromRgba(c.color)); - } - - if (points.size() == 1) - _painter->drawPoint(points.front()); - else - { - auto gety = [&r, &minValue, &maxValue, &height] (qreal y) - { - y = maxValue - y; - y /= height; - y *= r.height() - 10; - y += r.top() + 5; - return y; - }; - - if (c.chartType == ChartType::Points) - { - for (const auto& p : points) - _painter->drawPoint(QPointF {p.x() * scale, gety(p.y())}); - } - else - { - QPointF p1 = points.front(); - for (int i = 1; i < points.size(); ++i) - { - const auto& p2 = points[i]; - _painter->drawLine(QPointF {p1.x() * scale, gety(p1.y())}, QPointF {p2.x() * scale, gety(p2.y())}); - p1 = p2; - } - } - //_painter->drawPolyline(points.data(), static_cast(points.size())); - } + const auto range = bindMode ? widget->sliderWidth() : widget->range(); + paintMouseIndicator(_painter, m_boundingRect.top(), bottom, width, m_boundingRect.height(), font_h, + widget->value(), range); } + _painter->setPen(Qt::darkGray); + _painter->drawLine(QLineF(0, bottom, width, bottom)); + _painter->drawLine(QLineF(0, m_boundingRect.top(), width, m_boundingRect.top())); + _painter->restore(); } -QRectF EasyArbitraryValuesChartItem::boundingRect() const +void ArbitraryValuesChartItem::paintMouseIndicator(QPainter* _painter, qreal _top, qreal _bottom, qreal _width, qreal _height, + int _font_h, qreal _visibleRegionLeft, qreal _visibleRegionWidth) { - return m_boundingRect; + if (_font_h == 0) + return; + + const auto x = m_mousePos.x(); + const auto y = m_mousePos.y(); + + // Horizontal + const bool visibleY = (_top < y && y < _bottom); + if (visibleY) + { + const int half_font_h = _font_h >> 1; + const auto value = m_minValue + ((_bottom - 2 - y) / (_height - 4)) * (m_maxValue - m_minValue); + const auto mouseStr = QString::number(value, 'f', 3); + const int textWidth = _painter->fontMetrics().width(mouseStr) + 3; + const QRectF rect(0, y - _font_h - 2, _width, 4 + (_font_h << 1)); + + _painter->setPen(Qt::blue); + + qreal left = 0, right = _width; + const Qt::AlignmentFlag alignment = x < textWidth ? Qt::AlignRight : Qt::AlignLeft; + + if (y > _bottom - half_font_h) + { + _painter->drawText(rect, alignment | Qt::AlignTop, mouseStr); + } + else if (y < _top + half_font_h) + { + _painter->drawText(rect, alignment | Qt::AlignBottom, mouseStr); + } + else + { + _painter->drawText(rect, alignment | Qt::AlignVCenter, mouseStr); + if (x < textWidth) + right = _width - textWidth; + else + left = textWidth; + } + + _painter->drawLine(QLineF(left, y, right, y)); + } + + // Vertical + if (0 < x && x < m_boundingRect.width()) + { + const auto value = _visibleRegionLeft + _visibleRegionWidth * x / _width; + const auto mouseStr = profiler_gui::timeStringReal(EASY_GLOBALS.time_units, value, 3); + const int textWidth = _painter->fontMetrics().width(mouseStr) + 6; + const int textWidthHalf = textWidth >> 1; + + qreal left = x - textWidthHalf; + if (x < textWidthHalf) + left = 0; + else if (x > (_width - textWidthHalf)) + left = _width - textWidth; + + if (!visibleY) + _painter->setPen(Qt::blue); + + const QRectF rect(left, _bottom + 2, textWidth, _font_h); + _painter->drawText(rect, Qt::AlignCenter, mouseStr); + _painter->drawLine(QLineF(x, _top, x, _bottom)); + } } -void EasyArbitraryValuesChartItem::setBoundingRect(const QRectF& _rect) +bool ArbitraryValuesChartItem::updateImage() { - m_boundingRect = _rect; + if (!Parent::updateImage()) + return false; + + const auto widget = static_cast(scene()->parent()); + + m_imageScaleUpdate = widget->range() / widget->sliderWidth(); + m_imageOriginUpdate = widget->bindMode() ? (widget->value() - widget->sliderWidth() * 3) : widget->minimum(); + + m_workerThread = std::thread(&This::updateImageAsync, this, m_boundingRect, widget->getWindowScale(), + widget->minimum(), widget->maximum(), widget->range(), widget->value(), widget->sliderWidth(), + widget->bindMode(), EASY_GLOBALS.begin_time, EASY_GLOBALS.auto_adjust_chart_height); + + return true; } -void EasyArbitraryValuesChartItem::setBoundingRect(qreal _left, qreal _top, qreal _width, qreal _height) +void ArbitraryValuesChartItem::onImageUpdated() { - m_boundingRect.setRect(_left, _top, _width, _height); + m_maxValue = m_workerMaxValue; + m_minValue = m_workerMinValue; } -void EasyArbitraryValuesChartItem::update(Collections _collections) +void ArbitraryValuesChartItem::updateImageAsync(QRectF _boundingRect, qreal _current_scale, qreal _minimum, qreal _maximum, qreal _range, + qreal _value, qreal _width, bool _bindMode, profiler::timestamp_t _begin_time, bool _autoAdjust) { + const auto screenWidth = _boundingRect.width() * _current_scale; + //const auto maxColumnHeight = _boundingRect.height(); + const auto viewScale = _range / _width; + + if (_bindMode) + { + m_workerImageScale = viewScale; + m_workerImageOrigin = _value - _width * 3; + m_workerImage = new QImage(screenWidth * 7 + 0.5, _boundingRect.height(), QImage::Format_ARGB32); + } + else + { + m_workerImageScale = 1; + m_workerImageOrigin = _minimum; + m_workerImage = new QImage(screenWidth + 0.5, _boundingRect.height(), QImage::Format_ARGB32); + } + + m_workerImage->fill(0); + QPainter p(m_workerImage); + p.setBrush(Qt::NoBrush); + p.setRenderHint(QPainter::Antialiasing, true); + + // Draw grid + { + auto pen = p.pen(); + pen.setColor(Qt::darkGray); + pen.setStyle(Qt::DotLine); + p.setPen(pen); + + const int left = 0; + const int top = 0; + const int w = m_workerImage->width(); + const int h = m_workerImage->height(); + + const int hlines = h / 20; + for (int i = 0; i < hlines; ++i) + { + const auto y = top + 20 + i * 20; + p.drawLine(left, y, left + w, y); + } + + const int vlines = w / 20; + for (int i = 0; i < vlines; ++i) + { + const auto x = left + 20 + i * 20; + p.drawLine(x, top, x, top + h); + } + + p.setPen(Qt::SolidLine); + } + + if (m_collections.empty() || isReady()) + { + setReady(true); + return; + } + + using LeftBounds = std::vector; + qreal realScale = _current_scale, offset = 0; + + LeftBounds leftBounds; + leftBounds.reserve(m_collections.size()); + + if (_bindMode) + { + _minimum = m_workerImageOrigin; + _maximum = m_workerImageOrigin + _width * 7; + realScale *= viewScale; + offset = _minimum * realScale; + } + + const auto right = std::min(_value + _width, _maximum); + qreal minValue = 1e300, maxValue = -1e300; + for (const auto& c : m_collections) + { + if (isReady()) + return; + + const auto& collection = *c.ptr; + const auto& points = collection.points(); + + if (points.empty()) + { + leftBounds.emplace_back(points.end()); + continue; + } + + if (_bindMode) + { + auto first = std::lower_bound(points.begin(), points.end(), _minimum, [](const QPointF& point, qreal x) + { + return point.x() < x; + }); + + if (first != points.end()) + { + if (first != points.begin()) + --first; + } + else + { + first = points.begin() + points.size() - 1; + } + + leftBounds.emplace_back(first); + + if (_autoAdjust) + { + for (auto it = first; it != points.end() && it->x() < right; ++it) + { + if (it->x() < _value) + continue; + + const auto value = it->y(); + + if (minValue > value) + minValue = value; + + if (maxValue < value) + maxValue = value; + } + + continue; + } + } + else + { + leftBounds.emplace_back(points.begin()); + } + + if (minValue > collection.minValue()) + minValue = collection.minValue(); + + if (maxValue < collection.maxValue()) + maxValue = collection.maxValue(); + } + + if (minValue > maxValue) + { + // No points + m_workerMinValue = 0; + m_workerMaxValue = 0; + setReady(true); + return; + } + + m_workerMinValue = minValue; + m_workerMaxValue = maxValue; + + if (isReady()) + return; + + const bool singleValue = fabs(maxValue - minValue) < 2 * std::numeric_limits::epsilon(); + const auto middle = _boundingRect.height() * 0.5; + + const qreal height = std::max(maxValue - minValue, 0.01); + const auto gety = [&_boundingRect, maxValue, height, singleValue, middle] (qreal y) + { + if (singleValue) + { + y = middle; + } + else + { + y = maxValue - y; + y /= height; + y *= _boundingRect.height() - 4; + y += 2; + } + + return y; + }; + + size_t i = 0; + for (const auto& c : m_collections) + { + if (isReady()) + return; + + const auto& points = c.ptr->points(); + if (points.empty()) + { + ++i; + continue; + } + + if (c.selected) + { + auto pen = p.pen(); + pen.setColor(QColor::fromRgba(c.color)); + pen.setWidth(2); + p.setPen(pen); + } + else + { + p.setPen(QColor::fromRgba(c.color)); + } + + const auto first = leftBounds[i]; + + if (c.chartType == ChartType::Points) + { + for (auto it = first; it != points.end() && it->x() < _maximum; ++it) + { + if (it->x() < _minimum) + continue; + + if (isReady()) + return; + + const qreal x = it->x() * realScale - offset; + const qreal y = gety(it->y()); + p.drawPoint(QPointF {x, y}); + } + } + else if (first != points.end() && first->x() < _maximum) + { + QPointF p1 = *first; + qreal x = p1.x() * realScale - offset; + qreal y = gety(p1.y()); + p1.setX(x); + p1.setY(y); + + auto it = first; + for (++it; it != points.end(); ++it) + { + if (isReady()) + return; + + QPointF p2 = *it; + x = p2.x() * realScale - offset; + y = gety(p2.y()); + p2.setX(x); + p2.setY(y); + + if (it->x() >= _minimum) + p.drawLine(p1, p2); + + if (it->x() >= _maximum) + break; + + p1 = p2; + } + } + + if (c.selected) + { + auto color = profiler_gui::darken(c.color, 0.65f); + if (profiler_gui::alpha(color) < 0xc0) + p.setPen(QColor::fromRgba(profiler::colors::modify_alpha32(0xc0000000, color))); + else + p.setPen(QColor::fromRgba(color)); + p.setBrush(QColor::fromRgba(0xc0ffffff)); + + qreal prevX = -offset * 2, prevY = -500; + for (auto it = first; it != points.end() && it->x() < _maximum; ++it) + { + if (it->x() < _minimum) + continue; + + if (isReady()) + return; + + const qreal x = it->x() * realScale - offset; + const qreal y = gety(it->y()); + + const auto dx = x - prevX, dy = y - prevY; + const auto delta = estd::sqr(dx) + estd::sqr(dy); + + if (delta > 25) + { + p.drawEllipse(QPointF {x, y}, 3, 3); + prevX = x; + prevY = y; + } + } + } + + ++i; + } + + setReady(true); +} + +void ArbitraryValuesChartItem::clear() +{ + cancelAnyJob(); + m_boundaryTimer.stop(); + m_collections.clear(); + m_minValue = m_maxValue = 0; +} + +void ArbitraryValuesChartItem::update(Collections _collections) +{ + cancelImageUpdate(); m_collections = std::move(_collections); + updateImage(); } -void EasyArbitraryValuesChartItem::update(const ArbitraryValuesCollection* _selected) +void ArbitraryValuesChartItem::update(const ArbitraryValuesCollection* _selected) { + cancelImageUpdate(); + for (auto& collection : m_collections) collection.selected = collection.ptr == _selected; + + updateImage(); } ////////////////////////////////////////////////////////////////////////// -EasyGraphicsChart::EasyGraphicsChart(QWidget* _parent) +GraphicsChart::GraphicsChart(QWidget* _parent) : Parent(_parent) - , m_chartItem(new EasyArbitraryValuesChartItem()) - , m_left(0) - , m_right(100) - , m_offset(0) - , m_xscale(1) - , m_visibleRegionWidth(100) - , m_bBindMode(false) + , m_chartItem(new ArbitraryValuesChartItem()) { - setCacheMode(QGraphicsView::CacheNone); - setTransformationAnchor(QGraphicsView::AnchorUnderMouse); - //setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); - setViewportUpdateMode(QGraphicsView::FullViewportUpdate); - setOptimizationFlag(QGraphicsView::DontSavePainterState, true); - - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - setContentsMargins(0, 0, 0, 0); - - setScene(new QGraphicsScene(this)); - scene()->setSceneRect(0, -250, 500, 500); + m_imageItem = m_chartItem; scene()->addItem(m_chartItem); - connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::sceneSizeChanged, - this, &This::onSceneSizeChanged, Qt::QueuedConnection); + const auto rect = scene()->sceneRect(); + m_chartItem->setPos(0, 0); + m_chartItem->setBoundingRect(0, rect.top() + margin(), scene()->width(), rect.height() - margins() - 1); - onSceneSizeChanged(); + connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::autoAdjustChartChanged, [this] + { + if (m_chartItem->isVisible()) + m_chartItem->onModeChanged(); + }); + + if (!EASY_GLOBALS.scene.empty) + { + setRange(EASY_GLOBALS.scene.left, EASY_GLOBALS.scene.right); + setSliderWidth(EASY_GLOBALS.scene.window); + setValue(EASY_GLOBALS.scene.offset); + m_slider->show(); + } + + m_chartItem->updateImage(); } -EasyGraphicsChart::~EasyGraphicsChart() +GraphicsChart::~GraphicsChart() { } -void EasyGraphicsChart::onSceneSizeChanged() +void GraphicsChart::clear() { - setRange(EASY_GLOBALS.scene_left, EASY_GLOBALS.scene_right); + m_chartItem->clear(); + Parent::clear(); } -void EasyGraphicsChart::resizeEvent(QResizeEvent* _event) -{ - auto size = _event->size(); - onWindowSizeChanged(size.width(), size.height()); - scene()->update(); -} - -void EasyGraphicsChart::clear() -{ - m_chartItem->update(Collections {}); -} - -bool EasyGraphicsChart::bindMode() const -{ - return m_bBindMode; -} - -qreal EasyGraphicsChart::xscale() const -{ - return m_xscale; -} - -qreal EasyGraphicsChart::left() const -{ - return m_left; -} - -qreal EasyGraphicsChart::right() const -{ - return m_right; -} - -qreal EasyGraphicsChart::range() const -{ - return m_right - m_left; -} - -qreal EasyGraphicsChart::offset() const -{ - return m_bBindMode ? m_offset : 0; -} - -qreal EasyGraphicsChart::region() const -{ - return m_bBindMode ? m_visibleRegionWidth : range(); -} - -void EasyGraphicsChart::setOffset(qreal _offset) -{ - m_offset = std::min(std::max(m_left, m_offset), m_right - m_visibleRegionWidth); -} - -void EasyGraphicsChart::setRange(qreal _left, qreal _right) -{ - const auto oldRange = range(); - const auto oldOffsetPart = oldRange < 1e-3 ? 0.0 : m_offset / oldRange; - - m_left = _left; - m_right = _right; - - if (m_left > m_right) - std::swap(m_left, m_right); - - const auto sceneRange = range(); - //scene()->setSceneRect(m_left, -(height() >> 1), sceneRange, height()); - //m_chartItem->setBoundingRect(scene()->sceneRect()); - - m_offset = m_left + oldOffsetPart * sceneRange; - m_visibleRegionWidth = std::min(m_visibleRegionWidth, sceneRange); - - //const auto oldXScale = m_xscale; - m_xscale = sceneRange < 1e-3 ? 1.0 : width() / sceneRange; - //scale(m_xscale / oldXScale, 1); - - scene()->update(); -} - -void EasyGraphicsChart::setRegion(qreal _visibleRegionWidth) -{ - m_visibleRegionWidth = std::min(_visibleRegionWidth, range()); - setOffset(m_offset); -} - -void EasyGraphicsChart::onWindowSizeChanged(qreal _width, qreal _height) -{ - //const auto oldXScale = m_xscale; - const auto sceneRange = range(); - - m_xscale = sceneRange < 1e-3 ? 1.0 : _width / sceneRange; - - scene()->setSceneRect(0, -_height * 0.5, _width, _height); - //scene()->setSceneRect(m_left, -_height * 0.5, sceneRange, _height); - m_chartItem->setBoundingRect(scene()->sceneRect()); - //scale(m_xscale / oldXScale, 1); -} - -void EasyGraphicsChart::update(Collections _collections) +void GraphicsChart::update(Collections _collections) { m_chartItem->update(std::move(_collections)); - scene()->update(); } -void EasyGraphicsChart::update(const ArbitraryValuesCollection* _selected) +void GraphicsChart::update(const ArbitraryValuesCollection* _selected) { m_chartItem->update(_selected); - scene()->update(); } ////////////////////////////////////////////////////////////////////////// @@ -669,13 +906,13 @@ EASY_CONSTEXPR auto StdItemType = QTreeWidgetItem::UserType; EASY_CONSTEXPR auto ValueItemType = QTreeWidgetItem::UserType + 1; struct UsedValueTypes { - EasyArbitraryTreeWidgetItem* items[int_cast(profiler::DataType::TypesCount)]; + ArbitraryTreeWidgetItem* items[int_cast(profiler::DataType::TypesCount)]; UsedValueTypes(int = 0) { memset(items, 0, sizeof(items)); } }; ////////////////////////////////////////////////////////////////////////// -EasyArbitraryTreeWidgetItem::EasyArbitraryTreeWidgetItem(QTreeWidgetItem* _parent, profiler::color_t _color, profiler::vin_t _vin) +ArbitraryTreeWidgetItem::ArbitraryTreeWidgetItem(QTreeWidgetItem* _parent, profiler::color_t _color, profiler::vin_t _vin) : Parent(_parent, ValueItemType) , m_vin(_vin) , m_color(_color) @@ -685,29 +922,29 @@ EasyArbitraryTreeWidgetItem::EasyArbitraryTreeWidgetItem(QTreeWidgetItem* _paren setCheckState(CheckColumn, Qt::Unchecked); } -EasyArbitraryTreeWidgetItem::~EasyArbitraryTreeWidgetItem() +ArbitraryTreeWidgetItem::~ArbitraryTreeWidgetItem() { } -QVariant EasyArbitraryTreeWidgetItem::data(int _column, int _role) const +QVariant ArbitraryTreeWidgetItem::data(int _column, int _role) const { if (_column == CheckColumn && _role == Qt::SizeHintRole) return QSize(m_widthHint, 26); return Parent::data(_column, _role); } -void EasyArbitraryTreeWidgetItem::setWidthHint(int _width) +void ArbitraryTreeWidgetItem::setWidthHint(int _width) { m_widthHint = _width; } -const ArbitraryValuesCollection* EasyArbitraryTreeWidgetItem::collection() const +const ArbitraryValuesCollection* ArbitraryTreeWidgetItem::collection() const { return m_collection.get(); } -void EasyArbitraryTreeWidgetItem::collectValues(profiler::thread_id_t _threadId) +void ArbitraryTreeWidgetItem::collectValues(profiler::thread_id_t _threadId) { if (!m_collection) m_collection = CollectionPtr(new ArbitraryValuesCollection); @@ -717,7 +954,7 @@ void EasyArbitraryTreeWidgetItem::collectValues(profiler::thread_id_t _threadId) m_collection->collectValues(_threadId, m_vin, text(int_cast(ArbitraryColumns::Name)).toStdString().c_str(), EASY_GLOBALS.begin_time); } -void EasyArbitraryTreeWidgetItem::interrupt() +void ArbitraryTreeWidgetItem::interrupt() { if (!m_collection) return; @@ -726,24 +963,29 @@ void EasyArbitraryTreeWidgetItem::interrupt() m_collection.release(); } -profiler::color_t EasyArbitraryTreeWidgetItem::color() const +profiler::color_t ArbitraryTreeWidgetItem::color() const { return m_color; } ////////////////////////////////////////////////////////////////////////// -EasyArbitraryValuesWidget::EasyArbitraryValuesWidget(QWidget* _parent) +ArbitraryValuesWidget::ArbitraryValuesWidget(QWidget* _parent) : Parent(_parent) + , m_splitter(new QSplitter(Qt::Horizontal, this)) , m_treeWidget(new QTreeWidget(this)) - , m_chart(new EasyGraphicsChart(this)) + , m_chart(new GraphicsChart(this)) { - auto layout = new QHBoxLayout(this); + m_splitter->setHandleWidth(1); + m_splitter->setContentsMargins(0, 0, 0, 0); + m_splitter->addWidget(m_treeWidget); + m_splitter->addWidget(m_chart); + m_splitter->setStretchFactor(0, 1); + m_splitter->setStretchFactor(1, 1); + + auto layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(m_treeWidget); - layout->addWidget(m_chart); - layout->setStretch(0, 1); - layout->setStretch(1, 1); + layout->addWidget(m_splitter); m_treeWidget->setAutoFillBackground(false); m_treeWidget->setAlternatingRowColors(true); @@ -761,10 +1003,6 @@ EasyArbitraryValuesWidget::EasyArbitraryValuesWidget(QWidget* _parent) headerItem->setText(int_cast(ArbitraryColumns::Vin), "ID"); m_treeWidget->setHeaderItem(headerItem); -// auto mainLayout = new QVBoxLayout(this); -// mainLayout->setContentsMargins(1, 1, 1, 1); -// mainLayout->addWidget(m_treeWidget); - connect(&m_timer, &QTimer::timeout, this, &This::rebuild); connect(&m_collectionsTimer, &QTimer::timeout, this, &This::onCollectionsTimeout); @@ -775,14 +1013,16 @@ EasyArbitraryValuesWidget::EasyArbitraryValuesWidget(QWidget* _parent) connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChanged); connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChanged); connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockIdChanged, this, &This::onSelectedBlockIdChanged); + + loadSettings(); } -EasyArbitraryValuesWidget::~EasyArbitraryValuesWidget() +ArbitraryValuesWidget::~ArbitraryValuesWidget() { - + saveSettings(); } -void EasyArbitraryValuesWidget::clear() +void ArbitraryValuesWidget::clear() { if (m_collectionsTimer.isActive()) m_collectionsTimer.stop(); @@ -792,25 +1032,25 @@ void EasyArbitraryValuesWidget::clear() m_treeWidget->clear(); } -void EasyArbitraryValuesWidget::onSelectedThreadChanged(::profiler::thread_id_t) +void ArbitraryValuesWidget::onSelectedThreadChanged(::profiler::thread_id_t) { if (!m_timer.isActive()) m_timer.start(100); } -void EasyArbitraryValuesWidget::onSelectedBlockChanged(uint32_t) +void ArbitraryValuesWidget::onSelectedBlockChanged(uint32_t) { if (!m_timer.isActive()) m_timer.start(100); } -void EasyArbitraryValuesWidget::onSelectedBlockIdChanged(::profiler::block_id_t) +void ArbitraryValuesWidget::onSelectedBlockIdChanged(::profiler::block_id_t) { if (!m_timer.isActive()) m_timer.start(100); } -void EasyArbitraryValuesWidget::onItemDoubleClicked(QTreeWidgetItem* _item, int) +void ArbitraryValuesWidget::onItemDoubleClicked(QTreeWidgetItem* _item, int) { if (_item == nullptr || _item->type() != ValueItemType) return; @@ -818,12 +1058,12 @@ void EasyArbitraryValuesWidget::onItemDoubleClicked(QTreeWidgetItem* _item, int) _item->setCheckState(CheckColumn, _item->checkState(CheckColumn) == Qt::Checked ? Qt::Unchecked : Qt::Checked); } -void EasyArbitraryValuesWidget::onItemChanged(QTreeWidgetItem* _item, int _column) +void ArbitraryValuesWidget::onItemChanged(QTreeWidgetItem* _item, int _column) { if (_item == nullptr || _item->type() != ValueItemType || _column != CheckColumn) return; - auto item = static_cast(_item); + auto item = static_cast(_item); if (item->checkState(CheckColumn) == Qt::Checked) { @@ -840,7 +1080,7 @@ void EasyArbitraryValuesWidget::onItemChanged(QTreeWidgetItem* _item, int _colum } } -void EasyArbitraryValuesWidget::onCurrentItemChanged(QTreeWidgetItem* _current, QTreeWidgetItem*) +void ArbitraryValuesWidget::onCurrentItemChanged(QTreeWidgetItem* _current, QTreeWidgetItem*) { if (_current == nullptr || _current->type() != ValueItemType) { @@ -848,11 +1088,11 @@ void EasyArbitraryValuesWidget::onCurrentItemChanged(QTreeWidgetItem* _current, return; } - auto item = static_cast(_current); + auto item = static_cast(_current); m_chart->update(item->collection()); } -void EasyArbitraryValuesWidget::rebuild() +void ArbitraryValuesWidget::rebuild() { clear(); @@ -863,7 +1103,7 @@ void EasyArbitraryValuesWidget::rebuild() m_treeWidget->resizeColumnToContents(i); } -void EasyArbitraryValuesWidget::onCollectionsTimeout() +void ArbitraryValuesWidget::onCollectionsTimeout() { if (m_checkedItems.isEmpty()) { @@ -879,7 +1119,7 @@ void EasyArbitraryValuesWidget::onCollectionsTimeout() { if (item->collection()->status() != ArbitraryValuesCollection::InProgress) { - collections.push_back(EasyCollectionPaintData {item->collection(), item->color(), + collections.push_back(CollectionPaintData {item->collection(), item->color(), ChartType::Line, item == m_treeWidget->currentItem()}); } } @@ -892,7 +1132,7 @@ void EasyArbitraryValuesWidget::onCollectionsTimeout() } } -void EasyArbitraryValuesWidget::buildTree(profiler::thread_id_t _threadId, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId) +void ArbitraryValuesWidget::buildTree(profiler::thread_id_t _threadId, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId) { m_treeWidget->clear(); m_treeWidget->setColumnHidden(int_cast(ArbitraryColumns::Value), profiler_gui::is_max(_blockIndex)); @@ -916,7 +1156,7 @@ void EasyArbitraryValuesWidget::buildTree(profiler::thread_id_t _threadId, profi } } -QTreeWidgetItem* EasyArbitraryValuesWidget::buildTreeForThread(const profiler::BlocksTreeRoot& _threadRoot, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId) +QTreeWidgetItem* ArbitraryValuesWidget::buildTreeForThread(const profiler::BlocksTreeRoot& _threadRoot, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId) { auto fm = m_treeWidget->fontMetrics(); @@ -932,7 +1172,7 @@ QTreeWidgetItem* EasyArbitraryValuesWidget::buildTreeForThread(const profiler::B const auto& desc = easyDescriptor(block.node->id()); if (desc.type() == profiler::BlockType::Value) { - auto valueItem = new EasyArbitraryTreeWidgetItem(rootItem, desc.color(), block.value->value_id()); + auto valueItem = new ArbitraryTreeWidgetItem(rootItem, desc.color(), block.value->value_id()); valueItem->setText(int_cast(ArbitraryColumns::Type), profiler_gui::valueTypeString(*block.value)); valueItem->setText(int_cast(ArbitraryColumns::Name), desc.name()); valueItem->setText(int_cast(ArbitraryColumns::Vin), QString("0x%1").arg(block.value->value_id(), 0, 16)); @@ -1005,8 +1245,8 @@ QTreeWidgetItem* EasyArbitraryValuesWidget::buildTreeForThread(const profiler::B const auto typeIndex = int_cast(child.value->type()); auto vin = child.value->value_id(); - EasyArbitraryTreeWidgetItem** usedItems = nullptr; - EasyArbitraryTreeWidgetItem* valueItem = nullptr; + ArbitraryTreeWidgetItem** usedItems = nullptr; + ArbitraryTreeWidgetItem* valueItem = nullptr; if (vin == 0) { auto result = names.emplace(desc.name(), 0); @@ -1030,7 +1270,7 @@ QTreeWidgetItem* EasyArbitraryValuesWidget::buildTreeForThread(const profiler::B } } - valueItem = new EasyArbitraryTreeWidgetItem(blockItem, desc.color(), vin); + valueItem = new ArbitraryTreeWidgetItem(blockItem, desc.color(), vin); valueItem->setText(int_cast(ArbitraryColumns::Type), profiler_gui::valueTypeString(*child.value)); valueItem->setText(int_cast(ArbitraryColumns::Name), desc.name()); valueItem->setText(int_cast(ArbitraryColumns::Vin), QString("0x%1").arg(vin, 0, 16)); @@ -1058,3 +1298,27 @@ QTreeWidgetItem* EasyArbitraryValuesWidget::buildTreeForThread(const profiler::B return rootItem; } +void ArbitraryValuesWidget::loadSettings() +{ + QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + settings.beginGroup("ArbitraryValuesWidget"); + + auto geometry = settings.value("hsplitter/geometry").toByteArray(); + if (!geometry.isEmpty()) + m_splitter->restoreGeometry(geometry); + + auto state = settings.value("hsplitter/state").toByteArray(); + if (!state.isEmpty()) + m_splitter->restoreState(state); + + settings.endGroup(); +} + +void ArbitraryValuesWidget::saveSettings() +{ + QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + settings.beginGroup("ArbitraryValuesWidget"); + settings.setValue("hsplitter/geometry", m_splitter->saveGeometry()); + settings.setValue("hsplitter/state", m_splitter->saveState()); + settings.endGroup(); +} diff --git a/profiler_gui/arbitrary_value_inspector.h b/profiler_gui/arbitrary_value_inspector.h index 01779d4..2b27b95 100644 --- a/profiler_gui/arbitrary_value_inspector.h +++ b/profiler_gui/arbitrary_value_inspector.h @@ -58,8 +58,6 @@ #include #include #include -#include -#include #include #include #include @@ -72,6 +70,7 @@ #include #include #include +#include "graphics_slider_area.h" ////////////////////////////////////////////////////////////////////////// @@ -136,7 +135,7 @@ enum class ChartType : uint8_t Points }; -struct EasyCollectionPaintData EASY_FINAL +struct CollectionPaintData EASY_FINAL { const ArbitraryValuesCollection* ptr; QRgb color; @@ -144,44 +143,58 @@ struct EasyCollectionPaintData EASY_FINAL bool selected; }; -using Collections = std::vector; +using Collections = std::vector; ////////////////////////////////////////////////////////////////////////// -class EasyArbitraryValuesChartItem : public QGraphicsItem +class ArbitraryValuesChartItem : public GraphicsImageItem { - using Parent = QGraphicsItem; - using This = EasyArbitraryValuesChartItem; + using Parent = GraphicsImageItem; + using This = ArbitraryValuesChartItem; Collections m_collections; - QRectF m_boundingRect; + qreal m_workerMaxValue; + qreal m_workerMinValue; public: - explicit EasyArbitraryValuesChartItem(); - ~EasyArbitraryValuesChartItem() override; + explicit ArbitraryValuesChartItem(); + ~ArbitraryValuesChartItem() override; - void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override; + void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget) override; - QRectF boundingRect() const override; - void setBoundingRect(const QRectF& _rect); - void setBoundingRect(qreal _left, qreal _top, qreal _width, qreal _height); + bool updateImage() override; +protected: + + void onImageUpdated() override; + +public: + + void clear(); void update(Collections _collections); void update(const ArbitraryValuesCollection* _selected); -}; // end of class EasyArbitraryValuesChartItem. +private: -class EasyGraphicsChart : public QGraphicsView + void paintMouseIndicator(QPainter* _painter, qreal _top, qreal _bottom, qreal _width, qreal _height, int _font_h, + qreal _visibleRegionLeft, qreal _visibleRegionWidth); + + void updateImageAsync(QRectF _boundingRect, qreal _current_scale, qreal _minimum, qreal _maximum, qreal _range, + qreal _value, qreal _width, bool _bindMode, profiler::timestamp_t _begin_time, bool _autoAdjust); + +}; // end of class ArbitraryValuesChartItem. + +class GraphicsChart : public GraphicsSliderArea { Q_OBJECT private: - using Parent = QGraphicsView; - using This = EasyGraphicsChart; + using Parent = GraphicsSliderArea; + using This = GraphicsChart; - EasyArbitraryValuesChartItem* m_chartItem; + ArbitraryValuesChartItem* m_chartItem; qreal m_left; qreal m_right; qreal m_offset; @@ -191,42 +204,24 @@ private: public: - explicit EasyGraphicsChart(QWidget* _parent = nullptr); - ~EasyGraphicsChart() override; + explicit GraphicsChart(QWidget* _parent = nullptr); + ~GraphicsChart() override; - void resizeEvent(QResizeEvent* _event) override; + void clear() override; - void clear(); - - bool bindMode() const; - qreal xscale() const; - - qreal left() const; - qreal right() const; - qreal range() const; - qreal offset() const; - qreal region() const; - - void setOffset(qreal _offset); - void setRange(qreal _left, qreal _right); - void setRegion(qreal _visibleRegionWidth); +public: void update(Collections _collections); void update(const ArbitraryValuesCollection* _selected); -private slots: - - void onSceneSizeChanged(); - void onWindowSizeChanged(qreal _width, qreal _height); - -}; // end of class EasyGraphicsChart. +}; // end of class GraphicsChart. ////////////////////////////////////////////////////////////////////////// -class EasyArbitraryTreeWidgetItem : public QTreeWidgetItem +class ArbitraryTreeWidgetItem : public QTreeWidgetItem { using Parent = QTreeWidgetItem; - using This = EasyArbitraryTreeWidgetItem; + using This = ArbitraryTreeWidgetItem; using CollectionPtr = std::unique_ptr; CollectionPtr m_collection; @@ -236,8 +231,8 @@ class EasyArbitraryTreeWidgetItem : public QTreeWidgetItem public: - explicit EasyArbitraryTreeWidgetItem(QTreeWidgetItem* _parent, profiler::color_t _color, profiler::vin_t _vin = 0); - ~EasyArbitraryTreeWidgetItem() override; + explicit ArbitraryTreeWidgetItem(QTreeWidgetItem* _parent, profiler::color_t _color, profiler::vin_t _vin = 0); + ~ArbitraryTreeWidgetItem() override; QVariant data(int _column, int _role) const override; @@ -249,27 +244,28 @@ public: profiler::color_t color() const; -}; // end of class EasyArbitraryTreeWidgetItem. +}; // end of class ArbitraryTreeWidgetItem. ////////////////////////////////////////////////////////////////////////// -class EasyArbitraryValuesWidget : public QWidget +class ArbitraryValuesWidget : public QWidget { Q_OBJECT using Parent = QWidget; - using This = EasyArbitraryValuesWidget; + using This = ArbitraryValuesWidget; - QTimer m_timer; - QTimer m_collectionsTimer; - QList m_checkedItems; - QTreeWidget* m_treeWidget; - EasyGraphicsChart* m_chart; + QTimer m_timer; + QTimer m_collectionsTimer; + QList m_checkedItems; + class QSplitter* m_splitter; + QTreeWidget* m_treeWidget; + GraphicsChart* m_chart; public: - explicit EasyArbitraryValuesWidget(QWidget* _parent = nullptr); - ~EasyArbitraryValuesWidget() override; + explicit ArbitraryValuesWidget(QWidget* _parent = nullptr); + ~ArbitraryValuesWidget() override; void clear(); @@ -292,7 +288,10 @@ private: void buildTree(profiler::thread_id_t _threadId, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId); QTreeWidgetItem* buildTreeForThread(const profiler::BlocksTreeRoot& _threadRoot, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId); -}; // end of class EasyArbitraryValuesWidget. + void loadSettings(); + void saveSettings(); + +}; // end of class ArbitraryValuesWidget. ////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp index be5b90c..2e29958 100644 --- a/profiler_gui/blocks_graphics_view.cpp +++ b/profiler_gui/blocks_graphics_view.cpp @@ -72,9 +72,12 @@ #include #include #include +#include +#include #include #include #include +#include #include "blocks_graphics_view.h" #include "easy_graphics_item.h" #include "easy_chronometer_item.h" @@ -286,6 +289,7 @@ EasyGraphicsView::EasyGraphicsView(QWidget* _parent) , m_sceneWidth(0) , m_scale(1) , m_offset(0) + , m_visibleRegionWidth(0) , m_timelineStep(0) , m_idleTime(0) , m_mouseButtons(Qt::NoButton) @@ -389,21 +393,64 @@ void EasyGraphicsView::clear() m_bEmpty = true; m_sceneWidth = 10; + m_visibleRegionWidth = 10; setSceneRect(0, 0, 10, 10); + auto& sceneData = EASY_GLOBALS.scene; + sceneData.left = 0; + sceneData.right = m_sceneWidth; + sceneData.window = m_visibleRegionWidth; + sceneData.offset = m_offset; + sceneData.empty = true; + // notify ProfTreeWidget that selection was reset emit intervalChanged(m_selectedBlocks, m_beginTime, 0, 0, false); + + EASY_GLOBALS.selected_thread = 0; + emit EASY_GLOBALS.events.selectedThreadChanged(0); +} + +void EasyGraphicsView::notifySceneSizeChange() +{ + EASY_GLOBALS.scene.left = 0; + EASY_GLOBALS.scene.right = m_sceneWidth; + emit EASY_GLOBALS.events.sceneSizeChanged(0, m_sceneWidth); +} + +void EasyGraphicsView::notifyVisibleRegionSizeChange() +{ + auto vbar = verticalScrollBar(); + const int vbar_width = (vbar != nullptr && vbar->isVisible() ? vbar->width() + 2 : 0); + notifyVisibleRegionSizeChange((m_visibleSceneRect.width() + vbar_width) / m_scale); +} + +void EasyGraphicsView::notifyVisibleRegionSizeChange(qreal _size) +{ + m_visibleRegionWidth = _size; + EASY_GLOBALS.scene.window = _size; + emit EASY_GLOBALS.events.sceneVisibleRegionSizeChanged(_size); +} + +void EasyGraphicsView::notifyVisibleRegionPosChange() +{ + EASY_GLOBALS.scene.offset = m_offset; + emit EASY_GLOBALS.events.sceneVisibleRegionPosChanged(m_offset); +} + +void EasyGraphicsView::notifyVisibleRegionPosChange(qreal _pos) +{ + m_offset = estd::clamp(0., _pos, m_sceneWidth - m_visibleRegionWidth); + notifyVisibleRegionPosChange(); } void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTree) { // clear scene clear(); + emit EASY_GLOBALS.events.sceneCleared(); if (_blocksTree.empty()) - { return; - } auto bgItem = new EasyBackgroundItem(); scene()->addItem(bgItem); @@ -515,14 +562,14 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr // Calculating scene rect m_sceneWidth = time2position(finish); setSceneRect(0, 0, m_sceneWidth, y + TIMELINE_ROW_SIZE); - - EASY_GLOBALS.scene_left = 0; - EASY_GLOBALS.scene_right = m_sceneWidth; - emit EASY_GLOBALS.events.sceneSizeChanged(); + EASY_GLOBALS.scene.empty = false; // Center view on the beginning of the scene updateVisibleSceneRect(); - setScrollbar(m_pScrollbar); + //setScrollbar(m_pScrollbar); + + notifySceneSizeChange(); + notifyVisibleRegionSizeChange(); // Create new chronometer item (previous item was destroyed by scene on scene()->clear()). // It will be shown on mouse right button click. @@ -543,9 +590,7 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr emit treeChanged(); if (mainThreadItem != nullptr) - { longestItem = mainThreadItem; - } if (longestItem != nullptr) { @@ -555,7 +600,7 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr scrollTo(longestItem); m_pScrollbar->setHistogramSource(longestItem->threadId(), longestItem->items(0)); if (!longestItem->items(0).empty()) - m_pScrollbar->setValue(longestItem->items(0).front().left() - m_pScrollbar->sliderWidth() * 0.25); + notifyVisibleRegionPosChange(longestItem->items(0).front().left() - m_visibleRegionWidth * 0.25); } m_idleTimer.start(IDLE_TIMER_INTERVAL); @@ -683,11 +728,8 @@ void EasyGraphicsView::setScrollbar(EasyGraphicsScrollbar* _scrollbar) auto const prevScrollbar = m_pScrollbar; const bool makeConnect = prevScrollbar == nullptr || prevScrollbar != _scrollbar; - if (prevScrollbar != nullptr && prevScrollbar != _scrollbar) - { - disconnect(prevScrollbar, &EasyGraphicsScrollbar::valueChanged, this, &This::onGraphicsScrollbarValueChange); - disconnect(prevScrollbar, &EasyGraphicsScrollbar::wheeled, this, &This::onGraphicsScrollbarWheel); - } + disconnect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::chartSliderChanged, this, &This::onGraphicsScrollbarValueChange); + disconnect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::chartWheeled, this, &This::onGraphicsScrollbarWheel); m_pScrollbar = _scrollbar; m_pScrollbar->clear(); @@ -697,11 +739,8 @@ void EasyGraphicsView::setScrollbar(EasyGraphicsScrollbar* _scrollbar) const int vbar_width = (vbar != nullptr && vbar->isVisible() ? vbar->width() + 2 : 0); m_pScrollbar->setSliderWidth(m_visibleSceneRect.width() + vbar_width); - if (makeConnect) - { - connect(m_pScrollbar, &EasyGraphicsScrollbar::valueChanged, this, &This::onGraphicsScrollbarValueChange); - connect(m_pScrollbar, &EasyGraphicsScrollbar::wheeled, this, &This::onGraphicsScrollbarWheel); - } + connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::chartSliderChanged, this, &This::onGraphicsScrollbarValueChange); + connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::chartWheeled, this, &This::onGraphicsScrollbarWheel); EASY_GLOBALS.selected_thread = 0; emit EASY_GLOBALS.events.selectedThreadChanged(0); @@ -768,7 +807,7 @@ void EasyGraphicsView::scaleTo(qreal _scale) // Update slider width for scrollbar const auto windowWidth = (m_visibleSceneRect.width() + vbar_width) / m_scale; - m_pScrollbar->setSliderWidth(windowWidth); + notifyVisibleRegionSizeChange(windowWidth); updateTimelineStep(windowWidth); repaintScene(); @@ -803,7 +842,7 @@ void EasyGraphicsView::scrollTo(const EasyGraphicsItem* _item) { m_bUpdatingRect = true; auto vbar = verticalScrollBar(); - vbar->setValue(_item->y() + (_item->boundingRect().height() - vbar->pageStep()) * 0.5); + vbar->setValue(static_cast(_item->y() + (_item->boundingRect().height() - vbar->pageStep()) * 0.5)); m_bUpdatingRect = false; } @@ -822,21 +861,18 @@ void EasyGraphicsView::onWheel(qreal _mouseX, int _wheelDelta) //updateVisibleSceneRect(); // Update scene rect // Update slider width for scrollbar - auto vbar = verticalScrollBar(); - const int vbar_width = (vbar != nullptr && vbar->isVisible() ? vbar->width() + 2 : 0); - const auto windowWidth = (m_visibleSceneRect.width() + vbar_width) / m_scale; - m_pScrollbar->setSliderWidth(windowWidth); + notifyVisibleRegionSizeChange(); // Calculate new offset to simulate QGraphicsView::AnchorUnderMouse scaling behavior - m_offset = clamp(0., mousePosition - _mouseX / m_scale, m_sceneWidth - windowWidth); + m_offset = clamp(0., mousePosition - _mouseX / m_scale, m_sceneWidth - m_visibleRegionWidth); // Update slider position - m_bUpdatingRect = true; // To be sure that updateVisibleSceneRect will not be called by scrollbar change - m_pScrollbar->setValue(m_offset); - m_bUpdatingRect = false; + profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); // To be sure that updateVisibleSceneRect will not be called by scrollbar change + notifyVisibleRegionPosChange(); + guard.restore(); updateVisibleSceneRect(); // Update scene rect - updateTimelineStep(windowWidth); + updateTimelineStep(m_visibleRegionWidth); repaintScene(); // repaint scene } @@ -975,7 +1011,7 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event) // Jump to selected zone clicked = true; m_flickerSpeedX = m_flickerSpeedY = 0; - m_pScrollbar->setValue(m_chronometerItem->left() + m_chronometerItem->width() * 0.5 - m_pScrollbar->sliderHalfWidth()); + notifyVisibleRegionPosChange(m_chronometerItem->left() + (m_chronometerItem->width() - m_visibleRegionWidth) * 0.5); } if (!clicked && m_mouseMovePath.manhattanLength() < 5) @@ -993,7 +1029,7 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event) { ::profiler::block_index_t i = ~0U; auto block = item->intersect(mouseClickPos, i); - if (block) + if (block != nullptr) { changedSelectedItem = true; selectedBlock = block; @@ -1026,23 +1062,26 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event) if (changedSelectedItem) { - m_bUpdatingRect = true; + profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); + if (selectedBlock != nullptr && previouslySelectedBlock == EASY_GLOBALS.selected_block && !selectedBlock->tree.children.empty()) { EASY_GLOBALS.gui_blocks[previouslySelectedBlock].expanded = !EASY_GLOBALS.gui_blocks[previouslySelectedBlock].expanded; emit EASY_GLOBALS.events.itemsExpandStateChanged(); } + emit EASY_GLOBALS.events.selectedBlockChanged(EASY_GLOBALS.selected_block); if (EASY_GLOBALS.selecting_block_changes_thread && selectedBlock != nullptr && EASY_GLOBALS.selected_thread != selectedBlockThread) { EASY_GLOBALS.selected_thread = selectedBlockThread; - m_pScrollbar->lock(); + emit EASY_GLOBALS.events.lockCharts(); emit EASY_GLOBALS.events.selectedThreadChanged(EASY_GLOBALS.selected_thread); - m_pScrollbar->unlock(); + emit EASY_GLOBALS.events.unlockCharts(); } - m_bUpdatingRect = false; + + guard.restore(); if (selectedBlock != nullptr && selectedBlockThread == EASY_GLOBALS.selected_thread) m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, EASY_GLOBALS.selected_block_id); @@ -1164,10 +1203,10 @@ void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event) { auto vbar = verticalScrollBar(); - m_bUpdatingRect = true; // Block scrollbars from updating scene rect to make it possible to do it only once + profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); // Block scrollbars from updating scene rect to make it possible to do it only once vbar->setValue(vbar->value() - delta.y()); - m_pScrollbar->setValue(m_pScrollbar->value() - delta.x() / m_scale); - m_bUpdatingRect = false; + notifyVisibleRegionPosChange(m_offset - delta.x() / m_scale); + guard.restore(); // Seems like an ugly stub, but QSignalBlocker is also a bad decision // because if scrollbar does not emit valueChanged signal then viewport does not move @@ -1243,14 +1282,14 @@ void EasyGraphicsView::keyPressEvent(QKeyEvent* _event) case Qt::Key_Right: case Qt::Key_6: { - m_pScrollbar->setValue(m_pScrollbar->value() + KeyStep / m_scale); + notifyVisibleRegionPosChange(m_offset + KeyStep / m_scale); break; } case Qt::Key_Left: case Qt::Key_4: { - m_pScrollbar->setValue(m_pScrollbar->value() - KeyStep / m_scale); + notifyVisibleRegionPosChange(m_offset - KeyStep / m_scale); break; } @@ -1308,16 +1347,16 @@ void EasyGraphicsView::resizeEvent(QResizeEvent* _event) // Update slider width for scrollbar const auto windowWidth = (m_visibleSceneRect.width() + vbar_width) / m_scale; - m_pScrollbar->setSliderWidth(windowWidth); + notifyVisibleRegionSizeChange(windowWidth); // Calculate new offset to save old screen center const auto deltaWidth = m_visibleSceneRect.width() - previousRect.width(); m_offset = clamp(0., m_offset - deltaWidth * 0.5 / m_scale, m_sceneWidth - windowWidth); // Update slider position - m_bUpdatingRect = true; // To be sure that updateVisibleSceneRect will not be called by scrollbar change - m_pScrollbar->setValue(m_offset); - m_bUpdatingRect = false; + profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); // To be sure that updateVisibleSceneRect will not be called by scrollbar change + notifyVisibleRegionPosChange(); + guard.restore(); repaintScene(); // repaint scene } @@ -1379,6 +1418,9 @@ void EasyGraphicsView::initMode() if (!m_selectedBlocks.empty()) emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_chronometerItem->left()), position2time(m_chronometerItem->right()), m_chronometerItem->reverse()); }); + + connect(globalSignals, &profiler_gui::EasyGlobalSignals::chartSliderChanged, this, &This::onGraphicsScrollbarValueChange); + connect(globalSignals, &profiler_gui::EasyGlobalSignals::chartWheeled, this, &This::onGraphicsScrollbarWheel); } ////////////////////////////////////////////////////////////////////////// @@ -1444,10 +1486,10 @@ void EasyGraphicsView::onFlickerTimeout() auto vbar = verticalScrollBar(); - m_bUpdatingRect = true; // Block scrollbars from updating scene rect to make it possible to do it only once - m_pScrollbar->setValue(m_pScrollbar->value() - m_flickerSpeedX / m_scale); + profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); // Block scrollbars from updating scene rect to make it possible to do it only once + notifyVisibleRegionPosChange(m_offset - m_flickerSpeedX / m_scale); vbar->setValue(vbar->value() - m_flickerSpeedY); - m_bUpdatingRect = false; + guard.restore(); // Seems like an ugly stub, but QSignalBlocker is also a bad decision // because if scrollbar does not emit valueChanged signal then viewport does not move @@ -1954,22 +1996,20 @@ void EasyGraphicsView::onSelectedBlockChange(unsigned int _block_index) m_flickerSpeedX = m_flickerSpeedY = 0; - m_bUpdatingRect = true; + const profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); verticalScrollBar()->setValue(static_cast(thread_item->levelY(guiblock.graphics_item_level) - m_visibleSceneRect.height() * 0.5)); - m_pScrollbar->setValue(item.left() + item.width() * 0.5 - m_pScrollbar->sliderHalfWidth()); + notifyVisibleRegionPosChange(item.left() + (item.width() - m_visibleRegionWidth) * 0.5); if (EASY_GLOBALS.selecting_block_changes_thread && EASY_GLOBALS.selected_thread != thread_item->threadId()) { EASY_GLOBALS.selected_thread = thread_item->threadId(); - m_pScrollbar->lock(); + emit EASY_GLOBALS.events.lockCharts(); emit EASY_GLOBALS.events.selectedThreadChanged(EASY_GLOBALS.selected_thread); - m_pScrollbar->unlock(); + emit EASY_GLOBALS.events.unlockCharts(); } m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, guiblock.tree.node->id()); - - m_bUpdatingRect = false; } else if (EASY_GLOBALS.selected_thread != 0) { @@ -2006,7 +2046,8 @@ void EasyGraphicsView::onRefreshRequired() EasyGraphicsViewWidget::EasyGraphicsViewWidget(QWidget* _parent) : QWidget(_parent) - , m_scrollbar(new EasyGraphicsScrollbar(true, 85 + (QFontMetrics(font()).height() << 1), this)) + , m_splitter(new QSplitter(Qt::Vertical, this)) + , m_scrollbar(new EasyGraphicsScrollbar(85 + (QFontMetrics(font()).height() << 1), this)) , m_view(new EasyGraphicsView(this)) , m_threadNamesWidget(new EasyThreadNamesWidget(m_view, m_scrollbar->height(), this)) { @@ -2015,13 +2056,18 @@ EasyGraphicsViewWidget::EasyGraphicsViewWidget(QWidget* _parent) void EasyGraphicsViewWidget::initWidget() { - auto lay = new QGridLayout(this); + m_splitter->setHandleWidth(1); + m_splitter->setContentsMargins(0, 0, 0, 0); + m_splitter->addWidget(m_view); + m_splitter->addWidget(m_scrollbar); + m_splitter->setStretchFactor(0, 500); + m_splitter->setStretchFactor(1, 1); + + auto lay = new QHBoxLayout(this); lay->setContentsMargins(0, 0, 0, 0); lay->setSpacing(1); - lay->addWidget(m_threadNamesWidget, 0, 0, 2, 1); - lay->addWidget(m_view, 0, 1); - lay->addWidget(m_scrollbar, 1, 1); - setLayout(lay); + lay->addWidget(m_threadNamesWidget); + lay->addWidget(m_splitter); m_view->setScrollbar(m_scrollbar); } @@ -2043,6 +2089,23 @@ void EasyGraphicsViewWidget::clear() m_view->clear(); } +void EasyGraphicsViewWidget::save(QSettings& settings) +{ + settings.setValue("diagram/vsplitter/geometry", m_splitter->saveGeometry()); + settings.setValue("diagram/vsplitter/state", m_splitter->saveState()); +} + +void EasyGraphicsViewWidget::restore(QSettings& settings) +{ + auto geometry = settings.value("diagram/vsplitter/geometry").toByteArray(); + if (!geometry.isEmpty()) + m_splitter->restoreGeometry(geometry); + + auto state = settings.value("diagram/vsplitter/state").toByteArray(); + if (!state.isEmpty()) + m_splitter->restoreState(state); +} + ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -2117,6 +2180,9 @@ void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsIte _painter->drawRect(rect); } + if (h + 2 >= parentView->height()) + return; + // Draw separator between thread names area and information area _painter->setPen(::profiler_gui::SYSTEM_BORDER_COLOR); _painter->drawLine(QLineF(0, h, w, h)); diff --git a/profiler_gui/blocks_graphics_view.h b/profiler_gui/blocks_graphics_view.h index 255862b..2801285 100644 --- a/profiler_gui/blocks_graphics_view.h +++ b/profiler_gui/blocks_graphics_view.h @@ -136,6 +136,7 @@ private: qreal m_sceneWidth; ///< qreal m_scale; ///< Current scale qreal m_offset; ///< Have to use manual offset for all scene content instead of using scrollbars because QScrollBar::value is 32-bit integer :( + qreal m_visibleRegionWidth; ///< Visible scene rectangle in scene coordinates + width of vertical scrollbar (if visible) qreal m_timelineStep; ///< uint64_t m_idleTime; ///< QPoint m_mousePressPos; ///< Last mouse global position (used by mousePressEvent and mouseMoveEvent) @@ -156,7 +157,7 @@ private: public: explicit EasyGraphicsView(QWidget* _parent = nullptr); - virtual ~EasyGraphicsView(); + ~EasyGraphicsView() override; // Public virtual methods @@ -198,6 +199,12 @@ private: // Private non-virtual methods + void notifySceneSizeChange(); + void notifyVisibleRegionSizeChange(); + void notifyVisibleRegionSizeChange(qreal _size); + void notifyVisibleRegionPosChange(); + void notifyVisibleRegionPosChange(qreal _pos); + void removePopup(bool _removeFromScene = false); EasyChronometerItem* createChronometer(bool _main = true); @@ -230,33 +237,33 @@ public: // Public inline methods - inline qreal scale() const + qreal scale() const { return m_scale; } - inline qreal offset() const + qreal offset() const { return m_offset; } - inline const QRectF& visibleSceneRect() const + const QRectF& visibleSceneRect() const { return m_visibleSceneRect; } - inline qreal timelineStep() const + qreal timelineStep() const { return m_timelineStep; } - inline qreal time2position(const profiler::timestamp_t& _time) const + qreal time2position(const profiler::timestamp_t& _time) const { return PROF_MICROSECONDS(qreal(_time - m_beginTime)); //return PROF_MILLISECONDS(qreal(_time - m_beginTime)); } - inline ::profiler::timestamp_t position2time(qreal _pos) const + ::profiler::timestamp_t position2time(qreal _pos) const { return PROF_FROM_MICROSECONDS(_pos); //return PROF_FROM_MILLISECONDS(_pos); @@ -272,8 +279,8 @@ class EasyThreadNamesWidget : public QGraphicsView private: - typedef QGraphicsView Parent; - typedef EasyThreadNamesWidget This; + using Parent = QGraphicsView; + using This = EasyThreadNamesWidget; QTimer m_idleTimer; ///< uint64_t m_idleTime; ///< @@ -285,7 +292,7 @@ private: public: explicit EasyThreadNamesWidget(EasyGraphicsView* _view, int _additionalHeight, QWidget* _parent = nullptr); - virtual ~EasyThreadNamesWidget(); + ~EasyThreadNamesWidget() override; void mousePressEvent(QMouseEvent* _event) override; void mouseDoubleClickEvent(QMouseEvent* _event) override; @@ -325,6 +332,7 @@ class EasyGraphicsViewWidget : public QWidget private: + class QSplitter* m_splitter; EasyGraphicsScrollbar* m_scrollbar; EasyGraphicsView* m_view; EasyThreadNamesWidget* m_threadNamesWidget; @@ -332,11 +340,14 @@ private: public: explicit EasyGraphicsViewWidget(QWidget* _parent = nullptr); - virtual ~EasyGraphicsViewWidget(); + ~EasyGraphicsViewWidget() override; EasyGraphicsView* view(); void clear(); + void save(class QSettings& settings); + void restore(class QSettings& settings); + private: void initWidget(); diff --git a/profiler_gui/common_functions.h b/profiler_gui/common_functions.h index 7528962..2fbfe6d 100644 --- a/profiler_gui/common_functions.h +++ b/profiler_gui/common_functions.h @@ -128,6 +128,10 @@ void set_max(T& _value) { ////////////////////////////////////////////////////////////////////////// +inline EASY_CONSTEXPR_FCN QRgb alpha(::profiler::color_t _color) { + return (_color & 0xff000000) >> 24; +} + inline EASY_CONSTEXPR_FCN QRgb toRgb(uint32_t _red, uint32_t _green, uint32_t _blue) { return (_red << 16) + (_green << 8) + _blue; } @@ -136,6 +140,13 @@ inline EASY_CONSTEXPR_FCN QRgb fromProfilerRgb(uint32_t _red, uint32_t _green, u return _red == 0 && _green == 0 && _blue == 0 ? ::profiler::colors::Default : toRgb(_red, _green, _blue) | 0x00141414; } +inline QRgb darken(::profiler::color_t _color, float _part) { + const uint32_t r = (_color & 0x00ff0000) >> 16; + const uint32_t g = (_color & 0x0000ff00) >> 8; + const uint32_t b = _color & 0x000000ff; + return (_color & 0xff000000) | toRgb(r - static_cast(r * _part), g - static_cast(g * _part), b - static_cast(b * _part)); +} + EASY_FORCE_INLINE EASY_CONSTEXPR_FCN qreal colorSum(::profiler::color_t _color) { return 255. - (((_color & 0x00ff0000) >> 16) * 0.299 + ((_color & 0x0000ff00) >> 8) * 0.587 + (_color & 0x000000ff) * 0.114); } diff --git a/profiler_gui/common_types.h b/profiler_gui/common_types.h index c272703..6102a56 100644 --- a/profiler_gui/common_types.h +++ b/profiler_gui/common_types.h @@ -165,6 +165,20 @@ enum TimeUnits : int8_t ////////////////////////////////////////////////////////////////////////// +class BoolFlagGuard EASY_FINAL +{ + bool& m_ref; + bool m_restore; + +public: + + explicit BoolFlagGuard(bool& flag) : m_ref(flag), m_restore(!flag) {} + explicit BoolFlagGuard(bool& flag, bool value) : m_ref(flag), m_restore(!value) { m_ref = value; } + ~BoolFlagGuard() { restore(); } + + void restore() { m_ref = m_restore; } +}; + } // END of namespace profiler_gui. template diff --git a/profiler_gui/descriptors_tree_widget.cpp b/profiler_gui/descriptors_tree_widget.cpp index 4d4c813..8397999 100644 --- a/profiler_gui/descriptors_tree_widget.cpp +++ b/profiler_gui/descriptors_tree_widget.cpp @@ -67,6 +67,7 @@ #include #include #include +#include #include #include #include @@ -765,14 +766,20 @@ int EasyDescTreeWidget::findPrev(const QString& _str, Qt::MatchFlags _flags) ////////////////////////////////////////////////////////////////////////// EasyDescWidget::EasyDescWidget(QWidget* _parent) : Parent(_parent) + , m_splitter(new QSplitter(Qt::Vertical, this)) , m_tree(new EasyDescTreeWidget(this)) - , m_values(new EasyArbitraryValuesWidget(this)) + , m_values(new ArbitraryValuesWidget(this)) , m_searchBox(new QLineEdit(this)) , m_foundNumber(new QLabel("Found 0 matches", this)) , m_searchButton(nullptr) , m_bCaseSensitiveSearch(false) { - loadSettings(); + m_splitter->setHandleWidth(1); + m_splitter->setContentsMargins(0, 0, 0, 0); + m_splitter->addWidget(m_tree); + m_splitter->addWidget(m_values); + m_splitter->setStretchFactor(0, 1); + m_splitter->setStretchFactor(1, 1); m_searchBox->setFixedWidth(300); m_searchBox->setContentsMargins(5, 0, 0, 0); @@ -812,6 +819,7 @@ EasyDescWidget::EasyDescWidget(QWidget* _parent) : Parent(_parent) a->setChecked(m_bCaseSensitiveSearch); connect(a, &QAction::triggered, [this](bool _checked){ m_bCaseSensitiveSearch = _checked; }); menu->addAction(a); + QAction* caseSensitiveSwitch = a; menu->addSeparator(); auto headerItem = m_tree->headerItem(); @@ -845,11 +853,13 @@ EasyDescWidget::EasyDescWidget(QWidget* _parent) : Parent(_parent) auto lay = new QVBoxLayout(this); lay->setContentsMargins(1, 1, 1, 1); lay->addLayout(searchbox); - lay->addWidget(m_tree); - lay->addWidget(m_values); + lay->addWidget(m_splitter); connect(m_searchBox, &QLineEdit::returnPressed, this, &This::onSeachBoxReturnPressed); connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::connectionChanged, refreshButton, &QAction::setEnabled); + + loadSettings(); + caseSensitiveSwitch->setChecked(m_bCaseSensitiveSearch); } EasyDescWidget::~EasyDescWidget() @@ -866,6 +876,14 @@ void EasyDescWidget::loadSettings() if (!val.isNull()) m_bCaseSensitiveSearch = val.toBool(); + auto geometry = settings.value("vsplitter/geometry").toByteArray(); + if (!geometry.isEmpty()) + m_splitter->restoreGeometry(geometry); + + auto state = settings.value("vsplitter/state").toByteArray(); + if (!state.isEmpty()) + m_splitter->restoreState(state); + settings.endGroup(); } @@ -874,6 +892,8 @@ void EasyDescWidget::saveSettings() QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); settings.beginGroup("EasyDescWidget"); settings.setValue("case_sensitive", m_bCaseSensitiveSearch); + settings.setValue("vsplitter/geometry", m_splitter->saveGeometry()); + settings.setValue("vsplitter/state", m_splitter->saveState()); settings.endGroup(); } diff --git a/profiler_gui/descriptors_tree_widget.h b/profiler_gui/descriptors_tree_widget.h index 3a00d67..2d5c74e 100644 --- a/profiler_gui/descriptors_tree_widget.h +++ b/profiler_gui/descriptors_tree_widget.h @@ -90,7 +90,7 @@ private: public: explicit EasyDescWidgetItem(::profiler::block_id_t _desc, Parent* _parent = nullptr); - virtual ~EasyDescWidgetItem(); + ~EasyDescWidgetItem() override; bool operator < (const Parent& _other) const override; QVariant data(int _column, int _role) const override; @@ -99,12 +99,12 @@ public: // Public inline methods - inline ::profiler::block_id_t desc() const + ::profiler::block_id_t desc() const { return m_desc; } - inline void setType(Type _type) + void setType(Type _type) { m_type = _type; } @@ -117,12 +117,12 @@ class EasyDescTreeWidget : public QTreeWidget { Q_OBJECT - typedef QTreeWidget Parent; - typedef EasyDescTreeWidget This; + using Parent = QTreeWidget; + using This = EasyDescTreeWidget; - typedef ::std::vector Items; - typedef ::std::vector TreeItems; - typedef ::std::unordered_set<::std::string> ExpandedFiles; + using Items = ::std::vector; + using TreeItems = ::std::vector; + using ExpandedFiles = ::std::unordered_set<::std::string>; protected: @@ -140,7 +140,7 @@ public: // Public virtual methods explicit EasyDescTreeWidget(QWidget* _parent = nullptr); - virtual ~EasyDescTreeWidget(); + ~EasyDescTreeWidget() override; void contextMenuEvent(QContextMenuEvent* _event) override; public: @@ -183,13 +183,14 @@ class EasyDescWidget : public QWidget { Q_OBJECT - typedef QWidget Parent; - typedef EasyDescWidget This; + using Parent = QWidget; + using This = EasyDescWidget; private: + class QSplitter* m_splitter; EasyDescTreeWidget* m_tree; - class EasyArbitraryValuesWidget* m_values; + class ArbitraryValuesWidget* m_values; class QLineEdit* m_searchBox; class QLabel* m_foundNumber; class QAction* m_searchButton; @@ -200,7 +201,7 @@ public: // Public virtual methods explicit EasyDescWidget(QWidget* _parent = nullptr); - virtual ~EasyDescWidget(); + ~EasyDescWidget() override; void keyPressEvent(QKeyEvent* _event) override; void contextMenuEvent(QContextMenuEvent* _event) override; diff --git a/profiler_gui/easy_graphics_scrollbar.cpp b/profiler_gui/easy_graphics_scrollbar.cpp index 60bcd9f..57d4a3e 100644 --- a/profiler_gui/easy_graphics_scrollbar.cpp +++ b/profiler_gui/easy_graphics_scrollbar.cpp @@ -772,44 +772,12 @@ void GraphicsHistogramItem::pickFrameTime(qreal _y) const ////////////////////////////////////////////////////////////////////////// -void GraphicsHistogramItem::onValueChanged() -{ - const auto widget = static_cast(scene()->parent()); - - if (!widget->bindMode()) - return; - - m_boundaryTimer.stop(); - - const auto sliderWidth_inv = 1.0 / widget->sliderWidth(); - const auto k = widget->range() * sliderWidth_inv; - - const auto deltaScale = m_imageScaleUpdate < k ? (k / m_imageScaleUpdate) : (m_imageScaleUpdate / k); - if (deltaScale > 4) - { - updateImage(); - return; - } - - const auto deltaOffset = (widget->value() - m_imageOriginUpdate) * sliderWidth_inv; - if (deltaOffset < 1.5 || deltaOffset > 4.5) - { - updateImage(); - return; - } - - m_boundaryTimer.start(); -} - -////////////////////////////////////////////////////////////////////////// - void GraphicsHistogramItem::onModeChanged() { if (!isImageUpdatePermitted()) return; const auto widget = static_cast(scene()->parent()); - if (!widget->bindMode() && EASY_GLOBALS.auto_adjust_histogram_height) { m_topValue = m_maxValue; @@ -827,7 +795,7 @@ bool GraphicsHistogramItem::updateImage() if (!Parent::updateImage()) return false; - const auto widget = static_cast(scene()->parent()); + const auto widget = static_cast(scene()->parent()); m_imageScaleUpdate = widget->range() / widget->sliderWidth(); m_imageOriginUpdate = widget->bindMode() ? (widget->value() - widget->sliderWidth() * 3) : widget->minimum(); @@ -1151,62 +1119,21 @@ void GraphicsHistogramItem::updateImageAsync(QRectF _boundingRect, HistRegime _r ////////////////////////////////////////////////////////////////////////// -EasyGraphicsScrollbar::EasyGraphicsScrollbar(bool _fixedHeight, int _height, QWidget* _parent) +EasyGraphicsScrollbar::EasyGraphicsScrollbar(int _initialHeight, QWidget* _parent) : Parent(_parent) - , m_minimumValue(0) - , m_maximumValue(500) - , m_value(10) - , m_windowScale(1) - , m_mouseButtons(Qt::NoButton) - , m_slider(nullptr) - , m_selectionIndicator(nullptr) , m_histogramItem(nullptr) - , m_fontHeight(0) - , m_bScrolling(false) - , m_bBindMode(false) - , m_bLocked(false) { - setCacheMode(QGraphicsView::CacheNone); - setTransformationAnchor(QGraphicsView::AnchorUnderMouse); - //setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); - setViewportUpdateMode(QGraphicsView::FullViewportUpdate); - setOptimizationFlag(QGraphicsView::DontSavePainterState, true); - - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - setContentsMargins(0, 0, 0, 0); - - setScene(new QGraphicsScene(this)); - - m_fontHeight = QFontMetrics(font()).height(); - - const int sceneHeight = _height - 2; + const int sceneHeight = _initialHeight - 2; scene()->setSceneRect(0, -(sceneHeight >> 1), 500, sceneHeight); - if (_fixedHeight) - setFixedHeight(_height); m_histogramItem = new GraphicsHistogramItem(); + m_imageItem = m_histogramItem; scene()->addItem(m_histogramItem); m_histogramItem->setPos(0, 0); m_histogramItem->setBoundingRect(0, scene()->sceneRect().top() + margin(), scene()->width(), sceneHeight - margins() - 1); m_histogramItem->hide(); - m_selectionIndicator = new GraphicsSliderItem(6, false); - scene()->addItem(m_selectionIndicator); - - m_selectionIndicator->setPos(0, 0); - m_selectionIndicator->setColor(0x40000000 | profiler_gui::CHRONOMETER_COLOR.rgba()); - m_selectionIndicator->hide(); - - m_slider = new GraphicsSliderItem(6, true); - scene()->addItem(m_slider); - - m_slider->setPos(0, 0); - m_slider->setColor(0x40c0c0c0); - m_slider->hide(); - connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::expectedFrameTimeChanged, [this]() { if (m_histogramItem->isVisible()) @@ -1231,7 +1158,13 @@ EasyGraphicsScrollbar::EasyGraphicsScrollbar(bool _fixedHeight, int _height, QWi connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::threadNameDecorationChanged, this, &This::onThreadViewChanged); connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::hexThreadIdChanged, this, &This::onThreadViewChanged); - centerOn(0, 0); + if (!EASY_GLOBALS.scene.empty) + { + setRange(EASY_GLOBALS.scene.left, EASY_GLOBALS.scene.right); + setSliderWidth(EASY_GLOBALS.scene.window); + setValue(EASY_GLOBALS.scene.offset); + m_slider->show(); + } } EasyGraphicsScrollbar::~EasyGraphicsScrollbar() diff --git a/profiler_gui/easy_graphics_scrollbar.h b/profiler_gui/easy_graphics_scrollbar.h index 9bd98b3..a6efbea 100644 --- a/profiler_gui/easy_graphics_scrollbar.h +++ b/profiler_gui/easy_graphics_scrollbar.h @@ -112,7 +112,6 @@ public: bool decreaseBottomValue() override; void onModeChanged() override; - void onValueChanged() override; public: @@ -156,7 +155,7 @@ private: public: - explicit EasyGraphicsScrollbar(bool _fixedHeight, int _height, QWidget* _parent = nullptr); + explicit EasyGraphicsScrollbar(int _initialHeight, QWidget* _parent = nullptr); ~EasyGraphicsScrollbar() override; void clear() override; diff --git a/profiler_gui/globals.cpp b/profiler_gui/globals.cpp index 4487849..9865506 100644 --- a/profiler_gui/globals.cpp +++ b/profiler_gui/globals.cpp @@ -75,8 +75,6 @@ namespace profiler_gui { , chronometer_font(::profiler_gui::EFont("DejaVu Sans", 16, QFont::Bold)) , items_font(::profiler_gui::EFont("DejaVu Sans", 10, QFont::Medium)) , selected_item_font(::profiler_gui::EFont("DejaVu Sans", 10, QFont::Medium)) - , scene_left(0) - , scene_right(100) , begin_time(0) , selected_thread(0U) , selected_block(::profiler_gui::numeric_max()) @@ -109,6 +107,7 @@ namespace profiler_gui { , highlight_blocks_with_same_id(true) , selecting_block_changes_thread(true) , auto_adjust_histogram_height(true) + , auto_adjust_chart_height(true) , display_only_frames_on_histogram(false) , bind_scene_and_tree_expand_status(true) { diff --git a/profiler_gui/globals.h b/profiler_gui/globals.h index 6f346be..09481c5 100644 --- a/profiler_gui/globals.h +++ b/profiler_gui/globals.h @@ -162,6 +162,15 @@ namespace profiler_gui { ////////////////////////////////////////////////////////////////////////// + struct SceneData Q_DECL_FINAL + { + qreal left = 0; + qreal right = 100; + qreal window = 100; + qreal offset = 0; + bool empty = true; + }; + struct EasyGlobals Q_DECL_FINAL { static EasyGlobals& instance(); @@ -177,9 +186,8 @@ namespace profiler_gui { QFont items_font; ///< Font for easy_graphics_item QFont selected_item_font; ///< Font for easy_graphics_item - double scene_left; ///< Graphics scene left boundary - double scene_right; ///< Graphics scene right boundary - ::profiler::timestamp_t begin_time; ///< + SceneData scene; ///< + ::profiler::timestamp_t begin_time; ///< ::profiler::thread_id_t selected_thread; ///< Current selected thread id ::profiler::block_index_t selected_block; ///< Current selected profiler block index ::profiler::block_id_t selected_block_id; ///< Current selected profiler block id @@ -211,6 +219,7 @@ namespace profiler_gui { bool highlight_blocks_with_same_id; ///< Highlight all blocks with same id on diagram bool selecting_block_changes_thread; ///< If true then current selected thread will change every time you select block bool auto_adjust_histogram_height; ///< Automatically adjust histogram height to the visible region + bool auto_adjust_chart_height; ///< Automatically adjust arbitrary value chart height to the visible region bool display_only_frames_on_histogram; ///< Display only top-level blocks on histogram when drawing histogram by block id bool bind_scene_and_tree_expand_status; /** \brief If true then items on graphics scene and in the tree (blocks hierarchy) are binded on each other so expanding/collapsing items on scene also expands/collapse items in the tree. */ diff --git a/profiler_gui/globals_qobjects.h b/profiler_gui/globals_qobjects.h index 348aede..efee933 100644 --- a/profiler_gui/globals_qobjects.h +++ b/profiler_gui/globals_qobjects.h @@ -67,7 +67,7 @@ namespace profiler_gui { public: EasyGlobalSignals(); - virtual ~EasyGlobalSignals(); + ~EasyGlobalSignals() Q_DECL_OVERRIDE; signals: @@ -80,13 +80,23 @@ namespace profiler_gui { void blocksRefreshRequired(bool); void expectedFrameTimeChanged(); void autoAdjustHistogramChanged(); + void autoAdjustChartChanged(); void displayOnlyFramesOnHistogramChanged(); void hierarchyFlagChanged(bool); void threadNameDecorationChanged(); void hexThreadIdChanged(); void refreshRequired(); void blocksTreeModeChanged(); - void sceneSizeChanged(); + + void sceneCleared(); + void sceneSizeChanged(qreal left, qreal right); + void sceneVisibleRegionSizeChanged(qreal width); + void sceneVisibleRegionPosChanged(qreal pos); + void lockCharts(); + void unlockCharts(); + + void chartWheeled(qreal pos, int delta); + void chartSliderChanged(qreal pos); }; // END of class EasyGlobalSignals. diff --git a/profiler_gui/graphics_image_item.cpp b/profiler_gui/graphics_image_item.cpp index 44764f1..9b0bca7 100644 --- a/profiler_gui/graphics_image_item.cpp +++ b/profiler_gui/graphics_image_item.cpp @@ -71,14 +71,10 @@ bool GraphicsImageItem::updateImage() return true; } - void GraphicsImageItem::onValueChanged() { const auto widget = qobject_cast(scene()->parent()); - if (widget == nullptr) - return; - - if (!widget->bindMode()) + if (widget == nullptr || !widget->bindMode()) return; m_boundaryTimer.stop(); diff --git a/profiler_gui/graphics_image_item.h b/profiler_gui/graphics_image_item.h index f94b89e..99fa692 100644 --- a/profiler_gui/graphics_image_item.h +++ b/profiler_gui/graphics_image_item.h @@ -55,7 +55,6 @@ public: virtual bool updateImage(); virtual void onModeChanged(); - virtual void onValueChanged(); protected: @@ -63,6 +62,7 @@ protected: public: + void onValueChanged(); void setMousePos(const QPointF& pos); void setMousePos(qreal x, qreal y); void setBoundingRect(const QRectF& _rect); diff --git a/profiler_gui/graphics_slider_area.cpp b/profiler_gui/graphics_slider_area.cpp index 423b336..7dd891e 100644 --- a/profiler_gui/graphics_slider_area.cpp +++ b/profiler_gui/graphics_slider_area.cpp @@ -172,11 +172,13 @@ GraphicsSliderArea::GraphicsSliderArea(QWidget* _parent) , m_mouseButtons(Qt::NoButton) , m_slider(nullptr) , m_selectionIndicator(nullptr) - , m_histogramItem(nullptr) + , m_imageItem(nullptr) , m_fontHeight(0) , m_bScrolling(false) , m_bBindMode(false) , m_bLocked(false) + , m_bUpdatingPos(false) + , m_bEmitChange(true) { setCacheMode(QGraphicsView::CacheNone); setTransformationAnchor(QGraphicsView::AnchorUnderMouse); @@ -191,19 +193,13 @@ GraphicsSliderArea::GraphicsSliderArea(QWidget* _parent) setScene(new QGraphicsScene(this)); - m_fontHeight = QFontMetrics(font()).height(); + m_fontHeight = QFontMetrics(font()).height() + 1; EASY_CONSTEXPR int SceneHeight = 500; scene()->setSceneRect(0, -(SceneHeight >> 1), 500, SceneHeight); - m_histogramItem = new GraphicsHistogramItem(); - scene()->addItem(m_histogramItem); - - m_histogramItem->setPos(0, 0); - m_histogramItem->setBoundingRect(0, scene()->sceneRect().top() + margin(), scene()->width(), SceneHeight - margins() - 1); - m_histogramItem->hide(); - m_selectionIndicator = new GraphicsSliderItem(6, false); + m_selectionIndicator->setZValue(1); scene()->addItem(m_selectionIndicator); m_selectionIndicator->setPos(0, 0); @@ -211,16 +207,38 @@ GraphicsSliderArea::GraphicsSliderArea(QWidget* _parent) m_selectionIndicator->hide(); m_slider = new GraphicsSliderItem(6, true); + m_slider->setZValue(2); scene()->addItem(m_slider); m_slider->setPos(0, 0); m_slider->setColor(0x40c0c0c0); m_slider->hide(); - connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::threadNameDecorationChanged, this, &This::onThreadViewChanged); - connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::hexThreadIdChanged, this, &This::onThreadViewChanged); - centerOn(0, 0); + + auto globalEvents = &EASY_GLOBALS.events; + connect(globalEvents, &profiler_gui::EasyGlobalSignals::sceneCleared, this, &This::clear); + connect(globalEvents, &profiler_gui::EasyGlobalSignals::sceneVisibleRegionSizeChanged, this, &This::setSliderWidth); + connect(globalEvents, &profiler_gui::EasyGlobalSignals::sceneVisibleRegionPosChanged, this, &This::setValue); + + connect(globalEvents, &profiler_gui::EasyGlobalSignals::chartSliderChanged, [this] (qreal pos) + { + if (!m_bUpdatingPos) + { + m_bEmitChange = false; + setValue(pos); + m_bEmitChange = true; + } + }); + + connect(globalEvents, &profiler_gui::EasyGlobalSignals::sceneSizeChanged, [this] (qreal left, qreal right) + { + setRange(left, right); + m_slider->show(); + }); + + connect(globalEvents, &profiler_gui::EasyGlobalSignals::lockCharts, this, &This::lock); + connect(globalEvents, &profiler_gui::EasyGlobalSignals::unlockCharts, this, &This::unlock); } GraphicsSliderArea::~GraphicsSliderArea() @@ -232,10 +250,11 @@ GraphicsSliderArea::~GraphicsSliderArea() void GraphicsSliderArea::clear() { - m_selectionIndicator->hide(); setRange(0, 100); setSliderWidth(2); setValue(0); + m_selectionIndicator->hide(); + m_slider->hide(); } ////////////////////////////////////////////////////////////////////////// @@ -299,10 +318,18 @@ int GraphicsSliderArea::margins() const void GraphicsSliderArea::setValue(qreal _value) { - using estd::clamp; - m_value = clamp(m_minimumValue, _value, std::max(m_minimumValue, m_maximumValue - m_slider->width())); + if (m_bUpdatingPos) + return; + + const profiler_gui::BoolFlagGuard guard(m_bUpdatingPos, true); + + m_value = estd::clamp(m_minimumValue, _value, std::max(m_minimumValue, m_maximumValue - m_slider->width())); m_slider->setX(m_value + m_slider->halfwidth()); - emit valueChanged(m_value); + + if (m_bEmitChange) + { + emit EASY_GLOBALS.events.chartSliderChanged(m_value); + } if (m_imageItem->isVisible()) m_imageItem->onValueChanged(); @@ -324,8 +351,6 @@ void GraphicsSliderArea::setRange(qreal _minValue, qreal _maxValue) m_imageItem->cancelImageUpdate(); m_imageItem->setBoundingRect(_minValue, histogramRect.top(), range, histogramRect.height()); - emit rangeChanged(); - setValue(_minValue + oldValue * range); onWindowWidthChange(width()); @@ -432,7 +457,7 @@ void GraphicsSliderArea::mouseMoveEvent(QMouseEvent* _event) } } - m_imageItem->setMousePos(mapToScene(pos)); + m_imageItem->setMousePos(pos.x(), mapToScene(pos).y()); if (m_imageItem->isVisible()) scene()->update(); } @@ -475,28 +500,52 @@ void GraphicsSliderArea::wheelEvent(QWheelEvent* _event) { const auto w = m_slider->halfwidth() * (_event->delta() < 0 ? profiler_gui::SCALING_COEFFICIENT : profiler_gui::SCALING_COEFFICIENT_INV); setValue(mapToScene(_event->pos()).x() - m_minimumValue - w); - emit wheeled(w * m_windowScale, _event->delta()); + emit EASY_GLOBALS.events.chartWheeled(w * m_windowScale, _event->delta()); } else { const auto x = (mapToScene(_event->pos()).x() - m_minimumValue) * m_windowScale; - emit wheeled(x, _event->delta()); + emit EASY_GLOBALS.events.chartWheeled(x, _event->delta()); } } void GraphicsSliderArea::resizeEvent(QResizeEvent* _event) { const int h = _event->size().height(); + + if (h == 0) + { + if (m_imageItem->isVisible()) + m_imageItem->cancelImageUpdate(); + + onWindowWidthChange(_event->size().width()); + + return; + } + if (_event->oldSize().height() != h) { + auto rect = scene()->sceneRect(); + const int sceneHeight = h - 2; - scene()->setSceneRect(0, -(sceneHeight >> 1), 500, sceneHeight); + const int top = -(sceneHeight >> 1); + scene()->setSceneRect(rect.left(), top, rect.width(), sceneHeight); const auto br = m_imageItem->boundingRect(); - m_imageItem->setBoundingRect(br.left(), scene()->sceneRect().top() + margin(), br.width(), sceneHeight - margins() - 1); + m_imageItem->setBoundingRect(br.left(), top + margin(), br.width(), sceneHeight - margins() - 1); + + rect = m_slider->rect(); + m_slider->setRect(rect.left(), top, rect.width(), sceneHeight); + + if (m_selectionIndicator->isVisible()) + { + rect = m_selectionIndicator->rect(); + m_selectionIndicator->setRect(rect.left(), top, rect.width(), sceneHeight); + } } onWindowWidthChange(_event->size().width()); + if (m_imageItem->isVisible()) m_imageItem->updateImage(); } diff --git a/profiler_gui/graphics_slider_area.h b/profiler_gui/graphics_slider_area.h index cbb8590..a33a363 100644 --- a/profiler_gui/graphics_slider_area.h +++ b/profiler_gui/graphics_slider_area.h @@ -101,12 +101,13 @@ protected: Qt::MouseButtons m_mouseButtons; GraphicsSliderItem* m_slider; GraphicsSliderItem* m_selectionIndicator; - GraphicsHistogramItem* m_histogramItem; GraphicsImageItem* m_imageItem; int m_fontHeight; bool m_bScrolling; bool m_bBindMode; bool m_bLocked; + bool m_bUpdatingPos; + bool m_bEmitChange; public: @@ -121,6 +122,7 @@ public: void wheelEvent(QWheelEvent* _event) override; void resizeEvent(QResizeEvent* _event) override; void dragEnterEvent(QDragEnterEvent*) override {} + void contextMenuEvent(QContextMenuEvent*) override {} virtual void clear(); @@ -148,19 +150,10 @@ public: void showSelectionIndicator(); void hideSelectionIndicator(); - inline void lock() { - m_bLocked = true; - } +public slots: - inline void unlock() { - m_bLocked = false; - } - -signals: - - void rangeChanged(); - void valueChanged(qreal _value); - void wheeled(qreal _mouseX, int _wheelDelta); + void lock() { m_bLocked = true; } + void unlock() { m_bLocked = false; } protected slots: diff --git a/profiler_gui/main_window.cpp b/profiler_gui/main_window.cpp index 4c54883..ec3a3d5 100644 --- a/profiler_gui/main_window.cpp +++ b/profiler_gui/main_window.cpp @@ -215,6 +215,7 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_theme("default"), m_lastAddress(" m_graphicsView->setAllowedAreas(Qt::AllDockWidgetAreas); auto graphicsView = new EasyGraphicsViewWidget(this); + graphicsView->setObjectName("ProfilerGUI_Diagram_GraphicsView"); m_graphicsView->setWidget(graphicsView); m_treeWidget = new EasyDockWidget("Hierarchy", this); @@ -449,6 +450,16 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_theme("default"), m_lastAddress(" emit EASY_GLOBALS.events.autoAdjustHistogramChanged(); }); + action = submenu->addAction("Automatically adjust chart height"); + action->setToolTip("Same as similar option for histogram\nbut used for arbitrary values charts."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.auto_adjust_chart_height); + connect(action, &QAction::triggered, [](bool _checked) + { + EASY_GLOBALS.auto_adjust_chart_height = _checked; + emit EASY_GLOBALS.events.autoAdjustChartChanged(); + }); + action = submenu->addAction("Use decorated thread names"); action->setToolTip("Add \'Thread\' word into thread name if there is no one already.\nExamples: \'Render\' will change to \'Render Thread\'\n\'WorkerThread\' will not change."); action->setCheckable(true); @@ -1236,7 +1247,6 @@ void EasyMainWindow::onEditBlocksClicked(bool) m_descTreeDialog = new QDialog(); m_descTreeDialog->setAttribute(Qt::WA_DeleteOnClose, true); m_descTreeDialog->setWindowTitle(EASY_DEFAULT_WINDOW_TITLE); - m_descTreeDialog->resize(800, 600); connect(m_descTreeDialog, &QDialog::finished, this, &This::onDescTreeDialogClose); auto l = new QVBoxLayout(m_descTreeDialog); @@ -1379,6 +1389,10 @@ void EasyMainWindow::loadSettings() if (!flag.isNull()) EASY_GLOBALS.auto_adjust_histogram_height = flag.toBool(); + flag = settings.value("auto_adjust_chart_height"); + if (!flag.isNull()) + EASY_GLOBALS.auto_adjust_chart_height = flag.toBool(); + flag = settings.value("display_only_frames_on_histogram"); if (!flag.isNull()) EASY_GLOBALS.display_only_frames_on_histogram = flag.toBool(); @@ -1434,6 +1448,16 @@ void EasyMainWindow::loadGeometry() if (!geometry.isEmpty()) restoreGeometry(geometry); + geometry = settings.value("fpsGeometry").toByteArray(); + if (!geometry.isEmpty()) + m_fpsViewer->restoreGeometry(geometry); + + geometry = settings.value("diagramGeometry").toByteArray(); + if (!geometry.isEmpty()) + m_graphicsView->restoreGeometry(geometry); + + static_cast(m_graphicsView->widget())->restore(settings); + auto state = settings.value("windowState").toByteArray(); if (!state.isEmpty()) restoreState(state); @@ -1447,7 +1471,12 @@ void EasyMainWindow::saveSettingsAndGeometry() settings.beginGroup("main"); settings.setValue("geometry", this->saveGeometry()); + settings.setValue("fpsGeometry", m_fpsViewer->saveGeometry()); + settings.setValue("diagramGeometry", m_graphicsView->saveGeometry()); + static_cast(m_graphicsView->widget())->save(settings); + settings.setValue("windowState", this->saveState()); + settings.setValue("last_files", m_lastFiles); settings.setValue("ip_address", m_lastAddress); settings.setValue("port", (quint32)m_lastPort); @@ -1470,6 +1499,7 @@ void EasyMainWindow::saveSettingsAndGeometry() settings.setValue("selecting_block_changes_thread", EASY_GLOBALS.selecting_block_changes_thread); settings.setValue("enable_event_indicators", EASY_GLOBALS.enable_event_markers); settings.setValue("auto_adjust_histogram_height", EASY_GLOBALS.auto_adjust_histogram_height); + settings.setValue("auto_adjust_chart_height", EASY_GLOBALS.auto_adjust_chart_height); settings.setValue("display_only_frames_on_histogram", EASY_GLOBALS.display_only_frames_on_histogram); settings.setValue("use_decorated_thread_name", EASY_GLOBALS.use_decorated_thread_name); settings.setValue("hex_thread_id", EASY_GLOBALS.hex_thread_id); diff --git a/profiler_gui/themes/default.css b/profiler_gui/themes/default.css index 4d944f7..5fe9c34 100644 --- a/profiler_gui/themes/default.css +++ b/profiler_gui/themes/default.css @@ -30,6 +30,12 @@ QToolTip { QGraphicsView { border: 1px solid #cccccc; } +QSplitter::handle:hover { + background-color: rgba(244, 67, 54, 0.5); } + +QSplitter::handle:pressed { + background-color: #f44336; } + /* ****************************************************************************************************************** */ QLineEdit, QComboBox, QSpinBox { height: 24px; diff --git a/profiler_gui/themes/default.scss b/profiler_gui/themes/default.scss index 2bd248c..a5d7814 100644 --- a/profiler_gui/themes/default.scss +++ b/profiler_gui/themes/default.scss @@ -55,6 +55,14 @@ QGraphicsView { border: 1px solid $BorderColor; } +QSplitter::handle:hover { + background-color: rgb_a($MainColor, 0.5); +} + +QSplitter::handle:pressed { + background-color: $MainColor; +} + /* ****************************************************************************************************************** */ QLineEdit, QComboBox, QSpinBox { height: $DefaultHeight;