demotool/syncview.cc

227 lines
7 KiB
C++
Raw Normal View History

#include "externals.hh"
#include "syncview.hh"
#include "globals.hh"
#include "window.hh"
#define IMGUI_DEFINE_MATH_OPERATORS
#include <imgui_internal.h>
using namespace ebcl;
/*= T_SyncViewImpl_ ============================================================*/
namespace {
struct T_SyncViewImpl_
{
static constexpr float SeqHeaderHeight = 24;
static constexpr float BarWidth = 40;
const uint32_t ColFrame{ ImGui::GetColorU32( ImVec4{ 0 , 0 , 0 , .8 } ) };
const uint32_t ColHeader{ ImGui::GetColorU32( ImVec4{ .5 , .5 , .5 , .8 } ) };
const uint32_t ColHeaderText{ ImGui::GetColorU32( ImVec4{ 0 , 0 , 0 , 1 } ) };
const uint32_t ColMain{ ImGui::GetColorU32( ImVec4{ .4 , .4 , .4 , .8 } ) };
float zoomLevel{ 1.f };
float startPos{ 100.895f };
bool followTime{ true };
bool display( ) noexcept;
void sequencerWidget( ) noexcept;
void sequencerHeader( ImRect const& bb ) noexcept;
void sequencerBody( ImRect const& bb ) noexcept;
};
bool T_SyncViewImpl_::display( ) noexcept
{
using namespace ImGui;
auto const& dspSize( GetIO( ).DisplaySize );
auto& sync( Globals::Sync( ) );
// Window set-up
SetNextWindowSize( ImVec2( dspSize.x , 150 ) , ImGuiSetCond_Appearing );
SetNextWindowPos( ImVec2( 0 , dspSize.y - 150 ) , ImGuiSetCond_Appearing );
bool displayed{ true };
Begin( "Sequencer" , &displayed , ImGuiWindowFlags_NoCollapse
| ImGuiWindowFlags_NoScrollWithMouse );
if ( !displayed ) {
End( );
return false;
}
PushItemWidth( 100 );
SliderFloat( "Zoom" , &zoomLevel , 0 , 1 , "%.2f" );
PopItemWidth( );
SameLine( );
PushID( "playing" );
if ( Button( sync.playing( ) ? "Stop" : "Play" ) ) {
sync.playing( ) = !sync.playing( ) && !sync.finished( );
}
PopID( );
const float d( sync.duration( ) );
float tm( sync.time( ) );
SameLine( );
PushID( "sequencer" );
PushItemWidth( -1 );
if ( SliderFloat( "" , &tm , 0 , d , "%.1fs" ) ) {
sync.setTime( tm );
sync.playing( ) = sync.playing( ) && !sync.finished( );
}
PopItemWidth( );
PopID( );
PushItemWidth( -1 );
sequencerWidget( );
PopItemWidth( );
End( );
return true;
}
void T_SyncViewImpl_::sequencerWidget( ) noexcept
{
using namespace ImGui;
const auto width{ CalcItemWidth( ) };
auto* const win( GetCurrentWindow( ) );
const auto seqId{ win->GetID( "##sequencer" ) };
const ImVec2 cPos( win->DC.CursorPos );
const ImVec2 ws( GetWindowContentRegionMax( ) );
auto& style( ImGui::GetStyle( ) );
const ImRect bbHeader{ cPos , cPos + ImVec2( width , SeqHeaderHeight ) };
const ImRect bbDisplay{ ImVec2{ cPos.x , bbHeader.Max.y } ,
ImVec2{ cPos.x + width , GetWindowPos( ).y + ws.y - style.FramePadding.y } };
const ImRect bbAll{ bbHeader.Min , bbDisplay.Max };
ItemSize( bbAll , style.FramePadding.y );
if ( !ItemAdd( bbAll , seqId ) ) {
return;
}
/*
* Scaling:
* At minimal zoom level, we want the whole "track" to fit inside the
* available width.
* At maximal zoom level, we want units from the sync manager to cover
* $MIN_UNIT_WIDTH pixels (that should depend on the text size).
* The displayed size of the units increases along with the zoom level.
* Display units are rescaled when the distance between them
* becomes >= 2 * $MIN_UNIT_WIDTH
* At worst, the sequence is only one unit long; this means that no
* scaling occurs between minimal and maximal levels.
*/
auto& sync( Globals::Sync( ) );
const float innerWidth{ width - 2 };
const uint32_t units{ sync.durationUnits( ) };
zoomLevel = ImSaturate( zoomLevel );
const float zoom1Pixels{ std::max( units * BarWidth , innerWidth ) };
const float totalPixels{ zoomLevel * ( zoom1Pixels - innerWidth ) + innerWidth };
const uint32_t totalBars{ [=](){
const float b{ std::max( std::min( totalPixels / BarWidth , float( units ) ) , 1.f ) };
const float mod{ fmod( b , 1.f ) };
return uint32_t( b + ( mod ? ( 1 - mod ) : 0 ) );
}() };
const float unitsPerBar{ float( units ) / totalBars };
const float barWidth{ totalPixels / totalBars };
const float absCursorPos{ sync.time( ) * totalPixels / sync.duration( ) };
if ( followTime ) {
const float dispUnits{ innerWidth * units / totalPixels };
const float uSize{ sync.durationUnitSize( ) };
const float endPos{ std::min( startPos + dispUnits , float( units ) ) };
startPos = endPos - dispUnits;
const float spp{ startPos * totalPixels / units };
const float epp{ endPos * totalPixels / units };
if ( absCursorPos < spp || absCursorPos > epp ) {
startPos = std::max( 0.f , sync.time( ) / uSize - unitsPerBar * .5f );
}
}
const float unadjustedStartPixel{ totalPixels * startPos / units };
if ( unadjustedStartPixel + innerWidth > totalPixels ) {
startPos = std::max( 0.f , totalPixels - innerWidth );
}
const float startPixel{ totalPixels * startPos / units };
const uint32_t startBar{ [=](){
const float b{ startPixel * totalBars / totalPixels };
const float mod{ fmod( b , 1.f ) };
return uint32_t( std::max( 0 , int32_t( b - ( mod ? mod : 1 ) ) ) );
}() };
const float startBarPos{ startBar * barWidth - startPixel };
const float cursorPos{ absCursorPos - startPixel };
printf( "Z: %f; P@zl1: %f; Pt: %f; Bt: %d; U/B: %f; Bw: %f; Sp: %f; Sb: %d, SbP: %f\n" , zoomLevel , zoom1Pixels , totalPixels , totalBars , unitsPerBar , barWidth , startPixel , startBar , startBarPos );
assert( startBarPos <= 0 );
assert( totalPixels >= innerWidth );
PushID( seqId );
BeginGroup( );
const auto hdrId{ win->GetID( "##header" ) };
const auto dspId{ win->GetID( "##display" ) };
if ( ItemAdd( bbHeader , hdrId ) ) {
PushID( hdrId );
sequencerHeader( bbHeader );
PopID( );
}
if ( bbDisplay.Min.y < bbDisplay.Max.y && ItemAdd( bbDisplay , dspId ) ) {
PushID( dspId );
sequencerBody( bbDisplay );
PopID( );
}
if ( cursorPos >= 0 && cursorPos <= width ) {
auto* const dl( GetWindowDrawList( ) );
dl->AddLine( bbAll.Min + ImVec2{ cursorPos + 1 , 0 } ,
ImVec2{ bbAll.Min.x + cursorPos + 1 , bbAll.Max.y } ,
0xffffffff );
}
EndGroup( );
PopID( );
}
void T_SyncViewImpl_::sequencerHeader(
ImRect const& bb ) noexcept
{
using namespace ImGui;
auto* const dl( GetWindowDrawList( ) );
PushFont( Globals::Window( ).smallFont( ) );
PushStyleColor( ImGuiCol_Text , ColHeaderText );
dl->AddRectFilled( bb.Min + ImVec2( 1 , 1 ) , bb.Max - ImVec2( 1 , 1 ) ,
ColHeader );
dl->AddRect( bb.Min , bb.Max , ColFrame );
const ImRect textBb{ bb.Min - ImVec2{ 10 , 0 } , bb.Max + ImVec2{ 10 , 0 } };
RenderTextClipped( textBb.Min , textBb.Max , "test!" , nullptr , nullptr , ImVec2{ 0 , .25 } , &bb );
RenderTextClipped( textBb.Min , textBb.Max , "...icle" , nullptr , nullptr , ImVec2{ 0 , .75 } , &bb );
PopStyleColor( );
PopFont( );
}
void T_SyncViewImpl_::sequencerBody(
ImRect const& bb ) noexcept
{
using namespace ImGui;
auto* const dl( GetWindowDrawList( ) );
dl->AddRectFilled( bb.Min , bb.Max , ColMain );
}
} // namespace
/*= T_SyncView =================================================================*/
T_SyncView::T_SyncView( ) noexcept
: A_PrivateImplementation( new T_SyncViewImpl_( ) )
{ }
bool T_SyncView::display( ) noexcept
{
return p< T_SyncViewImpl_ >( ).display( );
}