audacia/src/UIHandle.h
Paul Licameli f64bc5a829 Pass non-const AudacityProject to HitTest::Preview...
... because that will be needed for elimination of some GetActiveProject()
calls.  Because some overrides need to find the focused track, but that may
mutate the project by setting the focused track when it wasn't yet defined.
2020-01-04 09:40:33 -05:00

167 lines
6.0 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
UIHandle.h
Paul Licameli
**********************************************************************/
#ifndef __AUDACITY_UI_HANDLE__
#define __AUDACITY_UI_HANDLE__
#include <utility>
#include "MemoryX.h"
#include "TrackPanelDrawable.h" // to inherit
class wxDC;
class wxRect;
class wxRegion;
class wxWindow;
class AudacityProject;
struct HitTestPreview;
class TrackPanelCell;
struct TrackPanelMouseEvent;
struct TrackPanelMouseState;
#include "MemoryX.h"
/// \brief Short-lived drawing and event-handling object associated with a TrackPanelCell
// A TrackPanelCell reports a handle object of some subclass, in response to a
// hit test at a mouse position; then this handle processes certain events,
// and maintains necessary state through click-drag-release event sequences.
class UIHandle /* not final */ : public TrackPanelDrawable
{
public:
// See RefreshCode.h for bit flags:
using Result = unsigned;
// Future: may generalize away from current Track class
using Cell = TrackPanelCell;
virtual ~UIHandle() = 0;
// Before clicking, the handle is notified that it has been "hit"
// This might put the handle into its first rotated state
// (or last, if forward is false) or mark itself as needing a highlight.
// Default does nothing.
virtual void Enter(bool forward, AudacityProject *pProject);
// Tell whether the handle has more than one TAB key rotation state.
// Default is always false.
virtual bool HasRotation() const;
// If not previously in the last rotation state (or first if !forward),
// change rotation state and return true; else return false
// Default does nothing and returns false
virtual bool Rotate(bool forward);
// Tell whether the handle has its own escape action. In case it is already
// clicked, it will not cancel on Escape key if true.
// Default is always false.
virtual bool HasEscape() const;
// The handle may change state and mark itself for highlight change.
// Default does nothing and returns false
virtual bool Escape(AudacityProject *pProject);
// Assume hit test (implemented in other classes) was positive.
// May return Cancelled, overriding the hit test decision and stopping drag.
// Otherwise the framework will later call Release or Cancel after
// some number of Drag calls.
virtual Result Click
(const TrackPanelMouseEvent &event, AudacityProject *pProject) = 0;
// Assume previously Clicked and not yet Released or Cancelled.
// pCell may be other than for Click; may be NULL, and rect empty.
// Return value may include the Cancelled return flag,
// in which case the handle will not be invoked again.
virtual Result Drag
(const TrackPanelMouseEvent &event, AudacityProject *pProject) = 0;
// 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
(const TrackPanelMouseState &state, AudacityProject *pProject) = 0;
// Assume previously Clicked and not yet Released or Cancelled.
// event.pCell may be other than for Click; may be NULL, and rect empty.
// Can use pParent as parent to pop up a context menu,
// connecting and disconnecting event handlers for the menu items.
// Cancelled in return flags has no effect.
virtual Result Release
(const TrackPanelMouseEvent &event, AudacityProject *pProject,
wxWindow *pParent) = 0;
// Assume previously Clicked and not yet Released or Cancelled.
// Cancelled in return flags has no effect.
virtual Result Cancel(AudacityProject *pProject) = 0;
// Whether to force Release (not Cancel!) of the drag when a
// keystroke command is about to be dispatched. Default is always false.
// When default is false, any remembered pointers to tracks should be
// weak_ptrs.
virtual bool StopsOnKeystroke();
// Notification after a command is dispatched; generally, it will need to
// be overridden only in case StopsOnKeystroke() returns false. Default
// does nothing.
// PRL: all former uses of this are now accomplished with weak_ptr instead
// to avoid dangling pointers to tracks. But maybe there will be a future
// use?
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>;
// 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>
std::shared_ptr<Subclass> AssignUIHandlePtr
( std::weak_ptr<Subclass> &holder, const std::shared_ptr<Subclass> &pNew )
{
// Either assign to a null weak_ptr, or else rewrite what the weak_ptr
// points at. Thus a handle already pointed at changes its state but not its
// identity. This may matter for the framework that holds the strong
// pointers.
auto ptr = holder.lock();
if (!ptr) {
holder = pNew;
return pNew;
}
else {
auto code = Subclass::NeedChangeHighlight( *ptr, *pNew );
*ptr = std::move(*pNew);
ptr->SetChangeHighlight( code );
return ptr;
}
}
#endif