From 0e98a1a940236d0ca3b57d88f0d1d60bf60cbcad Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 4 Apr 2021 22:12:45 +0200 Subject: [PATCH] Clip-Drag: investigate how to deal with motion events ...found out that GTK already implements an "implicit grab", and thus the tricky situation that the mouse slides off the widget can not happen at all; so in the end it's rather easy to build a trigger for a dragging gesture. The demo code is now activated only after the button is down and just prints the position... PS: did some research regarding the new Coroutines in C++ --- .../interact/drag-relocate-controller.hpp | 58 ++- src/stage/interact/interaction-state.hpp | 10 +- src/stage/timeline/clip-widget.cpp | 6 +- wiki/thinkPad.ichthyo.mm | 376 +++++++++++++++++- 4 files changed, 420 insertions(+), 30 deletions(-) diff --git a/src/stage/interact/drag-relocate-controller.hpp b/src/stage/interact/drag-relocate-controller.hpp index 4b67ac670..1ec5cfb2a 100644 --- a/src/stage/interact/drag-relocate-controller.hpp +++ b/src/stage/interact/drag-relocate-controller.hpp @@ -50,6 +50,8 @@ #include "stage/gtk-base.hpp" #include "stage/interact/interaction-state.hpp" #include "stage/interact/cmd-context.hpp" +#include "lib/format-string.hpp" +#include "lib/format-cout.hpp" //#include "lib/idi/entry-id.hpp" //#include "lib/symbol.hpp" #include "lib/nocopy.hpp" @@ -65,19 +67,19 @@ namespace interact { // using util::isnil; // using std::string; using util::isnil; + using util::_Fmt; /** - * Abstract foundation context dependent UI interactions. - * While forming an interaction gesture, context state is picked up - * incrementally, and maintained here, together with parameters of degree, - * amount or relative position. Typically, an concrete implementation subclass - * is geared for one specific kind of interaction or gesture and used as target - * for wiring Signals to trigger and carry out this specific interaction. - * The InteractionDirector is responsible for allocating and maintaining those - * concrete InteractionState instances, which can then be used to issue complex - * commands for subjects and further arguments picked up from the interaction. + * Gesture controller for dragging objects within the Timeline display. + * The gesture to drag an entity is triggered by observing mouse movements + * while a mouse key and possibly some modifier key is pressed. To recognise + * this condition, every possible subject for a drag gesture will be wired through + * the #linkTrigger call into this controller. When #detectActivation is fulfilled + * for one specific subject, the corresponding context data will be tracked as state + * of the ongoing gesture in formation, maintained within the member fields of this + * controller. * * @todo write type comment... * @todo WIP-WIP as of /3/2021 @@ -86,24 +88,50 @@ namespace interact { class DragRelocateController : public InteractionState { + bool buttonPressed_ = false; + void linkTrigger (Subject& subject, Symbol cmdID) override { REQUIRE (not isnil (cmdID)); - subject.exposeWidget().signal_button_press_event().connect( - [&](GdkEventButton* button) -> bool + auto& widget = subject.exposeWidget(); + widget.signal_button_press_event().connect( + sigc::mem_fun (*this, &DragRelocateController::watchButton)); + widget.signal_button_release_event().connect( + sigc::mem_fun (*this, &DragRelocateController::watchButton)); + widget.signal_motion_notify_event().connect( + [&](GdkEventMotion* motion) -> bool { - try{ return detectActivation(subject, button); } + try{ return detectActivation(subject, motion); } ON_EXCEPTION_RETURN (false, "activate dragging gesture") } ); } bool - detectActivation (Subject& subject, GdkEventButton* button_event) + watchButton (GdkEventButton* button_event) noexcept { - throw lumiera::error::Fatal("UNIMPLEMENTED Maybe DRAG-start?????"); - //return false; + REQUIRE (button_event); + if (button_event->type & GDK_BUTTON_PRESS) + buttonPressed_ = true; + else + if (button_event->type & GDK_BUTTON_RELEASE) + buttonPressed_ = true; + return false; + } + + bool + detectActivation (Subject& subject, GdkEventMotion* motion_event) + { + if (not buttonPressed_) + return false; + REQUIRE (motion_event); + std::cerr << _Fmt{"MOVE x=%3.1f y=%3.1f subject=%s"} + % motion_event->x + % motion_event->y + % subject + << std::endl; + return true; // Event handled } diff --git a/src/stage/interact/interaction-state.hpp b/src/stage/interact/interaction-state.hpp index 52642ab30..4b7cccd21 100644 --- a/src/stage/interact/interaction-state.hpp +++ b/src/stage/interact/interaction-state.hpp @@ -61,15 +61,15 @@ namespace interact { /** - * Abstract foundation context dependent UI interactions. + * Abstract foundation for context dependent UI interactions. * While forming an interaction gesture, context state is picked up * incrementally, and maintained here, together with parameters of degree, - * amount or relative position. Typically, an concrete implementation subclass + * amount or relative position. Typically, a concrete implementation subclass * is geared for one specific kind of interaction or gesture and used as target * for wiring Signals to trigger and carry out this specific interaction. - * The InteractionDirector is responsible for allocating and maintaining those - * concrete InteractionState instances, which can then be used to issue complex - * commands for subjects and further arguments picked up from the interaction. + * The GestureState holder within InteractionDirector is responsible for allocating + * and maintaining those concrete InteractionState instances, which can then be used to + * issue complex commands for subjects and further arguments picked up from the interaction. * * @todo write type comment... * @todo WIP-WIP as of /3/2021 diff --git a/src/stage/timeline/clip-widget.cpp b/src/stage/timeline/clip-widget.cpp index f98d89e74..aacbfd295 100644 --- a/src/stage/timeline/clip-widget.cpp +++ b/src/stage/timeline/clip-widget.cpp @@ -125,14 +125,10 @@ namespace timeline { const string ClipDelegate::defaultName{_("clip")}; + ClipDelegate::ClipDelegate() { } ClipDelegate::~ClipDelegate() { } - ClipDelegate::ClipDelegate() - { - } - - namespace {// details of concrete clip appearance styles... using WidgetHook = model::CanvasHook; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 8ad59f29b..cbc12cc74 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -30052,6 +30052,19 @@ + + + + + + + + + + + + + @@ -31634,8 +31647,8 @@ - + @@ -31664,6 +31677,7 @@ + @@ -32502,16 +32516,26 @@ - - + + - - + + + + + + +

+ habs in error.hpp untergebracht, direkt unter ERROR_LOG_AND_IGNORE +

+ +
+
@@ -32537,6 +32561,219 @@
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ Das bekommt dieser Controller dann nicht mit, und die Geste wird insofern auch nicht getriggert. Da hat der User eben Pech gehabt. +

+ +
+
+ + + + + + +

+ Wenig problematisch ist dieser Fall, wenn der Button gedrückt bleibt und wir irgendwann zurückkommen; dann setzt sich das Dragging eben an der Stelle fort. Wenn dagegen der Button außerhalb released wurde, handelt es sich tatsächlich um den 3.Fall ― wenn aber eine normale Maus-Bewegung später wieder über das Widget fährt, wird das Dragging fortgesetzt, fälschlicherweise. +

+ +
+
+ + + + + + + + + + +

+ wenn das Dragging funktioniert, sollte es eigentlich rein logisch unmöglich sein, das Widget zu verlassen, weil dieses sich ja mitbewegt. Allerdings sind vielerlei undlückliche Umstände denkbar, z.B. verspätete Reaktion der Software, sehr schnelle Mausbewegungen, limitierte Bewegung am Rand eines Containers. +

+ +
+
+ + + +
+
+
+
+ + + + + + + + + + +

+ Beispiel: das "grab" von Blender ist ein praktisches Konzept. Dort kann man ein Element überhaupt nur bewegen, wenn man vorher die "g"-Taste gedrückt hatte. So etwas will ich in Lumiera auch haben... ist aber nicht so ganz einfach +

+ +
+ + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + +

+ die Lösung auf X-Display / GDK-Ebene für die Maus und das grab-widget von GTK, welches alle Events fangen kann. +

+ +
+ + + + + + + + + + + + + + + + + +
+
+ + + + + + + + +

+  * Generated when a pointer or keyboard grab is broken. On X11, this happens +

+

+  * when the grab window becomes unviewable (i.e. it or one of its ancestors +

+

+  * is unmapped), or if the same application grabs the pointer or keyboard +

+

+  * again. Note that implicit grabs (which are initiated by button presses) +

+

+  * can also cause #GdkEventGrabBroken events. +

+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
@@ -54810,6 +55047,9 @@ + + + @@ -54878,6 +55118,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + +

+ jedoch indirekt dann auch für darauf aufbauende Events, z.B. GTK-Events, weil eben nur noch das zum grabbed window gehörige Widgets diese Events überhaupt sieht +

+ + +
+
+ + + +
+ + + + + + + + + + + + @@ -55869,6 +56156,85 @@ + + + + + + + + + + + + + +

+ Coroutinen sind nicht per se asynchron +

+ + +
+
+ + + + + + + + + + +

+ welche dann jedoch stets aus einer Coroutine heraus per co_await aktiviert werden. Beispiel: ein lock-free mutex +

+ + +
+
+
+ + + + + + +

+ Kontrollübergänge erfolgen ausschließlich an den suspend points in der Coroutine selber +

+ +
+ + + + + +

+ Das untermauert nochmal, daß Coroutinen inhärent synchron und deterministisch sind. Man kann allerdings das low-level-Framework nutzen, um an einem bestimmten suspend-point den "eingefrorenen" Zustand der Coroutine asynchron an einen anderen Thread zu übertragen +

+ +
+ + + + + + + + + + + + + + + + + + + +