The solution is to provide a standard implementation in the form of a mix-in, which directly houses a `ZoomWindow` instance. Moreover, the latter is deemed a prominent use case for the time::Control, allowing other components to attach and push changes of the zoom state or register as listeners to react to state changes. Actually, the `TimelineLayout`, which hosts all the actual visible widgets forming the timeline-UI, now integrates this mix-in; and since `TimelineLayout` is passed to `TimelineController` and used there as reference-`CanvasHook` for the root track, this implementation of the `DisplayMetric` interface will ''effectively be used by all widgets'' attached to the timeline canvas.
206 lines
6.3 KiB
C++
206 lines
6.3 KiB
C++
/*
|
|
CONTROL-IMPL.hpp - time::control implementation building blocks
|
|
|
|
Copyright (C) Lumiera.org
|
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of
|
|
the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
|
/** @file control-impl.hpp
|
|
** Implementation building blocks for time modification and propagation.
|
|
** The time::Control element allows to impose modifications to a connected
|
|
** time value entity and at the same time publish the changes to registered
|
|
** listeners. Due to the various flavours of actual time value entities, this
|
|
** is a complex undertaking, which is implemented here based on policies and
|
|
** template metaprogramming. This header/include defines two building blocks:
|
|
** - the actual Mutator to apply the changes to the target entity
|
|
** - a Propagator to register listeners and forward the changes.
|
|
**
|
|
** # implementation technique
|
|
**
|
|
** The Mutator uses functor objects to encapsulate the actual modification
|
|
** operations. When attaching to a target time entity to be manipulated, these
|
|
** functor objects will be configured by binding them to the appropriate
|
|
** implementation function. And picking this actual implementation is done
|
|
** through a time::mutation::Policy element, using the concrete time entity
|
|
** types as template parameter. Thus, the actual implementation to be used
|
|
** is determined by the compiler, through the template specialisations
|
|
** contained in control-policy.hpp
|
|
**
|
|
** @note the header control-policy.hpp with the template specialisations
|
|
** is included way down, after the class definitions. This is done
|
|
** so for sake of readability
|
|
**
|
|
** @see TimeControl_test
|
|
**
|
|
*/
|
|
|
|
#ifndef LIB_TIME_CONTROL_IMPL_H
|
|
#define LIB_TIME_CONTROL_IMPL_H
|
|
|
|
#include "lib/error.hpp"
|
|
#include "lib/time/mutation.hpp"
|
|
#include "lib/time/timevalue.hpp"
|
|
|
|
#include <functional>
|
|
#include <vector>
|
|
|
|
|
|
namespace lib {
|
|
namespace time {
|
|
namespace mutation {
|
|
|
|
using std::function;
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Implementation building block: impose changes to a Time element.
|
|
* The purpose of the Mutator is to attach a target time entity,
|
|
* which then will be subject to any received value changes,
|
|
* offsets and grid nudging. The actual attachment is to be
|
|
* performed in a subclass, by using the Mutation interface.
|
|
* When attaching to a target, the Mutator will be outfitted
|
|
* with a set of suitable functors, incorporating the specific
|
|
* behaviour for the concrete combination of input changes
|
|
* ("source values") and target object type. This works by
|
|
* binding to the appropriate implementation functionality,
|
|
* guided by a templated policy class. After installing
|
|
* these functors, these decisions remains opaque and
|
|
* encapsulated within the functor objects, so the
|
|
* mutator object doesn't need to carry this
|
|
* type information on the interface
|
|
*/
|
|
template<class TI>
|
|
class Mutator
|
|
: public Mutation
|
|
{
|
|
typedef function<TI(TI const&)> ValueSetter;
|
|
typedef function<TI(Offset const&)> Ofsetter;
|
|
typedef function<TI(int)> Nudger;
|
|
|
|
protected:
|
|
mutable ValueSetter setVal_;
|
|
mutable Ofsetter offset_;
|
|
mutable Nudger nudge_;
|
|
|
|
void
|
|
ensure_isArmed() const
|
|
{
|
|
if (!setVal_)
|
|
throw error::State("feeding time/value change "
|
|
"while not (yet) connected to any target to change"
|
|
,error::LUMIERA_ERROR_UNCONNECTED);
|
|
}
|
|
|
|
|
|
template<class TAR>
|
|
void bind_to (TAR& target) const;
|
|
|
|
void unbind();
|
|
|
|
// using default construction and copy
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* Implementation building block: propagate changes to listeners.
|
|
* The Propagator manages a set of callback signals, allowing to
|
|
* propagate notifications for changed Time values.
|
|
*
|
|
* There are no specific requirements on the acceptable listeners,
|
|
* besides exposing a function-call operator to feed the changed
|
|
* time value to. Both Mutator and Propagator employ one primary
|
|
* template parameter, which is the type of the time values
|
|
* to be fed in and propagated.
|
|
*/
|
|
template<class TI>
|
|
class Propagator
|
|
{
|
|
typedef function<void(TI const&)> ChangeSignal;
|
|
typedef std::vector<ChangeSignal> ListenerList;
|
|
|
|
ListenerList listeners_;
|
|
|
|
public:
|
|
/** install notification receiver */
|
|
template<class SIG>
|
|
void
|
|
attach (SIG const& toNotify)
|
|
{
|
|
ChangeSignal newListener (std::ref(toNotify));
|
|
listeners_.push_back (newListener);
|
|
}
|
|
|
|
/** disconnect any observers */
|
|
void
|
|
disconnect()
|
|
{
|
|
listeners_.clear();
|
|
}
|
|
|
|
/** publish a change */
|
|
TI
|
|
operator() (TI const& changedVal) const
|
|
{
|
|
typedef typename ListenerList::const_iterator Iter;
|
|
Iter p = listeners_.begin();
|
|
Iter e = listeners_.end();
|
|
|
|
for ( ; p!=e; ++p )
|
|
(*p) (changedVal);
|
|
return changedVal;
|
|
}
|
|
|
|
// using default construction and copy
|
|
};
|
|
|
|
}}} // lib::time::mutation
|
|
|
|
|
|
/* ===== Definition of actual operations ===== */
|
|
#include "lib/time/control-policy.hpp"
|
|
|
|
|
|
|
|
|
|
template<class TI>
|
|
template<class TAR>
|
|
void
|
|
lib::time::mutation::Mutator<TI>::bind_to (TAR& target) const
|
|
{
|
|
using lib::time::mutation::Policy;
|
|
|
|
setVal_ = Policy<TI,TI, TAR>::buildChangeHandler (target);
|
|
offset_ = Policy<TI,Offset,TAR>::buildChangeHandler (target);
|
|
nudge_ = Policy<TI,int, TAR>::buildChangeHandler (target);
|
|
}
|
|
|
|
template<class TI>
|
|
void
|
|
lib::time::mutation::Mutator<TI>::unbind()
|
|
{
|
|
setVal_ = ValueSetter();
|
|
offset_ = Ofsetter();
|
|
nudge_ = Nudger();
|
|
}
|
|
|
|
|
|
#endif
|