From 92a5ca4a751fbe0c72de0f0969d03f96bc563ca6 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Sun, 20 Oct 2019 16:12:37 +0300 Subject: [PATCH] Big bunch of changes: * update copyright * fix css parsing * fix block name search * add matching text highlighing for find results * add calculation of block statistics for selected area * new action: right-click on a block on "Diagram" selects region using left and right bounds of this block * other optimizations --- LICENSE | 2 +- LICENSE.MIT | 2 +- easy_profiler_converter/converter.cpp | 2 +- easy_profiler_converter/converter.h | 2 +- easy_profiler_converter/reader.cpp | 2 +- easy_profiler_converter/reader.h | 2 +- easy_profiler_core/LICENSE.MIT | 2 +- easy_profiler_core/alignment_helpers.h | 2 +- easy_profiler_core/base_block_descriptor.cpp | 2 +- easy_profiler_core/block.cpp | 2 +- easy_profiler_core/block_descriptor.cpp | 2 +- easy_profiler_core/block_descriptor.h | 2 +- easy_profiler_core/chunk_allocator.h | 2 +- easy_profiler_core/current_thread.h | 2 +- easy_profiler_core/current_time.h | 2 +- easy_profiler_core/easy_socket.cpp | 2 +- easy_profiler_core/event_trace_win.cpp | 2 +- easy_profiler_core/event_trace_win.h | 2 +- easy_profiler_core/hashed_cstr.h | 2 +- .../include/easy/arbitrary_value.h | 2 +- .../easy/details/arbitrary_value_aux.h | 2 +- .../details/arbitrary_value_public_types.h | 2 +- .../easy/details/easy_compiler_support.h | 2 +- .../include/easy/details/profiler_aux.h | 2 +- .../include/easy/details/profiler_colors.h | 2 +- .../include/easy/details/profiler_in_use.h | 2 +- .../easy/details/profiler_public_types.h | 2 +- easy_profiler_core/include/easy/easy_net.h | 2 +- easy_profiler_core/include/easy/easy_socket.h | 2 +- easy_profiler_core/include/easy/profiler.h | 2 +- easy_profiler_core/include/easy/reader.h | 3 +- .../include/easy/serialized_block.h | 2 +- easy_profiler_core/include/easy/writer.h | 2 +- easy_profiler_core/nonscoped_block.cpp | 2 +- easy_profiler_core/nonscoped_block.h | 2 +- easy_profiler_core/profile_manager.cpp | 2 +- easy_profiler_core/profile_manager.h | 2 +- easy_profiler_core/profiler.cpp | 2 +- easy_profiler_core/reader.cpp | 19 +- easy_profiler_core/resources.rc | 2 +- easy_profiler_core/serialized_block.cpp | 2 +- easy_profiler_core/spin_lock.h | 2 +- easy_profiler_core/stack_buffer.h | 2 +- easy_profiler_core/thread_storage.cpp | 2 +- easy_profiler_core/thread_storage.h | 2 +- easy_profiler_core/writer.cpp | 2 +- profiler_gui/CMakeLists.txt | 2 + profiler_gui/arbitrary_value_inspector.cpp | 85 +- profiler_gui/arbitrary_value_inspector.h | 6 +- profiler_gui/arbitrary_value_tooltip.cpp | 2 +- profiler_gui/arbitrary_value_tooltip.h | 2 +- profiler_gui/blocks_graphics_view.cpp | 262 ++- profiler_gui/blocks_graphics_view.h | 12 +- profiler_gui/blocks_tree_widget.cpp | 717 +++++-- profiler_gui/blocks_tree_widget.h | 27 +- profiler_gui/bookmarks_editor.cpp | 10 +- profiler_gui/bookmarks_editor.h | 2 +- profiler_gui/common_functions.cpp | 2 +- profiler_gui/common_functions.h | 2 +- profiler_gui/common_types.h | 9 +- profiler_gui/complexity_calculator.h | 2 +- profiler_gui/descriptors_tree_widget.cpp | 405 +++- profiler_gui/descriptors_tree_widget.h | 63 +- profiler_gui/dialog.cpp | 4 +- profiler_gui/dialog.h | 2 +- profiler_gui/fps_widget.cpp | 15 +- profiler_gui/fps_widget.h | 4 +- profiler_gui/globals.cpp | 120 +- profiler_gui/globals.h | 30 +- profiler_gui/globals_qobjects.cpp | 2 +- profiler_gui/globals_qobjects.h | 2 +- profiler_gui/graphics_block_item.cpp | 4 +- profiler_gui/graphics_block_item.h | 2 +- profiler_gui/graphics_image_item.cpp | 45 +- profiler_gui/graphics_image_item.h | 7 +- profiler_gui/graphics_ruler_item.cpp | 188 +- profiler_gui/graphics_ruler_item.h | 68 +- profiler_gui/graphics_scrollbar.cpp | 27 +- profiler_gui/graphics_scrollbar.h | 2 +- profiler_gui/graphics_slider_area.cpp | 2 +- profiler_gui/graphics_slider_area.h | 2 +- profiler_gui/images/attribution.txt | 1 - profiler_gui/images/default/to-fullscreen.svg | 5 +- profiler_gui/main.cpp | 2 +- profiler_gui/main_window.cpp | 153 +- profiler_gui/main_window.h | 3 +- profiler_gui/resources.rc | 2 +- profiler_gui/round_progress_widget.cpp | 2 +- profiler_gui/round_progress_widget.h | 2 +- profiler_gui/text_highlighter.cpp | 104 + profiler_gui/text_highlighter.h | 83 + profiler_gui/themes/default.css | 26 +- profiler_gui/themes/default.scss | 24 +- profiler_gui/thread_pool.cpp | 2 +- profiler_gui/thread_pool.h | 2 +- profiler_gui/thread_pool_task.cpp | 47 +- profiler_gui/thread_pool_task.h | 36 +- profiler_gui/timer.cpp | 2 +- profiler_gui/timer.h | 2 +- profiler_gui/tree_widget_item.cpp | 350 +++- profiler_gui/tree_widget_item.h | 59 +- profiler_gui/tree_widget_loader.cpp | 1698 ++++++++++++----- profiler_gui/tree_widget_loader.h | 127 +- profiler_gui/window_header.cpp | 31 +- profiler_gui/window_header.h | 4 +- 105 files changed, 3637 insertions(+), 1376 deletions(-) create mode 100644 profiler_gui/text_highlighter.cpp create mode 100644 profiler_gui/text_highlighter.h diff --git a/LICENSE b/LICENSE index 31643de..5642517 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright (c) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/LICENSE.MIT b/LICENSE.MIT index dac631a..448274c 100644 --- a/LICENSE.MIT +++ b/LICENSE.MIT @@ -1,4 +1,4 @@ -Copyright (c) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright (c) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/easy_profiler_converter/converter.cpp b/easy_profiler_converter/converter.cpp index ec64028..5265ef9 100644 --- a/easy_profiler_converter/converter.cpp +++ b/easy_profiler_converter/converter.cpp @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_converter/converter.h b/easy_profiler_converter/converter.h index 6c987cc..6d518c3 100644 --- a/easy_profiler_converter/converter.h +++ b/easy_profiler_converter/converter.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_converter/reader.cpp b/easy_profiler_converter/reader.cpp index 3f36649..3a79ad7 100644 --- a/easy_profiler_converter/reader.cpp +++ b/easy_profiler_converter/reader.cpp @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_converter/reader.h b/easy_profiler_converter/reader.h index 520d7b3..f16fe81 100644 --- a/easy_profiler_converter/reader.h +++ b/easy_profiler_converter/reader.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/LICENSE.MIT b/easy_profiler_core/LICENSE.MIT index dac631a..448274c 100644 --- a/easy_profiler_core/LICENSE.MIT +++ b/easy_profiler_core/LICENSE.MIT @@ -1,4 +1,4 @@ -Copyright (c) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright (c) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/easy_profiler_core/alignment_helpers.h b/easy_profiler_core/alignment_helpers.h index 619aff9..f1c4dcd 100644 --- a/easy_profiler_core/alignment_helpers.h +++ b/easy_profiler_core/alignment_helpers.h @@ -6,7 +6,7 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/base_block_descriptor.cpp b/easy_profiler_core/base_block_descriptor.cpp index a3d761e..54759a6 100644 --- a/easy_profiler_core/base_block_descriptor.cpp +++ b/easy_profiler_core/base_block_descriptor.cpp @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/block.cpp b/easy_profiler_core/block.cpp index 2b2ead2..4bb7114 100644 --- a/easy_profiler_core/block.cpp +++ b/easy_profiler_core/block.cpp @@ -8,7 +8,7 @@ * description : The file contains implementation of profiling blocks * : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/block_descriptor.cpp b/easy_profiler_core/block_descriptor.cpp index f46fa78..2b3b73c 100644 --- a/easy_profiler_core/block_descriptor.cpp +++ b/easy_profiler_core/block_descriptor.cpp @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/block_descriptor.h b/easy_profiler_core/block_descriptor.h index 3e8b04b..c5d9360 100644 --- a/easy_profiler_core/block_descriptor.h +++ b/easy_profiler_core/block_descriptor.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/chunk_allocator.h b/easy_profiler_core/chunk_allocator.h index 40acf33..2ab3048 100644 --- a/easy_profiler_core/chunk_allocator.h +++ b/easy_profiler_core/chunk_allocator.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/current_thread.h b/easy_profiler_core/current_thread.h index aecd810..fc39ea3 100644 --- a/easy_profiler_core/current_thread.h +++ b/easy_profiler_core/current_thread.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/current_time.h b/easy_profiler_core/current_time.h index cd59d79..5e4a4af 100644 --- a/easy_profiler_core/current_time.h +++ b/easy_profiler_core/current_time.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/easy_socket.cpp b/easy_profiler_core/easy_socket.cpp index 350a51b..1dcf609 100644 --- a/easy_profiler_core/easy_socket.cpp +++ b/easy_profiler_core/easy_socket.cpp @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/event_trace_win.cpp b/easy_profiler_core/event_trace_win.cpp index d827a8e..bdb4d7b 100644 --- a/easy_profiler_core/event_trace_win.cpp +++ b/easy_profiler_core/event_trace_win.cpp @@ -16,7 +16,7 @@ * : * 2016/09/17 Victor Zarubkin: added log messages printing. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/event_trace_win.h b/easy_profiler_core/event_trace_win.h index 76b5090..cfb8ff7 100644 --- a/easy_profiler_core/event_trace_win.h +++ b/easy_profiler_core/event_trace_win.h @@ -13,7 +13,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/hashed_cstr.h b/easy_profiler_core/hashed_cstr.h index 0f60cb0..fe8f1d9 100644 --- a/easy_profiler_core/hashed_cstr.h +++ b/easy_profiler_core/hashed_cstr.h @@ -13,7 +13,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/include/easy/arbitrary_value.h b/easy_profiler_core/include/easy/arbitrary_value.h index 1184302..2366957 100644 --- a/easy_profiler_core/include/easy/arbitrary_value.h +++ b/easy_profiler_core/include/easy/arbitrary_value.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/include/easy/details/arbitrary_value_aux.h b/easy_profiler_core/include/easy/details/arbitrary_value_aux.h index 9ac4a30..8fa5d47 100644 --- a/easy_profiler_core/include/easy/details/arbitrary_value_aux.h +++ b/easy_profiler_core/include/easy/details/arbitrary_value_aux.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/include/easy/details/arbitrary_value_public_types.h b/easy_profiler_core/include/easy/details/arbitrary_value_public_types.h index 8294a43..58c1c17 100644 --- a/easy_profiler_core/include/easy/details/arbitrary_value_public_types.h +++ b/easy_profiler_core/include/easy/details/arbitrary_value_public_types.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/include/easy/details/easy_compiler_support.h b/easy_profiler_core/include/easy/details/easy_compiler_support.h index 212359d..587465b 100644 --- a/easy_profiler_core/include/easy/details/easy_compiler_support.h +++ b/easy_profiler_core/include/easy/details/easy_compiler_support.h @@ -8,7 +8,7 @@ * description : This file contains auxiliary profiler macros for different compiler support. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/include/easy/details/profiler_aux.h b/easy_profiler_core/include/easy/details/profiler_aux.h index e7b5d62..b33a802 100644 --- a/easy_profiler_core/include/easy/details/profiler_aux.h +++ b/easy_profiler_core/include/easy/details/profiler_aux.h @@ -12,7 +12,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/include/easy/details/profiler_colors.h b/easy_profiler_core/include/easy/details/profiler_colors.h index af756c1..fa4002b 100644 --- a/easy_profiler_core/include/easy/details/profiler_colors.h +++ b/easy_profiler_core/include/easy/details/profiler_colors.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/include/easy/details/profiler_in_use.h b/easy_profiler_core/include/easy/details/profiler_in_use.h index 5c69473..176fb7f 100644 --- a/easy_profiler_core/include/easy/details/profiler_in_use.h +++ b/easy_profiler_core/include/easy/details/profiler_in_use.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/include/easy/details/profiler_public_types.h b/easy_profiler_core/include/easy/details/profiler_public_types.h index 25cb13a..7e5a3cb 100644 --- a/easy_profiler_core/include/easy/details/profiler_public_types.h +++ b/easy_profiler_core/include/easy/details/profiler_public_types.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/include/easy/easy_net.h b/easy_profiler_core/include/easy/easy_net.h index 164d295..5639f47 100644 --- a/easy_profiler_core/include/easy/easy_net.h +++ b/easy_profiler_core/include/easy/easy_net.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/include/easy/easy_socket.h b/easy_profiler_core/include/easy/easy_socket.h index 5059dae..c91a6cb 100644 --- a/easy_profiler_core/include/easy/easy_socket.h +++ b/easy_profiler_core/include/easy/easy_socket.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/include/easy/profiler.h b/easy_profiler_core/include/easy/profiler.h index 98cdcf9..98d162f 100644 --- a/easy_profiler_core/include/easy/profiler.h +++ b/easy_profiler_core/include/easy/profiler.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/include/easy/reader.h b/easy_profiler_core/include/easy/reader.h index 38ec906..fe4545d 100644 --- a/easy_profiler_core/include/easy/reader.h +++ b/easy_profiler_core/include/easy/reader.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -298,6 +298,7 @@ namespace profiler { using blocks_t = profiler::BlocksTree::blocks_t; using thread_blocks_tree_t = std::unordered_map >; using block_getter_fn = std::function; + using stats_map_t = std::unordered_map >; ////////////////////////////////////////////////////////////////////////// diff --git a/easy_profiler_core/include/easy/serialized_block.h b/easy_profiler_core/include/easy/serialized_block.h index 93828f8..6d1a16b 100644 --- a/easy_profiler_core/include/easy/serialized_block.h +++ b/easy_profiler_core/include/easy/serialized_block.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/include/easy/writer.h b/easy_profiler_core/include/easy/writer.h index 4ec1084..295a9ef 100644 --- a/easy_profiler_core/include/easy/writer.h +++ b/easy_profiler_core/include/easy/writer.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/nonscoped_block.cpp b/easy_profiler_core/nonscoped_block.cpp index 235dbf8..6d06aba 100644 --- a/easy_profiler_core/nonscoped_block.cpp +++ b/easy_profiler_core/nonscoped_block.cpp @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/nonscoped_block.h b/easy_profiler_core/nonscoped_block.h index 93149b3..ed1272a 100644 --- a/easy_profiler_core/nonscoped_block.h +++ b/easy_profiler_core/nonscoped_block.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/profile_manager.cpp b/easy_profiler_core/profile_manager.cpp index 45d641d..de70404 100644 --- a/easy_profiler_core/profile_manager.cpp +++ b/easy_profiler_core/profile_manager.cpp @@ -8,7 +8,7 @@ * description : The file contains implementation of Profile manager and implement access c-function * : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/profile_manager.h b/easy_profiler_core/profile_manager.h index 59d4bce..38e5444 100644 --- a/easy_profiler_core/profile_manager.h +++ b/easy_profiler_core/profile_manager.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/profiler.cpp b/easy_profiler_core/profiler.cpp index 0ebbe67..356edd4 100644 --- a/easy_profiler_core/profiler.cpp +++ b/easy_profiler_core/profiler.cpp @@ -8,7 +8,7 @@ * description : The file contains implementation of Profile manager and implement access c-function * : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/reader.cpp b/easy_profiler_core/reader.cpp index b27d3e6..caafce7 100644 --- a/easy_profiler_core/reader.cpp +++ b/easy_profiler_core/reader.cpp @@ -9,7 +9,7 @@ * : which reads profiler file and fill profiler blocks tree. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -231,7 +231,6 @@ namespace profiler { ////////////////////////////////////////////////////////////////////////// -using StatsMap = std::unordered_map >; using IdMap = std::unordered_map; using CsStatsMap = std::unordered_map; @@ -249,8 +248,14 @@ using CsStatsMap = std::unordered_mapduration(); //StatsMap::key_type key(_current.node->name()); //auto it = _stats_map.find(key); @@ -359,7 +364,7 @@ static profiler::BlockStatistics* update_statistics(CsStatsMap& _stats_map, cons ////////////////////////////////////////////////////////////////////////// -static void update_statistics_recursive(StatsMap& _stats_map, profiler::BlocksTree& _current, profiler::block_index_t _current_index, profiler::block_index_t _parent_index, profiler::blocks_t& _blocks) +static void update_statistics_recursive(profiler::stats_map_t& _stats_map, profiler::BlocksTree& _current, profiler::block_index_t _current_index, profiler::block_index_t _parent_index, profiler::blocks_t& _blocks) { _current.per_frame_stats = update_statistics(_stats_map, _current, _current_index, _parent_index, _blocks, false); for (auto i : _current.children) @@ -696,7 +701,7 @@ extern "C" PROFILER_API profiler::block_index_t fillTreesFromStream(std::atomic< } } - using PerThreadStats = std::unordered_map >; + using PerThreadStats = std::unordered_map >; PerThreadStats parent_statistics, frame_statistics; IdMap identification_table; @@ -808,7 +813,7 @@ extern "C" PROFILER_API profiler::block_index_t fillTreesFromStream(std::atomic< if (inStream.eof()) break; - StatsMap per_thread_statistics; + profiler::stats_map_t per_thread_statistics; blocks_number_in_thread = 0; read(inStream, blocks_number_in_thread); diff --git a/easy_profiler_core/resources.rc b/easy_profiler_core/resources.rc index 26e7a92..d33cfda 100644 --- a/easy_profiler_core/resources.rc +++ b/easy_profiler_core/resources.rc @@ -16,7 +16,7 @@ BEGIN BEGIN VALUE "CompanyName", "EasySolutions" VALUE "FileDescription", "Lightweight profiler library for C++" - VALUE "LegalCopyright", "Copyright (C) 2016-2018 Victor Zarubkin, Sergey Yagovtsev" + VALUE "LegalCopyright", "Copyright (C) 2016-2019 Victor Zarubkin, Sergey Yagovtsev" VALUE "LegalTrademarks1", "All Rights Reserved" VALUE "LegalTrademarks2", "All Rights Reserved" VALUE "ProductName", "easy_profiler lib" diff --git a/easy_profiler_core/serialized_block.cpp b/easy_profiler_core/serialized_block.cpp index cfb09c3..c646782 100644 --- a/easy_profiler_core/serialized_block.cpp +++ b/easy_profiler_core/serialized_block.cpp @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/spin_lock.h b/easy_profiler_core/spin_lock.h index 79f5685..63ddcab 100644 --- a/easy_profiler_core/spin_lock.h +++ b/easy_profiler_core/spin_lock.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/stack_buffer.h b/easy_profiler_core/stack_buffer.h index cb37680..4393ea0 100644 --- a/easy_profiler_core/stack_buffer.h +++ b/easy_profiler_core/stack_buffer.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/thread_storage.cpp b/easy_profiler_core/thread_storage.cpp index 7091362..bd376e8 100644 --- a/easy_profiler_core/thread_storage.cpp +++ b/easy_profiler_core/thread_storage.cpp @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/thread_storage.h b/easy_profiler_core/thread_storage.h index 27c1489..36a4cdb 100644 --- a/easy_profiler_core/thread_storage.h +++ b/easy_profiler_core/thread_storage.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/easy_profiler_core/writer.cpp b/easy_profiler_core/writer.cpp index 814091a..4c6b7d3 100644 --- a/easy_profiler_core/writer.cpp +++ b/easy_profiler_core/writer.cpp @@ -9,7 +9,7 @@ * : which reads profiler file and fill profiler blocks tree. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/CMakeLists.txt b/profiler_gui/CMakeLists.txt index 6a39852..c48227e 100644 --- a/profiler_gui/CMakeLists.txt +++ b/profiler_gui/CMakeLists.txt @@ -49,6 +49,8 @@ if (Qt5Widgets_FOUND) main_window.cpp round_progress_widget.h round_progress_widget.cpp + text_highlighter.h + text_highlighter.cpp timer.h timer.cpp thread_pool.h diff --git a/profiler_gui/arbitrary_value_inspector.cpp b/profiler_gui/arbitrary_value_inspector.cpp index 0981c4b..f4e1473 100644 --- a/profiler_gui/arbitrary_value_inspector.cpp +++ b/profiler_gui/arbitrary_value_inspector.cpp @@ -12,7 +12,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -1121,7 +1121,7 @@ void ArbitraryValuesChartItem::updateRegularImageAsync(QRectF _boundingRect, qre { 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))); + p.setPen(QColor::fromRgba(profiler::colors::modify_alpha32(color, 0xc0000000))); else p.setPen(QColor::fromRgba(color)); p.setBrush(QColor::fromRgba(0xc8ffffff)); @@ -1456,7 +1456,7 @@ void ArbitraryValuesChartItem::updateComplexityImageAsync(QRectF _boundingRect, 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))); + p.setPen(QColor::fromRgba(profiler::colors::modify_alpha32(color, 0xc0000000))); else p.setPen(QColor::fromRgba(color)); p.setBrush(QColor::fromRgba(0xc8ffffff)); @@ -1546,12 +1546,14 @@ void ArbitraryValuesChartItem::clear() m_collections.clear(); m_minValue = m_maxValue = 0; m_minDuration = m_maxDuration = 0; + setEmpty(true); } void ArbitraryValuesChartItem::update(Collections _collections) { cancelImageUpdate(); m_collections = std::move(_collections); + setEmpty(m_collections.empty()); updateImage(); } @@ -1866,6 +1868,7 @@ ArbitraryValuesWidget::ArbitraryValuesWidget(bool _isMainWidget, profiler::threa , m_threadId(_threadId) , m_blockIndex(_blockIndex) , m_blockId(_blockId) + , m_bInitialized(false) , m_bMainWidget(_isMainWidget) { m_collectionsTimer.setInterval(100); @@ -1903,12 +1906,12 @@ ArbitraryValuesWidget::ArbitraryValuesWidget(bool _isMainWidget, profiler::threa auto actionGroup = new QActionGroup(this); actionGroup->setExclusive(true); - auto actionRegulatChart = new QAction(QIcon(imagePath("yx-chart")), tr("Regular chart"), actionGroup); + auto actionRegulatChart = new QAction(QIcon(imagePath("yx-chart")), tr("Regular chart [ v(t) ]"), actionGroup); actionRegulatChart->setCheckable(true); actionRegulatChart->setChecked(true); tb->addAction(actionRegulatChart); - auto actionComplexityChart = new QAction(QIcon(imagePath("big-o-chart")), tr("Complexity chart"), actionGroup); + auto actionComplexityChart = new QAction(QIcon(imagePath("big-o-chart")), tr("Complexity chart [ t(v) ]"), actionGroup); actionComplexityChart->setCheckable(true); tb->addAction(actionComplexityChart); @@ -1950,6 +1953,8 @@ ArbitraryValuesWidget::ArbitraryValuesWidget(bool _isMainWidget, profiler::threa connect(globalEvents, &GlobalSignals::selectedBlockIdChanged, this, &This::onSelectedBlockIdChanged); connect(globalEvents, &GlobalSignals::allDataGoingToBeDeleted, this, &This::clear); + connect(m_treeWidget->header(), &QHeaderView::sectionResized, this, &This::onHeaderSectionResized); + if (_isMainWidget) { connect(m_treeWidget, &QTreeWidget::itemDoubleClicked, this, &This::onItemDoubleClicked); @@ -1989,6 +1994,69 @@ ArbitraryValuesWidget::ArbitraryValuesWidget(QWidget* _parent) m_exportToCsvAction->setEnabled(false); } +void ArbitraryValuesWidget::showEvent(QShowEvent* event) +{ + Parent::showEvent(event); + + if (!m_bInitialized) + { + m_columnsMinimumWidth.resize(static_cast(ArbitraryColumns::Count), 0); + +#if !defined(_WIN32) && !defined(__APPLE__) + const auto padding = px(9); +#else + const auto padding = px(6); +#endif + + auto header = m_treeWidget->header(); + auto headerItem = m_treeWidget->headerItem(); + + auto f = header->font(); +#if !defined(_WIN32) && !defined(__APPLE__) + f.setBold(true); +#endif + QFontMetrics fm(f); + + const auto indicatorSize = header->isSortIndicatorShown() ? px(11) : 0; + for (int i = 0; i < static_cast(m_columnsMinimumWidth.size()); ++i) + { + auto minSize = static_cast(fm.width(headerItem->text(i)) * profiler_gui::FONT_METRICS_FACTOR + padding); + m_columnsMinimumWidth[i] = minSize; + + if (header->isSortIndicatorShown() && header->sortIndicatorSection() == i) + { + minSize += indicatorSize; + } + + if (header->sectionSize(i) < minSize) + { + header->resizeSection(i, minSize); + } + } + + m_bInitialized = true; + } +} + +void ArbitraryValuesWidget::onHeaderSectionResized(int logicalIndex, int /*oldSize*/, int newSize) +{ + if (logicalIndex >= m_columnsMinimumWidth.size()) + { + return; + } + + auto header = m_treeWidget->header(); + const auto indicatorSize = header->isSortIndicatorShown() && header->sortIndicatorSection() == logicalIndex ? px(11) : 0; + const auto minSize = m_columnsMinimumWidth[logicalIndex] + indicatorSize; + + if (!m_bInitialized || newSize >= minSize) + { + return; + } + + header->resizeSection(logicalIndex, minSize); +} + ArbitraryTreeWidgetItem* findSimilarItem(QTreeWidgetItem* _parentItem, ArbitraryTreeWidgetItem* _item) { const auto index = _item->getSelfIndexInArray(); @@ -2583,12 +2651,7 @@ void ArbitraryValuesWidget::onOpenInNewWindowClicked(bool) auto viewer = new ArbitraryValuesWidget(m_checkedItems, m_treeWidget->currentItem(), m_threadId, m_blockIndex, m_blockId); -#ifdef WIN32 - const WindowHeader::Buttons buttons = WindowHeader::AllButtons; -#else - const WindowHeader::Buttons buttons {WindowHeader::MaximizeButton | WindowHeader::CloseButton}; -#endif - auto dialog = new Dialog(nullptr, "EasyProfiler", viewer, buttons, QMessageBox::NoButton); + auto dialog = new Dialog(nullptr, "EasyProfiler", viewer, WindowHeader::AllButtons, QMessageBox::NoButton); dialog->setProperty("stayVisible", true); dialog->setAttribute(Qt::WA_DeleteOnClose, true); connect(&EASY_GLOBALS.events, &profiler_gui::GlobalSignals::allDataGoingToBeDeleted, dialog, &QDialog::reject); diff --git a/profiler_gui/arbitrary_value_inspector.h b/profiler_gui/arbitrary_value_inspector.h index 2620380..79b8ace 100644 --- a/profiler_gui/arbitrary_value_inspector.h +++ b/profiler_gui/arbitrary_value_inspector.h @@ -12,7 +12,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -341,6 +341,7 @@ class ArbitraryValuesWidget : public QWidget QTimer m_collectionsTimer; QList m_checkedItems; + std::vector m_columnsMinimumWidth; class QSplitter* m_splitter; QTreeWidget* m_treeWidget; GraphicsChart* m_chart; @@ -353,6 +354,7 @@ class ArbitraryValuesWidget : public QWidget profiler::thread_id_t m_threadId; profiler::block_index_t m_blockIndex; profiler::block_id_t m_blockId; + bool m_bInitialized; const bool m_bMainWidget; explicit ArbitraryValuesWidget(bool _isMainWidget, profiler::thread_id_t _threadId @@ -366,6 +368,7 @@ public: explicit ArbitraryValuesWidget(QWidget* _parent = nullptr); ~ArbitraryValuesWidget() override; + void showEvent(class QShowEvent* event) override; void contextMenuEvent(QContextMenuEvent*) override { /* ignore context menu event */ } public slots: @@ -377,6 +380,7 @@ public slots: private slots: + void onHeaderSectionResized(int logicalIndex, int oldSize, int newSize); void onSelectedThreadChanged(profiler::thread_id_t); void onSelectedBlockChanged(uint32_t _block_index); void onSelectedBlockIdChanged(profiler::block_id_t _id); diff --git a/profiler_gui/arbitrary_value_tooltip.cpp b/profiler_gui/arbitrary_value_tooltip.cpp index fbfb23f..a78b586 100644 --- a/profiler_gui/arbitrary_value_tooltip.cpp +++ b/profiler_gui/arbitrary_value_tooltip.cpp @@ -9,7 +9,7 @@ * : for displaying arbitrary value in Diagram and Hierarchy widgets. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/arbitrary_value_tooltip.h b/profiler_gui/arbitrary_value_tooltip.h index a893eb9..99f8c0a 100644 --- a/profiler_gui/arbitrary_value_tooltip.h +++ b/profiler_gui/arbitrary_value_tooltip.h @@ -9,7 +9,7 @@ * : for displaying arbitrary value in Diagram and Hierarchy widgets. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp index ab76c30..dc8df2d 100644 --- a/profiler_gui/blocks_graphics_view.cpp +++ b/profiler_gui/blocks_graphics_view.cpp @@ -24,7 +24,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -500,7 +500,12 @@ bool BackgroundItem::mouseDoubleClick(const QPointF& scenePos) { qApp->restoreOverrideCursor(); auto editor = new BookmarkEditor(m_bookmark, false, sceneView->parentWidget()); +#ifndef _WIN32 + // Ugly workaround for Linux: without show-hide-show you can not drag the window editor->show(); + editor->hide(); +#endif + editor->exec(); } else { @@ -523,7 +528,12 @@ bool BackgroundItem::mouseDoubleClick(const QPointF& scenePos) { qApp->restoreOverrideCursor(); auto editor = new BookmarkEditor(m_bookmark, true, sceneView->parentWidget()); +#ifndef _WIN32 + // Ugly workaround for Linux: without show-hide-show you can not drag the window editor->show(); + editor->hide(); +#endif + editor->exec(); } } @@ -1528,11 +1538,11 @@ void BlocksGraphicsView::mouseReleaseEvent(QMouseEvent* _event) const profiler_gui::EasyBlock* selectedBlock = nullptr; profiler::thread_id_t selectedBlockThread = 0; - const auto previouslySelectedBlock = EASY_GLOBALS.selected_block; - if (m_mouseButtons & Qt::LeftButton) + bool jumpToZone = false; + bool changedSelectionBySelectingItem = false; + const bool leftClickSelect = (m_mouseButtons & Qt::LeftButton) != 0; + if (leftClickSelect) { - bool clicked = false; - if (m_rulerItem->isVisible() && m_rulerItem->width() < 1e-6) { chronoHidden = true; @@ -1542,44 +1552,83 @@ void BlocksGraphicsView::mouseReleaseEvent(QMouseEvent* _event) else if (m_selectionItem->isVisible() && m_selectionItem->hoverIndicator()) { // Jump to selected zone - clicked = true; + jumpToZone = true; m_flickerSpeedX = m_flickerSpeedY = 0; notifyVisibleRegionPosChange(m_selectionItem->left() + (m_selectionItem->width() - m_visibleRegionWidth) * 0.5); } + } - if (!clicked && m_mouseMovePath.manhattanLength() < 5 && !m_backgroundItem->contains(scenePos)) + const bool rightClickSelect = ((m_mouseButtons & Qt::RightButton) != 0 && !changedSelection); + if ((leftClickSelect || rightClickSelect) && !jumpToZone && m_mouseMovePath.manhattanLength() < 5 && !m_backgroundItem->contains(scenePos)) + { + // Handle Click + + //clicked = true; + auto mouseClickPos = mapToScene(m_mousePressPos); + if (mouseClickPos.x() >= 0) { - // Handle Click + mouseClickPos.setX(m_offset + mouseClickPos.x() / m_scale); - //clicked = true; - auto mouseClickPos = mapToScene(m_mousePressPos); - if (mouseClickPos.x() >= 0) + // Try to select one of item blocks + for (auto item : m_items) { - mouseClickPos.setX(m_offset + mouseClickPos.x() / m_scale); - - // Try to select one of item blocks - for (auto item : m_items) - { - profiler::block_index_t i = ~0U; - auto block = item->intersect(mouseClickPos, i); - if (block != nullptr) - { - changedSelectedItem = true; - selectedBlock = block; - selectedBlockThread = item->threadId(); - EASY_GLOBALS.selected_block = i; - EASY_GLOBALS.selected_block_id = easyBlock(i).tree.node->id(); - break; - } - } - - if (!changedSelectedItem && !profiler_gui::is_max(EASY_GLOBALS.selected_block)) + profiler::block_index_t i = ~0U; + auto block = item->intersect(mouseClickPos, i); + if (block != nullptr) { changedSelectedItem = true; - profiler_gui::set_max(EASY_GLOBALS.selected_block); - profiler_gui::set_max(EASY_GLOBALS.selected_block_id); + selectedBlock = block; + selectedBlockThread = item->threadId(); + EASY_GLOBALS.selected_block = i; + EASY_GLOBALS.selected_block_id = block->tree.node->id(); + break; } } + + if (!changedSelectedItem && !profiler_gui::is_max(EASY_GLOBALS.selected_block)) + { + changedSelectedItem = true; + profiler_gui::set_max(EASY_GLOBALS.selected_block); + profiler_gui::set_max(EASY_GLOBALS.selected_block_id); + } + } + + if (selectedBlock != nullptr && rightClickSelect) + { + if (!m_selectedBlocks.empty()) + { + changedSelection = true; + m_selectedBlocks.clear(); + } + + const auto thread_item = m_items[selectedBlock->graphics_item]; + const auto& selectedItem = thread_item->items(selectedBlock->graphics_item_level)[selectedBlock->graphics_item_index]; + const auto left = selectedItem.left(); + const auto right = selectedItem.right(); + + for (auto item : m_items) + { + if (!EASY_GLOBALS.only_current_thread_hierarchy || (EASY_GLOBALS.selecting_block_changes_thread && selectedBlockThread == item->threadId())) + { + item->getBlocks(left, right, m_selectedBlocks); + } + } + + if (!m_selectedBlocks.empty()) + { + changedSelection = true; + m_selectionItem->setLeftRight(left, right); + m_selectionItem->setReverse(true); + m_selectionItem->setStrict(true); + m_selectionItem->show(); + + m_pScrollbar->setSelectionPos(left, right); + m_pScrollbar->showSelectionIndicator(); + + emit EASY_GLOBALS.events.rulerVisible(true); + } + + changedSelectionBySelectingItem = changedSelection; } } @@ -1591,15 +1640,24 @@ void BlocksGraphicsView::mouseReleaseEvent(QMouseEvent* _event) if (changedSelection) { - emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_selectionItem->left()), position2time(m_selectionItem->right()), m_selectionItem->reverse()); + profiler::timestamp_t left=0, right=0; + if (changedSelectionBySelectingItem) + { + left = selectedBlock->tree.node->begin() - m_beginTime; + right = selectedBlock->tree.node->end() - m_beginTime; + } + else + { + left = position2time(m_selectionItem->left()); + right = position2time(m_selectionItem->right()); + } + emit intervalChanged(m_selectedBlocks, m_beginTime, left, right, m_selectionItem->strict()); } if (changedSelectedItem) { profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); - 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; @@ -1609,6 +1667,8 @@ void BlocksGraphicsView::mouseReleaseEvent(QMouseEvent* _event) emit EASY_GLOBALS.events.unlockCharts(); } + emit EASY_GLOBALS.events.selectedBlockChanged(EASY_GLOBALS.selected_block); + if (selectedBlock != nullptr && isDoubleClick) { if (!selectedBlock->tree.children.empty()) @@ -1641,6 +1701,10 @@ void BlocksGraphicsView::mouseReleaseEvent(QMouseEvent* _event) repaintScene(); } + else if (jumpToZone) + { + repaintScene(); + } else if (chronoHidden) { emit sceneUpdated(); @@ -1677,7 +1741,13 @@ void BlocksGraphicsView::addSelectionToHierarchy() if (changedSelection) { - emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_selectionItem->left()), position2time(m_selectionItem->right()), m_selectionItem->reverse()); + emit intervalChanged( + m_selectedBlocks, + m_beginTime, + position2time(m_selectionItem->left()), + position2time(m_selectionItem->right()), + m_selectionItem->strict() + ); } } @@ -1712,14 +1782,15 @@ void BlocksGraphicsView::onZoomSelection() repaintScene(); // repaint scene } -void BlocksGraphicsView::onInspectCurrentView(bool _strict) +void BlocksGraphicsView::onInspectCurrentView(bool strict) { if (m_bEmpty) return; if (!m_selectionItem->isVisible()) { - m_selectionItem->setReverse(_strict); + m_selectionItem->setReverse(strict); + m_selectionItem->setStrict(strict); m_selectionItem->setLeftRight(m_offset, m_offset + m_visibleRegionWidth); m_selectionItem->show(); m_pScrollbar->setSelectionPos(m_selectionItem->left(), m_selectionItem->right()); @@ -1737,48 +1808,53 @@ void BlocksGraphicsView::onInspectCurrentView(bool _strict) ////////////////////////////////////////////////////////////////////////// -bool BlocksGraphicsView::moveChrono(GraphicsRulerItem* _chronometerItem, qreal _mouseX) +bool BlocksGraphicsView::moveChrono(GraphicsRulerItem* ruler_item, qreal mouse_x) { - if (_chronometerItem->reverse()) + if (ruler_item->reverse()) { - if (_mouseX > _chronometerItem->right()) + if (mouse_x > ruler_item->right()) { - _chronometerItem->setReverse(false); - _chronometerItem->setLeftRight(_chronometerItem->right(), _mouseX); + ruler_item->setReverse(false); + ruler_item->setLeftRight(ruler_item->right(), mouse_x); - if (_chronometerItem->hoverLeft()) + if (ruler_item->hoverLeft()) { - _chronometerItem->setHoverLeft(false); - _chronometerItem->setHoverRight(true); + ruler_item->setHoverLeft(false); + ruler_item->setHoverRight(true); } } else { - _chronometerItem->setLeftRight(_mouseX, _chronometerItem->right()); + ruler_item->setLeftRight(mouse_x, ruler_item->right()); } } else { - if (_mouseX < _chronometerItem->left()) + if (mouse_x < ruler_item->left()) { - _chronometerItem->setReverse(true); - _chronometerItem->setLeftRight(_mouseX, _chronometerItem->left()); + ruler_item->setReverse(true); + ruler_item->setLeftRight(mouse_x, ruler_item->left()); - if (_chronometerItem->hoverRight()) + if (ruler_item->hoverRight()) { - _chronometerItem->setHoverLeft(true); - _chronometerItem->setHoverRight(false); + ruler_item->setHoverLeft(true); + ruler_item->setHoverRight(false); } } else { - _chronometerItem->setLeftRight(_chronometerItem->left(), _mouseX); + ruler_item->setLeftRight(ruler_item->left(), mouse_x); } } - if (!_chronometerItem->isVisible() && _chronometerItem->width() > 1e-6) + if (!ruler_item->hoverAnyBorder()) { - _chronometerItem->show(); + ruler_item->setStrict(ruler_item->reverse()); + } + + if (!ruler_item->isVisible() && ruler_item->width() > 1e-6) + { + ruler_item->show(); emit EASY_GLOBALS.events.rulerVisible(true); return true; } @@ -1786,7 +1862,7 @@ bool BlocksGraphicsView::moveChrono(GraphicsRulerItem* _chronometerItem, qreal _ return false; } -void BlocksGraphicsView::mouseMoveEvent(QMouseEvent* _event) +void BlocksGraphicsView::mouseMoveEvent(QMouseEvent* event) { if (needToIgnoreMouseEvent()) return; @@ -1795,26 +1871,26 @@ void BlocksGraphicsView::mouseMoveEvent(QMouseEvent* _event) if (m_bEmpty) { - _event->accept(); + event->accept(); return; } - auto scenePos = mapToScene(_event->pos()); + auto scenePos = mapToScene(event->pos()); scenePos.setX(m_offset + scenePos.x() / m_scale); if (m_backgroundItem->mouseMove(scenePos)) { - _event->accept(); + event->accept(); return; } if (m_mouseButtons == 0 && !m_selectionItem->isVisible() && !m_rulerItem->isVisible()) { - _event->accept(); + event->accept(); return; } bool needUpdate = false; - const auto pos = _event->pos(); + const auto pos = event->pos(); const auto delta = pos - m_mousePressPos; m_mousePressPos = pos; @@ -1851,7 +1927,9 @@ void BlocksGraphicsView::mouseMoveEvent(QMouseEvent* _event) { auto vbar = verticalScrollBar(); - profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); // Block scrollbars from updating scene rect to make it possible to do it only once + // Block scrollbars from updating scene rect to make it possible to do it only once + profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); + vbar->setValue(vbar->value() - delta.y()); notifyVisibleRegionPosChange(m_offset - delta.x() / m_scale); guard.restore(); @@ -1913,16 +1991,16 @@ void BlocksGraphicsView::mouseMoveEvent(QMouseEvent* _event) repaintScene(); // repaint scene } - _event->accept(); + event->accept(); } ////////////////////////////////////////////////////////////////////////// -void BlocksGraphicsView::keyPressEvent(QKeyEvent* _event) +void BlocksGraphicsView::keyPressEvent(QKeyEvent* event) { static const int KeyStep = 100; - const int key = _event->key(); + const int key = event->key(); switch (key) { @@ -1971,14 +2049,14 @@ void BlocksGraphicsView::keyPressEvent(QKeyEvent* _event) } m_idleTime = 0; - _event->accept(); + event->accept(); } ////////////////////////////////////////////////////////////////////////// -void BlocksGraphicsView::resizeEvent(QResizeEvent* _event) +void BlocksGraphicsView::resizeEvent(QResizeEvent* event) { - Parent::resizeEvent(_event); + Parent::resizeEvent(event); const QRectF previousRect = m_visibleSceneRect; const int vbar_width = updateVisibleSceneRect(); // Update scene visible rect only once @@ -2062,7 +2140,13 @@ void BlocksGraphicsView::initMode() connect(globalSignals, &profiler_gui::GlobalSignals::blocksTreeModeChanged, [this]() { if (!m_selectedBlocks.empty()) - emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_selectionItem->left()), position2time(m_selectionItem->right()), m_selectionItem->reverse()); + emit intervalChanged( + m_selectedBlocks, + m_beginTime, + position2time(m_selectionItem->left()), + position2time(m_selectionItem->right()), + m_selectionItem->strict() + ); }); connect(globalSignals, &profiler_gui::GlobalSignals::chartSliderChanged, this, &This::onGraphicsScrollbarValueChange); @@ -2095,11 +2179,11 @@ void BlocksGraphicsView::onScrollbarValueChange(int) updateVisibleSceneRect(); } -void BlocksGraphicsView::onGraphicsScrollbarValueChange(qreal _value) +void BlocksGraphicsView::onGraphicsScrollbarValueChange(qreal value) { if (!m_bEmpty) { - m_offset = _value; + m_offset = value; if (!m_bUpdatingRect) { updateVisibleSceneRect(); @@ -2184,7 +2268,7 @@ void BlocksGraphicsView::onIdleTimeout() if (m_popupWidget != nullptr) return; - if (!window()->isActiveWindow()) + if (window() == nullptr || !window()->isActiveWindow()) return; auto focusWidget = qApp->focusWidget(); @@ -2507,7 +2591,7 @@ void BlocksGraphicsView::onIdleTimeout() lay->addWidget(new QLabel(QString::number(itemBlock.per_frame_stats->calls_number), widget), row + 5, col, Qt::AlignHCenter); } - if (!profiler_gui::is_max(itemBlock.per_parent_stats->parent_block))// != item->threadId()) + if (!profiler_gui::is_max(itemBlock.per_parent_stats->parent_block)) { ++col; auto parent_duration = easyBlocksTree(itemBlock.per_parent_stats->parent_block).node->duration(); @@ -2592,18 +2676,24 @@ void BlocksGraphicsView::onHierarchyFlagChange(bool) if (changedSelection) { - emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_selectionItem->left()), position2time(m_selectionItem->right()), m_selectionItem->reverse()); + emit intervalChanged( + m_selectedBlocks, + m_beginTime, + position2time(m_selectionItem->left()), + position2time(m_selectionItem->right()), + m_selectionItem->strict() + ); } } -void BlocksGraphicsView::onSelectedThreadChange(profiler::thread_id_t _id) +void BlocksGraphicsView::onSelectedThreadChange(profiler::thread_id_t id) { - if (m_pScrollbar == nullptr || m_pScrollbar->hystThread() == _id) + if (m_pScrollbar == nullptr || m_pScrollbar->hystThread() == id) { return; } - if (_id == 0) + if (id == 0) { m_pScrollbar->setHistogramSource(0, nullptr); return; @@ -2611,9 +2701,9 @@ void BlocksGraphicsView::onSelectedThreadChange(profiler::thread_id_t _id) for (auto item : m_items) { - if (item->threadId() == _id) + if (item->threadId() == id) { - m_pScrollbar->setHistogramSource(_id, item->items(0)); + m_pScrollbar->setHistogramSource(id, item->items(0)); bool changedSelection = false; if (EASY_GLOBALS.only_current_thread_hierarchy) @@ -2634,7 +2724,13 @@ void BlocksGraphicsView::onSelectedThreadChange(profiler::thread_id_t _id) if (changedSelection) { - emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_selectionItem->left()), position2time(m_selectionItem->right()), m_selectionItem->reverse()); + emit intervalChanged( + m_selectedBlocks, + m_beginTime, + position2time(m_selectionItem->left()), + position2time(m_selectionItem->right()), + m_selectionItem->strict() + ); } repaintScene(); @@ -3007,7 +3103,7 @@ void ThreadNamesWidget::onIdleTimeout() if (m_popupWidget != nullptr) return; - if (!window()->isActiveWindow()) + if (window() == nullptr || !window()->isActiveWindow()) return; auto focusWidget = qApp->focusWidget(); @@ -3123,7 +3219,7 @@ void ThreadNamesWidget::onIdleTimeout() m_popupWidget = widget; if (m_popupWidget != nullptr) { - auto focusWidget = qApp->focusWidget(); + focusWidget = qApp->focusWidget(); m_popupWidget->move(QCursor::pos()); m_popupWidget->show(); diff --git a/profiler_gui/blocks_graphics_view.h b/profiler_gui/blocks_graphics_view.h index 7272685..f0e06c5 100644 --- a/profiler_gui/blocks_graphics_view.h +++ b/profiler_gui/blocks_graphics_view.h @@ -20,7 +20,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -236,9 +236,9 @@ public: void mousePressEvent(QMouseEvent* _event) override; void mouseDoubleClickEvent(QMouseEvent* _event) override; void mouseReleaseEvent(QMouseEvent* _event) override; - void mouseMoveEvent(QMouseEvent* _event) override; - void keyPressEvent(QKeyEvent* _event) override; - void resizeEvent(QResizeEvent* _event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; + void resizeEvent(QResizeEvent* event) override; void dragEnterEvent(QDragEnterEvent*) override {} @@ -288,7 +288,7 @@ private: bool needToIgnoreMouseEvent() const; GraphicsRulerItem* createRuler(bool _main = true); - bool moveChrono(GraphicsRulerItem* _chronometerItem, qreal _mouseX); + bool moveChrono(GraphicsRulerItem* ruler_item, qreal mouse_x); void initMode(); int updateVisibleSceneRect(); void updateTimelineStep(qreal _windowWidth); @@ -313,7 +313,7 @@ private slots: void onFlickerTimeout(); void onIdleTimeout(); void onHierarchyFlagChange(bool _value); - void onSelectedThreadChange(::profiler::thread_id_t _id); + void onSelectedThreadChange(::profiler::thread_id_t id); void onSelectedBlockChange(unsigned int _block_index); void onRefreshRequired(); void onThreadViewChanged(); diff --git a/profiler_gui/blocks_tree_widget.cpp b/profiler_gui/blocks_tree_widget.cpp index 2b3849b..5ae661d 100644 --- a/profiler_gui/blocks_tree_widget.cpp +++ b/profiler_gui/blocks_tree_widget.cpp @@ -21,7 +21,7 @@ * : * 2016/08/18 Victor Zarubkin: Moved sources of TreeWidgetItem into tree_widget_item.h/.cpp * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -98,35 +98,100 @@ const int HIERARCHY_BUILDER_TIMER_INTERVAL = 40; -const bool SIMPLIFIED_REGIME_COLUMNS[COL_COLUMNS_NUMBER] = { +const bool PLAIN_MODE_COLUMNS[COL_COLUMNS_NUMBER] = { true, //COL_NAME, true, //COL_BEGIN, - true, //COL_DURATION, - true, //COL_SELF_DURATION, - false, //COL_DURATION_SUM_PER_PARENT, - false, //COL_DURATION_SUM_PER_FRAME, - true, //COL_DURATION_SUM_PER_THREAD, - true, //COL_SELF_DURATION_PERCENT, + true, //COL_TIME, + true, //COL_SELF_TIME, + + false, //COL_TOTAL_TIME_PER_PARENT, + false, //COL_TOTAL_TIME_PER_FRAME, + true, //COL_TOTAL_TIME_PER_THREAD, + + true, //COL_SELF_TIME_PERCENT, + false, //COL_PERCENT_PER_PARENT, true, //COL_PERCENT_PER_FRAME, + false, //COL_PERCENT_SUM_PER_PARENT, false, //COL_PERCENT_SUM_PER_FRAME, true, //COL_PERCENT_SUM_PER_THREAD, + true, //COL_END, + true, //COL_MIN_PER_FRAME, true, //COL_MAX_PER_FRAME, - true, //COL_AVERAGE_PER_FRAME, + true, //COL_AVG_PER_FRAME, true, //COL_NCALLS_PER_FRAME, + true, //COL_MIN_PER_THREAD, true, //COL_MAX_PER_THREAD, - true, //COL_AVERAGE_PER_THREAD, + true, //COL_AVG_PER_THREAD, true, //COL_NCALLS_PER_THREAD, + false, //COL_MIN_PER_PARENT, false, //COL_MAX_PER_PARENT, - false, //COL_AVERAGE_PER_PARENT, + false, //COL_AVG_PER_PARENT, false, //COL_NCALLS_PER_PARENT, + true, //COL_ACTIVE_TIME, - true //COL_ACTIVE_PERCENT, + true, //COL_ACTIVE_PERCENT, + + true, //COL_PERCENT_PER_AREA, + true, //COL_TOTAL_TIME_PER_AREA, + true, //COL_PERCENT_SUM_PER_AREA, + true, //COL_MIN_PER_AREA, + true, //COL_MAX_PER_AREA, + true, //COL_AVG_PER_AREA, + true //COL_NCALLS_PER_AREA, +}; + +const bool SELECTION_MODE_COLUMNS[COL_COLUMNS_NUMBER] = { + true, //COL_NAME, + false, //COL_BEGIN, + true, //COL_TIME, + true, //COL_SELF_TIME, + + false, //COL_TOTAL_TIME_PER_PARENT, + false, //COL_TOTAL_TIME_PER_FRAME, + true, //COL_TOTAL_TIME_PER_THREAD, + + true, //COL_SELF_TIME_PERCENT, + + false, //COL_PERCENT_PER_PARENT, + false, //COL_PERCENT_PER_FRAME, + + false, //COL_PERCENT_SUM_PER_PARENT, + false, //COL_PERCENT_SUM_PER_FRAME, + true, //COL_PERCENT_SUM_PER_THREAD, + + false, //COL_END, + + false, //COL_MIN_PER_FRAME, + false, //COL_MAX_PER_FRAME, + false, //COL_AVG_PER_FRAME, + false, //COL_NCALLS_PER_FRAME, + + true, //COL_MIN_PER_THREAD, + true, //COL_MAX_PER_THREAD, + true, //COL_AVG_PER_THREAD, + true, //COL_NCALLS_PER_THREAD, + + false, //COL_MIN_PER_PARENT, + false, //COL_MAX_PER_PARENT, + false, //COL_AVG_PER_PARENT, + false, //COL_NCALLS_PER_PARENT, + + true, //COL_ACTIVE_TIME, + true, //COL_ACTIVE_PERCENT, + + false, //COL_PERCENT_PER_AREA, + true, //COL_TOTAL_TIME_PER_AREA, + true, //COL_PERCENT_SUM_PER_AREA, + true, //COL_MIN_PER_AREA, + true, //COL_MAX_PER_AREA, + true, //COL_AVG_PER_AREA, + true //COL_NCALLS_PER_AREA, }; ////////////////////////////////////////////////////////////////////////// @@ -139,12 +204,15 @@ BlocksTreeWidget::BlocksTreeWidget(QWidget* _parent) , m_hintLabel(nullptr) , m_valueTooltip(nullptr) , m_mode(TreeMode::Plain) + , m_lastFoundIndex(0) , m_bLocked(false) , m_bSilentExpandCollapse(false) + , m_bCaseSensitiveSearch(false) , m_bInitialized(false) { installEventFilter(this); memset(m_columnsHiddenStatus, 0, sizeof(m_columnsHiddenStatus)); + memset(m_columnsMinimumWidth, 0, sizeof(m_columnsMinimumWidth)); setAutoFillBackground(false); setAlternatingRowColors(true); @@ -154,117 +222,165 @@ BlocksTreeWidget::BlocksTreeWidget(QWidget* _parent) setColumnCount(COL_COLUMNS_NUMBER); setSelectionBehavior(QAbstractItemView::SelectRows); + header()->setSectionResizeMode(QHeaderView::Interactive); + auto header_item = new QTreeWidgetItem(); - auto f = header()->font(); - f.setBold(true); - header()->setFont(f);// profiler_gui::EFont("Helvetica", 9, QFont::Bold)); header_item->setText(COL_NAME, "Name"); header_item->setText(COL_BEGIN, "Begin, ms"); - header_item->setText(COL_DURATION, "Duration"); - header_item->setText(COL_SELF_DURATION, "Self dur."); - //header_item->setToolTip(COL_SELF_DURATION, ""); - header_item->setText(COL_DURATION_SUM_PER_PARENT, "Total / Parent"); - header_item->setText(COL_DURATION_SUM_PER_FRAME, "Total / Frame"); - header_item->setText(COL_DURATION_SUM_PER_THREAD, "Total / Thread"); + header_item->setText(COL_TIME, "Time"); + header_item->setText(COL_SELF_TIME, "SelfTime"); + //header_item->setToolTip(COL_SELF_TIME, ""); + header_item->setText(COL_TOTAL_TIME_PER_PARENT, "Total/parent"); + header_item->setText(COL_TOTAL_TIME_PER_FRAME, "Total/frame"); + header_item->setText(COL_TOTAL_TIME_PER_THREAD, "Total/thread"); - header_item->setText(COL_SELF_DURATION_PERCENT, "Self %"); - header_item->setText(COL_PERCENT_PER_PARENT, "% / Parent"); - header_item->setText(COL_PERCENT_PER_FRAME, "% / Frame"); - header_item->setText(COL_PERCENT_SUM_PER_FRAME, "Sum % / Frame"); - header_item->setText(COL_PERCENT_SUM_PER_PARENT, "Sum % / Parent"); - header_item->setText(COL_PERCENT_SUM_PER_THREAD, "Sum % / Thread"); + header_item->setText(COL_SELF_TIME_PERCENT, "Self%"); + header_item->setText(COL_PERCENT_PER_PARENT, "%/parent"); + header_item->setText(COL_PERCENT_PER_FRAME, "%/frame"); + header_item->setText(COL_PERCENT_SUM_PER_FRAME, "Total%/frame"); + header_item->setText(COL_PERCENT_SUM_PER_PARENT, "Total%/parent"); + header_item->setText(COL_PERCENT_SUM_PER_THREAD, "Total%/thread"); header_item->setText(COL_END, "End, ms"); - header_item->setText(COL_MIN_PER_FRAME, "Min / Frame"); - header_item->setText(COL_MAX_PER_FRAME, "Max / Frame"); - header_item->setText(COL_AVERAGE_PER_FRAME, "Avg / Frame"); - header_item->setText(COL_NCALLS_PER_FRAME, "N Calls / Frame"); + header_item->setText(COL_MIN_PER_FRAME, "Min/frame"); + header_item->setText(COL_MAX_PER_FRAME, "Max/frame"); + header_item->setText(COL_AVG_PER_FRAME, "Avg/frame"); + header_item->setText(COL_NCALLS_PER_FRAME, "N/frame"); - header_item->setText(COL_MIN_PER_PARENT, "Min / Parent"); - header_item->setText(COL_MAX_PER_PARENT, "Max / Parent"); - header_item->setText(COL_AVERAGE_PER_PARENT, "Avg / Parent"); - header_item->setText(COL_NCALLS_PER_PARENT, "N Calls / Parent"); + header_item->setText(COL_MIN_PER_PARENT, "Min/parent"); + header_item->setText(COL_MAX_PER_PARENT, "Max/parent"); + header_item->setText(COL_AVG_PER_PARENT, "Avg/parent"); + header_item->setText(COL_NCALLS_PER_PARENT, "N/parent"); - header_item->setText(COL_MIN_PER_THREAD, "Min / Thread"); - header_item->setText(COL_MAX_PER_THREAD, "Max / Thread"); - header_item->setText(COL_AVERAGE_PER_THREAD, "Avg / Thread"); - header_item->setText(COL_NCALLS_PER_THREAD, "N Calls / Thread"); + header_item->setText(COL_MIN_PER_THREAD, "Min/thread"); + header_item->setText(COL_MAX_PER_THREAD, "Max/thread"); + header_item->setText(COL_AVG_PER_THREAD, "Avg/thread"); + header_item->setText(COL_NCALLS_PER_THREAD, "N/thread"); - header_item->setText(COL_ACTIVE_TIME, "Active time"); - header_item->setText(COL_ACTIVE_PERCENT, "Active %"); + header_item->setText(COL_ACTIVE_TIME, "WorkTime"); + header_item->setText(COL_ACTIVE_PERCENT, "Work%"); + + header_item->setText(COL_PERCENT_PER_AREA, "%/area"); + header_item->setText(COL_TOTAL_TIME_PER_AREA, "Total/area"); + header_item->setText(COL_PERCENT_SUM_PER_AREA, "Total%/area"); + header_item->setText(COL_MIN_PER_AREA, "Min/area"); + header_item->setText(COL_MAX_PER_AREA, "Max/area"); + header_item->setText(COL_AVG_PER_AREA, "Avg/area"); + header_item->setText(COL_NCALLS_PER_AREA, "N/area"); auto color = QColor::fromRgb(profiler::colors::DeepOrange900); header_item->setForeground(COL_MIN_PER_THREAD, color); header_item->setForeground(COL_MAX_PER_THREAD, color); - header_item->setForeground(COL_AVERAGE_PER_THREAD, color); + header_item->setForeground(COL_AVG_PER_THREAD, color); header_item->setForeground(COL_NCALLS_PER_THREAD, color); header_item->setForeground(COL_PERCENT_SUM_PER_THREAD, color); - header_item->setForeground(COL_DURATION_SUM_PER_THREAD, color); + header_item->setForeground(COL_TOTAL_TIME_PER_THREAD, color); - color = QColor::fromRgb(profiler::colors::Blue900); + color = QColor::fromRgb(profiler::colors::Purple800); header_item->setForeground(COL_MIN_PER_FRAME, color); header_item->setForeground(COL_MAX_PER_FRAME, color); - header_item->setForeground(COL_AVERAGE_PER_FRAME, color); + header_item->setForeground(COL_AVG_PER_FRAME, color); header_item->setForeground(COL_NCALLS_PER_FRAME, color); header_item->setForeground(COL_PERCENT_SUM_PER_FRAME, color); - header_item->setForeground(COL_DURATION_SUM_PER_FRAME, color); + header_item->setForeground(COL_TOTAL_TIME_PER_FRAME, color); header_item->setForeground(COL_PERCENT_PER_FRAME, color); color = QColor::fromRgb(profiler::colors::Teal900); header_item->setForeground(COL_MIN_PER_PARENT, color); header_item->setForeground(COL_MAX_PER_PARENT, color); - header_item->setForeground(COL_AVERAGE_PER_PARENT, color); + header_item->setForeground(COL_AVG_PER_PARENT, color); header_item->setForeground(COL_NCALLS_PER_PARENT, color); header_item->setForeground(COL_PERCENT_SUM_PER_PARENT, color); - header_item->setForeground(COL_DURATION_SUM_PER_PARENT, color); + header_item->setForeground(COL_TOTAL_TIME_PER_PARENT, color); header_item->setForeground(COL_PERCENT_PER_PARENT, color); + color = QColor::fromRgb(profiler::colors::Blue900); + header_item->setForeground(COL_PERCENT_PER_AREA, color); + header_item->setForeground(COL_TOTAL_TIME_PER_AREA, color); + header_item->setForeground(COL_PERCENT_SUM_PER_AREA, color); + header_item->setForeground(COL_MIN_PER_AREA, color); + header_item->setForeground(COL_MAX_PER_AREA, color); + header_item->setForeground(COL_AVG_PER_AREA, color); + header_item->setForeground(COL_NCALLS_PER_AREA, color); + setHeaderItem(header_item); - connect(&EASY_GLOBALS.events, &profiler_gui::GlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange, Qt::QueuedConnection); - connect(&EASY_GLOBALS.events, &profiler_gui::GlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange, Qt::QueuedConnection); + connect(&EASY_GLOBALS.events, &profiler_gui::GlobalSignals::selectedThreadChanged, + this, &This::onSelectedThreadChange, Qt::QueuedConnection); + connect(&EASY_GLOBALS.events, &profiler_gui::GlobalSignals::selectedBlockChanged, + this, &This::onSelectedBlockChange, Qt::QueuedConnection); connect(&m_fillTimer, &QTimer::timeout, this, &This::onFillTimerTimeout); connect(&m_idleTimer, &QTimer::timeout, this, &This::onIdleTimeout); m_idleTimer.setInterval(500); + m_idleTimer.setSingleShot(true); loadSettings(); m_columnsHiddenStatus[0] = 0; setColumnHidden(0, false); - if (m_mode == TreeMode::Full) + switch (m_mode) { - for (int i = 1; i < COL_COLUMNS_NUMBER; ++i) - m_columnsHiddenStatus[i] = isColumnHidden(i) ? 1 : 0; - } - else - { - for (int i = 1; i < COL_COLUMNS_NUMBER; ++i) + case TreeMode::Full: { - if (SIMPLIFIED_REGIME_COLUMNS[i]) + for (int i = 1; i < COL_COLUMNS_NUMBER; ++i) { - if (isColumnHidden(i)) - m_columnsHiddenStatus[i] = 1; + m_columnsHiddenStatus[i] = static_cast(isColumnHidden(i) ? 1 : 0); } - else if (!isColumnHidden(i)) + break; + } + + case TreeMode::Plain: + { + for (int i = 1; i < COL_COLUMNS_NUMBER; ++i) { - setColumnHidden(i, true); + if (PLAIN_MODE_COLUMNS[i]) + { + if (isColumnHidden(i)) + m_columnsHiddenStatus[i] = 1; + } + else if (!isColumnHidden(i)) + { + setColumnHidden(i, true); + } } + break; + } + + case TreeMode::SelectedArea: + { + for (int i = 1; i < COL_COLUMNS_NUMBER; ++i) + { + if (SELECTION_MODE_COLUMNS[i]) + { + if (isColumnHidden(i)) + m_columnsHiddenStatus[i] = 1; + } + else if (!isColumnHidden(i)) + { + setColumnHidden(i, true); + } + } + break; } } m_hintLabel = new QLabel("Use Right Mouse Button on the Diagram to build a hierarchy...\n" - "Press and hold the button >> Move mouse >> Release the button", this); + "1. Press the button >> Move mouse >> Release the button\n" + "2. Or just click the right mouse button on any block", this); m_hintLabel->setObjectName(QStringLiteral("BlocksTreeWidget_HintLabel")); + m_hintLabel->setProperty("hovered", false); m_hintLabel->setAlignment(Qt::AlignCenter); QTimer::singleShot(1500, this, &This::alignProgressBar); setItemDelegateForColumn(0, new TreeWidgetItemDelegate(this)); + + connect(header(), &QHeaderView::sectionResized, this, &This::onHeaderSectionResized); } BlocksTreeWidget::~BlocksTreeWidget() @@ -315,6 +431,23 @@ bool BlocksTreeWidget::eventFilter(QObject* _object, QEvent* _event) { if (!m_bInitialized) { +#if !defined(_WIN32) && !defined(__APPLE__) + const auto padding = px(9); +#else + const auto padding = px(6); +#endif + + auto f = header()->font(); +#if !defined(_WIN32) && !defined(__APPLE__) + f.setBold(true); +#endif + QFontMetrics fm(f); + + for (int i = 0; i < COL_COLUMNS_NUMBER; ++i) + { + m_columnsMinimumWidth[i] = static_cast(fm.width(headerItem()->text(i)) * profiler_gui::FONT_METRICS_FACTOR + padding); + } + EASY_CONSTEXPR int Margins = 20; setMinimumSize(m_hintLabel->width() + Margins, m_hintLabel->height() + header()->height() + Margins); m_bInitialized = true; @@ -329,11 +462,34 @@ bool BlocksTreeWidget::eventFilter(QObject* _object, QEvent* _event) return false; } +void BlocksTreeWidget::updateHintLabelOnHover(bool hover) +{ + m_hintLabel->setProperty("hovered", hover); + m_hintLabel->style()->unpolish(m_hintLabel); + m_hintLabel->style()->polish(m_hintLabel); + if (m_hintLabel->isVisible()) + { + m_hintLabel->update(); + } +} + +void BlocksTreeWidget::onHeaderSectionResized(int logicalIndex, int /*oldSize*/, int newSize) +{ + const auto indicatorSize = header()->isSortIndicatorShown() && header()->sortIndicatorSection() == logicalIndex ? px(11) : 0; + const auto minSize = m_columnsMinimumWidth[logicalIndex] + indicatorSize; + + if (!m_bInitialized || newSize >= minSize) + { + return; + } + + header()->resizeSection(logicalIndex, minSize); +} + void BlocksTreeWidget::mousePressEvent(QMouseEvent* _event) { delete m_valueTooltip; m_valueTooltip = nullptr; - if (m_idleTimer.isActive()) m_idleTimer.stop(); m_idleTimer.start(); @@ -369,9 +525,27 @@ void BlocksTreeWidget::onFillTimerTimeout() setSortingEnabled(true); - sortByColumn(COL_BEGIN, Qt::AscendingOrder); // sort by begin time - if (m_mode == TreeMode::Plain) // and after that, sort by frame % - sortByColumn(COL_PERCENT_PER_FRAME, Qt::DescendingOrder); + switch (m_mode) + { + case TreeMode::Full: + { + sortByColumn(COL_BEGIN, Qt::AscendingOrder); // sort by begin time + break; + } + + case TreeMode::Plain: + { + sortByColumn(COL_BEGIN, Qt::AscendingOrder); // sort by begin time + sortByColumn(COL_PERCENT_PER_FRAME, Qt::DescendingOrder); // and after that, sort by frame % + break; + } + + case TreeMode::SelectedArea: + { + sortByColumn(COL_PERCENT_SUM_PER_AREA, Qt::DescendingOrder); + break; + } + } //resizeColumnToContents(COL_NAME); resizeColumnsToContents(); @@ -379,6 +553,7 @@ void BlocksTreeWidget::onFillTimerTimeout() connect(this, &Parent::itemExpanded, this, &This::onItemExpand); connect(this, &Parent::itemCollapsed, this, &This::onItemCollapse); connect(this, &Parent::currentItemChanged, this, &This::onCurrentItemChange); + connect(this, &Parent::itemDoubleClicked, this, &This::onItemDoubleClicked); onSelectedThreadChange(EASY_GLOBALS.selected_thread); onSelectedBlockChange(EASY_GLOBALS.selected_block); } @@ -390,9 +565,6 @@ void BlocksTreeWidget::onFillTimerTimeout() void BlocksTreeWidget::onIdleTimeout() { - if (m_idleTimer.isActive()) - m_idleTimer.stop(); - // Close old tooltip delete m_valueTooltip; m_valueTooltip = nullptr; @@ -430,34 +602,6 @@ void BlocksTreeWidget::onIdleTimeout() m_valueTooltip->setFixedSize(m_valueTooltip->size()); } -void BlocksTreeWidget::setTree(const unsigned int _blocksNumber, const profiler::thread_blocks_tree_t& _blocksTree) -{ - clearSilent(); - - if (!_blocksTree.empty()) - { - m_bLocked = true; - m_hintLabel->hide(); - createProgressDialog(); - m_hierarchyBuilder.fillTree(m_beginTime, _blocksNumber, _blocksTree, m_mode); - m_fillTimer.start(HIERARCHY_BUILDER_TIMER_INTERVAL); - } - - //StubLocker l; - //ThreadedItems toplevelitems; - //FillTreeClass::setTreeInternal1(l, m_items, toplevelitems, m_beginTime, _blocksNumber, _blocksTree, m_bColorRows); - //{ - // const QSignalBlocker b(this); - // for (auto& item : toplevelitems) - // { - // addTopLevelItem(item.second); - // m_roots[item.first] = item.second; - // if (item.first == EASY_GLOBALS.selected_thread) - // item.second->setMain(true); - // } - //} -} - void BlocksTreeWidget::setTreeBlocks(const profiler_gui::TreeBlocks& _blocks, profiler::timestamp_t _session_begin_time, profiler::timestamp_t _left, profiler::timestamp_t _right, bool _strict) { clearSilent(); @@ -516,32 +660,15 @@ void BlocksTreeWidget::clearSilent(bool _global) disconnect(this, &Parent::itemExpanded, this, &This::onItemExpand); disconnect(this, &Parent::itemCollapsed, this, &This::onItemCollapse); disconnect(this, &Parent::currentItemChanged, this, &This::onCurrentItemChange); - m_lastFound = nullptr; - m_lastSearch.clear(); + disconnect(this, &Parent::itemDoubleClicked, this, &This::onItemDoubleClicked); + resetSearch(false); - if (!_global) + if (!_global && EASY_GLOBALS.collapse_items_on_tree_close) { - if (EASY_GLOBALS.collapse_items_on_tree_close) -#ifdef EASY_TREE_WIDGET__USE_VECTOR - for (auto item : m_items) -#else - for (auto& item : m_items) -#endif + for (auto& item : m_items) { -#ifdef EASY_TREE_WIDGET__USE_VECTOR - auto& gui_block = item->guiBlock(); - gui_block.expanded = false; - profiler_gui::set_max(gui_block.tree_item); -#else item.second->guiBlock().expanded = false; -#endif } -#ifdef EASY_TREE_WIDGET__USE_VECTOR - else for (auto item : m_items) - { - profiler_gui::set_max(item->guiBlock().tree_item); - } -#endif } m_items.clear(); @@ -568,25 +695,71 @@ void BlocksTreeWidget::clearSilent(bool _global) ////////////////////////////////////////////////////////////////////////// +void BlocksTreeWidget::resetSearch(bool repaint) +{ + if (m_lastSearch.isEmpty()) + { + return; + } + + m_bCaseSensitiveSearch = false; + m_lastSearch.clear(); + m_lastFound = nullptr; + m_lastFoundIndex = 0; + + if (repaint) + { + viewport()->update(); + } +} + +QTreeWidgetItem* BlocksTreeWidget::lastFoundItem() const +{ + return m_lastFound; +} + +bool BlocksTreeWidget::caseSensitiveSearch() const +{ + return m_bCaseSensitiveSearch; +} + +const QString& BlocksTreeWidget::searchString() const +{ + return m_lastSearch; +} + +int BlocksTreeWidget::lastFoundIndex() const +{ + return m_lastFoundIndex; +} + int BlocksTreeWidget::findNext(const QString& _str, Qt::MatchFlags _flags) { - if (m_bLocked || _str.isEmpty()) + if (m_bLocked) + { return 0; + } + + if (_str.isEmpty()) + { + resetSearch(); + return 0; + } const bool isNewSearch = (m_lastSearch != _str); auto itemsList = findItems(_str, Qt::MatchContains | Qt::MatchRecursive | _flags, COL_NAME); + m_bCaseSensitiveSearch = _flags.testFlag(Qt::MatchCaseSensitive); + if (!isNewSearch) { if (!itemsList.empty()) { bool stop = false; + int i = 0; decltype(m_lastFound) next = nullptr; for (auto item : itemsList) { - if (item->parent() == nullptr) - continue; - if (stop) { next = item; @@ -594,19 +767,23 @@ int BlocksTreeWidget::findNext(const QString& _str, Qt::MatchFlags _flags) } stop = item == m_lastFound; + ++i; } m_lastFound = next == nullptr ? itemsList.front() : next; + m_lastFoundIndex = next == nullptr ? 0 : i; } else { m_lastFound = nullptr; + m_lastFoundIndex = 0; } } else { m_lastSearch = _str; m_lastFound = !itemsList.empty() ? itemsList.front() : nullptr; + m_lastFoundIndex = 0; } if (m_lastFound != nullptr) @@ -615,44 +792,68 @@ int BlocksTreeWidget::findNext(const QString& _str, Qt::MatchFlags _flags) setCurrentItem(m_lastFound); } + viewport()->update(); + return itemsList.size(); } int BlocksTreeWidget::findPrev(const QString& _str, Qt::MatchFlags _flags) { - if (m_bLocked || _str.isEmpty()) + if (m_bLocked) + { return 0; + } + + if (_str.isEmpty()) + { + resetSearch(); + return 0; + } const bool isNewSearch = (m_lastSearch != _str); auto itemsList = findItems(_str, Qt::MatchContains | Qt::MatchRecursive | _flags, COL_NAME); + m_bCaseSensitiveSearch = _flags.testFlag(Qt::MatchCaseSensitive); + if (!isNewSearch) { if (!itemsList.empty()) { + int i = 0; decltype(m_lastFound) prev = nullptr; for (auto item : itemsList) { - if (item->parent() == nullptr) - continue; - if (item == m_lastFound) + { + --i; break; + } prev = item; + ++i; } m_lastFound = prev == nullptr ? itemsList.back() : prev; + m_lastFoundIndex = prev == nullptr ? itemsList.length() - 1 : i; } else { m_lastFound = nullptr; + m_lastFoundIndex = 0; } } else { m_lastSearch = _str; - m_lastFound = !itemsList.empty() ? itemsList.front() : nullptr; + if (!itemsList.empty()) { + m_lastFound = itemsList.back(); + m_lastFoundIndex = itemsList.length() - 1; + } + else + { + m_lastFound = nullptr; + m_lastFoundIndex = 0; + } } if (m_lastFound != nullptr) @@ -661,6 +862,8 @@ int BlocksTreeWidget::findPrev(const QString& _str, Qt::MatchFlags _flags) setCurrentItem(m_lastFound); } + viewport()->update(); + return itemsList.size(); } @@ -674,6 +877,11 @@ void BlocksTreeWidget::contextMenuEvent(QContextMenuEvent* _event) return; } + delete m_valueTooltip; + m_valueTooltip = nullptr; + if (m_idleTimer.isActive()) + m_idleTimer.stop(); + const auto col = currentColumn(); auto item = static_cast(currentItem()); QMenu menu; @@ -709,22 +917,30 @@ void BlocksTreeWidget::contextMenuEvent(QContextMenuEvent* _event) auto actionGroup = new QActionGroup(&menu); actionGroup->setExclusive(true); - auto actionHierarchy = new QAction("Hierarchy mode", actionGroup); + auto actionHierarchy = new QAction("Call-stack", actionGroup); actionHierarchy->setCheckable(true); actionHierarchy->setChecked(m_mode == TreeMode::Full); - actionHierarchy->setToolTip("Display full blocks hierarchy"); + actionHierarchy->setToolTip("Display full call stack"); actionHierarchy->setData((quint32)TreeMode::Full); menu.addAction(actionHierarchy); - auto actionPlain = new QAction("Plain mode", actionGroup); + auto actionPlain = new QAction("Per-frame stats", actionGroup); actionPlain->setCheckable(true); actionPlain->setChecked(m_mode == TreeMode::Plain); actionPlain->setToolTip("Display plain list of blocks per frame.\nSome columns are disabled with this mode."); actionPlain->setData((quint32)TreeMode::Plain); menu.addAction(actionPlain); + auto actionSelectedArea = new QAction("Aggregate stats", actionGroup); + actionSelectedArea->setCheckable(true); + actionSelectedArea->setChecked(m_mode == TreeMode::SelectedArea); + actionSelectedArea->setToolTip("Display aggregate stats for selected area.\nSome columns are disabled with this mode."); + actionSelectedArea->setData((quint32)TreeMode::SelectedArea); + menu.addAction(actionSelectedArea); + connect(actionHierarchy, &QAction::triggered, this, &This::onModeChange); connect(actionPlain, &QAction::triggered, this, &This::onModeChange); + connect(actionSelectedArea, &QAction::triggered, this, &This::onModeChange); menu.addSeparator(); @@ -799,19 +1015,71 @@ void BlocksTreeWidget::contextMenuEvent(QContextMenuEvent* _event) auto hidemenu = menu.addMenu("Select columns"); auto hdr = headerItem(); - for (int i = 1; i < COL_COLUMNS_NUMBER; ++i) - { - auto columnAction = new QAction(hdr->text(i), nullptr); - columnAction->setData(i); - columnAction->setCheckable(true); - columnAction->setChecked(m_columnsHiddenStatus[i] == 0); - if ((m_mode == TreeMode::Full || SIMPLIFIED_REGIME_COLUMNS[i])) - connect(columnAction, &QAction::triggered, this, &This::onHideShowColumn); - else - columnAction->setEnabled(false); - hidemenu->addAction(columnAction); +#define ADD_COLUMN_ACTION(i) \ + { \ + auto columnAction = new QAction(hdr->text(i), nullptr); \ + columnAction->setData(i); \ + columnAction->setCheckable(true); \ + columnAction->setChecked(m_columnsHiddenStatus[i] == 0); \ + if ((m_mode == TreeMode::Full || (m_mode == TreeMode::Plain && PLAIN_MODE_COLUMNS[i]) || (m_mode == TreeMode::SelectedArea && SELECTION_MODE_COLUMNS[i]))) \ + connect(columnAction, &QAction::triggered, this, &This::onHideShowColumn); \ + else \ + columnAction->setEnabled(false); \ + hidemenu->addAction(columnAction); \ } + ADD_COLUMN_ACTION(COL_BEGIN); + ADD_COLUMN_ACTION(COL_END); + ADD_COLUMN_ACTION(COL_TIME); + ADD_COLUMN_ACTION(COL_SELF_TIME); + ADD_COLUMN_ACTION(COL_SELF_TIME_PERCENT); + + hidemenu->addSeparator(); + + ADD_COLUMN_ACTION(COL_TOTAL_TIME_PER_FRAME); + ADD_COLUMN_ACTION(COL_PERCENT_SUM_PER_FRAME); + ADD_COLUMN_ACTION(COL_PERCENT_PER_FRAME); + ADD_COLUMN_ACTION(COL_MIN_PER_FRAME); + ADD_COLUMN_ACTION(COL_MAX_PER_FRAME); + ADD_COLUMN_ACTION(COL_AVG_PER_FRAME); + ADD_COLUMN_ACTION(COL_NCALLS_PER_FRAME); + + hidemenu->addSeparator(); + + ADD_COLUMN_ACTION(COL_TOTAL_TIME_PER_THREAD); + ADD_COLUMN_ACTION(COL_PERCENT_SUM_PER_THREAD); + ADD_COLUMN_ACTION(COL_MIN_PER_THREAD); + ADD_COLUMN_ACTION(COL_MAX_PER_THREAD); + ADD_COLUMN_ACTION(COL_AVG_PER_THREAD); + ADD_COLUMN_ACTION(COL_NCALLS_PER_THREAD); + + hidemenu->addSeparator(); + + ADD_COLUMN_ACTION(COL_TOTAL_TIME_PER_PARENT); + ADD_COLUMN_ACTION(COL_PERCENT_SUM_PER_PARENT); + ADD_COLUMN_ACTION(COL_PERCENT_PER_PARENT); + ADD_COLUMN_ACTION(COL_MIN_PER_PARENT); + ADD_COLUMN_ACTION(COL_MAX_PER_PARENT); + ADD_COLUMN_ACTION(COL_AVG_PER_PARENT); + ADD_COLUMN_ACTION(COL_NCALLS_PER_PARENT); + + hidemenu->addSeparator(); + + ADD_COLUMN_ACTION(COL_ACTIVE_TIME); + ADD_COLUMN_ACTION(COL_ACTIVE_PERCENT); + + hidemenu->addSeparator(); + + ADD_COLUMN_ACTION(COL_TOTAL_TIME_PER_AREA); + ADD_COLUMN_ACTION(COL_PERCENT_SUM_PER_AREA); + ADD_COLUMN_ACTION(COL_PERCENT_PER_AREA); + ADD_COLUMN_ACTION(COL_MIN_PER_AREA); + ADD_COLUMN_ACTION(COL_MAX_PER_AREA); + ADD_COLUMN_ACTION(COL_AVG_PER_AREA); + ADD_COLUMN_ACTION(COL_NCALLS_PER_AREA); + +#undef ADD_STATUS_ACTION + menu.exec(QCursor::pos()); _event->accept(); @@ -819,6 +1087,30 @@ void BlocksTreeWidget::contextMenuEvent(QContextMenuEvent* _event) ////////////////////////////////////////////////////////////////////////// +void BlocksTreeWidget::onItemDoubleClicked(QTreeWidgetItem* _item, int _column) +{ + delete m_valueTooltip; + m_valueTooltip = nullptr; + if (m_idleTimer.isActive()) + m_idleTimer.stop(); + + if (_item == nullptr || _column != COL_NAME || _item == headerItem()) + { + return; + } + + auto item = static_cast(_item); + if (item->parent() != nullptr) + { + if (easyDescriptor(item->block()).type() == profiler::BlockType::Value) + { + // TODO: open dialog to view values + } + } +} + +////////////////////////////////////////////////////////////////////////// + void BlocksTreeWidget::resizeEvent(QResizeEvent* _event) { Parent::resizeEvent(_event); @@ -861,7 +1153,7 @@ void BlocksTreeWidget::createProgressDialog() { destroyProgressDialog(); - m_progress = new RoundProgressDialog("Building blocks hierarchy...", this); + m_progress = new RoundProgressDialog("Building tree...", this); m_progress->setWindowFlags(Qt::FramelessWindowHint); m_progress->setAttribute(Qt::WA_TranslucentBackground); m_progress->setValue(0); @@ -897,13 +1189,8 @@ void BlocksTreeWidget::onCollapseAllClicked(bool) if (EASY_GLOBALS.bind_scene_and_tree_expand_status) { -#ifdef EASY_TREE_WIDGET__USE_VECTOR - for (auto item : m_items) - item->guiBlock().expanded = false; -#else for (auto& item : m_items) item.second->guiBlock().expanded = false; -#endif emit EASY_GLOBALS.events.itemsExpandStateChanged(); } } @@ -919,13 +1206,9 @@ void BlocksTreeWidget::onExpandAllClicked(bool) if (EASY_GLOBALS.bind_scene_and_tree_expand_status) { -#ifdef EASY_TREE_WIDGET__USE_VECTOR - for (auto item : m_items){ - auto& b = item->guiBlock(); -#else - for (auto& item : m_items){ + for (auto& item : m_items) + { auto& b = item.second->guiBlock(); -#endif b.expanded = !b.tree.children.empty(); } @@ -1068,15 +1351,28 @@ void BlocksTreeWidget::onSelectedBlockChange(uint32_t _block_index) if (_block_index < EASY_GLOBALS.gui_blocks.size()) { -#ifdef EASY_TREE_WIDGET__USE_VECTOR - const auto i = easyBlock(_block_index).tree_item; - if (i < m_items.size()) - item = m_items[i]; -#else auto it = m_items.find(_block_index); if (it != m_items.end()) + { item = it->second; -#endif + } + else if (m_mode != TreeMode::Full) + { + const auto currentThread = EASY_GLOBALS.selected_thread; + for (auto& itemPair : m_items) + { + auto blockItem = itemPair.second; + if (blockItem->parent() != nullptr && blockItem->threadId() == currentThread) + { + const auto id = blockItem->block().node->id(); + if (id == EASY_GLOBALS.selected_block_id || easyDescriptor(id).id() == EASY_GLOBALS.selected_block_id) + { + item = blockItem; + break; + } + } + } + } } if (item != nullptr) @@ -1120,7 +1416,8 @@ void BlocksTreeWidget::resizeColumnsToContents() { for (int i = 0; i < COL_COLUMNS_NUMBER; ++i) { - resizeColumnToContents(i); + if (!isColumnHidden(i)) + resizeColumnToContents(i); } } @@ -1135,7 +1432,7 @@ void BlocksTreeWidget::onHideShowColumn(bool) const auto col = action->data().toInt(); const bool hideCol = m_columnsHiddenStatus[col] == 0; setColumnHidden(col, hideCol); - m_columnsHiddenStatus[col] = hideCol ? 1 : 0; + m_columnsHiddenStatus[col] = static_cast(hideCol ? 1 : 0); } void BlocksTreeWidget::onModeChange(bool) @@ -1153,12 +1450,23 @@ void BlocksTreeWidget::onModeChange(bool) if (m_mode == TreeMode::Full) { for (int i = 1; i < COL_COLUMNS_NUMBER; ++i) + { setColumnHidden(i, m_columnsHiddenStatus[i] != 0); + } + } + else if (m_mode == TreeMode::Plain) + { + for (int i = 1; i < COL_COLUMNS_NUMBER; ++i) + { + setColumnHidden(i, m_columnsHiddenStatus[i] != 0 || !PLAIN_MODE_COLUMNS[i]); + } } else { for (int i = 1; i < COL_COLUMNS_NUMBER; ++i) - setColumnHidden(i, m_columnsHiddenStatus[i] != 0 || !SIMPLIFIED_REGIME_COLUMNS[i]); + { + setColumnHidden(i, m_columnsHiddenStatus[i] != 0 || !SELECTION_MODE_COLUMNS[i]); + } } emit EASY_GLOBALS.events.blocksTreeModeChanged(); @@ -1205,7 +1513,7 @@ void BlocksTreeWidget::saveSettings() HierarchyWidget::HierarchyWidget(QWidget* _parent) : Parent(_parent) , m_tree(new BlocksTreeWidget(this)) , m_searchBox(new QLineEdit(this)) - , m_foundNumber(new QLabel("0 matches", this)) + , m_foundNumber(new QLabel(QStringLiteral("0 matches"), this)) , m_searchButton(nullptr) , m_bCaseSensitiveSearch(false) { @@ -1295,14 +1603,38 @@ void HierarchyWidget::saveSettings() settings.endGroup(); } +void HierarchyWidget::enterEvent(QEvent* event) +{ + Parent::enterEvent(event); + m_tree->updateHintLabelOnHover(true); +} + +void HierarchyWidget::leaveEvent(QEvent* event) +{ + Parent::leaveEvent(event); + m_tree->updateHintLabelOnHover(false); +} + void HierarchyWidget::keyPressEvent(QKeyEvent* _event) { - if (_event->key() == Qt::Key_F3) + switch (_event->key()) { - if (_event->modifiers() & Qt::ShiftModifier) - findPrev(true); - else - findNext(true); + case Qt::Key_F3: + { + if (_event->modifiers() & Qt::ShiftModifier) + findPrev(true); + else + findNext(true); + break; + } + + case Qt::Key_Escape: + { + m_searchBox->clear(); + break; + } + + default: break; } _event->accept(); @@ -1331,13 +1663,13 @@ BlocksTreeWidget* HierarchyWidget::tree() void HierarchyWidget::clear(bool _global) { m_tree->clearSilent(_global); - m_foundNumber->setText(QString("0 matches")); + m_foundNumber->setText(QStringLiteral("0 matches")); m_foundNumber->hide(); } void HierarchyWidget::onSeachBoxReturnPressed() { - if (m_searchButton->data().toBool() == true) + if (m_searchButton->data().toBool()) findNext(true); else findPrev(true); @@ -1346,7 +1678,10 @@ void HierarchyWidget::onSeachBoxReturnPressed() void HierarchyWidget::onSearchBoxTextChanged(const QString& _text) { if (_text.isEmpty()) + { m_foundNumber->hide(); + m_tree->resetSearch(); + } } void HierarchyWidget::findNext(bool) @@ -1356,15 +1691,27 @@ void HierarchyWidget::findNext(bool) { if (m_foundNumber->isVisible()) m_foundNumber->hide(); + m_tree->resetSearch(); return; } auto matches = m_tree->findNext(text, m_bCaseSensitiveSearch ? Qt::MatchCaseSensitive : Qt::MatchFlags()); - if (matches == 1) - m_foundNumber->setText(QString("1 match")); + if (matches == 0) + { + m_foundNumber->setText(QStringLiteral("0 matches")); + } + else if (matches == 1) + { + m_foundNumber->setText(QStringLiteral(" 1  match")); + } else - m_foundNumber->setText(QString("%1 matches").arg(matches)); + { + auto i = m_tree->lastFoundIndex() + 1; + m_foundNumber->setText(QString(" %1  of " + " %2  matches") + .arg(i).arg(matches)); + } if (!m_foundNumber->isVisible()) m_foundNumber->show(); @@ -1377,15 +1724,27 @@ void HierarchyWidget::findPrev(bool) { if (m_foundNumber->isVisible()) m_foundNumber->hide(); + m_tree->resetSearch(); return; } auto matches = m_tree->findPrev(text, m_bCaseSensitiveSearch ? Qt::MatchCaseSensitive : Qt::MatchFlags()); - if (matches == 1) - m_foundNumber->setText(QString("1 match")); + if (matches == 0) + { + m_foundNumber->setText(QStringLiteral("0 matches")); + } + else if (matches == 1) + { + m_foundNumber->setText(QStringLiteral(" 1  match")); + } else - m_foundNumber->setText(QString("%1 matches").arg(matches)); + { + auto i = m_tree->lastFoundIndex() + 1; + m_foundNumber->setText(QString(" %1  of " + " %2  matches") + .arg(i).arg(matches)); + } if (!m_foundNumber->isVisible()) m_foundNumber->show(); @@ -1396,7 +1755,7 @@ void HierarchyWidget::findNextFromMenu(bool _checked) if (!_checked) return; - if (m_searchButton->data().toBool() == false) + if (!m_searchButton->data().toBool()) { m_searchButton->setData(true); m_searchButton->setText(tr("Find next")); @@ -1413,7 +1772,7 @@ void HierarchyWidget::findPrevFromMenu(bool _checked) if (!_checked) return; - if (m_searchButton->data().toBool() == true) + if (m_searchButton->data().toBool()) { m_searchButton->setData(false); m_searchButton->setText(tr("Find prev")); diff --git a/profiler_gui/blocks_tree_widget.h b/profiler_gui/blocks_tree_widget.h index cdc9c8f..20e9998 100644 --- a/profiler_gui/blocks_tree_widget.h +++ b/profiler_gui/blocks_tree_widget.h @@ -20,7 +20,7 @@ * : Moved sources of TreeWidgetItem into tree_widget_item.h/.cpp * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -95,13 +95,18 @@ protected: class QLabel* m_hintLabel; class ArbitraryValueToolTip* m_valueTooltip; TreeMode m_mode; + int m_lastFoundIndex; bool m_bLocked; bool m_bSilentExpandCollapse; + bool m_bCaseSensitiveSearch; bool m_bInitialized; char m_columnsHiddenStatus[COL_COLUMNS_NUMBER]; + int m_columnsMinimumWidth[COL_COLUMNS_NUMBER]; public: + using Parent::indexFromItem; + explicit BlocksTreeWidget(QWidget* _parent = nullptr); ~BlocksTreeWidget() override; @@ -114,11 +119,22 @@ public: int findNext(const QString& _str, Qt::MatchFlags _flags); int findPrev(const QString& _str, Qt::MatchFlags _flags); + void resetSearch(bool repaint = true); + + TreeMode mode() const + { + return m_mode; + } + + QTreeWidgetItem* lastFoundItem() const; + const QString& searchString() const; + bool caseSensitiveSearch() const; + int lastFoundIndex() const; + public slots: - void setTree(const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree); - void setTreeBlocks(const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _session_begin_time, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict); + void updateHintLabelOnHover(bool hover); protected: @@ -127,6 +143,8 @@ protected: private slots: + void onHeaderSectionResized(int logicalIndex, int oldSize, int newSize); + void onJumpToItemClicked(bool); void onCollapseAllClicked(bool); @@ -140,6 +158,7 @@ private slots: void onItemExpand(QTreeWidgetItem* _item); void onItemCollapse(QTreeWidgetItem* _item); void onCurrentItemChange(QTreeWidgetItem* _item, QTreeWidgetItem*); + void onItemDoubleClicked(QTreeWidgetItem* _item, int _column); void onSelectedThreadChange(::profiler::thread_id_t _id); @@ -192,6 +211,8 @@ public: explicit HierarchyWidget(QWidget* _parent = nullptr); ~HierarchyWidget() override; + void enterEvent(QEvent* event) override; + void leaveEvent(QEvent* event) override; void keyPressEvent(QKeyEvent* _event) override; void contextMenuEvent(QContextMenuEvent* _event) override; void dragEnterEvent(QDragEnterEvent*) override {} diff --git a/profiler_gui/bookmarks_editor.cpp b/profiler_gui/bookmarks_editor.cpp index 07f6a02..9608e07 100644 --- a/profiler_gui/bookmarks_editor.cpp +++ b/profiler_gui/bookmarks_editor.cpp @@ -8,7 +8,7 @@ * description : The file contains implementation of BookmarkEditor. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -70,7 +70,6 @@ BookmarkEditor::BookmarkEditor(size_t bookmarkIndex, bool isNew, QWidget* parent { setAttribute(Qt::WA_DeleteOnClose, true); setSizeGripEnabled(EASY_GLOBALS.use_custom_window_header); - setModal(true); const auto& bookmark = EASY_GLOBALS.bookmarks[m_bookmarkIndex]; @@ -128,7 +127,7 @@ BookmarkEditor::BookmarkEditor(size_t bookmarkIndex, bool isNew, QWidget* parent contentLayout->addWidget(m_textEdit, 1); const WindowHeader::Buttons buttons {WindowHeader::MaximizeButton | WindowHeader::CloseButton}; - auto header = new WindowHeader(isNew ? "New bookmark" : "Edit bookmark", buttons, this); + auto header = new WindowHeader(isNew ? "New bookmark" : "Edit bookmark", buttons, *this); auto mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(1, 1, 1, 1); @@ -174,10 +173,11 @@ void BookmarkEditor::onColorButtonClicked(bool) QColorDialog colorDialog(palette.brush(QPalette::Background).color(), this); colorDialog.exec(); - palette.setBrush(QPalette::Background, QBrush(colorDialog.currentColor())); + auto color = colorDialog.currentColor(); + palette.setBrush(QPalette::Background, QBrush(color)); m_colorButton->setPalette(palette); - m_colorButton->setStyleSheet(QString("background-color: %1;").arg(colorDialog.currentColor().name())); + m_colorButton->setStyleSheet(QString("background-color: %1;").arg(color.name())); m_colorButton->style()->unpolish(m_colorButton); m_colorButton->style()->polish(m_colorButton); m_colorButton->update(); diff --git a/profiler_gui/bookmarks_editor.h b/profiler_gui/bookmarks_editor.h index 0e7fb45..d610b91 100644 --- a/profiler_gui/bookmarks_editor.h +++ b/profiler_gui/bookmarks_editor.h @@ -8,7 +8,7 @@ * description : The file contains declaration of BookmarkEditor. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/common_functions.cpp b/profiler_gui/common_functions.cpp index 5a924ab..ad2fa8f 100644 --- a/profiler_gui/common_functions.cpp +++ b/profiler_gui/common_functions.cpp @@ -12,7 +12,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/common_functions.h b/profiler_gui/common_functions.h index 690b850..9cdf7cd 100644 --- a/profiler_gui/common_functions.h +++ b/profiler_gui/common_functions.h @@ -12,7 +12,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/common_types.h b/profiler_gui/common_types.h index 3478d0e..51acd63 100644 --- a/profiler_gui/common_types.h +++ b/profiler_gui/common_types.h @@ -13,7 +13,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -96,13 +96,9 @@ struct EasyBlockItem Q_DECL_FINAL }; // END of struct EasyBlockItem. -//#define EASY_TREE_WIDGET__USE_VECTOR struct EasyBlock Q_DECL_FINAL { ::profiler::BlocksTree tree; -#ifdef EASY_TREE_WIDGET__USE_VECTOR - uint32_t tree_item; -#endif uint32_t graphics_item_index; uint8_t graphics_item_level; uint8_t graphics_item; @@ -112,9 +108,6 @@ struct EasyBlock Q_DECL_FINAL EasyBlock(EasyBlock&& that) EASY_NOEXCEPT : tree(::std::move(that.tree)) -#ifdef EASY_TREE_WIDGET__USE_VECTOR - , tree_item(that.tree_item) -#endif , graphics_item_index(that.graphics_item_index) , graphics_item_level(that.graphics_item_level) , graphics_item(that.graphics_item) diff --git a/profiler_gui/complexity_calculator.h b/profiler_gui/complexity_calculator.h index 884a415..499e928 100644 --- a/profiler_gui/complexity_calculator.h +++ b/profiler_gui/complexity_calculator.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/descriptors_tree_widget.cpp b/profiler_gui/descriptors_tree_widget.cpp index d5ce2ca..49f4dd5 100644 --- a/profiler_gui/descriptors_tree_widget.cpp +++ b/profiler_gui/descriptors_tree_widget.cpp @@ -13,7 +13,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -53,26 +53,31 @@ * : limitations under the License. ************************************************************************/ -#include +#include #include #include -#include -#include #include +#include +#include #include -#include -#include #include #include -#include -#include -#include +#include +#include +#include #include -#include +#include +#include #include +#include +#include +#include + #include "descriptors_tree_widget.h" -#include "arbitrary_value_inspector.h" + #include "globals.h" +#include "arbitrary_value_inspector.h" +#include "text_highlighter.h" #include "thread_pool.h" #ifdef max @@ -85,18 +90,6 @@ ////////////////////////////////////////////////////////////////////////// -enum DescColumns -{ - DESC_COL_FILE_LINE = 0, - DESC_COL_TYPE, - DESC_COL_NAME, - DESC_COL_STATUS, - - DESC_COL_COLUMNS_NUMBER -}; - -////////////////////////////////////////////////////////////////////////// - ::profiler::EasyBlockStatus nextStatus(::profiler::EasyBlockStatus _status) { switch (_status) @@ -240,10 +233,15 @@ QVariant DescriptorsTreeItem::data(int _column, int _role) const DescriptorsTreeWidget::DescriptorsTreeWidget(QWidget* _parent) : Parent(_parent) , m_lastFound(nullptr) + , m_lastFoundIndex(0) , m_lastSearchColumn(-1) , m_searchColumn(DESC_COL_NAME) , m_bLocked(false) + , m_bCaseSensitiveSearch(false) + , m_bInitialized(false) { + memset(m_columnsMinimumWidth, 0, sizeof(m_columnsMinimumWidth)); + setAutoFillBackground(false); setAlternatingRowColors(true); setItemsExpandable(true); @@ -264,8 +262,11 @@ DescriptorsTreeWidget::DescriptorsTreeWidget(QWidget* _parent) connect(this, &Parent::itemExpanded, this, &This::onItemExpand); connect(this, &Parent::itemDoubleClicked, this, &This::onDoubleClick); connect(this, &Parent::currentItemChanged, this, &This::onCurrentItemChange); + connect(header(), &QHeaderView::sectionResized, this, &This::onHeaderSectionResized); loadSettings(); + + setItemDelegateForColumn(m_searchColumn, new DescWidgetItemDelegate(this)); } DescriptorsTreeWidget::~DescriptorsTreeWidget() @@ -279,11 +280,83 @@ DescriptorsTreeWidget::~DescriptorsTreeWidget() saveSettings(); } +void DescriptorsTreeWidget::showEvent(QShowEvent* event) +{ + Parent::showEvent(event); + + if (!m_bInitialized) + { +#if !defined(_WIN32) && !defined(__APPLE__) + const auto padding = px(9); +#else + const auto padding = px(6); +#endif + + auto header = this->header(); + auto headerItem = this->headerItem(); + + auto f = header->font(); +#if !defined(_WIN32) && !defined(__APPLE__) + f.setBold(true); +#endif + QFontMetrics fm(f); + + const auto indicatorSize = header->isSortIndicatorShown() ? px(11) : 0; + for (int i = 0; i < DESC_COL_COLUMNS_NUMBER; ++i) + { + auto minSize = static_cast(fm.width(headerItem->text(i)) * profiler_gui::FONT_METRICS_FACTOR + padding); + m_columnsMinimumWidth[i] = minSize; + + if (header->isSortIndicatorShown() && header->sortIndicatorSection() == i) + { + minSize += indicatorSize; + } + + if (header->sectionSize(i) < minSize) + { + header->resizeSection(i, minSize); + } + } + + m_bInitialized = true; + } +} + ////////////////////////////////////////////////////////////////////////// +void DescriptorsTreeWidget::resetSearch(bool repaint) +{ + if (m_lastSearch.isEmpty()) + { + return; + } + + m_lastSearchColumn = m_searchColumn; + m_bCaseSensitiveSearch = false; + m_lastSearch.clear(); + m_lastFound = nullptr; + m_lastFoundIndex = 0; + + if (repaint) + { + viewport()->update(); + } +} + void DescriptorsTreeWidget::setSearchColumn(int column) { + const int prevColumn = m_searchColumn; m_searchColumn = column; + + if (m_searchColumn != prevColumn) + { + auto delegate = itemDelegateForColumn(prevColumn); + setItemDelegateForColumn(prevColumn, nullptr); + delete delegate; + + setItemDelegateForColumn(m_searchColumn, new DescWidgetItemDelegate(this)); + } + emit searchColumnChanged(column); } @@ -292,6 +365,26 @@ int DescriptorsTreeWidget::searchColumn() const return m_searchColumn; } +QTreeWidgetItem* DescriptorsTreeWidget::lastFoundItem() const +{ + return m_lastFound; +} + +bool DescriptorsTreeWidget::caseSensitiveSearch() const +{ + return m_bCaseSensitiveSearch; +} + +const QString& DescriptorsTreeWidget::searchString() const +{ + return m_lastSearch; +} + +int DescriptorsTreeWidget::lastFoundIndex() const +{ + return m_lastFoundIndex; +} + ////////////////////////////////////////////////////////////////////////// void DescriptorsTreeWidget::contextMenuEvent(QContextMenuEvent* _event) @@ -348,10 +441,8 @@ void DescriptorsTreeWidget::clearSilent(bool _global) const QSignalBlocker b(this); setSortingEnabled(false); - m_lastFound = nullptr; - m_lastSearch.clear(); + resetSearch(false); - m_highlightItems.clear(); m_items.clear(); if (topLevelItemCount() != 0) @@ -464,7 +555,23 @@ void DescriptorsTreeWidget::build() setSortingEnabled(true); sortByColumn(DESC_COL_FILE_LINE, Qt::AscendingOrder); resizeColumnsToContents(); - QTimer::singleShot(100, [this](){ onSelectedBlockChange(EASY_GLOBALS.selected_block); }); + + QTimer::singleShot(100, [this] { onSelectedBlockChange(EASY_GLOBALS.selected_block); }); +} + +////////////////////////////////////////////////////////////////////////// + +void DescriptorsTreeWidget::onHeaderSectionResized(int logicalIndex, int /*oldSize*/, int newSize) +{ + const auto indicatorSize = header()->isSortIndicatorShown() && header()->sortIndicatorSection() == logicalIndex ? px(11) : 0; + const auto minSize = m_columnsMinimumWidth[logicalIndex] + indicatorSize; + + if (!m_bInitialized || newSize >= minSize) + { + return; + } + + header()->resizeSection(logicalIndex, minSize); } ////////////////////////////////////////////////////////////////////////// @@ -500,20 +607,8 @@ void DescriptorsTreeWidget::onDoubleClick(QTreeWidgetItem* _item, int _column) void DescriptorsTreeWidget::onCurrentItemChange(QTreeWidgetItem* _item, QTreeWidgetItem* _prev) { - if (_prev != nullptr) - { - auto f = font(); - for (int i = 0; i < DESC_COL_STATUS; ++i) - _prev->setFont(i, f); - } - if (_item != nullptr) { - auto f = font(); - f.setBold(true); - for (int i = 0; i < DESC_COL_STATUS; ++i) - _item->setFont(i, f); - if (::profiler_gui::is_max(EASY_GLOBALS.selected_block) && _item->parent() != nullptr) { const auto id = static_cast(_item)->desc(); @@ -598,15 +693,6 @@ void DescriptorsTreeWidget::onSelectedBlockChange(uint32_t _block_index) ////////////////////////////////////////////////////////////////////////// -void DescriptorsTreeWidget::resetHighlight() -{ - for (auto item : m_highlightItems) { - for (int i = 0; i < DESC_COL_COLUMNS_NUMBER; ++i) - item->setBackground(i, Qt::NoBrush); - } - m_highlightItems.clear(); -} - void DescriptorsTreeWidget::loadSettings() { QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); @@ -635,19 +721,20 @@ int DescriptorsTreeWidget::findNext(const QString& _str, Qt::MatchFlags _flags) { if (_str.isEmpty()) { - resetHighlight(); - m_lastSearchColumn = m_searchColumn; + resetSearch(); return 0; } const bool isNewSearch = (m_lastSearchColumn != m_searchColumn || m_lastSearch != _str); auto itemsList = findItems(_str, Qt::MatchContains | Qt::MatchRecursive | _flags, m_searchColumn); + m_bCaseSensitiveSearch = _flags.testFlag(Qt::MatchCaseSensitive); if (!isNewSearch) { if (!itemsList.empty()) { bool stop = false; + int i = 0; decltype(m_lastFound) next = nullptr; for (auto item : itemsList) { @@ -658,29 +745,24 @@ int DescriptorsTreeWidget::findNext(const QString& _str, Qt::MatchFlags _flags) } stop = item == m_lastFound; + ++i; } m_lastFound = next == nullptr ? itemsList.front() : next; + m_lastFoundIndex = next == nullptr ? 0 : i; } else { m_lastFound = nullptr; + m_lastFoundIndex = 0; } } else { - resetHighlight(); - m_lastSearchColumn = m_searchColumn; m_lastSearch = _str; m_lastFound = !itemsList.empty() ? itemsList.front() : nullptr; - - for (auto item : itemsList) - { - m_highlightItems.push_back(item); - for (int i = 0; i < DESC_COL_COLUMNS_NUMBER; ++i) - item->setBackgroundColor(i, QColor::fromRgba(0x80000000 | (0x00ffffff & ::profiler::colors::Yellow))); - } + m_lastFoundIndex = 0; } if (m_lastFound != nullptr) @@ -689,6 +771,8 @@ int DescriptorsTreeWidget::findNext(const QString& _str, Qt::MatchFlags _flags) setCurrentItem(m_lastFound); } + viewport()->update(); + return itemsList.size(); } @@ -696,48 +780,54 @@ int DescriptorsTreeWidget::findPrev(const QString& _str, Qt::MatchFlags _flags) { if (_str.isEmpty()) { - resetHighlight(); - m_lastSearchColumn = m_searchColumn; + resetSearch(); return 0; } const bool isNewSearch = (m_lastSearchColumn != m_searchColumn || m_lastSearch != _str); auto itemsList = findItems(_str, Qt::MatchContains | Qt::MatchRecursive | _flags, m_searchColumn); + m_bCaseSensitiveSearch = _flags.testFlag(Qt::MatchCaseSensitive); if (!isNewSearch) { if (!itemsList.empty()) { + int i = 0; decltype(m_lastFound) prev = nullptr; for (auto item : itemsList) { if (item == m_lastFound) + { + --i; break; + } prev = item; + ++i; } m_lastFound = prev == nullptr ? itemsList.back() : prev; + m_lastFoundIndex = prev == nullptr ? itemsList.length() - 1 : i; } else { m_lastFound = nullptr; + m_lastFoundIndex = 0; } } else { - resetHighlight(); - m_lastSearchColumn = m_searchColumn; m_lastSearch = _str; - m_lastFound = !itemsList.empty() ? itemsList.front() : nullptr; - - m_highlightItems.reserve(itemsList.size()); - for (auto item : itemsList) + if (!itemsList.empty()) { - m_highlightItems.push_back(item); - for (int i = 0; i < DESC_COL_COLUMNS_NUMBER; ++i) - item->setBackgroundColor(i, QColor::fromRgba(0x80000000 | (0x00ffffff & ::profiler::colors::Yellow))); + m_lastFound = itemsList.back(); + m_lastFoundIndex = itemsList.length() - 1; + } + else + { + m_lastFound = nullptr; + m_lastFoundIndex = 0; } } @@ -747,6 +837,8 @@ int DescriptorsTreeWidget::findPrev(const QString& _str, Qt::MatchFlags _flags) setCurrentItem(m_lastFound); } + viewport()->update(); + return itemsList.size(); } @@ -757,7 +849,7 @@ BlockDescriptorsWidget::BlockDescriptorsWidget(QWidget* _parent) : Parent(_paren , m_tree(new DescriptorsTreeWidget(this)) , m_values(new ArbitraryValuesWidget(this)) , m_searchBox(new QLineEdit(this)) - , m_foundNumber(new QLabel("0 matches", this)) + , m_foundNumber(new QLabel(QStringLiteral("0 matches"), this)) , m_searchButton(nullptr) , m_bCaseSensitiveSearch(false) { @@ -897,12 +989,24 @@ void BlockDescriptorsWidget::saveSettings() void BlockDescriptorsWidget::keyPressEvent(QKeyEvent* _event) { - if (_event->key() == Qt::Key_F3) + switch (_event->key()) { - if (_event->modifiers() & Qt::ShiftModifier) - findPrev(true); - else - findNext(true); + case Qt::Key_F3: + { + if (_event->modifiers() & Qt::ShiftModifier) + findPrev(true); + else + findNext(true); + break; + } + + case Qt::Key_Escape: + { + m_searchBox->clear(); + break; + } + + default: break; } _event->accept(); @@ -922,7 +1026,7 @@ void BlockDescriptorsWidget::showEvent(QShowEvent* event) void BlockDescriptorsWidget::build() { m_tree->clearSilent(false); - m_foundNumber->setText(QString("0 matches")); + m_foundNumber->setText(QStringLiteral("0 matches")); m_foundNumber->hide(); m_tree->build(); m_values->rebuild(); @@ -931,7 +1035,7 @@ void BlockDescriptorsWidget::build() void BlockDescriptorsWidget::clear() { m_tree->clearSilent(true); - m_foundNumber->setText(QString("0 matches")); + m_foundNumber->setText(QStringLiteral("0 matches")); m_foundNumber->hide(); m_values->clear(); } @@ -943,7 +1047,7 @@ ArbitraryValuesWidget* BlockDescriptorsWidget::dataViewer() const void BlockDescriptorsWidget::onSeachBoxReturnPressed() { - if (m_searchButton->data().toBool() == true) + if (m_searchButton->data().toBool()) findNext(true); else findPrev(true); @@ -952,7 +1056,10 @@ void BlockDescriptorsWidget::onSeachBoxReturnPressed() void BlockDescriptorsWidget::onSearchBoxTextChanged(const QString& _text) { if (_text.isEmpty()) + { m_foundNumber->hide(); + m_tree->resetSearch(); + } } void BlockDescriptorsWidget::onSearchColumnChanged(int column) @@ -989,15 +1096,27 @@ void BlockDescriptorsWidget::findNext(bool) { if (m_foundNumber->isVisible()) m_foundNumber->hide(); + m_tree->resetSearch(); return; } auto matches = m_tree->findNext(text, m_bCaseSensitiveSearch ? Qt::MatchCaseSensitive : Qt::MatchFlags()); - if (matches == 1) - m_foundNumber->setText(QString("1 match")); + if (matches == 0) + { + m_foundNumber->setText(QStringLiteral("0 matches")); + } + else if (matches == 1) + { + m_foundNumber->setText(QStringLiteral(" 1  match")); + } else - m_foundNumber->setText(QString("%1 matches").arg(matches)); + { + auto i = m_tree->lastFoundIndex() + 1; + m_foundNumber->setText(QString(" %1  of " + " %2  matches") + .arg(i).arg(matches)); + } if (!m_foundNumber->isVisible()) m_foundNumber->show(); @@ -1010,15 +1129,27 @@ void BlockDescriptorsWidget::findPrev(bool) { if (m_foundNumber->isVisible()) m_foundNumber->hide(); + m_tree->resetSearch(); return; } auto matches = m_tree->findPrev(text, m_bCaseSensitiveSearch ? Qt::MatchCaseSensitive : Qt::MatchFlags()); - if (matches == 1) - m_foundNumber->setText(QString("1 match")); + if (matches == 0) + { + m_foundNumber->setText(QStringLiteral("0 matches")); + } + else if (matches == 1) + { + m_foundNumber->setText(QStringLiteral(" 1  match")); + } else - m_foundNumber->setText(QString("%1 matches").arg(matches)); + { + auto i = m_tree->lastFoundIndex() + 1; + m_foundNumber->setText(QString(" %1  of " + " %2  matches") + .arg(i).arg(matches)); + } if (!m_foundNumber->isVisible()) m_foundNumber->show(); @@ -1029,7 +1160,7 @@ void BlockDescriptorsWidget::findNextFromMenu(bool _checked) if (!_checked) return; - if (m_searchButton->data().toBool() == false) + if (!m_searchButton->data().toBool()) { m_searchButton->setData(true); m_searchButton->setText(tr("Find next")); @@ -1046,7 +1177,7 @@ void BlockDescriptorsWidget::findPrevFromMenu(bool _checked) if (!_checked) return; - if (m_searchButton->data().toBool() == true) + if (m_searchButton->data().toBool()) { m_searchButton->setData(false); m_searchButton->setText(tr("Find prev")); @@ -1059,3 +1190,103 @@ void BlockDescriptorsWidget::findPrevFromMenu(bool _checked) } ////////////////////////////////////////////////////////////////////////// + +DescWidgetItemDelegate::DescWidgetItemDelegate(DescriptorsTreeWidget* parent) + : QStyledItemDelegate(parent) + , m_treeWidget(parent) +{ + +} + +DescWidgetItemDelegate::~DescWidgetItemDelegate() +{ + +} + +void DescWidgetItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + QStyledItemDelegate::paint(painter, option, index); + highlightMatchingText(painter, option, index); +} + +void DescWidgetItemDelegate::highlightMatchingText( + QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index +) const { + if (m_treeWidget->lastFoundItem() != nullptr && !m_treeWidget->searchString().isEmpty()) + { + // Highlight matching word + auto displayData = m_treeWidget->model()->data(index); + if (displayData.canConvert()) + { + const auto text = displayData.toString(); + const auto caseSensitivity = m_treeWidget->caseSensitiveSearch() ? Qt::CaseSensitive : Qt::CaseInsensitive; + if (text.contains(m_treeWidget->searchString(), caseSensitivity)) + { + auto lastFoundIndex = m_treeWidget->indexFromItem(m_treeWidget->lastFoundItem(), index.column()); + highlightMatchingText( + painter, + option, + text, + m_treeWidget->searchString(), + caseSensitivity, + lastFoundIndex == index + ); + } + } + } +} + +void DescWidgetItemDelegate::highlightMatchingText( + QPainter* painter, + const QStyleOptionViewItem& option, + const QString& text, + const QString& pattern, + Qt::CaseSensitivity caseSensitivity, + bool current +) const { + const auto str = pattern.toStdString(); + (void)str; + + QTextDocument doc; + doc.setDefaultFont(painter->font()); + doc.setTextWidth(option.rect.width()); + + const auto elidedText = painter->fontMetrics().elidedText(text, Qt::ElideRight, std::max(option.rect.width(), 0)); + doc.setHtml(elidedText); + + TextHighlighter highlighter( + &doc, + painter->pen().color(), + QColor::fromRgb(profiler::colors::Grey100), + pattern, + caseSensitivity, + current + ); + + painter->save(); + +#ifdef _WIN32 + EASY_CONSTEXPR int fixed_padding_x = -1; + EASY_CONSTEXPR int fixed_padding_y = 0; +#else + EASY_CONSTEXPR int fixed_padding_x = -1; + EASY_CONSTEXPR int fixed_padding_y = -1; +#endif + + auto dh = std::max((option.rect.height() - doc.size().height()) * 0.5, 0.); + painter->translate(option.rect.left() + fixed_padding_x, option.rect.top() + dh + fixed_padding_y); + + QRect clip(0, 0, option.rect.width(), option.rect.height()); + painter->setClipRect(clip); + + QAbstractTextDocumentLayout::PaintContext ctx; + ctx.clip = clip; + ctx.palette.setColor(QPalette::Text, Qt::transparent); + doc.documentLayout()->draw(painter, ctx); + + painter->restore(); +} + +////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/descriptors_tree_widget.h b/profiler_gui/descriptors_tree_widget.h index f225a0a..5352821 100644 --- a/profiler_gui/descriptors_tree_widget.h +++ b/profiler_gui/descriptors_tree_widget.h @@ -13,7 +13,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -67,6 +67,18 @@ ////////////////////////////////////////////////////////////////////////// +enum DescColumns +{ + DESC_COL_FILE_LINE = 0, + DESC_COL_TYPE, + DESC_COL_NAME, + DESC_COL_STATUS, + + DESC_COL_COLUMNS_NUMBER +}; + +////////////////////////////////////////////////////////////////////////// + class DescriptorsTreeItem : public QTreeWidgetItem { using Parent = QTreeWidgetItem; @@ -121,19 +133,21 @@ class DescriptorsTreeWidget : public QTreeWidget using This = DescriptorsTreeWidget; using Items = ::std::vector; - using TreeItems = ::std::vector; using ExpandedFiles = ::std::unordered_set<::std::string>; protected: ExpandedFiles m_expandedFilesTemp; Items m_items; - TreeItems m_highlightItems; QString m_lastSearch; QTreeWidgetItem* m_lastFound; + int m_columnsMinimumWidth[DESC_COL_COLUMNS_NUMBER]; + int m_lastFoundIndex; int m_lastSearchColumn; int m_searchColumn; bool m_bLocked; + bool m_bCaseSensitiveSearch; + bool m_bInitialized; public: @@ -141,17 +155,29 @@ public: explicit DescriptorsTreeWidget(QWidget* _parent = nullptr); ~DescriptorsTreeWidget() override; + void contextMenuEvent(QContextMenuEvent* _event) override; + void showEvent(class QShowEvent* event) override; public: + using Parent::indexFromItem; + // Public non-virtual methods int findNext(const QString& _str, Qt::MatchFlags _flags); int findPrev(const QString& _str, Qt::MatchFlags _flags); + + void resetSearch(bool repaint = true); + void setSearchColumn(int column); int searchColumn() const; + QTreeWidgetItem* lastFoundItem() const; + const QString& searchString() const; + bool caseSensitiveSearch() const; + int lastFoundIndex() const; + signals: void searchColumnChanged(int column); @@ -163,6 +189,7 @@ public slots: private slots: + void onHeaderSectionResized(int logicalIndex, int oldSize, int newSize); void onBlockStatusChangeClicked(bool); void onCurrentItemChange(QTreeWidgetItem* _item, QTreeWidgetItem* _prev); void onItemExpand(QTreeWidgetItem* _item); @@ -175,7 +202,6 @@ private: // Private methods - void resetHighlight(); void loadSettings(); void saveSettings(); @@ -243,6 +269,35 @@ private: }; // END of class BlockDescriptorsWidget. + +////////////////////////////////////////////////////////////////////////// + +class DescWidgetItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT + DescriptorsTreeWidget* m_treeWidget; + +public: + + explicit DescWidgetItemDelegate(DescriptorsTreeWidget* parent = nullptr); + ~DescWidgetItemDelegate() override; + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + +private: + + void highlightMatchingText(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + + void highlightMatchingText( + QPainter* painter, + const QStyleOptionViewItem& option, + const QString& text, + const QString& pattern, + Qt::CaseSensitivity caseSensitivity, + bool current + ) const; + +}; // END of class DescWidgetItemDelegate. + ////////////////////////////////////////////////////////////////////////// #endif // EASY_DESCRIPTORS_WIDGET_H diff --git a/profiler_gui/dialog.cpp b/profiler_gui/dialog.cpp index 99d968d..48acb71 100644 --- a/profiler_gui/dialog.cpp +++ b/profiler_gui/dialog.cpp @@ -8,7 +8,7 @@ * description : The file contains implementation of Dialog. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -75,7 +75,7 @@ Dialog::Dialog(QWidget* parent, const QString& title, QWidget* content, WindowHe { setSizeGripEnabled(EASY_GLOBALS.use_custom_window_header); - m_header = new WindowHeader(title, headerButtons, this); + m_header = new WindowHeader(title, headerButtons, *this); auto mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(1, 1, 1, 1); diff --git a/profiler_gui/dialog.h b/profiler_gui/dialog.h index 66a0b99..74503bf 100644 --- a/profiler_gui/dialog.h +++ b/profiler_gui/dialog.h @@ -9,7 +9,7 @@ * : for all dialogs in the application. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/fps_widget.cpp b/profiler_gui/fps_widget.cpp index e8c465a..265c35a 100644 --- a/profiler_gui/fps_widget.cpp +++ b/profiler_gui/fps_widget.cpp @@ -12,7 +12,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -268,11 +268,12 @@ FpsWidget::FpsWidget(QWidget* _parent) : Parent(_parent), m_fpsItem(nullptr) centerOn(0, 0); // Dirty hack for QDockWidget stupid initial size policy :( + setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); setFixedHeight(10); // Set very small height to enable appropriate minimum height on the application startup QTimer::singleShot(100, [this] { // Now set appropriate minimum height - setMinimumHeight((QFontMetrics(scene()->font()).height() + 3) * 6); + setMinimumHeight((QFontMetrics(scene()->font()).height() + px(3)) * 3); setMaximumHeight(minimumHeight() * 20); }); } @@ -294,6 +295,16 @@ void FpsWidget::addPoint(uint32_t _maxFrameTime, uint32_t _avgFrameTime) scene()->update(); } +QSize FpsWidget::sizeHint() const +{ + return QSize(Parent::sizeHint().width(), minimumHeight()); +} + +QSize FpsWidget::minimumSizeHint() const +{ + return QSize(Parent::minimumSizeHint().width(), minimumHeight()); +} + void FpsWidget::resizeEvent(QResizeEvent* _event) { Parent::resizeEvent(_event); diff --git a/profiler_gui/fps_widget.h b/profiler_gui/fps_widget.h index 323f04a..d9ee49c 100644 --- a/profiler_gui/fps_widget.h +++ b/profiler_gui/fps_widget.h @@ -12,7 +12,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -109,6 +109,8 @@ public: explicit FpsWidget(QWidget* _parent = nullptr); ~FpsWidget() override; + QSize sizeHint() const override; + QSize minimumSizeHint() const override; void resizeEvent(QResizeEvent* _event) override; void hideEvent(QHideEvent* _event) override; void showEvent(QShowEvent* _event) override; diff --git a/profiler_gui/globals.cpp b/profiler_gui/globals.cpp index 06b7ce5..9aee7e0 100644 --- a/profiler_gui/globals.cpp +++ b/profiler_gui/globals.cpp @@ -12,7 +12,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -59,69 +59,69 @@ namespace profiler_gui { - Globals& Globals::instance() - { - // It's okay even without C++11 "magic statics" feature because first call happens - // on application initialization - there is only one thread and no data races occur. - static Globals globals; - return globals; - } +Globals& Globals::instance() +{ + // It's okay even without C++11 "magic statics" feature because first call happens + // on application initialization - there is only one thread and no data races occur. + static Globals globals; + return globals; +} - Globals::Fonts::Fonts() - : default_font(::profiler_gui::EFont("DejaVu Sans", 13)) - , background(::profiler_gui::EFont("DejaVu Sans", 13, QFont::Bold)) - , ruler(::profiler_gui::EFont("DejaVu Sans", 16, QFont::Bold)) - , item(::profiler_gui::EFont("DejaVu Sans", 13, QFont::Medium)) - , selected_item(::profiler_gui::EFont("DejaVu Sans", 13, QFont::Medium)) - { +Globals::Fonts::Fonts() + : default_font(::profiler_gui::EFont("DejaVu Sans", 13)) + , background(::profiler_gui::EFont("DejaVu Sans", 13, QFont::Bold)) + , ruler(::profiler_gui::EFont("DejaVu Sans", 16, QFont::Bold)) + , item(::profiler_gui::EFont("DejaVu Sans", 13, QFont::Medium)) + , selected_item(::profiler_gui::EFont("DejaVu Sans", 13, QFont::Medium)) +{ - } +} - Globals::Globals() - : theme("default") - , pid(0) - , begin_time(0) - , selected_thread(0U) - , selected_block(::profiler_gui::numeric_max()) - , selected_block_id(::profiler_gui::numeric_max()) - , version(0) - , frame_time(16700) - , blocks_spacing(0) - , blocks_size_min(2) - , blocks_narrow_size(20) - , max_fps_history(90) - , fps_timer_interval(500) - , fps_widget_line_width(2) - , bookmark_default_color(0) - , chrono_text_position(RulerTextPosition_Top) - , time_units(TimeUnits_ms) - , connected(false) - , has_local_changes(false) - , use_custom_window_header(true) - , is_right_window_header_controls(true) - , fps_enabled(true) - , use_decorated_thread_name(false) - , hex_thread_id(false) - , enable_event_markers(true) - , enable_statistics(true) - , enable_zero_length(true) - , add_zero_blocks_to_hierarchy(false) - , draw_graphics_items_borders(true) - , hide_narrow_children(false) - , hide_minsize_blocks(false) - , display_only_relevant_stats(true) - , collapse_items_on_tree_close(false) - , all_items_expanded_by_default(true) - , only_current_thread_hierarchy(false) - , highlight_blocks_with_same_id(true) - , selecting_block_changes_thread(true) - , auto_adjust_histogram_height(true) - , auto_adjust_chart_height(false) - , display_only_frames_on_histogram(false) - , bind_scene_and_tree_expand_status(true) - { +Globals::Globals() + : theme("default") + , pid(0) + , begin_time(0) + , selected_thread(0U) + , selected_block(::profiler_gui::numeric_max()) + , selected_block_id(::profiler_gui::numeric_max()) + , version(0) + , frame_time(16700) + , blocks_spacing(0) + , blocks_size_min(2) + , blocks_narrow_size(20) + , max_fps_history(90) + , fps_timer_interval(500) + , fps_widget_line_width(2) + , bookmark_default_color(0) + , chrono_text_position(RulerTextPosition_Top) + , time_units(TimeUnits_ms) + , connected(false) + , has_local_changes(false) + , use_custom_window_header(true) + , is_right_window_header_controls(true) + , fps_enabled(true) + , use_decorated_thread_name(false) + , hex_thread_id(false) + , enable_event_markers(true) + , enable_statistics(true) + , enable_zero_length(true) + , add_zero_blocks_to_hierarchy(false) + , draw_graphics_items_borders(true) + , hide_narrow_children(false) + , hide_minsize_blocks(false) + , display_only_relevant_stats(false) + , collapse_items_on_tree_close(false) + , all_items_expanded_by_default(true) + , only_current_thread_hierarchy(false) + , highlight_blocks_with_same_id(true) + , selecting_block_changes_thread(true) + , auto_adjust_histogram_height(true) + , auto_adjust_chart_height(false) + , display_only_frames_on_histogram(false) + , bind_scene_and_tree_expand_status(true) +{ - } +} } // END of namespace profiler_gui. diff --git a/profiler_gui/globals.h b/profiler_gui/globals.h index 7ffa3cf..63ce2b1 100644 --- a/profiler_gui/globals.h +++ b/profiler_gui/globals.h @@ -12,7 +12,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -273,38 +273,38 @@ inline profiler::SerializedBlockDescriptor& easyDescriptor(profiler::block_id_t return *EASY_GLOBALS.descriptors[i]; } -inline profiler::SerializedBlockDescriptor& easyDescriptor(const profiler::BlocksTree& _block) { - return easyDescriptor(_block.node->id()); +inline profiler::SerializedBlockDescriptor& easyDescriptor(const profiler::BlocksTree& block) { + return easyDescriptor(block.node->id()); } EASY_FORCE_INLINE const profiler::BlocksTree& easyBlocksTree(profiler::block_index_t i) { return easyBlock(i).tree; } -EASY_FORCE_INLINE const char* easyBlockName(const profiler::BlocksTree& _block) { - const char* name = _block.node->name(); - return *name != 0 ? name : easyDescriptor(_block.node->id()).name(); +EASY_FORCE_INLINE const char* easyBlockName(const profiler::BlocksTree& block) { + const char* name = block.node->name(); + return *name != 0 ? name : easyDescriptor(block.node->id()).name(); } -EASY_FORCE_INLINE const char* easyBlockName(const profiler::BlocksTree& _block, const profiler::SerializedBlockDescriptor& _desc) { - const char* name = _block.node->name(); - return *name != 0 ? name : _desc.name(); +EASY_FORCE_INLINE const char* easyBlockName(const profiler::BlocksTree& block, const profiler::SerializedBlockDescriptor& desc) { + const char* name = block.node->name(); + return *name != 0 ? name : desc.name(); } EASY_FORCE_INLINE const char* easyBlockName(profiler::block_index_t i) { return easyBlockName(easyBlock(i).tree); } -inline qreal sceneX(profiler::timestamp_t _time) { - return PROF_MICROSECONDS(qreal(_time - EASY_GLOBALS.begin_time)); +inline qreal sceneX(profiler::timestamp_t time) { + return PROF_MICROSECONDS(qreal(time - EASY_GLOBALS.begin_time)); } -inline QString imagePath(const QString& _resource) { - return QString(":/images/%1/%2").arg(EASY_GLOBALS.theme).arg(_resource); +inline QString imagePath(const QString& resource_name) { + return QString(":/images/%1/%2").arg(EASY_GLOBALS.theme).arg(resource_name); } -inline QString imagePath(const char* _resource) { - return QString(":/images/%1/%2").arg(EASY_GLOBALS.theme).arg(_resource); +inline QString imagePath(const char* resource_name) { + return QString(":/images/%1/%2").arg(EASY_GLOBALS.theme).arg(resource_name); } inline QSize applicationIconsSize() { diff --git a/profiler_gui/globals_qobjects.cpp b/profiler_gui/globals_qobjects.cpp index 2dabfec..99889ff 100644 --- a/profiler_gui/globals_qobjects.cpp +++ b/profiler_gui/globals_qobjects.cpp @@ -12,7 +12,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/globals_qobjects.h b/profiler_gui/globals_qobjects.h index 7ab6064..5e347e2 100644 --- a/profiler_gui/globals_qobjects.h +++ b/profiler_gui/globals_qobjects.h @@ -12,7 +12,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/graphics_block_item.cpp b/profiler_gui/graphics_block_item.cpp index 80fc3ad..bbd6148 100644 --- a/profiler_gui/graphics_block_item.cpp +++ b/profiler_gui/graphics_block_item.cpp @@ -8,7 +8,7 @@ * description : The file contains implementation of GraphicsBlockItem. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -1146,7 +1146,7 @@ void GraphicsBlockItem::getBlocks(qreal _left, qreal _right, ::profiler_gui::Tre size_t itemIndex = 0; if (first != level0.end()) { - itemIndex = first - level0.begin(); + itemIndex = static_cast(std::distance(level0.begin(), first)); if (itemIndex > 0) itemIndex -= 1; } diff --git a/profiler_gui/graphics_block_item.h b/profiler_gui/graphics_block_item.h index 1dcd9c1..5cced7a 100644 --- a/profiler_gui/graphics_block_item.h +++ b/profiler_gui/graphics_block_item.h @@ -9,7 +9,7 @@ * : used to draw profiler blocks on graphics scene. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/graphics_image_item.cpp b/profiler_gui/graphics_image_item.cpp index b46a396..0f4a985 100644 --- a/profiler_gui/graphics_image_item.cpp +++ b/profiler_gui/graphics_image_item.cpp @@ -8,7 +8,7 @@ * description : The file contains implementation of GraphicsImageItem. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -70,6 +70,7 @@ GraphicsImageItem::GraphicsImageItem() : Parent(nullptr) , m_maxValue(0) , m_minValue(0) , m_timer(::std::bind(&This::onTimeout, this)) + , m_bEmpty(true) , m_bPermitImageUpdate(true) { m_bReady = false; @@ -97,6 +98,16 @@ void GraphicsImageItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h) m_boundingRect.setRect(x, y, w, h); } +bool GraphicsImageItem::isEmpty() const +{ + return m_bEmpty; +} + +void GraphicsImageItem::setEmpty(bool empty) +{ + m_bEmpty = empty; +} + void GraphicsImageItem::setMousePos(const QPointF& pos) { m_mousePos = pos; @@ -173,6 +184,11 @@ bool GraphicsImageItem::cancelImageUpdate() bool GraphicsImageItem::pickTopValue() { + if (isEmpty()) + { + return false; + } + const auto y = m_mousePos.y(); if (isImageUpdatePermitted() && m_boundingRect.top() < y && y < m_boundingRect.bottom()) { @@ -187,6 +203,11 @@ bool GraphicsImageItem::pickTopValue() bool GraphicsImageItem::increaseTopValue() { + if (isEmpty()) + { + return false; + } + if (isImageUpdatePermitted() && m_topValue < m_maxValue) { auto step = 0.05 * (m_maxValue - m_bottomValue); @@ -202,6 +223,11 @@ bool GraphicsImageItem::increaseTopValue() bool GraphicsImageItem::decreaseTopValue() { + if (isEmpty()) + { + return false; + } + if (isImageUpdatePermitted() && m_topValue > m_bottomValue) { auto step = 0.05 * (m_maxValue - m_bottomValue); @@ -221,6 +247,11 @@ bool GraphicsImageItem::decreaseTopValue() bool GraphicsImageItem::pickBottomValue() { + if (isEmpty()) + { + return false; + } + const auto y = m_mousePos.y(); if (isImageUpdatePermitted() && m_boundingRect.top() < y && y < m_boundingRect.bottom()) { @@ -235,6 +266,11 @@ bool GraphicsImageItem::pickBottomValue() bool GraphicsImageItem::increaseBottomValue() { + if (isEmpty()) + { + return false; + } + if (isImageUpdatePermitted() && m_bottomValue < m_topValue) { auto step = 0.05 * (m_topValue - m_minValue); @@ -254,6 +290,11 @@ bool GraphicsImageItem::increaseBottomValue() bool GraphicsImageItem::decreaseBottomValue() { + if (isEmpty()) + { + return false; + } + if (isImageUpdatePermitted() && m_bottomValue > m_minValue) { auto step = 0.05 * (m_topValue - m_minValue); @@ -270,7 +311,7 @@ bool GraphicsImageItem::decreaseBottomValue() void GraphicsImageItem::paintImage(QPainter* _painter) { _painter->setPen(Qt::NoPen); - _painter->drawImage(0, m_boundingRect.top(), m_image); + _painter->drawImage(0, static_cast(m_boundingRect.top()), m_image); } void GraphicsImageItem::paintImage(QPainter* _painter, qreal _scale, qreal _sceneLeft, qreal _sceneRight, diff --git a/profiler_gui/graphics_image_item.h b/profiler_gui/graphics_image_item.h index bd3136e..8d1ba29 100644 --- a/profiler_gui/graphics_image_item.h +++ b/profiler_gui/graphics_image_item.h @@ -9,7 +9,7 @@ * : used to display, scroll and zoom QImage on graphics scene. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -80,11 +80,12 @@ protected: qreal m_bottomValue; qreal m_maxValue; qreal m_minValue; + bool m_bEmpty; std::atomic_bool m_bReady; private: - Timer m_timer; + Timer m_timer; bool m_bPermitImageUpdate; ///< Is false when m_workerThread is parsing input dataset (when setSource(_block_id) is called) public: @@ -111,6 +112,7 @@ protected: public: + bool isEmpty() const; void onValueChanged(); void setMousePos(const QPointF& pos); void setMousePos(qreal x, qreal y); @@ -120,6 +122,7 @@ public: protected: + void setEmpty(bool empty); void paintImage(QPainter* _painter); void paintImage(QPainter* _painter, qreal _scale, qreal _sceneLeft, qreal _sceneRight, qreal _visibleRegionLeft, qreal _visibleRegionWidth); diff --git a/profiler_gui/graphics_ruler_item.cpp b/profiler_gui/graphics_ruler_item.cpp index bab711d..c606d3c 100644 --- a/profiler_gui/graphics_ruler_item.cpp +++ b/profiler_gui/graphics_ruler_item.cpp @@ -12,7 +12,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -72,16 +72,17 @@ ////////////////////////////////////////////////////////////////////////// -GraphicsRulerItem::GraphicsRulerItem(bool _main) +GraphicsRulerItem::GraphicsRulerItem(bool main) : Parent() , m_color(profiler_gui::RULER_COLOR) , m_left(0) , m_right(0) - , m_bMain(_main) - , m_bReverse(false) - , m_bHoverIndicator(false) - , m_bHoverLeftBorder(false) - , m_bHoverRightBorder(false) + , m_main(main) + , m_reverse(false) + , m_strict(false) + , m_hover_on_indicator(false) + , m_hover_on_left_border(false) + , m_hover_on_right_border(false) { m_indicator.reserve(3); } @@ -92,10 +93,10 @@ GraphicsRulerItem::~GraphicsRulerItem() QRectF GraphicsRulerItem::boundingRect() const { - return m_boundingRect; + return m_bounding_rect; } -void GraphicsRulerItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) +void GraphicsRulerItem::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) { auto const sceneView = view(); const auto currentScale = sceneView->scale(); @@ -103,19 +104,19 @@ void GraphicsRulerItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem const auto visibleSceneRect = sceneView->visibleSceneRect(); auto sceneLeft = offset, sceneRight = offset + visibleSceneRect.width() / currentScale; - if (m_bMain) + if (m_main) m_indicator.clear(); if (m_left > sceneRight || m_right < sceneLeft) { // This item is out of screen - if (m_bMain) + if (m_main) { - const int size = m_bHoverIndicator ? 12 : 10; + const int size = m_hover_on_indicator ? 12 : 10; auto vcenter = visibleSceneRect.top() + visibleSceneRect.height() * 0.5; auto color = QColor::fromRgb(m_color.rgb()); - auto pen = _painter->pen(); + auto pen = painter->pen(); pen.setColor(color); m_indicator.clear(); @@ -134,19 +135,24 @@ void GraphicsRulerItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem m_indicator.push_back(QPointF(sceneLeft + size, vcenter + size)); } - _painter->save(); - _painter->setTransform(QTransform::fromTranslate(-x(), -y()), true); - _painter->setBrush(m_bHoverIndicator ? QColor::fromRgb(0xffff0000) : color); - _painter->setPen(pen); - _painter->drawPolygon(m_indicator); - _painter->restore(); + painter->save(); + painter->setTransform(QTransform::fromTranslate(-x(), -y()), true); + painter->setBrush(m_hover_on_indicator ? QColor::fromRgb(0xffff0000) : color); + painter->setPen(pen); + painter->drawPolygon(m_indicator); + painter->restore(); } return; } auto selectedInterval = width(); - QRectF rect((m_left - offset) * currentScale, visibleSceneRect.top(), ::std::max(selectedInterval * currentScale, 1.0), visibleSceneRect.height()); + QRectF rect( + (m_left - offset) * currentScale, + visibleSceneRect.top(), + ::std::max(selectedInterval * currentScale, 1.0), + visibleSceneRect.height() + ); selectedInterval = units2microseconds(selectedInterval); const QString text = profiler_gui::timeStringReal(EASY_GLOBALS.time_units, selectedInterval); // Displayed text @@ -156,10 +162,10 @@ void GraphicsRulerItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem // Paint!-------------------------- - _painter->save(); + painter->save(); // instead of scrollbar we're using manual offset - _painter->setTransform(QTransform::fromTranslate(-x(), -y()), true); + painter->setTransform(QTransform::fromTranslate(-x(), -y()), true); if (m_left < sceneLeft) rect.setLeft(0); @@ -174,87 +180,87 @@ void GraphicsRulerItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem g.setColorAt(0.2, QColor::fromRgba(0x14000000 | rgb)); g.setColorAt(0.8, QColor::fromRgba(0x14000000 | rgb)); g.setColorAt(1, m_color); - _painter->setBrush(g); - _painter->setPen(Qt::NoPen); - _painter->drawRect(rect); + painter->setBrush(g); + painter->setPen(Qt::NoPen); + painter->drawRect(rect); // draw left and right borders - _painter->setBrush(Qt::NoBrush); - if (m_bMain && !m_bReverse) + painter->setBrush(Qt::NoBrush); + if (m_main && !m_strict) { QPen p(QColor::fromRgba(0xd0000000 | rgb)); p.setStyle(Qt::DotLine); - _painter->setPen(p); + painter->setPen(p); } else { - _painter->setPen(QColor::fromRgba(0xd0000000 | rgb)); + painter->setPen(QColor::fromRgba(0xd0000000 | rgb)); } if (m_left > sceneLeft) { - if (m_bHoverLeftBorder) + if (m_hover_on_left_border) { // Set bold if border is hovered - QPen p = _painter->pen(); + QPen p = painter->pen(); p.setWidth(3); - _painter->setPen(p); + painter->setPen(p); } - _painter->drawLine(QPointF(rect.left(), rect.top()), QPointF(rect.left(), rect.bottom())); + painter->drawLine(QPointF(rect.left(), rect.top()), QPointF(rect.left(), rect.bottom())); } if (m_right < sceneRight) { - if (m_bHoverLeftBorder) + if (m_hover_on_left_border) { // Restore width - QPen p = _painter->pen(); + QPen p = painter->pen(); p.setWidth(1); - _painter->setPen(p); + painter->setPen(p); } - else if (m_bHoverRightBorder) + else if (m_hover_on_right_border) { // Set bold if border is hovered - QPen p = _painter->pen(); + QPen p = painter->pen(); p.setWidth(3); - _painter->setPen(p); + painter->setPen(p); } - _painter->drawLine(QPointF(rect.right(), rect.top()), QPointF(rect.right(), rect.bottom())); + painter->drawLine(QPointF(rect.right(), rect.top()), QPointF(rect.right(), rect.bottom())); // This is not necessary because another setPen() invoked for draw text - //if (m_bHoverRightBorder) + //if (m_hover_on_right_border) //{ // // Restore width - // QPen p = _painter->pen(); + // QPen p = painter->pen(); // p.setWidth(1); - // _painter->setPen(p); + // painter->setPen(p); //} } // draw text - _painter->setCompositionMode(QPainter::CompositionMode_Difference); // This lets the text to be visible on every background - _painter->setRenderHint(QPainter::TextAntialiasing); - _painter->setPen(0x00ffffff - rgb); - _painter->setFont(EASY_GLOBALS.font.ruler); + painter->setCompositionMode(QPainter::CompositionMode_Difference); // This lets the text to be visible on every background + painter->setRenderHint(QPainter::TextAntialiasing); + painter->setPen(0x00ffffff - rgb); + painter->setFont(EASY_GLOBALS.font.ruler); int textFlags = 0; switch (EASY_GLOBALS.chrono_text_position) { case profiler_gui::RulerTextPosition_Top: textFlags = Qt::AlignTop | Qt::AlignHCenter; - if (!m_bMain) rect.setTop(rect.top() + textRect.height() * 0.75); + if (!m_main) rect.setTop(rect.top() + textRect.height() * 0.75); break; case profiler_gui::RulerTextPosition_Center: textFlags = Qt::AlignCenter; - if (!m_bMain) rect.setTop(rect.top() + textRect.height() * 1.5); + if (!m_main) rect.setTop(rect.top() + textRect.height() * 1.5); break; case profiler_gui::RulerTextPosition_Bottom: textFlags = Qt::AlignBottom | Qt::AlignHCenter; - if (!m_bMain) rect.setHeight(rect.height() - textRect.height() * 0.75); + if (!m_main) rect.setHeight(rect.height() - textRect.height() * 0.75); break; } @@ -262,8 +268,8 @@ void GraphicsRulerItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem if (textRect_width < rect.width()) { // Text will be drawed inside rectangle - _painter->drawText(rect, textFlags, text); - _painter->restore(); + painter->drawText(rect, textFlags, text); + painter->restore(); return; } @@ -284,101 +290,107 @@ void GraphicsRulerItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem } //else // Text will be drawed inside rectangle - _painter->drawText(rect, textFlags | Qt::TextDontClip, text); + painter->drawText(rect, textFlags | Qt::TextDontClip, text); - _painter->restore(); + painter->restore(); // END Paint!~~~~~~~~~~~~~~~~~~~~~~ } void GraphicsRulerItem::hide() { - m_bHoverIndicator = false; - m_bHoverLeftBorder = false; - m_bHoverRightBorder = false; - m_bReverse = false; + m_hover_on_indicator = false; + m_hover_on_left_border = false; + m_hover_on_right_border = false; + m_reverse = false; + m_strict = false; Parent::hide(); } -bool GraphicsRulerItem::indicatorContains(const QPointF& _pos) const +bool GraphicsRulerItem::indicatorContains(const QPointF& pos) const { if (m_indicator.empty()) return false; - const auto itemX = toItem(_pos.x()); - return m_indicator.containsPoint(QPointF(itemX, _pos.y()), Qt::OddEvenFill); + const auto itemX = toItem(pos.x()); + return m_indicator.containsPoint(QPointF(itemX, pos.y()), Qt::OddEvenFill); } -void GraphicsRulerItem::setHoverLeft(bool _hover) +void GraphicsRulerItem::setHoverLeft(bool hover) { - m_bHoverLeftBorder = _hover; + m_hover_on_left_border = hover; } -void GraphicsRulerItem::setHoverRight(bool _hover) +void GraphicsRulerItem::setHoverRight(bool hover) { - m_bHoverRightBorder = _hover; + m_hover_on_right_border = hover; } -bool GraphicsRulerItem::hoverLeft(qreal _x) const +bool GraphicsRulerItem::hoverLeft(qreal x) const { - const auto dx = fabs(_x - m_left) * view()->scale(); + const auto dx = fabs(x - m_left) * view()->scale(); return dx < 4; } -bool GraphicsRulerItem::hoverRight(qreal _x) const +bool GraphicsRulerItem::hoverRight(qreal x) const { - const auto dx = fabs(_x - m_right) * view()->scale(); + const auto dx = fabs(x - m_right) * view()->scale(); return dx < 4; } -QPointF GraphicsRulerItem::toItem(const QPointF& _pos) const +QPointF GraphicsRulerItem::toItem(const QPointF& pos) const { const auto sceneView = view(); - return QPointF((_pos.x() - sceneView->offset()) * sceneView->scale() - x(), _pos.y()); + return QPointF((pos.x() - sceneView->offset()) * sceneView->scale() - x(), pos.y()); } -qreal GraphicsRulerItem::toItem(qreal _x) const +qreal GraphicsRulerItem::toItem(qreal x) const { const auto sceneView = view(); - return (_x - sceneView->offset()) * sceneView->scale() - x(); + return (x - sceneView->offset()) * sceneView->scale() - this->x(); } -void GraphicsRulerItem::setColor(const QColor& _color) +void GraphicsRulerItem::setColor(const QColor& color) { - m_color = _color; + m_color = color; } void GraphicsRulerItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h) { - m_boundingRect.setRect(x, y, w, h); + m_bounding_rect.setRect(x, y, w, h); } -void GraphicsRulerItem::setBoundingRect(const QRectF& _rect) +void GraphicsRulerItem::setBoundingRect(const QRectF& rect) { - m_boundingRect = _rect; + m_bounding_rect = rect; } -void GraphicsRulerItem::setLeftRight(qreal _left, qreal _right) +void GraphicsRulerItem::setLeftRight(qreal left, qreal right) { - if (_left < _right) + if (left < right) { - m_left = _left; - m_right = _right; + m_left = left; + m_right = right; } else { - m_left = _right; - m_right = _left; + m_left = right; + m_right = left; } } -void GraphicsRulerItem::setReverse(bool _reverse) +void GraphicsRulerItem::setReverse(bool reverse) { - m_bReverse = _reverse; + m_reverse = reverse; } -void GraphicsRulerItem::setHoverIndicator(bool _hover) +void GraphicsRulerItem::setStrict(bool strict) { - m_bHoverIndicator = _hover; + m_strict = strict; +} + +void GraphicsRulerItem::setHoverIndicator(bool hover) +{ + m_hover_on_indicator = hover; } const BlocksGraphicsView* GraphicsRulerItem::view() const diff --git a/profiler_gui/graphics_ruler_item.h b/profiler_gui/graphics_ruler_item.h index da8ff1d..278a7d4 100644 --- a/profiler_gui/graphics_ruler_item.h +++ b/profiler_gui/graphics_ruler_item.h @@ -13,7 +13,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -74,25 +74,26 @@ class GraphicsRulerItem : public QGraphicsItem typedef QGraphicsItem Parent; typedef GraphicsRulerItem This; - QPolygonF m_indicator; ///< Indicator displayed when this chrono item is out of screen (displaying only for main item) - QRectF m_boundingRect; ///< boundingRect (see QGraphicsItem) - QColor m_color; ///< Color of the item - 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_bHoverIndicator; ///< Mouse hover above indicator - bool m_bHoverLeftBorder; - bool m_bHoverRightBorder; + QPolygonF m_indicator; ///< Indicator displayed when this chrono item is out of screen (displaying only for main item) + QRectF m_bounding_rect; ///< boundingRect (see QGraphicsItem) + QColor m_color; ///< Color of the item + qreal m_left, m_right; ///< Left and right bounds of the selection zone + bool m_main; ///< Is this ruler item main (true, by default) + bool m_reverse; ///< + bool m_strict; ///< + bool m_hover_on_indicator; ///< Mouse hover above indicator + bool m_hover_on_left_border; + bool m_hover_on_right_border; public: - explicit GraphicsRulerItem(bool _main = true); + explicit GraphicsRulerItem(bool main = true); virtual ~GraphicsRulerItem(); // Public virtual methods QRectF boundingRect() const override; - void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override; + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; public: @@ -100,46 +101,57 @@ public: void hide(); - void setColor(const QColor& _color); + void setColor(const QColor& color); void setBoundingRect(qreal x, qreal y, qreal w, qreal h); - void setBoundingRect(const QRectF& _rect); + void setBoundingRect(const QRectF& rect); - void setLeftRight(qreal _left, qreal _right); + void setLeftRight(qreal left, qreal right); - void setReverse(bool _reverse); + void setReverse(bool reverse); + void setStrict(bool strict); - void setHoverIndicator(bool _hover); + void setHoverIndicator(bool hover); - bool indicatorContains(const QPointF& _pos) const; + bool indicatorContains(const QPointF& pos) const; - void setHoverLeft(bool _hover); - void setHoverRight(bool _hover); + void setHoverLeft(bool hover); + void setHoverRight(bool hover); - bool hoverLeft(qreal _x) const; - bool hoverRight(qreal _x) const; + bool hoverLeft(qreal x) const; + bool hoverRight(qreal x) const; - QPointF toItem(const QPointF& _pos) const; - qreal toItem(qreal _x) const; + QPointF toItem(const QPointF& pos) const; + qreal toItem(qreal x) const; inline bool hoverIndicator() const { - return m_bHoverIndicator; + return m_hover_on_indicator; } inline bool hoverLeft() const { - return m_bHoverLeftBorder; + return m_hover_on_left_border; } inline bool hoverRight() const { - return m_bHoverRightBorder; + return m_hover_on_right_border; + } + + inline bool hoverAnyBorder() const + { + return m_hover_on_left_border || m_hover_on_right_border; } inline bool reverse() const { - return m_bReverse; + return m_reverse; + } + + inline bool strict() const + { + return m_strict; } inline qreal left() const diff --git a/profiler_gui/graphics_scrollbar.cpp b/profiler_gui/graphics_scrollbar.cpp index 615118d..e026e4e 100644 --- a/profiler_gui/graphics_scrollbar.cpp +++ b/profiler_gui/graphics_scrollbar.cpp @@ -12,7 +12,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -115,6 +115,11 @@ void GraphicsHistogramItem::paint(QPainter* _painter, const QStyleOptionGraphics void GraphicsHistogramItem::paintMouseIndicator(QPainter* _painter, qreal _top, qreal _bottom, qreal _width, qreal _height, qreal _top_width, qreal _mouse_y, qreal _delta_time, int _font_h) { + if (isEmpty()) + { + return; + } + if (_font_h != 0 && _top < _mouse_y && _mouse_y < _bottom) { const int half_font_h = _font_h >> 1; @@ -178,7 +183,7 @@ void GraphicsHistogramItem::paintByPtr(QPainter* _painter) _painter->setPen(profiler_gui::TEXT_COLOR); _painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip, bindMode ? " Mode: Zoom" : " Mode: Overview"); - if (!m_topDurationStr.isEmpty()) + if (!isEmpty() && !m_topDurationStr.isEmpty()) { if (m_timeUnits != EASY_GLOBALS.time_units) { @@ -204,7 +209,7 @@ void GraphicsHistogramItem::paintByPtr(QPainter* _painter) paintMouseIndicator(_painter, m_boundingRect.top(), bottom, width, m_boundingRect.height(), top_width, m_mousePos.y(), dtime, font_h); - if (m_bottomValue < EASY_GLOBALS.frame_time && EASY_GLOBALS.frame_time < m_topValue) + if (!isEmpty() && m_bottomValue < EASY_GLOBALS.frame_time && EASY_GLOBALS.frame_time < m_topValue) { // Draw marker displaying expected frame_time step const auto h = bottom - (EASY_GLOBALS.frame_time - m_bottomValue) * coeff; @@ -270,7 +275,7 @@ void GraphicsHistogramItem::paintById(QPainter* _painter) _painter->setPen(profiler_gui::TEXT_COLOR); _painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip, bindMode ? " Mode: Zoom" : " Mode: Overview"); - if (!m_topDurationStr.isEmpty()) + if (!isEmpty() && !m_topDurationStr.isEmpty()) { if (m_timeUnits != EASY_GLOBALS.time_units) { @@ -296,7 +301,7 @@ void GraphicsHistogramItem::paintById(QPainter* _painter) paintMouseIndicator(_painter, m_boundingRect.top(), bottom, width, m_boundingRect.height(), top_width, m_mousePos.y(), dtime, font_h); - if (m_bottomValue < EASY_GLOBALS.frame_time && EASY_GLOBALS.frame_time < m_topValue) + if (!isEmpty() && m_bottomValue < EASY_GLOBALS.frame_time && EASY_GLOBALS.frame_time < m_topValue) { // Draw marker displaying required frame_time step const auto h = bottom - (EASY_GLOBALS.frame_time - m_bottomValue) * coeff; @@ -315,20 +320,20 @@ void GraphicsHistogramItem::paintById(QPainter* _painter) _painter->setPen(profiler_gui::TEXT_COLOR); rect.setRect(0, bottom + 2, width, font_h); - if (!m_selectedBlocks.empty()) + if (!items.empty()) { if (m_threadProfiledTime != 0) { _painter->drawText(rect, Qt::AlignCenter | Qt::TextDontClip, QString("%1 | %2 | %3 calls | %4% of thread profiled time") - .arg(m_threadName).arg(m_blockName).arg(m_selectedBlocks.size()) + .arg(m_threadName).arg(m_blockName).arg(items.size()) .arg(QString::number(100. * (double)m_blockTotalDuraion / (double)m_threadProfiledTime, 'f', 2))); } else { _painter->drawText(rect, Qt::AlignCenter | Qt::TextDontClip, QString("%1 | %2 | %3 calls | 100% of thread profiled time") - .arg(m_threadName).arg(m_blockName).arg(m_selectedBlocks.size())); + .arg(m_threadName).arg(m_blockName).arg(items.size())); } } else @@ -380,6 +385,7 @@ void GraphicsHistogramItem::setSource(profiler::thread_id_t _thread_id, const pr m_imageScaleUpdate = m_imageScale = 1; m_selectedBlocks.clear(); + setEmpty(true); { profiler::BlocksTree::children_t().swap(m_selectedBlocks); } setImageUpdatePermitted(false); @@ -464,6 +470,7 @@ void GraphicsHistogramItem::setSource(profiler::thread_id_t _thread_id, const pr m_bottomDurationStr.clear(); } + setEmpty(empty); setReady(true); }, m_bReady); @@ -505,6 +512,7 @@ void GraphicsHistogramItem::setSource(profiler::thread_id_t _thread_id, profiler m_imageScaleUpdate = m_imageScale = 1; m_selectedBlocks.clear(); + setEmpty(true); { profiler::BlocksTree::children_t().swap(m_selectedBlocks); } m_threadId = _thread_id; @@ -658,6 +666,7 @@ void GraphicsHistogramItem::setSource(profiler::thread_id_t _thread_id, profiler m_topValue = m_maxValue; m_bottomValue = m_minValue; + setEmpty(m_selectedBlocks.empty()); setReady(true); }, m_bReady); @@ -770,7 +779,7 @@ bool GraphicsHistogramItem::decreaseBottomValue() void GraphicsHistogramItem::pickFrameTime(qreal _y) const { - if (isImageUpdatePermitted() && m_boundingRect.top() < _y && _y < m_boundingRect.bottom() && !m_topDurationStr.isEmpty()) + if (!isEmpty() && isImageUpdatePermitted() && m_boundingRect.top() < _y && _y < m_boundingRect.bottom() && !m_topDurationStr.isEmpty()) { const auto frame_time = m_bottomValue + (m_topValue - m_bottomValue) * (m_boundingRect.bottom() - _y) / m_boundingRect.height(); EASY_GLOBALS.frame_time = static_cast(frame_time); diff --git a/profiler_gui/graphics_scrollbar.h b/profiler_gui/graphics_scrollbar.h index 1e93333..94fc295 100644 --- a/profiler_gui/graphics_scrollbar.h +++ b/profiler_gui/graphics_scrollbar.h @@ -12,7 +12,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/graphics_slider_area.cpp b/profiler_gui/graphics_slider_area.cpp index 7d86ff2..0c0167e 100644 --- a/profiler_gui/graphics_slider_area.cpp +++ b/profiler_gui/graphics_slider_area.cpp @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/graphics_slider_area.h b/profiler_gui/graphics_slider_area.h index e9b22ce..a41faed 100644 --- a/profiler_gui/graphics_slider_area.h +++ b/profiler_gui/graphics_slider_area.h @@ -1,6 +1,6 @@ /** Lightweight profiler library for c++ -Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin Licensed under either of * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/images/attribution.txt b/profiler_gui/images/attribution.txt index ca2c44f..3f41912 100644 --- a/profiler_gui/images/attribution.txt +++ b/profiler_gui/images/attribution.txt @@ -51,6 +51,5 @@ default/crop.svg - Icon made by Freepik from www.flaticon.com default/yx.svg - Icon made by Freepik from www.flaticon.com default/svg2.svg - Icon made by Freepik from www.flaticon.com default/svg3.svg - Icon made by Freepik from www.flaticon.com -default/to-fullscreen.svg - Icon made by Freepik from www.flaticon.com default/to-window.svg - Icon made by Freepik from www.flaticon.com default/window.svg - Icon made by Freepik from www.flaticon.com diff --git a/profiler_gui/images/default/to-fullscreen.svg b/profiler_gui/images/default/to-fullscreen.svg index 02415fd..8a9674f 100644 --- a/profiler_gui/images/default/to-fullscreen.svg +++ b/profiler_gui/images/default/to-fullscreen.svg @@ -1,9 +1,8 @@ + viewBox="0 0 128 128" style="enable-background:new 0 0 128 128;" xml:space="preserve"> - + diff --git a/profiler_gui/main.cpp b/profiler_gui/main.cpp index 66552b4..6e932ca 100644 --- a/profiler_gui/main.cpp +++ b/profiler_gui/main.cpp @@ -8,7 +8,7 @@ * description : Main file for EasyProfiler GUI. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/main_window.cpp b/profiler_gui/main_window.cpp index ecf17e6..1f16d54 100644 --- a/profiler_gui/main_window.cpp +++ b/profiler_gui/main_window.cpp @@ -18,7 +18,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -87,6 +87,7 @@ #include #include #include +#include #include #include #include @@ -128,7 +129,7 @@ const auto NETWORK_CACHE_FILE = "easy_profiler_stream.cache"; ////////////////////////////////////////////////////////////////////////// -inline const QStringList& UI_themes() +static const QStringList& UI_themes() { static const QStringList themes { "default" @@ -150,7 +151,80 @@ inline void clear_stream(std::stringstream& _stream) #endif } -inline void loadTheme(const QString& _theme) +static void convertPointSizes(QString& style) +{ + // Find font family + const auto fontMatch = QRegularExpression("font-family:\\s*\\\"(.*)\\\"\\s*;").match(style); + const auto fontFamily = fontMatch.hasMatch() ? fontMatch.captured(fontMatch.lastCapturedIndex()) : QString("DejaVu Sans"); + //QMessageBox::information(nullptr, "Found font family", fontFamily); + + // Calculate point size using current font + const auto pointSizeF = QFontMetricsF(QFont(fontFamily, 100)).height() * 1e-2; + //QMessageBox::information(nullptr, "Point size", QString("100pt = %1\n1pt = %2").arg(pointSizeF * 1e2).arg(pointSizeF)); + + // Find and convert all sizes from points to pixels + QRegularExpression re("(\\d+\\.?\\d*)ex"); + auto it = re.globalMatch(style); + + std::vector matches; + { + QSet uniqueMatches; + while (it.hasNext()) + { + const auto match = it.next(); + if (!uniqueMatches.contains(match.captured())) + { + uniqueMatches.insert(match.captured()); + matches.emplace_back(match.capturedTexts()); + } + } + } + + for (const auto& capturedTexts : matches) + { + const auto pt = capturedTexts.back().toDouble(); + const int pixels = static_cast(lround(pointSizeF * pt)); + style.replace(QString(" %1").arg(capturedTexts.front()), QString(" %1px").arg(pixels)); + style.replace(QString(":%1").arg(capturedTexts.front()), QString(":%1px").arg(pixels)); + } +} + +static void replaceOsDependentSettings(QString& style) +{ + // Find and convert all OS dependent options + // Example: "/*{lin}font-weight: bold;*/" -> "font-weight: bold;" + +#if defined(_WIN32) + QRegularExpression re("/\\*\\{win\\}(.*)\\*/"); +#elif defined(__APPLE__) + QRegularExpression re("/\\*\\{mac\\}(.*)\\*/"); +#else + QRegularExpression re("/\\*\\{lin\\}(.*)\\*/"); +#endif + + auto it = re.globalMatch(style); + + std::vector matches; + { + QSet uniqueMatches; + while (it.hasNext()) + { + const auto match = it.next(); + if (!uniqueMatches.contains(match.captured())) + { + uniqueMatches.insert(match.captured()); + matches.emplace_back(match.capturedTexts()); + } + } + } + + for (const auto& capturedTexts : matches) + { + style.replace(capturedTexts.front(), capturedTexts.back()); + } +} + +static void loadTheme(const QString& _theme) { QFile file(QStringLiteral(":/themes/") + _theme); if (file.open(QFile::ReadOnly | QFile::Text)) @@ -159,41 +233,8 @@ inline void loadTheme(const QString& _theme) QString style = in.readAll(); if (!style.isEmpty()) { - // Find font family - const auto fontMatch = QRegularExpression("font-family:\\s*\\\"(.*)\\\"\\s*;").match(style); - const auto fontFamily = fontMatch.hasMatch() ? fontMatch.captured(fontMatch.lastCapturedIndex()) : QString("DejaVu Sans"); - //QMessageBox::information(nullptr, "Found font family", fontFamily); - - // Calculate point size using current font - const auto pointSizeF = QFontMetricsF(QFont(fontFamily, 100)).height() * 1e-2; - //QMessageBox::information(nullptr, "Point size", QString("100pt = %1\n1pt = %2").arg(pointSizeF * 1e2).arg(pointSizeF)); - - // Find and convert all sizes from points to pixels - QRegularExpression re("(\\d+\\.?\\d*)ex"); - auto it = re.globalMatch(style); - - std::vector matches; - { - QSet uniqueMatches; - while (it.hasNext()) - { - const auto match = it.next(); - if (!uniqueMatches.contains(match.captured())) - { - uniqueMatches.insert(match.captured()); - matches.emplace_back(match.capturedTexts()); - } - } - } - - for (const auto& capturedTexts : matches) - { - const auto pt = capturedTexts.back().toDouble(); - const int pixels = static_cast(pointSizeF * pt + 0.5); - //QMessageBox::information(nullptr, "Style-sheet modification", QString("Replacing '%1'\nwith\n'%2px'\n\npt count: %3").arg(capturedTexts.front()).arg(pixels).arg(pt)); - style.replace(capturedTexts.front(), QString("%1px").arg(pixels)); - } - + convertPointSizes(style); + replaceOsDependentSettings(style); qApp->setStyleSheet(style); } } @@ -253,7 +294,7 @@ void MainWindow::configureSizes() EASY_GLOBALS.font.default_font = w.font(); const QFontMetricsF fm(w.font()); -#ifdef WIN32 +#ifdef _WIN32 EASY_CONSTEXPR qreal DefaultHeight = 16; #else EASY_CONSTEXPR qreal DefaultHeight = 17; @@ -268,7 +309,7 @@ void MainWindow::configureSizes() size.graphics_row_full = size.graphics_row_height; size.threads_row_spacing = size.graphics_row_full >> 1; size.timeline_height = size.font_height + px(10); - size.icon_size = size.font_height + px(11); + size.icon_size = size.font_height + px(5); const auto fontFamily = w.font().family(); const auto pixelSize = w.font().pixelSize(); @@ -543,6 +584,12 @@ MainWindow::MainWindow() : Parent(), m_theme("default"), m_lastAddress("localhos action->setChecked(EASY_GLOBALS.bind_scene_and_tree_expand_status); connect(action, &QAction::triggered, this, &This::onBindExpandStatusChange); + action = submenu->addAction("Hide stats for single blocks in tree"); + action->setToolTip("If checked then such stats like Min,Max,Avg etc.\nwill not be displayed in stats tree for blocks\nwith number of calls == 1"); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.display_only_relevant_stats); + connect(action, &QAction::triggered, this, &This::onDisplayRelevantStatsChange); + action = submenu->addAction("Selecting block changes current thread"); action->setToolTip("Automatically select thread while selecting a block.\nIf not checked then you will have to select current thread\nmanually double clicking on thread name on a diagram."); action->setCheckable(true); @@ -1375,6 +1422,11 @@ void MainWindow::onBindExpandStatusChange(bool _checked) EASY_GLOBALS.bind_scene_and_tree_expand_status = _checked; } +void MainWindow::onDisplayRelevantStatsChange(bool _checked) +{ + EASY_GLOBALS.display_only_relevant_stats = _checked; +} + void MainWindow::onHierarchyFlagChange(bool _checked) { EASY_GLOBALS.only_current_thread_hierarchy = _checked; @@ -1467,8 +1519,14 @@ void MainWindow::onEditBlocksClicked(bool) { if (m_descTreeDialog.ptr != nullptr) { + if (m_descTreeDialog.ptr->isMinimized()) + { + m_descTreeDialog.ptr->setWindowState((m_descTreeDialog.ptr->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); + } + m_descTreeDialog.ptr->raise(); m_descTreeDialog.ptr->setFocus(); + return; } @@ -1647,6 +1705,10 @@ void MainWindow::loadSettings() if (!flag.isNull()) EASY_GLOBALS.bind_scene_and_tree_expand_status = flag.toBool(); + flag = settings.value("display_only_relevant_stats"); + if (!flag.isNull()) + EASY_GLOBALS.display_only_relevant_stats = flag.toBool(); + flag = settings.value("selecting_block_changes_thread"); if (!flag.isNull()) EASY_GLOBALS.selecting_block_changes_thread = flag.toBool(); @@ -1779,6 +1841,7 @@ void MainWindow::saveSettingsAndGeometry() settings.setValue("add_zero_blocks_to_hierarchy", EASY_GLOBALS.add_zero_blocks_to_hierarchy); settings.setValue("highlight_blocks_with_same_id", EASY_GLOBALS.highlight_blocks_with_same_id); settings.setValue("bind_scene_and_tree_expand_status", EASY_GLOBALS.bind_scene_and_tree_expand_status); + settings.setValue("display_only_relevant_stats", EASY_GLOBALS.display_only_relevant_stats); 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); @@ -2106,9 +2169,6 @@ void MainWindow::onLoadingFinish(profiler::block_index_t& _nblocks) { auto& guiblock = EASY_GLOBALS.gui_blocks[i]; guiblock.tree = std::move(blocks[i]); -#ifdef EASY_TREE_WIDGET__USE_VECTOR - profiler_gui::set_max(guiblock.tree_item); -#endif } m_saveAction->setEnabled(true); @@ -2818,12 +2878,7 @@ void MainWindow::onSelectValue(profiler::thread_id_t _thread_id, uint32_t _value void DialogWithGeometry::create(QWidget* content, QWidget* parent) { -#ifdef WIN32 - const WindowHeader::Buttons buttons = WindowHeader::AllButtons; -#else - const WindowHeader::Buttons buttons {WindowHeader::MaximizeButton | WindowHeader::CloseButton}; -#endif - ptr = new Dialog(parent, EASY_DEFAULT_WINDOW_TITLE, content, buttons, QMessageBox::NoButton); + ptr = new Dialog(parent, EASY_DEFAULT_WINDOW_TITLE, content, WindowHeader::AllButtons, QMessageBox::NoButton); ptr->setProperty("stayVisible", true); ptr->setAttribute(Qt::WA_DeleteOnClose, true); } diff --git a/profiler_gui/main_window.h b/profiler_gui/main_window.h index a6c484f..f4d4518 100644 --- a/profiler_gui/main_window.h +++ b/profiler_gui/main_window.h @@ -11,7 +11,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -337,6 +337,7 @@ protected slots: void onAllItemsExpandedByDefaultChange(bool); void onBindExpandStatusChange(bool); void onHierarchyFlagChange(bool); + void onDisplayRelevantStatsChange(bool); void onExpandAllClicked(bool); void onCollapseAllClicked(bool); void onViewportInfoClicked(bool); diff --git a/profiler_gui/resources.rc b/profiler_gui/resources.rc index e09dbca..be6d2c6 100644 --- a/profiler_gui/resources.rc +++ b/profiler_gui/resources.rc @@ -18,7 +18,7 @@ BEGIN VALUE "CompanyName", "EasySolutions" VALUE "FileDescription", "EasyProfiler" VALUE "InternalName", "profiler_gui" - VALUE "LegalCopyright", "Copyright (C) 2016-2018 Victor Zarubkin, Sergey Yagovtsev" + VALUE "LegalCopyright", "Copyright (C) 2016-2019 Victor Zarubkin, Sergey Yagovtsev" VALUE "LegalTrademarks1", "All Rights Reserved" VALUE "LegalTrademarks2", "All Rights Reserved" VALUE "OriginalFilename", "profiler_gui.exe" diff --git a/profiler_gui/round_progress_widget.cpp b/profiler_gui/round_progress_widget.cpp index e157756..06feff6 100644 --- a/profiler_gui/round_progress_widget.cpp +++ b/profiler_gui/round_progress_widget.cpp @@ -8,7 +8,7 @@ * description : The file contains implementation of RoundProgressWidget. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/round_progress_widget.h b/profiler_gui/round_progress_widget.h index e7fdbd0..72b8ce1 100644 --- a/profiler_gui/round_progress_widget.h +++ b/profiler_gui/round_progress_widget.h @@ -8,7 +8,7 @@ * description : The file contains declaration of RoundProgressWidget. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/text_highlighter.cpp b/profiler_gui/text_highlighter.cpp new file mode 100644 index 0000000..0102a6c --- /dev/null +++ b/profiler_gui/text_highlighter.cpp @@ -0,0 +1,104 @@ +/************************************************************************ +* file name : text_highlighter.cpp +* ----------------- : +* creation time : 2019/10/12 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementation of TextHighlighter. +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : Permission is hereby granted, free of charge, to any person obtaining a copy +* : of this software and associated documentation files (the "Software"), to deal +* : in the Software without restriction, including without limitation the rights +* : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +* : of the Software, and to permit persons to whom the Software is furnished +* : to do so, subject to the following conditions: +* : +* : The above copyright notice and this permission notice shall be included in all +* : copies or substantial portions of the Software. +* : +* : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* : INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +* : PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +* : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* : TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +* : USE OR OTHER DEALINGS IN THE SOFTWARE. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include "text_highlighter.h" +#include +#include + +#include +#include "common_functions.h" + +EASY_CONSTEXPR auto HighlightColor = profiler::colors::Yellow; +EASY_CONSTEXPR auto CurrentHighlightColor = profiler::colors::Magenta; + +TextHighlighter::TextHighlighter( + QTextDocument* doc, + const QColor& normalTextColor, + const QColor& lightTextColor, + const QString& pattern, + Qt::CaseSensitivity caseSensitivity, + bool current +) + : QSyntaxHighlighter(doc) + , m_pattern(pattern) + , m_caseSensitivity(caseSensitivity) +{ + auto color = current ? CurrentHighlightColor : HighlightColor; + m_textCharFormat.setBackground(QColor::fromRgba(color)); + m_textCharFormat.setForeground(profiler_gui::isLightColor(color) ? normalTextColor : lightTextColor); +} + +TextHighlighter::~TextHighlighter() +{ + +} + +void TextHighlighter::highlightBlock(const QString& text) +{ + if (m_pattern.isEmpty()) + { + return; + } + + QRegExp expression(m_pattern, m_caseSensitivity); + int index = text.indexOf(expression); + while (index >= 0) + { + const auto length = expression.cap().length(); + setFormat(index, length, m_textCharFormat); + + auto prevIndex = index; + index = text.indexOf(expression, index + length); + if (index <= prevIndex) + { + break; + } + } +} diff --git a/profiler_gui/text_highlighter.h b/profiler_gui/text_highlighter.h new file mode 100644 index 0000000..d1320a4 --- /dev/null +++ b/profiler_gui/text_highlighter.h @@ -0,0 +1,83 @@ +/************************************************************************ +* file name : text_highlighter.h +* ----------------- : +* creation time : 2019/10/12 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains declaration of TextHighlighter - an auxiliary class +* : for highlighting text in QTextDocument. +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : Permission is hereby granted, free of charge, to any person obtaining a copy +* : of this software and associated documentation files (the "Software"), to deal +* : in the Software without restriction, including without limitation the rights +* : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +* : of the Software, and to permit persons to whom the Software is furnished +* : to do so, subject to the following conditions: +* : +* : The above copyright notice and this permission notice shall be included in all +* : copies or substantial portions of the Software. +* : +* : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* : INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +* : PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +* : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* : TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +* : USE OR OTHER DEALINGS IN THE SOFTWARE. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_PROFILER_TEXT_HIGHLIGHTER_H +#define EASY_PROFILER_TEXT_HIGHLIGHTER_H + +#include + +class TextHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT + + QString m_pattern; + Qt::CaseSensitivity m_caseSensitivity; + QTextCharFormat m_textCharFormat; + +public: + + TextHighlighter( + QTextDocument* doc, + const QColor& normalTextColor, + const QColor& lightTextColor, + const QString& pattern, + Qt::CaseSensitivity caseSensitivity, + bool current + ); + + ~TextHighlighter() override; + +protected: + + void highlightBlock(const QString& text) override; +}; + +#endif //EASY_PROFILER_TEXT_HIGHLIGHTER_H diff --git a/profiler_gui/themes/default.css b/profiler_gui/themes/default.css index 9b64872..83f39d2 100644 --- a/profiler_gui/themes/default.css +++ b/profiler_gui/themes/default.css @@ -64,7 +64,10 @@ QSplitter::handle:pressed { QLabel#BlocksTreeWidget_HintLabel { color: gray; - font-size: 13pt; } + font-size: 12pt; } + +QLabel#BlocksTreeWidget_HintLabel[hovered=true] { + color: black; } /* ****************************************************************************************************************** */ QLineEdit, QSpinBox { @@ -303,9 +306,10 @@ QMenu::indicator:exclusive:checked:disabled { /* ****************************************************************************************************************** */ QHeaderView::section { height: 15.3ex; - width: 60ex; - min-width: 40ex; - background: #eeeeee; } + min-height: 15.3ex; + max-height: 15.3ex; + background: #eeeeee; + /*{lin}font-weight: bold;*/ } /* ****************************************************************************************************************** */ DockWidget QWidget#EasyDockWidgetTitle { @@ -399,9 +403,9 @@ QScrollBar::add-line, QScrollBar::sub-line { WindowHeader { background-color: white; - height: 24ex; - min-height: 24ex; - max-height: 24ex; + height: 15.6ex; + min-height: 15.6ex; + max-height: 15.6ex; margin: 0; padding: 0 0 0 6ex; border: none; } @@ -426,8 +430,8 @@ WindowHeader { image: url(":/images/default/minimize"); padding: 2ex; } WindowHeader QPushButton#WindowHeader_MinButton:hover { - background-color: #d1d1d1; - border-bottom: 1px solid #d1d1d1; } + background-color: #e8e8e8; + border-bottom: 1px solid #e8e8e8; } WindowHeader QPushButton#WindowHeader_MinButton:pressed { background-color: #c4c4c4; border-bottom: 1px solid #c4c4c4; } @@ -438,8 +442,8 @@ WindowHeader { image: url(":/images/default/to-window"); padding: 1ex; } WindowHeader QPushButton#WindowHeader_MaxButton:hover { - background-color: #d1d1d1; - border-bottom: 1px solid #d1d1d1; } + background-color: #e8e8e8; + border-bottom: 1px solid #e8e8e8; } WindowHeader QPushButton#WindowHeader_MaxButton:pressed { background-color: #c4c4c4; border-bottom: 1px solid #c4c4c4; } diff --git a/profiler_gui/themes/default.scss b/profiler_gui/themes/default.scss index e5efb79..c96aa56 100644 --- a/profiler_gui/themes/default.scss +++ b/profiler_gui/themes/default.scss @@ -40,11 +40,12 @@ $IndicatorBorderColor: darken($DarkSelectionColor, 5%); $DisabledIndicatorBackgroundColor: $SelectionColor; $DisabledIndicatorBorderColor: darken($SelectionColor, 5%); -$FocusBorderColor: $DarkSelectionColor;//#ffbcbc; +$FocusBorderColor: $DarkSelectionColor; // #ffbcbc; $TooltipColor: #ffeccc; $InputHeight: 15ex; -$WindowHeaderSize: 24ex; +$WindowHeaderSize: 15.6ex; +$WindowHeaderHoverColor: lighten($BorderColor, 14%); // #e8e8e8 // STYLES ------------------------------------------------- * { @@ -122,7 +123,11 @@ QSplitter::handle:pressed { QLabel#BlocksTreeWidget_HintLabel { color: gray; - font-size: 13pt; + font-size: 12pt; +} + +QLabel#BlocksTreeWidget_HintLabel[hovered=true] { + color: black; } /* ****************************************************************************************************************** */ @@ -407,9 +412,10 @@ QMenu::indicator:exclusive:checked:disabled { /* ****************************************************************************************************************** */ QHeaderView::section { height: 15.3ex; - width: 60ex; - min-width: 40ex; + min-height: 15.3ex; + max-height: 15.3ex; background: #eeeeee; + /*{lin}font-weight: bold;*/ } /* ****************************************************************************************************************** */ @@ -548,8 +554,8 @@ WindowHeader { } QPushButton#WindowHeader_MinButton:hover { - background-color: lighten($BorderColor, 5%); - border-bottom: 1px solid lighten($BorderColor, 5%); + background-color: $WindowHeaderHoverColor; + border-bottom: 1px solid $WindowHeaderHoverColor; } QPushButton#WindowHeader_MinButton:pressed { @@ -568,8 +574,8 @@ WindowHeader { } QPushButton#WindowHeader_MaxButton:hover { - background-color: lighten($BorderColor, 5%); - border-bottom: 1px solid lighten($BorderColor, 5%); + background-color: $WindowHeaderHoverColor; + border-bottom: 1px solid $WindowHeaderHoverColor; } QPushButton#WindowHeader_MaxButton:pressed { diff --git a/profiler_gui/thread_pool.cpp b/profiler_gui/thread_pool.cpp index 588afbe..50958e7 100644 --- a/profiler_gui/thread_pool.cpp +++ b/profiler_gui/thread_pool.cpp @@ -8,7 +8,7 @@ * description : The file contains implementation of ThreadPool. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/thread_pool.h b/profiler_gui/thread_pool.h index 1f4968d..636e39f 100644 --- a/profiler_gui/thread_pool.h +++ b/profiler_gui/thread_pool.h @@ -8,7 +8,7 @@ * description : The file contains declaration of ThreadPool. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/thread_pool_task.cpp b/profiler_gui/thread_pool_task.cpp index edf743a..031ec61 100644 --- a/profiler_gui/thread_pool_task.cpp +++ b/profiler_gui/thread_pool_task.cpp @@ -8,7 +8,7 @@ * description : The file contains implementation of ThreadPoolTask. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -51,22 +51,25 @@ #include "thread_pool_task.h" #include "thread_pool.h" -ThreadPoolTask::ThreadPoolTask() : m_func([]{}), m_interrupt(nullptr) +static std::atomic_bool s_dummy_flag {false}; + +ThreadPoolTask::ThreadPoolTask() : m_func([] {}), m_interrupt(&s_dummy_flag) { m_status = static_cast(TaskStatus::Finished); } ThreadPoolTask::~ThreadPoolTask() { + m_signals.disconnect(); dequeue(); } -void ThreadPoolTask::enqueue(std::function&& func, std::atomic_bool& interruptFlag) +void ThreadPoolTask::enqueue(Func&& func, std::atomic_bool& interruptFlag) { dequeue(); setStatus(TaskStatus::Enqueued); - m_interrupt = & interruptFlag; + m_interrupt = &interruptFlag; m_interrupt->store(false, std::memory_order_release); m_func = std::move(func); @@ -75,19 +78,22 @@ void ThreadPoolTask::enqueue(std::function&& func, std::atomic_bool& int void ThreadPoolTask::dequeue() { - if (m_interrupt == nullptr || status() == TaskStatus::Finished) + if (m_interrupt == nullptr || m_interrupt == &s_dummy_flag || status() == TaskStatus::Finished) + { return; + } m_interrupt->store(true, std::memory_order_release); ThreadPool::instance().dequeue(*this); // wait for finish - m_mutex.lock(); - setStatus(TaskStatus::Finished); - m_mutex.unlock(); + { + const std::lock_guard guard(m_mutex); + setStatus(TaskStatus::Finished); + } - m_interrupt->store(false, std::memory_order_release); + //m_interrupt->store(false, std::memory_order_release); } TaskStatus ThreadPoolTask::status() const @@ -97,16 +103,29 @@ TaskStatus ThreadPoolTask::status() const void ThreadPoolTask::execute() { - const std::lock_guard guard(m_mutex); + // execute if not cancelled + { + const std::lock_guard guard(m_mutex); - if (status() == TaskStatus::Finished || m_interrupt->load(std::memory_order_acquire)) - return; + if (status() == TaskStatus::Finished || m_interrupt->load(std::memory_order_acquire)) + { + // cancelled + return; + } - m_func(); - setStatus(TaskStatus::Finished); + m_func(); + setStatus(TaskStatus::Finished); + } + + emit m_signals.finished(); } void ThreadPoolTask::setStatus(TaskStatus status) { m_status.store(static_cast(status), std::memory_order_release); } + +ThreadPoolTaskSignals& ThreadPoolTask::events() +{ + return m_signals; +} diff --git a/profiler_gui/thread_pool_task.h b/profiler_gui/thread_pool_task.h index 42ca13d..fb26238 100644 --- a/profiler_gui/thread_pool_task.h +++ b/profiler_gui/thread_pool_task.h @@ -8,7 +8,7 @@ * description : The file contains declaration of ThreadPoolTask. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -56,6 +56,8 @@ #include #include +#include + enum class TaskStatus : int8_t { Enqueued, @@ -63,14 +65,34 @@ enum class TaskStatus : int8_t Finished, }; +class ThreadPoolTaskSignals : public QObject +{ + Q_OBJECT; + +public: + + ThreadPoolTaskSignals() : QObject() {} + +signals: + + void finished(); +}; + class ThreadPoolTask EASY_FINAL { +public: + + using Func = std::function; + +private: + friend class ThreadPool; - std::function m_func; - std::mutex m_mutex; - std::atomic m_status; - std::atomic_bool* m_interrupt; + ThreadPoolTaskSignals m_signals; + Func m_func; + std::atomic_bool* m_interrupt; + std::mutex m_mutex; + std::atomic m_status; public: @@ -80,9 +102,11 @@ public: ThreadPoolTask(); ~ThreadPoolTask(); - void enqueue(std::function&& func, std::atomic_bool& interruptFlag); + void enqueue(Func&& func, std::atomic_bool& interruptFlag); void dequeue(); + ThreadPoolTaskSignals& events(); + private: void execute(); diff --git a/profiler_gui/timer.cpp b/profiler_gui/timer.cpp index e94ab1b..36fba90 100644 --- a/profiler_gui/timer.cpp +++ b/profiler_gui/timer.cpp @@ -13,7 +13,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/timer.h b/profiler_gui/timer.h index 68f7c59..c0cfeda 100644 --- a/profiler_gui/timer.h +++ b/profiler_gui/timer.h @@ -13,7 +13,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) diff --git a/profiler_gui/tree_widget_item.cpp b/profiler_gui/tree_widget_item.cpp index 1ba1620..2d1caf7 100644 --- a/profiler_gui/tree_widget_item.cpp +++ b/profiler_gui/tree_widget_item.cpp @@ -13,7 +13,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -54,32 +54,39 @@ ************************************************************************/ #include "tree_widget_item.h" -#include "globals.h" + +#include +#include #include #include #include #include #include +#include #include +#include "globals.h" +#include "blocks_tree_widget.h" +#include "text_highlighter.h" + ////////////////////////////////////////////////////////////////////////// EASY_CONSTEXPR int BlockColorRole = Qt::UserRole + 1; -////////////////////////////////////////////////////////////////////////// +namespace { EASY_CONSTEXPR int ColumnBit[COL_COLUMNS_NUMBER] = { -1 // COL_NAME = 0, , 0 // COL_BEGIN, - , 1 // COL_DURATION, - , 2 // COL_SELF_DURATION, - , 3 // COL_DURATION_SUM_PER_PARENT, - , 4 // COL_DURATION_SUM_PER_FRAME, - , 5 // COL_DURATION_SUM_PER_THREAD, + , 1 // COL_TIME, + , 2 // COL_SELF_TIME, + , 3 // COL_TOTAL_TIME_PER_PARENT, + , 4 // COL_TOTAL_TIME_PER_FRAME, + , 5 // COL_TOTAL_TIME_PER_THREAD, - , -1 // COL_SELF_DURATION_PERCENT, + , -1 // COL_SELF_TIME_PERCENT, , -1 // COL_PERCENT_PER_PARENT, , -1 // COL_PERCENT_PER_FRAME, , -1 // COL_PERCENT_SUM_PER_PARENT, @@ -90,30 +97,41 @@ EASY_CONSTEXPR int ColumnBit[COL_COLUMNS_NUMBER] = { , 7 // COL_MIN_PER_FRAME, , 8 // COL_MAX_PER_FRAME, - , 9 // COL_AVERAGE_PER_FRAME, + , 9 // COL_AVG_PER_FRAME, , -1 // COL_NCALLS_PER_FRAME, , 10 // COL_MIN_PER_THREAD, , 11 // COL_MAX_PER_THREAD, - , 12 // COL_AVERAGE_PER_THREAD, + , 12 // COL_AVG_PER_THREAD, , -1 // COL_NCALLS_PER_THREAD, , 13 // COL_MIN_PER_PARENT, , 14 // COL_MAX_PER_PARENT, - , 15 // COL_AVERAGE_PER_PARENT, + , 15 // COL_AVG_PER_PARENT, , -1 // COL_NCALLS_PER_PARENT, , 16 // COL_ACTIVE_TIME, , -1 // COL_ACTIVE_PERCENT, + + , -1 // COL_PERCENT_PER_AREA, + , 17 // COL_TOTAL_TIME_PER_AREA, + , -1 // COL_PERCENT_SUM_PER_AREA, + , 18 // COL_MIN_PER_AREA, + , 19 // COL_MAX_PER_AREA, + , 20 // COL_AVG_PER_AREA, + , -1 // COL_NCALLS_PER_AREA, }; +} // end of namespace . + ////////////////////////////////////////////////////////////////////////// -TreeWidgetItem::TreeWidgetItem(const profiler::block_index_t _treeBlock, Parent* _parent) +TreeWidgetItem::TreeWidgetItem(profiler::block_index_t _treeBlock, Parent* _parent) : Parent(_parent, QTreeWidgetItem::UserType) , m_block(_treeBlock) , m_customBGColor(0) , m_bMain(false) + , m_partial(false) { } @@ -139,16 +157,19 @@ bool TreeWidgetItem::operator < (const Parent& _other) const case COL_NCALLS_PER_THREAD: case COL_NCALLS_PER_PARENT: case COL_NCALLS_PER_FRAME: + case COL_NCALLS_PER_AREA: { return data(col, Qt::UserRole).toUInt() < _other.data(col, Qt::UserRole).toUInt(); } - case COL_SELF_DURATION_PERCENT: + case COL_SELF_TIME_PERCENT: case COL_PERCENT_PER_PARENT: case COL_PERCENT_PER_FRAME: case COL_PERCENT_SUM_PER_PARENT: case COL_PERCENT_SUM_PER_FRAME: case COL_PERCENT_SUM_PER_THREAD: + case COL_PERCENT_PER_AREA: + case COL_PERCENT_SUM_PER_AREA: { return data(col, Qt::UserRole).toInt() < _other.data(col, Qt::UserRole).toInt(); } @@ -166,6 +187,11 @@ bool TreeWidgetItem::operator < (const Parent& _other) const } } +bool TreeWidgetItem::isPartial() const +{ + return m_partial; +} + bool TreeWidgetItem::hasToolTip(int _column) const { const int bit = ColumnBit[_column]; @@ -194,16 +220,143 @@ QVariant TreeWidgetItem::data(int _column, int _role) const switch (_role) { case Qt::ForegroundRole: - return m_bMain ? QVariant::fromValue(QColor::fromRgb(profiler_gui::SELECTED_THREAD_FOREGROUND)) : QVariant(); + { + if (m_bMain) + return QVariant::fromValue(QColor::fromRgb(profiler_gui::SELECTED_THREAD_FOREGROUND)); + auto fg = Parent::data(_column, _role); + if (!fg.isNull()) + return fg; + if (m_partial) + return partialForeground(); + break; + } case Qt::ToolTipRole: - return hasToolTip(_column) ? - QVariant::fromValue(QString("%1 ns").arg(QTreeWidgetItem::data(_column, Qt::UserRole).toULongLong())) : - QVariant(); + { + if (hasToolTip(_column)) + return QVariant::fromValue(QString("%1 ns").arg(data(_column, Qt::UserRole).toULongLong())); + break; + } default: - return QTreeWidgetItem::data(_column, _role); + { + if (_role != Qt::UserRole && _role != Qt::DisplayRole) + return QTreeWidgetItem::data(_column, _role); + return relevantData(_column, _role); + } } + + return QVariant(); +} + +QVariant TreeWidgetItem::relevantData(int _column, int _role) const +{ + switch (_column) + { + case COL_NAME: + case COL_BEGIN: + case COL_END: + case COL_NCALLS_PER_PARENT: + case COL_NCALLS_PER_FRAME: + case COL_NCALLS_PER_THREAD: + case COL_NCALLS_PER_AREA: + case COL_SELF_TIME: + case COL_SELF_TIME_PERCENT: + case COL_ACTIVE_TIME: + case COL_ACTIVE_PERCENT: + case COL_PERCENT_PER_PARENT: + case COL_PERCENT_PER_FRAME: + case COL_PERCENT_PER_AREA: + case COL_PERCENT_SUM_PER_THREAD: + { + return Parent::data(_column, _role); + } + + default: + { + break; + } + } + + auto var = Parent::data(_column, _role); + if (!var.isNull() || (EASY_GLOBALS.display_only_relevant_stats && _role == Qt::DisplayRole)) + { + return var; + } + + switch (_column) + { + case COL_TOTAL_TIME_PER_PARENT: + case COL_TOTAL_TIME_PER_FRAME: + case COL_TOTAL_TIME_PER_THREAD: + case COL_TOTAL_TIME_PER_AREA: + case COL_MIN_PER_PARENT: + case COL_MIN_PER_FRAME: + case COL_MIN_PER_THREAD: + case COL_MIN_PER_AREA: + case COL_MAX_PER_PARENT: + case COL_MAX_PER_FRAME: + case COL_MAX_PER_THREAD: + case COL_MAX_PER_AREA: + case COL_AVG_PER_PARENT: + case COL_AVG_PER_FRAME: + case COL_AVG_PER_THREAD: + case COL_AVG_PER_AREA: + { + return Parent::data(COL_TIME, _role); + } + + case COL_PERCENT_SUM_PER_PARENT: + { + return Parent::data(COL_PERCENT_PER_PARENT, _role); + } + + case COL_PERCENT_SUM_PER_FRAME: + { + return Parent::data(COL_PERCENT_PER_FRAME, _role); + } + + case COL_PERCENT_SUM_PER_AREA: + { + return Parent::data(COL_PERCENT_PER_AREA, _role); + } + + default: + { + break; + } + } + + return var; +} + +QVariant TreeWidgetItem::partialForeground() const +{ + const auto mode = static_cast(treeWidget())->mode(); + profiler::calls_number_t ncalls = 0; + + switch (mode) + { + case TreeMode::Plain: + { + ncalls = data(COL_NCALLS_PER_FRAME, Qt::UserRole).toUInt(); + break; + } + + case TreeMode::SelectedArea: + { + ncalls = data(COL_NCALLS_PER_AREA, Qt::UserRole).toUInt(); + break; + } + + default: + break; + } + + if (ncalls < 3) + return QVariant::fromValue(QColor::fromRgb(profiler::colors::Grey500)); + + return QVariant::fromValue(QColor::fromRgb(profiler::colors::Grey700)); } profiler::block_index_t TreeWidgetItem::block_index() const @@ -221,16 +374,26 @@ const profiler::BlocksTree& TreeWidgetItem::block() const return easyBlocksTree(m_block); } +profiler::thread_id_t TreeWidgetItem::threadId() const +{ + const QTreeWidgetItem* parentItem = this; + while (parentItem->parent() != nullptr) + { + parentItem = parent(); + } + return static_cast(parentItem->data(COL_NAME, Qt::UserRole).toULongLong()); +} + profiler::timestamp_t TreeWidgetItem::duration() const { if (parent() != nullptr) return block().node->duration(); - return data(COL_DURATION, Qt::UserRole).toULongLong(); + return data(COL_TIME, Qt::UserRole).toULongLong(); } profiler::timestamp_t TreeWidgetItem::selfDuration() const { - return data(COL_SELF_DURATION, Qt::UserRole).toULongLong(); + return data(COL_SELF_TIME, Qt::UserRole).toULongLong(); } void TreeWidgetItem::setTimeSmart(int _column, profiler_gui::TimeUnits _units, const profiler::timestamp_t& _time, const QString& _prefix) @@ -240,23 +403,6 @@ void TreeWidgetItem::setTimeSmart(int _column, profiler_gui::TimeUnits _units, c setData(_column, Qt::UserRole, (quint64)nanosecondsTime); setHasToolTip(_column); setText(_column, QString("%1%2").arg(_prefix).arg(profiler_gui::timeStringRealNs(_units, nanosecondsTime, 3))); - -// if (_time < 1e3) -// { -// setText(_column, QString("%1%2 ns").arg(_prefix).arg(nanosecondsTime)); -// } -// else if (_time < 1e6) -// { -// setText(_column, QString("%1%2 us").arg(_prefix).arg(double(nanosecondsTime) * 1e-3, 0, 'f', 3)); -// } -// else if (_time < 1e9) -// { -// setText(_column, QString("%1%2 ms").arg(_prefix).arg(double(nanosecondsTime) * 1e-6, 0, 'f', 3)); -// } -// else -// { -// setText(_column, QString("%1%2 s").arg(_prefix).arg(double(nanosecondsTime) * 1e-9, 0, 'f', 3)); -// } } void TreeWidgetItem::setTimeSmart(int _column, profiler_gui::TimeUnits _units, const profiler::timestamp_t& _time) @@ -294,6 +440,11 @@ void TreeWidgetItem::setMain(bool _main) m_bMain = _main; } +void TreeWidgetItem::setPartial(bool partial) +{ + m_partial = partial; +} + void TreeWidgetItem::collapseAll() { for (int i = 0, childrenNumber = childCount(); i < childrenNumber; ++i) @@ -320,7 +471,9 @@ void TreeWidgetItem::expandAll() ////////////////////////////////////////////////////////////////////////// -TreeWidgetItemDelegate::TreeWidgetItemDelegate(QTreeWidget* parent) : QStyledItemDelegate(parent), m_treeWidget(parent) +TreeWidgetItemDelegate::TreeWidgetItemDelegate(BlocksTreeWidget* parent) + : QStyledItemDelegate(parent) + , m_treeWidget(parent) { } @@ -337,9 +490,11 @@ void TreeWidgetItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem { // Draw item as usual QStyledItemDelegate::paint(painter, option, index); + highlightMatchingText(painter, option, index); return; } + const auto padding = px(2); const auto colorBlockSize = option.rect.height() >> 1; const auto currentTreeIndex = m_treeWidget->currentIndex(); if (index.parent() == currentTreeIndex.parent() && index.row() == currentTreeIndex.row()) @@ -348,16 +503,17 @@ void TreeWidgetItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem painter->save(); painter->setBrush(m_treeWidget->palette().highlight()); painter->setPen(Qt::NoPen); - painter->drawRect(QRect(option.rect.left(), option.rect.top(), colorBlockSize, option.rect.height())); + painter->drawRect(QRect(option.rect.left(), option.rect.top(), colorBlockSize + padding, option.rect.height())); painter->restore(); } // Adjust rect size for drawing color marker QStyleOptionViewItem opt = option; - opt.rect.adjust(colorBlockSize, 0, 0, 0); + opt.rect.adjust(colorBlockSize + padding, 0, 0, 0); // Draw item as usual QStyledItemDelegate::paint(painter, opt, index); + highlightMatchingText(painter, opt, index); const auto colorBlockRest = option.rect.height() - colorBlockSize; @@ -374,7 +530,117 @@ void TreeWidgetItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem const auto bottomLeft = opt.rect.bottomLeft(); painter->setBrush(Qt::NoBrush); painter->setPen(profiler_gui::SYSTEM_BORDER_COLOR); - painter->drawLine(QPoint(bottomLeft.x() - colorBlockSize, bottomLeft.y()), bottomLeft); + painter->drawLine(QPoint(bottomLeft.x() - colorBlockSize - padding, bottomLeft.y()), bottomLeft); painter->restore(); } + +void TreeWidgetItemDelegate::highlightMatchingText( + QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index +) const { + if (m_treeWidget->lastFoundItem() != nullptr && !m_treeWidget->searchString().isEmpty()) + { + // Highlight matching word + auto displayData = m_treeWidget->model()->data(index); + if (displayData.canConvert()) + { + const auto text = displayData.toString(); + const auto caseSensitivity = m_treeWidget->caseSensitiveSearch() ? Qt::CaseSensitive : Qt::CaseInsensitive; + if (text.contains(m_treeWidget->searchString(), caseSensitivity)) + { + auto lastFoundIndex = m_treeWidget->indexFromItem(m_treeWidget->lastFoundItem(), index.column()); + highlightMatchingText( + painter, + option, + text, + m_treeWidget->searchString(), + caseSensitivity, + lastFoundIndex == index + ); + } + } + } +} + +void TreeWidgetItemDelegate::highlightMatchingText( + QPainter* painter, + const QStyleOptionViewItem& option, + const QString& text, + const QString& pattern, + Qt::CaseSensitivity caseSensitivity, + bool current +) const { + const auto padding = px(2); + + QTextDocument doc; + doc.setDefaultFont(painter->font()); + doc.setTextWidth(option.rect.width() - padding); + + const auto elidedText = painter->fontMetrics().elidedText(text, Qt::ElideRight, std::max(option.rect.width() - padding, 0)); + doc.setHtml(elidedText); + + TextHighlighter highlighter( + &doc, + painter->pen().color(), + QColor::fromRgb(profiler::colors::Grey100), + pattern, + caseSensitivity, + current + ); + + painter->save(); + +#ifdef _WIN32 + EASY_CONSTEXPR int fixed_padding_x = -1; + EASY_CONSTEXPR int fixed_padding_y = 0; +#else + EASY_CONSTEXPR int fixed_padding_x = -1; + EASY_CONSTEXPR int fixed_padding_y = -1; +#endif + + auto dh = std::max((option.rect.height() - doc.size().height()) * 0.5, 0.); + painter->translate(option.rect.left() + fixed_padding_x, option.rect.top() + dh + fixed_padding_y); + + QRect clip(0, 0, option.rect.width(), option.rect.height()); + painter->setClipRect(clip); + + QAbstractTextDocumentLayout::PaintContext ctx; + ctx.clip = clip; + ctx.palette.setColor(QPalette::Text, Qt::transparent); + doc.documentLayout()->draw(painter, ctx); + + painter->restore(); +} + +QSize TreeWidgetItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + auto* tree = m_treeWidget; + if (tree == nullptr) + return QStyledItemDelegate::sizeHint(option, index); + + auto* model = tree->model(); + if (model == nullptr) + return QStyledItemDelegate::sizeHint(option, index); + + auto displayData = m_treeWidget->model()->data(index); + if (!displayData.canConvert()) + { + return QStyledItemDelegate::sizeHint(option, index); + } + + // unfortunately, Qt does not take padding into account, so have to add it manually... + const auto padding = px(15); + auto text = displayData.toString(); + const auto width = static_cast((m_treeWidget->fontMetrics().width(text) + padding) * 1.05); + + const auto brushData = m_treeWidget->model()->data(index, BlockColorRole); + if (brushData.isNull()) + { + return QSize(width, option.rect.height()); + } + + const auto colorBlockSize = option.rect.height() >> 1; + return QSize(width + colorBlockSize + px(2), option.rect.height()); +} diff --git a/profiler_gui/tree_widget_item.h b/profiler_gui/tree_widget_item.h index 9432e08..9be3aa0 100644 --- a/profiler_gui/tree_widget_item.h +++ b/profiler_gui/tree_widget_item.h @@ -14,7 +14,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -65,6 +65,8 @@ #include "common_functions.h" +class BlocksTreeWidget; + ////////////////////////////////////////////////////////////////////////// enum EasyColumnsIndexes @@ -75,13 +77,13 @@ enum EasyColumnsIndexes COL_BEGIN, - COL_DURATION, - COL_SELF_DURATION, - COL_DURATION_SUM_PER_PARENT, - COL_DURATION_SUM_PER_FRAME, - COL_DURATION_SUM_PER_THREAD, + COL_TIME, + COL_SELF_TIME, + COL_TOTAL_TIME_PER_PARENT, + COL_TOTAL_TIME_PER_FRAME, + COL_TOTAL_TIME_PER_THREAD, - COL_SELF_DURATION_PERCENT, + COL_SELF_TIME_PERCENT, COL_PERCENT_PER_PARENT, COL_PERCENT_PER_FRAME, COL_PERCENT_SUM_PER_PARENT, @@ -92,22 +94,30 @@ enum EasyColumnsIndexes COL_MIN_PER_FRAME, COL_MAX_PER_FRAME, - COL_AVERAGE_PER_FRAME, + COL_AVG_PER_FRAME, COL_NCALLS_PER_FRAME, COL_MIN_PER_THREAD, COL_MAX_PER_THREAD, - COL_AVERAGE_PER_THREAD, + COL_AVG_PER_THREAD, COL_NCALLS_PER_THREAD, COL_MIN_PER_PARENT, COL_MAX_PER_PARENT, - COL_AVERAGE_PER_PARENT, + COL_AVG_PER_PARENT, COL_NCALLS_PER_PARENT, COL_ACTIVE_TIME, COL_ACTIVE_PERCENT, + COL_PERCENT_PER_AREA, + COL_TOTAL_TIME_PER_AREA, + COL_PERCENT_SUM_PER_AREA, + COL_MIN_PER_AREA, + COL_MAX_PER_AREA, + COL_AVG_PER_AREA, + COL_NCALLS_PER_AREA, + COL_COLUMNS_NUMBER }; @@ -120,12 +130,13 @@ class TreeWidgetItem : public QTreeWidgetItem const profiler::block_index_t m_block; QRgb m_customBGColor; - std::bitset<17> m_bHasToolTip; + std::bitset<21> m_bHasToolTip; bool m_bMain; + bool m_partial; public: - explicit TreeWidgetItem(const profiler::block_index_t _treeBlock = profiler_gui::numeric_max() + explicit TreeWidgetItem(profiler::block_index_t _treeBlock = profiler_gui::numeric_max() , Parent* _parent = nullptr); ~TreeWidgetItem() override; @@ -135,6 +146,7 @@ public: public: + bool isPartial() const; bool hasToolTip(int _column) const; profiler::block_index_t block_index() const; profiler_gui::EasyBlock& guiBlock(); @@ -143,6 +155,8 @@ public: profiler::timestamp_t duration() const; profiler::timestamp_t selfDuration() const; + profiler::thread_id_t threadId() const; + void setTimeSmart(int _column, profiler_gui::TimeUnits _units, const profiler::timestamp_t& _time, const QString& _prefix); void setTimeSmart(int _column, profiler_gui::TimeUnits _units, const profiler::timestamp_t& _time); @@ -152,6 +166,7 @@ public: void setBackgroundColor(QRgb _color); void setMain(bool _main); + void setPartial(bool partial); void collapseAll(); @@ -160,6 +175,8 @@ public: private: void setHasToolTip(int _column); + QVariant relevantData(int _column, int _role) const; + QVariant partialForeground() const; }; // END of class TreeWidgetItem. @@ -168,13 +185,27 @@ private: class TreeWidgetItemDelegate : public QStyledItemDelegate { Q_OBJECT - QTreeWidget* m_treeWidget; + BlocksTreeWidget* m_treeWidget; public: - explicit TreeWidgetItemDelegate(QTreeWidget* parent = nullptr); + explicit TreeWidgetItemDelegate(BlocksTreeWidget* parent = nullptr); ~TreeWidgetItemDelegate() override; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + +private: + + void highlightMatchingText(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + + void highlightMatchingText( + QPainter* painter, + const QStyleOptionViewItem& option, + const QString& text, + const QString& pattern, + Qt::CaseSensitivity caseSensitivity, + bool current + ) const; }; // END of class TreeWidgetItemDelegate. diff --git a/profiler_gui/tree_widget_loader.cpp b/profiler_gui/tree_widget_loader.cpp index a49d402..f50a630 100644 --- a/profiler_gui/tree_widget_loader.cpp +++ b/profiler_gui/tree_widget_loader.cpp @@ -14,7 +14,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -75,6 +75,112 @@ #define EASY_INIT_ATOMIC(v) {v} #endif +struct ThreadData +{ + StatsMap stats; + IdItems iditems; + TreeWidgetItem* item = nullptr; +}; + +using ThreadDataMap = std::unordered_map >; + +static void fillStatsColumns( + TreeWidgetItem* item, + const profiler::BlockStatistics* stats, + profiler_gui::TimeUnits units, + int min_column, + int max_column, + int avg_column, + int total_column, + int n_calls_column +) { + item->setData(n_calls_column, Qt::UserRole, stats->calls_number); + item->setText(n_calls_column, QString::number(stats->calls_number)); + + if (stats->calls_number < 2)// && EASY_GLOBALS.display_only_relevant_stats) + { + return; + } + + const auto min_duration = easyBlock(stats->min_duration_block).tree.node->duration(); + const auto max_duration = easyBlock(stats->max_duration_block).tree.node->duration(); + const auto avg_duration = stats->average_duration(); + const auto tot_duration = stats->total_duration; + + item->setTimeSmart(min_column, units, min_duration); + item->setTimeSmart(max_column, units, max_duration); + item->setTimeSmart(avg_column, units, avg_duration); + item->setTimeSmart(total_column, units, tot_duration); + + if (stats->calls_number > 1 && tot_duration != 0) + { + if (max_duration >= (tot_duration >> 1)) + { + item->setForeground(max_column, QColor::fromRgb(profiler::colors::Red500)); + } + else if (max_duration >= (tot_duration >> 2)) + { + item->setForeground(max_column, QColor::fromRgb(profiler::colors::Red700)); + } + } +} + +inline void fillStatsColumnsThread(TreeWidgetItem* item, const profiler::BlockStatistics* stats, profiler_gui::TimeUnits units) +{ + fillStatsColumns( + item, + stats, + units, + COL_MIN_PER_THREAD, + COL_MAX_PER_THREAD, + COL_AVG_PER_THREAD, + COL_TOTAL_TIME_PER_THREAD, + COL_NCALLS_PER_THREAD + ); +} + +inline void fillStatsColumnsFrame(TreeWidgetItem* item, const profiler::BlockStatistics* stats, profiler_gui::TimeUnits units) +{ + fillStatsColumns( + item, + stats, + units, + COL_MIN_PER_FRAME, + COL_MAX_PER_FRAME, + COL_AVG_PER_FRAME, + COL_TOTAL_TIME_PER_FRAME, + COL_NCALLS_PER_FRAME + ); +} + +inline void fillStatsColumnsParent(TreeWidgetItem* item, const profiler::BlockStatistics* stats, profiler_gui::TimeUnits units) +{ + fillStatsColumns( + item, + stats, + units, + COL_MIN_PER_PARENT, + COL_MAX_PER_PARENT, + COL_AVG_PER_PARENT, + COL_TOTAL_TIME_PER_PARENT, + COL_NCALLS_PER_PARENT + ); +} + +inline void fillStatsColumnsSelection(TreeWidgetItem* item, const profiler::BlockStatistics* stats, profiler_gui::TimeUnits units) +{ + fillStatsColumns( + item, + stats, + units, + COL_MIN_PER_AREA, + COL_MAX_PER_AREA, + COL_AVG_PER_AREA, + COL_TOTAL_TIME_PER_AREA, + COL_NCALLS_PER_AREA + ); +} + TreeWidgetLoader::TreeWidgetLoader() : m_bDone(EASY_INIT_ATOMIC(false)) , m_bInterrupt(EASY_INIT_ATOMIC(false)) @@ -104,7 +210,7 @@ void TreeWidgetLoader::setProgress(int _progress) m_progress.store(_progress, std::memory_order_release); } -bool TreeWidgetLoader::interrupted() const +bool TreeWidgetLoader::interrupted() const volatile { return m_bInterrupt.load(std::memory_order_acquire); } @@ -118,7 +224,7 @@ void TreeWidgetLoader::takeTopLevelItems(ThreadedItems& _output) { if (done()) { - _output = ::std::move(m_topLevelItems); + _output = std::move(m_topLevelItems); m_topLevelItems.clear(); } } @@ -127,7 +233,7 @@ void TreeWidgetLoader::takeItems(Items& _output) { if (done()) { - _output = ::std::move(m_items); + _output = std::move(m_items); m_items.clear(); } } @@ -157,27 +263,16 @@ void TreeWidgetLoader::interrupt(bool _wait) m_items.clear(); m_topLevelItems.clear(); - m_iditems.clear(); } -void TreeWidgetLoader::fillTree(::profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree, TreeMode _mode) -{ - interrupt(); - m_mode = _mode; - - const auto zeroBlocks = EASY_GLOBALS.add_zero_blocks_to_hierarchy; - const auto decoratedNames = EASY_GLOBALS.use_decorated_thread_name; - const auto hexThreadIds = EASY_GLOBALS.hex_thread_id; - const auto timeUnits = EASY_GLOBALS.time_units; - const auto beginTime = std::ref(_beginTime); - const auto blocksTree = std::ref(_blocksTree); - m_worker.enqueue([=] { - setTreeInternal1(beginTime, _blocksNumber, blocksTree, zeroBlocks, decoratedNames, hexThreadIds, timeUnits); - }, m_bInterrupt); -} - -void TreeWidgetLoader::fillTreeBlocks(const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _beginTime, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, TreeMode _mode) -{ +void TreeWidgetLoader::fillTreeBlocks( + const profiler_gui::TreeBlocks& _blocks, + profiler::timestamp_t _beginTime, + profiler::timestamp_t _left, + profiler::timestamp_t _right, + bool _strict, + TreeMode _mode +) { interrupt(); m_mode = _mode; @@ -186,83 +281,32 @@ void TreeWidgetLoader::fillTreeBlocks(const ::profiler_gui::TreeBlocks& _blocks, const auto hexThreadIds = EASY_GLOBALS.hex_thread_id; const auto timeUnits = EASY_GLOBALS.time_units; const auto blocks = std::ref(_blocks); + const auto mode = m_mode; m_worker.enqueue([=] { - setTreeInternal2(_beginTime, blocks, _left, _right, _strict, zeroBlocks, decoratedNames, hexThreadIds, timeUnits); + switch (mode) + { + case TreeMode::Full: + { + setTreeInternalTop(_beginTime, blocks, _left, _right, _strict, zeroBlocks, decoratedNames, hexThreadIds, timeUnits); + break; + } + case TreeMode::Plain: + { + setTreeInternalPlainTop(_beginTime, blocks, _left, _right, _strict, zeroBlocks, decoratedNames, hexThreadIds, timeUnits); + break; + } + case TreeMode::SelectedArea: + { + setTreeInternalAggregateTop(_beginTime, blocks, _left, _right, _strict, zeroBlocks, decoratedNames, hexThreadIds, timeUnits); + break; + } + } }, m_bInterrupt); } ////////////////////////////////////////////////////////////////////////// -void TreeWidgetLoader::setTreeInternal1(::profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree, bool _addZeroBlocks, bool _decoratedThreadNames, bool _hexThreadId, ::profiler_gui::TimeUnits _units) -{ - m_items.reserve(_blocksNumber + _blocksTree.size()); // _blocksNumber does not include Thread root blocks - - ::profiler::timestamp_t finishtime = 0; - for (const auto& threadTree : _blocksTree) - { - const auto node_block = easyBlocksTree(threadTree.second.children.front()).node; - const auto startTime = node_block->begin(); - const auto endTime = node_block->end(); - - if (_beginTime > startTime) - _beginTime = startTime; - - if (finishtime < endTime) - finishtime = endTime; - } - - //const QSignalBlocker b(this); - const auto u_thread = ::profiler_gui::toUnicode("thread"); - int i = 0; - const int total = static_cast(_blocksTree.size()); - for (const auto& threadTree : _blocksTree) - { - if (interrupted()) - break; - - const auto& root = threadTree.second; - auto item = new TreeWidgetItem(); - item->setText(COL_NAME, ::profiler_gui::decoratedThreadName(_decoratedThreadNames, root, u_thread, _hexThreadId)); - - ::profiler::timestamp_t duration = 0; - if (!root.children.empty()) - duration = easyBlocksTree(root.children.back()).node->end() - easyBlocksTree(root.children.front()).node->begin(); - - item->setTimeSmart(COL_DURATION, _units, duration); - item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND); - - //_items.push_back(item); - - item->setTimeSmart(COL_SELF_DURATION, _units, root.profiled_time); - - ::profiler::timestamp_t children_duration = 0; - const auto children_items_number = setTreeInternal(root, 0, _beginTime, root.children, item, nullptr, - _beginTime, finishtime + 1000000000ULL, false, - children_duration, _addZeroBlocks, _units); - - if (children_items_number > 0) - { - //total_items += children_items_number + 1; - //addTopLevelItem(item); - //m_roots[threadTree.first] = item; - m_topLevelItems.emplace_back(root.thread_id, item); - } - else - { - //_items.pop_back(); - delete item; - } - - setProgress((100 * ++i) / total); - } - - setDone(); - //return total_items; -} - -////////////////////////////////////////////////////////////////////////// - -// auto calculateTotalChildrenNumber(const ::profiler::BlocksTree& _tree) -> decltype(_tree.children.size()) +// auto calculateTotalChildrenNumber(const profiler::BlocksTree& _tree) -> decltype(_tree.children.size()) // { // auto children_number = _tree.children.size(); // for (auto i : _tree.children) @@ -270,124 +314,106 @@ void TreeWidgetLoader::setTreeInternal1(::profiler::timestamp_t& _beginTime, con // return children_number; // } -using BeginEndIndicesMap = ::std::unordered_map<::profiler::thread_id_t, ::profiler::block_index_t, - ::estd::hash<::profiler::thread_id_t> >; - -void TreeWidgetLoader::setTreeInternal2(const ::profiler::timestamp_t& _beginTime, - const ::profiler_gui::TreeBlocks& _blocks, - ::profiler::timestamp_t _left, - ::profiler::timestamp_t _right, - bool _strict, - bool _addZeroBlocks, - bool _decoratedThreadNames, - bool _hexThreadId, - ::profiler_gui::TimeUnits _units) -{ - //size_t blocksNumber = 0; - //for (const auto& block : _blocks) - // blocksNumber += calculateTotalChildrenNumber(*block.tree); - // //blocksNumber += block.tree->total_children_number; - //m_items.reserve(blocksNumber + _blocks.size()); // blocksNumber does not include root blocks +using BeginEndIndicesMap = std::unordered_map >; +void TreeWidgetLoader::setTreeInternalTop( + const profiler::timestamp_t& _beginTime, + const profiler_gui::TreeBlocks& _blocks, + profiler::timestamp_t _left, + profiler::timestamp_t _right, + bool _strict, + bool _addZeroBlocks, + bool _decoratedThreadNames, + bool _hexThreadId, + profiler_gui::TimeUnits _units +) { BeginEndIndicesMap beginEndMap; - RootsMap threadsMap; + ThreadDataMap threadsMap; - auto const setTree = (m_mode == TreeMode::Full) ? &TreeWidgetLoader::setTreeInternal : &TreeWidgetLoader::setTreeInternalPlain; - - const auto u_thread = ::profiler_gui::toUnicode("thread"); + const auto u_thread = profiler_gui::toUnicode("thread"); int i = 0, total = static_cast(_blocks.size()); - //const QSignalBlocker b(this); for (const auto& block : _blocks) { if (interrupted()) break; auto& gui_block = easyBlock(block.tree); - const auto startTime = gui_block.tree.node->begin(); - const auto endTime = gui_block.tree.node->end(); + auto& tree = gui_block.tree; + const auto startTime = tree.node->begin(); + const auto endTime = tree.node->end(); + if (startTime > _right || endTime < _left) { setProgress((95 * ++i) / total); continue; } - ::profiler::timestamp_t duration = 0; - TreeWidgetItem* thread_item = nullptr; - ::profiler::block_index_t& firstCswitch = beginEndMap[block.root->thread_id]; - auto thread_item_it = threadsMap.find(block.root->thread_id); - if (thread_item_it != threadsMap.end()) + const profiler::timestamp_t duration = endTime - startTime; + + const bool partial = _strict && (startTime < _left || endTime > _right); + if (partial && duration != 0 && (startTime == _right || endTime == _left)) { - thread_item = thread_item_it->second; + setProgress((95 * ++i) / total); + continue; } - else + + auto& thread_data = threadsMap[block.root->thread_id]; + auto thread_item = thread_data.item; + profiler::block_index_t& firstCswitch = beginEndMap[block.root->thread_id]; + StatsMap& stats = thread_data.stats; + IdItems& iditems = thread_data.iditems; + + if (thread_item == nullptr) { thread_item = new TreeWidgetItem(); - thread_item->setText(COL_NAME, ::profiler_gui::decoratedThreadName(_decoratedThreadNames, *block.root, u_thread, _hexThreadId)); + thread_data.item = thread_item; + thread_item->setText(COL_NAME, profiler_gui::decoratedThreadName(_decoratedThreadNames, *block.root, u_thread, _hexThreadId)); + thread_item->setData(COL_NAME, Qt::UserRole, static_cast(block.root->thread_id)); + + profiler::timestamp_t thread_duration = 0; if (!block.root->children.empty()) - duration = easyBlocksTree(block.root->children.back()).node->end() - easyBlocksTree(block.root->children.front()).node->begin(); + thread_duration = easyBlocksTree(block.root->children.back()).node->end() + - easyBlocksTree(block.root->children.front()).node->begin(); - thread_item->setTimeSmart(COL_DURATION, _units, duration); - thread_item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND); + thread_item->setTimeSmart(COL_TIME, _units, thread_duration); + thread_item->setBackgroundColor(profiler_gui::SELECTED_THREAD_BACKGROUND); // Sum of all children durations: - thread_item->setTimeSmart(COL_SELF_DURATION, _units, block.root->profiled_time); - - threadsMap.insert(::std::make_pair(block.root->thread_id, thread_item)); + thread_item->setTimeSmart(COL_SELF_TIME, _units, block.root->profiled_time); firstCswitch = 0; - auto it = ::std::lower_bound(block.root->sync.begin(), block.root->sync.end(), _left, [](::profiler::block_index_t ind, decltype(_left) _val) + auto it = std::lower_bound(block.root->sync.begin(), block.root->sync.end(), _left, [](profiler::block_index_t ind, decltype(_left) _val) { return EASY_GLOBALS.gui_blocks[ind].tree.node->begin() < _val; }); if (it != block.root->sync.end()) { - firstCswitch = it - block.root->sync.begin(); + firstCswitch = static_cast(std::distance(block.root->sync.begin(), it)); if (firstCswitch > 0) --firstCswitch; } else { - firstCswitch = static_cast<::profiler::block_index_t>(block.root->sync.size()); + firstCswitch = static_cast(block.root->sync.size()); } } - bool hasContextSwitch = false; - ::profiler::timestamp_t idleTime = 0; - for (::profiler::block_index_t ind = firstCswitch, ncs = static_cast<::profiler::block_index_t>(block.root->sync.size()); ind < ncs; ++ind) - { - auto cs_index = block.root->sync[ind]; - const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node; - - if (cs->begin() > endTime) - { - if (!hasContextSwitch) - firstCswitch = ind; - break; - } - - if (startTime <= cs->begin() && cs->end() <= endTime) - { - if (!hasContextSwitch) - { - firstCswitch = ind; - hasContextSwitch = true; - } - - idleTime += cs->duration(); - } - } + // Variable firstCswitch modified on each call to calculateIdleTime(). + // Just because all blocks are sorted by startTime, it's OK to modify firstCswitch - this will speed-up future calculateIdleTime calls + const auto idleTime = calculateIdleTime(*block.root, firstCswitch, startTime, endTime); auto item = new TreeWidgetItem(block.tree, thread_item); - duration = endTime - startTime; + item->setPartial(partial); - auto name = *gui_block.tree.node->name() != 0 ? gui_block.tree.node->name() : easyDescriptor(gui_block.tree.node->id()).name(); - item->setText(COL_NAME, ::profiler_gui::toUnicode(name)); - item->setTimeSmart(COL_DURATION, _units, duration); + auto name = *tree.node->name() != 0 ? tree.node->name() : easyDescriptor(tree.node->id()).name(); + item->setText(COL_NAME, profiler_gui::toUnicode(name)); + item->setTimeSmart(COL_TIME, _units, duration); auto active_time = duration - idleTime; - auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration); + auto active_percent = duration == 0 ? 100. : profiler_gui::percentReal(active_time, duration); item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); @@ -397,55 +423,23 @@ void TreeWidgetLoader::setTreeInternal2(const ::profiler::timestamp_t& _beginTim item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0); - auto percentage_per_thread = ::profiler_gui::percent(duration, block.root->profiled_time); + auto percentage_per_thread = profiler_gui::percent(duration, block.root->profiled_time); item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread); item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread)); - if (gui_block.tree.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also + if (tree.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also { - const ::profiler::BlockStatistics* per_thread_stats = gui_block.tree.per_thread_stats; - const ::profiler::BlockStatistics* per_parent_stats = gui_block.tree.per_parent_stats; - const ::profiler::BlockStatistics* per_frame_stats = gui_block.tree.per_frame_stats; + const auto per_thread_stats = tree.per_thread_stats; + const auto per_parent_stats = tree.per_parent_stats; + const auto per_frame_stats = tree.per_frame_stats; + fillStatsColumnsThread(item, per_thread_stats, _units); + fillStatsColumnsParent(item, per_parent_stats, _units); + fillStatsColumnsFrame(item, per_frame_stats, _units); - if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) - { - item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration()); - item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration()); - item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration()); - item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration); - } - - item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number); - item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number)); - - percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, block.root->profiled_time); + percentage_per_thread = profiler_gui::percent(per_thread_stats->total_duration, block.root->profiled_time); item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread); item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread)); - - - if (per_parent_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) - { - item->setTimeSmart(COL_MIN_PER_PARENT, _units, easyBlock(per_parent_stats->min_duration_block).tree.node->duration()); - item->setTimeSmart(COL_MAX_PER_PARENT, _units, easyBlock(per_parent_stats->max_duration_block).tree.node->duration()); - item->setTimeSmart(COL_AVERAGE_PER_PARENT, _units, per_parent_stats->average_duration()); - item->setTimeSmart(COL_DURATION_SUM_PER_PARENT, _units, per_parent_stats->total_duration); - } - - item->setData(COL_NCALLS_PER_PARENT, Qt::UserRole, per_parent_stats->calls_number); - item->setText(COL_NCALLS_PER_PARENT, QString::number(per_parent_stats->calls_number)); - - - if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) - { - item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration()); - item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration()); - item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration()); - item->setTimeSmart(COL_DURATION_SUM_PER_FRAME, _units, per_frame_stats->total_duration); - } - - item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number); - item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number)); } else { @@ -456,23 +450,24 @@ void TreeWidgetLoader::setTreeInternal2(const ::profiler::timestamp_t& _beginTim const auto color = easyDescriptor(gui_block.tree.node->id()).color(); item->setBackgroundColor(color); -#ifdef EASY_TREE_WIDGET__USE_VECTOR - auto item_index = static_cast(m_items.size()); - m_items.push_back(item); -#endif - size_t children_items_number = 0; - ::profiler::timestamp_t children_duration = 0; + profiler::timestamp_t children_duration = 0; if (!gui_block.tree.children.empty()) { - m_iditems.clear(); + iditems.clear(); - children_items_number = (this->*setTree)(*block.root, firstCswitch, _beginTime, gui_block.tree.children, - item, item, _left, _right, _strict, children_duration, - _addZeroBlocks, _units); + children_items_number = setTreeInternal(*block.root, iditems, stats, firstCswitch, _beginTime, tree.children, + item, item, _left, _right, _strict, partial, children_duration, _addZeroBlocks, _units, 1); if (interrupted()) break; + + if (partial && children_items_number == 0) + { + delete item; + setProgress((95 * ++i) / total); + continue; + } } int percentage = 100; @@ -482,31 +477,16 @@ void TreeWidgetLoader::setTreeInternal2(const ::profiler::timestamp_t& _beginTim percentage = static_cast(0.5 + 100. * static_cast(self_duration) / static_cast(duration)); } - item->setTimeSmart(COL_SELF_DURATION, _units, self_duration); - item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage); - item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage)); + item->setTimeSmart(COL_SELF_TIME, _units, self_duration); + item->setData(COL_SELF_TIME_PERCENT, Qt::UserRole, percentage); + item->setText(COL_SELF_TIME_PERCENT, QString::number(percentage)); - if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right)) - { - //total_items += children_items_number + 1; -#ifdef EASY_TREE_WIDGET__USE_VECTOR - gui_block.tree_item = item_index; -#endif + //total_items += children_items_number + 1; - if (gui_block.expanded) - item->setExpanded(true); + if (gui_block.expanded) + item->setExpanded(true); -#ifndef EASY_TREE_WIDGET__USE_VECTOR - m_items.insert(::std::make_pair(block.tree, item)); -#endif - } - else - { -#ifdef EASY_TREE_WIDGET__USE_VECTOR - m_items.pop_back(); -#endif - delete item; - } + m_items.insert(std::make_pair(block.tree, item)); setProgress((95 * ++i) / total); } @@ -515,17 +495,13 @@ void TreeWidgetLoader::setTreeInternal2(const ::profiler::timestamp_t& _beginTim total = static_cast(threadsMap.size()); for (auto& it : threadsMap) { - auto item = it.second; + auto& thread_data = it.second; + auto item = thread_data.item; if (item->childCount() > 0) { - //addTopLevelItem(item); - //m_roots[it.first] = item; - - //_items.push_back(item); m_topLevelItems.emplace_back(it.first, item); - - //++total_items; + fillStatsForTree(item, thread_data.stats, _units, _right - _left); } else { @@ -536,26 +512,534 @@ void TreeWidgetLoader::setTreeInternal2(const ::profiler::timestamp_t& _beginTim } setDone(); - //return total_items; } ////////////////////////////////////////////////////////////////////////// -size_t TreeWidgetLoader::setTreeInternal(const ::profiler::BlocksTreeRoot& _threadRoot, - ::profiler::block_index_t _firstCswitch, - const ::profiler::timestamp_t& _beginTime, - const ::profiler::BlocksTree::children_t& _children, - TreeWidgetItem* _parent, - TreeWidgetItem* _frame, - ::profiler::timestamp_t _left, - ::profiler::timestamp_t _right, - bool _strict, - ::profiler::timestamp_t& _duration, - bool _addZeroBlocks, - ::profiler_gui::TimeUnits _units) -{ - auto const setTree = m_mode == TreeMode::Full ? &TreeWidgetLoader::setTreeInternal : &TreeWidgetLoader::setTreeInternalPlain; +void TreeWidgetLoader::setTreeInternalPlainTop( + const profiler::timestamp_t& _beginTime, + const profiler_gui::TreeBlocks& _blocks, + profiler::timestamp_t _left, + profiler::timestamp_t _right, + bool _strict, + bool _addZeroBlocks, + bool _decoratedThreadNames, + bool _hexThreadId, + profiler_gui::TimeUnits _units +) { + BeginEndIndicesMap beginEndMap; + ThreadDataMap threadsMap; + const auto u_thread = profiler_gui::toUnicode("thread"); + int i = 0, total = static_cast(_blocks.size()); + for (const auto& block : _blocks) + { + if (interrupted()) + break; + + const auto block_index = block.tree; + auto& gui_block = easyBlock(block_index); + auto& tree = gui_block.tree; + const auto startTime = tree.node->begin(); + const auto endTime = tree.node->end(); + + if (startTime > _right || endTime < _left) + { + setProgress((95 * ++i) / total); + continue; + } + + const profiler::timestamp_t duration = endTime - startTime; + + const bool partial = _strict && (startTime < _left || endTime > _right); + if (partial && duration != 0 && (startTime == _right || endTime == _left)) + { + setProgress((95 * ++i) / total); + continue; + } + + auto& thread_data = threadsMap[block.root->thread_id]; + auto thread_item = thread_data.item; + profiler::block_index_t& firstCswitch = beginEndMap[block.root->thread_id]; + StatsMap& stats = thread_data.stats; + IdItems& iditems = thread_data.iditems; + + if (thread_item == nullptr) + { + thread_item = new TreeWidgetItem(); + thread_data.item = thread_item; + + thread_item->setText(COL_NAME, profiler_gui::decoratedThreadName(_decoratedThreadNames, *block.root, u_thread, _hexThreadId)); + thread_item->setData(COL_NAME, Qt::UserRole, static_cast(block.root->thread_id)); + + profiler::timestamp_t thread_duration = 0; + if (!block.root->children.empty()) + thread_duration = easyBlocksTree(block.root->children.back()).node->end() + - easyBlocksTree(block.root->children.front()).node->begin(); + + thread_item->setTimeSmart(COL_TIME, _units, thread_duration); + thread_item->setBackgroundColor(profiler_gui::SELECTED_THREAD_BACKGROUND); + + // Sum of all children durations: + thread_item->setTimeSmart(COL_SELF_TIME, _units, block.root->profiled_time); + + firstCswitch = 0; + auto it = std::lower_bound(block.root->sync.begin(), block.root->sync.end(), _left, [] (profiler::block_index_t ind, decltype(_left) _val) + { + return EASY_GLOBALS.gui_blocks[ind].tree.node->begin() < _val; + }); + + if (it != block.root->sync.end()) + { + firstCswitch = static_cast(std::distance(block.root->sync.begin(), it)); + if (firstCswitch > 0) + --firstCswitch; + } + else + { + firstCswitch = static_cast(block.root->sync.size()); + } + } + + // Variable firstCswitch modified on each call to calculateIdleTime(). + // Just because all blocks are sorted by startTime, it's OK to modify firstCswitch - this will speed-up future calculateIdleTime calls + const auto idleTime = calculateIdleTime(*block.root, firstCswitch, startTime, endTime); + + auto it = iditems.find(tree.node->id()); + if (it != iditems.end()) + { + auto item = it->second.first; + size_t children_items_count = 0; + profiler::timestamp_t children_duration = 0; + + if (!tree.children.empty()) + { + children_items_count = setTreeInternalPlain(*block.root, iditems, stats, firstCswitch, _beginTime, tree.children, + item, item, _left, _right, _strict, partial, children_duration, _addZeroBlocks, _units, 1); + + if (interrupted()) + break; + + if (partial && children_items_count == 0) + { + setProgress((95 * ++i) / total); + continue; + } + } + + if (tree.per_thread_stats != nullptr) + { + // stats for current selection + updateStats(stats, tree.node->id(), block_index, duration, children_duration); + + // stats + if (children_duration != 0) + { + const auto self_duration = duration - children_duration + item->data(COL_SELF_TIME, Qt::UserRole).toULongLong(); + + int percentage = 100; + if (tree.per_thread_stats->total_duration > 0) + { + percentage = profiler_gui::percent(self_duration, tree.per_thread_stats->total_duration); + } + + item->setTimeSmart(COL_SELF_TIME, _units, self_duration); + item->setData(COL_SELF_TIME_PERCENT, Qt::UserRole, percentage); + item->setText(COL_SELF_TIME_PERCENT, QString::number(percentage)); + } + + auto active_time = duration - idleTime + item->data(COL_ACTIVE_TIME, Qt::UserRole).toULongLong(); + auto active_percent = tree.per_thread_stats->total_duration == 0 ? 100. : profiler_gui::percentReal(active_time, tree.per_thread_stats->total_duration); + item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); + item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); + item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); + } + + const auto total_duration = item->data(COL_TIME, Qt::UserRole).toULongLong() + duration; + auto percentage_per_thread = profiler_gui::percent(total_duration, block.root->profiled_time); + item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread); + item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread)); + + setProgress((95 * ++i) / total); + + continue; + } + + auto item = new TreeWidgetItem(block.tree, thread_item); + item->setPartial(partial); + + iditems[tree.node->id()] = std::make_pair(item, 0); + + auto name = *tree.node->name() != 0 ? tree.node->name() : easyDescriptor(tree.node->id()).name(); + item->setText(COL_NAME, profiler_gui::toUnicode(name)); + item->setTimeSmart(COL_TIME, _units, duration); + + auto active_time = duration - idleTime; + auto active_percent = duration == 0 ? 100. : profiler_gui::percentReal(active_time, duration); + item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); + item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); + item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); + + item->setTimeMs(COL_BEGIN, startTime - _beginTime); + item->setTimeMs(COL_END, endTime - _beginTime); + + item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 100); + + auto percentage_per_thread = profiler_gui::percent(duration, block.root->profiled_time); + item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread); + item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread)); + + if (tree.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also + { + const auto per_thread_stats = tree.per_thread_stats; + const auto per_parent_stats = tree.per_parent_stats; + const auto per_frame_stats = tree.per_frame_stats; + + fillStatsColumnsThread(item, per_thread_stats, _units); + fillStatsColumnsParent(item, per_parent_stats, _units); + fillStatsColumnsFrame(item, per_frame_stats, _units); + + percentage_per_thread = profiler_gui::percent(per_thread_stats->total_duration, block.root->profiled_time); + item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread); + item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread)); + } + else + { + item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0); + item->setText(COL_PERCENT_SUM_PER_THREAD, ""); + } + + const auto color = easyDescriptor(tree.node->id()).color(); + item->setBackgroundColor(color); + + size_t children_items_number = 0; + profiler::timestamp_t children_duration = 0; + if (!tree.children.empty()) + { + children_items_number = setTreeInternalPlain(*block.root, iditems, stats, firstCswitch, _beginTime, + tree.children, item, item, _left, _right, _strict, partial, children_duration, _addZeroBlocks, _units, 1); + + if (interrupted()) + break; + + if (partial && children_items_number == 0) + { + delete item; + iditems.clear(); + setProgress((95 * ++i) / total); + continue; + } + } + + iditems.clear(); + + int percentage = 100; + auto self_duration = duration - children_duration; + if (children_duration > 0 && duration > 0) + { + percentage = static_cast(0.5 + 100. * static_cast(self_duration) / static_cast(duration)); + } + + item->setTimeSmart(COL_SELF_TIME, _units, self_duration); + item->setData(COL_SELF_TIME_PERCENT, Qt::UserRole, percentage); + item->setText(COL_SELF_TIME_PERCENT, QString::number(percentage)); + + updateStats(stats, tree.node->id(), block_index, duration, children_duration); + + if (gui_block.expanded) + item->setExpanded(true); + + m_items.insert(std::make_pair(block.tree, item)); + + setProgress((95 * ++i) / total); + } + + i = 0; + total = static_cast(threadsMap.size()); + for (auto& it : threadsMap) + { + auto& thread_data = it.second; + auto item = thread_data.item; + + if (item->childCount() > 0) + { + m_topLevelItems.emplace_back(it.first, item); + fillStatsForTree(item, thread_data.stats, _units, _right - _left); + } + else + { + delete item; + } + + setProgress(95 + (5 * ++i) / total); + } + + setDone(); +} + +////////////////////////////////////////////////////////////////////////// + +void TreeWidgetLoader::setTreeInternalAggregateTop( + const profiler::timestamp_t& _beginTime, + const profiler_gui::TreeBlocks& _blocks, + profiler::timestamp_t _left, + profiler::timestamp_t _right, + bool _strict, + bool _addZeroBlocks, + bool _decoratedThreadNames, + bool _hexThreadId, + profiler_gui::TimeUnits _units +) { + BeginEndIndicesMap beginEndMap; + ThreadDataMap threadsMap; + + const auto u_thread = profiler_gui::toUnicode("thread"); + int i = 0, total = static_cast(_blocks.size()); + for (const auto& block : _blocks) + { + if (interrupted()) + break; + + const auto block_index = block.tree; + auto& gui_block = easyBlock(block_index); + auto& tree = gui_block.tree; + const auto startTime = tree.node->begin(); + const auto endTime = tree.node->end(); + + if (startTime > _right || endTime < _left) + { + setProgress((95 * ++i) / total); + continue; + } + + const profiler::timestamp_t duration = endTime - startTime; + + const bool partial = _strict && (startTime < _left || endTime > _right); + if (partial && duration != 0 && (startTime == _right || endTime == _left)) + { + setProgress((95 * ++i) / total); + continue; + } + + auto& thread_data = threadsMap[block.root->thread_id]; + auto thread_item = thread_data.item; + profiler::block_index_t& firstCswitch = beginEndMap[block.root->thread_id]; + StatsMap& stats = thread_data.stats; + IdItems& iditems = thread_data.iditems; + + if (thread_item == nullptr) + { + thread_item = new TreeWidgetItem(); + thread_data.item = thread_item; + + thread_item->setText(COL_NAME, profiler_gui::decoratedThreadName(_decoratedThreadNames, *block.root, u_thread, _hexThreadId)); + thread_item->setData(COL_NAME, Qt::UserRole, static_cast(block.root->thread_id)); + + profiler::timestamp_t thread_duration = 0; + if (!block.root->children.empty()) + thread_duration = easyBlocksTree(block.root->children.back()).node->end() + - easyBlocksTree(block.root->children.front()).node->begin(); + + thread_item->setTimeSmart(COL_TIME, _units, thread_duration); + thread_item->setBackgroundColor(profiler_gui::SELECTED_THREAD_BACKGROUND); + + // Sum of all children durations: + thread_item->setTimeSmart(COL_SELF_TIME, _units, block.root->profiled_time); + + firstCswitch = 0; + auto it = std::lower_bound(block.root->sync.begin(), block.root->sync.end(), _left, [] (profiler::block_index_t ind, decltype(_left) _val) + { + return EASY_GLOBALS.gui_blocks[ind].tree.node->begin() < _val; + }); + + if (it != block.root->sync.end()) + { + firstCswitch = static_cast(std::distance(block.root->sync.begin(), it)); + if (firstCswitch > 0) + --firstCswitch; + } + else + { + firstCswitch = static_cast(block.root->sync.size()); + } + } + + // Variable firstCswitch modified on each call to calculateIdleTime(). + // Just because all blocks are sorted by startTime, it's OK to modify firstCswitch - this will speed-up future calculateIdleTime calls + const auto idleTime = calculateIdleTime(*block.root, firstCswitch, startTime, endTime); + + auto it = iditems.find(tree.node->id()); + if (it != iditems.end()) + { + auto item = it->second.first; + size_t children_items_count = 0; + profiler::timestamp_t children_duration = 0; + + if (!tree.children.empty()) + { + children_items_count = setTreeInternalAggregate(*block.root, iditems, stats, firstCswitch, _beginTime, + tree.children, thread_item, _left, _right, _strict, partial, children_duration, _addZeroBlocks, _units, 1); + + if (interrupted()) + break; + + if (partial && children_items_count == 0) + { + setProgress((95 * ++i) / total); + continue; + } + } + + const auto total_duration = item->data(COL_TIME, Qt::UserRole).toULongLong() + duration; + item->setTimeSmart(COL_TIME, _units, total_duration); + + if (tree.per_thread_stats != nullptr) + { + // stats for current selection + updateStats(stats, tree.node->id(), block_index, duration, children_duration); + } + + // stats + if (children_duration != 0) + { + const auto self_duration = duration - children_duration + item->data(COL_SELF_TIME, Qt::UserRole).toULongLong(); + + int percentage = 100; + if (total_duration > 0) + { + percentage = profiler_gui::percent(self_duration, total_duration); + } + + item->setTimeSmart(COL_SELF_TIME, _units, self_duration); + item->setData(COL_SELF_TIME_PERCENT, Qt::UserRole, percentage); + item->setText(COL_SELF_TIME_PERCENT, QString::number(percentage)); + } + + auto active_time = duration - idleTime + item->data(COL_ACTIVE_TIME, Qt::UserRole).toULongLong(); + auto active_percent = total_duration == 0 ? 100. : profiler_gui::percentReal(active_time, total_duration); + item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); + item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); + item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); + + setProgress((95 * ++i) / total); + + continue; + } + + auto item = new TreeWidgetItem(block.tree, thread_item); + item->setPartial(partial); + + iditems[tree.node->id()] = std::make_pair(item, 0); + + auto name = *tree.node->name() != 0 ? tree.node->name() : easyDescriptor(tree.node->id()).name(); + item->setText(COL_NAME, profiler_gui::toUnicode(name)); + item->setTimeSmart(COL_TIME, _units, duration); + + auto active_time = duration - idleTime; + auto active_percent = duration == 0 ? 100. : profiler_gui::percentReal(active_time, duration); + item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); + item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); + item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); + + const auto per_thread_stats = gui_block.tree.per_thread_stats; + if (per_thread_stats != nullptr) + { + fillStatsColumnsThread(item, per_thread_stats, _units); + auto percent_per_thread = profiler_gui::percent(per_thread_stats->total_duration, block.root->profiled_time); + item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percent_per_thread); + item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percent_per_thread)); + } + else + { + item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0); + } + + const auto color = easyDescriptor(gui_block.tree.node->id()).color(); + item->setBackgroundColor(color); + + size_t children_items_number = 0; + profiler::timestamp_t children_duration = 0; + if (!tree.children.empty()) + { + children_items_number = setTreeInternalAggregate(*block.root, iditems, stats, firstCswitch, _beginTime, + gui_block.tree.children, thread_item, _left, _right, _strict, partial, children_duration, _addZeroBlocks, _units, 1); + + if (interrupted()) + break; + + if (partial && children_items_number == 0) + { + delete item; + iditems.erase(tree.node->id()); + setProgress((95 * ++i) / total); + continue; + } + } + + int percent_value = 100; + auto self_duration = duration - children_duration; + if (children_duration > 0 && duration > 0) + { + percent_value = static_cast(0.5 + 100. * static_cast(self_duration) / static_cast(duration)); + } + + item->setTimeSmart(COL_SELF_TIME, _units, self_duration); + item->setData(COL_SELF_TIME_PERCENT, Qt::UserRole, percent_value); + item->setText(COL_SELF_TIME_PERCENT, QString::number(percent_value)); + + updateStats(stats, tree.node->id(), block_index, duration, children_duration); + + if (gui_block.expanded) + item->setExpanded(true); + + m_items.insert(std::make_pair(block.tree, item)); + + setProgress((95 * ++i) / total); + } + + i = 0; + total = static_cast(threadsMap.size()); + for (auto& it : threadsMap) + { + auto& thread_data = it.second; + auto item = thread_data.item; + + if (item->childCount() > 0) + { + m_topLevelItems.emplace_back(it.first, item); + fillStatsForTree(item, thread_data.stats, _units, _right - _left); + } + else + { + delete item; + } + + setProgress(95 + (5 * ++i) / total); + } + + setDone(); +} + +////////////////////////////////////////////////////////////////////////// + +size_t TreeWidgetLoader::setTreeInternal( + const profiler::BlocksTreeRoot& _threadRoot, + IdItems& _iditems, + StatsMap& _statsMap, + profiler::block_index_t _firstCswitch, + profiler::timestamp_t _beginTime, + const profiler::BlocksTree::children_t& _children, + TreeWidgetItem* _parent, + TreeWidgetItem* _frame, + profiler::timestamp_t left, + profiler::timestamp_t right, + bool strict, + bool partial_parent, + profiler::timestamp_t& _duration, + bool _addZeroBlocks, + profiler_gui::TimeUnits _units, + int _depth +) { size_t total_items = 0; for (auto child_index : _children) { @@ -568,48 +1052,28 @@ size_t TreeWidgetLoader::setTreeInternal(const ::profiler::BlocksTreeRoot& _thre const auto endTime = child.node->end(); const auto duration = endTime - startTime; - if (duration == 0 && !_addZeroBlocks && easyDescriptor(child.node->id()).type() == profiler::BlockType::Block) + if (startTime > right || endTime < left) continue; - _duration += duration; - - if (startTime > _right || endTime < _left) + const bool partial = strict && (startTime < left || endTime > right); + if (partial && partial_parent && duration != 0 && (startTime == right || endTime == left)) continue; - bool hasContextSwitch = false; - ::profiler::timestamp_t idleTime = 0; - for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind) - { - auto cs_index = _threadRoot.sync[ind]; - const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node; + const auto& desc = easyDescriptor(child.node->id()); - if (cs->begin() > endTime) - { - if (!hasContextSwitch) - _firstCswitch = ind; - break; - } + if (duration == 0 && !_addZeroBlocks && desc.type() == profiler::BlockType::Block) + continue; - if (startTime <= cs->begin() && cs->end() <= endTime) - { - if (!hasContextSwitch) - { - _firstCswitch = ind; - hasContextSwitch = true; - } - - idleTime += cs->duration(); - } - } + const auto idleTime = calculateIdleTime(_threadRoot, _firstCswitch, startTime, endTime); auto item = new TreeWidgetItem(child_index, _parent); - auto name = *child.node->name() != 0 ? child.node->name() : easyDescriptor(child.node->id()).name(); - item->setText(COL_NAME, ::profiler_gui::toUnicode(name)); - item->setTimeSmart(COL_DURATION, _units, duration); + auto name = *child.node->name() != 0 ? child.node->name() : desc.name(); + item->setText(COL_NAME, profiler_gui::toUnicode(name)); + item->setTimeSmart(COL_TIME, _units, duration); auto active_time = duration - idleTime; - auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration); + auto active_percent = duration == 0 ? 100. : profiler_gui::percentReal(active_time, duration); item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); @@ -620,13 +1084,13 @@ size_t TreeWidgetLoader::setTreeInternal(const ::profiler::BlocksTreeRoot& _thre if (child.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also { - const ::profiler::BlockStatistics* per_thread_stats = child.per_thread_stats; - const ::profiler::BlockStatistics* per_parent_stats = child.per_parent_stats; - const ::profiler::BlockStatistics* per_frame_stats = child.per_frame_stats; + const auto per_thread_stats = child.per_thread_stats; + const auto per_parent_stats = child.per_parent_stats; + const auto per_frame_stats = child.per_frame_stats; auto parent_duration = _parent->duration(); - auto percentage = duration == 0 ? 0 : ::profiler_gui::percent(duration, parent_duration); - auto percentage_sum = ::profiler_gui::percent(per_parent_stats->total_duration, parent_duration); + auto percentage = duration == 0 ? 0 : profiler_gui::percent(duration, parent_duration); + auto percentage_sum = profiler_gui::percent(per_parent_stats->total_duration, parent_duration); item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage); item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage)); item->setData(COL_PERCENT_SUM_PER_PARENT, Qt::UserRole, percentage_sum); @@ -637,8 +1101,8 @@ size_t TreeWidgetLoader::setTreeInternal(const ::profiler::BlocksTreeRoot& _thre if (_parent != _frame) { parent_duration = _frame->duration(); - percentage = duration == 0 ? 0 : ::profiler_gui::percent(duration, parent_duration); - percentage_sum = ::profiler_gui::percent(per_frame_stats->total_duration, parent_duration); + percentage = duration == 0 ? 0 : profiler_gui::percent(duration, parent_duration); + percentage_sum = profiler_gui::percent(per_frame_stats->total_duration, parent_duration); } item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, percentage); @@ -651,56 +1115,24 @@ size_t TreeWidgetLoader::setTreeInternal(const ::profiler::BlocksTreeRoot& _thre item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0); item->setData(COL_PERCENT_SUM_PER_FRAME, Qt::UserRole, 0); - auto percentage_per_thread = ::profiler_gui::percent(duration, _threadRoot.profiled_time); + auto percentage_per_thread = profiler_gui::percent(duration, _threadRoot.profiled_time); item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread); item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread)); } + fillStatsColumnsThread(item, per_thread_stats, _units); + fillStatsColumnsParent(item, per_parent_stats, _units); + fillStatsColumnsFrame(item, per_frame_stats, _units); - if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) - { - item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration()); - item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration()); - item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration()); - item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration); - } - - item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number); - item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number)); - - auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, _threadRoot.profiled_time); + auto percentage_per_thread = profiler_gui::percent(per_thread_stats->total_duration, _threadRoot.profiled_time); item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread); item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread)); - - - if (per_parent_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) - { - item->setTimeSmart(COL_MIN_PER_PARENT, _units, easyBlock(per_parent_stats->min_duration_block).tree.node->duration()); - item->setTimeSmart(COL_MAX_PER_PARENT, _units, easyBlock(per_parent_stats->max_duration_block).tree.node->duration()); - item->setTimeSmart(COL_AVERAGE_PER_PARENT, _units, per_parent_stats->average_duration()); - item->setTimeSmart(COL_DURATION_SUM_PER_PARENT, _units, per_parent_stats->total_duration); - } - - item->setData(COL_NCALLS_PER_PARENT, Qt::UserRole, per_parent_stats->calls_number); - item->setText(COL_NCALLS_PER_PARENT, QString::number(per_parent_stats->calls_number)); - - - if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) - { - item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration()); - item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration()); - item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration()); - item->setTimeSmart(COL_DURATION_SUM_PER_FRAME, _units, per_frame_stats->total_duration); - } - - item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number); - item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number)); } else { if (_frame == nullptr) { - auto percentage_per_thread = ::profiler_gui::percent(duration, _threadRoot.profiled_time); + auto percentage_per_thread = profiler_gui::percent(duration, _threadRoot.profiled_time); item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread); item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread)); } @@ -713,60 +1145,48 @@ size_t TreeWidgetLoader::setTreeInternal(const ::profiler::BlocksTreeRoot& _thre item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0); } - const auto color = easyDescriptor(child.node->id()).color(); - item->setBackgroundColor(color); - -#ifdef EASY_TREE_WIDGET__USE_VECTOR - auto item_index = static_cast(m_items.size()); - m_items.push_back(item); -#endif + item->setBackgroundColor(desc.color()); size_t children_items_number = 0; - ::profiler::timestamp_t children_duration = 0; + profiler::timestamp_t children_duration = 0; if (!child.children.empty()) { - m_iditems.clear(); + _iditems.clear(); - children_items_number = (this->*setTree)(_threadRoot, _firstCswitch, _beginTime, child.children, item, - _frame ? _frame : item, _left, _right, _strict, children_duration, - _addZeroBlocks, _units); + children_items_number = setTreeInternal(_threadRoot, _iditems, _statsMap, _firstCswitch, _beginTime, child.children, + item, _frame ? _frame : item, left, right, strict, partial, children_duration, _addZeroBlocks, _units, _depth + 1); if (interrupted()) break; + + if (partial && children_items_number == 0) + { + delete item; + continue; + } } + _duration += duration; + int percentage = 100; auto self_duration = duration - children_duration; if (children_duration > 0 && duration > 0) { - percentage = ::profiler_gui::percent(self_duration, duration); + percentage = profiler_gui::percent(self_duration, duration); } - item->setTimeSmart(COL_SELF_DURATION, _units, self_duration); - item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage); - item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage)); + item->setTimeSmart(COL_SELF_TIME, _units, self_duration); + item->setData(COL_SELF_TIME_PERCENT, Qt::UserRole, percentage); + item->setText(COL_SELF_TIME_PERCENT, QString::number(percentage)); - if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right)) - { - total_items += children_items_number + 1; -#ifdef EASY_TREE_WIDGET__USE_VECTOR - gui_block.tree_item = item_index; -#endif + total_items += children_items_number + 1; - if (gui_block.expanded) - item->setExpanded(true); + if (gui_block.expanded) + item->setExpanded(true); -#ifndef EASY_TREE_WIDGET__USE_VECTOR - m_items.insert(::std::make_pair(child_index, item)); -#endif - } - else - { -#ifdef EASY_TREE_WIDGET__USE_VECTOR - m_items.pop_back(); -#endif - delete item; - } + m_items.insert(std::make_pair(child_index, item)); + + updateStats(_statsMap, child.node->id(), child_index, duration, children_duration); } return total_items; @@ -774,9 +1194,9 @@ size_t TreeWidgetLoader::setTreeInternal(const ::profiler::BlocksTreeRoot& _thre ////////////////////////////////////////////////////////////////////////// -::profiler::timestamp_t TreeWidgetLoader::calculateChildrenDurationRecursive(const ::profiler::BlocksTree::children_t& _children, ::profiler::block_id_t _id) +profiler::timestamp_t TreeWidgetLoader::calculateChildrenDurationRecursive(const profiler::BlocksTree::children_t& _children, profiler::block_id_t _id) { - ::profiler::timestamp_t total_duration = 0; + profiler::timestamp_t total_duration = 0; for (auto child_index : _children) { @@ -792,22 +1212,27 @@ size_t TreeWidgetLoader::setTreeInternal(const ::profiler::BlocksTreeRoot& _thre return total_duration; } -size_t TreeWidgetLoader::setTreeInternalPlain(const ::profiler::BlocksTreeRoot& _threadRoot, - ::profiler::block_index_t _firstCswitch, - const ::profiler::timestamp_t& _beginTime, - const ::profiler::BlocksTree::children_t& _children, - TreeWidgetItem*, - TreeWidgetItem* _frame, - ::profiler::timestamp_t _left, - ::profiler::timestamp_t _right, - bool _strict, - ::profiler::timestamp_t& _duration, - bool _addZeroBlocks, - ::profiler_gui::TimeUnits _units) -{ +size_t TreeWidgetLoader::setTreeInternalPlain( + const profiler::BlocksTreeRoot& threadRoot, + IdItems& iditems, + StatsMap& statsMap, + profiler::block_index_t firstCswitch, + profiler::timestamp_t beginTime, + const profiler::BlocksTree::children_t& children, + TreeWidgetItem* root, + TreeWidgetItem* frame, + profiler::timestamp_t left, + profiler::timestamp_t right, + bool strict, + bool partial_parent, + profiler::timestamp_t& total_duration, + bool addZeroBlocks, + profiler_gui::TimeUnits units, + int depth +) { size_t total_items = 0; - for (auto child_index : _children) + for (auto child_index : children) { if (interrupted()) break; @@ -818,70 +1243,59 @@ size_t TreeWidgetLoader::setTreeInternalPlain(const ::profiler::BlocksTreeRoot& const auto endTime = child.node->end(); const auto duration = endTime - startTime; - _duration += duration; + if (startTime > right || endTime < left) + continue; - auto it = m_iditems.find(child.node->id()); - if (it != m_iditems.end()) + const bool partial = strict && (startTime < left || endTime > right); + if (partial && partial_parent && duration != 0 && (startTime == right || endTime == left)) + continue; + + const auto& desc = easyDescriptor(child.node->id()); + + auto it = iditems.find(child.node->id()); + if (it != iditems.end()) { - ++total_items; - - ::profiler::timestamp_t children_duration = 0; + size_t children_items_number = 0; + profiler::timestamp_t children_duration = 0; if (!child.children.empty()) { - setTreeInternalPlain(_threadRoot, _firstCswitch, _beginTime, child.children, _frame, _frame, _left, - _right, _strict, children_duration, _addZeroBlocks, _units); + children_items_number = setTreeInternalPlain(threadRoot, iditems, statsMap, firstCswitch, beginTime, + child.children, root, frame, left, right, strict, partial, children_duration, addZeroBlocks, units, depth + 1); if (interrupted()) break; + + if (partial && children_items_number == 0) + continue; } - if (it->second != nullptr && child.per_frame_stats != nullptr) + total_duration += duration; + ++total_items; + + updateStats(statsMap, child.node->id(), child_index, duration, children_duration); + + if (it->second.first != nullptr && child.per_frame_stats != nullptr) { - auto item = it->second; + auto item = it->second.first; //auto children_duration = calculateChildrenDurationRecursive(child.children, it->first); if (children_duration != 0) { - auto self_duration = item->data(COL_SELF_DURATION, Qt::UserRole).toULongLong() - children_duration; + auto self_duration = duration + item->data(COL_SELF_TIME, Qt::UserRole).toULongLong() - children_duration; int percentage = 100; if (child.per_frame_stats->total_duration > 0) - percentage = ::profiler_gui::percent(self_duration, child.per_frame_stats->total_duration); + percentage = profiler_gui::percent(self_duration, child.per_frame_stats->total_duration); - item->setTimeSmart(COL_SELF_DURATION, _units, self_duration); - item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage); - item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage)); + item->setTimeSmart(COL_SELF_TIME, units, self_duration); + item->setData(COL_SELF_TIME_PERCENT, Qt::UserRole, percentage); + item->setText(COL_SELF_TIME_PERCENT, QString::number(percentage)); } - bool hasContextSwitch = false; - ::profiler::timestamp_t idleTime = 0; - for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind) - { - auto cs_index = _threadRoot.sync[ind]; - const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node; - - if (cs->begin() > endTime) - { - if (!hasContextSwitch) - _firstCswitch = ind; - break; - } - - if (startTime <= cs->begin() && cs->end() <= endTime) - { - if (!hasContextSwitch) - { - _firstCswitch = ind; - hasContextSwitch = true; - } - - idleTime += cs->duration(); - } - } - - auto active_time = item->data(COL_ACTIVE_TIME, Qt::UserRole).toULongLong() - idleTime; - auto active_percent = child.per_frame_stats->total_duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, child.per_frame_stats->total_duration); - item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); + const auto idleTime = calculateIdleTime(threadRoot, firstCswitch, startTime, endTime); + const auto active_time = duration + item->data(COL_ACTIVE_TIME, Qt::UserRole).toULongLong() - idleTime; + const auto active_percent = child.per_frame_stats->total_duration == 0 ? 100. : profiler_gui::percentReal(active_time, child.per_frame_stats->total_duration); + item->setTimeSmart(COL_ACTIVE_TIME, units, active_time); item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); } @@ -889,73 +1303,29 @@ size_t TreeWidgetLoader::setTreeInternalPlain(const ::profiler::BlocksTreeRoot& continue; } - if (startTime > _right || endTime < _left) - continue; + const auto idleTime = calculateIdleTime(threadRoot, firstCswitch, startTime, endTime); - bool hasContextSwitch = false; - ::profiler::timestamp_t idleTime = 0; - for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind) - { - auto cs_index = _threadRoot.sync[ind]; - const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node; + auto item = new TreeWidgetItem(child_index, root); - if (cs->begin() > endTime) - { - if (!hasContextSwitch) - _firstCswitch = ind; - break; - } - - if (startTime <= cs->begin() && cs->end() <= endTime) - { - if (!hasContextSwitch) - { - _firstCswitch = ind; - hasContextSwitch = true; - } - - idleTime += cs->duration(); - } - } - - auto item = new TreeWidgetItem(child_index, _frame); - - auto name = *child.node->name() != 0 ? child.node->name() : easyDescriptor(child.node->id()).name(); - item->setText(COL_NAME, ::profiler_gui::toUnicode(name)); + auto name = *child.node->name() != 0 ? child.node->name() : desc.name(); + item->setText(COL_NAME, profiler_gui::toUnicode(name)); if (child.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also { - const ::profiler::BlockStatistics* per_thread_stats = child.per_thread_stats; - if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) - { - item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration()); - item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration()); - item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration()); - } + const auto per_thread_stats = child.per_thread_stats; + const auto per_frame_stats = child.per_frame_stats; - item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration); - item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number); - item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number)); - - auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, _threadRoot.profiled_time); + fillStatsColumnsThread(item, per_thread_stats, units); + fillStatsColumnsFrame(item, per_frame_stats, units); + + auto percentage_per_thread = profiler_gui::percent(per_thread_stats->total_duration, threadRoot.profiled_time); item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread); item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread)); - const ::profiler::BlockStatistics* per_frame_stats = child.per_frame_stats; - const auto percentage_sum = ::profiler_gui::percent(per_frame_stats->total_duration, _frame->duration()); + const auto percentage_sum = profiler_gui::percent(per_frame_stats->total_duration, frame->duration()); item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, percentage_sum); item->setText(COL_PERCENT_PER_FRAME, QString::number(percentage_sum)); - - if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) - { - item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration()); - item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration()); - item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration()); - } - - item->setTimeSmart(COL_DURATION, _units, per_frame_stats->total_duration); - item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number); - item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number)); + item->setTimeSmart(COL_TIME, units, per_frame_stats->total_duration); } else { @@ -963,72 +1333,348 @@ size_t TreeWidgetLoader::setTreeInternalPlain(const ::profiler::BlocksTreeRoot& item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0); } - const auto color = easyDescriptor(child.node->id()).color(); - item->setBackgroundColor(color); + item->setBackgroundColor(desc.color()); -#ifdef EASY_TREE_WIDGET__USE_VECTOR - auto item_index = static_cast(m_items.size()); - m_items.push_back(item); -#endif - m_iditems[child.node->id()] = nullptr; + iditems[child.node->id()] = std::make_pair(nullptr, depth); + item->setPartial(partial); size_t children_items_number = 0; - ::profiler::timestamp_t children_duration = 0; + profiler::timestamp_t children_duration = 0; if (!child.children.empty()) { - children_items_number = setTreeInternalPlain(_threadRoot, _firstCswitch, _beginTime, child.children, _frame, - _frame, _left, _right, _strict, children_duration, - _addZeroBlocks, _units); + children_items_number = setTreeInternalPlain(threadRoot, iditems, statsMap, firstCswitch, beginTime, child.children, root, + frame, left, right, strict, partial, children_duration, + addZeroBlocks, units, depth + 1); if (interrupted()) break; + + if (partial && children_items_number == 0) + { + delete item; + iditems.erase(child.node->id()); + continue; + } } - m_iditems[child.node->id()] = item; + iditems[child.node->id()] = std::make_pair(item, depth); + total_duration += duration; if (child.per_frame_stats != nullptr) { int percentage = 100; auto self_duration = child.per_frame_stats->total_duration - children_duration; if (child.per_frame_stats->total_duration > 0) - percentage = ::profiler_gui::percent(self_duration, child.per_frame_stats->total_duration); + percentage = profiler_gui::percent(self_duration, child.per_frame_stats->total_duration); - item->setTimeSmart(COL_SELF_DURATION, _units, self_duration); - item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage); - item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage)); + item->setTimeSmart(COL_SELF_TIME, units, self_duration); + item->setData(COL_SELF_TIME_PERCENT, Qt::UserRole, percentage); + item->setText(COL_SELF_TIME_PERCENT, QString::number(percentage)); auto active_time = child.per_frame_stats->total_duration - idleTime; - auto active_percent = child.per_frame_stats->total_duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, child.per_frame_stats->total_duration); - item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); + auto active_percent = child.per_frame_stats->total_duration == 0 ? 100. : profiler_gui::percentReal(active_time, child.per_frame_stats->total_duration); + item->setTimeSmart(COL_ACTIVE_TIME, units, active_time); item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); } - if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right)) - { - total_items += children_items_number + 1; -#ifdef EASY_TREE_WIDGET__USE_VECTOR - gui_block.tree_item = item_index; -#endif + total_items += children_items_number + 1; - if (gui_block.expanded) - item->setExpanded(true); + if (gui_block.expanded) + item->setExpanded(true); -#ifndef EASY_TREE_WIDGET__USE_VECTOR - m_items.insert(::std::make_pair(child_index, item)); -#endif - } - else - { -#ifdef EASY_TREE_WIDGET__USE_VECTOR - m_items.pop_back(); -#endif - delete item; - m_iditems.erase(gui_block.tree.node->id()); - } + m_items.insert(std::make_pair(child_index, item)); + + updateStats(statsMap, child.node->id(), child_index, duration, children_duration); } return total_items; } ////////////////////////////////////////////////////////////////////////// + +size_t TreeWidgetLoader::setTreeInternalAggregate( + const profiler::BlocksTreeRoot& threadRoot, + IdItems& iditems, + StatsMap& statsMap, + profiler::block_index_t firstCswitch, + profiler::timestamp_t beginTime, + const profiler::BlocksTree::children_t& children, + TreeWidgetItem* root, + profiler::timestamp_t left, + profiler::timestamp_t right, + bool strict, + bool partial_parent, + profiler::timestamp_t& total_duration, + bool addZeroBlocks, + profiler_gui::TimeUnits units, + int depth +) { + size_t total_items = 0; + + for (auto child_index : children) + { + if (interrupted()) + break; + + const auto& gui_block = easyBlock(child_index); + const auto& child = gui_block.tree; + const auto startTime = child.node->begin(); + const auto endTime = child.node->end(); + const auto duration = endTime - startTime; + + if (startTime > right || endTime < left) + continue; + + const bool partial = strict && (startTime < left || endTime > right); + if (partial && partial_parent && duration != 0 && (startTime == right || endTime == left)) + continue; + + const auto& desc = easyDescriptor(child.node->id()); + + auto it = iditems.find(child.node->id()); + if (it != iditems.end()) + { + size_t children_items_number = 0; + profiler::timestamp_t children_duration = 0; + if (!child.children.empty()) + { + children_items_number = setTreeInternalAggregate(threadRoot, iditems, statsMap, firstCswitch, beginTime, + child.children, root, left, right, strict, partial, children_duration, addZeroBlocks, units, depth + 1); + + if (interrupted()) + break; + + if (partial && children_items_number == 0) + continue; + } + + auto item = it->second.first; + + total_duration += duration; + ++total_items; + + updateStats(statsMap, child.node->id(), child_index, duration, children_duration); + + const auto total_block_duration = duration + item->data(COL_TIME, Qt::UserRole).toULongLong(); + + int percentage = 100; + auto self_duration = duration - children_duration + item->data(COL_SELF_TIME, Qt::UserRole).toULongLong(); + if (total_block_duration > 0) + percentage = profiler_gui::percent(self_duration, total_block_duration); + + item->setTimeSmart(COL_TIME, units, total_block_duration); + item->setTimeSmart(COL_SELF_TIME, units, self_duration); + item->setData(COL_SELF_TIME_PERCENT, Qt::UserRole, percentage); + item->setText(COL_SELF_TIME_PERCENT, QString::number(percentage)); + + const auto idleTime = calculateIdleTime(threadRoot, firstCswitch, startTime, endTime); + const auto active_time = duration + item->data(COL_ACTIVE_TIME, Qt::UserRole).toULongLong() - idleTime; + const auto active_percent = total_block_duration == 0 ? 100. : profiler_gui::percentReal(active_time, total_block_duration); + item->setTimeSmart(COL_ACTIVE_TIME, units, active_time); + item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); + item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); + + continue; + } + + const auto idleTime = calculateIdleTime(threadRoot, firstCswitch, startTime, endTime); + + auto item = new TreeWidgetItem(child_index, root); + + auto name = *child.node->name() != 0 ? child.node->name() : desc.name(); + item->setText(COL_NAME, profiler_gui::toUnicode(name)); + item->setTimeSmart(COL_TIME, units, duration); + + if (child.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also + { + const auto per_thread_stats = child.per_thread_stats; + + fillStatsColumnsThread(item, per_thread_stats, units); + + auto percent_per_thread = profiler_gui::percent(per_thread_stats->total_duration, threadRoot.profiled_time); + item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percent_per_thread); + item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percent_per_thread)); + } + else + { + item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0); + } + + item->setBackgroundColor(desc.color()); + + iditems[child.node->id()] = std::make_pair(item, depth); + + item->setPartial(partial); + + size_t children_items_number = 0; + profiler::timestamp_t children_duration = 0; + if (!child.children.empty()) + { + children_items_number = setTreeInternalAggregate(threadRoot, iditems, statsMap, firstCswitch, beginTime, + child.children, root, left, right, strict, partial, children_duration, addZeroBlocks, units, depth + 1); + + if (interrupted()) + break; + + if (partial && children_items_number == 0) + { + delete item; + iditems.erase(child.node->id()); + continue; + } + } + + total_duration += duration; + + int percentage = 100; + auto self_duration = duration - children_duration; + if (duration > 0) + percentage = profiler_gui::percent(self_duration, duration); + + item->setTimeSmart(COL_SELF_TIME, units, self_duration); + item->setData(COL_SELF_TIME_PERCENT, Qt::UserRole, percentage); + item->setText(COL_SELF_TIME_PERCENT, QString::number(percentage)); + + auto active_time = duration - idleTime; + auto active_percent = duration == 0 ? 100. : profiler_gui::percentReal(active_time, duration); + item->setTimeSmart(COL_ACTIVE_TIME, units, active_time); + item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); + item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); + + total_items += children_items_number + 1; + + if (gui_block.expanded) + item->setExpanded(true); + + m_items.insert(std::make_pair(child_index, item)); + + updateStats(statsMap, child.node->id(), child_index, duration, children_duration); + } + + return total_items; +} + +////////////////////////////////////////////////////////////////////////// + +profiler::timestamp_t TreeWidgetLoader::calculateIdleTime( + const profiler::BlocksTreeRoot& _threadRoot, + profiler::block_index_t& _firstCSwitch, + profiler::timestamp_t _begin, + profiler::timestamp_t _end +) const { + bool hasContextSwitch = false; + profiler::timestamp_t idleTime = 0; + for (profiler::block_index_t ind = _firstCSwitch, ncs = static_cast(_threadRoot.sync.size()); ind < ncs; ++ind) + { + auto cs_index = _threadRoot.sync[ind]; + const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node; + + if (cs->begin() > _end) + { + if (!hasContextSwitch) + _firstCSwitch = ind; + break; + } + + if (_begin <= cs->begin() && cs->end() <= _end) + { + if (!hasContextSwitch) + { + _firstCSwitch = ind; + hasContextSwitch = true; + } + + idleTime += cs->duration(); + } + } + return idleTime; +} + +////////////////////////////////////////////////////////////////////////// + +void TreeWidgetLoader::updateStats( + StatsMap& statsMap, + profiler::block_id_t id, + profiler::block_index_t index, + profiler::timestamp_t duration, + profiler::timestamp_t children_duration +) const { + auto stats_it = statsMap.find(id); + if (stats_it == statsMap.end()) + { + stats_it = statsMap.emplace(id, profiler::BlockStatistics(duration, index, 0)).first; + stats_it->second.total_children_duration = children_duration; + } + else + { + auto& stat = stats_it->second; + ++stat.calls_number; + stat.total_duration += duration; + stat.total_children_duration += children_duration; + + if (duration > easyBlock(stat.max_duration_block).tree.node->duration()) + { + stat.max_duration_block = index; + } + + if (duration < easyBlock(stat.min_duration_block).tree.node->duration()) + { + stat.min_duration_block = index; + } + } +} + +////////////////////////////////////////////////////////////////////////// + +void TreeWidgetLoader::fillStatsForTree(TreeWidgetItem* root, const StatsMap& stats, profiler_gui::TimeUnits _units, profiler::timestamp_t selectionDuration) const +{ + if (stats.empty()) + { + return; + } + + std::deque queue; + + if (root->parent() != nullptr) + { + queue.push_back(root); + } + else + { + for (int i = 0, count = root->childCount(); i < count; ++i) + { + queue.push_back(static_cast(root->child(i))); + } + } + + while (!queue.empty() && !interrupted()) + { + auto item = queue.front(); + + for (int i = 0, count = item->childCount(); i < count; ++i) + { + queue.push_back(static_cast(item->child(i))); + } + + auto stat_it = stats.find(item->block().node->id()); + if (stat_it != stats.end()) + { + const auto& stat = stat_it->second; + + fillStatsColumnsSelection(item, &stat, _units); + + auto percent_per_selection = std::min(100, profiler_gui::percent(stat.total_duration, selectionDuration)); + item->setData(COL_PERCENT_SUM_PER_AREA, Qt::UserRole, percent_per_selection); + item->setText(COL_PERCENT_SUM_PER_AREA, QString::number(percent_per_selection)); + + percent_per_selection = std::min(100, profiler_gui::percent(item->block().node->duration(), selectionDuration)); + item->setData(COL_PERCENT_PER_AREA, Qt::UserRole, percent_per_selection); + item->setText(COL_PERCENT_PER_AREA, QString::number(percent_per_selection)); + } + + queue.pop_front(); + } +} + +////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/tree_widget_loader.h b/profiler_gui/tree_widget_loader.h index 2b3a139..fc56fd5 100644 --- a/profiler_gui/tree_widget_loader.h +++ b/profiler_gui/tree_widget_loader.h @@ -14,7 +14,7 @@ * : * * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -68,22 +68,19 @@ class TreeWidgetItem; -#ifndef EASY_TREE_WIDGET__USE_VECTOR using Items = ::std::unordered_map >; -#else -using Items = ::std::vector; -#endif - -using ThreadedItems = ::std::vector<::std::pair >; -using RootsMap = ::std::unordered_map >; -using IdItems = ::std::unordered_map >; +using ThreadedItems = std::vector >; +using RootsMap = std::unordered_map >; +using IdItems = std::unordered_map, estd::hash >; +using StatsMap = std::unordered_map >; ////////////////////////////////////////////////////////////////////////// enum class TreeMode : uint8_t { Full, - Plain + Plain, + SelectedArea }; ////////////////////////////////////////////////////////////////////////// @@ -92,7 +89,6 @@ class TreeWidgetLoader Q_DECL_FINAL { ThreadedItems m_topLevelItems; ///< Items m_items; ///< - IdItems m_iditems; ///< ThreadPoolTask m_worker; ///< std::atomic_bool m_bDone; ///< std::atomic_bool m_bInterrupt; ///< @@ -111,23 +107,118 @@ public: void takeItems(Items& _output); void interrupt(bool _wait = false); - void fillTree(profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const profiler::thread_blocks_tree_t& _blocksTree, TreeMode _mode); - void fillTreeBlocks(const::profiler_gui::TreeBlocks& _blocks, profiler::timestamp_t _beginTime, profiler::timestamp_t _left, profiler::timestamp_t _right, bool _strict, TreeMode _mode); + void fillTreeBlocks( + const::profiler_gui::TreeBlocks& _blocks, + profiler::timestamp_t _beginTime, + profiler::timestamp_t _left, + profiler::timestamp_t _right, + bool _strict, TreeMode _mode + ); private: - bool interrupted() const; + bool interrupted() const volatile; void setDone(); void setProgress(int _progress); - void setTreeInternal1(profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const profiler::thread_blocks_tree_t& _blocksTree, bool _addZeroBlocks, bool _decoratedThreadNames, bool _hexThreadId, ::profiler_gui::TimeUnits _units); - void setTreeInternal2(const profiler::timestamp_t& _beginTime, const ::profiler_gui::TreeBlocks& _blocks, profiler::timestamp_t _left, profiler::timestamp_t _right, bool _strict, bool _addZeroBlocks, bool _decoratedThreadNames, bool _hexThreadId, ::profiler_gui::TimeUnits _units); + void setTreeInternalTop( + const profiler::timestamp_t& _beginTime, + const ::profiler_gui::TreeBlocks& _blocks, + profiler::timestamp_t _left, + profiler::timestamp_t _right, + bool _strict, + bool _addZeroBlocks, + bool _decoratedThreadNames, + bool _hexThreadId, + ::profiler_gui::TimeUnits _units + ); - size_t setTreeInternal(const profiler::BlocksTreeRoot& _threadRoot, profiler::block_index_t _firstCswitch, const profiler::timestamp_t& _beginTime, const profiler::BlocksTree::children_t& _children, TreeWidgetItem* _parent, TreeWidgetItem* _frame, profiler::timestamp_t _left, profiler::timestamp_t _right, bool _strict, profiler::timestamp_t& _duration, bool _addZeroBlocks, ::profiler_gui::TimeUnits _units); - size_t setTreeInternalPlain(const profiler::BlocksTreeRoot& _threadRoot, profiler::block_index_t _firstCswitch, const profiler::timestamp_t& _beginTime, const profiler::BlocksTree::children_t& _children, TreeWidgetItem* _parent, TreeWidgetItem* _frame, profiler::timestamp_t _left, profiler::timestamp_t _right, bool _strict, profiler::timestamp_t& _duration, bool _addZeroBlocks, ::profiler_gui::TimeUnits _units); + size_t setTreeInternal( + const profiler::BlocksTreeRoot& _threadRoot, + IdItems& iditems, + StatsMap& _statsMap, + profiler::block_index_t _firstCswitch, + profiler::timestamp_t _beginTime, + const profiler::BlocksTree::children_t& _children, + TreeWidgetItem* _parent, + TreeWidgetItem* _frame, + profiler::timestamp_t left, + profiler::timestamp_t right, + bool strict, + bool partial_parent, + profiler::timestamp_t& _duration, + bool _addZeroBlocks, + ::profiler_gui::TimeUnits _units, + int _depth + ); + + void setTreeInternalPlainTop( + const profiler::timestamp_t& _beginTime, + const ::profiler_gui::TreeBlocks& _blocks, + profiler::timestamp_t _left, + profiler::timestamp_t _right, + bool _strict, + bool _addZeroBlocks, + bool _decoratedThreadNames, + bool _hexThreadId, + ::profiler_gui::TimeUnits _units + ); + + size_t setTreeInternalPlain( + const profiler::BlocksTreeRoot& threadRoot, + IdItems& iditems, + StatsMap& statsMap, + profiler::block_index_t firstCswitch, + profiler::timestamp_t beginTime, + const profiler::BlocksTree::children_t& children, + TreeWidgetItem* root, + TreeWidgetItem* frame, + profiler::timestamp_t left, + profiler::timestamp_t right, + bool strict, + bool partial_parent, + profiler::timestamp_t& total_duration, + bool _addZeroBlocks, + ::profiler_gui::TimeUnits units, + int depth + ); + + void setTreeInternalAggregateTop( + const profiler::timestamp_t& _beginTime, + const ::profiler_gui::TreeBlocks& _blocks, + profiler::timestamp_t _left, + profiler::timestamp_t _right, + bool _strict, + bool _addZeroBlocks, + bool _decoratedThreadNames, + bool _hexThreadId, + ::profiler_gui::TimeUnits _units + ); + + size_t setTreeInternalAggregate( + const profiler::BlocksTreeRoot& threadRoot, + IdItems& iditems, + StatsMap& statsMap, + profiler::block_index_t firstCswitch, + profiler::timestamp_t beginTime, + const profiler::BlocksTree::children_t& children, + TreeWidgetItem* root, + profiler::timestamp_t left, + profiler::timestamp_t right, + bool strict, + bool partial_parent, + profiler::timestamp_t& total_duration, + bool addZeroBlocks, + ::profiler_gui::TimeUnits units, + int depth + ); profiler::timestamp_t calculateChildrenDurationRecursive(const profiler::BlocksTree::children_t& _children, profiler::block_id_t _id); + profiler::timestamp_t calculateIdleTime(const profiler::BlocksTreeRoot& _threadRoot, profiler::block_index_t& _firstCSwitch, profiler::timestamp_t _begin, profiler::timestamp_t _end) const; + void updateStats(StatsMap& stats, profiler::block_id_t id, profiler::block_index_t index, profiler::timestamp_t duration, profiler::timestamp_t children_duration) const; + void fillStatsForTree(TreeWidgetItem* root, const StatsMap& stats, profiler_gui::TimeUnits _units, profiler::timestamp_t selectionDuration) const; + }; // END of class TreeWidgetLoader. ////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/window_header.cpp b/profiler_gui/window_header.cpp index 64d727b..3d9d204 100644 --- a/profiler_gui/window_header.cpp +++ b/profiler_gui/window_header.cpp @@ -8,7 +8,7 @@ * description : The file contains implementation of WindowHeader. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -60,11 +60,11 @@ static void setButtonSize(QPushButton* button, int size) { if (button != nullptr) - button->setFixedSize(size * 5 / 4, size); + button->setFixedSize(size * 191 / 100, size); } -WindowHeader::WindowHeader(const QString& title, Buttons buttons, QWidget* parent) - : Parent(parent) +WindowHeader::WindowHeader(const QString& title, Buttons buttons, QWidget& parentRef) + : Parent(&parentRef) , m_minimizeButton(nullptr) , m_maximizeButton(nullptr) , m_closeButton(nullptr) @@ -72,6 +72,8 @@ WindowHeader::WindowHeader(const QString& title, Buttons buttons, QWidget* paren , m_title(new QLabel(title)) , m_isDragging(false) { + auto parent = &parentRef; + m_title->installEventFilter(this); m_pixmap->installEventFilter(this); @@ -85,7 +87,7 @@ WindowHeader::WindowHeader(const QString& title, Buttons buttons, QWidget* paren { m_minimizeButton = new QPushButton(); m_minimizeButton->setObjectName("WindowHeader_MinButton"); - connect(m_minimizeButton, &QPushButton::clicked, this, &This::onMinimizeClicked); + connect(m_minimizeButton, &QPushButton::clicked, this, &This::onMinimizeClicked, Qt::QueuedConnection); } if (buttons.testFlag(WindowHeader::MaximizeButton)) @@ -93,16 +95,23 @@ WindowHeader::WindowHeader(const QString& title, Buttons buttons, QWidget* paren m_maximizeButton = new QPushButton(); m_maximizeButton->setProperty("max", parent->isMaximized()); m_maximizeButton->setObjectName("WindowHeader_MaxButton"); - connect(m_maximizeButton, &QPushButton::clicked, this, &This::onMaximizeClicked); + connect(m_maximizeButton, &QPushButton::clicked, this, &This::onMaximizeClicked, Qt::QueuedConnection); } if (buttons.testFlag(WindowHeader::CloseButton)) { m_closeButton = new QPushButton(); m_closeButton->setObjectName("WindowHeader_CloseButton"); - connect(m_closeButton, &QPushButton::clicked, parent, &QWidget::close); + connect(m_closeButton, &QPushButton::clicked, parent, &QWidget::close, Qt::QueuedConnection); } +#if !defined(_WIN32) + if (m_maximizeButton != nullptr || m_minimizeButton != nullptr) + { + parent->setWindowFlag(Qt::SubWindow, true); + } +#endif + auto layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(1); @@ -157,9 +166,9 @@ WindowHeader::WindowHeader(const QString& title, Buttons buttons, QWidget* paren this, &This::onWindowHeaderPositionChanged); if (EASY_GLOBALS.use_custom_window_header) - parentWidget()->setWindowFlags(parentWidget()->windowFlags() | Qt::FramelessWindowHint); + parent->setWindowFlags(parentWidget()->windowFlags() | Qt::FramelessWindowHint); else - parentWidget()->setWindowFlags(parentWidget()->windowFlags() & ~Qt::FramelessWindowHint); + parent->setWindowFlags(parentWidget()->windowFlags() & ~Qt::FramelessWindowHint); setVisible(EASY_GLOBALS.use_custom_window_header); } @@ -332,9 +341,13 @@ void WindowHeader::onMaximizeClicked(bool) { auto parent = parentWidget(); if (parent->isMaximized()) + { parent->showNormal(); + } else + { parent->showMaximized(); + } } void WindowHeader::onMinimizeClicked(bool) diff --git a/profiler_gui/window_header.h b/profiler_gui/window_header.h index f1edd90..3ea27ad 100644 --- a/profiler_gui/window_header.h +++ b/profiler_gui/window_header.h @@ -9,7 +9,7 @@ * : for standard system window header. * ----------------- : * license : Lightweight profiler library for c++ -* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin +* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) @@ -86,7 +86,7 @@ public: Q_DECLARE_FLAGS(Buttons, Button) Q_FLAG(Buttons) - explicit WindowHeader(const QString& title, Buttons buttons, QWidget* parent); + explicit WindowHeader(const QString& title, Buttons buttons, QWidget& parentRef); ~WindowHeader() override; void setTitle(const QString& title);