From 5436ea88d817f1e6abfa9e38969be65340eda680 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Sun, 13 Aug 2023 17:01:56 +0200 Subject: [PATCH] ScrollPane: improved/fixed calculation of left/right padding for rounded border --- .../flatlaf/ui/FlatScrollPaneBorder.java | 25 ++++++++-- .../formdev/flatlaf/ui/FlatScrollPaneUI.java | 14 +++--- .../testing/FlatRoundedScrollPaneTest.java | 48 +++++++++++++------ .../testing/FlatRoundedScrollPaneTest.jfd | 36 +++++++++----- 4 files changed, 85 insertions(+), 38 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneBorder.java index 845ea12f..ba4f785c 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneBorder.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneBorder.java @@ -41,11 +41,8 @@ public class FlatScrollPaneBorder // if view is rounded, increase left and right insets to avoid that the viewport // is painted over the rounded border on the corners - int arc = getArc( c ); - if( arc > 0 ) { - // increase insets by radius minus lineWidth because radius is measured - // from the outside of the line, but insets from super include lineWidth - int padding = UIScale.scale( (arc / 2) - getLineWidth( c ) ); + int padding = getLeftRightPadding( c ); + if( padding > 0 ) { insets.left += padding; insets.right += padding; } @@ -60,4 +57,22 @@ public class FlatScrollPaneBorder return arc; } + + /** + * Returns the scaled left/right padding used when arc is larger than zero. + *

+ * This is the distance from the inside of the left border to the left side of the view component. + * On the right side, this is the distance between the right side of the view component and + * the vertical scrollbar. Or the inside of the right border if the scrollbar is hidden. + */ + public int getLeftRightPadding( Component c ) { + // Subtract lineWidth from radius because radius is given for the outside + // of the painted line, but insets from super already include lineWidth. + // Reduce padding by 10% to make padding slightly smaller because it is not recognizable + // when the view is minimally painted over the beginning of the border curve. + int arc = getArc( c ); + return (arc > 0) + ? Math.max( Math.round( UIScale.scale( ((arc / 2f) - getLineWidth( c )) * 0.9f ) ), 0 ) + : 0; + } } 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 bb65fac8..c785698c 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 @@ -521,17 +521,19 @@ public class FlatScrollPaneUI super.layoutContainer( parent ); JScrollPane scrollPane = (JScrollPane) parent; - float arc = getBorderArc( scrollPane ); - if( arc > 0 ) { + 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 - int padding = Math.round( (arc / 2) - FlatUIUtils.getBorderLineWidth( scrollPane ) ); - Insets insets = parent.getInsets(); + 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, parent.getHeight() - insets.bottom - padding ); - boolean ltr = parent.getComponentOrientation().isLeftToRight(); + 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 ); } 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 967b1358..c347c18a 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 @@ -23,9 +23,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.stream.Collectors; import javax.swing.*; +import javax.swing.border.Border; import javax.swing.table.AbstractTableModel; import javax.swing.tree.*; import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.ui.FlatScrollPaneBorder; import com.formdev.flatlaf.util.UIScale; import net.miginfocom.swing.*; @@ -203,6 +205,11 @@ public class FlatRoundedScrollPaneTest int arc = arcSlider.getValue(); for( JScrollPane scrollPane : allJScrollPanes ) scrollPane.putClientProperty( FlatClientProperties.STYLE, "arc: " + arc ); + + Border border = allJScrollPanes[0].getBorder(); + paddingField.setText( border instanceof FlatScrollPaneBorder + ? Integer.toString( ((FlatScrollPaneBorder)border).getLeftRightPadding( allJScrollPanes[0] ) ) + : "?" ); } private void initComponents() { @@ -210,6 +217,8 @@ public class FlatRoundedScrollPaneTest splitPane2 = new JSplitPane(); panel1 = new FlatTestPanel(); listLabel = new JLabel(); + paddingLabel = new JLabel(); + paddingField = new JLabel(); treeLabel = new JLabel(); tableLabel = new JLabel(); autoResizeModeCheckBox = new JCheckBox(); @@ -237,8 +246,8 @@ public class FlatRoundedScrollPaneTest arcSlider = new JSlider(); cornersCheckBox = new JCheckBox(); columnHeaderCheckBox = new JCheckBox(); - rowHeaderCheckBox = new JCheckBox(); horizontalScrollBarCheckBox = new JCheckBox(); + rowHeaderCheckBox = new JCheckBox(); verticalScrollBarCheckBox = new JCheckBox(); //======== this ======== @@ -273,6 +282,14 @@ public class FlatRoundedScrollPaneTest listLabel.setHorizontalTextPosition(SwingConstants.LEADING); panel1.add(listLabel, "cell 0 0,aligny top,growy 0"); + //---- paddingLabel ---- + paddingLabel.setText("Padding:"); + panel1.add(paddingLabel, "cell 0 0,alignx trailing,growx 0"); + + //---- paddingField ---- + paddingField.setText("0"); + panel1.add(paddingField, "cell 0 0,alignx trailing,growx 0"); + //---- treeLabel ---- treeLabel.setText("JTree:"); treeLabel.setHorizontalTextPosition(SwingConstants.LEADING); @@ -382,12 +399,11 @@ public class FlatRoundedScrollPaneTest // columns "[fill]" + "[grow,fill]para" + - "[fill]" + - "[fill]" + - "[fill]" + - "[fill]" + - "[fill]", + "[]" + + "[]" + + "[]", // rows + "[]" + "[]")); //---- arcLabel ---- @@ -403,7 +419,7 @@ public class FlatRoundedScrollPaneTest arcSlider.setPaintTicks(true); arcSlider.setPaintLabels(true); arcSlider.addChangeListener(e -> arcSliderChanged()); - panel3.add(arcSlider, "cell 1 0"); + panel3.add(arcSlider, "cell 1 0 1 2"); //---- cornersCheckBox ---- cornersCheckBox.setText("Corners"); @@ -415,22 +431,22 @@ public class FlatRoundedScrollPaneTest columnHeaderCheckBox.addActionListener(e -> columnHeaderChanged()); panel3.add(columnHeaderCheckBox, "cell 3 0"); - //---- rowHeaderCheckBox ---- - rowHeaderCheckBox.setText("Row Header"); - rowHeaderCheckBox.addActionListener(e -> rowHeaderChanged()); - panel3.add(rowHeaderCheckBox, "cell 4 0"); - //---- horizontalScrollBarCheckBox ---- horizontalScrollBarCheckBox.setText("Horizontal ScrollBar"); horizontalScrollBarCheckBox.setSelected(true); horizontalScrollBarCheckBox.addActionListener(e -> horizontalScrollBarChanged()); - panel3.add(horizontalScrollBarCheckBox, "cell 5 0"); + panel3.add(horizontalScrollBarCheckBox, "cell 4 0"); + + //---- rowHeaderCheckBox ---- + rowHeaderCheckBox.setText("Row Header"); + rowHeaderCheckBox.addActionListener(e -> rowHeaderChanged()); + panel3.add(rowHeaderCheckBox, "cell 3 1"); //---- verticalScrollBarCheckBox ---- verticalScrollBarCheckBox.setText("Vertical ScrollBar"); verticalScrollBarCheckBox.setSelected(true); verticalScrollBarCheckBox.addActionListener(e -> verticalScrollBarChanged()); - panel3.add(verticalScrollBarCheckBox, "cell 6 0"); + panel3.add(verticalScrollBarCheckBox, "cell 4 1"); } add(panel3, "cell 0 1"); // JFormDesigner - End of component initialization //GEN-END:initComponents @@ -440,6 +456,8 @@ public class FlatRoundedScrollPaneTest private JSplitPane splitPane2; private FlatTestPanel panel1; private JLabel listLabel; + private JLabel paddingLabel; + private JLabel paddingField; private JLabel treeLabel; private JLabel tableLabel; private JCheckBox autoResizeModeCheckBox; @@ -466,8 +484,8 @@ public class FlatRoundedScrollPaneTest private JSlider arcSlider; private JCheckBox cornersCheckBox; private JCheckBox columnHeaderCheckBox; - private JCheckBox rowHeaderCheckBox; private JCheckBox horizontalScrollBarCheckBox; + 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 bb602b71..a6af6ad5 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 @@ -26,6 +26,18 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 0 0,aligny top,growy 0" } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "paddingLabel" + "text": "Padding:" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0,alignx trailing,growx 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "paddingField" + "text": "0" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0,alignx trailing,growx 0" + } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "treeLabel" "text": "JTree:" @@ -156,8 +168,8 @@ new FormModel { } ) add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { "$layoutConstraints": "hidemode 3" - "$columnConstraints": "[fill][grow,fill]para[fill][fill][fill][fill][fill]" - "$rowConstraints": "[]" + "$columnConstraints": "[fill][grow,fill]para[][][]" + "$rowConstraints": "[][]" } ) { name: "panel3" add( new FormComponent( "javax.swing.JLabel" ) { @@ -180,7 +192,7 @@ new FormModel { "paintLabels": true addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "arcSliderChanged", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 0" + "value": "cell 1 0 1 2" } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "cornersCheckBox" @@ -196,20 +208,20 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 3 0" } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "rowHeaderCheckBox" - "text": "Row Header" - addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "rowHeaderChanged", false ) ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 4 0" - } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "horizontalScrollBarCheckBox" "text": "Horizontal ScrollBar" "selected": true addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "horizontalScrollBarChanged", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 5 0" + "value": "cell 4 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "rowHeaderCheckBox" + "text": "Row Header" + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "rowHeaderChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 3 1" } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "verticalScrollBarCheckBox" @@ -217,7 +229,7 @@ new FormModel { "selected": true addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "verticalScrollBarChanged", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 6 0" + "value": "cell 4 1" } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 0 1"