extended planning to define the operation of UI-Bus and model update

this includes a decision about the tree diff representation and handling format
This commit is contained in:
Fischlurch 2015-01-17 16:08:56 +01:00
parent 28d18a7326
commit 9a9e17578c
10 changed files with 118 additions and 52 deletions

View file

@ -128,8 +128,9 @@ namespace lumiera {
* type of object. Registering such a TypeHandler should create * type of object. Registering such a TypeHandler should create
* the necessary handler functions to be installed into * the necessary handler functions to be installed into
* the Prolog system. * the Prolog system.
* @todo it can't be done exactly this way, but I leave it in * @deprecated it can't be done exactly this way, but I leave it in the
* as a reminder for later, to show the intention * current shape as a reminder for later, to show the intention...
* @todo 6/2010 unify this with the TypeHandler in typed-id.hpp
*/ */
template<class TY> template<class TY>
class TypeHandler class TypeHandler

View file

@ -61,6 +61,7 @@ namespace asset {
* By virtue of the Category, Assets can be organised in nested bins (folders). * By virtue of the Category, Assets can be organised in nested bins (folders).
* This includes the distinction of different kinds of Assets, like Audio, Video, Effects... * This includes the distinction of different kinds of Assets, like Audio, Video, Effects...
* *
* @remark the path in the tree constitutes a type classification scheme
* @todo could be far more elaborate. could be a singleton like centralised tree, while * @todo could be far more elaborate. could be a singleton like centralised tree, while
* just holding references to Category nodes in the individual Asset. At the moment, * just holding references to Category nodes in the individual Asset. At the moment,
* we just use the most simplistic implementation and handle Category objects * we just use the most simplistic implementation and handle Category objects

View file

@ -102,8 +102,8 @@ namespace asset {
class StructFactoryImpl class StructFactoryImpl
{ {
/** @internal derive a sensible asset ident tuple when creating /** @internal derive a sensible asset ident tuple when creating
* structural asset instances based on a capability query * structural asset instances based on a capability query
*/ */
template<class STRU> template<class STRU>
const Asset::Ident const Asset::Ident
@ -176,8 +176,8 @@ namespace asset {
template<class STRU> template<class STRU>
STRU* fabricate (Query<STRU> const& caps) STRU* fabricate (Query<STRU> const& caps)
{ {
throw error::Config ("The following Query could not be resolved: " + caps.asKey() throw error::Config ("The following Query could not be resolved: " + caps.asKey()
, LUMIERA_ERROR_CAPABILITY_QUERY ); , LUMIERA_ERROR_CAPABILITY_QUERY );
} }
}; };

View file

@ -99,7 +99,7 @@ namespace asset {
* which is fully integrated into a generic rules driven query subsystem, but * which is fully integrated into a generic rules driven query subsystem, but
* has the additional ability to "translate" capabilities directly into the * has the additional ability to "translate" capabilities directly into the
* respective properties of of asset::Struct subclasses. * respective properties of of asset::Struct subclasses.
* @return an Struct smart ptr linked to the internally registered smart ptr * @return a Struct smart ptr linked to the internally registered smart ptr
* created as a side effect of calling the concrete Struct subclass ctor. * created as a side effect of calling the concrete Struct subclass ctor.
*/ */
template<class STRU> template<class STRU>

View file

@ -100,7 +100,11 @@ namespace asset {
* *
* Examples being tracks, sequences, timelines, pipes, processing patterns * Examples being tracks, sequences, timelines, pipes, processing patterns
* @note embedded access point to instance creation or retrieval * @note embedded access point to instance creation or retrieval
* through the static field #retrieve * through the static field #retrieve
* @todo the actual meaning of a "structural asset" needs to be sharpened and evolved.
* The idea is to have a generic mechanism for attaching properties and relations.
* This will become relevant once we build the real query subsystem.
* Right now (as of 1/2015), asset::Struct is just an ID provider.
*/ */
class Struct : public Asset class Struct : public Asset
{ {

View file

@ -115,10 +115,10 @@ namespace mobject {
typedef lib::RefArray<asset::PSequence>& SequenceAccess; typedef lib::RefArray<asset::PSequence>& SequenceAccess;
Session (DefaultsAccess Session (DefaultsAccess,
,ElementsAccess ElementsAccess,
,TimelineAccess TimelineAccess,
,SequenceAccess) throw(); SequenceAccess) throw();
virtual ~Session (); virtual ~Session ();

View file

@ -11,6 +11,12 @@ PLANNED "Concept demonstration: retrieve session contents" SessionStructureMappi
return: 0 return: 0
END END
PLANNED "Concept demonstration: update hierarchical elements" TangibleUpdate_test <<END
return: 0
END
PLANNED "ModelClip_test" ModelClip_test <<END PLANNED "ModelClip_test" ModelClip_test <<END
END END

View file

@ -30,6 +30,7 @@
** **
** @note as of 10/2014 this is a initial draft into the blue... ** @note as of 10/2014 this is a initial draft into the blue...
** @todo WIP ///////////////////////TICKET #955 ** @todo WIP ///////////////////////TICKET #955
** @todo WIP ///////////////////////TICKET #961
** **
** @see gui::model::SessionFacade ** @see gui::model::SessionFacade
** **

View file

@ -1,8 +1,8 @@
/* /*
SessionStructureMapping(Test) - map session structure to GUI widgets TangibleUpdate(Test) - how to update nested tangible UI elements
Copyright (C) Lumiera.org Copyright (C) Lumiera.org
2014, Hermann Vosseler <Ichthyostega@web.de> 2015, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as modify it under the terms of the GNU General Public License as
@ -21,25 +21,24 @@
* *****************************************************/ * *****************************************************/
/** @file session-structure-mapping-test.cpp /** @file tangible-update-test.cpp
** This test is a concept study how to organise the proxy model ** This test is a concept study regarding a generic structure of UI elements.
** in the Lumiera GUI. This mediating model shields access to the ** Such gui::model::Tangible elements share the ability to be updated through
** actual "high-level model" in Proc-Layer, it translates signals ** a structural diff message. Which implies this test to be also an integration
** into command invocations and helps to push structure changes ** test of Lumiera's tree diff handling framework
** up to the timeline display.
** **
** @note as of 10/2014 this is a initial draft into the blue... ** @note as of 1/2015 this is a draft into the blue...
** @todo WIP ///////////////////////TICKET #955 ** @todo WIP ///////////////////////TICKET #959
** @todo WIP ///////////////////////TICKET #961 ** @todo WIP ///////////////////////TICKET #961
** **
** @see gui::model::SessionFacade ** @see gui::UiBus
** **
*/ */
#include "lib/test/run.hpp" #include "lib/test/run.hpp"
#include "gui/model/session-facade.hpp" //#include "gui/model/session-facade.hpp"
#include "gui/model/diagnostics.hpp" //#include "gui/model/diagnostics.hpp"
//#include "lib/util.hpp" //#include "lib/util.hpp"
@ -84,14 +83,12 @@ namespace test {
* @see SessionElementQuery_test * @see SessionElementQuery_test
* @see gui::model::SessionFacade * @see gui::model::SessionFacade
*/ */
class SessionStructureMapping_test : public Test class TangibleUpdate_test : public Test
{ {
virtual void virtual void
run (Arg) run (Arg)
{ {
gui::model::Diagnostics lock(TEST_SESSION_1);
retrieveSessionStructure();
} }
@ -108,7 +105,7 @@ namespace test {
/** Register this test class... */ /** Register this test class... */
LAUNCHER (SessionStructureMapping_test, "unit gui"); LAUNCHER (TangibleUpdate_test, "unit gui");
}}} // namespace gui::model::test }}} // namespace gui::model::test

View file

@ -1910,8 +1910,8 @@ Within the Facade, there is the definition of the {{{EngineService::Quality}}} t
Actually this interface is a strategy, allowing to define quite specific quality levels, in case we need that. Clients can usually just use Actually this interface is a strategy, allowing to define quite specific quality levels, in case we need that. Clients can usually just use
these ~QoS-tags like enum values (they are copyable), without caring for the engine implementation related details.</pre> these ~QoS-tags like enum values (they are copyable), without caring for the engine implementation related details.</pre>
</div> </div>
<div title="EntryID" modifier="Ichthyostega" created="201012300026" modified="201012300036" tags="def Types"> <div title="EntryID" modifier="Ichthyostega" created="201012300026" modified="201501091117" tags="def Types" changecount="1">
<pre>A general identification scheme, ombining a human readable symbolic name, unique within a //specifically typed context,// and machine readable hash ID (LUID). ~Entry-IDs allow for asset-like position accounting and for type safe binding between configuration rules and model obects. They allow for creating an entry with symbolic id and distinct type, combined with an derived hash value, without the overhead in storage and instance management imposed by using a full-fledged Asset. <pre>A general identification scheme, combining a human readable symbolic name, unique within a //specifically typed context,// and machine readable hash ID (LUID). ~Entry-IDs allow for asset-like position accounting and for type safe binding between configuration rules and model obects. They allow for creating an entry with symbolic id and distinct type, combined with an derived hash value, without the overhead in storage and instance management imposed by using a full-fledged Asset.
Similar to an Asset, an identification tuple is available (generated on the fly), as is an unique LUID and total ordering. The type information is attached as template parameter, but included into the hash calculation. All instantiations of the EntryID template share a common baseclass, usable for type erased common registration. Similar to an Asset, an identification tuple is available (generated on the fly), as is an unique LUID and total ordering. The type information is attached as template parameter, but included into the hash calculation. All instantiations of the EntryID template share a common baseclass, usable for type erased common registration.
@ -2210,6 +2210,13 @@ For this Lumiera design, we could consider making GOP just another raw media dat
&amp;rarr;see in [[Wikipedia|http://en.wikipedia.org/wiki/Group_of_pictures]] &amp;rarr;see in [[Wikipedia|http://en.wikipedia.org/wiki/Group_of_pictures]]
</pre> </pre>
</div> </div>
<div title="GenNode" creator="Ichthyostega" modifier="Ichthyostega" created="201501171413" modified="201501171431" tags="Model GuiIntegration GuiPattern def draft" changecount="8">
<pre>//Abstract generic node element to build a ~DOM-like rendering of Lumiera's [[session model|HighLevelModel]].//
GenNode elements are values, yet behave polymorphic. They are rather light-weight, have an well established identity and can be compared. They are //generic// insofar they encompass several heterogeneous ordering systems, which in themselves can not be subsumed into a single ordering hierarchy. The //purpose// of these generic nodes is to build a symbolic representation, somewhere &quot;outside&quot;, at a level where the fine points of ordering system relations do not really matter.
To be more specific, within the actual model there are [[Placements|Placement]]. These refer to [[M-Objects|MObject]]. Which in turn rely on [[Assets|Asset]]. Moreover, we have some processing rules, and -- last but not least -- the &quot;objects&quot; encountered in the model have state, visible as attributes of atomic value type (integral, floating point, string, boolean, time, time ranges and [[quantised time entities|TimeQuant]]).
A generic node may //represent any of these kind// -- and it may have ~GenNode children, forming a tree.</pre>
</div>
<div title="GlobalPipe" modifier="Ichthyostega" created="201007110200" modified="201202032308" tags="Model spec draft"> <div title="GlobalPipe" modifier="Ichthyostega" created="201007110200" modified="201202032308" tags="Model spec draft">
<pre>Each [[Timeline]] has an associated set of global [[pipes|Pipe]] (global busses), similar to the subgroups of a sound mixing desk. <pre>Each [[Timeline]] has an associated set of global [[pipes|Pipe]] (global busses), similar to the subgroups of a sound mixing desk.
In the typical standard configuration, there is (at least) a video master and a sound master pipe. Like any pipe, ingoing connections attach to the input side, attached effects form a chain, where the last node acts as exit node. The PipeID of such a global bus can be used to route media streams, allowing the global pipe to act as a summation bus bar. In the typical standard configuration, there is (at least) a video master and a sound master pipe. Like any pipe, ingoing connections attach to the input side, attached effects form a chain, where the last node acts as exit node. The PipeID of such a global bus can be used to route media streams, allowing the global pipe to act as a summation bus bar.
@ -2298,7 +2305,7 @@ We acknowledge that the gui model is typically used from within the GUI event di
The forwarding of model changes to the GUI widgets is another concern, since notifications from session mutations arrive asynchronous after each [[Builder]] run. In this case, we send a notification to the widgets registered as listeners, but wait for //them// to call back and fetch the [[diffed state|TreeDiffModel]]. This callback will be scheduled by the widgets to perform in the GUI event thread. The forwarding of model changes to the GUI widgets is another concern, since notifications from session mutations arrive asynchronous after each [[Builder]] run. In this case, we send a notification to the widgets registered as listeners, but wait for //them// to call back and fetch the [[diffed state|TreeDiffModel]]. This callback will be scheduled by the widgets to perform in the GUI event thread.
</pre> </pre>
</div> </div>
<div title="GuiModelElements" creator="Ichthyostega" modifier="Ichthyostega" created="201501061138" modified="201501061231" tags="GuiIntegration design draft img" changecount="17"> <div title="GuiModelElements" creator="Ichthyostega" modifier="Ichthyostega" created="201501061138" modified="201501171411" tags="GuiIntegration design draft img" changecount="37">
<pre>''Building Blocks for the User Interface Model and Control structure'' <pre>''Building Blocks for the User Interface Model and Control structure''
The fundamental pattern for building graphical user interfaces is to segregates into the roles of __M__odel, __V__iew and __C__controler ([[MVC|http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller]]). This approach is so succesful, that it turned into a de-facto standard in commen UI toolkit sets. But larger, more elaborate and specialised applications introduce several cross cutting concerns, which create a tension within this MVC solution pattern. The fundamental pattern for building graphical user interfaces is to segregates into the roles of __M__odel, __V__iew and __C__controler ([[MVC|http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller]]). This approach is so succesful, that it turned into a de-facto standard in commen UI toolkit sets. But larger, more elaborate and specialised applications introduce several cross cutting concerns, which create a tension within this MVC solution pattern.
[&lt;img[UI-Bus and GUI model elements|uml/fig158213.png]] [&lt;img[UI-Bus and GUI model elements|uml/fig158213.png]]
@ -2307,7 +2314,24 @@ The fundamental pattern for building graphical user interfaces is to segregates
The Lumiera GTK UI is built around a distinct backbone, separate from the structures required and provided by GTK. The Lumiera GTK UI is built around a distinct backbone, separate from the structures required and provided by GTK.
While GTK -- especially in the object oirented incantation given by Gtkmm -- hooks up a hierarchy of widgets into a UI workspace, each of these widgets can and should incorporate the necessary control and data elements. But actually, these elements are local access points to the backbone structure, which we define as the UI-Bus. So, in fact, the local widgets and controllers wired into the interface are turned into ''Decorators'' of a backbone structure. This backbone is a ''messaging system'' (hence the name &quot;Bus&quot;). The terminal points of this messaging system allow for direct wiring of GTK signals. Operations triggered by UI interactions are transformed into [[Command]] invocations into the Proc-Layer, while the model data elements remain abstract and generic. The entities in our UI model are not directly connected to the actual model, but they are in correspondence to such actual model elements within the [[Session]]. Moreover, there is an uniform identification scheme. While GTK -- especially in the object oriented incantation given by Gtkmm -- hooks up a hierarchy of widgets into a UI workspace, each of these widgets can and should incorporate the necessary control and data elements. But actually, these elements are local access points to our backbone structure, which we define as the UI-Bus. So, in fact, the local widgets and controllers wired into the interface are turned into ''Decorators'' of a backbone structure. This backbone is a ''messaging system'' (hence the name &quot;Bus&quot;). The terminal points of this messaging system allow for direct wiring of GTK signals. Operations triggered by UI interactions are transformed into [[Command]] invocations into the Proc-Layer, while the model data elements remain abstract and generic. The entities in our UI model are not directly connected to the actual model, but they are in correspondence to such actual model elements within the [[Session]]. Moreover, there is an uniform [[identification scheme|GenNode]].
;connections
:all connections are defined to be strictly //optional.//
:inactive connections render each element passive
;attributes
:the GuiModel supports a notion of generic attributes, treated as unordered unique children and referred by key name.
;local state
:recommendation is to have widget local state represented //within the UI toolkit (GTK).// On loosing bus connection, any element should disable itself or maybe even shut down.
;updates
:tangible UI elements are //passive.// Any update and mutation, on notification from the bus, is [[pulled from the model|GuiModelUpdate]]. The individual element is thus required to update itself (and its children recursively) into compliance with the provided state.
!building and updating the tree
The workspace starts out with a single element, corresponding to the »model root« in the ~Proc-Layer HighLevelModel. Initially, or on notification, an element requests a //status update// -- which conceptually implies there is some kind of conversation state. The backbone, as represented by the UI-Bus, might be aware of the knowledge state of its clients and just send an update. Yet the authority or the backbone is absolute. It might, at its own discretion, send a full update, to which the client elements are expected to comply. The status and update information is exposed in the form of a diff iterator. The client element, which can be a widget or a controller within the workspace, is expected to pull and extract this diff information, adjusting itself and destroying or creating children as applicable. This implies a recursive tree visitation, passing down the diff iterator alongside.
Speaking of implementation, this state and update mechanics relies on two crucial provisions: Lumiera's framework for [[tree diff representation|TreeDiffModel]] and an abstracted, ~DOM-like rendering of the relevant parts of the session; this model tree is comprised of [[generic node elements|GenNode]] acting as proxy for [[calls into|SessionInterface]] the [[session model|HighLevelModel]] proper.
</pre> </pre>
</div> </div>
<div title="GuiModelUpdate" creator="Ichthyostega" modifier="Ichthyostega" created="201410250121" modified="201501061118" tags="GuiIntegration GuiPattern design decision discuss draft" changecount="26"> <div title="GuiModelUpdate" creator="Ichthyostega" modifier="Ichthyostega" created="201410250121" modified="201501061118" tags="GuiIntegration GuiPattern design decision discuss draft" changecount="26">
@ -3016,8 +3040,8 @@ For the case here in question this seems to be the __R__esource __A__llocation _
And, last but not least, doing large scale allocations is the job of the backend. Exceptions being long-lived objects, like the session or the sequences, which are created once and don't bear the danger of causing memory pressure. Generally speaking, client code shouldn't issue &quot;new&quot; and &quot;delete&quot; when it comes in handy. Questions of setup and lifecycle should allways be delegated, typically through the usage of some [[factory|Factories]], which might return the product conveniently wrapped into a RAII style handle. Memory allocation is crucial for performance, and needs to be adapted to the actual platform -- which is impossible unless abstracted and treated as a separate concern. And, last but not least, doing large scale allocations is the job of the backend. Exceptions being long-lived objects, like the session or the sequences, which are created once and don't bear the danger of causing memory pressure. Generally speaking, client code shouldn't issue &quot;new&quot; and &quot;delete&quot; when it comes in handy. Questions of setup and lifecycle should allways be delegated, typically through the usage of some [[factory|Factories]], which might return the product conveniently wrapped into a RAII style handle. Memory allocation is crucial for performance, and needs to be adapted to the actual platform -- which is impossible unless abstracted and treated as a separate concern.
</pre> </pre>
</div> </div>
<div title="MetaAsset" modifier="Ichthyostega" created="201012290320" modified="201101151026" tags="def"> <div title="MetaAsset" modifier="Ichthyostega" created="201012290320" modified="201501091121" tags="def" changecount="1">
<pre>This category is comprised of the various aspects of the way the application controls and manages its own behaviour. They are more related to the way the application behaves, as opposed to the way the edited data is structured and organised (which is the realm of [[structural assets|StructAsset]] <pre>This category encompasses the various aspects of the way the application controls and manages its own behaviour. They are more related to the way the application behaves, as opposed to the way the edited data is structured and organised (which is the realm of [[structural assets|StructAsset]]
* StreamType &amp;rarr; a type system for describing and relating media data streams * StreamType &amp;rarr; a type system for describing and relating media data streams
* ScaleGrid &amp;rarr; to manage time scales and frame alignment * ScaleGrid &amp;rarr; to manage time scales and frame alignment
@ -5529,9 +5553,10 @@ A sequence is always tied to a root-placed track, it can't exist without such. W
&amp;rarr; see detailed [[discussion of dependent objects' behaviour|ModelDependencies]] &amp;rarr; see detailed [[discussion of dependent objects' behaviour|ModelDependencies]]
</pre> </pre>
</div> </div>
<div title="Session" modifier="Ichthyostega" created="200712100525" modified="201112222247" tags="def SessionLogic"> <div title="Session" modifier="Ichthyostega" created="200712100525" modified="201501171410" tags="def SessionLogic" changecount="1">
<pre>The Session contains all information, state and objects to be edited by the User. From a users view, the Session is synonymous to the //current Project//. It can be [[saved and loaded|SessionLifecycle]]. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one (or several) collections within the Session, which we call [[Sequence]]. <pre>The Session contains all information, state and objects to be edited by the User. From a users view, the Session is synonymous to the //current Project//. It can be [[saved and loaded|SessionLifecycle]]. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one (or several) collections within the Session, which we call [[Sequence]].
&amp;rarr; [[Session design overview|SessionOverview]] &amp;rarr; [[Session design overview|SessionOverview]]
&amp;rarr; Structure of the SessionInterface
!Session structure !Session structure
The Session object is a singleton &amp;mdash; actually it is a »~PImpl«-Facade object (because the actual implementation object can be swapped for (re)loading Sessions).&lt;br/&gt;The Session is the access point to the HighLevelModel; it is comprised of The Session object is a singleton &amp;mdash; actually it is a »~PImpl«-Facade object (because the actual implementation object can be swapped for (re)loading Sessions).&lt;br/&gt;The Session is the access point to the HighLevelModel; it is comprised of
@ -6102,11 +6127,11 @@ Instead, we should try to just connect the various subsystems via Interfaces and
* to shield the rendering code of all complexities of thread communication and synchronization, we use the StateProxy * to shield the rendering code of all complexities of thread communication and synchronization, we use the StateProxy
</pre> </pre>
</div> </div>
<div title="StructAsset" modifier="Ichthyostega" created="200709221353" modified="201105280022" tags="def classes img"> <div title="StructAsset" modifier="Ichthyostega" created="200709221353" modified="201501091147" tags="def classes img" changecount="4">
<pre>Structural Assets are intended mainly for internal use, but the user should be able to see and query them. They are not &quot;loaded&quot; or &quot;created&quot; directly, rather they //leap into existence // by creating or extending some other structures in the session, hence the name. Some of the structural Asset parametrisation can be modified to exert control on some aspects of the Proc Layer's (default) behaviour. <pre>Structural Assets are intended mainly for internal use, but the user should be able to see and query them. They are not &quot;loaded&quot; or &quot;created&quot; directly, rather they //leap into existence // by creating or extending some other structures in the session, hence the name. Some of the structural Asset parametrisation can be modified to exert control on some aspects of the Proc Layer's (default) behaviour.
* [[Processing Patterns|ProcPatt]] encode information how to set up some parts of the render network to be created automatically: for example, when building a clip, we use the processing pattern how to decode and pre-process the actual media data. * [[Processing Patterns|ProcPatt]] encode information how to set up some parts of the render network to be created automatically: for example, when building a clip, we use the processing pattern how to decode and pre-process the actual media data.
* [[Tracks|Track]] are one of the dimensions used for organizing the session data. They serve as an Anchor to attach parametrisation of output pipe, overlay mode etc. By [[placing|Placement]] to a track, a media object inherits placement properties from this track. * [[Tracks|Track]] are one of the dimensions used for organizing the session data. They serve as an Anchor to attach parametrisation of output pipe, overlay mode etc. By [[placing|Placement]] to a track, a media object inherits placement properties from this track.
* [[Pipes|Pipe]] form &amp;mdash; at least as visible to the user &amp;mdash; the basic building block of the render network, because the latter appears to be a collection of interconnected processing pipelines. (this is the //outward view; // in fact the render network consists of [[nodes|ProcNode]] and is [[built|Builder]] from the Pipes, clips, effects...)[&gt;img[Asset Classess|uml/fig131205.png]] * [[Pipes|Pipe]] form &amp;mdash; at least as visible to the user &amp;mdash; the basic building block of the render network, because the latter appears to be a collection of interconnected processing pipelines. This is the //outward view; // in fact the render network consists of [[nodes|ProcNode]] and is [[built|Builder]] from the Pipes, clips, effects...[&gt;img[Asset Classess|uml/fig131205.png]]&lt;br/&gt;Yet these //inner workings// of the render proces are implementation detail we tend to conceal.
* [[Sequence]] assets act as a façade to the fundamental compound building blocks within the model, a sequence being a collection of clips placed onto a tree of tracks. Sequences, as well as the top-level tracks enclosed will be created automatically on demand. Of course you may create them deliberately. Without binding it to a timeline or meta-clip, a sequence remains invisible. * [[Sequence]] assets act as a façade to the fundamental compound building blocks within the model, a sequence being a collection of clips placed onto a tree of tracks. Sequences, as well as the top-level tracks enclosed will be created automatically on demand. Of course you may create them deliberately. Without binding it to a timeline or meta-clip, a sequence remains invisible.
* [[Timeline]] assets are the top level structures to access the model; similar to the sequences, they act as façade to relevant parts of the model (BindingMO) and will be created on demand, alongside with a new session if necessary, bound to the new timeline. Likewise, they can be referred by their name-ID * [[Timeline]] assets are the top level structures to access the model; similar to the sequences, they act as façade to relevant parts of the model (BindingMO) and will be created on demand, alongside with a new session if necessary, bound to the new timeline. Likewise, they can be referred by their name-ID
* [[Viewer|ViewerAsset]] assets correspond to the available viewer elements in the GUI. By [[connecting to a viewer|ViewConnection]], session elements derive a concrete (physical) output and gain the ability to be [[played|PlayService]]. * [[Viewer|ViewerAsset]] assets correspond to the available viewer elements in the GUI. By [[connecting to a viewer|ViewConnection]], session elements derive a concrete (physical) output and gain the ability to be [[played|PlayService]].
@ -7184,6 +7209,25 @@ setStylesheet( stylesheet, &quot;TaskMacroPluginStylesheet&quot; )
//}}} //}}}
</pre> </pre>
</div> </div>
<div title="TestSupport" creator="Ichthyostega" modifier="Ichthyostega" created="201501171348" modified="201501171354" tags="Concepts spec draft" changecount="3">
<pre>The Lumiera development is //test driven.//
This does not mean, that we're using a formalised method, nor does it mean that we're aiming at a high coverage rate as a value //per se.// But indeed it does mean that we always design the usage situation before we go into the details of the implementation. Every major design work in the application starts out as a usage sketch in the form of a ''test narrative''. But our tests are limited in a characteristic way: We do not write unit tests to cover obvious details of the implementation -- whenever we test, we test abstractions. Which means, we have to bring in abstractions into even the fine grained detail level of implementation. Another consequence is that we don't use any mocking framework, nor do we rely on a dependency injection framework to build our application. Rather, we tend to build in testability and test rigging and diagnostics facilities into every major component of the application.
Over time, a selection of techniques and usage patterns for test support has emerged
!diagnositc output
As a rule, any component of significance has an overloaded {{{operator string()}}}. The {{{&lt;iostream&gt;}}} subsystem from the standard library will use these operators automatically. Moreover, we use a wrapper on top of {{{boost::format}}}, which does so as well, while providing exception safety and reducing code bloat by confining the actual {{{boost::format}}} instantiation to a single compilation unit with preselected specialisations. Thus, tests changing the relevant, tangible state of some application component will change the rendered diagnostic representation. While building those tests, effects can be seen immediately or in the debugger. Later the expected output of running such a test can be easily verified.
!equality and ordering
We go into great lengths of defining identity, equality and ordering operators when appropriate. This can be seen as an investment into the future. While certainly this doesn't pay off all the time, together with the diagnostic output it allows to write tests on the level of symbolic representation.
!test adapters
several core services offer a docking point to attach a test adapter. This special {{{friend class}}} is deeply integrated into the implementation level of said facility and may activate, when established, an extensive and costly additional layer of diagnostic reporting. For example, it may store an audit trail of every invoked state change. This approach allows us to write tests almost like formal specifications. We expect these test adapters to evolve into a diagnostic framework eventually -- Lumiera can be expected to become a challenge for diagnostics and user support, given the extensive use of rules based programming.
!rigged diagnositc setup
We build a collection of hard wired test configurations of the application. These can be activated by use of predefined name-~IDs. Depending on the level in use, a well defined set of processing rules, low-level wiring, mocked diagnostic output, faked input media and specific session contents will be injected. These can be used to write test scenarios against a known setup. Again we expect these provision to evolve into a general diagnostic facility, likely to be accessible for more advanced users too.
</pre>
</div>
<div title="TextAreaPlugin" modifier="Jeremy" created="200601261745" tags="systemConfig"> <div title="TextAreaPlugin" modifier="Jeremy" created="200601261745" tags="systemConfig">
<pre>/*** <pre>/***
''TextAreaPlugin for TiddlyWiki version 2.0'' ''TextAreaPlugin for TiddlyWiki version 2.0''
@ -7765,7 +7809,7 @@ Used this way, diff representation helps to separate structure and raw data in e
:Chunks of raw data are attached inline to the structural diff, assuming that each element implicitly knows the kind of data to expect :Chunks of raw data are attached inline to the structural diff, assuming that each element implicitly knows the kind of data to expect
</pre> </pre>
</div> </div>
<div title="TreeDiffImplementation" creator="Ichthyostega" modifier="Ichthyostega" created="201412210015" modified="201501010306" tags="Model GuiPattern design draft" changecount="12"> <div title="TreeDiffImplementation" creator="Ichthyostega" modifier="Ichthyostega" created="201412210015" modified="201501171503" tags="Model GuiPattern design draft" changecount="15">
<pre>//This page details decisions taken for implementation of Lumiera's diff handling framework// <pre>//This page details decisions taken for implementation of Lumiera's diff handling framework//
This topic is rather abstract, since diff handling is multi purpose within Lumiera: Diff representation is seen as a meta language and abstraction mechanism; it enables tight collaboration without the need to tie and tangle the involved implementation data structures. Used this way, diff representation reduces coupling and helps to cut down overall complexity -- so to justify the considerable amount of complexity seen within the diff framework implementation. This topic is rather abstract, since diff handling is multi purpose within Lumiera: Diff representation is seen as a meta language and abstraction mechanism; it enables tight collaboration without the need to tie and tangle the involved implementation data structures. Used this way, diff representation reduces coupling and helps to cut down overall complexity -- so to justify the considerable amount of complexity seen within the diff framework implementation.
@ -7796,9 +7840,19 @@ A first attempt was made to come up with a rather clever swapping and rotating s
!!!goal of a generic implementation !!!goal of a generic implementation
The diff handling framework we intend to build here is meant to be //generic// -- the actual element data type, as well as the underlying data structure and the index access shall be supplied by strategy and specialisation. This has the downside (maybe this is even a benefit?) that most efficiency considerations are moot at this level; we need to look at actual use cases and investigate the composite performance in practice later. The diff handling framework we intend to build here is meant to be //generic// -- the actual element data type, as well as the underlying data structure and the index access shall be supplied by strategy and specialisation. This has the downside (maybe this is even a benefit?) that most efficiency considerations are moot at this level; we need to look at actual use cases and investigate the composite performance in practice later.
!{{red{WIP 1/2015}}}building the tree diff handling
Our tree diff handling framework is conceived as a direct specialisation of and extension to list diffing. We rely on a very specific element type for the tree nodes: the GenNode. This specific node element type is inherently generic, and the tree diff handling explicitly relies on some part of this generic nature to represent object features. Following the gist of the linearised list diff representation, also tree handling relies on an implicit ''tree representation protocol'':
* every GenNode is a single node and considered unique
* nodes are inherently typed, but this typing is outside of the diff representation scope
* nodes and subtrees are spelled out in prefix order; a node optionally is followed by a list of children.
* nodes representing //objects// may have attribute children, which are always mentioned before the sub node children.
* when a node is //opened for diffing,// we first spell out any structural alterations (added, deleted or re-ordered children)
* the recursive descent into children happens postfix depth-first, each enclosed into a bracketing construct.
Consequently, when an element appears in the diff description sequence, first of all, its type is immediately clear, so the receiver can use an appropriate handler for this kind of element. Moreover, if the element is of atomic type (an attribute), its value is part of the element itself and thus is known just by spelling out the element. Any structural changes can be dealt with on a completely generic level, without further knowledge about the object's nature. And finally, any internal alterations of the object and its children happen after the generic part and clearly delineated in the sequence of diff tokens -- a sub handler can be invoked recursively
</pre> </pre>
</div> </div>
<div title="TreeDiffModel" creator="Ichthyostega" modifier="Ichthyostega" created="201410270313" modified="201501010256" tags="Model GuiPattern spec draft" changecount="49"> <div title="TreeDiffModel" creator="Ichthyostega" modifier="Ichthyostega" created="201410270313" modified="201501092026" tags="Model GuiPattern spec draft" changecount="50">
<pre>for the purpose of handling updates in the GUI timeline display efficiently, we need to determine and represent //structural differences// <pre>for the purpose of handling updates in the GUI timeline display efficiently, we need to determine and represent //structural differences//
We build a slightly abstracted representation of tree changes and use this to propagate //change notifications// to the actual widgets. To keep the whole process space efficient, a demand-driven, stateless implementation approach is chosen. This reduces the problem into several layered stages. We build a slightly abstracted representation of tree changes and use this to propagate //change notifications// to the actual widgets. To keep the whole process space efficient, a demand-driven, stateless implementation approach is chosen. This reduces the problem into several layered stages.
* our model is a heterogeneous tree &amp;rArr; use demand-driven recursion * our model is a heterogeneous tree &amp;rArr; use demand-driven recursion
@ -7840,7 +7894,7 @@ Thus, for our specific usage scenario, the foremost relevant question is //how t
|{{{pick}}}(a~~4~~) |!| (b~~1~~, a~~3~~, a~~5~~, b~~2~~, b~~3~~, a~~4~~)|() | |{{{pick}}}(a~~4~~) |!| (b~~1~~, a~~3~~, a~~5~~, b~~2~~, b~~3~~, a~~4~~)|() |
|{{{ins}}}(b~~4~~) |!| (b~~1~~, a~~3~~, a~~5~~, b~~2~~, b~~3~~, a~~4~~, b~~4~~)|() | |{{{ins}}}(b~~4~~) |!| (b~~1~~, a~~3~~, a~~5~~, b~~2~~, b~~3~~, a~~4~~, b~~4~~)|() |
__Implementation note__:The representation chosen here uses terms of constant size for the individual diff steps; in most cases, the argument is redundant and can be used for verification when applying the diff -- exceptions being the {{{ins}}} term, where it actually encodes additional information. Especially the {{{find}}}-representation is a compromise, since we encode as &quot;search for the term a~~5~~ and insert it at curent position&quot;. The more obvious rendering -- &quot;push term a~~4~~ back by +1 steps&quot; -- requires an additional integer argument not neccesary for any of the other diff verbs, defeating a fixed size value implementation. __Implementation note__:The representation chosen here uses terms of constant size for the individual diff steps; in most cases, the argument is redundant and can be used for verification when applying the diff -- with the exception of the {{{ins}}} term, where it actually encodes additional information. Especially the {{{find}}}-representation is a compromise, since we encode as &quot;search for the term a~~5~~ and insert it at curent position&quot;. The more obvious rendering -- &quot;push term a~~4~~ back by +1 steps&quot; -- requires an additional integer argument not neccesary for any of the other diff verbs, defeating a fixed size value implementation.
!!!extension to tree changes !!!extension to tree changes
Basically we could send messages for recursive descent right after each {{{pick}}} token -- yet, while minimal, such a representation would be unreadable, and requires a dedicated stack storage on both sides. Thus we arrange for the //recursive treatment of children// to be sent //postfix,// after the messages for the current node. Recursive descent is indicated by explicit (and slightly redundant) //bracketing tokens:// Basically we could send messages for recursive descent right after each {{{pick}}} token -- yet, while minimal, such a representation would be unreadable, and requires a dedicated stack storage on both sides. Thus we arrange for the //recursive treatment of children// to be sent //postfix,// after the messages for the current node. Recursive descent is indicated by explicit (and slightly redundant) //bracketing tokens://
@ -7855,14 +7909,14 @@ On receiving the terms of this &quot;diff language&quot;, it is possible to gene
i.e. a ''unified diff'' or the ''predicate notation'' used above to describe the list diffing algorithm, just by accumulating changes. i.e. a ''unified diff'' or the ''predicate notation'' used above to describe the list diffing algorithm, just by accumulating changes.
</pre> </pre>
</div> </div>
<div title="TypedID" modifier="Ichthyostega" created="201003200157" modified="201112222250" tags="Model Types Rules design draft"> <div title="TypedID" modifier="Ichthyostega" created="201003200157" modified="201501142250" tags="Model Types Rules design draft" changecount="9">
<pre>//drafted service as of 4/10 &amp;mdash; &amp;rarr;[[implementation plans|TypedLookup]]// <pre>//drafted service as of 4/10 &amp;mdash; &amp;rarr;[[implementation plans|TypedLookup]]//
A registration service to associate object identities, symbolic identifiers and types. A registration service to associate object identities, symbolic identifiers and types.
!Motivation !Motivation
For maintaining persistent objects, generally an unique object ID is desirable. Within Lumiera, we employ 128 hash-~IDs (&amp;raquo;{{{LUID}}}&amp;laquo;). But hash-~IDs are difficult to handle for testing and configuration, as they aren't self explanatory for human readers. They're best used in a way avoiding textual representation. For maintaining persistent objects, generally an unique object ID is desirable. Within Lumiera, we employ 128 hash-~IDs (&amp;raquo;{{{LUID}}}&amp;laquo;). But hash-~IDs are difficult to handle for testing and configuration, as they aren't self explanatory for human readers. They're best used in a way avoiding textual representation altogether.
Formal symmetry might be another motivation: we separate into objects and assets, where the latter represent the //bookkeeping view.// But there remain some cases where the asset side, the bookkeeping is void of any substantial functionality. Just for the sake of orthogonality it //would be preferable//&amp;nbsp; to maintain a registration (for scripted use, for overview and organisational activities by the user, for diagnostics and repair work in the saved session state). A mere data record suffices to fulfil these requirements &amp;mdash; while there are other kinds of asset exposing a substantial amount of functionality. Formal symmetry might be another motivation: we separate into objects and assets, where the latter represent the //bookkeeping view.// But there remain some cases where the asset side, the bookkeeping, is void of any substantial functionality. Just for the sake of orthogonality it //would be preferable//&amp;nbsp; to maintain a registration (for scripted use, for overview and organisational activities by the user, for diagnostics and repair work in the saved session state). A mere data record suffices to fulfil these requirements &amp;mdash; while there are indeed other kinds of asset exposing a substantial amount of functionality.
Both these motivations highlight a tension between having a single global namespace of unique ~IDs, and having type-bound sub namespaces of limited scope, as the latter allows for using human readable symbolic ~IDs. Usually, when it comes to writing rules and configuration or creating instances explicitly from code, the specific context of the situation allows or even requires to focus down upon a single and distinct kind of objects immediately &amp;mdash; global uniqueness is not much of a concern here. Both these motivations highlight a tension between having a single global namespace of unique ~IDs, and having type-bound sub namespaces of limited scope, as the latter allows for using human readable symbolic ~IDs. Usually, when it comes to writing rules and configuration or creating instances explicitly from code, the specific context of the situation allows or even requires to focus down upon a single and distinct kind of objects immediately &amp;mdash; global uniqueness is not much of a concern here.
@ -7877,13 +7931,14 @@ A registration service backed by an index table can be used to //translate//&amp
!!!usage scenarios !!!usage scenarios
* automatically maintained lists of all clips, labels, tracks and sequences in the &amp;raquo;Asset&amp;laquo; section of the application * automatically maintained lists of all clips, labels, tracks and sequences in the &amp;raquo;Asset&amp;laquo; section of the application
* addressing objects in script driven operations, just based on symbolic names, allowing for additional conventions * addressing objects in script driven operations, just based on symbolic names, allowing for additional conventions
* implementing a predicate {{{isTypeXXX(Element)}}}, which is crucial for [[rules based configuration|ConfigQuery]]. * implementing a predicate {{{isTypeXXX(Element)}}}, (a »type guard«) which is crucial for [[rules based configuration|ConfigQuery]].
!!!{{red{WIP}}}Analysis and discussion !!!{{red{WIP}}}Analysis and discussion
Still some contradictions &amp;mdash; and rather seems helpful, not so much necessary. Still some contradictions &amp;mdash; and rather seems helpful, not so much necessary right now.
We //already have an registration service,// both for Assets (AssetManager) and for Placements (PlacementIndex). These facilities maintain not only a raw ID &amp;harr; object association, but also structuring information, albeit bound to more specific circumstances (the system of placement scopes, and the asset category). The lookup uniqueID &amp;rArr; object could be implemented by sequentially querying this small number of central registration facilities. Thus, still lacking is a ''system of sub index tables''. We //already have an registration service,// both for Assets (AssetManager) and for Placements (PlacementIndex). These facilities maintain not only a raw ID &amp;harr; object association, but also structuring information, albeit bound to more specific circumstances (the system of placement scopes, and the asset category). The lookup uniqueID &amp;rArr; object could be implemented by sequentially querying this small number of central registration facilities. Thus, still lacking is a ''system of sub index tables''.
As mentioned above, an ID &amp;harr; type association plays a crucial role when it comes to implementing any kind of rules based configuration. It would allow to bridge from our session objects to rules and resolution working entirely symbolic. (&amp;rarr; [[more|ConfigQueryIntegration]]). But, as of 3/2010 this is a //planned feature and not required to get the initial pipeline working.// Thus, according to the YAGNI principle, we shouldn't engage into any implementation details right now and just create the extension points. As mentioned above, an ID &amp;harr; type association plays a crucial role when it comes to implementing any kind of rules based configuration. It would allow to bridge from our session objects to rules and resolution working entirely symbolic. (&amp;rarr; [[more|ConfigQueryIntegration]]). But, as of 3/2010 this is a //planned feature and not required to get the initial pipeline working.// Thus, according to the YAGNI principle, we shouldn't engage into any implementation details right now and just create the extension points.
{{red{1/2015 Remark}}}: meanwhile we've come accross several situations calling for //some element// present here in this design draft. So: yes, we //will nedd that lookup system,// but not right now.
The immediate need prompting to start development on this facility, is how to get sub-selections from the objects in the session and for certain kinds of asset &amp;mdash; especially how to deal with retrieving the referred track for the &amp;rarr; [[sequence and timeline handling|ModelDependencies]]. The immediate need prompting to start development on this facility, is how to get sub-selections from the objects in the session and for certain kinds of asset &amp;mdash; especially how to deal with retrieving the referred track for the &amp;rarr; [[sequence and timeline handling|ModelDependencies]].
&lt;&lt;&lt; &lt;&lt;&lt;
@ -7899,11 +7954,12 @@ Just an ''registration scheme'' should be implemented right now, working complet
** but it would be redundant (both PlacementIndex and AssetManager //are// implemented with a large hashtable) ** but it would be redundant (both PlacementIndex and AssetManager //are// implemented with a large hashtable)
** moreover, ennumeration of all elements of a specific kind was one of the reasons to build this facility ** moreover, ennumeration of all elements of a specific kind was one of the reasons to build this facility
* supporting 1:n ? (e.g. one track-ID for many track objects?). Rather not directly, because this defeats lookup by ID * supporting 1:n ? (e.g. one track-ID for many track objects?). Rather not directly, because this defeats lookup by ID
* possible drawback is Lock contention -- instance creation and disposal tends to become single threaded.
see [[implementation planning|TypedLookup]] see [[implementation planning|TypedLookup]]
</pre> </pre>
</div> </div>
<div title="TypedLookup" modifier="Ichthyostega" created="201004031607" modified="201112222250" tags="Rules Types spec impl draft"> <div title="TypedLookup" modifier="Ichthyostega" created="201004031607" modified="201501142253" tags="Rules Types spec impl draft" changecount="7">
<pre>TypedID is a registration service to associate object identities, symbolic identifiers and types. It acts as frontend to the TypedLookup service within Proc-Layer, at the implementation level. While TypedID works within a strictly typed context, this type information is translated into an internal index on passing over to the implementation, which manages a set of tables containing base entries with an combined symbolic+hash ID, plus an opaque buffer. Thus, the strictly typed context is required to re-access the stored data. But the type information wasn't erased entirely, so this typed context can be re-gained with the help of an internal type index. All of this is considered implementation detail and may be subject to change without further notice; any access is assumed to happen through the TypedID frontend. Besides, there are two more specialised frontends. <pre>TypedID is a registration service to associate object identities, symbolic identifiers and types. It acts as frontend to the TypedLookup service within Proc-Layer, at the implementation level. While TypedID works within a strictly typed context, this type information is translated into an internal index on passing over to the implementation, which manages a set of tables holding base entries with a combined symbolic+hash ID, plus an opaque buffer. Thus, the strictly typed context is required to re-access the stored data. But the type information wasn't erased entirely, so this typed context can be re-gained with the help of an internal type index. All of this is considered implementation detail and may be subject to change without further notice; any access is assumed to happen through the TypedID frontend. Besides, there are two more specialised frontends.
!Front-ends !Front-ends
* TypedID uses static but templated access functions, plus an singleton instance to manage a ~PImpl pointing to the ~TypedLookup table * TypedID uses static but templated access functions, plus an singleton instance to manage a ~PImpl pointing to the ~TypedLookup table
@ -7911,9 +7967,9 @@ see [[implementation planning|TypedLookup]]
* TypeHandler is heavily used by the ConfigRules {{red{planned}}}; each participating primary type provides an implementation * TypeHandler is heavily used by the ConfigRules {{red{planned}}}; each participating primary type provides an implementation
!Tables and index !Tables and index
The Table consists of several registration groups, each of which contains a hashtable and deals with one specific type. Groups are created on demand, but there is initially one group holding the internal type index (translation table). There may be even sub-groups, usable to create clusterings within one group. The Table consists of several registration groups, each of which contains a hashtable and deals with one specific type. Groups are created on demand, but there is initially one special group to hold the internal type index (translation table). There may be arbitrary further groups, with special meaning for parts of the application, there may be even sub-groups, usable to create clusterings within one group.
__Individual entries__ are comprised of a EntryID as key (actually a ~BareEntryID, without the typing) and a payload, which //doesn't store data,// but information necessary to ''lookup and access'' the registered object. Obviously, this information is type specific, and thus the ~TypedLookup implementation can't know how to deal with it directly. Rather, we store a ''functor'' in the payload of the type index group. This functor is directly linked to the TypeHandler, i.e. any type wanting to be a primary type within Lumiera, so as to be directly usable within the ConfigRules, needs to provide a suitable functor implementation through its ~TypeHandler. These functors are then invoked by the ~TypedID frontend, when it comes to re-accessing a registered entity by ID __Individual entries__ are comprised of an EntryID as key (actually a ~BareEntryID, without the typing) and a payload, which //doesn't store data,// but information necessary to //lookup and access// the registered object. Obviously, this information is type specific, and thus the ~TypedLookup implementation can't know how to deal with it directly. Rather, we store a ''functor'' in the payload of the type index group. This functor is directly linked to the TypeHandler, and the notion of a ''primary type'' within Lumiera. The system is not based on a hierarchy of data, but a network of related concepts. Any type involved into the metadata and self-adaptation encoded into this setup -- more specifically, any type to act as first calss citizen within the ConfigRules -- any such type needs to provide a suitable functor implementation through its TypeHandler. The concrete &quot;incantation&quot; of these functors, as neccessary to access and use the type, is stored in the mentioned special ''type index group''. From there it can be invoked by the ~TypedID frontend, to interpret the access data in the individual entries, to be able to retrieve or re-access a registered entity by ID, by name or by type.
!link for automatic registration !link for automatic registration
An entity can be linked with the TypedLookup system to be registered and deregistered automatically. This is achieved by mixing in the {{{TypedID::Link}}}. On creation, this will set up an EntryID for this individual instance and cause creation of an empty entry within the suitable registration group. As a side-effect, uniqueness of any symbolic-ID within one group (type) is enforced. Obviously, the dtor of this registration Link cares for de-registration automatically. Be forwarned though, by creating an unique identity, this mechanism will interfere with copying and cloning of the registered entity. An entity can be linked with the TypedLookup system to be registered and deregistered automatically. This is achieved by mixing in the {{{TypedID::Link}}}. On creation, this will set up an EntryID for this individual instance and cause creation of an empty entry within the suitable registration group. As a side-effect, uniqueness of any symbolic-ID within one group (type) is enforced. Obviously, the dtor of this registration Link cares for de-registration automatically. Be forwarned though, by creating an unique identity, this mechanism will interfere with copying and cloning of the registered entity.
@ -7960,12 +8016,12 @@ The UI-Bus is a ''Mediator'' -- impersonating the role of the //Model// and the
The ~MVC-Pattern as such is fine, and probably the best we know for construction of user interfaces. But it doesn't scale well towards the integration into a larger and more structured system. There is a tension between the Controller in the UI and other parts of an application, which as well need to be //in control.// And, even more important, there is a tension between the demands of UI elements for support by a model, and the demands to be placed on a core domain model of a large scale application. This tension is resolved by enacting these roles while transforming the requests and demands into //Messages.//</pre> The ~MVC-Pattern as such is fine, and probably the best we know for construction of user interfaces. But it doesn't scale well towards the integration into a larger and more structured system. There is a tension between the Controller in the UI and other parts of an application, which as well need to be //in control.// And, even more important, there is a tension between the demands of UI elements for support by a model, and the demands to be placed on a core domain model of a large scale application. This tension is resolved by enacting these roles while transforming the requests and demands into //Messages.//</pre>
</div> </div>
<div title="ViewConnection" modifier="Ichthyostega" created="201105221854" modified="201105221902" tags="def Model SessionLogic"> <div title="ViewConnection" modifier="Ichthyostega" created="201105221854" modified="201501091154" tags="def Model SessionLogic" changecount="3">
<pre>For any kind of playback to happen, timeline elements (or similar model objects) need to be attached to a Viewer element through a special kind of [[binding|BindingMO]], called a ''view connection''. In the most general case, this creates an additional OutputMapping (and in the typical standard case, this boils down to a 1:1 association, sending the master bus of each media kind to the standard OutputDesignation for that kind). <pre>For any kind of playback to happen, timeline elements (or similar model objects) need to be attached to a Viewer element through a special kind of [[binding|BindingMO]], called a ''view connection''. In the most general case, this creates an additional OutputMapping (and in the typical standard case, this boils down to a 1:1 association, sending the master bus of each media kind to the standard OutputDesignation for that kind).
Establishing a ~ViewConnection is prerequisite for creating or attaching an PlayController through the PlayService. Multiple &quot;play control&quot; GUI elements can be associated with such a play controller, causing them to work as being linked together: if you e.g. push &quot;play&quot; on one of them, the button states of all linked GUI controls will reflect the state change of the underlying play controller. Establishing a ~ViewConnection is prerequisite for creating or attaching an PlayController through the PlayService. Multiple &quot;play control&quot; GUI elements can be associated with such a play controller, causing them to work as being linked together: if you e.g. push &quot;play&quot; on one of them, the button states of all linked GUI controls will reflect the state change of the underlying play controller.
View connections are part of the model and thus persistent. They can be created explicitly, or just derived by //allocating a viewer.// And a new view connection can push aside (and thus &quot;break&quot;) an existing one from another timeline or model element. When a view connection is //broken,// any associated PlayProcess needs to be terminated (this is a blocking operation). Thus, at any time, there can be only one active view connection to a given viewer; here &quot;active&quot; means, that a PlayController has been hooked up, and the connection is ready for playback or rendering. But on the other hand, nothing prevents a timeline (or similar model object) to maintain multiple view connections -- consequently the actual playback position behaves as if associated with the view connection. View connections are part of the model and thus persistent. They can be created explicitly, or just derived by //allocating a viewer.// And a new view connection can push aside (and thus &quot;break&quot;) an existing one from another timeline or model element. When a view connection is //broken,// any associated PlayProcess needs to be terminated (this is a blocking operation). Thus, at any time, there can be only one active view connection to a given viewer or output sink; here &quot;active&quot; means, that a PlayController has been hooked up, and the connection is ready for playback or rendering. But on the other hand, nothing prevents a timeline (or similar model object) to maintain multiple view connections -- consequently the actual playback position behaves as if associated with the view connection; it has only meaning with respect to this specific connection. An obvious example is that you may play back, without interfering with an ongoing render.
</pre> </pre>
</div> </div>
<div title="ViewerAsset" modifier="Ichthyostega" created="201105230116" modified="201105232228" tags="Model spec"> <div title="ViewerAsset" modifier="Ichthyostega" created="201105230116" modified="201105232228" tags="Model spec">