JSlider question: Position after leftclick
Asked Answered
E

3

13

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?

Echinoid answered 5/2, 2009 at 23:30 Comment(0)
C
17

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);
    }
});
Chambertin answered 6/2, 2009 at 0:46 Comment(7)
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?Echinoid
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.Chambertin
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?Echinoid
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.Chambertin
OK, check out the example above now, it does exactly as you want.Chambertin
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.Chambertin
I found that I needed to subclass BasicSliderUI instead of MetalSliderUI, because I wasn't using the metal L+F. Otherwise I got NullPointerExceptions.Ministration
K
3

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);
    }
};
Kriss answered 1/6, 2009 at 20:50 Comment(2)
doesn't this solution mean that you can no longer use a scroll wheel to move the slider?Chambertin
+1, I made a slight change to also remove and add the MouseMotionListener. This gives a minor cosmetic change in that the mouse will remain centered in the slider thumb as you drag the thumb instead of jumping to the left edge of the thumb.Peterson
C
-2

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

Cognomen answered 9/2, 2009 at 7:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.