Relative-Hook: rectify the design
Partially as a leftover from the way more ambitious initial design, we ended up with CanvasHook as an elaboration/specialisation of the ViewHook abstraction. However, as it stands, this design is tilted, since CanvasHook is not just an elaboration, but rather a variation of the same basic idea. And this is now more like a building pattern and less of a generic framework, it seems adequate to separate these two variations completely, even if this incurs a small amount of code duplication. Actually this refactoring is necessary to resolve a bug, where we ended up with the same Clip widgets attached two times to the same Canvas control, one time through the ViewHook baseclass, and a second time by the ctor of the "derived" CanvasHook
This commit is contained in:
parent
e175805481
commit
2e4cd56f4f
8 changed files with 97 additions and 64 deletions
|
|
@ -23,12 +23,14 @@
|
|||
|
||||
/** @file canvas-hook.hpp
|
||||
** Specialised (abstracted) presentation context with positioning by coordinates.
|
||||
** This extends the generic abstraction of a ViewHook, and works in a similar way,
|
||||
** This expands the idea behind the ViewHook abstraction, and works in a similar way,
|
||||
** in close collaboration with the corresponding CanvasHooked entity (abstraction).
|
||||
** Elements relying on those abstractions maintain an attachment to "their view",
|
||||
** while remaining agnostic about the view's implementation details. But the key
|
||||
** point with this extended abstraction is that elements can be placed onto a
|
||||
** coordinate system or canvas, and they can be moved to a different position.
|
||||
** while remaining agnostic about the view's implementation details. However,
|
||||
** the key point with this extended variant of the abstraction is that elements
|
||||
** can be placed onto a coordinate system or canvas, and they can be moved to a
|
||||
** different position.
|
||||
**
|
||||
** A CanvasHooked element is basically a decorator directly attached to the
|
||||
** element, adding automatic detachment on destruction, similar to a smart-ptr.
|
||||
** So the "hooked" widget will live within the common allocation, together with
|
||||
|
|
@ -50,7 +52,6 @@
|
|||
#ifndef STAGE_MODEL_CANVAS_HOOK_H
|
||||
#define STAGE_MODEL_CANVAS_HOOK_H
|
||||
|
||||
#include "stage/model/view-hook.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "lib/nocopy.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
|
@ -67,24 +68,25 @@ namespace model {
|
|||
|
||||
/**
|
||||
* Interface to represent _"some presentation layout entity",_
|
||||
* with the ability to _place_ widgets (managed elsewhere), to
|
||||
* relocate those widgets to another position, and to re-establish
|
||||
* a different sequence of the widgets (whatever this means).
|
||||
* with the ability to _place_ widgets (managed elsewhere) onto it,
|
||||
* to relocate those widgets to another position.
|
||||
* @remark the canonical example is a _canvas widget,_ (e.g. `Gtk::Layout`),
|
||||
* allowing to attach child widgets at specific positions, together with
|
||||
* custom drawing.
|
||||
* @warning please ensure the CanvasHook outlives any attached CanvasHooked.
|
||||
* @see ViewHook embodies the same scheme for widgets just "added" into
|
||||
* the presentation without the notion of explicit coordinates.
|
||||
*/
|
||||
template<class WID>
|
||||
class CanvasHook
|
||||
: public ViewHook<WID>
|
||||
{
|
||||
public:
|
||||
virtual ~CanvasHook() { } ///< this is an interface
|
||||
|
||||
virtual void hook (WID& widget, int xPos, int yPos) =0;
|
||||
virtual void move (WID& widget, int xPos, int yPos) =0;
|
||||
|
||||
virtual void hook (WID& widget, int xPos, int yPos) =0;
|
||||
virtual void move (WID& widget, int xPos, int yPos) =0;
|
||||
virtual void remove (WID& widget) =0;
|
||||
|
||||
/** Anchor point to build chains of related View Hooks */
|
||||
virtual CanvasHook<WID>&
|
||||
getAnchorHook() noexcept
|
||||
|
|
@ -94,14 +96,14 @@ namespace model {
|
|||
|
||||
struct Pos
|
||||
{
|
||||
CanvasHook& view;
|
||||
CanvasHook* view;
|
||||
int x,y;
|
||||
};
|
||||
|
||||
Pos
|
||||
hookedAt (int x, int y)
|
||||
{
|
||||
return Pos{*this, x,y};
|
||||
return Pos{this, x,y};
|
||||
}
|
||||
|
||||
/** build the "construction hook" for a \ref ViewHooked element,
|
||||
|
|
@ -118,20 +120,16 @@ namespace model {
|
|||
protected:
|
||||
/** extension point for time axis zoom management. */
|
||||
virtual int translateTimeToPixels (Time) const =0;
|
||||
|
||||
private:
|
||||
/** adapted but also turned unaccessible; use #hook(WID&,int,int) */
|
||||
void hook (WID& w) override { hook (w,0,0); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A widget attached onto a display canvas or similar central presentation context.
|
||||
* This decorator is an extension to the ViewHooked decorator, and thus inherits from
|
||||
* the widget to be attached, i.e. the widget itself becomes embedded; moreover, the attachment
|
||||
* is immediately performed at construction time and managed automatically thereafter. When the
|
||||
* `CanvasHooked` element goes out of scope, it is automatically detached from presentation.
|
||||
* This decorator is a variation of the ViewHooked decorator, and likewise embodies
|
||||
* the widget to be attached; moreover, the attachment is immediately performed at
|
||||
* construction time and managed automatically thereafter. When the `CanvasHooked`
|
||||
* element goes out of scope, it is automatically detached from presentation.
|
||||
* With the help of the CanvasHooked API, a widget (or similar entity) may control the coordinates
|
||||
* of its placement onto some kind of _canvas_ (-> `Gtk::Layout`), while remaining agnostic
|
||||
* regarding any further implementation details of the canvas and its placement thereon.
|
||||
|
|
@ -150,28 +148,33 @@ namespace model {
|
|||
*/
|
||||
template<class WID, class BASE =WID>
|
||||
class CanvasHooked
|
||||
: public ViewHooked<WID,BASE>
|
||||
: public WID
|
||||
, util::NonCopyable
|
||||
{
|
||||
using Hooked = ViewHooked<WID,BASE>;
|
||||
using Canvas = CanvasHook<BASE>;
|
||||
Canvas* view_;
|
||||
|
||||
protected:
|
||||
Canvas&
|
||||
getCanvas() const
|
||||
{
|
||||
REQUIRE (INSTANCEOF(ViewHook<BASE>, &Hooked::getView() ));
|
||||
return static_cast<Canvas&> (Hooked::getView());
|
||||
}
|
||||
Canvas& getCanvas() const { return *view_; }
|
||||
|
||||
public:
|
||||
template<typename...ARGS>
|
||||
CanvasHooked (typename Canvas::Pos attachmentPos, ARGS&& ...args)
|
||||
: ViewHooked<WID,BASE>{attachmentPos.view
|
||||
,std::forward<ARGS>(args)...}
|
||||
: WID{std::forward<ARGS>(args)...}
|
||||
, view_{attachmentPos.view}
|
||||
{
|
||||
getCanvas().hook (*this, attachmentPos.x, attachmentPos.y);
|
||||
}
|
||||
|
||||
~CanvasHooked() noexcept
|
||||
{
|
||||
try {
|
||||
if (view_)
|
||||
view_->remove (*this);
|
||||
}
|
||||
ERROR_LOG_AND_IGNORE (progress, "Detaching of CanvasHooked widgets from the presentation")
|
||||
}
|
||||
|
||||
void
|
||||
moveTo (int xPos, int yPos)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ namespace model {
|
|||
* - a _canvas widget,_ (e.g. `Gtk::Layout`), allowing to attach
|
||||
* child widgets at specific positions, together with custom drawing.
|
||||
* @warning please ensure the ViewHook outlives any attached ViewHooked.
|
||||
* @see CanvasHook extended interface to support positioning by coordinates
|
||||
* @see CanvasHook similar interface to support positioning by coordinates
|
||||
*/
|
||||
template<class WID>
|
||||
class ViewHook
|
||||
|
|
@ -127,10 +127,6 @@ namespace model {
|
|||
using View = ViewHook<BASE>;
|
||||
View* view_;
|
||||
|
||||
protected:
|
||||
View&
|
||||
getView() const { return *view_; }
|
||||
|
||||
public:
|
||||
template<typename...ARGS>
|
||||
ViewHooked (View& view, ARGS&& ...args)
|
||||
|
|
|
|||
|
|
@ -561,12 +561,6 @@ namespace timeline {
|
|||
getCanvas(0).remove (widget);
|
||||
}
|
||||
|
||||
void
|
||||
BodyCanvasWidget::rehook (Gtk::Widget&) noexcept
|
||||
{
|
||||
/* NOOP */
|
||||
}
|
||||
|
||||
void
|
||||
BodyCanvasWidget::move (Gtk::Widget& widget, int xPos, int yPos)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -175,7 +175,6 @@ namespace timeline {
|
|||
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;
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@
|
|||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/nocopy.hpp"
|
||||
#include "stage/model/view-hook.hpp"
|
||||
#include "stage/model/canvas-hook.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
|
|
@ -122,12 +123,6 @@ namespace timeline {
|
|||
refHook_.remove (widget);
|
||||
}
|
||||
|
||||
void
|
||||
rehook (WID& hookedWidget) noexcept override
|
||||
{
|
||||
refHook_.rehook (hookedWidget);
|
||||
}
|
||||
|
||||
/** allow to build a derived ViewRefHook with different offset */
|
||||
model::CanvasHook<WID>&
|
||||
getAnchorHook() noexcept override
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@
|
|||
#include "stage/timeline/header-pane-widget.hpp"
|
||||
#include "stage/timeline/body-canvas-widget.hpp"
|
||||
#include "stage/model/canvas-hook.hpp"
|
||||
#include "stage/model/view-hook.hpp"
|
||||
|
||||
//#include "lib/util.hpp"
|
||||
|
||||
|
|
|
|||
|
|
@ -150,17 +150,6 @@ namespace test {
|
|||
widgets_.remove_if ([&](Attachment const& a) { return a.widget == elm; });
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
rehook (DummyWidget& existingHook) noexcept override
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
protected:
|
||||
int
|
||||
translateTimeToPixels (Time) const override
|
||||
|
|
|
|||
|
|
@ -19213,7 +19213,7 @@
|
|||
<node CREATED="1575581015219" ID="ID_1015965713" MODIFIED="1575581026974" TEXT="für den Widget -> Canvas - Fall"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1576757730509" FOLDED="true" ID="ID_1949130658" MODIFIED="1584932567087" TEXT="»Widget-Position« heraus-abstrahieren">
|
||||
<node COLOR="#338800" CREATED="1576757730509" FOLDED="true" ID="ID_1949130658" MODIFIED="1612002019554" TEXT="»Widget-Position« heraus-abstrahieren">
|
||||
<linktarget COLOR="#47cdcd" DESTINATION="ID_1949130658" ENDARROW="Default" ENDINCLINATION="-1150;77;" ID="Arrow_ID_662295339" SOURCE="ID_730955223" STARTARROW="None" STARTINCLINATION="877;53;"/>
|
||||
<linktarget COLOR="#605bb8" DESTINATION="ID_1949130658" ENDARROW="Default" ENDINCLINATION="204;611;" ID="Arrow_ID_993970158" SOURCE="ID_1816490333" STARTARROW="None" STARTINCLINATION="299;12;"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
|
|
@ -19280,6 +19280,32 @@
|
|||
<font ITALIC="true" NAME="SansSerif" SIZE="12"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1612002421081" ID="ID_704646941" MODIFIED="1612003549568">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
aber dieses Design ist <i>schief</i>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<linktarget COLOR="#ab3058" DESTINATION="ID_704646941" ENDARROW="Default" ENDINCLINATION="434;181;" ID="Arrow_ID_932322384" SOURCE="ID_781800644" STARTARROW="None" STARTINCLINATION="240;20;"/>
|
||||
<icon BUILTIN="broken-line"/>
|
||||
<node CREATED="1612002459090" ID="ID_336676484" MODIFIED="1612002485623" TEXT="und zwar die Code-reuse-Inheritance">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node CREATED="1612002487402" ID="ID_922768406" MODIFIED="1612002504506" TEXT="dadurch sind beide Belange übereinander geschichtet"/>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1612002505382" ID="ID_1693567978" MODIFIED="1612009171119" TEXT="Bug: deshalb wird ein Widget zweimal an den Canvas geheftet">
|
||||
<linktarget COLOR="#c16b96" DESTINATION="ID_1693567978" ENDARROW="Default" ENDINCLINATION="223;491;" ID="Arrow_ID_624678696" SOURCE="ID_1950742827" STARTARROW="None" STARTINCLINATION="1328;54;"/>
|
||||
<icon BUILTIN="broken-line"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1612008761963" ID="ID_1362647591" MODIFIED="1612008774354" TEXT="die beiden Varianten komplett voneinander getrennt">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</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;"/>
|
||||
|
|
@ -19708,6 +19734,37 @@
|
|||
<node CREATED="1582833303639" ID="ID_1136204632" MODIFIED="1582833315633" TEXT="und konvertiert die Referenzen automatisch hoch"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1612003404589" ID="ID_1629772875" MODIFIED="1612008746849" TEXT="Problem Code-reuse-Inheritance">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1612003423875" ID="ID_781800644" MODIFIED="1612003549567">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Ursache ist ein <i>schiefes Design</i>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...und das ist wohl entstanden, weil ich ursprünglich einen generischen Visitor im Blick hatte; es hat sich aber dann gezeigt, daß eine solche universelle "Quer-Beweglichkeit" weder notwendig noch wünschenswert ist
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<arrowlink COLOR="#ab3058" DESTINATION="ID_704646941" ENDARROW="Default" ENDINCLINATION="434;181;" ID="Arrow_ID_932322384" STARTARROW="None" STARTINCLINATION="240;20;"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1612003579934" ID="ID_852410527" MODIFIED="1612008743069" TEXT="Refactoring: ViewHook und CanvasHook komplett trennen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1573242259267" ID="ID_1903570032" MODIFIED="1576166466194" TEXT="Test">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1573242271641" ID="ID_226481127" MODIFIED="1573242355581" TEXT="Basisfall">
|
||||
|
|
@ -28945,8 +29002,7 @@
|
|||
diese Alternative würde dann attraktiv, wenn es häufig vorkommt, daß zwischen einem Clip-Widget und einer anderen Repräsentation des ClipDelegate dynamisch hin- und hergeschaltet werden muß. Weil man dann den relativ schwergewichtigen Datencontainer einfach umhängen könnte
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
|
|||
Loading…
Reference in a new issue