1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-22 23:30:07 +02:00

Smarter iterators for all track panel cells, not just track areas

This commit is contained in:
Paul Licameli 2017-06-17 03:01:58 -04:00
commit d7a9c1554e
9 changed files with 194 additions and 170 deletions

View File

@ -600,7 +600,11 @@ VisibleTrackIterator::VisibleTrackIterator(AudacityProject *project)
bool VisibleTrackIterator::Condition(Track *t) bool VisibleTrackIterator::Condition(Track *t)
{ {
wxRect r(0, t->GetY(), 1, t->GetHeight()); wxRect r(0, t->GetY(), 1, t->GetHeight());
return r.Intersects(mPanelRect); if( r.Intersects(mPanelRect) )
return true;
auto partner = t->GetLink();
if ( partner && t->GetLinked() )
return Condition( partner );
} }
// SyncLockedTracksIterator // SyncLockedTracksIterator

View File

@ -825,7 +825,9 @@ void TrackPanel::HandlePageDownKey()
void TrackPanel::HandleCursorForLastMouseEvent() void TrackPanel::HandleCursorForLastMouseEvent()
{ {
// Come here on modifier key transitions and change the cursor appropriately. // Come here on modifier key transitions,
// or on starting or stopping of play or record,
// and change the cursor appropriately.
HandleCursor( &mLastMouseEvent ); HandleCursor( &mLastMouseEvent );
} }
@ -2227,6 +2229,11 @@ void TrackPanel::SetBackgroundCell
mpBackground = pCell; mpBackground = pCell;
} }
std::shared_ptr< TrackPanelCell > TrackPanel::GetBackgroundCell()
{
return mpBackground;
}
/// Draw a three-level highlight gradient around the focused track. /// Draw a three-level highlight gradient around the focused track.
void TrackPanel::HighlightFocusedTrack(wxDC * dc, const wxRect & rect) void TrackPanel::HighlightFocusedTrack(wxDC * dc, const wxRect & rect)
{ {
@ -2488,137 +2495,28 @@ void TrackPanel::DrawShadow(Track * /* t */ , wxDC * dc, const wxRect & rect)
/// @param mouseY - mouse Y position. /// @param mouseY - mouse Y position.
TrackPanel::FoundCell TrackPanel::FindCell(int mouseX, int mouseY) TrackPanel::FoundCell TrackPanel::FindCell(int mouseX, int mouseY)
{ {
enum class CellType { Label, Track, VRuler, Background }; auto range = Cells();
auto size = GetSize(); auto &iter = range.first, &end = range.second;
size.x -= kRightMargin; auto prev = iter;
wxRect rect { 0, 0, 0, 0 }; while
bool betweenTracks = false; ( iter != end &&
!(*iter).second.Contains( mouseX, mouseY ) )
// The type of cell that may be found is determined by the x coordinate. prev = iter++;
CellType type = CellType::Track; if ( iter == end )
if (mouseX < kLeftMargin) // Default to the background cell, which is always last in the sequence,
; // even if it does not contain the point
else if (mouseX < GetVRulerOffset()) iter = prev;
type = CellType::Label, auto found = *iter;
rect.x = kLeftMargin, return {
rect.width = GetVRulerOffset() - kLeftMargin; static_cast<CommonTrackPanelCell*>( found.first )->FindTrack(),
else if (mouseX < GetLeftOffset()) found.first,
type = CellType::VRuler, found.second
rect.x = GetVRulerOffset(),
rect.width = GetLeftOffset() - GetVRulerOffset();
else if (mouseX < size.x)
type = CellType::Track,
rect.x = GetLeftOffset(),
rect.width = size.x - GetLeftOffset();
auto output = [&](Track *pTrack) -> FoundCell {
TrackPanelCell *pCell {};
// Did we actually hit in the resizer region, which encompasses the
// bottom margin proper to "this" track, plus the top margin of the
// "next" track (or, an equally wide zone below, in case there is no
// next track)?
const auto margin = kBottomMargin + kTopMargin;
if ( rect.y + rect.height - mouseY <= margin ) {
auto instance = &TrackPanelResizerCell::Instance();
instance->mpTrack = pTrack;
instance->mBetweenTracks = betweenTracks;
pCell = instance;
rect.y = rect.y + rect.height - kTopMargin;
rect.height = margin;
return { pTrack, pCell, rect };
}
// Undo the bias mentioned below.
rect.y -= kTopMargin;
if (pTrack) switch (type) {
case CellType::Label:
pCell = pTrack->GetTrackControl(); break;
case CellType::VRuler:
pCell = pTrack->GetVRulerControl(); break;
default:
pCell = pTrack; break;
}
if (pTrack)
return { pTrack, pCell, rect };
else
return { nullptr, nullptr, {} };
}; };
VisibleTrackIterator iter(GetProject());
for (Track * t = iter.First(); t; t = iter.Next()) {
// The zone to hit the track is biased to exclude the margin above
// but include the top margin of the track below. That makes the change
// to the track resizing cursor work right.
rect.y = t->GetY() - mViewInfo->vpos + kTopMargin;
rect.height = t->GetHeight();
if (type == CellType::Label) {
if (t->GetLink()) {
Track *l = t->GetLink();
int h = l->GetHeight();
if (!t->GetLinked())
rect.y = l->GetY() - mViewInfo->vpos + kTopMargin;
else
t = l;
rect.height += h;
}
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
else if( MONO_WAVE_PAN(t) )
rect.height += t->GetHeight(true);
#endif
}
else {
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
if( MONO_WAVE_PAN(t) )
betweenTracks = true;
else
#endif
betweenTracks = t->GetLinked();
} }
//Determine whether the mouse is inside // This finds the rectangle of a given track (including all channels),
//the current rectangle. If so, return. // either that of the label 'adornment' or the track itself
if (rect.Contains(mouseX, mouseY)) { // The given track is assumed to be the first channel
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
// PRL: Is it good to have a side effect in a hit-testing routine?
t->SetVirtualStereo(false);
#endif
return output(t);
}
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
if(type != CellType::Label && MONO_WAVE_PAN(t)){
betweenTracks = false;
rect.y = t->GetY(true) - mViewInfo->vpos + kTopMargin;
rect.height = t->GetHeight(true);
if (rect.Contains(mouseX, mouseY)) {
// PRL: Is it good to have a side effect in a hit-testing routine?
t->SetVirtualStereo(true);
return output(t);
}
}
#endif // EXPERIMENTAL_OUTPUT_DISPLAY
}
if (mpBackground) {
// In default of hits on any other cells
// Find a disjoint, maybe empty, rectangle
// for the empty space appearing at bottom
rect.x = kLeftMargin;
rect.width = size.x - rect.x;
rect.y =
std::min( size.y,
std::max( 0,
rect.y + rect.height ) );
rect.height = size.y - rect.y;
return { nullptr, mpBackground.get(), rect };
}
else
return { nullptr, nullptr, {} };
}
/// This finds the rectangle of a given track, either the
/// of the label 'adornment' or the track itself
wxRect TrackPanel::FindTrackRect( const Track * target, bool label ) wxRect TrackPanel::FindTrackRect( const Track * target, bool label )
{ {
if (!target) { if (!target) {
@ -3163,17 +3061,68 @@ void TrackInfo::UpdatePrefs()
} while (textWidth >= allowableWidth); } while (textWidth >= allowableWidth);
} }
TrackPanelCellIterator::TrackPanelCellIterator(TrackPanel *trackPanel, bool begin) IteratorRange< TrackPanelCellIterator > TrackPanel::Cells()
: mPanel(trackPanel)
, mIter(trackPanel->GetProject())
, mpCell(begin ? mIter.First() : NULL)
{ {
return {
TrackPanelCellIterator( this, true ),
TrackPanelCellIterator( this, false )
};
}
TrackPanelCellIterator::TrackPanelCellIterator(TrackPanel *trackPanel, bool begin)
: mPanel{ trackPanel }
, mIter{ trackPanel->GetProject() }
, mpTrack{ begin ? mIter.First() : nullptr }
, mpCell{ begin
? ( mpTrack ? mpTrack : trackPanel->GetBackgroundCell().get() )
: nullptr
}
{
const auto size = mPanel->GetSize();
mRect = { 0, 0, size.x, size.y };
UpdateRect();
} }
TrackPanelCellIterator &TrackPanelCellIterator::operator++ () TrackPanelCellIterator &TrackPanelCellIterator::operator++ ()
{ {
// To do, iterate over the other cells that are not tracks if ( mpTrack ) {
mpCell = mIter.Next(); if ( ++ mType == CellType::Background )
mType = CellType::Track, mpTrack = mIter.Next();
}
if ( mpTrack ) {
if ( mType == CellType::Label &&
mpTrack->GetLink() && !mpTrack->GetLinked() )
// Visit label of stereo track only once
++mType;
switch ( mType ) {
case CellType::Track:
mpCell = mpTrack;
break;
case CellType::Label:
mpCell = mpTrack->GetTrackControl();
break;
case CellType::VRuler:
mpCell = mpTrack->GetVRulerControl();
break;
case CellType::Resizer: {
auto instance = &TrackPanelResizerCell::Instance();
instance->mpTrack = mpTrack;
mpCell = instance;
break;
}
default:
// should not happen
mpCell = nullptr;
break;
}
}
else if ( !mDidBackground )
mpCell = mPanel->GetBackgroundCell().get(), mDidBackground = true;
else
mpCell = nullptr;
UpdateRect();
return *this; return *this;
} }
@ -3186,24 +3135,79 @@ TrackPanelCellIterator TrackPanelCellIterator::operator++ (int)
auto TrackPanelCellIterator::operator* () const -> value_type auto TrackPanelCellIterator::operator* () const -> value_type
{ {
Track *const pTrack = dynamic_cast<Track*>(mpCell); return { mpCell, mRect };
if (!pTrack) }
// to do: handle cells that are not tracks
return std::make_pair((Track*)nullptr, wxRect());
// Convert virtual coordinate to physical void TrackPanelCellIterator::UpdateRect()
int width; {
mPanel->GetTracksUsableArea(&width, NULL); // TODO: cooperate with EXPERIMENTAL_OUTPUT_DISPLAY
int y = pTrack->GetY() - mPanel->GetViewInfo()->vpos; const auto size = mPanel->GetSize();
return std::make_pair( if ( mpTrack ) {
mpCell, mRect = {
wxRect( 0,
mPanel->GetLeftOffset(), mpTrack->GetY() - mPanel->GetViewInfo()->vpos,
y + kTopMargin, size.x,
width, mpTrack->GetHeight()
pTrack->GetHeight() - (kTopMargin + kBottomMargin) };
) switch ( mType ) {
); case CellType::Track:
mRect.x = mPanel->GetLeftOffset();
mRect.width -= (mRect.x + kRightMargin);
mRect.y += kTopMargin;
mRect.height -= (kBottomMargin + kTopMargin);
break;
case CellType::Label: {
mRect.x = kLeftMargin;
mRect.width = kTrackInfoWidth - mRect.x;
mRect.y += kTopMargin;
mRect.height -= (kBottomMargin + kTopMargin);
auto partner = mpTrack->GetLink();
if ( partner && mpTrack->GetLinked() )
mRect.height += partner->GetHeight();
break;
}
case CellType::VRuler:
mRect.x = kTrackInfoWidth;
mRect.width = mPanel->GetLeftOffset() - mRect.x;
mRect.y += kTopMargin;
mRect.height -= (kBottomMargin + kTopMargin);
break;
case CellType::Resizer: {
// The resizer region encompasses the bottom margin proper to this
// track, plus the top margin of the next track (or, an equally
// tall zone below, in case there is no next track)
auto partner = mpTrack->GetLink();
if ( partner && mpTrack->GetLinked() )
mRect.x = mPanel->GetLeftOffset();
else
mRect.x = kLeftMargin;
mRect.width -= (mRect.x + kRightMargin);
mRect.y += (mRect.height - kBottomMargin);
mRect.height = (kBottomMargin + kTopMargin);
break;
}
default:
// should not happen
break;
}
}
else if ( mpCell ) {
// Find a disjoint, maybe empty, rectangle
// for the empty space appearing at bottom
mRect.x = kLeftMargin;
mRect.width = size.x - (mRect.x + kRightMargin);
// Use previous value of the bottom, either the whole area if
// there were no tracks, or else the resizer of the last track
mRect.y =
std::min( size.y,
std::max( 0,
mRect.y + mRect.height ) );
mRect.height = size.y - mRect.y;
}
else
mRect = {};
} }
static TrackPanel * TrackPanelFactory(wxWindow * parent, static TrackPanel * TrackPanelFactory(wxWindow * parent,

View File

@ -44,6 +44,7 @@ class MixerBoard;
class AudacityProject; class AudacityProject;
class TrackPanelAx; class TrackPanelAx;
class TrackPanelCellIterator;
struct TrackPanelMouseEvent; struct TrackPanelMouseEvent;
class ViewInfo; class ViewInfo;
@ -259,6 +260,8 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
virtual ~ TrackPanel(); virtual ~ TrackPanel();
IteratorRange< TrackPanelCellIterator > Cells();
void UpdatePrefs(); void UpdatePrefs();
void ApplyUpdatedTheme(); void ApplyUpdatedTheme();
@ -418,6 +421,7 @@ public:
// is not in any track or ruler or control panel. // is not in any track or ruler or control panel.
void SetBackgroundCell void SetBackgroundCell
(const std::shared_ptr< TrackPanelCell > &pCell); (const std::shared_ptr< TrackPanelCell > &pCell);
std::shared_ptr< TrackPanelCell > GetBackgroundCell();
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY #ifdef EXPERIMENTAL_OUTPUT_DISPLAY
void UpdateVirtualStereoOrder(); void UpdateVirtualStereoOrder();

View File

@ -12,15 +12,26 @@ Paul Licameli
#define __AUDACITY_TRACK_PANEL_CELL_ITERATOR__ #define __AUDACITY_TRACK_PANEL_CELL_ITERATOR__
#include "Track.h" #include "Track.h"
#include <wx/gdicmn.h>
#include <iterator>
class Track;
class TrackPanelCell; class TrackPanelCell;
class TrackPanel; class TrackPanel;
// A class that allows iteration over the rectangles of visible cells. // A class that allows iteration over the rectangles of visible cells.
class TrackPanelCellIterator class TrackPanelCellIterator
: public std::iterator<
std::forward_iterator_tag,
const std::pair<TrackPanelCell*, wxRect>
>
{ {
public: public:
enum class CellType {
Track, Label, VRuler, Resizer, Background
};
TrackPanelCellIterator(TrackPanel *trackPanel, bool begin); TrackPanelCellIterator(TrackPanel *trackPanel, bool begin);
// implement the STL iterator idiom // implement the STL iterator idiom
@ -38,11 +49,24 @@ public:
value_type operator * () const; value_type operator * () const;
private: private:
void UpdateRect();
TrackPanel *mPanel; TrackPanel *mPanel;
VisibleTrackIterator mIter; VisibleTrackIterator mIter;
Track *mpTrack;
TrackPanelCell *mpCell; TrackPanelCell *mpCell;
CellType mType{ CellType::Track };
bool mDidBackground{ false };
wxRect mRect;
}; };
inline TrackPanelCellIterator::CellType &operator++
( TrackPanelCellIterator::CellType &type )
{
type = TrackPanelCellIterator::CellType( 1 + int( type ) );
return type;
}
inline bool operator != inline bool operator !=
(const TrackPanelCellIterator &lhs, const TrackPanelCellIterator &rhs) (const TrackPanelCellIterator &lhs, const TrackPanelCellIterator &rhs)
{ {

View File

@ -16,6 +16,7 @@ Paul Licameli split from TrackPanel.cpp
struct HitTestResult; struct HitTestResult;
class Track; class Track;
class TrackPanelCellIterator;
class TrackPanelResizeHandle final : public UIHandle class TrackPanelResizeHandle final : public UIHandle
{ {
@ -80,7 +81,7 @@ public:
Track *FindTrack() override { return mpTrack; }; Track *FindTrack() override { return mpTrack; };
private: private:
friend class TrackPanel; friend class TrackPanelCellIterator;
Track *mpTrack {}; Track *mpTrack {};
bool mBetweenTracks {}; bool mBetweenTracks {};
}; };

View File

@ -102,13 +102,10 @@ void EditCursorOverlay::Draw(OverlayPanel &panel, wxDC &dc)
if (auto tp = dynamic_cast<TrackPanel*>(&panel)) { if (auto tp = dynamic_cast<TrackPanel*>(&panel)) {
wxASSERT(mIsMaster); wxASSERT(mIsMaster);
AColor::CursorColor(&dc); AColor::CursorColor(&dc);
TrackPanelCellIterator begin(tp, true);
TrackPanelCellIterator end(tp, false);
// Draw cursor in all selected tracks // Draw cursor in all selected tracks
for (; begin != end; ++begin) for ( const auto &data : tp->Cells() )
{ {
TrackPanelCellIterator::value_type data(*begin);
Track *const pTrack = dynamic_cast<Track*>(data.first); Track *const pTrack = dynamic_cast<Track*>(data.first);
if (!pTrack) if (!pTrack)
continue; continue;

View File

@ -69,13 +69,9 @@ void PlayIndicatorOverlayBase::Draw(OverlayPanel &panel, wxDC &dc)
if(auto tp = dynamic_cast<TrackPanel*>(&panel)) { if(auto tp = dynamic_cast<TrackPanel*>(&panel)) {
wxASSERT(mIsMaster); wxASSERT(mIsMaster);
TrackPanelCellIterator begin(tp, true);
TrackPanelCellIterator end(tp, false);
// Draw indicator in all visible tracks // Draw indicator in all visible tracks
for (; begin != end; ++begin) for ( const auto &data : tp->Cells() )
{ {
TrackPanelCellIterator::value_type data(*begin);
Track *const pTrack = dynamic_cast<Track*>(data.first); Track *const pTrack = dynamic_cast<Track*>(data.first);
if (!pTrack) if (!pTrack)
continue; continue;

View File

@ -17,7 +17,6 @@ Paul Licameli split from TrackPanel.cpp
#include "../../Project.h" #include "../../Project.h"
#include "../../TrackPanel.h" #include "../../TrackPanel.h"
#include "../../TrackPanelCell.h" #include "../../TrackPanelCell.h"
#include "../../TrackPanelCellIterator.h"
#include "../../commands/CommandFunctors.h" #include "../../commands/CommandFunctors.h"
#include "../../prefs/TracksPrefs.h" #include "../../prefs/TracksPrefs.h"
#include "../../toolbars/ControlToolBar.h" #include "../../toolbars/ControlToolBar.h"

View File

@ -1826,10 +1826,6 @@ std::pair<wxRect, bool> QuickPlayIndicatorOverlay::DoGetRectangle(wxSize size)
void QuickPlayIndicatorOverlay::Draw(OverlayPanel &panel, wxDC &dc) void QuickPlayIndicatorOverlay::Draw(OverlayPanel &panel, wxDC &dc)
{ {
TrackPanel &tp = static_cast<TrackPanel&>(panel);
TrackPanelCellIterator begin(&tp, true);
TrackPanelCellIterator end(&tp, false);
mOldQPIndicatorPos = mNewQPIndicatorPos; mOldQPIndicatorPos = mNewQPIndicatorPos;
mOldQPIndicatorSnapped = mNewQPIndicatorSnapped; mOldQPIndicatorSnapped = mNewQPIndicatorSnapped;
mOldPreviewingScrub = mNewPreviewingScrub; mOldPreviewingScrub = mNewPreviewingScrub;
@ -1843,9 +1839,8 @@ void QuickPlayIndicatorOverlay::Draw(OverlayPanel &panel, wxDC &dc)
; ;
// Draw indicator in all visible tracks // Draw indicator in all visible tracks
for (; begin != end; ++begin) for ( const auto &data : static_cast<TrackPanel&>(panel).Cells() )
{ {
TrackPanelCellIterator::value_type data(*begin);
Track *const pTrack = dynamic_cast<Track*>(data.first); Track *const pTrack = dynamic_cast<Track*>(data.first);
if (!pTrack) if (!pTrack)
continue; continue;