2015-07-07 03:12:16 +00:00
|
|
|
/**********************************************************************
|
|
|
|
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
|
|
|
|
UIHandle.h
|
|
|
|
|
|
|
|
Paul Licameli
|
|
|
|
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
|
|
#ifndef __AUDACITY_UI_HANDLE__
|
|
|
|
#define __AUDACITY_UI_HANDLE__
|
|
|
|
|
|
|
|
#include <utility>
|
2021-05-21 18:13:13 +00:00
|
|
|
#include <memory>
|
2018-11-03 04:04:50 +00:00
|
|
|
#include "TrackPanelDrawable.h" // to inherit
|
2015-07-07 03:12:16 +00:00
|
|
|
|
|
|
|
class wxDC;
|
|
|
|
class wxRect;
|
|
|
|
class wxRegion;
|
|
|
|
class wxWindow;
|
|
|
|
|
|
|
|
class AudacityProject;
|
|
|
|
struct HitTestPreview;
|
|
|
|
class TrackPanelCell;
|
|
|
|
struct TrackPanelMouseEvent;
|
2017-06-29 03:21:20 +00:00
|
|
|
struct TrackPanelMouseState;
|
2015-07-07 03:12:16 +00:00
|
|
|
|
2018-11-03 04:04:50 +00:00
|
|
|
/// \brief Short-lived drawing and event-handling object associated with a TrackPanelCell
|
2015-07-07 03:12:16 +00:00
|
|
|
// 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.
|
2020-09-28 12:50:18 +00:00
|
|
|
class AUDACITY_DLL_API UIHandle /* not final */ : public TrackPanelDrawable
|
2015-07-07 03:12:16 +00:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
// See RefreshCode.h for bit flags:
|
|
|
|
using Result = unsigned;
|
|
|
|
|
|
|
|
// Future: may generalize away from current Track class
|
|
|
|
using Cell = TrackPanelCell;
|
|
|
|
|
|
|
|
virtual ~UIHandle() = 0;
|
|
|
|
|
2017-07-12 04:28:49 +00:00
|
|
|
// Before clicking, the handle is notified that it has been "hit"
|
2017-07-12 17:56:16 +00:00
|
|
|
// This might put the handle into its first rotated state
|
|
|
|
// (or last, if forward is false) or mark itself as needing a highlight.
|
2017-07-12 04:28:49 +00:00
|
|
|
// Default does nothing.
|
2019-05-21 17:36:29 +00:00
|
|
|
virtual void Enter(bool forward, AudacityProject *pProject);
|
2017-07-12 17:56:16 +00:00
|
|
|
|
|
|
|
// 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);
|
2017-07-12 04:28:49 +00:00
|
|
|
|
2017-07-16 00:13:18 +00:00
|
|
|
// 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
|
2019-05-21 17:36:29 +00:00
|
|
|
virtual bool Escape(AudacityProject *pProject);
|
2017-07-16 00:13:18 +00:00
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
// 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;
|
|
|
|
|
2017-06-18 01:37:41 +00:00
|
|
|
// Can be called when the handle has been hit but not yet clicked,
|
|
|
|
// or called after Drag().
|
|
|
|
// Specifies cursor and status bar message.
|
2015-07-07 03:12:16 +00:00
|
|
|
virtual HitTestPreview Preview
|
2020-01-04 14:40:33 +00:00
|
|
|
(const TrackPanelMouseState &state, AudacityProject *pProject) = 0;
|
2015-07-07 03:12:16 +00:00
|
|
|
|
|
|
|
// 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.
|
2017-06-24 21:04:07 +00:00
|
|
|
// When default is false, any remembered pointers to tracks should be
|
|
|
|
// weak_ptrs.
|
2015-07-07 03:12:16 +00:00
|
|
|
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.
|
2017-06-24 21:04:07 +00:00
|
|
|
// 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?
|
2015-07-07 03:12:16 +00:00
|
|
|
virtual void OnProjectChange(AudacityProject *pProject);
|
2017-06-18 01:37:41 +00:00
|
|
|
|
|
|
|
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 };
|
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
};
|
|
|
|
|
Changed lifetime management of UIHandle objects, no singletons...
... Rather, construct them during hit tests (also capturing more state sooner
rather than at Click time, and adding some accessors for later use)
This also fixes bug 1677 by other means and avoids similar problems.
A cell may be implemented to re-use a previously hit handle object, not yet
clicked, in a later hit test, by remembering a weak pointer, but TrackPanel
holds the strong pointers that determine when the object is destroyed.
And the objects will surely be destroyed after drag-release, or ESC key.
For now they are also destroyed whenever not dragging, and hit-testing is
re-invoked; that will be changed later, so that the re-use mentioned above
becomes effective, but still they will be destroyed when the pointer moves
from one cell to another.
2017-07-05 20:45:55 +00:00
|
|
|
using UIHandlePtr = std::shared_ptr<UIHandle>;
|
|
|
|
|
2017-06-18 01:37:41 +00:00
|
|
|
// 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.
|
Changed lifetime management of UIHandle objects, no singletons...
... Rather, construct them during hit tests (also capturing more state sooner
rather than at Click time, and adding some accessors for later use)
This also fixes bug 1677 by other means and avoids similar problems.
A cell may be implemented to re-use a previously hit handle object, not yet
clicked, in a later hit test, by remembering a weak pointer, but TrackPanel
holds the strong pointers that determine when the object is destroyed.
And the objects will surely be destroyed after drag-release, or ESC key.
For now they are also destroyed whenever not dragging, and hit-testing is
re-invoked; that will be changed later, so that the re-use mentioned above
becomes effective, but still they will be destroyed when the pointer moves
from one cell to another.
2017-07-05 20:45:55 +00:00
|
|
|
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 {
|
2017-06-18 01:37:41 +00:00
|
|
|
auto code = Subclass::NeedChangeHighlight( *ptr, *pNew );
|
Changed lifetime management of UIHandle objects, no singletons...
... Rather, construct them during hit tests (also capturing more state sooner
rather than at Click time, and adding some accessors for later use)
This also fixes bug 1677 by other means and avoids similar problems.
A cell may be implemented to re-use a previously hit handle object, not yet
clicked, in a later hit test, by remembering a weak pointer, but TrackPanel
holds the strong pointers that determine when the object is destroyed.
And the objects will surely be destroyed after drag-release, or ESC key.
For now they are also destroyed whenever not dragging, and hit-testing is
re-invoked; that will be changed later, so that the re-use mentioned above
becomes effective, but still they will be destroyed when the pointer moves
from one cell to another.
2017-07-05 20:45:55 +00:00
|
|
|
*ptr = std::move(*pNew);
|
2017-06-18 01:37:41 +00:00
|
|
|
ptr->SetChangeHighlight( code );
|
Changed lifetime management of UIHandle objects, no singletons...
... Rather, construct them during hit tests (also capturing more state sooner
rather than at Click time, and adding some accessors for later use)
This also fixes bug 1677 by other means and avoids similar problems.
A cell may be implemented to re-use a previously hit handle object, not yet
clicked, in a later hit test, by remembering a weak pointer, but TrackPanel
holds the strong pointers that determine when the object is destroyed.
And the objects will surely be destroyed after drag-release, or ESC key.
For now they are also destroyed whenever not dragging, and hit-testing is
re-invoked; that will be changed later, so that the re-use mentioned above
becomes effective, but still they will be destroyed when the pointer moves
from one cell to another.
2017-07-05 20:45:55 +00:00
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
#endif
|