Forum Stats

  • 3,875,872 Users
  • 2,266,977 Discussions
  • 7,912,361 Comments

Discussions

Cursor caging

Hello,
I need to implement a functionality in my program. When the user click the arrow of a a conbobox, the mouse must disappear and caged to the popup of the combo.
I have done that by setting an empty cursor for the combobox and by pushing a new EventQueue which replace the mouse each time it goes out of the popup.

The problem is that when the mouse goes out, we can see the pointer that appears before being replace. It lasts a couple of milliseconds but it's ugly.

Have you got an idea to make the cursor totally invisible?

This is my code, if you need it :
import java.awt.AWTEvent;
import java.awt.AWTException;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EmptyStackException;

import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.metal.MetalComboBoxUI;

public class CagingMode
{

  private static final class Myui extends MetalComboBoxUI
  {
    //Make the getpopoup function available
    private BasicComboPopup getpopup()
    {
      return (BasicComboPopup)popup;
    }
  }
  
  private final CagedListener CAGING_LISTENER;
  private final Cursor INVISIBLE_CURSOR;
  private Cursor oldCursor;
  private Component c;
  private Robot robot;

  
  ///////// SINGLETON ////
  private static CagingMode instance = new CagingMode();

  private CagingMode()
  {
    Image cursorImage;
    cursorImage = Toolkit.getDefaultToolkit().getImage("image/TransparentCursor.png");
    INVISIBLE_CURSOR = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(0, 0), "invisible");
    CAGING_LISTENER  = new CagedListener();
  }

  public static void main(String[] args)
  {
    final JFrame frame = new JFrame();
    frame.setLayout(new FlowLayout());
    frame.setSize(500, 500);
    JComboBox combo = new JComboBox();
    for(int i=0; i<10; i++){
      combo.addItem(i);
    }
    frame.getContentPane().add(combo);
    frame.setVisible(true);
    frame.setDefaultCloseOperation(3);
    
    final Myui ui = new Myui();
    combo.setUI(ui);
    ui.getpopup().addPropertyChangeListener("visible",new PropertyChangeListener() {
      
      @Override
      public void propertyChange(PropertyChangeEvent evt)
      {
        if(evt.getNewValue().equals(Boolean.TRUE))
        {
          CagingMode.getInstance().startCagingMode(ui.getpopup());
        }
        else
        {
          CagingMode.getInstance().stopCagingMode();
        }
      }
    });
  }
  public static CagingMode getInstance()
  {
    return instance;
  }
  
  public void startCagingMode(Component c){
    if(c==null)
      return;
    this.c =c;
    try
    {
      robot = new Robot(c.getGraphicsConfiguration().getDevice());
      oldCursor = c.getCursor();
      CAGING_LISTENER.changeComponent();
      c.setCursor(INVISIBLE_CURSOR);
      Toolkit defaultToolkit = Toolkit.getDefaultToolkit();
      defaultToolkit.getSystemEventQueue().push(CAGING_LISTENER);
    }
    catch (AWTException e)
    {
      e.printStackTrace();
    }
  }
  public void stopCagingMode(){
    this.c =null;
    if(c!=null && oldCursor!=null)
      c.setCursor(oldCursor);
    CAGING_LISTENER.pop();
  }
  
  
  private final class CagedListener extends EventQueue
  {
    private int cursorX;
    private int cursorY;
    private int compX;
    private int compY;
    private int compWidth;
    private int compHeight;

    public CagedListener()
    {
    }
    
    public void changeComponent()
    {
      compX = c.getLocationOnScreen().x;
      compY = c.getLocationOnScreen().y;
      compWidth = c.getWidth();
      compHeight = c.getHeight();
      
    }
    
    @Override
    protected void dispatchEvent(AWTEvent event)
    {
      if(c!=null && event instanceof MouseEvent)
      {
        cursorX = ((MouseEvent)event).getXOnScreen();
        cursorY = ((MouseEvent)event).getYOnScreen();
        boolean changed = false;
        
        if(cursorX < compX || (cursorX > (compX + compWidth-1)&&compWidth>1))
        {
          cursorX = compX+ compWidth/2 - 1;
          changed = true;
        }
          
        if(cursorY < compY)
        {
          cursorY = compY;
          changed = true;
        }
        else if(cursorY > (compY+compHeight-1)&&compHeight>1 )
        {
          changed = true;
          cursorY = compY+compHeight-1;
        }
          
        if(changed)
        {
//          System.out.println("modif "+((MouseEvent)event).getModifiers());
          robot.mouseMove(cursorX, cursorY);
        }
      }
      super.dispatchEvent(event);
    }
    @Override
    protected void pop() throws EmptyStackException
    {
      super.pop();
    }
  }
}
This discussion has been closed.