1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 16:10:06 +02:00

Reorganize code for drawing cursor and indicator, so it can generalize to...

... other things (like scrub speed)
This commit is contained in:
Paul Licameli 2015-08-13 10:59:42 -04:00
parent 7ee8032f3c
commit 9ba6e03bd4
2 changed files with 187 additions and 118 deletions

View File

@ -484,7 +484,6 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
UpdatePrefs();
mRedrawAfterStop = false;
mIndicatorShowing = false;
mPencilCursor = MakeCursor( wxCURSOR_PENCIL, DrawCursorXpm, 12, 22);
mSelectCursor = MakeCursor( wxCURSOR_IBEAM, IBeamCursorXpm, 17, 16);
@ -565,8 +564,9 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
mSnapLeft = -1;
mSnapRight = -1;
mLastCursorX = -1;
mLastIndicatorX = -1;
mLastCursorX = mNewCursorX = -1;
mLastIndicatorX = mNewIndicatorX = -1;
mCursorTime = -1.0;
mOldQPIndicatorPos = -1;
// Register for tracklist updates
@ -1106,20 +1106,8 @@ void TrackPanel::OnTimer()
DisplaySelection();
}
// AS: The "indicator" is the little graphical mark shown in the ruler
// that indicates where the current play/record position is. (This also
// draws the moving vertical line.)
if (!gAudioIO->IsPaused() && // Don't redraw paused indicator needlessly for timer
( mIndicatorShowing || IsAudioActive())
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
&& !mSmoothScrollingScrub
#endif
)
{
DrawIndicator();
}
TimerUpdateIndicator();
DrawOverlays(false);
if(IsAudioActive() && gAudioIO->GetNumCaptureChannels()) {
@ -1182,6 +1170,8 @@ void TrackPanel::ScrollDuringDrag()
}
}
#if 0
// now unused
/// This updates the indicator (on a timer tick) that shows
/// where the current play or record position is. To do this,
/// we cheat a little. The indicator is drawn during the ruler
@ -1194,6 +1184,7 @@ void TrackPanel::DrawIndicator()
wxClientDC dc( this );
DoDrawIndicator( dc );
}
#endif
void TrackPanel::DrawQuickPlayIndicator(wxDC & dc, double pos)
{
@ -1225,18 +1216,74 @@ void TrackPanel::DrawQuickPlayIndicator(wxDC & dc, double pos)
}
}
/// Second level DrawIndicator()
void TrackPanel::DoDrawIndicator
(wxDC & dc, bool repairOld /* = false */, double indicator /* = -1 */)
void TrackPanel::TimerUpdateIndicator()
{
bool onScreen;
double pos;
double pos = 0.0;
if (!repairOld)
if (!IsAudioActive())
mNewIndicatorX = -1;
else {
// Calculate the horizontal position of the indicator
pos = gAudioIO->GetStreamTime();
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (mSmoothScrollingScrub) {
// Pan the view, so that we center the play indicator.
mViewInfo->h = pos - mViewInfo->screen / 2.0;
if (!mScrollBeyondZero)
// Can't scroll too far left
mViewInfo->h = std::max(0.0, mViewInfo->h);
Refresh(false);
}
#endif
AudacityProject *p = GetProject();
const bool
onScreen = between_inclusive(mViewInfo->h,
pos,
mViewInfo->h + mViewInfo->screen);
// This displays the audio time, too...
DisplaySelection();
// BG: Scroll screen if option is set
// msmeyer: But only if not playing looped or in one-second mode
if( mViewInfo->bUpdateTrackIndicator &&
p->mLastPlayMode != loopedPlay &&
p->mLastPlayMode != oneSecondPlay &&
pos >= 0 &&
!onScreen &&
!gAudioIO->IsPaused() )
{
mListener->TP_ScrollWindow( pos );
}
// Always update scrollbars even if not scrolling the window. This is
// important when new audio is recorded, because this can change the
// length of the project and therefore the appearance of the scrollbar.
MakeParentRedrawScrollbars();
mNewIndicatorX = mViewInfo->TimeToPosition(pos, GetLeftOffset());
}
}
std::pair<wxRect, bool> TrackPanel::GetIndicatorRectangle()
{
return std::make_pair(
wxRect(mLastIndicatorX, 0, 1, mBacking->GetHeight()),
mLastIndicatorX != mNewIndicatorX
);
}
void TrackPanel::UndrawIndicator(wxDC & dc)
{
// AS: The "indicator" is the little graphical mark shown in the ruler
// that indicates where the current play/record position is. (This also
// draws the moving vertical line.)
// Erase the old indicator.
if (mLastIndicatorX != -1)
{
const bool
onScreen = between_inclusive(GetLeftOffset(),
mLastIndicatorX,
GetLeftOffset() + mViewInfo->GetScreenWidth());
@ -1253,54 +1300,17 @@ void TrackPanel::DoDrawIndicator
dc.Blit(mLastIndicatorX, 0, 1, mBacking->GetHeight(), &mBackingDC, mLastIndicatorX, 0);
}
// Nothing's ever perfect...
//
// Redraw the cursor since we may have just wiped it out
if (mLastCursorX == mLastIndicatorX)
{
DoDrawCursor( dc );
}
mRuler->ClearIndicator();
}
}
pos = indicator;
if (pos < 0.0)
// The stream time can be < 0 if the audio is currently stopped
pos = gAudioIO->GetStreamTime();
void TrackPanel::DoDrawIndicator(wxDC & dc)
{
mLastIndicatorX = mNewIndicatorX;
if (mLastIndicatorX == -1)
return;
AudacityProject *p = GetProject();
bool audioActive = IsAudioActive();
onScreen = between_inclusive( mViewInfo->h,
pos,
mViewInfo->h + mViewInfo->screen );
// This displays the audio time, too...
DisplaySelection();
// BG: Scroll screen if option is set
// msmeyer: But only if not playing looped or in one-second mode
if( mViewInfo->bUpdateTrackIndicator &&
p->mLastPlayMode != loopedPlay &&
p->mLastPlayMode != oneSecondPlay &&
audioActive &&
pos >= 0 &&
!onScreen &&
!gAudioIO->IsPaused() )
{
mListener->TP_ScrollWindow( pos );
}
// Always update scrollbars even if not scrolling the window. This is
// important when new audio is recorded, because this can change the
// length of the project and therefore the appearance of the scrollbar.
MakeParentRedrawScrollbars();
mIndicatorShowing = ( onScreen && audioActive );
// Calculate the horizontal position of the indicator
mLastIndicatorX = mViewInfo->TimeToPosition(pos, GetLeftOffset());
}
else
pos = mViewInfo->PositionToTime(mLastIndicatorX, GetLeftOffset());
double pos = mViewInfo->PositionToTime(mLastIndicatorX, GetLeftOffset());
// Set play/record color
bool rec = (gAudioIO->GetNumCaptureChannels() > 0);
@ -1340,6 +1350,8 @@ void TrackPanel::DoDrawIndicator
}
}
#if 0
// now unused
/// This method draws the cursor things, both in the
/// ruler as seen at the top of the screen, but also in each of the
/// selected tracks.
@ -1349,12 +1361,27 @@ void TrackPanel::DrawCursor()
wxClientDC dc( this );
DoDrawCursor( dc );
}
#endif
/// Second level DrawCursor()
void TrackPanel::DoDrawCursor(wxDC & dc)
std::pair<wxRect, bool> TrackPanel::GetCursorRectangle()
{
DisableAntialiasing(dc);
if (!mViewInfo->selectedRegion.isPoint()) {
mCursorTime = -1.0;
mNewCursorX = -1;
}
else {
mCursorTime = mViewInfo->selectedRegion.t0();
mNewCursorX = mViewInfo->TimeToPosition(mCursorTime, GetLeftOffset());
}
return std::make_pair(
wxRect(mLastCursorX, 0, 1, mBacking->GetHeight()),
mLastCursorX != mNewCursorX
);
}
void TrackPanel::UndrawCursor(wxDC & dc)
{
bool onScreen;
if( mLastCursorX != -1 )
@ -1365,18 +1392,23 @@ void TrackPanel::DoDrawCursor(wxDC & dc)
if( onScreen )
dc.Blit(mLastCursorX, 0, 1, mBacking->GetHeight(), &mBackingDC, mLastCursorX, 0);
}
}
const double time = mViewInfo->selectedRegion.t0();
mLastCursorX = mViewInfo->TimeToPosition(time, GetLeftOffset());
void TrackPanel::DoDrawCursor(wxDC & dc)
{
mLastCursorX = mNewCursorX;
if (mLastCursorX == -1)
return;
const bool
onScreen = between_inclusive( mViewInfo->h,
time,
mCursorTime,
mViewInfo->h + mViewInfo->screen );
if( !onScreen )
return;
AColor::CursorColor( &dc );
AColor::CursorColor(&dc);
// Draw cursor in all selected tracks
VisibleTrackIterator iter( GetProject() );
@ -1404,7 +1436,7 @@ void TrackPanel::DoDrawCursor(wxDC & dc)
}
// AS: Ah, no, this is where we draw the blinky thing in the ruler.
mRuler->DrawCursor( time );
mRuler->DrawCursor(mCursorTime);
DisplaySelection();
}
@ -1462,8 +1494,6 @@ void TrackPanel::OnPaint(wxPaintEvent & /* event */)
// Retrieve the damage rectangle
wxRect box = GetUpdateRegion().GetBox();
double indicator = -1;
// Recreate the backing bitmap if we have a full refresh
// (See TrackPanel::Refresh())
if (mRefreshBacking || (box == GetRect()))
@ -1471,22 +1501,6 @@ void TrackPanel::OnPaint(wxPaintEvent & /* event */)
// Reset (should a mutex be used???)
mRefreshBacking = false;
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (mSmoothScrollingScrub && IsAudioActive()) {
// Pan the view, so that we center the play indicator.
// By the time DoDrawIndicator() is reached, gAudioIO->GetStreamTime()
// may be a little different.
// Cause DoDrawIndicator to show the value as it was at this time when we begin
// the drawing of the tracks. This prevents flashing of the indicator
// at higher magnifications, and keeps the green line still in the middle.
indicator = gAudioIO->GetStreamTime();
mViewInfo->h = indicator - mViewInfo->screen / 2.0;
if (!mScrollBeyondZero)
// Can't scroll too far left
mViewInfo->h = std::max(0.0, mViewInfo->h);
}
#endif
// Redraw the backing bitmap
DrawTracks(&mBackingDC);
@ -1503,23 +1517,7 @@ void TrackPanel::OnPaint(wxPaintEvent & /* event */)
delete dc;
// Drawing now goes directly to the client area
wxClientDC cdc(this);
DisableAntialiasing(cdc);
// Update the indicator in case it was damaged if this project is playing
if (// !gAudioIO->IsPaused() && // Bug1139: do repaint the line even if paused.
(mIndicatorShowing || IsAudioActive()))
{
// If not smooth scrolling, then
// we just want to repair, not update the old, so set the second param to true.
// This is important because this onPaint could be for just some of the tracks.
DoDrawIndicator(cdc, (indicator < 0), indicator);
}
// Draw the cursor
if (mViewInfo->selectedRegion.isPoint())
DoDrawCursor(cdc);
DrawOverlays(true);
#if DEBUG_DRAW_TIMING
sw.Pause();
@ -7592,6 +7590,67 @@ void TrackPanel::DrawOutsideOfTrack(Track * t, wxDC * dc, const wxRect & rect)
#endif
}
void TrackPanel::DrawOverlays(bool repaint)
{
bool isOutdated = false;
// Determine which overlays are outdated.
enum { n_pairs = 2 };
std::pair<wxRect, bool> pairs[n_pairs] = {
GetIndicatorRectangle(),
GetCursorRectangle(),
};
{
// Drawing now goes directly to the client area
wxClientDC dc(this);
DisableAntialiasing(dc);
// See what requires redrawing. If repainting, all.
// If not, then whatever is outdated, and whatever will be damaged by
// undrawing.
// By redrawing only what needs it, we avoid flashing things like
// the cursor that are drawn with xor.
if (!repaint) {
bool done;
do {
done = true;
for (int ii = 0; ii < n_pairs; ++ii) {
for (int jj = ii + 1; jj < n_pairs; ++jj) {
if (pairs[ii].second != pairs[jj].second &&
pairs[ii].first.Intersects(pairs[jj].first)) {
done = false;
pairs[ii].second = pairs[jj].second = true;
}
}
}
} while (!done);
}
}
if (repaint || pairs[0].second) {
wxClientDC dc(this);
DisableAntialiasing(dc);
UndrawIndicator(dc);
}
if (repaint || pairs[1].second) {
wxClientDC dc(this);
DisableAntialiasing(dc);
UndrawCursor(dc);
}
if (repaint || pairs[0].second) {
wxClientDC dc(this);
DisableAntialiasing(dc);
DoDrawIndicator(dc);
}
if (repaint || pairs[1].second) {
wxClientDC dc(this);
DisableAntialiasing(dc);
DoDrawCursor(dc);
}
}
/// Draw a three-level highlight gradient around the focused track.
void TrackPanel::HighlightFocusedTrack(wxDC * dc, const wxRect & rect)
{
@ -8153,7 +8212,7 @@ void TrackPanel::SeekLeftOrRight
mViewInfo->selectedRegion.collapseToT0();
// Move the visual cursor
DrawCursor();
DrawOverlays(false);
}
else
{

View File

@ -237,7 +237,7 @@ class AUDACITY_DLL_API TrackPanel:public wxPanel {
* initial items.
*
* Ensures that all pop-down menus start with Name, and the commands for moving
* the track around, via a single set of code.
* the track around, via a single set of c ode.
* @param menu the menu to add the commands to.
*/
virtual void BuildCommonDropMenuItems(wxMenu * menu);
@ -250,12 +250,17 @@ class AUDACITY_DLL_API TrackPanel:public wxPanel {
virtual bool HandleTrackLocationMouseEvent(WaveTrack * track, wxRect &rect, wxMouseEvent &event);
virtual bool IsOverCutline(WaveTrack * track, wxRect &rect, wxMouseEvent &event);
virtual void HandleTrackSpecificMouseEvent(wxMouseEvent & event);
virtual void DrawIndicator();
virtual void TimerUpdateIndicator();
// Second member of pair indicates whether the indicator is out of date:
virtual std::pair<wxRect, bool> GetIndicatorRectangle();
virtual void UndrawIndicator(wxDC & dc);
/// draws the green line on the tracks to show playback position
/// @param repairOld if true the playback position is not updated/erased, and simply redrawn
/// @param indicator if nonnegative, overrides the indicator value obtainable from AudioIO
virtual void DoDrawIndicator(wxDC & dc, bool repairOld = false, double indicator = -1);
virtual void DrawCursor();
virtual void DoDrawIndicator(wxDC & dc);
// Second member of pair indicates whether the cursor is out of date:
virtual std::pair<wxRect, bool> GetCursorRectangle();
virtual void UndrawCursor(wxDC & dc);
virtual void DoDrawCursor(wxDC & dc);
virtual void ScrollDuringDrag();
@ -540,6 +545,10 @@ protected:
virtual void DrawBordersAroundTrack(Track *t, wxDC* dc, const wxRect & rect, const int labelw, const int vrul);
virtual void DrawOutsideOfTrack (Track *t, wxDC* dc, const wxRect & rect);
// Erase and redraw things like the cursor, cheaply and directly to the
// client area, without full refresh.
virtual void DrawOverlays(bool repaint);
virtual int IdOfRate( int rate );
virtual int IdOfFormat( int format );
@ -581,7 +590,10 @@ protected:
// This stores the parts of the screen that get overwritten by the indicator
// and cursor
int mLastIndicatorX;
int mNewIndicatorX;
int mLastCursorX;
double mCursorTime;
int mNewCursorX;
// Quick-Play indicator postion
double mOldQPIndicatorPos;
@ -664,8 +676,6 @@ protected:
bool mRedrawAfterStop;
bool mIndicatorShowing;
wxMouseEvent mLastMouseEvent;
int mMouseClickX;