Define visitation procedure skeleton for subdivision of CellularPanel

This commit is contained in:
Paul Licameli 2018-11-01 11:13:13 -04:00
parent 4fe97acbc9
commit 588c050bbb
2 changed files with 144 additions and 0 deletions

View File

@ -860,6 +860,122 @@ void CellularPanel::OnKillFocus(wxFocusEvent & WXUNUSED(event))
Refresh( false);
}
// Empty out-of-line default functions to fill Visitor's vtable
CellularPanel::Visitor::~Visitor() {}
void CellularPanel::Visitor::VisitCell(
const wxRect &rect, TrackPanelCell &cell ) {}
void CellularPanel::Visitor::BeginGroup(
const wxRect &rect, TrackPanelGroup &group ) {}
void CellularPanel::Visitor::EndGroup(
const wxRect &rect, TrackPanelGroup &group ) {}
// Public, top-level entry for generalized Visit
void CellularPanel::Visit( Visitor &visitor )
{
Visit( GetClientRect(), Root(), visitor );
}
// Common utility class for the functions that follow
namespace {
struct Adaptor : CellularPanel::Visitor {
using SimpleCellVisitor = CellularPanel::SimpleCellVisitor;
using SimpleNodeVisitor = CellularPanel::SimpleNodeVisitor;
// Visit cells only
Adaptor( const SimpleCellVisitor& function_ )
: function{ [&](const wxRect &rect, TrackPanelNode &cell) {
return function_( rect, static_cast<TrackPanelCell&>(cell) );
} }
{}
// Visit cells and groups, each once only, choosing pre- or post- ordering
// for the groups
Adaptor( const SimpleNodeVisitor &function_, bool pre_ )
: function{ function_ }, pre{ pre_ }, post{ ! pre_ } {}
void VisitCell( const wxRect &rect, TrackPanelCell &cell ) override
{ return function( rect, cell ); }
void BeginGroup( const wxRect &rect, TrackPanelGroup &group ) override
{ if (pre) return function( rect, group ); }
void EndGroup( const wxRect &rect, TrackPanelGroup &group ) override
{ if (post) return function( rect, group ); }
SimpleNodeVisitor function;
const bool pre{ false }, post{ false };
};
}
// Simplified entry points for visits that don't need all the generality of
// CellularPanel::Visitor
void CellularPanel::VisitCells( const SimpleCellVisitor &visitor )
{
Adaptor adaptor{ visitor };
Visit( adaptor );
}
void CellularPanel::VisitPreorder( const SimpleNodeVisitor &visitor )
{
Adaptor adaptor{ visitor, true };
Visit( adaptor );
}
void CellularPanel::VisitPostorder( const SimpleNodeVisitor &visitor )
{
Adaptor adaptor{ visitor, false };
Visit( adaptor );
}
namespace {
wxRect Subdivide(
const wxRect &rect, bool divideX,
const TrackPanelGroup::Refinement &children,
const TrackPanelGroup::Refinement::const_iterator iter)
{
const auto lowerBound = (divideX ? rect.GetLeft() : rect.GetTop());
const auto upperBound = (divideX ? rect.GetRight() : rect.GetBottom());
const auto next = iter + 1;
const auto end = children.end();
const auto nextCoord = ((next == end) ? upperBound : next->first - 1);
// Some defense against bad overrides of TrackPanelGroup::Children
auto lesser = std::max(lowerBound, std::min(upperBound, iter->first));
auto greater = std::max(lesser, std::min(upperBound, nextCoord));
auto result = rect;
if (divideX)
result.SetLeft(lesser), result.SetRight(greater);
else
result.SetTop(lesser), result.SetBottom(greater);
return result;
};
}
// Private, recursive implementation function of Visit
void CellularPanel::Visit(
const wxRect &rect, const std::shared_ptr<TrackPanelNode> &node,
Visitor &visitor )
{
if (auto pCell = dynamic_cast<TrackPanelCell*>(node.get()))
visitor.VisitCell( rect, *pCell );
else if (auto pGroup = dynamic_cast<TrackPanelGroup*>(node.get())) {
visitor.BeginGroup( rect, *pGroup );
// Recur on children
const auto results = pGroup->Children( rect );
const bool divideX = results.first == TrackPanelGroup::Axis::X;
const auto &children = results.second;
const auto begin = children.begin(), end = children.end();
for (auto iter = begin; iter != end; ++iter)
Visit(
Subdivide(rect, divideX, children, iter), iter->second, visitor );
visitor.EndGroup( rect, *pGroup );
}
else
return;
}
UIHandlePtr CellularPanel::Target()
{
auto &state = *mState;

View File

@ -18,6 +18,7 @@ class ViewInfo;
class AudacityProject;
class TrackPanelCell;
class TrackPanelGroup;
class TrackPanelNode;
struct TrackPanelMouseEvent;
struct TrackPanelMouseState;
@ -55,6 +56,29 @@ public:
virtual FoundCell FindCell(int mouseX, int mouseY) = 0;
virtual wxRect FindRect(const TrackPanelCell &cell) = 0;
// Structure and functions for generalized visitation of the subdivision
struct Visitor {
virtual ~Visitor();
virtual void VisitCell( const wxRect &rect, TrackPanelCell &cell );
virtual void BeginGroup( const wxRect &rect, TrackPanelGroup &group );
virtual void EndGroup( const wxRect &rect, TrackPanelGroup &group );
};
// Most general visit
void Visit( Visitor &visitor );
// Easier visit when you care only about cells
using SimpleCellVisitor =
std::function< void( const wxRect &rect, TrackPanelCell &cell ) >;
void VisitCells( const SimpleCellVisitor &visitor );
// Easier visits when you want to visit each node once only
using SimpleNodeVisitor =
std::function< void( const wxRect &rect, TrackPanelNode &node ) >;
void VisitPreorder( const SimpleNodeVisitor &visitor );
void VisitPostorder( const SimpleNodeVisitor &visitor );
virtual TrackPanelCell *GetFocusedCell() = 0;
virtual void SetFocusedCell() = 0;
@ -86,6 +110,10 @@ protected:
void ClearTargets();
private:
void Visit(
const wxRect &rect, const std::shared_ptr<TrackPanelNode> &node,
Visitor &visitor );
bool HasRotation();
bool ChangeTarget(bool forward, bool cycle);