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

Move CellularPanel data members into a state structure out of the .h file

This commit is contained in:
Paul Licameli 2018-08-02 16:46:18 -04:00
parent f6609e5ffb
commit 4e1a80f5ac
2 changed files with 132 additions and 91 deletions

View File

@ -35,6 +35,23 @@
#include "HitTestResult.h" #include "HitTestResult.h"
#include "RefreshCode.h" #include "RefreshCode.h"
struct CellularPanel::State
{
UIHandlePtr mUIHandle;
std::weak_ptr<TrackPanelCell> mLastCell;
std::vector<UIHandlePtr> mTargets;
size_t mTarget {};
unsigned mMouseOverUpdateFlags{};
int mMouseMostRecentX;
int mMouseMostRecentY;
std::weak_ptr<TrackPanelCell> mpClickedCell;
bool mEnableTab{};
};
BEGIN_EVENT_TABLE(CellularPanel, OverlayPanel) BEGIN_EVENT_TABLE(CellularPanel, OverlayPanel)
EVT_MOUSE_EVENTS(CellularPanel::OnMouseEvent) EVT_MOUSE_EVENTS(CellularPanel::OnMouseEvent)
@ -48,9 +65,24 @@ BEGIN_EVENT_TABLE(CellularPanel, OverlayPanel)
EVT_CONTEXT_MENU(CellularPanel::OnContextMenu) EVT_CONTEXT_MENU(CellularPanel::OnContextMenu)
END_EVENT_TABLE() END_EVENT_TABLE()
CellularPanel::CellularPanel(
wxWindow * parent, wxWindowID id,
const wxPoint & pos, const wxSize & size,
ViewInfo *viewInfo,
long style)
: OverlayPanel(parent, id, pos, size, style)
, mViewInfo( viewInfo )
, mState{ std::make_unique<State>() }
{
}
CellularPanel::~CellularPanel() = default;
void CellularPanel::HandleInterruptedDrag() void CellularPanel::HandleInterruptedDrag()
{ {
if (mUIHandle && mUIHandle->StopsOnKeystroke() ) { auto &state = *mState;
if (state.mUIHandle &&
state.mUIHandle->StopsOnKeystroke() ) {
// The bogus id isn't used anywhere, but may help with debugging. // The bogus id isn't used anywhere, but may help with debugging.
// as this is sending a bogus mouse up. The mouse button is still actually down // as this is sending a bogus mouse up. The mouse button is still actually down
// and may go up again. // and may go up again.
@ -78,18 +110,19 @@ void CellularPanel::Uncapture(wxMouseState *pState)
bool CellularPanel::CancelDragging() bool CellularPanel::CancelDragging()
{ {
if (mUIHandle) { auto &state = *mState;
if (state.mUIHandle) {
// copy shared_ptr for safety, as in HandleClick // copy shared_ptr for safety, as in HandleClick
auto handle = mUIHandle; auto handle = state.mUIHandle;
// UIHANDLE CANCEL // UIHANDLE CANCEL
UIHandle::Result refreshResult = handle->Cancel(GetProject()); UIHandle::Result refreshResult = handle->Cancel(GetProject());
auto pClickedCell = mpClickedCell.lock(); auto pClickedCell = state.mpClickedCell.lock();
if (pClickedCell) if (pClickedCell)
ProcessUIHandleResult( ProcessUIHandleResult(
pClickedCell.get(), {}, pClickedCell.get(), {},
refreshResult | mMouseOverUpdateFlags ); refreshResult | state.mMouseOverUpdateFlags );
mpClickedCell.reset(); state.mpClickedCell.reset();
mUIHandle.reset(), handle.reset(), ClearTargets(); state.mUIHandle.reset(), handle.reset(), ClearTargets();
Uncapture(); Uncapture();
return true; return true;
} }
@ -109,7 +142,8 @@ bool CellularPanel::HandleEscapeKey(bool down)
} }
} }
if (mUIHandle) { auto &state = *mState;
if (state.mUIHandle) {
CancelDragging(); CancelDragging();
return true; return true;
} }
@ -183,7 +217,8 @@ void CellularPanel::HandleMotion( wxMouseState &inState, bool doHit )
void CellularPanel::HandleMotion void CellularPanel::HandleMotion
( const TrackPanelMouseState &tpmState, bool doHit ) ( const TrackPanelMouseState &tpmState, bool doHit )
{ {
auto handle = mUIHandle; auto &state = *mState;
auto handle = state.mUIHandle;
auto newCell = tpmState.pCell; auto newCell = tpmState.pCell;
@ -196,15 +231,15 @@ void CellularPanel::HandleMotion
handle = Target(); handle = Target();
// Assume cell does not change but target does // Assume cell does not change but target does
refreshCode = mMouseOverUpdateFlags; refreshCode = state.mMouseOverUpdateFlags;
mMouseOverUpdateFlags = 0; state.mMouseOverUpdateFlags = 0;
} }
else if ( !mUIHandle ) { else if ( !state.mUIHandle ) {
// Not yet dragging. // Not yet dragging.
auto oldCell = mLastCell.lock(); auto oldCell = state.mLastCell.lock();
unsigned updateFlags = mMouseOverUpdateFlags; unsigned updateFlags = state.mMouseOverUpdateFlags;
// First check whether crossing cell to cell // First check whether crossing cell to cell
if ( newCell == oldCell ) if ( newCell == oldCell )
@ -220,29 +255,29 @@ void CellularPanel::HandleMotion
} }
auto oldHandle = Target(); auto oldHandle = Target();
auto oldPosition = mTarget; auto oldPosition = state.mTarget;
// Now do the // Now do the
// UIHANDLE HIT TEST ! // UIHANDLE HIT TEST !
mTargets = newCell->HitTest(tpmState, GetProject()); state.mTargets = newCell->HitTest(tpmState, GetProject());
mTarget = 0; state.mTarget = 0;
// Find the old target's NEW place if we can // Find the old target's NEW place if we can
if (oldHandle) { if (oldHandle) {
auto begin = mTargets.begin(), end = mTargets.end(), auto begin = state.mTargets.begin(), end = state.mTargets.end(),
iter = std::find(begin, end, oldHandle); iter = std::find(begin, end, oldHandle);
if (iter != end) { if (iter != end) {
size_t newPosition = iter - begin; size_t newPosition = iter - begin;
if (newPosition <= oldPosition) if (newPosition <= oldPosition)
mTarget = newPosition; state.mTarget = newPosition;
// else, some NEW hit and this position takes priority // else, some NEW hit and this position takes priority
} }
} }
handle = Target(); handle = Target();
mLastCell = newCell; state.mLastCell = newCell;
if (!oldCell && oldHandle != handle) if (!oldCell && oldHandle != handle)
// Did not move cell to cell, but did change the target // Did not move cell to cell, but did change the target
@ -262,7 +297,7 @@ void CellularPanel::HandleMotion
auto code = handle->GetChangeHighlight(); auto code = handle->GetChangeHighlight();
handle->SetChangeHighlight(RefreshCode::RefreshNone); handle->SetChangeHighlight(RefreshCode::RefreshNone);
refreshCode |= code; refreshCode |= code;
mMouseOverUpdateFlags |= code; state.mMouseOverUpdateFlags |= code;
} }
if (newCell && if (newCell &&
(!pCursor || status.empty() || tooltip.empty())) { (!pCursor || status.empty() || tooltip.empty())) {
@ -301,8 +336,9 @@ void CellularPanel::HandleMotion
bool CellularPanel::HasRotation() bool CellularPanel::HasRotation()
{ {
auto &state = *mState;
// Is there a nontrivial TAB key rotation? // Is there a nontrivial TAB key rotation?
if ( mTargets.size() > 1 ) if ( state.mTargets.size() > 1 )
return true; return true;
auto target = Target(); auto target = Target();
return target && target->HasRotation(); return target && target->HasRotation();
@ -313,17 +349,19 @@ bool CellularPanel::HasEscape()
if (IsMouseCaptured()) if (IsMouseCaptured())
return true; return true;
if (mTarget + 1 == mTargets.size() && auto &state = *mState;
if (state.mTarget + 1 == state.mTargets.size() &&
Target() && Target() &&
!Target()->HasEscape()) !Target()->HasEscape())
return false; return false;
return mTargets.size() > 0; return state.mTargets.size() > 0;
} }
bool CellularPanel::ChangeTarget(bool forward, bool cycle) bool CellularPanel::ChangeTarget(bool forward, bool cycle)
{ {
auto size = mTargets.size(); auto &state = *mState;
auto size = state.mTargets.size();
auto target = Target(); auto target = Target();
if (target && target->HasRotation()) { if (target && target->HasRotation()) {
@ -337,16 +375,16 @@ bool CellularPanel::ChangeTarget(bool forward, bool cycle)
} }
if (!cycle && if (!cycle &&
((forward && mTarget + 1 == size) || ((forward && state.mTarget + 1 == size) ||
(!forward && mTarget == 0))) (!forward && state.mTarget == 0)))
return false; return false;
if (size > 1) { if (size > 1) {
if (forward) if (forward)
++mTarget; ++state.mTarget;
else else
mTarget += size - 1; state.mTarget += size - 1;
mTarget %= size; state.mTarget %= size;
if (Target()) if (Target())
Target()->Enter(forward); Target()->Enter(forward);
return true; return true;
@ -358,7 +396,8 @@ bool CellularPanel::ChangeTarget(bool forward, bool cycle)
/// Determines if a modal tool is active /// Determines if a modal tool is active
bool CellularPanel::IsMouseCaptured() bool CellularPanel::IsMouseCaptured()
{ {
return mUIHandle != NULL; auto &state = *mState;
return state.mUIHandle != NULL;
} }
void CellularPanel::OnContextMenu(wxContextMenuEvent & WXUNUSED(event)) void CellularPanel::OnContextMenu(wxContextMenuEvent & WXUNUSED(event))
@ -415,7 +454,8 @@ void CellularPanel::HandleWheelRotation( TrackPanelMouseEvent &tpmEvent )
void CellularPanel::OnCaptureKey(wxCommandEvent & event) void CellularPanel::OnCaptureKey(wxCommandEvent & event)
{ {
mEnableTab = false; auto &state = *mState;
state.mEnableTab = false;
wxKeyEvent *kevent = static_cast<wxKeyEvent *>(event.GetEventObject()); wxKeyEvent *kevent = static_cast<wxKeyEvent *>(event.GetEventObject());
const auto code = kevent->GetKeyCode(); const auto code = kevent->GetKeyCode();
if ( WXK_ESCAPE != code ) if ( WXK_ESCAPE != code )
@ -554,8 +594,9 @@ void CellularPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(event))
wxMouseEvent e(wxEVT_LEFT_UP); wxMouseEvent e(wxEVT_LEFT_UP);
e.SetId( kCaptureLostEventId ); e.SetId( kCaptureLostEventId );
e.m_x = mMouseMostRecentX; auto &state = *mState;
e.m_y = mMouseMostRecentY; e.m_x = state.mMouseMostRecentX;
e.m_y = state.mMouseMostRecentY;
OnMouseEvent(e); OnMouseEvent(e);
} }
@ -602,8 +643,9 @@ try
event.ResumePropagation(wxEVENT_PROPAGATE_MAX); event.ResumePropagation(wxEVENT_PROPAGATE_MAX);
} }
mMouseMostRecentX = event.m_x; auto &state = *mState;
mMouseMostRecentY = event.m_y; state.mMouseMostRecentX = event.m_x;
state.mMouseMostRecentY = event.m_y;
if (event.LeftDown()) { if (event.LeftDown()) {
// The activate event is used to make the // The activate event is used to make the
@ -618,7 +660,7 @@ try
if (event.Leaving()) if (event.Leaving())
{ {
if ( !mUIHandle ) if ( !state.mUIHandle )
ClearTargets(); ClearTargets();
auto buttons = auto buttons =
@ -640,21 +682,21 @@ try
} }
} }
if (mUIHandle) { if (state.mUIHandle) {
auto pClickedCell = mpClickedCell.lock(); auto pClickedCell = state.mpClickedCell.lock();
if (event.Dragging()) { if (event.Dragging()) {
// UIHANDLE DRAG // UIHANDLE DRAG
// copy shared_ptr for safety, as in HandleClick // copy shared_ptr for safety, as in HandleClick
auto handle = mUIHandle; auto handle = state.mUIHandle;
const UIHandle::Result refreshResult = const UIHandle::Result refreshResult =
handle->Drag( tpmEvent, GetProject() ); handle->Drag( tpmEvent, GetProject() );
ProcessUIHandleResult ProcessUIHandleResult
(pClickedCell.get(), pCell.get(), refreshResult); (pClickedCell.get(), pCell.get(), refreshResult);
mMouseOverUpdateFlags |= refreshResult; state.mMouseOverUpdateFlags |= refreshResult;
if (refreshResult & RefreshCode::Cancelled) { if (refreshResult & RefreshCode::Cancelled) {
// Drag decided to abort itself // Drag decided to abort itself
mUIHandle.reset(), handle.reset(), ClearTargets(); state.mUIHandle.reset(), handle.reset(), ClearTargets();
mpClickedCell.reset(); state.mpClickedCell.reset();
Uncapture( &event ); Uncapture( &event );
} }
else { else {
@ -665,14 +707,14 @@ try
} }
else if (event.ButtonUp()) { else if (event.ButtonUp()) {
// UIHANDLE RELEASE // UIHANDLE RELEASE
unsigned moreFlags = mMouseOverUpdateFlags; unsigned moreFlags = state.mMouseOverUpdateFlags;
UIHandle::Result refreshResult = UIHandle::Result refreshResult =
mUIHandle->Release( tpmEvent, GetProject(), this ); state.mUIHandle->Release( tpmEvent, GetProject(), this );
ProcessUIHandleResult ProcessUIHandleResult
(pClickedCell.get(), pCell.get(), (pClickedCell.get(), pCell.get(),
refreshResult | moreFlags); refreshResult | moreFlags);
mUIHandle.reset(), ClearTargets(); state.mUIHandle.reset(), ClearTargets();
mpClickedCell.reset(); state.mpClickedCell.reset();
// will also Uncapture() below // will also Uncapture() below
} }
} }
@ -720,19 +762,20 @@ void CellularPanel::HandleClick( const TrackPanelMouseEvent &tpmEvent )
HandleMotion( tpmState ); HandleMotion( tpmState );
} }
mUIHandle = Target(); auto &state = *mState;
state.mUIHandle = Target();
if (mUIHandle) { if (state.mUIHandle) {
// UIHANDLE CLICK // UIHANDLE CLICK
// Make another shared pointer to the handle, in case recursive // Make another shared pointer to the handle, in case recursive
// event dispatching otherwise tries to delete the handle. // event dispatching otherwise tries to delete the handle.
auto handle = mUIHandle; auto handle = state.mUIHandle;
UIHandle::Result refreshResult = UIHandle::Result refreshResult =
handle->Click( tpmEvent, GetProject() ); handle->Click( tpmEvent, GetProject() );
if (refreshResult & RefreshCode::Cancelled) if (refreshResult & RefreshCode::Cancelled)
mUIHandle.reset(), handle.reset(), ClearTargets(); state.mUIHandle.reset(), handle.reset(), ClearTargets();
else { else {
mpClickedCell = pCell; state.mpClickedCell = pCell;
// Perhaps the clicked handle wants to update cursor and state message // Perhaps the clicked handle wants to update cursor and state message
// after a click. // after a click.
@ -745,7 +788,7 @@ void CellularPanel::HandleClick( const TrackPanelMouseEvent &tpmEvent )
} }
ProcessUIHandleResult( ProcessUIHandleResult(
pCell.get(), pCell.get(), refreshResult); pCell.get(), pCell.get(), refreshResult);
mMouseOverUpdateFlags |= refreshResult; state.mMouseOverUpdateFlags |= refreshResult;
} }
} }
@ -782,3 +825,30 @@ void CellularPanel::OnKillFocus(wxFocusEvent & WXUNUSED(event))
} }
Refresh( false); Refresh( false);
} }
UIHandlePtr CellularPanel::Target()
{
auto &state = *mState;
if (state.mTargets.size())
return state.mTargets[state.mTarget];
else
return {};
}
wxCoord CellularPanel::MostRecentXCoord() const
{
auto &state = *mState;
return state.mMouseMostRecentX;
}
void CellularPanel::ClearTargets()
{
auto &state = *mState;
// Forget the rotation of hit test candidates when the mouse moves from
// cell to cell or outside of the panel entirely.
state.mLastCell.reset();
state.mTargets.clear();
state.mTarget = 0;
state.mMouseOverUpdateFlags = 0;
}

View File

@ -35,10 +35,8 @@ public:
const wxSize & size, const wxSize & size,
ViewInfo *viewInfo, ViewInfo *viewInfo,
// default as for wxPanel: // default as for wxPanel:
long style = wxTAB_TRAVERSAL | wxNO_BORDER) long style = wxTAB_TRAVERSAL | wxNO_BORDER);
: OverlayPanel(parent, id, pos, size, style) ~CellularPanel() override;
, mViewInfo( viewInfo )
{}
// Overridables: // Overridables:
@ -61,17 +59,11 @@ public:
virtual void UpdateStatusMessage( const wxString & ) = 0; virtual void UpdateStatusMessage( const wxString & ) = 0;
public: public:
UIHandlePtr Target() UIHandlePtr Target();
{
if (mTargets.size())
return mTargets[mTarget];
else
return {};
}
bool IsMouseCaptured(); bool IsMouseCaptured();
wxCoord MostRecentXCoord() const { return mMouseMostRecentX; } wxCoord MostRecentXCoord() const;
void HandleCursorForPresentMouseState(bool doHit = true); void HandleCursorForPresentMouseState(bool doHit = true);
@ -79,15 +71,7 @@ protected:
bool HasEscape(); bool HasEscape();
bool CancelDragging(); bool CancelDragging();
void DoContextMenu( TrackPanelCell *pCell = nullptr ); void DoContextMenu( TrackPanelCell *pCell = nullptr );
void ClearTargets() void ClearTargets();
{
// Forget the rotation of hit test candidates when the mouse moves from
// cell to cell or outside of the panel entirely.
mLastCell.reset();
mTargets.clear();
mTarget = 0;
mMouseOverUpdateFlags = 0;
}
private: private:
bool HasRotation(); bool HasRotation();
@ -121,26 +105,13 @@ private:
protected: protected:
ViewInfo *mViewInfo; ViewInfo *mViewInfo;
private:
UIHandlePtr mUIHandle;
std::weak_ptr<TrackPanelCell> mLastCell;
std::vector<UIHandlePtr> mTargets;
size_t mTarget {};
unsigned mMouseOverUpdateFlags{};
protected:
// To do: make a drawing method and make this private // To do: make a drawing method and make this private
wxMouseState mLastMouseState; wxMouseState mLastMouseState;
private: private:
int mMouseMostRecentX; struct State;
int mMouseMostRecentY; std::unique_ptr<State> mState;
std::weak_ptr<TrackPanelCell> mpClickedCell;
bool mEnableTab{};
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()
}; };