changeset 1208:00e6bff9ea39 broker

handling of mouse interactions in ViewportController
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 03 Dec 2019 18:51:03 +0100
parents 17a92c39c633
children 644baa70373d
files Framework/Scene2D/PointerEvent.cpp Framework/Scene2D/PointerEvent.h Framework/Scene2DViewport/ViewportController.cpp Framework/Scene2DViewport/ViewportController.h Framework/StoneEnumerations.h Framework/Viewport/IViewport.h
diffstat 6 files changed, 186 insertions(+), 74 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Scene2D/PointerEvent.cpp	Tue Dec 03 12:29:06 2019 +0100
+++ b/Framework/Scene2D/PointerEvent.cpp	Tue Dec 03 18:51:03 2019 +0100
@@ -26,6 +26,7 @@
 namespace OrthancStone
 {
   PointerEvent::PointerEvent() :
+    button_(MouseButton_None),
     hasAltModifier_(false),
     hasControlModifier_(false),
     hasShiftModifier_(false)
--- a/Framework/Scene2D/PointerEvent.h	Tue Dec 03 12:29:06 2019 +0100
+++ b/Framework/Scene2D/PointerEvent.h	Tue Dec 03 18:51:03 2019 +0100
@@ -31,6 +31,7 @@
   class PointerEvent : public boost::noncopyable
   {
   private:
+    MouseButton                button_;
     std::vector<ScenePoint2D>  positions_;
     bool                       hasAltModifier_;
     bool                       hasControlModifier_;
@@ -88,5 +89,15 @@
     {
       return hasShiftModifier_;
     }
+
+    void SetMouseButton(MouseButton button)
+    {
+      button_ = button;
+    }
+
+    MouseButton GetMouseButton() const
+    {
+      return button_;
+    }
   };
 }
--- a/Framework/Scene2DViewport/ViewportController.cpp	Tue Dec 03 12:29:06 2019 +0100
+++ b/Framework/Scene2DViewport/ViewportController.cpp	Tue Dec 03 18:51:03 2019 +0100
@@ -24,39 +24,75 @@
 #include "MeasureCommands.h"
 
 #include "../StoneException.h"
+#include "../Scene2D/PanSceneTracker.h"
+#include "../Scene2D/RotateSceneTracker.h"
+#include "../Scene2D/ZoomSceneTracker.h"
 
 #include <boost/make_shared.hpp>
 
 namespace OrthancStone
 {
+  IFlexiblePointerTracker* DefaultViewportInteractor::CreateTracker(boost::shared_ptr<ViewportController> controller,
+                                                                    const PointerEvent& event)
+  {
+    switch (event.GetMouseButton())
+    {
+      case MouseButton_Left:
+        return new RotateSceneTracker(controller, event);
+
+      case MouseButton_Middle:
+        return new PanSceneTracker(controller, event);
+      
+      case MouseButton_Right:
+      {
+        std::auto_ptr<IViewport::ILock> lock(controller->GetViewport().Lock());
+        if (lock->HasCompositor())
+        {
+          return new ZoomSceneTracker(controller, event, lock->GetCompositor().GetCanvasWidth());
+        }
+        else
+        {
+          return NULL;
+        }
+      }
+
+      default:
+        return NULL;
+    }
+  }
+
+
   ViewportController::ViewportController(boost::weak_ptr<UndoStack> undoStackW,
-                                         IViewport& viewport)
+                                         boost::shared_ptr<IViewport> viewport)
     : undoStackW_(undoStackW)
-    , canvasToSceneFactor_(0.0)
     , viewport_(viewport)
+    , canvasToSceneFactor_(1)
   {
+    if (viewport.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+
+    interactor_.reset(new DefaultViewportInteractor);
   }
  
   ViewportController::~ViewportController()
   {
-
   }
 
-  boost::shared_ptr<UndoStack> ViewportController::GetUndoStack()
+  void ViewportController::SetInteractor(boost::shared_ptr<IViewportInteractor> interactor)
   {
-    return undoStackW_.lock();
-  }
-
-  boost::shared_ptr<const UndoStack> ViewportController::GetUndoStack() const
-  {
-    return undoStackW_.lock();
+    activeTracker_.reset();
+    interactor_ = interactor;
   }
 
   void ViewportController::PushCommand(boost::shared_ptr<MeasureCommand> command)
   {
     boost::shared_ptr<UndoStack> undoStack = undoStackW_.lock();
-    if(undoStack.get() != NULL)
+    if (undoStack.get() != NULL)
+    {
       undoStack->PushCommand(command);
+    }
     else
     {
       LOG(ERROR) << "Internal error: no undo stack in the viewport controller!";
@@ -67,7 +103,9 @@
   {
     boost::shared_ptr<UndoStack> undoStack = undoStackW_.lock();
     if (undoStack.get() != NULL)
+    {
       undoStack->Undo();
+    }
     else
     {
       LOG(ERROR) << "Internal error: no undo stack in the viewport controller!";
@@ -78,7 +116,9 @@
   {
     boost::shared_ptr<UndoStack> undoStack = undoStackW_.lock();
     if (undoStack.get() != NULL)
+    {
       undoStack->Redo();
+    }
     else
     {
       LOG(ERROR) << "Internal error: no undo stack in the viewport controller!";
@@ -89,7 +129,9 @@
   {
     boost::shared_ptr<UndoStack> undoStack = undoStackW_.lock();
     if (undoStack.get() != NULL)
+    {
       return undoStack->CanUndo();
+    }
     else
     {
       LOG(ERROR) << "Internal error: no undo stack in the viewport controller!";
@@ -101,7 +143,9 @@
   {
     boost::shared_ptr<UndoStack> undoStack = undoStackW_.lock();
     if (undoStack.get() != NULL)
+    {
       return undoStack->CanRedo();
+    }
     else
     {
       LOG(ERROR) << "Internal error: no undo stack in the viewport controller!";
@@ -109,11 +153,6 @@
     }
   }
   
-  bool ViewportController::HandlePointerEvent(PointerEvent e)
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-  }
-
   std::vector<boost::shared_ptr<MeasureTool> > ViewportController::HitTestMeasureTools(
     ScenePoint2D p)
   {
@@ -138,37 +177,22 @@
 
   OrthancStone::AffineTransform2D ViewportController::GetCanvasToSceneTransform() const
   {
-    std::auto_ptr<IViewport::ILock> lock(viewport_.Lock());
+    std::auto_ptr<IViewport::ILock> lock(viewport_->Lock());
     return lock->GetScene().GetCanvasToSceneTransform();
   }
 
   OrthancStone::AffineTransform2D ViewportController::GetSceneToCanvasTransform() const
   {
-    std::auto_ptr<IViewport::ILock> lock(viewport_.Lock());
+    std::auto_ptr<IViewport::ILock> lock(viewport_->Lock());
     return lock->GetScene().GetSceneToCanvasTransform();
   }
 
-  void ViewportController::SetSceneToCanvasTransform(
-    const AffineTransform2D& transform)
+  void ViewportController::SetSceneToCanvasTransform(const AffineTransform2D& transform)
   {
     {
-      std::auto_ptr<IViewport::ILock> lock(viewport_.Lock());
+      std::auto_ptr<IViewport::ILock> lock(viewport_->Lock());
       lock->GetScene().SetSceneToCanvasTransform(transform);
-    }
-    
-    BroadcastMessage(SceneTransformChanged(*this));
-    
-    // update the canvas to scene factor
-    canvasToSceneFactor_ = 0.0;
-    canvasToSceneFactor_ = GetCanvasToSceneFactor();
-  }
-
-  void ViewportController::FitContent(
-    unsigned int canvasWidth, unsigned int canvasHeight)
-  {
-    {
-      std::auto_ptr<IViewport::ILock> lock(viewport_.Lock());
-      lock->GetScene().FitContent(canvasWidth, canvasHeight);
+      canvasToSceneFactor_ = lock->GetScene().GetCanvasToSceneTransform().ComputeZoom();
     }
     
     BroadcastMessage(SceneTransformChanged(*this));
@@ -176,14 +200,13 @@
 
   void ViewportController::FitContent()
   {
-    std::auto_ptr<IViewport::ILock> lock(viewport_.Lock());
+    {
+      std::auto_ptr<IViewport::ILock> lock(viewport_->Lock());
+      lock->FitContent();
+      canvasToSceneFactor_ = lock->GetScene().GetCanvasToSceneTransform().ComputeZoom();
+    }
 
-    if (lock->HasCompositor())
-    {
-      const ICompositor& compositor = lock->GetCompositor();
-      lock->GetScene().FitContent(compositor.GetCanvasWidth(), compositor.GetCanvasHeight());
-      BroadcastMessage(SceneTransformChanged(*this));
-    }
+    BroadcastMessage(SceneTransformChanged(*this));
   }
 
   void ViewportController::AddMeasureTool(boost::shared_ptr<MeasureTool> measureTool)
@@ -202,14 +225,8 @@
       measureTools_.end());
   }
 
-
   double ViewportController::GetCanvasToSceneFactor() const
   {
-    if (canvasToSceneFactor_ == 0)
-    {
-      std::auto_ptr<IViewport::ILock> lock(viewport_.Lock());
-      canvasToSceneFactor_ = lock->GetScene().GetCanvasToSceneTransform().ComputeZoom();
-    }
     return canvasToSceneFactor_;
   }
 
@@ -232,4 +249,62 @@
   {
     return TEXT_CENTER_DISTANCE_CANVAS_COORD * GetCanvasToSceneFactor();
   }
+
+
+  void ViewportController::HandleMousePress(const PointerEvent& event)
+  {
+    if (activeTracker_)
+    {
+      // We are dealing with a multi-stage tracker (that is made of several interactions)
+      activeTracker_->PointerDown(event);
+
+      if (!activeTracker_->IsAlive())
+      {
+        activeTracker_.reset();
+      }
+    }
+    else
+    {
+      // Check whether there is already a measure tool at that position
+      for (size_t i = 0; i < measureTools_.size(); ++i)
+      {
+        if (measureTools_[i]->HitTest(event.GetMainPosition()))
+        {
+          activeTracker_ = measureTools_[i]->CreateEditionTracker(event);
+          return;
+        }
+      }
+
+      // No measure tool, create new tracker from the interactor
+      if (interactor_)
+      {
+        activeTracker_.reset(interactor_->CreateTracker(shared_from_this(), event));
+      }
+      else
+      {
+        activeTracker_.reset();
+      }
+    }
+  }
+
+  void ViewportController::HandleMouseMove(const PointerEvent& event)
+  {
+    if (activeTracker_)
+    {
+      activeTracker_->PointerMove(event);
+    }
+  }
+
+  void ViewportController::HandleMouseRelease(const PointerEvent& event)
+  {
+    if (activeTracker_)
+    {
+      activeTracker_->PointerUp(event);
+
+      if (!activeTracker_->IsAlive())
+      {
+        activeTracker_.reset();
+      }
+    }
+  }
 }
--- a/Framework/Scene2DViewport/ViewportController.h	Tue Dec 03 12:29:06 2019 +0100
+++ b/Framework/Scene2DViewport/ViewportController.h	Tue Dec 03 18:51:03 2019 +0100
@@ -23,13 +23,35 @@
 #include "PredeclaredTypes.h"
 
 #include "../Viewport/IViewport.h"
-#include "../Scene2D/PointerEvent.h"
 #include "../Scene2DViewport/IFlexiblePointerTracker.h"
 
+#include <boost/enable_shared_from_this.hpp>
 #include <stack>
 
 namespace OrthancStone
 {
+  // TODO - Move this to another file
+  class IViewportInteractor : public boost::noncopyable
+  {
+  public:
+    virtual ~IViewportInteractor()
+    {
+    }
+
+    virtual IFlexiblePointerTracker* CreateTracker(boost::shared_ptr<ViewportController> controller,
+                                                   const PointerEvent& event) = 0;
+  };
+
+
+  // TODO - Move this to another file
+  class DefaultViewportInteractor : public IViewportInteractor
+  {
+  public:
+    virtual IFlexiblePointerTracker* CreateTracker(boost::shared_ptr<ViewportController> controller,
+                                                   const PointerEvent& event) ORTHANC_OVERRIDE;
+  };
+
+
   class UndoStack;
 
   const double ARC_RADIUS_CANVAS_COORD = 30.0;
@@ -74,23 +96,20 @@
   Each canvas or other GUI area where we want to display a 2D image, either 
   directly or through slicing must be assigned a ViewportController.
   */
-  class ViewportController : public IObservable
+  class ViewportController : 
+    public IObservable,
+    public boost::enable_shared_from_this<ViewportController>
   {
   public:
     ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, \
-      SceneTransformChanged, ViewportController);
+                                        SceneTransformChanged, ViewportController);
 
     ViewportController(boost::weak_ptr<UndoStack> undoStackW,
-                       IViewport& viewport);
-
+                       boost::shared_ptr<IViewport> viewport);
 
     ~ViewportController();
 
-    /** 
-    This method is called by the GUI system and should update/delete the
-    current tracker
-    */
-    bool HandlePointerEvent(PointerEvent e);
+    void SetInteractor(boost::shared_ptr<IViewportInteractor> interactor);
 
     /**
     This method returns the list of measure tools containing the supplied point
@@ -109,7 +128,7 @@
     With this method, the object takes ownership of the supplied tracker and
     updates it according to user interaction
     */
-    void SetActiveTracker(boost::shared_ptr<IFlexiblePointerTracker> tracker);
+    void AcquireActiveTracker(IFlexiblePointerTracker* tracker);
 
     /** Forwarded to the underlying scene */
     AffineTransform2D GetCanvasToSceneTransform() const;
@@ -121,7 +140,6 @@
     void SetSceneToCanvasTransform(const AffineTransform2D& transform);
 
     /** Forwarded to the underlying scene, and broadcasted to the observers */
-    void FitContent(unsigned int canvasWidth, unsigned int canvasHeight);
     void FitContent();
 
     /** Adds a new measure tool */
@@ -174,24 +192,29 @@
 
     IViewport& GetViewport() const
     {
-      return viewport_;
+      return *viewport_;
     }
 
+
+    // Must be expressed in canvas coordinates
+    void HandleMousePress(const PointerEvent& event);
+
+    // Must be expressed in canvas coordinates
+    void HandleMouseMove(const PointerEvent& event);
+
+    // Must be expressed in canvas coordinates
+    void HandleMouseRelease(const PointerEvent& event);
+
   private:
     double GetCanvasToSceneFactor() const;
 
-    boost::weak_ptr<UndoStack>                   undoStackW_;
-
-    boost::shared_ptr<UndoStack>                 GetUndoStack();
-    boost::shared_ptr<const UndoStack>           GetUndoStack() const;
-
-    std::vector<boost::shared_ptr<MeasureTool> > measureTools_;
-    boost::shared_ptr<IFlexiblePointerTracker>   tracker_;
+    boost::weak_ptr<UndoStack>                    undoStackW_;  // Global stack, possibly shared by all viewports
+    boost::shared_ptr<IViewport>                  viewport_;
+    boost::shared_ptr<IViewportInteractor>        interactor_;   // Application-specific factory of trackers
+    std::vector<boost::shared_ptr<MeasureTool> >  measureTools_;
+    boost::shared_ptr<IFlexiblePointerTracker>    activeTracker_;  // TODO - Can't this be a "std::auto_ptr"?
     
     // this is cached
-    mutable double              canvasToSceneFactor_;
-    
-    // Refactoring on 2019-07-10: Removing shared_ptr from scene
-    IViewport&      viewport_;
+    double  canvasToSceneFactor_;    
   };
 }
--- a/Framework/StoneEnumerations.h	Tue Dec 03 12:29:06 2019 +0100
+++ b/Framework/StoneEnumerations.h	Tue Dec 03 18:51:03 2019 +0100
@@ -59,7 +59,8 @@
   {
     MouseButton_Left,
     MouseButton_Right,
-    MouseButton_Middle
+    MouseButton_Middle,
+    MouseButton_None   // For instance, because of touch event
   };
 
   enum MouseWheelDirection
--- a/Framework/Viewport/IViewport.h	Tue Dec 03 12:29:06 2019 +0100
+++ b/Framework/Viewport/IViewport.h	Tue Dec 03 18:51:03 2019 +0100
@@ -49,6 +49,7 @@
 
       virtual Scene2D& GetScene() = 0;
 
+      // Get the center of the given pixel, in canvas coordinates
       virtual ScenePoint2D GetPixelCenterCoordinates(int x, int y) = 0;
 
       virtual bool HasCompositor() const = 0;