This is an automated email from the ASF dual-hosted git repository.

ebakke pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new 8f79d085ec Improve vertical text alignment in FlatLAF window system 
tabs.
8f79d085ec is described below

commit 8f79d085ece3e25423e7e106336e60809e686a8c
Author: Eirik Bakke <eba...@ultorg.com>
AuthorDate: Thu Jul 7 22:49:45 2022 -0400

    Improve vertical text alignment in FlatLAF window system tabs.
    
    This makes the tab labels align with the tab icon and the "X" button, with 
any
    font and at any font size. The font-agnostic calculation is useful because
    FlatLAF uses a different UI font on each OS. Accurate vertical alignment 
also
    allows more compact tab styles if desired in the future.
    
    See the Pull Request for before/after screenshots.
    
    Details:
    * API change: Allow AbstractTabCellRenderer subclasses to override text 
position
      calculation.
    * In FlatLAF, improve the calculation of tab labels' Y position, to avoid 
the
      need for ad hoc adjustments and to work with a variety of font families 
and
      font sizes. Ensure that text in view tabs and editor tabs still line up.
    * In FlatLAF, ensure a minimum tab height for smaller fonts, based on the
      expected icon size, regardless of inset settings. This will allow the same
      inset settings to work for e.g. Tahoma 11 and Segoe UI 12 (the old and new
      default fonts on Windows), and other fonts which may be used by default 
for
      FlatLAF on other operating systems.
    * In FlatEditorTabDisplayerUI.getPreferredSize, use the correct graphics 
context
      for font metrics calculations.
    * Also avoid unnecessary rounding error in the vertical centering of tab 
icons
      and buttons, by doing "(a-b)/2" instead of "a/2-b/2". This is done for all
      LAFs; the difference is at most one pixel compared to the old behavior.
---
 .../o.n.swing.laf.flatlaf/nbproject/project.xml    |  2 +-
 .../laf/flatlaf/ui/FlatEditorTabCellRenderer.java  | 37 +++++++++--------
 .../laf/flatlaf/ui/FlatEditorTabDisplayerUI.java   | 15 +++----
 .../swing/laf/flatlaf/ui/FlatTabControlIcon.java   |  3 +-
 .../laf/flatlaf/ui/FlatViewTabDisplayerUI.java     | 20 +++++++---
 platform/o.n.swing.tabcontrol/apichanges.xml       | 17 ++++++++
 platform/o.n.swing.tabcontrol/manifest.mf          |  2 +-
 .../tabcontrol/plaf/AbstractTabCellRenderer.java   | 46 ++++++++++++++--------
 .../plaf/AbstractViewTabDisplayerUI.java           |  4 +-
 .../plaf/BasicScrollingTabDisplayerUI.java         |  4 +-
 10 files changed, 95 insertions(+), 55 deletions(-)

diff --git a/platform/o.n.swing.laf.flatlaf/nbproject/project.xml 
b/platform/o.n.swing.laf.flatlaf/nbproject/project.xml
index 08b6834a67..fc8a5ffa34 100644
--- a/platform/o.n.swing.laf.flatlaf/nbproject/project.xml
+++ b/platform/o.n.swing.laf.flatlaf/nbproject/project.xml
@@ -71,7 +71,7 @@
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>1.63</specification-version>
+                        <specification-version>1.74</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
diff --git 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatEditorTabCellRenderer.java
 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatEditorTabCellRenderer.java
index 3caaf98c82..fc0e0f8b67 100644
--- 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatEditorTabCellRenderer.java
+++ 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatEditorTabCellRenderer.java
@@ -22,13 +22,14 @@ import com.formdev.flatlaf.util.UIScale;
 import java.awt.Color;
 import java.awt.Component;
 import java.awt.Dimension;
-import java.awt.FontMetrics;
+import java.awt.Font;
 import java.awt.GradientPaint;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.Insets;
 import java.awt.Polygon;
 import java.awt.Rectangle;
+import java.awt.font.FontRenderContext;
 import javax.swing.Icon;
 import javax.swing.JComponent;
 import javax.swing.UIManager;
@@ -102,23 +103,23 @@ public class FlatEditorTabCellRenderer extends 
AbstractTabCellRenderer {
     }
 
     @Override
-    protected int getCaptionYAdjustment() {
-        // Workaround for a issue in 
AbstractTabCellRenderer.paintIconAndText(Graphics),
-        // which uses font height (which includes font descent) to calculate 
Y-coordinate
-        // when available height is equal to font height (availH <= txtH),
-        // but HtmlRenderer.renderString() expects Y-coordinate at baseline.
-        // So the text is painted vertically out of center.
-        //
-        // This seems to be no issue with other LAFs because they seem to use
-        // TabPainter insets differently and the available height is larger 
than
-        // the font height (availH > txtH), in which case 3 pixels are removed 
from
-        // the Y-coordinate to avoid that the text is painted vertically out 
of center.
-
-        FontMetrics fm = getFontMetrics(getFont());
-        int txtH = fm.getHeight();
+    protected int getCaptionYPosition(Graphics g) {
+        Font font = getFont();
+        FontRenderContext frc = (g instanceof Graphics2D)
+                ? ((Graphics2D) g).getFontRenderContext()
+                : g.getFontMetrics(font).getFontRenderContext();
+        /* Don't rely on FontMetrics.getAscent() to get the ascent; it can 
return values much bigger
+        than the actual, visual size of the letters. Use the actual height of 
a flat-topped
+        upper-case letter instead. */
+        double txtVisualAscent = font.createGlyphVector(frc, "H")
+                .getVisualBounds().getHeight();
         Insets ins = getInsets();
         int availH = getHeight() - (ins.top + ins.bottom);
-        return (availH <= txtH) ? -fm.getDescent() : -1;
+        final int effectiveIconYAdjustment = 2 + getIconYAdjustment();
+
+        // Center the visual ascent portion of the text vertically with 
respect to the icon.
+        return ins.top + (int) Math.round((availH + txtVisualAscent) / 2)
+                + effectiveIconYAdjustment;
     }
 
     @Override
@@ -178,7 +179,9 @@ public class FlatEditorTabCellRenderer extends 
AbstractTabCellRenderer {
             int iconWidth = icon.getIconWidth();
             int iconHeight = icon.getIconHeight();
             rect.x = bounds.x + bounds.width - iconWidth - 
UIScale.scale(CLOSE_ICON_RIGHT_PAD);
-            rect.y = bounds.y + Math.max(0, (bounds.height - iconHeight) / 2) 
- 1;
+            // Ad hoc adjustment.
+            int yAdjustment = 2;
+            rect.y = bounds.y + Math.max(0, (bounds.height - iconHeight) / 2) 
- 1 + yAdjustment;
             rect.width = iconWidth;
             rect.height = iconHeight;
         }
diff --git 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatEditorTabDisplayerUI.java
 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatEditorTabDisplayerUI.java
index 56475a806d..e7c0d22e31 100644
--- 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatEditorTabDisplayerUI.java
+++ 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatEditorTabDisplayerUI.java
@@ -66,15 +66,12 @@ public class FlatEditorTabDisplayerUI extends 
BasicScrollingTabDisplayerUI {
 
     @Override
     public Dimension getPreferredSize(JComponent c) {
-        int prefHeight;
-        Graphics g = BasicScrollingTabDisplayerUI.getOffscreenGraphics();
-        if (g != null) {
-            FontMetrics fm = g.getFontMetrics(displayer.getFont());
-            Insets ins = getTabAreaInsets();
-            prefHeight = fm.getHeight() + ins.top + ins.bottom
-                    + tabInsets.top + tabInsets.bottom;
-        } else
-            prefHeight = UIScale.scale(28);
+        Graphics g = BasicScrollingTabDisplayerUI.getOffscreenGraphics(c);
+        FontMetrics fm = g.getFontMetrics(displayer.getFont());
+        Insets ins = getTabAreaInsets();
+        // Standard icons are 16 pixels tall, so always allocate space for 
them.
+        int prefHeight = Math.max(fm.getHeight(), 16) + ins.top + ins.bottom
+                + tabInsets.top + tabInsets.bottom;
         return new Dimension(displayer.getWidth(), prefHeight);
     }
 
diff --git 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatTabControlIcon.java
 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatTabControlIcon.java
index c5064981b5..4b7eb85909 100644
--- 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatTabControlIcon.java
+++ 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatTabControlIcon.java
@@ -110,7 +110,8 @@ public final class FlatTabControlIcon extends VectorIcon {
     }
 
     private FlatTabControlIcon(int buttonId, Integer buttonState) {
-        super(UIScale.scale(16), UIScale.scale(16));
+        super(UIScale.scale(16),
+            UIScale.scale(buttonId == TabControlButton.ID_CLOSE_BUTTON ? 15 : 
16));
         this.buttonId = buttonId;
         this.buttonState = buttonState;
         this.userScaleFactor = UIScale.getUserScaleFactor();
diff --git 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatViewTabDisplayerUI.java
 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatViewTabDisplayerUI.java
index b7c656e0ab..0faa6b2053 100644
--- 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatViewTabDisplayerUI.java
+++ 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/FlatViewTabDisplayerUI.java
@@ -110,7 +110,10 @@ public class FlatViewTabDisplayerUI extends 
AbstractViewTabDisplayerUI {
     @Override
     public Dimension getPreferredSize(JComponent c) {
         FontMetrics fm = getTxtFontMetrics();
-        int height = fm.getHeight() + tabInsets.top + tabInsets.bottom;
+        /* In FlatEditorTabDisplayerUI, we always allocate 16 pixels for the 
icon. Do the same here,
+        even though view tabs do not normally have icons, so that view tabs 
always line up with
+        editor tabs. */
+        int height = Math.max(16, fm.getHeight()) + tabInsets.top + 
tabInsets.bottom;
         return new Dimension(100, height);
     }
 
@@ -133,7 +136,9 @@ public class FlatViewTabDisplayerUI extends 
AbstractViewTabDisplayerUI {
                 } else {
                     buttons.setVisible(true);
                     availTxtWidth -= (buttonsSize.width + ICON_X_PAD);
-                    buttons.setLocation(x + width - buttonsSize.width - 
ICON_X_PAD, y + ((height - buttonsSize.height) / 2) - 1);
+                    // Ad hoc adjustment.
+                    int yAdjustment = 2;
+                    buttons.setLocation(x + width - buttonsSize.width - 
ICON_X_PAD, y + ((height - buttonsSize.height) / 2) - 1 + yAdjustment);
                 }
             }
         }
@@ -188,16 +193,19 @@ public class FlatViewTabDisplayerUI extends 
AbstractViewTabDisplayerUI {
 
         // paint text
         int txtX = x + txtLeftPad;
-        int txtY = y + tabInsets.top + fm.getAscent();
         int availH = height - tabInsets.top - tabInsets.bottom;
-        if (availH > fm.getHeight()) {
-            txtY += (availH - fm.getHeight()) / 2;
-        }
         int style = HtmlRenderer.STYLE_TRUNCATE;
         if (!isSelected(index)) {
             // center text of unselected tabs
             txtX = Math.max(x + 1, x + ((width - realTxtWidth) / 2));
         }
+
+        /* Keep the txtY calculation the same as for 
FlatEditorTabCellRenderer, with an offset that
+        makes the text in view tabs and editor tabs always line up. */
+        double txtVisualAscent = 
getTxtFont().createGlyphVector(fm.getFontRenderContext(), "H")
+            .getVisualBounds().getHeight();
+        int txtY = tabInsets.top + (int) Math.round((availH + txtVisualAscent) 
/ 2) + 1;
+
         HtmlRenderer.renderString(text, g, txtX, txtY, availTxtWidth, height,
                 getTxtFont(), c, style, true);
     }
diff --git a/platform/o.n.swing.tabcontrol/apichanges.xml 
b/platform/o.n.swing.tabcontrol/apichanges.xml
index 3e45572bd4..42fef25a2a 100644
--- a/platform/o.n.swing.tabcontrol/apichanges.xml
+++ b/platform/o.n.swing.tabcontrol/apichanges.xml
@@ -85,6 +85,23 @@ is the proper place.
 
 <changes>
 
+    <change>
+        <api name="tabcontrol"/>
+        <summary>Allow tab control renderers to override text position 
calculation</summary>
+        <version major="1" minor="74"/>
+        <date day="8" month="7" year="2022"/>
+        <author login="ebakke"/>
+        <compatibility addition="yes"/>
+        <description>
+            A new method 
<code>AbstractTabCellRenderer.getCaptionYPosition</code>
+            may now be overridden by subclasses, to customize the calculation 
of
+            the tab label's Y position. This allows specific implementations to
+            improve the calculation code without affecting other LAFs (which
+            may contain adjustments hard-coded to work with the old 
calculation.)
+        </description>
+        <class package="org.netbeans.swing.tabcontrol.plaf" 
name="AbstractTabCellRenderer"/>
+    </change>
+
     <change id="autoscroll">
       <api name="tabcontrol"/>
       <summary>Added ability to scroll document tabs when dragging a 
TopComponent
diff --git a/platform/o.n.swing.tabcontrol/manifest.mf 
b/platform/o.n.swing.tabcontrol/manifest.mf
index 0e2df427ad..de5acb779e 100644
--- a/platform/o.n.swing.tabcontrol/manifest.mf
+++ b/platform/o.n.swing.tabcontrol/manifest.mf
@@ -1,6 +1,6 @@
 Manifest-Version: 1.0
 OpenIDE-Module-Localizing-Bundle: 
org/netbeans/swing/tabcontrol/Bundle.properties
 OpenIDE-Module: org.netbeans.swing.tabcontrol
-OpenIDE-Module-Specification-Version: 1.73
+OpenIDE-Module-Specification-Version: 1.74
 AutoUpdate-Essential-Module: true
 
diff --git 
a/platform/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractTabCellRenderer.java
 
b/platform/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractTabCellRenderer.java
index 60f4a1d5c9..2037d0c00a 100644
--- 
a/platform/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractTabCellRenderer.java
+++ 
b/platform/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractTabCellRenderer.java
@@ -425,7 +425,9 @@ public abstract class AbstractTabCellRenderer extends JLabel
         paintIconAndText(g);
     }
 
-    /** Return non-zero to shift the text up or down by the specified number 
of pixels when painting.
+    /**
+     * Return non-zero to shift the text up or down by the specified number of 
pixels when painting.
+     * Used by the default implementation of {@link 
#getCaptionYPosition(Graphics)}.
      *
      * @return A positive or negative number of pixels
      */
@@ -442,12 +444,14 @@ public abstract class AbstractTabCellRenderer extends 
JLabel
     }
 
     /**
-     * Actually paints the icon and text (using the lightweight HTML renderer)
+     * Get the Y position of the caption. May be overridden. The default 
implementation uses an old
+     * formula which is retained for backwards compatibility (since subclasses 
may have tuned their
+     * value of {@link #getCaptionYAdjustment()} based on it).
      *
      * @param g The graphics context
+     * @return The Y position of the caption's baseline
      */
-    protected void paintIconAndText(Graphics g) {
-        g.setFont(getFont());
+    protected int getCaptionYPosition(Graphics g) {
         FontMetrics fm = g.getFontMetrics(getFont());
         //Find out what height we need
         int txtH = fm.getHeight();
@@ -460,6 +464,23 @@ public abstract class AbstractTabCellRenderer extends 
JLabel
         } else {
             txtY = txtH + ins.top;
         }
+        txtY += getCaptionYAdjustment();
+        return txtY;
+    }
+
+    /**
+     * Actually paints the icon and text (using the lightweight HTML renderer)
+     *
+     * @param g The graphics context
+     */
+    protected void paintIconAndText(Graphics g) {
+        g.setFont(getFont());
+        FontMetrics fm = g.getFontMetrics(getFont());
+        //Find out what height we need
+        int txtH = fm.getHeight();
+        Insets ins = getInsets();
+        //find out the available height
+        int availH = getHeight() - (ins.top + ins.bottom);
         int txtX;
 
         int centeringToAdd = getPixelsToAddToSelection() != 0 ?
@@ -467,18 +488,11 @@ public abstract class AbstractTabCellRenderer extends 
JLabel
 
         Icon icon = getIcon();
         //Check the icon non-null and height (see TabData.NO_ICON for why)
-        if (!isClipLeft() && icon != null && icon.getIconWidth() > 0
-                && icon.getIconHeight() > 0) {
-            int iconY;
-            if (availH > icon.getIconHeight()) {
-                //add 2 to make sure icon top pixels are not cut off by outline
-                iconY = ins.top
-                        + ((availH / 2) - (icon.getIconHeight() / 2))
+        if (!isClipLeft() && icon != null && icon.getIconWidth() > 0 && 
icon.getIconHeight() > 0) {
+            int iconY = ins.top
+                        + Math.max(0, (availH - icon.getIconHeight())) / 2
+                        //add 2 to make sure icon top pixels are not cut off 
by outline
                         + 2;
-            } else {
-                //add 2 to make sure icon top pixels are not cut off by outline
-                iconY = ins.top + 2;
-            }
             int iconX = ins.left + centeringToAdd;
 
             iconY += getIconYAdjustment();
@@ -495,7 +509,7 @@ public abstract class AbstractTabCellRenderer extends JLabel
             txtX += 5;
         }
 
-        txtY += getCaptionYAdjustment();
+        int txtY = getCaptionYPosition(g);
 
         //Get the available horizontal pixels for text
         int txtW = getWidth() - (txtX + ins.right);
diff --git 
a/platform/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractViewTabDisplayerUI.java
 
b/platform/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractViewTabDisplayerUI.java
index e8b88732d5..6fe907cb10 100644
--- 
a/platform/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractViewTabDisplayerUI.java
+++ 
b/platform/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/AbstractViewTabDisplayerUI.java
@@ -225,7 +225,7 @@ public abstract class AbstractViewTabDisplayerUI extends 
TabDisplayerUI {
 
             if( null != btnClose ) {
                 Icon icon = btnClose.getIcon();
-                btnClose.setBounds( width, height/2-icon.getIconHeight()/2, 
icon.getIconWidth(), icon.getIconHeight() );
+                btnClose.setBounds( width, (height - icon.getIconHeight()) / 
2, icon.getIconWidth(), icon.getIconHeight() );
                 width += icon.getIconWidth();
             }
 
@@ -233,7 +233,7 @@ public abstract class AbstractViewTabDisplayerUI extends 
TabDisplayerUI {
                 if( 0 != width )
                     width += ICON_X_PAD;
                 Icon icon = btnAutoHidePin.getIcon();
-                btnAutoHidePin.setBounds( width, 
height/2-icon.getIconHeight()/2, icon.getIconWidth(), icon.getIconHeight() );
+                btnAutoHidePin.setBounds( width, (height - 
icon.getIconHeight()) / 2, icon.getIconWidth(), icon.getIconHeight() );
                 width += icon.getIconWidth();
                 width += ICON_X_PAD;
             }
diff --git 
a/platform/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicScrollingTabDisplayerUI.java
 
b/platform/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicScrollingTabDisplayerUI.java
index 1bdc632e7f..72cafe6278 100644
--- 
a/platform/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicScrollingTabDisplayerUI.java
+++ 
b/platform/o.n.swing.tabcontrol/src/org/netbeans/swing/tabcontrol/plaf/BasicScrollingTabDisplayerUI.java
@@ -383,7 +383,7 @@ public abstract class BasicScrollingTabDisplayerUI extends 
BasicTabDisplayerUI {
 
     /**
      * Provides an offscreen graphics context so that widths based on character
-     * size can be calculated correctly before the component is shown.
+     * size can be calculated correctly before the component is shown. Never 
returns null.
      *
      * <p>For more accurate text measurements, clients should prefer calling
      * {@link #getOffscreenGraphics(JComponent)}.
@@ -399,7 +399,7 @@ public abstract class BasicScrollingTabDisplayerUI extends 
BasicTabDisplayerUI {
 
     /**
      * Provides an offscreen graphics context so that widths based on character
-     * size can be calculated correctly before the component is shown
+     * size can be calculated correctly before the component is shown. Never 
returns null.
      *
      * @param component may be null without causing fatal errors, but should 
be set for accurate
      *        text measurement (especially on displays with HiDPI scaling 
enabled)


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org
For additional commands, e-mail: commits-h...@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists

Reply via email to