<div dir="ltr">Something else. Ideally handleEvent() should be a generic dispatcher that simply forwards the events to other methods that handle the specific event types.<div><br></div><div>E.g. handleEvent(vtkEventData *) would forward button events to</div><div><br></div><div>  handleButtonEvent(vtkButtonEventData *)</div><div><br></div><div>So the question is, how to do this dispatching generically without using an "if" for each event type?</div><div><br></div><div><br></div><div>I use a method like the following to add callbacks:</div><div><br></div><div><div>  template<class T, class E></div><div>  void EventDispatcher::Bind(int type, T* observer, void (T::*callback)(E *e));</div><div><br></div><div>Each event class E has a static member E::TypeBase that can be used to check that class "E" is allowed to handle "type".</div><div><br></div><div>The job of the this Bind() method is to build a binding table, which is basically a map of (type, callback) pairs.</div><div><br></div><div><br></div><div>The trick here is that the "callback" stored in the table is polymorphic:</div><div><br></div><div>class EventCallback</div><div>{</div><div>public:</div><div>  virtual void operator()(Event *event) const = 0;</div><div>};</div><div><br></div><div><br></div><div>And the Bind() creates subclasses on the fly, thanks to the magic of templates.  The operator() method is responsible for performing the downcast:</div><div><br></div><div><div>template<class T, class E></div><div>class EventCallbackTE : public EventCallback</div></div><div>{</div><div>public:</div><div><div>  EventCallbackTE(T *object, void (T::*method)(E *event)) :</div><div>    Object(object), Method(method) {}</div></div><div><br></div><div><div>  void operator()(Event *event) const override</div><div>  {</div><div>    (this->Object->*(this->Method))(static_cast<E *>(event));</div><div>  }</div></div><div><br></div><div>private:</div><div><div>  T *Object;</div><div>  void (T::*Method)(E *event);</div><div>};</div></div><div><br></div></div><div><br></div><div>So, basically, the "if" statements are replaced by a lookup table that can grow as needed.  At least some of the ideas are from the book "Modern C++ Design" (unfortunately I don't have my own copy, so I can't locate pages or chapters right now).</div><div><br></div><div>My own implementation adds things like weak pointers and dispatching based on more than just the event type, e.g. dispatching also considers which button is held down, what modifiers are held down, and other relevant state.</div><div><br></div><div> - David</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Jul 10, 2017 at 2:00 PM, Ken Martin <span dir="ltr"><<a href="mailto:ken.martin@kitware.com" target="_blank">ken.martin@kitware.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">I like that too :-) So what are your thoughts on down casting to access the properties? e.g.<div><br></div><div>void handleEvent(vtkEventData *event)</div><div>{</div><div>  if (event->GetType() == Button)</div><div>  {</div><div>    vtkButtonEvent *be = static_cast<vtkButtonEvent *>(event);</div><div>    be->GetButtonId()</div><div>  }</div><div><br></div><div>or something else?</div><div><br></div><div><br></div><div><br></div></div><div class="HOEnZb"><div class="h5"><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Jul 10, 2017 at 2:59 PM, David Gobbi <span dir="ltr"><<a href="mailto:david.gobbi@gmail.com" target="_blank">david.gobbi@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Hi Ken,<div><br></div><div>Why not use polymorphism here?  I use an event hierarchy like so:</div><div><br></div><div>  Event <- InputEvent <- PointEvent, KeyEvent</div><div><br></div><div>I've never used a union; the code that generates the event knows what type of event object to instantiate, and the observer methods just get a pointer to the event object.  Qt does things similarly.</div><div><br></div><div> - David</div><div><br></div><div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Jul 10, 2017 at 9:15 AM, Ken Martin <span dir="ltr"><<a href="mailto:ken.martin@kitware.com" target="_blank">ken.martin@kitware.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><br></div><div>Lucas and I have been working on VTK's VR support and one issue we bumped into was event handing. VTK's event handling tends to be data light where most events follow this approach</div><div><br></div><div>1) event happens</div><div><br></div><div>2) we set properties on the interactor such as alt/shift/control key state, mouse position, etc</div><div><br></div><div>3) fire the event (with little or no calldata)</div><div><br></div><div>4) event handlers query needed properties from the interactor</div><div><br></div><div>The challenge here is that as more event types are supported, the interactor's state and structure is required to grow to match them.  For example with multitouch events, the interactor now has to support multiple mouse positions and pointer indexes. Now add in multi controller VR and we have to also store arrays of world coordinate positions and orientations and what controller the current event corresponds to.</div><div><br></div><div>Many systems use a different approach where they define an event data structure or class that get passed with the event (or associated with the event). Then whatever is handling the event receives that data structure which contains most of the key properties of the event.  Such an event structure can also be used for filtering. For example currently in VTK you can map keyboard events on a widget using the vtkWidgetCallbackMapper method</div><div><br></div><div><div>void SetCallbackMethod(unsigned long VTKEvent, int modifiers, char keyCode,</div><div>                         int repeatCount, const char* keySym,</div><div>                         unsigned long widgetEvent,</div><div>                         vtkAbstractWidget *w, CallbackType f);</div></div><div><br></div><div>Note that this signature is designed for keyboard events as it contains keyboard event specific parameters. To add a mapping for multitouch, or VR controller events would require more event specific signatures. In contrast if we had a formal vtkEventData structure then the method could use a vtkEventData as a filter and handle many different event types as long as the EventData has an equality operator on it (or maybe a filtering specific equality).</div><div><br></div><div>Lucas and I have tried this out for VR and it has made event handling reasonable and fairly clean and I would like to get some feedback on the idea/approach/etc.  Right now our vtkEventData is included below. It uses the approach of a union of event type structs and what has been fleshed out is targeted at VR but it should be easy to see how keyboard or mouse devices/events could get added as well.</div><div><br></div><div>Thanks!</div><div>Ken</div><div><br></div><div><br></div><div><br></div><div><div>enum class vtkEventDataDevice {</div><div>  Unknown = -1,</div><div>  HeadMountedDisplay,</div><div>  RightController,</div><div>  LeftController,</div><div>  NumberOfDevices</div><div>};</div><div><br></div><div>const int vtkEventDataNumberOfDevices =</div><div>  static_cast<int>(vtkEventDataD<wbr>evice::NumberOfDevices);</div><div><br></div><div>enum class vtkEventDataDeviceInput {</div><div>  Unknown = -1,</div><div>  Trigger,</div><div>  TrackPad,</div><div>  Grip,</div><div>  ApplicationMenu,</div><div>  NumberOfInputs</div><div>};</div><div><br></div><div>const int vtkEventDataNumberOfInputs =</div><div>  static_cast<int>(vtkEventDataD<wbr>eviceInput::NumberOfInputs);</div><div><br></div><div>enum class vtkEventDataAction {</div><div>  Unknown = -1,</div><div>  Press,</div><div>  Release,</div><div>  NumberOfActions</div><div>};</div><div><br></div><div>struct vtkEventDataButton3D</div><div>{</div><div>  vtkEventDataDevice Device;</div><div>  vtkEventDataDeviceInput Input;</div><div>  vtkEventDataAction Action;</div><div>  double WorldPosition[3];</div><div>  double WorldOrientation[4];</div><div><br></div><div>  bool operator==(const vtkEventDataButton3D& rh)</div><div>  {</div><div>    return (Device == rh.Device && Input == rh.Input && Action == rh.Action);</div><div>  }</div><div>  void SetWorldPosition(const double p[3])</div><div>  {</div><div>    WorldPosition[0] = p[0];</div><div>    WorldPosition[1] = p[1];</div><div>    WorldPosition[2] = p[2];</div><div>  }</div><div>  void SetWorldOrientation(const double p[4])</div><div>  {</div><div>    WorldOrientation[0] = p[0];</div><div>    WorldOrientation[1] = p[1];</div><div>    WorldOrientation[2] = p[2];</div><div>    WorldOrientation[3] = p[3];</div><div>  }</div><div>};</div><div><br></div><div>struct vtkEventDataMove3D</div><div>{</div><div>  vtkEventDataDevice Device;</div><div>  double WorldPosition[3];</div><div>  double WorldOrientation[4];</div><div><br></div><div>  bool operator==(const vtkEventDataMove3D& rh)</div><div>  {</div><div>    return (Device == rh.Device);</div><div>  }</div><div>  void SetWorldPosition(const double p[3])</div><div>  {</div><div>    WorldPosition[0] = p[0];</div><div>    WorldPosition[1] = p[1];</div><div>    WorldPosition[2] = p[2];</div><div>  }</div><div>  void SetWorldOrientation(const double p[4])</div><div>  {</div><div>    WorldOrientation[0] = p[0];</div><div>    WorldOrientation[1] = p[1];</div><div>    WorldOrientation[2] = p[2];</div><div>    WorldOrientation[3] = p[3];</div><div>  }</div><div>};</div><div><br></div><div>typedef union</div><div>{</div><div>  vtkEventDataButton3D Button;</div><div>  vtkEventDataMove3D Move;</div><div>} vtkEventDataUnion;</div><div><br></div><div>struct vtkEventData</div><div>{</div><div>  int Type; // see vtkCommand.h</div><div>  vtkEventDataUnion Data;</div><div><br></div><div>  bool operator==(const vtkEventData& rh)</div><div>  {</div><div>    if (Type != rh.Type)</div><div>    {</div><div>      return false;</div><div>    }</div><div>    switch (Type)</div><div>    {</div><div>      case vtkCommand::ButtonEvent3D:</div><div>        return Data.Button == rh.Data.Button;</div><div>      case vtkCommand::MoveEvent3D:</div><div>        return Data.Move == rh.Data.Move;</div><div>    }</div><div>    return false;</div><div>  }</div><div><br></div><div>  bool GetDevice(vtkEventDataDevice &val)</div><div>  {</div><div>    switch (Type)</div><div>    {</div><div>      case vtkCommand::ButtonEvent3D:</div><div>        val = Data.Button.Device;</div><div>        return true;</div><div>      case vtkCommand::MoveEvent3D:</div><div>        val = Data.Move.Device;</div><div>        return true;</div><div>    }</div><div>    return false;</div><div>  }</div><div><br></div><div>  bool GetWorldPosition(double *&pos)</div><div>  {</div><div>    switch (Type)</div><div>    {</div><div>      case vtkCommand::ButtonEvent3D:</div><div>        pos = Data.Button.WorldPosition;</div><div>        return true;</div><div>      case vtkCommand::MoveEvent3D:</div><div>        pos = Data.Move.WorldPosition;</div><div>        return true;</div><div>    }</div><div>    return false;</div><div>  }</div><div><br></div><div>  bool GetWorldOrientation(double *&val)</div><div>  {</div><div>    switch (Type)</div><div>    {</div><div>      case vtkCommand::ButtonEvent3D:</div><div>        val = Data.Button.WorldOrientation;</div><div>        return true;</div><div>      case vtkCommand::MoveEvent3D:</div><div>        val = Data.Move.WorldOrientation;</div><div>        return true;</div><div>    }</div><div>    return false;</div><div>  }</div><div>};</div></div><span class="m_1942444153477777827m_-323947179837958825m_7750914335351201299HOEnZb"><font color="#888888"><div><br></div><br clear="all"><span class="m_1942444153477777827HOEnZb"><font color="#888888"><div><br></div>-- <br><div class="m_1942444153477777827m_-323947179837958825m_7750914335351201299m_8919680072765453055gmail_signature"><div dir="ltr"><div>Ken Martin PhD<div>Distinguished Engineer<br><span style="font-size:12.8px">Kitware Inc.</span><br></div><div>28 Corporate Drive<br>Clifton Park NY 12065<br><div><br></div><div><span style="font-size:10pt;font-family:Tahoma,sans-serif">This communication,
including all attachments, contains confidential and legally privileged
information, and it is intended only for the use of the addressee.  Access to this email by anyone else is
unauthorized. If you are not the intended recipient, any disclosure, copying,
distribution or any action taken in reliance on it is prohibited and may be
unlawful. If you received this communication in error please notify us
immediately and destroy the original message. 
Thank you.</span></div></div></div></div></div>
</font></span></font></span></div>
</blockquote></div><br></div></div></div>
</blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="m_1942444153477777827gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div>Ken Martin PhD<div>Distinguished Engineer<br><span style="font-size:12.8px">Kitware Inc.</span><br></div><div>28 Corporate Drive<br>Clifton Park NY 12065<br><div><br></div><div><span style="font-size:10pt;font-family:Tahoma,sans-serif">This communication,
including all attachments, contains confidential and legally privileged
information, and it is intended only for the use of the addressee.  Access to this email by anyone else is
unauthorized. If you are not the intended recipient, any disclosure, copying,
distribution or any action taken in reliance on it is prohibited and may be
unlawful. If you received this communication in error please notify us
immediately and destroy the original message. 
Thank you.</span></div></div></div></div></div>
</div>
</div></div></blockquote></div><br></div>