Relative-Hook: complete refactoring down to tracks and clips (see #1213)

...and the result was very much worth the effort,
leading to more focused and cleaner code.

 - all the concerns of moving widgets and translating coordinates
   are now confined to the second abstraction layer (CanvasHook)

 - while the ViewHook now deals exclusively with attachment, detachment
   and reordering of attachment sequence
This commit is contained in:
Fischlurch 2020-03-23 04:16:10 +01:00
parent f956f27ff4
commit e33eae729b
19 changed files with 157 additions and 280 deletions

View file

@ -85,6 +85,13 @@ namespace model {
virtual void hook (WID& widget, int xPos, int yPos) =0;
virtual void move (WID& widget, int xPos, int yPos) =0;
/** Anchor point to build chains of related View Hooks */
virtual CanvasHook<WID>&
getAnchorHook() noexcept
{
return *this;
}
struct Pos
{
CanvasHook& view;

View file

@ -92,13 +92,6 @@ namespace model {
template<class IT>
void reOrder (IT newOrder);
/** Anchor point to build chains of related View Hooks */
virtual ViewHook<WID>&
getAnchorHook() noexcept
{
return *this;
}
};
@ -176,7 +169,7 @@ namespace model {
void
ViewHook<WID>::reOrder (IT newOrder)
{
for (ViewHooked<WID>& existingHook : newOrder)
for (WID& existingHook : newOrder)
this->rehook (existingHook);
}

View file

@ -83,6 +83,11 @@ namespace timeline {
using CairoC = PCairoContext const&;
using StyleC = PStyleContext const&;
namespace { /////////////////////////////////////////////////////TICKET #1213 : use proper zoom handling instead of dummy constants!!
const int TODO_px_per_second = 25;
const int TODO_px_per_microtick = TODO_px_per_second / Time::SCALE;
} /////////////////////////////////////////////////////TICKET #1213 : (END) get rid of these dummy constants!!
namespace { // details of track background painting
const int INITIAL_TIMERULER_HEIGHT_px = 30;
@ -569,6 +574,12 @@ namespace timeline {
getCanvas(yPos).move (widget, xPos, yPos);
}
int
BodyCanvasWidget::translateTimeToPixels (Time startTimePoint) const
{
return TODO_px_per_microtick * _raw(startTimePoint); //////////TICKET #1213 : delegate zoom handling to the display manager (field #layout_) !!
}
/** respond to the DisplayEvaluation pass.
* @remark assuming that each track has already established it own vertical space requirement,
* thereby placing the extension values into TrackBody::contentHeight_

View file

@ -77,7 +77,7 @@
#include "stage/gtk-base.hpp"
#include "stage/timeline/track-profile.hpp"
#include "stage/timeline/display-evaluation.hpp"
#include "stage/model/view-hook.hpp"
#include "stage/model/canvas-hook.hpp"
//#include "lib/util.hpp"
@ -90,6 +90,8 @@
namespace stage {
namespace timeline {
using lib::time::Time;
using CairoC = Cairo::RefPtr<Cairo::Context> const&;
class DisplayManager;
@ -142,7 +144,7 @@ namespace timeline {
*/
class BodyCanvasWidget
: public Gtk::Box
, public model::ViewHook<Gtk::Widget>
, public model::CanvasHook<Gtk::Widget>
, public LayoutElement
{
DisplayManager& layout_;
@ -168,13 +170,15 @@ namespace timeline {
return contentArea_.get_vadjustment();
}
protected: /* ==== Interface: ViewHook ===== */
protected: /* ==== Interface: CanvasHook ===== */
void hook (Gtk::Widget&, int xPos=0, int yPos=0) override;
void move (Gtk::Widget&, int xPos, int yPos) override;
void remove (Gtk::Widget&) override;
void rehook (Gtk::Widget&) noexcept override;
int translateTimeToPixels (Time) const override;
protected: /* ==== Interface: LayoutElement ===== */
void establishLayout (DisplayEvaluation&) override;

View file

@ -70,7 +70,7 @@ namespace timeline {
* @param view (abstracted) canvas or display framework to attach this clip to
* @param offsetX offset relative to the start of the track ///////////////////////////////TICKET #1213 : translation time->offset should be built into the ViewHook!!!
*/
ClipPresenter::ClipPresenter (ID identity, ctrl::BusTerm& nexus, WidgetViewHook& view, optional<int> offsetX)
ClipPresenter::ClipPresenter (ID identity, ctrl::BusTerm& nexus, WidgetHook& view, optional<int> offsetX)
: Controller{identity, nexus}
, channels_{}
, effects_{}
@ -172,7 +172,7 @@ namespace timeline {
ClipDelegate::switchAppearance (this->widget_, defaultAppearance);
}
WidgetViewHook&
WidgetHook&
ClipPresenter::getClipContentCanvas()
{
UNIMPLEMENTED ("how to create and wire an embedded canvas for the clip contents/effects");

View file

@ -94,7 +94,7 @@ namespace timeline {
static const ClipDelegate::Appearance defaultAppearance = ClipDelegate::COMPACT;
public:
ClipPresenter (ID, ctrl::BusTerm&, WidgetViewHook&, optional<int> offsetX);
ClipPresenter (ID, ctrl::BusTerm&, WidgetHook&, optional<int> offsetX);
~ClipPresenter();
@ -121,7 +121,7 @@ namespace timeline {
*/
void resetAppearanceStyle();
WidgetViewHook& getClipContentCanvas();
WidgetHook& getClipContentCanvas();
};

View file

@ -75,34 +75,36 @@ namespace timeline {
namespace {// details of concrete clip appearance styles...
using WidgetHook = model::CanvasHook<Gtk::Widget>;
using HookedWidget = model::CanvasHooked<Gtk::Button, Gtk::Widget>; ///////////////////////////////////////////TICKET #1211 : need preliminary placeholder clip widget for timeline layout
class ClipData
: public ClipDelegate
, util::NonCopyable
{
WidgetViewHook& display_;
WidgetHook& display_;
/* === Interface ClipDelegate === */
public:
ClipData(WidgetViewHook& displayAnchor)
ClipData(WidgetHook& displayAnchor)
: ClipDelegate{}
, display_{displayAnchor}
{ }
};
using ViewHookedWidget = model::ViewHooked<Gtk::Button, Gtk::Widget>; ///////////////////////////////////////////TICKET #1211 : need preliminary placeholder clip widget for timeline layout
class ClipWidget
: public ViewHookedWidget
: public HookedWidget
, public ClipDelegate
, util::NonCopyable
{
/* === Interface ClipDelegate === */
public:
ClipWidget(WidgetViewHook& displayAnchor, int x, int y, uString clipName)
: ViewHookedWidget{displayAnchor.hookedAt(x,y), clipName}
ClipWidget(WidgetHook::Pos hookPoint, uString clipName)
: HookedWidget{hookPoint, clipName}
, ClipDelegate{}
{ }
};
@ -114,16 +116,16 @@ namespace timeline {
/* === Appearance Style state transitions === */
ClipDelegate::Appearance
ClipDelegate::switchAppearance (PDelegate& manager, Appearance desired, WidgetViewHook* newView)
ClipDelegate::switchAppearance (PDelegate& manager, Appearance desired, WidgetHook* newView)
{
UNIMPLEMENTED ("clip appearance style state management");
}
ClipDelegate::Appearance
ClipDelegate::buildDelegate (PDelegate& manager, WidgetViewHook& view, optional<int> startOffsetX) ////////////////TICKET #1213 : translation time->offset should be built into the ViewHook!!!
ClipDelegate::buildDelegate (PDelegate& manager, WidgetHook& view, optional<int> startOffsetX) ////////////////TICKET #1213 : translation time->offset should be built into the ViewHook!!!
{
if (startOffsetX)
manager.reset (new ClipWidget{view, *startOffsetX, defaultOffsetY, defaultName});
manager.reset (new ClipWidget{view.hookedAt(*startOffsetX, defaultOffsetY), defaultName});
else
manager.reset (new ClipData{view});
}

View file

@ -102,7 +102,7 @@
#define STAGE_TIMELINE_CLIP_WIDGET_H
#include "stage/gtk-base.hpp"
#include "stage/model/view-hook.hpp"
#include "stage/model/canvas-hook.hpp"
//#include "lib/util.hpp"
@ -118,7 +118,7 @@ namespace timeline {
using std::string;
using WidgetViewHook = model::ViewHook<Gtk::Widget>;
using WidgetHook = model::CanvasHook<Gtk::Widget>;
class ClipDelegate;
using PDelegate = std::unique_ptr<ClipDelegate>;
@ -167,12 +167,12 @@ namespace timeline {
*/
static Appearance switchAppearance (PDelegate& manager,
Appearance desired =PENDING,
WidgetViewHook* newView =nullptr);
WidgetHook* newView =nullptr);
/** build the initial presentation widget on construction, using a minimally
* viable appearance style. This is the first incantation of #switchAppearance.
*/
static Appearance buildDelegate (PDelegate& manager, WidgetViewHook& view,
static Appearance buildDelegate (PDelegate& manager, WidgetHook& view,
std::optional<int> startOffsetX); ///////////////////////TICKET #1213 : translation time->offset should be built into the ViewHook!!!
private:/* ===== Internals ===== */

View file

@ -65,7 +65,7 @@ namespace timeline {
using util::max;
using model::ViewHooked;
using lib::time::Time;
class TrackHeadWidget;
class TrackBody;
@ -75,10 +75,10 @@ namespace timeline {
* when attaching or moving Widgets on the shared canvas.
*/
template<class WID>
class ViewRefHook
: public model::ViewHook<WID>
class RelativeCanvasHook
: public model::CanvasHook<WID>
{
model::ViewHook<WID>& refHook_;
model::CanvasHook<WID>& refHook_;
/* ==== Interface: ViewHook ===== */
@ -108,7 +108,7 @@ namespace timeline {
}
/** allow to build a derived ViewRefHook with different offset */
model::ViewHook<WID>&
model::CanvasHook<WID>&
getAnchorHook() noexcept override
{
return this->refHook_;
@ -118,8 +118,15 @@ namespace timeline {
virtual int hookAdjX (int xPos) =0;
virtual int hookAdjY (int yPos) =0;
/** delegating default implementation for timeline zoom */
int
translateTimeToPixels (Time startTimePoint) const override
{
return refHook_.hookedAt(startTimePoint).x;
}
public:
ViewRefHook(model::ViewHook<WID>& baseHook)
RelativeCanvasHook(model::CanvasHook<WID>& baseHook)
: refHook_{baseHook.getAnchorHook()}
{ }
};
@ -144,7 +151,7 @@ namespace timeline {
virtual model::ViewHook<TrackHeadWidget>& getHeadHook() =0;
virtual model::ViewHook<TrackBody>& getBodyHook() =0;
virtual model::ViewHook<Gtk::Widget>& getClipHook() =0;
virtual model::CanvasHook<Gtk::Widget>& getClipHook() =0;
};

View file

@ -135,16 +135,14 @@ namespace timeline {
/* ==== Interface: ViewHook ===== */
void
TimelineLayout::hook (TrackHeadWidget& head, int xPos, int yPos)
TimelineLayout::hook (TrackHeadWidget& head)
{
REQUIRE (xPos==0 && yPos==0, "arbitrary positioning of sub-Tracks contradicts the concept of track-profile."); ///TODO remove that API
headerPane_.installForkRoot (head);
}
void
TimelineLayout::hook (TrackBody& body, int xPos, int yPos)
TimelineLayout::hook (TrackBody& body)
{
REQUIRE (xPos==0 && yPos==0, "arbitrary positioning of sub-Tracks contradicts the concept of track-profile."); ///TODO remove that API
bodyCanvas_.installForkRoot (body);
// detect changes of the track structure
@ -179,18 +177,5 @@ namespace timeline {
}
void
TimelineLayout::move (TrackHeadWidget& head, int xPos, int yPos)
{
NOTREACHED ("ViewHooked: not supported -- refactor?");
}
void
TimelineLayout::move (TrackBody& body, int xPos, int yPos)
{
NOTREACHED ("ViewHooked: not supported -- refactor?");
}
}}// namespace stage::timeline

View file

@ -87,7 +87,7 @@
#include "stage/timeline/display-evaluation.hpp"
#include "stage/timeline/header-pane-widget.hpp"
#include "stage/timeline/body-canvas-widget.hpp"
#include "stage/model/view-hook.hpp"
#include "stage/model/canvas-hook.hpp"
//#include "lib/util.hpp"
@ -143,19 +143,17 @@ namespace timeline {
model::ViewHook<TrackHeadWidget>& getHeadHook() override { return *this; };
model::ViewHook<TrackBody>& getBodyHook() override { return *this; };
model::ViewHook<Gtk::Widget>& getClipHook() override { return bodyCanvas_; };
model::CanvasHook<Gtk::Widget>& getClipHook() override { return bodyCanvas_; };
protected: /* ==== Interface: ViewHook ===== */
void hook (TrackHeadWidget&, int xPos=0, int yPos=0) override;
void move (TrackHeadWidget&, int xPos, int yPos) override;
void remove (TrackHeadWidget&) override;
void rehook (TrackHeadWidget&) noexcept override;
void hook (TrackHeadWidget&) override;
void remove (TrackHeadWidget&) override;
void rehook (TrackHeadWidget&) noexcept override;
void hook (TrackBody&, int xPos=0, int yPos=0) override;
void move (TrackBody&, int xPos, int yPos) override;
void remove (TrackBody&) override;
void rehook (TrackBody&) noexcept override;
void hook (TrackBody&) override;
void remove (TrackBody&) override;
void rehook (TrackBody&) noexcept override;
private:/* ===== Internals ===== */

View file

@ -95,21 +95,14 @@ namespace timeline {
/* ==== Interface: ViewHook ===== */
void
TrackBody::hook (TrackBody& subBody, int xPos, int yPos)
TrackBody::hook (TrackBody& subBody)
{
REQUIRE (xPos==0 && yPos==0, "arbitrary positioning of TrackBody contradicts the concept of track-profile."); ///TODO remove that API
subTracks_.push_back (&subBody);
// notify presentation code of the changed structure
subBody.signalStructureChange_ = signalStructureChange_;
signalStructureChange_(); // this _is_ such a change
}
void
TrackBody::move (TrackBody& subBody, int xPos, int yPos)
{
NOTREACHED ("woot?? can we even move a sub-TrackBody????");
}
void
TrackBody::remove (TrackBody& subBody)

View file

@ -41,6 +41,7 @@
#define STAGE_TIMELINE_TRACK_BODY_H
#include "stage/gtk-base.hpp"
#include "stage/model/view-hook.hpp"
#include "stage/timeline/ruler-track.hpp"
#include "stage/timeline/display-manager.hpp"
@ -125,10 +126,9 @@ namespace timeline {
/* ==== Interface: ViewHook ===== */
void hook (TrackBody&, int xPos=0, int yPos=0) override;
void move (TrackBody&, int xPos, int yPos) override;
void remove (TrackBody&) override;
void rehook (TrackBody&) noexcept override;
void hook (TrackBody&) override;
void remove (TrackBody&) override;
void rehook (TrackBody&) noexcept override;
/* ===== Internals ===== */
/**

View file

@ -131,17 +131,10 @@ namespace timeline {
/* ==== Interface: ViewHook ===== */
void
TrackHeadWidget::hook (TrackHeadWidget& subHead, int xPos, int yPos)
TrackHeadWidget::hook (TrackHeadWidget& subHead)
{
REQUIRE (xPos==0 && yPos==0, "selection of arbitrary row not yet implemented");
attachSubFork (subHead);
}
void
TrackHeadWidget::move (TrackHeadWidget& subHead, int xPos, int yPos)
{
NOTREACHED ("woot?? can we even move a sub-TrackHead????");
}
void
TrackHeadWidget::remove (TrackHeadWidget& subHead)

View file

@ -61,7 +61,6 @@ namespace stage {
namespace timeline {
using ID = ctrl::BusTerm::ID;
using model::ViewHooked;
/**
* Header pane control area corresponding to a Track with nested child Tracks.
@ -81,10 +80,9 @@ namespace timeline {
/* ==== Interface: ViewHook ===== */
void hook (TrackHeadWidget&, int xPos=0, int yPos=0) override;
void move (TrackHeadWidget&, int xPos, int yPos) override;
void remove (TrackHeadWidget&) override;
void rehook (TrackHeadWidget&) noexcept override;
void hook (TrackHeadWidget&) override;
void remove (TrackHeadWidget&) override;
void rehook (TrackHeadWidget&) noexcept override;
public:
TrackHeadWidget();

View file

@ -141,10 +141,10 @@ namespace timeline {
class DisplayFrame
: util::NonCopyable
, public DisplayViewHooks
, public ViewRefHook<Gtk::Widget>
, public RelativeCanvasHook<Gtk::Widget>
{
ViewHooked<TrackHeadWidget> head_;
ViewHooked<TrackBody> body_;
model::ViewHooked<TrackHeadWidget> head_;
model::ViewHooked<TrackBody> body_;
/* === extended Interface for relative view hook === */
@ -155,11 +155,11 @@ namespace timeline {
model::ViewHook<TrackHeadWidget>& getHeadHook() override { return head_; };
model::ViewHook<TrackBody>& getBodyHook() override { return body_; };
model::ViewHook<Gtk::Widget>& getClipHook() override { return *this; };
model::CanvasHook<Gtk::Widget>& getClipHook() override { return *this; };
public:
DisplayFrame (DisplayViewHooks& displayAnchor)
: ViewRefHook{displayAnchor.getClipHook()}
: RelativeCanvasHook{displayAnchor.getClipHook()}
, head_{displayAnchor.getHeadHook()}
, body_{displayAnchor.getBodyHook()}
{ }
@ -259,7 +259,7 @@ namespace timeline {
{
uint x = rand() % 50;
uint y = 0;
Gtk::Button* butt = Gtk::manage (new ViewHooked<Gtk::Button, Gtk::Widget>{display_.hookedAt(x,y), TODO_trackName_});
Gtk::Button* butt = Gtk::manage (new model::CanvasHooked<Gtk::Button, Gtk::Widget>{display_.hookedAt(x,y), TODO_trackName_});
butt->signal_clicked().connect(
[butt]{ cout << "|=="<<butt->get_label()<<endl; });
butt->show();

View file

@ -123,24 +123,8 @@ namespace test {
and pos->posY == y_expected;
}
/** verify our internal sequence matches the given one */
template<class IT>
bool
testContainsSequence (IT refSeq)
{
// NOTE the implementation twist of using a std::forward_list,
// which causes reversed order of our internal elements
lib::IterStack<int> ids;
for (auto& entry : widgets_)
ids.push (entry.widget.i);
for ( ; refSeq and ids; ++refSeq, ++ids)
if (refSeq->i != *ids) break;
return isnil(refSeq)
and isnil(ids);
}
/* === Interface ViewHook === */
/* === Interface CanvasHook === */
void
hook (DummyWidget& elm, int xPos, int yPos) override
@ -208,39 +192,22 @@ namespace test {
virtual void
run (Arg)
{
verify_standardUsage();
verify_multiplicity();
attach2canvas();
relocateWidget();
reOrderHooked();
}
/** @test the standard use case is to hook up a widget to a canvas for display.
/** @test attach several widgets with distinct coordinates
* and verify automated detaching on destruction.
*/
void
verify_standardUsage()
{
FakeCanvas canvas;
CHECK (canvas.empty());
{
HookedWidget widget{canvas.hookedAt(0,0) };
CHECK (canvas.testContains (widget.i));
CHECK (not canvas.empty());
}// hook goes out of scope...
CHECK (canvas.empty());
}
/** @test each hooking has a distinct identity and is managed on its own.
*/
void
verify_multiplicity()
attach2canvas()
{
FakeCanvas canvas;
CHECK (canvas.empty());
HookedWidget widget{canvas.hookedAt(1,1)};
CHECK (canvas.testContains (widget.i));
CHECK (canvas.testVerifyPos (widget, 1,1));
CHECK (not canvas.empty());
int someID;
@ -249,6 +216,8 @@ namespace test {
someID = otherWidget.i;
CHECK (canvas.testContains (someID));
CHECK (canvas.testContains (widget.i));
CHECK (canvas.testVerifyPos (widget, 1,1));
CHECK (canvas.testVerifyPos (otherWidget, 2,2));
}// hook goes out of scope...
CHECK (not canvas.testContains (someID));
CHECK (canvas.testContains (widget.i));
@ -292,46 +261,6 @@ namespace test {
CHECK (canvas.testVerifyPos (w1, x1,y1));
CHECK (canvas.testVerifyPos (w3, x3,y3));
}
/** @test a mechanism to re-attach elements in changed order.
* @remarks this test looks somewhat convoluted, because `ViewHooked<W>` is defined
* to be non-copyable (for good reason, since we can assume that the canvas
* somehow maintains a pointer to each widget added, so we can not allow the
* attached widgets to move in memory). However, in practice the "order sequence"
* is typically defined as a delegating iterator over some sequence of model elements,
* which themselves are managed as a STL container of `std::unique_ptr`; thus it is
* very much possible to alter the sequence of the model elements, without actually
* touching the memory location of the slave widgets used for presentation.
*/
void
reOrderHooked()
{
using Widgets = lib::ScopedCollection<HookedWidget>;
using OrderIdx = vector<HookedWidget*>;
FakeCanvas canvas; // WARNING: Canvas must outlive the widgets!
// create 20 (random) widgets and hook them onto the canvas
Widgets widgets{20};
OrderIdx orderIdx;
for (uint i=0; i<20; ++i)
orderIdx.push_back (& widgets.emplace<HookedWidget>(canvas.hookedAt(0,1)));
// helper: use the orderIdx to generate sequence of widget refs (not pointers)
auto orderSequence = [&] { return lib::ptrDeref(eachElm(orderIdx)); };
CHECK (canvas.testContainsSequence (eachElm(widgets)));
CHECK (canvas.testContainsSequence (orderSequence()));
// now lets assume the relevant order of the widgets has been altered
shuffle (orderIdx.begin(),orderIdx.end(), std::random_device());
CHECK (not canvas.testContainsSequence (orderSequence()));
// so we need to re-construct the canvas attachments in the new order
canvas.reOrder (orderSequence());
CHECK (canvas.testContainsSequence (orderSequence()));
}
};

View file

@ -33,8 +33,8 @@
#include "lib/iter-adapter-stl.hpp"
#include "lib/util.hpp"
#include <list>
#include <vector>
#include <forward_list>
#include <algorithm>
#include <random>
@ -43,9 +43,8 @@ using util::isnil;
using util::contains;
using util::isSameObject;
using lib::iter_stl::eachElm;
using std::vector;
using std::forward_list;
using std::shuffle;
using std::vector;
namespace stage{
@ -73,21 +72,15 @@ namespace test {
class FakeCanvas
: public ViewHook<DummyWidget>
{
struct Attachment
{
DummyWidget& widget;
int posX, posY;
};
forward_list<Attachment> widgets_;
std::list<DummyWidget> widgets_;
auto
allWidgetIDs() const
{
return lib::treeExplore(widgets_)
.transform([](Attachment const& entry)
.transform([](DummyWidget const& entry)
{
return entry.widget.i;
return entry.i;
});
}
@ -96,7 +89,7 @@ namespace test {
{
return std::find_if (widgets_.begin()
,widgets_.end()
, [&](Attachment const& a) { return a.widget == someWidget; });
, [&](DummyWidget const& widget) { return widget == someWidget; });
}
public:
@ -113,26 +106,12 @@ namespace test {
return util::linearSearch (allWidgetIDs(), someWidgetID);
}
bool
testVerifyPos (DummyWidget const& someWidget, int x_expected, int y_expected)
{
auto end = widgets_.end();
auto pos = findEntry (someWidget);
return pos != end
and pos->posX == x_expected
and pos->posY == y_expected;
}
/** verify our internal sequence matches the given one */
template<class IT>
bool
testContainsSequence (IT refSeq)
{
// NOTE the implementation twist of using a std::forward_list,
// which causes reversed order of our internal elements
lib::IterStack<int> ids;
for (auto& entry : widgets_)
ids.push (entry.widget.i);
auto ids = allWidgetIDs();
for ( ; refSeq and ids; ++refSeq, ++ids)
if (refSeq->i != *ids) break;
return isnil(refSeq)
@ -143,27 +122,15 @@ namespace test {
/* === Interface ViewHook === */
void
hook (DummyWidget& elm, int xPos, int yPos) override
hook (DummyWidget& elm) override
{
widgets_.push_front (Attachment{elm, xPos,yPos});
}
void
move (DummyWidget& elm, int xPos, int yPos) override
{
auto end = widgets_.end();
auto pos = findEntry (elm);
if (pos != end)
{
pos->posX = xPos;
pos->posY = yPos;
}
widgets_.push_back (elm);
}
void
remove (DummyWidget& elm) override
{
widgets_.remove_if ([&](Attachment const& a) { return a.widget == elm; });
widgets_.remove_if ([&](auto const& widget) { return widget == elm; });
}
@ -172,9 +139,9 @@ namespace test {
{
auto pos = findEntry (existingHook);
REQUIRE (pos != widgets_.end(), "the given iterator must yield previously hooked-up elements");
Attachment existing{*pos};
this->remove (existing.widget);
this->hook (existing.widget, existing.posX,existing.posY);
DummyWidget& widget{*pos};
this->remove (widget);
this->hook (widget);
}
};
}
@ -202,7 +169,6 @@ namespace test {
{
verify_standardUsage();
verify_multiplicity();
relocateWidget();
reOrderHooked();
}
@ -248,44 +214,6 @@ namespace test {
}
/** @test hook a widget at a specific position and then later
* relocate it on the canvas through the ViewHook front-end.
*/
void
relocateWidget()
{
int x1 = rand() % 100;
int y1 = rand() % 100;
int x2 = rand() % 100;
int y2 = rand() % 100;
int x3 = rand() % 100;
int y3 = rand() % 100;
FakeCanvas canvas;
HookedWidget w1{canvas.hookedAt(x1,y1)};
HookedWidget w3{canvas.hookedAt(x3,y3)};
int id2;
{
HookedWidget w2{canvas.hookedAt(x2,y2)};
id2 = w2.i;
CHECK (canvas.testContains (id2));
CHECK (canvas.testVerifyPos (w2, x2,y2));
int newX = ++x2;
int newY = --y2;
w2.moveTo (newX,newY);
CHECK (canvas.testVerifyPos (w2, newX,newY));
CHECK (canvas.testVerifyPos (w1, x1,y1));
CHECK (canvas.testVerifyPos (w3, x3,y3));
}
CHECK (not canvas.testContains (id2));
CHECK (canvas.testVerifyPos (w1, x1,y1));
CHECK (canvas.testVerifyPos (w3, x3,y3));
}
/** @test a mechanism to re-attach elements in changed order.
* @remarks this test looks somewhat convoluted, because `ViewHooked<W>` is defined
* to be non-copyable (for good reason, since we can assume that the canvas

View file

@ -791,6 +791,13 @@
</p>
</body>
</html></richcontent>
<node COLOR="#435e98" CREATED="1584932162467" ID="ID_813910229" MODIFIED="1584932190012" TEXT="inzwischen wohl gekl&#xe4;rt">
<icon BUILTIN="help"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584932093080" ID="ID_1234616959" MODIFIED="1584932222621" TEXT="es gibt aber noch einen &#xe4;hnlichen Fehler....">
<arrowlink COLOR="#b183a2" DESTINATION="ID_535289114" ENDARROW="Default" ENDINCLINATION="-2880;0;" ID="Arrow_ID_1219325605" STARTARROW="None" STARTINCLINATION="595;36;"/>
<icon BUILTIN="messagebox_warning"/>
</node>
</node>
<node CREATED="1484800480472" ID="ID_1441013524" MODIFIED="1576282358158" TEXT="Sanity-Check in CoreService anpassen">
<richcontent TYPE="NOTE"><html>
@ -19174,10 +19181,10 @@
<node CREATED="1575581015219" ID="ID_1015965713" MODIFIED="1575581026974" TEXT="f&#xfc;r den Widget -&gt; Canvas - Fall"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1576757730509" ID="ID_1949130658" MODIFIED="1584894698264" TEXT="&#xbb;Widget-Position&#xab; heraus-abstrahieren">
<node COLOR="#338800" CREATED="1576757730509" FOLDED="true" ID="ID_1949130658" MODIFIED="1584932567087" TEXT="&#xbb;Widget-Position&#xab; heraus-abstrahieren">
<linktarget COLOR="#605bb8" DESTINATION="ID_1949130658" ENDARROW="Default" ENDINCLINATION="204;611;" ID="Arrow_ID_993970158" SOURCE="ID_1816490333" STARTARROW="None" STARTINCLINATION="299;12;"/>
<linktarget COLOR="#cd478c" DESTINATION="ID_1949130658" ENDARROW="Default" ENDINCLINATION="-1150;77;" ID="Arrow_ID_662295339" SOURCE="ID_730955223" STARTARROW="None" STARTINCLINATION="872;48;"/>
<icon BUILTIN="pencil"/>
<linktarget COLOR="#47cdcd" DESTINATION="ID_1949130658" ENDARROW="Default" ENDINCLINATION="-1150;77;" ID="Arrow_ID_662295339" SOURCE="ID_730955223" STARTARROW="None" STARTINCLINATION="877;53;"/>
<icon BUILTIN="button_ok"/>
<node CREATED="1576757867075" ID="ID_1947629675" MODIFIED="1576757891150" TEXT="der Themenkomplex &quot;move()&quot; steht isoliert da">
<icon BUILTIN="idea"/>
<node CREATED="1576757893399" ID="ID_309464876" MODIFIED="1576757905274" TEXT="eigentlich ein orthogonaler Belang"/>
@ -19213,8 +19220,8 @@
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584894608428" ID="ID_1214957016" MODIFIED="1584894628941" TEXT="Sub-Interface CanvasHooked herausl&#xf6;sen">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1584894608428" ID="ID_1214957016" MODIFIED="1584932472009" TEXT="Sub-Interface CanvasHooked herausl&#xf6;sen">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1584894703681" ID="ID_101352353" MODIFIED="1584894725463" TEXT="ViewHook / CanvasHook">
<icon BUILTIN="button_ok"/>
<node CREATED="1584894715892" ID="ID_70510619" MODIFIED="1584894722576" TEXT="oh Ja... viel sch&#xf6;ner">
@ -19224,19 +19231,23 @@
<node COLOR="#338800" CREATED="1584894728286" ID="ID_414399172" MODIFIED="1584894738145" TEXT="Test umschreiben auf CanvasHook">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584894739041" ID="ID_1191395338" MODIFIED="1584894750232" TEXT="separaten Test f&#xfc;r den Basis-Fall">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1584894739041" ID="ID_1191395338" MODIFIED="1584932467447" TEXT="separaten Test f&#xfc;r den Basis-Fall">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584894751623" ID="ID_674921950" MODIFIED="1584894785196" TEXT="Interfaces in DisplayManager differenzieren">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1584894751623" ID="ID_674921950" MODIFIED="1584932474570" TEXT="Interfaces in DisplayManager differenzieren">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584894766013" ID="ID_1003825239" MODIFIED="1584894784715" TEXT="Interfaces im TrackPresenter differenzieren">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1584894766013" ID="ID_1003825239" MODIFIED="1584932476358" TEXT="Interfaces im TrackPresenter differenzieren">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584894775935" ID="ID_241955108" MODIFIED="1584894783892" TEXT="Interfaces im ClipPresenter differenzieren">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1584894775935" ID="ID_241955108" MODIFIED="1584932477944" TEXT="Interfaces im ClipPresenter differenzieren">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node COLOR="#435e98" CREATED="1584932523675" ID="ID_715711411" MODIFIED="1584932535644" TEXT="hat auf allen Ebenen den Code verbessert">
<font ITALIC="true" NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="yes"/>
</node>
</node>
<node COLOR="#338800" CREATED="1575057172259" ID="ID_1193961919" MODIFIED="1575670553256" TEXT="Umordnen">
<linktarget COLOR="#507b9b" DESTINATION="ID_1193961919" ENDARROW="Default" ENDINCLINATION="-877;88;" ID="Arrow_ID_1469804818" SOURCE="ID_876124745" STARTARROW="None" STARTINCLINATION="576;33;"/>
@ -22097,8 +22108,8 @@
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584888309716" ID="ID_236695014" MODIFIED="1584888323264" TEXT="explizit modelliert (CanvasHook)">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584888309716" ID="ID_236695014" MODIFIED="1584932638731" TEXT="explizit modelliert (CanvasHook)">
<icon BUILTIN="forward"/>
<node CREATED="1584888472583" ID="ID_1663593154" MODIFIED="1584888561167" TEXT="Idee: smart-Handle">
<linktarget COLOR="#775ae8" DESTINATION="ID_1663593154" ENDARROW="Default" ENDINCLINATION="84;-81;" ID="Arrow_ID_348205317" SOURCE="ID_384525360" STARTARROW="None" STARTINCLINATION="-297;47;"/>
<icon BUILTIN="idea"/>
@ -22106,11 +22117,14 @@
<node COLOR="#338800" CREATED="1584888672307" ID="ID_213768440" MODIFIED="1584888703767" TEXT="brauche zus&#xe4;tzlich lokalen Offset">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584888686257" ID="ID_380050520" MODIFIED="1584888921383" TEXT="brauche zus&#xe4;tzlich &#xdc;bersetzung Time -&gt; pixel">
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584888686257" ID="ID_380050520" MODIFIED="1584932631032" TEXT="brauche zus&#xe4;tzlich &#xdc;bersetzung Time -&gt; pixel">
<linktarget COLOR="#354ce1" DESTINATION="ID_380050520" ENDARROW="Default" ENDINCLINATION="-2737;0;" ID="Arrow_ID_459309015" SOURCE="ID_1764561185" STARTARROW="None" STARTINCLINATION="-885;49;"/>
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584889060615" ID="ID_730955223" MODIFIED="1584889187770" TEXT="mu&#xdf; Design des ViewHook ausdifferenzieren">
<arrowlink COLOR="#cd478c" DESTINATION="ID_1949130658" ENDARROW="Default" ENDINCLINATION="-1150;77;" ID="Arrow_ID_662295339" STARTARROW="None" STARTINCLINATION="872;48;"/>
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1584889060615" ID="ID_730955223" MODIFIED="1584932567086" TEXT="mu&#xdf; Design des ViewHook ausdifferenzieren">
<arrowlink COLOR="#47cdcd" DESTINATION="ID_1949130658" ENDARROW="Default" ENDINCLINATION="-1150;77;" ID="Arrow_ID_662295339" STARTARROW="None" STARTINCLINATION="877;53;"/>
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584932570334" ID="ID_626245969" MODIFIED="1584932585348" TEXT="TODO: Aufruf an den DisplayManager weiterleiten">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
@ -27943,7 +27957,8 @@
</node>
</node>
</node>
<node CREATED="1584201751260" ID="ID_1492285349" MODIFIED="1584201760641" TEXT="Problem: initiale Koordinaten">
<node CREATED="1584201751260" ID="ID_1492285349" MODIFIED="1584932876269" TEXT="Problem: initiale Koordinaten">
<linktarget COLOR="#6fa9d7" DESTINATION="ID_1492285349" ENDARROW="Default" ENDINCLINATION="-156;14;" ID="Arrow_ID_819317359" SOURCE="ID_726635217" STARTARROW="None" STARTINCLINATION="170;10;"/>
<node COLOR="#338800" CREATED="1584201782842" ID="ID_560620961" MODIFIED="1584319384536" TEXT="sind nicht (zwingend) im Populations-Diff">
<arrowlink COLOR="#38abc2" DESTINATION="ID_1687516658" ENDARROW="Default" ENDINCLINATION="30;37;" ID="Arrow_ID_901736037" STARTARROW="None" STARTINCLINATION="129;9;"/>
<icon BUILTIN="button_ok"/>
@ -27991,6 +28006,13 @@
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584932756457" HGAP="142" ID="ID_726635217" MODIFIED="1584932886403" TEXT="Einh&#xe4;ngen, wenn m&#xf6;glich" VSHIFT="-7">
<arrowlink COLOR="#6fa9d7" DESTINATION="ID_1492285349" ENDARROW="Default" ENDINCLINATION="-156;14;" ID="Arrow_ID_819317359" STARTARROW="None" STARTINCLINATION="170;10;"/>
<icon BUILTIN="flag-yellow"/>
<node CREATED="1584932910051" HGAP="63" ID="ID_1737560927" MODIFIED="1584932955050" TEXT="Zustands-Wechsel-Logik" VSHIFT="16">
<arrowlink COLOR="#4768d0" DESTINATION="ID_1833569495" ENDARROW="Default" ENDINCLINATION="943;-75;" ID="Arrow_ID_416244548" STARTARROW="None" STARTINCLINATION="207;23;"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584229199851" HGAP="47" ID="ID_1146096669" MODIFIED="1584229499004" TEXT="Vorsicht: Darstellung von Spuren und Effekten im Clip" VSHIFT="2">
<richcontent TYPE="NOTE"><html>
<head>
@ -28159,7 +28181,8 @@
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584319635024" ID="ID_1833569495" MODIFIED="1584319648254" TEXT="Logik im Detail ausimplementieren">
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584319635024" ID="ID_1833569495" MODIFIED="1584932955050" TEXT="Logik im Detail ausimplementieren">
<linktarget COLOR="#4768d0" DESTINATION="ID_1833569495" ENDARROW="Default" ENDINCLINATION="943;-75;" ID="Arrow_ID_416244548" SOURCE="ID_1737560927" STARTARROW="None" STARTINCLINATION="207;23;"/>
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1584319522106" ID="ID_600779996" MODIFIED="1584319541312" TEXT="Vorsicht Falle: Kopieren beim Wechsel des Anzeigestils">
@ -28259,7 +28282,7 @@
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1540640109628" ID="ID_147809215" MODIFIED="1557498707231" TEXT="TODO: TimelineGui (proxy) mu&#xdf; kaskadieren">
<icon BUILTIN="flag-pink"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1544135984042" ID="ID_429363053" MODIFIED="1576282358038" TEXT="Grund f&#xfc;r den Fehler im Nexus?">
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1544135984042" ID="ID_429363053" MODIFIED="1584932261288" TEXT="Grund f&#xfc;r den Fehler im Nexus?">
<richcontent TYPE="NOTE"><html>
<head>
@ -28270,12 +28293,18 @@
</p>
</body>
</html></richcontent>
<arrowlink COLOR="#d23194" DESTINATION="ID_535289114" ENDARROW="Default" ENDINCLINATION="90;0;" ID="Arrow_ID_186398670" STARTARROW="None" STARTINCLINATION="110;9;"/>
<icon BUILTIN="help"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1540640144175" ID="ID_158939327" MODIFIED="1557498707231" TEXT="Zugang via TimelinePanel">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1584932054082" ID="ID_535289114" MODIFIED="1584932261288" TEXT="#1216 Some UI components are still connected to the backbone at shutdown">
<linktarget COLOR="#d23194" DESTINATION="ID_535289114" ENDARROW="Default" ENDINCLINATION="90;0;" ID="Arrow_ID_186398670" SOURCE="ID_429363053" STARTARROW="None" STARTINCLINATION="110;9;"/>
<linktarget COLOR="#b183a2" DESTINATION="ID_535289114" ENDARROW="Default" ENDINCLINATION="-2880;0;" ID="Arrow_ID_1219325605" SOURCE="ID_1234616959" STARTARROW="None" STARTINCLINATION="595;36;"/>
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
<node CREATED="1540639255700" ID="ID_1855001421" MODIFIED="1557498707231" TEXT="Anzeige..">