diff --git a/ui-sequencer.cc b/ui-sequencer.cc index f0d0f2c..d7c4a0e 100644 --- a/ui-sequencer.cc +++ b/ui-sequencer.cc @@ -207,6 +207,14 @@ struct T_SyncViewImpl_ SW_OVERRIDE_SELECTOR , SW_TRACK , SW_SEGMENT , + SW_POINT , + }; + + // Type of change in progress + enum class E_ChangeType { + NONE , + POINT_DND , + POINT_VALUE , }; // Make sure all displayed curves/inputs/overrides still exist @@ -248,6 +256,7 @@ struct T_SyncViewImpl_ void displayTrackWindow( ) noexcept; void displaySegmentWindow( ) noexcept; + void displayPointWindow( ) noexcept; //---------------------------------------------------------------------- @@ -296,8 +305,11 @@ struct T_SyncViewImpl_ T_Optional< uint32_t > selPoint; bool selPointDnD{ false }; float selPointDnDStart , selPointDnDCur; - T_Optional< T_SyncCurve > selPointDnDOriginal; - T_Optional< T_SyncCurve > selPointDnDCopy; + + // Original and copy of curve being modified + E_ChangeType selUpdate{ E_ChangeType::NONE }; + T_Optional< T_SyncCurve > selUpdatingOriginal; + T_Optional< T_SyncCurve > selUpdatingCopy; // Sub-windows E_SubWindow sub{ SW_NONE }; @@ -355,6 +367,10 @@ bool T_SyncViewImpl_::display( ) noexcept case SW_SEGMENT: displaySegmentWindow( ); break; + + case SW_POINT: + displayPointWindow( ); + break; } return true; @@ -597,11 +613,12 @@ void T_SyncViewImpl_::sequencerWidget( ) noexcept selPoint = dPoint.index; selPointDnD = io.MouseDown[ 0 ] && dPoint.index != 0; if ( selPointDnD ) { + assert( selUpdate == E_ChangeType::NONE ); selPointDnDStart = selPointDnDCur = mPixels; - selPointDnDOriginal = *sync.getCurve( dTrack.id ); // XXX + selUpdatingOriginal = *sync.getCurve( dTrack.id ); // XXX + selUpdate = E_ChangeType::POINT_DND; } -// sub = E_SubWindow::SW_SEGMENT; -#warning fix that shit + sub = E_SubWindow::SW_POINT; } } } @@ -833,13 +850,13 @@ void T_SyncViewImpl_::sequencerTrack( // If there's a curve, go through all segments const auto units{ Common::Sync( ).durationUnits( ) }; - const bool useCopy{ sCurve && curve && selPointDnDCopy }; + const bool useCopy{ sCurve && curve && selUpdatingCopy }; const auto nSeg{ curve - ? ( useCopy ? *selPointDnDCopy : *curve ).segments.size( ) + ? ( useCopy ? *selUpdatingCopy : *curve ).segments.size( ) : 0u }; uint32_t segStart{ 0 }; for ( auto i = 0u ; i < nSeg ; i ++ ) { - auto const& seg{ ( useCopy ? *selPointDnDCopy : *curve ).segments[ i ] }; + auto const& seg{ ( useCopy ? *selUpdatingCopy : *curve ).segments[ i ] }; const auto segDur{ [&](){ auto t{ 0u }; for ( auto d : seg.durations ) { @@ -1005,8 +1022,8 @@ bool T_SyncViewImpl_::handlePointDrag( // Update the point as necessary if ( moved ) { - selPointDnDCopy = selPointDnDOriginal; // XXX - auto& seg{ selPointDnDCopy->segments[ *selSegment ] }; + selUpdatingCopy = selUpdatingOriginal; // XXX + auto& seg{ selUpdatingCopy->segments[ *selSegment ] }; if ( *selPoint == seg.durations.size( ) ) { // We're dragging the end point // XXX make it work "normally" @@ -1024,21 +1041,23 @@ bool T_SyncViewImpl_::handlePointDrag( d0 += diff; d1 -= diff; } - sync.setCurve( *selPointDnDCopy ); + sync.setCurve( *selUpdatingCopy ); } else { - selPointDnDCopy.clear( ); + selUpdatingCopy.clear( ); } if ( mouseDown ) { return true; } + assert( selUpdate == E_ChangeType::POINT_DND ); + selUpdate = E_ChangeType::NONE; selPointDnD = false; - sync.setCurve( std::move( *selPointDnDOriginal ) ); - selPointDnDOriginal.clear( ); + sync.setCurve( std::move( *selUpdatingOriginal ) ); + selUpdatingOriginal.clear( ); if ( moved ) { - SyncEditor::ReplaceCurve( std::move( *selPointDnDCopy ) ); - selPointDnDCopy.clear( ); + SyncEditor::ReplaceCurve( std::move( *selUpdatingCopy ) ); + selUpdatingCopy.clear( ); } return false; } @@ -1466,6 +1485,164 @@ void T_SyncViewImpl_::displaySegmentWindow( ) noexcept End( ); } +void T_SyncViewImpl_::displayPointWindow( ) noexcept +{ + using namespace ImGui; + auto const& dspSize( GetIO( ).DisplaySize ); + + // Window set-up + SetNextWindowSize( ImVec2( dspSize.x * .25f , dspSize.y * .66f - 20 ) , ImGuiSetCond_Appearing ); + SetNextWindowPos( ImVec2( dspSize.x * .75f , 20 ) , ImGuiSetCond_Appearing ); + bool displayed{ true }; + Begin( "Selected point" , &displayed , ImGuiWindowFlags_NoCollapse ); + if ( !displayed ) { + End( ); + sub = SW_NONE; + return; + } + + // Access curve, segment and point + const uint32_t sid{ *selSegment }; + const uint32_t pid{ *selPoint }; + auto& sync{ Common::Sync( ) }; + auto const* const curve{ selUpdatingCopy + ? selUpdatingCopy.target( ) + : sync.getCurve( selId ) }; + auto const& segment{ curve->segments[ sid ] }; + + Text( "Curve:" ); + SameLine( 110 ); + Text( "%s" , selId.toOSString( ).data( ) ); + + Text( "Segment index:" ); + SameLine( 110 ); + Text( "%d" , sid ); + + Text( "Point index:" ); + SameLine( 110 ); + Text( "%d / %d" , pid , segment.durations.size( ) ); + + Separator( ); + + // Find start and duration + uint32_t segStart{ 0 } , pointPosRel{ 0 }; + for ( auto i = 0u ; i <= sid ; i ++ ) { + auto const& s{ curve->segments[ i ] }; + auto& tgt{ i == sid ? pointPosRel : segStart }; + const auto end{ i == sid ? pid : s.durations.size( ) }; + for ( auto j = 0u ; j < end ; j ++ ) { + tgt += s.durations[ j ]; + } + } + const uint32_t pointPosAbs{ segStart + pointPosRel }; + const float tPointPosAbs{ sync.durationUnitSize( ) * pointPosAbs } , + tPointPosRel{ sync.durationUnitSize( ) * pointPosRel }; + + Text( "Absolute:" ); + SameLine( 110 ); + Text( "%d units" , pointPosAbs ); + + Text( " " ); + SameLine( 110 ); + Text( "%.3f seconds" , tPointPosAbs ); + + Text( "Relative:" ); + SameLine( 110 ); + Text( "%d units" , pointPosRel ); + + Text( " " ); + SameLine( 110 ); + Text( "%.3f seconds" , tPointPosRel ); + + Separator( ); + + Text( "Value:" ); + SameLine( 110 ); + float value{ segment.values[ pid ] }; + PushItemWidth( -1 ); + DragFloat( "##value" , &value , .01f , 0 , 0 , "%.6f" ); + const bool changed{ IsItemActive( ) }; + PopItemWidth( ); + + const bool canUseButtons{ !changed && selUpdate == E_ChangeType::NONE }; + if ( pid != 0 && pid != segment.durations.size( ) ) { + Separator( ); + Text( " " ); + SameLine( 110 ); + if ( Button( "Delete point" , ImVec2{ -1 , 0 } ) && canUseButtons ) { + // FIXME move to syncedit + auto c{ *curve }; + auto& ns{ c.segments[ sid ] }; + ns.durations[ pid ] += ns.durations[ pid - 1 ]; + ns.durations.remove( pid - 1 ); + ns.values.remove( pid ); + SyncEditor::ReplaceCurve( std::move( c ) ); + selPoint.clear( ); + sub = SW_SEGMENT; + } + } + + const auto canInsertBefore{ pid != 0 + && segment.durations[ pid - 1 ] > 1 }; + const auto canInsertAfter{ pid != segment.durations.size( ) + && segment.durations[ pid ] > 1 }; + if ( canInsertAfter || canInsertBefore ) { + Separator( ); + if ( canInsertBefore ) { + Text( " " ); + SameLine( 110 ); + if ( Button( "Insert before" , ImVec2{ -1 , 0 } ) && canUseButtons ) { + // FIXME move to syncedit + auto c{ *curve }; + auto& ns{ c.segments[ sid ] }; + const auto hd{ ns.durations[ pid - 1 ] / 2 }; + // FIXME: this should use the actual value + const float hv{ ( ns.values[ pid ] + ns.values[ pid - 1 ] ) * .5f }; + ns.durations[ pid - 1 ] -= hd; + ns.durations.insert( pid - 1 , hd ); + ns.values.insert( pid , hv ); + SyncEditor::ReplaceCurve( std::move( c ) ); + (*selPoint) ++; + } + } + if ( canInsertAfter ) { + Text( " " ); + SameLine( 110 ); + if ( Button( "Insert after" , ImVec2{ -1 , 0 } ) && canUseButtons ) { + // FIXME move to syncedit + auto c{ *curve }; + auto& ns{ c.segments[ sid ] }; + const auto hd{ ns.durations[ pid ] / 2 }; + // FIXME: this should use the actual value + const float hv{ ( ns.values[ pid ] + ns.values[ pid + 1 ] ) * .5f }; + ns.durations[ pid ] -= hd; + ns.durations.insert( pid , hd ); + ns.values.insert( pid + 1 , hv ); + SyncEditor::ReplaceCurve( std::move( c ) ); + } + } + } + + if ( changed ) { + if ( selUpdate == E_ChangeType::NONE ) { + selUpdatingOriginal = *curve; + selUpdatingCopy = *curve; + selUpdate = E_ChangeType::POINT_VALUE; + } else { + assert( selUpdate == E_ChangeType::POINT_VALUE ); + } + selUpdatingCopy->segments[ sid ].values[ pid ] = value; + sync.setCurve( *selUpdatingCopy ); + } else if ( selUpdate == E_ChangeType::POINT_VALUE ) { + selUpdate = E_ChangeType::NONE; + sync.setCurve( *selUpdatingOriginal ); + SyncEditor::ReplaceCurve( std::move( *selUpdatingCopy ) ); + selUpdatingCopy.clear( ); + } + + End( ); +} + } // namespace