UI-Lifecycle: research regarding GTK's activation signal. Document the findings
- activation signal is a facility offered and used solely by Gtk::Application - we do not need nor want an Gtk::Application, we deal with our own application concerns as we see fit.
This commit is contained in:
parent
f33573daec
commit
7db8bf4c0c
4 changed files with 758 additions and 141 deletions
9
doc/technical/code/gtk/index.txt
Normal file
9
doc/technical/code/gtk/index.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
GTK -- UI toolkit and framework
|
||||
===============================
|
||||
|
||||
//Menu: label GTK
|
||||
|
||||
|
||||
Within this subsection we collect some random bits of information related to our
|
||||
use of the GTK windowing toolkit for building Lumiera's user interface.
|
||||
|
||||
69
doc/technical/code/gtk/startup.txt
Normal file
69
doc/technical/code/gtk/startup.txt
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
GTK start-up
|
||||
============
|
||||
:Date: 2018
|
||||
|
||||
_some insights regarding the start-up of GTK and related framework aspects_
|
||||
|
||||
A facility like GTK can be seen to serve various needs. It can be used to simplify
|
||||
the arduous task of building a graphical user interface, but it can also be seen as
|
||||
a one-stop solution for just creating a (``damn modern cool'') application, which
|
||||
all ``reasonable'' people today expect to have a shiny GUI. In fact, we can identify
|
||||
these two different levels of support, which inevitably create conflicting goals.
|
||||
|
||||
- GTK-the-framework shall be easy to use and cover everything I never wanted to know
|
||||
about user interfaces. Ideally, I just inherit from a base class, implement two or
|
||||
three abstract methods and fill in my actual working logic.
|
||||
- GTK-the-toolkit is a collection of prefabricated building blocks, ready to be used
|
||||
and put into action, by people with a clear conception about what is required for
|
||||
a productive UI and how to achieve that in detail.
|
||||
|
||||
Needless to say that Lumiera's use of GTK falls into the second category. Even more so,
|
||||
as the GTK UI is just a plug-in, loaded optionally, and not identical with the application
|
||||
as such. Which often places us into a tricky situation -- obviously GTK-the-framework is
|
||||
what attracts most attention, both from the users and the developers.
|
||||
|
||||
The Gtk::Application
|
||||
--------------------
|
||||
*In short*: we do not use it, we do not want it, we do not need it, it's just obnoxious.
|
||||
|
||||
In the _good old days(TM)_ there used to be a singleton class `GTK::Main`. You'd activate
|
||||
your application by invoking the blocking function `Main::run()`. This design was simple
|
||||
and sweet, but turned out to be too rigid once people started to expect lots of things
|
||||
to ``just work''. Consequently, `Gtk::Main` was deprecated by the GTK-developers and
|
||||
``replaced'' by `Gtk::Application`. Unfortunately, this move reflects a paradigm shift
|
||||
from _toolkit_ towards an _application building framework._ This framework includes
|
||||
|
||||
- command line parsing with extension points for custom argument handling
|
||||
- a ready-made framework of _actions_, to be arranged into menus and toolbars
|
||||
- management of ``the application instance'', with inter process communication
|
||||
in case the deaf user double clicks the application icon a second time
|
||||
- registration with the desktop, interconnection with the D-Bus
|
||||
- the notion of a ``associated document type'' and ``desktop actions''
|
||||
to be forwarded to the implementing application, which thus needs to
|
||||
be invocable in service-style.
|
||||
|
||||
None of the above is _evil_ in any sense, much is even useful. However, there is a notion
|
||||
of a working style, underlying the vision for Lumiera: work is a long-term undertaking,
|
||||
organised into a project and carried out in a fixed and controlled environment over the
|
||||
course of an extended time period. Basically we envision the user to make some _footage_
|
||||
available to a _editing workstation_, and then to return to this very setup over the
|
||||
course of weeks, or months, or years, expecting everything to remain reliably the same
|
||||
as configured initially.
|
||||
|
||||
Based on this model, we basically want to shape all application global concerns in a
|
||||
very specific way -- and almost all the standard solutions offered by GTK-the-framework
|
||||
tend to get into our way of working. For this reason
|
||||
|
||||
- we have our own framework of subsystems
|
||||
- we build our own approach towards command line handling
|
||||
- we rely on the notion of a project to define a specific work environment
|
||||
- we want menus and toolbars to be configurable based on both the project and user preference
|
||||
- we deliberately allow for various ways to launch the application, possibly in multiple instances
|
||||
- we build our own system to navigate within the UI, spanning several top-level windows and desktops.
|
||||
|
||||
Consequently, none of the services offered by `Gtk::Application` is of any use for us. After reading
|
||||
the source code, we came to the conclusion that it is perfectly valid to sidestep all those aspects
|
||||
of GTK, and just perform those small number of toolkit initialisation steps -- previously invoked
|
||||
by `Gtk::Main` -- directly from our application code. Basically Lumiera's `gui::ctrl::UiManager`
|
||||
replaces `Gtk::Main`, invokes `gtk_init` and enters the blocking event loop by calling `gtk_main`.
|
||||
|
||||
|
|
@ -3058,7 +3058,7 @@ The consequence is that both sides, "the core" and "the UI"
|
|||
Rather, the core sends ''diff messages'' up to the UI, indicating how it sees this virtual structure to be changing. The UI reflects these changes into //its own understanding and representation,// that is here a structure of display widgets. When the user interacts with these structures of the presentation layer, ''command messages'' are generated, using the element ~IDs to designate the arguments of the intended operation. This again causes reaction and change in the core, which is reflected back in the form of further diff messages. (→ GuiCommandCycle)
|
||||
</pre>
|
||||
</div>
|
||||
<div title="GuiContentPopulation" creator="Ichthyostega" modifier="Ichthyostega" created="201807121909" modified="201807281627" tags="GuiIntegration spec draft" changecount="4">
|
||||
<div title="GuiContentPopulation" creator="Ichthyostega" modifier="Ichthyostega" created="201807121909" modified="201808031636" tags="GuiIntegration spec draft" changecount="9">
|
||||
<pre>//install a listener into the session to cause sending of population diff messages.//
|
||||
The Lumiera application is not structured as an UI with internal functionality dangling below. Rather, the architecture calls for several self-contained subsystems, where all of the actual "application content" is modelled within the [[Session]] subsystem. The UI implements the //mechanics of user interaction// -- yet as far as content is concerned, it plays a passive role. Within the UI-Layer, there is a hierarchy of presentation entities, to mirror the structure of the session contents. These entities are built step by step, in reception of //population diff messages// sent upwards from the session.
|
||||
|
||||
|
|
@ -3067,8 +3067,10 @@ To establish this interaction pattern, a listener gets installed into the sessio
|
|||
!Trigger
|
||||
It is clear that content population can commence only when the GTK event loop is already running and the application frame is visible and active. For starters, this sequence avoids all kinds of nasty race conditions. And, in addition, it ensures a reactive UI; if populating content takes some time, the user may watch this process through the visible clues given just by changing the window contents and layout in live state.
|
||||
|
||||
And since we are talking about a generic facility, the framework of content population has to be established in the GuiTopLevel. Now, the top-level in turn //starts the event loop// -- thus we need to //schedule// the trigger for content population. In fact, we have already a generic framework to schedule any kind of "reaction" into the UI: the {{{NotificationService}}} (accessible via {{{GuiNotification}}} façade). The general plan to trigger content population thus boils down to
|
||||
* have {{red{someone ^^WIP 7/18^^}}} inject the population trigger via {{{NotificationService}}}
|
||||
And since we are talking about a generic facility, the framework of content population has to be established in the GuiTopLevel. Now, the top-level in turn //starts the event loop// -- thus we need to //schedule// the trigger for content population. The existing mechanisms are not of much help here, since in our case we //really need a fully operative application// once the results start bubbling up from Proc-Layer. The {{{Gio::Application}}} offers an "activation signal" -- yet in fact this is only necessary due to the internals of {{{Gio::Application}}}, with all this ~D-Bus registration stuff. Just showing a GTK window widget in itself does not require a running event loop (although one does not make much sense without the other). The mentioned {{{signal_activation()}}} is emitted from {{{g_application_run()}}} (actually the invocation of {{{g_application_activate()}}} is burried within {{{g_application_real_local_command_line()}}}, which means, the activation happens //immediately before// entering the event loop. Which pretty much rules out this approach in our case, since Lumiera doesn't use a {{{Gtk::Application}}}, and moreover the signal would still induce the (small) possibility of a race between the actual opening of the GuiNotificationFacade and the start of content population from the [[Proc-Layer thread|SessionSubsystem]].
|
||||
|
||||
The general plan to trigger content population thus boils down to
|
||||
* have the InteractionDirector inject the population trigger with the help of {{{Glib::signal_timeout()}}}
|
||||
* the trigger itself issues a command towards the session
|
||||
* execution of aforementioned command activates sending of population / diff messages
|
||||
* on shutdown of the top-level, send the corresponding deactivation command </pre>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue