diff --git a/darklaf.iml b/darklaf.iml deleted file mode 100644 index 6b47d550..00000000 --- a/darklaf.iml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/java/com/weis/darklaf/DarkLaf.java b/src/main/java/com/weis/darklaf/DarkLaf.java index 9c70d6b0..5ee65569 100644 --- a/src/main/java/com/weis/darklaf/DarkLaf.java +++ b/src/main/java/com/weis/darklaf/DarkLaf.java @@ -161,7 +161,7 @@ public class DarkLaf extends BasicLookAndFeel { Properties properties = LafUtil.loadProperties(this); final HashMap darculaGlobalSettings = new HashMap<>(); - final String prefix = getPrefix(); + final String prefix = getPrefix() + '.'; for (final String key : properties.stringPropertyNames()) { if (key.startsWith(prefix)) { darculaGlobalSettings diff --git a/src/main/java/com/weis/darklaf/LafManager.java b/src/main/java/com/weis/darklaf/LafManager.java index 7ea4f823..bf06ed03 100644 --- a/src/main/java/com/weis/darklaf/LafManager.java +++ b/src/main/java/com/weis/darklaf/LafManager.java @@ -16,7 +16,7 @@ import java.awt.*; */ public final class LafManager { - private static Theme currentLaf; + private static Theme currentLaf = Theme.Dark; static void setCurrentLaf(final Theme currentLaf) { LafManager.currentLaf = currentLaf; diff --git a/src/main/java/com/weis/darklaf/components/ScrollPopupMenu.java b/src/main/java/com/weis/darklaf/components/ScrollPopupMenu.java index 8ad109aa..7ec3165a 100644 --- a/src/main/java/com/weis/darklaf/components/ScrollPopupMenu.java +++ b/src/main/java/com/weis/darklaf/components/ScrollPopupMenu.java @@ -34,7 +34,7 @@ public class ScrollPopupMenu extends JPopupMenu { } }; - private final int maxHeight; + private int maxHeight; private final JPanel contentPane; private final JScrollPane scrollPane; private JWindow popWin; @@ -52,6 +52,10 @@ public class ScrollPopupMenu extends JPopupMenu { addMenuKeyListener(menuKeyListener); } + public void setMaxHeight(final int maxHeight) { + this.maxHeight = maxHeight; + } + @NotNull private OverlayScrollPane createScrollPane() { var view = new JPanel(new BorderLayout()); diff --git a/src/main/java/com/weis/darklaf/icons/IconLoader.java b/src/main/java/com/weis/darklaf/icons/IconLoader.java index 3a333aef..78719329 100644 --- a/src/main/java/com/weis/darklaf/icons/IconLoader.java +++ b/src/main/java/com/weis/darklaf/icons/IconLoader.java @@ -159,7 +159,7 @@ public final class IconLoader { if (imgURL != null) { return new ImageIcon(imgURL, description); } else { - System.err.println("Couldn't find file: " + path); + LOGGER.severe("Could not find icon file: '" + path + "'"); return null; } } diff --git a/src/main/java/com/weis/darklaf/ui/button/DarkButtonBorder.java b/src/main/java/com/weis/darklaf/ui/button/DarkButtonBorder.java index 34be610d..770969ae 100644 --- a/src/main/java/com/weis/darklaf/ui/button/DarkButtonBorder.java +++ b/src/main/java/com/weis/darklaf/ui/button/DarkButtonBorder.java @@ -28,7 +28,7 @@ public class DarkButtonBorder implements Border, UIResource { @Override public void paintBorder(final Component c, @NotNull final Graphics g, final int x, final int y, final int width, final int height) { - if (DarkButtonUI.isShadowVariant(c)) { + if (DarkButtonUI.isShadowVariant(c) || DarkButtonUI.isLabelButton(c)) { return; } Graphics2D g2 = (Graphics2D) g; diff --git a/src/main/java/com/weis/darklaf/ui/button/DarkButtonUI.java b/src/main/java/com/weis/darklaf/ui/button/DarkButtonUI.java index fd80753c..f2773a93 100644 --- a/src/main/java/com/weis/darklaf/ui/button/DarkButtonUI.java +++ b/src/main/java/com/weis/darklaf/ui/button/DarkButtonUI.java @@ -12,8 +12,12 @@ import javax.swing.*; import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicButtonListener; import javax.swing.plaf.basic.BasicButtonUI; +import javax.swing.plaf.basic.BasicHTML; +import javax.swing.text.View; import java.awt.*; +import java.awt.geom.RoundRectangle2D; /** * Custom adaption of {@link DarculaButtonUI}. @@ -25,6 +29,11 @@ public class DarkButtonUI extends BasicButtonUI { public static final int SQUARE_ARC_SIZE = 3; public static final int ARC_SIZE = 5; + protected static final Rectangle viewRect = new Rectangle(); + protected static final Rectangle textRect = new Rectangle(); + protected static final Rectangle iconRect = new Rectangle(); + protected AbstractButton button; + private BasicButtonListener listener; @NotNull @Contract(value = "_ -> new", pure = true) @@ -33,9 +42,74 @@ public class DarkButtonUI extends BasicButtonUI { } @Override - public void paint(final Graphics g, @NotNull final JComponent c) { + public void installUI(final JComponent c) { + button = (AbstractButton) c; + super.installUI(c); + } + + @Override + public boolean contains(@NotNull final JComponent c, final int x, final int y) { + if (!(x >= 0 && x <= c.getWidth() && y >= 0 && y <= c.getHeight())) return false; + int bs = DarkButtonBorder.BORDER_SIZE; + return new RoundRectangle2D.Float(bs, bs, c.getWidth() - 2 * bs, c.getWidth() - 2 * bs, getArcSize(c), + getArcSize(c)).contains(x, y); + } + + @Override + public void paint(final Graphics g, final JComponent c) { + GraphicsContext config = new GraphicsContext(g); + AbstractButton b = (AbstractButton) c; + paintButton(g, c); + + String text = layout(b, c, SwingUtilities2.getFontMetrics(b, g), + b.getWidth(), b.getHeight()); + + paintIcon(g, b, c); + paintText(g, b, c, text); + config.restore(); + } + + protected String layout(@NotNull final AbstractButton b, final JComponent c, final FontMetrics fm, + final int width, final int height) { + Insets i = b.getInsets(); + viewRect.x = i.left; + viewRect.y = i.top; + viewRect.width = width - (i.right + viewRect.x); + viewRect.height = height - (i.bottom + viewRect.y); + + textRect.x = textRect.y = textRect.width = textRect.height = 0; + iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0; + + // layout the text and icon + return SwingUtilities.layoutCompoundLabel( + b, fm, b.getText(), b.getIcon(), + b.getVerticalAlignment(), b.getHorizontalAlignment(), + b.getVerticalTextPosition(), b.getHorizontalTextPosition(), + viewRect, iconRect, textRect, + b.getText() == null ? 0 : b.getIconTextGap()); + } + + protected void paintText(final Graphics g, final AbstractButton b, final JComponent c, final String text) { + GraphicsUtil.setupAAPainting(g); + if (text != null && !text.equals("")) { + View v = (View) c.getClientProperty(BasicHTML.propertyKey); + if (v != null) { + v.paint(g, textRect); + } else { + paintText(g, b, textRect, text); + } + } + } + + protected void paintIcon(final Graphics g, @NotNull final AbstractButton b, final JComponent c) { + if (b.getIcon() != null) { + paintIcon(g, c, iconRect); + } + } + + + protected void paintButton(final Graphics g, @NotNull final JComponent c) { Graphics2D g2 = (Graphics2D) g; - GraphicsContext config = new GraphicsContext(g2); int borderSize = DarkButtonBorder.BORDER_SIZE; if (shouldDrawBackground(c)) { int arc = getArcSize(c); @@ -58,8 +132,6 @@ public class DarkButtonUI extends BasicButtonUI { } } } - config.restore(); - super.paint(g2, c); } protected int getArcSize(final JComponent c) { @@ -128,9 +200,10 @@ public class DarkButtonUI extends BasicButtonUI { } private boolean shouldDrawBackground(@NotNull final JComponent c) { + if (isLabelButton(c)) return false; AbstractButton button = (AbstractButton) c; Border border = c.getBorder(); - return c.isEnabled() && border != null && button.isContentAreaFilled() && !(c instanceof JToggleButton); + return c.isEnabled() && border != null && button.isContentAreaFilled(); } @Contract("null -> false") @@ -150,6 +223,12 @@ public class DarkButtonUI extends BasicButtonUI { && Boolean.TRUE.equals(((JButton) c).getClientProperty("JButton.forceRoundCorner")); } + @Contract("null -> false") + public static boolean isLabelButton(final Component c) { + return c instanceof JButton + && "onlyLabel".equals(((JButton) c).getClientProperty("JButton.variant")); + } + protected Color getForeground(@NotNull final AbstractButton button) { Color fg = button.getForeground(); if (fg instanceof UIResource && button instanceof JButton && ((JButton) button).isDefaultButton()) { diff --git a/src/main/java/com/weis/darklaf/ui/button/DarkToggleButtonUI.java b/src/main/java/com/weis/darklaf/ui/button/DarkToggleButtonUI.java index a066b97c..0ee53fb2 100644 --- a/src/main/java/com/weis/darklaf/ui/button/DarkToggleButtonUI.java +++ b/src/main/java/com/weis/darklaf/ui/button/DarkToggleButtonUI.java @@ -1,26 +1,176 @@ package com.weis.darklaf.ui.button; +import com.weis.darklaf.util.DarkUIUtil; +import com.weis.darklaf.util.GraphicsContext; +import com.weis.darklaf.util.GraphicsUtil; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import sun.swing.SwingUtilities2; import javax.swing.*; import javax.swing.plaf.ComponentUI; import java.awt.*; +import java.awt.geom.Ellipse2D; +import java.awt.geom.RoundRectangle2D; +import java.beans.PropertyChangeListener; public class DarkToggleButtonUI extends DarkButtonUI { + private static final int SLIDER_HEIGHT = 17; + private static final int SLIDER_WIDTH = 35; + private static Rectangle rect = new Rectangle(); + + private final PropertyChangeListener propertyChangeListener = evt -> { + if ("ToggleButton.variant".equals(evt.getPropertyName())) { + var oldVal = evt.getOldValue(); + var newVal = evt.getNewValue(); + if (oldVal != null && oldVal.equals(newVal)) { + return; + } + if ("slider".equals(newVal)) { + button.setBorderPainted(false); + } else { + button.setBorderPainted(true); + } + } + }; + @NotNull @Contract(value = "_ -> new", pure = true) public static ComponentUI createUI(final JComponent c) { return new DarkToggleButtonUI(); } + @Override + protected void installListeners(final AbstractButton b) { + super.installListeners(b); + button.addPropertyChangeListener(propertyChangeListener); + } + + @Override + protected void uninstallListeners(final AbstractButton b) { + super.uninstallListeners(b); + button.removePropertyChangeListener(propertyChangeListener); + } + + @Override + public boolean contains(@NotNull final JComponent c, final int x, final int y) { + if (!(x >= 0 && x <= c.getWidth() && y >= 0 && y <= c.getHeight())) return false; + var bounds = getSliderBounds(c); + return new RoundRectangle2D.Float(bounds.x, bounds.y, bounds.width, bounds.height, + bounds.height, bounds.height).contains(x,y); + } + + @Override + public void paint(final Graphics g, @NotNull final JComponent c) { + if (isSlider(c)) { + GraphicsContext config = GraphicsUtil.setupStrokePainting(g); + AbstractButton b = (AbstractButton) c; + String text = layout(b, c, SwingUtilities2.getFontMetrics(b, g), + b.getWidth(), b.getHeight()); + + paintSlider((Graphics2D) g, b); + paintIcon(g, b, c); + paintText(g, b, c, text); + config.restore(); + } else { + super.paint(g, c); + } + } + + private void paintSlider(@NotNull final Graphics2D g, final AbstractButton c) { + var bounds = getSliderBounds(c); + g.translate(bounds.x, bounds.y); + Shape slider = new RoundRectangle2D.Float(0, 0, bounds.width, bounds.height, + bounds.height, bounds.height); + g.setColor(getBackgroundColor(c)); + g.fill(slider); + g.setColor(getToggleBorderColor(c)); + g.draw(slider); + if (c.hasFocus()) { + var config = new GraphicsContext(g); + g.setComposite(DarkUIUtil.ALPHA_COMPOSITE); + g.translate(-2, -2); + DarkUIUtil.paintFocusBorder(g, bounds.width + 4, bounds.height + 4, + (float) (bounds.height / 2.0) + 2, true); + g.translate(2, 2); + config.restore(); + } + g.setColor(getSliderColor(c)); + if (c.isSelected()) { + g.fill(new Ellipse2D.Float( + bounds.width - bounds.height + 1, 1, bounds.height - 1.5f, bounds.height - 1.5f)); + } else { + g.fill(new Ellipse2D.Float(1, 1, bounds.height - 1.5f, bounds.height - 1.5f)); + } + g.translate(-bounds.x, -bounds.y); + } + + private static Color getSliderColor(@NotNull final AbstractButton b) { + return b.isEnabled() ? UIManager.getColor("ToggleButton.sliderColor") + : UIManager.getColor("ToggleButton.disabledSliderColor"); + } + + private static Color getToggleBorderColor(@NotNull final AbstractButton b) { + return b.isEnabled() ? UIManager.getColor("ToggleButton.sliderBorderColor") + : UIManager.getColor("ToggleButton.disabledSliderBorderColor"); + } + + + public Dimension getPreferredSize(final JComponent c) { + Dimension d = super.getPreferredSize(c); + d.width += SLIDER_WIDTH + DarkButtonBorder.BORDER_SIZE; + return d; + } + + @Override + protected String layout(@NotNull final AbstractButton b, final JComponent c, + final FontMetrics fm, final int width, final int height) { + if (isSlider(c)) { + Insets i = b.getInsets(); + var bounds = getSliderBounds(c); + viewRect.x = bounds.x + bounds.width + DarkButtonBorder.BORDER_SIZE; + viewRect.y = i.top; + viewRect.width = width - (i.right + viewRect.x); + viewRect.height = height - (i.bottom + viewRect.y); + + textRect.x = textRect.y = textRect.width = textRect.height = 0; + iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0; + + // layout the text and icon + return SwingUtilities.layoutCompoundLabel( + b, fm, b.getText(), b.getIcon(), + b.getVerticalAlignment(), b.getHorizontalAlignment(), + b.getVerticalTextPosition(), b.getHorizontalTextPosition(), + viewRect, iconRect, textRect, + b.getText() == null ? 0 : b.getIconTextGap()); + } else { + return super.layout(b, c, fm, width, height); + } + } + + @NotNull + private Rectangle getSliderBounds(@NotNull final JComponent c) { + int x = DarkButtonBorder.BORDER_SIZE; + int y = (c.getHeight() - SLIDER_HEIGHT) / 2; + rect.x = x; + rect.y = y; + rect.width = SLIDER_WIDTH; + rect.height = SLIDER_HEIGHT; + return rect; + } + + @Contract("null -> false") + private static boolean isSlider(final JComponent c) { + return c instanceof JToggleButton + && "slider".equals(c.getClientProperty("ToggleButton.variant")); + } protected Color getBackgroundColor(@NotNull final JComponent c) { - return c.isEnabled() || (c instanceof JToggleButton && ((JToggleButton) c).isSelected()) - ? (c instanceof JButton && (((JButton) c).isDefaultButton())) - ? UIManager.getColor("Button.darcula.defaultFillColor") - : UIManager.getColor("Button.darcula.activeFillColor") - : UIManager.getColor("Button.darcula.inactiveFillColor"); + if ((c instanceof JToggleButton && ((JToggleButton) c).isSelected())) { + return UIManager.getColor("Button.darcula.activeFillColor"); + } else { + return UIManager.getColor("Button.darcula.inactiveFillColor"); + } } } diff --git a/src/main/java/com/weis/darklaf/ui/scrollpane/DarkScrollBarUI.java b/src/main/java/com/weis/darklaf/ui/scrollpane/DarkScrollBarUI.java index 38d45303..5c4c044d 100644 --- a/src/main/java/com/weis/darklaf/ui/scrollpane/DarkScrollBarUI.java +++ b/src/main/java/com/weis/darklaf/ui/scrollpane/DarkScrollBarUI.java @@ -42,8 +42,8 @@ public class DarkScrollBarUI extends BasicScrollBarUI { && !((JScrollPane) scrollbar.getParent()).isWheelScrollingEnabled()) { return; } - if (scrollbar.getOrientation() == VERTICAL && e.getModifiersEx() == 0 - || scrollbar.getOrientation() == HORIZONTAL && e.getModifiersEx() == KeyEvent.SHIFT_DOWN_MASK) { + if (scrollbar.getOrientation() == VERTICAL && !e.isShiftDown() + || scrollbar.getOrientation() == HORIZONTAL && e.isShiftDown()) { scrollbar.setValueIsAdjusting(true); if (scrollbar.getParent() instanceof JScrollPane) { doScroll(scrollbar, ((JScrollPane) scrollbar.getParent()).getViewport(), e, @@ -448,10 +448,10 @@ public class DarkScrollBarUI extends BasicScrollBarUI { public static void doScroll(@NotNull final JScrollBar toScroll, final JViewport vp, @NotNull final MouseWheelEvent e, final boolean leftToRight) { int direction = e.getWheelRotation() < 0 ? -1 : 1; - if (!leftToRight) { + int orientation = toScroll.getOrientation(); + if (!leftToRight && orientation == JScrollBar.HORIZONTAL) { direction *= -1; } - int orientation = toScroll.getOrientation(); if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) { int units = Math.abs(e.getUnitsToScroll()); diff --git a/src/main/java/com/weis/darklaf/ui/scrollpane/DarkScrollPaneUI.java b/src/main/java/com/weis/darklaf/ui/scrollpane/DarkScrollPaneUI.java index dda2d5f5..2722a4a1 100644 --- a/src/main/java/com/weis/darklaf/ui/scrollpane/DarkScrollPaneUI.java +++ b/src/main/java/com/weis/darklaf/ui/scrollpane/DarkScrollPaneUI.java @@ -7,7 +7,6 @@ import javax.swing.*; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicScrollPaneUI; import java.awt.*; -import java.awt.event.KeyEvent; import java.awt.event.MouseWheelListener; import java.beans.PropertyChangeListener; @@ -25,8 +24,7 @@ public class DarkScrollPaneUI extends BasicScrollPaneUI { scrollbar.setValueIsAdjusting(false); }; private final MouseWheelListener horizontalMouseWheelListener = e -> { - if (!scrollpane.isWheelScrollingEnabled() || - e.getModifiersEx() == KeyEvent.SHIFT_DOWN_MASK || !verticalScrollBarEnabled()) { + if (!scrollpane.isWheelScrollingEnabled() || e.isShiftDown() || !verticalScrollBarEnabled()) { return; } var scrollbar = scrollpane.getVerticalScrollBar(); @@ -39,8 +37,8 @@ public class DarkScrollPaneUI extends BasicScrollPaneUI { if (!scrollpane.isWheelScrollingEnabled()) { return; } - var scrollbar = e.getModifiersEx() == KeyEvent.SHIFT_DOWN_MASK ? scrollpane.getHorizontalScrollBar() - : scrollpane.getVerticalScrollBar(); + var scrollbar = e.isShiftDown() ? scrollpane.getHorizontalScrollBar() + : scrollpane.getVerticalScrollBar(); scrollbar.setValueIsAdjusting(true); DarkScrollBarUI.doScroll(scrollbar, scrollpane.getViewport(), e, scrollpane.getComponentOrientation().isLeftToRight()); @@ -118,6 +116,13 @@ public class DarkScrollPaneUI extends BasicScrollPaneUI { vertBounds.height += horBounds.height; } } + Insets barInsets = UIManager.getInsets("ScrollPane.barInsets"); + if (barInsets != null) { + vertBounds.height -= barInsets.top + barInsets.bottom; + vertBounds.y += barInsets.top; + horBounds.width -= barInsets.left + barInsets.right; + horBounds.x += barInsets.left; + } vsb.setBounds(vertBounds); hsb.setBounds(horBounds); } diff --git a/src/main/java/com/weis/darklaf/ui/tabbedpane/DarkTabbedPaneUI.java b/src/main/java/com/weis/darklaf/ui/tabbedpane/DarkTabbedPaneUI.java index 58d1e7db..f7624efa 100644 --- a/src/main/java/com/weis/darklaf/ui/tabbedpane/DarkTabbedPaneUI.java +++ b/src/main/java/com/weis/darklaf/ui/tabbedpane/DarkTabbedPaneUI.java @@ -1,15 +1,74 @@ package com.weis.darklaf.ui.tabbedpane; +import com.weis.darklaf.components.ScrollPopupMenu; +import com.weis.darklaf.decorators.PopupMenuAdapter; +import com.weis.darklaf.icons.EmptyIcon; +import com.weis.darklaf.icons.IconLoader; import com.weis.darklaf.util.DarkUIUtil; +import com.weis.darklaf.util.GraphicsContext; +import com.weis.darklaf.util.GraphicsUtil; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ContainerEvent; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.beans.PropertyChangeEvent; +import java.util.TooManyListenersException; +import java.util.function.Function; public class DarkTabbedPaneUI extends DarkTabbedPaneUIBridge { + private static final TabbedPaneTransferHandler TRANSFER_HANDLER = new TabbedPaneTransferHandler(); + private final FocusListener focusListener = new FocusListener() { + @Override + public void focusGained(final FocusEvent e) { + repaintTab(tabPane.getSelectedIndex()); + } + + @Override + public void focusLost(final FocusEvent e) { + repaintTab(tabPane.getSelectedIndex()); + } + }; + private DarkScrollableTabSupport scrollableTabSupport; + private DarkTabbedPaneScrollLayout scrollLayout; + private int currentShiftX = 0; + private int currentShiftXTmp = 0; + private int scrollShiftX = 0; + private int currentShiftY = 0; + private int currentShiftYTmp = 0; + private int scrollShiftY = 0; + private int minVisibleOld = -2; + private int maxVisibleOld = -2; + private int minVisible = -1; + private int maxVisible = -1; + private final Rectangle tabAreaBounds = new Rectangle(0, 0, 0, 0); + + private boolean dndEnabled; + private final Rectangle dropRect = new Rectangle(0, 0, 0, 0); + private int dropTargetIndex = -1; + private int dropSourceIndex = -1; + private boolean sourceEqualsTarget; + private boolean drawDropRect; + + + private DarkScrollHandler scrollHandler; + private Component leadingComp; + private Component trailingComp; + @NotNull @Contract("_ -> new") public static ComponentUI createUI(final JComponent c) { @@ -17,22 +76,1712 @@ public class DarkTabbedPaneUI extends DarkTabbedPaneUIBridge { } @Override - protected void paintTabBorder(final Graphics g, final int tabPlacement, final int tabIndex, + protected void paintTabBorder(@NotNull final Graphics g, final int tabPlacement, final int tabIndex, final int x, final int y, final int w, final int h, final boolean isSelected) { - super.paintTabBorder(g, tabPlacement, tabIndex, x, y, w, h, isSelected); + g.setColor(getTabBorderColor()); + switch (tabPlacement) { + case TOP -> g.fillRect(x, y + h - 1, w, 1); + case BOTTOM -> g.fillRect(x, y, w, 1); + case LEFT -> g.fillRect(x + w - 1, y, 1, h); + case RIGHT -> g.fillRect(x, y, 1, h); + } + } + + public void paint(final Graphics g, final JComponent c) { + int selectedIndex = tabPane.getSelectedIndex(); + int tabPlacement = tabPane.getTabPlacement(); + + ensureCurrentLayout(); + + // Paint content border and tab area + if (tabsOverlapBorder) { + paintContentBorder(g, tabPlacement, selectedIndex); + } + + int width = tabPane.getWidth(); + int height = tabPane.getHeight(); + int y = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); + int x = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); + switch (tabPlacement) { + case TOP -> paintTabAreaBorder(g, tabPlacement, 0, 0, width, y); + case BOTTOM -> paintTabAreaBorder(g, tabPlacement, 0, height - y, width, y + 1); + case LEFT -> paintTabAreaBorder(g, tabPlacement, 0, 0, x, height); + case RIGHT -> paintTabAreaBorder(g, tabPlacement, width - x, 0, x, height); + } + + // If scrollable tabs are enabled, the tab area will be + // painted by the scrollable tab panel instead. + if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT + paintTabArea(g, tabPlacement, selectedIndex); + } + if (!tabsOverlapBorder) { + paintContentBorder(g, tabPlacement, selectedIndex); + } + + if (!scrollableTabLayoutEnabled() && drawDropRect) { + paintDrop(g); + } + } + + protected void paintDrop(@NotNull final Graphics g) { + g.setColor(getDropColor()); + var context = new GraphicsContext(g); + if (!scrollableTabLayoutEnabled()) { + ((Graphics2D) g).setComposite(DarkUIUtil.ALPHA_COMPOSITE_2); + } + switch (tabPane.getTabPlacement()) { + case TOP -> g.fillRect(dropRect.x, dropRect.y, dropRect.width, dropRect.height - 1); + case BOTTOM -> g.fillRect(dropRect.x, dropRect.y + 1, dropRect.width, dropRect.height - 1); + case LEFT -> g.fillRect(dropRect.x, dropRect.y, dropRect.width - 1, dropRect.height); + case RIGHT -> g.fillRect(dropRect.x + 1, dropRect.y, dropRect.width - 1, dropRect.height); + } + context.restore(); + } + + protected void paintTabAreaBorder(@NotNull final Graphics g, final int tabPlacement, + final int x, final int y, final int w, final int h) { + g.setColor(getTabBorderColor()); + switch (tabPlacement) { + case TOP -> g.fillRect(x, y + h - 1, w, 1); + case BOTTOM -> g.fillRect(x, y, w, 1); + case LEFT -> g.fillRect(w - 1, y, 1, h); + case RIGHT -> g.fillRect(x, y, 1, h); + } } @Override - protected void paintFocusIndicator(final Graphics g, final int tabPlacement, final Rectangle[] rects, final int tabIndex, - final Rectangle iconRect, final Rectangle textRect, final boolean isSelected) { + protected void paintTabBackground(@NotNull final Graphics g, final int tabPlacement, final int tabIndex, + final int x, + final int y, final int w, final int h, + final boolean isSelected) { + g.setColor(getTabBackgroundColor(tabIndex, isSelected, getRolloverTab() == tabIndex)); + var r = rects[tabIndex]; + g.fillRect(r.x, r.y, r.width, r.height); + } + + protected Color getTabBackgroundColor(final int tabIndex, final boolean isSelected, final boolean hover) { if (isSelected) { - g.setColor(UIManager.getColor("DnDTabbedPane.selectionAccentUnfocused")); - if (DarkUIUtil.hasFocus(tabPane)) { - g.setColor(UIManager.getColor("DnDTabbedPane.selectionAccent")); - } + return hover ? UIManager.getColor("TabbedPane.selectHighlight") + : UIManager.getColor("TabbedPane.selected"); + } else { + return hover ? UIManager.getColor("TabbedPane.highlight") + : tabPane.getBackgroundAt(tabIndex); + } + } + + protected Color getTabBorderColor() { + return UIManager.getColor("TabbedPane.tabBorder"); + } + + protected Color getAccentColor(final boolean focus) { + return focus ? UIManager.getColor("TabbedPane.accentFocus") + : UIManager.getColor("TabbedPane.accent"); + } + + protected Color getDropColor() { + if (scrollableTabLayoutEnabled()) { + return UIManager.getColor("TabbedPane.dropFill"); + } else { + return getTabBackgroundColor(0, false, true); + } + } + + protected Color getDragBorderColor() { + return UIManager.getColor("TabbedPane.dragBorderColor"); + } + + @Override + protected void paintFocusIndicator(final Graphics g, final int tabPlacement, final Rectangle[] rects, + final int tabIndex, final Rectangle iconRect, + final Rectangle textRect, final boolean isSelected) { + if (isSelected) { + if (!drawFocusBar()) return; + g.setColor(getAccentColor(DarkUIUtil.hasFocus(tabPane))); var r = rects[tabIndex]; - g.fillRect(r.x, r.y + r.height - 4, r.width, 4); + switch (tabPlacement) { + case LEFT -> g.fillRect(r.x, r.y + r.height - 4, r.width - 1, 4); + case RIGHT -> g.fillRect(r.x + 1, r.y + r.height - 4, r.width - 1, 4); + default -> g.fillRect(r.x, r.y + r.height - 4, r.width, 4); + } + } + } + + @Override + protected void paintContentBorderBottomEdge(final Graphics g, final int tabPlacement, final int selectedIndex, + final int x, final int y, final int w, final int h) { + } + + @Override + protected void paintContentBorderLeftEdge(final Graphics g, final int tabPlacement, final int selectedIndex, + final int x, final int y, final int w, final int h) { + } + + @Override + protected void paintContentBorderRightEdge(final Graphics g, final int tabPlacement, final int selectedIndex, + final int x, final int y, final int w, final int h) { + } + + @Override + protected void paintContentBorderTopEdge(final Graphics g, final int tabPlacement, final int selectedIndex, + final int x, final int y, final int w, final int h) { + } + + @Override + protected void paintCroppedTabEdge(final Graphics g) { + } + + protected boolean shouldRotateTabRuns(final int tabPlacement) { + return Boolean.TRUE.equals(tabPane.getClientProperty("JTabbedPane.rotateTabRuns")); + } + + protected boolean drawFocusBar() { + return !Boolean.FALSE.equals(tabPane.getClientProperty("JTabbedPane.drawFocusBar")); + } + + protected Action getNewTabAction() { + var action = tabPane.getClientProperty("JTabbedPane.newTabAction"); + return action instanceof Action ? (Action) action : null; + } + + @Override + public void installUI(final JComponent c) { + super.installUI(c); + installDragSupport(); + } + + protected void installDragSupport() { + tabPane.setTransferHandler(TRANSFER_HANDLER); + try { + tabPane.getDropTarget().addDropTargetListener(TRANSFER_HANDLER); + tabPane.getDropTarget().setActive(dndEnabled); + } catch (TooManyListenersException e) { + e.printStackTrace(); + } + } + + public void clearDropIndicator() { + drawDropRect = false; + dropTargetIndex = -1; + tabPane.doLayout(); + tabPane.repaint(); + } + + public void clearSourceIndicator() { + dropSourceIndex = -1; + tabPane.doLayout(); + tabPane.repaint(); + } + + public void setSourceIndicator(final int sourceIndex) { + this.dropSourceIndex = sourceIndex; + tabPane.doLayout(); + tabPane.repaint(); + } + + public void setDnDIndicatorRect(final int x, final int y, final int width, final int height, final int targetIndex, + final boolean sourceEqualsTarget) { + dropRect.setBounds(x, y, width, height); + if (scrollableTabLayoutEnabled()) { + var p = scrollableTabSupport.viewport.getLocation(); + dropRect.x -= p.x; + dropRect.y -= p.y; + } + drawDropRect = true; + this.sourceEqualsTarget = sourceEqualsTarget; + dropTargetIndex = targetIndex; + tabPane.doLayout(); + tabPane.repaint(); + } + + @Override + protected void installComponents() { + if (scrollableTabLayoutEnabled()) { + if (tabScroller == null) { + tabScroller = new DarkScrollableTabSupport(tabPane.getTabPlacement()); + tabPane.add(tabScroller.viewport); + scrollableTabSupport = (DarkScrollableTabSupport) tabScroller; + } + } + installTabContainer(); + } + + @Override + protected void installListeners() { + super.installListeners(); + if (scrollableTabLayoutEnabled()) { + tabPane.removeMouseListener(getHandler()); + tabPane.removeMouseMotionListener(getHandler()); + tabScroller.viewport.addMouseMotionListener(getScrollHandler()); + tabScroller.viewport.addMouseListener(getScrollHandler()); + } + } + + + @Override + protected void installDefaults() { + super.installDefaults(); + var ins = tabPane.getClientProperty("JTabbedPane.tabAreaInsets"); + if (ins instanceof Insets) { + tabAreaInsets = (Insets) ins; + } + ins = tabPane.getClientProperty("JTabbedPane.contentBorderInsets"); + if (ins instanceof Insets) { + contentBorderInsets = (Insets) ins; + } + var lead = tabPane.getClientProperty("JTabbedPane.leadingComponent"); + if (lead instanceof Component) { + leadingComp = (Component) lead; + tabPane.add(leadingComp); + } + var trail = tabPane.getClientProperty("JTabbedPane.trailingComponent"); + if (trail instanceof Component) { + trailingComp = (Component) trail; + tabPane.add(trailingComp); + } + dndEnabled = Boolean.TRUE.equals(tabPane.getClientProperty("JTabbedPane.dndEnabled")); + } + + @Override + public void uninstallUI(final JComponent c) { + super.uninstallUI(c); + scrollableTabSupport = null; + } + + @Override + protected Handler getHandler() { + if (handler == null) { + handler = new DarkHandler(); + } + return handler; + } + + protected DarkScrollHandler getScrollHandler() { + if (scrollHandler == null) { + scrollHandler = new DarkScrollHandler(); + } + return scrollHandler; + } + + @Override + protected LayoutManager createLayoutManager() { + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) { + scrollLayout = new DarkTabbedPaneScrollLayout(); + return scrollLayout; + } else { /* WRAP_TAB_LAYOUT */ + return new DarkTabbedPaneLayout(); + } + } + + @Override + protected void setRolloverTab(final int index) { + int oldRollover = rolloverTabIndex; + super.setRolloverTab(index); + if (oldRollover != getRolloverTab()) { + repaintTab(oldRollover); + repaintTab(getRolloverTab()); + } + } + + @Override + protected Insets getTabAreaInsets(final int tabPlacement) { + Insets insets = super.getTabAreaInsets(tabPlacement); + if (leadingComp != null) { + var b = leadingComp.getPreferredSize(); + if (isHorizontalTabPlacement()) { + insets.left += b.width; + } else { + insets.top += b.height; + } + } + if (trailingComp != null) { + var b = trailingComp.getPreferredSize(); + if (isHorizontalTabPlacement()) { + insets.right += b.width; + } else { + insets.bottom += b.height; + } + } + return insets; + } + + @Override + public Rectangle getTabBounds(final JTabbedPane pane, final int i) { + var rect = super.getTabBounds(pane, i); + if (scrollableTabLayoutEnabled() && rect != null + && dropTargetIndex >= 0 && i == dropTargetIndex) { + switch (pane.getTabPlacement()) { + case TOP, BOTTOM -> { + if (pane.getComponentOrientation().isLeftToRight()) { + rect.x -= dropRect.width; + rect.width += dropRect.width; + } else { + rect.width += dropRect.width; + } + } + case LEFT, RIGHT -> { + rect.y -= dropRect.height; + rect.height += dropRect.height; + } + } + } + return rect; + } + + public Rectangle getTabAreaBounds() { + if (scrollableTabLayoutEnabled()) { + return scrollableTabSupport.viewport.getBounds(); + } else { + return new Rectangle(tabAreaBounds); + } + } + + @Override + protected int tabForCoordinate(final JTabbedPane pane, final int x, final int y, + final boolean validateIfNecessary) { + int tab = super.tabForCoordinate(pane, x, y, validateIfNecessary); + Point p = new Point(x, y); + if (scrollableTabLayoutEnabled()) { + translatePointToTabPanel(x, y, p); + if (tab == -1 && dropTargetIndex >= 0 && dropRect.contains(p)) { + return dropTargetIndex; + } + } + return tab; + } + + protected class DarkScrollableTabPanel extends ScrollableTabPanel { + public void doLayout() { + if (getComponentCount() > 0) { + for (int i = 0; i < getComponentCount(); i++) { + Component child = getComponent(i); + if (child == scrollableTabSupport.newTabButton) { + boolean leftToRight = tabPane.getComponentOrientation().isLeftToRight(); + int tabCount = tabPane.getTabCount(); + var b = child.getPreferredSize(); + if (isHorizontalTabPlacement()) { + int off = dropTargetIndex == tabCount ? dropRect.width : 0; + if (leftToRight) { + int x = rects[tabCount - 1].x + rects[tabCount - 1].width + off; + child.setBounds(x, 0, b.width, maxTabHeight); + } else { + int x = rects[tabCount - 1].x - off; + child.setBounds(x - b.width, 0, b.width, maxTabHeight); + } + } else { + int off = dropTargetIndex == tabCount ? dropRect.height : 0; + int y = rects[tabCount - 1].y + rects[tabCount - 1].height + off; + child.setBounds(0, y, maxTabWidth, b.height); + } + } else { + child.setBounds(0, 0, getWidth(), getHeight()); + } + } + } + } + + @Override + public void paintComponent(final Graphics g) { + super.paintComponent(g); + if (drawDropRect) { + paintDrop(g); + } + } + } + + protected class DarkTabAreaButton extends JButton implements UIResource { + + @Override + protected void paintComponent(final Graphics g) { + super.paintComponent(g); + paintButton(g); + paintTabAreaBorder(g, tabPane.getTabPlacement(), 0, 0, getWidth(), getHeight()); + } + + protected void paintButton(final Graphics g) { + } + } + + protected class NewTabButton extends JPanel implements UIResource { + + private final JButton button; + + protected NewTabButton() { + button = new JButton(); + button.setIcon(getNewTabIcon()); + button.putClientProperty("JButton.variant", "shadow"); + button.putClientProperty("JButton.buttonType", "square"); + button.putClientProperty("JButton.thin", Boolean.TRUE); + button.setRolloverEnabled(true); + button.addActionListener(e -> { + var action = getNewTabAction(); + if (action != null) { + action.actionPerformed(e); + } + }); + button.setOpaque(false); + add(button); + setOpaque(false); + setLayout(null); + } + + @Override + public void doLayout() { + var b = button.getPreferredSize(); + int x = (getWidth() - b.width) / 2; + int y = (getHeight() - b.height) / 2; + button.setBounds(x, y, b.width, b.height); + } + + @Override + public Dimension getPreferredSize() { + return button.getPreferredSize(); + } + + @Override + protected void paintComponent(final Graphics g) { + super.paintComponent(g); + paintTabAreaBorder(g, tabPane.getTabPlacement(), 0, 0, getWidth() + 1, getHeight()); + } + } + + private Icon getNewTabIcon() { + return UIManager.getIcon("TabbedPane.newTab.icon"); + } + + protected class MoreTabsButton extends DarkTabAreaButton { + + private final Icon icon; + private final static String INFINITY = "\u221e"; + private final static int PAD = 2; + + protected MoreTabsButton() { + icon = IconLoader.get().getUIAwareIcon("navigation/moreTabs.svg"); + setIcon(EmptyIcon.create(icon.getIconWidth(), icon.getIconHeight())); + putClientProperty("JButton.variant", "onlyLabel"); + putClientProperty("JButton.buttonType", "square"); + setFont(getFont().deriveFont(8f)); + } + + protected void paintButton(@NotNull final Graphics g) { + FontMetrics metrics = g.getFontMetrics(); + String label = getLabelString(); + int labelWidth = metrics.stringWidth(label); + int x = (getWidth() - (icon.getIconWidth() + labelWidth + PAD)) / 2; + int y = (getHeight() - icon.getIconHeight()) / 2; + + + var config = GraphicsUtil.setupAAPainting(g); + /* + * These offsets are due to the nature of the used icon. They are applied to match the baseline of + * the label text. + * A different icon might need a different offset. + */ + int off = 5; + switch (tabPane.getTabPlacement()) { + case TOP -> y += 2; + case BOTTOM -> y -= 1; + default -> { + } + } + + icon.paintIcon(this, g, x, y); + g.drawString(label, x + icon.getIconWidth() + PAD, y + icon.getIconHeight() - off); + config.restore(); + } + + @NotNull + private String getLabelString() { + int invisible = Math.min(minVisible - 1 + tabPane.getTabCount() - maxVisible, tabPane.getTabCount()); + return invisible >= 100 ? INFINITY : String.valueOf(invisible); + } + + @Override + public Dimension getPreferredSize() { + var size = super.getPreferredSize(); + var metrics = getFontMetrics(getFont()); + size.width += metrics.stringWidth(getLabelString()) + PAD; + return size; + } + } + + public class DarkScrollableTabViewport extends ScrollableTabViewport { + private final Point viewPos = new Point(0, 0); + + @Override + public Point getViewPosition() { + return viewPos; + } + } + + public class DarkScrollableTabSupport extends ScrollableTabSupport implements MouseWheelListener, ActionListener { + + private static final int SCROLL_REWIND_DELAY = 1200; + private long lastClickEvent; + protected ScrollPopupMenu scrollPopupMenu; + protected JButton moreTabsButton; + protected JComponent newTabButton; + private final Timer timer; + + public DarkScrollableTabSupport(final int tabPlacement) { + super(tabPlacement); + viewport = new DarkScrollableTabViewport(); + tabPanel = new DarkScrollableTabPanel(); + + viewport.setView(tabPanel); + viewport.addMouseWheelListener(this); + + moreTabsButton = new MoreTabsButton(); + moreTabsButton.setVisible(false); + moreTabsButton.addActionListener(this); + + newTabButton = new NewTabButton(); + newTabButton.setVisible(Boolean.TRUE.equals(tabPane.getClientProperty("JTabbedPane.showNewTabButton"))); + + scrollPopupMenu = new ScrollPopupMenu(UIManager.getInt("TabbedPane.maxPopupHeight")); + PopupMenuListener popupMenuListener = new PopupMenuAdapter() { + @Override + public void popupMenuWillBecomeInvisible(@NotNull final PopupMenuEvent e) { + lastClickEvent = System.currentTimeMillis(); + } + }; + scrollPopupMenu.addPopupMenuListener(popupMenuListener); + + tabPane.add(moreTabsButton); + timer = new Timer(SCROLL_REWIND_DELAY, e -> endScroll()); + timer.setRepeats(false); + } + + @Override + public void actionPerformed(final ActionEvent e) { + if (scrollPopupMenu.isVisible()) { + scrollPopupMenu.setVisible(false); + } else { + if (!tabPane.isEnabled()) return; + if (lastClickEvent == 0 || (System.currentTimeMillis() - lastClickEvent) > 250) { + var pref = scrollPopupMenu.getPreferredSize(); + boolean leftToRight = tabPane.getComponentOrientation().isLeftToRight(); + switch (tabPane.getTabPlacement()) { + case LEFT -> scrollPopupMenu.show(moreTabsButton, moreTabsButton.getWidth(), + moreTabsButton.getHeight() - pref.height); + case RIGHT -> scrollPopupMenu.show(moreTabsButton, -pref.width, + moreTabsButton.getHeight() - pref.height); + case TOP -> { + if (leftToRight) { + scrollPopupMenu.show(moreTabsButton, moreTabsButton.getWidth() - pref.width, + moreTabsButton.getHeight()); + } else { + scrollPopupMenu.show(moreTabsButton, 0, moreTabsButton.getHeight()); + } + } + case BOTTOM -> { + if (leftToRight) { + scrollPopupMenu.show(moreTabsButton, moreTabsButton.getWidth() - pref.width, + -pref.height); + } else { + scrollPopupMenu.show(moreTabsButton, 0, -pref.height); + } + } + } + } + } + } + + @Override + void createButtons() { + super.createButtons(); + tabPane.remove(scrollForwardButton); + tabPane.remove(scrollBackwardButton); } + + public void hideMoreTabsButton() { + moreTabsButton.setVisible(false); + } + + public void showMoreTabsButton() { + moreTabsButton.setVisible(true); + scrollPopupMenu.removeAll(); + if (maxVisible < 0 || minVisible >= tabPane.getTabCount()) { + scrollLayout.updateVisibleRange(tabPane.getTabPlacement()); + } + if (minVisible != tabPane.getTabCount() && maxVisible >= 0) { + for (int i = 0; i < minVisible; i++) { + scrollPopupMenu.add(createMenuItem(i)); + } + } + for (int i = maxVisible + 1; i < tabPane.getTabCount(); i++) { + scrollPopupMenu.add(createMenuItem(i)); + } + moreTabsButton.repaint(); + } + + @NotNull + @Contract("_ -> new") + private JMenuItem createMenuItem(final int i) { + Icon icon = tabPane.getIconAt(i); + if (icon != null && !tabPane.getComponentAt(i).isEnabled()) { + icon = tabPane.getDisabledIconAt(i); + } + Component comp = tabPane.getComponentAt(i); + return new JMenuItem(new AbstractAction(tabPane.getTitleAt(i), icon) { + @Override + public void actionPerformed(final ActionEvent e) { + if (i >= 0 && i <= tabPane.getTabCount()) { + //Use component instead of index as index may have changed in between creation + //and invocation of action. + tabPane.setSelectedComponent(comp); + tabPane.doLayout(); + comp.requestFocus(); + } + } + }); + } + + protected void updateRollover() { + var pos = MouseInfo.getPointerInfo().getLocation(); + SwingUtilities.convertPointFromScreen(pos, tabPane); + setRolloverTab(pos.x, pos.y); + } + + @Override + public void mouseWheelMoved(final MouseWheelEvent e) { + if (!tabPane.isEnabled() || tabPane.getTabCount() == 0) return; + int tabPosition = tabPane.getTabPlacement(); + int scrollAmount = -1 * e.getUnitsToScroll() * e.getScrollAmount(); + int scrolled; + if (tabPosition == LEFT || tabPosition == RIGHT) { + if (e.isShiftDown() || !moreTabsButton.isVisible()) return; + timer.stop(); + scrolled = scroll(scrollAmount, false); + } else { + if (!e.isShiftDown() || !moreTabsButton.isVisible()) return; + timer.stop(); + scrolled = scroll(scrollAmount, true); + } + if (scrolled != 0) { + showMoreTabsButton(); + updateRollover(); + viewport.repaint(); + } + timer.start(); + } + + private int scroll(final int amount, final boolean horizontal) { + Dimension size = tabPane.getSize(); + Insets insets = tabPane.getInsets(); + Insets tabAreaInsets = getTabAreaInsets(tabPane.getTabPlacement()); + int tabCount = tabPane.getTabCount(); + int shift; + if (horizontal) { + int rightMargin = size.width - (insets.left + insets.right + + tabAreaInsets.right + tabAreaInsets.left); + if (moreTabsButton.isVisible()) { + rightMargin -= moreTabsButton.getWidth(); + } + int low = rects[0].x; + int high = rects[tabCount - 1].x + rects[tabCount - 1].width; + shift = Math.abs(amount); + if (amount > 0) { + shift = Math.min(Math.max(-1 * low, 0), shift); + } else { + shift = Math.min(Math.max(high - rightMargin, 0), shift); + shift *= -1; + } + scrollLayout.commitShiftX(shift, tabCount); + scrollShiftX += shift; + } else { + int bottomMargin = size.height - (insets.bottom + tabAreaInsets.bottom + + insets.top + tabAreaInsets.top); + if (moreTabsButton.isVisible()) { + bottomMargin -= moreTabsButton.getHeight(); + } + int low = rects[0].y; + int high = rects[tabCount - 1].y + rects[tabCount - 1].height; + shift = Math.abs(amount); + if (amount > 0) { + shift = Math.min(Math.max(-1 * low, 0), shift); + } else { + shift = Math.min(Math.max(high - bottomMargin, 0), shift); + shift *= -1; + } + scrollLayout.commitShiftY(shift, tabCount); + scrollShiftY += shift; + } + return shift; + } + + protected void endScroll() { + currentShiftX += scrollShiftX; + currentShiftY += scrollShiftY; + scrollShiftX = 0; + scrollShiftY = 0; + scrollLayout.calculateTabRects(tabPane.getTabPlacement(), tabPane.getTabCount()); + updateRollover(); + viewport.repaint(); + } + } + + public class DarkTabbedPaneScrollLayout extends TabbedPaneScrollLayout { + + @Override + protected int preferredTabAreaHeight(final int tabPlacement, final int width) { + return calculateMaxTabHeight(tabPlacement); + } + + @Override + protected int preferredTabAreaWidth(final int tabPlacement, final int height) { + return calculateMaxTabWidth(tabPlacement); + } + + @Override + protected Dimension calculateSize(final boolean minimum) { + int tabPlacement = tabPane.getTabPlacement(); + Insets insets = tabPane.getInsets(); + Insets contentInsets = getContentBorderInsets(tabPlacement); + Insets tabAreaInsets = getTabAreaInsets(tabPlacement); + + int height = 0; + int width = 0; + int cWidth = 0; + int cHeight = 0; + + // Determine minimum size required to display largest + // child in each dimension + // + for (int i = 0; i < tabPane.getTabCount(); i++) { + Component component = tabPane.getComponentAt(i); + if (component != null) { + Dimension size = minimum ? component.getMinimumSize() : component.getPreferredSize(); + if (size != null) { + cHeight = Math.max(size.height, cHeight); + cWidth = Math.max(size.width, cWidth); + } + } + } + // Add content border insets to minimum size + width += cWidth; + height += cHeight; + int tabExtent; + + // Calculate how much space the tabs will need, based on the + // minimum size required to display largest child + content border + // + switch (tabPlacement) { + case LEFT, RIGHT -> { + int tabHeight = calculateTabHeight(tabPlacement, tabPane.getSelectedIndex(), + getFontMetrics().getHeight()); + if (scrollableTabSupport.moreTabsButton.isVisible()) { + tabHeight += scrollableTabSupport.moreTabsButton.getPreferredSize().height; + } + height = Math.max(height, tabHeight); + tabExtent = preferredTabAreaWidth(tabPlacement, + height - tabAreaInsets.top - tabAreaInsets.bottom); + width += tabExtent; + } + default -> { + int tabWidth = calculateTabWidth(tabPlacement, tabPane.getSelectedIndex(), getFontMetrics()); + if (scrollableTabSupport.moreTabsButton.isVisible()) { + tabWidth += scrollableTabSupport.moreTabsButton.getPreferredSize().width; + } + width = Math.max(width, tabWidth); + tabExtent = preferredTabAreaHeight(tabPlacement, width - tabAreaInsets.left - tabAreaInsets.right); + height += tabExtent; + } + } + return new Dimension(width + insets.left + insets.right + contentInsets.left + contentInsets.right, + height + insets.bottom + insets.top + contentInsets.top + contentInsets.bottom); + + } + + @Override + protected void calculateTabRects(final int tabPlacement, final int tabCount) { + FontMetrics metrics = getFontMetrics(); + Dimension size = tabPane.getSize(); + Insets insets = tabPane.getInsets(); + Insets tabAreaInsets = getTabAreaInsets(tabPlacement); + boolean verticalTabRuns = !isHorizontalTabPlacement(); + boolean leftToRight = tabPane.getComponentOrientation().isLeftToRight(); + + switch (tabPlacement) { + case LEFT, RIGHT -> maxTabWidth = calculateMaxTabWidth(tabPlacement); + default -> maxTabHeight = calculateMaxTabHeight(tabPlacement); + } + + runCount = 0; + selectedRun = -1; + + if (tabCount == 0) return; + + selectedRun = 0; + runCount = 1; + + Rectangle tabBounds = new Rectangle(0, 0, 0, 0); + for (int i = 0; i < tabCount; i++) { + calculateRect(i, tabBounds, metrics, verticalTabRuns, tabPlacement); + } + + JButton tabsButton = scrollableTabSupport.moreTabsButton; + Rectangle selectedBounds = new Rectangle(rects[tabPane.getSelectedIndex()]); + if (!verticalTabRuns) { + int rightMargin = size.width - (insets.right + tabAreaInsets.right + + insets.left + tabAreaInsets.left); + var p = getMargins(tabPlacement); + int leftMargin = p.x; + int returnAt = p.y; + + currentShiftXTmp = currentShiftX; + shiftTabsX(0, leftMargin, returnAt, tabCount, false); + if (!(minVisible == maxVisible && minVisible == tabPane.getSelectedIndex())) { + selectedBounds.x += currentShiftXTmp; + shiftBoundsToVisibleX(selectedBounds, leftMargin, returnAt, tabCount); + } + restoreHiddenTabsX(leftMargin, returnAt, tabCount); + if (tabsButton.isVisible() && tabPane.getSelectedIndex() < maxVisible) { + //Shift again. Hiding the the tab button might reveal the last tab. + //Only do this if the last visible tab is not currently selected. + //Otherwise the selected tab forces the whole tab area the jump by the width of the tab button. + int margin = returnAt + tabsButton.getPreferredSize().width; + shiftTabsX(0, leftMargin, margin, tabCount, false); + if (minVisible > 0 || maxVisible < tabCount - 1) { + //Tab button is still visible but may hide a further tab. restore visible bounds. + shiftTabsX(0, leftMargin, returnAt, tabCount, false); + } + } + adjustForDropX(leftMargin, returnAt, tabCount); + + layoutMoreTabsButton(tabCount); + commitShiftX(currentShiftXTmp, tabCount); + currentShiftX = currentShiftXTmp; + + if (!leftToRight) { + if (tabsButton.isVisible()) { + rightMargin -= tabsButton.getWidth(); + } + var newTabButton = scrollableTabSupport.newTabButton; + if (newTabButton.isVisible() && newTabButton.getParent() == tabPane) { + rightMargin -= newTabButton.getWidth(); + } + for (int i = 0; i < tabCount; i++) { + rects[i].x = rightMargin - rects[i].x - rects[i].width; + } + } + if (scrollableTabSupport.newTabButton.isVisible()) { + layoutNewTabButton(true, leftToRight, leftMargin, returnAt, tabCount); + } + + } else { + int bottomMargin = size.height - (insets.bottom + tabAreaInsets.bottom + + insets.top + tabAreaInsets.top); + var p = getMargins(tabPlacement); + int topMargin = p.x; + int returnAt = p.y; + currentShiftYTmp = currentShiftY; + shiftTabsY(0, topMargin, returnAt, tabCount, false); + if (!(minVisible == maxVisible && minVisible == tabPane.getSelectedIndex())) { + selectedBounds.y += currentShiftYTmp; + shiftBoundsToVisibleY(selectedBounds, topMargin, returnAt, tabCount); + } + restoreHiddenTabsY(topMargin, returnAt, tabCount); + if (tabsButton.isVisible() && tabPane.getSelectedIndex() < maxVisible) { + shiftTabsY(0, topMargin, bottomMargin, tabCount, false); + if (minVisible > 0 || maxVisible < tabCount - 1) { + shiftTabsY(0, topMargin, returnAt, tabCount, false); + } + } + adjustForDropY(topMargin, returnAt, tabCount); + + layoutMoreTabsButton(tabCount); + commitShiftY(currentShiftYTmp, tabCount); + currentShiftY = currentShiftYTmp; + + if (scrollableTabSupport.newTabButton.isVisible()) { + layoutNewTabButton(false, leftToRight, topMargin, returnAt, tabCount); + } + } + tabScroller.tabPanel.setPreferredSize(tabBounds.getSize()); + tabScroller.tabPanel.invalidate(); + } + + private void layoutNewTabButton(final boolean horizontal, final boolean leftToRight, + final int minVal, final int maxVal, final int tabCount) { + JComponent button = scrollableTabSupport.newTabButton; + var buttonBounds = button.getPreferredSize(); + if (horizontal) { + if (leftToRight) { + if (rects[tabCount - 1].x + rects[tabCount - 1].width + buttonBounds.width > maxVal) { + tabPane.add(button); + } else { + scrollableTabSupport.tabPanel.add(button); + } + } else { + int x = rects[tabCount - 1].x; + if (x - buttonBounds.width < minVal) { + tabPane.add(button); + } else { + scrollableTabSupport.tabPanel.add(button); + } + } + } else { + if (rects[tabCount - 1].y + rects[tabCount - 1].height + buttonBounds.height > maxVal) { + tabPane.add(button); + } else { + scrollableTabSupport.tabPanel.add(button); + } + } + } + + @SuppressWarnings("SuspiciousNameCombination") + @NotNull + @Contract("_ -> new") + private Point getMargins(final int tabPlacement) { + Dimension size = tabPane.getSize(); + Insets insets = tabPane.getInsets(); + Insets tabAreaInsets = getTabAreaInsets(tabPlacement); + var tabsButton = scrollableTabSupport.moreTabsButton; + var newTabsButton = scrollableTabSupport.newTabButton; + if (isHorizontalTabPlacement()) { + int leftMargin = 0; + int returnAt = size.width - (insets.right + tabAreaInsets.right + + insets.left + tabAreaInsets.left); + if (tabsButton.isVisible()) { + returnAt -= tabsButton.getPreferredSize().width; + } + if (newTabsButton.isVisible() && newTabsButton.getParent() == tabPane) { + returnAt -= newTabsButton.getPreferredSize().width; + } + return new Point(leftMargin, returnAt); + } else { + int topMargin = 0; + int returnAt = size.height - (insets.bottom + tabAreaInsets.bottom + + insets.top + tabAreaInsets.top); + if (tabsButton.isVisible()) { + returnAt -= tabsButton.getPreferredSize().height; + } + if (newTabsButton.isVisible() && newTabsButton.getParent() == tabPane) { + returnAt -= newTabsButton.getPreferredSize().height; + } + return new Point(topMargin, returnAt); + } + } + + @SuppressWarnings("SuspiciousNameCombination") + public void updateVisibleRange(final int tapPlacement) { + var p = getMargins(tapPlacement); + if (isHorizontalTabPlacement()) { + shiftTabsX(0, p.x, p.y, tabPane.getTabCount(), false); + } else { + shiftTabsY(0, p.x, p.y, tabPane.getTabCount(), false); + } + } + + private void commitShiftX(final int shift, final int tabCount) { + commitShiftX(0, tabCount - 1, shift, tabCount); + } + + private void commitShiftX(final int low, final int high, final int shift, final int tabCount) { + for (int i = Math.max(low, 0); i <= Math.min(high, tabCount - 1); i++) { + rects[i].x += shift; + } + } + + private void commitShiftY(final int shift, final int tabCount) { + commitShiftY(0, tabCount - 1, shift, tabCount); + } + + private void commitShiftY(final int low, final int high, final int shift, final int tabCount) { + for (int i = Math.max(low, 0); i <= Math.min(high, tabCount - 1); i++) { + rects[i].y += shift; + } + } + + private void shiftBoundsToVisibleX(@NotNull final Rectangle selectedBounds, final int leftMargin, + final int rightMargin, final int tabCount) { + if (selectedBounds.x + selectedBounds.width > rightMargin) { + //SelectedTab is not fully visible. Covered on right side. + shiftTabsX(rightMargin - selectedBounds.x - selectedBounds.width, + leftMargin, rightMargin, tabCount, true); + } else if (selectedBounds.x < leftMargin) { + //SelectedTab is not fully visible. Covered on left side. + shiftTabsX(-selectedBounds.x + leftMargin, leftMargin, rightMargin, tabCount, true); + } + } + + private void shiftBoundsToVisibleY(@NotNull final Rectangle selectedBounds, final int topMargin, + final int bottomMargin, final int tabCount) { + if (selectedBounds.y + selectedBounds.height > bottomMargin) { + //SelectedTab is not fully visible. Covered on right side. + shiftTabsY(bottomMargin - selectedBounds.y - selectedBounds.height, + topMargin, bottomMargin, tabCount, true); + } else if (selectedBounds.y < topMargin) { + //SelectedTab is not fully visible. Covered on left side. + shiftTabsY(-selectedBounds.y + topMargin, + topMargin, bottomMargin, tabCount, true); + } + } + + private void calculateRect(final int i, final Rectangle tabBounds, final FontMetrics metrics, + final boolean verticalTabRuns, final int tabPlacement) { + Rectangle rect = rects[i]; + if (!verticalTabRuns) { + if (i > 0) { + rect.x = rects[i - 1].x + rects[i - 1].width; + } else { + tabRuns[0] = 0; + maxTabWidth = 0; + tabBounds.height = maxTabHeight; + rect.x = tabBounds.x; + } + rect.width = calculateTabWidth(tabPlacement, i, metrics); + tabBounds.width = rect.x + rect.width; + maxTabWidth = Math.max(maxTabWidth, rect.width); + rect.y = tabBounds.y; + rect.height = maxTabHeight; + } else { + if (i > 0) { + rect.y = rects[i - 1].y + rects[i - 1].height; + } else { + tabRuns[0] = 0; + maxTabHeight = 0; + tabBounds.width = maxTabWidth; + rect.y = tabBounds.y; + } + rect.height = calculateTabHeight(tabPlacement, i, metrics.getHeight()); + tabBounds.height = rect.y + rect.height; + maxTabHeight = Math.max(maxTabHeight, rect.height); + rect.x = tabBounds.x; + rect.width = maxTabWidth; + } + } + + private void restoreHiddenTabsX(final int minX, final int maxX, final int tabCount) { + if (maxVisible < 0 || maxVisible >= tabCount) return; + int space = Math.max(maxX - rects[maxVisible].x - rects[maxVisible].width - currentShiftXTmp, 0); + int shift = Math.min(minX - rects[0].x - currentShiftXTmp, space); + shiftTabsX(shift, minX, maxX, tabCount, true); + } + + private void restoreHiddenTabsY(final int minY, final int maxY, final int tabCount) { + if (maxVisible < 0 || maxVisible >= tabCount) return; + int space = Math.max(maxY - rects[maxVisible].y - rects[maxVisible].height - currentShiftYTmp, 0); + int shift = Math.min(minY - rects[0].y - currentShiftYTmp, space); + shiftTabsY(shift, minY, maxY, tabCount, true); + } + + private void adjustForDropX(final int minX, final int maxX, final int tabCount) { + if (dropSourceIndex >= 0) { + //Hide the source tab. + int shift = rects[dropSourceIndex].width; + rects[dropSourceIndex].setSize(0, 0); + commitShiftX(dropSourceIndex + 1, tabCount - 1, -1 * shift, tabCount); + } + if (dropTargetIndex >= 0) { + commitShiftX(dropTargetIndex, tabCount - 1, dropRect.width, tabCount); + } + shiftTabsX(0, minX, maxX, tabCount, false); + } + + private void adjustForDropY(final int minY, final int maxY, final int tabCount) { + if (dropSourceIndex >= 0) { + //Hide the source tab. + int shift = rects[dropSourceIndex].height; + rects[dropSourceIndex].setSize(0, 0); + commitShiftY(dropSourceIndex + 1, tabCount - 1, -1 * shift, tabCount); + } + if (sourceEqualsTarget && dropTargetIndex >= 0) { + commitShiftY(dropTargetIndex, tabCount - 1, dropRect.height, tabCount); + } + shiftTabsY(0, minY, maxY, tabCount, false); + } + + private void layoutMoreTabsButton(final int tabCount) { + final JButton button = scrollableTabSupport.moreTabsButton; + if (minVisible > 0 || maxVisible < tabCount - 1) { + if (scrollableTabSupport.moreTabsButton.isVisible()) { + if (minVisible != minVisibleOld || maxVisible != maxVisibleOld) { + scrollableTabSupport.showMoreTabsButton(); + } + } else { + scrollableTabSupport.showMoreTabsButton(); + } + // Update old values. + minVisibleOld = minVisible; + maxVisibleOld = maxVisible; + } else if (button.isVisible()) { + scrollableTabSupport.hideMoreTabsButton(); + } + } + + private void shiftTabsX(final int shift, final int minX, final int returnAt, + final int tabCount, final boolean updateShift) { + shiftTabs(shift, minX, returnAt, tabCount, updateShift, true); + } + + private void shiftTabsY(final int shift, final int minY, final int returnAt, + final int tabCount, final boolean updateShift) { + shiftTabs(shift, minY, returnAt, tabCount, updateShift, false); + } + + private void shiftTabs(final int shift, final int minVal, final int returnAt, + final int tabCount, final boolean updateShift, final boolean isX) { + int min = -1; + int max = -1; + int minStart = minVisible < 0 || minVisible >= tabCount ? 0 : minVisible; + int maxStart = maxVisible < 0 || maxVisible >= tabCount ? tabCount - 1 : maxVisible; + int currShift = isX ? currentShiftXTmp + shift : currentShiftYTmp + shift; + Function isVisible = isX ? (i -> isVisibleX(i, currShift, minVal, returnAt)) + : (i -> isVisibleY(i, currShift, minVal, returnAt)); + if (isVisible.apply(minStart)) { + //Descent to find minimum. + min = minStart; + for (int i = minStart - 1; i >= 0; i--) { + if (isVisible.apply(i)) { + min = i; + } else { + break; + } + } + } else { + //Ascent to find minimum. + for (int i = minStart + 1; i < tabCount; i++) { + if (isVisible.apply(i)) { + min = i; + break; + } + } + } + if (min == -1) { + min = tabCount; + } + if (isVisible.apply(maxStart)) { + //Ascent to find maximum. + max = maxStart; + for (int i = maxStart + 1; i < tabCount; i++) { + if (isVisible.apply(i)) { + max = i; + } else { + break; + } + } + } else { + //Descent to find maximum. + for (int i = maxStart - 1; i >= 0; i--) { + if (isVisible.apply(i)) { + max = i; + break; + } + } + } + minVisible = min; + maxVisible = max; + + if (updateShift) { + if (isX) { + currentShiftXTmp += shift; + } else { + currentShiftYTmp += shift; + } + } + } + + @Contract(pure = true) + private boolean isVisibleX(final int i, final int shift, final int minX, final int maxX) { + int begin = rects[i].x + shift; + int end = begin + rects[i].width; + return !(begin >= maxX || end < minX); + } + + @Contract(pure = true) + private boolean isVisibleY(final int i, final int shift, final int minX, final int maxX) { + int begin = rects[i].y + shift; + int end = begin + rects[i].height; + return !(begin >= maxX || end < minX); + } + + public void layoutContainer(final Container parent) { + int tabPlacement = tabPane.getTabPlacement(); + Insets insets = tabPane.getInsets(); + Insets tabAreaInsets = getTabAreaInsets(tabPlacement); + int selectedIndex = tabPane.getSelectedIndex(); + Component visibleComponent = getVisibleComponent(); + + calculateLayoutInfo(); + + Component selectedComponent = null; + if (selectedIndex < 0) { + if (visibleComponent != null) { + // The last tab was removed, so remove the component + setVisibleComponent(null); + } + } else { + selectedComponent = tabPane.getComponentAt(selectedIndex); + } + + if (tabPane.getTabCount() == 0) { + scrollableTabSupport.hideMoreTabsButton(); + return; + } + + boolean shouldChangeFocus = false; + + // In order to allow programs to use a single component + // as the display for multiple tabs, we will not change + // the visible compnent if the currently selected tab + // has a null component. This is a bit dicey, as we don't + // explicitly state we support this in the spec, but since + // programs are now depending on this, we're making it work. + // + if (selectedComponent != null) { + if (selectedComponent != visibleComponent && + visibleComponent != null) { + if (SwingUtilities.findFocusOwner(visibleComponent) != null) { + shouldChangeFocus = true; + } + } + setVisibleComponent(selectedComponent); + } + int tx, ty, tw, th; // tab area bounds + int cx, cy, cw, ch; // content area bounds + Insets contentInsets = getContentBorderInsets(tabPlacement); + Rectangle bounds = tabPane.getBounds(); + int numChildren = tabPane.getComponentCount(); + + if (numChildren > 0) { + switch (tabPlacement) { + case LEFT -> { + tw = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); + th = bounds.height - insets.top - insets.bottom - tabAreaInsets.top - tabAreaInsets.bottom; + tx = insets.left + tabAreaInsets.left; + ty = insets.top + tabAreaInsets.top; + + cx = insets.left + tw + contentInsets.left + tabAreaInsets.left + tabAreaInsets.right; + cy = insets.top + contentInsets.top; + cw = bounds.width - insets.left - insets.right - tw - contentInsets.left - contentInsets.right + - tabAreaInsets.left - tabAreaInsets.right; + ch = bounds.height - insets.top - insets.bottom - contentInsets.top - contentInsets.bottom; + + tw -= tabAreaInsets.left + tabAreaInsets.right; + } + case RIGHT -> { + tw = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); + th = bounds.height - insets.top - insets.bottom - tabAreaInsets.top - tabAreaInsets.bottom; + tx = bounds.width - insets.right - tw + tabAreaInsets.left; + ty = insets.top + tabAreaInsets.top; + + cx = insets.left + contentInsets.left; + cy = insets.top + contentInsets.top; + cw = bounds.width - insets.left - insets.right - tw - contentInsets.left - contentInsets.right; + ch = bounds.height - insets.top - insets.bottom - contentInsets.top - contentInsets.bottom; + + tw -= tabAreaInsets.left + tabAreaInsets.right; + } + case BOTTOM -> { + tw = bounds.width - insets.left - insets.right - tabAreaInsets.left - tabAreaInsets.right; + th = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); + tx = insets.left + tabAreaInsets.left; + ty = bounds.height - insets.bottom - th + tabAreaInsets.top; + + cx = insets.left + contentInsets.left; + cy = insets.top + contentInsets.top; + cw = bounds.width - insets.left - insets.right - contentInsets.left - contentInsets.right; + ch = bounds.height - insets.top - insets.bottom - th - contentInsets.top - contentInsets.bottom; + + th -= tabAreaInsets.top + tabAreaInsets.bottom; + } + default -> { + tw = bounds.width - insets.left - insets.right - tabAreaInsets.left - tabAreaInsets.right; + th = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); + tx = insets.left + tabAreaInsets.left; + ty = insets.top + tabAreaInsets.top; + + cx = insets.left + contentInsets.left; + cy = insets.top + th + contentInsets.top; + cw = bounds.width - insets.left - insets.right - contentInsets.left - contentInsets.right; + ch = bounds.height - insets.top - insets.bottom - th - contentInsets.top - contentInsets.bottom; + + th -= tabAreaInsets.top + tabAreaInsets.bottom; + } + } + JButton moreTabs = scrollableTabSupport.moreTabsButton; + JComponent newTab = scrollableTabSupport.newTabButton; + + for (int i = 0; i < numChildren; i++) { + Component child = tabPane.getComponent(i); + + if (tabScroller != null && child == tabScroller.viewport) { + int vw = tw; + int vh = th; + Dimension butSize = moreTabs.isVisible() + ? moreTabs.getPreferredSize() + : new Dimension(0, 0); + boolean showNewTabButton = newTab.isVisible() && newTab.getParent() == tabPane; + Dimension butSize2 = showNewTabButton + ? newTab.getPreferredSize() + : new Dimension(0, 0); + boolean leftToRight = tabPane.getComponentOrientation().isLeftToRight(); + switch (tabPlacement) { + case LEFT, RIGHT -> { + vh = th - butSize.height - butSize2.height; + moreTabs.setBounds(tx, ty + vh + butSize2.height, maxTabWidth, butSize.height); + if (showNewTabButton) { + newTab.setBounds(tx, ty + vh, maxTabWidth, butSize2.height); + } + } + default -> { + if (leftToRight) { + vw = tw - butSize.width - butSize2.width; + moreTabs.setBounds(tx + vw + butSize2.width, ty, butSize.width, maxTabHeight); + if (showNewTabButton) { + newTab.setBounds(tx + vw, ty, butSize2.width, maxTabHeight); + } + } else { + vw = tw - butSize.width - butSize2.width; + moreTabs.setBounds(tx, ty, butSize.width, maxTabHeight); + if (showNewTabButton) { + newTab.setBounds(tx + butSize.width, ty, butSize2.width, maxTabHeight); + } + tx += butSize.width + butSize2.width; + } + } + } + child.setBounds(tx, ty, vw, vh); + } else { + int tabHeight = maxTabHeight + tabAreaInsets.top + tabAreaInsets.bottom; + int tabWidth = maxTabWidth + tabAreaInsets.left + tabAreaInsets.right; + if (child == leadingComp && leadingComp != null) { + layoutLeadingComponent(child, tabWidth, tabHeight, insets, tx, ty, tabPlacement); + } else if (child == trailingComp && trailingComp != null) { + layoutTrailingComponent(child, tabWidth, tabHeight, insets, tx, ty, tw, th, tabPlacement); + } else if (child != moreTabs && child != newTab) { + child.setBounds(cx, cy, cw, ch); + } + } + } + super.layoutTabComponents(); + if (shouldChangeFocus) { + if (!requestFocusForVisibleComponent()) { + tabPane.requestFocus(); + } + } + } + } + } + + public class DarkTabbedPaneLayout extends TabbedPaneLayout { + + @Override + public void layoutContainer(final Container parent) { + int tabPlacement = tabPane.getTabPlacement(); + Insets insets = tabPane.getInsets(); + Insets tabAreaInsets = getTabAreaInsets(tabPlacement); + int selectedIndex = tabPane.getSelectedIndex(); + Component visibleComponent = getVisibleComponent(); + + calculateLayoutInfo(); + + Component selectedComponent = null; + if (selectedIndex < 0) { + if (visibleComponent != null) { + // The last tab was removed, so remove the component + setVisibleComponent(null); + } + } else { + selectedComponent = tabPane.getComponentAt(selectedIndex); + } + int cx, cy, cw, ch; + int tx, ty, tw, th; + Insets contentInsets = getContentBorderInsets(tabPlacement); + + boolean shouldChangeFocus = false; + + // In order to allow programs to use a single component + // as the display for multiple tabs, we will not change + // the visible compnent if the currently selected tab + // has a null component. This is a bit dicey, as we don't + // explicitly state we support this in the spec, but since + // programs are now depending on this, we're making it work. + // + if (selectedComponent != null) { + if (selectedComponent != visibleComponent && + visibleComponent != null) { + if (SwingUtilities.findFocusOwner(visibleComponent) != null) { + shouldChangeFocus = true; + } + } + setVisibleComponent(selectedComponent); + } + + Rectangle bounds = tabPane.getBounds(); + int numChildren = tabPane.getComponentCount(); + + if (numChildren > 0) { + switch (tabPlacement) { + case LEFT -> { + tw = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); + tx = insets.left + tabAreaInsets.left; + ty = insets.top + tabAreaInsets.top; + th = bounds.height - insets.top - tabAreaInsets.top - insets.bottom - tabAreaInsets.bottom; + + cx = insets.left + tw + contentInsets.left + tabAreaInsets.left + tabAreaInsets.right; + cy = insets.top + contentInsets.top; + cw = bounds.width - insets.left - insets.right - contentInsets.left - contentInsets.right - tw + - tabAreaInsets.left - tabAreaInsets.right; + ch = bounds.height - insets.top - insets.bottom - contentInsets.top - contentInsets.bottom; + } + case RIGHT -> { + tw = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); + tx = bounds.width - insets.left - tw - tabAreaInsets.right - tabAreaInsets.left; + ty = insets.top + tabAreaInsets.top; + th = bounds.height - insets.top - tabAreaInsets.top - insets.bottom - tabAreaInsets.bottom; + + cx = insets.left + contentInsets.left; + cy = insets.top + contentInsets.top; + cw = bounds.width - insets.left - insets.right - contentInsets.left - contentInsets.right - tw + - tabAreaInsets.left - tabAreaInsets.right; + ch = bounds.height - insets.top - insets.bottom - contentInsets.top - contentInsets.bottom; + } + case BOTTOM -> { + th = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); + ty = bounds.height - insets.bottom - th; + tx = insets.left + tabAreaInsets.left; + tw = bounds.width - insets.left - insets.right - tabAreaInsets.left - tabAreaInsets.right; + + cx = insets.left + contentInsets.left; + cy = insets.top + contentInsets.top; + cw = bounds.width - insets.left - insets.right - contentInsets.left - contentInsets.right; + ch = bounds.height - th - insets.top - insets.bottom - contentInsets.top - contentInsets.bottom; + } + default -> { + ty = insets.top + tabAreaInsets.top; + tx = insets.left + tabAreaInsets.left; + tw = bounds.width - insets.left - insets.right - tabAreaInsets.left - tabAreaInsets.right; + th = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); + + cx = insets.left + contentInsets.left; + cy = insets.top + th + contentInsets.top + tabAreaInsets.top + tabAreaInsets.bottom; + cw = bounds.width - insets.left - insets.right - contentInsets.left - contentInsets.right; + ch = bounds.height - th - insets.top - insets.bottom - contentInsets.top - contentInsets.bottom + - tabAreaInsets.top - tabAreaInsets.bottom; + } + } + + tabAreaBounds.setRect(tx, ty, tw, th); + for (int i = 0; i < numChildren; i++) { + Component child = tabPane.getComponent(i); + if (child == tabContainer) { + child.setBounds(tx, ty, tw, th); + } else { + int tabHeight = maxTabHeight + tabAreaInsets.top + tabAreaInsets.bottom; + int tabWidth = maxTabWidth + tabAreaInsets.left + tabAreaInsets.right; + if (child == leadingComp && leadingComp != null) { + layoutLeadingComponent(child, tabWidth, tabHeight, insets, tx, ty, tabPlacement); + } else if (child == trailingComp && trailingComp != null) { + layoutTrailingComponent(child, tabWidth, tabHeight, insets, tx, ty, tw, th, tabPlacement); + } else { + child.setBounds(cx, cy, cw, ch); + } + } + } + } + layoutTabComponents(); + if (shouldChangeFocus) { + if (!requestFocusForVisibleComponent()) { + tabPane.requestFocus(); + } + } + } + } + + protected void layoutLeadingComponent(final Component comp, final int tabWidth, final int tabHeight, + final Insets insets, final int tx, final int ty, final int tabPlacement) { + var b = leadingComp.getPreferredSize(); + int h = Math.min(tabHeight, b.height); + int w = Math.min(tabWidth, b.width); + int centerY = (tabHeight - h) / 2; + int centerX = (tabWidth - w) / 2; + switch (tabPlacement) { + case LEFT -> comp.setBounds(insets.left + centerX, ty - b.height, w, b.height); + case RIGHT -> comp.setBounds(tx - tabAreaInsets.left + centerX, ty - b.height, + w, b.height); + case TOP -> comp.setBounds(tx - b.width, insets.top + centerY, b.width, h); + case BOTTOM -> comp.setBounds(tx - b.width, ty - tabAreaInsets.bottom + centerY, + b.width, h); + } + } + + protected void layoutTrailingComponent(final Component comp, final int tabWidth, final int tabHeight, + final Insets insets, final int tx, final int ty, + final int tw, final int th, final int tabPlacement) { + var b = trailingComp.getPreferredSize(); + int h = Math.min(tabHeight, b.height); + int w = Math.min(tabWidth, b.width); + int centerY = (tabHeight - h) / 2; + int centerX = (tabWidth - w) / 2; + switch (tabPlacement) { + case LEFT -> comp.setBounds(insets.left + centerX, ty + th, w, b.height); + case RIGHT -> comp.setBounds(tx - tabAreaInsets.left + centerX, ty + th, + w, b.height); + case TOP -> comp.setBounds(tx + tw, insets.top + centerY, b.width, h); + case BOTTOM -> comp.setBounds(tx + tw, ty - tabAreaInsets.bottom + centerY, + b.width, h); + } + } + + + public class DarkHandler extends Handler { + + public void stateChanged(@NotNull final ChangeEvent e) { + JTabbedPane tabPane = (JTabbedPane) e.getSource(); + tabPane.revalidate(); + tabPane.repaint(); + setFocusIndex(tabPane.getSelectedIndex(), false); + } + + @Override + public void propertyChange(final PropertyChangeEvent e) { + super.propertyChange(e); + var key = e.getPropertyName(); + if ("TabbedPane.maxPopupHeight".equals(key)) { + Integer newVal = (Integer) e.getNewValue(); + if (newVal != null && newVal >= 0) { + scrollableTabSupport.scrollPopupMenu.setMaxHeight(newVal); + } + } else if ("JTabbedPane.tabAreaInsets".equals(key)) { + var ins = e.getNewValue(); + if (ins instanceof Insets) { + tabAreaInsets = (Insets) ins; + } else if (ins == null) { + tabAreaInsets = new Insets(0, 0, 0, 0); + } + } else if ("JTabbedPane.contentBorderInsets".equals(key)) { + var ins = e.getNewValue(); + if (ins instanceof Insets) { + contentBorderInsets = (Insets) ins; + } else if (ins == null) { + contentBorderInsets = new Insets(0, 0, 0, 0); + } + } else if ("tabPlacement".equals(key)) { + if (scrollableTabLayoutEnabled()) { + currentShiftX = 0; + currentShiftY = 0; + scrollLayout.calculateTabRects(tabPane.getTabPlacement(), tabPane.getTabCount()); + } + } else if ("JTabbedPane.showNewTabButton".equals(key)) { + var val = e.getNewValue(); + if (val instanceof Boolean && scrollableTabLayoutEnabled()) { + boolean show = (Boolean) val; + if (show == scrollableTabSupport.newTabButton.isVisible()) { + return; + } + scrollableTabSupport.newTabButton.setVisible(show); + } + } else if ("JTabbedPane.leadingComponent".equals(key)) { + tabPane.remove(leadingComp); + var val = e.getNewValue(); + if (val instanceof Component) { + leadingComp = (Component) val; + tabPane.add(leadingComp); + } else { + leadingComp = null; + } + } else if ("JTabbedPane.trailingComponent".equals(key)) { + tabPane.remove(trailingComp); + var val = e.getNewValue(); + if (val instanceof Component) { + trailingComp = (Component) val; + tabPane.add(trailingComp); + } else { + trailingComp = null; + } + } else if ("JTabbedPane.dndEnabled".equals(key)) { + dndEnabled = Boolean.TRUE.equals(tabPane.getClientProperty("JTabbedPane.dndEnabled")); + tabPane.getDropTarget().setActive(dndEnabled); + } else if ("componentOrientation".equals(key)) { + tabPane.doLayout(); + tabPane.repaint(); + } + } + + @Override + public void componentAdded(@NotNull final ContainerEvent e) { + if (!(e.getChild() instanceof UIResource)) { + e.getChild().addFocusListener(focusListener); + } + super.componentAdded(e); + } + + @Override + public void componentRemoved(@NotNull final ContainerEvent e) { + if (!(e.getChild() instanceof UIResource)) { + e.getChild().removeFocusListener(focusListener); + } + super.componentRemoved(e); + } + + @Override + public void mousePressed(@NotNull final MouseEvent e) { + super.mousePressed(e); + tabPane.requestFocus(); + } + + @Override + public void mouseDragged(final MouseEvent e) { + super.mouseDragged(e); + if (!dndEnabled) return; + + int index = tabForCoordinate(tabPane, e.getX(), e.getY()); + if (index >= 0 && index < tabPane.getTabCount()) { + TransferHandler handler = tabPane.getTransferHandler(); + handler.exportAsDrag(tabPane, e, TransferHandler.MOVE); + } + } + } + + public class DarkScrollHandler extends DarkHandler { + + @Override + public void mousePressed(final MouseEvent e) { + scrollableTabSupport.timer.stop(); + super.mousePressed(convertEvent(e)); + scrollableTabSupport.endScroll(); + } + + @Override + public void mouseClicked(final MouseEvent e) { + super.mouseClicked(convertEvent(e)); + } + + @Override + public void mouseReleased(final MouseEvent e) { + super.mouseReleased(convertEvent(e)); + } + + @Override + public void mouseEntered(final MouseEvent e) { + super.mouseEntered(convertEvent(e)); + } + + @Override + public void mouseExited(final MouseEvent e) { + super.mouseExited(convertEvent(e)); + } + + @Override + public void mouseMoved(final MouseEvent e) { + super.mouseMoved(convertEvent(e)); + } + + @Override + public void mouseDragged(final MouseEvent e) { + super.mouseDragged(convertEvent(e)); + } + } + + protected MouseEvent convertEvent(@NotNull final MouseEvent e) { + var p = e.getPoint(); + var pos = scrollableTabSupport.viewport.getLocation(); + p.x += pos.x; + p.y += pos.y; + return new MouseEvent(e.getComponent(), e.getID(), e.getWhen(), e.getModifiersEx(), + p.x, p.y, e.getClickCount(), e.isPopupTrigger(), e.getButton()); } } diff --git a/src/main/java/com/weis/darklaf/ui/tabbedpane/DarkTabbedPaneUIBridge.java b/src/main/java/com/weis/darklaf/ui/tabbedpane/DarkTabbedPaneUIBridge.java index d56d8651..f3114b8e 100644 --- a/src/main/java/com/weis/darklaf/ui/tabbedpane/DarkTabbedPaneUIBridge.java +++ b/src/main/java/com/weis/darklaf/ui/tabbedpane/DarkTabbedPaneUIBridge.java @@ -16,7 +16,6 @@ import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicArrowButton; import javax.swing.plaf.basic.BasicGraphicsUtils; import javax.swing.plaf.basic.BasicHTML; -import javax.swing.plaf.basic.BasicTabbedPaneUI; import javax.swing.text.View; import java.awt.*; import java.awt.event.ActionEvent; @@ -35,176 +34,184 @@ import java.beans.PropertyChangeListener; import java.util.Hashtable; import java.util.Vector; -public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements SwingConstants { +/** + * A Basic L&F implementation of TabbedPaneUI. + * + * @author Amy Fowler + * @author Philip Milne + * @author Steve Wilson + * @author Tom Santos + * @author Dave Moore + */ +public class DarkTabbedPaneUIBridge extends TabbedPaneUI implements SwingConstants { + // Instance variables initialized at installation - /** - * The tab pane - */ + + /** The tab pane */ protected JTabbedPane tabPane; - /** - * Highlight color - */ + /** Highlight color */ protected Color highlight; - /** - * Light highlight color - */ + /** Light highlight color */ protected Color lightHighlight; - /** - * Shadow color - */ + /** Shadow color */ protected Color shadow; - /** - * Dark shadow color - */ + /** Dark shadow color */ protected Color darkShadow; - /** - * Focus color - */ + /** Focus color */ protected Color focus; - private Color selectedColor; + protected Color selectedColor; - /** - * Text icon gap - */ + /** Text icon gap */ protected int textIconGap; - /** - * Tab run overlay - */ + /** Tab run overlay */ protected int tabRunOverlay; - /** - * Tab insets - */ + /** Tab insets */ protected Insets tabInsets; - /** - * Selected tab insets - */ + /** Selected tab insets */ protected Insets selectedTabPadInsets; - /** - * Tab area insets - */ + /** Tab area insets */ protected Insets tabAreaInsets; - /** - * Content border insets - */ + /** Content border insets */ protected Insets contentBorderInsets; - private boolean tabsOverlapBorder; - private boolean tabsOpaque = true; - private boolean contentOpaque = true; + protected boolean tabsOverlapBorder; + protected boolean tabsOpaque = true; + protected boolean contentOpaque = true; -// Transient variables (recalculated each time TabbedPane is layed out) /** - * Tab runs + * As of Java 2 platform v1.3 this previously undocumented field is no + * longer used. + * Key bindings are now defined by the LookAndFeel, please refer to + * the key bindings specification for further details. + * + * @deprecated As of Java 2 platform v1.3. */ - protected int[] tabRuns = new int[10]; + @Deprecated + protected KeyStroke upKey; /** - * Run count + * As of Java 2 platform v1.3 this previously undocumented field is no + * longer used. + * Key bindings are now defined by the LookAndFeel, please refer to + * the key bindings specification for further details. + * + * @deprecated As of Java 2 platform v1.3. */ - protected int runCount = 0; + @Deprecated + protected KeyStroke downKey; /** - * Selected run + * As of Java 2 platform v1.3 this previously undocumented field is no + * longer used. + * Key bindings are now defined by the LookAndFeel, please refer to + * the key bindings specification for further details. + * + * @deprecated As of Java 2 platform v1.3. */ - protected int selectedRun = -1; + @Deprecated + protected KeyStroke leftKey; /** - * Tab rects + * As of Java 2 platform v1.3 this previously undocumented field is no + * longer used. + * Key bindings are now defined by the LookAndFeel, please refer to + * the key bindings specification for further details. + * + * @deprecated As of Java 2 platform v1.3. */ + @Deprecated + protected KeyStroke rightKey; + + +// Transient variables (recalculated each time TabbedPane is layed out) + /** Tab runs */ + protected int[] tabRuns = new int[10]; + /** Run count */ + protected int runCount = 0; + /** Selected run */ + protected int selectedRun = -1; + /** Tab rects */ protected Rectangle[] rects = new Rectangle[0]; - /** - * Maximum tab height - */ + /** Maximum tab height */ protected int maxTabHeight; - /** - * Maximum tab width - */ + /** Maximum tab width */ protected int maxTabWidth; // Listeners - /** - * Tab change listener - */ + /** Tab change listener */ protected ChangeListener tabChangeListener; - /** - * Property change listener - */ + /** Property change listener */ protected PropertyChangeListener propertyChangeListener; - /** - * Mouse change listener - */ + /** Mouse change listener */ protected MouseListener mouseListener; - /** - * Focus change listener - */ + /** Focus change listener */ protected FocusListener focusListener; // Private instance data - private final Insets currentPadInsets = new Insets(0, 0, 0, 0); - private final Insets currentTabAreaInsets = new Insets(0, 0, 0, 0); + protected Insets currentPadInsets = new Insets(0,0,0,0); + protected Insets currentTabAreaInsets = new Insets(0,0,0,0); - private Component visibleComponent; + protected Component visibleComponent; // PENDING(api): See comment for ContainerHandler - private Vector htmlViews; + protected Vector htmlViews; - private Hashtable mnemonicToIndexMap; + protected Hashtable mnemonicToIndexMap; /** * InputMap used for mnemonics. Only non-null if the JTabbedPane has * mnemonics associated with it. Lazily created in initMnemonics. */ - private InputMap mnemonicInputMap; + protected InputMap mnemonicInputMap; // For use when tabLayoutPolicy = SCROLL_TAB_LAYOUT - private ScrollableTabSupport tabScroller; + protected ScrollableTabSupport tabScroller; - private TabContainer tabContainer; + protected TabContainer tabContainer; /** * A rectangle used for general layout calculations in order * to avoid constructing many new Rectangles on the fly. */ - protected transient Rectangle calcRect = new Rectangle(0, 0, 0, 0); + protected transient Rectangle calcRect = new Rectangle(0,0,0,0); /** * Tab that has focus. */ - private int focusIndex; + protected int focusIndex; /** * Combined listeners. */ - private Handler handler; + protected Handler handler; /** * Index of the tab the mouse is over. */ - private int rolloverTabIndex; + protected int rolloverTabIndex; /** * This is set to true when a component is added/removed from the tab * pane and set to false when layout happens. If true it indicates that * tabRuns is not valid and shouldn't be used. */ - private boolean isRunsDirty; + protected boolean isRunsDirty; - private boolean calculatedBaseline; - private int baseline; + protected boolean calculatedBaseline; + protected int baseline; // UI creation /** * Create a UI. - * * @param c a component * @return a UI */ - public static ComponentUI createUI(final JComponent c) { - return new BasicTabbedPaneUI(); + public static ComponentUI createUI(JComponent c) { + return new DarkTabbedPaneUIBridge(); } - static void loadActionMap(final LazyActionMap map) { + static void loadActionMap(LazyActionMap map) { map.put(new Actions(Actions.NEXT)); map.put(new Actions(Actions.PREVIOUS)); map.put(new Actions(Actions.RIGHT)); @@ -223,8 +230,8 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // UI Installation/De-installation - public void installUI(final JComponent c) { - this.tabPane = (JTabbedPane) c; + public void installUI(JComponent c) { + this.tabPane = (JTabbedPane)c; calculatedBaseline = false; rolloverTabIndex = -1; @@ -236,7 +243,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi installKeyboardActions(); } - public void uninstallUI(final JComponent c) { + public void uninstallUI(JComponent c) { uninstallKeyboardActions(); uninstallListeners(); uninstallDefaults(); @@ -252,6 +259,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * the JTabbedPane. * * @return a layout manager object + * * @see TabbedPaneLayout * @see javax.swing.JTabbedPane#getTabLayoutPolicy */ @@ -268,7 +276,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * UI uses the installed layoutManager (and not tabLayoutPolicy) to * determine if scrollTabLayout is enabled. */ - private boolean scrollableTabLayoutEnabled() { + protected boolean scrollableTabLayoutEnabled() { return (tabPane.getLayout() instanceof TabbedPaneScrollLayout); } @@ -281,24 +289,24 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi protected void installComponents() { if (scrollableTabLayoutEnabled()) { if (tabScroller == null) { - tabScroller = new ScrollableTabSupport(); + tabScroller = new ScrollableTabSupport(tabPane.getTabPlacement()); tabPane.add(tabScroller.viewport); } } installTabContainer(); } - private void installTabContainer() { + protected void installTabContainer() { for (int i = 0; i < tabPane.getTabCount(); i++) { Component tabComponent = tabPane.getTabComponentAt(i); if (tabComponent != null) { - if (tabContainer == null) { + if(tabContainer == null) { tabContainer = new TabContainer(); } tabContainer.add(tabComponent); } } - if (tabContainer == null) { + if(tabContainer == null) { return; } if (scrollableTabLayoutEnabled()) { @@ -314,15 +322,15 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * returned JButton must be instance of UIResource. * * @param direction One of the SwingConstants constants: - * SOUTH, NORTH, EAST or WEST + * SOUTH, NORTH, EAST or WEST * @return Widget for user to - * @throws IllegalArgumentException if direction is not one of - * NORTH, SOUTH, EAST or WEST * @see javax.swing.JTabbedPane#setTabPlacement * @see javax.swing.SwingConstants + * @throws IllegalArgumentException if direction is not one of + * NORTH, SOUTH, EAST or WEST * @since 1.5 */ - protected JButton createScrollButton(final int direction) { + protected JButton createScrollButton(int direction) { if (direction != SOUTH && direction != NORTH && direction != EAST && direction != WEST) { throw new IllegalArgumentException("Direction must be one of: " + @@ -347,15 +355,15 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } } - private void uninstallTabContainer() { - if (tabContainer == null) { + protected void uninstallTabContainer() { + if(tabContainer == null) { return; } // Remove all the tabComponents, making sure not to notify // the tabbedpane. tabContainer.notifyTabbedPane = false; tabContainer.removeAll(); - if (scrollableTabLayoutEnabled()) { + if(scrollableTabLayoutEnabled()) { tabContainer.remove(tabScroller.croppedEdge); tabScroller.tabPanel.remove(tabContainer); } else { @@ -395,10 +403,10 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // Fix for 6711145 BasicTabbedPanuUI should not throw a NPE if these // keys are missing. So we are setting them to there default values here // if the keys are missing. - if (tabInsets == null) tabInsets = new Insets(0, 4, 1, 4); - if (selectedTabPadInsets == null) selectedTabPadInsets = new Insets(2, 2, 2, 1); - if (tabAreaInsets == null) tabAreaInsets = new Insets(3, 2, 0, 2); - if (contentBorderInsets == null) contentBorderInsets = new Insets(2, 2, 3, 3); + if (tabInsets == null) tabInsets = new Insets(0,4,1,4); + if (selectedTabPadInsets == null) selectedTabPadInsets = new Insets(2,2,2,1); + if (tabAreaInsets == null) tabAreaInsets = new Insets(3,2,0,2); + if (contentBorderInsets == null) contentBorderInsets = new Insets(2,2,3,3); } /** @@ -434,7 +442,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi tabPane.addFocusListener(focusListener); } tabPane.addContainerListener(getHandler()); - if (tabPane.getTabCount() > 0) { + if (tabPane.getTabCount()>0) { htmlViews = createHTMLVector(); } } @@ -454,7 +462,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } tabPane.removeContainerListener(getHandler()); - if (htmlViews != null) { + if (htmlViews!=null) { htmlViews.removeAllElements(); htmlViews = null; } @@ -471,7 +479,6 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Creates a mouse listener. - * * @return a mouse listener */ protected MouseListener createMouseListener() { @@ -480,7 +487,6 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Creates a focus listener. - * * @return a focus listener */ protected FocusListener createFocusListener() { @@ -489,7 +495,6 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Creates a change listener. - * * @return a change listener */ protected ChangeListener createChangeListener() { @@ -498,14 +503,13 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Creates a property change listener. - * * @return a property change listener */ protected PropertyChangeListener createPropertyChangeListener() { return getHandler(); } - private Handler getHandler() { + protected Handler getHandler() { if (handler == null) { handler = new Handler(); } @@ -525,17 +529,19 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi km = getInputMap(JComponent.WHEN_FOCUSED); SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, km); - LazyActionMap.installLazyActionMap(tabPane, BasicTabbedPaneUI.class, "TabbedPane.actionMap"); + LazyActionMap.installLazyActionMap(tabPane, DarkTabbedPaneUIBridge.class, + "TabbedPane.actionMap"); updateMnemonics(); } - InputMap getInputMap(final int condition) { + InputMap getInputMap(int condition) { if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) { - return (InputMap) DefaultLookup.get(tabPane, this, - "TabbedPane.ancestorInputMap"); - } else if (condition == JComponent.WHEN_FOCUSED) { - return (InputMap) DefaultLookup.get(tabPane, this, - "TabbedPane.focusInputMap"); + return (InputMap)DefaultLookup.get(tabPane, this, + "TabbedPane.ancestorInputMap"); + } + else if (condition == JComponent.WHEN_FOCUSED) { + return (InputMap)DefaultLookup.get(tabPane, this, + "TabbedPane.focusInputMap"); } return null; } @@ -561,7 +567,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * Reloads the mnemonics. This should be invoked when a memonic changes, * when the title of a mnemonic changes, or when tabs are added/removed. */ - private void updateMnemonics() { + protected void updateMnemonics() { resetMnemonics(); for (int counter = tabPane.getTabCount() - 1; counter >= 0; counter--) { @@ -576,7 +582,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Resets the mnemonics bindings to an empty state. */ - private void resetMnemonics() { + protected void resetMnemonics() { if (mnemonicToIndexMap != null) { mnemonicToIndexMap.clear(); mnemonicInputMap.clear(); @@ -586,24 +592,23 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Adds the specified mnemonic at the specified index. */ - @SuppressWarnings("MagicConstant") - private void addMnemonic(final int index, final int mnemonic) { + protected void addMnemonic(int index, int mnemonic) { if (mnemonicToIndexMap == null) { initMnemonics(); } mnemonicInputMap.put(KeyStroke.getKeyStroke(mnemonic, DarkUIUtil.getFocusAcceleratorKeyMask()), "setSelectedIndex"); mnemonicInputMap.put(KeyStroke.getKeyStroke(mnemonic, - SwingUtilities2 - .setAltGraphMask(DarkUIUtil.getFocusAcceleratorKeyMask())), + SwingUtilities2.setAltGraphMask( + DarkUIUtil.getFocusAcceleratorKeyMask())), "setSelectedIndex"); - mnemonicToIndexMap.put(mnemonic, index); + mnemonicToIndexMap.put(Integer.valueOf(mnemonic), Integer.valueOf(index)); } /** * Installs the state needed for mnemonics. */ - private void initMnemonics() { + protected void initMnemonics() { mnemonicToIndexMap = new Hashtable(); mnemonicInputMap = new ComponentInputMapUIResource(tabPane); mnemonicInputMap.setParent(SwingUtilities.getUIInputMap(tabPane, @@ -617,7 +622,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * Sets the tab the mouse is over by location. This is a cover method * for setRolloverTab(tabForCoordinate(x, y, false)). */ - private void setRolloverTab(final int x, final int y) { + protected void setRolloverTab(int x, int y) { // NOTE: // This calls in with false otherwise it could trigger a validate, // which should NOT happen if the user is only dragging the @@ -634,7 +639,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * @param index Index of the tab the mouse is over. * @since 1.5 */ - protected void setRolloverTab(final int index) { + protected void setRolloverTab(int index) { rolloverTabIndex = index; } @@ -650,12 +655,12 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi return rolloverTabIndex; } - public Dimension getMinimumSize(final JComponent c) { + public Dimension getMinimumSize(JComponent c) { // Default to LayoutManager's minimumLayoutSize return null; } - public Dimension getMaximumSize(final JComponent c) { + public Dimension getMaximumSize(JComponent c) { // Default to LayoutManager's maximumLayoutSize return null; } @@ -663,19 +668,19 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Returns the baseline. * - * @throws NullPointerException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} * @throws IllegalArgumentException {@inheritDoc} * @see javax.swing.JComponent#getBaseline(int, int) * @since 1.6 */ - public int getBaseline(final JComponent c, final int width, final int height) { + public int getBaseline(JComponent c, int width, int height) { super.getBaseline(c, width, height); int baseline = calculateBaselineIfNecessary(); if (baseline != -1) { int placement = tabPane.getTabPlacement(); Insets insets = tabPane.getInsets(); Insets tabAreaInsets = getTabAreaInsets(placement); - switch (placement) { + switch(placement) { case JTabbedPane.TOP: baseline += insets.top + tabAreaInsets.top; return baseline; @@ -701,9 +706,9 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * @since 1.6 */ public Component.BaselineResizeBehavior getBaselineResizeBehavior( - final JComponent c) { + JComponent c) { super.getBaselineResizeBehavior(c); - switch (tabPane.getTabPlacement()) { + switch(tabPane.getTabPlacement()) { case JTabbedPane.LEFT: case JTabbedPane.RIGHT: case JTabbedPane.TOP: @@ -718,13 +723,13 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * Returns the baseline for the specified tab. * * @param tab index of tab to get baseline for + * @exception IndexOutOfBoundsException if index is out of range + * (index < 0 || index >= tab count) * @return baseline or a value < 0 indicating there is no reasonable - * baseline - * @throws IndexOutOfBoundsException if index is out of range - * (index < 0 || index >= tab count) + * baseline * @since 1.6 */ - protected int getBaseline(final int tab) { + protected int getBaseline(int tab) { if (tabPane.getTabComponentAt(tab) != null) { int offset = getBaselineOffset(); if (offset != 0) { @@ -739,12 +744,13 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi int cellHeight = maxTabHeight - tabInsets.top - tabInsets.bottom; return c.getBaseline(pref.width, pref.height) + (cellHeight - pref.height) / 2 + tabInsets.top; - } else { + } + else { View view = getTextViewForTab(tab); if (view != null) { - int viewHeight = (int) view.getPreferredSpan(View.Y_AXIS); + int viewHeight = (int)view.getPreferredSpan(View.Y_AXIS); int baseline = BasicHTML.getHTMLBaseline( - view, (int) view.getPreferredSpan(View.X_AXIS), viewHeight); + view, (int)view.getPreferredSpan(View.X_AXIS), viewHeight); if (baseline >= 0) { return maxTabHeight / 2 - viewHeight / 2 + baseline + getBaselineOffset(); @@ -767,17 +773,19 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * @since 1.6 */ protected int getBaselineOffset() { - switch (tabPane.getTabPlacement()) { + switch(tabPane.getTabPlacement()) { case JTabbedPane.TOP: if (tabPane.getTabCount() > 1) { return 1; - } else { + } + else { return -1; } case JTabbedPane.BOTTOM: if (tabPane.getTabCount() > 1) { return -1; - } else { + } + else { return 1; } default: // RIGHT|LEFT @@ -785,7 +793,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } } - private int calculateBaselineIfNecessary() { + protected int calculateBaselineIfNecessary() { if (!calculatedBaseline) { calculatedBaseline = true; baseline = -1; @@ -796,25 +804,26 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi return baseline; } - private void calculateBaseline() { + protected void calculateBaseline() { int tabCount = tabPane.getTabCount(); int tabPlacement = tabPane.getTabPlacement(); maxTabHeight = calculateMaxTabHeight(tabPlacement); baseline = getBaseline(0); if (isHorizontalTabPlacement()) { - for (int i = 1; i < tabCount; i++) { + for(int i = 1; i < tabCount; i++) { if (getBaseline(i) != baseline) { baseline = -1; break; } } - } else { + } + else { // left/right, tabs may be different sizes. FontMetrics fontMetrics = getFontMetrics(); int fontHeight = fontMetrics.getHeight(); int height = calculateTabHeight(tabPlacement, 0, fontHeight); - for (int i = 1; i < tabCount; i++) { - int newHeight = calculateTabHeight(tabPlacement, i, fontHeight); + for(int i = 1; i < tabCount; i++) { + int newHeight = calculateTabHeight(tabPlacement, i,fontHeight); if (height != newHeight) { // assume different baseline baseline = -1; @@ -826,7 +835,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // UI Rendering - public void paint(final Graphics g, final JComponent c) { + public void paint(Graphics g, JComponent c) { int selectedIndex = tabPane.getSelectedIndex(); int tabPlacement = tabPane.getTabPlacement(); @@ -858,12 +867,13 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * tab count - 1, inclusive) or -1 if no tab is currently selected. * The handling of invalid parameters is unspecified. * - * @param g the graphics object to use for rendering - * @param tabPlacement the placement for the tabs within the JTabbedPane + * @param g the graphics object to use for rendering + * @param tabPlacement the placement for the tabs within the JTabbedPane * @param selectedIndex the tab index of the selected component + * * @since 1.4 */ - protected void paintTabArea(final Graphics g, final int tabPlacement, final int selectedIndex) { + protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) { int tabCount = tabPane.getTabCount(); Rectangle iconRect = new Rectangle(), @@ -873,8 +883,8 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // Paint tabRuns of tabs from back to front for (int i = runCount - 1; i >= 0; i--) { int start = tabRuns[i]; - int next = tabRuns[(i == runCount - 1) ? 0 : i + 1]; - int end = (next != 0 ? next - 1 : tabCount - 1); + int next = tabRuns[(i == runCount - 1)? 0 : i + 1]; + int end = (next != 0? next - 1: tabCount - 1); for (int j = start; j <= end; j++) { if (j != selectedIndex && rects[j].intersects(clipRect)) { paintTab(g, tabPlacement, rects, j, iconRect, textRect); @@ -891,17 +901,16 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Paints a tab. - * - * @param g the graphics + * @param g the graphics * @param tabPlacement the tab placement - * @param rects rectangles - * @param tabIndex the tab index - * @param iconRect the icon rectangle - * @param textRect the text rectangle + * @param rects rectangles + * @param tabIndex the tab index + * @param iconRect the icon rectangle + * @param textRect the text rectangle */ - protected void paintTab(final Graphics g, final int tabPlacement, - final Rectangle[] rects, final int tabIndex, - final Rectangle iconRect, final Rectangle textRect) { + protected void paintTab(Graphics g, int tabPlacement, + Rectangle[] rects, int tabIndex, + Rectangle iconRect, Rectangle textRect) { Rectangle tabRect = rects[tabIndex]; int selectedIndex = tabPane.getSelectedIndex(); boolean isSelected = selectedIndex == tabIndex; @@ -943,28 +952,147 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi iconRect, textRect, isSelected); } - private boolean isHorizontalTabPlacement() { + protected boolean isHorizontalTabPlacement() { return tabPane.getTabPlacement() == TOP || tabPane.getTabPlacement() == BOTTOM; } + /* This method will create and return a polygon shape for the given tab rectangle + * which has been cropped at the specified cropline with a torn edge visual. + * e.g. A "File" tab which has cropped been cropped just after the "i": + * ------------- + * | ..... | + * | . | + * | ... . | + * | . . | + * | . . | + * | . . | + * -------------- + * + * The x, y arrays below define the pattern used to create a "torn" edge + * segment which is repeated to fill the edge of the tab. + * For tabs placed on TOP and BOTTOM, this righthand torn edge is created by + * line segments which are defined by coordinates obtained by + * subtracting xCropLen[i] from (tab.x + tab.width) and adding yCroplen[i] + * to (tab.y). + * For tabs placed on LEFT or RIGHT, the bottom torn edge is created by + * subtracting xCropLen[i] from (tab.y + tab.height) and adding yCropLen[i] + * to (tab.x). + */ + protected static int[] xCropLen = {1,1,0,0,1,1,2,2}; + protected static int[] yCropLen = {0,3,3,6,6,9,9,12}; + protected static final int CROP_SEGMENT = 12; + + protected static Polygon createCroppedTabShape(int tabPlacement, Rectangle tabRect, int cropline) { + int rlen; + int start; + int end; + int ostart; + + switch(tabPlacement) { + case LEFT: + case RIGHT: + rlen = tabRect.width; + start = tabRect.x; + end = tabRect.x + tabRect.width; + ostart = tabRect.y + tabRect.height; + break; + case TOP: + case BOTTOM: + default: + rlen = tabRect.height; + start = tabRect.y; + end = tabRect.y + tabRect.height; + ostart = tabRect.x + tabRect.width; + } + int rcnt = rlen/CROP_SEGMENT; + if (rlen%CROP_SEGMENT > 0) { + rcnt++; + } + int npts = 2 + (rcnt*8); + int[] xp = new int[npts]; + int[] yp = new int[npts]; + int pcnt = 0; + + xp[pcnt] = ostart; + yp[pcnt++] = end; + xp[pcnt] = ostart; + yp[pcnt++] = start; + for(int i = 0; i < rcnt; i++) { + for(int j = 0; j < xCropLen.length; j++) { + xp[pcnt] = cropline - xCropLen[j]; + yp[pcnt] = start + (i*CROP_SEGMENT) + yCropLen[j]; + if (yp[pcnt] >= end) { + yp[pcnt] = end; + pcnt++; + break; + } + pcnt++; + } + } + if (tabPlacement == JTabbedPane.TOP || tabPlacement == JTabbedPane.BOTTOM) { + return new Polygon(xp, yp, pcnt); + + } else { // LEFT or RIGHT + return new Polygon(yp, xp, pcnt); + } + } + + /* If tabLayoutPolicy == SCROLL_TAB_LAYOUT, this method will paint an edge + * indicating the tab is cropped in the viewport display + */ + protected void paintCroppedTabEdge(Graphics g) { + int tabIndex = tabScroller.croppedEdge.getTabIndex(); + int cropline = tabScroller.croppedEdge.getCropline(); + int x,y; + switch(tabPane.getTabPlacement()) { + case LEFT: + case RIGHT: + x = rects[tabIndex].x; + y = cropline; + int xx = x; + g.setColor(shadow); + while(xx <= x+rects[tabIndex].width) { + for (int i=0; i < xCropLen.length; i+=2) { + g.drawLine(xx+yCropLen[i],y-xCropLen[i], + xx+yCropLen[i+1]-1,y-xCropLen[i+1]); + } + xx+=CROP_SEGMENT; + } + break; + case TOP: + case BOTTOM: + default: + x = cropline; + y = rects[tabIndex].y; + int yy = y; + g.setColor(shadow); + while(yy <= y+rects[tabIndex].height) { + for (int i=0; i < xCropLen.length; i+=2) { + g.drawLine(x-xCropLen[i],yy+yCropLen[i], + x-xCropLen[i+1],yy+yCropLen[i+1]-1); + } + yy+=CROP_SEGMENT; + } + } + } + /** * Laysout a label. - * * @param tabPlacement the tab placement - * @param metrics the font metric - * @param tabIndex the tab index - * @param title the title - * @param icon the icon - * @param tabRect the tab rectangle - * @param iconRect the icon rectangle - * @param textRect the text rectangle - * @param isSelected selection status + * @param metrics the font metric + * @param tabIndex the tab index + * @param title the title + * @param icon the icon + * @param tabRect the tab rectangle + * @param iconRect the icon rectangle + * @param textRect the text rectangle + * @param isSelected selection status */ - protected void layoutLabel(final int tabPlacement, - final FontMetrics metrics, final int tabIndex, - final String title, final Icon icon, - final Rectangle tabRect, final Rectangle iconRect, - final Rectangle textRect, final boolean isSelected) { + protected void layoutLabel(int tabPlacement, + FontMetrics metrics, int tabIndex, + String title, Icon icon, + Rectangle tabRect, Rectangle iconRect, + Rectangle textRect, boolean isSelected ) { textRect.x = textRect.y = iconRect.x = iconRect.y = 0; View v = getTextViewForTab(tabIndex); @@ -995,21 +1123,20 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Paints an icon. - * - * @param g the graphics + * @param g the graphics * @param tabPlacement the tab placement - * @param tabIndex the tab index - * @param icon the icon - * @param iconRect the icon rectangle - * @param isSelected selection status + * @param tabIndex the tab index + * @param icon the icon + * @param iconRect the icon rectangle + * @param isSelected selection status */ - protected void paintIcon(final Graphics g, final int tabPlacement, - final int tabIndex, final Icon icon, final Rectangle iconRect, - final boolean isSelected) { + protected void paintIcon(Graphics g, int tabPlacement, + int tabIndex, Icon icon, Rectangle iconRect, + boolean isSelected ) { if (icon != null) { // Clip the icon within iconRect bounds Shape oldClip = g.getClip(); - ((Graphics2D) g).clip(iconRect); + ((Graphics2D)g).clip(iconRect); icon.paintIcon(tabPane, g, iconRect.x, iconRect.y); g.setClip(oldClip); } @@ -1017,20 +1144,19 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Paints text. - * - * @param g the graphics + * @param g the graphics * @param tabPlacement the tab placement - * @param font the font - * @param metrics the font metrics - * @param tabIndex the tab index - * @param title the title - * @param textRect the text rectangle - * @param isSelected selection status + * @param font the font + * @param metrics the font metrics + * @param tabIndex the tab index + * @param title the title + * @param textRect the text rectangle + * @param isSelected selection status */ - protected void paintText(final Graphics g, final int tabPlacement, - final Font font, final FontMetrics metrics, final int tabIndex, - final String title, final Rectangle textRect, - final boolean isSelected) { + protected void paintText(Graphics g, int tabPlacement, + Font font, FontMetrics metrics, int tabIndex, + String title, Rectangle textRect, + boolean isSelected) { g.setFont(font); @@ -1072,13 +1198,12 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Returns the tab label shift x. - * * @param tabPlacement the tab placement - * @param tabIndex the tab index - * @param isSelected selection status + * @param tabIndex the tab index + * @param isSelected selection status * @return the tab label shift x */ - protected int getTabLabelShiftX(final int tabPlacement, final int tabIndex, final boolean isSelected) { + protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) { Rectangle tabRect = rects[tabIndex]; String propKey = (isSelected ? "selectedLabelShift" : "labelShift"); int nudge = DefaultLookup.getInt( @@ -1098,13 +1223,12 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Returns the tab label shift y. - * * @param tabPlacement the tab placement - * @param tabIndex the tab index - * @param isSelected selection status + * @param tabIndex the tab index + * @param isSelected selection status * @return the tab label shift y */ - protected int getTabLabelShiftY(final int tabPlacement, final int tabIndex, final boolean isSelected) { + protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) { Rectangle tabRect = rects[tabIndex]; int nudge = (isSelected ? DefaultLookup.getInt(tabPane, this, "TabbedPane.selectedLabelShift", -1) : DefaultLookup.getInt(tabPane, this, "TabbedPane.labelShift", 1)); @@ -1123,24 +1247,23 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Paints the focus indicator. - * - * @param g the graphics + * @param g the graphics * @param tabPlacement the tab placement - * @param rects rectangles - * @param tabIndex the tab index - * @param iconRect the icon rectangle - * @param textRect the text rectangle - * @param isSelected selection status + * @param rects rectangles + * @param tabIndex the tab index + * @param iconRect the icon rectangle + * @param textRect the text rectangle + * @param isSelected selection status */ - protected void paintFocusIndicator(final Graphics g, final int tabPlacement, - final Rectangle[] rects, final int tabIndex, - final Rectangle iconRect, final Rectangle textRect, - final boolean isSelected) { + protected void paintFocusIndicator(Graphics g, int tabPlacement, + Rectangle[] rects, int tabIndex, + Rectangle iconRect, Rectangle textRect, + boolean isSelected) { Rectangle tabRect = rects[tabIndex]; if (tabPane.hasFocus() && isSelected) { int x, y, w, h; g.setColor(focus); - switch (tabPlacement) { + switch(tabPlacement) { case LEFT: x = tabRect.x + 3; y = tabRect.y + 3; @@ -1175,119 +1298,117 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * note that this function does now draw the background of the tab. * that is done elsewhere * - * @param g the graphics context in which to paint - * @param tabPlacement the placement (left, right, bottom, top) of the tab - * @param tabIndex the index of the tab with respect to other tabs - * @param x the x coordinate of tab - * @param y the y coordinate of tab - * @param w the width of the tab - * @param h the height of the tab - * @param isSelected a {@code boolean} which determines whether or not - * the tab is selected + * @param g the graphics context in which to paint + * @param tabPlacement the placement (left, right, bottom, top) of the tab + * @param tabIndex the index of the tab with respect to other tabs + * @param x the x coordinate of tab + * @param y the y coordinate of tab + * @param w the width of the tab + * @param h the height of the tab + * @param isSelected a {@code boolean} which determines whether or not + * the tab is selected */ - protected void paintTabBorder(final Graphics g, final int tabPlacement, - final int tabIndex, - final int x, final int y, final int w, final int h, - final boolean isSelected) { + protected void paintTabBorder(Graphics g, int tabPlacement, + int tabIndex, + int x, int y, int w, int h, + boolean isSelected ) { g.setColor(lightHighlight); switch (tabPlacement) { case LEFT: - g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2); // bottom-left highlight - g.drawLine(x, y + 2, x, y + h - 3); // left highlight - g.drawLine(x + 1, y + 1, x + 1, y + 1); // top-left highlight - g.drawLine(x + 2, y, x + w - 1, y); // top highlight + g.drawLine(x+1, y+h-2, x+1, y+h-2); // bottom-left highlight + g.drawLine(x, y+2, x, y+h-3); // left highlight + g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight + g.drawLine(x+2, y, x+w-1, y); // top highlight g.setColor(shadow); - g.drawLine(x + 2, y + h - 2, x + w - 1, y + h - 2); // bottom shadow + g.drawLine(x+2, y+h-2, x+w-1, y+h-2); // bottom shadow g.setColor(darkShadow); - g.drawLine(x + 2, y + h - 1, x + w - 1, y + h - 1); // bottom dark shadow + g.drawLine(x+2, y+h-1, x+w-1, y+h-1); // bottom dark shadow break; case RIGHT: - g.drawLine(x, y, x + w - 3, y); // top highlight + g.drawLine(x, y, x+w-3, y); // top highlight g.setColor(shadow); - g.drawLine(x, y + h - 2, x + w - 3, y + h - 2); // bottom shadow - g.drawLine(x + w - 2, y + 2, x + w - 2, y + h - 3); // right shadow + g.drawLine(x, y+h-2, x+w-3, y+h-2); // bottom shadow + g.drawLine(x+w-2, y+2, x+w-2, y+h-3); // right shadow g.setColor(darkShadow); - g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1); // top-right dark shadow - g.drawLine(x + w - 2, y + h - 2, x + w - 2, y + h - 2); // bottom-right dark shadow - g.drawLine(x + w - 1, y + 2, x + w - 1, y + h - 3); // right dark shadow - g.drawLine(x, y + h - 1, x + w - 3, y + h - 1); // bottom dark shadow + g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right dark shadow + g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow + g.drawLine(x+w-1, y+2, x+w-1, y+h-3); // right dark shadow + g.drawLine(x, y+h-1, x+w-3, y+h-1); // bottom dark shadow break; case BOTTOM: - g.drawLine(x, y, x, y + h - 3); // left highlight - g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2); // bottom-left highlight + g.drawLine(x, y, x, y+h-3); // left highlight + g.drawLine(x+1, y+h-2, x+1, y+h-2); // bottom-left highlight g.setColor(shadow); - g.drawLine(x + 2, y + h - 2, x + w - 3, y + h - 2); // bottom shadow - g.drawLine(x + w - 2, y, x + w - 2, y + h - 3); // right shadow + g.drawLine(x+2, y+h-2, x+w-3, y+h-2); // bottom shadow + g.drawLine(x+w-2, y, x+w-2, y+h-3); // right shadow g.setColor(darkShadow); - g.drawLine(x + 2, y + h - 1, x + w - 3, y + h - 1); // bottom dark shadow - g.drawLine(x + w - 2, y + h - 2, x + w - 2, y + h - 2); // bottom-right dark shadow - g.drawLine(x + w - 1, y, x + w - 1, y + h - 3); // right dark shadow + g.drawLine(x+2, y+h-1, x+w-3, y+h-1); // bottom dark shadow + g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow + g.drawLine(x+w-1, y, x+w-1, y+h-3); // right dark shadow break; case TOP: default: - g.drawLine(x, y + 2, x, y + h - 1); // left highlight - g.drawLine(x + 1, y + 1, x + 1, y + 1); // top-left highlight - g.drawLine(x + 2, y, x + w - 3, y); // top highlight + g.drawLine(x, y+2, x, y+h-1); // left highlight + g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight + g.drawLine(x+2, y, x+w-3, y); // top highlight g.setColor(shadow); - g.drawLine(x + w - 2, y + 2, x + w - 2, y + h - 1); // right shadow + g.drawLine(x+w-2, y+2, x+w-2, y+h-1); // right shadow g.setColor(darkShadow); - g.drawLine(x + w - 1, y + 2, x + w - 1, y + h - 1); // right dark-shadow - g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1); // top-right shadow + g.drawLine(x+w-1, y+2, x+w-1, y+h-1); // right dark-shadow + g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right shadow } } /** * Paints the tab background. - * - * @param g the graphics context in which to paint - * @param tabPlacement the placement (left, right, bottom, top) of the tab - * @param tabIndex the index of the tab with respect to other tabs - * @param x the x coordinate of tab - * @param y the y coordinate of tab - * @param w the width of the tab - * @param h the height of the tab - * @param isSelected a {@code boolean} which determines whether or not - * the tab is selected + * @param g the graphics context in which to paint + * @param tabPlacement the placement (left, right, bottom, top) of the tab + * @param tabIndex the index of the tab with respect to other tabs + * @param x the x coordinate of tab + * @param y the y coordinate of tab + * @param w the width of the tab + * @param h the height of the tab + * @param isSelected a {@code boolean} which determines whether or not + * the tab is selected */ - protected void paintTabBackground(final Graphics g, final int tabPlacement, - final int tabIndex, - final int x, final int y, final int w, final int h, - final boolean isSelected) { - g.setColor(!isSelected || selectedColor == null ? + protected void paintTabBackground(Graphics g, int tabPlacement, + int tabIndex, + int x, int y, int w, int h, + boolean isSelected ) { + g.setColor(!isSelected || selectedColor == null? tabPane.getBackgroundAt(tabIndex) : selectedColor); - switch (tabPlacement) { + switch(tabPlacement) { case LEFT: - g.fillRect(x + 1, y + 1, w - 1, h - 3); + g.fillRect(x+1, y+1, w-1, h-3); break; case RIGHT: - g.fillRect(x, y + 1, w - 2, h - 3); + g.fillRect(x, y+1, w-2, h-3); break; case BOTTOM: - g.fillRect(x + 1, y, w - 3, h - 1); + g.fillRect(x+1, y, w-3, h-1); break; case TOP: default: - g.fillRect(x + 1, y + 1, w - 3, h - 1); + g.fillRect(x+1, y+1, w-3, h-1); } } /** * Paints the content border. - * * @param g the graphics context in which to paint * @param tabPlacement the placement (left, right, bottom, top) of the tab * @param selectedIndex the tab index of the selected component */ - protected void paintContentBorder(final Graphics g, final int tabPlacement, final int selectedIndex) { + protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) { int width = tabPane.getWidth(); int height = tabPane.getHeight(); Insets insets = tabPane.getInsets(); @@ -1298,7 +1419,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi int w = width - insets.right - insets.left; int h = height - insets.top - insets.bottom; - switch (tabPlacement) { + switch(tabPlacement) { case LEFT: x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); if (tabsOverlapBorder) { @@ -1327,17 +1448,19 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi h -= (y - insets.top); } - if (tabPane.getTabCount() > 0 && (contentOpaque || tabPane.isOpaque())) { + if ( tabPane.getTabCount() > 0 && (contentOpaque || tabPane.isOpaque()) ) { // Fill region behind content area Color color = UIManager.getColor("TabbedPane.contentAreaColor"); if (color != null) { g.setColor(color); - } else if (selectedColor == null || selectedIndex == -1) { + } + else if ( selectedColor == null || selectedIndex == -1 ) { g.setColor(tabPane.getBackground()); - } else { + } + else { g.setColor(selectedColor); } - g.fillRect(x, y, w, h); + g.fillRect(x,y,w,h); } paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h); @@ -1349,7 +1472,6 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Paints the content border top edge. - * * @param g the graphics context in which to paint * @param tabPlacement the placement (left, right, bottom, top) of the tab * @param selectedIndex the tab index of the selected component @@ -1358,10 +1480,10 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * @param w the width of the tab * @param h the height of the tab */ - protected void paintContentBorderTopEdge(final Graphics g, final int tabPlacement, - final int selectedIndex, - final int x, final int y, final int w, final int h) { - Rectangle selRect = selectedIndex < 0 ? null : + protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, + int selectedIndex, + int x, int y, int w, int h) { + Rectangle selRect = selectedIndex < 0? null : getTabBounds(selectedIndex, calcRect); g.setColor(lightHighlight); @@ -1373,23 +1495,22 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi if (tabPlacement != TOP || selectedIndex < 0 || (selRect.y + selRect.height + 1 < y) || (selRect.x < x || selRect.x > x + w)) { - g.drawLine(x, y, x + w - 2, y); + g.drawLine(x, y, x+w-2, y); } else { // Break line to show visual connection to selected tab g.drawLine(x, y, selRect.x - 1, y); if (selRect.x + selRect.width < x + w - 2) { g.drawLine(selRect.x + selRect.width, y, - x + w - 2, y); + x+w-2, y); } else { g.setColor(shadow); - g.drawLine(x + w - 2, y, x + w - 2, y); + g.drawLine(x+w-2, y, x+w-2, y); } } } /** * Paints the content border left edge. - * * @param g the graphics context in which to paint * @param tabPlacement the placement (left, right, bottom, top) of the tab * @param selectedIndex the tab index of the selected component @@ -1398,10 +1519,10 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * @param w the width of the tab * @param h the height of the tab */ - protected void paintContentBorderLeftEdge(final Graphics g, final int tabPlacement, - final int selectedIndex, - final int x, final int y, final int w, final int h) { - Rectangle selRect = selectedIndex < 0 ? null : + protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement, + int selectedIndex, + int x, int y, int w, int h) { + Rectangle selRect = selectedIndex < 0? null : getTabBounds(selectedIndex, calcRect); g.setColor(lightHighlight); @@ -1413,20 +1534,19 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi if (tabPlacement != LEFT || selectedIndex < 0 || (selRect.x + selRect.width + 1 < x) || (selRect.y < y || selRect.y > y + h)) { - g.drawLine(x, y, x, y + h - 2); + g.drawLine(x, y, x, y+h-2); } else { // Break line to show visual connection to selected tab g.drawLine(x, y, x, selRect.y - 1); if (selRect.y + selRect.height < y + h - 2) { g.drawLine(x, selRect.y + selRect.height, - x, y + h - 2); + x, y+h-2); } } } /** * Paints the content border bottom edge. - * * @param g the graphics context in which to paint * @param tabPlacement the placement (left, right, bottom, top) of the tab * @param selectedIndex the tab index of the selected component @@ -1435,10 +1555,10 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * @param w the width of the tab * @param h the height of the tab */ - protected void paintContentBorderBottomEdge(final Graphics g, final int tabPlacement, - final int selectedIndex, - final int x, final int y, final int w, final int h) { - Rectangle selRect = selectedIndex < 0 ? null : + protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement, + int selectedIndex, + int x, int y, int w, int h) { + Rectangle selRect = selectedIndex < 0? null : getTabBounds(selectedIndex, calcRect); g.setColor(shadow); @@ -1450,19 +1570,19 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi if (tabPlacement != BOTTOM || selectedIndex < 0 || (selRect.y - 1 > h) || (selRect.x < x || selRect.x > x + w)) { - g.drawLine(x + 1, y + h - 2, x + w - 2, y + h - 2); + g.drawLine(x+1, y+h-2, x+w-2, y+h-2); g.setColor(darkShadow); - g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); + g.drawLine(x, y+h-1, x+w-1, y+h-1); } else { // Break line to show visual connection to selected tab - g.drawLine(x + 1, y + h - 2, selRect.x - 1, y + h - 2); + g.drawLine(x+1, y+h-2, selRect.x - 1, y+h-2); g.setColor(darkShadow); - g.drawLine(x, y + h - 1, selRect.x - 1, y + h - 1); + g.drawLine(x, y+h-1, selRect.x - 1, y+h-1); if (selRect.x + selRect.width < x + w - 2) { g.setColor(shadow); - g.drawLine(selRect.x + selRect.width, y + h - 2, x + w - 2, y + h - 2); + g.drawLine(selRect.x + selRect.width, y+h-2, x+w-2, y+h-2); g.setColor(darkShadow); - g.drawLine(selRect.x + selRect.width, y + h - 1, x + w - 1, y + h - 1); + g.drawLine(selRect.x + selRect.width, y+h-1, x+w-1, y+h-1); } } @@ -1470,7 +1590,6 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Paints the content border right edge. - * * @param g the graphics context in which to paint * @param tabPlacement the placement (left, right, bottom, top) of the tab * @param selectedIndex the tab index of the selected component @@ -1479,10 +1598,10 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * @param w the width of the tab * @param h the height of the tab */ - protected void paintContentBorderRightEdge(final Graphics g, final int tabPlacement, - final int selectedIndex, - final int x, final int y, final int w, final int h) { - Rectangle selRect = selectedIndex < 0 ? null : + protected void paintContentBorderRightEdge(Graphics g, int tabPlacement, + int selectedIndex, + int x, int y, int w, int h) { + Rectangle selRect = selectedIndex < 0? null : getTabBounds(selectedIndex, calcRect); g.setColor(shadow); @@ -1494,27 +1613,27 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi if (tabPlacement != RIGHT || selectedIndex < 0 || (selRect.x - 1 > w) || (selRect.y < y || selRect.y > y + h)) { - g.drawLine(x + w - 2, y + 1, x + w - 2, y + h - 3); + g.drawLine(x+w-2, y+1, x+w-2, y+h-3); g.setColor(darkShadow); - g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); + g.drawLine(x+w-1, y, x+w-1, y+h-1); } else { // Break line to show visual connection to selected tab - g.drawLine(x + w - 2, y + 1, x + w - 2, selRect.y - 1); + g.drawLine(x+w-2, y+1, x+w-2, selRect.y - 1); g.setColor(darkShadow); - g.drawLine(x + w - 1, y, x + w - 1, selRect.y - 1); + g.drawLine(x+w-1, y, x+w-1, selRect.y - 1); if (selRect.y + selRect.height < y + h - 2) { g.setColor(shadow); - g.drawLine(x + w - 2, selRect.y + selRect.height, - x + w - 2, y + h - 2); + g.drawLine(x+w-2, selRect.y + selRect.height, + x+w-2, y+h-2); g.setColor(darkShadow); - g.drawLine(x + w - 1, selRect.y + selRect.height, - x + w - 1, y + h - 2); + g.drawLine(x+w-1, selRect.y + selRect.height, + x+w-1, y+h-2); } } } - private void ensureCurrentLayout() { + protected void ensureCurrentLayout() { if (!tabPane.isValid()) { tabPane.validate(); } @@ -1523,7 +1642,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * is still invalid. See bug 4237677. */ if (!tabPane.isValid()) { - TabbedPaneLayout layout = (TabbedPaneLayout) tabPane.getLayout(); + TabbedPaneLayout layout = (TabbedPaneLayout)tabPane.getLayout(); layout.calculateLayoutInfo(); } } @@ -1535,13 +1654,13 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * Returns the bounds of the specified tab index. The bounds are * with respect to the JTabbedPane's coordinate space. */ - public Rectangle getTabBounds(final JTabbedPane pane, final int i) { + public Rectangle getTabBounds(JTabbedPane pane, int i) { ensureCurrentLayout(); Rectangle tabRect = new Rectangle(); return getTabBounds(i, tabRect); } - public int getTabRunCount(final JTabbedPane pane) { + public int getTabRunCount(JTabbedPane pane) { ensureCurrentLayout(); return runCount; } @@ -1550,12 +1669,12 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * Returns the tab index which intersects the specified point * in the JTabbedPane's coordinate space. */ - public int tabForCoordinate(final JTabbedPane pane, final int x, final int y) { + public int tabForCoordinate(JTabbedPane pane, int x, int y) { return tabForCoordinate(pane, x, y, true); } - private int tabForCoordinate(final JTabbedPane pane, final int x, final int y, - final boolean validateIfNecessary) { + protected int tabForCoordinate(JTabbedPane pane, int x, int y, + boolean validateIfNecessary) { if (validateIfNecessary) { ensureCurrentLayout(); } @@ -1597,11 +1716,12 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * The handling of invalid parameters is unspecified. * * @param tabIndex the index of the tab - * @param dest the rectangle where the result should be placed + * @param dest the rectangle where the result should be placed * @return the resulting rectangle + * * @since 1.4 */ - protected Rectangle getTabBounds(final int tabIndex, final Rectangle dest) { + protected Rectangle getTabBounds(int tabIndex, Rectangle dest) { dest.width = rects[tabIndex].width; dest.height = rects[tabIndex].height; @@ -1624,7 +1744,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * Returns the index of the tab closest to the passed in location, note * that the returned tab may not contain the location x,y. */ - private int getClosestTab(final int x, final int y) { + protected int getClosestTab(int x, int y) { int min = 0; int tabCount = Math.min(rects.length, tabPane.getTabCount()); int max = tabCount; @@ -1640,7 +1760,8 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi if (useX) { minLoc = rects[current].x; maxLoc = minLoc + rects[current].width; - } else { + } + else { minLoc = rects[current].y; maxLoc = minLoc + rects[current].height; } @@ -1649,12 +1770,14 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi if (min == max) { return Math.max(0, current - 1); } - } else if (want >= maxLoc) { + } + else if (want >= maxLoc) { min = current; if (max - min <= 1) { return Math.max(current + 1, tabCount - 1); } - } else { + } + else { return current; } } @@ -1666,7 +1789,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * JTabbedPane's coordinate space to the coordinate space of the * ScrollableTabPanel. This is used for SCROLL_TAB_LAYOUT ONLY. */ - private Point translatePointToTabPanel(final int srcx, final int srcy, final Point dest) { + protected Point translatePointToTabPanel(int srcx, int srcy, Point dest) { Point vpp = tabScroller.viewport.getLocation(); Point viewp = tabScroller.viewport.getViewPosition(); dest.x = srcx - vpp.x + viewp.x; @@ -1678,7 +1801,6 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Returns the visible component. - * * @return the visible component */ protected Component getVisibleComponent() { @@ -1687,10 +1809,9 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Sets the visible component. - * * @param component the component */ - protected void setVisibleComponent(final Component component) { + protected void setVisibleComponent(Component component) { if (visibleComponent != null && visibleComponent != component && visibleComponent.getParent() == tabPane @@ -1706,12 +1827,11 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Assure the rectangles are created. - * * @param tabCount the tab count */ - protected void assureRectsCreated(final int tabCount) { + protected void assureRectsCreated(int tabCount) { int rectArrayLen = rects.length; - if (tabCount != rectArrayLen) { + if (tabCount != rectArrayLen ) { Rectangle[] tempRectArray = new Rectangle[tabCount]; System.arraycopy(rects, 0, tempRectArray, 0, Math.min(rectArrayLen, tabCount)); @@ -1728,19 +1848,18 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi */ protected void expandTabRunsArray() { int rectLen = tabRuns.length; - int[] newArray = new int[rectLen + 10]; + int[] newArray = new int[rectLen+10]; System.arraycopy(tabRuns, 0, newArray, 0, runCount); tabRuns = newArray; } /** * Returns the run for a tab. - * * @param tabCount the tab count * @param tabIndex the tab index. * @return the run for a tab */ - protected int getRunForTab(final int tabCount, final int tabIndex) { + protected int getRunForTab(int tabCount, int tabIndex) { for (int i = 0; i < runCount; i++) { int first = tabRuns[i]; int last = lastTabInRun(tabCount, i); @@ -1753,72 +1872,66 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Returns the last tab in a run. - * * @param tabCount the tab count - * @param run the run + * @param run the run * @return the last tab in a run */ - protected int lastTabInRun(final int tabCount, final int run) { + protected int lastTabInRun(int tabCount, int run) { if (runCount == 1) { return tabCount - 1; } - int nextRun = (run == runCount - 1 ? 0 : run + 1); + int nextRun = (run == runCount - 1? 0 : run + 1); if (tabRuns[nextRun] == 0) { return tabCount - 1; } - return tabRuns[nextRun] - 1; + return tabRuns[nextRun]-1; } /** * Returns the tab run overlay. - * * @param tabPlacement the placement (left, right, bottom, top) of the tab * @return the tab run overlay */ - protected int getTabRunOverlay(final int tabPlacement) { + protected int getTabRunOverlay(int tabPlacement) { return tabRunOverlay; } /** * Returns the tab run indent. - * * @param tabPlacement the placement (left, right, bottom, top) of the tab - * @param run the tab run + * @param run the tab run * @return the tab run indent */ - protected int getTabRunIndent(final int tabPlacement, final int run) { + protected int getTabRunIndent(int tabPlacement, int run) { return 0; } /** * Returns whether or not the tab run should be padded. - * * @param tabPlacement the placement (left, right, bottom, top) of the tab - * @param run the tab run + * @param run the tab run * @return whether or not the tab run should be padded */ - protected boolean shouldPadTabRun(final int tabPlacement, final int run) { + protected boolean shouldPadTabRun(int tabPlacement, int run) { return runCount > 1; } /** * Returns whether or not the tab run should be rotated. - * * @param tabPlacement the placement (left, right, bottom, top) of the tab * @return whether or not the tab run should be rotated */ - protected boolean shouldRotateTabRuns(final int tabPlacement) { + protected boolean shouldRotateTabRuns(int tabPlacement) { return true; } /** * Returns the icon for a tab. - * * @param tabIndex the index of the tab * @return the icon for a tab */ - protected Icon getIconForTab(final int tabIndex) { - return (!tabPane.isEnabled() || !tabPane.isEnabledAt(tabIndex)) ? + protected Icon getIconForTab(int tabIndex) { + return (!tabPane.isEnabled() || !tabPane.isEnabledAt(tabIndex))? tabPane.getDisabledIconAt(tabIndex) : tabPane.getIconAt(tabIndex); } @@ -1829,10 +1942,11 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi * * @param tabIndex the index of the tab * @return the text view to render the tab's text or null if no - * specialized rendering is required + * specialized rendering is required + * * @since 1.4 */ - protected View getTextViewForTab(final int tabIndex) { + protected View getTextViewForTab(int tabIndex) { if (htmlViews != null) { return htmlViews.elementAt(tabIndex); } @@ -1841,13 +1955,12 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Calculates the tab height. - * - * @param tabPlacement the placement (left, right, bottom, top) of the tab - * @param tabIndex the index of the tab with respect to other tabs - * @param fontHeight the font height + * @param tabPlacement the placement (left, right, bottom, top) of the tab + * @param tabIndex the index of the tab with respect to other tabs + * @param fontHeight the font height * @return the tab height */ - protected int calculateTabHeight(final int tabPlacement, final int tabIndex, final int fontHeight) { + protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) { int height = 0; Component c = tabPane.getTabComponentAt(tabIndex); if (c != null) { @@ -1874,16 +1987,15 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Calculates the maximum tab height. - * - * @param tabPlacement the placement (left, right, bottom, top) of the tab + * @param tabPlacement the placement (left, right, bottom, top) of the tab * @return the maximum tab height */ - protected int calculateMaxTabHeight(final int tabPlacement) { + protected int calculateMaxTabHeight(int tabPlacement) { FontMetrics metrics = getFontMetrics(); int tabCount = tabPane.getTabCount(); int result = 0; int fontHeight = metrics.getHeight(); - for (int i = 0; i < tabCount; i++) { + for(int i = 0; i < tabCount; i++) { result = Math.max(calculateTabHeight(tabPlacement, i, fontHeight), result); } return result; @@ -1891,13 +2003,12 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Calculates the tab width. - * - * @param tabPlacement the placement (left, right, bottom, top) of the tab - * @param tabIndex the index of the tab with respect to other tabs - * @param metrics the font metrics + * @param tabPlacement the placement (left, right, bottom, top) of the tab + * @param tabIndex the index of the tab with respect to other tabs + * @param metrics the font metrics * @return the tab width */ - protected int calculateTabWidth(final int tabPlacement, final int tabIndex, final FontMetrics metrics) { + protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) { Insets tabInsets = getTabInsets(tabPlacement, tabIndex); int width = tabInsets.left + tabInsets.right + 3; Component tabComponent = tabPane.getTabComponentAt(tabIndex); @@ -1923,15 +2034,14 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Calculates the maximum tab width. - * - * @param tabPlacement the placement (left, right, bottom, top) of the tab + * @param tabPlacement the placement (left, right, bottom, top) of the tab * @return the maximum tab width */ - protected int calculateMaxTabWidth(final int tabPlacement) { + protected int calculateMaxTabWidth(int tabPlacement) { FontMetrics metrics = getFontMetrics(); int tabCount = tabPane.getTabCount(); int result = 0; - for (int i = 0; i < tabCount; i++) { + for(int i = 0; i < tabCount; i++) { result = Math.max(calculateTabWidth(tabPlacement, i, metrics), result); } return result; @@ -1939,84 +2049,77 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Calculates the tab area height. - * * @param tabPlacement the placement (left, right, bottom, top) of the tab * @param horizRunCount horizontal run count - * @param maxTabHeight maximum tab height + * @param maxTabHeight maximum tab height * @return the tab area height */ - protected int calculateTabAreaHeight(final int tabPlacement, final int horizRunCount, final int maxTabHeight) { + protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount, int maxTabHeight) { Insets tabAreaInsets = getTabAreaInsets(tabPlacement); int tabRunOverlay = getTabRunOverlay(tabPlacement); - return (horizRunCount > 0 ? - horizRunCount * (maxTabHeight - tabRunOverlay) + tabRunOverlay + + return (horizRunCount > 0? + horizRunCount * (maxTabHeight-tabRunOverlay) + tabRunOverlay + tabAreaInsets.top + tabAreaInsets.bottom : 0); } /** * Calculates the tab area width. - * - * @param tabPlacement the placement (left, right, bottom, top) of the tab + * @param tabPlacement the placement (left, right, bottom, top) of the tab * @param vertRunCount vertical run count - * @param maxTabWidth maximum tab width + * @param maxTabWidth maximum tab width * @return the tab area width */ - protected int calculateTabAreaWidth(final int tabPlacement, final int vertRunCount, final int maxTabWidth) { + protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount, int maxTabWidth) { Insets tabAreaInsets = getTabAreaInsets(tabPlacement); int tabRunOverlay = getTabRunOverlay(tabPlacement); - return (vertRunCount > 0 ? - vertRunCount * (maxTabWidth - tabRunOverlay) + tabRunOverlay + + return (vertRunCount > 0? + vertRunCount * (maxTabWidth-tabRunOverlay) + tabRunOverlay + tabAreaInsets.left + tabAreaInsets.right : 0); } /** * Returns the tab insets. - * - * @param tabPlacement the placement (left, right, bottom, top) of the tab - * @param tabIndex the tab index + * @param tabPlacement the placement (left, right, bottom, top) of the tab + * @param tabIndex the tab index * @return the tab insets */ - protected Insets getTabInsets(final int tabPlacement, final int tabIndex) { + protected Insets getTabInsets(int tabPlacement, int tabIndex) { return tabInsets; } /** * Returns the selected tab pad insets. - * - * @param tabPlacement the placement (left, right, bottom, top) of the tab + * @param tabPlacement the placement (left, right, bottom, top) of the tab * @return the selected tab pad insets */ - protected Insets getSelectedTabPadInsets(final int tabPlacement) { + protected Insets getSelectedTabPadInsets(int tabPlacement) { rotateInsets(selectedTabPadInsets, currentPadInsets, tabPlacement); return currentPadInsets; } /** * Returns the tab area insets. - * - * @param tabPlacement the placement (left, right, bottom, top) of the tab + * @param tabPlacement the placement (left, right, bottom, top) of the tab * @return the pad area insets */ - protected Insets getTabAreaInsets(final int tabPlacement) { + protected Insets getTabAreaInsets(int tabPlacement) { rotateInsets(tabAreaInsets, currentTabAreaInsets, tabPlacement); return currentTabAreaInsets; } /** * Returns the content border insets. - * - * @param tabPlacement the placement (left, right, bottom, top) of the tab + * @param tabPlacement the placement (left, right, bottom, top) of the tab * @return the content border insets */ - protected Insets getContentBorderInsets(final int tabPlacement) { + protected Insets getContentBorderInsets(int tabPlacement) { return contentBorderInsets; } /** * Returns the font metrics. - * * @return the font metrics */ protected FontMetrics getFontMetrics() { @@ -2029,10 +2132,9 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Navigate the selected tab. - * * @param direction the direction */ - protected void navigateSelectedTab(final int direction) { + protected void navigateSelectedTab(int direction) { int tabPlacement = tabPane.getTabPlacement(); int current = DefaultLookup.getBoolean(tabPane, this, "TabbedPane.selectionFollowsFocus", true) ? @@ -2046,10 +2148,10 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } int offset; - switch (tabPlacement) { + switch(tabPlacement) { case LEFT: case RIGHT: - switch (direction) { + switch(direction) { case NEXT: selectNextTab(current); break; @@ -2076,7 +2178,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi case BOTTOM: case TOP: default: - switch (direction) { + switch(direction) { case NEXT: selectNextTab(current); break; @@ -2112,14 +2214,13 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Select the next tab in the run. - * * @param current the current tab */ - protected void selectNextTabInRun(final int current) { + protected void selectNextTabInRun(int current) { int tabCount = tabPane.getTabCount(); int tabIndex = getNextTabIndexInRun(tabCount, current); - while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) { + while(tabIndex != current && !tabPane.isEnabledAt(tabIndex)) { tabIndex = getNextTabIndexInRun(tabCount, tabIndex); } navigateTo(tabIndex); @@ -2127,14 +2228,13 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Select the previous tab in the run. - * * @param current the current tab */ - protected void selectPreviousTabInRun(final int current) { + protected void selectPreviousTabInRun(int current) { int tabCount = tabPane.getTabCount(); int tabIndex = getPreviousTabIndexInRun(tabCount, current); - while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) { + while(tabIndex != current && !tabPane.isEnabledAt(tabIndex)) { tabIndex = getPreviousTabIndexInRun(tabCount, tabIndex); } navigateTo(tabIndex); @@ -2142,10 +2242,9 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Select the next tab. - * * @param current the current tab */ - protected void selectNextTab(final int current) { + protected void selectNextTab(int current) { int tabIndex = getNextTabIndex(current); while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) { @@ -2156,10 +2255,9 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Select the previous tab. - * * @param current the current tab */ - protected void selectPreviousTab(final int current) { + protected void selectPreviousTab(int current) { int tabIndex = getPreviousTabIndex(current); while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) { @@ -2170,29 +2268,28 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Selects an adjacent run of tabs. - * - * @param tabPlacement the placement (left, right, bottom, top) of the tab - * @param tabIndex the index of the tab with respect to other tabs - * @param offset selection offset + * @param tabPlacement the placement (left, right, bottom, top) of the tab + * @param tabIndex the index of the tab with respect to other tabs + * @param offset selection offset */ - protected void selectAdjacentRunTab(final int tabPlacement, - final int tabIndex, final int offset) { - if (runCount < 2) { + protected void selectAdjacentRunTab(int tabPlacement, + int tabIndex, int offset) { + if ( runCount < 2 ) { return; } int newIndex; Rectangle r = rects[tabIndex]; - switch (tabPlacement) { + switch(tabPlacement) { case LEFT: case RIGHT: - newIndex = tabForCoordinate(tabPane, r.x + r.width / 2 + offset, - r.y + r.height / 2); + newIndex = tabForCoordinate(tabPane, r.x + r.width/2 + offset, + r.y + r.height/2); break; case BOTTOM: case TOP: default: - newIndex = tabForCoordinate(tabPane, r.x + r.width / 2, - r.y + r.height / 2 + offset); + newIndex = tabForCoordinate(tabPane, r.x + r.width/2, + r.y + r.height/2 + offset); } if (newIndex != -1) { while (!tabPane.isEnabledAt(newIndex) && newIndex != tabIndex) { @@ -2202,7 +2299,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } } - private void navigateTo(final int index) { + protected void navigateTo(int index) { if (DefaultLookup.getBoolean(tabPane, this, "TabbedPane.selectionFollowsFocus", true)) { tabPane.setSelectedIndex(index); @@ -2212,12 +2309,13 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } } - void setFocusIndex(final int index, final boolean repaint) { + void setFocusIndex(int index, boolean repaint) { if (repaint && !isRunsDirty) { repaintTab(focusIndex); focusIndex = index; repaintTab(focusIndex); - } else { + } + else { focusIndex = index; } } @@ -2225,7 +2323,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Repaints the specified tab. */ - private void repaintTab(final int index) { + protected void repaintTab(int index) { // If we're not valid that means we will shortly be validated and // painted, which means we don't have to do anything here. if (!isRunsDirty && index >= 0 && index < tabPane.getTabCount()) { @@ -2236,7 +2334,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Makes sure the focusIndex is valid. */ - private void validateFocusIndex() { + protected void validateFocusIndex() { if (focusIndex >= tabPane.getTabCount()) { setFocusIndex(tabPane.getSelectedIndex(), false); } @@ -2254,73 +2352,72 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Returns the tab run offset. - * - * @param tabPlacement the placement (left, right, bottom, top) of the tab - * @param tabCount the tab count - * @param tabIndex the index of the tab with respect to other tabs - * @param forward forward or not + * @param tabPlacement the placement (left, right, bottom, top) of the tab + * @param tabCount the tab count + * @param tabIndex the index of the tab with respect to other tabs + * @param forward forward or not * @return the tab run offset */ - protected int getTabRunOffset(final int tabPlacement, final int tabCount, - final int tabIndex, final boolean forward) { + protected int getTabRunOffset(int tabPlacement, int tabCount, + int tabIndex, boolean forward) { int run = getRunForTab(tabCount, tabIndex); int offset; - switch (tabPlacement) { + switch(tabPlacement) { case LEFT: { if (run == 0) { - offset = (forward ? - -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth) - maxTabWidth) : + offset = (forward? + -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth) : -maxTabWidth); } else if (run == runCount - 1) { - offset = (forward ? + offset = (forward? maxTabWidth : - calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth) - maxTabWidth); + calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth); } else { - offset = (forward ? maxTabWidth : -maxTabWidth); + offset = (forward? maxTabWidth : -maxTabWidth); } break; } case RIGHT: { if (run == 0) { - offset = (forward ? + offset = (forward? maxTabWidth : - calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth) - maxTabWidth); + calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth); } else if (run == runCount - 1) { - offset = (forward ? - -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth) - maxTabWidth) : + offset = (forward? + -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth) : -maxTabWidth); } else { - offset = (forward ? maxTabWidth : -maxTabWidth); + offset = (forward? maxTabWidth : -maxTabWidth); } break; } case BOTTOM: { if (run == 0) { - offset = (forward ? + offset = (forward? maxTabHeight : - calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight) - maxTabHeight); + calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight); } else if (run == runCount - 1) { - offset = (forward ? - -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight) - maxTabHeight) : + offset = (forward? + -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight) : -maxTabHeight); } else { - offset = (forward ? maxTabHeight : -maxTabHeight); + offset = (forward? maxTabHeight : -maxTabHeight); } break; } case TOP: default: { if (run == 0) { - offset = (forward ? - -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight) - maxTabHeight) : + offset = (forward? + -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight) : -maxTabHeight); } else if (run == runCount - 1) { - offset = (forward ? + offset = (forward? maxTabHeight : - calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight) - maxTabHeight); + calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight); } else { - offset = (forward ? maxTabHeight : -maxTabHeight); + offset = (forward? maxTabHeight : -maxTabHeight); } } } @@ -2329,33 +2426,30 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Returns the previous tab index. - * * @param base the base * @return the previous tab index */ - protected int getPreviousTabIndex(final int base) { - int tabIndex = (base - 1 >= 0 ? base - 1 : tabPane.getTabCount() - 1); - return (Math.max(tabIndex, 0)); + protected int getPreviousTabIndex(int base) { + int tabIndex = (base - 1 >= 0? base - 1 : tabPane.getTabCount() - 1); + return (tabIndex >= 0? tabIndex : 0); } /** * Returns the next tab index. - * * @param base the base * @return the next tab index */ - protected int getNextTabIndex(final int base) { - return (base + 1) % tabPane.getTabCount(); + protected int getNextTabIndex(int base) { + return (base+1)%tabPane.getTabCount(); } /** * Returns the next tab index in the run. - * * @param tabCount the tab count - * @param base the base + * @param base the base * @return the next tab index in the run */ - protected int getNextTabIndexInRun(final int tabCount, final int base) { + protected int getNextTabIndexInRun(int tabCount, int base) { if (runCount < 2) { return getNextTabIndex(base); } @@ -2369,54 +2463,50 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Returns the previous tab index in the run. - * * @param tabCount the tab count - * @param base the base + * @param base the base * @return the previous tab index in the run */ - protected int getPreviousTabIndexInRun(final int tabCount, final int base) { + protected int getPreviousTabIndexInRun(int tabCount, int base) { if (runCount < 2) { return getPreviousTabIndex(base); } int currentRun = getRunForTab(tabCount, base); if (base == tabRuns[currentRun]) { - int previous = tabRuns[getNextTabRun(currentRun)] - 1; - return (previous != -1 ? previous : tabCount - 1); + int previous = tabRuns[getNextTabRun(currentRun)]-1; + return (previous != -1? previous : tabCount-1); } return getPreviousTabIndex(base); } /** * Returns the previous tab run. - * * @param baseRun the base run * @return the previous tab run */ - protected int getPreviousTabRun(final int baseRun) { - int runIndex = (baseRun - 1 >= 0 ? baseRun - 1 : runCount - 1); - return (Math.max(runIndex, 0)); + protected int getPreviousTabRun(int baseRun) { + int runIndex = (baseRun - 1 >= 0? baseRun - 1 : runCount - 1); + return (runIndex >= 0? runIndex : 0); } /** * Returns the next tab run. - * * @param baseRun the base run * @return the next tab run */ - protected int getNextTabRun(final int baseRun) { - return (baseRun + 1) % runCount; + protected int getNextTabRun(int baseRun) { + return (baseRun+1)%runCount; } /** * Rotates the insets. - * - * @param topInsets the top insets - * @param targetInsets the target insets + * @param topInsets the top insets + * @param targetInsets the target insets * @param targetPlacement the target placement */ - protected static void rotateInsets(final Insets topInsets, final Insets targetInsets, final int targetPlacement) { + protected static void rotateInsets(Insets topInsets, Insets targetInsets, int targetPlacement) { - switch (targetPlacement) { + switch(targetPlacement) { case LEFT: targetInsets.top = topInsets.left; targetInsets.left = topInsets.top; @@ -2451,7 +2541,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi return SwingUtilities2.tabbedPaneChangeFocusTo(getVisibleComponent()); } - private static class Actions extends UIAction { + protected static class Actions extends UIAction { static final String NEXT = "navigateNext"; static final String PREVIOUS = "navigatePrevious"; static final String RIGHT = "navigateRight"; @@ -2468,73 +2558,84 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi static final String SCROLL_FORWARD = "scrollTabsForwardAction"; static final String SCROLL_BACKWARD = "scrollTabsBackwardAction"; - Actions(final String key) { + Actions(String key) { super(key); } - public void actionPerformed(final ActionEvent e) { + public void actionPerformed(ActionEvent e) { String key = getName(); - JTabbedPane pane = (JTabbedPane) e.getSource(); - DarkTabbedPaneUIBridge ui = (DarkTabbedPaneUIBridge) DarkUIUtil - .getUIOfType(pane.getUI(), - DarkTabbedPaneUIBridge.class); + JTabbedPane pane = (JTabbedPane)e.getSource(); + DarkTabbedPaneUIBridge ui = (DarkTabbedPaneUIBridge) DarkUIUtil.getUIOfType(pane.getUI(), DarkTabbedPaneUIBridge.class); if (ui == null) { return; } - if (NEXT.equals(key)) { + if (key == NEXT) { ui.navigateSelectedTab(SwingConstants.NEXT); - } else if (PREVIOUS.equals(key)) { + } + else if (key == PREVIOUS) { ui.navigateSelectedTab(SwingConstants.PREVIOUS); - } else if (RIGHT.equals(key)) { + } + else if (key == RIGHT) { ui.navigateSelectedTab(SwingConstants.EAST); - } else if (LEFT.equals(key)) { + } + else if (key == LEFT) { ui.navigateSelectedTab(SwingConstants.WEST); - } else if (UP.equals(key)) { + } + else if (key == UP) { ui.navigateSelectedTab(SwingConstants.NORTH); - } else if (DOWN.equals(key)) { + } + else if (key == DOWN) { ui.navigateSelectedTab(SwingConstants.SOUTH); - } else if (PAGE_UP.equals(key)) { + } + else if (key == PAGE_UP) { int tabPlacement = pane.getTabPlacement(); - if (tabPlacement == TOP || tabPlacement == BOTTOM) { + if (tabPlacement == TOP|| tabPlacement == BOTTOM) { ui.navigateSelectedTab(SwingConstants.WEST); } else { ui.navigateSelectedTab(SwingConstants.NORTH); } - } else if (PAGE_DOWN.equals(key)) { + } + else if (key == PAGE_DOWN) { int tabPlacement = pane.getTabPlacement(); if (tabPlacement == TOP || tabPlacement == BOTTOM) { ui.navigateSelectedTab(SwingConstants.EAST); } else { ui.navigateSelectedTab(SwingConstants.SOUTH); } - } else if (REQUEST_FOCUS.equals(key)) { + } + else if (key == REQUEST_FOCUS) { pane.requestFocus(); - } else if (REQUEST_FOCUS_FOR_VISIBLE.equals(key)) { + } + else if (key == REQUEST_FOCUS_FOR_VISIBLE) { ui.requestFocusForVisibleComponent(); - } else if (SET_SELECTED.equals(key)) { + } + else if (key == SET_SELECTED) { String command = e.getActionCommand(); if (command != null && command.length() > 0) { - int mnemonic = e.getActionCommand().charAt(0); - if (mnemonic >= 'a' && mnemonic <= 'z') { - mnemonic -= ('a' - 'A'); + int mnemonic = (int)e.getActionCommand().charAt(0); + if (mnemonic >= 'a' && mnemonic <='z') { + mnemonic -= ('a' - 'A'); } - Integer index = ui.mnemonicToIndexMap.get(mnemonic); - if (index != null && pane.isEnabledAt(index)) { - pane.setSelectedIndex(index); + Integer index = ui.mnemonicToIndexMap.get(Integer.valueOf(mnemonic)); + if (index != null && pane.isEnabledAt(index.intValue())) { + pane.setSelectedIndex(index.intValue()); } } - } else if (SELECT_FOCUSED.equals(key)) { + } + else if (key == SELECT_FOCUSED) { int focusIndex = ui.getFocusIndex(); if (focusIndex != -1) { pane.setSelectedIndex(focusIndex); } - } else if (SCROLL_FORWARD.equals(key)) { + } + else if (key == SCROLL_FORWARD) { if (ui.scrollableTabLayoutEnabled()) { ui.tabScroller.scrollForward(pane.getTabPlacement()); } - } else if (SCROLL_BACKWARD.equals(key)) { + } + else if (key == SCROLL_BACKWARD) { if (ui.scrollableTabLayoutEnabled()) { ui.tabScroller.scrollBackward(pane.getTabPlacement()); } @@ -2548,33 +2649,30 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi */ public class TabbedPaneLayout implements LayoutManager { - public void addLayoutComponent(final String name, final Component comp) { - } + public void addLayoutComponent(String name, Component comp) {} - public void removeLayoutComponent(final Component comp) { - } + public void removeLayoutComponent(Component comp) {} - public Dimension preferredLayoutSize(final Container parent) { + public Dimension preferredLayoutSize(Container parent) { return calculateSize(false); } - public Dimension minimumLayoutSize(final Container parent) { + public Dimension minimumLayoutSize(Container parent) { return calculateSize(true); } /** * Returns the calculated size. - * * @param minimum use the minimum size or preferred size * @return the calculated size */ - protected Dimension calculateSize(final boolean minimum) { + protected Dimension calculateSize(boolean minimum) { int tabPlacement = tabPane.getTabPlacement(); Insets insets = tabPane.getInsets(); Insets contentInsets = getContentBorderInsets(tabPlacement); Insets tabAreaInsets = getTabAreaInsets(tabPlacement); - Dimension zeroSize = new Dimension(0, 0); + Dimension zeroSize = new Dimension(0,0); int height = 0; int width = 0; int cWidth = 0; @@ -2603,7 +2701,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // Calculate how much space the tabs will need, based on the // minimum size required to display largest child + content border // - switch (tabPlacement) { + switch(tabPlacement) { case LEFT: case RIGHT: height = Math.max(height, calculateMaxTabHeight(tabPlacement)); @@ -2624,12 +2722,11 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Returns the preferred tab area height. - * * @param tabPlacement the tab placement - * @param width the width + * @param width the width * @return the preferred tab area height */ - protected int preferredTabAreaHeight(final int tabPlacement, final int width) { + protected int preferredTabAreaHeight(int tabPlacement, int width) { FontMetrics metrics = getFontMetrics(); int tabCount = tabPane.getTabCount(); int total = 0; @@ -2655,12 +2752,11 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Returns the preferred tab area width. - * * @param tabPlacement the tab placement - * @param height the height + * @param height the height * @return the preferred tab area widty */ - protected int preferredTabAreaWidth(final int tabPlacement, final int height) { + protected int preferredTabAreaWidth(int tabPlacement, int height) { FontMetrics metrics = getFontMetrics(); int tabCount = tabPane.getTabCount(); int total = 0; @@ -2685,11 +2781,9 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi return total; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @SuppressWarnings("deprecation") - public void layoutContainer(final Container parent) { + public void layoutContainer(Container parent) { /* Some of the code in this method deals with changing the * visibility of components to hide and show the contents for the * selected tab. This is older code that has since been duplicated @@ -2733,10 +2827,10 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // explicitly state we support this in the spec, but since // programs are now depending on this, we're making it work. // - if (selectedComponent != null) { - if (selectedComponent != visibleComponent && - visibleComponent != null) { - if (SwingUtilities.findFocusOwner(visibleComponent) != null) { + if(selectedComponent != null) { + if(selectedComponent != visibleComponent && + visibleComponent != null) { + if(SwingUtilities.findFocusOwner(visibleComponent) != null) { shouldChangeFocus = true; } } @@ -2746,9 +2840,9 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi Rectangle bounds = tabPane.getBounds(); int numChildren = tabPane.getComponentCount(); - if (numChildren > 0) { + if(numChildren > 0) { - switch (tabPlacement) { + switch(tabPlacement) { case LEFT: totalTabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); cx = insets.left + totalTabWidth + contentInsets.left; @@ -2778,9 +2872,9 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi insets.top - insets.bottom - contentInsets.top - contentInsets.bottom; - for (int i = 0; i < numChildren; i++) { + for(int i = 0; i < numChildren; i++) { Component child = tabPane.getComponent(i); - if (child == tabContainer) { + if(child == tabContainer) { int tabContainerWidth = totalTabWidth == 0 ? bounds.width : totalTabWidth + insets.left + insets.right + @@ -2791,9 +2885,9 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi int tabContainerX = 0; int tabContainerY = 0; - if (tabPlacement == BOTTOM) { + if(tabPlacement == BOTTOM) { tabContainerY = bounds.height - tabContainerHeight; - } else if (tabPlacement == RIGHT) { + } else if(tabPlacement == RIGHT) { tabContainerX = bounds.width - tabContainerWidth; } child.setBounds(tabContainerX, tabContainerY, tabContainerWidth, tabContainerHeight); @@ -2803,8 +2897,8 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } } layoutTabComponents(); - if (shouldChangeFocus) { - if (!requestFocusForVisibleComponent()) { + if(shouldChangeFocus) { + if(!requestFocusForVisibleComponent()) { tabPane.requestFocus(); } } @@ -2820,7 +2914,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi isRunsDirty = false; } - private void layoutTabComponents() { + protected void layoutTabComponents() { if (tabContainer == null) { return; } @@ -2854,11 +2948,10 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Calculate the tab rectangles. - * * @param tabPlacement the tab placement - * @param tabCount the tab count + * @param tabCount the tab count */ - protected void calculateTabRects(final int tabPlacement, final int tabCount) { + protected void calculateTabRects(int tabPlacement, int tabCount) { FontMetrics metrics = getFontMetrics(); Dimension size = tabPane.getSize(); Insets insets = tabPane.getInsets(); @@ -2875,7 +2968,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // // Calculate bounds within which a tab run must fit // - switch (tabPlacement) { + switch(tabPlacement) { case LEFT: maxTabWidth = calculateMaxTabWidth(tabPlacement); x = insets.left + tabAreaInsets.left; @@ -2920,7 +3013,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi if (!verticalTabRuns) { // Tabs on TOP or BOTTOM.... if (i > 0) { - rect.x = rects[i - 1].x + rects[i - 1].width; + rect.x = rects[i-1].x + rects[i-1].width; } else { tabRuns[0] = 0; runCount = 1; @@ -2948,7 +3041,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } else { // Tabs on LEFT or RIGHT... if (i > 0) { - rect.y = rects[i - 1].y + rects[i - 1].height; + rect.y = rects[i-1].y + rects[i-1].height; } else { tabRuns[0] = 0; runCount = 1; @@ -2981,7 +3074,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi if (runCount > 1) { // Re-distribute tabs in case last run has leftover space - normalizeTabRuns(tabPlacement, tabCount, verticalTabRuns ? y : x, returnAt); + normalizeTabRuns(tabPlacement, tabCount, verticalTabRuns? y : x, returnAt); selectedRun = getRunForTab(tabCount, selectedIndex); @@ -2995,8 +3088,8 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // tab y locations and to pad runs appropriately for (i = runCount - 1; i >= 0; i--) { int start = tabRuns[i]; - int next = tabRuns[i == (runCount - 1) ? 0 : i + 1]; - int end = (next != 0 ? next - 1 : tabCount - 1); + int next = tabRuns[i == (runCount - 1)? 0 : i + 1]; + int end = (next != 0? next - 1 : tabCount - 1); if (!verticalTabRuns) { for (j = start; j <= end; j++) { rect = rects[j]; @@ -3045,30 +3138,28 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Rotates the run-index array so that the selected run is run[0]. - * * @param tabPlacement the tab placement - * @param selectedRun the selected run + * @param selectedRun the selected run */ - protected void rotateTabRuns(final int tabPlacement, final int selectedRun) { + protected void rotateTabRuns(int tabPlacement, int selectedRun) { for (int i = 0; i < selectedRun; i++) { int save = tabRuns[0]; for (int j = 1; j < runCount; j++) { tabRuns[j - 1] = tabRuns[j]; } - tabRuns[runCount - 1] = save; + tabRuns[runCount-1] = save; } } /** * Normalizes the tab runs. - * * @param tabPlacement the tab placement - * @param tabCount the tab count - * @param start the start - * @param max the max + * @param tabCount the tab count + * @param start the start + * @param max the max */ - protected void normalizeTabRuns(final int tabPlacement, final int tabCount, - final int start, final int max) { + protected void normalizeTabRuns(int tabPlacement, int tabCount, + int start, int max) { boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT); int run = runCount - 1; boolean keepAdjusting = true; @@ -3087,16 +3178,16 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // while (keepAdjusting) { int last = lastTabInRun(tabCount, run); - int prevLast = lastTabInRun(tabCount, run - 1); + int prevLast = lastTabInRun(tabCount, run-1); int end; int prevLastLen; if (!verticalTabRuns) { end = rects[last].x + rects[last].width; - prevLastLen = (int) (maxTabWidth * weight); + prevLastLen = (int)(maxTabWidth*weight); } else { end = rects[last].y + rects[last].height; - prevLastLen = (int) (maxTabHeight * weight * 2); + prevLastLen = (int)(maxTabHeight*weight*2); } // Check if the run has enough extra space to fit the last tab @@ -3110,11 +3201,11 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } else { rects[prevLast].y = start; } - for (int i = prevLast + 1; i <= last; i++) { + for (int i = prevLast+1; i <= last; i++) { if (!verticalTabRuns) { - rects[i].x = rects[i - 1].x + rects[i - 1].width; + rects[i].x = rects[i-1].x + rects[i-1].width; } else { - rects[i].y = rects[i - 1].y + rects[i - 1].height; + rects[i].y = rects[i-1].y + rects[i-1].height; } } @@ -3137,38 +3228,37 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Pads the tab run. - * * @param tabPlacement the tab placement - * @param start the start - * @param end the end - * @param max the max + * @param start the start + * @param end the end + * @param max the max */ - protected void padTabRun(final int tabPlacement, final int start, final int end, final int max) { + protected void padTabRun(int tabPlacement, int start, int end, int max) { Rectangle lastRect = rects[end]; if (tabPlacement == TOP || tabPlacement == BOTTOM) { int runWidth = (lastRect.x + lastRect.width) - rects[start].x; int deltaWidth = max - (lastRect.x + lastRect.width); - float factor = (float) deltaWidth / (float) runWidth; + float factor = (float)deltaWidth / (float)runWidth; for (int j = start; j <= end; j++) { Rectangle pastRect = rects[j]; if (j > start) { - pastRect.x = rects[j - 1].x + rects[j - 1].width; + pastRect.x = rects[j-1].x + rects[j-1].width; } - pastRect.width += Math.round((float) pastRect.width * factor); + pastRect.width += Math.round((float)pastRect.width * factor); } lastRect.width = max - lastRect.x; } else { int runHeight = (lastRect.y + lastRect.height) - rects[start].y; int deltaHeight = max - (lastRect.y + lastRect.height); - float factor = (float) deltaHeight / (float) runHeight; + float factor = (float)deltaHeight / (float)runHeight; for (int j = start; j <= end; j++) { Rectangle pastRect = rects[j]; if (j > start) { - pastRect.y = rects[j - 1].y + rects[j - 1].height; + pastRect.y = rects[j-1].y + rects[j-1].height; } - pastRect.height += Math.round((float) pastRect.height * factor); + pastRect.height += Math.round((float)pastRect.height * factor); } lastRect.height = max - lastRect.y; } @@ -3176,11 +3266,10 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi /** * Pads selected tab. - * - * @param tabPlacement the tab placement + * @param tabPlacement the tab placement * @param selectedIndex the selected index */ - protected void padSelectedTab(final int tabPlacement, final int selectedIndex) { + protected void padSelectedTab(int tabPlacement, int selectedIndex) { if (selectedIndex >= 0) { Rectangle selRect = rects[selectedIndex]; @@ -3221,18 +3310,18 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } } - private class TabbedPaneScrollLayout extends TabbedPaneLayout { + protected class TabbedPaneScrollLayout extends TabbedPaneLayout { - protected int preferredTabAreaHeight(final int tabPlacement, final int width) { + protected int preferredTabAreaHeight(int tabPlacement, int width) { return calculateMaxTabHeight(tabPlacement); } - protected int preferredTabAreaWidth(final int tabPlacement, final int height) { + protected int preferredTabAreaWidth(int tabPlacement, int height) { return calculateMaxTabWidth(tabPlacement); } @SuppressWarnings("deprecation") - public void layoutContainer(final Container parent) { + public void layoutContainer(Container parent) { /* Some of the code in this method deals with changing the * visibility of components to hide and show the contents for the * selected tab. This is older code that has since been duplicated @@ -3280,10 +3369,10 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // explicitly state we support this in the spec, but since // programs are now depending on this, we're making it work. // - if (selectedComponent != null) { - if (selectedComponent != visibleComponent && - visibleComponent != null) { - if (SwingUtilities.findFocusOwner(visibleComponent) != null) { + if(selectedComponent != null) { + if(selectedComponent != visibleComponent && + visibleComponent != null) { + if(SwingUtilities.findFocusOwner(visibleComponent) != null) { shouldChangeFocus = true; } } @@ -3295,8 +3384,8 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi Rectangle bounds = tabPane.getBounds(); int numChildren = tabPane.getComponentCount(); - if (numChildren > 0) { - switch (tabPlacement) { + if(numChildren > 0) { + switch(tabPlacement) { case LEFT: // calculate tab area bounds tw = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); @@ -3359,23 +3448,23 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi contentInsets.top - contentInsets.bottom; } - for (int i = 0; i < numChildren; i++) { + for(int i = 0; i < numChildren; i++) { Component child = tabPane.getComponent(i); - if (tabScroller != null && child == tabScroller.viewport) { + if(tabScroller != null && child == tabScroller.viewport) { JViewport viewport = (JViewport) child; Rectangle viewRect = viewport.getViewRect(); int vw = tw; int vh = th; Dimension butSize = tabScroller.scrollForwardButton.getPreferredSize(); - switch (tabPlacement) { + switch(tabPlacement) { case LEFT: case RIGHT: int totalTabHeight = rects[tabCount - 1].y + rects[tabCount - 1].height; - if (totalTabHeight > th) { + if(totalTabHeight > th) { // Allow space for scrollbuttons vh = (th > 2 * butSize.height) ? th - 2 * butSize.height : 0; - if (totalTabHeight - viewRect.y <= vh) { + if(totalTabHeight - viewRect.y <= vh) { // Scrolled to the end, so ensure the viewport size is // such that the scroll offset aligns with a tab vh = totalTabHeight - viewRect.y; @@ -3386,10 +3475,10 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi case TOP: default: int totalTabWidth = rects[tabCount - 1].x + rects[tabCount - 1].width; - if (totalTabWidth > tw) { + if(totalTabWidth > tw) { // Need to allow space for scrollbuttons vw = (tw > 2 * butSize.width) ? tw - 2 * butSize.width : 0; - if (totalTabWidth - viewRect.x <= vw) { + if(totalTabWidth - viewRect.x <= vw) { // Scrolled to the end, so ensure the viewport size is // such that the scroll offset aligns with a tab vw = totalTabWidth - viewRect.x; @@ -3398,9 +3487,9 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } child.setBounds(tx, ty, vw, vh); - } else if (tabScroller != null && - (child == tabScroller.scrollForwardButton || - child == tabScroller.scrollBackwardButton)) { + } else if(tabScroller != null && + (child == tabScroller.scrollForwardButton || + child == tabScroller.scrollBackwardButton)) { Component scrollbutton = child; Dimension bsize = scrollbutton.getPreferredSize(); int bx = 0; @@ -3409,11 +3498,11 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi int bh = bsize.height; boolean visible = false; - switch (tabPlacement) { + switch(tabPlacement) { case LEFT: case RIGHT: int totalTabHeight = rects[tabCount - 1].y + rects[tabCount - 1].height; - if (totalTabHeight > th) { + if(totalTabHeight > th) { visible = true; bx = (tabPlacement == LEFT ? tx + tw - bsize.width : tx); by = (child == tabScroller.scrollForwardButton) ? @@ -3427,7 +3516,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi default: int totalTabWidth = rects[tabCount - 1].x + rects[tabCount - 1].width; - if (totalTabWidth > tw) { + if(totalTabWidth > tw) { visible = true; bx = (child == tabScroller.scrollForwardButton) ? bounds.width - insets.left - bsize.width : @@ -3436,7 +3525,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } } child.setVisible(visible); - if (visible) { + if(visible) { child.setBounds(bx, by, bw, bh); } @@ -3447,15 +3536,15 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } super.layoutTabComponents(); layoutCroppedEdge(); - if (shouldChangeFocus) { - if (!requestFocusForVisibleComponent()) { + if(shouldChangeFocus) { + if(!requestFocusForVisibleComponent()) { tabPane.requestFocus(); } } } } - private void layoutCroppedEdge() { + protected void layoutCroppedEdge() { tabScroller.croppedEdge.resetParams(); Rectangle viewRect = tabScroller.viewport.getViewRect(); int cropline; @@ -3466,8 +3555,8 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi case RIGHT: cropline = viewRect.y + viewRect.height; if ((tabRect.y < cropline) && (tabRect.y + tabRect.height > cropline)) { - tabScroller.croppedEdge.setParams(i, cropline - tabRect.y - 1 - ); + tabScroller.croppedEdge.setParams(i, cropline - tabRect.y - 1, + -currentTabAreaInsets.left, 0); } break; case TOP: @@ -3475,14 +3564,14 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi default: cropline = viewRect.x + viewRect.width; if ((tabRect.x < cropline - 1) && (tabRect.x + tabRect.width > cropline)) { - tabScroller.croppedEdge.setParams(i, cropline - tabRect.x - 1 - ); + tabScroller.croppedEdge.setParams(i, cropline - tabRect.x - 1, + 0, -currentTabAreaInsets.top); } } } } - protected void calculateTabRects(final int tabPlacement, final int tabCount) { + protected void calculateTabRects(int tabPlacement, int tabCount) { FontMetrics metrics = getFontMetrics(); Dimension size = tabPane.getSize(); Insets insets = tabPane.getInsets(); @@ -3500,7 +3589,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // // Calculate bounds within which a tab run must fit // - switch (tabPlacement) { + switch(tabPlacement) { case LEFT: case RIGHT: maxTabWidth = calculateMaxTabWidth(tabPlacement); @@ -3529,7 +3618,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi if (!verticalTabRuns) { // Tabs on TOP or BOTTOM.... if (i > 0) { - rect.x = rects[i - 1].x + rects[i - 1].width; + rect.x = rects[i-1].x + rects[i-1].width; } else { tabRuns[0] = 0; maxTabWidth = 0; @@ -3546,7 +3635,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } else { // Tabs on LEFT or RIGHT... if (i > 0) { - rect.y = rects[i - 1].y + rects[i - 1].height; + rect.y = rects[i-1].y + rects[i-1].height; } else { tabRuns[0] = 0; maxTabHeight = 0; @@ -3582,7 +3671,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } } - private class ScrollableTabSupport implements ActionListener, + protected class ScrollableTabSupport implements ActionListener, ChangeListener { public ScrollableTabViewport viewport; public ScrollableTabPanel tabPanel; @@ -3591,9 +3680,9 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi public CroppedEdge croppedEdge; public int leadingTabIndex; - private final Point tabViewPosition = new Point(0, 0); + protected Point tabViewPosition = new Point(0,0); - ScrollableTabSupport() { + ScrollableTabSupport(int tabPlacement) { viewport = new ScrollableTabViewport(); tabPanel = new ScrollableTabPanel(); viewport.setView(tabPanel); @@ -3623,13 +3712,11 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } scrollForwardButton.addActionListener(this); scrollBackwardButton.addActionListener(this); - scrollBackwardButton.addActionListener(System.out::println); - scrollForwardButton.addActionListener(System.out::println); tabPane.add(scrollForwardButton); tabPane.add(scrollBackwardButton); } - public void scrollForward(final int tabPlacement) { + public void scrollForward(int tabPlacement) { Dimension viewSize = viewport.getViewSize(); Rectangle viewRect = viewport.getViewRect(); @@ -3642,25 +3729,25 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi return; } } - setLeadingTabIndex(tabPlacement, leadingTabIndex + 1); + setLeadingTabIndex(tabPlacement, leadingTabIndex+1); } - public void scrollBackward(final int tabPlacement) { + public void scrollBackward(int tabPlacement) { if (leadingTabIndex == 0) { return; // no room left to scroll } - setLeadingTabIndex(tabPlacement, leadingTabIndex - 1); + setLeadingTabIndex(tabPlacement, leadingTabIndex-1); } - public void setLeadingTabIndex(final int tabPlacement, final int index) { + public void setLeadingTabIndex(int tabPlacement, int index) { leadingTabIndex = index; Dimension viewSize = viewport.getViewSize(); Rectangle viewRect = viewport.getViewRect(); - switch (tabPlacement) { + switch(tabPlacement) { case TOP: case BOTTOM: - tabViewPosition.x = leadingTabIndex == 0 ? 0 : rects[leadingTabIndex].x; + tabViewPosition.x = leadingTabIndex == 0? 0 : rects[leadingTabIndex].x; if ((viewSize.width - tabViewPosition.x) < viewRect.width) { // We've scrolled to the end, so adjust the viewport size @@ -3672,7 +3759,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi break; case LEFT: case RIGHT: - tabViewPosition.y = leadingTabIndex == 0 ? 0 : rects[leadingTabIndex].y; + tabViewPosition.y = leadingTabIndex == 0? 0 : rects[leadingTabIndex].y; if ((viewSize.height - tabViewPosition.y) < viewRect.height) { // We've scrolled to the end, so adjust the viewport size @@ -3685,13 +3772,13 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi viewport.setViewPosition(tabViewPosition); } - public void stateChanged(final ChangeEvent e) { + public void stateChanged(ChangeEvent e) { updateView(); } - private void updateView() { + protected void updateView() { int tabPlacement = tabPane.getTabPlacement(); - int tabCount = tabPane.getTabCount(); + int tabCount = tabPane.getTabCount(); assureRectsCreated(tabCount); Rectangle vpRect = viewport.getBounds(); Dimension viewSize = viewport.getViewSize(); @@ -3717,71 +3804,82 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } } Insets contentInsets = getContentBorderInsets(tabPlacement); - switch (tabPlacement) { + switch(tabPlacement) { case LEFT: - tabPane.repaint(vpRect.x + vpRect.width, vpRect.y, + tabPane.repaint(vpRect.x+vpRect.width, vpRect.y, contentInsets.left, vpRect.height); scrollBackwardButton.setEnabled( viewRect.y > 0 && leadingTabIndex > 0); scrollForwardButton.setEnabled( - leadingTabIndex < tabCount - 1 && - viewSize.height - viewRect.y > viewRect.height); + leadingTabIndex < tabCount-1 && + viewSize.height-viewRect.y > viewRect.height); break; case RIGHT: - tabPane.repaint(vpRect.x - contentInsets.right, vpRect.y, + tabPane.repaint(vpRect.x-contentInsets.right, vpRect.y, contentInsets.right, vpRect.height); scrollBackwardButton.setEnabled( viewRect.y > 0 && leadingTabIndex > 0); scrollForwardButton.setEnabled( - leadingTabIndex < tabCount - 1 && - viewSize.height - viewRect.y > viewRect.height); + leadingTabIndex < tabCount-1 && + viewSize.height-viewRect.y > viewRect.height); break; case BOTTOM: - tabPane.repaint(vpRect.x, vpRect.y - contentInsets.bottom, + tabPane.repaint(vpRect.x, vpRect.y-contentInsets.bottom, vpRect.width, contentInsets.bottom); scrollBackwardButton.setEnabled( viewRect.x > 0 && leadingTabIndex > 0); scrollForwardButton.setEnabled( - leadingTabIndex < tabCount - 1 && - viewSize.width - viewRect.x > viewRect.width); + leadingTabIndex < tabCount-1 && + viewSize.width-viewRect.x > viewRect.width); break; case TOP: default: - tabPane.repaint(vpRect.x, vpRect.y + vpRect.height, + tabPane.repaint(vpRect.x, vpRect.y+vpRect.height, vpRect.width, contentInsets.top); scrollBackwardButton.setEnabled( viewRect.x > 0 && leadingTabIndex > 0); scrollForwardButton.setEnabled( - leadingTabIndex < tabCount - 1 && - viewSize.width - viewRect.x > viewRect.width); + leadingTabIndex < tabCount-1 && + viewSize.width-viewRect.x > viewRect.width); } } /** * ActionListener for the scroll buttons. */ - public void actionPerformed(final ActionEvent e) { + public void actionPerformed(ActionEvent e) { ActionMap map = tabPane.getActionMap(); + if (map != null) { + String actionKey; + if (e.getSource() == scrollForwardButton) { - scrollForward(tabPane.getTabPlacement()); - } else { - scrollBackward(tabPane.getTabPlacement()); + actionKey = "scrollTabsForwardAction"; + } + else { + actionKey = "scrollTabsBackwardAction"; + } + Action action = map.get(actionKey); + + if (action != null && action.isEnabled()) { + action.actionPerformed(new ActionEvent(tabPane, + ActionEvent.ACTION_PERFORMED, null, e.getWhen(), + e.getModifiers())); } } } public String toString() { return "viewport.viewSize=" + viewport.getViewSize() + "\n" + - "viewport.viewRectangle=" + viewport.getViewRect() + "\n" + - "leadingTabIndex=" + leadingTabIndex + "\n" + + "viewport.viewRectangle="+viewport.getViewRect()+"\n"+ + "leadingTabIndex="+leadingTabIndex+"\n"+ "tabViewPosition=" + tabViewPosition; } } @SuppressWarnings("serial") // Superclass is not serializable across versions - private class ScrollableTabViewport extends JViewport implements UIResource { + protected class ScrollableTabViewport extends JViewport implements UIResource { public ScrollableTabViewport() { super(); setName("TabbedPane.scrollableViewport"); @@ -3796,7 +3894,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } @SuppressWarnings("serial") // Superclass is not serializable across versions - private class ScrollableTabPanel extends JPanel implements UIResource { + protected class ScrollableTabPanel extends JPanel implements UIResource { public ScrollableTabPanel() { super(null); setOpaque(tabPane.isOpaque()); @@ -3806,11 +3904,16 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } setBackground(bgColor); } - - public void paintComponent(final Graphics g) { + public void paintComponent(Graphics g) { super.paintComponent(g); DarkTabbedPaneUIBridge.this.paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex()); + if (tabScroller.croppedEdge.isParamsSet() && tabContainer == null) { + Rectangle croppedRect = rects[tabScroller.croppedEdge.getTabIndex()]; + g.translate(croppedRect.x, croppedRect.y); + tabScroller.croppedEdge.paintComponent(g); + g.translate(-croppedRect.x, -croppedRect.y); + } } public void doLayout() { @@ -3822,9 +3925,9 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi } @SuppressWarnings("serial") // Superclass is not serializable across versions - private static class ScrollableTabButton extends BasicArrowButton implements UIResource, - SwingConstants { - public ScrollableTabButton(final int direction) { + protected class ScrollableTabButton extends BasicArrowButton implements UIResource, + SwingConstants { + public ScrollableTabButton(int direction) { super(direction, UIManager.getColor("TabbedPane.selected"), UIManager.getColor("TabbedPane.shadow"), @@ -3836,51 +3939,53 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // Controller: event listeners - private class Handler implements ChangeListener, ContainerListener, + protected class Handler implements ChangeListener, ContainerListener, FocusListener, MouseListener, MouseMotionListener, PropertyChangeListener { // // PropertyChangeListener // - public void propertyChange(final PropertyChangeEvent e) { - JTabbedPane pane = (JTabbedPane) e.getSource(); + public void propertyChange(PropertyChangeEvent e) { + JTabbedPane pane = (JTabbedPane)e.getSource(); String name = e.getPropertyName(); boolean isScrollLayout = scrollableTabLayoutEnabled(); - if ("mnemonicAt".equals(name)) { + if (name == "mnemonicAt") { updateMnemonics(); pane.repaint(); - } else if ("displayedMnemonicIndexAt".equals(name)) { + } + else if (name == "displayedMnemonicIndexAt") { pane.repaint(); - } else if ("indexForTitle".equals(name)) { + } + else if (name =="indexForTitle") { calculatedBaseline = false; Integer index = (Integer) e.getNewValue(); updateHtmlViews(index, false); - } else if ("tabLayoutPolicy".equals(name)) { + } else if (name == "tabLayoutPolicy") { DarkTabbedPaneUIBridge.this.uninstallUI(pane); DarkTabbedPaneUIBridge.this.installUI(pane); calculatedBaseline = false; - } else if ("tabPlacement".equals(name)) { + } else if (name == "tabPlacement") { if (scrollableTabLayoutEnabled()) { tabScroller.createButtons(); } calculatedBaseline = false; - } else if ("opaque".equals(name) && isScrollLayout) { - boolean newVal = (Boolean) e.getNewValue(); + } else if (name == "opaque" && isScrollLayout) { + boolean newVal = ((Boolean)e.getNewValue()).booleanValue(); tabScroller.tabPanel.setOpaque(newVal); tabScroller.viewport.setOpaque(newVal); - } else if ("background".equals(name) && isScrollLayout) { - Color newVal = (Color) e.getNewValue(); + } else if (name == "background" && isScrollLayout) { + Color newVal = (Color)e.getNewValue(); tabScroller.tabPanel.setBackground(newVal); tabScroller.viewport.setBackground(newVal); Color newColor = selectedColor == null ? newVal : selectedColor; tabScroller.scrollForwardButton.setBackground(newColor); tabScroller.scrollBackwardButton.setBackground(newColor); - } else if ("indexForTabComponent".equals(name)) { + } else if (name == "indexForTabComponent") { if (tabContainer != null) { tabContainer.removeUnusedTabComponents(); } Component c = tabPane.getTabComponentAt( - (Integer) e.getNewValue()); + (Integer)e.getNewValue()); if (c != null) { if (tabContainer == null) { installTabContainer(); @@ -3891,33 +3996,33 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi tabPane.revalidate(); tabPane.repaint(); calculatedBaseline = false; - } else if ("indexForNullComponent".equals(name)) { + } else if (name == "indexForNullComponent") { isRunsDirty = true; - updateHtmlViews((Integer) e.getNewValue(), true); - } else if ("font".equals(name) || SwingUtilities2.isScaleChanged(e)) { + updateHtmlViews((Integer)e.getNewValue(), true); + } else if (name == "font" || SwingUtilities2.isScaleChanged(e)) { calculatedBaseline = false; } } - private void updateHtmlViews(final int index, final boolean inserted) { + protected void updateHtmlViews(int index, boolean inserted) { String title = tabPane.getTitleAt(index); boolean isHTML = BasicHTML.isHTMLString(title); if (isHTML) { - if (htmlViews == null) { // Initialize vector + if (htmlViews==null) { // Initialize vector htmlViews = createHTMLVector(); } else { // Vector already exists View v = BasicHTML.createHTMLView(tabPane, title); setHtmlView(v, inserted, index); } } else { // Not HTML - if (htmlViews != null) { // Add placeholder + if (htmlViews!=null) { // Add placeholder setHtmlView(null, inserted, index); } // else nada! } updateMnemonics(); } - private void setHtmlView(final View v, final boolean inserted, final int index) { + protected void setHtmlView(View v, boolean inserted, int index) { if (inserted || index >= htmlViews.size()) { htmlViews.insertElementAt(v, index); } else { @@ -3928,8 +4033,8 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // // ChangeListener // - public void stateChanged(final ChangeEvent e) { - JTabbedPane tabPane = (JTabbedPane) e.getSource(); + public void stateChanged(ChangeEvent e) { + JTabbedPane tabPane = (JTabbedPane)e.getSource(); tabPane.revalidate(); tabPane.repaint(); @@ -3940,7 +4045,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi int index = tabPane.getSelectedIndex(); if (index < rects.length && index != -1) { tabScroller.tabPanel.scrollRectToVisible( - (Rectangle) rects[index].clone()); + (Rectangle)rects[index].clone()); } } } @@ -3948,21 +4053,21 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // // MouseListener // - public void mouseClicked(final MouseEvent e) { + public void mouseClicked(MouseEvent e) { } - public void mouseReleased(final MouseEvent e) { + public void mouseReleased(MouseEvent e) { } - public void mouseEntered(final MouseEvent e) { + public void mouseEntered(MouseEvent e) { setRolloverTab(e.getX(), e.getY()); } - public void mouseExited(final MouseEvent e) { + public void mouseExited(MouseEvent e) { setRolloverTab(-1); } - public void mousePressed(final MouseEvent e) { + public void mousePressed(MouseEvent e) { if (!tabPane.isEnabled()) { return; } @@ -3974,7 +4079,8 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // This will trigger the focusIndex to change by way // of stateChanged. tabPane.setSelectedIndex(tabIndex); - } else if (tabPane.isRequestFocusEnabled()) { + } + else if (tabPane.isRequestFocusEnabled()) { // Clicking on selected tab, try and give the tabbedpane // focus. Repaint will occur in focusGained. tabPane.requestFocus(); @@ -3985,21 +4091,20 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // // MouseMotionListener // - public void mouseDragged(final MouseEvent e) { + public void mouseDragged(MouseEvent e) { } - public void mouseMoved(final MouseEvent e) { + public void mouseMoved(MouseEvent e) { setRolloverTab(e.getX(), e.getY()); } // // FocusListener // - public void focusGained(final FocusEvent e) { + public void focusGained(FocusEvent e) { setFocusIndex(tabPane.getSelectedIndex(), true); } - - public void focusLost(final FocusEvent e) { + public void focusLost(FocusEvent e) { repaintTab(focusIndex); } @@ -4037,8 +4142,8 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi changes to tab text, this code should be removed and replaced by something which uses that. */ - public void componentAdded(final ContainerEvent e) { - JTabbedPane tp = (JTabbedPane) e.getContainer(); + public void componentAdded(ContainerEvent e) { + JTabbedPane tp = (JTabbedPane)e.getContainer(); Component child = e.getChild(); if (child instanceof UIResource) { return; @@ -4046,9 +4151,8 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi isRunsDirty = true; updateHtmlViews(tp.indexOfComponent(child), true); } - - public void componentRemoved(final ContainerEvent e) { - JTabbedPane tp = (JTabbedPane) e.getContainer(); + public void componentRemoved(ContainerEvent e) { + JTabbedPane tp = (JTabbedPane)e.getContainer(); Component child = e.getChild(); if (child instanceof UIResource) { return; @@ -4059,9 +4163,10 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // currently no IndexPropertyChangeEvent. Once // IndexPropertyChangeEvents have been added this code should be // modified to use it. - Integer indexObj = (Integer) tp.getClientProperty("__index_to_remove__"); + Integer indexObj = + (Integer)tp.getClientProperty("__index_to_remove__"); if (indexObj != null) { - int index = indexObj; + int index = indexObj.intValue(); if (htmlViews != null && htmlViews.size() > index) { htmlViews.removeElementAt(index); } @@ -4083,7 +4188,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // its functionality has been moved into Handler. If you need to add // new functionality add it to the Handler, but make sure this // class calls into the Handler. - public void propertyChange(final PropertyChangeEvent e) { + public void propertyChange(PropertyChangeEvent e) { getHandler().propertyChange(e); } } @@ -4097,7 +4202,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // its functionality has been moved into Handler. If you need to add // new functionality add it to the Handler, but make sure this // class calls into the Handler. - public void stateChanged(final ChangeEvent e) { + public void stateChanged(ChangeEvent e) { getHandler().stateChanged(e); } } @@ -4111,7 +4216,7 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // its functionality has been moved into Handler. If you need to add // new functionality add it to the Handler, but make sure this // class calls into the Handler. - public void mousePressed(final MouseEvent e) { + public void mousePressed(MouseEvent e) { getHandler().mousePressed(e); } } @@ -4125,20 +4230,19 @@ public abstract class DarkTabbedPaneUIBridge extends TabbedPaneUI implements Swi // its functionality has been moved into Handler. If you need to add // new functionality add it to the Handler, but make sure this // class calls into the Handler. - public void focusGained(final FocusEvent e) { + public void focusGained(FocusEvent e) { getHandler().focusGained(e); } - - public void focusLost(final FocusEvent e) { + public void focusLost(FocusEvent e) { getHandler().focusLost(e); } } - private Vector createHTMLVector() { + protected Vector createHTMLVector() { Vector htmlViews = new Vector(); int count = tabPane.getTabCount(); - if (count > 0) { - for (int i = 0; i < count; i++) { + if (count>0) { + for (int i=0 ; i 1) { + if (index == 0) { + index++; + } else { + index--; + } + } else { + index = -1; + } + tabPane.setSelectedIndex(index); + if (ui != null) { + ui.setRolloverTab(-1); + createDragImage(tabPane, ui); + ui.setSourceIndicator(currentTransferable.transferData.tabIndex); + } else { + createDragImage(tabPane, null); + } + if ((ui != null && !ui.scrollableTabLayoutEnabled()) + || tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) { + tabPane.setSelectedIndex(currentTransferable.transferData.tabIndex); + } + return currentTransferable; + } + + protected void createDragImage(@NotNull final JTabbedPane tabbedPane, final DarkTabbedPaneUI ui) { + Image tabImage = ImageUtil.imageFromComponent(tabbedPane, currentTransferable.transferData.tabBounds); + int w = tabImage.getWidth(null); + int h = tabImage.getHeight(null); + var g = tabImage.getGraphics(); + + if (ui != null) { + g.setColor(ui.getDragBorderColor()); + } else { + g.setColor(tabbedPane.getBackgroundAt(currentTransferable.transferData.tabIndex).brighter()); + } + + int lw = 2; + g.fillRect(0, 0, w, lw); + g.fillRect(0, 0, lw, h); + g.fillRect(w - lw, 0, lw, h); + g.fillRect(0, h - lw, w, lw); + g.dispose(); + + setDragImageOffset(new Point(w / 2, h / 2)); + setDragImage(tabImage); + } + + + @Override + public void dragEnter(final DropTargetDragEvent e) { + } + + + @Override + public void dragExit(@NotNull final DropTargetEvent e) { + Component c = e.getDropTargetContext().getComponent(); + lastTab = -1; + var ui = supportsIndicator(c); + if (ui != null) { + ui.clearDropIndicator(); + } + } + + @Contract("null -> null") + private DarkTabbedPaneUI supportsIndicator(final Component c) { + if (c instanceof JComponent && ((JComponent) c).getUI() instanceof DarkTabbedPaneUI) { + return ((DarkTabbedPaneUI) ((JComponent) c).getUI()); + } + return null; + } + + @Override + public void dragOver(@NotNull final DropTargetDragEvent e) { + e.getDropTargetContext().getComponent().setCursor(Cursor.getDefaultCursor()); + mouseLocation = e.getLocation(); + + Component c = e.getDropTargetContext().getComponent(); + JTabbedPane destTabbedPane = (JTabbedPane) c; + + var ui = supportsIndicator(destTabbedPane); + if (ui != null) { + TabTransferable t = currentTransferable; + if (t != null) { + int tab = getDroppedTabIndex(t, destTabbedPane, mouseLocation); + + if (tab == -1) { + lastTab = tab; + ui.clearDropIndicator(); + return; + } + int tabPlacement = destTabbedPane.getTabPlacement(); + Rectangle dropRect = t.getTabBounds(); + Rectangle destRect = destTabbedPane.getBoundsAt(Math.min(tab, destTabbedPane.getTabCount() - 1)); + JTabbedPane source = t.transferData.sourceTabbedPane; + int sourceIndex = t.transferData.tabIndex; + + + if (ui.scrollableTabLayoutEnabled()) { + boolean lastInSource = false; + if (destTabbedPane == source && (tab == sourceIndex || (sourceIndex == source.getTabCount() - 1 + && tab == source.getTabCount()))) { + lastInSource = true; + destRect.width = dropRect.width; + destRect.height = dropRect.height; + } + + switch (tabPlacement) { + case TOP, BOTTOM -> { + if (destTabbedPane.getComponentOrientation().isLeftToRight()) { + if (tab >= destTabbedPane.getTabCount() && !lastInSource) { + destRect.x += destRect.width; + } + dropRect.x = destRect.x; + if (lastTab != -1 && lastTab < tab) { + dropRect.x -= dropRect.width; + } + } else { + if (tab >= destTabbedPane.getTabCount()) { + destRect.x -= 2 * dropRect.width; + if (lastTab < tab) { + destRect.x += destRect.width; + } + } + dropRect.x = destRect.x + dropRect.width; + if (lastTab != -1 && lastTab > tab) { + dropRect.x -= dropRect.width; + } + } + dropRect.y = destRect.y + destRect.height - dropRect.height; + } + case LEFT, RIGHT -> { + if (tab >= destTabbedPane.getTabCount()) { + destRect.y += destRect.height; + } + dropRect.y = destRect.y; + dropRect.x = destRect.x + destRect.width - dropRect.width; + if (lastTab != -1 && lastTab < tab) { + dropRect.y -= dropRect.height; + } + } + } + } else { + if (source == destTabbedPane && (tab == sourceIndex || tab == sourceIndex + 1)) { + dropRect.setRect(0, 0, 0, 0); + } else { + switch (destTabbedPane.getTabPlacement()) { + case TOP, BOTTOM -> { + var b = ui.getTabAreaBounds(); + if (tab == destTabbedPane.getTabCount()) { + dropRect.x = destRect.x + destRect.width / 2; + dropRect.width = Math.min(b.x + b.width - dropRect.x, dropRect.width); + } else if (tab == 0) { + dropRect.x = destRect.x; + dropRect.width = Math.min(dropRect.width / 2, destRect.width / 2); + } else { + var prev = destTabbedPane.getBoundsAt(tab - 1); + if (destRect.y + destRect.height <= mouseLocation.y && + prev.y <= mouseLocation.y && mouseLocation.y <= prev.y + prev.height) { + destRect.x = prev.x + prev.width; + destRect.y = prev.y; + destRect.height = prev.height; + } + + dropRect.x = destRect.x; + dropRect.setLocation(destRect.getLocation()); + dropRect.x -= dropRect.width / 2; + if (dropRect.x < b.x) { + int diff = b.x - dropRect.x; + dropRect.width -= diff; + dropRect.x = b.x; + } + if (dropRect.x + dropRect.width > b.x + b.width) { + int diff = dropRect.width + dropRect.x - b.width - b.x; + dropRect.width -= diff; + } + } + dropRect.y = destRect.y + destRect.height - dropRect.height; + } + case LEFT, RIGHT -> { + + } + } + } + } + ui.setDnDIndicatorRect(dropRect.x, dropRect.y, dropRect.width, dropRect.height, + tab, source == destTabbedPane); + lastTab = tab; + } + } + } + + + @Override + public void drop(@NotNull final DropTargetDropEvent e) { + Component c = e.getDropTargetContext().getComponent(); + var ui = supportsIndicator(c); + if (ui != null) { + ui.clearDropIndicator(); + } + } + + + @Override + public void dropActionChanged(final DropTargetDragEvent e) { + } + + + /** + * Returns the index at which to add a tab if it is dropped at the mouse + * location specified by p. + * + * @param tabbedPane The tabbed pane who would be receiving the tab. + * @param p The mouse location. + * @return The index at which to add the tab. + */ + protected int getDroppedTabIndex(final TabTransferable t, @NotNull final JTabbedPane tabbedPane, + @NotNull final Point p) { + var tab = tabbedPane.indexAtLocation(p.x, p.y); + var ui = supportsIndicator(tabbedPane); + if (ui != null) { + if (tab == -1) { + var bounds = ui.getTabAreaBounds(); + if (bounds.contains(p)) { + if (tabbedPane.getTabCount() > 0) { + var minb = ui.getTabBounds(tabbedPane, 0); + var maxb = ui.getTabBounds(tabbedPane, tabbedPane.getTabCount() - 1); + if (tabbedPane.getComponentOrientation().isLeftToRight()) { + int x = Math.max(bounds.x, minb.x); + bounds.width = Math.min(bounds.x + bounds.width - x, maxb.x + maxb.width - x); + bounds.x = x; + } else { + int x = Math.max(bounds.x, maxb.x); + bounds.width = Math.min(bounds.x + bounds.width - x, minb.x + minb.width - x); + bounds.x = x; + } + int y = Math.max(bounds.y, minb.y); + bounds.height = Math.min(bounds.y + bounds.height - y, maxb.x + maxb.height - y); + } + + switch (tabbedPane.getTabPlacement()) { + case TOP, BOTTOM -> { + if (tabbedPane.getComponentOrientation().isLeftToRight()) { + tab = p.x <= bounds.x + bounds.width / 2 ? 0 : tabbedPane.getTabCount(); + } else { + tab = p.x >= bounds.x + bounds.width / 2 ? 1 : tabbedPane.getTabCount(); + } + } + case LEFT, RIGHT -> tab = p.y <= bounds.y + bounds.height / 2 ? 0 : tabbedPane.getTabCount(); + } + } + } else { + if (tab < tabbedPane.getTabCount()) { + var b = tabbedPane.getBoundsAt(tab); + + if (tab >= 1 && !ui.scrollableTabLayoutEnabled()) { + var prev = tabbedPane.getBoundsAt(tab - 1); + if (b.y + b.height < mouseLocation.y && + prev.y <= mouseLocation.y && mouseLocation.y <= prev.y + prev.height) { + b = prev; + } + } + + + var sb = (ui.scrollableTabLayoutEnabled()) ? t.getTabBounds() : EMPTY_RECT; + switch (tabbedPane.getTabPlacement()) { + case TOP, BOTTOM -> { + if (tabbedPane.getComponentOrientation().isLeftToRight()) { + if (p.x >= b.x + sb.width + (b.width - sb.width) / 2) { + tab += 1; + } + } else { + if (p.x <= b.x + (b.width - sb.width) / 2) { + tab += 1; + } + } + } + case LEFT, RIGHT -> { + if (p.y >= b.y + sb.height + (b.height - sb.height) / 2) { + tab += 1; + } + } + } + } + } + } else if (tab == -1) { + tab = tabbedPane.getTabCount(); + } + return tab; + } + + + /** + * We can only move tabs, we cannot copy them. + * + * @param c This parameter is ignored. + * @return TransferHandler.MOVE, as we can only move tabs. + */ + @Override + public int getSourceActions(final JComponent c) { + return MOVE; + } + + + protected boolean hasTabFlavor(final DataFlavor[] flavors) { + if (tabFlavor == null) { + return false; + } + for (DataFlavor flavor : flavors) { + if (tabFlavor.equals(flavor)) { + return true; + } + } + return false; + } + + + /** + * Called when the drag-and-drop operation has just completed. This + * creates a new tab identical to the one "dragged" and places it in the + * destination JTabbedPane. + * + * @param c The component receiving the "drop" (the instance of + * JTabbedPane). + * @param t The data being transfered (information about the tab and the + * component contained by the tab). + * @return Whether or not the import was successful. + */ + @Override + public boolean importData(final JComponent c, @NotNull final Transferable t) { + + boolean successful = false; + if (hasTabFlavor(t.getTransferDataFlavors()) && mouseLocation != null) { + try { + JTabbedPane tabbedPane = (JTabbedPane) c; + int tab = getDroppedTabIndex(currentTransferable, tabbedPane, mouseLocation); + TabTransferable.TabTransferData td = (TabTransferable.TabTransferData) t.getTransferData(tabFlavor); + JTabbedPane sourcePane = td.sourceTabbedPane; + int sourceIndex = td.tabIndex; + + if (tabbedPane == sourcePane && sourceIndex == tab) { + //Nothing to do. Just select the tab to be sure. + selectTab(sourcePane, sourceIndex); + return true; + } + if (tab < 0 || tab > tabbedPane.getTabCount()) { + return false; + } + + String tabName = sourcePane.getTitleAt(sourceIndex); + Icon icon = sourcePane.getIconAt(sourceIndex); + Component comp = sourcePane.getComponentAt(sourceIndex); + String toolTip = sourcePane.getToolTipTextAt(sourceIndex); + Color foreground = sourcePane.getForegroundAt(sourceIndex); + Component tabComp = sourcePane.getTabComponentAt(sourceIndex); + + tabbedPane.insertTab(tabName, icon, comp, toolTip, tab); + + int index = tab; + if (tabbedPane == sourcePane) { + if (sourceIndex < index) index--; + } + + if (tabComp != null) { + tabbedPane.setTabComponentAt(index, tabComp); + } + tabbedPane.setForegroundAt(index, foreground); + selectTab(tabbedPane, index); + + successful = true; + var ui = supportsIndicator(c); + if (ui != null) { + ui.clearDropIndicator(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + return successful; + } + + /** + * Selects the specified tab in the specified tabbed pane. This method + * can be overridden by subclasses to do more stuff than simply select + * the tab. + * + * @param tabbedPane The tabbed pane. + * @param index The index of the tab to select. + */ + protected void selectTab(final JTabbedPane tabbedPane, final int index) { + SwingUtilities.invokeLater(() -> { + tabbedPane.setSelectedIndex(index); + tabbedPane.requestFocus(); + }); + } + + + /** + * Transferable representing a tab from a tabbed pane and its contents. + */ + public class TabTransferable implements Transferable { + + private final TabTransferData transferData; + + /** + * The data remembered about the tab. + */ + public class TabTransferData { + + private final JTabbedPane sourceTabbedPane; + private final int tabIndex; + private final Rectangle tabBounds; + + @Contract(pure = true) + public TabTransferData(@NotNull final JTabbedPane tabbedPane, final int tabIndex) { + this.sourceTabbedPane = tabbedPane; + this.tabIndex = tabIndex; + this.tabBounds = tabbedPane.getBoundsAt(tabIndex); + } + + } + + public TabTransferable(@NotNull final JTabbedPane tabbedPane) { + int index = tabbedPane.getSelectedIndex(); + transferData = new TabTransferData(tabbedPane, index); + } + + public Rectangle getTabBounds() { + return new Rectangle(transferData.tabBounds); + } + + @Override + public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException { + if (!isDataFlavorSupported(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + return transferData; + } + + @Override + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[]{tabFlavor}; + } + + @Override + public boolean isDataFlavorSupported(final DataFlavor flavor) { + return tabFlavor.equals(flavor); + } + + } + + protected class TabbedPaneDragHandler implements DragGestureListener, DragSourceListener { + + private boolean scrolls; + + // --- DragGestureListener methods ----------------------------------- + + /** + * a Drag gesture has been recognized + */ + public void dragGestureRecognized(@NotNull final DragGestureEvent dge) { + JComponent c = (JComponent) dge.getComponent(); + TabbedPaneTransferHandler th = (TabbedPaneTransferHandler) c.getTransferHandler(); + Transferable t = th.createTransferable(c); + if (t != null) { + scrolls = c.getAutoscrolls(); + c.setAutoscrolls(false); + try { + Image im = th.getDragImage(); + if (im == null) { + dge.startDrag(Cursor.getDefaultCursor(), t, this); + } else { + dge.startDrag(Cursor.getDefaultCursor(), im, th.getDragImageOffset(), t, this); + } + return; + } catch (RuntimeException re) { + c.setAutoscrolls(scrolls); + } + } + + th.exportDone(c, t, NONE); + } + + // --- DragSourceListener methods ----------------------------------- + + /** + * as the hotspot enters a platform dependent drop site + */ + public void dragEnter(final DragSourceDragEvent dsde) { + } + + /** + * as the hotspot moves over a platform dependent drop site + */ + public void dragOver(final DragSourceDragEvent dsde) { + } + + /** + * as the hotspot exits a platform dependent drop site + */ + public void dragExit(final DragSourceEvent dsde) { + } + + /** + * as the operation completes + */ + public void dragDropEnd(@NotNull final DragSourceDropEvent dsde) { + DragSourceContext dsc = dsde.getDragSourceContext(); + JComponent c = (JComponent) dsc.getComponent(); + if (dsde.getDropSuccess()) { + ((TabbedPaneTransferHandler) c.getTransferHandler()).exportDone(c, dsc.getTransferable(), + dsde.getDropAction()); + } else { + ((TabbedPaneTransferHandler) c.getTransferHandler()).exportDone(c, dsc.getTransferable(), NONE); + } + c.setAutoscrolls(scrolls); + + var ui = supportsIndicator(currentTransferable.transferData.sourceTabbedPane); + if (ui != null) { + ui.clearSourceIndicator(); + } + if (!dsde.getDropSuccess()) { + selectTab(currentTransferable.transferData.sourceTabbedPane, + currentTransferable.transferData.tabIndex); + } + currentTransferable = null; + } + + public void dropActionChanged(final DragSourceDragEvent dsde) { + } + } + + protected static class TabbedPaneDragGestureRecognizer extends DragGestureRecognizer { + + protected TabbedPaneDragGestureRecognizer(final DragGestureListener dgl) { + super(DragSource.getDefaultDragSource(), null, NONE, dgl); + } + + void gestured(final JComponent c, final MouseEvent e, final int srcActions, final int action) { + setComponent(c); + setSourceActions(srcActions); + appendEvent(e); + fireDragGestureRecognized(action, e.getPoint()); + } + + /** + * register this DragGestureRecognizer's Listeners with the Component + */ + protected void registerListeners() { + } + + /** + * unregister this DragGestureRecognizer's Listeners with the Component + *

+ * subclasses must override this method + */ + protected void unregisterListeners() { + } + } +} diff --git a/src/main/java/com/weis/darklaf/ui/text/DarkCaret.java b/src/main/java/com/weis/darklaf/ui/text/DarkCaret.java index b5c96526..2cc43d40 100644 --- a/src/main/java/com/weis/darklaf/ui/text/DarkCaret.java +++ b/src/main/java/com/weis/darklaf/ui/text/DarkCaret.java @@ -48,9 +48,9 @@ public class DarkCaret extends DefaultCaret implements UIResource { } private void adjustCaretLocation(@NotNull final MouseEvent e) { - if ((e.getModifiersEx() & ActionEvent.SHIFT_MASK) != 0 && getDot() != -1) { + if (e.isShiftDown() && getDot() != -1) { moveCaret(e); - } else { + } else{ positionCaret(e); } } diff --git a/src/main/java/com/weis/darklaf/ui/text/DarkTextFieldUI.java b/src/main/java/com/weis/darklaf/ui/text/DarkTextFieldUI.java index 65831b5f..3e740b7b 100644 --- a/src/main/java/com/weis/darklaf/ui/text/DarkTextFieldUI.java +++ b/src/main/java/com/weis/darklaf/ui/text/DarkTextFieldUI.java @@ -24,8 +24,10 @@ import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; -public class DarkTextFieldUI extends DarkTextFieldUIBridge { +public class DarkTextFieldUI extends DarkTextFieldUIBridge implements PropertyChangeListener { public static final int ARC_SIZE = 3; public static final int SEARCH_ARC_SIZE = 5; @@ -61,7 +63,6 @@ public class DarkTextFieldUI extends DarkTextFieldUIBridge { @Override public void popupMenuWillBecomeInvisible(@NotNull final PopupMenuEvent e) { lastSearchEvent = System.currentTimeMillis(); - ((JPopupMenu) e.getSource()).removePopupMenuListener(this); } }; @@ -112,7 +113,6 @@ public class DarkTextFieldUI extends DarkTextFieldUIBridge { if (lastSearchEvent == 0 || (System.currentTimeMillis() - lastSearchEvent) > 250) { var menu = getSearchPopup(getComponent()); if (menu != null) { - menu.addPopupMenuListener(searchPopupListener); menu.show(getComponent(), getSearchIconCoord().x, getComponent().getHeight()); } } @@ -267,6 +267,21 @@ public class DarkTextFieldUI extends DarkTextFieldUIBridge { return DarkCaret.CaretStyle.THICK_VERTICAL_LINE_STYLE; } + @Override + public void propertyChange(final PropertyChangeEvent evt) { + String key = evt.getPropertyName(); + if ("JTextField.Search.FindPopup".equals(key)) { + var oldVal = evt.getOldValue(); + var newVal = evt.getNewValue(); + if (oldVal instanceof JPopupMenu) { + ((JPopupMenu)oldVal).removePopupMenuListener(searchPopupListener); + } + if (newVal instanceof JPopupMenu) { + ((JPopupMenu)newVal).addPopupMenuListener(searchPopupListener); + } + } + } + private enum ClickAction { CLEAR, SEARCH_POPUP, diff --git a/src/main/java/com/weis/darklaf/util/DarkUIUtil.java b/src/main/java/com/weis/darklaf/util/DarkUIUtil.java index 16348de3..2d1bc9a4 100644 --- a/src/main/java/com/weis/darklaf/util/DarkUIUtil.java +++ b/src/main/java/com/weis/darklaf/util/DarkUIUtil.java @@ -18,6 +18,7 @@ import java.awt.geom.RoundRectangle2D; public final class DarkUIUtil { public final static AlphaComposite ALPHA_COMPOSITE = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f); + public final static AlphaComposite ALPHA_COMPOSITE_2 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f); public final static AlphaComposite SHADOW_COMPOSITE = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f); public static final boolean USE_QUARTZ = "true".equals(System.getProperty("apple.awt.graphics.UseQuartz")); diff --git a/src/main/java/com/weis/darklaf/util/ImageUtil.java b/src/main/java/com/weis/darklaf/util/ImageUtil.java new file mode 100644 index 00000000..7fa586fc --- /dev/null +++ b/src/main/java/com/weis/darklaf/util/ImageUtil.java @@ -0,0 +1,63 @@ +package com.weis.darklaf.util; + +import com.weis.darklaf.LogFormatter; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.logging.ConsoleHandler; +import java.util.logging.Logger; + +/** + * Image utilities. + * + * @author Jannis Weis + * @since 2018 + */ +public final class ImageUtil { + + /** + * The scaling factor. + */ + public static final double SCALE_X; + public static final double SCALE_Y; + private static final Logger LOGGER = Logger.getLogger(ImageUtil.class.getName()); + + static { + LOGGER.setUseParentHandlers(false); + ConsoleHandler handler = new ConsoleHandler(); + handler.setFormatter(new LogFormatter()); + LOGGER.addHandler(handler); + + var mode = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode(); + var screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + SCALE_X = mode.getWidth() / (double) screenSize.width; + SCALE_Y = mode.getHeight() / (double) screenSize.height; + LOGGER.info("Using screen scaling SCALE_X=" + SCALE_X + ", SCALE_Y=" + SCALE_Y); + } + + @Contract(pure = true) + private ImageUtil() { + } + + /** + * Create image from component. + * + * @param c the component. + * @param bounds the bounds inside the component to capture. + * @return image containing the captured area. + */ + @NotNull + public static Image imageFromComponent(@NotNull final Component c, @NotNull final Rectangle bounds) { + BufferedImage image = new BufferedImage((int) (SCALE_X * bounds.width), (int) (SCALE_Y * bounds.height), + BufferedImage.TYPE_INT_RGB); + final Graphics2D g2d = (Graphics2D) image.getGraphics(); + g2d.scale(SCALE_Y, SCALE_Y); + g2d.translate(-bounds.x, -bounds.y); + c.printAll(g2d); + + g2d.dispose(); + return image; + } +} diff --git a/src/main/resources/com/weis/darklaf/darcula.properties b/src/main/resources/com/weis/darklaf/darcula.properties index d7225a11..2b6b7606 100644 --- a/src/main/resources/com/weis/darklaf/darcula.properties +++ b/src/main/resources/com/weis/darklaf/darcula.properties @@ -40,6 +40,7 @@ RadioButtonMenuItem.checkIcon = empty(16,16) Table.ascendingSortIcon = navigation/arrowSplitCenterH.svg[aware] Table.descendingSortIcon = navigation/arrowSplitCenterH.svg[aware] FileView.fileIcon = files/general.svg[aware] +TabbedPane.newTab.icon = navigation/add.svg[aware] InternalFrame.icon = frame/frame.svg[aware] OptionPane.informationIcon = dialog/informationDialog.svg[aware](32,32) @@ -157,7 +158,7 @@ Border.line2 = 82888C #ScrollPane ScrollPaneUI = com.weis.darklaf.ui.scrollpane.DarkScrollPaneUI -ScrollPane.fillLowerCorner = true +ScrollPane.barInsets = 0,0,0,0 #Scrollbar (done) ScrollBarUI = com.weis.darklaf.ui.scrollpane.DarkScrollBarUI @@ -169,15 +170,27 @@ Scrollbar.thumbBorderColor = 515151 #TabbedPane TabbedPaneUI = com.weis.darklaf.ui.tabbedpane.DarkTabbedPaneUI TabbedPane.tabInsets = 5,7,5,7 -TabbedPane.highlight = 292b2d +TabbedPane.tabRunOverlay = 0 + TabbedPane.light = 444444 -TabbedPane.selected = 3c3f41 -TabbedPane.selectHighlight = 3c3f41 + +TabbedPane.tabBorder = 323232 +TabbedPane.accent = 747A80 +TabbedPane.accentFocus = 4A88C7 +TabbedPane.selected = 4E5254 +TabbedPane.highlight = 27292A +TabbedPane.selectHighlight = 333537 + TabbedPane.contentBorderInsets = 0,0,0,0 TabbedPane.darkShadow = 292b2d TabbedPane.shadow = 3c3f41 -TabbedPane.tabsOverlapBorder = true +TabbedPane.maxPopupHeight = 500 +TabbedPane.dropFill = 4B4B4B +TabbedPane.dragBorderColor = C0C0C0 + +TabbedPane.tabsOpaque = true +TabbedPane.tabsOverlapBorder = false TabbedPane.labelShift = 0 TabbedPane.selectedLabelShift = 0 TabbedPane.selectedTabPadInsets = 0,0,0,0 @@ -269,7 +282,7 @@ Button.disabledText = 777777 Button.darcula.shadow = 000000 Button.darcula.selectedButtonForeground = bbbbbb Button.darcula.activeBorderColor = 6B6B6B -Button.darcula.inactiveBorderColor = 6B6B6B +Button.darcula.inactiveBorderColor = 535353 Button.darcula.defaultBorderColor = 4C708C Button.darcula.inactiveFillColor = 3C3F41 Button.darcula.defaultFillColor = 365880 @@ -286,6 +299,10 @@ Button.shadow.click = 5C6164 ToggleButtonUI = com.weis.darklaf.ui.button.DarkToggleButtonUI ToggleButton.border = com.weis.darklaf.ui.button.DarkButtonBorder +ToggleButton.sliderBorderColor = 6B6B6B +ToggleButton.disabledSliderBorderColor = 535353 +ToggleButton.sliderColor = A7A7A7 +ToggleButton.disabledSliderColor = 606060 #Slider (done...) [timeStampVariant] SliderUI = com.weis.darklaf.ui.slider.DarkSliderUI @@ -330,4 +347,57 @@ InternalFrameTitlePane.darcula.selected.borderColorBottom = 313C4C InternalFrameTitlePane.darcula.backgroundColor = 3A3D3F InternalFrameTitlePane.darcula.selected.backgroundColor = 424D5F InternalFrameTitlePane.darcula.buttonColor = 8B8B8B -InternalFrameTitlePane.darcula.hovered.buttonColor = A8A8A8 \ No newline at end of file +InternalFrameTitlePane.darcula.hovered.buttonColor = A8A8A8 + + + + +###Custom### + +#Misc +Button.separator = 5F5F5F +Focus.color = ff0000 + +#TabbedPane +DnDTabbedPaneUI = edu.kit.mima.gui.laf.DarkDnDTabbedPaneUI +EditorTabbedPaneUI = edu.kit.mima.gui.laf.DarkEditorTabbedPaneUI +DnDTabbedPane.selectionAccent = 40B6E0 +DnDTabbedPane.selectedTab = 515658 +DnDTabbedPane.dropColor = 4B4B4B +DnDTabbedPane.selectedTab2 = 343638 +DnDTabbedPane.selectionAccentUnfocused = 787878 + +#Numbering +Numbering.foreground = 606366 +Numbering.background = 313335 +Numbering.selectedForeground = a3a3a3 +Numbering.selectedBackground = 323232 +Numbering.font = Monospaced-0-12 + +#Editor +Editor.selectedBackground = 323232 +Editor.selection = 2852a0 +Editor.breakpoint = 3a2323 +Editor.mark = 2D71D2 +Editor.vertLine = 515151 + +#TabFrame +TabFramePopup.background = 3c3f41 +TabFramePopup.focus = 3B4754 +TabFramePopup.borderColor = 323232 +TabFrameTab.selected = 2D2F30 +TabFrameTab.hover = 353739 +TabFrameTab.selectedFontColor = ffffff +TabFrameTab.defaultForeground = bbbbbb +TabFrame.line = 515151 + +#Tooltip +Tooltip.background = 4B4D4D +Tooltip.borderColor = 5B5D5F + +#Terminal +Terminal.background = 2b2b2b + +#FileDialog +FileDialog.errorBorder = 73454B +FileDialog.error = 593D41 diff --git a/src/test/java/DnDTest.java b/src/test/java/DnDTest.java new file mode 100644 index 00000000..a4913c13 --- /dev/null +++ b/src/test/java/DnDTest.java @@ -0,0 +1,96 @@ +import com.weis.darklaf.icons.IconLoader; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class DnDTest extends JFrame { + + public DnDTest() { + + initUI(); + } + + private void initUI() { + + var icon1 = IconLoader.get().getUIAwareIcon("files/folder.svg"); + var icon2 = IconLoader.get().getUIAwareIcon("files/text.svg"); + var icon3 = IconLoader.get().getUIAwareIcon("files/unknown.svg"); + + var label1 = new JLabel(icon1, JLabel.CENTER); + var label2 = new JLabel(icon2, JLabel.CENTER); + var label3 = new JLabel(icon3, JLabel.CENTER); + + var listener = new DragMouseAdapter(); + label1.addMouseListener(listener); + label2.addMouseListener(listener); + label3.addMouseListener(listener); + + var button = new JButton(icon2); + button.setFocusable(false); + + label1.setTransferHandler(new TransferHandler("icon")); + label2.setTransferHandler(new TransferHandler("icon")); + label3.setTransferHandler(new TransferHandler("icon")); + button.setTransferHandler(new TransferHandler("icon")); + + createLayout(label1, label2, label3, button); + + setTitle("Icon Drag & Drop"); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLocationRelativeTo(null); + } + + private class DragMouseAdapter extends MouseAdapter { + + public void mousePressed(MouseEvent e) { + + var c = (JComponent) e.getSource(); + var handler = c.getTransferHandler(); + handler.exportAsDrag(c, e, TransferHandler.COPY); + } + } + + private void createLayout(JComponent... arg) { + + var pane = getContentPane(); + var gl = new GroupLayout(pane); + pane.setLayout(gl); + + gl.setAutoCreateContainerGaps(true); + gl.setAutoCreateGaps(true); + + gl.setHorizontalGroup(gl.createParallelGroup(GroupLayout.Alignment.CENTER) + .addGroup(gl.createSequentialGroup() + .addComponent(arg[0]) + .addGap(30) + .addComponent(arg[1]) + .addGap(30) + .addComponent(arg[2]) + ) + .addComponent(arg[3], GroupLayout.DEFAULT_SIZE, + GroupLayout.DEFAULT_SIZE, Integer.MAX_VALUE) + ); + + gl.setVerticalGroup(gl.createSequentialGroup() + .addGroup(gl.createParallelGroup() + .addComponent(arg[0]) + .addComponent(arg[1]) + .addComponent(arg[2])) + .addGap(30) + .addComponent(arg[3]) + ); + + pack(); + } + + public static void main(String[] args) { + + EventQueue.invokeLater(() -> { + + var ex = new DnDTest(); + ex.setVisible(true); + }); + } +} diff --git a/src/test/java/ScrollPaneDemo.java b/src/test/java/ScrollPaneDemo.java index c2029a5a..5fcb3136 100644 --- a/src/test/java/ScrollPaneDemo.java +++ b/src/test/java/ScrollPaneDemo.java @@ -1,5 +1,4 @@ import com.weis.darklaf.LafManager; -import com.weis.darklaf.components.OverlayScrollPane; import org.jdesktop.swingx.MultiSplitLayout; import javax.swing.*; @@ -16,7 +15,7 @@ public final class ScrollPaneDemo extends MultiSplitLayout { LafManager.loadLaf(LafManager.Theme.Dark); final var frame = new JFrame(); frame.setSize(500, 500); - var overlayScroll = new OverlayScrollPane(new JTextPane(){{ + var overlayScroll = new JScrollPane(new JTextPane() {{ setText("orem ipsum dolor sit amet, consectetur adipiscing elit. In tempor quis nibh a semper. Nullam" + " auctor, erat non viverra commodo, libero orci aliquam quam, ac interdum nunc est sed " + "ligula. Aliquam vel velit non dolor accumsan blandit id eu metus. Aenean iaculis urna in " @@ -46,7 +45,7 @@ public final class ScrollPaneDemo extends MultiSplitLayout { // setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); setFont(Font.getFont(Font.MONOSPACED)); }}); - frame.setContentPane(new JPanel(new BorderLayout()){{ + frame.setContentPane(new JPanel(new BorderLayout()) {{ add(overlayScroll, BorderLayout.CENTER); }}); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); diff --git a/src/test/java/TabbedPaneDemo.java b/src/test/java/TabbedPaneDemo.java index 8c160bb2..73c813e9 100644 --- a/src/test/java/TabbedPaneDemo.java +++ b/src/test/java/TabbedPaneDemo.java @@ -1,7 +1,10 @@ import com.weis.darklaf.LafManager; import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.UIResource; import java.awt.*; +import java.awt.event.ItemEvent; public class TabbedPaneDemo extends JFrame { @@ -9,26 +12,95 @@ public class TabbedPaneDemo extends JFrame { SwingUtilities.invokeLater(() -> { LafManager.loadLaf(LafManager.Theme.Dark); final JFrame frame = new JFrame(); - frame.setSize(500, 500); - var p = new JPanel(new BorderLayout()); - final var tabbedPane = new JTabbedPane(); - for (int i = 0; i < 20; i++) { - var editor = new JTextPane(); - editor.setText("mima.TabFrameDemo\n".repeat(i + 1)); - tabbedPane.addTab("Tab number " + i, editor); + int tabCount = 2; + frame.setSize(500 * tabCount, 500); + var c = new JPanel(new GridLayout(1,2)); + for (int j = 0; j < tabCount; j++) { + var p = new JPanel(new BorderLayout()); + final var tabbedPane = new JTabbedPane(); + tabbedPane.setName("TabPane " + j); + + for (int i = 0; i < 3; i++) { + var editor = new JTextPane(); + editor.setText("TabPaneDemo TabPane-" + j + "\n".repeat(i + 1)); + tabbedPane.addTab("Tab (" + i + "," + j +")", editor); + } + tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + tabbedPane.putClientProperty("JTabbedPane.showNewTabButton", Boolean.TRUE); + tabbedPane.putClientProperty("JTabbedPane.dndEnabled", Boolean.TRUE); + tabbedPane.putClientProperty("JTabbedPane.trailingComponent", new Label("Trailing")); + tabbedPane.putClientProperty("JTabbedPane.leadingComponent", new Label("Leading")); + + p.add(tabbedPane, BorderLayout.CENTER); + p.add(new JPanel() {{ + setLayout(new FlowLayout(FlowLayout.LEFT)); + add(new JComboBox() {{ + addItem("TOP"); + addItem("BOTTOM"); + addItem("LEFT"); + addItem("RIGHT"); + setEditable(false); + addItemListener(e -> { + if (e.getStateChange() == ItemEvent.SELECTED) { + if ("TOP".equals(e.getItem())) { + tabbedPane.setTabPlacement(JTabbedPane.TOP); + } else if ("BOTTOM".equals(e.getItem())) { + tabbedPane.setTabPlacement(JTabbedPane.BOTTOM); + } else if ("LEFT".equals(e.getItem())) { + tabbedPane.setTabPlacement(JTabbedPane.LEFT); + } else if ("RIGHT".equals(e.getItem())) { + tabbedPane.setTabPlacement(JTabbedPane.RIGHT); + } + tabbedPane.requestFocus(); + } + }); + }}); + add(new JCheckBox("Scroll-layout enabled") {{ + addActionListener(e -> { + if (tabbedPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) { + tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + } else { + tabbedPane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); + } + }); + setSelected(true); + }}); + add(new JCheckBox("Left To Right") {{ + addActionListener(e -> { + if (tabbedPane.getComponentOrientation().isLeftToRight()) { + tabbedPane.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); + } else { + tabbedPane.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); + } + }); + setSelected(true); + }}); + add(new JCheckBox("DnD enabled") {{ + addActionListener(e -> { + if (tabbedPane.getDropTarget().isActive()) { + tabbedPane.putClientProperty("JTabbedPane.dndEnabled", Boolean.FALSE); + } else { + tabbedPane.putClientProperty("JTabbedPane.dndEnabled", Boolean.TRUE); + } + }); + setSelected(true); + }}); + }}, BorderLayout.SOUTH); + c.add(p); } -// tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); - p.add(tabbedPane, BorderLayout.CENTER); - p.add(new JPanel(){{ - setLayout(new FlowLayout(FlowLayout.LEFT)); - add(new JButton("Unfocus")); - }}, BorderLayout.SOUTH); - frame.setContentPane(p); + frame.setContentPane(c); frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); frame.repaint(); }); } + + private static final class Label extends JLabel implements UIResource { + private Label(final String title) { + super(title); + setBorder(new EmptyBorder(5, 5,5,5)); + } + } } diff --git a/src/test/java/UIDemo.java b/src/test/java/UIDemo.java index 9081fc8b..27af9a93 100644 --- a/src/test/java/UIDemo.java +++ b/src/test/java/UIDemo.java @@ -58,6 +58,11 @@ public final class UIDemo { putClientProperty("JButton.variant", "shadow"); }}); }}); + add(new JToggleButton("toggle"){{ + putClientProperty("ToggleButton.variant", "slider"); + setEnabled(false); + setSelected(true); + }}); }}); panel.add(new JPanel() {{ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); @@ -113,13 +118,14 @@ public final class UIDemo { panel.add(new JPanel() {{ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); add(new JButton("enabled") {{ - setRolloverEnabled(true); }}); add(new JButton("disabled") {{ setEnabled(false); }}); add(defaultButton); - add(new JToggleButton("toggle")); + add(new JToggleButton("toggle"){{ + putClientProperty("ToggleButton.variant", "slider"); + }}); add(new JButton("square") {{ putClientProperty("JButton.buttonType", "square"); }});