From f0f82c01e26390f3ef672f35a3822d14737b1db6 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Mon, 8 Aug 2016 22:17:56 +0300 Subject: [PATCH] (ProfGraphicsView) Selecting items on scene; (ProfGraphicsView) Clickable indicators of ProfChronometerItem; (ProfTreeWidget) Jump to items on scene from ProfTreeWidget; (profiler GUI) Fixed typos and warnings; --- profiler_gui/blocks_graphics_view.cpp | 323 +++++++++++++++++++++++--- profiler_gui/blocks_graphics_view.h | 14 ++ profiler_gui/blocks_tree_widget.cpp | 96 +++++--- profiler_gui/blocks_tree_widget.h | 8 +- profiler_gui/globals.cpp | 2 +- profiler_gui/graphics_scrollbar.cpp | 36 ++- profiler_gui/graphics_scrollbar.h | 2 + profiler_gui/main_window.cpp | 2 + 8 files changed, 390 insertions(+), 93 deletions(-) diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp index 6a8522f..a94638c 100644 --- a/profiler_gui/blocks_graphics_view.cpp +++ b/profiler_gui/blocks_graphics_view.cpp @@ -157,7 +157,7 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* // Search for first visible top-level item - auto& level0 = m_levels[0]; + auto& level0 = m_levels.front(); auto first = ::std::lower_bound(level0.begin(), level0.end(), sceneLeft, [](const ::profiler_gui::ProfBlockItem& _item, qreal _value) { return _item.left() < _value; @@ -261,7 +261,16 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _painter->setBrush(brush); } - if (::profiler_gui::EASY_GLOBALS.draw_graphics_items_borders) + bool changepen = false; + if (!m_bTest && item.block->block_index == ::profiler_gui::EASY_GLOBALS.selected_block) + { + changepen = true; + QPen pen(Qt::SolidLine); + pen.setColor(Qt::red); + pen.setWidth(2); + _painter->setPen(pen); + } + else if (::profiler_gui::EASY_GLOBALS.draw_graphics_items_borders) { if (w < 3) { @@ -284,6 +293,14 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* rect.setRect(x, item.top(), w, item.totalHeight); _painter->drawRect(rect); + if (changepen) + { + if (previousPenStyle == Qt::NoPen) + _painter->setPen(Qt::NoPen); + else + _painter->setPen(BORDERS_COLOR); // restore pen for rectangle painting + } + if (next_level < levelsNumber && item.children_begin != -1) { // Mark that we would not paint children of current item @@ -326,7 +343,14 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _painter->setBrush(brush); } - if (::profiler_gui::EASY_GLOBALS.draw_graphics_items_borders && previousPenStyle != Qt::SolidLine) + if (!m_bTest && item.block->block_index == ::profiler_gui::EASY_GLOBALS.selected_block) + { + QPen pen(Qt::SolidLine); + pen.setColor(Qt::red); + pen.setWidth(2); + _painter->setPen(pen); + } + else if (::profiler_gui::EASY_GLOBALS.draw_graphics_items_borders && previousPenStyle != Qt::SolidLine) { // Restore pen for item which is wide enough to paint borders previousPenStyle = Qt::SolidLine; @@ -370,13 +394,9 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* // restore previous pen color if (previousPenStyle == Qt::NoPen) - { _painter->setPen(Qt::NoPen); - } else - { _painter->setPen(BORDERS_COLOR); // restore pen for rectangle painting - } // END Draw text~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ } } @@ -394,7 +414,7 @@ void ProfGraphicsItem::getBlocks(qreal _left, qreal _right, ::profiler_gui::Tree //} // Search for first visible top-level item - auto& level0 = m_levels[0]; + auto& level0 = m_levels.front(); auto first = ::std::lower_bound(level0.begin(), level0.end(), _left, [](const ::profiler_gui::ProfBlockItem& _item, qreal _value) { return _item.left() < _value; @@ -436,6 +456,128 @@ void ProfGraphicsItem::getBlocks(qreal _left, qreal _right, ::profiler_gui::Tree ////////////////////////////////////////////////////////////////////////// +const ::profiler_gui::ProfBlockItem* ProfGraphicsItem::intersect(const QPointF& _pos) const +{ + if (m_levels.empty() || m_levels.front().empty()) + { + return nullptr; + } + + const auto& level0 = m_levels.front(); + const auto top = level0.front().top(); + + if (top > _pos.y()) + { + return nullptr; + } + + const auto bottom = top + m_levels.size() * GRAPHICS_ROW_SIZE_FULL; + if (bottom < _pos.y()) + { + return nullptr; + } + + const unsigned int levelIndex = static_cast(_pos.y() - top) / GRAPHICS_ROW_SIZE_FULL; + if (levelIndex >= m_levels.size()) + { + return nullptr; + } + + const auto currentScale = view()->scale(); + unsigned int i = 0; + size_t itemIndex = -1; + size_t firstItem = 0, lastItem = static_cast(level0.size()); + while (i <= levelIndex) + { + const auto& level = m_levels[i]; + + // Search for first visible item + auto first = ::std::lower_bound(level.begin() + firstItem, level.begin() + lastItem, _pos.x(), [](const ::profiler_gui::ProfBlockItem& _item, qreal _value) + { + return _item.left() < _value; + }); + + if (first != level.end()) + { + itemIndex = first - level.begin(); + if (itemIndex != 0) + --itemIndex; + } + else + { + itemIndex = level.size() - 1; + } + + for (auto size = level.size(); itemIndex < size; ++itemIndex) + { + const auto& item = level[itemIndex]; + + if (item.left() > _pos.x()) + { + return nullptr; + } + + if (item.right() < _pos.x()) + { + continue; + } + + const auto w = item.width() * currentScale; + if (i == levelIndex || w < 20) + { + return &item; + } + + if (item.children_begin == -1) + { + if (itemIndex != 0) + { + auto j = itemIndex; + firstItem = 0; + do { + + --j; + const auto& item2 = level[j]; + if (item2.children_begin != -1) + { + firstItem = item2.children_begin; + break; + } + + } while (j != 0); + } + else + { + firstItem = 0; + } + } + else + { + firstItem = item.children_begin; + } + + lastItem = m_levels[i + 1].size(); + for (auto j = itemIndex + 1; j < size; ++j) + { + const auto& item2 = level[j]; + if (item2.children_begin != -1) + { + lastItem = item2.children_begin; + break; + } + } + + break; + } + + ++i; + } + + return nullptr; +} + +////////////////////////////////////////////////////////////////////////// + void ProfGraphicsItem::setBackgroundColor(QRgb _color) { m_backgroundColor = _color; @@ -503,7 +645,7 @@ unsigned int ProfGraphicsItem::addItem(unsigned short _level) ////////////////////////////////////////////////////////////////////////// -ProfChronometerItem::ProfChronometerItem(bool _main) : QGraphicsItem(), m_font(QFont("CourierNew", 16, 2)), m_color(CHRONOMETER_COLOR), m_left(0), m_right(0), m_bMain(_main), m_bReverse(false) +ProfChronometerItem::ProfChronometerItem(bool _main) : QGraphicsItem(), m_font(QFont("CourierNew", 16, 2)), m_color(CHRONOMETER_COLOR), m_left(0), m_right(0), m_bMain(_main), m_bReverse(false), m_bHover(false) { setZValue(10); m_indicator.reserve(3); @@ -526,34 +668,41 @@ void ProfChronometerItem::paint(QPainter* _painter, const QStyleOptionGraphicsIt const auto visibleSceneRect = sceneView->visibleSceneRect(); auto sceneLeft = offset, sceneRight = offset + visibleSceneRect.width() / currentScale; + if (m_bMain) + m_indicator.clear(); + if (m_left > sceneRight || m_right < sceneLeft) { // This item is out of screen if (m_bMain) { + const int size = m_bHover ? 12 : 10; auto vcenter = visibleSceneRect.top() + visibleSceneRect.height() * 0.5; + auto color = QColor::fromRgb(m_color.rgb()); + auto pen = _painter->pen(); + pen.setColor(color); m_indicator.clear(); if (m_left > sceneRight) { sceneRight = (sceneRight - offset) * currentScale; - m_indicator.push_back(QPointF(sceneRight - 10, vcenter - 10)); + m_indicator.push_back(QPointF(sceneRight - size, vcenter - size)); m_indicator.push_back(QPointF(sceneRight, vcenter)); - m_indicator.push_back(QPointF(sceneRight - 10, vcenter + 10)); + m_indicator.push_back(QPointF(sceneRight - size, vcenter + size)); } else { sceneLeft = (sceneLeft - offset) * currentScale; - m_indicator.push_back(QPointF(sceneLeft + 10, vcenter - 10)); + m_indicator.push_back(QPointF(sceneLeft + size, vcenter - size)); m_indicator.push_back(QPointF(sceneLeft, vcenter)); - m_indicator.push_back(QPointF(sceneLeft + 10, vcenter + 10)); + m_indicator.push_back(QPointF(sceneLeft + size, vcenter + size)); } _painter->save(); _painter->setTransform(QTransform::fromTranslate(-x(), -y()), true); - _painter->setBrush(QColor::fromRgb(m_color.rgb())); - _painter->setPen(Qt::NoPen); + _painter->setBrush(m_bHover ? QColor::fromRgb(0xffff0000) : color); + _painter->setPen(pen); _painter->drawPolygon(m_indicator); _painter->restore(); } @@ -637,6 +786,15 @@ void ProfChronometerItem::paint(QPainter* _painter, const QStyleOptionGraphicsIt // END Paint!~~~~~~~~~~~~~~~~~~~~~~ } +bool ProfChronometerItem::contains(const QPointF& _pos) const +{ + const auto sceneView = view(); + const auto clickX = (_pos.x() - sceneView->offset()) * sceneView->scale() - x(); + if (!m_indicator.empty() && m_indicator.containsPoint(QPointF(clickX, _pos.y()), Qt::OddEvenFill)) + return true; + return false; +} + void ProfChronometerItem::setColor(const QColor& _color) { m_color = _color; @@ -671,6 +829,11 @@ void ProfChronometerItem::setReverse(bool _reverse) m_bReverse = _reverse; } +void ProfChronometerItem::setHover(bool _hover) +{ + m_bHover = _hover; +} + const ProfGraphicsView* ProfChronometerItem::view() const { return static_cast(scene()->parent()); @@ -875,9 +1038,13 @@ void ProfGraphicsView::test(unsigned int _frames_number, unsigned int _total_ite m_offset = 0; updateVisibleSceneRect(); setScrollbar(m_pScrollbar); - m_pScrollbar->setMinimapFrom(0, longestItem->items(0)); - ::profiler_gui::EASY_GLOBALS.selected_thread = 0; - emit ::profiler_gui::EASY_GLOBALS.events.selectedThreadChanged(0); + + if (longestItem != nullptr) + { + m_pScrollbar->setMinimapFrom(0, longestItem->items(0)); + ::profiler_gui::EASY_GLOBALS.selected_thread = 0; + emit::profiler_gui::EASY_GLOBALS.events.selectedThreadChanged(0); + } // Create new chronometer item (previous item was destroyed by scene on scene()->clear()). // It will be shown on mouse right button click. @@ -926,6 +1093,11 @@ void ProfGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr // clear scene clearSilent(); + if (_blocksTree.empty()) + { + return; + } + // set new blocks tree // calculate scene size and fill it with items @@ -991,9 +1163,13 @@ void ProfGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr // Center view on the beginning of the scene updateVisibleSceneRect(); setScrollbar(m_pScrollbar); - m_pScrollbar->setMinimapFrom(longestItem->threadId(), longestItem->items(0)); - ::profiler_gui::EASY_GLOBALS.selected_thread = longestItem->threadId(); - emit ::profiler_gui::EASY_GLOBALS.events.selectedThreadChanged(longestItem->threadId()); + + if (longestItem != nullptr) + { + m_pScrollbar->setMinimapFrom(longestItem->threadId(), longestItem->items(0)); + ::profiler_gui::EASY_GLOBALS.selected_thread = longestItem->threadId(); + emit::profiler_gui::EASY_GLOBALS.events.selectedThreadChanged(longestItem->threadId()); + } // Create new chronometer item (previous item was destroyed by scene on scene()->clear()). // It will be shown on mouse right button click. @@ -1192,15 +1368,11 @@ void ProfGraphicsView::mousePressEvent(QMouseEvent* _event) } m_mouseButtons = _event->buttons(); - - if (m_mouseButtons & Qt::LeftButton) - { - m_mousePressPos = _event->globalPos(); - } + m_mousePressPos = _event->pos(); if (m_mouseButtons & Qt::RightButton) { - const auto mouseX = m_offset + mapToScene(_event->pos()).x() / m_scale; + const auto mouseX = m_offset + mapToScene(m_mousePressPos).x() / m_scale; m_chronometerItem->setLeftRight(mouseX, mouseX); m_chronometerItem->setReverse(false); m_chronometerItem->hide(); @@ -1219,11 +1391,12 @@ void ProfGraphicsView::mouseDoubleClickEvent(QMouseEvent* _event) } m_mouseButtons = _event->buttons(); + m_mousePressPos = _event->pos(); m_bDoubleClick = true; if (m_mouseButtons & Qt::LeftButton) { - const auto mouseX = m_offset + mapToScene(_event->pos()).x() / m_scale; + const auto mouseX = m_offset + mapToScene(m_mousePressPos).x() / m_scale; m_chronometerItemAux->setLeftRight(mouseX, mouseX); m_chronometerItemAux->setReverse(false); m_chronometerItemAux->hide(); @@ -1242,12 +1415,13 @@ void ProfGraphicsView::mouseReleaseEvent(QMouseEvent* _event) return; } - bool changedSelection = false; + bool changedSelection = false, clicked = false, changedSelectedItem = false; if (m_mouseButtons & Qt::RightButton) { if (m_chronometerItem->isVisible() && m_chronometerItem->width() < 1e-6) { m_chronometerItem->hide(); + m_chronometerItem->setHover(false); m_pScrollbar->hideChrono(); } @@ -1279,10 +1453,39 @@ void ProfGraphicsView::mouseReleaseEvent(QMouseEvent* _event) { m_chronometerItemAux->hide(); } + else if (m_chronometerItem->isVisible() && m_chronometerItem->hover()) + { + // 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()); + } + + if (!clicked && m_mouseMovePath.manhattanLength() < 5 && !m_bTest) + { + // Handle Click + + clicked = true; + auto mouseClickPos = mapToScene(m_mousePressPos); + mouseClickPos.setX(m_offset + mouseClickPos.x() / m_scale); + + // Try to select one of item blocks + for (auto item : m_items) + { + auto block = item->intersect(mouseClickPos); + if (block) + { + changedSelectedItem = true; + ::profiler_gui::EASY_GLOBALS.selected_block = block->block->block_index; + break; + } + } + } } m_bDoubleClick = false; m_mouseButtons = _event->buttons(); + m_mouseMovePath = QPoint(); _event->accept(); if (changedSelection) @@ -1291,6 +1494,15 @@ void ProfGraphicsView::mouseReleaseEvent(QMouseEvent* _event) } m_chronometerItem->setReverse(false); + + if (changedSelectedItem) + { + m_bUpdatingRect = true; + emit ::profiler_gui::EASY_GLOBALS.events.selectedBlockChanged(::profiler_gui::EASY_GLOBALS.selected_block); + m_bUpdatingRect = false; + + updateScene(); + } } ////////////////////////////////////////////////////////////////////////// @@ -1333,18 +1545,29 @@ bool ProfGraphicsView::moveChrono(ProfChronometerItem* _chronometerItem, qreal _ void ProfGraphicsView::mouseMoveEvent(QMouseEvent* _event) { - if (m_bEmpty) + if (m_bEmpty || (m_mouseButtons == 0 && !m_chronometerItem->isVisible())) { _event->accept(); return; } bool needUpdate = false; + const auto pos = _event->pos(); + const auto delta = pos - m_mousePressPos; + m_mousePressPos = pos; + + if (m_mouseButtons != 0) + { + m_mouseMovePath.setX(m_mouseMovePath.x() + ::std::abs(delta.x())); + m_mouseMovePath.setY(m_mouseMovePath.y() + ::std::abs(delta.y())); + } + + auto mouseScenePos = mapToScene(m_mousePressPos); + mouseScenePos.setX(m_offset + mouseScenePos.x() / m_scale); if (m_mouseButtons & Qt::RightButton) { - const auto mouseX = m_offset + mapToScene(_event->pos()).x() / m_scale; - bool showItem = moveChrono(m_chronometerItem, mouseX); + bool showItem = moveChrono(m_chronometerItem, mouseScenePos.x()); m_pScrollbar->setChronoPos(m_chronometerItem->left(), m_chronometerItem->right()); if (showItem) @@ -1359,15 +1582,10 @@ void ProfGraphicsView::mouseMoveEvent(QMouseEvent* _event) { if (m_bDoubleClick) { - const auto mouseX = m_offset + mapToScene(_event->pos()).x() / m_scale; - moveChrono(m_chronometerItemAux, mouseX); + moveChrono(m_chronometerItemAux, mouseScenePos.x()); } else { - const auto pos = _event->globalPos(); - const auto delta = pos - m_mousePressPos; - m_mousePressPos = pos; - auto vbar = verticalScrollBar(); m_bUpdatingRect = true; // Block scrollbars from updating scene rect to make it possible to do it only once @@ -1391,6 +1609,13 @@ void ProfGraphicsView::mouseMoveEvent(QMouseEvent* _event) needUpdate = true; } + if (m_chronometerItem->isVisible()) + { + auto prevValue = m_chronometerItem->hover(); + m_chronometerItem->setHover(m_chronometerItem->contains(mouseScenePos)); + needUpdate = needUpdate || (prevValue != m_chronometerItem->hover()); + } + if (needUpdate) { updateScene(); // repaint scene @@ -1424,6 +1649,7 @@ void ProfGraphicsView::initMode() connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &This::onScrollbarValueChange); connect(&m_flickerTimer, &QTimer::timeout, this, &This::onFlickerTimeout); connect(&::profiler_gui::EASY_GLOBALS.events, &::profiler_gui::ProfGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange); + connect(&::profiler_gui::EASY_GLOBALS.events, &::profiler_gui::ProfGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange); } ////////////////////////////////////////////////////////////////////////// @@ -1515,6 +1741,27 @@ void ProfGraphicsView::onSelectedThreadChange(::profiler::thread_id_t _id) ////////////////////////////////////////////////////////////////////////// +void ProfGraphicsView::onSelectedBlockChange(unsigned int _block_index) +{ + if (!m_bUpdatingRect) + { + if (_block_index < ::profiler_gui::EASY_GLOBALS.gui_blocks.size()) + { + // Scroll to item + + const auto& guiblock = ::profiler_gui::EASY_GLOBALS.gui_blocks[_block_index]; + const auto& item = guiblock.graphics_item->items(guiblock.graphics_item_level)[guiblock.graphics_item_index]; + + m_flickerSpeedX = m_flickerSpeedY = 0; + m_pScrollbar->setValue(item.left() + item.width() * 0.5 - m_pScrollbar->sliderHalfWidth()); + } + + updateScene(); + } +} + +////////////////////////////////////////////////////////////////////////// + ProfGraphicsViewWidget::ProfGraphicsViewWidget(bool _test) : QWidget(nullptr) , m_scrollbar(new ProfGraphicsScrollbar(nullptr)) diff --git a/profiler_gui/blocks_graphics_view.h b/profiler_gui/blocks_graphics_view.h index 4814f1d..42d4caf 100644 --- a/profiler_gui/blocks_graphics_view.h +++ b/profiler_gui/blocks_graphics_view.h @@ -127,6 +127,8 @@ public: \param _blocks Reference to the array of selected blocks */ void getBlocks(qreal _left, qreal _right, ::profiler_gui::TreeBlocks& _blocks) const; + const ::profiler_gui::ProfBlockItem* intersect(const QPointF& _pos) const; + private: ///< Returns pointer to the ProfGraphicsView widget. @@ -145,6 +147,7 @@ class ProfChronometerItem : public QGraphicsItem qreal m_left, m_right; ///< Left and right bounds of the selection zone bool m_bMain; ///< Is this chronometer main (true, by default) bool m_bReverse; + bool m_bHover; ///< Mouse hover above indicator public: @@ -169,6 +172,15 @@ public: void setReverse(bool _reverse); + void setHover(bool _hover); + + bool contains(const QPointF& _pos) const; + + inline bool hover() const + { + return m_bHover; + } + inline bool reverse() const { return m_bReverse; @@ -215,6 +227,7 @@ private: 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 :( QPoint m_mousePressPos; ///< Last mouse global position (used by mousePressEvent and mouseMoveEvent) + QPoint m_mouseMovePath; ///< Mouse move path between press and release of any button Qt::MouseButtons m_mouseButtons; ///< Pressed mouse buttons ProfGraphicsScrollbar* m_pScrollbar; ///< Pointer to the graphics scrollbar widget ProfChronometerItem* m_chronometerItem; ///< Pointer to the ProfChronometerItem which is displayed when you press right mouse button and move mouse left or right. This item is used to select blocks to display in tree widget. @@ -278,6 +291,7 @@ private slots: void onGraphicsScrollbarValueChange(qreal); void onFlickerTimeout(); void onSelectedThreadChange(::profiler::thread_id_t _id); + void onSelectedBlockChange(unsigned int _block_index); public: diff --git a/profiler_gui/blocks_tree_widget.cpp b/profiler_gui/blocks_tree_widget.cpp index 43927e0..13c7df6 100644 --- a/profiler_gui/blocks_tree_widget.cpp +++ b/profiler_gui/blocks_tree_widget.cpp @@ -252,6 +252,7 @@ ProfTreeWidget::ProfTreeWidget(QWidget* _parent) : Parent(_parent), m_beginTime( header->setText(COL_DURATION, "Duration"); header->setText(COL_SELF_DURATION, "Self Dur."); + //header->setToolTip(COL_SELF_DURATION, ""); header->setText(COL_DURATION_SUM_PER_PARENT, "Tot. Dur./Parent"); header->setText(COL_DURATION_SUM_PER_FRAME, "Tot. Dur./Frame"); header->setText(COL_DURATION_SUM_PER_THREAD, "Tot. Dur./Thread"); @@ -283,6 +284,7 @@ ProfTreeWidget::ProfTreeWidget(QWidget* _parent) : Parent(_parent), m_beginTime( setHeaderItem(header); connect(&::profiler_gui::EASY_GLOBALS.events, &::profiler_gui::ProfGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange); + connect(&::profiler_gui::EASY_GLOBALS.events, &::profiler_gui::ProfGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange); loadSettings(); } @@ -332,6 +334,7 @@ void ProfTreeWidget::setTreeBlocks(const ::profiler_gui::TreeBlocks& _blocks, :: resizeColumnToContents(COL_NAME); connect(this, &Parent::itemExpanded, this, &This::onItemExpand); + onSelectedBlockChange(::profiler_gui::EASY_GLOBALS.selected_block); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -813,30 +816,46 @@ void ProfTreeWidget::contextMenuEvent(QContextMenuEvent* _event) connect(action, &QAction::triggered, this, &This::onColorizeRowsTriggered); menu.addAction(action); - if (item != nullptr && col >= 0) + if (item != nullptr) { - switch (col) - { - case COL_MIN_PER_THREAD: - case COL_MIN_PER_PARENT: - case COL_MIN_PER_FRAME: - { - menu.addSeparator(); - auto itemAction = new ProfItemAction("Jump to such item", item); - connect(itemAction, &ProfItemAction::clicked, this, &This::onJumpToMinItemClicked); - menu.addAction(itemAction); - break; - } + auto itemAction = new ProfItemAction("Show this item on scene", item->block()->block_index); + itemAction->setToolTip("Scroll graphics scene to current item in the tree"); + connect(itemAction, &ProfItemAction::clicked, this, &This::onJumpToItemClicked); + menu.addAction(itemAction); - case COL_MAX_PER_THREAD: - case COL_MAX_PER_PARENT: - case COL_MAX_PER_FRAME: + if (col >= 0) + { + switch (col) { - menu.addSeparator(); - auto itemAction = new ProfItemAction("Jump to such item", item); - connect(itemAction, &ProfItemAction::clicked, this, &This::onJumpToMaxItemClicked); - menu.addAction(itemAction); - break; + case COL_MIN_PER_THREAD: + case COL_MIN_PER_PARENT: + case COL_MIN_PER_FRAME: + case COL_MAX_PER_THREAD: + case COL_MAX_PER_PARENT: + case COL_MAX_PER_FRAME: + { + unsigned int i = -1; + switch (col) + { + case COL_MIN_PER_THREAD: i = item->block()->per_thread_stats->min_duration_block; break; + case COL_MIN_PER_PARENT: i = item->block()->per_parent_stats->min_duration_block; break; + case COL_MIN_PER_FRAME: i = item->block()->per_frame_stats->min_duration_block; break; + case COL_MAX_PER_THREAD: i = item->block()->per_thread_stats->max_duration_block; break; + case COL_MAX_PER_PARENT: i = item->block()->per_parent_stats->max_duration_block; break; + case COL_MAX_PER_FRAME: i = item->block()->per_frame_stats->max_duration_block; break; + } + + if (i != -1) + { + menu.addSeparator(); + itemAction = new ProfItemAction("Jump to such item", i); + itemAction->setToolTip("Jump to item with min/max duration (depending on clicked column)"); + connect(itemAction, &ProfItemAction::clicked, this, &This::onJumpToItemClicked); + menu.addAction(itemAction); + } + + break; + } } } } @@ -862,24 +881,10 @@ void ProfTreeWidget::contextMenuEvent(QContextMenuEvent* _event) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void ProfTreeWidget::onJumpToMinItemClicked(ProfTreeWidgetItem* _item) +void ProfTreeWidget::onJumpToItemClicked(unsigned int _block_index) { - auto item = ::profiler_gui::EASY_GLOBALS.gui_blocks[_item->block()->per_thread_stats->min_duration_block].tree_item; - if (item != nullptr) - { - scrollToItem(item, QAbstractItemView::PositionAtCenter); - setCurrentItem(item); - } -} - -void ProfTreeWidget::onJumpToMaxItemClicked(ProfTreeWidgetItem* _item) -{ - auto item = ::profiler_gui::EASY_GLOBALS.gui_blocks[_item->block()->per_thread_stats->max_duration_block].tree_item; - if (item != nullptr) - { - scrollToItem(item, QAbstractItemView::PositionAtCenter); - setCurrentItem(item); - } + ::profiler_gui::EASY_GLOBALS.selected_block = _block_index; + emit ::profiler_gui::EASY_GLOBALS.events.selectedBlockChanged(_block_index); } void ProfTreeWidget::onCollapseAllClicked(bool) @@ -958,6 +963,21 @@ void ProfTreeWidget::onSelectedThreadChange(::profiler::thread_id_t _id) } } +void ProfTreeWidget::onSelectedBlockChange(unsigned int _block_index) +{ + if (_block_index < ::profiler_gui::EASY_GLOBALS.gui_blocks.size()) + { + auto item = ::profiler_gui::EASY_GLOBALS.gui_blocks[_block_index].tree_item; + if (item != nullptr) + scrollToItem(item, QAbstractItemView::PositionAtCenter); + setCurrentItem(item); + } + else + { + setCurrentItem(nullptr); + } +} + ////////////////////////////////////////////////////////////////////////// void ProfTreeWidget::resizeColumnsToContents() diff --git a/profiler_gui/blocks_tree_widget.h b/profiler_gui/blocks_tree_widget.h index baf4b53..8b52e77 100644 --- a/profiler_gui/blocks_tree_widget.h +++ b/profiler_gui/blocks_tree_widget.h @@ -94,7 +94,7 @@ public: \ private: \ void onToggle(bool) { emit clicked(m_item); } -DECLARE_QACTION(ProfItemAction, ProfTreeWidgetItem*) signals: void clicked(ProfTreeWidgetItem* _item); }; +DECLARE_QACTION(ProfItemAction, unsigned int) signals: void clicked(unsigned int _item); }; DECLARE_QACTION(ProfHideShowColumnAction, int) signals: void clicked(int _item); }; #undef DECLARE_QACTION @@ -144,9 +144,7 @@ protected: private slots: - void onJumpToMinItemClicked(ProfTreeWidgetItem* _item); - - void onJumpToMaxItemClicked(ProfTreeWidgetItem* _item); + void onJumpToItemClicked(unsigned int _block_index); void onCollapseAllClicked(bool); @@ -162,6 +160,8 @@ private slots: void onSelectedThreadChange(::profiler::thread_id_t _id); + void onSelectedBlockChange(unsigned int _block_index); + void resizeColumnsToContents(); void onHideShowColumn(int _column); diff --git a/profiler_gui/globals.cpp b/profiler_gui/globals.cpp index ad3d909..1c27624 100644 --- a/profiler_gui/globals.cpp +++ b/profiler_gui/globals.cpp @@ -38,7 +38,7 @@ namespace profiler_gui { ProfGlobals::ProfGlobals() : selected_thread(0) - , selected_block(0) + , selected_block(-1) , draw_graphics_items_borders(true) { diff --git a/profiler_gui/graphics_scrollbar.cpp b/profiler_gui/graphics_scrollbar.cpp index 9e207ca..5de4eb0 100644 --- a/profiler_gui/graphics_scrollbar.cpp +++ b/profiler_gui/graphics_scrollbar.cpp @@ -206,21 +206,23 @@ void ProfMinimapItem::setSource(::profiler::thread_id_t _thread_id, const ::prof { m_pSource = nullptr; } - - m_maxDuration = 0; - m_minDuration = 1e30; - for (const auto& item : *m_pSource) + else { - auto w = item.width(); - - if (w > m_maxDuration) + m_maxDuration = 0; + m_minDuration = 1e30; + for (const auto& item : *m_pSource) { - m_maxDuration = item.width(); - } + auto w = item.width(); - if (w < m_minDuration) - { - m_minDuration = w; + if (w > m_maxDuration) + { + m_maxDuration = item.width(); + } + + if (w < m_minDuration) + { + m_minDuration = w; + } } } } @@ -325,6 +327,16 @@ qreal ProfGraphicsScrollbar::value() const return m_value; } +qreal ProfGraphicsScrollbar::sliderWidth() const +{ + return m_slider->width(); +} + +qreal ProfGraphicsScrollbar::sliderHalfWidth() const +{ + return m_slider->halfwidth(); +} + ////////////////////////////////////////////////////////////////////////// void ProfGraphicsScrollbar::setValue(qreal _value) diff --git a/profiler_gui/graphics_scrollbar.h b/profiler_gui/graphics_scrollbar.h index 28e9a96..3297200 100644 --- a/profiler_gui/graphics_scrollbar.h +++ b/profiler_gui/graphics_scrollbar.h @@ -177,6 +177,8 @@ public: qreal maximum() const; qreal range() const; qreal value() const; + qreal sliderWidth() const; + qreal sliderHalfWidth() const; void setValue(qreal _value); void setRange(qreal _minValue, qreal _maxValue); diff --git a/profiler_gui/main_window.cpp b/profiler_gui/main_window.cpp index 24f897d..cea3d12 100644 --- a/profiler_gui/main_window.cpp +++ b/profiler_gui/main_window.cpp @@ -144,6 +144,7 @@ void ProfMainWindow::onReloadFileClicked(bool) static_cast(m_treeWidget->widget())->clearSilent(true); ::profiler_gui::EASY_GLOBALS.selected_thread = 0; + ::profiler_gui::EASY_GLOBALS.selected_block = -1; ::profiler_gui::EASY_GLOBALS.profiler_blocks.swap(prof_blocks); ::profiler_gui::EASY_GLOBALS.gui_blocks.resize(nblocks); memset(::profiler_gui::EASY_GLOBALS.gui_blocks.data(), 0, sizeof(::profiler_gui::ProfBlock) * nblocks); @@ -171,6 +172,7 @@ void ProfMainWindow::onTestViewportClicked(bool) ::profiler_gui::EASY_GLOBALS.gui_blocks.clear(); ::profiler_gui::EASY_GLOBALS.profiler_blocks.clear(); ::profiler_gui::EASY_GLOBALS.selected_thread = 0; + ::profiler_gui::EASY_GLOBALS.selected_block = -1; view->test(18000, 40000000, 2); //view->test(3, 300, 1);