Skip navigation

Introduction

Most ZVTM-based applications do not make use of scroll bars in Views, simply because they do not make sense as virtual spaces are infinite (unbounded) surfaces. Panning/scrolling in a View is usually achieved through direct dragging of the representation (as with the "hand" in tools such as Adobe Photoshop) or through rate-based scrolling (where the amount of dragging controls the camera panning speed). The latter is probably the most efficient way of panning a large/unbounded 2D representation.

However there are situations where client applications might want to offer more common panning controls such as scroll bars. ZVTM now makes this possible. This requires defining practical bounds to the virtual space. We chose to define those bounds as the smallest rectangular region containing all glyphs in the virtual space to which the camera controlled by the scroll bars belongs.

Scroll bars can coexist with the other navigation techniques supported by ZVTM (such as the above-mentioned rate-based scrolling). Synchronizing the scroll bar sliders is automatically taken care of by the toolkit. It is always possible to zoom in/out in a view, and the size of scroll bar sliders is updated accordingly, as illustrated in the figures below.

View with scroll barsView with scroll bars

Instantiating the scroll bar component

The scroll bars themselves are implemented using glyphs observed through a camera in a dedicated virtual space. All these elements are managed through an instance of fr.inria.zvtm.engine.ScrollLayer which has to be created:

VirtualSpaceManager vsm; View v; Camera mainCamera; // main camera observing your graphical objects, which you want to control with the scroll bars ScrollLayer sl; ... /* Creating a view to display what is observed through */ /* mainCamera, with scroll bars to control that camera. */ Vector cameras = new Vector(); cameras.add(mainCamera); sl = new ScrollLayer(vsm, mainCamera); cameras.add(sl.getWidgetCamera()); v = vsm.addExternalView(cameras, ...); sl.setView(v);

In the above example, scroll bar glyphs are created automatically. It is also possible to instantiate manually the eight glyphs that make both scroll bars and pass them to the ScrollLayer constructor.

Managing ViewEventHandler events sent to the scroll bar layer

Instantiating a ScrollLayer and associating it with a view and camera to be controlled by it is not sufficient to make it interactive. It is up to the client application to redirect events from the corresponding layer in the view to the ScrollLayer object.

The first thing to do is associate a specific ViewEventHandler implementation with the scroll layer:

// continued from above ViewEventHandler mainEVH = new FooEventHandler(sl, 0, 1); /* main camera is on layer 0 in the view (1st element of the cameras vector above) */ /* camera of the scroll layer is on layer 1 in the view (2nd element of the cameras vector above) */ ViewEventHandler scrollEVH = new DefaultScrollEventHandler(sl, 1, 0); /* Associating the main event handler with layer 0 (to which the main camera belongs) */ v.setEventHandler(mainEVH, 0); /* Associating the scroll bar event handler with layer 1 (to which the scroll layer camera belongs) */ v.setEventHandler(scrollEVH, 1);

Here we used the default, very basic implementation provided by DefaultScrollEventHandler. Buttons are actuated by clicking them with the left mouse button. Sliders are dragged with the left mouse button. Client applications are free to define scroll bar event handling through their own implementation of ViewEventHandler.

The last thing that needs to be taken care of is activating the scroll layer when the user moves her cursor in the scroll bar area. Client applications are free to do that any manner they want. The following is only one possible solution, that more or less "mirrors" what is done in DefaultScrollEventHandler to activate the main layer when the user moves her cursor outside the scroll bar area:

// continued from above /* Specify that mouseMoved events should be sent to view event handlers (they are not by default) */ /* Without this call mouseMoved() is never called in ViewEventHandlers associated with the view*/ v.setNotifyMouseMoved(true);

/*------------ excerpt of ViewEventHandler implementation bound to mainEVH above ----------*/ class FooEventHandler implements ViewEventHandler { ScrollLayer sl; int mli; int sli; FooEventHandler(ScrollLayer sl, int mainLayerIndex, int scrollLayerIndex){ this.sl = sl; mli = mainLayerIndex; // layer associated with this event handler sli = scrollLayerIndex; // scroll layer } ... public void mouseMoved(ViewPanel v, int jpx, int jpy, MouseEvent e){ if (sl.cursorInside(jpx, jpy)){ // if the cursor is in the scroll bar area if (v.parent.getActiveLayer() == mli){ // and if the scroll layer is not the active one, make it active v.parent.setActiveLayer(sli); } } } }

Updating scroll bar sliders when controlled camera is moved by other means than scroll bar widgets

If your application allows for the camera to be moved directly or indirectly by other means than the scroll bar slides and buttons, e.g., using rate-based scrolling, a key stroke that animates the camera to a position which gives an overview of the virtual space, etc. (as is the case in the Scrollbar demo), the client application must notify the ScrollLayer object about changes to the controlled camera's position whenever such changes occur outside the control of the scroll layer.

/* example modification */ mainCamera.moveTo(100, -100); mainCamera.setAltitude(500.0f); /* Notify the scroll layer about this modification */ sl.cameraUpdated();

If the camera's position is changed through the animation manager, a convenient way to keep scrollbars synchronized is to call cameraUpdated() through an AnimationListener callback, as shown below:

... implements AnimationListener { ...{ .... vsm.animator.setAnimationListener(this); ... } public void cameraMoved(){ sl.cameraUpdated(); } }

Updating scroll bar sliders when the content of the virtual space changes

In order to keep the position and size of scroll bar sliders coherent with the virtual space's content, it is also necessary to notify the ScrollLayer object of changes to the glyphs in the virtual space observed through the controlled camera, as such changes can cause the practical bounds of the virtual space (as defined in the introduction) to be modified: new glyph added, glyph removed, glyph position, size or orientation changed...

/* Any modification to the position, size or orientation of glyphs that is likely to change the practical bounds of the virtual space */ ... sl.virtualSpaceUpdated();