Example: Implementing the IlvDragRectangleInteractor Class
This example demonstrates how the
IlvDragRectangleInteractor member functions are implemented. The example can be used as a starting point to create your own interactor.
The
IlvDragRectangleInteractor interactor allows the user to designate a rectangular region in a view. This rectangle can then be used for various purposes in derived interactors; for instance, a subclass dedicated to the creation of a graphic object can use the rectangle to define the bounding box of the new object.
Here is a slightly revised version of the synopsis of this class:
class IlvDragRectangleInteractor : public IlvManagerViewInteractor { public: IlvDragRectangleInteractor(IlvManager* manager, IlvView* view) : IlvManagerViewInteractor(manager, view) {} virtual void handleEvent(IlvEvent& event); virtual void drawGhost(); virtual void doIt(IlvRect&); virtual void abort(); IlvRect& getRectangle(); protected: IlvRect _xor_rectangle; IlvPos _firstx; IlvPos _firsty; }; |
Three protected fields are defined:
_xor_rectangle - Holds the coordinates of the rectangle being dragged by the user.
_firstx and
_firsty - The coordinates of the first button-down event received. This point is used as the start of the selected rectangle. It can be any one of the 4 corners depending on the direction in which the user drags the rectangle.
The constructor does nothing and the initialization is done by the doIt member function.
Also, four member functions of the
IlvManagerViewInteractor class are overloaded:
abort Member Function
This member function is called to cancel the interaction. The rectangle width is set to 0.
void
IlvDragRectangleInteractor::abort()
{
_xor_rectangle.w(0);
}
handleEvent Member Function
The following shows a simplified version of the
IlvDragRectangleInteractor::handleEvent member function.
void
IlvDragRectangleInteractor::handleEvent(IlvEvent& event)
{
switch(event.type()) {
case IlvKeyUp:
case IlvKeyDown:
getManager()->shortCut(event, getView());
break;
case IlvButtonDown:
if (event.button() != IlvLeftButton)
getManager()->shortCut(event, getView());
else {
_xor_rectangle.w(0);
IlvPoint p(event.x(), event.y());
if (getTransformer()) getTransformer()->inverse(p);
_firstx = p.x();
_firsty = p.y();
}
break;
case IlvButtonDragged:
if ((event.button() != IlvLeftButton))
getManager()->shortCut(event, getView());
else {
if (_xor_rectangle.w()) drawGhost();
IlvPoint p(event.x(), event.y());
if (getTransformer()) getTransformer()->inverse(p);
_xor_rectangle.move(IlvMin(_firstx, p.x()),
IlvMin(_firsty, p.y()));
_xor_rectangle.resize((IlvDim)(IlvMax(_firstx, p.x())
-_xor_rectangle.x()),
(IlvDim)(IlvMax(_firsty, p.y())
-_xor_rectangle.y()));
ensureVisible(IlvPoint(event.x(), event.y()));
drawGhost();
}
break;
case IlvButtonUp:
if (event.button() != IlvLeftButton)
getManager()->shortCut(event, getView());
else {
if (!_xor_rectangle.w()) return;
drawGhost();
IlvRect rect(_xor_rectangle);
_xor_rectangle.w(0);
doIt(rect);
}
break;
}
Here, only button events are managed. Other events are discarded or sent to the manager for possible dispatch to accelerators by means of a call to the
IlvManager::shortCut member function.
The following types of events are handled by the handleEvent member function:
Keyboard Events
You want to ignore these events. The best way to do this without losing the information conveyed by the event is to bypass the natural view interactor process and send the event back to the manager where it might match an accelerator:
case IlvKeyUp: case IlvKeyDown: getManager()->shortCut(event, getView()); break; |
Button-Down Events
case IlvButtonDown: ... break; |
The mouse position is stored in _firstx and _firsty and the rectangle is reset. This is done by setting the width of the rectangle to 0. Then, the coordinates are stored in the object coordinate system:
if (event.button() != IlvLeftButton)
getManager()->shortCut(event, getView());
else {
_xor_rectangle.w(0);
IlvPoint p(event.x(), event.y());
if (getTransformer()) getTransformer()->inverse(p);
_firstx = p.x();
_firsty = p.y();
}
Button-Dragged Events
case IlvButtonDragged:
...
break;
If _xor_rectangle is valid, the rectangle has been drawn with drawGhost and has to be erased:
if (_xor_rectangle.w()) drawGhost();
The new rectangle is computed in the object coordinate system:
IlvPoint p(event.x(), event.y()); if (getTransformer()) getTransformer()->inverse(p); _xor_rectangle.move(IlvMin(_firstx, p.x()), IlvMin(_firsty, p.y())); _xor_rectangle.resize((IlvDim)(IlvMax(_firstx, p.x()) -_xor_rectangle.x()), (IlvDim)(IlvMax(_firsty, p.y()) -_xor_rectangle.y())); |
The following ensures that the dragged point remains on the screen. When the view is in a scrolled view, you can change the view coordinates to keep the mouse position visible:
ensureVisible(IlvPoint(event.x(), event.y()));
The new rectangle is drawn:
drawGhost();
Button-Up Events
A button-up event signifies the end of the interaction; the rectangle has been defined:
case IlvButtonUp: ... break; |
The previous ghost image is erased:
The current rectangle is saved and the interactor is reset:
IlvRect rect(_xor_rectangle); _xor_rectangle.w(0); |
The doIt virtual member function is called. Subclasses overload this method to perform their final task using the rectangle provided as the parameter:
drawGhost Member Function
The
IlvDragRectangleInteractor::drawGhost member function draws a ghost image of
_xor_rectangle:
void IlvDragRectangleInteractor::drawGhost() { IlvManager* mgr = getManager(); if (_xor_rectangle.w()) { IlvRect rect = _xor_rectangle; if(getTransformer()) getTransformer()->apply(rect); getView()->drawRectangle(mgr->getPalette(),rect); } } |
Because _xor_rectangle is expressed in the object coordinate system, the transformer of the view must be applied before drawing the rectangle.
doIt Member Function
The
IlvDragRectangleInteractor::doIt member function does nothing; it is designed to be overloaded to perform actions once the user has selected a rectangular region.
Two examples of how to overload this member function are presented:
The second example shows how to select all the objects located in the rectangular region. This illustrates how to manipulate the selection within a manager without using the select interactor.
Example 1: IlvMakeRectangleInteractor
Here is a simplified version of the
IlvMakeRectangleInteractor::doIt member function, derived from the
IlvDragRectangleInteractor class. This member function deselects all the objects of the manager, creates an
IlvRectangle instance, adds it to the manager, and sets the selection on it.
void IlvMakeRectangleInteractor::doIt(IlvRect& rect) { IlvGraphic* obj = new IlvRectangle(getDisplay(), rect); getManager()->deSelect(); getManager()->addObject(obj); getManager()->makeSelected(obj); } IlvGraphic* obj = new IlvRectangle(getDisplay(), rect); |
Example 2: Selector
This example shows how to implement a simple interactor to select graphic objects. The
IlvDragRectangleInteractor::doIt member function is overloaded in order to select every object located within the region the user has created.
The SelectAnObject function is defined. This is called by an application member function of the manager. The manager is available in the manager parameter:
static void SelectAnObject(IlvGraphic* object, IlAny manager) { ((IlvManager*)manager)->setSelected(object, IlTrue); } |
The doIt member function calls SelectAnObject for each object located in the designated rectangle. To find these objects, call the manager member function applyInside:
void MyRectangleSelector::doIt(IlvRect& rect) { getManager()->applyInside(rect, SelectAnObject, (IlAny)getManager()); } |
Version 6.0
Copyright © 2015, Rogue Wave Software, Inc. All Rights Reserved.