Merge pull request #519 from tvoeroes/vertical-timeline-rescale-center

Vertical timeline centering
This commit is contained in:
Bartosz Taudul 2023-01-31 21:02:51 +01:00 committed by GitHub
commit a26df1d810
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 157 additions and 41 deletions

View File

@ -1,3 +1,5 @@
#include <algorithm>
#include "imgui.h" #include "imgui.h"
#include "TracyTimelineController.hpp" #include "TracyTimelineController.hpp"
@ -8,6 +10,8 @@ namespace tracy
TimelineController::TimelineController( View& view, Worker& worker ) TimelineController::TimelineController( View& view, Worker& worker )
: m_height( 0 ) : m_height( 0 )
, m_scroll( 0 ) , m_scroll( 0 )
, m_centerItemkey( nullptr )
, m_centerItemOffsetY( 0 )
, m_firstFrame( true ) , m_firstFrame( true )
, m_view( view ) , m_view( view )
, m_worker( worker ) , m_worker( worker )
@ -24,17 +28,107 @@ void TimelineController::Begin()
m_items.clear(); m_items.clear();
} }
void TimelineController::End( double pxns, int offset, const ImVec2& wpos, bool hover, float yMin, float yMax ) void TimelineController::UpdateCenterItem()
{ {
ImVec2 mousePos = ImGui::GetMousePos();
m_centerItemkey = nullptr;
m_centerItemOffsetY = 0;
if( m_firstFrame || !ImGui::IsMousePosValid( &mousePos ) ) return;
const auto timelineMousePosY = mousePos.y - ImGui::GetWindowPos().y;
int centerY = timelineMousePosY + ImGui::GetScrollY();
int yBegin = 0;
int yEnd = 0;
for( auto& item : m_items ) for( auto& item : m_items )
{ {
item->Draw( m_firstFrame, pxns, offset, wpos, hover, yMin, yMax ); m_centerItemkey = item->GetKey();
yBegin = yEnd;
yEnd += item->GetNextFrameHeight();
const auto inLowerBounds = m_centerItemkey == m_items.front()->GetKey() || yBegin <= centerY;
const auto inUpperBounds = m_centerItemkey == m_items.back()->GetKey() || centerY < yEnd;
if( inLowerBounds && inUpperBounds )
{
m_centerItemOffsetY = centerY - yBegin;
break;
}
}
}
std::optional<int> TimelineController::CalculateScrollPosition() const
{
if( !m_centerItemkey ) return std::nullopt;
ImVec2 mousePos = ImGui::GetMousePos();
if( !ImGui::IsMousePosValid( &mousePos ) ) return std::nullopt;
const auto timelineMousePosY = mousePos.y - ImGui::GetWindowPos().y;
int yBegin = 0;
int yEnd = 0;
for( auto& item : m_items )
{
yBegin = yEnd;
yEnd += item->GetNextFrameHeight();
if( item->GetKey() != m_centerItemkey ) continue;
int scrollY = yBegin + m_centerItemOffsetY - timelineMousePosY;
return scrollY;
}
return std::nullopt;
}
void TimelineController::End( double pxns, const ImVec2& wpos, bool hover, bool vcenter, float yMin, float yMax )
{
auto shouldUpdateCenterItem = [&] () {
const auto imguiChangedScroll = m_scroll != ImGui::GetScrollY();
const auto& mouseDelta = ImGui::GetIO().MouseDelta;
const auto mouseMoved = mouseDelta.x != 0.0f || mouseDelta.y != 0.0f;
const auto& mousePos = ImGui::GetIO().MousePos;
const auto mouseVisible = ImGui::IsMousePosValid( &mousePos );
return ( ( imguiChangedScroll || mouseMoved || !mouseVisible ) && !ImGui::IsMouseDown( 1 ) ) || !m_centerItemkey;
};
if( !vcenter )
{
m_centerItemkey = nullptr;
m_centerItemOffsetY = 0;
}
else if( shouldUpdateCenterItem() )
{
UpdateCenterItem();
}
int yOffset = 0;
for( auto& item : m_items )
{
auto currentFrameItemHeight = item->GetNextFrameHeight();
item->Draw( m_firstFrame, pxns, yOffset, wpos, hover, yMin, yMax );
if( m_firstFrame ) currentFrameItemHeight = item->GetNextFrameHeight();
yOffset += currentFrameItemHeight;
}
if( const auto scrollY = CalculateScrollPosition() )
{
int clampedScrollY = std::min<int>( *scrollY, yOffset );
ImGui::SetScrollY( clampedScrollY );
int minHeight = ImGui::GetWindowHeight() + clampedScrollY;
yOffset = std::max( yOffset, minHeight );
} }
const auto scrollPos = ImGui::GetScrollY(); const auto scrollPos = ImGui::GetScrollY();
if( ( scrollPos == 0 && m_scroll != 0 ) || offset > m_height ) if( ( scrollPos == 0 && m_scroll != 0 ) || yOffset > m_height )
{ {
m_height = offset; m_height = yOffset;
} }
m_scroll = scrollPos; m_scroll = scrollPos;
} }

View File

@ -2,6 +2,7 @@
#define __TRACYTIMELINECONTROLLER_HPP__ #define __TRACYTIMELINECONTROLLER_HPP__
#include <assert.h> #include <assert.h>
#include <optional>
#include <vector> #include <vector>
#include "../public/common/TracyForceInline.hpp" #include "../public/common/TracyForceInline.hpp"
@ -18,7 +19,7 @@ public:
void FirstFrameExpired(); void FirstFrameExpired();
void Begin(); void Begin();
void End( double pxns, int offset, const ImVec2& wpos, bool hover, float yMin, float yMax ); void End( double pxns, const ImVec2& wpos, bool hover, bool vcenter, float yMin, float yMax );
template<class T, class U> template<class T, class U>
void AddItem( U* data ) void AddItem( U* data )
@ -39,12 +40,18 @@ public:
} }
private: private:
void UpdateCenterItem();
std::optional<int> CalculateScrollPosition() const;
std::vector<TimelineItem*> m_items; std::vector<TimelineItem*> m_items;
unordered_flat_map<const void*, std::unique_ptr<TimelineItem>> m_itemMap; unordered_flat_map<const void*, std::unique_ptr<TimelineItem>> m_itemMap;
float m_height; float m_height;
float m_scroll; float m_scroll;
const void* m_centerItemkey;
int m_centerItemOffsetY;
bool m_firstFrame; bool m_firstFrame;
View& m_view; View& m_view;

View File

@ -8,20 +8,24 @@
namespace tracy namespace tracy
{ {
TimelineItem::TimelineItem( View& view, Worker& worker ) TimelineItem::TimelineItem( View& view, Worker& worker, const void* key )
: m_visible( true ) : m_visible( true )
, m_showFull( true ) , m_showFull( true )
, m_height( 0 ) , m_height( 0 )
, m_key( key )
, m_view( view ) , m_view( view )
, m_worker( worker ) , m_worker( worker )
{ {
} }
void TimelineItem::Draw( bool firstFrame, double pxns, int& offset, const ImVec2& wpos, bool hover, float yMin, float yMax ) void TimelineItem::Draw( bool firstFrame, double pxns, int yOffset, const ImVec2& wpos, bool hover, float yMin, float yMax )
{ {
const auto yBegin = yOffset;
auto yEnd = yOffset;
if( !IsVisible() ) if( !IsVisible() )
{ {
if( m_height != 0 ) AdjustThreadHeight( firstFrame, offset, offset ); if( m_height != 0 ) AdjustThreadHeight( firstFrame, yBegin, yEnd );
return; return;
} }
if( IsEmpty() ) return; if( IsEmpty() ) return;
@ -29,32 +33,31 @@ void TimelineItem::Draw( bool firstFrame, double pxns, int& offset, const ImVec2
const auto w = ImGui::GetContentRegionAvail().x - 1; const auto w = ImGui::GetContentRegionAvail().x - 1;
const auto ty = ImGui::GetTextLineHeight(); const auto ty = ImGui::GetTextLineHeight();
const auto ostep = ty + 1; const auto ostep = ty + 1;
const auto yPos = wpos.y + offset; const auto yPos = wpos.y + yBegin;
const auto dpos = wpos + ImVec2( 0.5f, 0.5f ); const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
const auto oldOffset = offset;
auto draw = ImGui::GetWindowDrawList(); auto draw = ImGui::GetWindowDrawList();
ImGui::PushID( this ); ImGui::PushID( this );
ImGui::PushClipRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + m_height ), true ); ImGui::PushClipRect( wpos + ImVec2( 0, yBegin ), wpos + ImVec2( w, yBegin + m_height ), true );
offset += ostep; yEnd += ostep;
if( m_showFull ) if( m_showFull )
{ {
if( !DrawContents( pxns, offset, wpos, hover, yMin, yMax ) && !m_view.GetViewData().drawEmptyLabels ) if( !DrawContents( pxns, yEnd, wpos, hover, yMin, yMax ) && !m_view.GetViewData().drawEmptyLabels )
{ {
offset = oldOffset; yEnd = yBegin;
AdjustThreadHeight( firstFrame, oldOffset, offset ); AdjustThreadHeight( firstFrame, yBegin, yEnd );
ImGui::PopClipRect(); ImGui::PopClipRect();
ImGui::PopID(); ImGui::PopID();
return; return;
} }
} }
DrawOverlay( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( w, offset ) ); DrawOverlay( wpos + ImVec2( 0, yBegin ), wpos + ImVec2( w, yEnd ) );
ImGui::PopClipRect(); ImGui::PopClipRect();
float labelWidth; float labelWidth;
const auto hdrOffset = oldOffset; const auto hdrOffset = yBegin;
const bool drawHeader = yPos + ty >= yMin && yPos <= yMax; const bool drawHeader = yPos + ty >= yMin && yPos <= yMax;
if( drawHeader ) if( drawHeader )
{ {
@ -112,39 +115,38 @@ void TimelineItem::Draw( bool firstFrame, double pxns, int& offset, const ImVec2
ImGui::EndPopup(); ImGui::EndPopup();
} }
offset += 0.2f * ostep; yEnd += 0.2f * ostep;
AdjustThreadHeight( firstFrame, oldOffset, offset ); AdjustThreadHeight( firstFrame, yBegin, yEnd );
ImGui::PopID(); ImGui::PopID();
} }
void TimelineItem::AdjustThreadHeight( bool firstFrame, int oldOffset, int& offset ) void TimelineItem::AdjustThreadHeight( bool firstFrame, int yBegin, int yEnd )
{ {
const auto speed = 4.0; const auto speed = 4.0;
const auto baseMove = 1.0; const auto baseMove = 1.0;
const auto h = offset - oldOffset; const auto newHeight = yEnd - yBegin;
if( firstFrame ) if( firstFrame )
{ {
m_height = h; m_height = newHeight;
} }
else if( m_height != h ) else if( m_height != newHeight )
{ {
const auto diff = h - m_height; const auto diff = newHeight - m_height;
const auto preClampMove = diff * speed * ImGui::GetIO().DeltaTime; const auto preClampMove = diff * speed * ImGui::GetIO().DeltaTime;
if( diff > 0 ) if( diff > 0 )
{ {
const auto move = preClampMove + baseMove; const auto move = preClampMove + baseMove;
m_height = int( std::min<double>( m_height + move, h ) ); m_height = int( std::min<double>( m_height + move, newHeight ) );
} }
else else
{ {
const auto move = preClampMove - baseMove; const auto move = preClampMove - baseMove;
m_height = int( std::max<double>( m_height + move, h ) ); m_height = int( std::max<double>( m_height + move, newHeight ) );
} }
s_wasActive = true; s_wasActive = true;
} }
offset = oldOffset + m_height;
} }
void TimelineItem::VisibilityCheckbox() void TimelineItem::VisibilityCheckbox()

View File

@ -14,10 +14,11 @@ class Worker;
class TimelineItem class TimelineItem
{ {
public: public:
TimelineItem( View& view, Worker& worker ); TimelineItem( View& view, Worker& worker, const void* key );
virtual ~TimelineItem() = default; virtual ~TimelineItem() = default;
void Draw( bool firstFrame, double pxns, int& offset, const ImVec2& wpos, bool hover, float yMin, float yMax ); // draws the timeilne item and also updates the next frame height value
void Draw( bool firstFrame, double pxns, int yOffset, const ImVec2& wpos, bool hover, float yMin, float yMax );
void VisibilityCheckbox(); void VisibilityCheckbox();
virtual void SetVisible( bool visible ) { m_visible = visible; } virtual void SetVisible( bool visible ) { m_visible = visible; }
@ -25,6 +26,11 @@ public:
void SetShowFull( bool showFull ) { m_showFull = showFull; } void SetShowFull( bool showFull ) { m_showFull = showFull; }
// returns 0 instead of the correct value for the first frame
int GetNextFrameHeight() const { return m_height; }
const void* GetKey() const { return m_key; }
protected: protected:
virtual uint32_t HeaderColor() const = 0; virtual uint32_t HeaderColor() const = 0;
virtual uint32_t HeaderColorInactive() const = 0; virtual uint32_t HeaderColorInactive() const = 0;
@ -46,10 +52,12 @@ protected:
bool m_showFull; bool m_showFull;
private: private:
void AdjustThreadHeight( bool firstFrame, int oldOffset, int& offset ); void AdjustThreadHeight( bool firstFrame, int yBegin, int yEnd );
int m_height; int m_height;
const void* m_key;
protected: protected:
View& m_view; View& m_view;
Worker& m_worker; Worker& m_worker;

View File

@ -8,8 +8,8 @@
namespace tracy namespace tracy
{ {
TimelineItemCpuData::TimelineItemCpuData( View& view, Worker& worker, void* ) TimelineItemCpuData::TimelineItemCpuData( View& view, Worker& worker, void* key )
: TimelineItem( view, worker ) : TimelineItem( view, worker, key )
{ {
} }

View File

@ -10,7 +10,7 @@ namespace tracy
class TimelineItemCpuData final : public TimelineItem class TimelineItemCpuData final : public TimelineItem
{ {
public: public:
TimelineItemCpuData( View& view, Worker& worker, void* ); TimelineItemCpuData( View& view, Worker& worker, void* key );
void SetVisible( bool visible ) override; void SetVisible( bool visible ) override;
bool IsVisible() const override; bool IsVisible() const override;

View File

@ -10,7 +10,7 @@ namespace tracy
{ {
TimelineItemGpu::TimelineItemGpu( View& view, Worker& worker, GpuCtxData* gpu ) TimelineItemGpu::TimelineItemGpu( View& view, Worker& worker, GpuCtxData* gpu )
: TimelineItem( view, worker ) : TimelineItem( view, worker, gpu )
, m_gpu( gpu ) , m_gpu( gpu )
, m_idx( view.GetNextGpuIdx() ) , m_idx( view.GetNextGpuIdx() )
{ {

View File

@ -9,7 +9,7 @@ namespace tracy
{ {
TimelineItemPlot::TimelineItemPlot( View& view, Worker& worker, PlotData* plot ) TimelineItemPlot::TimelineItemPlot( View& view, Worker& worker, PlotData* plot )
: TimelineItem( view, worker ) : TimelineItem( view, worker, plot )
, m_plot( plot ) , m_plot( plot )
{ {
} }

View File

@ -12,7 +12,7 @@ namespace tracy
{ {
TimelineItemThread::TimelineItemThread( View& view, Worker& worker, const ThreadData* thread ) TimelineItemThread::TimelineItemThread( View& view, Worker& worker, const ThreadData* thread )
: TimelineItem( view, worker ) : TimelineItem( view, worker, thread )
, m_thread( thread ) , m_thread( thread )
, m_ghost( false ) , m_ghost( false )
{ {

View File

@ -321,19 +321,24 @@ void View::DrawTimeline()
const auto yMin = ImGui::GetCursorScreenPos().y; const auto yMin = ImGui::GetCursorScreenPos().y;
const auto yMax = linepos.y + lineh; const auto yMax = linepos.y + lineh;
ImGui::SetNextWindowContentSize( ImVec2( 0, m_tc.GetHeight() ) );
ImGui::BeginChild( "##zoneWin", ImVec2( ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y ), false, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_NoScrollWithMouse ); ImGui::BeginChild( "##zoneWin", ImVec2( ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y ), false, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_NoScrollWithMouse );
const auto verticallyCenterTimeline = true;
if( m_yDelta != 0 ) if( m_yDelta != 0 )
{ {
auto& io = ImGui::GetIO(); auto& io = ImGui::GetIO();
auto y = ImGui::GetScrollY(); if( !verticallyCenterTimeline )
ImGui::SetScrollY( y - m_yDelta ); {
auto y = ImGui::GetScrollY();
ImGui::SetScrollY( y - m_yDelta );
}
io.MouseClickedPos[1].y = io.MousePos.y; io.MouseClickedPos[1].y = io.MousePos.y;
} }
const auto wpos = ImGui::GetCursorScreenPos(); const auto wpos = ImGui::GetCursorScreenPos();
const auto dpos = wpos + ImVec2( 0.5f, 0.5f ); const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
// note that m_tc.GetHeight() returns the height from the previous draw
const auto h = std::max<float>( m_tc.GetHeight(), ImGui::GetContentRegionAvail().y - 4 ); // magic border value const auto h = std::max<float>( m_tc.GetHeight(), ImGui::GetContentRegionAvail().y - 4 ); // magic border value
ImGui::ItemSize( ImVec2( w, h ) ); ImGui::ItemSize( ImVec2( w, h ) );
@ -341,7 +346,6 @@ void View::DrawTimeline()
draw = ImGui::GetWindowDrawList(); draw = ImGui::GetWindowDrawList();
const auto ty = ImGui::GetTextLineHeight(); const auto ty = ImGui::GetTextLineHeight();
int offset = 0;
const auto to = 9.f; const auto to = 9.f;
const auto th = ( ty - to ) * sqrt( 3 ) * 0.5; const auto th = ( ty - to ) * sqrt( 3 ) * 0.5;
@ -381,7 +385,8 @@ void View::DrawTimeline()
} }
} }
m_tc.End( pxns, offset, wpos, hover, yMin, yMax ); const auto vcenter = verticallyCenterTimeline && drawMouseLine && m_viewMode == ViewMode::Paused;
m_tc.End( pxns, wpos, hover, vcenter, yMin, yMax );
ImGui::EndChild(); ImGui::EndChild();
m_lockHighlight = m_nextLockHighlight; m_lockHighlight = m_nextLockHighlight;