May 04 2009

Java Drag And Drop Closeable Colored Tab Control

Category: Java, Programmingzach @ 10:17 pm

I recently prototyped a drag-and-drop enabled tab control with closeable tabs and custom colors. I put together a driver to demo the control, which will serve as the basis of this article. You’ll find all the code for the control self contained in a single directory: /src/rlink/dndTabPanel. You can change the icons and images used in the tab control by replacing them in the /src/rlink/dndTabPanel/images directory. Copy the files (and the image directory) into your project and use the driver code as an example. The code is released under BSD, meaning you can do pretty much whatever you want with it. Questions? Contact Me or leave a comment at the bottom…

Supported Drag Image
Windows Yes Icon
Linux Yes Full Bitmap
Mac OS X Yes Full Bitmap

Download The Demo Jar

Get dirty with the full source and Eclipse project
By downloading you agree to the terms of the License Agreement
Price: Free DOWNLOAD



Here’s a screencast of the tab control in action:

Driver

The driver is very simple, and consists of 2 files: EntryPoint.java and DemoTabPanel.java. Pretty straight forward – creates the main frame, splitter pane, etc. and displays it. It also creates 2 DemoTabPanel instances, which are the heart of the driver. It contains a panel which uses the tab control. Here’s the contents of EntryPoint.java:

Open Unformatted Code In New Window

package rlink.application;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.SystemColor;
import java.util.Random;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSplitPane;

import rlink.controls.dndTabPanel.DnDTabPanel;
import rlink.controls.dndTabPanel.DnDTabPanelListener;
import rlink.controls.dndTabPanel.TabItem;
import rlink.controls.dndTabPanel.TabUtils;

public class EntryPoint 
{
	
	public static void main(String[] args)
	{
		try
		{
			// 1. Set up frame
			JFrame frame = new JFrame("Drag-N-Drop Tab Control Demo");
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			
			// 2. Set up tab panels
			JPanel left = new DemoTabPanel("Originally On Left");
			JPanel right = new DemoTabPanel("Originally On Right");
			
			// 3. Set up split pane
			JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right);
			splitPane.setResizeWeight(0.5);
			splitPane.setOneTouchExpandable(true);
			splitPane.setContinuousLayout(true);
			splitPane.setBackground(Color.gray);
			
			// 4. Add the split pane
			Container content = frame.getContentPane();
			content.add(splitPane, BorderLayout.CENTER);
	
			// 5. Show it
			frame.pack();
			frame.setSize(800, 600);
			frame.setVisible(true);
		}
		catch(Exception ex)
		{
			System.out.println("Failed: " + ex.getMessage());
		}
	}
}

Demo Tab Panel Functions

On line 45 (below) the function addTabs contains the intuitive addTab() function. This function does a couple of things: line 47 declares the colors for the tabs, line 64 will alternate between icon/no icon and long name/short name, line 75 randomly selects a tab so we start up with a different selection every time.

Gradient

One of the trickier features of the demo is the background gradient. The background gradient of the content area is actually not part of the tab control – I’ve added it to show how the TabListener interface can be used to take advantage of the tab events and TabItem data and manage content.

Content

Whenever a tab is selected, the content is retrieved from the TabItem.data member that was set on addTab(). Whenever a tab is removed, the content for it is also removed. Whenever a tab item changes, the panel is repainted to reflect the change.

Open Unformatted Code In New Window

package rlink.application;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.SystemColor;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.Random;

import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;

import rlink.controls.dndTabPanel.DnDTabPanel;
import rlink.controls.dndTabPanel.DnDTabPanelListener;
import rlink.controls.dndTabPanel.TabItem;
import rlink.controls.dndTabPanel.TabUtils;

class DemoTabPanel extends JPanel implements DnDTabPanelListener
{
	protected DnDTabPanel tabPanel = new DnDTabPanel();
	protected Component curTabPage = null;
	
	public DemoTabPanel(String prefix) throws Exception
	{	
		// 1. Set up the tab panel
		this.tabPanel.setBackground(SystemColor.gray);
		
		this.setBackground(Color.green);
		
		this.setLayout(new BorderLayout());
		
		this.tabPanel.addTabListener(this);
		
		this.add(this.tabPanel, BorderLayout.NORTH);
		
		// 2. Add the tabs
		this.addTabs(prefix);
	}
	
	protected void addTabs(String prefix) throws Exception
	{
		Color[] colors = new Color[]
		{
			new Color(0x193B54),
			new Color(0xB33B00),
			new Color(0xFFFF00),
			new Color(0x5D92BA),
			new Color(0x787878),
		};
		
		int tabCount = 5;

		for(int i = 0; i < tabCount; i++)
		{
			ImageIcon icon = null;
			
			String title = "Tab with a long name " + i;
			
			if(i % 2 == 0)
			{
				title = "Tab " + i;
				icon = TabUtils.getAppIcon("Tabs/tab" + i + ".png", 16);
			}
			
			this.tabPanel.addTab(i, title, icon, colors[i], new JLabel(prefix + " Tab " + i));
		}
		
		Random rand = new Random();
		
		this.tabPanel.setSelectedIndex(Math.abs(rand.nextInt() % tabCount));
	}

	/**
	 * Update the background whenever the tab changes since we're using
	 * the gradient
	 */
	public void onTabItemChanged(TabItem ti) 
	{	
		this.repaint();
	}

	/**
	 * Change the page whenever a new tab is selected
	 */
	public void onTabSelected(TabItem ti) 
	{	
		if(this.curTabPage != null)
		{
			this.remove(this.curTabPage);
		}
		
		this.curTabPage = (Component) ti.data;
		
		this.add(this.curTabPage, BorderLayout.CENTER);
		
		this.invalidate();
		this.doLayout();
		this.repaint();
	}
	
	public void onTabRemoved(TabItem ti)
	{
		if(this.curTabPage == null)
		{
			return;
		}
		
		if(this.curTabPage != ti.data)
		{
			return;
		}
		
		this.remove(this.curTabPage);
		
		this.curTabPage = null;
		
		this.invalidate();
		this.doLayout();
		this.repaint();
	}

	/**
	 * Paint the tab gradient into the background
	 */
	@Override
	public void paint(Graphics g) 
	{
		TabItem ti = this.tabPanel.getSelectedTab();
		
		if(ti != null)
		{
			GradientPaint gp = ti.getGradient();
			
			Graphics2D g2d = (Graphics2D) g;
			
			g2d.setPaint(gp);
			
			g.fillRect(0, 0, this.getWidth(), this.getHeight());
			
			for(Component c : this.getComponents())
			{
				c.paint(g2d);
			}
		}
		else
		{
			super.paint(g);
		}
	}
}

Adding Functionality

For the 90% case this control will work straight out of the box. If you require a custom drag data type, to change the control appearance, use DnD for IPC, etc., you’ll need to change the tab control source directly. The interface implementations are each in their own class, state is stored in DnDTabPanel. I don’t comment much, but that should be no problem – there are no hacks and nothing tricky about it. The drag and drop is a little involved, but DnD always is and if you’ve worked with it before in Java you shouldn’t have any big issues.

Get dirty with the full source and Eclipse project
By downloading you agree to the terms of the License Agreement
Price: Free DOWNLOAD

Bookmark and Share

Tags: , , ,

Leave a Reply