views:

808

answers:

3

Whenever I click a JSlider it gets positioned one majorTick in the direction of the click instead of jumping to the spot I actually click. (If slider is at point 47 and I click 5 it'll jump to 37 instead of 5). Is there any way to change this while using JSliders, or do I have to use another datastructure?

+3  A: 

As bizarre as this might seem, it's actually the Look and Feel which controls this behaviour. Take a look at BasicSliderUI, the method that you need to override is scrollDueToClickInTrack(int).

In order to set the value of the JSlider to the nearest value to where the user clicked on the track, you'd need to do some fancy pants translation between the mouse coordinates from getMousePosition() to a valid track value, taking into account the position of the Component, it's orientation, size and distance between ticks etc. Luckily, BasicSliderUI gives us two handy functions to do this: valueForXPosition(int xPos) and valueForYPosition(int yPos):

JSlider slider = new JSlider(JSlider.HORIZONTAL);
slider.setUI(new MetalSliderUI() {
    protected void scrollDueToClickInTrack(int direction) {
        // this is the default behaviour, let's comment that out
        //scrollByBlock(direction);

        int value = slider.getValue(); 

        if (slider.getOrientation() == JSlider.HORIZONTAL) {
            value = this.valueForXPosition(slider.getMousePosition().x);
        } else if (slider.getOrientation() == JSlider.VERTICAL) {
            value = this.valueForYPosition(slider.getMousePosition().y);
        }
        slider.setValue(value);
    }
});
ninesided
That is sorta bizarre, nice catch. Trying to figure out how the input to scrollDue..(int) is created. For the direction to be determined there has to be some check where I click compared sliderposition. For that to be determined wouldn't some method need to know exactly where on the slider I click?
Ppaa
not really, direction is easy - for a horizontal track it just needs to know that the x coordinate of the mouse click is less than or greater than the x coordinate of the slider handle.
ninesided
Yeah, but to know if it's less than or greater than the X coordinate wouldn't you need to know where on slider you click to do the comparing? If slider handle is at 50 you need to know where you click to see if that position is > or < 50, no?
Ppaa
just to clarify the terminology, the "track" is the horizontal/vertical bar which the ticks are marked on, the "thumb" or "slider" is what you drag around. I've updated the answer above with some extra detail that I dug out. Hopefully that should get you further with this.
ninesided
OK, check out the example above now, it does exactly as you want.
ninesided
Ppaa, in answer to your earlier question, no it wouldn't need to know where on the "track" you had clicked to compare the X coordinate, it just takes the X coordinate of the mouse position wherever on the screen that might be and checks whether it is < or > the X coordinate of the "thumb" Component.
ninesided
A: 

This behavior is derived from OS. Are you sure you want to redefine it and confuse users? I don't think so. ;)

Rastislav Komara
A: 

This question is kind of old, but I just ran across this problem myself. This is my solution:

    JSlider slider = new JSlider(/* your options here if desired */) {
        {
            MouseListener[] listeners = getMouseListeners();
            for (MouseListener l : listeners)
                removeMouseListener(l); // remove UI-installed TrackListener
            final BasicSliderUI ui = (BasicSliderUI) getUI();
            BasicSliderUI.TrackListener tl = ui.new TrackListener() {
                // this is where we jump to absolute value of click
                @Override public void mouseClicked(MouseEvent e) {
                    Point p = e.getPoint();
                    int value = ui.valueForXPosition(p.x);

                    setValue(value);
                }
                // disable check that will invoke scrollDueToClickInTrack
                @Override public boolean shouldScroll(int dir) {
                    return false;
                }
            };
            addMouseListener(tl);
        }
    };
pfn