Results 1 to 10 of 10
  1. #1
    snoopygee is offline Member
    Join Date
    Dec 2010
    Posts
    7
    Rep Power
    0

    Default Adding a JPanel to a JMenu - Focus issues

    Hi all,

    I am working on a requirement and have come across something unexpected.

    The expected behaviour when working with menu items is when adding a JMenuItems to a JMenu and making the Menu visible, moving the mouse over each of the items highlights that items and allows each to be selected.

    However when I add a JPanel to a JMenu, and the JPanel contains the JMenuItems, these items are no longer highlighted or selectable. Further, when mousing over the contained JMenuItems, focus seems to be lost from the JMenu entirely and the popup disappears altogether.

    I've constructed the following code to demonstrate this issue:

    Java Code:
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Component;
    import java.awt.Dimension;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    
    import javax.swing.BoxLayout;
    import javax.swing.JFrame;
    import javax.swing.JMenu;
    import javax.swing.JMenuItem;
    import javax.swing.JPanel;
    import javax.swing.JPopupMenu;
    
    public class TestMenuItems extends JFrame {
    
      private JPopupMenu _thePopup = new JPopupMenu();
      
      public TestMenuItems() {
        
        this.setLayout( new BorderLayout() );    
        JPanel thePanel = new JPanel();
        this.add( thePanel, BorderLayout.CENTER );
        
        this.addMouseListener( new MouseAdapter(){
          public void mousePressed(MouseEvent e) {
            _thePopup.show((Component)e.getSource(), e.getX(), e.getY());
          }
        });
        
        JMenu theMenu = new JMenu("Menu");
        _thePopup.add( theMenu );
        
        thePanel.setBackground( Color.BLUE );
        thePanel.setLayout( new BorderLayout() );
        
        JPanel theInnerPanel = new JPanel();
        theInnerPanel.setLayout( new BoxLayout( theInnerPanel, BoxLayout.X_AXIS ) );
        theInnerPanel.add( new JMenuItem("Press 1") );
        theInnerPanel.add( new JMenuItem("Press 2") );  
        
        theMenu.add(theInnerPanel); 
        theMenu.add(new JMenuItem("Press 3"));
        theMenu.add(new JMenuItem("Press 4"));
      }
      
      public static void main( String[] args ){
        
        TestMenuItems theApp = new TestMenuItems();
        theApp.setSize( new Dimension(200, 200));
        theApp.setLocation(200, 200);
        theApp.setVisible( true );
      }
      
    }

    Does anyone know the reason why this happens and how to make the JMenuItems that are wrapped by the JPanel receive the focus of the mouse. Any idea's?

    Just as a reminder, this is a small demonstration of a larger issue I have. I am not interested in how I can perform the same GUI representation using different techniques. Please only respond if you understand what is happening with my example and how to pass the events to the correct components.

    Thanks

  2. #2
    hosscomp is offline Member
    Join Date
    Oct 2010
    Posts
    63
    Rep Power
    0

    Default

    Interesting. I have never seen a JPanel placed in menu. Maybe the inner panel is eating the mouse-over evnets instead of passing them on to the menu. Just a guess.

  3. #3
    snoopygee is offline Member
    Join Date
    Dec 2010
    Posts
    7
    Rep Power
    0

    Default

    Maybe the inner panel is eating the mouse-over evnets instead of passing them on to the menu
    This was my initial thought but when I placed listeners on both the inner panel and the JMenuItems non of them get triggered meaning that they never get the focus and never get the events. It's as if the Popup thinks that the focus has moved outside of it's boundry and therefore it closes itself.

    Does anyone else have any other idea's?

    Thanks

  4. #4
    hosscomp is offline Member
    Join Date
    Oct 2010
    Posts
    63
    Rep Power
    0

    Default

    Is there some requirement to have item 1 to the right of item 2?

    If you just want to separate them you can place a separator between 2 and 3.
    Or you can cascade 1 and 2 on a sub-menu. These are customary styles for menus.

    If you need need a grid of menu buttons you should probably change the popup to a
    dialog intead of a JPopupMenu. Let us know your goal and we might be able to help
    more.

  5. #5
    snoopygee is offline Member
    Join Date
    Dec 2010
    Posts
    7
    Rep Power
    0

    Default

    Hi hosscomp,

    Thanks again for your reply however as I previously stated, my app is:

    ... a small demonstration of a larger issue I have. I am not interested in how I can perform the same GUI representation using different techniques
    Your last statement was:

    Let us know your goal and we might be able to help
    more.
    My goal is again as I have stated previously. I would like to understand why wrapping JMenuItems within a panel alters the behaviour of being able to select the Menu Items and further causes this popup menu to close? How can I update the event handling such that using a JPanel in this position still allows the popup to functions as it would usually?

    Thanks again

  6. #6
    hosscomp is offline Member
    Join Date
    Oct 2010
    Posts
    63
    Rep Power
    0

    Default

    I am working on a requirement and have come across something unexpected.
    Sorry. I never expect things to work in an unintended way, and only 95% of the time in the specified manner.

    Best of luck,
    hosscomp

  7. #7
    snoopygee is offline Member
    Join Date
    Dec 2010
    Posts
    7
    Rep Power
    0

    Default

    When you try and construct more than basic application you often come up again unforeseen issues. Swing components are intended to be used both as advertised and extended to include new functionality.

    Even though you did not know the answer hosscomp, thanks for trying.

    Does anyone else have any suggestions.

    Thanks

  8. #8
    hosscomp is offline Member
    Join Date
    Oct 2010
    Posts
    63
    Rep Power
    0

    Default

    The pleasure was all mine.

  9. #9
    verma018 is offline Member
    Join Date
    Jan 2011
    Posts
    1
    Rep Power
    0

    Default

    I have solved that issue, I don't know why this is happening but doing below i have resolved.

    Changed the JMenuItem to JLabel, then add these to JPanel.

    Need to handle mousePressed and mouseMoved method for these JLabel.

    Happy Coding ... :)

  10. #10
    snoopygee is offline Member
    Join Date
    Dec 2010
    Posts
    7
    Rep Power
    0

    Default

    Hi verma018,

    Thanks for your reply, this indeed solves the issue but I have investigated further and worked out and solved the issue using the original JMenuItems also - the solution was to use a customer UI on the JMenuItem objects themselves to return the correct path to the selected menu item (the original underline problem being that this path was not being returned correctly).

    Please see solution below which demonstrates both problem and solutions within a single app:

    Java Code:
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Component;
    import java.awt.Dimension;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.KeyEvent;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    
    import javax.swing.BoxLayout;
    import javax.swing.ButtonGroup;
    import javax.swing.JCheckBox;
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JMenu;
    import javax.swing.JMenuBar;
    import javax.swing.JMenuItem;
    import javax.swing.JPanel;
    import javax.swing.JPopupMenu;
    import javax.swing.JRadioButton;
    import javax.swing.MenuElement;
    import javax.swing.MenuSelectionManager;
    import javax.swing.border.EtchedBorder;
    import javax.swing.plaf.basic.BasicMenuItemUI;
    
    public class TestMenuItems extends JFrame {
     
      private JPopupMenu _thePopup = new JPopupMenu(){
        public void setVisible( boolean isVisible ){
          super.setVisible( isVisible );
        }
      };
      private JRadioButton _menuItemButton = new JRadioButton("Use Wrapped JMenuItems"); //$NON-NLS-1$
      private JRadioButton _labelButton = new JRadioButton("Use Wrapped JLabels"); //$NON-NLS-1$
      private JRadioButton _standardButton = new JRadioButton("Use Standard JMenuItems"); //$NON-NLS-1$
      private JCheckBox _useMenuItemIU = new JCheckBox("Use Custom MenuItemUI"); //$NON-NLS-1$
      private JMenu _MainMenu = new JMenu("Menu"); //$NON-NLS-1$
      
      public TestMenuItems() {
        
        this.setLayout( new BorderLayout() );    
        JPanel thePanel = new JPanel();
        this.add( thePanel, BorderLayout.CENTER );
        
        this.setDefaultCloseOperation( DISPOSE_ON_CLOSE );
        
        this.addMouseListener( new MouseAdapter(){
          public void mousePressed(MouseEvent e) {
            _thePopup.show((Component)e.getSource(), e.getX(), e.getY());
          }
        });
          
        _thePopup.add( _MainMenu );
        
        thePanel.setLayout( new BorderLayout() );
        
        addMainMenu();
        
        // default implementation
        _menuItemButton.setSelected( true );
        onRadioButtonPress();
      }
      
      private void addMainMenu(){
        
        JMenu theMenu = new JMenu("Main Menu"){ //$NON-NLS-1$ 
          public void setVisible( boolean isVisible ){
            super.setVisible( isVisible );
          }
        };  
        
        ActionListener theActionListener = new ActionListener(){
          public void actionPerformed( ActionEvent evt ){
            onRadioButtonPress();
          }
        };    
        _menuItemButton.addActionListener( theActionListener );
        _labelButton.addActionListener( theActionListener );
        _standardButton.addActionListener( theActionListener );
        _useMenuItemIU.addActionListener( theActionListener );
        
        ButtonGroup theGroup = new ButtonGroup();
        theGroup.add( _menuItemButton );
        theGroup.add( _labelButton );
        theGroup.add( _standardButton );
        theMenu.add( _menuItemButton ); 
        theMenu.add( _labelButton );
        theMenu.add( _standardButton );
        theMenu.addSeparator();
        theMenu.add( _useMenuItemIU ); 
        
        JMenuBar theMenuBar = new JMenuBar();
        theMenuBar.add( theMenu );    
        this.add( theMenuBar, BorderLayout.NORTH );
      }
      
      private void onRadioButtonPress(){
        
        _MainMenu.removeAll();
        
        if ( _menuItemButton.isSelected() ){
          // Using Menu Item
          addMenuItems( _MainMenu );
        }else if ( _labelButton.isSelected() ){
          // Using Custom JLabels    
          addMenuLabels( _MainMenu );
        }else{
          // build original 
          addStandardItems( _MainMenu );
        }
        
      }
      
      private void addMenuItems( JMenu menu ){
        MyJPanel theInnerPanel = new MyJPanel();
        theInnerPanel.setLayout( new BoxLayout( theInnerPanel, BoxLayout.X_AXIS ) );
        
        MyJMenuItem menuOne = new MyJMenuItem("Press 1"); //$NON-NLS-1$
        MyJMenuItem menuTwo = new MyJMenuItem("Press 2"); //$NON-NLS-1$
        MyJMenuItem menuThree = new MyJMenuItem("Press 3"); //$NON-NLS-1$
        MyJMenuItem menuFour = new MyJMenuItem("Press 4"); //$NON-NLS-1$
        
        if ( _useMenuItemIU.isSelected() ){
          menuOne.setUI( new MyMenuItemUI() );
          menuTwo.setUI( new MyMenuItemUI() );
          menuThree.setUI( new MyMenuItemUI() );
          menuFour.setUI( new MyMenuItemUI() );
        }
        
        theInnerPanel.add( menuOne );
        theInnerPanel.add( menuTwo );     
        menu.add(theInnerPanel); 
        menu.add(menuThree); 
        menu.add(menuFour);
      }
      
      private void addMenuLabels( JMenu menu ){
        MyJPanel theInnerPanel = new MyJPanel();
        theInnerPanel.setLayout( new BoxLayout( theInnerPanel, BoxLayout.X_AXIS ) );
        theInnerPanel.add( new MyJLabel("Press 1") ); //$NON-NLS-1$
        theInnerPanel.add( new MyJLabel("Press 2") ); //$NON-NLS-1$    
        
        menu.add(theInnerPanel); 
        menu.add(new MyJLabel("Press 3")); //$NON-NLS-1$
        menu.add(new MyJLabel("Press 4")); //$NON-NLS-1$
      }
      
      private void addStandardItems( JMenu menu ){
        
        JMenuItem menuOne = new JMenuItem("Press 1"); //$NON-NLS-1$
        JMenuItem menuTwo = new JMenuItem("Press 2"); //$NON-NLS-1$
        JMenuItem menuThree = new JMenuItem("Press 3"); //$NON-NLS-1$
        JMenuItem menuFour = new JMenuItem("Press 4"); //$NON-NLS-1$
        
        if ( _useMenuItemIU.isSelected() ){
          menuOne.setUI( new MyMenuItemUI() );
          menuTwo.setUI( new MyMenuItemUI() );
          menuThree.setUI( new MyMenuItemUI() );
          menuFour.setUI( new MyMenuItemUI() );
        }
        
        menu.add(menuOne);
        menu.add(menuTwo);
        menu.add(menuThree);
        menu.add(menuFour);
      }
      
      public static void main( String[] args ){
        
        TestMenuItems theApp = new TestMenuItems();
        theApp.setSize( new Dimension(300, 300));
        theApp.setLocation(300, 300);
        theApp.setVisible( true );
      }
      
      class MyJMenuItem extends JMenuItem {   
        public MyJMenuItem( String text ){
          super(text);      
          addCustomisation( this );
        }    
      }
      
      class MyJLabel extends JLabel {    
        public MyJLabel( String text ){
          super(text);      
          addCustomisation( this );      
        }        
      }  
      
      public class MyJPanel extends JPanel implements MenuElement {
        public void processMouseEvent(MouseEvent event, MenuElement[] path, MenuSelectionManager manager) {
          //TODO need to implement this method, possible delegate to the contained JMenuItem(s)
        }
        public void processKeyEvent(KeyEvent event, MenuElement[] path, MenuSelectionManager manager) {
          //TODO need to implement this method, possible delegate to the contained JMenuItem(s)
        }
        public void menuSelectionChanged(boolean isIncluded) {      
          //TODO need to implement this method, possible delegate to the contained JMenuItem(s)
        }
        public MenuElement[] getSubElements() {
          return this.getSubElements();
        }
        public Component getComponent() {
          return this;
        }    
      }
      
      public class MyMenuItemUI extends BasicMenuItemUI {
        
        public MenuElement[] getPath() {
          
          MenuSelectionManager m = MenuSelectionManager.defaultManager();
          MenuElement oldPath[] = m.getSelectedPath();
          MenuElement newPath[];
          int i = oldPath.length;
          if (i == 0)
              return new MenuElement[0];
          Component parent = menuItem.getParent();
          
          if ( parent instanceof MyJPanel ) {
            // To deal with JXLayer/JPanel wrapping
            MenuElement tempPath[];
            tempPath = new MenuElement[i+1];
            System.arraycopy(oldPath, 0, tempPath, 0, i);
            tempPath[i++] = (MyJPanel)parent;
            oldPath = tempPath;    
          } 
          
          if (oldPath[i-1].getComponent() == parent) {
              // The parent popup menu is the last so far
              newPath = new MenuElement[i+1];
              System.arraycopy(oldPath, 0, newPath, 0, i);
              newPath[i] = menuItem;
              
          } else {
              // A sibling menuitem is the current selection
              // 
              //  This probably needs to handle 'exit submenu into 
              // a menu item.  Search backwards along the current
              // selection until you find the parent popup menu,
              // then copy up to that and add yourself...
              int j;
              for (j = oldPath.length-1; j >= 0; j--) {
                  if (oldPath[j].getComponent() == parent)
                      break;
              }
              newPath = new MenuElement[j+2];
              System.arraycopy(oldPath, 0, newPath, 0, j+1);
              newPath[j+1] = menuItem;
              /*
              System.out.println("Sibling condition -- ");
              System.out.println("Old array : ");
              printMenuElementArray(oldPath, false);
              System.out.println("New array : ");
              printMenuElementArray(newPath, false);
              */
          }
          return newPath;
      }
        
      }
      
      private void addCustomisation( final JComponent comp ){
        
        final Color defaultBackgroundColor = this.getBackground();
        final Color defaultForegroundColor = this.getForeground();
        
        comp.setBorder( new EtchedBorder() );
        
        comp.addMouseListener( new MouseAdapter(){
          public void mouseClicked(MouseEvent e) {        
            comp.setBackground( defaultBackgroundColor );
            comp.setForeground( defaultForegroundColor );
            onMouseClickEvent(e);
          }
          public void mousePressed(MouseEvent e) {
            comp.setBackground( defaultBackgroundColor );
            comp.setForeground( defaultForegroundColor );
            onMouseClickEvent(e);
          }
          public void mouseReleased(MouseEvent e) {
            comp.setBackground( defaultBackgroundColor );
            comp.setForeground( defaultForegroundColor );
            onMouseClickEvent(e);
          }
          public void mouseEntered(MouseEvent e) {
            comp.setBackground( Color.RED );
            comp.setForeground( Color.BLUE );
          }
          public void mouseExited(MouseEvent e) {
            comp.setBackground( defaultBackgroundColor );
            comp.setForeground( defaultForegroundColor );
          }
        });
      }
      
      private void onMouseClickEvent( MouseEvent evt ){
        
        String strComponentText = ""; //$NON-NLS-1$
        
        if ( evt.getSource() instanceof JLabel )
          strComponentText = "JLabel MouseClicked: " +((JLabel)evt.getSource()).getText(); //$NON-NLS-1$
        
        if ( evt.getSource() instanceof JMenuItem )
          strComponentText = "JMenuItem MouseClicked: " +((JMenuItem)evt.getSource()).getText(); //$NON-NLS-1$
            
        System.out.println( strComponentText );
        
        if ( evt.getSource() instanceof JLabel ){
          _thePopup.setVisible( false );
        }
          
      }
    }
    Running the application initially demonstrates the issue of the JMenuItems disappearing. Turning on the "Use Custom MenuItemUI" option demonstrates the solution. Further turning on the "Use Wrapped JLabels" demonstrates the principle solution suggested by verma018.

    Thanks again for all your help guys.
    Last edited by snoopygee; 05-06-2011 at 09:58 AM.

Similar Threads

  1. adding a buffered image over a jpanel
    By fatmat9 in forum New To Java
    Replies: 1
    Last Post: 11-23-2010, 03:23 AM
  2. How to display info when a JPanel gains focus
    By toymachiner62 in forum Java Applets
    Replies: 8
    Last Post: 10-19-2009, 05:13 AM
  3. adding a jpanel in the middle of the script
    By 2o2 in forum AWT / Swing
    Replies: 11
    Last Post: 10-12-2008, 05:50 PM
  4. Replies: 3
    Last Post: 01-05-2008, 11:13 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •