diff --git a/src/view/src/rocprofvis_flame_track_item.cpp b/src/view/src/rocprofvis_flame_track_item.cpp index 8fce75f3..fc3d4f47 100644 --- a/src/view/src/rocprofvis_flame_track_item.cpp +++ b/src/view/src/rocprofvis_flame_track_item.cpp @@ -32,6 +32,9 @@ inline constexpr float TOOLTIP_OFFSET = 16.0f; inline constexpr int MAX_CHARACTERS_PER_LINE = 40; inline constexpr float MAX_TABLE_HEIGHT = 300.0f; inline constexpr float PILL_SPACING = 4.0f; +// Absolute floor for flame-track height so the meta area never collapses far +// enough for its controls to collide, regardless of the event row height. +inline constexpr float FLAME_MIN_TRACK_HEIGHT = 28.0f; /* For IMGUI rectangle borders ANTI_ALIASING_WORKAROUND is needed to avoid anti-aliasing @@ -58,6 +61,8 @@ FlameTrackItem::FlameTrackItem(DataProvider& dp, , m_event_color_mode(EventColorMode::kByEventName) , m_text_padding(SettingsManager::GetInstance().GetDefaultIMGUIStyle().FramePadding) , m_level_height(SettingsManager::GetInstance().GetEventLevelHeight()) +, m_min_level(0.0f) +, m_max_level(0.0f) , m_timeline_selection(timeline_selection) , m_measurement(measurement) , m_deferred_click_handled(false) @@ -106,14 +111,24 @@ FlameTrackItem::FlameTrackItem(DataProvider& dp, static_cast(RocEvents::kTimelineEventHighlightChanged), timeline_highlight_changed_handler); + auto font_size_changed_handler = [this](std::shared_ptr e) { + this->HandleFontSizeChanged(e); + }; + + m_font_size_changed_token = EventManager::GetInstance()->Subscribe( + static_cast(RocEvents::kFontSizeChanged), font_size_changed_handler); + if(m_flame_track_project_settings.Valid()) { m_event_color_mode = m_flame_track_project_settings.ColorEvents(); m_compact_mode = m_flame_track_project_settings.CompactMode(); - if(m_compact_mode) - { - m_level_height = m_settings.GetEventLevelCompactHeight(); - } + } + + RefreshLevelHeight(); + + if(!HasSavedTrackHeight()) + { + m_track_height = DefaultTrackHeight(); } } @@ -129,9 +144,14 @@ FlameTrackItem::RenderMetaAreaExpand() ImVec2(ImGui::GetContentRegionMax() - m_metadata_padding - ImVec2(ImGui::GetTextLineHeight() + m_meta_area_scale_width, ImGui::GetTextLineHeight())); - int visible_levels = static_cast(std::ceil(m_track_height / m_level_height)); - - if(visible_levels <= m_max_level + 1) + // Height that fully shows every event level (levels are 0-indexed), and the + // natural resting height the expand action restores to. + const float default_height = DefaultTrackHeight(); + const float full_height = (m_max_level + 1.0f) * m_level_height; + const float target_height = std::max(full_height, default_height); + constexpr float EXPAND_EPSILON = 1.0f; + + if(m_track_height + EXPAND_EPSILON < target_height) { ImGui::SetCursorPos(button_pos); if(ImGui::ArrowButton("##expand", ImGuiDir_Down)) @@ -141,15 +161,12 @@ FlameTrackItem::RenderMetaAreaExpand() } if(ImGui::IsItemHovered()) SetTooltipStyled("Expand track to see all events"); } - else if(m_track_height > - std::max(m_max_level * m_level_height + m_level_height, - DEFAULT_TRACK_HEIGHT)) // stand-in for default height.. + else if(m_track_height > default_height + EXPAND_EPSILON) { ImGui::SetCursorPos(button_pos); if(ImGui::ArrowButton("##contract", ImGuiDir_Up)) { - m_track_height = - DEFAULT_TRACK_HEIGHT; // Default track height defined in parent class. + m_track_height = default_height; m_track_height_changed = true; m_is_expanded = false; } @@ -166,6 +183,8 @@ FlameTrackItem::~FlameTrackItem() EventManager::GetInstance()->Unsubscribe( static_cast(RocEvents::kTimelineEventHighlightChanged), m_timeline_event_highlight_changed_token); + EventManager::GetInstance()->Unsubscribe( + static_cast(RocEvents::kFontSizeChanged), m_font_size_changed_token); } bool @@ -337,6 +356,64 @@ FlameTrackItem::HandleTimelineHighlightChanged(std::shared_ptr e) } } +void +FlameTrackItem::HandleFontSizeChanged(std::shared_ptr e) +{ + (void) e; + const float previous_level_height = m_level_height; + RefreshLevelHeight(); + if(m_is_expanded) + { + RecalculateTrackHeight(); + } + else if(previous_level_height > 0.0f && m_level_height != previous_level_height) + { + const float scale = m_level_height / previous_level_height; + m_track_height = std::max(m_min_track_height, m_track_height * scale); + m_track_height_changed = true; + } +} + +float +FlameTrackItem::CalculateCenteredTextY(const std::string& label, float rect_min_y, + float box_height) const +{ + ImFontBaked* baked = ImGui::GetFontBaked(); + float min_y = std::numeric_limits::max(); + float max_y = std::numeric_limits::lowest(); + + if(baked != nullptr) + { + const float font_size = ImGui::GetFontSize(); + const float scale = baked->Size > 0.0f ? font_size / baked->Size : 1.0f; + + for(char c : label) + { + unsigned char codepoint = static_cast(c); + if(codepoint == 0 || codepoint >= 0x80) + { + continue; + } + + ImFontGlyph* glyph = baked->FindGlyph(codepoint); + if(glyph != nullptr && glyph->Visible) + { + min_y = std::min(min_y, glyph->Y0 * scale); + max_y = std::max(max_y, glyph->Y1 * scale); + } + } + } + + if(min_y == std::numeric_limits::max()) + { + min_y = 0.0f; + max_y = ImGui::GetTextLineHeight(); + } + + const float glyph_center = (min_y + max_y) * 0.5f; + return rect_min_y + std::max(0.0f, box_height * 0.5f - glyph_center); +} + void FlameTrackItem::DrawBox(ImVec2 start_position, int color_index, ChartItem& chart_item, float duration, ImDrawList* draw_list, bool use_highlight_color) @@ -344,9 +421,11 @@ FlameTrackItem::DrawBox(ImVec2 start_position, int color_index, ChartItem& chart ImVec2 cursor_position = ImGui::GetCursorScreenPos(); ImVec2 content_size = ImGui::GetContentRegionAvail(); + const float box_height = std::max(1.0f, m_level_height - m_settings.GetEventLevelSpacing()); + ImVec2 rectMin = ImVec2(start_position.x, start_position.y + cursor_position.y); ImVec2 rectMax = ImVec2(start_position.x + duration, - start_position.y + m_level_height + cursor_position.y); + start_position.y + box_height + cursor_position.y); ImU32 rectColor; if(use_highlight_color) @@ -369,13 +448,15 @@ FlameTrackItem::DrawBox(ImVec2 start_position, int color_index, ChartItem& chart if(rectMax.x - rectMin.x > MIN_LABEL_WIDTH) { draw_list->PushClipRect(rectMin, rectMax, true); - ImVec2 textPos = - ImVec2(rectMin.x + m_text_padding.x, rectMin.y + m_text_padding.y); + const std::string label = + (chart_item.event.m_child_count > 1) + ? std::to_string(chart_item.event.m_child_count) + " events" + : chart_item.event.m_name; + const float text_y = CalculateCenteredTextY(label, rectMin.y, box_height); + ImVec2 textPos = ImVec2(rectMin.x + m_text_padding.x, text_y); if(chart_item.event.m_child_count > 1) { - std::string label = - std::to_string(chart_item.event.m_child_count) + " events"; draw_list->AddText(textPos, m_settings.GetColor(Colors::kTextMain), label.c_str()); } @@ -386,16 +467,15 @@ FlameTrackItem::DrawBox(ImVec2 start_position, int color_index, ChartItem& chart { // If the rectangle is partially outside the viewport then start rendering // the text at the viewport edge to maintain readability. - textPos = ImVec2(draw_list->GetClipRectMin().x + m_text_padding.x, - rectMin.y + m_text_padding.y); + textPos = ImVec2(draw_list->GetClipRectMin().x + m_text_padding.x, text_y); draw_list->AddText(textPos, m_settings.GetColor(Colors::kTextMain), - chart_item.event.m_name.c_str()); + label.c_str()); } else { // The rectangle is fully inside the viewport, render text normally. draw_list->AddText(textPos, m_settings.GetColor(Colors::kTextMain), - chart_item.event.m_name.c_str()); + label.c_str()); } } draw_list->PopClipRect(); @@ -647,10 +727,30 @@ void FlameTrackItem::RecalculateTrackHeight() { m_track_height = std::max(m_max_level * m_level_height + m_level_height + 2.0f, - DEFAULT_TRACK_HEIGHT); + DefaultTrackHeight()); m_track_height_changed = true; } +void +FlameTrackItem::UpdateMinTrackHeight() +{ + m_min_track_height = std::max(FLAME_MIN_TRACK_HEIGHT, m_level_height); +} + +void +FlameTrackItem::RefreshLevelHeight() +{ + m_level_height = m_compact_mode ? m_settings.GetEventLevelCompactHeight() + : m_settings.GetEventLevelHeight(); + UpdateMinTrackHeight(); +} + +float +FlameTrackItem::DefaultTrackHeight() const +{ + return 2.0f * m_level_height; +} + void FlameTrackItem::RenderChart(float graph_width) { @@ -733,13 +833,17 @@ FlameTrackItem::RenderChart(float graph_width) ImVec2 cursor_position = ImGui::GetCursorScreenPos(); + // Match the shortened event box so the outline hugs the drawn rectangle. + const float box_height = + std::max(1.0f, m_level_height - m_settings.GetEventLevelSpacing()); + ImVec2 rectMin = ImVec2(start_position.x - HIGHLIGHT_THICKNESS_HALF, start_position.y + cursor_position.y + HIGHLIGHT_THICKNESS_HALF - ANTI_ALIASING_WORKAROUND); ImVec2 rectMax = ImVec2(start_position.x + static_cast(normalized_duration) + HIGHLIGHT_THICKNESS_HALF, - start_position.y + m_level_height + cursor_position.y - + start_position.y + box_height + cursor_position.y - HIGHLIGHT_THICKNESS_HALF + ANTI_ALIASING_WORKAROUND); ImU32 border_color = item.highlighted @@ -842,19 +946,13 @@ FlameTrackItem::RenderMetaAreaOptions() if(ImGui::Checkbox("Compact Mode", &m_compact_mode)) { - if(m_compact_mode) - { - m_level_height = m_settings.GetEventLevelCompactHeight(); - } - else + RefreshLevelHeight(); + if(!m_compact_mode && m_is_expanded) { - m_level_height = m_settings.GetEventLevelHeight(); - if(m_is_expanded) - { - RecalculateTrackHeight(); - } + RecalculateTrackHeight(); } - if(m_track_height > std::max(m_max_level * m_level_height + m_level_height, DEFAULT_TRACK_HEIGHT)) + if(m_track_height > + std::max(m_max_level * m_level_height + m_level_height, DefaultTrackHeight())) { RecalculateTrackHeight(); } diff --git a/src/view/src/rocprofvis_flame_track_item.h b/src/view/src/rocprofvis_flame_track_item.h index 7c00a7e5..81e8e152 100644 --- a/src/view/src/rocprofvis_flame_track_item.h +++ b/src/view/src/rocprofvis_flame_track_item.h @@ -89,6 +89,7 @@ class FlameTrackItem : public TrackItem void HandleTimelineSelectionChanged(std::shared_ptr e); void HandleTimelineHighlightChanged(std::shared_ptr e); + void HandleFontSizeChanged(std::shared_ptr e); void DrawBox(ImVec2 start_position, int boxplot_box_id, ChartItem& flame, float duration, ImDrawList* draw_list, bool use_highlight_color); @@ -99,6 +100,11 @@ class FlameTrackItem : public TrackItem void RenderTooltip(ChartItem& chart_item, int color_index); void RecalculateTrackHeight(); + void UpdateMinTrackHeight(); + void RefreshLevelHeight(); + float DefaultTrackHeight() const; + float CalculateCenteredTextY(const std::string& label, float rect_min_y, + float box_height) const; void RequestAnalysis() override; @@ -118,6 +124,7 @@ class FlameTrackItem : public TrackItem std::vector m_selected_chart_items; EventManager::SubscriptionToken m_timeline_event_selection_changed_token; EventManager::SubscriptionToken m_timeline_event_highlight_changed_token; + EventManager::SubscriptionToken m_font_size_changed_token; ImVec2 m_tooltip_size; static float s_max_event_label_width; diff --git a/src/view/src/rocprofvis_settings_manager.cpp b/src/view/src/rocprofvis_settings_manager.cpp index 1dc0b690..2b6cfe74 100644 --- a/src/view/src/rocprofvis_settings_manager.cpp +++ b/src/view/src/rocprofvis_settings_manager.cpp @@ -12,6 +12,7 @@ #include "rocprofvis_utils.h" #include +#include #include #include @@ -286,8 +287,9 @@ inline constexpr const char* FLAME_LIGHT_COLORMAP_NAME = "flame_light"; inline constexpr const char* CONTRAST_DARK_COLORMAP_NAME = "contrast_dark"; inline constexpr const char* CONTRAST_LIGHT_COLORMAP_NAME = "contrast_light"; inline constexpr const char* SETTINGS_FILE_NAME = "settings_application.json"; -inline constexpr float EVENT_LEVEL_HEIGHT = 40.0f; inline constexpr float COMPACT_EVENT_HEIGHT = 6.0f; +inline constexpr float EVENT_LEVEL_VERTICAL_MARGIN = 6.0f; +inline constexpr float EVENT_LEVEL_SPACING = 1.0f; SettingsManager& SettingsManager::GetInstance() @@ -824,7 +826,8 @@ SettingsManager::DeserializeUnitSettings(jt::Json& json) const float SettingsManager::GetEventLevelHeight() const { - return EVENT_LEVEL_HEIGHT; + const float font_size = m_font_manager.GetFontSize(FontSize::kDefault); + return std::ceil(font_size + EVENT_LEVEL_VERTICAL_MARGIN + EVENT_LEVEL_SPACING); } const float @@ -833,6 +836,12 @@ SettingsManager::GetEventLevelCompactHeight() const return COMPACT_EVENT_HEIGHT; } +const float +SettingsManager::GetEventLevelSpacing() const +{ + return EVENT_LEVEL_SPACING; +} + void SettingsManager::SerializeHotkeySettings(jt::Json& json) { diff --git a/src/view/src/rocprofvis_settings_manager.h b/src/view/src/rocprofvis_settings_manager.h index 0d21a2f6..11e5c836 100644 --- a/src/view/src/rocprofvis_settings_manager.h +++ b/src/view/src/rocprofvis_settings_manager.h @@ -251,6 +251,7 @@ class SettingsManager // Constant for event height; const float GetEventLevelHeight() const; const float GetEventLevelCompactHeight() const; + const float GetEventLevelSpacing() const; private: diff --git a/src/view/src/rocprofvis_track_item.cpp b/src/view/src/rocprofvis_track_item.cpp index c5706588..f2e43919 100644 --- a/src/view/src/rocprofvis_track_item.cpp +++ b/src/view/src/rocprofvis_track_item.cpp @@ -25,6 +25,8 @@ inline constexpr float META_TOOLTIP_MAX_WIDTH = 320.0f; inline constexpr uint64_t META_TOOLTIP_COMPACT_COUNT_MIN = 1000; inline constexpr float NAME_LABEL_HITBOX_PADDING_X = 4.0f; inline constexpr float NAME_LABEL_HITBOX_PADDING_Y = 3.0f; +inline constexpr float PILL_BOTTOM_INSET = 2.0f; +inline constexpr float PILL_TITLE_MIN_GAP = 2.0f; constexpr const char* TRACK_COPY_MENU_POPUP_NAME = "TrackCopyMenu"; float TrackItem::s_metadata_width = 400.0f; @@ -38,7 +40,7 @@ TrackItem::TrackItem(DataProvider& dp, uint64_t id, , m_track_content_height(0.0f) , m_min_track_height(DEFAULT_MIN_TRACK_HEIGHT) , m_is_in_view_vertical(false) -, m_metadata_padding(ImVec2(8.0f, 5.0f)) +, m_metadata_padding(ImVec2(8.0f, 2.0f)) , m_resize_grip_thickness(4.0f) , m_request_state(TrackDataRequestState::kIdle) , m_track_height_changed(false) @@ -75,6 +77,12 @@ TrackItem::TrackItem(DataProvider& dp, uint64_t id, SetDefaultPillLabel(track_info); } +bool +TrackItem::HasSavedTrackHeight() const +{ + return m_track_project_settings.Valid(); +} + bool TrackItem::TrackHeightChanged() { @@ -230,7 +238,7 @@ TrackItem::RenderMetaArea() ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(4, 4)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4, 4)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 3)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(5, 5)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(5, 2)); ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, m_settings.GetDefaultStyle().ChildRounding); @@ -306,10 +314,18 @@ TrackItem::RenderMetaArea() float available_for_text = content_size.x - m_meta_area_scale_width - m_reorder_grip_width; + ImGui::PushFont(m_settings.GetFontManager().GetFont(FontType::kDefault), + m_settings.GetFontManager().GetFontSize(FontSize::kSmall)); + ImVec2 text_size = ImGui::CalcTextSize(m_meta_area_label.c_str()); + const float pill_vertical_budget = content_size.y - text_size.y; + const float pill_vertical_needed = m_pill.GetPillSize().y + + m_metadata_padding.y + PILL_BOTTOM_INSET + + PILL_TITLE_MIN_GAP; + if(available_for_text < m_pill.GetPillSize().x || - content_size.y - text_size.y < m_pill.GetPillSize().y) + pill_vertical_budget < pill_vertical_needed) m_pill.Hide(); else m_pill.Show(); @@ -332,6 +348,7 @@ TrackItem::RenderMetaArea() } ImGui::PopStyleColor(); ImGui::EndGroup(); + ImGui::PopFont(); RenderMetaAreaScale(); RenderMetaAreaExpand(); @@ -1004,7 +1021,11 @@ Pill::GetPillSize() void Pill::CalculatePillSize() { + SettingsManager& settings = SettingsManager::GetInstance(); + ImGui::PushFont(settings.GetFontManager().GetFont(FontType::kDefault), + settings.GetFontManager().GetFontSize(FontSize::kSmall)); ImVec2 text_size = ImGui::CalcTextSize(m_pill_label.c_str()); + ImGui::PopFont(); m_pillbox_size = ImVec2(text_size.x + 2 * m_padding_x, text_size.y + 2 * m_padding_y); } diff --git a/src/view/src/rocprofvis_track_item.h b/src/view/src/rocprofvis_track_item.h index 4099adbb..a3991241 100644 --- a/src/view/src/rocprofvis_track_item.h +++ b/src/view/src/rocprofvis_track_item.h @@ -73,7 +73,7 @@ class Pill std::string m_tooltip_label; ImVec2 m_pillbox_size; const float m_padding_x = 8.0f; - const float m_padding_y = 2.0f; + const float m_padding_y = 1.0f; EventManager::SubscriptionToken m_font_changed_token; }; @@ -134,6 +134,9 @@ class TrackItem void FetchHelper(); void SetDefaultPillLabel(const TrackInfo* track_info); void SetMetaAreaLabel(const TrackInfo* track_info); + // True if this track has a persisted (user-chosen) height in the project, + // so subclasses can avoid clobbering it with their own default. + bool HasSavedTrackHeight() const; const TrackInfo* m_track_metadata; uint64_t m_track_id;