So what is the problem when working with viewports? To put it bluntly I think their biggest advantage most certainly is also their biggest disadvantage. That is, the possibility to reduce a container figure's external bounds (making it thus easier to embed it within a larger graphical context) while preserving the ability to access its internal contents in-place (even if not completely at a time) on the other hand yields the problem that navigating and editing this internal contents is often quite painful, as a lot of scrolling is usually necessary to access hidden parts, and orientation is easily lost.
To emphasize this, consider the following two versions of the same (slightly modified) 4-bit adder example, provided with GEF's sample logic editor. Despite having resized the circuit figure, both diagrams are indeed identical. And having not seen the first version before, the accessibility of the second one would most certainly be quite limited.
BTW: You might notice that by using the original GEF logic example editor you may not produce these screenshots, because connections are not clipped correctly, and because the scroll bars within the circuit figure are not transparent but opaque. The first problem can be addressed by a patch I contributed to bug #195527 (fix for clipping problems of connections in combination with viewports). The second one is something I will document herein, so keep on reading...
While those troubles mentioned before with respect to navigating and editing the contents of a viewport seem to be somehow the price to pay, the fact that its contents may never be visualized completely at a time, is something that can be circumvented by adequate feedback support. What I am referring to is a feature we implemented as part of a reasearch project during my time at the university.
The idea was born when Philip Ritzkopf (who was a student assistant in our research group at that time and has very much contributed to the conception and implementation of the feature) pointed me to a post in the GEF newsgroup about a "ghost image" figure (i.e. a semi-transparent copy of some original figure) that was discussed as an option to show feedback during a drag & drop operation. As we thought that the sketched mechanism could also be suited to show the hidden contents of a viewport, Philip and I directly started to implement a first prototype. Having made some further iterations, the following screenshot shows the final result.
Note that in case the circuit figure is not selected, everything will look like before. However, in case it is selected you get the 'Ghost Image Feedback'. What a difference!
So how is it realized? Indeed the approach is rather lightweight. It basically consists of a respective SelectionEditPolicy (namely ScrollableSelectionFeedbackEditPolicy), some interfaces (IScrollableFigure, IScrollableEditPart) to assure it can only registered to host edit parts, having a figure with a nested Viewport (or a ScrollPane containing a nested Viewport to be more precise), and an implementation of a GhostImageFigure, being used to render the feedback.
The GhostImageFigure being used is pretty much inspired by the one depicted in above mentioned use newsgroup entry. In difference to the version being posted, our's does not extend ImageFigure but constructs the original figure's image lazily within its paintFigure() method. The reason to do so is that this way, disposal of the used image can be performed directly after having painting it, so that no client has to take care (while this could be an option in case any performance problems might arise). It was furthermore enhanced by a possibility to specify a transparency color, which is needed in case the original figure is a connection (whose background should indeed be regarded as transparent). Here is the source code:
|01 /** |
The second central piece of the solution of course is the ScrollableSelectionFeedbackEditPolicy. Upon primary selection of its host IScrollableEditPart, it creates GhostImageFigures for all nodes nested within the host figure's Viewport and all connection figures related to them. To keep up with changes to the host figure and its nested viewport respectively, a couple of listeners are required, which are registered and unregistered in the activate() and deactivate() methods respectively. Note that the layout constraints for the feedback figures are determined by translating the original figure's bounds into absolute coordinates first, then into coordinates relative to the feedback layer (which is chosen to be the SCALABLE_FEEDBACK_LAYER), so feedback is correctly displayed during zooming as well.
Those interfaces created to allow type-safe access to the host edit part's figure, namely IScrollableFigure and IScrollableEditPart are rather unexciting, thus I will skip them here, as well as the utility classes (EditPartSupport and ViewportUtilities) we introduced to support some of the calculations within the edit policy.
What may be of interest however is how the circuit figure of the logic example can be beautified with respect to its scroll bars (as mentioned above, the screenshots were produced after having performed some beautifications on the GEF logic example):
It is indeed done by exchanging the ScrollPane being used within the CircuitFigure of the logic example with a custom implementation we have chosen to name PuristicScrollPane. It uses special PuristicScrollBars, which do not show a 'thumb' and use non-opaque navigation buttons. Here is the code:
Note that in order to achieve that children are also painted below the now transparent scroll bars, the paintChildren() and invalidate() methods within the PuristicScrollPane have to be refined accordingly. To achieve that the navigation buttons are only shown in case they are needed, the PuristicScrollBar implementation reacts to changes to its underlying range model by means of the PropertyChangeListener mechanism.
If with some of the aforementioned I could raise your interest, I propose you simply try the feature out yourself. All source code related to it (including those utility classes not explicitly shown here) is included in a patch I added to bug #303557 (which I have created to contribute the feature to GEF) as well as another patch (which contains a ViewportUtilities helper class that is used by the contribution) I had created to address aforementioned clipping problems as part of bug #195527. You may thus simply download both patches and apply them to the current HEAD version of GEF (and the GEF logic example).
The patch added to bug #303557 may also serve as a good starting point in case you not only want to trial the logic example but apply the feature to your own code, because you can infer all that is required from those few changes, we have made to CircuitFigure and CircuitEditPart respectively.