Skip to main content

JXLayer with a fixed GUI in a JScrollPane

3 replies [Last post]
Joined: 2011-01-31

I'm currently reworking our old GUI to become scalable and test-driving JXLayer for that. At first many thanks that this solution exists! I found it quite hard though to find and pack the needed things together, i.e. TransformUI and the DefaultTransformModel are not part of the base distribution and it is really really unclear what version "3" and version "4" should be. After a long struggling and some manual imports (SwingUtilities3 and EventQueueDelegate were missing in my OpenJDK) I could get it to work, somehow.
Now while the scaling and rendering works in general, I get a lot rendering artifacts and the mouse input is not routed properly to the individual components either.
What I'm basically doing is this:

DefaultTransformModel zoomModel = new DefaultTransformModel();
TransformUI ui = new TransformUI(zoomModel);
TransformUI ui = new TransformUI(zoomModel);
JXLayer<jcomponent> layer = new JXLayer<jcomponent>();<br />layer.setUI(ui);<br />layer.setView(<some complex="" jpanel="" ui="">[my-complex-jpanel-based-view]);<br />myScrollPane.setViewportView(layer); </some></jcomponent></jcomponent>

If such a view is scaled, artifacts like this are popping up:

Now as you can see we're colorizing and adapting the scale of the default components a bit - should this make any problems? If not, what could cause these drawing and mouse event problems?
Many thanks in advance,

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Joined: 2011-01-31

The drawing issues seem to only pop up on Linux, the scaling and updating works perfectly on Windows machines.
I had no problems running Piet's webstart demos on Linux, so I still suspect something within my code, but its very weird that it works very well on Windows.
Another problem I recently discovered (and couldn't solve on my own) results from custom scaling. The DefaultTransformModel scales by the bigger of two sides and centers the component, which leaves whitespace left/right or top/bottom in case the layer's size ratio doesn't equal the component's size ratio, so what I did is making my own TransformModel implementation like this:

<br />
    @Override<br />
    public AffineTransform getTransform(JXLayer<? extends JComponent> layer)<br />
    {<br />
        if (layer == null)<br />
        {<br />
            throw new IllegalArgumentException("layer is NULL");<br />
<p>        JComponent view = layer.getView();<br />
        if (view == null)<br />
        {<br />
            throw new IllegalStateException("layer view is NULL");<br />
<p>        double scale = 1.0;</p>
<p>        // if any change to previous values, recompute the transform.<br />
        if (!layer.getSize().equals(layerSize) || !view.getSize().equals(viewSize))<br />
        {<br />
            layerSize = layer.getSize();<br />
            viewSize = view.getSize();<br />
<p>            transform.setToIdentity();</p>
<p>            double centerX = layer.getWidth() / 2.0;<br />
            double centerY = layer.getHeight() / 2.0;<br />
            double scaleX = layer.getWidth() / (double) view.getWidth();<br />
            double scaleY = layer.getHeight() / (double) view.getHeight();</p>
<p>            // preserve the aspect ratio<br />
            scale = Math.max(scaleX, scaleY);</p>
<p>            // correct the positioning<br />
            double xCorrect = (scale * view.getWidth() - layer.getWidth()) / 2;<br />
            double yCorrect = (scale * view.getHeight() - layer.getHeight()) / 2;<br />
            transform.translate(xCorrect, yCorrect);</p>
<p>            transform.translate(centerX, centerY);<br />
            transform.scale(scale, scale);<br />
            transform.translate(-centerX, -centerY);<br />
<p>        return transform;<br />
    }<br />

This works fairly well, just that the JScrollPane, in which the JXLayer with the transformed component is put in, can't determine the correct (scaled) view size, i.e. it only displays scroll bars if the scroll pane is made smaller than the _unscaled_ size of the component (i.e. its preferred size) is reached. I saw that JXLayer implements the Scrollable interface, which then forwards its methods, for example getPreferredScrollableViewportSize, to the LayerUI, which then, again, forwards it to either the layer's preferred size or, in case the decorated component implements scrollable, to its own getPreferredScrollableViewportSize implementation.
The bottom line of all this redirecting is that neither setting the preferred size of the layer on demand after scaling nor implementing a custom Scrollable component class which returns a special (scaled) Dimension from its getPreferredScrollableViewportSize method help. In the former case the scrollbars look correct in first instance, but one cannot reset the once resized JXLayer to a smaller size or even its original one. Doing the getPreferredScrollableViewportSize hack on the other hand has nearly identical issues.
I have to admit that I'm still a bit clueless how (and why) exactly JXLayer needs a fixed preferred size on the decorated component and I think this is the major issue I'm struggling with here. Any hints would be greatly appreciated.

Joined: 2011-01-31

Ok, the forum software ate my inline image - here is it again: