Java Swing - Show glasspane and consume events without losing previous focus owner

126 Views Asked by At

We are trying to show the user a milky JGlassPane to indicate that events are currently not accepted, due to a progress-bar or something else currently being shown. While this would usually automatically work, since we are showing a modal dialog, we also want the JGlassPane to be usable without a JDialog, meaning just a JGlassPane and a waiting cursor.

So far, we did this by using a JGlassPane that ignores events and paints a translucent color. Initially we call requestFocus on the JGlassPane. The problem with this is, that we lose the previously focused components when the glasspane goes away. The solution for this was to simply remember the previously focused component. However, in cases where the Action, that triggered the JGlassPane, was invoked via a menu, the focused component was the windows JRootPane. Usually swing correctly restores the focus after closing a menu, however, in this case, we are breaking this functionallity. Is there a way to achieve what we want without breaking the focus behaviour?

In this demo you can see, that the focus is correctly returned when hitting ESC after using one of the two buttons, but not with the context menu on the table.

public class FocusDemo
{
  //Temporary static veriable for demo purposes.
  private static final AtomicReference<Component> previouslyFocused = new AtomicReference<>();

  private static class ConsumingGlassPane extends JPanel
  {
    public ConsumingGlassPane()
    {
      setOpaque( false );
      setFocusable( true );

      addMouseListener( new MouseListener()
      {
        @Override
        public void mouseClicked( @NonNull final MouseEvent e )
        {
          e.consume();
        }

        @Override
        public void mousePressed( @NonNull final MouseEvent e )
        {
          e.consume();
        }

        @Override
        public void mouseReleased( @NonNull final MouseEvent e )
        {
          e.consume();
        }

        @Override
        public void mouseEntered( @NonNull final MouseEvent e )
        {
          e.consume();
        }

        @Override
        public void mouseExited( @NonNull final MouseEvent e )
        {
          e.consume();
        }
      } );

      addKeyListener( new KeyListener()
      {

        @Override
        public void keyTyped( final KeyEvent e )
        {
          e.consume();
        }

        @Override
        public void keyReleased( final KeyEvent e )
        {
          e.consume();
        }

        @Override
        public void keyPressed( final KeyEvent e )
        {
          if ( e.getKeyCode() == KeyEvent.VK_ESCAPE )
          {
            setVisible( false );
            final Component component = previouslyFocused.get();
            if ( component != null )
            {
              component.requestFocusInWindow();
            }
          }
          e.consume();
        }
      } );

      // This component keeps the focus until is made hidden
      setInputVerifier( new InputVerifier()
      {
        @Override
        public boolean verify( final JComponent input )
        {
          return !isVisible();
        }
      } );
    }

    @Override
    protected void paintComponent( final Graphics g )
    {
      final Graphics2D g2 = (Graphics2D) g.create();
      g2.setColor( new Color( 240, 230, 230, 128 ) );
      g2.fillRect( 0, 0, getWidth(), getHeight() );
      g2.dispose();
    }
  }

  public static void main( final String[] args )
  {
    final JFrame frame = new JFrame();
    frame.setGlassPane( new ConsumingGlassPane() );
    final ActionListener showglasspane = __ ->
    {
      previouslyFocused.set( frame.getFocusOwner() );
      frame.getRootPane().getGlassPane().setVisible( true );
      frame.getRootPane().getGlassPane().requestFocus();
    };

    final JButton button1 = new JButton( "Show GlassPane" );
    final JButton button2 = new JButton( "Show GlassPane" );
    button1.addActionListener( showglasspane );
    button2.addActionListener( showglasspane );

    final Object[][] objects = { { 1, 2, 3 }, { 1, 2, 3 } };
    final String[] col = { "Lel", "Lol", "Lul" };
    final JTable table = new JTable( objects, col );
    final JMenuItem menuItem = new JMenuItem( "Show GlassPane" );
    menuItem.addActionListener( showglasspane );
    final JPopupMenu popup = new JPopupMenu();
    popup.add( menuItem );
    table.setComponentPopupMenu( popup );

    frame.setLayout( new GridBagLayout() );
    frame.add( button1 );
    frame.add( button2 );
    frame.add( table );
    frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
    frame.pack();
    frame.setLocationRelativeTo( null );
    frame.setVisible( true );
  }
}
0

There are 0 best solutions below