Using the dock.zip source you have provided and the updated code my application is working perfectly Thank you!!!
Regarding the popupmenu on the dockable:
In my case, I may have different popupmenus on each dockable. And I don’t know, my application how many dockables has.
Is it feasible, upon creation of the dockable to append elements on it’s popupmenu?
Is it feasible to hide those additional menus from the title bar of the dokbales?
You can add/remove actions from the (Default)DockActionSource at any time. The popup menu will be updated every time it is opened. You could also store one or more DockActionSources directly in your Dockable object, thus every Dockable could have its custom set of actions.
If you extend a DockAction you can override the „createView“ method and return null for those places where the action should not appear.
Your requirements are kind of changing the entire time… what exactly do you want to do? With storing the source in the Dockables I meant making a subclass, like in the example below.
You could also use your second approach, but in that case the action would appear independent of the current location of the Dockable. And overriding “createView” gets harder because CActions are wrappers around DockActions - there is an additional level of indirection to cover.
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import javax.swing.Icon;
import javax.swing.JFrame;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.StackDockStation;
import bibliothek.gui.dock.action.ActionGuard;
import bibliothek.gui.dock.action.DefaultDockActionSource;
import bibliothek.gui.dock.action.DockActionSource;
import bibliothek.gui.dock.action.LocationHint;
import bibliothek.gui.dock.action.actions.SimpleButtonAction;
import bibliothek.gui.dock.action.view.ActionViewConverter;
import bibliothek.gui.dock.action.view.ViewTarget;
import bibliothek.gui.dock.common.CContentArea;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CLocation;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.intern.CDockable;
import bibliothek.gui.dock.common.intern.CommonDockable;
public class Dock70 {
public static void main( String[] args ){
JFrame frame = new JFrame();
CControl control = new CControl( frame );
// here we install our custom ActionGuard
control.getController().addActionGuard( new CustomActionGuard() );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setBounds( 20, 20, 500, 400 );
CContentArea area = control.getContentArea();
frame.add( area );
DefaultSingleCDockable dockableA = new CustomDockable( "test a", "Test A" );
DefaultSingleCDockable dockableB = new CustomDockable( "test b", "Test B" );
DefaultSingleCDockable dockableC = new CustomDockable( "test c", "Test C" );
control.addDockable( dockableA );
control.addDockable( dockableB );
control.addDockable( dockableC );
dockableA.setVisible( true );
dockableB.setLocation( dockableA.getBaseLocation().aside() );
dockableB.setVisible( true );
dockableC.setLocation( CLocation.base().minimalEast() );
dockableC.setVisible( true );
frame.setVisible( true );
}
// Custom dockable storing some additional actions
private static class CustomDockable extends DefaultSingleCDockable{
private DefaultDockActionSource customActions;
public CustomDockable( String id, String title ){
super( id, title );
customActions = new DefaultDockActionSource( new LocationHint( LocationHint.ACTION_GUARD, LocationHint.LEFT ) );
customActions.add( new CustomAction() );
customActions.add( new CustomAction() );
customActions.add( new CustomAction() );
}
public DefaultDockActionSource getCustomActions(){
return customActions;
}
}
private static class CustomActionGuard implements ActionGuard{
@Override
public boolean react( Dockable dockable ){
// We can define any rule we want to decide which dockable should receive
// our actions and which should not
// in this case we make sure only the dockable "dockableA/B/C" from the
// main-method receive the actions
if( !(dockable instanceof CommonDockable) ){
return false;
}
CDockable cdockable = ((CommonDockable)dockable).getDockable();
DockStation parent = dockable.getDockParent();
// and in your code this would be the class "CStackDockStation" and not
// yust "StackDockStation"
return parent instanceof StackDockStation && cdockable instanceof CustomDockable;
}
@Override
public DockActionSource getSource( Dockable dockable ){
return ((CustomDockable)((CommonDockable)dockable).getDockable()).getCustomActions();
}
}
// there are a number of different actions available, but the button-action is the
// most basic.
private static class CustomAction extends SimpleButtonAction{
public CustomAction(){
setIcon( new RectIcon() );
setText( "Hello" );
}
@Override
public void action( Dockable dockable ){
// note that these casts will always work because of the checks our CustomActionGuard performs
DefaultSingleCDockable cdockable = (DefaultSingleCDockable)((CommonDockable)dockable).getDockable();
System.out.println( "Ping: " + cdockable.getTitleText() );
}
@Override
public <V> V createView( ViewTarget<V> target, ActionViewConverter converter, Dockable dockable ){
if( target == ViewTarget.MENU || target == ViewTarget.DROP_DOWN ){
return super.createView( target, converter, dockable );
}
else{
return null;
}
}
}
// just add something to look at :-)
private static class RectIcon implements Icon{
public int getIconHeight(){
return 16;
}
public int getIconWidth(){
return 16;
}
public void paintIcon( Component c, Graphics g, int x, int y ){
g.setColor( Color.GREEN );
g.fillRect( x+1, y+1, 14, 14 );
}
}
}```
Your requirements are kind of changing the entire time…
I’ll try to exaplain in details my requirements
My framework offers the possibilty to the user to define popupmenus on each components. So my target is to add popup menus on my framework components.
Since my framework components are using DefaultSingleCDockable and CStack I need to define menus on those 2 components.
For the CStack, I will loop on its children DefaultSingleCDockable and add the popupmenu. In that way the popup menu will appear everywhere on the CStack
Note that in my framework, I have two types of menu items: static and dynamic
static menu items : built initially on the creation of the components and used everytime the popupmenu is shown
dynamic menu items: built every time the user right click on the components. Some business requires that the menu items changes based on the staus or time for example
For the static menu items and based on your example I can see the implementation. Any advice for the dynamic menu items? Is their any way to be notified when the user right click in order to show the popupmenu? something like menuwillbecomevisible notification?
what exactly do you want to do? With storing the source in the Dockables I meant making a subclass, like in the example below.
While reading the API, I noticed that for the Dockable class we can define ActionSource then add the ActionSource to the dockable. I thought this is what you are talking about
DefaultDockActionSource source = new DefaultDockActionSource(new LocationHint(LocationHint.DOCKABLE, LocationHint.LEFT));
source.add(new Action());
// Sets the below defined action to this Dockable
customDefaultDockable.setActionOffers(source);
But after reading your example (thank you for it), I got your point and I’m using the way you’ve provided in the example.
Dynamic menus: I’ll create a listener interface for that event, the menus are always created by the same code so a listener basically is 3 more lines of code. And once the listener exists you can update (or replace) the DockActionSources before the menu opens.
I’m thinking of a work around, please advice if it works
Behind your “menu actions” implementation, and in order to display the actions in a popupmenu are you using a JPopupMenu?
If yes, is there any way to get this JPopupMenu?
For my side, it will be great , much easier and consistent with other component popupmenu implementation if I can add/remove directly to/from JPopupMenu directly.
That can be arranged. I would add an interface that constructs the menu, and it is up to the interface how the menu really looks like (like all the other strategy-interfaces that float around).
Ok, these are two new libraries for you to play around. I wanted to make the popup menus customizeable for some time, but I never started… so your request was very appreciated
You can now set the “ActionPopupMenuFactory”, this factory creates new “ActionPopupMenu”. The ActionPopupMenu basically is a wrapper around JPopupMenu, but the framework is no longer interested in what exactly you use as menu (feel free to create a JFrame…).
These menus are used for all the popup menus that are ever shown by the framework: normal popup menus when the user right clicks onto a component, but also popup menus that appear when clicking on a MenuDockAction. You can distinguish the cases by looking at the “source” object that is given to the factory, exactly like in the example.
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import javax.swing.Icon;
import javax.swing.JFrame;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.DockElementRepresentative;
import bibliothek.gui.dock.action.DefaultDockActionSource;
import bibliothek.gui.dock.action.DockActionSource;
import bibliothek.gui.dock.action.MultiDockActionSource;
import bibliothek.gui.dock.action.actions.SimpleButtonAction;
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.common.CContentArea;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CLocation;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.action.CDropDownButton;
import bibliothek.gui.dock.common.action.CMenu;
import bibliothek.gui.dock.common.intern.CommonDockable;
public class Dock71 {
public static void main( String[] args ){
JFrame frame = new JFrame();
CControl control = new CControl( frame );
// installing our custom menu factory
control.getController().setPopupMenuFactory( new CustomPopupFactory() );
control.getController().getPopupController().setAllowEmptyMenu( true );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setBounds( 20, 20, 500, 400 );
CContentArea area = control.getContentArea();
frame.add( area );
DefaultSingleCDockable dockableA = new DefaultSingleCDockable( "test a", "Test A" );
DefaultSingleCDockable dockableB = new DefaultSingleCDockable( "test b", "Test B" );
DefaultSingleCDockable dockableC = new DefaultSingleCDockable( "test c", "Test C" );
// these menus should not be affected
dockableA.addAction( new CMenu( "Test", new RectIcon() ) );
dockableA.addAction( new CDropDownButton( "Test2", new RectIcon() ) );
control.addDockable( dockableA );
control.addDockable( dockableB );
control.addDockable( dockableC );
dockableA.setVisible( true );
dockableB.setLocation( dockableA.getBaseLocation().aside() );
dockableB.setVisible( true );
dockableC.setLocation( CLocation.base().minimalEast() );
dockableC.setVisible( true );
frame.setVisible( true );
}
// this is our custom menu factory. Clients are completely free in how the menu should look like, they
// can even choose not to use a JPopupMenu at all.
private static class CustomPopupFactory implements ActionPopupMenuFactory{
@Override
public ActionPopupMenu createMenu( Component owner, Dockable dockable, DockActionSource actions, Object source ){
if( !(source instanceof DockElementRepresentative )){
return new DefaultActionPopupMenu( dockable, actions );
}
DefaultDockActionSource newActions = new DefaultDockActionSource();
newActions.add( new CustomAction() );
newActions.add( new CustomAction() );
newActions.add( new CustomAction() );
MultiDockActionSource finalActions = new MultiDockActionSource( newActions, actions );
return new DefaultActionPopupMenu( dockable, finalActions );
}
}
// there are a number of different actions available, but the button-action is the
// most basic.
private static class CustomAction extends SimpleButtonAction{
public CustomAction(){
setIcon( new RectIcon() );
setText( "Hello" );
}
@Override
public void action( Dockable dockable ){
// note that these casts will always work because of the checks our CustomActionGuard performs
DefaultSingleCDockable cdockable = (DefaultSingleCDockable)((CommonDockable)dockable).getDockable();
System.out.println( "Ping: " + cdockable.getTitleText() );
}
}
// just add something to look at :-)
private static class RectIcon implements Icon{
public int getIconHeight(){
return 16;
}
public int getIconWidth(){
return 16;
}
public void paintIcon( Component c, Graphics g, int x, int y ){
g.setColor( Color.GREEN );
g.fillRect( x+1, y+1, 14, 14 );
}
}
}```
Thank you… I’m working with the new code, still not finished my implementation… I’ll keep you updated
How can I set the south station as a default flap station?
For example, if in the example above, I minimize a dockable, it will directly go to the north station at left. Is it feasible to customize it to go initially to the south flap station and if possible at right?
Currently the only option is to set the default location of each dockable like this:
control.getLocationManager().setLocation( cdockable.intern(), ExtendedMode.MINIMIZED, CLocation.base().minimalSouth() );```
... you'll need to have the dockable registered at the CControl, otherwise no meta information is stored for it.
Sorry for the delay. Such problems are best solved with a DockAcceptance. Implement the interface, forbid any combination of stations and Dockable you don’t want, install the interface with “DockController.addAcceptance”.
There is a method “DefaultCDockable.setTitleToolTip” which sets a tooltip. I just checked, the EclipseTheme does not support the tooltip, I’ll add/fix that feature during the weekend.