define the ClipPresenter header-only (no change)

A separate translation unit turns out to be unnecessary here,
since this is implementation level code included into one single
other translation unit (timeline-controller.cpp). In such a situation,
having the whole class definition at one code location improves
readability.

Moreover, there clearly is now another abstraction barrier,
insofar all of the clip widget's implementation technicalities
are buried within clip-widget.cpp
This commit is contained in:
Fischlurch 2021-03-27 13:47:43 +01:00
parent 98a675e54c
commit d4b1735013
4 changed files with 123 additions and 209 deletions

View file

@ -1,181 +0,0 @@
/*
ClipPresenter - presentation control element for a clip within the timeline
Copyright (C) Lumiera.org
2016, 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 clip-presenter.cpp
** Implementation details of clip presentation management.
**
** @todo WIP-WIP-WIP as of 12/2016
** @todo as of 10/2018 timeline display in the UI is rebuilt to match the architecture
**
*/
#include "stage/gtk-base.hpp"
#include "include/ui-protocol.hpp"
#include "stage/timeline/clip-presenter.hpp"
#include "stage/timeline/marker-widget.hpp"
//#include "stage/ui-bus.hpp"
//#include "lib/format-string.hpp"
//#include "lib/format-cout.hpp"
//#include "lib/util.hpp"
//#include <algorithm>
//#include <vector>
//using util::_Fmt;
using lib::diff::TreeMutator;
using lib::diff::collection;
using std::make_unique;
//using util::contains;
//using Gtk::Widget;
//using sigc::mem_fun;
//using sigc::ptr_fun;
//using std::cout;
//using std::endl;
namespace stage {
namespace timeline {
/**
* @param identity referring to the corresponding session::Clip in Steam-Layer.
* @param nexus a way to connect this Controller to the UI-Bus.
* @param view (abstracted) canvas or display framework to attach this clip to
* @param timing (optional) start time point and duration of the clip.
* @note Clip can not be displayed unless #timing is given
*/
ClipPresenter::ClipPresenter (ID identity, ctrl::BusTerm& nexus, WidgetHook& view, optional<TimeSpan> const& timing)
: Controller{identity, nexus}
, channels_{}
, effects_{}
, markers_{}
, widget_{}
{
establishAppearance (&view, timing);
ENSURE (widget_);
}
ClipPresenter::~ClipPresenter()
{
}
void
ClipPresenter::buildMutator (TreeMutator::Handle buffer)
{
using PChannel = unique_ptr<ClipPresenter>;
using PEffect = unique_ptr<ClipPresenter>;
using PMarker = unique_ptr<MarkerWidget>;
buffer.create (
TreeMutator::build()
.attach (collection(markers_)
.isApplicableIf ([&](GenNode const& spec) -> bool
{ // »Selector« : require object-like sub scope with type-field "Marker"
return TYPE_Marker == spec.data.recordType();
})
.constructFrom ([&](GenNode const& spec) -> PMarker
{
return make_unique<MarkerWidget> (spec.idi, this->uiBus_);
}))
.attach (collection(effects_)
.isApplicableIf ([&](GenNode const& spec) -> bool
{ // »Selector« : require object-like sub scope with type-field "Effect"
return TYPE_Effect == spec.data.recordType();
})
.constructFrom ([&](GenNode const& spec) -> PEffect
{
std::optional<TimeSpan> timing = spec.retrieveAttribute<TimeSpan> (string{ATTR_timing});
return make_unique<ClipPresenter> (spec.idi, this->uiBus_
,getClipContentCanvas()
,timing);
}))
.attach (collection(channels_)
.isApplicableIf ([&](GenNode const& spec) -> bool
{ // »Selector« : require object-like sub scope with type-field "Channel"
return TYPE_Channel == spec.data.recordType();
})
.constructFrom ([&](GenNode const& spec) -> PChannel
{
return make_unique<ClipPresenter> (spec.idi, this->uiBus_
,getClipContentCanvas()
,std::nullopt); /////////////////////////TICKET #1213 : time → horizontal extension : how to represent "always" / "the whole track"??
}))
.change(ATTR_name, [&](string val)
{ // »Attribute Setter« : receive a new value for the clip name field
REQUIRE (widget_);
widget_->setClipName (val);
})
.change(ATTR_timing, [&](TimeSpan val)
{
REQUIRE (widget_);
widget_->changeTiming (val);
})
//-Diff-Change-Listener----------------
.onLocalChange ([this]()
{
this->establishAppearance();
}));
}
void
ClipPresenter::establishAppearance (WidgetHook* newView, optional<TimeSpan> const& timing)
{
ClipDelegate::selectAppearance (this->widget_, defaultAppearance, newView, timing);
}
WidgetHook&
ClipPresenter::getClipContentCanvas()
{
UNIMPLEMENTED ("how to create and wire an embedded canvas for the clip contents/effects");
}
uint
ClipPresenter::determineRequiredVerticalExtension() const
{
REQUIRE (widget_);
return widget_->calcRequiredHeight()
+ widget_->getVerticalOffset();
}
void
ClipPresenter::relink()
{
REQUIRE (widget_);
widget_->updatePosition();
}
}}// namespace stage::timeline

View file

@ -53,8 +53,10 @@
#define STAGE_TIMELINE_CLIP_PRESENTER_H
#include "stage/gtk-base.hpp"
#include "include/ui-protocol.hpp"
#include "stage/model/controller.hpp"
#include "stage/timeline/clip-widget.hpp"
#include "stage/timeline/marker-widget.hpp"
#include "stage/interact/cmd-context.hpp"
#include "lib/time/timevalue.hpp"
@ -73,8 +75,9 @@ namespace timeline {
using std::optional;
using std::unique_ptr;
using lib::time::TimeSpan;
class MarkerWidget;
using lib::diff::TreeMutator;
using lib::diff::collection;
using std::make_unique;
/**
@ -100,43 +103,136 @@ namespace timeline {
public:
ClipPresenter (ID, ctrl::BusTerm&, WidgetHook&, optional<TimeSpan> const&);
/**
* @param identity referring to the corresponding session::Clip in Steam-Layer.
* @param nexus a way to connect this Controller to the UI-Bus.
* @param view (abstracted) canvas or display framework to attach this clip to
* @param timing (optional) start time point and duration of the clip.
* @note Clip can not be displayed unless #timing is given
*/
ClipPresenter (ID identity, ctrl::BusTerm& nexus, WidgetHook& view, optional<TimeSpan> const& timing)
: Controller{identity, nexus}
, channels_{}
, effects_{}
, markers_{}
, widget_{}
{
establishAppearance (&view, timing);
ENSURE (widget_);
}
~ClipPresenter();
~ClipPresenter() { };
/** set up a binding to respond to mutation messages via UiBus */
virtual void buildMutator (lib::diff::TreeMutator::Handle) override;
virtual void
buildMutator (TreeMutator::Handle buffer) override
{
using PChannel = unique_ptr<ClipPresenter>;
using PEffect = unique_ptr<ClipPresenter>;
using PMarker = unique_ptr<MarkerWidget>;
buffer.create (
TreeMutator::build()
.attach (collection(markers_)
.isApplicableIf ([&](GenNode const& spec) -> bool
{ // »Selector« : require object-like sub scope with type-field "Marker"
return TYPE_Marker == spec.data.recordType();
})
.constructFrom ([&](GenNode const& spec) -> PMarker
{
return make_unique<MarkerWidget> (spec.idi, this->uiBus_);
}))
.attach (collection(effects_)
.isApplicableIf ([&](GenNode const& spec) -> bool
{ // »Selector« : require object-like sub scope with type-field "Effect"
return TYPE_Effect == spec.data.recordType();
})
.constructFrom ([&](GenNode const& spec) -> PEffect
{
std::optional<TimeSpan> timing = spec.retrieveAttribute<TimeSpan> (string{ATTR_timing});
return make_unique<ClipPresenter> (spec.idi, this->uiBus_
,getClipContentCanvas()
,timing);
}))
.attach (collection(channels_)
.isApplicableIf ([&](GenNode const& spec) -> bool
{ // »Selector« : require object-like sub scope with type-field "Channel"
return TYPE_Channel == spec.data.recordType();
})
.constructFrom ([&](GenNode const& spec) -> PChannel
{
return make_unique<ClipPresenter> (spec.idi, this->uiBus_
,getClipContentCanvas()
,std::nullopt); /////////////////////////TICKET #1213 : time → horizontal extension : how to represent "always" / "the whole track"??
}))
.change(ATTR_name, [&](string val)
{ // »Attribute Setter« : receive a new value for the clip name field
REQUIRE (widget_);
widget_->setClipName (val);
})
.change(ATTR_timing, [&](TimeSpan val)
{
REQUIRE (widget_);
widget_->changeTiming (val);
})
//-Diff-Change-Listener----------------
.onLocalChange ([this]()
{
this->establishAppearance();
}));
}
/** find out the number of pixels necessary to render this clip properly,
* assuming its current presentation mode (abbreviated, full, expanded).
/**
* find out the number of pixels necessary to render this clip properly,
* assuming its current presentation mode (abbreviated, full, expanded).
*/
uint determineRequiredVerticalExtension() const;
uint
determineRequiredVerticalExtension() const
{
REQUIRE (widget_);
return widget_->calcRequiredHeight()
+ widget_->getVerticalOffset();
}
/** update and re-attach the presentation widget into its presentation context.
* Will be called during the "re-link phase" of DisplayEvaluation, after the
* timeline layout has been (re)established globally. Often, this incurs
* attaching the presentation widget (ClipDelegate) at a different actual
* position onto the drawing canvas, be it due to a zoom change, or
* as result of layout re-flow.
/**
* update and re-attach the presentation widget into its presentation context.
* Will be called during the "re-link phase" of DisplayEvaluation, after the timeline layout
* has been (re)established globally. Often, this incurs attaching the presentation widget
* (ClipDelegate) at a different actual position onto the drawing canvas, be it due to a
* zoom change, or as result of layout re-flow.
*/
void relink();
void
relink()
{
REQUIRE (widget_);
widget_->updatePosition();
}
private:/* ===== Internals ===== */
/** reevaluate desired presentation mode and available data,
/**
* reevaluate desired presentation mode and available data,
* possibly leading to a changed appearance style of the clip.
* @remark a typical example would be, when a clip's temporal position,
* previously unspecified, now becomes defined through a diff message.
* With this data, it becomes feasible _actually to show the clip_ in
* the timeline. Thus the [Appearance style](\ref ClipDelegate::Appearance)
* of the presentation widget (delegate) can be switched up from `PENDING`
* to `ABRIDGED`.
* @remark a typical example would be, when a clip's temporal position, previously unspecified,
* now becomes defined through a diff message. With this data, it becomes feasible
* _actually to show the clip_ in the timeline. Thus the [Appearance style](\ref ClipDelegate::Appearance)
* of the presentation widget (delegate) can be switched up from `PENDING` to `ABRIDGED`.
*/
void establishAppearance(WidgetHook* newView =nullptr, optional<TimeSpan> const& timing =nullopt);
void
establishAppearance(WidgetHook* newView =nullptr,
optional<TimeSpan> const& timing =nullopt)
{
ClipDelegate::selectAppearance (this->widget_, defaultAppearance, newView, timing);
}
WidgetHook& getClipContentCanvas();
WidgetHook&
getClipContentCanvas()
{
UNIMPLEMENTED ("how to create and wire an embedded canvas for the clip contents/effects");
}
};

View file

@ -73,8 +73,8 @@
**
** When especially the optional argument `timing` is provided by the _population diff_
** creating the clip, then we can use the given lib::time::TimeSpan data for actually
** allocating a screen rectangle, and thus only in this case, a ClipWidget is constructed
** and mapped into presentation.
** allocating a screen rectangle, and thus only when this condition is met (which should
** be the default), a ClipWidget is constructed and mapped into presentation.
**
** Later the appearance style can be switched, which might incur the necessity also to
** exchange the actual implementation of the clip delegate. The latter is the case whenever

View file

@ -34,7 +34,6 @@
#include "stage/gtk-base.hpp"
#include "stage/style-scheme.hpp" /////////////////////TODO needed?
#include "stage/timeline/ruler-track.hpp"
#include "stage/timeline/track-presenter.hpp"
//#include "stage/ui-bus.hpp"
//#include "lib/format-string.hpp"