fw4spl
SViewportRangeSelector.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2009-2017.
3  * Distributed under the terms of the GNU Lesser General Public License (LGPL) as
4  * published by the Free Software Foundation.
5  * ****** END LICENSE BLOCK ****** */
6 
7 #include "scene2D/adaptor/SViewportRangeSelector.hpp"
8 
9 #include <fwCom/Signal.hpp>
10 #include <fwCom/Signal.hxx>
11 
12 #include <fwRenderQt/data/Viewport.hpp>
13 #include <fwRenderQt/Scene2DGraphicsView.hpp>
14 
15 #include <fwServices/macros.hpp>
16 
17 #include <QGraphicsRectItem>
18 
20 
21 namespace scene2D
22 {
23 namespace adaptor
24 {
25 
26 static const ::fwServices::IService::KeyType s_VIEWPORT_INOUT = "viewport";
27 
28 //---------------------------------------------------------------------------------------------------------------
29 
30 SViewportRangeSelector::SViewportRangeSelector() noexcept :
31  m_shutter(nullptr),
32  m_isLeftInteracting( false ),
33  m_isRightInteracting( false ),
34  m_isInteracting( false ),
35  m_clickCatchRange( 15 ),
36  m_layer(nullptr),
37  m_initialX(0.f),
38  m_initialWidth(0.f)
39 {
40 }
41 
42 //---------------------------------------------------------------------------------------------------------------
43 
44 SViewportRangeSelector::~SViewportRangeSelector() noexcept
45 {
46 }
47 
48 //---------------------------------------------------------------------------------------------------------------
49 
51 {
52  this->configureParams();
53 
54  const ConfigType config = this->getConfigTree().get_child("config.<xmlattr>");
55  if (config.count("initialWidth"))
56  {
57  m_initialWidth = config.get<float>("initialWidth");
58  }
59 
60  if (config.count("initialPos"))
61  {
62  m_initialX = config.get<float>("initialPos");
63  }
64 }
65 
66 //---------------------------------------------------------------------------------------------------------------
67 
69 {
70  {
71  ::fwRenderQt::data::Viewport::sptr sceneViewport = this->getScene2DRender()->getViewport();
72 
73  const double viewportWidth = sceneViewport->getWidth();
74  const double defaultWidth = 2. * viewportWidth / 4.;
75 
76  if( m_initialWidth > viewportWidth || m_initialWidth < m_clickCatchRange )
77  {
78  SLM_WARN("Set viewport width to a default value instead of the given one because it can't be accepted.");
79  m_initialWidth = defaultWidth;
80  }
81 
82  const double defaultPos = (viewportWidth - m_initialWidth) / 2.;
83  if( m_initialX < sceneViewport->getX() || (m_initialX + m_initialWidth) > viewportWidth)
84  {
85  SLM_WARN("Set viewport position to a default value since the given one is not correct.");
86  m_initialX = defaultPos;
87  }
88  }
89 
90  ::fwRenderQt::data::Viewport::sptr viewport =
91  this->getInOut< ::fwRenderQt::data::Viewport>(s_VIEWPORT_INOUT);
92  QRectF sceneRect = this->getScene2DRender()->getScene()->sceneRect();
93 
94  Point2DType pair = this->mapAdaptorToScene(
95  Point2DType( m_initialX, viewport->getHeight() ), m_xAxis, m_yAxis );
96  m_shutter = new QGraphicsRectItem(
97  pair.first, 0, m_initialWidth * m_xAxis->getScale(), pair.second );
98  m_shutter->setBrush( QBrush(QColor(127, 127, 127, 127)) );
99  m_shutter->setPen( Qt::NoPen );
100 
101  m_layer = new QGraphicsItemGroup();
102  m_layer->addToGroup( m_shutter );
103 
104  // Adjust the layer's position and zValue depending on the associated axis
105  m_layer->setPos(m_xAxis->getOrigin(), m_yAxis->getOrigin());
106  m_layer->setZValue(m_zValue);
107 
108  this->getScene2DRender()->getScene()->addItem( m_layer );
109 
110  QRectF rect = m_shutter->rect();
111  updateViewportFromShutter( rect.x(), rect.y(), rect.width(), rect.height() );
112 
113  ::fwData::Object::ModifiedSignalType::sptr sig;
115  {
116  ::fwCom::Connection::Blocker block(sig->getConnection(m_slotUpdate));
117  sig->asyncEmit();
118  }
119 }
120 
121 //---------------------------------------------------------------------------------------------------------------
122 
124 {
125 }
126 
127 //---------------------------------------------------------------------------------------------------------------
128 
130 {
131 }
132 
133 //---------------------------------------------------------------------------------------------------------------
134 
136 {
137  // Event coordinates in scene
139  coord = this->getScene2DRender()->mapToScene( _event.getCoord() );
140 
141  // Shutter coordinates in scene
142  const Point2DType shutterCoordPair = this->mapAdaptorToScene(
143  Point2DType( m_shutter->rect().x(), m_shutter->rect().y()),
144  m_xAxis, m_yAxis);
145  const double shutterWidth = m_shutter->rect().width() * m_xAxis->getScale();
146 
147  const QRectF sceneRect = this->getScene2DRender()->getScene()->sceneRect();
148 
149  const bool onShutterLeft = mouseOnShutterLeft( coord );
150  const bool onShutterRight = mouseOnShutterRight( coord );
151  const bool onShutterMiddle = mouseOnShutterMiddle( coord );
152 
153  QRectF rect = m_shutter->rect();
154 
155  if( _event.getType() == ::fwRenderQt::data::Event::MouseButtonPress )
156  {
157  if( onShutterLeft )
158  {
159  m_isLeftInteracting = true;
160  }
161  else if( onShutterRight )
162  {
163  m_isRightInteracting = true;
164  }
165  else if( onShutterMiddle )
166  {
167  this->getScene2DRender()->getView()->setCursor( Qt::ClosedHandCursor );
168 
169  // Interaction when clicking on the center of the shutter
170  m_isInteracting = true;
171  m_dragStartPoint = coord;
172  m_dragStartShutterPos.setX( shutterCoordPair.first );
173  m_dragStartShutterPos.setY( shutterCoordPair.second );
174  }
175  }
176  else if( _event.getType() == ::fwRenderQt::data::Event::MouseButtonRelease )
177  {
178  m_isInteracting = false;
179  m_isLeftInteracting = false;
180  m_isRightInteracting = false;
181 
182  // Reset cursor
183  if( onShutterMiddle )
184  {
185  this->getScene2DRender()->getView()->setCursor( Qt::OpenHandCursor );
186  }
187  else
188  {
189  this->getScene2DRender()->getView()->setCursor( Qt::ArrowCursor );
190  }
191  }
192  else if( _event.getType() == ::fwRenderQt::data::Event::MouseMove )
193  {
194  // If the mouse is moving onto the shutter, without interactions, the cursor is changed to an other cursor
195  // that symbolize the available interactions
196  if( !m_isLeftInteracting && !m_isRightInteracting && !m_isInteracting )
197  {
198  if( onShutterLeft || onShutterRight )
199  {
200  this->getScene2DRender()->getView()->setCursor( Qt::SizeHorCursor ); // horizontal double arrow
201  }
202  else if( onShutterMiddle )
203  {
204  this->getScene2DRender()->getView()->setCursor( Qt::OpenHandCursor ); // open hand, for moving the
205  // whole shutter
206  }
207  else
208  {
209  this->getScene2DRender()->getView()->setCursor( Qt::ArrowCursor ); // reset the cursor to the
210  // default cursor
211  }
212  }
213 
214  bool update = false; // if a viewport update will be requested
215 
216  if( m_isLeftInteracting )
217  {
218  // Shutter right side position
219  const double rightSide = rect.x() + rect.width();
220 
221  if( coord.getX() < rightSide - m_clickCatchRange)
222  {
223  if( coord.getX() >= sceneRect.x() )
224  {
225  rect.setX( coord.getX() );
226  }
227  else
228  {
229  rect.setX( sceneRect.x() );
230  }
231  }
232  else
233  {
234  rect.setX( rightSide - m_clickCatchRange );
235  }
236 
237  update = true;
238  }
239  else if( m_isRightInteracting )
240  {
241  const double newWidth = coord.getX() - shutterCoordPair.first;
242  const double shutterRightPos = abs(sceneRect.x()) + shutterCoordPair.first + newWidth;
243 
244  if( newWidth > m_clickCatchRange ) // Shutter's width must be greater than the allowed picking range
245  {
246  if( shutterRightPos < sceneRect.width() )
247  {
248  rect.setWidth( newWidth );
249  }
250  else
251  {
252  rect.setWidth( sceneRect.width() - shutterCoordPair.first - abs(sceneRect.x()) );
253  }
254  }
255  else
256  {
257  rect.setWidth( m_clickCatchRange );
258  }
259 
260  update = true;
261  }
262  else if( m_isInteracting )
263  {
264  const double offset = coord.getX() - m_dragStartPoint.getX();
265  const double newX = m_dragStartShutterPos.getX() + offset;
266  const double shutterRightPos = abs(sceneRect.x()) + newX + shutterWidth;
267 
268  if( newX >= sceneRect.x() && shutterRightPos < sceneRect.width() )
269  {
270  rect.setX( newX );
271  }
272  else if( newX < sceneRect.x() )
273  {
274  rect.setX( sceneRect.x() );
275  }
276  else if( shutterRightPos >= sceneRect.width() )
277  {
278  rect.setX( sceneRect.width() - shutterWidth - abs(sceneRect.x()) );
279  }
280 
281  rect.setWidth( shutterWidth );
282  update = true;
283  }
284 
285  if( update )
286  {
287  // Update graphical shutter
288  m_shutter->setRect( rect );
289  m_layer->removeFromGroup( m_shutter );
290  m_layer->addToGroup( m_shutter );
291 
292  // Update object
293  this->updateViewportFromShutter( rect.x(), rect.y(), rect.width(), rect.height() );
294 
295  ::fwRenderQt::data::Viewport::sptr viewport =
296  this->getInOut< ::fwRenderQt::data::Viewport>(s_VIEWPORT_INOUT);
297 
298  ::fwData::Object::ModifiedSignalType::sptr sig =
300  {
301  ::fwCom::Connection::Blocker block(sig->getConnection(m_slotUpdate));
302  sig->asyncEmit();
303  }
304  }
305  }
306 }
307 
308 //---------------------------------------------------------------------------------------------------------------
309 
310 void SViewportRangeSelector::updateViewportFromShutter( double _x, double _y, double _width, double _height )
311 {
312  ::fwRenderQt::data::Viewport::sptr viewport =
313  this->getInOut< ::fwRenderQt::data::Viewport>(s_VIEWPORT_INOUT);
314 
315  const Point2DType fromSceneCoord = this->mapSceneToAdaptor(Point2DType( _x, _y ), m_xAxis, m_yAxis );
316  viewport->setX( fromSceneCoord.first );
317  viewport->setY( fromSceneCoord.second );
318 
319  const Point2DType pair = this->mapSceneToAdaptor(Point2DType(_width, _height), m_xAxis, m_yAxis);
320  viewport->setWidth( pair.first );
321  viewport->setHeight( this->getScene2DRender()->getViewport()->getHeight() );
322 }
323 
324 //---------------------------------------------------------------------------------------------------------------
325 
326 bool SViewportRangeSelector::mouseOnShutterMiddle( ::fwRenderQt::data::Coord _coord)
327 {
328  Point2DType shutterCoordPair;
329  shutterCoordPair = this->mapAdaptorToScene( Point2DType( m_shutter->rect().x(), m_shutter->rect().y()),
330  m_xAxis, m_yAxis );
331 
332  return ( _coord.getX() > m_shutter->rect().x() + m_clickCatchRange )
333  && ( _coord.getX() < m_shutter->rect().x() + m_shutter->rect().width() - m_clickCatchRange );
334 }
335 
336 //---------------------------------------------------------------------------------------------------------------
337 
338 bool SViewportRangeSelector::mouseOnShutterLeft( ::fwRenderQt::data::Coord _coord)
339 {
340  Point2DType shutterCoordPair = this->mapAdaptorToScene(
341  Point2DType( m_shutter->rect().x(), m_shutter->rect().y() ), m_xAxis, m_yAxis );
342 
343  return ( _coord.getX() >= shutterCoordPair.first - m_clickCatchRange )
344  && ( _coord.getX() <= shutterCoordPair.first + m_clickCatchRange );
345 }
346 
347 //---------------------------------------------------------------------------------------------------------------
348 
349 bool SViewportRangeSelector::mouseOnShutterRight( ::fwRenderQt::data::Coord _coord)
350 {
351  const Point2DType shutterCoordPair = this->mapAdaptorToScene(
352  Point2DType( m_shutter->rect().x(), m_shutter->rect().y()),
353  m_xAxis, m_yAxis );
354 
355  const double shutterRightPos = shutterCoordPair.first + m_shutter->rect().width() * m_xAxis->getScale();
356 
357  return ( _coord.getX() >= shutterRightPos - m_clickCatchRange )
358  && ( _coord.getX() <= shutterRightPos + m_clickCatchRange );
359 }
360 
361 //----------------------------------------------------------------------------------------------------------
362 
364 {
365  KeyConnectionsMap connections;
366  connections.push( s_VIEWPORT_INOUT, ::fwRenderQt::data::Viewport::s_MODIFIED_SIG, s_UPDATE_SLOT );
367  return connections;
368 }
369 
370 //---------------------------------------------------------------------------------------------------------------
371 
372 } // namespace adaptor
373 } // namespace scene2D
Root class for all scene2d adaptors.
::fwRenderQt::data::Axis::sptr m_xAxis
The x Axis.
This class is a helper to define the connections of a service and its data.
Definition: IService.hpp:454
SCENE2D_API void updateViewportFromShutter(double x, double y, double width, double height)
Class allowing to block a Connection.
Definition: Connection.hpp:20
FWRENDERQT_API Point2DType mapAdaptorToScene(const Point2DType &_xy, const ::fwRenderQt::data::Axis::sptr &_xAxis, const ::fwRenderQt::data::Axis::sptr &_yAxis) const
SCENE2D_API void stopping() override
Uninitialize the service activity. The stop() method is always invoked before destroying a service...
FWSERVICES_API SharedFutureType update()
Invoke updating() if m_globalState == STARTED. Does nothing otherwise.
Definition: IService.cpp:334
FWRENDERQT_API std::shared_ptr< ::fwRenderQt::SRender > getScene2DRender() const
Get the render that manages the IAdaptor.
std::pair< double, double > Point2DType
Point2D coordinate <X, Y>
#define SLM_WARN(message)
Definition: spyLog.hpp:261
SCENE2D_API void processInteraction(::fwRenderQt::data::Event &_event) override
SCENE2D_API void configuring() override
Configure the service before starting. Apply the configuration to service.
This bundles contains data and services used to display a 2D Qt scene.
UpdateSlotType::sptr m_slotUpdate
Slot to call update method.
Definition: IService.hpp:690
FWRENDERQT_API Point2DType mapSceneToAdaptor(const Point2DType &_xy, const ::fwRenderQt::data::Axis::sptr &_xAxis, const ::fwRenderQt::data::Axis::sptr &_yAxis) const
::fwRenderQt::data::Axis::sptr m_yAxis
The y Axis.
FWRENDERQT_API void configureParams()
Parse the xml configuration for Axis, z value and opacity.
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_MODIFIED_SIG
Key in m_signals map of signal m_sigModified.
The viewport range selector adaptor allows to select a delimited range of a viewport. It uses a graphical delimiter (called shutter) that can be moved from both left to right and right to left directions (in those cases, shutter&#39;s width is changing).
SCENE2D_API KeyConnectionsMap getAutoConnections() const override
Returns proposals to connect service slots to associated objects signals, this method is used for obj...
SCENE2D_API void starting() override
Initialize the service activity.
This class manage events on the scene 2D (mouse event, keyboard event , ...).
Definition: Event.hpp:26
SCENE2D_API void updating() override
Perform some computations according to object (this service is attached to) attribute values and its ...
static FWSERVICES_APIconst::fwCom::Slots::SlotKeyType s_UPDATE_SLOT
Slot to call start method.
Definition: IService.hpp:177
FWSERVICES_API ConfigType getConfigTree() const
Return the configuration, in an boost property tree.
Definition: IService.cpp:247