easy_graphics_item.cpp 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474
  1. /************************************************************************
  2. * file name : easy_graphics_item.cpp
  3. * ----------------- :
  4. * creation time : 2016/09/15
  5. * author : Victor Zarubkin
  6. * email : [email protected]
  7. * ----------------- :
  8. * description : The file contains implementation of EasyGraphicsItem.
  9. * ----------------- :
  10. * change log : * 2016/09/15 Victor Zarubkin: Moved sources from blocks_graphics_view.cpp
  11. * :
  12. * : *
  13. * ----------------- :
  14. * license : Lightweight profiler library for c++
  15. * : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin
  16. * :
  17. * : Licensed under either of
  18. * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
  19. * : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
  20. * : at your option.
  21. * :
  22. * : The MIT License
  23. * :
  24. * : Permission is hereby granted, free of charge, to any person obtaining a copy
  25. * : of this software and associated documentation files (the "Software"), to deal
  26. * : in the Software without restriction, including without limitation the rights
  27. * : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  28. * : of the Software, and to permit persons to whom the Software is furnished
  29. * : to do so, subject to the following conditions:
  30. * :
  31. * : The above copyright notice and this permission notice shall be included in all
  32. * : copies or substantial portions of the Software.
  33. * :
  34. * : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  35. * : INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  36. * : PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  37. * : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  38. * : TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  39. * : USE OR OTHER DEALINGS IN THE SOFTWARE.
  40. * :
  41. * : The Apache License, Version 2.0 (the "License")
  42. * :
  43. * : You may not use this file except in compliance with the License.
  44. * : You may obtain a copy of the License at
  45. * :
  46. * : http://www.apache.org/licenses/LICENSE-2.0
  47. * :
  48. * : Unless required by applicable law or agreed to in writing, software
  49. * : distributed under the License is distributed on an "AS IS" BASIS,
  50. * : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  51. * : See the License for the specific language governing permissions and
  52. * : limitations under the License.
  53. ************************************************************************/
  54. #include <QGraphicsScene>
  55. #include <QDebug>
  56. #include <algorithm>
  57. #include "easy_graphics_item.h"
  58. #include "blocks_graphics_view.h"
  59. #include "globals.h"
  60. //////////////////////////////////////////////////////////////////////////
  61. //////////////////////////////////////////////////////////////////////////
  62. enum BlockItemState : int8_t
  63. {
  64. BLOCK_ITEM_DO_PAINT_FIRST = -2,
  65. BLOCK_ITEM_DO_NOT_PAINT = -1,
  66. BLOCK_ITEM_UNCHANGED,
  67. BLOCK_ITEM_DO_PAINT
  68. };
  69. //////////////////////////////////////////////////////////////////////////
  70. const int MIN_SYNC_SPACING = 1;
  71. const int MIN_SYNC_SIZE = 3;
  72. const QRgb BORDERS_COLOR = ::profiler::colors::Grey600 & 0x00ffffff;// 0x00686868;
  73. inline QRgb selectedItemBorderColor(::profiler::color_t _color) {
  74. return ::profiler_gui::isLightColor(_color, 192) ? ::profiler::colors::Black : ::profiler::colors::RichRed;
  75. }
  76. const QPen HIGHLIGHTER_PEN = ([]() -> QPen { QPen p(::profiler::colors::Black); p.setStyle(Qt::DotLine); p.setWidth(2); return p; })();
  77. const auto ITEMS_FONT = ::profiler_gui::EFont("Helvetica", 10, QFont::Medium);
  78. const auto SELECTED_ITEM_FONT = ::profiler_gui::EFont("Helvetica", 10, QFont::Bold);
  79. #ifdef max
  80. #undef max
  81. #endif
  82. #ifdef min
  83. #undef min
  84. #endif
  85. //////////////////////////////////////////////////////////////////////////
  86. EasyGraphicsItem::EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot& _root)
  87. : QGraphicsItem(nullptr)
  88. , m_threadName(::profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, _root, EASY_GLOBALS.hex_thread_id))
  89. , m_pRoot(&_root)
  90. , m_index(_index)
  91. {
  92. }
  93. EasyGraphicsItem::~EasyGraphicsItem()
  94. {
  95. }
  96. void EasyGraphicsItem::validateName()
  97. {
  98. m_threadName = ::profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, *m_pRoot, EASY_GLOBALS.hex_thread_id);
  99. }
  100. const EasyGraphicsView* EasyGraphicsItem::view() const
  101. {
  102. return static_cast<const EasyGraphicsView*>(scene()->parent());
  103. }
  104. //////////////////////////////////////////////////////////////////////////
  105. QRectF EasyGraphicsItem::boundingRect() const
  106. {
  107. return m_boundingRect;
  108. }
  109. //////////////////////////////////////////////////////////////////////////
  110. struct EasyPainterInformation EASY_FINAL
  111. {
  112. const QRectF visibleSceneRect;
  113. QRectF rect;
  114. QBrush brush;
  115. const qreal visibleBottom;
  116. const qreal currentScale;
  117. const qreal offset;
  118. const qreal sceneLeft;
  119. const qreal sceneRight;
  120. const qreal dx;
  121. QRgb previousColor;
  122. QRgb textColor;
  123. Qt::PenStyle previousPenStyle;
  124. bool is_light;
  125. bool selectedItemsWasPainted;
  126. explicit EasyPainterInformation(const EasyGraphicsView* sceneView)
  127. : visibleSceneRect(sceneView->visibleSceneRect())
  128. , visibleBottom(visibleSceneRect.bottom() - 1)
  129. , currentScale(sceneView->scale())
  130. , offset(sceneView->offset())
  131. , sceneLeft(offset)
  132. , sceneRight(offset + visibleSceneRect.width() / currentScale)
  133. , dx(offset * currentScale)
  134. , previousColor(0)
  135. , textColor(0)
  136. , previousPenStyle(Qt::NoPen)
  137. , is_light(false)
  138. , selectedItemsWasPainted(false)
  139. {
  140. brush.setStyle(Qt::SolidPattern);
  141. }
  142. EasyPainterInformation() = delete;
  143. };
  144. #ifdef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT
  145. void EasyGraphicsItem::paintChildren(const float _minWidth, const int _narrowSizeHalf, const uint8_t _levelsNumber, QPainter* _painter, struct EasyPainterInformation& p, ::profiler_gui::EasyBlockItem& _item, const ::profiler_gui::EasyBlock& _itemBlock, RightBounds& _rightBounds, uint8_t _level, int8_t _mode)
  146. {
  147. if (_level >= _levelsNumber || _itemBlock.tree.children.empty())
  148. return;
  149. const auto top = levelY(_level);
  150. if (top > p.visibleBottom)
  151. return;
  152. qreal& prevRight = _rightBounds[_level];
  153. auto& level = m_levels[_level];
  154. const short next_level = _level + 1;
  155. uint32_t neighbours = (uint32_t)_itemBlock.tree.children.size();
  156. uint32_t last = neighbours - 1;
  157. uint32_t neighbour = 0;
  158. if (_mode == BLOCK_ITEM_DO_PAINT_FIRST)
  159. {
  160. neighbour = last = _item.max_depth_child;
  161. neighbours = neighbour + 1;
  162. }
  163. for (uint32_t i = _item.children_begin + neighbour; neighbour < neighbours; ++i, ++neighbour)
  164. {
  165. auto& item = level[i];
  166. if (item.left() > p.sceneRight)
  167. break; // This is first totally invisible item. No need to check other items.
  168. if (item.right() < p.sceneLeft)
  169. continue; // This item is not visible
  170. const auto& itemBlock = easyBlock(item.block);
  171. const uint16_t totalHeight = itemBlock.tree.depth * ::profiler_gui::GRAPHICS_ROW_SIZE_FULL + ::profiler_gui::GRAPHICS_ROW_SIZE;
  172. if ((top + totalHeight) < p.visibleSceneRect.top())
  173. continue; // This item is not visible
  174. const auto item_width = ::std::max(item.width(), _minWidth);
  175. auto x = item.left() * p.currentScale - p.dx;
  176. auto w = item_width * p.currentScale;
  177. //const auto right = x + w;
  178. if ((x + w) <= prevRight)
  179. {
  180. // This item is not visible
  181. if (!(EASY_GLOBALS.hide_narrow_children && w < EASY_GLOBALS.blocks_narrow_size))
  182. paintChildren(_minWidth, _narrowSizeHalf, _levelsNumber, _painter, p, item, itemBlock, _rightBounds, next_level, BLOCK_ITEM_DO_PAINT_FIRST);
  183. continue;
  184. }
  185. if (x < prevRight)
  186. {
  187. w -= prevRight - x;
  188. x = prevRight;
  189. }
  190. if (EASY_GLOBALS.hide_minsize_blocks && w < EASY_GLOBALS.blocks_size_min)
  191. continue; // Hide blocks (except top-level blocks) which width is less than 1 pixel
  192. const auto& itemDesc = easyDescriptor(itemBlock.tree.node->id());
  193. int h = 0, flags = 0;
  194. bool do_paint_children = false;
  195. if ((EASY_GLOBALS.hide_narrow_children && w < EASY_GLOBALS.blocks_narrow_size) || !itemBlock.expanded)
  196. {
  197. // Items which width is less than 20 will be painted as big rectangles which are hiding it's children
  198. //x = item.left() * p.currentScale - p.dx;
  199. h = totalHeight;
  200. const auto dh = top + h - p.visibleBottom;
  201. if (dh > 0)
  202. h -= dh;
  203. if (item.block == EASY_GLOBALS.selected_block)
  204. p.selectedItemsWasPainted = true;
  205. const bool colorChange = (p.previousColor != itemDesc.color());
  206. if (colorChange)
  207. {
  208. // Set background color brush for rectangle
  209. p.previousColor = itemDesc.color();
  210. //p.inverseColor = 0xffffffff - p.previousColor;
  211. p.is_light = ::profiler_gui::isLightColor(p.previousColor);
  212. p.textColor = ::profiler_gui::textColorForFlag(p.is_light);
  213. p.brush.setColor(p.previousColor);
  214. _painter->setBrush(p.brush);
  215. }
  216. if (EASY_GLOBALS.highlight_blocks_with_same_id && (EASY_GLOBALS.selected_block_id == itemBlock.tree.node->id()
  217. || (::profiler_gui::is_max(EASY_GLOBALS.selected_block) && EASY_GLOBALS.selected_block_id == itemDesc.id())))
  218. {
  219. if (p.previousPenStyle != Qt::DotLine)
  220. {
  221. p.previousPenStyle = Qt::DotLine;
  222. _painter->setPen(HIGHLIGHTER_PEN);
  223. }
  224. }
  225. else if (EASY_GLOBALS.draw_graphics_items_borders)
  226. {
  227. if (p.previousPenStyle != Qt::SolidLine)// || colorChange)
  228. {
  229. // Restore pen for item which is wide enough to paint borders
  230. p.previousPenStyle = Qt::SolidLine;
  231. _painter->setPen(BORDERS_COLOR);//BORDERS_COLOR & inverseColor);
  232. }
  233. }
  234. else if (p.previousPenStyle != Qt::NoPen)
  235. {
  236. p.previousPenStyle = Qt::NoPen;
  237. _painter->setPen(Qt::NoPen);
  238. }
  239. const auto wprev = w;
  240. decltype(w) dw = 0;
  241. if (item.left() < p.sceneLeft)
  242. {
  243. // if item left border is out of screen then attach text to the left border of the screen
  244. // to ensure text is always visible for items presenting on the screen.
  245. w += (item.left() - p.sceneLeft) * p.currentScale;
  246. x = p.sceneLeft * p.currentScale - p.dx - 2;
  247. w += 2;
  248. dw = 2;
  249. }
  250. if (item.right() > p.sceneRight)
  251. {
  252. w -= (item.right() - p.sceneRight) * p.currentScale;
  253. w += 2;
  254. dw += 2;
  255. }
  256. if (w < EASY_GLOBALS.blocks_size_min)
  257. w = EASY_GLOBALS.blocks_size_min;
  258. // Draw rectangle
  259. p.rect.setRect(x, top, w, h);
  260. _painter->drawRect(p.rect);
  261. prevRight = p.rect.right() + EASY_GLOBALS.blocks_spacing;
  262. //skip_children(next_level, item.children_begin);
  263. if (wprev < EASY_GLOBALS.blocks_narrow_size)
  264. continue;
  265. if (totalHeight > ::profiler_gui::GRAPHICS_ROW_SIZE)
  266. flags = Qt::AlignCenter;
  267. else if (!(item.width() < 1))
  268. flags = Qt::AlignHCenter;
  269. if (dw > 1)
  270. {
  271. w -= dw;
  272. x += 2;
  273. }
  274. }
  275. else
  276. {
  277. if (item.block == EASY_GLOBALS.selected_block)
  278. p.selectedItemsWasPainted = true;
  279. const bool colorChange = (p.previousColor != itemDesc.color());
  280. if (colorChange)
  281. {
  282. // Set background color brush for rectangle
  283. p.previousColor = itemDesc.color();
  284. //p.inverseColor = 0xffffffff - p.previousColor;
  285. p.is_light = ::profiler_gui::isLightColor(p.previousColor);
  286. p.textColor = ::profiler_gui::textColorForFlag(p.is_light);
  287. p.brush.setColor(p.previousColor);
  288. _painter->setBrush(p.brush);
  289. }
  290. if (EASY_GLOBALS.highlight_blocks_with_same_id && (EASY_GLOBALS.selected_block_id == itemBlock.tree.node->id()
  291. || (::profiler_gui::is_max(EASY_GLOBALS.selected_block) && EASY_GLOBALS.selected_block_id == itemDesc.id())))
  292. {
  293. if (p.previousPenStyle != Qt::DotLine)
  294. {
  295. p.previousPenStyle = Qt::DotLine;
  296. _painter->setPen(HIGHLIGHTER_PEN);
  297. }
  298. }
  299. else if (EASY_GLOBALS.draw_graphics_items_borders)
  300. {
  301. if (p.previousPenStyle != Qt::SolidLine)// || colorChange)
  302. {
  303. // Restore pen for item which is wide enough to paint borders
  304. p.previousPenStyle = Qt::SolidLine;
  305. _painter->setPen(BORDERS_COLOR);// BORDERS_COLOR & inverseColor);
  306. }
  307. }
  308. else if (p.previousPenStyle != Qt::NoPen)
  309. {
  310. p.previousPenStyle = Qt::NoPen;
  311. _painter->setPen(Qt::NoPen);
  312. }
  313. // Draw rectangle
  314. //x = item.left() * currentScale - p.dx;
  315. h = ::profiler_gui::GRAPHICS_ROW_SIZE;
  316. const auto dh = top + h - p.visibleBottom;
  317. if (dh > 0)
  318. h -= dh;
  319. const auto wprev = w;
  320. decltype(w) dw = 0;
  321. if (item.left() < p.sceneLeft)
  322. {
  323. // if item left border is out of screen then attach text to the left border of the screen
  324. // to ensure text is always visible for items presenting on the screen.
  325. w += (item.left() - p.sceneLeft) * p.currentScale;
  326. x = p.sceneLeft * p.currentScale - p.dx - 2;
  327. w += 2;
  328. dw = 2;
  329. }
  330. if (item.right() > p.sceneRight)
  331. {
  332. w -= (item.right() - p.sceneRight) * p.currentScale;
  333. w += 2;
  334. dw += 2;
  335. }
  336. if (w < EASY_GLOBALS.blocks_size_min)
  337. w = EASY_GLOBALS.blocks_size_min;
  338. p.rect.setRect(x, top, w, h);
  339. _painter->drawRect(p.rect);
  340. prevRight = p.rect.right() + EASY_GLOBALS.blocks_spacing;
  341. if (wprev < EASY_GLOBALS.blocks_narrow_size)
  342. {
  343. paintChildren(_minWidth, _narrowSizeHalf, _levelsNumber, _painter, p, item, itemBlock, _rightBounds, next_level, wprev < _narrowSizeHalf ? BLOCK_ITEM_DO_PAINT_FIRST : BLOCK_ITEM_DO_PAINT);
  344. continue;
  345. }
  346. if (!(item.width() < 1))
  347. flags = Qt::AlignHCenter;
  348. if (dw > 1)
  349. {
  350. w -= dw;
  351. x += 2;
  352. }
  353. do_paint_children = true;
  354. }
  355. // Draw text-----------------------------------
  356. p.rect.setRect(x + 1, top, w - 1, h);
  357. // text will be painted with inverse color
  358. //auto textColor = inverseColor < 0x00808080 ? profiler::colors::Black : profiler::colors::White;
  359. //if (textColor == previousColor) textColor = 0;
  360. _painter->setPen(p.textColor);
  361. if (item.block == EASY_GLOBALS.selected_block)
  362. _painter->setFont(SELECTED_ITEM_FONT);
  363. // drawing text
  364. auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : itemDesc.name();
  365. _painter->drawText(p.rect, flags, ::profiler_gui::toUnicode(name));
  366. // restore previous pen color
  367. if (p.previousPenStyle == Qt::NoPen)
  368. _painter->setPen(Qt::NoPen);
  369. else if (p.previousPenStyle == Qt::DotLine)
  370. {
  371. _painter->setPen(HIGHLIGHTER_PEN);
  372. }
  373. else
  374. _painter->setPen(BORDERS_COLOR);// BORDERS_COLOR & inverseColor); // restore pen for rectangle painting
  375. // restore font
  376. if (item.block == EASY_GLOBALS.selected_block)
  377. _painter->setFont(ITEMS_FONT);
  378. // END Draw text~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  379. if (do_paint_children)
  380. paintChildren(_minWidth, _narrowSizeHalf, _levelsNumber, _painter, p, item, itemBlock, _rightBounds, next_level, _mode);
  381. }
  382. }
  383. #endif
  384. void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*)
  385. {
  386. const bool gotItems = !m_levels.empty() && !m_levels.front().empty();
  387. const bool gotSync = !m_pRoot->sync.empty();
  388. if (!gotItems && !gotSync)
  389. {
  390. return;
  391. }
  392. EasyPainterInformation p(view());
  393. _painter->save();
  394. _painter->setFont(ITEMS_FONT);
  395. // Reset indices of first visible item for each layer
  396. const auto levelsNumber = levels();
  397. m_rightBounds[0] = -1e100;
  398. for (uint8_t i = 1; i < levelsNumber; ++i) {
  399. ::profiler_gui::set_max(m_levelsIndexes[i]);
  400. m_rightBounds[i] = -1e100;
  401. }
  402. // Search for first visible top-level item
  403. if (gotItems)
  404. {
  405. auto& level0 = m_levels.front();
  406. auto first = ::std::lower_bound(level0.begin(), level0.end(), p.sceneLeft, [](const ::profiler_gui::EasyBlockItem& _item, qreal _value)
  407. {
  408. return _item.left() < _value;
  409. });
  410. if (first != level0.end())
  411. {
  412. m_levelsIndexes[0] = first - level0.begin();
  413. if (m_levelsIndexes[0] > 0)
  414. m_levelsIndexes[0] -= 1;
  415. }
  416. else
  417. {
  418. m_levelsIndexes[0] = static_cast<unsigned int>(level0.size() - 1);
  419. }
  420. }
  421. // This is to make _painter->drawText() work properly
  422. // (it seems there is a bug in Qt5.6 when drawText called for big coordinates,
  423. // drawRect at the same time called for actually same coordinates
  424. // works fine without using this additional shifting)
  425. //const auto dx = p.offset * p.currentScale;
  426. // Shifting coordinates to current screen offset
  427. _painter->setTransform(QTransform::fromTranslate(0, -y()), true);
  428. if (EASY_GLOBALS.draw_graphics_items_borders)
  429. {
  430. p.previousPenStyle = Qt::SolidLine;
  431. _painter->setPen(BORDERS_COLOR);
  432. }
  433. else
  434. {
  435. _painter->setPen(Qt::NoPen);
  436. }
  437. const auto MIN_WIDTH = EASY_GLOBALS.enable_zero_length ? 0.f : 0.25f;
  438. // Iterate through layers and draw visible items
  439. if (gotItems)
  440. {
  441. const int narrow_size_half = EASY_GLOBALS.blocks_narrow_size >> 1;
  442. #ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT
  443. static const auto MAX_CHILD_INDEX = ::profiler_gui::numeric_max<decltype(::profiler_gui::EasyBlockItem::children_begin)>();
  444. auto const dont_skip_children = [this, &levelsNumber](short next_level, decltype(::profiler_gui::EasyBlockItem::children_begin) children_begin, int8_t _state)
  445. {
  446. if (next_level < levelsNumber && children_begin != MAX_CHILD_INDEX)
  447. {
  448. if (m_levelsIndexes[next_level] == MAX_CHILD_INDEX)
  449. {
  450. // Mark first potentially visible child item on next sublevel
  451. m_levelsIndexes[next_level] = children_begin;
  452. }
  453. // Mark children items that we want to draw them
  454. m_levels[next_level][children_begin].state = _state;
  455. }
  456. };
  457. #endif
  458. //size_t iterations = 0;
  459. #ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT
  460. for (uint8_t l = 0; l < levelsNumber; ++l)
  461. #else
  462. for (uint8_t l = 0; l < 1; ++l)
  463. #endif
  464. {
  465. auto& level = m_levels[l];
  466. const short next_level = l + 1;
  467. const auto top = levelY(l);
  468. if (top > p.visibleBottom)
  469. break;
  470. //qreal& prevRight = m_rightBounds[l];
  471. qreal prevRight = -1e100;
  472. uint32_t neighbour = 0;
  473. for (uint32_t i = m_levelsIndexes[l], end = static_cast<uint32_t>(level.size()); i < end; ++i, ++neighbour)
  474. {
  475. //++iterations;
  476. auto& item = level[i];
  477. if (item.left() > p.sceneRight)
  478. break; // This is first totally invisible item. No need to check other items.
  479. #ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT
  480. char state = BLOCK_ITEM_DO_PAINT;
  481. if (item.state != BLOCK_ITEM_UNCHANGED)
  482. {
  483. neighbour = 0; // first block in parent's children list
  484. state = item.state;
  485. item.state = BLOCK_ITEM_DO_NOT_PAINT;
  486. }
  487. #endif
  488. if (item.right() < p.sceneLeft)
  489. continue; // This item is not visible
  490. #ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT
  491. if (state == BLOCK_ITEM_DO_NOT_PAINT)
  492. {
  493. // This item is not visible
  494. if (neighbour < item.neighbours)
  495. i += item.neighbours - neighbour - 1; // Skip all neighbours
  496. continue;
  497. }
  498. if (state == BLOCK_ITEM_DO_PAINT_FIRST && item.children_begin == MAX_CHILD_INDEX && next_level < levelsNumber && neighbour < (item.neighbours-1))
  499. // Paint only first child which has own children
  500. continue; // This item has no children and would not be painted
  501. #endif
  502. const auto& itemBlock = easyBlock(item.block);
  503. const uint16_t totalHeight = itemBlock.tree.depth * ::profiler_gui::GRAPHICS_ROW_SIZE_FULL + ::profiler_gui::GRAPHICS_ROW_SIZE;
  504. if ((top + totalHeight) < p.visibleSceneRect.top())
  505. continue; // This item is not visible
  506. const auto item_width = ::std::max(item.width(), MIN_WIDTH);
  507. auto x = item.left() * p.currentScale - p.dx;
  508. auto w = item_width * p.currentScale;
  509. if ((x + w) <= prevRight)
  510. {
  511. // This item is not visible
  512. #ifdef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT
  513. if (!EASY_GLOBALS.hide_narrow_children || w >= EASY_GLOBALS.blocks_narrow_size)
  514. paintChildren(MIN_WIDTH, narrow_size_half, levelsNumber, _painter, p, item, itemBlock, m_rightBounds, next_level, BLOCK_ITEM_DO_PAINT_FIRST);
  515. #else
  516. if (!(EASY_GLOBALS.hide_narrow_children && w < EASY_GLOBALS.blocks_narrow_size) && l > 0)
  517. dont_skip_children(next_level, item.children_begin, BLOCK_ITEM_DO_PAINT_FIRST);
  518. #endif
  519. continue;
  520. }
  521. if (x < prevRight)
  522. {
  523. w -= prevRight - x;
  524. x = prevRight;
  525. }
  526. #ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT
  527. if (EASY_GLOBALS.hide_minsize_blocks && w < EASY_GLOBALS.blocks_size_min && l > 0)
  528. continue; // Hide blocks (except top-level blocks) which width is less than 1 pixel
  529. if (state == BLOCK_ITEM_DO_PAINT_FIRST && neighbour < item.neighbours)
  530. {
  531. // Paint only first child which has own children
  532. i += item.neighbours - neighbour - 1; // Skip all neighbours
  533. }
  534. #endif
  535. const auto& itemDesc = easyDescriptor(itemBlock.tree.node->id());
  536. int h = 0, flags = 0;
  537. #ifdef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT
  538. bool do_paint_children = false;
  539. #endif
  540. if ((EASY_GLOBALS.hide_narrow_children && w < EASY_GLOBALS.blocks_narrow_size) || !itemBlock.expanded)
  541. {
  542. // Items which width is less than 20 will be painted as big rectangles which are hiding it's children
  543. //x = item.left() * p.currentScale - p.dx;
  544. h = totalHeight;
  545. const auto dh = top + h - p.visibleBottom;
  546. if (dh > 0)
  547. h -= dh;
  548. if (item.block == EASY_GLOBALS.selected_block)
  549. p.selectedItemsWasPainted = true;
  550. const bool colorChange = (p.previousColor != itemDesc.color());
  551. if (colorChange)
  552. {
  553. // Set background color brush for rectangle
  554. p.previousColor = itemDesc.color();
  555. //p.inverseColor = 0xffffffff - p.previousColor;
  556. p.is_light = ::profiler_gui::isLightColor(p.previousColor);
  557. p.textColor = ::profiler_gui::textColorForFlag(p.is_light);
  558. p.brush.setColor(p.previousColor);
  559. _painter->setBrush(p.brush);
  560. }
  561. if (EASY_GLOBALS.highlight_blocks_with_same_id && (EASY_GLOBALS.selected_block_id == itemBlock.tree.node->id()
  562. || (::profiler_gui::is_max(EASY_GLOBALS.selected_block) && EASY_GLOBALS.selected_block_id == itemDesc.id())))
  563. {
  564. if (p.previousPenStyle != Qt::DotLine)
  565. {
  566. p.previousPenStyle = Qt::DotLine;
  567. _painter->setPen(HIGHLIGHTER_PEN);
  568. }
  569. }
  570. else if (EASY_GLOBALS.draw_graphics_items_borders)
  571. {
  572. if (p.previousPenStyle != Qt::SolidLine)// || colorChange)
  573. {
  574. // Restore pen for item which is wide enough to paint borders
  575. p.previousPenStyle = Qt::SolidLine;
  576. _painter->setPen(BORDERS_COLOR);//BORDERS_COLOR & inverseColor);
  577. }
  578. }
  579. else if (p.previousPenStyle != Qt::NoPen)
  580. {
  581. p.previousPenStyle = Qt::NoPen;
  582. _painter->setPen(Qt::NoPen);
  583. }
  584. const auto wprev = w;
  585. decltype(w) dw = 0;
  586. if (item.left() < p.sceneLeft)
  587. {
  588. // if item left border is out of screen then attach text to the left border of the screen
  589. // to ensure text is always visible for items presenting on the screen.
  590. w += (item.left() - p.sceneLeft) * p.currentScale;
  591. x = p.sceneLeft * p.currentScale - p.dx - 2;
  592. w += 2;
  593. dw = 2;
  594. }
  595. if (item.right() > p.sceneRight)
  596. {
  597. w -= (item.right() - p.sceneRight) * p.currentScale;
  598. w += 2;
  599. dw += 2;
  600. }
  601. if (w < EASY_GLOBALS.blocks_size_min)
  602. w = EASY_GLOBALS.blocks_size_min;
  603. // Draw rectangle
  604. p.rect.setRect(x, top, w, h);
  605. _painter->drawRect(p.rect);
  606. prevRight = p.rect.right() + EASY_GLOBALS.blocks_spacing;
  607. //skip_children(next_level, item.children_begin);
  608. if (wprev < EASY_GLOBALS.blocks_narrow_size)
  609. continue;
  610. if (totalHeight > ::profiler_gui::GRAPHICS_ROW_SIZE)
  611. flags = Qt::AlignCenter;
  612. else if (!(item.width() < 1))
  613. flags = Qt::AlignHCenter;
  614. if (dw > 1) {
  615. w -= dw;
  616. x += 2;
  617. }
  618. }
  619. else
  620. {
  621. if (item.block == EASY_GLOBALS.selected_block)
  622. p.selectedItemsWasPainted = true;
  623. const bool colorChange = (p.previousColor != itemDesc.color());
  624. if (colorChange)
  625. {
  626. // Set background color brush for rectangle
  627. p.previousColor = itemDesc.color();
  628. //p.inverseColor = 0xffffffff - p.previousColor;
  629. p.is_light = ::profiler_gui::isLightColor(p.previousColor);
  630. p.textColor = ::profiler_gui::textColorForFlag(p.is_light);
  631. p.brush.setColor(p.previousColor);
  632. _painter->setBrush(p.brush);
  633. }
  634. if (EASY_GLOBALS.highlight_blocks_with_same_id && (EASY_GLOBALS.selected_block_id == itemBlock.tree.node->id()
  635. || (::profiler_gui::is_max(EASY_GLOBALS.selected_block) && EASY_GLOBALS.selected_block_id == itemDesc.id())))
  636. {
  637. if (p.previousPenStyle != Qt::DotLine)
  638. {
  639. p.previousPenStyle = Qt::DotLine;
  640. _painter->setPen(HIGHLIGHTER_PEN);
  641. }
  642. }
  643. else if (EASY_GLOBALS.draw_graphics_items_borders)
  644. {
  645. if (p.previousPenStyle != Qt::SolidLine)// || colorChange)
  646. {
  647. // Restore pen for item which is wide enough to paint borders
  648. p.previousPenStyle = Qt::SolidLine;
  649. _painter->setPen(BORDERS_COLOR);// BORDERS_COLOR & inverseColor);
  650. }
  651. }
  652. else if (p.previousPenStyle != Qt::NoPen)
  653. {
  654. p.previousPenStyle = Qt::NoPen;
  655. _painter->setPen(Qt::NoPen);
  656. }
  657. // Draw rectangle
  658. //x = item.left() * currentScale - p.dx;
  659. h = ::profiler_gui::GRAPHICS_ROW_SIZE;
  660. const auto dh = top + h - p.visibleBottom;
  661. if (dh > 0)
  662. h -= dh;
  663. const auto wprev = w;
  664. decltype(w) dw = 0;
  665. if (item.left() < p.sceneLeft)
  666. {
  667. // if item left border is out of screen then attach text to the left border of the screen
  668. // to ensure text is always visible for items presenting on the screen.
  669. w += (item.left() - p.sceneLeft) * p.currentScale;
  670. x = p.sceneLeft * p.currentScale - p.dx - 2;
  671. w += 2;
  672. dw = 2;
  673. }
  674. if (item.right() > p.sceneRight)
  675. {
  676. w -= (item.right() - p.sceneRight) * p.currentScale;
  677. w += 2;
  678. dw += 2;
  679. }
  680. if (w < EASY_GLOBALS.blocks_size_min)
  681. w = EASY_GLOBALS.blocks_size_min;
  682. p.rect.setRect(x, top, w, h);
  683. _painter->drawRect(p.rect);
  684. prevRight = p.rect.right() + EASY_GLOBALS.blocks_spacing;
  685. if (wprev < EASY_GLOBALS.blocks_narrow_size)
  686. {
  687. #ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT
  688. dont_skip_children(next_level, item.children_begin, wprev < narrow_size_half ? BLOCK_ITEM_DO_PAINT_FIRST : BLOCK_ITEM_DO_PAINT);
  689. #else
  690. paintChildren(MIN_WIDTH, narrow_size_half, levelsNumber, _painter, p, item, itemBlock, m_rightBounds, next_level, wprev < narrow_size_half ? BLOCK_ITEM_DO_PAINT_FIRST : BLOCK_ITEM_DO_PAINT);
  691. #endif
  692. continue;
  693. }
  694. #ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT
  695. dont_skip_children(next_level, item.children_begin, BLOCK_ITEM_DO_PAINT);
  696. #endif
  697. if (!(item.width() < 1))
  698. flags = Qt::AlignHCenter;
  699. if (dw > 1) {
  700. w -= dw;
  701. x += 2;
  702. }
  703. #ifdef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT
  704. do_paint_children = true;
  705. #endif
  706. }
  707. // Draw text-----------------------------------
  708. p.rect.setRect(x + 1, top, w - 1, h);
  709. // text will be painted with inverse color
  710. //auto textColor = inverseColor < 0x00808080 ? profiler::colors::Black : profiler::colors::White;
  711. //if (textColor == previousColor) textColor = 0;
  712. _painter->setPen(p.textColor);
  713. if (item.block == EASY_GLOBALS.selected_block)
  714. _painter->setFont(SELECTED_ITEM_FONT);
  715. // drawing text
  716. auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : itemDesc.name();
  717. _painter->drawText(p.rect, flags, ::profiler_gui::toUnicode(name));
  718. // restore previous pen color
  719. if (p.previousPenStyle == Qt::NoPen)
  720. _painter->setPen(Qt::NoPen);
  721. else if (p.previousPenStyle == Qt::DotLine)
  722. {
  723. _painter->setPen(HIGHLIGHTER_PEN);
  724. }
  725. else
  726. _painter->setPen(BORDERS_COLOR);// BORDERS_COLOR & inverseColor); // restore pen for rectangle painting
  727. // restore font
  728. if (item.block == EASY_GLOBALS.selected_block)
  729. _painter->setFont(ITEMS_FONT);
  730. // END Draw text~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  731. #ifdef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT
  732. if (do_paint_children)
  733. paintChildren(MIN_WIDTH, narrow_size_half, levelsNumber, _painter, p, item, itemBlock, m_rightBounds, next_level, BLOCK_ITEM_DO_PAINT);
  734. #endif
  735. }
  736. }
  737. if (EASY_GLOBALS.selected_block < EASY_GLOBALS.gui_blocks.size())
  738. {
  739. const auto& guiblock = EASY_GLOBALS.gui_blocks[EASY_GLOBALS.selected_block];
  740. if (guiblock.graphics_item == m_index)
  741. {
  742. const auto& item = m_levels[guiblock.graphics_item_level][guiblock.graphics_item_index];
  743. if (item.left() < p.sceneRight && item.right() > p.sceneLeft)
  744. {
  745. const auto& itemBlock = easyBlock(item.block);
  746. const auto item_width = ::std::max(item.width(), MIN_WIDTH);
  747. auto top = levelY(guiblock.graphics_item_level);
  748. auto w = ::std::max(item_width * p.currentScale, 1.0);
  749. decltype(top) h = (!itemBlock.expanded ||
  750. (w < EASY_GLOBALS.blocks_narrow_size && EASY_GLOBALS.hide_narrow_children))
  751. ? (itemBlock.tree.depth * ::profiler_gui::GRAPHICS_ROW_SIZE_FULL + ::profiler_gui::GRAPHICS_ROW_SIZE)
  752. : ::profiler_gui::GRAPHICS_ROW_SIZE;
  753. auto dh = top + h - p.visibleBottom;
  754. if (dh < h)
  755. {
  756. if (dh > 0)
  757. h -= dh;
  758. const auto& itemDesc = easyDescriptor(itemBlock.tree.node->id());
  759. QPen pen(Qt::SolidLine);
  760. pen.setJoinStyle(Qt::MiterJoin);
  761. pen.setColor(selectedItemBorderColor(itemDesc.color()));//Qt::red);
  762. pen.setWidth(3);
  763. _painter->setPen(pen);
  764. if (!p.selectedItemsWasPainted)
  765. {
  766. p.brush.setColor(itemDesc.color());// SELECTED_ITEM_COLOR);
  767. _painter->setBrush(p.brush);
  768. }
  769. else
  770. {
  771. _painter->setBrush(Qt::NoBrush);
  772. }
  773. auto x = item.left() * p.currentScale - p.dx;
  774. decltype(w) dw = 0;
  775. if (item.left() < p.sceneLeft)
  776. {
  777. // if item left border is out of screen then attach text to the left border of the screen
  778. // to ensure text is always visible for items presenting on the screen.
  779. w += (item.left() - p.sceneLeft) * p.currentScale;
  780. x = p.sceneLeft * p.currentScale - p.dx - 2;
  781. w += 2;
  782. dw = 2;
  783. }
  784. if (item.right() > p.sceneRight)
  785. {
  786. w -= (item.right() - p.sceneRight) * p.currentScale;
  787. w += 2;
  788. dw += 2;
  789. }
  790. p.rect.setRect(x, top, w, h);
  791. _painter->drawRect(p.rect);
  792. if (!p.selectedItemsWasPainted && w > EASY_GLOBALS.blocks_narrow_size)
  793. {
  794. if (dw > 1) {
  795. w -= dw;
  796. x += 2;
  797. }
  798. // Draw text-----------------------------------
  799. p.rect.setRect(x + 1, top, w - 1, h);
  800. // text will be painted with inverse color
  801. //auto textColor = 0x00ffffff - previousColor;
  802. //if (textColor == previousColor) textColor = 0;
  803. p.textColor = ::profiler_gui::textColorForRgb(itemDesc.color());// SELECTED_ITEM_COLOR);
  804. _painter->setPen(p.textColor);
  805. _painter->setFont(SELECTED_ITEM_FONT);
  806. // drawing text
  807. auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : itemDesc.name();
  808. _painter->drawText(p.rect, Qt::AlignCenter, ::profiler_gui::toUnicode(name));
  809. // END Draw text~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  810. }
  811. }
  812. }
  813. }
  814. }
  815. //printf("%u: %llu\n", m_index, iterations);
  816. }
  817. if (gotSync)
  818. {
  819. const auto sceneView = view();
  820. auto firstSync = ::std::lower_bound(m_pRoot->sync.begin(), m_pRoot->sync.end(), p.sceneLeft, [&sceneView](::profiler::block_index_t _index, qreal _value)
  821. {
  822. return sceneView->time2position(blocksTree(_index).node->begin()) < _value;
  823. });
  824. if (firstSync != m_pRoot->sync.end())
  825. {
  826. if (firstSync != m_pRoot->sync.begin())
  827. --firstSync;
  828. }
  829. else if (!m_pRoot->sync.empty())
  830. {
  831. firstSync = m_pRoot->sync.begin() + m_pRoot->sync.size() - 1;
  832. }
  833. //firstSync = m_pRoot->sync.begin();
  834. p.previousColor = 0;
  835. qreal prevRight = -1e100, top = y() - 4, h = 3;
  836. if (top + h < p.visibleBottom)
  837. {
  838. _painter->setPen(BORDERS_COLOR);
  839. for (auto it = firstSync, end = m_pRoot->sync.end(); it != end; ++it)
  840. {
  841. const auto& item = blocksTree(*it);
  842. auto left = sceneView->time2position(item.node->begin());
  843. if (left > p.sceneRight)
  844. break; // This is first totally invisible item. No need to check other items.
  845. decltype(left) width = sceneView->time2position(item.node->end()) - left;
  846. if (left + width < p.sceneLeft) // This item is not visible
  847. continue;
  848. left *= p.currentScale;
  849. left -= p.dx;
  850. width *= p.currentScale;
  851. if (left + width <= prevRight) // This item is not visible
  852. continue;
  853. if (left < prevRight)
  854. {
  855. width -= prevRight - left;
  856. left = prevRight;
  857. }
  858. if (width < MIN_SYNC_SIZE)
  859. width = MIN_SYNC_SIZE;
  860. const ::profiler::thread_id_t tid = EASY_GLOBALS.version < ::profiler_gui::V130 ? item.node->id() : item.cs->tid();
  861. const bool self_thread = tid != 0 && EASY_GLOBALS.profiler_blocks.find(tid) != EASY_GLOBALS.profiler_blocks.end();
  862. ::profiler::color_t color = 0;
  863. if (self_thread)
  864. color = ::profiler::colors::Coral;
  865. else if (item.node->id() == 0)
  866. color = ::profiler::colors::Black;
  867. else
  868. color = ::profiler::colors::RedA400;
  869. if (p.previousColor != color)
  870. {
  871. p.previousColor = color;
  872. _painter->setBrush(QColor::fromRgb(color));
  873. }
  874. p.rect.setRect(left, top, width, h);
  875. _painter->drawRect(p.rect);
  876. prevRight = left + width + MIN_SYNC_SPACING;
  877. }
  878. }
  879. }
  880. if (EASY_GLOBALS.enable_event_markers && !m_pRoot->events.empty())
  881. {
  882. const auto sceneView = view();
  883. auto first = ::std::lower_bound(m_pRoot->events.begin(), m_pRoot->events.end(), p.offset, [&sceneView](::profiler::block_index_t _index, qreal _value)
  884. {
  885. return sceneView->time2position(blocksTree(_index).node->begin()) < _value;
  886. });
  887. if (first != m_pRoot->events.end())
  888. {
  889. if (first != m_pRoot->events.begin())
  890. --first;
  891. }
  892. else if (!m_pRoot->events.empty())
  893. {
  894. first = m_pRoot->events.begin() + m_pRoot->events.size() - 1;
  895. }
  896. p.previousColor = 0;
  897. qreal prevRight = -1e100, top = y() + boundingRect().height() - 1, h = 3;
  898. if (top + h < p.visibleBottom)
  899. {
  900. _painter->setPen(BORDERS_COLOR);
  901. for (auto it = first, end = m_pRoot->events.end(); it != end; ++it)
  902. {
  903. const auto& item = blocksTree(*it);
  904. auto left = sceneView->time2position(item.node->begin());
  905. if (left > p.sceneRight)
  906. break; // This is first totally invisible item. No need to check other items.
  907. decltype(left) width = MIN_WIDTH;
  908. if (left + width < p.sceneLeft) // This item is not visible
  909. continue;
  910. left *= p.currentScale;
  911. left -= p.dx;
  912. width *= p.currentScale;
  913. if (width < 2) width = 2;
  914. if (left + width <= prevRight) // This item is not visible
  915. continue;
  916. if (left < prevRight)
  917. {
  918. width -= prevRight - left;
  919. left = prevRight;
  920. }
  921. if (width < 2)
  922. width = 2;
  923. ::profiler::color_t color = easyDescriptor(item.node->id()).color();
  924. if (p.previousColor != color)
  925. {
  926. p.previousColor = color;
  927. _painter->setBrush(QColor::fromRgb(color));
  928. }
  929. p.rect.setRect(left, top, width, h);
  930. _painter->drawRect(p.rect);
  931. prevRight = left + width + 2;
  932. }
  933. }
  934. }
  935. _painter->restore();
  936. }
  937. //////////////////////////////////////////////////////////////////////////
  938. const ::profiler::BlocksTreeRoot* EasyGraphicsItem::root() const
  939. {
  940. return m_pRoot;
  941. }
  942. const QString& EasyGraphicsItem::threadName() const
  943. {
  944. return m_threadName;
  945. }
  946. //////////////////////////////////////////////////////////////////////////
  947. QRect EasyGraphicsItem::getRect() const
  948. {
  949. return view()->mapFromScene(m_boundingRect).boundingRect();
  950. }
  951. //////////////////////////////////////////////////////////////////////////
  952. void EasyGraphicsItem::getBlocks(qreal _left, qreal _right, ::profiler_gui::TreeBlocks& _blocks) const
  953. {
  954. // Search for first visible top-level item
  955. auto& level0 = m_levels.front();
  956. auto first = ::std::lower_bound(level0.begin(), level0.end(), _left, [](const ::profiler_gui::EasyBlockItem& _item, qreal _value)
  957. {
  958. return _item.left() < _value;
  959. });
  960. size_t itemIndex = 0;
  961. if (first != level0.end())
  962. {
  963. itemIndex = first - level0.begin();
  964. if (itemIndex > 0)
  965. itemIndex -= 1;
  966. }
  967. else
  968. {
  969. itemIndex = level0.size() - 1;
  970. }
  971. // Add all visible top-level items into array of visible blocks
  972. for (size_t i = itemIndex, end = level0.size(); i < end; ++i)
  973. {
  974. const auto& item = level0[i];
  975. if (item.left() > _right)
  976. {
  977. // First invisible item. No need to check other items.
  978. break;
  979. }
  980. if (item.right() < _left)
  981. {
  982. // This item is not visible yet
  983. // This is just to be sure
  984. continue;
  985. }
  986. _blocks.emplace_back(m_pRoot, item.block);
  987. }
  988. }
  989. //////////////////////////////////////////////////////////////////////////
  990. const ::profiler_gui::EasyBlock* EasyGraphicsItem::intersect(const QPointF& _pos, ::profiler::block_index_t& _blockIndex) const
  991. {
  992. if (m_levels.empty() || m_levels.front().empty())
  993. {
  994. return nullptr;
  995. }
  996. const auto& level0 = m_levels.front();
  997. const auto top = y();
  998. if (top > _pos.y())
  999. {
  1000. return nullptr;
  1001. }
  1002. static const auto OVERLAP = ::profiler_gui::THREADS_ROW_SPACING >> 1;
  1003. const auto bottom = top + m_levels.size() * ::profiler_gui::GRAPHICS_ROW_SIZE_FULL + OVERLAP;
  1004. if (bottom < _pos.y())
  1005. {
  1006. return nullptr;
  1007. }
  1008. const unsigned int levelIndex = static_cast<unsigned int>(_pos.y() - top) / ::profiler_gui::GRAPHICS_ROW_SIZE_FULL;
  1009. if (levelIndex >= m_levels.size())
  1010. {
  1011. // The Y position is out of blocks range
  1012. if (EASY_GLOBALS.enable_event_markers && !m_pRoot->events.empty())
  1013. {
  1014. // If event indicators are enabled then try to intersect with one of event indicators
  1015. const auto& sceneView = view();
  1016. auto first = ::std::lower_bound(m_pRoot->events.begin(), m_pRoot->events.end(), _pos.x(), [&sceneView](::profiler::block_index_t _index, qreal _value)
  1017. {
  1018. return sceneView->time2position(blocksTree(_index).node->begin()) < _value;
  1019. });
  1020. if (first != m_pRoot->events.end())
  1021. {
  1022. if (first != m_pRoot->events.begin())
  1023. --first;
  1024. }
  1025. else if (!m_pRoot->events.empty())
  1026. {
  1027. first = m_pRoot->events.begin() + m_pRoot->events.size() - 1;
  1028. }
  1029. const auto MIN_WIDTH = EASY_GLOBALS.enable_zero_length ? 0.f : 0.25f;
  1030. const auto currentScale = sceneView->scale();
  1031. const auto dw = 5. / currentScale;
  1032. for (auto it = first, end = m_pRoot->events.end(); it != end; ++it)
  1033. {
  1034. _blockIndex = *it;
  1035. const auto& item = easyBlock(_blockIndex);
  1036. auto left = sceneView->time2position(item.tree.node->begin());
  1037. if (left - dw > _pos.x())
  1038. break; // This is first totally invisible item. No need to check other items.
  1039. decltype(left) width = MIN_WIDTH;
  1040. if (left + width + dw < _pos.x()) // This item is not visible
  1041. continue;
  1042. return &item;
  1043. }
  1044. }
  1045. return nullptr;
  1046. }
  1047. // The Y position is inside blocks range
  1048. const auto MIN_WIDTH = EASY_GLOBALS.enable_zero_length ? 0.f : 0.25f;
  1049. const auto currentScale = view()->scale();
  1050. const auto dw = 5. / currentScale;
  1051. unsigned int i = 0;
  1052. size_t itemIndex = ::std::numeric_limits<size_t>::max();
  1053. size_t firstItem = 0, lastItem = static_cast<unsigned int>(level0.size());
  1054. while (i <= levelIndex)
  1055. {
  1056. const auto& level = m_levels[i];
  1057. // Search for first visible item
  1058. auto first = ::std::lower_bound(level.begin() + firstItem, level.begin() + lastItem, _pos.x(), [](const ::profiler_gui::EasyBlockItem& _item, qreal _value)
  1059. {
  1060. return _item.left() < _value;
  1061. });
  1062. if (first != level.end())
  1063. {
  1064. itemIndex = first - level.begin();
  1065. if (itemIndex != 0)
  1066. --itemIndex;
  1067. }
  1068. else
  1069. {
  1070. itemIndex = level.size() - 1;
  1071. }
  1072. for (auto size = level.size(); itemIndex < size; ++itemIndex)
  1073. {
  1074. const auto& item = level[itemIndex];
  1075. static const auto MAX_CHILD_INDEX = ::profiler_gui::numeric_max(item.children_begin);
  1076. if (item.left() - dw > _pos.x())
  1077. {
  1078. return nullptr;
  1079. }
  1080. const auto item_width = ::std::max(item.width(), MIN_WIDTH);
  1081. if (item.left() + item_width + dw < _pos.x())
  1082. {
  1083. continue;
  1084. }
  1085. const auto w = item_width * currentScale;
  1086. const auto& guiItem = easyBlock(item.block);
  1087. if (i == levelIndex || (w < EASY_GLOBALS.blocks_narrow_size && EASY_GLOBALS.hide_narrow_children) || !guiItem.expanded)
  1088. {
  1089. _blockIndex = item.block;
  1090. return &guiItem;
  1091. }
  1092. if (item.children_begin == MAX_CHILD_INDEX)
  1093. {
  1094. if (itemIndex != 0)
  1095. {
  1096. auto j = itemIndex;
  1097. firstItem = 0;
  1098. do {
  1099. --j;
  1100. const auto& item2 = level[j];
  1101. if (item2.children_begin != MAX_CHILD_INDEX)
  1102. {
  1103. firstItem = item2.children_begin;
  1104. break;
  1105. }
  1106. } while (j != 0);
  1107. }
  1108. else
  1109. {
  1110. firstItem = 0;
  1111. }
  1112. }
  1113. else
  1114. {
  1115. firstItem = item.children_begin;
  1116. }
  1117. lastItem = m_levels[i + 1].size();
  1118. for (auto j = itemIndex + 1; j < size; ++j)
  1119. {
  1120. const auto& item2 = level[j];
  1121. if (item2.children_begin != MAX_CHILD_INDEX)
  1122. {
  1123. lastItem = item2.children_begin;
  1124. break;
  1125. }
  1126. }
  1127. break;
  1128. }
  1129. ++i;
  1130. }
  1131. return nullptr;
  1132. }
  1133. const ::profiler_gui::EasyBlock* EasyGraphicsItem::intersectEvent(const QPointF& _pos) const
  1134. {
  1135. if (m_pRoot->sync.empty())
  1136. {
  1137. return nullptr;
  1138. }
  1139. const auto top = y() - 6;
  1140. if (top > _pos.y())
  1141. {
  1142. return nullptr;
  1143. }
  1144. const auto bottom = top + 5;
  1145. if (bottom < _pos.y())
  1146. {
  1147. return nullptr;
  1148. }
  1149. const auto sceneView = view();
  1150. auto firstSync = ::std::lower_bound(m_pRoot->sync.begin(), m_pRoot->sync.end(), _pos.x(), [&sceneView](::profiler::block_index_t _index, qreal _value)
  1151. {
  1152. return sceneView->time2position(blocksTree(_index).node->begin()) < _value;
  1153. });
  1154. if (firstSync == m_pRoot->sync.end())
  1155. firstSync = m_pRoot->sync.begin() + m_pRoot->sync.size() - 1;
  1156. else if (firstSync != m_pRoot->sync.begin())
  1157. --firstSync;
  1158. const auto dw = 4. / view()->scale();
  1159. for (auto it = firstSync, end = m_pRoot->sync.end(); it != end; ++it)
  1160. {
  1161. const auto& item = easyBlock(*it);
  1162. const auto left = sceneView->time2position(item.tree.node->begin()) - dw;
  1163. if (left > _pos.x())
  1164. break;
  1165. const auto right = sceneView->time2position(item.tree.node->end()) + dw;
  1166. if (right < _pos.x())
  1167. continue;
  1168. return &item;
  1169. }
  1170. return nullptr;
  1171. }
  1172. //////////////////////////////////////////////////////////////////////////
  1173. void EasyGraphicsItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h)
  1174. {
  1175. m_boundingRect.setRect(x, y, w, h);
  1176. }
  1177. void EasyGraphicsItem::setBoundingRect(const QRectF& _rect)
  1178. {
  1179. m_boundingRect = _rect;
  1180. }
  1181. //////////////////////////////////////////////////////////////////////////
  1182. ::profiler::thread_id_t EasyGraphicsItem::threadId() const
  1183. {
  1184. return m_pRoot->thread_id;
  1185. }
  1186. //////////////////////////////////////////////////////////////////////////
  1187. uint8_t EasyGraphicsItem::levels() const
  1188. {
  1189. return static_cast<uint8_t>(m_levels.size());
  1190. }
  1191. float EasyGraphicsItem::levelY(uint8_t _level) const
  1192. {
  1193. return y() + static_cast<int>(_level) * static_cast<int>(::profiler_gui::GRAPHICS_ROW_SIZE_FULL);
  1194. }
  1195. void EasyGraphicsItem::setLevels(uint8_t _levels)
  1196. {
  1197. typedef decltype(m_levelsIndexes) IndexesT;
  1198. static const auto MAX_CHILD_INDEX = ::profiler_gui::numeric_max<IndexesT::value_type>();
  1199. m_levels.resize(_levels);
  1200. m_levelsIndexes.resize(_levels, MAX_CHILD_INDEX);
  1201. m_rightBounds.resize(_levels, -1e100);
  1202. }
  1203. void EasyGraphicsItem::reserve(uint8_t _level, unsigned int _items)
  1204. {
  1205. m_levels[_level].reserve(_items);
  1206. }
  1207. //////////////////////////////////////////////////////////////////////////
  1208. const EasyGraphicsItem::Children& EasyGraphicsItem::items(uint8_t _level) const
  1209. {
  1210. return m_levels[_level];
  1211. }
  1212. const ::profiler_gui::EasyBlockItem& EasyGraphicsItem::getItem(uint8_t _level, unsigned int _index) const
  1213. {
  1214. return m_levels[_level][_index];
  1215. }
  1216. ::profiler_gui::EasyBlockItem& EasyGraphicsItem::getItem(uint8_t _level, unsigned int _index)
  1217. {
  1218. return m_levels[_level][_index];
  1219. }
  1220. unsigned int EasyGraphicsItem::addItem(uint8_t _level)
  1221. {
  1222. m_levels[_level].emplace_back();
  1223. return static_cast<unsigned int>(m_levels[_level].size() - 1);
  1224. }
  1225. //////////////////////////////////////////////////////////////////////////
  1226. //////////////////////////////////////////////////////////////////////////