I have a vertical JScrollBar
which default behaviour is to show the thumb at the top when the scrollbar value is at the model minimum.
I'd like to have the opposite behaviour: the thumb would be at the bottom when the value is at the minimum.
I have tried setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT)
and it works for horizontal scrollbars but not for vertical ones...
How can I get it to work as expected (without creating a full ScrollBarUI
if possible)?
Is that expected behaviour (a kind of "UI inconsistency"* between horizontal and vertical scrollbars, or a missing ComponentOrientation.BOTTOM_TO_TOP
constant)?
Here is some SSCE that illustrate the problem:
public static void main(String[] args) throws Exception {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int orient = JScrollBar.VERTICAL; // <<--- Change to HORIZONTAL
JScrollBar sb = new JScrollBar(orient, 0, 100, 0, 1000);
sb.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); // <<--- Works only for HORIZONTAL
sb.addAdjustmentListener(new AdjustmentListener() {
@Override public void adjustmentValueChanged(AdjustmentEvent e) {
System.out.println(e.getValue());
}
});
f.add(sb);
if (orient == JScrollBar.HORIZONTAL)
f.setBounds(100, 100, 500, 100);
else
f.setBounds(100, 100, 100, 500);
f.setVisible(true);
}
*: "inconsistency" because if it reverses the thumb for horizontal, I would expect the thumb to be reversed as well for vertical...
EDIT: The scrollbar is used to control scrolling in a map that can be configured to have its origin at the top-or-bottom of the screen, going down-or-up, or at the left-or-right, going right-or-left (it has to match a physical setup, so is configurable). Handling the left-or-right setup is just setting the scrollbar to horizontal, with right-to-left orientation, but the same is not working for vertical.
I also checked the source of BasicScrollBarUI
and it indeed handles the getComponentOrientation().isLeftToRight()
only when the scrollbar is HORIZONTAL
. So it is indeed expected behaviour (just not the one I expected...).
I ended up subclassing JScrollBar
with a getAdjustedValue()
that does the translation. The idea is to "reverse" min and max, so [min;max]
becomes [-max;-min]
. Then we have to take the extent into account when adjusting the model value, as the bottom of the thumb (value + extent
) is the value we want:
public static class MySB extends JScrollBar {
private boolean inv;
public MySB(int orient, int value, int extent, int min, int max, boolean inv) {
super(orient, inv ? -value-extent : value, extent, inv ? -max : min, inv ? -min : max);
this.inv = inv;
}
public int getAdjustedValue() {
int v = super.getValue();
if (inv)
v = -(v + model.getExtent());
return v;
}
}
Then modified the SSCE above, adding sb.getAdjustedValue()
in the AdjustmentListener:
...
MySB sb = new MySB(orient, 0, 100, 0, 1000, true);
sb.addAdjustmentListener(new AdjustmentListener() {
@Override
public void adjustmentValueChanged(AdjustmentEvent e) {
System.out.println(e.getValue()+", "+((MySB)e.getAdjustable()).getMyValue());
}
});
...
-100, 0
-200, 100
-200, 100
-200, 100
-300, 200
-300, 200
We see the original value, and the corrected one. I initially started subclassing the model and overriding getValue()
but it messed up the page up/down and arrows behaviour.