Managing Events: Object Interactors
An object interactor filters user events for the graphic object to which it is attached. If an appropriate series of events occurs, the object interactor triggers a visible response from that graphic object. This response is called the object behavior.
Rogue Wave® Views provides a comprehensive set of predefined object interactors. If you find yourself needing a very specific functionality not already predefined in Rogue Wave Views, you can subtype one of the interactor classes and replace its member function handleEvent with the functionality you need.
Figure 9.1 Attaching an Interactor to an Object
The class
IlvInteractor lets you associate a behavior with an object.
Note: These object interactors are not intended for creating editors. The Rogue Wave Views Manager interactors that are associated with the whole view instead of individual objects are used to create interactive editors. |
For more details on object interactors, see:
Using Object Interactors
The member functions that deal with object interactors are the following:
In the following example, a predefined Rogue Wave Views interactor is associated with an
IlvLabel graphic. It is an instance of the
IlvMoveInteractor class which lets the user move an object by pressing the left mouse button when pointing to this object.
IlvRect size(0, 0, 300, 300); IlvContainer* cont = new IlvContainer(display, "Cont", "My Window", size, IlTrue, IlFalse); IlvLabel* label = new IlvLabel(display, IlvPoint(100,100), "Hello world!"); cont->addObject(label); label->setInteractor(IlvInteractor::Get("Move")); |
The static member function
IlvInteractor::Get returns the unique instance of the object interactor whose name is “Move.” You usually do not create an interactor by calling its constructor directly, but by using this static member function. This is because most object interactors can be shared by numerous graphic objects at the same time.
Registering a New IlvInteractor Subclass
If you subtype the
IlvInteractor class, you have to register your subclass in order to use the static member function
IlvInteractor::Get. Below is that part of the header file where the
IlvInteractor class in
MyInteractor class is subtyped:
class MyInteractor : public IlvInteractor { public: IlBoolean handleEvent(IlvGraphic* obj, IlvEvent& event, IlvTransformer* t); ... DeclareInteractorTypeInfo(MyInteractor); }; |
Note that we have added the line that calls the macro
DeclareInteractorTypeInfo that allows for class persistence, as well as registration. This macro forces you to define a constructor that expects a reference to an
IlvInputFile, a copy constructor that expects a reference to a
MyInteractor, and a
write member function that is needed to save the interactor instance. Of course the constructor and the write must match, just as in the case of the class
IlvGraphic.
In the situation where your interactor has no extra information to save, you would have used the macro DeclareInteractorTypeInfoRO that does not force you to define a write member function.
In the example there is not any extra information to save, but for the completeness of the example, we pretend to have a dummy integer value to save and read:
MyInteractor::MyInteractor(IlvInputFile& file) : IlvInteractor(file) { IlInt i; file.getStream() >> i; // Read a (dummy) integer value } IlvInteractor* MyInteractor::write(IlvOutputFile& file) { file.getStream() << (IlInt)0; } |
If you had used DeclareInteractorTypeInfoRO, the constructor would have been empty, and write would not have been defined.
In the source file, outside the body of any other function, you must write the two following instructions:
IlvPredefinedInteractorIOMembers(MyInteractor) IlvPredefinedInteractorIOMembers is a macro that generates the proxy function for you that will call your constructor from input file and define the copy member function.
IlvRegisterInteractorClass(MyInteractor, IlvInteractor); IlvRegisterInteractorClass is a macro that registers the class MyInteractor as a new available interactor class. The second parameter must be the name of the parent class.
Predefined Object Interactors
Several classes help you program predefined object interactors:
IlvButtonInteractor This class can be attached to any graphic object to make it behave like a standard interface button.
IlvToggleInteractor This class is a subtype of the class
IlvButtonInteractor. This subclass inverts the object (calls its member function
invert) to which this interactor is associated when the user presses then releases the mouse button over it.
IlvMoveInteractor Lets the user move an object by clicking on it and dragging the pointing device to another place.
IlvReshapeInteractor Lets the user reshape an object by creating a rectangle with the right mouse button. The rectangle becomes the object’s new bounding box.
IlvDragDropInteractor Provides a way to drag and drop an object from a container to another view. It lets you click an object and move a copy of the object around, even outside the container itself.
Example: Linking an Interactor and an Accelerator
In the following example, we create the window below and the two enclosed drawings:
Figure 9.2 Container with Two Objects
In other words, we create a container as a top window, then we add two objects to the container: a gray ellipse and an arc. However, before actually placing these objects in the container, we create a reshaping interactor, enabling us to use the mouse to change the shape of a graphic object. We actually use this single interactor in two places:
Immediately after adding the ellipse to the container, we associate the reshaping interactor with this object.
Immediately after adding the arc to the container, we associate the same reshaping interactor with it.
We can now click either of the two graphic objects, and the reshaping interactor lets us change the shape of the selected object by dragging the mouse.
Furthermore, after creating the two objects and the unique interactor, we create an accelerator for the container, so we can get printed information about an object by double-clicking it with the left mouse button:
If we double-click the ellipse, we get the following message:
Object is an IlvFilledEllipseIf we double-click the arc, we get the following message:
Object is an IlvArcWe write a three-argument function called
PrintType, which has the predefined type
IlvContainerAction, to produce these messages. The name of this function is given to the container as the first argument of the call to the member function
addAccelerator.
Following is the entire demonstration program. To keep the example short, we do not provide any way to exit from the program:
#include <ilviews/contain/contain.h> #include <ilviews/graphics/ellipse.h> #include <ilviews/graphics/arc.h> #include <ilviews/graphics/inter.h> static void PrintType(IlvContainer*, IlvEvent&, IlAny); int main(int argc, char* argv[]) { IlvDisplay* display = new IlvDisplay("Demo", "", argc, argv); if (!display || display->isBad()) { IlvFatalError("Couldn't open display"); delete display; IlvExit(-1); } IlvContainer* container = new IlvContainer(display, "Demo", "Demo", IlvRect(0, 0, 200, 200), IlTrue, IlFalse); IlvInteractor* reshape = IlvInteractor::Get("Reshape"); IlvGraphic* object = new IlvFilledEllipse(display, IlvRect(150, 50, 40, 20)); container->addObject(object); object->setInteractor(reshape); container->addObject(object = new IlvArc(display, IlvRect(10, 150, 40, 40), 0., 60.)); object->setInteractor(reshape); container->addAccelerator(PrintType, IlvDoubleClick, IlvLeftButton); container->show(); IlvMainLoop(); return 0; } static void PrintType(IlvContainer* view, IlvEvent& event, IlAny) { IlvGraphic* object = view->contains(IlvPoint(event.x(), event.y())); if (object) IlvPrint("Object is an ‘%s’\n" , object->className()); } |
Analyzing the Example
This section explains the code used for the above example.
static void PrintType(IlvContainer*, IlvEvent&, IlAny);
The user-defined PrintType function is called by an accelerator, which is launched when the user double-clicks a graphic object with the left mouse button. The signature of the PrintType function corresponds to the type that is called IlvContainerAction.
IlvContainer* container = new IlvContainer(display, "Demo", "Demo",
IlvRect(0, 0, 200, 20), IlTrue, IlFalse);
A container is created as the top-level view.
IlvInteractor* reshape = IlvInteractor::Get("Reshape");
A reshaping interactor is located. This was registered automatically when you included <ilviews/graphics/inter.h>.
IlvGraphic* object = new IlvFilledEllipse(display, IlvRect(150,50, 40,20));
container->addObject(object);
A filled ellipse is created and added to the container.
object->setInteractor(reshape);
The reshaping interactor is associated with the filled ellipse.
container->addObject(object =
new IlvArc(display, IlvRect(10, 150, 40, 40), 0., 60.));
An arc is added to the container.
object->setInteractor(reshape);
The reshaping interactor is associated with the arc.
container->addAccelerator(PrintType, IlvDoubleClick, IlvLeftButton);
An accelerator is added to the container. This accelerator applies the user-defined function named PrintType whenever the user double-clicks an object with the left mouse button.
static void PrintType(IlvContainer* view, IlvEvent& ev, IlAny) { IlvGraphic* object = view->contains(IlvPoint(ev.x(), ev.y())); if (object) IlvPrint("Object is a '%s’\n", object–>className()); } |
This is the actual implementation of the user-defined
PrintType function. We access the object located under the mouse pointer by calling the member function
contains. We use the function
IlvPrint to ensure the portability of this example.
Version 6.0
Copyright © 2015, Rogue Wave Software, Inc. All Rights Reserved.