1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-09-18 09:00:52 +02:00

TrackPanelCellIterator visits all cells, and FindCell simply uses it

This commit is contained in:
Paul Licameli 2017-06-17 01:26:36 -04:00
parent ffbc4d5f90
commit 91c86fd937
5 changed files with 173 additions and 150 deletions

View File

@ -600,7 +600,11 @@ VisibleTrackIterator::VisibleTrackIterator(AudacityProject *project)
bool VisibleTrackIterator::Condition(Track *t)
{
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

View File

@ -2229,6 +2229,11 @@ void TrackPanel::SetBackgroundCell
mpBackground = pCell;
}
std::shared_ptr< TrackPanelCell > TrackPanel::GetBackgroundCell()
{
return mpBackground;
}
/// Draw a three-level highlight gradient around the focused track.
void TrackPanel::HighlightFocusedTrack(wxDC * dc, const wxRect & rect)
{
@ -2490,133 +2495,23 @@ void TrackPanel::DrawShadow(Track * /* t */ , wxDC * dc, const wxRect & rect)
/// @param mouseY - mouse Y position.
TrackPanel::FoundCell TrackPanel::FindCell(int mouseX, int mouseY)
{
enum class CellType { Label, Track, VRuler, Background };
auto size = GetSize();
size.x -= kRightMargin;
wxRect rect { 0, 0, 0, 0 };
bool betweenTracks = false;
// The type of cell that may be found is determined by the x coordinate.
CellType type = CellType::Track;
if (mouseX < kLeftMargin)
;
else if (mouseX < GetVRulerOffset())
type = CellType::Label,
rect.x = kLeftMargin,
rect.width = GetVRulerOffset() - kLeftMargin;
else if (mouseX < GetLeftOffset())
type = CellType::VRuler,
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, {} };
auto range = Cells();
auto &iter = range.first, &end = range.second;
auto prev = iter;
while
( iter != end &&
!(*iter).second.Contains( mouseX, mouseY ) )
prev = iter++;
if ( iter == end )
// Default to the background cell, which is always last in the sequence,
// even if it does not contain the point
iter = prev;
auto found = *iter;
return {
static_cast<CommonTrackPanelCell*>( found.first )->FindTrack(),
found.first,
found.second
};
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
//the current rectangle. If so, return.
if (rect.Contains(mouseX, mouseY)) {
#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 (including all channels),
@ -3175,16 +3070,59 @@ IteratorRange< TrackPanelCellIterator > TrackPanel::Cells()
}
TrackPanelCellIterator::TrackPanelCellIterator(TrackPanel *trackPanel, bool begin)
: mPanel(trackPanel)
, mIter(trackPanel->GetProject())
, mpCell(begin ? mIter.First() : NULL)
: 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++ ()
{
// To do, iterate over the other cells that are not tracks
mpCell = mIter.Next();
if ( mpTrack ) {
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;
}
@ -3197,24 +3135,79 @@ TrackPanelCellIterator TrackPanelCellIterator::operator++ (int)
auto TrackPanelCellIterator::operator* () const -> value_type
{
Track *const pTrack = dynamic_cast<Track*>(mpCell);
if (!pTrack)
// to do: handle cells that are not tracks
return std::make_pair((Track*)nullptr, wxRect());
return { mpCell, mRect };
}
// Convert virtual coordinate to physical
int width;
mPanel->GetTracksUsableArea(&width, NULL);
int y = pTrack->GetY() - mPanel->GetViewInfo()->vpos;
return std::make_pair(
mpCell,
wxRect(
mPanel->GetLeftOffset(),
y + kTopMargin,
width,
pTrack->GetHeight() - (kTopMargin + kBottomMargin)
)
);
void TrackPanelCellIterator::UpdateRect()
{
// TODO: cooperate with EXPERIMENTAL_OUTPUT_DISPLAY
const auto size = mPanel->GetSize();
if ( mpTrack ) {
mRect = {
0,
mpTrack->GetY() - mPanel->GetViewInfo()->vpos,
size.x,
mpTrack->GetHeight()
};
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,

View File

@ -421,6 +421,7 @@ public:
// is not in any track or ruler or control panel.
void SetBackgroundCell
(const std::shared_ptr< TrackPanelCell > &pCell);
std::shared_ptr< TrackPanelCell > GetBackgroundCell();
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
void UpdateVirtualStereoOrder();

View File

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

View File

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