/* Author: Maple * Feb. 2 2026 * */ package org.openautonomousconnection.oacswing.component; import lombok.NonNull; import org.openautonomousconnection.oacswing.component.design.DesignManager; import javax.swing.*; import javax.swing.plaf.basic.BasicTabbedPaneUI; import java.awt.*; public class OACTabbedPane extends JTabbedPane implements OACComponent { private boolean selectionSyncInstalled; public OACTabbedPane() { super(); } public OACTabbedPane(int tabPlacement) { super(tabPlacement); } public OACTabbedPane(int tabPlacement, int tabLayoutPolicy) { super(tabPlacement, tabLayoutPolicy); } @Override public void init() { applyDesignColors(); } @Override public void updateUI() { super.updateUI(); applyDesignColors(); } private void applyDesignColors() { Color bg = DesignManager.resolveBackground(OACTabbedPane.class, getBackground()); Color fg = DesignManager.resolveForeground(OACTabbedPane.class, getForeground()); Color selectedBg = DesignManager.resolveHovered(OACButton.class, bg.darker()); Color selectedFg = fg; Color border = DesignManager.resolveBorderColor(bg.darker()); setOpaque(true); setBackground(bg); setForeground(fg); setUI(new DesignTabbedPaneUI(bg, selectedBg, border)); applyTabItemColors(bg, fg, selectedBg, selectedFg); installSelectionSyncIfNeeded(); } private void applyTabItemColors(Color bg, Color fg, Color selectedBg, Color selectedFg) { int selectedIndex = getSelectedIndex(); for (int i = 0; i < getTabCount(); i++) { boolean selected = i == selectedIndex; setBackgroundAt(i, selected ? selectedBg : bg); setForegroundAt(i, selected ? selectedFg : fg); } repaint(); } private void installSelectionSyncIfNeeded() { if (selectionSyncInstalled) { return; } addChangeListener(e -> { Color bg = DesignManager.resolveBackground(OACTabbedPane.class, getBackground()); Color fg = DesignManager.resolveForeground(OACTabbedPane.class, getForeground()); Color selectedBg = DesignManager.resolveHovered(OACButton.class, bg.darker()); applyTabItemColors(bg, fg, selectedBg, fg); }); selectionSyncInstalled = true; } private static final class DesignTabbedPaneUI extends BasicTabbedPaneUI { private final Color background; private final Color selectedBackground; private final Color borderColor; private DesignTabbedPaneUI(Color background, Color selectedBackground, Color borderColor) { this.background = background; this.selectedBackground = selectedBackground; this.borderColor = borderColor; } @Override protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { g.setColor(isSelected ? selectedBackground : background); g.fillRect(x, y, w, h); } @Override protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { g.setColor(borderColor); g.drawRect(x, y, w, h); } @Override protected void paintFocusIndicator(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect, boolean isSelected) { // no-op to avoid bright LAF focus ring } @Override protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) { int width = tabPane.getWidth(); int height = tabPane.getHeight(); Insets insets = tabPane.getInsets(); int x = insets.left; int y = insets.top; int w = width - insets.right - insets.left; int h = height - insets.top - insets.bottom; switch (tabPlacement) { case LEFT -> { x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); w -= (x - insets.left); } case RIGHT -> w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); case BOTTOM -> h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); default -> { y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); h -= (y - insets.top); } } g.setColor(background); g.fillRect(x, y, w, h); g.setColor(borderColor); g.drawRect(x, y, Math.max(0, w - 1), Math.max(0, h - 1)); } } @Override public void addTab(String title, Component component) { this.initOther(component); super.addTab(title, component); applyDesignColors(); } @Override public void addTab(String title, Icon icon, Component component) { this.initOther(component); super.addTab(title, icon, component); applyDesignColors(); } @Override public void addTab(String title, Icon icon, Component component, String tip) { this.initOther(component); super.addTab(title, icon, component, tip); applyDesignColors(); } @Override public Component add(Component comp) { this.initOther(comp); return super.add(comp); } @Override public Component add(Component comp, int index) { this.initOther(comp); return super.add(comp, index); } @Override public void add(@NonNull Component comp, Object constraints) { this.initOther(comp); super.add(comp, constraints); } @Override public Component add(String name, Component comp) { this.initOther(comp); return super.add(name, comp); } @Override public void add(Component comp, Object constraints, int index) { this.initOther(comp); super.add(comp, constraints, index); } }