components/visual-panels/core/src/java/util/com/oracle/solaris/vp/util/swing/SleekTabbedPaneUI.java
author Dan Labrecque <dan.labrecque@oracle.com>
Thu, 24 May 2012 04:16:47 -0400
changeset 827 0944d8c0158b
permissions -rw-r--r--
7169052 Integrate Visual Panels into Userland

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
 */

package com.oracle.solaris.vp.util.swing;

import java.awt.*;
import java.awt.geom.Area;
import javax.swing.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.BasicTabbedPaneUI;

public class SleekTabbedPaneUI extends BasicTabbedPaneUI {
    //
    // Static data
    //

    // Default color of the border
    static final Color _COLOR = new Color(163, 184, 203);

    // Default color of the selected tab
    static final Color _BORDER_COLOR = Color.DARK_GRAY;

    // Default color of the inactive tabs
    private static final Color _INACTIVE_COLOR =
	UIManager.getColor("TabbedPane.background");

    // Gap between tabs
    private static final int TAB_GAP = 2;

    // Arc of the tab corners
    private static final int TAB_ARC = 8;

    // Space around the contents of each tab, within the tab
    private static final Insets TAB_INSETS = new Insets(2, 10, 0, 10);

    // Additional space around selected tab
    private static final Insets SELECTED_TAB_INSETS = new Insets(0, 0, 0, 0);

    // Space around tab group
    private static final int TAB_AREA_GAP = TAB_GAP;
    private static final Insets TAB_AREA_INSETS_NO_BORDER =
	new Insets(0, TAB_AREA_GAP, 0, TAB_AREA_GAP);
    private static final Insets TAB_AREA_INSETS_BORDER =
	new Insets(0, 0, 0, 0);

    // Height of the solid bar running underneath tabs, above the content area
    private static final int CONTENT_TOP_HEIGHT = 5;
    private static final Insets CONTENT_INSETS_NO_BORDER =
	new Insets(CONTENT_TOP_HEIGHT, 0, 0, 0);
    private static final Insets CONTENT_INSETS_BORDER =
	new Insets(CONTENT_TOP_HEIGHT, 1, 1, 1);

    //
    // Instance data
    //

    private Color color = _COLOR;
    private Color borderColor = _BORDER_COLOR;
    private Color contentBorderColor = _BORDER_COLOR;
    private Color inactiveColor = _INACTIVE_COLOR;

    private boolean drawContentBorder = false;

    //
    // BasicTabbedPaneUI methods
    //

    @Override
    protected int calculateTabWidth(
	int tabPlacement, int tabIndex, FontMetrics metrics) {

	// Uniform tab widths
	int width = 0;
	for (int i = 0, n = tabPane.getTabCount(); i < n; i++) {
	    width = Math.max(width,
		super.calculateTabWidth(tabPlacement, i, metrics));
	}

	return width + TAB_GAP + 1;
    }

    @Override
    protected Insets getContentBorderInsets(int tabPlacement) {
	return getDrawContentBorder() ?
	    CONTENT_INSETS_BORDER : CONTENT_INSETS_NO_BORDER;
    }

    @Override
    protected int getTabLabelShiftY(
	int tabPlacement, int tabIndex, boolean isSelected) {

	// Don't offset the label of the selected tab
	return 0;
    }

    @Override
    protected void installDefaults() {
	super.installDefaults();

	// Let the JTabbedPane determine it's own opacity
        UIManager.put("TabbedPane.contentOpaque", false);

	tabAreaInsets = getDrawContentBorder() ?
	    TAB_AREA_INSETS_BORDER : TAB_AREA_INSETS_NO_BORDER;

	tabInsets = TAB_INSETS;
	selectedTabPadInsets = SELECTED_TAB_INSETS;

	// Embolden font
	tabPane.setFont(tabPane.getFont().deriveFont(Font.BOLD));
    }

    @Override
    protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
	int selectedIndex, int x, int y, int w, int h) {

	if (getDrawContentBorder()) {
	    Graphics2D g2 = (Graphics2D)g;
	    g2.setPaint(getContentBorderColor());
	    g2.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
	}
    }

    @Override
    protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
	int selectedIndex, int x, int y, int w, int h) {

	if (getDrawContentBorder()) {
	    Graphics2D g2 = (Graphics2D)g;
	    g2.setPaint(getContentBorderColor());
	    g2.drawLine(x, y, x, y + h - 1);
	}
    }

    @Override
    protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
	int selectedIndex, int x, int y, int w, int h) {

	if (getDrawContentBorder()) {
	    Graphics2D g2 = (Graphics2D)g;
	    g2.setPaint(getContentBorderColor());
	    g2.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
	}
    }

    @Override
    protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
	int selectedIndex, int x, int y, int w, int h) {

        Graphics2D g2 = (Graphics2D)g;

	g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
	    RenderingHints.VALUE_ANTIALIAS_ON);

	h = CONTENT_TOP_HEIGHT;

	if (h > 0) {
	    Color color = getColor();

	    // Paint the background
	    g2.setPaint(color);
	    g2.fillRect(x, y, w, h - 1);

	    if (h > 2) {
		// Paint a light gradient
		g2.setComposite(AlphaComposite.getInstance(
		    AlphaComposite.SRC_OVER, .25f));
		Paint paint = new GradientPaint(
		    x, y, color, x, y + h - 1, Color.white, false);
		g2.setPaint(paint);
		g2.fillRect(x, y, w, h - 1);

		// Reset alpha
		g2.setPaintMode();
	    }

	    g2.setColor(getBorderColor());

	    // Draw top line
	    if (selectedIndex >= 0) {
		Rectangle selectedRect = getTabBounds(selectedIndex, calcRect);
		int sWidth = selectedRect.width - TAB_GAP - 1;

		g2.drawLine(x, y, selectedRect.x, y);
		g2.drawLine(selectedRect.x + sWidth, y, x + w - 1, y);
	    } else {
		g2.drawLine(x, y, x + w - 1, y);
	    }

	    if (h > 1) {
		// Draw bottom line
		g2.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
	    }
	}
    }

    @Override
    protected void paintFocusIndicator(Graphics g, int tabPlacement,
	Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect,
	boolean isSelected) {

        Rectangle tabRect = rects[tabIndex];
	Rectangle tmp = (Rectangle)tabRect.clone();

	tmp.width -= TAB_GAP;
	rects[tabIndex] = tmp;

	super.paintFocusIndicator(
	    g, tabPlacement, rects, tabIndex, iconRect, textRect, isSelected);

	rects[tabIndex] = tabRect;
    }

    @Override
    protected void paintIcon(Graphics g, int tabPlacement,
	int tabIndex, Icon icon, Rectangle iconRect, boolean isSelected) {
        if (icon != null) {
	    Rectangle tmp = (Rectangle)iconRect.clone();

	    // The superclass centers the icon, so restore insets
	    tmp.x -= (tabInsets.right - tabInsets.left) / 2;
	    tmp.y -= (tabInsets.bottom - tabInsets.top) / 2;

	    // The superclass centers the icon, so account for gap
	    tmp.x -= (TAB_GAP + 1) / 2;

	    super.paintIcon(g, tabPlacement, tabIndex, icon, tmp, isSelected);
        }
    }

    @Override
    protected void paintTabBackground(Graphics g, int tabPlacement,
	int tabIndex, int x, int y, int w, int h, boolean isSelected) {

        Graphics2D g2 = (Graphics2D)g;

	Color color = isSelected ? getColor() : getInactiveColor();

	Shape clip = g2.getClip();
	Area aClip = new Area(clip);
	aClip.intersect(new Area(new Rectangle(x, y, w, h)));
	g2.setClip(aClip);

	// Paint the background of the tab
	g2.setColor(color);
	g2.fillRoundRect(x, y, w - TAB_GAP - 1, (3 * h) / 2, TAB_ARC, TAB_ARC);

	// Paint the light (top) gradient of the tab
	g2.setComposite(AlphaComposite.getInstance(
	    AlphaComposite.SRC_OVER, .8f));
	Paint paint = new GradientPaint(
	    x, y, Color.WHITE, x, y + ((float)h / 2f), color, false);
	g2.setPaint(paint);
	g2.fillRoundRect(x, y, w - TAB_GAP - 1, (3 * h) / 2, TAB_ARC, TAB_ARC);

	g2.setClip(clip);
    }

    @Override
    protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
	int x, int y, int w, int h, boolean isSelected) {

        Graphics2D g2 = (Graphics2D)g;

        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
	    RenderingHints.VALUE_ANTIALIAS_ON);

	Shape clip = g2.getClip();
	Area aClip = new Area(clip);
	aClip.intersect(new Area(new Rectangle(x, y, w, h)));
	g2.setClip(aClip);

	g2.setColor(getBorderColor());
	g2.drawRoundRect(x, y, w - TAB_GAP - 1, (3 * h) / 2, TAB_ARC, TAB_ARC);

	// Paint a shadow on the sides
	g2.setComposite(AlphaComposite.getInstance(
	    AlphaComposite.SRC_OVER, .25f));
	g2.setColor(Color.BLACK);
	g2.drawRoundRect(
	    x + 1, y, w - TAB_GAP - 3, (3 * h) / 2, TAB_ARC, TAB_ARC);

	// Reset alpha
	g2.setPaintMode();
	g2.setClip(clip);
    }

    @Override
    protected void paintText(Graphics g, int tabPlacement, Font font,
	FontMetrics metrics, int tabIndex, String title, Rectangle textRect,
	boolean isSelected) {

	Rectangle tmp = (Rectangle)textRect.clone();

	// The superclass centers the text, so restore insets
	tmp.x -= (tabInsets.right - tabInsets.left) / 2;
	tmp.y -= (tabInsets.bottom - tabInsets.top) / 2;

	// The superclass centers the text, so account for gap
	tmp.x -= (TAB_GAP + 1) / 2;

	super.paintText(g, tabPlacement, font, metrics,
	    tabIndex, title, tmp, isSelected);
    }

    //
    // SleekTabbedPaneUI methods
    //

    public Color getBorderColor() {
	return borderColor;
    }

    public Color getContentBorderColor() {
	return contentBorderColor;
    }

    public Color getColor() {
	return color;
    }

    public boolean getDrawContentBorder() {
	return drawContentBorder;
    }

    public Color getInactiveColor() {
	return inactiveColor;
    }

    public void setBorderColor(Color borderColor) {
	this.borderColor = borderColor;
    }

    public void setContentBorderColor(Color contentBorderColor) {
	this.contentBorderColor = contentBorderColor;
    }

    public void setColor(Color color) {
	this.color = color;
    }

    public void setDrawContentBorder(boolean drawContentBorder) {
	this.drawContentBorder = drawContentBorder;
    }

    public void setInactiveColor(Color inactiveColor) {
	this.inactiveColor = inactiveColor;
    }

    //
    // Static methods
    //

    public static ComponentUI createUI(JComponent c) {
	return new SleekTabbedPaneUI();
    }

    public static void main(String[] args) {
	Class[] uis = {
	    null, SleekTabbedPaneUI.class
	};

	int[] placements = {
	    JTabbedPane.TOP,
	    JTabbedPane.LEFT,
	};

	for (int k = 0; k < uis.length; k++) {
	    for (int i = 0; i < placements.length; i++) {
		JTabbedPane tabs = new JTabbedPane();

		if (uis[k] != null) {
		    try {
			tabs.setUI((TabbedPaneUI)uis[k].newInstance());
		    } catch (Throwable e) {
			e.printStackTrace();
		    }
		}

		tabs.setTabPlacement(placements[i]);

		for (int j = 0; j < 5; j++) {
		    tabs.addTab("Tab " + j, new JLabel("Label " + j));
		}

		JFrame frame = new JFrame();
		Container c = frame.getContentPane();
		c.setLayout(new BorderLayout());
		c.add(tabs, BorderLayout.CENTER);
		frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		frame.pack();
		frame.setVisible(true);

		frame.setSize(new Dimension(300, 300));
	    }
	}
    }
}