Customization of drag and drop behavior

Hi all,
I’m extensively using the common library to see if it could really fits all my needs. And from what I have learned, it is extraordinary customizable.

In this thread, I would like to have your advice about a specific behavior that I’m trying to reproduce.

I basically want to drag the mouse on the title of a DefaultSingleCDockable and, at the instant when I start dragging it, the dockable is externalized to a custom ScreenDockWindow whose JDialog “continues” to be dragged as the mouse move (the effect should be a continuous movement from the docked title of the CDockable to the title of the JDialog). Finally, while dragging the title of the JDialog, the framework looks for a compatible spot and when the mouse is released and a target is compatible, the dockable is dropped, otherwise it stays on the JDialog.

Actually I’ve almost achieved this behavior as follows:

  • create a direct remote relocator for the dockable;
  • hide the title of the dockable and create a customized component on the jpanel of the dockable to listen for mouse motion events,
  • when a mouse motion event occurs (in the JPanel), the related dockable is set to externalized mode, and the remote relocator methods init and drag are called;
  • the customized screen dock dialog hides the title component in the JPanel of the dockable and register mouse motion listeners to the JDialog title in order to continue to call the dockable’s relocator methods;
  • when the mouse is released, if a spot is found the relocator method drop is called.

This is the basic idea.
Any suggestions to improve it? Or whether something would not work?

I’ve also a side question. Is it possible to avoid the highlighting of the dockable when searching for a spot to drop it?

Thank you all for any suggestion.
The effect that I’ve in mind should be very nice, and use a lot of framework’s components.

You might be interested in the interface “DockableMovingImageFactory”. It is responsible for creating the image that is usually attached to the mouse when a Dockable is moved around. The factory can be replaced by calling

control.putProperty(DockTheme.DOCKABLE_MOVING_IMAGE_FACTORY, <your new factory>)

There are several implementations in the library, but in your case you propably want to set it to an implementation that always returns “null” (thus disabling the images).

Or you write a new factory that makes a screenshot of the dockable that you want to move, and show that screenshot. That might not look as fancy as your solution, but it will certainly not have any unintended side effects.

Instead of removing the title you might also use the library to track the mouse for you. The code below is only a prototype I wrote to ensure my idea works (and I’ve found some bugs in doing so, I’ll upload a fix today). Its a MouseMotionListener that tracks the location of the mouse, and a VetoableDockRelocatorListener that waits until the user started moving a Dockable. As soon as the user is starting to move the Dockable the operation is cancelled, and the Dockable is put in a JDialog. After this point the MouseMotionListener will still be receiving events and you could use these events to move the Dockable around.


import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;

import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
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;
import bibliothek.gui.dock.common.mode.ExtendedMode;
import bibliothek.gui.dock.control.relocator.DockRelocatorEvent;
import bibliothek.gui.dock.control.relocator.VetoableDockRelocatorAdapter;

public class Marcello {
	public static void main( String[] args ) {
		new Marcello();
	}
	
	private CControl control;
	private MouseEvent lastEvent;
	
	public Marcello(){
		JFrame frame = new JFrame("Test");
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		
		control = new CControl( frame );
		frame.add( control.getContentArea() );
		
		control.getController().getRelocator().addVetoableDockRelocatorListener( new KickToWindow() );
		control.getController().getGlobalMouseDispatcher().addMouseMotionListener( new KickToWindowInitialMouseOffset() );
		
		CDockable a = new DefaultSingleCDockable( "a", "Aaaa" );
		CDockable b = new DefaultSingleCDockable( "b", "Bbbb" );
		CDockable c = new DefaultSingleCDockable( "c", "Cccc" );
		CGrid grid = new CGrid( control );
		grid.add( 0, 0, 2, 1, a );
		grid.add( 0, 1, 1, 1, b );
		grid.add( 1, 1, 1, 1, c );
		control.getContentArea().deploy( grid );
		
		frame.setBounds( 20, 20, 1000, 1000 );
		frame.setVisible( true );
	}
	
	private class KickToWindowInitialMouseOffset extends MouseAdapter{
		@Override
		public void mouseDragged( MouseEvent e ) {
			lastEvent = e;
			System.out.println("dragged: " + e);
		}
	}
	
	private class KickToWindow extends VetoableDockRelocatorAdapter{
		@Override
		public void grabbed( DockRelocatorEvent event ) {
			System.out.println("cancel grabbed for " + event.getDockable().getTitleText());
			
			// no, don't start moving the Dockable, we want to do this ourselves
			event.cancel();
			
			// may throw a ClassCastException, it's ok for a simple test
			CDockable cdockable = ((CommonDockable)event.getDockable()).getDockable();
			
			cdockable.setExtendedMode( ExtendedMode.EXTERNALIZED );
			
			System.out.println("mouse event responsible for this call is: " + lastEvent);
			
			// we would need to calculate the exact location of the window using 'lastEvent'
			int x = event.getMouseLocation().x;
			int y = event.getMouseLocation().y;
			int width = event.getDockable().getComponent().getWidth();
			int height = event.getDockable().getComponent().getHeight();
			
			cdockable.setLocation( CLocation.external( x, y, width, height ) );
		}
	}
}

*** Edit ***

About the painting effects. Painting is done by the interface StationPaint (property DockTheme.STATION_PAINT). You could just replace the default with an empty implementation.

Also the library is able to resize Dockables during drag and drop operations to better indicate how the layout would look like if the user would drop the Dockable. Resizing Dockables is done through the interface SpanFactory (property DockTheme.SPAN_FACTORY). You might want to replace the default setting with a NoSpanFactory

P.S. properties are set using `CControl.putProperty(…)[/inlne]

Hi Beni,
thanks a lot for all your answers (I will thank you everywhere).
I’ve actually implemented my solution, and the effect that I was able to reach is surprisingly amazing.
Indeed, I was actually looking for a continuous movement of the dockable from the docked station to the screen. And then it actually looks for a relocation when the screen dock is dragged.

You can see a small video (low-resolution though) here:
https://dl.dropboxusercontent.com/u/9161704/Test.wmv

I’ve basically used the remote relocator, and modifed the ScreenDockWindowFactory in order to create a new JDialog that is assigned to a DefaultSingleCDockable and then disposed when the relocator.drop is successful. The custom ScreenDockWindow returned to the ScreenDockWindowFactory never gets visible (as my custom dialog needs to handle everything).

The video looks quite nice. How did you code it in the end? You know, if you post the code I might even make a new tutorial out of it :slight_smile: