I played around a bit with your solution. Before I post some results, I would like to introduce you to the „CPanelPopup“. It’s basically a dialog that behaves like a menu, and that does not close so easily. It’s look and feel however is not like a menu. Try this out:
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.action.CPanelPopup;
public class OpenMenuTest2 {
public static void main( String[] args ) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setBounds( 50, 50, 800, 500 );
CControl control = new CControl( frame );
frame.add( control.getContentArea() );
CPanelPopup popup = new CPanelPopup();
popup.setText( "P" );
popup.setShowTextOnButtons( true );
JPanel panel = new JPanel( new GridLayout( 3, 1 ));
popup.setContent( panel );
panel.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
panel.add( new JCheckBox( "Check Box A" ) );
panel.add( new JCheckBox( "Check Box B" ) );
panel.add( new JCheckBox( "Check Box C" ) );
DefaultSingleCDockable dockable = new DefaultSingleCDockable( "id", "Title" );
dockable.addAction( popup );
control.addDockable( dockable );
dockable.setVisible( true );
frame.setVisible( true );
}
}
*** Edit ***
- You will need version 1.1.2p11 to run this code.
- It does not work.
Well, it might work, but unfortunately the framework is not opening a JMenu, but a JPopupMenu. And JPopupMenus seem to have a different behavior than normal JMenus. I searched around a bit, but could not find any satisfying solution that would keep the popupmenu open. In the end I think the only thing I can do, is to show you where the JPopupMenu is created (so you may replace it with something else), and to show you where your code would have gone.
Let me know if you find a good solution to keep a JPopupMenu open. If such a solution exists, then I am certain that it can be combined with the framework.
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractButton;
import javax.swing.JFrame;
import javax.swing.JPopupMenu;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.action.DockActionSource;
import bibliothek.gui.dock.action.popup.ActionPopupMenu;
import bibliothek.gui.dock.action.popup.ActionPopupMenuFactory;
import bibliothek.gui.dock.action.popup.DefaultActionPopupMenu;
import bibliothek.gui.dock.action.view.ActionViewConverter;
import bibliothek.gui.dock.action.view.ViewTarget;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.action.CAction;
import bibliothek.gui.dock.common.action.CCheckBox;
import bibliothek.gui.dock.common.action.CMenu;
import bibliothek.gui.dock.common.action.core.CommonSimpleCheckAction;
import bibliothek.gui.dock.themes.basic.action.menu.MenuViewItem;
public class OpenMenuTest {
public static void main( String[] args ) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setBounds( 50, 50, 800, 500 );
CControl control = new CControl( frame );
frame.add( control.getContentArea() );
CMenu menu = new CMenu( "X", null );
menu.setShowTextOnButtons( true );
menu.add( new NotClosingCheckBox( "Top level checkbox ") );
CMenu submenu = new CMenu( "Submenu", null );
submenu.add( new NotClosingCheckBox( "Sub level" ) );
menu.add( submenu );
DefaultSingleCDockable dockable = new DefaultSingleCDockable( "id", "Title" );
dockable.addAction( menu );
control.addDockable( dockable );
dockable.setVisible( true );
// by setting the popup menu factory, you can replace the default JPopupMenu by any custom implementation you would like to use
control.getController().setPopupMenuFactory( new ActionPopupMenuFactory() {
@Override
public ActionPopupMenu createMenu( Component owner, Dockable dockable, DockActionSource actions, Object source ) {
JPopupMenu menu = new JPopupMenu(); // ... for example you could subclass this JPopupMenu, use a JDialog instead, etc.
return new DefaultActionPopupMenu( dockable, actions, menu );
}
} );
frame.setVisible( true );
}
// This section shows how you can access all the JMenuItems, and add listeners to them.
private static class NotClosingCheckBox extends CCheckBox{
public NotClosingCheckBox( String text ){
super( null );
// The CCheckBox makes use of a CommonSimpleCheckAction to create and handle menus (CCheckBox implores the facade pattern)
init( new NotClosingCheckBoxImpl( this ) );
setText( text );
}
@Override
protected void changed() {
System.out.println( "checkbox changed" );
}
}
private static class NotClosingCheckBoxImpl extends CommonSimpleCheckAction{
public NotClosingCheckBoxImpl( CAction action ) {
super( action );
}
// Before a popup menu opens, this method is called and it creates the JComponent representing this item
@Override
public <V> V createView( ViewTarget<V> target, ActionViewConverter converter, Dockable dockable ) {
V result = super.createView( target, converter, dockable );
if( result instanceof MenuViewItem<?> ){
// Using some magic, we find the JCheckBoxMenuItem that is going to be shown
ensureNotClosing( (MenuViewItem<?>)result );
}
return result;
}
}
private static void ensureNotClosing( MenuViewItem<?> item ){
Object button = item.getItem();
if( button instanceof AbstractButton ){
ensureNotClosing( (AbstractButton)button );
}
}
private static void ensureNotClosing( AbstractButton button ){
// And here the code you sent me is called
new KeepOpen( button );
}
private static class KeepOpen implements ChangeListener, ActionListener{
private AbstractButton button;
private MenuElement[] pathToButton;
public KeepOpen(AbstractButton button){
this.button = button;
button.getModel().addChangeListener( this );
button.addActionListener( this );
}
public void stateChanged( ChangeEvent e ) {
if (button.getModel().isArmed() && button.isShowing()){
pathToButton = MenuSelectionManager.defaultManager().getSelectedPath();
}
}
@Override
public void actionPerformed( ActionEvent e ) {
MenuSelectionManager.defaultManager().setSelectedPath( pathToButton );
}
}
}