diff --git a/src/stage/model/view-hook.hpp b/src/stage/model/view-hook.hpp index 46abb5b32..0f9434fad 100644 --- a/src/stage/model/view-hook.hpp +++ b/src/stage/model/view-hook.hpp @@ -66,6 +66,9 @@ namespace model { virtual ViewHook hook (ELM& elm, int xPos, int yPos) =0; virtual void move (ELM& elm, int xPos, int yPos) =0; virtual void remove (ELM& elm) noexcept =0; + + template + void reOrder (IT newOrder); }; @@ -93,25 +96,25 @@ namespace model { using View = ViewHookable; ELM* widget_; - View& view_; + View* view_; public: ViewHook (ELM& elm, View& view) : widget_{&elm} - , view_{view} + , view_{&view} { } - ViewHook (ViewHook&& existing) + ViewHook (ViewHook&& existing) noexcept : widget_{nullptr} - , view_{existing.view_} + , view_{nullptr} { - std::swap (widget_, existing.widget_); + swap (*this, existing); } ~ViewHook() noexcept { - if (widget_) - view_.remove (*widget_); + if (widget_ and view_) + view_->remove (*widget_); } ELM& @@ -123,11 +126,29 @@ namespace model { void moveTo (int xPos, int yPos) { - view_.move (*widget_, xPos,yPos); + view_->move (*widget_, xPos,yPos); } + + + friend void + swap (ViewHook& a, ViewHook& b) noexcept + { + std::swap (a.widget_, b.widget_); + std::swap (a.view_, b.view_); + } }; + template + template + void + ViewHookable::reOrder (IT newOrder) + { + UNIMPLEMENTED ("generic mechanism for re-attachment in given order"); + } + + + }}// namespace stage::model #endif /*STAGE_MODEL_VIEW_HOOK_H*/ diff --git a/tests/stage/model/view-hook-test.cpp b/tests/stage/model/view-hook-test.cpp index 70866c099..8cae5d8f1 100644 --- a/tests/stage/model/view-hook-test.cpp +++ b/tests/stage/model/view-hook-test.cpp @@ -29,21 +29,26 @@ #include "lib/test/test-helper.hpp" #include "stage/model/view-hook.hpp" #include "lib/iter-tree-explorer.hpp" +#include "lib/iter-adapter-stl.hpp" #include "lib/util.hpp" //#include //#include -#include #include +#include +#include +#include -using util::isSameObject; using util::contains; +using util::isSameObject; +using lib::iter_stl::eachElm; //using util::isnil; //using std::make_unique; //using std::move; using std::vector; using std::forward_list; +using std::shuffle; namespace stage{ @@ -105,7 +110,7 @@ namespace test { } public: - + /* === diagnostic functions for the test === */ bool empty() const { @@ -128,6 +133,13 @@ namespace test { and pos->posY == y_expected; } + template + bool + testContainsSequence (CON const& refSeq) + { + UNIMPLEMENTED("compare our internal sequence with the given one"); + } + /* === Interface ViewHookable === */ @@ -176,6 +188,7 @@ namespace test { { verify_standardUsage(); relocateWidget(); + reOrderHooked(); } @@ -224,6 +237,33 @@ namespace test { } + /** @test a mechanism to re-attach elements in changed order. + */ + void + reOrderHooked() + { + using WidgetVec = vector; + using HookVec = vector; + + // create 20 (random) widgets and hook them onto the canvas + WidgetVec widgets{20}; + FakeCanvas canvas; + HookVec hooks; + for (auto& w : widgets) + hooks.emplace_back (canvas.hook (w, w.x,w.y)); + + CHECK (canvas.testContainsSequence (hooks)); + + // now lets assume the relevant order of the widgets has been altered + shuffle (hooks.begin(),hooks.end(), std::random_device()); + CHECK (not canvas.testContainsSequence (hooks)); + + // so we need to re-construct the canvas attachments in the new order + canvas.reOrder (eachElm (hooks)); + CHECK (canvas.testContainsSequence (hooks)); + } + + /** @test */ }; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index eddbf0fdb..18aab0f8d 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -2257,7 +2257,7 @@ It would thus be desirable to have a fixed-sized allocation, able to hold the pl !!!Storage Explicit placements are value objects and stored at the respective usage site, most notably the [[Segmentation]]. They are //not// attached to the placement index in the session, nor do they bear any referential or indexing semantics. The only dynamic side effect of an explicit placement is to keep the reference count of the corresponding MObject up and thus keep it alive and accessible. -
+
//to symbolically represent hierarchically structured elements, without actually implementing them.//
 The purpose of this »external« description is to remove the need of a central data model to work against. We consider such a foundation data model as a good starting point, yet harmful for the evolution of any larger structure to be built. According to the subsidiarity principle, we prefer to turn the working data representation into a local concern. Which leaves us with the issue of collaboration. Any collaboration requires, as an underlying, some kind of common understanding. Yet any formalised, mechanical collaboration requires to represent that common point of attachment -- at least as a symbolic representation. The ExternalTreeDescription is shaped to fulfil this need: //in theory,// the whole field could be represented, symbolically, as a network of hierarchically structured elements. Yet, //in practice,// all we need is to conceive the presence of such a representation, as a backdrop to work against. And we do so -- we work against that symbolic representation, by describing ''changes'' made to the structure and its elements. Thus, the description of changes, the ''diff language'', refers to and partially embodies such symbolically represented elements and relations.
 → TreeDiffFundamentals
@@ -2287,6 +2287,7 @@ Basically, you can expect to encounter the following kinds of data
 The last option is what makes our representation recursive.
 Regarding the implementation, all these data elements are embedded //inline,// as values.
 With the exception of the record, which, like any {{{std::vector}}} implicitly uses heap allocations for the members of the collection.
+We consider this memory layout an implementation detail, which could be changed without affecting the API
 
 !{{red{open questions 5/2015}}}
 !!!the monadic nature of records
@@ -3524,7 +3525,7 @@ In accordance with the Lumiera application architecture in general, the UI is no
 
A specially configured LumieraPlugin, which actually contains or loads the complete code of the (GTK)GUI, and additionally is linked dynamically against the application core lib. During the [[UI startup process|GuiStart]], loading of this Plugin is triggered from {{{main()}}}. Actually this causes spawning of the GTK event thread and execution of the GTK main loop.
 
-
+
The presentation of the track body area relies on the [[Gtk::Layout "canvas widget"|GtkLayoutWidget]], thus allowing for a mixture of custom drawing with embedded custom Gtk widgets. The actual drawing routine is activated in response to the {{{on_draw}}} signal -- and invoking the inherited handler function will initiate the standard drawing for the embedded child widgets. This partitions the additional, specific drawing activities into a pre-widget drawing phase to prepare the background and framework structure of the track area, and a post-widget drawing phase to show all kinds of overlays, markers cursors and similar UI indicators. A nested structure of {{{TrackBody}}} objects serves as organisational device to structure these custom drawing activities in accordance with the nested structure of the track fork.
 
 !Building a nested 3D structure
@@ -3571,9 +3572,10 @@ Another consequence  is that we do not need to apply an overall "stacked bo
 *:* while {{{margin}}} and {{{padding}}} apply as usual outside and within the border
 *:* here the {{{box shadow}}} will be drawn with the background and aligned with the frame -- and again, only an {{{inset}}} really makes sense,<br/>while a regular (outer) box shadow must be small enough to stick within the limits of the {{{margin}}}
 *;slopes
-*:we use several classes to define the effect of consecutive nested frames forming a common slope; however, we perform this combination effect only 5 steps deep
+*:we use several classes to define the effect of consecutive nested frames forming a common slope; however,
+*:we perform this combination effect only with a limit of 5 steps depth. Only closing (upward) slopes can be combined.
 *:* we use class {{{.track-slope--deep1}}} for a simple slope (inset by one nested level)
-*:* class {{{.track-slope--deep2}}} for two consecutive nested steps combined. Only closing (upward) slopes can be combined.
+*:* class {{{.track-slope--deep2}}} for two consecutive nested steps combined.
 *:* likewise class {{{.track-slope--deep3}}} for three and class {{{.track-slope--deep4}}} for four combined slopes
 *:* finally, class {{{.track-slope--verydeep}}} is used for five and more combined upward (closing) slopes
 
@@ -9266,7 +9268,7 @@ Placements are __resolved__ resulting in an ExplicitPlacement. In most cases thi
 &rarr; [[Definition|Pipe]] and [[handling of Pipes|PipeHandling]]
 
-
+
//mediating entity used to guide and control the track-like nested working space in the timeline display of the UI.//
 
 Similar to the ClipPresenter, judged from a global angle, this element fulfils a model-like role, while at the same time guiding and controlling a mostly passive view component, implemented as GTK widget. Here, the authority of the presenter over the widget must be total, since display management //needs to work automatically,// due to model updates and mutations arriving as [[diff messages|MutationMessage]]. In addition, this structure is prerequisite for (possibly) implementing UI rendering optimisations, since it allows us to leave out widgets entirely, when it is clear they won't become visible: A ''display evaluation pass'', which is effectively a //tree walk,// consecutively visits each part of the timeline structure, to negotiate its concrete display properties in collaboration with a global TimelineDisplayManager. As a result, the presenter knows where to show its corresponding view, and it knows if to show it at all, allowing to either adjust, create or destroy actual GTK widgets within its local reference frame.
@@ -9282,7 +9284,7 @@ To deal with this typical problem of recursive programming, we introduce a bindi
 !Organisation of track profile and rulers
 Related to the problem of building the nested track structure is the problem of proper graphical representation of this nested structure -- especially within the right pane, //the body part,// where the actual track content will be shown. Due to various limitations of typical UI frameworks, we prefer to handle this body part by [[custom drawing|GuiCustomWidget]] onto a canvas widget. Which places us into the situation to generate suitable graphical elements based on the effective nested structure of the tracks. We solve this challenge by performing an //evaluation pass// over the actual {{{TrackBody}}} structure, which is built starting from the {{{DisplayFrame}}} for each track. The {{{TrackBody}}} elements are helper objects, and serve the sole purpose of getting this drawing and display control requirements settled; basically they mirror the track structure, and the corresponding structure of GTK widgets in the header pane, and they likewise form a tree structure, which can be traversed to collect information. Which is what we, for building up a "track profile", which can then later be &rarr; [[rendered consistently onto the body canvas|GuiTimelineDraw]].
 
-Another related concern are the [[track rulers|TrackRuler]]. These are overview panes running alongside the track time axis. As UI model entities, they are attached to the corresponding {{{TrackBody}}} element, while collaborating with the {{{TrackPresenter}}} to retrieve the actual content to be shown in the overview. The purpose of track rulers is to show effects or selection states; and especially in the top-level track the ''time ruler'' indicates the current time and frame position.
+Another related concern are the [[track rulers|TrackRuler]]. These are overview panes running alongside the track time axis. As UI model entities, they are attached to the corresponding {{{TrackBody}}} element, while collaborating with the {{{TrackPresenter}}} to retrieve the actual content to be shown in the overview. The purpose of track rulers is to show effects, aggregation marks or selection states; and especially in the top-level track the ''time ruler'' indicates the current time and frame position.
 
@@ -9538,7 +9540,7 @@ On receiving a stream of tokens of this "diff language", it is possibl i.e. a ''unified diff'' or the ''predicate notation'' used above to describe the list diffing algorithm, just by accumulating changes.
-
+
The TreeMutator is an intermediary to translate a generic structure pattern into heterogeneous local invocation sequences.
 within the [[diff framework|TreeDiffModel]], this is a crucial joint, since here the abstract, generic, ~DOM-like ExternalTreeDescription meeds opaque, local and undisclosed data structures.
 
@@ -9685,11 +9687,12 @@ attached to a clip, or the mixture of clips, effects and labels found within a [
 :obviously, this unified implementation comes at the price of one additional indirection (namely through the ~TreeMutator ~VTable), while the requirements on temporary storage size are almost identical (two iterators and two back references instead of one). Moreover, all the explicitly coded complexities regarding the attribute / children distinction is gone, relying on the logic of layered decorators solely. So the effort to shape the TreeMutator API payed off in the end, to improve and rectify the whole design...
 
 
-You should note that this entire design is recursive: only after understanding the part, where, for handling a sub-structure, a nested mutator is fabricated and placed into a given buffer, it becomes clear to what effect we're creating a customised mutator: we always need some (relative) parent scope, which happens to know more about the actual data to be treated with a TreeMutator. This scope assists with creating a suitable binding. Obviously, from within that binding, it is known what the sub-structures and children of that local data are all about and what semantics to give to the fundamental operations.
+You might have noticed already that this entire design is recursive: there is some part, where -- for handling a sub-structure -- a nested mutator is fabricated and placed into a given buffer. And only after understanding that part, it becomes clear to what end we are creating a customised mutator: we always need some (relative) parent scope, which happens to know more about the actual data to be treated with a TreeMutator. This scope assists with creating a suitable binding. Obviously, from within that binding, it is known what the sub-structures and children of that local data are all about and what semantics to give to the fundamental operations.
 
-
+
The TreeMutator is an intermediary to translate a generic structure pattern into heterogeneous local invocation sequences.
+This page details some of the reasoning and documents decisions taken while shaping the chosen design.
 
 !Motivation
 In the context of Lumiera's metadata model and data exchange framework, we're facing a quite characteristic problem of generic programming: While in a classical ~OO-solution, a problem is defined by imposing distinct structural and typing constraints, the very idea of generic programming is to treat heterogeneous structures. More specifically, the whole approach of collaboration between loosely coupled components through the exchange of structural diff messages was chosen to subsume a wide array of individual structures under a model largely conceptual in nature. Without the need of a »master model« to be defined up-front. Without the danger of obsoleting this master plan in the course of discovering the actual specifics of implementation.
@@ -9833,11 +9836,15 @@ We have a distinct typing for each individual attribute, but this typing remains
 
 !!!policy to deal with non optional object fields
 It turns out we're frequently forced to make a problematic decision here: in practice we often have the choice to treat a field as optional or mandatory. The diff protocol demands to send all mandatory fields immediately, with the {{{INS}}}  verb causing object creation, but, generally speaking, both cases can be supported. Now, what makes this decision tough is the fact it seems way more cheap to cheat our way around this decision: we may implement the fields as being optional, but actually treat them as being mandatory. And we just hope the diff will always provide the necessary values with further {{{INS}}} verbs right within the first population diff. Judging by widely accepted principles of pragmatism, this way to deal with the problem is even the most sensible way. Incidentally, this also relates to the pervasive use of property setters and getters in typical object based programming.
-Unfortunately, there is a price to pay on the long run. This fudge tends to spread in neighbouring code, and, over time, what is //practically irrelevant// tends to happen non the less, causing fixes and complexities to creep into the code. A good guideline thus is not to base this decision on practicality, but to base it on the nature of those matters to be represented in objects: is something //by its own nature is mandatory,// then represent it as mandatory. If something is optional, maybe try to decompose to get rid of it altogether.
+Unfortunately, there is a price to pay on the long run. Taking this approach creates fudge, and this fudge tends to spread into neighbouring code, and even worse, over time, what is deemed //practically irrelevant// tends to happen non the less, sooner or later, causing fixes and complexities to creep into the code. A good guideline thus is not to base this decision on practicality, but to base it on the nature of those matters to be represented in objects: when something //by its own nature is mandatory,// then we should take the necessary steps to //represent it as mandatory.// If something is optional, maybe we can even try to decompose to get rid of it altogether.
+
+So we arrive a the following guidelines:
+* what is essential to the concept represented with an object should be initialised in the ctor and thus needs to be sent with a single {{{INS}}} verb.
+* optional and configurable parts need a setter or similar mechanism, and can then be populated or manipulated through an //attribute binding.//
 
 
 !!!types, attributes and layering
-During the analysis it turned out that support for a mixed set of //typed child elements// is an important goal. However, we may impose the restriction that these typed sub collections will be kept segregated and not be mixed up within the child scope. Effectively, this was already the fundamental trait for the //symbolic object representation// chosen here. Thus, "attributes" become just one further kind of typed children; even "metadata" might be treated along these lines in future (currently as of 2016 we have only a type field in {{{diff::Record}}}, which is treated explicitly).
+During the analysis it turned out that support for a mixed set of //typed child elements// is an important goal. However, we may impose the restriction that these typed sub collections will be kept segregated and not be mixed up within the child scope. Effectively, this was already the fundamental trait for the //symbolic object representation// chosen here. Thus, "attributes" become just one further kind of typed children; even "metadata" might be treated along these lines in future (currently, as of 2016 we have only one single piece of metadata, the type field in {{{diff::Record}}}, which is treated explicitly in the code).
 This observation leads directly to an implementation based on layered decorators. Each kind of target elements will be treated by another decorator exclusively, and control is passed through a chain of such decorators. This means, each decorator (or "onion-layer") gets into charge based on a //selector predicate,// which works on the given diff verb solely (no introspection and no "peeking" into the implementation data). We should note though, that this implies some fine points of the diff language semantics are subject to this architectural decision -- especially the precise behaviour of the {{{AFTER}}}-verb depends on the //concrete layer structure// used at the target of the diff. Yet this seems acceptable, since it is the very nature of diff application to be dependent on internals of the target location -- just by definition we assume this inner structure of the target to be semantically congruent with the diff's source structure.
 
 
@@ -9854,9 +9861,9 @@ Right now it looks sensible to start with the simplistic approach, while keeping
 !!!implementation structure and invocation
 To receive and apply a diff, client code has to build a {{{DiffApplicator<TreeMutator>}}} and somehow attach this to the target data structure. Then, after feeding the diff sequence, the target data structure will be transformed. This leads to kind of a chicken-and-egg problem, since only the target data structure has the ability to know how to fabricate a suitable concrete TreeMutator. Assuming the //size problem// (as detailed above) is already solved one way or the other, we're bound to use an interface where the target structure is prompted to build a suitable mutator into a provided buffer or storage frame. Typically, we'd either use metaprogramming or some ADL extension point to derive a suitable //mutator building closure// from the given target structure. In fact, the same situation repeats itself recursively when it comes to mutating nested scopes, but in that case, the necessary //mutator building closure// is already part of the mutator binding created for the respective parent scope, and thus can just be invoked right away.
 
-The {{{DiffApplicator}}} itself can be made non-copyable; preferably it should be small and cheap to construct. At least we should strive at keeping the number of indirections and heap allocations low. Together with the requirement to separate from the actual TreeMutator implementation, which is built late, just before invocation, we arrive at a ~PImpl structure, with the TreeMutator as ~PImpl and a scope manager hidden within the implementation part; the latter will be responsible for creating a suitable stack, and to use the //mutator building closure// in order to incorporate the concrete TreeMutator into the stack frames.
+The {{{DiffApplicator}}} itself can be made non-copyable; preferably it should be small and cheap to construct. At least we should strive at keeping the number of indirections and heap allocations low. Together with the requirement to keep it separate from the actual TreeMutator implementation, which is built //late, just before invocation,// we arrive at a ~PImpl structure, with the TreeMutator as ~PImpl and a scope manager hidden within the implementation part; the latter will be responsible for creating a suitable stack, and to use the //mutator building closure// in order to incorporate the concrete TreeMutator into the stack frames.
 
-Pulling all those building blocks together, this architecture finally allows us to offer a (tree) diff-applicator for //»basically anything«:// The ctor respective the init method of {{{DiffAplicator<Something>}}} relies on the metaprogramming or ADL technique embedded within the {{{TreeDiffTraits<Something>}}} to //somehow// get at a {{{DiffMutable&}}} -- the latter offering the crucial method to build a TreeMutator implementation internally wired in a proper way to {{{Something}}}'s internals. This in turn enables us to build a suitalbe {{{ScopeManager}}} implementation with a stack size sufficient to hold this TreeMutator implementation. After this type specific setup, the rest of the diff application works entirely generic and can thus be delegated to the non-templated baseclass {{{TreeDiffMutatorBinding}}}...
+Pulling all those building blocks together, this architecture finally allows us to offer a (tree) diff-applicator for //»basically anything«:// The ctor respective the init method of {{{DiffAplicator<Something>}}} relies on the metaprogramming or ADL technique embedded within the {{{TreeDiffTraits<Something>}}} to //somehow// get at a {{{DiffMutable&}}} -- the latter offers the one central and crucial method to build a TreeMutator implementation, internally wired in a proper way to {{{Something}}}'s internals. Which in turn enables us to build a suitalbe {{{ScopeManager}}} implementation with a stack size sufficient to hold this TreeMutator implementation. After this type specific setup, the rest of the diff application works entirely generic and can thus be delegated to the non-templated baseclass {{{TreeDiffMutatorBinding}}}...
 
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 3651974d2..d628dd49e 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -19332,7 +19332,7 @@ - + @@ -19360,7 +19360,7 @@ - + @@ -19393,8 +19393,8 @@ - - + + @@ -19426,7 +19426,7 @@ - + @@ -19486,12 +19486,32 @@ - + + + + + + +

+ ...hab mich davon überzeugt, daß die Namen anders herum verwendet werden sollten. +

+

+ Das hängt auch damit zusammen, daß in der Praxsis die urspünglich konzipierte »Beziehungs-Entität« niemals eigens und eigenständig auftretend wird; vielmehr bekommen wir es mit einem speziellen Dekorator zu tun. Und dieser wird besser ViewHooked<X> heißen. Damit ist das bisher schon verwirrend benannte "ViewHookable" komplett daneben, und es erscheint viel sinnvoller, diesem den titelgebenenden Namen ViewHook  zu zuzuordnen +

+ + +
- + + + + + + + @@ -19503,8 +19523,8 @@ - - + + @@ -21283,12 +21303,40 @@ - + - + + + + + + + + + + +

+ denn er erzwingt entweder.... +

+
    +
  • + die relevante Implementierung in einm einzigen Objekt zu konzentrieren -- was dann den double-dispatch überflüssig macht, denn dann kann man direkt die bekannte andere Methode aufrufen +
  • +
  • + oder, wenn man die verschiedenen Belange auf getrennte Objekte verteilt, müssen sich diese gegenseitig erreichen können, und die hierfür notwendigen Rückreferenzen untergraben die Trennung +
  • +
+ + +
+ +
+ + + @@ -21345,7 +21393,7 @@ - + @@ -21357,7 +21405,7 @@ - +