...and perform the initialisation once, when attaching the first timeline to the UI
Now our code produces the following Gtk::WidgetPath (note the last node, which our code added)
window:backdrop:dir-ltr.background box:backdrop:dir-ltr.vertical box:backdrop:dir-ltr[2/3].horizontal widget:backdrop:dir-ltr[2/2] widget:backdrop:dir-ltr paned:backdrop:dir-ltr.vertical widget:backdrop:dir-ltr box:backdrop:dir-ltr.vertical notebook:backdrop:dir-ltr[1/1].frame paned:backdrop:dir-ltr.horizontal box:backdrop:dir-ltr.vertical fork.timeline
For context: The »Advice System« was coined a long time ago, in 2010,
based on the vague impression that it might be useful for that kind of application
we are about to build here. And, as can be expected, none of the usage situations
envisioned at that time was brought to bear. Non the less, the facility came in
handy at times, precisely because it is cross-cutting and allows to pass
information without imposing any systematic relationship between the
communication partners.
And now we've got again such a situation.
The global style manager in the UI has to build a virtual CSS path,
which is needed by drawing code somewhere deep down, and we absolutely
do not want to pass a reference to the style manager over 20 recursive calls.
The alternatives would be
(1) to turn the style manager into a public service
(2) to have a static access function somewhere
(3) to use a global variable.
For rationale, (1) would be overblown, because we do not actually request
a service to do work for us, rather we need some global piece of information.
(2) would be equivalent to (1), just more confusing. And (3) is basically
what the Advice system does, with the added benefit of a clear-cut service
access point and a well defined lifecycle.
This changeset adds the ability to check if actual Advice has been published,
which allows us to invoke the (possibly expensive) GTK path building and
style context building code only once.
- at some (yet to be defined) location, a virtual WidgetPath is constructed
and used to build a Gtk::StyleContext in accordance to the curren CSS
- within the drawing routine, we use Lumiera's Advice-System to access this info
Mostly, std::regexp can be used as a drop-in replacement.
Note: unfortunately ECMA regexps do not support lookbehind assertions.
This lookbehind is necesary here because we want to allow parsing values
from strings with additional content, which means we need explicitly to
exclude mismatches due to invalid syntax.
We can work around that issue like "either line start, or *not* one of these characters.
Alternatively we could consider to make the match more rigid,
i.e we would require the string to conain *only* the timecode spec to be parsed.
The existing implementation created a Buffer-Type based on various traits,
including the constructor and destructor functions for the buffer content.
However, this necessitates calculating the hash_value of a std::function,
which (see #294) is generally not possible to implement.
So with this changeset we now store an additional identity hash value
right into the TypeHandler, based on the target type placed into the buffer
This was prompted by a test failing under Boost-1.65 (--> see #294)
When reviewed now, the whole idea of testing Steam-Layer Commands for
equivalence feels a bit sketchy.
Just the comparison for the command ''identity'' alone seems sufficient,
i.e. the test if a command-ID is associated with the same backend-handle
and thus the same functor binding.
<rant>
the "improved" boost::rational can no longer compute 1/x
quite brilliant
</rant>
well... the reason is again signed vs unsigned int.
FrameRate is based on unsigned int (since a negative frame rate makes no sense).
seemingly, the newer boost libraries added an internal type rational<I>::bool_type
together with an overload for the equality comparison operator.
Unfortunately this now renders a comparison ambiguous with the constant zero (i.e. int{0})
because in our use case we employ rational<uint>.
Workaround is to compare explicitly to a zero of the underlying integer type.
some scripting to help creating a clean build environment for testing and bugfixes
* build_lumiera-build-dependencies.sh
All necessary steps to prepare a pristine Debian/Ubuntu/xx distro
for building Lumiera from source.
+ install the GPG pub key to trust
+ install a Lumiera DEP Repository to get the sources from
+ install build-essential
+ prepare, build and install NoBug
+ prepare, build and install libGDLmm
+ install the Lumiera build-dependencies from the DEB package
At that point, you should be able to start the build just with `scons`
* docker_open-lumiera-buildenv.sh
Additional bash magic to launch a docker container and inject the
build_lumiera-build-dependencies.sh script into an interactive shell
...when rendering this part, which shall be always visible.
And the rest of the profile needs to be rendered into a second canvas,
which is placed within a pane with scrollbar.
Implemented as a statefull iterator filter
TODO:
- actual draw operations not yet implemented
- find a way how to select the prelude / body part of the track profile
This is a consequence of subsuming the timeline ruler under the concept of an overview track
the template lib::PolymorphicValue seemingly picked the wrong
implementation strategy for "virtual copy support": In fact it is possible
to use the optimal strategy here, since our interface inherits from CloneSupport,
yet the metaprogramming logic picked the mix-in-adapter (which requires one additional "slot"
of storage plus a dynamic_cast at runtime).
The reason for this malfunction was the fact that we used META_DETECT_FUNCTION
to detect the presence of a clone-support-function. This is not correct, since
it can only detect a function in the *same* class, not an inherited function.
Thus, switching to META_DETECT_FUNCTION_NAME solves this problem
Well, this solution has some downsides, but since I intend to rewrite the
whole virtual copy support (#1197) anyway, I'll deem this acceptable for now
TODO / WIP: still some diagnostics code to clean up, plus a better solution for the EmptyBase
...which, in the end, can even be considered the more logical design choice,
since the "verb visitor" is a more elaborated and sophisiticated Verb-Token,
adding the special twist of embedded storage for variable function arguments
...yet still not successful.
The mechanism used for std::apply(tuple&) works fine when applied directly to the target function,
but fails to select the proper overload when passed to a std::forward-call for
"perfect forwarding". I tried again to re-build the situation of std::forward
with an explicitly coded function, but failed in the end to supply a type parameter
to std::forward suitably for all possible cases
...the simplified demo variant in try.cpp is accepted by the compiler and works as intended,
while the seemingly equivalent construction in verb-visitor.hpp is rejected by the compiler
This discrepancy might lead to a solution....?
...but bad news on the main issue:
the workaround consumes the tuple and thus is not tenable!
And what is even worse: the textbook implementation of std::apply is
equivalent to our workaround and also consumes the argument tuple
A simple yet weird workaround (and basically equivalent to our helper function)
is to wrap the argument tuple itself into std::forward<Args> -- which has the
effect of exposing RValue references to the forwarding function, thus silencing
the compiler.
I am not happy with this result, since it contradicts the notion of perfect forwarding.
As an asside, the ressearch has sorted out some secondary suspicions..
- it is *not* the Varargs argument pack as such
- it is *not* the VerbToken type as such
The problem clearly is related to exposing tuple elements to a forwarding function.
basically this is similar to std::invoke...
However, we can not yet use std::invoke, and in addition to this,
the actual situation is somewhat more contrieved, so even using std::invoke
would require to inject another argument into the passed argument tuple.
In the previous commit, I more or less blindly coded some solution,
while I did not fully understand the complaints of the compiler and why
it finally passed. I still have some doubts that I am in fact moving the
contents out of the tuple, which would lead to insidious errors on
repeated invocation.
Thus this invstigation here, starting from a clean slate textbook implementation
this is a generalisation of what we use in the diff framework;
typically you'd package the VerbToken into some kind of container,
together with the concrete invocation argument.
However, the specific twist here is that we want *variable arguments*,
depending on the actual operation called on the interpreter interface.
Up to now, PolymorphicValue was always used as-is, packaged into a typedef.
Now we consider using it as building block within an adapter for visitor-like tokens.
Which requires to pass-down the ctor call directly from the subclass, at least if we
want to emplace the resulting entity directly into a stdlib container.
As an asside, PolymorphicValue also used explicit specialisations for N-arguments,
which meanwhile can be replaced by variadic templates
...which leads to a specific twist here; while in the simple version
we still could hope to get away with a simple uniform uint argument,
the situation has changed altogether now. The canvas has turned into
some generic component, since it is instantiated two times, onece for
the time ruler and once for the actual body content. Thus all of the
specifics of the drawing code need to be pushed into a new, dedicated
renderer component. And this more or less forces us to pass all the
actual presentation variations through the invocation arguments of
the visitor.
So we're now off again for a digression, we need a more generalised visitor
After thinking the whole concept over several times, it occurred to me that
a separate implementation of a time ruler would be quite redundant with the
envisioned feature of per-track overview rulers. Following this line of thought,
the time ruler would just be some specifically configured overview ruler.
This has the somewhat unfortunate consequence, that it becomes the responsibility
of the body canvas to render the overview ruler, thereby somehow delegating
to a common renderer implementation. Which makes the whole setup of the body canvas
way more complex, because now we get *two* canvas like painting areas, one
always visible at top, and the second one, the content area, fully scrollable
within the lower part.
Even while EveryoneElese indulges in cool "flat" UI graphics,
we still think that a plausible 3D structure of UI widgets supports intuitive user interaction
As an asside, this commit fixes a mistake with the licenses of several of these documentation drawings.
I am the author of all these SVGs and thus can fix such a license glitch without much ado.
These drawing shall be licensed in accordance to the general rule for Lumiera Documentation,
which is to use a Libre-style license, here CC-by-sa (which does *not* limit commercial use)
(ab)using the Lumiera tree here for research work on behalf of the Yoshimi project
For context, we stumbled over sonic changes due to using different random number algorighims,
in spite of all those algorithms producing mathematically sane numbers