diff --git a/src/common/ui-protocol.cpp b/src/common/ui-protocol.cpp index 4dfc4b13a..f0041692e 100644 --- a/src/common/ui-protocol.cpp +++ b/src/common/ui-protocol.cpp @@ -41,6 +41,7 @@ namespace gui { const Symbol ATTR_name{"name"}; const Symbol ATTR_fork{"fork"}; + const Symbol ATTR_assets{"assets"}; /* ======== UI-Element protocol ======== */ diff --git a/src/gui/interact/interaction-director.cpp b/src/gui/interact/interaction-director.cpp index 0c272b6e1..bf7cdd92a 100644 --- a/src/gui/interact/interaction-director.cpp +++ b/src/gui/interact/interaction-director.cpp @@ -42,6 +42,7 @@ #include "gui/ctrl/ui-state.hpp" #include "gui/setting/asset-controller.hpp" #include "gui/timeline/timeline-controller.hpp" +#include "include/ui-protocol.hpp" #include "proc/mobject/session/root.hpp" #include "proc/asset/sequence.hpp" ///////////////////////////////////////////////////////////TICKET #1096 : avoid direct inclusion to reduce compile times #include "proc/mobject/session/fork.hpp" ///////////////////////////////////////////////////////////TICKET #1096 : avoid direct inclusion to reduce compile times @@ -61,6 +62,8 @@ using lib::idi::EntryID; using lib::hash::LuidH; using lib::diff::TreeMutator; +using lib::diff::collection; +using std::make_unique; using util::toString; @@ -128,8 +131,30 @@ namespace interact { { buffer.create ( TreeMutator::build() - ); - unimplemented ("create a sensible binding between root-controller and root-model element"); + .attach (collection(timelines_) + .isApplicableIf ([&](GenNode const& spec) -> bool + { // »Selector« : require object-like sub scope + return spec.data.isNested(); + }) + .matchElement ([&](GenNode const& spec, PTimelineCtrl const& elm) -> bool + { // »Matcher« : how to know we're dealing with the right timeline object + return spec.idi == ID(elm); + }) + .constructFrom ([&](GenNode const& spec) -> PTimelineCtrl + { // »Constructor« : what to do when the diff mentions a new entity + return injectTimeline (spec); + }) + .buildChildMutator ([&](PTimelineCtrl& targetTimeline, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool + { // »Mutator« : how to apply the diff recursively to a nested scope + if (ID(targetTimeline) != subID) return false; + targetTimeline->buildMutator (buff); // - delegate to child(Timeline) to build nested TreeMutator + return true; + })) + .mutateAttrib(ATTR_fork, [&](TreeMutator::Handle buff) + { // »Attribute Mutator« : how enter an object field as nested scope + REQUIRE (assets_); + assets_->buildMutator(buff); + })); } @@ -280,6 +305,15 @@ namespace interact { { return globalCtx_.windowLoc_.findActiveWindow(); } + + + /** @internal allocate a new TimelineWidget and attach it as child */ + InteractionDirector::PTimelineCtrl + InteractionDirector::injectTimeline (GenNode const& spec) + { + unimplemented ("allocate a TimelineWidget in some TimelinePanel and attach it's controller as child entity"); + } + diff --git a/src/gui/interact/interaction-director.hpp b/src/gui/interact/interaction-director.hpp index d6d22d076..6a82f43ed 100644 --- a/src/gui/interact/interaction-director.hpp +++ b/src/gui/interact/interaction-director.hpp @@ -80,6 +80,7 @@ namespace workspace { } namespace interact { + using lib::diff::GenNode; //using std::string; using std::unique_ptr; @@ -113,7 +114,8 @@ namespace interact { unique_ptr tracker_; // == Model globals == - using Timelines = std::vector>; + using PTimelineCtrl = unique_ptr; + using Timelines = std::vector; using Assets = unique_ptr; using State = unique_ptr; @@ -150,6 +152,7 @@ namespace interact { private: workspace::WorkspaceWindow& getWorkspaceWindow(); + PTimelineCtrl injectTimeline (GenNode const&); }; diff --git a/src/gui/setting/asset-controller.hpp b/src/gui/setting/asset-controller.hpp index fce280526..d4e6ad67e 100644 --- a/src/gui/setting/asset-controller.hpp +++ b/src/gui/setting/asset-controller.hpp @@ -77,14 +77,14 @@ namespace setting { : public model::Controller { ///////TODO create UI representation for the asset subsections - - /** content population and manipulation via UI-Bus */ - void buildMutator (lib::diff::TreeMutator::Handle) override; public: AssetController (ID identity, ctrl::BusTerm& nexus); ~AssetController(); + /** content population and manipulation via UI-Bus */ + void buildMutator (lib::diff::TreeMutator::Handle) override; + private: }; diff --git a/src/include/ui-protocol.hpp b/src/include/ui-protocol.hpp index 999e95c27..6aa5cdb4a 100644 --- a/src/include/ui-protocol.hpp +++ b/src/include/ui-protocol.hpp @@ -74,6 +74,7 @@ namespace gui { extern const Symbol ATTR_name; extern const Symbol ATTR_fork; + extern const Symbol ATTR_assets; /* ======== UI-Element protocol ======== */ diff --git a/wiki/renderengine.html b/wiki/renderengine.html index a6a6f8ceb..02deabddb 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -2082,6 +2082,10 @@ The Method of pushing frames to the Viewer widget is changed fundamentally durin +
+
//A self referential intermediary, used to collect and consolidate structural information about some element.//
+This structural information can then be emanated as ''population diff'' and is thus the foundation to build a further representation of the same structure //"elsewhere".// This //further representation// could be a model entity within the session's HighLevelModel, or it could be a widget tree in the UI. And, to close the circle, this other representation resulting from that diff could in turn again be a DiffConstituent.
+
The Dispatcher Tables are hosted within the [[Segmentation]] and serve as a ''strategy'' governing the play process.
 For each [[calculation stream|CalcStream]] there is a concrete implementation of the [[dispatcher|FrameDispatcher]] interface, based on a specific configuration of dispatcher tables.
@@ -3243,7 +3247,7 @@ More specifically, the integration is based on ''messaging''. To start with, the ---- In a preliminary attempt to establish an integration between the GUI and the lower layers, in 1/2009 we created an PlayerDummy, which "pulls" dummy frames from the (not yet existing) engine and displays them within an XV viewer widget. This highlighted the problems we're about to encounter and made us think about the more radically decoupled approach we followed thereafter...
-
+
Building a layered architecture is a challenge, since the lower layer //really// needs to be self-contained, while prepared for usage by the higher layer.
 A major fraction of all desktop applications is written in a way where operational logic is built around the invocation from UI events -- what should be a shell turns into a backbone. One possible way to escape from this common anti pattern is to introduce a mediating entity, to translate between two partially incompatible demands and concerns: Sure, the "tangible stuff" is what matters, but you can not build any significant piece of technology if all you want is to "serve" the user.
 
@@ -3258,6 +3262,11 @@ The whole Idea to have a "UI model" appears more questionable than eve
 * {{red{as of 8/2018}}}, matters on a large scale converge towards a structure //without a classical UI model.// Seemingly we can do without...
 * there is a working demonstration in {{{BusTerm_test}}}, which pushes mutation diffs against a mock UI-Element. The immediate response to receiving such a diff notification via the UI-Bus has now been coded; it incurs invoking a passed callback (functor), which performs within the originating context, but within the ~UI-Thread, and produces the actual diff //on-demand.// ("pull principle")
 
+!!!building the model structures
+A fundamental decision within the Lumiera UI is to build every model-like structure as immediate response to receiving a diff message pushed up into the UI.
+* either this happens when some change occured, which is directly reflected into the UI by a local diff
+* or a whole subtree of elements is built up step wise in response to a ''population diff''. This is an systematic description of a complete sub-structure in current shape, and is produced as emanation from a DiffConstituent.
+
 !synchronisation guarantees
 We acknowledge that the gui model is typically used from within the GUI event dispatch thread. This is //not// the thread where any session state is mutated. Thus it is the responsibility of this proxying model within the GUI to ensure that the retrieved structure is a coherent snapshot of the session state. Especially the {{{gui::model::SessionFacade}}} ensures that there was a read barrier between the state retrieval and any preceding mutation command. Actually, this is implemented down in Proc-Layer, with the help of the ProcDispatcher.
 
@@ -3425,7 +3434,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.
 
-
+
Within the Lumieara GUI, the [[Timeline]] structure(s) from the HighLevelModel are arranged and presented according to the following principles and conventions.
 Several timeline views may be present at the same time -- and there is not necessarily a relation between them, since »a Timeline« is the top-level concept within the [[Session]]. Obviously, there can also be several //views// based on the same »Timeline« model element, and in this latter case, these //coupled views// behave according to a linked common state. An entity »Timeline« as represented through the GUI, emerges from the combination of several model elements
 * a root level [[Binding|BindingMO]] acts as framework
@@ -3459,7 +3468,7 @@ This collapsed, expanded and possibly nested workspace structure is always exact
 !!!lifecycle and instances
 A given instance of the {{{TimelineWidget}}} is always dedicated to render the contents of //one specific timeline.// We never switch the data model while retaining the UI entities. This also means, a given instance is tied to one conversation with the core; it is created when the core tells us about this timeline with an initial population diff, and it lives until either this timeline is discarded in the core model, or the whole session is shut down.
 
-The dockable ''timeline pannel'' holds onto the existing {{{TimelineWidget}}} instances, allowing to make one of them visible for interaction. Yet those instances are managed by the InteractionDirector, who incorporates the role of representing the [[model root|ModelRootMO]]. Aside of the timeline display, there is also an ''asset pannel''; display of and interaction with those asset views is handled by dedicated widgets, backed by the {{{AssetControler}}} -- which is also a child of and managed by the InteractionDirector, since assets are modelled as global part of the [[Session]].
+The dockable ''timeline pannel'' holds onto the existing {{{TimelineWidget}}} instances, allowing to make one of them visible for interaction. Yet the timeline itself is represented by the {{{TimelineControler}}}, which lives //within this widget,// and is attached to and managed by the InteractionDirector, who incorporates the role of representing the [[model root|ModelRootMO]]. Aside of the timeline display, there is also an ''asset pannel''; display of and interaction with those asset views is handled by dedicated widgets, backed by the {{{AssetControler}}} -- which is also a child of and managed by the InteractionDirector, since assets are modelled as global part of the [[Session]].
 
 In case the UI starts with no session present in the core, an //empty timeline placeholder// will be displayed, which provides UI for creating a new session...
 
@@ -3471,7 +3480,7 @@ In the most general case, there can be per-track content and nested content at t
 → important question: how to [[organise the widgets|GuiTimelineWidgetStructure]]
 
-
+
The Timeline is probably the most prominent place in the GUI where we need to come up with a custom UI design.
 Instead of combining standard components in one of the well-known ways, here we need to come up with our own handling solution -- which also means to write one or several custom GTK widgets. Thus the question of layout and screen space division and organisation becomes a crucial design decision. The ~GTK-2 Gui, as implemented currently, did already take some steps along this route, yet this kind of decision should be cast and documented explicitly (be it after the fact).
 
@@ -3510,6 +3519,12 @@ Here, the "component" relevant for such structural changes is always t
 
 From these observations we can draw the conclusion, that we'll build a ''local structural model'', to reflect the logical relations between the parts comprising the timeline display. More precisely, these structuring components are not mere model objects, rather they are mediating entities used to guide and organise the actual view entities, which in turn are passive. They are more like a view model, while also bearing some local controller responsibilities. For this reason, we prefer to term these as ''presenters'' -- i.e. TrackPresenter and ClipPresenter. And each of these local representation components holds onto a display context, which generally links it into two different display widget stacks in the two parts of the actual timeline display. Adding a child component thus becomes a rather tricky operation, involving to link possibly two child widgets into two disjoint parent widgets, thereby forming a similar display context for the child presenter. Overall, the guiding idea is that of self similarity: on each level, we have to reproduce the same relations and collaborations as present in the parent level.
 
+!!!building the timeline representation structure
+It is a fundamental decision within the Lumiera UI that any structure as to be injected as diff by the core. We do not hold a structure or data model within some UI entity and then "interpret" that model into a widget structure -- rather we build the widget structure itself in response to a diff message describing the structure. Especially in the case of timelines, the receiver of those messages is the InteractionDirector, which corresponds to the model root. On being prompted to set up a timeline, it will allocate a {{{TimelineWidget}}} within a suitable timeline docking panel and then attach to the {{{TimelineController}}} embedded within the widget.
+{{red{Problem 10/2018}}} how can the InteractionDirector //manage// a timeline while the timeline widget physically resides within the panel? Can we exploit a simliar structure as we did for the error log?
+
+The diff describing and thus assembling the UI representation of a timeline is typically a ''population diff'' -- which means, it is a consolidated complete description of the whole sub-structure rooted below that timeline. Such a population diff is generated as emanation from the respective DiffConstituent.
+
 !!!interplay with diff mutation
 Applying a diff changes the structure, that is, the structure of the local model, not the structure of the display widgets. Because the latter are an entirely private concern of the UI and their structure is controlled by the model components in conjunction with the display manager. And since diff application effects the contents of the model such as to make the intended structural changes happen (indirectly), we are well advised to tie the display control and the widgets very closely to those local model elements, such as to //adjust the display automatically.//
 * when a new model element is added, it has to inject something automatically into the display
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index e77f228e0..0f2363833 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -45,7 +45,7 @@
 
 
 
-
+
 
 
 
@@ -5246,6 +5246,10 @@
 
 
 
+
+
+
+
 
 
 
@@ -5256,7 +5260,7 @@
 
 
 
-
+
 
 
 
@@ -16475,7 +16479,62 @@
 
 
 
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  
+    
+  
+  
+    

+ how can the InteractionDirector manage a timeline +

+

+  while the timeline widget physically resides within the panel? +

+ + +
+ + + + + +

+ Can we exploit a simliar structure as we did for the error log? +

+ + +
+ +
+
+
+
@@ -19857,12 +19916,103 @@ - + + + + + + + + + + + + +
    +
  • + Komponente hat eine interne Struktur-Repräsentation +
  • +
  • + traversiert diese Struktur-Beschreibung +
  • +
  • + und übersetzt sie in UI-Widgets +
  • +
+ + +
+ +
+ + + + + + +
    +
  • + jedes Teilelement wird per Push injiziert +
  • +
  • + es wird in dem Moment sofort und ausschließlich als Widget realisiert +
  • +
+ + +
+ +
+
+ + + + + + + + + + + + + + + + +

+ Basis: DiffConstituent +

+ + +
+ + + + + +
    +
  • + ein selbstbezüglicher substanzloser Strukturkern +
  • +
  • + DiffConstituent -> emanation as diff +
  • +
+ + +
+ + + + + + @@ -34875,7 +35025,19 @@
- + + + + + + + + + + + + + @@ -37790,7 +37952,7 @@ - +