Lumiera  0.pre.03
»edit your freedom«
drag-relocate-controller.hpp
Go to the documentation of this file.
1 /*
2  DRAG-RELOCATE-CONTROLLER.hpp - concrete gesture controller to relocate a widget by dragging
3 
4  Copyright (C)
5  2021, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 */
13 
14 
37 #ifndef STAGE_INTERACT_DRAG_RELOCATE_CONTROLLER_H
38 #define STAGE_INTERACT_DRAG_RELOCATE_CONTROLLER_H
39 
40 
41 #include "stage/gtk-base.hpp"
44 #include "lib/opaque-holder.hpp"
45 #include "lib/format-string.hpp"
46 #include "lib/format-cout.hpp"
47 //#include "lib/idi/entry-id.hpp"
48 //#include "lib/symbol.hpp"
49 #include "lib/nocopy.hpp"
50 #include "lib/util.hpp"
51 
52 //#include <string>
53 
54 
55 namespace stage {
56 namespace interact {
57 
58 // using lib::HashVal;
59 // using util::isnil;
60 // using std::string;
61  using util::isnil;
62  using util::_Fmt;
63 
64  namespace {
65  const gdouble DISTANCE_THRESHOLD = 5.0;
66 
69  const size_t OBSERVER_BUFF_SIZ = sizeof(void*)
70  + sizeof(Symbol)
71  + 4 * sizeof(void*);
72  }
73 
74 
97  : public InteractionState
98  {
99  bool buttonPressed_ = false;
100  Subject* subject_ = nullptr;
101  bool isInFormation_ = false;
102  gdouble anchorX_ = 0.0;
103  gdouble anchorY_ = 0.0;
104 
106 
107  Observer observer_;
108 
109  void
110  linkTrigger (Subject& subject, Symbol cmdID) override
111  {
112  REQUIRE (not isnil (cmdID));
113  auto& widget = subject.exposeWidget();
114  widget.signal_button_press_event().connect(
115  sigc::mem_fun (*this, &DragRelocateController::watchButton));
116  widget.signal_button_release_event().connect(
117  sigc::mem_fun (*this, &DragRelocateController::watchButton));
118  widget.signal_motion_notify_event().connect(
119  [&, cmdID](GdkEventMotion* motion) -> bool
120  {
121  try{ return maybeActivate(cmdID, subject, motion); }
122  ON_EXCEPTION_RETURN (false, "activate dragging gesture")
123  }
124  );
125  }
126 
127  bool
128  watchButton (GdkEventButton* button_event) noexcept
129  {
130  REQUIRE (button_event);
131  if (GDK_BUTTON_PRESS == button_event->type)
132  buttonPressed_ = true;
133  else
134  if (GDK_BUTTON_RELEASE == button_event->type)
135  {
136  buttonPressed_ = false;
137  if (isActive())
138  doCompleteGesture();
139  resetState();
140  }
141  std::cerr << _Fmt{"BUTT %s flag=%d"} % buttonPressed_ % button_event->type << std::endl;
142  return false;
143  }
144 
146  bool
147  maybeActivate (Symbol cmdID, Subject& subject, GdkEventMotion* motion_event)
148  {
149  if (not buttonPressed_)
150  return false; // Event not handled by this controller
151  REQUIRE (motion_event);
152  std::cerr << _Fmt{"MOVE x=%3.1f y=%3.1f subject=%s"}
153  % motion_event->x_root
154  % motion_event->y_root
155  % subject
156  << std::endl;
157  if (not isAnchored())
158  anchor (cmdID, subject, motion_event);
159  if (not isActive())
160  {
161  probeActivation (motion_event);
162  if (isActive())
163  initGestureTracking(cmdID, subject);
164  return false;
165  }
166  else
167  {
168  doTrackGesture (motion_event);
169  return true; // Event handled
170  }
171  }
172 
173 
174  /* === gesture implementation === */
175  bool
176  isActive()
177  {
178  return subject_
179  and isInFormation_;
180  }
181 
182  bool
183  isAnchored()
184  {
185  return bool{subject_};
186  }
187 
188  void
189  anchor (Symbol cmdID, Subject& subject, GdkEventMotion* motion_event)
190  {
191  REQUIRE (motion_event);
192  this->subject_ = & subject;
193  this->anchorX_ = motion_event->x_root;
194  this->anchorY_ = motion_event->y_root;
195  std::cerr << _Fmt{"ANCHOR at x=%3.1f y=%3.1f ('%s')"}
196  % anchorX_
197  % anchorY_
198  % cmdID
199  << std::endl;
200  }
201 
202  void
203  probeActivation (GdkEventMotion* motion_event)
204  {
205  isInFormation_ = DISTANCE_THRESHOLD < abs (motion_event->x_root - anchorX_)
206  or DISTANCE_THRESHOLD < abs (motion_event->y_root - anchorY_);
207  }
208 
209  void
210  initGestureTracking(Symbol cmdID, Subject& subject)
211  {
212  subject.buildGestureObserver (cmdID, Observer::Handle{observer_});
213  }
214 
215  void
216  doTrackGesture (GdkEventMotion* motion_event)
217  {
218  REQUIRE (motion_event);
219  gdouble deltaX = motion_event->x_root - this->anchorX_;
220  gdouble deltaY = motion_event->y_root - this->anchorY_;
221  // notify Subject to feed current delta
222  observer_->updateOffset (deltaX, deltaY);
223  }
224 
225  void
226  doCompleteGesture()
227  {
228  observer_->markGestureCompleted();
229  }
230 
231  void
232  resetState()
233  {
234  isInFormation_ = false;
235  anchorX_ = anchorY_ = 0.0;
236  subject_ = nullptr;
237  observer_.reset();
238  }
239 
240 
241  public:
243  : observer_{}
244  { }
245 
246  private:
247  };
248 
249 
250 
251 }} // namespace stage::interact
252 #endif /*STAGE_INTERACT_DRAG_RELOCATE_CONTROLLER_H*/
Abstraction: a component to watch, maintain and guide UI state.
bool maybeActivate(Symbol cmdID, Subject &subject, GdkEventMotion *motion_event)
Gesture detection state logic.
#define ON_EXCEPTION_RETURN(_VAL_, _OP_DESCR_)
convenience shortcut to catch and absorb any exception, then returning a default value instead...
Definition: error.hpp:304
Automatically use custom string conversion in C++ stream output.
Front-end for printf-style string template interpolation.
const size_t OBSERVER_BUFF_SIZ
heuristics for sizing the inline buffer where the Subject will construct its Observer/Adapter ...
A front-end for using printf-style formatting.
Abstract foundation for context dependent UI interactions.
Token or Atom with distinct identity.
Definition: symbol.hpp:117
Mix-Ins to allow or prohibit various degrees of copying and cloning.
A handle to allow for safe »remote implantation« of an unknown subclass into a given opaque InPlaceBu...
Definition: record.hpp:104
Lumiera GTK UI implementation root.
Definition: guifacade.cpp:37
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Helper allowing type erasure while holding the actual object inline.
virtual void buildGestureObserver(Symbol cmdID, Buffer)=0
prompt the Subject to build an Observer for the gesture in formation
virtual Gtk::Widget & exposeWidget()=0
the exposed widget can be used for wiring signal handlers
Gesture controller for dragging objects within the Timeline display.
A set of basic GTK includes for the UI.
Abstraction: support for binding command invocation into an UI context.
Role-Interface: the Subject of Interaction.
void linkTrigger(Subject &subject, Symbol cmdID) override
Hook up a trigger signal to initiate a specific interaction gesture.