Dockable Menu and Toolbar

Hello,

Could you provide an example, tutorial, or any ideas on how to implement dockable menus and toolbars using DockingFrames. I would like to have dockable menus and toolbars in my application similar to MS Office 2003 applications.

I appreciate any ideas and suggestions.

Thank you,

Alex

You could implement a specialized DockStation to handle the possible layouts of the menus and toolbars. The menus and toolbars could be made from ordinary Dockables with a custom DockTitle. All of this would require some work, especially the DockStation would not be easy to implement (I’m speaking of several days).

I’m not sure if this framework is the best basis for such a task, perhaps it would be easier to start from scratch.

Hi Beni,

Thank you.

Could you point me to the example or tutorial to start with?

Thank you again,

Alex

I’m currently at work so no big example at this time, but you may have a look at the “chess” application that is delivered with the framework. The application is related to your idea because it also takes a DockStation and some Dockables and combines them in an unusual way.

(I think about an example later, I’ll write again).

Below is a basic example how toolbars could be implemented. You would certainly have to write your own code to insert and layout toolbars. Also if you want to store the location of toolbars you’ll have to implemented the “ToolbarBaseFactory”.

This simple version offers nothing but an option to move around “toolbars” by grabbing a very simple “knob”.

As you see, there will be some work involved in order to get toolbars running.


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.BevelBorder;
import javax.swing.event.MouseInputListener;

import bibliothek.gui.DockController;
import bibliothek.gui.DockStation;
import bibliothek.gui.DockUI;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.DefaultDockable;
import bibliothek.gui.dock.DockElement;
import bibliothek.gui.dock.DockFactory;
import bibliothek.gui.dock.action.DockActionSource;
import bibliothek.gui.dock.event.DockTitleEvent;
import bibliothek.gui.dock.layout.DockableProperty;
import bibliothek.gui.dock.layout.LocationEstimationMap;
import bibliothek.gui.dock.perspective.PerspectiveDockable;
import bibliothek.gui.dock.perspective.PerspectiveElement;
import bibliothek.gui.dock.station.AbstractDockStation;
import bibliothek.gui.dock.station.DisplayerCollection;
import bibliothek.gui.dock.station.DisplayerFactory;
import bibliothek.gui.dock.station.DockableDisplayer;
import bibliothek.gui.dock.station.support.PlaceholderMap;
import bibliothek.gui.dock.station.support.PlaceholderStrategy;
import bibliothek.gui.dock.themes.basic.BasicDockableDisplayer;
import bibliothek.gui.dock.title.DockTitle;
import bibliothek.gui.dock.title.DockTitleFactory;
import bibliothek.gui.dock.title.DockTitleRequest;
import bibliothek.gui.dock.title.DockTitleVersion;
import bibliothek.util.xml.XElement;

public class Dock39 {
	private static final String TOOLBAR = "toolbar";

	public static void main( String[] args ){
		JFrame frame = new JFrame();
		DockController controller = new DockController();
		controller.setRootWindow( frame );

		ToolbarBase north = new ToolbarBase();
		ToolbarBase south = new ToolbarBase();

		controller.add( north );
		controller.add( south );

		frame.add( north.panel, BorderLayout.NORTH );
		frame.add( south.panel, BorderLayout.SOUTH );

		for( int i = 0; i < 5; i++ ) {
			north.drop( new DefaultDockable( new JLabel( "Content of toolbar " + i ), "Toolbar " + i ) );
		}

		frame.setBounds( 20, 20, 500, 300 );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.setVisible( true );
	}

	// the factory stores and loads the layout of the toolbar
	
	private static class ToolbarBaseFactory implements DockFactory<ToolbarBase, PerspectiveElement, Object> {
		@Override
		public void estimateLocations( Object layout, LocationEstimationMap children ){
			// TODO Auto-generated method stub

		}

		@Override
		public Object getPerspectiveLayout( PerspectiveElement element, Map<PerspectiveDockable, Integer> children ){
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public ToolbarBase layout( Object layout, Map<Integer, Dockable> children ){
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public ToolbarBase layout( Object layout ){
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public PerspectiveElement layoutPerspective( Object layout, Map<Integer, PerspectiveDockable> children ){
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public void layoutPerspective( PerspectiveElement perspective, Object layout, Map<Integer, PerspectiveDockable> children ){
			// TODO Auto-generated method stub

		}

		@Override
		public String getID(){
			return TOOLBAR;
		}

		@Override
		public Object getLayout( ToolbarBase element, Map<Dockable, Integer> children ){
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public Object read( DataInputStream in, PlaceholderStrategy placeholders ) throws IOException{
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public Object read( XElement element, PlaceholderStrategy placeholders ){
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public void setLayout( ToolbarBase element, Object layout, Map<Integer, Dockable> children ){
			// TODO Auto-generated method stub

		}

		@Override
		public void setLayout( ToolbarBase element, Object layout ){
			// TODO Auto-generated method stub

		}

		@Override
		public void write( Object layout, DataOutputStream out ) throws IOException{
			// TODO Auto-generated method stub

		}

		@Override
		public void write( Object layout, XElement element ){
			// TODO Auto-generated method stub

		}

	}

	// information about where to insert a toolbar
	private static class ToolbarBaseInsert {
		private Dockable dockable;
		private int index;

		public ToolbarBaseInsert( Dockable dockable, int index ){
			this.dockable = dockable;
			this.index = index;
		}

		public Dockable getDockable(){
			return dockable;
		}

		public int getIndex(){
			return index;
		}
	}

	// the title (knob) of a toolbar
	private static class ToolbarTitle extends JPanel implements DockTitle {
		private Dockable dockable;
		private DockTitleVersion version;

		private boolean active = false;

		public ToolbarTitle( DockTitleVersion version, Dockable dockable ){
			this.dockable = dockable;
			this.version = version;

			setMinimumSize( new Dimension( 5, 20 ) );
			setBorder( BorderFactory.createBevelBorder( BevelBorder.RAISED ) );
		}

		@Override
		public void addMouseInputListener( MouseInputListener listener ){
			addMouseListener( listener );
			addMouseMotionListener( listener );
		}

		@Override
		public void bind(){
			// ignore
		}

		@Override
		public void changed( DockTitleEvent event ){
			active = event.isActive();
			if( active ) {
				setBorder( BorderFactory.createBevelBorder( BevelBorder.LOWERED ) );
			}
			else {
				setBorder( BorderFactory.createBevelBorder( BevelBorder.RAISED ) );
			}
		}

		@Override
		public Component getComponent(){
			return this;
		}

		@Override
		public Dockable getDockable(){
			return dockable;
		}

		@Override
		public Orientation getOrientation(){
			return Orientation.WEST_SIDED;
		}

		@Override
		public DockTitleVersion getOrigin(){
			return version;
		}

		@Override
		public boolean isActive(){
			return active;
		}

		@Override
		public void removeMouseInputListener( MouseInputListener listener ){
			removeMouseListener( listener );
			removeMouseMotionListener( listener );
		}

		@Override
		public void setOrientation( Orientation orientation ){
			// ignore
		}

		@Override
		public void unbind(){
			// ignore
		}

		@Override
		public DockElement getElement(){
			return dockable;
		}

		@Override
		public Point getPopupLocation( Point click, boolean popupTrigger ){
			return click;
		}

		@Override
		public boolean isUsedAsTitle(){
			return true;
		}

		@Override
		public boolean shouldFocus(){
			return true;
		}

		@Override
		public boolean shouldTransfersFocus(){
			return true;
		};
	}

	// the panel on which toolbars are dropped
	private static class ToolbarBase extends AbstractDockStation {
		// represents a single toolbar
		private class Child extends DockTitleRequest {
			private DockableDisplayer displayer;

			public Child( Dockable dockable ){
				super( ToolbarBase.this, dockable, titleVersion );

				dockable.setDockParent( ToolbarBase.this );
				displayer = displayers.fetch( insert.getDockable(), null );

				install();
				request();
			}

			@Override
			protected void answer( DockTitle previous, DockTitle title ){
				if( previous != null ) {
					displayer.getDockable().unbind( previous );
				}
				if( title != null ) {
					displayer.getDockable().bind( title );
				}
				displayer.setTitle( title );
			}

			public void destroy(){
				uninstall();
				DockTitle title = displayer.getTitle();
				if( title != null ) {
					displayer.getDockable().unbind( title );
				}
				displayer.getDockable().setDockParent( null );
				displayers.release( displayer );
			}

			public DockableDisplayer getDisplayer(){
				return displayer;
			}
		}

		private JPanel panel;
		private DisplayerCollection displayers;

		private List<Child> children = new ArrayList<Child>();
		private ToolbarBaseInsert insert;

		private DockTitleVersion titleVersion;
		private Dockable frontDockable;

		public ToolbarBase(){
			displayers = new DisplayerCollection( this, new DisplayerFactory(){
				public DockableDisplayer create( DockStation station, Dockable dockable, DockTitle title ){
					return new BasicDockableDisplayer( station, dockable, title, DockableDisplayer.Location.LEFT );
				}
			} );

			panel = new JPanel();
			panel.setMinimumSize( new Dimension( 20, 20 ) );
			panel.setBackground( Color.RED );
			panel.setOpaque( true );
		}

		@Override
		public void setController( DockController controller ){
			super.setController( controller );
			if( controller != null ) {
				titleVersion = controller.getDockTitleManager().getVersion( "toolbar", new DockTitleFactory(){
					@Override
					public void uninstall( DockTitleRequest request ){
						// ignore
					}

					@Override
					public void request( DockTitleRequest request ){
						request.answer( new ToolbarTitle( request.getVersion(), request.getTarget() ) );
					}

					@Override
					public void install( DockTitleRequest request ){
						// ignore
					}
				} );
			}
		}

		private void rebuildLayout(){
			panel.removeAll();
			panel.setLayout( new GridLayout( children.size(), 1 ) );

			panel.setMinimumSize( new Dimension( 20, Math.max( 20, 20 * children.size() ) ) );
			panel.setPreferredSize( new Dimension( 20, Math.max( 20, 20 * children.size() ) ) );

			for( Child child : children ) {
				panel.add( child.getDisplayer().getComponent() );
			}

			panel.revalidate();
		}

		protected void callDockUiUpdateTheme() throws IOException{
			DockUI.updateTheme( this, new ToolbarBaseFactory() );
		}

		@Override
		public boolean canDrag( Dockable dockable ){
			return true;
		}

		@Override
		public boolean canReplace( Dockable old, Dockable next ){
			return false;
		}

		public int indexOf( Dockable dockable ){
			for( int i = 0, n = children.size(); i < n; i++ ) {
				if( children.get( i ).getDisplayer().getDockable() == dockable ) {
					return i;
				}
			}
			return -1;
		}

		@Override
		public void drag( Dockable dockable ){
			int index = indexOf( dockable );
			if( index >= 0 ) {
				if( dockable == frontDockable ) {
					setFrontDockable( null );
				}

				listeners.fireDockableRemoving( dockable );

				Child child = children.remove( index );
				panel.remove( child.getDisplayer().getComponent() );
				child.destroy();
				rebuildLayout();

				listeners.fireDockableRemoved( dockable );
			}
		}

		@Override
		public void draw(){
			// ignore
		}

		@Override
		public void drop(){
			listeners.fireDockableAdding( insert.getDockable() );

			children.add( insert.getIndex(), new Child( insert.getDockable() ) );
			rebuildLayout();

			listeners.fireDockableAdded( insert.getDockable() );
		}

		@Override
		public void drop( Dockable dockable ){
			insert = new ToolbarBaseInsert( dockable, 0 );
			drop();
			forget();
		}

		@Override
		public boolean drop( Dockable dockable, DockableProperty property ){
			drop( dockable );
			return true;
		}

		@Override
		public void move(){
			int index = indexOf( insert.getDockable() );
			if( index != insert.getIndex() ) {
				Child child = children.remove( index );
				if( index < insert.getIndex() ) {
					index = insert.getIndex() - 1;
				}
				else {
					index = insert.getIndex();
				}
				children.add( index, child );
				rebuildLayout();
			}
		}

		@Override
		public void move( Dockable dockable, DockableProperty property ){
			// ignore
		}

		@Override
		public boolean prepareDrop( int mouseX, int mouseY, int titleX, int titleY, boolean checkOverrideZone, Dockable dockable ){
			Point point = new Point( mouseX, mouseY );
			SwingUtilities.convertPointFromScreen( point, panel );

			int index = (int) ((point.y / (double) panel.getHeight()) * children.size());

			index = Math.max( 0, Math.min( index, children.size() ) );

			insert = new ToolbarBaseInsert( dockable, index );
			return true;
		}

		@Override
		public boolean prepareMove( int mouseX, int mouseY, int titleX, int titleY, boolean checkOverrideZone, Dockable dockable ){
			return prepareDrop( mouseX, mouseY, titleX, titleY, checkOverrideZone, dockable );
		}

		@Override
		public void forget(){
			insert = null;
		}

		@Override
		public DockActionSource getDirectActionOffers( Dockable dockable ){
			return null;
		}

		@Override
		public Dockable getDockable( int index ){
			return children.get( index ).getDisplayer().getDockable();
		}

		@Override
		public int getDockableCount(){
			return children.size();
		}

		@Override
		public DockableProperty getDockableProperty( Dockable child, Dockable target ){
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public Dockable getFrontDockable(){
			return null;
		}

		@Override
		public DockActionSource getIndirectActionOffers( Dockable dockable ){
			return null;
		}

		@Override
		public PlaceholderMap getPlaceholders(){
			return null;
		}

		@Override
		public Rectangle getStationBounds(){
			Point point = new Point( 0, 0 );
			SwingUtilities.convertPointToScreen( point, panel );
			return new Rectangle( point, panel.getSize() );
		}

		@Override
		public <D extends Dockable & DockStation> boolean isInOverrideZone( int x, int y, D invoker, Dockable drop ){
			return false;
		}

		@Override
		public void replace( Dockable old, Dockable next ){
			// ignore
		}

		@Override
		public void replace( DockStation old, Dockable next ){
			// ignore	
		}

		@Override
		public void setFrontDockable( Dockable dockable ){
			Dockable oldSelected = frontDockable;
			frontDockable = dockable;

			listeners.fireDockableSelected( oldSelected, frontDockable );
		}

		@Override
		public void setPlaceholders( PlaceholderMap placeholders ){
			// ignore
		}

		@Override
		public DockStation asDockStation(){
			return this;
		}

		@Override
		public Dockable asDockable(){
			return null;
		}

		@Override
		public String getFactoryID(){
			return TOOLBAR;
		}

	}
}

Hi Beni,

Thank you! This is really good example!

All the best,

Alex