package org.geogebra.desktop.gui.util; import java.awt.Color; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; import java.awt.SystemColor; import javax.swing.Icon; import javax.swing.JTabbedPane; import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.plaf.TabbedPaneUI; import javax.swing.plaf.basic.BasicTabbedPaneUI; /** * Tabbed pane with icons and a minimalistic appearance. * * This tabbed pane will just use the special IconTabbedPaneUI class for it's * UI. It's required to reconstruct the UI object if updateUI() is called (e.g. * because the font size changed). * * @author Florian Sonner */ public class IconTabbedPane extends JTabbedPane { /** */ private static final long serialVersionUID = 1L; /** * Set the UI of this component to the IconTabbedPaneUI. */ public IconTabbedPane() { setUI(new IconTabbedPaneUI()); } /** * Ignore any non IconTabbedPaneUI objects. */ @Override public void setUI(TabbedPaneUI ui) { if (ui instanceof IconTabbedPaneUI) { super.setUI(ui); } } /** * Update the UI of this component. * * This will lead to an update of the fonts of the UI as just the font size * should change. */ @Override public void updateUI() { if (ui instanceof IconTabbedPaneUI) { ((IconTabbedPaneUI) getUI()).updateFont(); } } /** * Custom UI for the tabbed pane with icons. * * @author Florian Sonner */ class IconTabbedPaneUI extends BasicTabbedPaneUI { /** * The background color for tabs which are neither active not hovered. */ private Color bgColor; /** * The background color of active tabs (i.e. the content of this tab is * currently displayed). */ private Color bgActiveColor; /** * The background color of tabs the mouse is over at the moment. Will * not apply to active tabs. */ private Color bgHoverColor; /** * Initialization of default values. */ @Override protected void installDefaults() { super.installDefaults(); tabAreaInsets = new Insets(0, 15, 0, 15); contentBorderInsets = new Insets(3, 3, 3, 3); tabInsets = new Insets(10, 10, 10, 10); selectedTabPadInsets = new Insets(0, 0, 0, 0); bgColor = Color.white; bgActiveColor = new Color(193, 210, 238); bgHoverColor = new Color(224, 232, 246); } /** * Uninstall our custom defaults. */ @Override protected void uninstallDefaults() { super.uninstallDefaults(); bgColor = null; bgActiveColor = null; bgHoverColor = null; } /** * Update the font. */ public void updateFont() { LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background", "TabbedPane.foreground", "TabbedPane.font"); } /** * Paint the tab border. */ @Override protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { g.setColor(SystemColor.controlShadow); g.drawLine(x, y, x, y + h - 1); g.drawLine(x + w, y, x + w, y + h - 1); } /** * Paint the background of the tabs. */ @Override protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { g.setColor(isSelected ? bgActiveColor : (tabIndex == getRolloverTab() ? bgHoverColor : bgColor)); g.fillRect(x, y, w, h); } /** * Repaint the tabbed pane if the mouse is hovering a new tab. */ @Override protected void setRolloverTab(int index) { if (getRolloverTab() != index) { super.setRolloverTab(index); repaint(); } } /** * Fill the background with white. */ @Override protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) { g.setColor(Color.white); g.fillRect(0, 0, tabPane.getBounds().width, calculateTabAreaHeight( tabPlacement, runCount, maxTabHeight)); super.paintTabArea(g, tabPlacement, selectedIndex); } /** * Use a custom layout for the label (icon centered, text below icon). * * Copy 'n' paste from the original source of BasicTabbedPaneUI. */ @Override protected void layoutLabel(int tabPlacement, FontMetrics metrics, int tabIndex, String title, Icon icon, Rectangle tabRect, Rectangle iconRect, Rectangle textRect, boolean isSelected) { textRect.x = 0; textRect.y = 0; textRect.width = 0; textRect.height = 0; iconRect.x = 0; iconRect.y = 0; iconRect.width = 0; iconRect.height = 0; // -- just this has to be changed to change the layout of the tabs // -- SwingUtilities.layoutCompoundLabel(tabPane, metrics, title, icon, SwingConstants.CENTER, SwingConstants.CENTER, SwingConstants.BOTTOM, SwingConstants.CENTER, tabRect, iconRect, textRect, textIconGap); int shiftX = getTabLabelShiftX(tabPlacement, tabIndex, isSelected); int shiftY = getTabLabelShiftY(tabPlacement, tabIndex, isSelected); iconRect.x += shiftX; iconRect.y += shiftY; textRect.x += shiftX; textRect.y += shiftY; } /** * The tab should always have enough space for a 32x32 icon and the * label. */ @Override protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) { if (!isEnabledAt(tabIndex)) { return 0; } return fontHeight + 45; } /** * Reduce the tab width by 32 as the icon is not drawn in one line with * the text. */ @Override protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) { if (!isEnabledAt(tabIndex)) { return 0; } return super.calculateTabWidth(tabPlacement, tabIndex, metrics) - 32; } /** * Do not move the label if we select a tab (always return 0 as shift). */ @Override protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) { return 0; } /** * Paint the top border. */ @Override protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { g.setColor(SystemColor.controlDkShadow); g.drawLine(x, y, x + w, y); g.setColor(SystemColor.controlLtHighlight); g.drawLine(x, y + 1, x + w, y + 1); } @Override protected void paintFocusIndicator(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect, boolean isSelected) { /* paint nothing.. */ } @Override protected void paintContentBorderRightEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { /* paint nothing */ } @Override protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { /* paint nothing */ } @Override protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { /* paint nothing */ } } }