Commit graph

4246 commits

Author SHA1 Message Date
d3fc6fd4de Timeline: Analysis of possibly toxic input values
- protection against negative numbers seems adequate
- a possible concern are handling of very large time spans
- definitively have to guard against "poisonous" fractions
  (e.g. n / INT_MAX)
2022-11-12 17:35:47 +01:00
cc16953fd8 Timeline: implement and verify ZoomWindow change notification 2022-11-11 16:30:27 +01:00
3f396ef3b2 Timeline: ZoomWindow now passes all tests according to spec
- some test definitions were simply numerically wrong
- changed some aspects of the specified behaviour, to be more consistent
  + scrolling is more liberal and always allows to extend canvas
  + setting window to a given duration expands around anchor point
2022-11-11 03:06:33 +01:00
fa4fbe5225 Timeline: clarify or fix further ZoomWindow_test failures
- forgot the bias towards the next larger grid aligned size
- implement safeguard against empty window
2022-11-09 03:25:35 +01:00
29f030d2a1 Timeline: implement rule for relative ZoomWindow positioning
This function allows to move the visible range such that it contains
a given time position; the relative location of this point within
the visible range however is in turn determined by relating it to
the current overall canvas: if we are close to the beginning, the
position is also located rather to the left side, and if we're
approaching the canvas end, the position tends to the right side...

(and yes, I am aware that the variant taking a rational number
can be derailed by causing internal numeric overflow, when passing
a maliciously crafted rational number, like INT_MAX-1 / INT_MAX )
2022-11-08 02:55:38 +01:00
a9df13f078 Timeline: define basic ZoomWindow setters on top of the normalisation
Rearrange the internal mutator functions to follow a common scheme,
so that most of the setters can be implemented by simple forwarding.
Move the change-listener triggering up into the actual setters.

This makes further test cases pass
 - verify_setup
 - verify_calibration
...implying that the pixel width is now retained
and basic behaviour matches expectations
2022-11-07 03:24:04 +01:00
14da237d5c Timeline: integrate relative scaling into ZoomWindow invariant handling
Since conformWindowToMetric() is always called prior to performing
the complete invariant-reestablishment sequence, we can even integrate
the rule for relative scaling into this central function, which
simplifies the mutation implementation significantly. Should
relative positioning go south, the following sanity checks
will push the window back into bounds.

With these changes, the verify_simpleUsage() test passes!
2022-11-07 01:30:27 +01:00
292be817b7 Timeline: investigate problem with numeric overflow in fractional arithmetic
Extensive tests with corner cases soon highlighted this problem
inherent to integer calculations with fractional numbers: it is
possible to derail the calculation by numeric overflow with values
not excessively large, but using large numbers as denominator.
This problem is typically triggered by addition and subtraction,
where you'd naively not expect any problems.

Thus changed the approach in the normalisation function, relying
on an explicitly coded test rather, and performing the adjustment
only after conversion back to simple integral micro-tick scale.
2022-11-07 00:19:28 +01:00
5ed5905d7f Timeline: work out a scheme of Invariants and Normalisation for the ZoomWindow
Getting all those requirements translated into code turns out to be a challenging task;
and the usual ascent to handle such a situation is to define **Invariants**
in conjunction with a normalisation scheme; each manipulation will then be
translated into invocation of one of the three fundamental mutators,
and these in turn always lead into the common normalisation sequence.

__Invariants__
- oriented and non-empty windows
- never alter given pxWidth
- zoom metric factor < max zoom
- visibleWindow ⊂ Canvas
2022-11-06 02:46:11 +01:00
f2ef893adb Timeline: complete specification of ZoomWindow expected behaviour
Writing this specification unveiled a limitation of our internal
time base implementation, which is a 64bit microsecond grid.
As it turns out, any grid based time representation will always
be not precise enough to handle some relevant time specifications,
which are defined by a divisor. Most notably this affects the precise
display of frame duration in the GUI, and even more relevant,
the sample accurate editing of sound in the timeline.

Thus I decided to perform the internal computation in ZoomWindow
as rational numbers, based on boost::rational

Note: implementation stubbed only, test fails
2022-11-04 03:40:36 +01:00
f1b3f4e666 Timeline: reconsider time handling and Stage/Steam integration
This ZoomWindow_test highlights again the question about the intended usage
of the Lumiera time entities. In which way do we want to perform time calculations,
and under which circumstances is it adequate to perform arithmetic on
raw time values?

These questions made me think about rather far reaching concerns regarding
subsidiarity and implicit or explicit usage context. Basically I could
reconfirm the design choices taken some years ago -- while I must admit
that the project is headed towards a way larger scale and more loose
coupling of the parts, than I could imagine several years ago, at the
time when the design started...

As a side note: we can not avoid that some knowledge about the time implementation
leaks out from the support lib; time codes themselves are tightly coupled
to the usage scenario within the session and can not be used as means
for implementing UI concerns. And the more generic time frameworks,
like std::chrono (as much as it is desirable to have some integration here)
will not be of any help for most of our specific usage patterns.
The reason is, for film editing we do not have a global time scale,
rather the truth is when the film starts....
2022-10-30 23:12:34 +01:00
7145d0d9ce Timeline: ZoomWindow implementation draft
implement the first test case: nudge the zoom factor
⟹ scale factor doubled
⟹ visible window reduced to half size
⟹ visible window placed in the middle of the overall range
2022-10-30 01:31:25 +02:00
b3fe6e16c6 Timeline: requirement analysis to define the ZoomWindow (see #1196) 2022-10-29 01:59:42 +02:00
7eca11b332 Timeline: draft arrangement to provide a display-metric (closes #1213)
The solution is to provide a standard implementation in the form of a mix-in,
which directly houses a `ZoomWindow` instance. Moreover, the latter
is deemed a prominent use case for the time::Control, allowing other
components to attach and push changes of the zoom state or register
as listeners to react to state changes.

Actually, the `TimelineLayout`, which hosts all the actual visible
widgets forming the timeline-UI, now integrates this mix-in; and since
`TimelineLayout` is passed to `TimelineController` and used there as
reference-`CanvasHook` for the root track, this implementation of
the `DisplayMetric` interface will ''effectively be used by all
widgets'' attached to the timeline canvas.
2022-10-28 02:08:34 +02:00
6637864be5 Timeline: reassess design of relative widget coordinates (see #1207)
Reading my work notes from two years ago, the concept can be validated.
Clarify the relation of the interfaces involved, and the role foreseen
for the upcoming `ZoomWindow` abstraction. This solution approach
will lead to multiple-stage indirect calls, which however are deemed
not to be overly concerning and will be investigated later, to avoid
premature optimisation (see #1254)

- `DisplayMetric` is a focused special purpose abstraction
- it belongs into the general abstraction of the `DisplayManager`
- it is rather linked by use to the other abstraction, the `CanvasHook`
- while the `RelativeCanvasHook` is not an interface, but an implementation mix-in
- and the actual `DisplayMetric` implementation can likewise be provided
  as mix-in, since it will typically be implemented in terms of a `ZoomWindow`

Using this scheme, it will be possible to avoid some of the indirect cally
by making this mix-in visible higher up the call graph -- in case the
actual need for optimisation can be confirmed in practice.
2022-10-27 23:10:39 +02:00
eb500dd453 ElementBox: successfully implemented size-constrained ClipWidget (closes #1235)
* restructure the widgets used to implement ElementBox
 * inject a Gtk::EventBox top-level base type to capture all Gdk-Events
 * push the Gtk::Frame one level down (TODO: API for managing children)

With these changes
 * dragging of Clips in the timeline works as expected
 * size constraints are observed precisely
 * all warnings and assertion failures from GTK disappeared

Thus we can conclude that the solution approach for size constrained widgets
was successful and this challenging problem is solved.
2022-10-21 19:18:21 +02:00
3953bbd5ee ElementBox: investigate and fix problem with label
...as it turned out, the ClipWidget did still invoke the
Gtk::Frame::set_label() function, thereby deactivate our
elaborate custom IDLabel and showing the label text
unconditionally instead (also violating our size constraint)
2022-10-21 01:24:54 +02:00
15e00e01c2 ElementBox: lower requirements for size-constraint handling
...as it turns out, this code basically works already when the
widget is not(yet) realized:
- when a widget is hidden, it responds with size=0
- when a widget is shown, it reponds with proper or at least
  preliminary size requirement, irrespective if already drawn

After injecting the diff, the widgets are created and then adjusted
in several steps; however, this code all executes from within a single
call to the UI-bus, and thus just piles up a sequence of realize()
and resize() messages, which are only executed later, in case the
Application-UI as a whole is visible on screen.

*Remaining Problems*:
 - size-constraint code not working correct in all cases
 - dragging works only on the buttons, not on the background
2022-10-20 19:36:59 +02:00
fd31f47498 ElementBox: integrate as base for Clip widget (see #1038)
According to plan, this was more or less a drop-in replacement.
However, this first integration prototype highlights some design problems

 * `ElementBoxWidget` is designed ''constructor-centric''
 * but the population by diff messages will supply crucial information later
 * and seemingly the size-constraint code is now invoked prior to widget realisation \\
   ⟹ Assertion Failure
2022-10-17 04:19:26 +02:00
aa26180824 ElementBox: integrate new Placement and Menu icon design (closes #1236)
The menu icon is possibly sightly to bright,
but it will do for now....
2022-10-15 19:44:41 +02:00
0a2f3ba395 ElementBox: further testing and investigation
- Test the new layout code with debugger + dump messages
- Experiment: live changes to the name-ID content
  (send msg. "manip" -> Text changed and Layout properly revalidated)
2022-10-04 00:51:05 +02:00
3bf3391a63 ElementBox: coping with size constraints (closes #1238)
devise a more fine grained algorithm for adapting the display of IDLabel
to a situation with size constrained layout, e.g. for a time calibrated canvas.
We still do not implement the shortening of ID labels (see #1242),
since doing so would be surprisingly expensive. But at least we
do proceed in several steps now

- first attempt to reduce the name-ID (for now: hide it)
- if this doesn't suffice, also hide the menu
- and as a last resort, hide the icon and thus make the IDLabel empty
2022-10-03 02:51:23 +02:00
8b6991e0f5 ElementBox: establish basic styling
We are using buttons now, but the standard theme introduces a lot of padding arount button's contents.
Thus we need to consider ways to address the compound of widgets forming an ElementBox; moreover,
this is the classical situation where the BEM notation helps to clarify the intention....

The problem leading to custom styling here is the padding within buttons;
the default stylesheet seemingly adds a min-width and min-height setting,
and some padding within the Button; based on systematic CSS class names,
it is possible to remove these settings specifically for buttons
within the IDLabel in general (no need to treat only the case of an EventBoxLabel
-- IDLabel could become a custom widget on its own
2022-10-02 03:57:16 +02:00
53c614afc2 ElementBox: extract the caption and size handling logic into sub widget
as it turns out, this is a self-contained separate concern,
and thus this arrangement of two icons plus a caption shall
now manage itself as a custom widget.

And while touching this subject, I have also reconsidered
the purpose and arrangement of those icons and completed
the specification with some decisions...
- context menus will be left-click, selection right-click (Blender!)
- we will always show those two icons, just allocate different graphics
- when there is no expander, the 2nd icon will just serve to open the menu
- so the button is almost redundant in that case (except when dragging)
2022-09-30 18:55:22 +02:00
c05abb88ab ElementBox: complete (base) implementation of size constrained layout
Further extended GTK code survey to clarify the role of the minimum_size,
it is indeed ignored by most standard containers, but it is actually
used by Gtk::Layout as starting point for the query sequence. Thus
it does not make sense to treat minimum and natural size differently;
both queries should be responded by returning our size constraint.

Unless we define additional borders and margins in the CSS, we can be sure
that GTK will base the size allocation on the exact values returned
from the get_required_* functions.

These functions will be invoked only from within the Event Loop
and after the ctor is finished, but before the first "draw".
They will be re-invoked on each "size change" event and on each
focus change (since a focus change may change the style and thus
the actual extension).
2022-09-30 01:55:12 +02:00
b500fcd8f4 ElementBox: investigate adverse behaviour and work out solution
...the key point is to ask the embedded box holding the label
about it's preferred_size() -- this info is updated immediately,
even at begin, when the nested child widgets did not yet receive
an allocation.

Even while the preferred-size is something different than the
actual allocation, it will always be smaller and is thus sufficient
to decide if the size constraint can be met
2022-09-27 23:29:35 +02:00
f393780845 Lib: fix a bug with diagnostic output
The header "format-cout.hpp" offers a convenience function
to print pretty much any object or data in human readable form.
However, the formatter for pointers used within this framework
switched std::cout into hexadecimal display of numbers and failed
to clean-up this state.

Since the "stickyness" of IOS stream manipulators is generally a problem,
we now provide a RAII helper to capture the previous stream state and
automatically restore it when leaving the scope.
2022-09-27 01:51:21 +02:00
8fdde735d4 ElementBox: draft logic to impose size constraint on child widgets
...does not work reliably yet...

- on first invocation, the child allocation is still zero
- later on, there seem to be lots of further invocations,
  always when the application window gains focus
- these further invocations somehow change the visible extension
  of the widget's background
2022-09-26 02:48:49 +02:00
36594cd774 ElementBox: wire the standard implementation for size allocation
...if the strategy does not impose an explicit size constraint,
then use the inherited layout functions from GTK
2022-09-25 22:17:44 +02:00
acaeb330c3 ElementBox: Analysis and design for layout strategy...
identify the various dimensions, which require flexibility
to support the intended use cases; try to come up with a
design draft, allowing to settle on a preliminary version
soon, while not hampering further development later on.

Obviously this is a very deep and challenging topic,
and we're far from even remotely addressing it adequately;
we just need to get to the point to use this drafted version
as building block, since these usages will then push us further
into the right direction...
2022-09-25 01:16:10 +02:00
0f3298f004 ElementBox: push Icon spec into the Strategy helper
...the actual selection strategy remains to be implemented
...decision to stick to the Stock-ID scheme until we consider
   migration to GTK-4
2022-09-23 02:53:52 +02:00
01740619f1 Code Review with Benny 2022-09-08 14:24:36 +02:00
4977d75014 ElementBox: support CSS selectors
The CSS class .background ensures there is an opaque backgdrop;
adding ID names to allow direct addressing in the stylesheet
2022-09-02 01:29:17 +02:00
38b6228fac ElementBox: working draft of ElementBoxWidget, establishing ctor framework
The flexible custom styling yet needs to be definied.
Just adding a stock icon and a standard sized label field for now.

Widget can be constructed and successfully attached to a track.
2022-09-01 19:34:38 +02:00
ed7e3b4b32 ElementBox: extract builder qualifier support as library implementation
Complete the investigation and turn the solution into a generic
mix-in-template, which can be used in flexible ways to support
this qualifier notation.

Moreover, recapitulate requirements for the ElementBoxWidget
2022-08-28 23:36:27 +02:00
ff6234829a ElementBox: investigate ctor syntax possibilites
Basically we want to create ElementBoxWidgets according to a
preconfigured layout scheme, yet we'll need to pass some additional
qualifiers and optional features, and these need to be checked
and used in accordance with the chosen flavour...

Investigating a possible solution based on additional ctor parameters,
which are given as "algebraic terms", and actually wrap a functor
to manipulate a builder configuration record
2022-08-28 01:02:10 +02:00
0622ddece8 private.mm: infos noted while debugging Yoshimi 2021-11-05 21:19:10 +01:00
e15e893a01 Util: use case-insensitive matching for parsing bool values 2021-08-20 14:33:59 +02:00
ae10442cf2 some whitespace clean-up 2021-08-20 14:33:21 +02:00
5b1dfe4534 Clip-Drag: further investigation and clean-up
Seems to work solid now, after switching to the root coordinates provided by GDK.
With local relative coordinates, the subject fidgets while being dragged,
for obvious reasons, since we're shifting the relative point of reference.

Also clarified a strange behaviour of the test drawing code:
Cairo is "turtle graphics", so we need to set the starting point explicitly.
2021-06-19 17:12:02 +02:00
b9657320ed Clip-Drag: Yay! dummy clip moves in the timeline when dragged
...well, the metric translation is not quite correct,
so it doesn't yet stick to the mouse. But all the challenging
problems within the framework for implementing such a generic
gesture seem to be solved now.
2021-05-14 19:42:20 +02:00
526f1d09e7 Clip-Drag: integrate indirectly with the TimelineLayout for metric translation
The ClipPresenter can access the CanvasHook wired into its actual ClipDelegate (widget).
And this in turn exposes the DisplayMetric, with the ability to transform
presentation coordinates (pixels) into a model representation (Time)

The actual translation is still hardwired placeholder code,
since it is planned to build an generic component "ZoomWindow"
to provide all the typical zomming and view window translations
found in every timeline editor
2021-05-13 18:29:37 +02:00
3e9aae30b3 Clip-Drag: switch implementation to the new observer/adapter 2021-05-13 16:14:11 +02:00
aad4087e26 Clip-Drag: reshape the Subject API to use a delegate/adapter
...for tracking and updating while the gesture is in formation
2021-05-09 15:14:14 +02:00
4caf790339 Library: verify PlantingHandle's extended capabilities
- move construct into the buffer
- directly invoke the payload constructor through PlantingHandle
- reconsider type signature and size constraint
- extend the unit test
- document a corner case of c++ "perfect forwarding",
  which caused me some grief here
2021-05-07 22:50:13 +02:00
5a37bce855 Lib/Diff: extend PlantingHandle to allow for placment-new
...this extension was spurred by the previeous refactoring.
Since 'emplace' now clearly denotes an operation to move-embed an existing object,
we could as well offer a separate 'create' API, which would take forwarding
arguments as usual and just delegates to the placement-new operation 'create'
already available in the InPlaceBuffer class.

Such would be a convenience shortcut and is not strictly necessary,
since move-construction is typically optimised away; yet it would also
allow to support strictly non-copyable payload types.

This refactoring also highlights a fuzziness in the existing design,
where we just passed the interface type, while being sloppy about the
DEFAULT type. In fact this *is* relevant, since any kind of construction
might fail, necessitating to default-construct a placeholder, since
InPlaceBuffer was intended for zero-overhead usage and thus has in itself
no means to know about the state of its buffer's contents. Thus the
only sane contract is that there is always a valid object emplaced
into the buffer, which in turn forces us to provide a loophole for
class hierarchies with an abstract base class -- in such a case the
user has to provide a fallback type explicitly.
2021-05-02 19:40:11 +02:00
5aa41accfc Lib/Diff: prefer the name "emplace" over "build"
...for the operation on a PlantingHandle, which allows
to implant a sub type instance into the opaque buffer.

 * "create" should be used for a constructor invocation
 * "emplace" takes an existing object and move-constructs
2021-05-02 18:31:47 +02:00
73740a24e3 Clip-Drag: refactor into sub-component of TimelineLayout
this allows to avoid multi-step indirection
when translating mouse dragging pixel coordinates
into a time offset for the dragged clip widget.

Moreover this also improves the design,
since the handling of canvas metric is pretty much
a self contained, separate concern
2021-04-30 19:46:32 +02:00
0fb3aab95b Clip-Drag: introduce sub-interface for canvas metric
...previously this was modelled as part of the CanvasHook abstraction,
and in fact it will in any case be implemented by delegation to the
TimelineLayout or some kind of display manager.

We need this to tanslate mouse pixel movements into a time change
2021-04-30 18:44:10 +02:00
910a521ded Clip-Drag: investigate how to approach metric translation
while dragging, effectively we have to translate a mouse position delta
into a TimeValue delta, and we want to avoid direct coupling to some
timeline display manager, to keep the gesture logic mostly generic.
2021-04-29 14:41:02 +02:00