1#include <Core/CoreMacros.hpp>
2#include <Core/Math/Math.hpp>
3#include <Gui/Timeline/Configurations.hpp>
4#include <Gui/Timeline/TimelineFrameSelector.hpp>
5#include <Gui/Timeline/TimelineScrollArea.hpp>
6#include <Gui/Timeline/TimelineSlider.hpp>
16#include "ui_Timeline.h"
20TimelineFrameSelector::TimelineFrameSelector( QWidget* parent ) : QFrame( parent ) {
21 m_timer =
new QTimer(
this );
22 connect( m_timer, SIGNAL( timeout() ),
this, SLOT( update() ) );
25TimelineFrameSelector::~TimelineFrameSelector() {
29void TimelineFrameSelector::setTimelineUi( Ui::Timeline* ui ) {
34 m_start = Scalar( m_timelineUI->m_startSpin->value() );
35 m_end = Scalar( m_timelineUI->m_endSpin->value() );
36 m_cursor = Scalar( m_timelineUI->m_cursorSpin->value() );
37 m_timelineUI->m_scrollArea->setMaxDuration( Scalar( m_timelineUI->m_durationSpin->value() ) );
40#define CAST_DOUBLE static_cast<void ( QDoubleSpinBox::* )( double )>
41 connect( m_timelineUI->m_startSpin,
42 CAST_DOUBLE( &QDoubleSpinBox::valueChanged ),
43 [
this](
double start ) { onChangeStart( Scalar( start ) ); } );
44 connect( m_timelineUI->m_cursorSpin,
45 CAST_DOUBLE( &QDoubleSpinBox::valueChanged ),
46 [
this](
double cursor ) { onChangeCursor( Scalar( cursor ) ); } );
47 connect( m_timelineUI->m_endSpin,
48 CAST_DOUBLE( &QDoubleSpinBox::valueChanged ),
49 [
this](
double end ) { onChangeEnd( Scalar( end ) ); } );
50 connect( m_timelineUI->m_durationSpin,
51 CAST_DOUBLE( &QDoubleSpinBox::valueChanged ),
52 [
this](
double duration ) { onChangeDuration( Scalar( duration ) ); } );
55 connect( m_timelineUI->m_leftSlider,
56 &TimelineSlider::slide,
58 &TimelineFrameSelector::onSlideLeftSlider );
59 connect( m_timelineUI->m_rightSlider,
60 &TimelineSlider::slide,
62 &TimelineFrameSelector::onSlideRightSlider );
64 connect( m_timelineUI->toolButton_start,
65 &QToolButton::clicked,
67 &TimelineFrameSelector::onSetCursorToStart );
68 connect( m_timelineUI->toolButton_rearward,
69 &QToolButton::clicked,
71 &TimelineFrameSelector::onSetCursorToPreviousKeyFrame );
72 connect( m_timelineUI->toolButton_forward,
73 &QToolButton::clicked,
75 &TimelineFrameSelector::onSetCursorToNextKeyFrame );
76 connect( m_timelineUI->toolButton_end,
77 &QToolButton::clicked,
79 &TimelineFrameSelector::onSetCursorToEnd );
82 m_timelineUI->toolButton_keyFrame, SIGNAL( clicked() ),
this, SLOT( onAddingKeyFrame() ) );
83 connect( m_timelineUI->m_removeKeyFrameButton,
86 SLOT( onDeletingKeyFrame() ) );
89 m_timelineUI->m_scrollArea, SIGNAL( addKeyFrame() ),
this, SLOT( onAddingKeyFrame() ) );
90 connect( m_timelineUI->m_scrollArea,
91 SIGNAL( removeKeyFrame() ),
93 SLOT( onDeletingKeyFrame() ) );
94 connect( m_timelineUI->m_scrollArea,
95 &TimelineScrollArea::nextKeyFrame,
97 &TimelineFrameSelector::onSetCursorToNextKeyFrame );
98 connect( m_timelineUI->m_scrollArea,
99 &TimelineScrollArea::previousKeyFrame,
101 &TimelineFrameSelector::onSetCursorToPreviousKeyFrame );
102 connect( m_timelineUI->m_scrollArea,
103 &TimelineScrollArea::togglePlayPause,
104 m_timelineUI->toolButton_playPause,
105 &QToolButton::toggle );
106 connect( m_timelineUI->m_scrollArea, &TimelineScrollArea::durationIncrement, [
this]() {
107 onChangeDuration( Scalar( m_timelineUI->m_durationSpin->value() +
108 m_timelineUI->m_durationSpin->singleStep() ) );
110 connect( m_timelineUI->m_scrollArea, &TimelineScrollArea::durationDecrement, [
this]() {
111 onChangeDuration( Scalar( m_timelineUI->m_durationSpin->value() -
112 m_timelineUI->m_durationSpin->singleStep() ) );
114 connect( m_timelineUI->m_scrollArea, &TimelineScrollArea::stepChanged, [
this](
double step ) {
115 m_timelineUI->m_startSpin->setSingleStep( 0.5 * step );
116 m_timelineUI->m_endSpin->setSingleStep( 0.5 * step );
117 m_timelineUI->m_cursorSpin->setSingleStep( 0.5 * step );
118 m_timelineUI->m_durationSpin->setSingleStep( 0.5 * step );
29void TimelineFrameSelector::setTimelineUi( Ui::Timeline* ui ) {
…}
122void TimelineFrameSelector::onChangeStart( Scalar time,
bool internal ) {
125 bool out = std::abs( newStart - time ) > 1e-5_ra;
126 bool change = std::abs( newStart - m_start ) > 1e-5_ra;
134 if ( internal || out ) { emit startChanged( m_start ); }
137 if ( out ) { updateStartSpin(); }
122void TimelineFrameSelector::onChangeStart( Scalar time,
bool internal ) {
…}
141void TimelineFrameSelector::onChangeEnd( Scalar time,
bool internal ) {
143 std::min(
std::max( time, m_start ), m_timelineUI->m_scrollArea->getMaxDuration() );
145 bool out = std::abs( newEnd - time ) > 1e-5_ra;
146 bool change = std::abs( newEnd - m_end ) > 1e-5_ra;
152 if ( internal || out ) { emit endChanged( m_end ); }
156 if ( out ) { updateEndSpin(); }
141void TimelineFrameSelector::onChangeEnd( Scalar time,
bool internal ) {
…}
160void TimelineFrameSelector::onChangeDuration( Scalar time,
bool internal ) {
161 Scalar newDuration =
std::max( time, 0_ra );
162 Scalar oldDuration = m_timelineUI->m_scrollArea->getMaxDuration();
164 bool out = std::abs( newDuration - time ) > 1e-5_ra;
165 bool change = std::abs( newDuration - oldDuration ) > 1e-5_ra;
168 oldDuration = newDuration;
169 m_timelineUI->m_scrollArea->setMaxDuration( newDuration );
170 m_timelineUI->m_scrollArea->onDrawRuler(
171 m_timelineUI->m_scrollArea->widget()->minimumWidth() );
172 updateDurationSpin();
175 if ( internal || out ) { emit durationChanged( newDuration ); }
178 if ( out ) { updateDurationSpin(); }
181 if ( oldDuration < m_start ) onChangeStart( oldDuration );
183 if ( oldDuration < m_end ) onChangeEnd( oldDuration );
160void TimelineFrameSelector::onChangeDuration( Scalar time,
bool internal ) {
…}
188void TimelineFrameSelector::onChangeCursor( Scalar time,
bool internal ) {
189 Scalar newCursor =
std::max( 0_ra, time );
191 if ( internal ) { newCursor = nearestStep( newCursor ); }
193 bool out = std::abs( newCursor - time ) > 1e-5_ra;
194 bool change = std::abs( newCursor - m_cursor ) > 1e-5_ra;
197 m_cursor = newCursor;
199 if ( internal || out ) { emit cursorChanged( m_cursor ); }
203 if ( out ) { updateCursorSpin(); }
188void TimelineFrameSelector::onChangeCursor( Scalar time,
bool internal ) {
…}
207void TimelineFrameSelector::onAddingKeyFrame( Scalar time,
bool internal ) {
209 if (
static_cast<int>( time ) == -1 ) time = m_cursor;
211 auto it =
std::find_if( m_keyFrames.begin(), m_keyFrames.end(), [time](
const auto& t ) {
212 return Ra::Core::Math::areApproxEqual( t, time );
216 if ( it == m_keyFrames.end() ) {
219 m_keyFrames.push_back( time );
220 std::sort( m_keyFrames.begin(), m_keyFrames.end() );
222 if ( internal ) { emit keyFrameAdded( time ); }
224 updateNbKeyFrameSpin();
230 if ( internal ) { emit keyFrameChanged(
std::distance( m_keyFrames.begin(), it ) ); }
232 m_updateKeyFrameFlash = 6;
233 m_keyFrameFlash = time;
235 m_timer->start( 50 );
207void TimelineFrameSelector::onAddingKeyFrame( Scalar time,
bool internal ) {
…}
241void TimelineFrameSelector::onDeletingKeyFrame(
bool internal ) {
242 auto it =
std::find_if( m_keyFrames.begin(), m_keyFrames.end(), [
this](
const auto& t ) {
243 return Ra::Core::Math::areApproxEqual( t, m_cursor );
245 if ( it == m_keyFrames.end() ) {
return; }
247 if ( internal ) { emit keyFrameDeleted(
std::distance( m_keyFrames.begin(), it ) ); }
249 m_keyFrames.erase( it );
253 updateNbKeyFrameSpin();
241void TimelineFrameSelector::onDeletingKeyFrame(
bool internal ) {
…}
258void TimelineFrameSelector::onMoveKeyFrame( Scalar time0, Scalar time1,
bool internal ) {
261 auto it =
std::find_if( m_keyFrames.begin(), m_keyFrames.end(), [
this](
const auto& t ) {
262 return Ra::Core::Math::areApproxEqual( t, m_cursor );
264 if ( it == m_keyFrames.end() ) {
return; }
266 if ( internal ) { emit keyFrameMoved(
std::distance( m_keyFrames.begin(), it ), time1 ); }
270 m_keyFrames.erase( it );
271 m_keyFrames.push_back( m_cursor );
272 std::sort( m_keyFrames.begin(), m_keyFrames.end() );
258void TimelineFrameSelector::onMoveKeyFrame( Scalar time0, Scalar time1,
bool internal ) {
…}
279void TimelineFrameSelector::onMoveKeyFrames( Scalar time, Scalar offset,
bool internal ) {
282 auto it =
std::find_if( m_keyFrames.begin(), m_keyFrames.end(), [time](
const auto& t ) {
283 return Ra::Core::Math::areApproxEqual( t, time );
285 if ( it == m_keyFrames.end() ) {
return; }
288 if ( internal ) { emit keyFramesMoved(
std::distance( m_keyFrames.begin(), it ), offset ); }
294 [time, offset]( Scalar p ) { return ( p < time ? p : p + offset ); } );
295 std::exchange( m_keyFrames, clone );
297 Scalar left = ( offset > 0 ) ? ( time ) : ( time + offset );
299 if ( m_start >= left ) {
301 std::min( m_start + offset, m_timelineUI->m_scrollArea->getMaxDuration() ), 0_ra );
303 if ( internal ) { emit startChanged( m_start ); }
306 Scalar right = *m_keyFrames.rbegin();
307 if ( right > m_timelineUI->m_scrollArea->getMaxDuration() ) {
308 m_timelineUI->m_scrollArea->setMaxDuration( right );
309 updateDurationSpin();
310 if ( internal ) { emit durationChanged( right ); }
313 if ( m_end >= left ) {
314 m_end = m_end + offset;
316 if ( internal ) { emit endChanged( m_end ); }
319 if ( m_cursor >= left ) {
320 m_cursor =
std::max( m_cursor + offset, 0_ra );
323 if ( internal ) { emit cursorChanged( m_cursor ); }
279void TimelineFrameSelector::onMoveKeyFrames( Scalar time, Scalar offset,
bool internal ) {
…}
329void TimelineFrameSelector::onClearKeyFrames() {
331 updateNbKeyFrameSpin();
329void TimelineFrameSelector::onClearKeyFrames() {
…}
338void TimelineFrameSelector::onSlideLeftSlider(
int deltaX ) {
339 { m_timelineUI->m_leftSlider->setStyleSheet(
"background-color: green" ); }
341 Scalar pixPerSec = m_timelineUI->m_scrollArea->getPixPerSec();
342 Scalar newStart = m_start + deltaX / pixPerSec;
344 onChangeStart( newStart );
347void TimelineFrameSelector::onSlideRightSlider(
int deltaX ) {
348 { m_timelineUI->m_rightSlider->setStyleSheet(
"background-color: red" ); }
349 Scalar pixPerSec = m_timelineUI->m_scrollArea->getPixPerSec();
350 Scalar newEnd = m_end + deltaX / pixPerSec;
352 onChangeEnd( newEnd );
355void TimelineFrameSelector::onSetCursorToStart() {
356 onChangeCursor( m_start );
359void TimelineFrameSelector::onSetCursorToEnd() {
360 onChangeCursor( m_end );
363void TimelineFrameSelector::onSetCursorToPreviousKeyFrame() {
364 auto it = m_keyFrames.rbegin();
365 while ( it != m_keyFrames.rend() && *it >= m_cursor )
368 if ( it != m_keyFrames.rend() ) { onChangeCursor( *it ); }
371void TimelineFrameSelector::onSetCursorToNextKeyFrame() {
372 auto it = m_keyFrames.begin();
373 while ( it != m_keyFrames.end() && *it <= m_cursor )
376 if ( it != m_keyFrames.end() ) { onChangeCursor( *it ); }
379void TimelineFrameSelector::paintEvent( QPaintEvent* ) {
381 QPainter painter(
this );
388 painter.setPen( QPen( Qt::lightGray ) );
389 Scalar frameDuration = 1_ra / TIMELINE_FPS;
391 Scalar nbFrame = m_timelineUI->m_scrollArea->getMaxDuration() / frameDuration;
392 Scalar pixPerSec = m_timelineUI->m_scrollArea->getPixPerSec();
393 Scalar step = m_timelineUI->m_scrollArea->getStep();
394 int zero = m_timelineUI->m_scrollArea->getZero();
395 for (
int i = 0; i < nbFrame; i++ ) {
396 int x = int( i * frameDuration * pixPerSec + zero );
397 painter.drawLine( x, 0, x, hUp );
401 painter.setPen( QPen( QColor( 0, 0, 255, 255 ), 3 ) );
402 int xCursor = int( zero + m_cursor * pixPerSec );
403 painter.drawLine( xCursor, 0, xCursor, h );
406 painter.setPen( QPen( QColor( 255, 255, 0, 255 ), 3 ) );
407 int hTemp = h / 3 + 2;
408 for ( Scalar keyFrame : m_keyFrames ) {
409 int xKeyFrame = int( zero + keyFrame * pixPerSec );
410 painter.drawLine( xKeyFrame, hTemp, xKeyFrame, h );
414 int hDown = 2 * h / 3;
415 painter.setPen( Qt::black );
416 for (
int i = 1; i < m_timelineUI->m_scrollArea->getNbInterval(); i++ ) {
417 int x = int( i * step * pixPerSec );
418 painter.drawLine( x, hDown, x, h );
420 int hDown2 = 3 * h / 4;
421 painter.setPen( Qt::darkGray );
422 for (
int i = 1; i < m_timelineUI->m_scrollArea->getNbInterval() - 1; i++ ) {
423 int middle = int( ( i + 0.5_ra ) * step * pixPerSec );
424 painter.drawLine( middle, hDown2, middle, h );
427 if ( m_updateKeyFrameFlash > 0 ) {
429 if ( m_updateKeyFrameFlash % 2 == 0 ) {
430 painter.setPen( QPen( QColor( 0, 0, 255, 255 ), 3 ) );
431 int xKeyFrame =
static_cast<int>( zero + m_keyFrameFlash * pixPerSec );
432 painter.drawLine( xKeyFrame, hTemp, xKeyFrame, h );
435 if ( --m_updateKeyFrameFlash == 0 ) m_timer->stop();
440 painter.setPen( Qt::white );
441 painter.drawLine( 0, h / 2 + gap + 2, w, h / 2 + gap + 2 );
442 painter.drawLine( 0, h / 2 + gap + 1, w, h / 2 + gap + 1 );
444 painter.setPen( Qt::darkGray );
445 painter.drawLine( 0, h / 3, w, h / 3 );
446 painter.drawLine( 0, h / 2 + gap, w, h / 2 + gap );
449void TimelineFrameSelector::mousePressEvent( QMouseEvent* event ) {
450 bool shiftDown =
event->modifiers() & Qt::Modifier::SHIFT;
451 bool ctrlDown =
event->modifiers() & Qt::Modifier::CTRL;
453 if ( event->button() == Qt::LeftButton ) {
454 Scalar newCursor =
std::max( Scalar( event->x() - m_timelineUI->m_scrollArea->getZero() ) /
455 m_timelineUI->m_scrollArea->getPixPerSec(),
458 if ( ctrlDown ) { onChangeCursor( newCursor,
false ); }
460 else if ( shiftDown ) { deleteZone( m_cursor, newCursor ); }
463 onChangeCursor( newCursor );
464 m_mouseLeftClicked =
true;
468 else if ( event->button() == Qt::RightButton ) {
469 Scalar newFrame =
std::max( Scalar( event->x() - m_timelineUI->m_scrollArea->getZero() ) /
470 m_timelineUI->m_scrollArea->getPixPerSec(),
472 auto it =
std::find_if( m_keyFrames.begin(), m_keyFrames.end(), [
this](
const auto& t ) {
473 return Ra::Core::Math::areApproxEqual( t, m_cursor );
477 if ( it != m_keyFrames.end() ) {
478 Scalar nearest = nearestStep( newFrame );
483 m_keyFrames.begin(), m_keyFrames.end(), [nearest](
const auto& t ) {
484 return Ra::Core::Math::areApproxEqual( t, nearest );
486 if ( it2 == m_keyFrames.end() && ( std::abs( m_cursor - nearest ) > 1e-5_ra ) ) {
487 onMoveKeyFrame( m_cursor, nearest );
495 Scalar
left = ( it == m_keyFrames.begin() ) ? ( 0.0 ) : ( *itLeft );
496 if ( nearest > left ) { onMoveKeyFrames( m_cursor, nearest - m_cursor ); }
504 auto itRight =
std::lower_bound( m_keyFrames.begin(), m_keyFrames.end(), newFrame );
506 if ( itRight != m_keyFrames.end() ) {
507 onMoveKeyFrames( *itRight, newFrame - *itRight );
516 if ( itLeft != m_keyFrames.end() ) {
517 onMoveKeyFrames( *itLeft, newFrame - *itLeft );
530void TimelineFrameSelector::mouseMoveEvent( QMouseEvent* event ) {
531 if ( m_mouseLeftClicked ) {
532 Scalar newCursor =
std::max( Scalar( event->x() - m_timelineUI->m_scrollArea->getZero() ) /
533 m_timelineUI->m_scrollArea->getPixPerSec(),
536 onChangeCursor( newCursor );
538 else {
event->ignore(); }
541void TimelineFrameSelector::mouseReleaseEvent( QMouseEvent* event ) {
542 if ( event->button() == Qt::LeftButton ) {
543 m_mouseLeftClicked =
false;
546 else {
event->ignore(); }
549void TimelineFrameSelector::updateCursorSpin() {
550 auto it =
std::find_if( m_keyFrames.begin(), m_keyFrames.end(), [
this](
const auto& t ) {
551 return Ra::Core::Math::areApproxEqual( t, m_cursor );
553 if ( it != m_keyFrames.end() ) {
554 m_timelineUI->m_cursorSpin->setStyleSheet(
"background-color: yellow" );
555 m_timelineUI->m_removeKeyFrameButton->setEnabled(
true );
558 m_timelineUI->m_cursorSpin->setStyleSheet(
"background-color: #5555ff" );
559 m_timelineUI->m_removeKeyFrameButton->setEnabled(
false );
561 m_timelineUI->m_cursorSpin->setValue(
double( m_cursor ) );
564void TimelineFrameSelector::updateStartSpin() {
565 m_timelineUI->m_startSpin->setValue(
double( m_start ) );
566 updateDurationSpin();
569void TimelineFrameSelector::updateEndSpin() {
570 m_timelineUI->m_endSpin->setValue(
double( m_end ) );
571 updateDurationSpin();
574void TimelineFrameSelector::updateDurationSpin() {
575 m_timelineUI->m_durationSpin->setValue(
576 double( m_timelineUI->m_scrollArea->getMaxDuration() ) );
579void TimelineFrameSelector::updateNbKeyFrameSpin() {
580 m_timelineUI->m_nbKeyFramesSpin->setText(
" " + QString::number( m_keyFrames.size() ) +
" " );
583Scalar TimelineFrameSelector::nearestStep( Scalar time )
const {
585 TIMELINE_AUTO_SUGGEST_CURSOR_RADIUS / m_timelineUI->m_scrollArea->getPixPerSec();
587 Scalar minDist = Scalar( m_timelineUI->m_durationSpin->maximum() );
590 Scalar newCursor =
time;
592 for ( Scalar keyFrame : m_keyFrames ) {
593 dist = qAbs( keyFrame - time );
594 if ( dist < deltaT && dist < minDist ) {
596 newCursor = keyFrame;
598 if ( time < keyFrame )
break;
601 Scalar step = m_timelineUI->m_scrollArea->getStep();
602 for (
int i = 0; i < m_timelineUI->m_scrollArea->getNbInterval() - 1; ++i ) {
603 Scalar pos = i * step;
604 dist = std::abs( pos - time );
605 if ( dist < deltaT && dist < minDist ) {
610 pos = i * step + 0.5_ra * step;
611 dist = std::abs( pos - time );
612 if ( dist < deltaT && dist < minDist ) {
617 if ( time < pos )
break;
623void TimelineFrameSelector::deleteZone( Scalar time, Scalar time2 ) {
630 auto it = m_keyFrames.begin();
632 while ( it != m_keyFrames.end() ) {
633 Scalar keyFrame = *it;
635 if ( keyFrame >= left ) {
636 it = m_keyFrames.erase( it );
638 if ( keyFrame > right ) {
640 emit keyFramesMoved(
std::distance( m_keyFrames.begin(), it ), -dist );
643 it = m_keyFrames.insert( it, keyFrame - dist );
646 else { emit keyFrameDeleted(
std::distance( m_keyFrames.begin(), it ) ); }
650 updateNbKeyFrameSpin();
654 if ( std::abs( newStart - m_start ) > 1e-5_ra ) {
658 emit startChanged( m_start );
662 if ( std::abs( newEnd - m_end ) > 1e-5_ra ) {
666 emit endChanged( m_end );
669 emit cursorChanged( m_cursor );
674void TimelineFrameSelector::redrawPlayZone() {
675 m_timelineUI->m_leftSpacer->setMinimumWidth(
676 int( m_timelineUI->m_scrollArea->getZero() +
677 m_start * m_timelineUI->m_scrollArea->getPixPerSec() -
678 m_timelineUI->m_leftSlider->width() ) );
679 m_timelineUI->m_playZone->setMinimumWidth(
680 int( ( m_end - m_start ) * m_timelineUI->m_scrollArea->getPixPerSec() ) );
T back_inserter(T... args)
std::enable_if<!std::numeric_limits< T >::is_integer, bool >::type areApproxEqual(T x, T y, T espilonBoostFactor=T(10))
Compare two numbers such that |x-y| < espilon*epsilonBoostFactor.