This is a very pervasive change and basically turns the whole top-level
of the GTK-UI bottom-up. If this change turns out right, it would likely
solve #1048
WARNING: in parts not implemented, breaks UI
...which itself is obsolete and needs to be redesigned from scratch.
For now we create a local instance of this obsolete PlaybackController
in each viewer panel and we use a static accessor function to just some
instance. Which would break if we start playback with multiple viewer
panels. But we can't anyway, since the Player itself is also a broken
leftover from an obsoleted design study from the early days.
so why care...
unfortunately boost/program-options make the boost reference-wrapper visible
And it doesn't help to alias to std::ref at the definition site of the
problematic function (in TimeControl), because this itself is picked up
via ADL
So this is not really a solution, rather a workaround, in the hope
that boost will clean-up this ambiguity eventually
as a rule, one should not rely on "using namespace xyz",
since this makes organisation of minimal header includes near impossible.
You end up with mass includes in some "top level" headers, resulting
in painfully slow compilation turnaround times.
In exceptional cases, using namespace foo might be adequate though
- WindowList (ex WindowManager)
- Project & Controller
the latter ones are defunct and can be replicated down into each
of the old timeline pannel instances. They just serve the purpose
to keep this old code barely functional, so it can be used as reference
for building the new timeline
There seems to be a mismatch in the arrangement of the top-level entities
* we support multiple windows, yet from reading the code, you'd ge the impression we aren't really aware we have multiple top-level windows
* the `WindowManager` is the core UI manager, which feels like a mix-up in concerns
* the `WorkspaceWindow::createUI()` does the global UI initialisation. Again, we have multiple workspace windows.
* `GtkLumiera::main()` creates a `Model` and a `Controller` in local function scope, but stores the `WindowManager` in an object field.
* it seems, for that very reason, `GtlLumiera` needed to be a singleton, to allow by-name access to "the" `WindowManager`
* needless to say, this causes a host of problems when shutting down the UI.
The idea is to introduce a dedicated UiManager, to deal with the central
framework induced concerns solely, and to demote the WindowManager and the
WorkspaceWindows to care only for their local concerns
in fact it just does not fulfil any of the behavioural properties
of a full-fledged UI-Element. All it needs is an uplink bus connection,
so let's just keep it as that
Sidenote: I've realised today that such a "free standing" BusTerm
without registration in Nexus is a good idea and acceptable solution.
yes, it's a cycle and indeed quite tricky.
Just verified it (again) with the debugger and saw all
dtor calls happening in the expected order. Also the number
of Nexus registration is sane
Now I've realised that there are two degrees of connectedness.
It is very much possible to have a "free standing" BusTerm, which
only allows to send uplink messages. In fact, this is how CoreService
is implemented, and probably it should also the way how to connect
the GuiNotification service...
due to investigating that Heisenbug, I understand the storage layout
more clearly. It occured to me that there is no reason to copy the
terminationHandler (functor) into an instance variable, since it is
easily possible to keep all of the invocation and error handling
confined within the scope of the run function, i.e. on stack.
So the effective memory layout does not change, but the legibility
of the code is improved, since we're able to remove the dtor and
simplyfy the ctor and avoid most of the member fields.
Reason was some insideous detail regarding Lambdas:
When a Lambda captures context, a *closure* is created.
And while the Lambda itself is generated code, pretty much
like an anonymous function, the closure depends on the context
that was captured. In our case here, the Lambda used to start
the thread was the problem: it captured the termCallback functor
from the argument of the enclosing function. In fact it did not
help or change anything if we successively package that lambda
into a function objet and store this by value, because the
lambda still refers to the transient function context present
on stack at the moment it was captured.
The solution is to revert back to a bind expression, since this
creates a dedicated storage for the bound function arguments
managed within the bind-functor. This makes us independent
from the call context
...because some Bus connections stem from elements which are
member of CoreService, thus the'll still be connected when the
sanity check in the dtor runs
But even with this fix, we still get a SEGFAULT
TODO
- is this actually a sensible idea, from a design viewpoint?
- in which way to bind GuiNotification for receiving diff messages?
- Problem with disconnnecting from Nexus on shutdown
Writing and debugging such tests is always an interesting challenge...
Fortunately this exercise didn't unveil any problem in the newly written
code, only some insidious problems in the test fixture itself. Which
again highlights the necessity, that each *command instance* needs
to be an independent clone from the original *command prototype*,
since argument binding messages and trigger messages can appear
in arbitrary order.
This is a little bit of functionality needed again and again;
first I thought to use the TypedCounter, but this would be overkill,
since we do not actually need different instances, and we do not need
to select by type when incrementing the counter. In fact, we do not
even need anything beyond just allocating a number.
So I made a new class, which can be used RAII style
the intention is to cover more of the full invocation path,
without running all of the application infrastructure. So this
second test cases simulates how messages are handled in CoreService,
where the CommandHandler (visitor) actually invokes the SessionCommand
facade
this was a spin-off activity from writing the SessionCommand
function(integration) test, where I noted that we can't just
capture "a time value" as command memento
basically this is not necessary, since the compiler figures out
to use the conversion to target type when attempting to resolve
an equality comparison. But it helps to avoid ambiguities in cases
where several conversion paths do exist, e.g. when comparing string
with C-string
explicitly observed with the debugger that the call path is sane;
the code looks innocuous, but it is quite magic how the compiler
picks precisely the right ctors and inserts conversions apropriately
command processing against the session is not yet implemented,
so to allow for unit testing, we magically recognise all commands
starting with "test." and invoke them directly within the Dispatcher.
With this addition, the basic functionality of the dispatcher works now
need also to start and stop the interface registry,
since by policy we do not run the application framework itself
for execution of the test suite; thus if some test actually needs
an application service, it must be started/stopped manually
From a purely logical viewpoint, it looked sensible to require an actual
value for an offset, especially since our time values are immutable.
But this has the unfortunate consequence that we'd be unable to use
an offset value as parameter for any command, since we store the arguments
as tuple and the tuple type has a default constructor. We might be able
to get around that problem, but such looks brittle to me; it is just
plain surprising for anyone not familiar with the internals of the
command system.
For that reason, I've now added a default ctor to the Offset type