CustomMetalTabbedPaneUI.java

/*******************************************************************************
 * Copyhacked (H) 2012-2020.
 * This program and the accompanying materials
 * are made available under no term at all, use it like
 * you want, but share and discuss about it
 * every time possible with every body.
 *
 * Contributors:
 *      ron190 at ymail dot com - initial implementation
 *******************************************************************************/
package com.jsql.view.swing.ui;

import com.jsql.view.swing.util.UiUtil;

import java.awt.*;
import java.awt.geom.GeneralPath;

/**
 * Tab UI to remove inner borders on empty tabbedpane and force header height on
 * Linux.
 */
public class CustomMetalTabbedPaneUI extends BorderlessTabButtonUI {
    
    private static final float ADJ2 = 0f;
    
    private static final Color TAB_BACKGROUND = UiUtil.COLOR_DEFAULT_BACKGROUND;
    private static final Color TAB_BORDER = UiUtil.COLOR_COMPONENT_BORDER;
    
    @Override
    protected int calculateMaxTabHeight(int tabPlacement) {
        return 22;
    }

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

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

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

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

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

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

    @Override
    protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) {

        int tabCount = this.tabPane.getTabCount();

        var iconRect = new Rectangle();
        var textRect = new Rectangle();
        Rectangle clipRect = g.getClipBounds();

        // copied from BasicTabbedPaneUI#paintTabArea(...)
        for (int i = this.runCount - 1 ; i >= 0 ; i--) {
            
            int start = this.tabRuns[i];
            int next = this.tabRuns[(i == this.runCount - 1) ? 0 : i + 1];
            int end = next == 0 ? tabCount - 1 : next - 1;
            
            // https://stackoverflow.com/questions/41566659/tabs-rendering-order-in-custom-jtabbedpane
            for (int j = end; j >= start; j--) {
                
                if (j != selectedIndex && this.rects[j].intersects(clipRect)) {
                    
                    this.paintTab(g, tabPlacement, this.rects, j, iconRect, textRect);
                }
            }
        }
        
        if (selectedIndex >= 0 && this.rects[selectedIndex].intersects(clipRect)) {
            
            this.paintTab(g, tabPlacement, this.rects, selectedIndex, iconRect, textRect);
        }
    }

    @Override
    protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
        
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        var textShiftOffset = 0f;
        var trapezoid = new GeneralPath();
        trapezoid.moveTo(x - ADJ2, (float) y + h);
        trapezoid.lineTo(x + ADJ2, y + textShiftOffset);
        trapezoid.lineTo(x + w - ADJ2, y + textShiftOffset);
        trapezoid.lineTo(x + w + ADJ2, (float) y + h);

        g2.setColor(isSelected ? UiUtil.COLOR_FOCUS_GAINED : TAB_BACKGROUND);
        g2.fill(trapezoid);

        g2.setColor(TAB_BORDER);
        g2.draw(trapezoid);

        g2.dispose();
    }
}