javaswingmousedragjslider

Java: force JSlider to be detached/released from mouse when dragged


I am using a JSlider to seek through an audio file in a custom Java music player application. When the slider is released, the player jumps to the corresponding media time. What I want to achieve is the following: when ESCAPE is pressed while the slider is being dragged, I want it to be released/detached from the mouse cursor and jump back to its initial position. In other words: I want to interrupt/stop the user's dragging manually.

I have tried the following to achieve this:

  1. calling slider.setValue(initialValue)
  2. firing a custom mouseReleased() event (even if the mouse was still being pressed)
  3. firing a custom windowDeactivated() event on the main frame to withdraw the slider its focus

Nothing worked. The slider's knob keeps sticking to the mouse until I physically release it. Is there any other way I can achieve this?

import java.awt.Dimension;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.border.BevelBorder;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;

public class DetachSlider {

    // true when slider is being dragged
    private boolean dragged;
    
    public static void main (String[] args) {
        new DetachSlider().initFrame();
    }
    
    public void initFrame() {

        JFrame frame = new JFrame();
        frame.setTitle("Detach Slider Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                
        JSlider slider = new JSlider();
        slider.setPreferredSize(new Dimension(500,50));
        slider.addMouseListener(new MouseHandler());
        slider.addKeyListener(new KeyHandler());
        
        JPanel panel = new JPanel();
        panel.setBorder(new CompoundBorder(new EmptyBorder(5,5,5,5), new BevelBorder(BevelBorder.LOWERED)));
        panel.add(slider);

        frame.setContentPane(panel);
        frame.setLocation(500,400);
        frame.pack();
        frame.setVisible(true);
    }

    // MouseListener for Slider
    private class MouseHandler extends MouseAdapter {
        public void mousePressed(MouseEvent me) {
            dragged = true;
        }
        public void mouseReleased(MouseEvent me) {
            dragged = false;
        }
    }
    
    // KeyListener for Slider
    private class KeyHandler extends KeyAdapter {
        public void keyPressed (KeyEvent ke) {
            if (ke.getKeyCode() == KeyEvent.VK_ESCAPE) {
                JSlider slider = (JSlider)ke.getSource();
                if (dragged) {
                    System.out.println("ESCAPE pressed when slider is dragged");
                    slider.setValue(0);
                }
                else {
                    System.out.println("ESCAPE pressed when slider is idle");
                    slider.setValue(0);
                }
            }
        }
    }

}

Solution

  • Ok I think we have a solution. It is to send the mouseRelease event to all the mouseListeners attached to the slider. As follows:

    MouseListener[] cs=slider.getMouseListeners();
    MouseEvent me = new MouseEvent((JSlider)ke.getSource(), MouseEvent.MOUSE_RELEASED, 0, 0, 100, 100, 1, false);
    for(MouseListener m:cs) m.mouseReleased(me);
    

    This happens inside the keyPressed method.