1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-10-29 16:53:51 +01:00

Rename TrackPanel::HandleCursor as HandleMotion...

Call HitTest in just one place

Can now preserve repeatedly hit UIHandle objects during pre-click motion

Fields of HitTestResult besides the handle pointer are now unused

The need to repaint a track during mouse movement can be indicated when
constructing a UIHandle or when updating it for move; HitPreview no longer
does this

And the last allows simplifications of LabelTrack glyph highlighting

Also move the temporary state for label glyph dragging out of LabelTrack
This commit is contained in:
Paul Licameli
2017-06-17 21:37:41 -04:00
parent 2c1a16f593
commit f5b0afc2fc
10 changed files with 336 additions and 210 deletions

View File

@@ -24,17 +24,12 @@ struct HitTestPreview
HitTestPreview() HitTestPreview()
{} {}
HitTestPreview(const wxString &message_, wxCursor *cursor_ HitTestPreview(const wxString &message_, wxCursor *cursor_)
, unsigned refreshCode_ = 0) : message(message_), cursor(cursor_)
: message(message_), cursor(cursor_), refreshCode(refreshCode_)
{} {}
wxString message {}; wxString message {};
wxCursor *cursor {}; wxCursor *cursor {};
// Making this non-zero allows mouse-over highlighting
// See RefreshCode.h for bit flags:
unsigned refreshCode {};
}; };
struct HitTestResult struct HitTestResult

View File

@@ -102,14 +102,9 @@ LabelTrack::Holder TrackFactory::NewLabelTrack()
LabelTrack::LabelTrack(const std::shared_ptr<DirManager> &projDirManager): LabelTrack::LabelTrack(const std::shared_ptr<DirManager> &projDirManager):
Track(projDirManager), Track(projDirManager),
mbHitCenter(false),
mOldEdge(-1),
mSelIndex(-1), mSelIndex(-1),
mMouseOverLabelLeft(-1),
mMouseOverLabelRight(-1),
mRestoreFocus(-1), mRestoreFocus(-1),
mClipLen(0.0), mClipLen(0.0),
mIsAdjustingLabel(false),
miLastLabel(-1) miLastLabel(-1)
{ {
SetDefaultName(_("Label Track")); SetDefaultName(_("Label Track"));
@@ -129,13 +124,8 @@ LabelTrack::LabelTrack(const std::shared_ptr<DirManager> &projDirManager):
LabelTrack::LabelTrack(const LabelTrack &orig) : LabelTrack::LabelTrack(const LabelTrack &orig) :
Track(orig), Track(orig),
mbHitCenter(false),
mOldEdge(-1),
mSelIndex(-1), mSelIndex(-1),
mMouseOverLabelLeft(-1), mClipLen(0.0)
mMouseOverLabelRight(-1),
mClipLen(0.0),
mIsAdjustingLabel(false)
{ {
for (auto &original: orig.mLabels) { for (auto &original: orig.mLabels) {
LabelStruct l { original.selectedRegion, original.title }; LabelStruct l { original.selectedRegion, original.title };
@@ -766,6 +756,23 @@ void LabelTrack::CalcHighlightXs(int *x1, int *x2) const
labelStruct.getXPos(dc, x2, pos2); labelStruct.getXPos(dc, x2, pos2);
} }
#include "tracks/labeltrack/ui/LabelGlyphHandle.h"
// TODO: don't rely on the global ::GetActiveProject() to find this.
// Rather, give TrackPanelCell a drawing function and pass context into it.
namespace {
LabelTrackHit *findHit()
{
// Fetch the highlighting state
auto target = GetActiveProject()->GetTrackPanel()->Target();
if (target) {
auto handle = dynamic_cast<LabelGlyphHandle*>( target->handle.get() );
if (handle)
return &handle->mHit;
}
return nullptr;
}
}
/// Draw calls other functions to draw the LabelTrack. /// Draw calls other functions to draw the LabelTrack.
/// @param dc the device context /// @param dc the device context
/// @param r the LabelTrack rectangle. /// @param r the LabelTrack rectangle.
@@ -773,6 +780,8 @@ void LabelTrack::Draw(wxDC & dc, const wxRect & r,
const SelectedRegion &selectedRegion, const SelectedRegion &selectedRegion,
const ZoomInfo &zoomInfo) const const ZoomInfo &zoomInfo) const
{ {
auto pHit = findHit();
if(msFont.Ok()) if(msFont.Ok())
dc.SetFont(msFont); dc.SetFont(msFont);
@@ -819,10 +828,10 @@ void LabelTrack::Draw(wxDC & dc, const wxRect & r,
{ int i = -1; for (auto &labelStruct : mLabels) { ++i; { int i = -1; for (auto &labelStruct : mLabels) { ++i;
GlyphLeft=0; GlyphLeft=0;
GlyphRight=1; GlyphRight=1;
if( i==mMouseOverLabelLeft ) if( pHit && i == pHit->mMouseOverLabelLeft )
GlyphLeft = mbHitCenter ? 6:9; GlyphLeft = (pHit->mEdge & 4) ? 6:9;
if( i==mMouseOverLabelRight ) if( pHit && i == pHit->mMouseOverLabelRight )
GlyphRight = mbHitCenter ? 7:4; GlyphRight = (pHit->mEdge & 4) ? 7:4;
labelStruct.DrawGlyphs( dc, r, GlyphLeft, GlyphRight ); labelStruct.DrawGlyphs( dc, r, GlyphLeft, GlyphRight );
}} }}
@@ -1119,20 +1128,10 @@ void LabelTrack::SetSelected(bool s)
Unselect(); Unselect();
} }
/// OverGlyph returns 0 if not over a glyph,
/// 1 if over the left-hand glyph, and
/// 2 if over the right-hand glyph on a label.
/// 3 if over both right and left.
///
/// It also sets up member variables:
/// mMouseLabelLeft - index of any left label hit
/// mMouseLabelRight - index of any right label hit
/// mbHitCenter - if (x,y) 'hits the spot'.
///
/// TODO: Investigate what happens with large /// TODO: Investigate what happens with large
/// numbers of labels, might need a binary search /// numbers of labels, might need a binary search
/// rather than a linear one. /// rather than a linear one.
int LabelTrack::OverGlyph(int x, int y) void LabelTrack::OverGlyph(LabelTrackHit &hit, int x, int y) const
{ {
//Determine the NEW selection. //Determine the NEW selection.
int result=0; int result=0;
@@ -1140,9 +1139,9 @@ int LabelTrack::OverGlyph(int x, int y)
const int d2=5; //distance in pixels, used for have we hit drag handle center. const int d2=5; //distance in pixels, used for have we hit drag handle center.
//If not over a label, reset it //If not over a label, reset it
mMouseOverLabelLeft = -1; hit.mMouseOverLabelLeft = -1;
mMouseOverLabelRight = -1; hit.mMouseOverLabelRight = -1;
mbHitCenter = false; hit.mEdge = 0;
{ int i = -1; for (auto &labelStruct : mLabels) { ++i; { int i = -1; for (auto &labelStruct : mLabels) { ++i;
//over left or right selection bound //over left or right selection bound
//Check right bound first, since it is drawn after left bound, //Check right bound first, since it is drawn after left bound,
@@ -1150,16 +1149,16 @@ int LabelTrack::OverGlyph(int x, int y)
if( abs(labelStruct.y - (y - (LabelTrack::mTextHeight+3)/2)) < d1 && if( abs(labelStruct.y - (y - (LabelTrack::mTextHeight+3)/2)) < d1 &&
abs(labelStruct.x1 - d2 -x) < d1) abs(labelStruct.x1 - d2 -x) < d1)
{ {
mMouseOverLabelRight = i; hit.mMouseOverLabelRight = i;
if(abs(labelStruct.x1 - x) < d2 ) if(abs(labelStruct.x1 - x) < d2 )
{ {
mbHitCenter = true; result |= 4;
// If left and right co-incident at this resolution, then we drag both. // If left and right co-incident at this resolution, then we drag both.
// We could be a little less stringent about co-incidence here if we liked. // We could be a little less stringent about co-incidence here if we liked.
if( abs(labelStruct.x1-labelStruct.x) < 1.0 ) if( abs(labelStruct.x1-labelStruct.x) < 1.0 )
{ {
result |=1; result |=1;
mMouseOverLabelLeft = i; hit.mMouseOverLabelLeft = i;
} }
} }
result |= 2; result |= 2;
@@ -1169,9 +1168,9 @@ int LabelTrack::OverGlyph(int x, int y)
else if( abs(labelStruct.y - (y - (LabelTrack::mTextHeight+3)/2)) < d1 && else if( abs(labelStruct.y - (y - (LabelTrack::mTextHeight+3)/2)) < d1 &&
abs(labelStruct.x + d2 - x) < d1 ) abs(labelStruct.x + d2 - x) < d1 )
{ {
mMouseOverLabelLeft = i; hit.mMouseOverLabelLeft = i;
if(abs(labelStruct.x - x) < d2 ) if(abs(labelStruct.x - x) < d2 )
mbHitCenter = true; result |= 4;
result |= 1; result |= 1;
} }
@@ -1182,7 +1181,7 @@ int LabelTrack::OverGlyph(int x, int y)
} }
}} }}
return result; hit.mEdge = result;
} }
int LabelTrack::OverATextBox(int xx, int yy) const int LabelTrack::OverATextBox(int xx, int yy) const
@@ -1403,7 +1402,8 @@ auto LabelStruct::RegionRelation(
/// @iEdge - which edge is requested to move, -1 for left +1 for right. /// @iEdge - which edge is requested to move, -1 for left +1 for right.
/// @bAllowSwapping - if we can switch which edge is being dragged. /// @bAllowSwapping - if we can switch which edge is being dragged.
/// fNewTime - the NEW time for this edge of the label. /// fNewTime - the NEW time for this edge of the label.
void LabelTrack::MayAdjustLabel( int iLabel, int iEdge, bool bAllowSwapping, double fNewTime) void LabelTrack::MayAdjustLabel
( LabelTrackHit &hit, int iLabel, int iEdge, bool bAllowSwapping, double fNewTime)
{ {
if( iLabel < 0 ) if( iLabel < 0 )
return; return;
@@ -1424,9 +1424,7 @@ void LabelTrack::MayAdjustLabel( int iLabel, int iEdge, bool bAllowSwapping, dou
} }
// Swap our record of what we are dragging. // Swap our record of what we are dragging.
int Temp = mMouseOverLabelLeft; std::swap( hit.mMouseOverLabelLeft, hit.mMouseOverLabelRight );
mMouseOverLabelLeft = mMouseOverLabelRight;
mMouseOverLabelRight = Temp;
} }
// If the index is for a real label, adjust its left and right boundary. // If the index is for a real label, adjust its left and right boundary.
@@ -1450,27 +1448,28 @@ static int Constrain( int value, int min, int max )
return result; return result;
} }
bool LabelTrack::HandleGlyphDragRelease(const wxMouseEvent & evt, bool LabelTrack::HandleGlyphDragRelease
wxRect & r, const ZoomInfo &zoomInfo, (LabelTrackHit &hit, const wxMouseEvent & evt,
SelectedRegion *newSel) wxRect & r, const ZoomInfo &zoomInfo,
SelectedRegion *newSel)
{ {
if(evt.LeftUp()) if(evt.LeftUp())
{ {
bool lupd = false, rupd = false; bool lupd = false, rupd = false;
if(mMouseOverLabelLeft>=0) { if( hit.mMouseOverLabelLeft >= 0 ) {
auto &labelStruct = mLabels[mMouseOverLabelLeft]; auto &labelStruct = mLabels[ hit.mMouseOverLabelLeft ];
lupd = labelStruct.updated; lupd = labelStruct.updated;
labelStruct.updated = false; labelStruct.updated = false;
} }
if(mMouseOverLabelRight>=0) { if( hit.mMouseOverLabelRight >= 0 ) {
auto &labelStruct = mLabels[mMouseOverLabelRight]; auto &labelStruct = mLabels[ hit.mMouseOverLabelRight ];
rupd = labelStruct.updated; rupd = labelStruct.updated;
labelStruct.updated = false; labelStruct.updated = false;
} }
mIsAdjustingLabel = false; hit.mIsAdjustingLabel = false;
mMouseOverLabelLeft = -1; hit.mMouseOverLabelLeft = -1;
mMouseOverLabelRight = -1; hit.mMouseOverLabelRight = -1;
return lupd || rupd; return lupd || rupd;
} }
@@ -1483,23 +1482,25 @@ bool LabelTrack::HandleGlyphDragRelease(const wxMouseEvent & evt,
int x = Constrain( evt.m_x + mxMouseDisplacement - r.x, 0, r.width); int x = Constrain( evt.m_x + mxMouseDisplacement - r.x, 0, r.width);
// If exactly one edge is selected we allow swapping // If exactly one edge is selected we allow swapping
bool bAllowSwapping = (mMouseOverLabelLeft >=0 ) ^ ( mMouseOverLabelRight >= 0); bool bAllowSwapping =
( hit.mMouseOverLabelLeft >=0 ) !=
( hit.mMouseOverLabelRight >= 0);
// If we're on the 'dot' and nowe're moving, // If we're on the 'dot' and nowe're moving,
// Though shift-down inverts that. // Though shift-down inverts that.
// and if both edges the same, then we're always moving the label. // and if both edges the same, then we're always moving the label.
bool bLabelMoving = mbIsMoving; bool bLabelMoving = hit.mbIsMoving;
bLabelMoving ^= evt.ShiftDown(); bLabelMoving ^= evt.ShiftDown();
bLabelMoving |= mMouseOverLabelLeft==mMouseOverLabelRight; bLabelMoving |= ( hit.mMouseOverLabelLeft == hit.mMouseOverLabelRight );
double fNewX = zoomInfo.PositionToTime(x, 0); double fNewX = zoomInfo.PositionToTime(x, 0);
if( bLabelMoving ) if( bLabelMoving )
{ {
MayMoveLabel( mMouseOverLabelLeft, -1, fNewX ); MayMoveLabel( hit.mMouseOverLabelLeft, -1, fNewX );
MayMoveLabel( mMouseOverLabelRight, +1, fNewX ); MayMoveLabel( hit.mMouseOverLabelRight, +1, fNewX );
} }
else else
{ {
MayAdjustLabel( mMouseOverLabelLeft, -1, bAllowSwapping, fNewX ); MayAdjustLabel( hit, hit.mMouseOverLabelLeft, -1, bAllowSwapping, fNewX );
MayAdjustLabel( mMouseOverLabelRight, +1, bAllowSwapping, fNewX ); MayAdjustLabel( hit, hit.mMouseOverLabelRight, +1, bAllowSwapping, fNewX );
} }
if( mSelIndex >=0 ) if( mSelIndex >=0 )
@@ -1508,7 +1509,7 @@ bool LabelTrack::HandleGlyphDragRelease(const wxMouseEvent & evt,
//the NEW size of the label. //the NEW size of the label.
*newSel = mLabels[mSelIndex].selectedRegion; *newSel = mLabels[mSelIndex].selectedRegion;
} }
SortLabels(); SortLabels( &hit );
} }
return false; return false;
@@ -1556,23 +1557,24 @@ void LabelTrack::HandleTextDragRelease(const wxMouseEvent & evt)
return; return;
} }
void LabelTrack::HandleGlyphClick(const wxMouseEvent & evt, void LabelTrack::HandleGlyphClick
const wxRect & r, const ZoomInfo &zoomInfo, (LabelTrackHit &hit, const wxMouseEvent & evt,
SelectedRegion *newSel) const wxRect & r, const ZoomInfo &zoomInfo,
SelectedRegion *newSel)
{ {
if (evt.ButtonDown()) if (evt.ButtonDown())
{ {
//OverGlyph sets mMouseOverLabel to be the chosen label. //OverGlyph sets mMouseOverLabel to be the chosen label.
int iGlyph = OverGlyph(evt.m_x, evt.m_y); OverGlyph(hit, evt.m_x, evt.m_y);
mIsAdjustingLabel = evt.Button(wxMOUSE_BTN_LEFT) && hit.mIsAdjustingLabel = evt.Button(wxMOUSE_BTN_LEFT) &&
iGlyph != 0; ( hit.mEdge & 3 ) != 0;
if (mIsAdjustingLabel) if (hit.mIsAdjustingLabel)
{ {
float t = 0.0; float t = 0.0;
// We move if we hit the centre, we adjust one edge if we hit a chevron. // We move if we hit the centre, we adjust one edge if we hit a chevron.
// This is if we are moving just one edge. // This is if we are moving just one edge.
mbIsMoving = mbHitCenter; hit.mbIsMoving = hit.mEdge & 4;
// When we start dragging the label(s) we don't want them to jump. // When we start dragging the label(s) we don't want them to jump.
// so we calculate the displacement of the mouse from the drag center // so we calculate the displacement of the mouse from the drag center
// and use that in subsequent dragging calculations. The mouse stays // and use that in subsequent dragging calculations. The mouse stays
@@ -1585,27 +1587,27 @@ void LabelTrack::HandleGlyphClick(const wxMouseEvent & evt,
// position when we start dragging. // position when we start dragging.
// Dragging of three label edges at the same time is not supported (yet). // Dragging of three label edges at the same time is not supported (yet).
if( (mMouseOverLabelRight >=0) && if( ( hit.mMouseOverLabelRight >= 0 ) &&
(mMouseOverLabelLeft >=0) ( hit.mMouseOverLabelLeft >= 0 )
) )
{ {
t = (mLabels[mMouseOverLabelRight].getT1() + t = (mLabels[ hit.mMouseOverLabelRight ].getT1() +
mLabels[mMouseOverLabelLeft].getT0()) / 2.0f; mLabels[ hit.mMouseOverLabelLeft ].getT0()) / 2.0f;
// If we're moving two edges, then it's a move (label size preserved) // If we're moving two edges, then it's a move (label size preserved)
// if both edges are the same label, and it's an adjust (label sizes change) // if both edges are the same label, and it's an adjust (label sizes change)
// if we're on a boundary between two different labels. // if we're on a boundary between two different labels.
mbIsMoving = (mMouseOverLabelLeft == mMouseOverLabelRight); hit.mbIsMoving =
( hit.mMouseOverLabelLeft == hit.mMouseOverLabelRight );
} }
else if(mMouseOverLabelRight >=0) else if( hit.mMouseOverLabelRight >=0)
{ {
t = mLabels[mMouseOverLabelRight].getT1(); t = mLabels[ hit.mMouseOverLabelRight ].getT1();
} }
else if(mMouseOverLabelLeft >=0) else if( hit.mMouseOverLabelLeft >=0)
{ {
t = mLabels[mMouseOverLabelLeft].getT0(); t = mLabels[ hit.mMouseOverLabelLeft ].getT0();
} }
mxMouseDisplacement = zoomInfo.TimeToPosition(t, r.x) - evt.m_x; mxMouseDisplacement = zoomInfo.TimeToPosition(t, r.x) - evt.m_x;
return;
} }
} }
} }
@@ -2879,7 +2881,7 @@ bool LabelTrack::IsGoodLabelEditKey(const wxKeyEvent & evt)
/// This function is called often (whilst dragging a label) /// This function is called often (whilst dragging a label)
/// We expect them to be very nearly in order, so insertion /// We expect them to be very nearly in order, so insertion
/// sort (with a linear search) is a reasonable choice. /// sort (with a linear search) is a reasonable choice.
void LabelTrack::SortLabels() void LabelTrack::SortLabels( LabelTrackHit *pHit )
{ {
const auto begin = mLabels.begin(); const auto begin = mLabels.begin();
const auto nn = (int)mLabels.size(); const auto nn = (int)mLabels.size();
@@ -2914,8 +2916,10 @@ void LabelTrack::SortLabels()
++index; ++index;
} }
}; };
update(mMouseOverLabelLeft); if ( pHit ) {
update(mMouseOverLabelRight); update( pHit->mMouseOverLabelLeft );
update( pHit->mMouseOverLabelRight );
}
update(mSelIndex); update(mSelIndex);
} }
} }

View File

@@ -39,6 +39,8 @@ class TimeWarper;
class ZoomInfo; class ZoomInfo;
struct LabelTrackHit;
class LabelStruct class LabelStruct
{ {
public: public:
@@ -150,7 +152,6 @@ class AUDACITY_DLL_API LabelTrack final : public Track
const ZoomInfo &zoomInfo) const; const ZoomInfo &zoomInfo) const;
int getSelectedIndex() const { return mSelIndex; } int getSelectedIndex() const { return mSelIndex; }
bool IsAdjustingLabel() const { return mIsAdjustingLabel; }
int GetKind() const override { return Label; } int GetKind() const override { return Label; }
@@ -180,7 +181,7 @@ class AUDACITY_DLL_API LabelTrack final : public Track
void Silence(double t0, double t1) override; void Silence(double t0, double t1) override;
void InsertSilence(double t, double len) override; void InsertSilence(double t, double len) override;
int OverGlyph(int x, int y); void OverGlyph(LabelTrackHit &hit, int x, int y) const;
static wxBitmap & GetGlyph( int i); static wxBitmap & GetGlyph( int i);
@@ -206,13 +207,16 @@ class AUDACITY_DLL_API LabelTrack final : public Track
static bool IsTextClipSupported(); static bool IsTextClipSupported();
void HandleGlyphClick void HandleGlyphClick
(const wxMouseEvent & evt, const wxRect & r, const ZoomInfo &zoomInfo, (LabelTrackHit &hit,
const wxMouseEvent & evt, const wxRect & r, const ZoomInfo &zoomInfo,
SelectedRegion *newSel); SelectedRegion *newSel);
void HandleTextClick void HandleTextClick
(const wxMouseEvent & evt, const wxRect & r, const ZoomInfo &zoomInfo, (const wxMouseEvent & evt, const wxRect & r, const ZoomInfo &zoomInfo,
SelectedRegion *newSel); SelectedRegion *newSel);
bool HandleGlyphDragRelease(const wxMouseEvent & evt, wxRect & r, const ZoomInfo &zoomInfo, bool HandleGlyphDragRelease
SelectedRegion *newSel); (LabelTrackHit &hit,
const wxMouseEvent & evt, wxRect & r, const ZoomInfo &zoomInfo,
SelectedRegion *newSel);
void HandleTextDragRelease(const wxMouseEvent & evt); void HandleTextDragRelease(const wxMouseEvent & evt);
bool OnKeyDown(SelectedRegion &sel, wxKeyEvent & event); bool OnKeyDown(SelectedRegion &sel, wxKeyEvent & event);
@@ -243,7 +247,9 @@ class AUDACITY_DLL_API LabelTrack final : public Track
void CalcHighlightXs(int *x1, int *x2) const; void CalcHighlightXs(int *x1, int *x2) const;
void MayAdjustLabel( int iLabel, int iEdge, bool bAllowSwapping, double fNewTime); void MayAdjustLabel
( LabelTrackHit &hit,
int iLabel, int iEdge, bool bAllowSwapping, double fNewTime);
void MayMoveLabel( int iLabel, int iEdge, double fNewTime); void MayMoveLabel( int iLabel, int iEdge, double fNewTime);
// This pastes labels without shifting existing ones // This pastes labels without shifting existing ones
@@ -266,19 +272,12 @@ class AUDACITY_DLL_API LabelTrack final : public Track
int FindPrevLabel(const SelectedRegion& currentSelection); int FindPrevLabel(const SelectedRegion& currentSelection);
public: public:
void SortLabels(); void SortLabels(LabelTrackHit *pHit = nullptr);
//These two are used by a TrackPanel KLUDGE, which is why they are public.
bool mbHitCenter;
//The edge variable tells us what state the icon is in.
//mOldEdge is useful for telling us when there has been a state change.
int mOldEdge;
private: private:
void ShowContextMenu(); void ShowContextMenu();
void OnContextMenu(wxCommandEvent & evt); void OnContextMenu(wxCommandEvent & evt);
int mSelIndex; /// Keeps track of the currently selected label int mSelIndex; /// Keeps track of the currently selected label
int mMouseOverLabelLeft; /// Keeps track of which left label the mouse is currently over.
int mMouseOverLabelRight; /// Keeps track of which right label the mouse is currently over.
int mxMouseDisplacement; /// Displacement of mouse cursor from the centre being dragged. int mxMouseDisplacement; /// Displacement of mouse cursor from the centre being dragged.
LabelArray mLabels; LabelArray mLabels;
@@ -314,9 +313,6 @@ private:
void calculateFontHeight(wxDC & dc) const; void calculateFontHeight(wxDC & dc) const;
void RemoveSelectedText(); void RemoveSelectedText();
bool mIsAdjustingLabel;
bool mbIsMoving;
static wxFont msFont; static wxFont msFont;
std::weak_ptr<LabelGlyphHandle> mGlyphHandle; std::weak_ptr<LabelGlyphHandle> mGlyphHandle;

View File

@@ -758,7 +758,7 @@ void TrackPanel::Uncapture(wxMouseEvent *pEvent)
{ {
if (HasCapture()) if (HasCapture())
ReleaseMouse(); ReleaseMouse();
HandleCursor( pEvent ); HandleMotion( pEvent );
} }
void TrackPanel::CancelDragging() void TrackPanel::CancelDragging()
@@ -768,9 +768,10 @@ void TrackPanel::CancelDragging()
auto pTrack = GetTracks()->Lock(mpClickedTrack); auto pTrack = GetTracks()->Lock(mpClickedTrack);
if (pTrack) if (pTrack)
ProcessUIHandleResult( ProcessUIHandleResult(
this, mRuler, pTrack.get(), NULL, refreshResult); this, mRuler, pTrack.get(), NULL,
refreshResult | mMouseOverUpdateFlags );
mpClickedTrack.reset(); mpClickedTrack.reset();
mUIHandle.reset(); mUIHandle.reset(), ClearTargets();
Uncapture(); Uncapture();
} }
} }
@@ -830,7 +831,7 @@ void TrackPanel::HandleCursorForLastMouseState()
// Come here on modifier key or mouse button transitions, // Come here on modifier key or mouse button transitions,
// or on starting or stopping of play or record, // or on starting or stopping of play or record,
// and change the cursor appropriately. // and change the cursor appropriately.
HandleCursor( &mLastMouseState ); HandleMotion( &mLastMouseState );
} }
bool TrackPanel::IsAudioActive() bool TrackPanel::IsAudioActive()
@@ -840,10 +841,14 @@ bool TrackPanel::IsAudioActive()
} }
/// TrackPanel::HandleCursor( ) sets the cursor drawn at the mouse location. /// TrackPanel::HandleMotion( ) sets the cursor drawn at the mouse location,
/// and updates the status bar message.
/// We treat certain other changes of mouse button and key state as "motions"
/// too, and also starting and stopping of playback or recording, all of which
/// may cause the appropriate cursor and message to change.
/// As this procedure checks which region the mouse is over, it is /// As this procedure checks which region the mouse is over, it is
/// appropriate to establish the message in the status bar. /// appropriate to establish the message in the status bar.
void TrackPanel::HandleCursor( wxMouseState *pState ) void TrackPanel::HandleMotion( wxMouseState *pState )
{ {
wxMouseState dummy; wxMouseState dummy;
if (!pState) if (!pState)
@@ -857,39 +862,78 @@ void TrackPanel::HandleCursor( wxMouseState *pState )
auto &rect = foundCell.rect; auto &rect = foundCell.rect;
auto &pCell = foundCell.pCell; auto &pCell = foundCell.pCell;
const TrackPanelMouseState tpmState{ state, rect, pCell }; const TrackPanelMouseState tpmState{ state, rect, pCell };
HandleCursor( tpmState ); HandleMotion( tpmState );
} }
void TrackPanel::HandleCursor( const TrackPanelMouseState &tpmState ) void TrackPanel::HandleMotion( const TrackPanelMouseState &tpmState )
{ {
if ( mUIHandle ) { HitTestResult result;
// UIHANDLE PREVIEW auto handle = mUIHandle;
// Update status message and cursor during drag
HitTestPreview preview = mUIHandle->Preview( tpmState, GetProject() );
mListener->TP_DisplayStatusMessage( preview.message );
if ( preview.cursor )
SetCursor( *preview.cursor );
}
else {
wxCursor *pCursor = NULL;
wxString tip; auto oldHandle = mLastHitTest.handle;
auto oldCell = mLastCell.lock();
auto newCell = tpmState.pCell;
auto pCell = tpmState.pCell; std::shared_ptr<Track> newTrack;
auto track = static_cast<CommonTrackPanelCell*>( pCell.get() )->FindTrack(); if ( newCell )
if (pCell && pCursor == NULL && tip == wxString()) { newTrack = static_cast<CommonTrackPanelCell*>( newCell.get() )->FindTrack();
HitTestResult hitTest( pCell->HitTest(tpmState, GetProject()) );
tip = hitTest.preview.message; std::shared_ptr<Track> oldTrack;
ProcessUIHandleResult if ( oldCell )
(this, mRuler, track.get(), track.get(), hitTest.preview.refreshCode); oldTrack = static_cast<CommonTrackPanelCell*>( oldCell.get() )->FindTrack();
pCursor = hitTest.preview.cursor;
if (pCursor) wxString tip{};
SetCursor(*pCursor); wxCursor *pCursor{};
unsigned refreshCode = 0;
if ( !mUIHandle ) {
// Not yet dragging.
unsigned updateFlags = mMouseOverUpdateFlags;
// First check whether crossing cell to cell
if ( newCell == oldCell )
oldCell.reset();
else {
// Forget old targets
ClearTargets();
// Re-draw any highlighting
if (oldCell) {
ProcessUIHandleResult(
this, GetRuler(), oldTrack.get(), oldTrack.get(), updateFlags);
}
} }
if (pCursor != NULL || tip != wxString()) // Now do the
mListener->TP_DisplayStatusMessage(tip); // UIHANDLE HIT TEST !
result = newCell->HitTest(tpmState, GetProject());
handle = result.handle;
mLastCell = newCell;
mLastHitTestValid = true;
mLastHitTest = result;
if (!oldCell && oldHandle != handle)
// Did not move cell to cell, but did change the target
refreshCode = updateFlags;
} }
// UIHANDLE PREVIEW
// Update status message and cursor, whether dragging or not
if (handle) {
auto preview = handle->Preview( tpmState, GetProject() );
tip = preview.message;
pCursor = preview.cursor;
auto code = handle->GetChangeHighlight();
handle->SetChangeHighlight(RefreshCode::RefreshNone);
refreshCode |= code;
mMouseOverUpdateFlags |= code;
}
mListener->TP_DisplayStatusMessage(tip);
if (pCursor)
SetCursor( *pCursor );
ProcessUIHandleResult(
this, GetRuler(), newTrack.get(), newTrack.get(), refreshCode);
} }
void TrackPanel::UpdateSelectionDisplay() void TrackPanel::UpdateSelectionDisplay()
@@ -1238,7 +1282,8 @@ void TrackPanel::HandleWheelRotation( TrackPanelMouseEvent &tpmEvent )
unsigned result = unsigned result =
pCell->HandleWheelRotation( tpmEvent, GetProject() ); pCell->HandleWheelRotation( tpmEvent, GetProject() );
auto pTrack = static_cast<CommonTrackPanelCell*>(pCell.get())->FindTrack(); auto pTrack = static_cast<CommonTrackPanelCell*>(pCell.get())->FindTrack();
ProcessUIHandleResult(this, mRuler, pTrack.get(), pTrack.get(), result); ProcessUIHandleResult(
this, mRuler, pTrack.get(), pTrack.get(), result);
} }
/// Filter captured keys typed into LabelTracks. /// Filter captured keys typed into LabelTracks.
@@ -1361,6 +1406,8 @@ void TrackPanel::OnKeyUp(wxKeyEvent & event)
/// Should handle the case when the mouse capture is lost. /// Should handle the case when the mouse capture is lost.
void TrackPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(event)) void TrackPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(event))
{ {
ClearTargets();
wxMouseEvent e(wxEVT_LEFT_UP); wxMouseEvent e(wxEVT_LEFT_UP);
e.m_x = mMouseMostRecentX; e.m_x = mMouseMostRecentX;
@@ -1436,6 +1483,9 @@ try
if (event.Leaving()) if (event.Leaving())
{ {
if ( !mUIHandle )
ClearTargets();
auto buttons = auto buttons =
// Bug 1325: button state in Leaving events is unreliable on Mac. // Bug 1325: button state in Leaving events is unreliable on Mac.
// Poll the global state instead. // Poll the global state instead.
@@ -1463,32 +1513,32 @@ try
mUIHandle->Drag( tpmEvent, GetProject() ); mUIHandle->Drag( tpmEvent, GetProject() );
ProcessUIHandleResult ProcessUIHandleResult
(this, mRuler, pClickedTrack.get(), pTrack.get(), refreshResult); (this, mRuler, pClickedTrack.get(), pTrack.get(), refreshResult);
mMouseOverUpdateFlags |= refreshResult;
if (refreshResult & RefreshCode::Cancelled) { if (refreshResult & RefreshCode::Cancelled) {
// Drag decided to abort itself // Drag decided to abort itself
mUIHandle.reset(); mUIHandle.reset(), ClearTargets();
mpClickedTrack.reset(); mpClickedTrack.reset();
Uncapture( &event ); Uncapture( &event );
} }
else { else {
TrackPanelMouseState tpmState{ UpdateMouseState(event);
tpmEvent.event, TrackPanelMouseState tpmState{ mLastMouseState, rect, pCell };
tpmEvent.rect, HandleMotion( tpmState );
tpmEvent.pCell
};
HandleCursor( tpmState );
} }
} }
else if (event.ButtonUp()) { else if (event.ButtonUp()) {
// UIHANDLE RELEASE // UIHANDLE RELEASE
auto uiHandle = mUIHandle; auto uiHandle = mUIHandle;
// Null this pointer out first before calling Release -- because on Windows, we can // Null mUIHandle out first before calling Release -- because on Windows, we can
// come back recursively to this place during handling of the context menu, // come back recursively to this place during handling of the context menu,
// because of a capture lost event. // because of a capture lost event.
mUIHandle.reset(); unsigned moreFlags = mMouseOverUpdateFlags;
mUIHandle.reset(), ClearTargets();
UIHandle::Result refreshResult = UIHandle::Result refreshResult =
uiHandle->Release( tpmEvent, GetProject(), this ); uiHandle->Release( tpmEvent, GetProject(), this );
ProcessUIHandleResult ProcessUIHandleResult
(this, mRuler, pClickedTrack.get(), pTrack.get(), refreshResult); (this, mRuler, pClickedTrack.get(), pTrack.get(),
refreshResult | moreFlags);
mpClickedTrack.reset(); mpClickedTrack.reset();
// will also Uncapture() below // will also Uncapture() below
} }
@@ -1498,7 +1548,7 @@ try
// consider it not a drag, even if button is down during motion, if // consider it not a drag, even if button is down during motion, if
// mUIHandle is null, as it becomes during interrupted drag // mUIHandle is null, as it becomes during interrupted drag
// (e.g. by hitting space to play while dragging an envelope point) // (e.g. by hitting space to play while dragging an envelope point)
HandleCursor( &event ); HandleMotion( &event );
else if ( event.ButtonDown() || event.ButtonDClick() ) else if ( event.ButtonDown() || event.ButtonDClick() )
HandleClick( tpmEvent ); HandleClick( tpmEvent );
@@ -1538,32 +1588,44 @@ void TrackPanel::HandleClick( const TrackPanelMouseEvent &tpmEvent )
const auto &rect = tpmEvent.rect; const auto &rect = tpmEvent.rect;
auto pTrack = static_cast<CommonTrackPanelCell *>( pCell.get() )->FindTrack(); auto pTrack = static_cast<CommonTrackPanelCell *>( pCell.get() )->FindTrack();
if ( !mUIHandle && pCell ) { // Do hit test once more, in case the button really pressed was not the
// one "anticipated."
{
TrackPanelMouseState tpmState{ TrackPanelMouseState tpmState{
tpmEvent.event, tpmEvent.event,
tpmEvent.rect, tpmEvent.rect,
tpmEvent.pCell tpmEvent.pCell
}; };
mUIHandle = HandleMotion( tpmState );
pCell->HitTest( tpmState, GetProject() ).handle;
} }
auto target = Target();
if (target)
mUIHandle = target->handle;
else
mUIHandle = {};
if (mUIHandle) { if (mUIHandle) {
// UIHANDLE CLICK // UIHANDLE CLICK
UIHandle::Result refreshResult = UIHandle::Result refreshResult =
mUIHandle->Click( tpmEvent, GetProject() ); mUIHandle->Click( tpmEvent, GetProject() );
if (refreshResult & RefreshCode::Cancelled) if (refreshResult & RefreshCode::Cancelled)
mUIHandle.reset(); mUIHandle.reset(), ClearTargets();
else else {
mpClickedTrack = pTrack; mpClickedTrack = pTrack;
ProcessUIHandleResult
(this, mRuler, pTrack.get(), pTrack.get(), refreshResult); // Perhaps the clicked handle wants to update cursor and state message
TrackPanelMouseState tpmState{ // after a click.
tpmEvent.event, TrackPanelMouseState tpmState{
tpmEvent.rect, tpmEvent.event,
tpmEvent.pCell tpmEvent.rect,
}; tpmEvent.pCell
HandleCursor( tpmState ); };
HandleMotion( tpmState );
}
ProcessUIHandleResult(
this, mRuler, pTrack.get(), pTrack.get(), refreshResult);
mMouseOverUpdateFlags |= refreshResult;
} }
} }
@@ -1574,6 +1636,9 @@ double TrackPanel::GetMostRecentXPos()
void TrackPanel::RefreshTrack(Track *trk, bool refreshbacking) void TrackPanel::RefreshTrack(Track *trk, bool refreshbacking)
{ {
if (!trk)
return;
Track *link = trk->GetLink(); Track *link = trk->GetLink();
if (link && !trk->GetLinked()) { if (link && !trk->GetLinked()) {

View File

@@ -18,6 +18,8 @@
#include "Experimental.h" #include "Experimental.h"
#include "HitTestResult.h"
#include "SelectedRegion.h" #include "SelectedRegion.h"
#include "widgets/OverlayPanel.h" #include "widgets/OverlayPanel.h"
@@ -373,8 +375,8 @@ protected:
}; };
FoundCell FindCell(int mouseX, int mouseY); FoundCell FindCell(int mouseX, int mouseY);
void HandleCursor( wxMouseState *pState ); void HandleMotion( wxMouseState *pState );
void HandleCursor( const TrackPanelMouseState &tpmState ); void HandleMotion( const TrackPanelMouseState &tpmState );
// If label, rectangle includes track control panel only. // If label, rectangle includes track control panel only.
// If !label, rectangle includes all of that, and the vertical ruler, and // If !label, rectangle includes all of that, and the vertical ruler, and
@@ -528,6 +530,31 @@ protected:
wxSize vrulerSize; wxSize vrulerSize;
protected: protected:
std::weak_ptr<TrackPanelCell> mLastCell;
HitTestResult mLastHitTest{};
bool mLastHitTestValid{};
unsigned mMouseOverUpdateFlags{};
public:
HitTestResult *Target()
{
if ( mLastHitTestValid )
return &mLastHitTest;
else
return nullptr;
}
protected:
void ClearTargets()
{
// Forget the rotation of hit test candidates when the mouse moves from
// cell to cell or outside of the TrackPanel entirely.
mLastCell.reset();
mLastHitTestValid = false;
mLastHitTest = {};
mMouseOverUpdateFlags = 0;
}
std::weak_ptr<Track> mpClickedTrack; std::weak_ptr<Track> mpClickedTrack;
UIHandlePtr mUIHandle; UIHandlePtr mUIHandle;

View File

@@ -60,7 +60,9 @@ public:
virtual Result Drag virtual Result Drag
(const TrackPanelMouseEvent &event, AudacityProject *pProject) = 0; (const TrackPanelMouseEvent &event, AudacityProject *pProject) = 0;
// Update the cursor and status message. // Can be called when the handle has been hit but not yet clicked,
// or called after Drag().
// Specifies cursor and status bar message.
virtual HitTestPreview Preview virtual HitTestPreview Preview
(const TrackPanelMouseState &state, const AudacityProject *pProject) = 0; (const TrackPanelMouseState &state, const AudacityProject *pProject) = 0;
@@ -99,11 +101,35 @@ public:
// to avoid dangling pointers to tracks. But maybe there will be a future // to avoid dangling pointers to tracks. But maybe there will be a future
// use? // use?
virtual void OnProjectChange(AudacityProject *pProject); virtual void OnProjectChange(AudacityProject *pProject);
public:
Result GetChangeHighlight() const { return mChangeHighlight; }
void SetChangeHighlight(Result val) { mChangeHighlight = val; }
// If AssignUIHandlePtr is used, then this function is also called before any
// overwrite.
// Make overloads of this for other subclasses, to cause refresh
// of the cell during mouse motion within it.
static UIHandle::Result NeedChangeHighlight
(const UIHandle &/*oldState*/, const UIHandle &/*newState*/)
{
return 0;
}
protected:
// Derived classes can set this nonzero in a constructor, which is enough
// to cause repaint of the cell whenever the pointer hits the target,
// or leaves it without clicking, or releases or escapes from a drag.
Result mChangeHighlight { 0 };
}; };
using UIHandlePtr = std::shared_ptr<UIHandle>; using UIHandlePtr = std::shared_ptr<UIHandle>;
// A frequent convenience // A frequent convenience for defining a hit test.
// Construct a NEW handle as if hit the first time; then either keep it, or
// use it to overwrite the state of a previously constructed handle that has not
// yet been released.
template<typename Subclass> template<typename Subclass>
std::shared_ptr<Subclass> AssignUIHandlePtr std::shared_ptr<Subclass> AssignUIHandlePtr
( std::weak_ptr<Subclass> &holder, const std::shared_ptr<Subclass> &pNew ) ( std::weak_ptr<Subclass> &holder, const std::shared_ptr<Subclass> &pNew )
@@ -118,7 +144,9 @@ std::shared_ptr<Subclass> AssignUIHandlePtr
return pNew; return pNew;
} }
else { else {
auto code = Subclass::NeedChangeHighlight( *ptr, *pNew );
*ptr = std::move(*pNew); *ptr = std::move(*pNew);
ptr->SetChangeHighlight( code );
return ptr; return ptr;
} }
} }

View File

@@ -24,23 +24,32 @@ Paul Licameli split from TrackPanel.cpp
#include <wx/translation.h> #include <wx/translation.h>
LabelGlyphHandle::LabelGlyphHandle LabelGlyphHandle::LabelGlyphHandle
(const std::shared_ptr<LabelTrack> &pLT, const wxRect &rect) (const std::shared_ptr<LabelTrack> &pLT,
const wxRect &rect, const LabelTrackHit &hit)
: mpLT{ pLT } : mpLT{ pLT }
, mRect{ rect } , mRect{ rect }
{} , mHit{ hit }
{
mChangeHighlight = RefreshCode::RefreshCell;
}
HitTestPreview LabelGlyphHandle::HitPreview UIHandle::Result LabelGlyphHandle::NeedChangeHighlight
(bool hitCenter, unsigned refreshResult) (const LabelGlyphHandle &oldState, const LabelGlyphHandle &newState)
{
if (oldState.mHit.mEdge != newState.mHit.mEdge)
// pointer moves between the circle and the chevron
return RefreshCode::RefreshCell;
return 0;
}
HitTestPreview LabelGlyphHandle::HitPreview(bool hitCenter)
{ {
static wxCursor arrowCursor{ wxCURSOR_ARROW }; static wxCursor arrowCursor{ wxCURSOR_ARROW };
return { return {
(hitCenter (hitCenter
? _("Drag one or more label boundaries.") ? _("Drag one or more label boundaries.")
: _("Drag label boundary.")), : _("Drag label boundary.")),
&arrowCursor, &arrowCursor
// Unusually, can have a non-zero third member of HitTestPreview, so that
// mouse-over highlights it.
refreshResult
}; };
} }
@@ -49,39 +58,22 @@ HitTestResult LabelGlyphHandle::HitTest
const wxMouseState &state, const wxMouseState &state,
const std::shared_ptr<LabelTrack> &pLT, const wxRect &rect) const std::shared_ptr<LabelTrack> &pLT, const wxRect &rect)
{ {
using namespace RefreshCode; LabelTrackHit hit{};
unsigned refreshResult = RefreshNone; pLT->OverGlyph(hit, state.m_x, state.m_y);
// Note: this has side effects on pLT!
int edge = pLT->OverGlyph(state.m_x, state.m_y);
//KLUDGE: We refresh the whole Label track when the icon hovered over
//changes colouration. Inefficient.
edge += pLT->mbHitCenter ? 4 : 0;
if (edge != pLT->mOldEdge)
{
pLT->mOldEdge = edge;
refreshResult |= RefreshCell;
}
// IF edge!=0 THEN we've set the cursor and we're done. // IF edge!=0 THEN we've set the cursor and we're done.
// signal this by setting the tip. // signal this by setting the tip.
if (edge != 0) if ( hit.mEdge & 3 )
{ {
auto result = std::make_shared<LabelGlyphHandle>( pLT, rect ); auto result = std::make_shared<LabelGlyphHandle>( pLT, rect, hit );
result = AssignUIHandlePtr(holder, result); result = AssignUIHandlePtr(holder, result);
return { return {
HitPreview(pLT->mbHitCenter, refreshResult), HitPreview( hit.mEdge & 4 ),
result result
}; };
} }
else {
// An empty result, except maybe, unusually, the refresh return {};
return {
{ wxString{}, nullptr, refreshResult },
{}
};
}
} }
LabelGlyphHandle::~LabelGlyphHandle() LabelGlyphHandle::~LabelGlyphHandle()
@@ -96,9 +88,10 @@ UIHandle::Result LabelGlyphHandle::Click
const wxMouseEvent &event = evt.event; const wxMouseEvent &event = evt.event;
ViewInfo &viewInfo = pProject->GetViewInfo(); ViewInfo &viewInfo = pProject->GetViewInfo();
mpLT->HandleGlyphClick(event, mRect, viewInfo, &viewInfo.selectedRegion); mpLT->HandleGlyphClick
(mHit, event, mRect, viewInfo, &viewInfo.selectedRegion);
if (! mpLT->IsAdjustingLabel() ) if (! mHit.mIsAdjustingLabel )
{ {
// The positive hit test should have ensured otherwise // The positive hit test should have ensured otherwise
//wxASSERT(false); //wxASSERT(false);
@@ -125,7 +118,8 @@ UIHandle::Result LabelGlyphHandle::Drag
const wxMouseEvent &event = evt.event; const wxMouseEvent &event = evt.event;
ViewInfo &viewInfo = pProject->GetViewInfo(); ViewInfo &viewInfo = pProject->GetViewInfo();
mpLT->HandleGlyphDragRelease(event, mRect, viewInfo, &viewInfo.selectedRegion); mpLT->HandleGlyphDragRelease
(mHit, event, mRect, viewInfo, &viewInfo.selectedRegion);
// Refresh all so that the change of selection is redrawn in all tracks // Refresh all so that the change of selection is redrawn in all tracks
return result | RefreshCode::RefreshAll | RefreshCode::DrawOverlays; return result | RefreshCode::RefreshAll | RefreshCode::DrawOverlays;
@@ -134,7 +128,7 @@ UIHandle::Result LabelGlyphHandle::Drag
HitTestPreview LabelGlyphHandle::Preview HitTestPreview LabelGlyphHandle::Preview
(const TrackPanelMouseState &, const AudacityProject *) (const TrackPanelMouseState &, const AudacityProject *)
{ {
return HitPreview(mpLT->mbHitCenter, 0); return HitPreview( mHit.mEdge & 4 );
} }
UIHandle::Result LabelGlyphHandle::Release UIHandle::Result LabelGlyphHandle::Release
@@ -142,11 +136,11 @@ UIHandle::Result LabelGlyphHandle::Release
wxWindow *pParent) wxWindow *pParent)
{ {
auto result = LabelDefaultClickHandle::Release( evt, pProject, pParent ); auto result = LabelDefaultClickHandle::Release( evt, pProject, pParent );
mpLT->mOldEdge = 0;
const wxMouseEvent &event = evt.event; const wxMouseEvent &event = evt.event;
ViewInfo &viewInfo = pProject->GetViewInfo(); ViewInfo &viewInfo = pProject->GetViewInfo();
if (mpLT->HandleGlyphDragRelease(event, mRect, viewInfo, &viewInfo.selectedRegion)) { if (mpLT->HandleGlyphDragRelease
(mHit, event, mRect, viewInfo, &viewInfo.selectedRegion)) {
pProject->PushState(_("Modified Label"), pProject->PushState(_("Modified Label"),
_("Label Edit"), _("Label Edit"),
UndoPush::CONSOLIDATE); UndoPush::CONSOLIDATE);
@@ -158,7 +152,6 @@ UIHandle::Result LabelGlyphHandle::Release
UIHandle::Result LabelGlyphHandle::Cancel(AudacityProject *pProject) UIHandle::Result LabelGlyphHandle::Cancel(AudacityProject *pProject)
{ {
mpLT->mOldEdge = 0;
pProject->RollbackState(); pProject->RollbackState();
auto result = LabelDefaultClickHandle::Cancel( pProject ); auto result = LabelDefaultClickHandle::Cancel( pProject );
return result | RefreshCode::RefreshAll; return result | RefreshCode::RefreshAll;

View File

@@ -19,14 +19,33 @@ class wxMouseState;
struct HitTestResult; struct HitTestResult;
class LabelTrack; class LabelTrack;
/// mEdge:
/// 0 if not over a glyph,
/// else a bitwise or of :
/// 1 if over the left-hand glyph,
/// 2 if over the right-hand glyph on a label,
/// 4 if over center.
///
/// mMouseLabelLeft - index of any left label hit
/// mMouseLabelRight - index of any right label hit
///
struct LabelTrackHit {
int mEdge{};
int mMouseOverLabelLeft{ -1 }; /// Keeps track of which left label the mouse is currently over.
int mMouseOverLabelRight{ -1 }; /// Keeps track of which right label the mouse is currently over.
bool mbIsMoving {};
bool mIsAdjustingLabel {};
};
class LabelGlyphHandle final : public LabelDefaultClickHandle class LabelGlyphHandle final : public LabelDefaultClickHandle
{ {
LabelGlyphHandle(const LabelGlyphHandle&) = delete; LabelGlyphHandle(const LabelGlyphHandle&) = delete;
static HitTestPreview HitPreview(bool hitCenter, unsigned refreshResult); static HitTestPreview HitPreview(bool hitCenter);
public: public:
explicit LabelGlyphHandle explicit LabelGlyphHandle
(const std::shared_ptr<LabelTrack> &pLT, const wxRect &rect); (const std::shared_ptr<LabelTrack> &pLT,
const wxRect &rect, const LabelTrackHit &hit);
LabelGlyphHandle &operator=(LabelGlyphHandle&&) = default; LabelGlyphHandle &operator=(LabelGlyphHandle&&) = default;
@@ -55,6 +74,11 @@ public:
bool StopsOnKeystroke() override { return true; } bool StopsOnKeystroke() override { return true; }
LabelTrackHit mHit{};
static UIHandle::Result NeedChangeHighlight
(const LabelGlyphHandle &oldState, const LabelGlyphHandle &newState);
private: private:
std::shared_ptr<LabelTrack> mpLT {}; std::shared_ptr<LabelTrack> mpLT {};
wxRect mRect {}; wxRect mRect {};

View File

@@ -31,13 +31,11 @@ HitTestResult LabelTrack::DetailedHitTest
// Try label movement handles first // Try label movement handles first
result = LabelGlyphHandle::HitTest( result = LabelGlyphHandle::HitTest(
mGlyphHandle, state, Pointer<LabelTrack>(this), st.rect); mGlyphHandle, state, Pointer<LabelTrack>(this), st.rect);
auto refresh = result.preview.refreshCode; // kludge
if ( !result.handle ) { if ( !result.handle ) {
// Missed glyph, try text box // Missed glyph, try text box
result = LabelTextHandle::HitTest( result = LabelTextHandle::HitTest(
mTextHandle, state, Pointer<LabelTrack>(this)); mTextHandle, state, Pointer<LabelTrack>(this));
result.preview.refreshCode |= refresh; // kludge
} }
return result; return result;

View File

@@ -46,9 +46,6 @@ HitTestResult Track::HitTest
// If there is no detailed hit for the subclass, there are still some // If there is no detailed hit for the subclass, there are still some
// general cases. // general cases.
// Label track kludge!
auto refresh = result.preview.refreshCode;
// Sliding applies in more than one track type. // Sliding applies in more than one track type.
if ( !result.handle && !isMultiTool && currentTool == slideTool ) if ( !result.handle && !isMultiTool && currentTool == slideTool )
result = TimeShiftHandle::HitAnywhere( result = TimeShiftHandle::HitAnywhere(
@@ -65,7 +62,6 @@ HitTestResult Track::HitTest
result = SelectHandle::HitTest( result = SelectHandle::HitTest(
mSelectHandle, st, pProject, Pointer(this)); mSelectHandle, st, pProject, Pointer(this));
result.preview.refreshCode |= refresh;
return result; return result;
} }