expand analysis on the (possible) global structure of timeline display
This commit is contained in:
parent
3757a56ac9
commit
b1d0eaad8e
2 changed files with 59 additions and 8 deletions
|
|
@ -1432,7 +1432,7 @@ Lumiera agrees to this common understanding (of most film editing and sound hand
|
|||
→ PipeHandling
|
||||
</pre>
|
||||
</div>
|
||||
<div title="ClipPresenter" creator="Ichthyostega" modifier="Ichthyostega" created="201611200101" modified="201611200204" tags="def GuiPattern img" changecount="7">
|
||||
<div title="ClipPresenter" creator="Ichthyostega" modifier="Ichthyostega" created="201611200101" modified="201611201650" tags="def GuiPattern img" changecount="9">
|
||||
<pre>//mediating entity used to guide and control the presentation of a clip in the UI.//
|
||||
|
||||
The clip representation in the UI links together two distinct realms and systems of concerns. For one, there are the properties and actions performed on the clip for sake of editing a movie. Everything which has a tangible effect on the resulting render. These information and operations are embodied into the HighLevelModel and can be manipulated script driven, without relying on an UI. But beyond that, there is also the local mechanics of the interface, everything which makes working on the edit and interacting with the model into a smooth experience and workflow. With respect to this concerns, a clip is a self-contained entity which owns its own state and behaviour. The ClipPresenter is the pivotal element to link together those two realms.
|
||||
|
|
@ -2420,13 +2420,13 @@ On a second thought, the fact that the [[Bus-MObject|BusMO]] is rather void of a
|
|||
:sound mixing desks use list style arrangement, and this has proven to be quite viable, when combined with the ability to //send over// output from one mixer stripe to the input of another, allowing to build arbitrary complex filter matrices. On the other hand, organising a mix in //subgroups// can be considered best practice. This leads to arranging the pipes //as wood:// by default and on top level as list, optionally expanding into a subtree with automatic rooting, augmented by the ability to route any output to any input (cycles being detected and flagged as error).
|
||||
</pre>
|
||||
</div>
|
||||
<div title="GtkCustomDrawing" creator="Ichthyostega" modifier="Ichthyostega" created="201610300111" modified="201611172341" tags="spec GuiPattern" changecount="11">
|
||||
<div title="GtkCustomDrawing" creator="Ichthyostega" modifier="Ichthyostega" created="201610300111" modified="201611201629" tags="spec GuiPattern" changecount="12">
|
||||
<pre>//some information how to achieve custom drawing with ~GTKmm...//
|
||||
valuable reference documentation comes bundled with lib ~GTKmm, in the guide [["Programming with GTKmm"|https://developer.gnome.org/gtkmm-tutorial/stable/index.html.en]]
|
||||
* the chapter detailing [[use of the Gtk::DrawingArea|https://developer.gnome.org/gtkmm-tutorial/stable/chapter-drawingarea.html.en]], including an introduction to [[Cairomm|https://www.cairographics.org/documentation/cairomm/reference/]]
|
||||
* the chapter about [[constructing a custom widget|https://developer.gnome.org/gtkmm-tutorial/stable/sec-custom-widgets.html.en]]
|
||||
|
||||
Basically we have to handle the {{{signal_draw}}} events. Since we need to control the event processing, it is recommended to do this event handling by //overriding the {{{on_draw()}}} function,// not by connecting a slot directly to the signal. Two details are to be considered here: the //return value// controls if the event can be considered as fully handled. If we return {{{false}}}, enclosing (parent) widgets get also to handle this event. This is typically what we want in case of custom drawing. And, secondly, if we derive from any widget, it is a good idea to invoke the //parent implementation of {{{on_draw()}}} at the appropriate point.// This is especially relevant when our custom drawing involves the ''canvas widget'' ({{{Gtk::Layout}}}), which has the ability to place several further widgets embedded onto the canvas area. Without invoking this parent event handler, those embedded widgets won't be shown.
|
||||
Basically we have to handle the {{{signal_draw}}} events. Since we need to control the event processing, it is recommended to do this event handling by //overriding the {{{on_draw()}}} function,// not by connecting a slot directly to the signal. Two details are to be considered here: the //return value// controls if the event can be considered as fully handled. If we return {{{false}}}, enclosing (parent) widgets get also to handle this event. This is typically what we want in case of custom drawing. And, secondly, if we derive from any widget, it is a good idea to invoke the //parent implementation of {{{on_draw()}}} at the appropriate point.// This is especially relevant when our custom drawing involves the ''canvas widget'' [[Gtk::Layout|GtkLayoutWidget]], which has the ability to place several further widgets embedded onto the canvas area. Without invoking this parent event handler, those embedded widgets won't be shown.
|
||||
|
||||
Typically, when starting the draw operation, we retrieve our //allocation.// This is precisely the rectangle reserved for the current widget, //insofar it is visible.// Especially this means, when a larger canvas is partially shown with the help of scrollbars, the allocation is the actually visible rectangle, not the virtual extension of the canvas. Each scrollbar is associated with a {{{Gtk::Adjustment}}}, which is basically a bracketed value with preconfigured step increments. The //value// of the adjustment corresponds to the //coordinates of the viewport// within the larger area of the canvas. Since coordinates in Gtk and Cairo are oriented towards the right and downwards, the value properties of both adjustments (horizontal and vertical) together give us the coordinates of the upper left corner of the viewport. The maximum value possible within such an adjustment is such as to fulfil {{{max(value) + viewport-size == canvas-size}}}. By printing values from the {{{on_draw()}}} callback, it can be verified that Gtk indeed handles it precisely that way.
|
||||
|
||||
|
|
@ -2797,7 +2797,7 @@ Now, when invoking an operation on some public interface, the code in the lower
|
|||
<pre>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.
|
||||
</pre>
|
||||
</div>
|
||||
<div title="GuiTimelineView" creator="Ichthyostega" modifier="Ichthyostega" created="201410160100" modified="201611180106" tags="GuiPattern design decision draft" changecount="11">
|
||||
<div title="GuiTimelineView" creator="Ichthyostega" modifier="Ichthyostega" created="201410160100" modified="201611201642" tags="GuiPattern design decision draft" changecount="13">
|
||||
<pre>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
|
||||
|
|
@ -2810,10 +2810,10 @@ Several timeline views may be present at the same time -- and there is not neces
|
|||
Session, Binding and Sequence are the mandatory ingredients.
|
||||
|
||||
!Basic layout
|
||||
The representation is split into a ''Header pane'' exposing structure and configuration, and a ''Content pane'' extending in time. A ''Time ruler'' running alongside the content pane represents the //position in time.// Beyond this temporal dimension, the content area is conceived as a flexible working space. This working space //can// be structured hierarchically -- when interacting with the GUI, hierarchical nesting will be created and collapsed on demand. Contrast this with conventional editing applications which are built upon the rigid notion of "Tracks": Lumiera is rather based on //Pipes// than Tracks.
|
||||
The representation is split into a ''Header pane'' exposing structure and configuration, and a ''Content pane'' extending in time. A ''Time ruler'' running alongside the content pane represents the //position in time.// Beyond this temporal dimension, the content area is conceived as a flexible working space. This working space //can// be structured hierarchically -- when interacting with the GUI, hierarchical nesting will be created and collapsed on demand. Contrast this with conventional editing applications which are built upon the rigid notion of "Tracks": Lumiera is based on //Pipes// rather than Tracks.
|
||||
In the temporal dimension, there is the usual scrolling and zooming of content, and possibly a selected time range, and after establishing a ViewerPlayConnection, there is an effective playback location featured as a "Playhead"
|
||||
The workspace dimension (vertical layout) is more like a ''Fork'', which can be expanded recursively. More specifically, each strip or layer or "track" can be featured in //collapsed// or //expanded state.//
|
||||
* the collapsed state features a condensed representation ("the tip of the iceberg"). It exposes just the topmost entity, and might show a rendered (pre)view. Elements might be stacked on top, but any element visible here //is accessible.//
|
||||
* the collapsed state features a condensed representation ("the tip of the iceberg"). It exposes just the topmost entity, and might show a rendered (pre)view. Elements might be stacked on top, but any element visible here //is still accessible.//
|
||||
* when expanding, the content unfolds into...
|
||||
** a ''scope ruler'' to represent the whole sub-scope. This is rendered as a small pane, extending horizontally, holding any locally attached labels and track-wide or temporally scoped effects
|
||||
** the content stack, comprised of [[clip widgets|GuiClipWidget]], attached effects and transitions
|
||||
|
|
@ -2827,14 +2827,37 @@ In the most general case, there can be per-track content and nested content at t
|
|||
&rarr; important question: how to [[organise the widgets|GuiTimelineWidgetStructure]]
|
||||
</pre>
|
||||
</div>
|
||||
<div title="GuiTimelineWidgetStructure" creator="Ichthyostega" modifier="Ichthyostega" created="201410250002" modified="201611180443" tags="GuiPattern discuss decision impl" changecount="61">
|
||||
<div title="GuiTimelineWidgetStructure" creator="Ichthyostega" modifier="Ichthyostega" created="201410250002" modified="201611202144" tags="GuiPattern discuss decision impl" changecount="70">
|
||||
<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).
|
||||
|
||||
Right away, this topic touches a tricky design and architectural challenge: the → question [[how to organise custom widgets|GuiCustomWidget]].
|
||||
|
||||
In a nutshell, ~GTKmm offers several degrees of customisation, namely to build a custom widget class, to build a custom container widget, and to use the {{{Gtk::Layout}}} "canvas widget", possibly combined with //custom drawing.// In addition to assembling a timeline widget class by combining several nested panes, the timeline display needs to rely on the latter approach to allow for the necessary flexible arrangement of [[clip widgets|GuiClipWidget]] within the [[track fork|Fork]].
|
||||
In a nutshell, ~GTKmm offers several degrees of customisation, namely to build a custom widget class, to build a custom container widget, and to use the [[Gtk::Layout "canvas widget"|GtkLayoutWidget]], possibly combined with //custom drawing.// In addition to assembling a timeline widget class by combining several nested panes, the timeline display needs to rely on the latter approach to allow for the necessary flexible arrangement of [[clip widgets|GuiClipWidget]] within the [[track fork|Fork]].
|
||||
|
||||
!the header pane problem
|
||||
From general considerations we draw the conclusion to have an track header pane area always visible to the left. For one this means that our top level widget organisation in the timeline will be a horizontal split. And then this means that we get two distinct sub widgets, whose vertical layout needs to be kept in sync. And even more so, it is likely the most adequate implementation technique is different in both parts: the header pane looks like a classical fit for the paradigm of nested boxes and grid layout within those boxes, while the right part, the actual track contents impose very specific layout constraints not served by any of the pre-existing layout containers, which means we have to resort to custom drawing on a canvas widget. Following this line of thought, we need an overarching layout manager to coordinate these two disjoint technologies. Any viable alternatives?
|
||||
|
||||
!!!considering a table grid layout
|
||||
The layout mechanics we try to establish here by explicit implementation would be more or less a given, if instead we'd build the whole timeline display from one base widget, which needs to be a table layout, i.e. {{{Gtk::Grid}}}. We'd use two columns, one for the header pane area, one for the timeline display, and we'd use N+1 rows, with the head row holding the time ruler and the additional rows holding individual tracks. But to get the specific UI mechanics desirable for a timeline display, we had to introduce some twists:
|
||||
* //Scrolling is rather special.// We could use the default scrolling mechanisms in vertical direction only, while, as far as the Gtk Layout management is involved, we have to dress up things such as to make it appear as limited to the available horizontal space, so GTK never attempts to scroll the grid as a whole horizontally.
|
||||
* rather, we have to integrate a free standing horizontal scrollbar somewhere //in the second column.// This scrollbar has to be tied to a scrolling/zooming function, which we apply by explicit coding synchronously to all cell content in all rows of the second column.
|
||||
* and then obviously we'd have to add a canvas widget with custom drawing to each of those cells in the second column, each featuring the contents of a single track.
|
||||
* the //nested scopes are difficult to represent in this layout.// This is, because we use a row for each track-like structure at most fine grained level. If we were to create additional visual clues to indicate nested structures, like indentation or a bracketing structure, we'd have to implement those by repetitively adding appropriate graphical structures to each row in the first column.
|
||||
* it would be non trivial to place controls (e.g. a volume or fade control) acting on all sub-tracks within a group. We'd have to add those controls in a top row representing the whole group and then we'd have to indicate somehow graphically that those pertain to all nested tracks. In fact, this problem is not limited to a table grid implementation approach, since it is related to usage of screen real estate. But a table grid implementation takes away almost all remaining flexibility and thus makes a workable solution much harder: we'd have to allocate a lot of vertical space for those controls, space, which is wasted on the right side, within the track content display, since there all we need is a small overview ruler without much content demands in vertical direction.
|
||||
* this problem is mitigated once we add a track with //automation data// linked to the mentioned controls. Yet still, this is not the default situation...
|
||||
On the other hand, what would be the //obvious benefits...?//
|
||||
* we just have to add stuff in both the left / right part of the display and get the vertical space management sorted out by framework code
|
||||
While the special setup for scrolling doesn't really count (since it is necessary anyway), after this initial investigation it seems clear that a global grid layout doesn't yield enough benefit to justify all the quirks and limitations its use would impose.
|
||||
|
||||
!!!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.
|
||||
|
||||
|
||||
!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 "current" or a "parent" reference point.
|
||||
</pre>
|
||||
</div>
|
||||
<div title="HighLevelModel" modifier="Ichthyostega" created="200808152311" modified="201505310109" tags="Model spec design discuss img" changecount="2">
|
||||
|
|
|
|||
|
|
@ -96,6 +96,34 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1479678484210" ID="ID_1561971595" MODIFIED="1479678488573" TEXT="Struktur">
|
||||
<node CREATED="1479678496272" ID="ID_807567871" LINK="file:///home/hiv/devel/lumi/wiki/renderengine.html#GuiTimelineWidgetStructure" MODIFIED="1479678687574" TEXT="Analyse">
|
||||
<node CREATED="1479678503071" ID="ID_806149006" MODIFIED="1479678681170" TEXT="Grid bringt nix">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Das war zwar schon meine Bauchgefühl,
|
||||
</p>
|
||||
<p>
|
||||
habe aber sicherheitshalber diese Analyse nochmal gemacht.
|
||||
</p>
|
||||
<p>
|
||||
Details im  TiddlyWiki....
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1479678634213" ID="ID_432405176" MODIFIED="1479678650821" TEXT="rekursive Struktur schaffen">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node CREATED="1479678509822" ID="ID_915690336" MODIFIED="1479678663117" TEXT="Scrolling ist notwendig speziell"/>
|
||||
</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">
|
||||
|
|
|
|||
Loading…
Reference in a new issue