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 <[email protected]>
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: [email protected]
For additional commands, e-mail: [email protected]
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists