diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java index b164973d..54f2f3cd 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java @@ -454,6 +454,26 @@ public class FlatScrollPaneUI paint( g, c ); } + @Override + public void paint( Graphics g, JComponent c ) { + Border viewportBorder = scrollpane.getViewportBorder(); + if( viewportBorder != null ) { + Rectangle r = scrollpane.getViewportBorderBounds(); + int padding = getBorderLeftRightPadding( scrollpane ); + JScrollBar vsb = scrollpane.getVerticalScrollBar(); + if( padding > 0 && + vsb != null && vsb.isVisible() && + scrollpane.getLayout() instanceof FlatScrollPaneLayout && + ((FlatScrollPaneLayout)scrollpane.getLayout()).canIncreaseViewportWidth( scrollpane ) ) + { + boolean ltr = scrollpane.getComponentOrientation().isLeftToRight(); + int extraWidth = Math.min( padding, vsb.getWidth() ); + viewportBorder.paintBorder( scrollpane, g, r.x - (ltr ? 0 : extraWidth), r.y, r.width + extraWidth, r.height ); + } else + viewportBorder.paintBorder( scrollpane, g, r.x, r.y, r.width, r.height ); + } + } + /** @since 1.3 */ public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) { Component view = getView( scrollPane ); @@ -488,6 +508,13 @@ public class FlatScrollPaneUI : 0; } + private static int getBorderLeftRightPadding( JScrollPane scrollPane ) { + Border border = scrollPane.getBorder(); + return (border instanceof FlatScrollPaneBorder) + ? ((FlatScrollPaneBorder)border).getLeftRightPadding( scrollPane ) + : 0; + } + //---- class Handler ------------------------------------------------------ /** @@ -533,23 +560,46 @@ public class FlatScrollPaneUI super.layoutContainer( parent ); JScrollPane scrollPane = (JScrollPane) parent; - Border border = scrollPane.getBorder(); - int padding; - if( border instanceof FlatScrollPaneBorder && - (padding = ((FlatScrollPaneBorder)border).getLeftRightPadding( scrollPane )) > 0 ) - { - JScrollBar vsb = getVerticalScrollBar(); - if( vsb != null && vsb.isVisible() ) { - // move vertical scrollbar to trailing edge - Insets insets = scrollPane.getInsets(); - Rectangle r = vsb.getBounds(); - int y = Math.max( r.y, insets.top + padding ); - int y2 = Math.min( r.y + r.height, scrollPane.getHeight() - insets.bottom - padding ); - boolean ltr = scrollPane.getComponentOrientation().isLeftToRight(); + int padding = getBorderLeftRightPadding( scrollPane ); + if( padding > 0 && vsb != null && vsb.isVisible() ) { + // move vertical scrollbar to trailing edge + Insets insets = scrollPane.getInsets(); + Rectangle r = vsb.getBounds(); + int y = Math.max( r.y, insets.top + padding ); + int y2 = Math.min( r.y + r.height, scrollPane.getHeight() - insets.bottom - padding ); + boolean ltr = scrollPane.getComponentOrientation().isLeftToRight(); - vsb.setBounds( r.x + (ltr ? padding : -padding), y, r.width, y2 - y ); + vsb.setBounds( r.x + (ltr ? padding : -padding), y, r.width, y2 - y ); + + // increase width of viewport, column header and horizontal scrollbar + if( canIncreaseViewportWidth( scrollPane ) ) { + int extraWidth = Math.min( padding, vsb.getWidth() ); + resizeViewport( viewport, extraWidth, ltr ); + resizeViewport( colHead, extraWidth, ltr ); + resizeViewport( hsb, extraWidth, ltr ); } } } + + boolean canIncreaseViewportWidth( JScrollPane scrollPane ) { + return scrollPane.getComponentOrientation().isLeftToRight() + ? !isCornerVisible( upperRight ) && !isCornerVisible( lowerRight ) + : !isCornerVisible( upperLeft ) && !isCornerVisible( lowerLeft ); + } + + private static boolean isCornerVisible( Component corner ) { + return corner != null && + corner.getWidth() > 0 && + corner.getHeight() > 0 && + corner.isVisible(); + } + + private static void resizeViewport( Component c, int extraWidth, boolean ltr ) { + if( c == null ) + return; + + Rectangle vr = c.getBounds(); + c.setBounds( vr.x - (ltr ? 0 : extraWidth), vr.y, vr.width + extraWidth, vr.height ); + } } } diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatRoundedScrollPaneTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatRoundedScrollPaneTest.java index c347c18a..ebea0428 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatRoundedScrollPaneTest.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatRoundedScrollPaneTest.java @@ -24,6 +24,8 @@ import java.util.Arrays; import java.util.stream.Collectors; import javax.swing.*; import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.MatteBorder; import javax.swing.table.AbstractTableModel; import javax.swing.tree.*; import com.formdev.flatlaf.FlatClientProperties; @@ -212,6 +214,19 @@ public class FlatRoundedScrollPaneTest : "?" ); } + private void viewportBorderChanged() { + Border viewportBorder = viewportBorderCheckBox.isSelected() + ? new CompoundBorder( + new MatteBorder( 1, 1, 0, 0, Color.red ), + new MatteBorder( 0, 0, 1, 1, Color.blue ) ) + : null; + for( JScrollPane scrollPane : allJScrollPanes ) { + scrollPane.setViewportBorder( viewportBorder ); + scrollPane.revalidate(); + scrollPane.repaint(); + } + } + private void initComponents() { // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents splitPane2 = new JSplitPane(); @@ -247,6 +262,7 @@ public class FlatRoundedScrollPaneTest cornersCheckBox = new JCheckBox(); columnHeaderCheckBox = new JCheckBox(); horizontalScrollBarCheckBox = new JCheckBox(); + viewportBorderCheckBox = new JCheckBox(); rowHeaderCheckBox = new JCheckBox(); verticalScrollBarCheckBox = new JCheckBox(); @@ -437,6 +453,11 @@ public class FlatRoundedScrollPaneTest horizontalScrollBarCheckBox.addActionListener(e -> horizontalScrollBarChanged()); panel3.add(horizontalScrollBarCheckBox, "cell 4 0"); + //---- viewportBorderCheckBox ---- + viewportBorderCheckBox.setText("Viewport border"); + viewportBorderCheckBox.addActionListener(e -> viewportBorderChanged()); + panel3.add(viewportBorderCheckBox, "cell 2 1"); + //---- rowHeaderCheckBox ---- rowHeaderCheckBox.setText("Row Header"); rowHeaderCheckBox.addActionListener(e -> rowHeaderChanged()); @@ -485,6 +506,7 @@ public class FlatRoundedScrollPaneTest private JCheckBox cornersCheckBox; private JCheckBox columnHeaderCheckBox; private JCheckBox horizontalScrollBarCheckBox; + private JCheckBox viewportBorderCheckBox; private JCheckBox rowHeaderCheckBox; private JCheckBox verticalScrollBarCheckBox; // JFormDesigner - End of variables declaration //GEN-END:variables diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatRoundedScrollPaneTest.jfd b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatRoundedScrollPaneTest.jfd index a6af6ad5..ffaf2a6a 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatRoundedScrollPaneTest.jfd +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatRoundedScrollPaneTest.jfd @@ -1,4 +1,4 @@ -JFDML JFormDesigner: "8.1.0.0.283" Java: "19.0.2" encoding: "UTF-8" +JFDML JFormDesigner: "8.1.1.0.298" Java: "19.0.2" encoding: "UTF-8" new FormModel { contentType: "form/swing" @@ -216,6 +216,13 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 4 0" } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "viewportBorderCheckBox" + "text": "Viewport border" + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "viewportBorderChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 1" + } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "rowHeaderCheckBox" "text": "Row Header"