consider how diff application might interplay with display changes

...it seemed first that we'd might run into a very fundamental problem;
but after some consideration it turns out the interspersed display manager
and the decoupling between model/presenter and widget happens to mitigate
this problem as well.
This commit is contained in:
Fischlurch 2016-11-26 04:18:43 +01:00
parent 5badfe211e
commit f5ea31a533
2 changed files with 200 additions and 15 deletions

View file

@ -2827,7 +2827,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]]
</pre>
</div>
<div title="GuiTimelineWidgetStructure" creator="Ichthyostega" modifier="Ichthyostega" created="201410250002" modified="201611210035" tags="GuiPattern discuss decision impl" changecount="74">
<div title="GuiTimelineWidgetStructure" creator="Ichthyostega" modifier="Ichthyostega" created="201410250002" modified="201611260315" tags="GuiPattern discuss decision impl" changecount="79">
<pre>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).
@ -2853,18 +2853,26 @@ While the special setup for scrolling doesn't really count (since it is necessar
!!!follow-up to the obvious choices
We came to this point of re-considering the overall organisation of widgets, after having to re-write the initial version of our timeline widget. This initial version was developed by Joel Holdsworth, and it followed a similar reasoning, involving a global timeline layout manager. The distinction between the two panes was not so clear though, and the access to the model code was awkward at places, so the necessity to re-write the timeline widget due to the transition to ~GTK-3 looks like a good opportunity to re-do the same basic reasoning a second time, verify decisions taken and improve matters turning out as difficult on first attempt.
So we get a timeline custom widget, which at top level establishes this two-part layout, provides the global scrollbars and integrates custom widget components for both parts. And this top-level timeline widget owns a layout manager, plus it exposes a common view management interface, to be used both from internal components (e.g. zoom widgets within the UI) and from external actors controlling the timeline display from a global level. Also at this global level, we get to define a layout control interface, which has to be implemented by the recursively structured parts of the timeline display, and which is used by the global layout manager to execute its control over the layout as a whole. {{red{Note (11/2016)}}}: if this layout control interface works push or pull style is a decision to be worked out still in the course of the implementation.
So we get a timeline custom widget, which at top level establishes this two-part layout, provides the global scrollbars and integrates custom widget components for both parts. And this top-level timeline widget owns a layout manager, plus it exposes a common view management interface, to be used both from internal components (e.g. zoom widgets within the UI) and from external actors controlling the timeline display from a global level. Also at this global level, we get to define a layout control interface, which has to be implemented by the recursively structured parts of the timeline display, and which is used by the global layout manager to execute its control over the layout as a whole. {{red{Note (11/2016)}}}: if this layout control interface works push or pull style is a decision yet to be worked out during the course of the implementation.
!dealing with nested structures
The handling of objects structured into nested scopes is a hallmark of the very specific approach taken by Lumiera when it comes to attaching, arranging and relating media objects. But here in the UI display of the timeline, this approach creates a special architectural challenge: the only sane way to deal with nested structures without exploding complexity is to find some way to exploit the ''recursive self similarity'' inherent in any tree structure. But the problematic consequence of this assessment is the tension, even contradiction it creates to the necessities of GUI programming, which forces us to come up with one single, definitive widget representation of what is going on eventually. The conclusion is that we need to come up with an interface such as to allow building and remoulding of the UI display through incremental steps -- where each of this incremental steps relies solely on relative, context based information. Because this is the only way we can deal with building a tree structure by recursive programming. We must not demand the individual step to know its arrangement within the tree, other than indicating a &quot;current&quot; or a &quot;parent&quot; reference point.
The structure of the display is extended or altered under two circumstances:
#. some component receives a [[diff mutation message|MutationMessage]], prompting to add or remove a //child component.//
#. the display (style) of some component is expanded or collapsed.
Here, the &quot;component&quot; relevant for such structural changes is always the UI representation of a track. But beyond that, the layout can be changed //without changing the display structure,// when some embedded component, be it placement (in the track heads / the patchbay) or a clip, effect or transition, is expanded or collapsed. In such a case, a resizing challenge needs to be directed towards the next enclosing track container.
# some component receives a [[diff mutation message|MutationMessage]], prompting to add or remove a //child component.//
# the display (style) of some component is expanded or collapsed.
Here, the &quot;component&quot; relevant for such structural changes is always the UI representation of a track. Beyond that, the layout can also be changed //without changing the display structure,// when some embedded component, be it placement (in the track heads / the patchbay) or a clip, effect or transition, is expanded or collapsed. In such a case, a resizing challenge needs to be directed towards the next enclosing track container.
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. And each of these local representation components holds onto a display context, which in fact links it into two different display widget stacks in the two parts of the actual timeline display. Thus, if a component, especially a track, adds a child, it has to pass this child to its own display context, which in turn has to consult its parts, attach new child widgets to those parts and form a new child display context, which then has to be returned to the newly formed child and stored there for future referral.
!!!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 a 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 automatically to inject something into the display
* when a model element happens to be destructed, the corresponding display element has to be removed.
* such might be triggered indirectly, by clean-up of leftovers, since the DiffApplicator re-orders and deletes by leaving some data behind
* the diff also re-orders model elements, which does not have an immediate effect on the display, but needs to be interpreted separately.
Together this means we get a fix up stage after model changes, where the display is re-adjusted to fit the new situation. This works in concert with the display manager representing only those elements as actual widgets, which get a real chance to become visible. This way we can build on the assumption that the actual number of widgets to be managed any time remains so small as to get away with simple linear list processing. It remains to be seen how far this assumption can be pushed -- the problem is that the GTK container components don't support anything beyond such simple linear list processing; there isn't even a call to remove all child widgets of a container in a single pass.
</pre>
</div>
<div title="HighLevelModel" modifier="Ichthyostega" created="200808152311" modified="201505310109" tags="Model spec design discuss img" changecount="2">

View file

@ -114,8 +114,7 @@
Details im&#160;&#160;TiddlyWiki....
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="idea"/>
</node>
<node CREATED="1479678634213" ID="ID_432405176" MODIFIED="1479678650821" TEXT="rekursive Struktur schaffen">
@ -135,6 +134,188 @@
<node CREATED="1479688653913" ID="ID_1435784278" MODIFIED="1479688666394" TEXT="dieser wiederum mu&#xdf; f&#xfc;r jede Erweiterung konsultiert werden"/>
</node>
</node>
<node CREATED="1479774700668" ID="ID_1407821684" MODIFIED="1479774704971" TEXT="Mutation">
<node CREATED="1479774705839" ID="ID_301222108" MODIFIED="1479774729887" TEXT="Problem">
<icon BUILTIN="messagebox_warning"/>
<node COLOR="#d30f0f" CREATED="1479774731141" ID="ID_1523088286" MODIFIED="1479774739456" TEXT="Element != Widget"/>
<node CREATED="1479774747753" ID="ID_279553704" MODIFIED="1479774761996" TEXT="Umordnen der Elemente hat keinen Effekt auf die Anzeige"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1479774764575" ID="ID_1338013643" MODIFIED="1480120324802" TEXT="DiffApplikator geht einfach von STL-Collection aus"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1480120294912" HGAP="31" ID="ID_1734639851" MODIFIED="1480120514431" VSHIFT="23">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
grunds&#228;tzliches
</p>
<p>
Problem
</p>
</body>
</html></richcontent>
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1480120349873" ID="ID_795629510" MODIFIED="1480120398685" TEXT="Diff repr&#xe4;sentiert &#xc4;nderungen indirekt"/>
<node CREATED="1480120487509" ID="ID_1693545323" MODIFIED="1480120500957">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
speziell die Umordnungen <i>ergeben sich</i>
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1480120359191" ID="ID_1039094699" MODIFIED="1480120390087" TEXT="Widget-Container hat keine &#xc4;nderungs-Schnittstelle"/>
</node>
</node>
<node CREATED="1480120516442" ID="ID_880374813" MODIFIED="1480120518812" TEXT="Ans&#xe4;tze">
<node CREATED="1480120531935" FOLDED="true" ID="ID_1894474191" MODIFIED="1480123859812" TEXT="Dekorator auf TreeMutator">
<icon BUILTIN="button_cancel"/>
<node CREATED="1480121069464" ID="ID_1707876611" MODIFIED="1480121073803" TEXT="Operationen mitlesen"/>
<node CREATED="1480121074703" ID="ID_1346494338" MODIFIED="1480123649491" TEXT="Notifikations-Schnittstelle">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...und der Dekorator w&#252;rde die beobachteten Operationen
</p>
<p>
an diese Notifikations-Schnittstelle senden.
</p>
<p>
Implementiert w&#252;rde sie vom jeweiligen Widget
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1480123478013" ID="ID_1281325973" MODIFIED="1480123610437" TEXT="eine Ebene zu tief, aber geht noch">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
korrekt w&#228;re, die Diff-Verben mitzulesen.
</p>
<p>
<i>Das </i>geht aber nicht, weil wir intern (aktiv) iterieren.
</p>
<p>
Wollten wir das doch, m&#252;&#223;ten wir das gesamte Diff-Applikator-Design wegwerfen.
</p>
<p>
</p>
<p>
Da aber eigentlich eine 1:1-Zuordnung zwischen Diff-Verben und Operations-Primitiven besteht,
</p>
<p>
k&#246;nnte man trotzdem (mit etwas H&#228;ngen und W&#252;rgen) noch hinkommen.
</p>
<p>
Der Dekorator w&#252;rde also auf dem TreeMutator sitzen...
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1480121092492" ID="ID_1543810707" MODIFIED="1480123739970" TEXT="L&#xf6;schungen nur heuristisch zu erkennen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Weil wir die &quot;skip&quot;-Operation f&#252;r zwei Zwecke verwenden,
</p>
<p>
und man im Skip nicht wei&#223;, ob man das Element &#252;berhaupt noch anfassen darf,
</p>
<p>
denn es k&#246;nnte ja auch ein von &quot;find&quot; zur&#252;ckgelassener M&#252;ll sein.
</p>
<p>
Daher gibt es die matchSrc-Operation. Effektiv wird die aber nur bei einem Delete aufgerufen...
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1480123741362" ID="ID_586646895" MODIFIED="1480123856739" TEXT="unsauber, h&#xe4;sslich, ungl&#xfc;cklich">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<ul>
<li>
man sitzt mit dem Detektor unter dem API
</li>
<li>
dadurch entstehen &quot;ungeschriebene Regeln&quot;, wie das API auzurufen ist
</li>
<li>
alternativ k&#246;nnten wir die Operationen komplett 1:1 definieren, also eine explizite delete-Operation einf&#252;hren
</li>
<li>
daf&#252;r w&#252;rde dann die matchSrc wegfallen, was praktisch alle sinnvollen Unit-Tests stark beschr&#228;nkt.
</li>
</ul>
</body>
</html>
</richcontent>
</node>
<node CREATED="1480123380843" ID="ID_1717666432" MODIFIED="1480123757244" TEXT="w&#xe4;re prinzipiell machbar">
<icon BUILTIN="forward"/>
</node>
</node>
<node CREATED="1480121177145" ID="ID_1964453367" MODIFIED="1480123456994" TEXT="Widget is delegate/slave">
<icon BUILTIN="button_ok"/>
<node CREATED="1480121209277" ID="ID_834494092" MODIFIED="1480123869816" TEXT="Hinzuf&#xfc;gungen erzeugen neue Widgets">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1480121219579" ID="ID_1906821886" MODIFIED="1480123871892" TEXT="L&#xf6;schungen entfernen das zugeh&#xf6;rige Widget">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1480121271493" ID="ID_1169797519" MODIFIED="1480123874577" TEXT="Reihenfolge ist nebens&#xe4;chlich">
<icon BUILTIN="idea"/>
<node CREATED="1480123311260" ID="ID_1288896772" MODIFIED="1480123320575" TEXT="Reihenfolge der Widgets == Z-Ordnung"/>
<node CREATED="1480123321707" ID="ID_1842225087" MODIFIED="1480123329269" TEXT="das ist ein reiner UI-Belang"/>
<node CREATED="1480123330521" ID="ID_255024472" MODIFIED="1480123352490" TEXT="Reihenfolge der Tracks wird im UI realisiert"/>
<node CREATED="1480123353030" ID="ID_871730723" MODIFIED="1480123366705" TEXT="Reihenfolge der Clips wird redundant (durch Position) gegeben"/>
</node>
</node>
</node>
<node CREATED="1480123881912" HGAP="6" ID="ID_1550301342" MODIFIED="1480123892997" TEXT="Schlu&#xdf;folgerung" VSHIFT="3">
<icon BUILTIN="yes"/>
<node CREATED="1480123900845" ID="ID_1099759004" MODIFIED="1480123908024" TEXT="wir mutieren ein Modell-Objekt"/>
<node CREATED="1480123908524" ID="ID_599072568" MODIFIED="1480123917999" TEXT="das Widget h&#xe4;ngt an diesem als kompletter Slave"/>
<node CREATED="1480123920371" ID="ID_413127533" MODIFIED="1480123932333" TEXT="Registrierung / Deregistrierung mu&#xdf; vollautomatisch sein"/>
<node CREATED="1480124094867" ID="ID_1956638484" MODIFIED="1480124123833">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
<i>nach </i>der Mutation erfolgt <b>Display-Neubewertung</b>
</p>
</body>
</html>
</richcontent>
<node CREATED="1480124145476" ID="ID_1268974326" MODIFIED="1480124152519" TEXT="f&#xfc;r einen ganzen Scope"/>
<node CREATED="1480124153979" ID="ID_887707794" MODIFIED="1480124172604" TEXT="stellt fest, was gezeigt werden mu&#xdf;"/>
<node CREATED="1480124174144" ID="ID_1145067443" MODIFIED="1480124210543" TEXT="synthetisiert Anzeige-Parameter (z.B: Koordinaten, Z-Ordnung)"/>
<node CREATED="1480124235128" ID="ID_162610711" MODIFIED="1480124250602" TEXT="mu&#xdf; inkrementell arbeiten und bestehende Widgets anpassen"/>
</node>
</node>
</node>
</node>
<node CREATED="1476376943985" HGAP="22" ID="ID_1422206856" MODIFIED="1479434895083" TEXT="Viewer" VSHIFT="10"/>
<node CREATED="1479434763643" HGAP="25" ID="ID_1572413636" MODIFIED="1479434887744" TEXT="Clip" VSHIFT="31">
@ -195,8 +376,7 @@
UI-Bus gilt nur f&#252;r <b>globale Belange</b>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<node CREATED="1479601788237" ID="ID_1871715779" MODIFIED="1479601795259" TEXT="wichtige neue Unterscheidung">
<icon BUILTIN="idea"/>
</node>
@ -216,8 +396,7 @@
es geht nur um <i>Rollen</i>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1479601907221" ID="ID_286625648" MODIFIED="1479601914101" TEXT="es ist ein Design-Pattern">
<icon BUILTIN="yes"/>
@ -232,8 +411,7 @@
das lokale Element mu&#223; nur als View <i>fungieren</i>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1479601945839" ID="ID_254470029" MODIFIED="1479601954903" TEXT="Model-View-Presenter">
<icon BUILTIN="forward"/>
@ -257,8 +435,7 @@
transformieren
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<node CREATED="1479602412095" ID="ID_832808487" MODIFIED="1479602415154" TEXT="Frame"/>
<node CREATED="1479602415654" ID="ID_1358720" MODIFIED="1479602418642" TEXT="Frame + Layout"/>
<node CREATED="1479602419134" ID="ID_1788706818" MODIFIED="1479602425033" TEXT="sub-Clips im Layout"/>