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 1e920a3a71 FlatLAF improvements and new configuration properties See 
the Pull Request for screenshots and links to related discussion/PRs.
1e920a3a71 is described below

commit 1e920a3a713937430233e466c6afaa4813d06b05
Author: Eirik Bakke <eba...@ultorg.com>
AuthorDate: Tue Jul 5 13:53:43 2022 -0400

    FlatLAF improvements and new configuration properties
    See the Pull Request for screenshots and links to related discussion/PRs.
    
    Visual improvements, annotated in screenshot in Pull Request:
    1) Align and connect borders between tabs and the container panel below,
       including on fractional HiDPI scalings (e.g. 150%). Some visual artifacts
       still exist, e.g. on 125% scaling, but they are less common, and not very
       visible.
    2) Remove an unnecessary border around the editor area.
    3) Make sure the "X" in editor tabs are closer to the label than to the
       separator line.
    4) Add some top/bottom margin in the toolbar
    5) On FlatLAF Dark, make the tab background color connect with the 
background
       of the Project/Files/Services and Navigator panes.
    6) Slightly decrease the height of regular tab components.
    
    Other visual improvements:
    * On FlatLAF Dark and Light, make the tab hover effect more subtle. Avoid
      showing a hover effect on the already-selected tab. Use the blueish 
active tab
      area background as a base for this color, rather than plain gray.
    * Fix positioning of "spinner" icon in sidebar tabs
      (see illustration in PR 2967, which did the same on the Windows LAF).
    * Properly clip editor tab borders when tab is partially invisible due to
      tab scrolling.
    
    New configuration settings for FlatLAF.properties
    * EditorTab.selectedBackgroundBottomGradient
      (Not used in the current theme, but was tested.)
    * Add ViewTab/EditorTab.unscaledBorders
      (Not used in the current FlatLAF theme, but is used in the Windows LAF.)
    * EditorTab/ViewTab.selectedHoverBackground/selectedHoverForeground
      (Tweaks in these commits make use of selectedHoverBackground.)
    * ViewTab/EditorTab.showSelectedTabBorder
      (Used to explicitly enable borders around the selected tab.)
---
 .../netbeans/swing/laf/flatlaf/DPISafeBorder.java  |   9 +-
 .../swing/laf/flatlaf/FlatDarkLaf.properties       |  11 ++-
 .../netbeans/swing/laf/flatlaf/FlatLFCustoms.java  |   4 +-
 .../netbeans/swing/laf/flatlaf/FlatLaf.properties  |  11 ++-
 .../swing/laf/flatlaf/FlatLightLaf.properties      |   7 +-
 .../org/netbeans/swing/laf/flatlaf/HiDPIUtils.java |  29 ++++--
 .../laf/flatlaf/ui/FlatEditorTabCellRenderer.java  | 102 ++++++++++++++------
 .../laf/flatlaf/ui/FlatEditorTabDisplayerUI.java   |   3 +-
 .../laf/flatlaf/ui/FlatViewTabDisplayerUI.java     | 103 ++++++++++++++-------
 .../org/netbeans/swing/laf/flatlaf/ui/Utils.java   |  10 ++
 10 files changed, 212 insertions(+), 77 deletions(-)

diff --git 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/DPISafeBorder.java
 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/DPISafeBorder.java
index 1cb8de6182..1b6c387451 100644
--- 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/DPISafeBorder.java
+++ 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/DPISafeBorder.java
@@ -57,7 +57,14 @@ final class DPISafeBorder implements Border {
 
     @Override
     public void paintBorder(Component c, Graphics g, int x, int y, int width, 
int height) {
-        HiDPIUtils.paintAtScale1x(g, x, y, width, height, 
this::paintBorderAtScale1x);
+        /* Detect borders which may need to visually connect with a prior 
adjacent component. This
+        applies in particular to view/editor tabs connecting to their tab 
containers underneath.
+        Round the starting position down towards the previous component to try 
to avoid a gap on
+        fractional HiDPI scalings (e.g. 150%). */
+        boolean roundXdown = insets.left == 0;
+        boolean roundYdown = insets.top == 0;
+        HiDPIUtils.paintAtScale1x(
+            g, x, y, width, height, roundXdown, roundYdown, 
this::paintBorderAtScale1x);
     }
 
     private void paintBorderAtScale1x(Graphics2D g, int deviceWidth, int 
deviceHeight, double scale) {
diff --git 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatDarkLaf.properties
 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatDarkLaf.properties
index 42d2af0991..374489a103 100644
--- 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatDarkLaf.properties
+++ 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatDarkLaf.properties
@@ -22,6 +22,10 @@ nb.preferred.color.profile=FlatLaf Dark
 nb.errorForeground=#DB5860
 nb.warningForeground=@foreground
 
+# Keep the tree background at @background rather than the very slightly lighter
+# @componentBackground, to make the Projects/Files/Services and Navigator 
backgrounds connect
+# seamlessly with the background of their tab on top.
+Tree.background = @background
 
 #---- EditorView ----
 
@@ -36,12 +40,14 @@ 
EditorTab.activeBackground=shade($TabbedPane.underlineColor,75%)
 EditorTab.activeForeground=darken(@foreground,10%)
 EditorTab.selectedBackground=@background
 EditorTab.selectedForeground=@foreground
-EditorTab.hoverBackground=lighten(@componentBackground,5%)
+# Add the hover effect only for unselected tabs.
+EditorTab.hoverBackground=lighten($EditorTab.activeBackground,7%)
+EditorTab.selectedHoverBackground=$EditorTab.selectedBackground
 EditorTab.hoverForeground=@foreground
 EditorTab.attentionBackground=#E6C840
 EditorTab.attentionForeground=#000
 EditorTab.underlineColor=$TabbedPane.underlineColor
-EditorTab.inactiveUnderlineColor=darken(@foreground,40%)
+EditorTab.inactiveUnderlineColor=#00000000
 EditorTab.tabSeparatorColor=darken($Component.borderColor,15%)
 
 #---- ViewTab ----
@@ -53,6 +59,7 @@ ViewTab.activeForeground=$EditorTab.activeForeground
 ViewTab.selectedBackground=$EditorTab.selectedBackground
 ViewTab.selectedForeground=$EditorTab.selectedForeground
 ViewTab.hoverBackground=$EditorTab.hoverBackground
+ViewTab.selectedHoverBackground=$EditorTab.selectedHoverBackground
 ViewTab.hoverForeground=$EditorTab.hoverForeground
 ViewTab.attentionBackground=$EditorTab.attentionBackground
 ViewTab.attentionForeground=$EditorTab.attentionForeground
diff --git 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLFCustoms.java
 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLFCustoms.java
index 0a4c510cff..5cf17babcb 100644
--- 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLFCustoms.java
+++ 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLFCustoms.java
@@ -82,8 +82,8 @@ public class FlatLFCustoms extends LFCustoms {
             VIEW_TAB_DISPLAYER_UI, 
"org.netbeans.swing.laf.flatlaf.ui.FlatViewTabDisplayerUI", // NOI18N
             SLIDING_BUTTON_UI, 
"org.netbeans.swing.laf.flatlaf.ui.FlatSlidingButtonUI", // NOI18N
 
-            EDITOR_TABSCOMPONENT_BORDER, DPISafeBorder.matte(0, 1, 1, 1, 
editorContentBorderColor),
-            EDITOR_TOOLBAR_BORDER, DPISafeBorder.matte(0, 0, 1, 0, 
editorContentBorderColor),
+            EDITOR_TABSCOMPONENT_BORDER, BorderFactory.createEmptyBorder(),
+            EDITOR_TOOLBAR_BORDER, new CompoundBorder(DPISafeBorder.matte(0, 
0, 1, 0, editorContentBorderColor), BorderFactory.createEmptyBorder(1, 0, 1, 
0)),
             EDITOR_TAB_CONTENT_BORDER, DPISafeBorder.matte(0, 1, 1, 1, 
editorContentBorderColor),
             VIEW_TAB_CONTENT_BORDER, DPISafeBorder.matte(0, 1, 1, 1, 
UIManager.getColor("TabbedContainer.view.contentBorderColor")), // NOI18N
 
diff --git 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLaf.properties
 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLaf.properties
index a358f9d36c..be48cc194d 100644
--- 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLaf.properties
+++ 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLaf.properties
@@ -28,7 +28,12 @@ controlShadow=$Component.borderColor
 # hide grip in divider
 SplitPaneDivider.style = plain
 
-TabbedPane.inactiveUnderlineColor = $EditorTab.inactiveUnderlineColor
+#---- TabbedPane ----
+
+# Decrease a bit from the default of 32. For comparison, a selected tab on the 
standard Swing
+# Windows LAF is 22 pixels tall.
+TabbedPane.tabHeight=30
+TabbedPane.inactiveUnderlineColor = $TabbedContainer.editor.contentBorderColor
 
 
 #---- TabbedContainer ----
@@ -39,11 +44,12 @@ 
TabbedContainer.view.contentBorderColor=$Component.borderColor
 
 #---- EditorTab ----
 
-EditorTab.tabInsets=5,6,7,8
+EditorTab.tabInsets=5,7,7,6
 EditorTab.underlineHeight=3
 EditorTab.underlineAtTop=true
 EditorTab.tabSeparatorColor=$Component.borderColor
 EditorTab.showTabSeparators=true
+EditorTab.showSelectedTabBorder=true
 
 #---- ViewTab ----
 
@@ -52,6 +58,7 @@ ViewTab.underlineHeight=3
 ViewTab.underlineAtTop=true
 ViewTab.tabSeparatorColor=$Component.borderColor
 ViewTab.showTabSeparators=true
+ViewTab.showSelectedTabBorder=true
 
 #---- Multi-tabs ----
 
diff --git 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLightLaf.properties
 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLightLaf.properties
index 51a7791da4..234ece4f51 100644
--- 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLightLaf.properties
+++ 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLightLaf.properties
@@ -30,12 +30,14 @@ EditorTab.activeBackground=mix($EditorTab.background, 
$TabbedPane.underlineColor
 EditorTab.activeForeground=lighten(@foreground,25%)
 EditorTab.selectedBackground=@background
 EditorTab.selectedForeground=@foreground
-EditorTab.hoverBackground=@componentBackground
+# Add the hover effect only for unselected tabs.
+EditorTab.hoverBackground=lighten($EditorTab.activeBackground,7%)
+EditorTab.selectedHoverBackground=$EditorTab.selectedBackground
 EditorTab.hoverForeground=@foreground
 EditorTab.attentionBackground=#E6C840
 EditorTab.attentionForeground=#000
 EditorTab.underlineColor=$TabbedPane.underlineColor
-EditorTab.inactiveUnderlineColor=lighten(@foreground,60%)
+EditorTab.inactiveUnderlineColor=#00000000
 
 
 #---- ViewTab ----
@@ -47,6 +49,7 @@ ViewTab.activeForeground=$EditorTab.activeForeground
 ViewTab.selectedBackground=$EditorTab.selectedBackground
 ViewTab.selectedForeground=$EditorTab.selectedForeground
 ViewTab.hoverBackground=$EditorTab.hoverBackground
+ViewTab.selectedHoverBackground=$EditorTab.selectedHoverBackground
 ViewTab.hoverForeground=$EditorTab.hoverForeground
 ViewTab.attentionBackground=$EditorTab.attentionBackground
 ViewTab.attentionForeground=$EditorTab.attentionForeground
diff --git 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/HiDPIUtils.java
 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/HiDPIUtils.java
index ba5bb0ed3f..16546a407f 100644
--- 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/HiDPIUtils.java
+++ 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/HiDPIUtils.java
@@ -38,6 +38,18 @@ public class HiDPIUtils {
      * {@link org.netbeans.swing.plaf.windows8.DPISafeBorder}.
      */
     public static void paintAtScale1x(Graphics g0, int x, int y, int width, 
int height, HiDPIPainter painter) {
+        paintAtScale1x(g0, x, y, width, height, false, false, painter);
+    }
+
+    /**
+     * @param roundXdown if true, round the starting X position down when 
converting to device
+     *        pixels, otherwise round to the nearest device pixel position
+     * @param roundYdown if true, round the starting Y position down when 
converting to device
+     *        pixels, otherwise round to the nearest device pixel position
+     */
+    static void paintAtScale1x(Graphics g0, int x, int y, int width, int 
height,
+        boolean roundXdown, boolean roundYdown, HiDPIPainter painter)
+    {
         Graphics2D g = (Graphics2D) g0;
         final AffineTransform oldTransform = g.getTransform();
         g.translate(x, y);
@@ -54,13 +66,16 @@ public class HiDPIUtils {
         {
               // HiDPI scaling is active.
               double scale = tx.getScaleX();
-              /* Round the starting (top-left) position up and the end 
(bottom-right) position down,
-              to ensure we are painting the border in an area that will not be 
painted over by an
-              adjacent component. */
-              int deviceX = (int) Math.ceil(tx.getTranslateX());
-              int deviceY = (int) Math.ceil(tx.getTranslateY());
-              int deviceXend = (int) (tx.getTranslateX() + width * scale);
-              int deviceYend = (int) (tx.getTranslateY() + height * scale);
+              int deviceX = (int) (roundXdown
+                  ? Math.floor(tx.getTranslateX())
+                  : Math.round(tx.getTranslateX()));
+              int deviceY = (int) (roundYdown
+                  ? Math.floor(tx.getTranslateY())
+                  : Math.round(tx.getTranslateY()));
+              /* Round the the end (bottom-right) position down, to ensure we 
are painting the
+              border in an area that will not be painted over by an adjacent 
component. */
+              int deviceXend = (int) Math.floor(tx.getTranslateX() + width * 
scale);
+              int deviceYend = (int) Math.floor(tx.getTranslateY() + height * 
scale);
               int deviceWidth = deviceXend - deviceX;
               int deviceHeight = deviceYend - deviceY;
               /* Deactivate the HiDPI scaling transform so we can do paint 
operations in the device
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 3676ab1d24..3caaf98c82 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
@@ -23,6 +23,7 @@ import java.awt.Color;
 import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.FontMetrics;
+import java.awt.GradientPaint;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.Insets;
@@ -41,19 +42,19 @@ import org.netbeans.swing.tabcontrol.plaf.TabPainter;
  * FlatLaf implementation of tab renderer
  */
 public class FlatEditorTabCellRenderer extends AbstractTabCellRenderer {
-
-    private static final int CLOSE_ICON_RIGHT_PAD = 2;
-
     private static final Color background = 
UIManager.getColor("EditorTab.background"); // NOI18N
     private static final Color activeBackground = 
Utils.getUIColor("EditorTab.activeBackground", background); // NOI18N
     private static final Color selectedBackground = 
Utils.getUIColor("EditorTab.selectedBackground", activeBackground); // NOI18N
+    private static final Color selectedBackgroundBottomGradient = 
Utils.getUIColor("EditorTab.selectedBackgroundBottomGradient", 
selectedBackground); // NOI18N
     private static final Color hoverBackground = 
UIManager.getColor("EditorTab.hoverBackground"); // NOI18N
+    private static final Color selectedHoverBackground = 
Utils.getUIColor("EditorTab.selectedHoverBackground", hoverBackground); // 
NOI18N
     private static final Color attentionBackground = 
UIManager.getColor("EditorTab.attentionBackground"); // NOI18N
 
     private static final Color foreground = Utils.getUIColor( 
"EditorTab.foreground", "TabbedPane.foreground" ); // NOI18N
     private static final Color activeForeground = Utils.getUIColor( 
"EditorTab.activeForeground", foreground ); // NOI18N
     private static final Color selectedForeground = Utils.getUIColor( 
"EditorTab.selectedForeground", activeForeground ); // NOI18N
     private static final Color hoverForeground = Utils.getUIColor( 
"EditorTab.hoverForeground", foreground ); // NOI18N
+    private static final Color selectedHoverForeground = Utils.getUIColor( 
"EditorTab.selectedHoverForeground", hoverForeground ); // NOI18N
     private static final Color attentionForeground = Utils.getUIColor( 
"EditorTab.attentionForeground", foreground ); // NOI18N
 
     private static final Color underlineColor = 
UIManager.getColor("EditorTab.underlineColor"); // NOI18N
@@ -66,14 +67,29 @@ public class FlatEditorTabCellRenderer extends 
AbstractTabCellRenderer {
     private static final boolean underlineAtTop = 
UIManager.getBoolean("EditorTab.underlineAtTop"); // NOI18N
     private static boolean showTabSeparators = 
UIManager.getBoolean("EditorTab.showTabSeparators"); // NOI18N
 
-    private static final FlatTabPainter painter = new FlatTabPainter();
+    /**
+     * Margin on the right of the close button. Note that {@code 
tabInsets.right} denotes the space
+     * between the caption text and the "close" icon. Here, we set the right 
margin to the same
+     * value as the left margin (before the tab's caption), minus 2 pixels to 
account for the fact
+     * that the close button has some margin of its own between the "X" and 
the red box that is
+     * shown only on hover.
+     */
+    private static final int CLOSE_ICON_RIGHT_PAD = tabInsets.left - 2;
+
+    private static final boolean showSelectedTabBorder = 
Utils.getUIBoolean("EditorTab.showSelectedTabBorder", underlineAtTop); // NOI18N
+    private static final boolean unscaledBorders = 
Utils.getUIBoolean("EditorTab.unscaledBorders", false); // NOI18N
+
+    private static final FlatTabPainter leftClipPainter = new 
FlatTabPainter(true, false);
+    private static final FlatTabPainter noClipPainter = new 
FlatTabPainter(false, false);
+    private static final FlatTabPainter rightClipPainter = new 
FlatTabPainter(false, true);
 
     boolean firstTab;
     boolean lastTab;
     boolean nextTabSelected;
 
     public FlatEditorTabCellRenderer() {
-        super(painter, new Dimension(tabInsets.left + tabInsets.right, 
tabInsets.top + tabInsets.bottom));
+        super(leftClipPainter, noClipPainter, rightClipPainter,
+                new Dimension(tabInsets.left + tabInsets.right, tabInsets.top 
+ tabInsets.bottom));
     }
 
     @Override
@@ -111,14 +127,16 @@ public class FlatEditorTabCellRenderer extends 
AbstractTabCellRenderer {
 
         // set text color
         setForeground(colorForState(foreground, activeForeground, 
selectedForeground,
-                hoverForeground, attentionForeground));
+                selectedHoverForeground, hoverForeground, 
attentionForeground));
 
         return result;
     }
 
-    private Color colorForState(Color normal, Color active, Color selected, 
Color hover, Color attention) {
+    private Color colorForState(Color normal, Color active, Color selected,
+            Color selectedHover, Color unselectedHover, Color attention)
+    {
         return isAttention() ? attention
-                : isArmed() ? hover
+                : isArmed() ? (isSelected() ? selectedHover : unselectedHover)
                 : isSelected() ? selected
                 : isActive() ? active
                 : normal;
@@ -132,6 +150,13 @@ public class FlatEditorTabCellRenderer extends 
AbstractTabCellRenderer {
     }
 
     private static class FlatTabPainter implements TabPainter {
+        final boolean leftClip;
+        final boolean rightClip;
+
+        public FlatTabPainter(boolean leftClip, boolean rightClip) {
+            this.leftClip = leftClip;
+            this.rightClip = rightClip;
+        }
 
         @Override
         public Insets getBorderInsets(Component c) {
@@ -153,7 +178,7 @@ 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 / 2 - iconHeight / 
2)) - 1;
+            rect.y = bounds.y + Math.max(0, (bounds.height - iconHeight) / 2) 
- 1;
             rect.width = iconWidth;
             rect.height = iconHeight;
         }
@@ -200,6 +225,19 @@ public class FlatEditorTabCellRenderer extends 
AbstractTabCellRenderer {
                 paintCloseButton(g, ren);
         }
 
+        private static void fillGradientRect(Graphics2D g, int x, int y, int 
width, int height,
+                Color color, Color bottomGradient, int gradientOffset)
+        {
+            if (bottomGradient.equals(color)) {
+                g.setColor(color);
+            } else {
+                g.setPaint(new GradientPaint(
+                        0, y          + gradientOffset, color,
+                        0, y + height - gradientOffset, bottomGradient, 
false));
+            }
+            g.fillRect(x, y, width, height);
+        }
+
         private void paintInteriorAtScale1x(Graphics2D g, Component c, int 
width, int height, double scale) {
             FlatEditorTabCellRenderer ren = (FlatEditorTabCellRenderer) c;
             boolean selected = ren.isSelected();
@@ -207,35 +245,43 @@ public class FlatEditorTabCellRenderer extends 
AbstractTabCellRenderer {
             // get background color
             Color bg = ren.colorForState(
                     background, activeBackground, selectedBackground,
-                    hoverBackground, attentionBackground);
+                    selectedHoverBackground, hoverBackground, 
attentionBackground);
 
             boolean showSeparator = showTabSeparators && !selected && 
!ren.nextTabSelected
-                    && !ren.lastTab;
+                    && !ren.lastTab && !rightClip;
 
-            // do not round tab separator width to get nice small lines at 
125%, 150% and 175%
-            int tabSeparatorWidth = showSeparator ? (int) (1 * scale) : 0;
+            int contentBorderWidth = unscaledBorders ? 1 : 
HiDPIUtils.deviceBorderWidth(scale, 1);
+            int tabSeparatorWidth = showSeparator ? contentBorderWidth : 0;
+            int underlineHeight = (int) 
Math.round(FlatEditorTabCellRenderer.underlineHeight * scale);
 
-            // paint background
-            g.setColor(bg);
-            g.fillRect(0, 0, width - (bg != background ? tabSeparatorWidth : 
0), height);
+            fillGradientRect(g, 0, 0, width - (bg != background ? 
tabSeparatorWidth : 0), height, bg,
+                    selected && 
!selectedBackground.equals(selectedBackgroundBottomGradient)
+                            ? selectedBackgroundBottomGradient : bg,
+                    (underlineAtTop ? underlineHeight : 0));
 
-            if (selected && underlineHeight > 0) {
-                // paint underline if tab is selected
-                int underlineHeight = (int) 
Math.round(FlatEditorTabCellRenderer.underlineHeight * scale);
-                if (underlineAtTop) {
+            if (selected) {
+                if (showSelectedTabBorder) {
                     g.setColor(contentBorderColor);
-                    int borderWidth = (int) (1 * scale);
-                    g.fillRect(0, 0, borderWidth, height);
-                    g.fillRect(width - tabSeparatorWidth - borderWidth, 0, 
borderWidth, height);
-                    g.setColor(ren.isActive() ? underlineColor : 
inactiveUnderlineColor);
-                    g.fillRect(0, 0, width - tabSeparatorWidth, 
underlineHeight);
-                } else {
+                    g.fillRect(0, 0, width - tabSeparatorWidth, 
contentBorderWidth); // Top
+                    if (!leftClip) {
+                        g.fillRect(0, 0, contentBorderWidth, height); // Left
+                    }
+                    if (!rightClip) {
+                        g.fillRect(width - tabSeparatorWidth - 
contentBorderWidth, 0, contentBorderWidth, height); // Right
+                    }
+                }
+
+                if (underlineHeight > 0) {
+                    // paint underline if tab is selected
                     g.setColor(ren.isActive() ? underlineColor : 
inactiveUnderlineColor);
-                    g.fillRect(0, height - underlineHeight, width - 
tabSeparatorWidth, underlineHeight);
+                    if (underlineAtTop) {
+                        g.fillRect(0, 0, width - tabSeparatorWidth, 
underlineHeight);
+                    } else {
+                        g.fillRect(0, height - underlineHeight, width - 
tabSeparatorWidth, underlineHeight);
+                    }
                 }
             } else {
                 // paint bottom border
-                int contentBorderWidth = HiDPIUtils.deviceBorderWidth(scale, 
1);
                 g.setColor(contentBorderColor);
                 g.fillRect(0, height - contentBorderWidth, width, 
contentBorderWidth);
             }
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 d4219aea5b..56475a806d 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
@@ -48,6 +48,7 @@ public class FlatEditorTabDisplayerUI extends 
BasicScrollingTabDisplayerUI {
     private final Color background = 
UIManager.getColor("EditorTab.background"); // NOI18N
     private final Color activeBackground = 
Utils.getUIColor("EditorTab.activeBackground", background); // NOI18N
     private final Color contentBorderColor = 
UIManager.getColor("TabbedContainer.editor.contentBorderColor"); // NOI18N
+    private final boolean unscaledBorders = 
Utils.getUIBoolean("EditorTab.unscaledBorders", false); // NOI18N
     private final Insets tabInsets = 
UIScale.scale(UIManager.getInsets("EditorTab.tabInsets")); // NOI18N
 
     public FlatEditorTabDisplayerUI(TabDisplayer displayer) {
@@ -118,7 +119,7 @@ public class FlatEditorTabDisplayerUI extends 
BasicScrollingTabDisplayerUI {
         g.fillRect (0, 0, width, height);
 
         // paint bottom border
-        int contentBorderWidth = HiDPIUtils.deviceBorderWidth(scale, 1);
+        int contentBorderWidth = unscaledBorders ? 1 : 
HiDPIUtils.deviceBorderWidth(scale, 1);
         g.setColor(contentBorderColor);
         g.fillRect(0, height - contentBorderWidth, width, contentBorderWidth);
     }
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 a24a5ad552..b7c656e0ab 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
@@ -62,12 +62,14 @@ public class FlatViewTabDisplayerUI extends 
AbstractViewTabDisplayerUI {
             activeBackground,       // background of tabs and tabs area if 
view group is active;  optional; defaults to foreground
             selectedBackground,     // background of tab if tab is selected in 
active view group; optional; defaults to activeBackground
             hoverBackground,        // background of tab if mouse is over tab
+            selectedHoverBackground, // if defined, use this color instead of 
hoverBackground for selected tabs
             attentionBackground,    // background of tab if tab is in 
attension mode
 
             foreground,             // text color if view group is inactive;   
            optional; defaults to TabbedPane.foreground
             activeForeground,       // text color if view group is active;     
            optional; defaults to foreground
             selectedForeground,     // text color if tab is selected in active 
view group; optional; defaults to activeForeground
             hoverForeground,        // text color if mouse is over tab;        
            optional; defaults to foreground
+            selectedHoverForeground,  // if defined, use this color instead of 
hoverForeground for selected tabs
             attentionForeground,    // text color if tab is in attension mode; 
            optional; defaults to foreground
 
             underlineColor,         // underline color of selected active tabs
@@ -80,6 +82,9 @@ public class FlatViewTabDisplayerUI extends 
AbstractViewTabDisplayerUI {
     private static boolean underlineAtTop;  // paint "underline" at top of tab
     private static boolean showTabSeparators; // paint tab separators
 
+    private static boolean showSelectedTabBorder; // Paint a border around the 
selected tab
+    private static boolean unscaledBorders; // Leave the thickness of borders 
unaffected by HiDPI scaling
+
     private Font font;
 
     public FlatViewTabDisplayerUI(TabDisplayer displayer) {
@@ -133,14 +138,15 @@ public class FlatViewTabDisplayerUI extends 
AbstractViewTabDisplayerUI {
             }
         }
 
-        // paint busy icon
+        final Icon busyIcon;
+        final int busyWidth;
         if (isTabBusy(index)) {
-            Icon busyIcon = 
BusyTabsSupport.getDefault().getBusyIcon(isSelected(index));
-            availTxtWidth -= busyIcon.getIconWidth() - UIScale.scale(3) - 
txtLeftPad;
-            busyIcon.paintIcon(displayer, g, x + txtLeftPad, y + (height - 
busyIcon.getIconHeight()) / 2);
-            int busyWidth = busyIcon.getIconWidth() + UIScale.scale(3);
-            x += busyWidth;
-            width -= busyWidth;
+            busyIcon = 
BusyTabsSupport.getDefault().getBusyIcon(isSelected(index));
+            busyWidth = busyIcon.getIconWidth() + UIScale.scale(3);
+            availTxtWidth -= busyWidth;
+        } else {
+            busyIcon = null;
+            busyWidth = 0;
         }
 
         // make sure that as much text as possible is shown (and avoid empty 
tabs)
@@ -150,7 +156,7 @@ public class FlatViewTabDisplayerUI extends 
AbstractViewTabDisplayerUI {
                 : fm.stringWidth(text);
         if (realTxtWidth > availTxtWidth) {
             // add left and right insets to available width
-            int left = Math.min(txtLeftPad - 1, realTxtWidth - availTxtWidth);
+            int left = Math.min(txtLeftPad - 2, realTxtWidth - availTxtWidth);
             availTxtWidth += left + txtRightPad;
             txtLeftPad -= left;
 
@@ -170,9 +176,15 @@ public class FlatViewTabDisplayerUI extends 
AbstractViewTabDisplayerUI {
             }
         }
 
+        if (busyIcon != null) {
+            busyIcon.paintIcon(displayer, g, x + txtLeftPad, y + (height - 
busyIcon.getIconHeight()) / 2);
+            x += busyWidth;
+            width -= busyWidth;
+        }
+
         // text color
         Color c = colorForState(index, foreground, activeForeground, 
selectedForeground,
-                hoverForeground, attentionForeground);
+                selectedHoverForeground, hoverForeground, attentionForeground);
 
         // paint text
         int txtX = x + txtLeftPad;
@@ -192,7 +204,8 @@ public class FlatViewTabDisplayerUI extends 
AbstractViewTabDisplayerUI {
 
     @Override
     protected void paintTabBorder(Graphics g, int index, int x, int y, int 
width, int height) {
-        // not using borders
+        /* In the showSelectedTabBorder case, we draw borders as part of 
paintTabBackground, for
+        consistency with FlatEditorTabCellRenderer. */
     }
 
     @Override
@@ -206,41 +219,60 @@ public class FlatViewTabDisplayerUI extends 
AbstractViewTabDisplayerUI {
                 });
     }
 
+    private boolean shouldShowSeparator(int index) {
+        if (!showTabSeparators) {
+            return false;
+        }
+        /* Don't show separators around the selected tab, if there's already a 
border or color
+        contrast at its sides. */
+        boolean selectedTabHasContrastWithAdjacentTabs =
+                showSelectedTabBorder || 
!selectedBackground.equals(activeBackground);
+        boolean separatorIsAdjacentToSelectedTab = isSelected(index) || 
isSelected(index + 1);
+        if (selectedTabHasContrastWithAdjacentTabs && 
separatorIsAdjacentToSelectedTab) {
+            return false;
+        }
+        // Show separators _between_ tabs (not after the last one), like for 
editor tabs.
+        return index >= 0 && index < getDataModel().size() - 1;
+    }
+
     private void paintTabBackgroundAtScale1x(Graphics2D g, int index, int 
width, int height, double scale) {
-        // do not round tab separator width to get nice small lines at 125%, 
150% and 175%
-        int tabSeparatorWidth = (showTabSeparators && index >= 0) ? (int) (1 * 
scale) : 0;
+        Color bg = colorForState(index, background, activeBackground, 
selectedBackground,
+                selectedHoverBackground, hoverBackground, attentionBackground);
+
+        boolean showSeparator = shouldShowSeparator(index);
+
+        int contentBorderWidth = unscaledBorders ? 1 : 
HiDPIUtils.deviceBorderWidth(scale, 1);
+        int tabSeparatorWidth = showSeparator ? contentBorderWidth : 0;
 
         // paint background
-        Color bg = colorForState(index, background, activeBackground, 
selectedBackground,
-                hoverBackground, attentionBackground);
         g.setColor(bg);
         g.fillRect(0, 0, width - (bg != background ? tabSeparatorWidth : 0), 
height);
 
-        if (isSelected(index) && underlineHeight > 0) {
-            // paint underline if tab is selected
-            int underlineHeight = (int) Math.round(this.underlineHeight * 
scale);
-            g.setColor(isActive() ? underlineColor : inactiveUnderlineColor);
-            if (underlineAtTop) {
+        if (isSelected(index)) {
+            if (showSelectedTabBorder) {
                 g.setColor(contentBorderColor);
-                int borderWidth = (int) (1 * scale);
-                g.fillRect(0, 0, borderWidth, height);
-                g.fillRect(width - tabSeparatorWidth - borderWidth, 0, 
borderWidth, height);
-                g.setColor(isActive() ? underlineColor : 
inactiveUnderlineColor);
-                g.fillRect(0, 0, width - tabSeparatorWidth, underlineHeight);
-            } else {
+                g.fillRect(0, 0, width - tabSeparatorWidth, 
contentBorderWidth); // Top
+                g.fillRect(0, 0, contentBorderWidth, height); // Left
+                g.fillRect(width - tabSeparatorWidth - contentBorderWidth, 0, 
contentBorderWidth, height); // Right
+            }
+
+            if (underlineHeight > 0) {
+                // paint underline if tab is selected
+                int underlineHeight = (int) Math.round(this.underlineHeight * 
scale);
                 g.setColor(isActive() ? underlineColor : 
inactiveUnderlineColor);
-                g.fillRect(0, height - underlineHeight, width - 
tabSeparatorWidth, underlineHeight);
+                if (underlineAtTop) {
+                    g.fillRect(0, 0, width - tabSeparatorWidth, 
underlineHeight);
+                } else {
+                    g.fillRect(0, height - underlineHeight, width - 
tabSeparatorWidth, underlineHeight);
+                }
             }
         } else {
             // paint bottom border
-            int contentBorderWidth = HiDPIUtils.deviceBorderWidth(scale, 1);
             g.setColor(contentBorderColor);
             g.fillRect(0, height - contentBorderWidth, width, 
contentBorderWidth);
         }
 
-        if (showTabSeparators && index >= 0 &&
-                ((!isSelected(index) && index < getDataModel().size() - 1 && 
!isSelected(index + 1))
-                || selectedBackground.equals(activeBackground))) {
+        if (showSeparator) {
             int offset = (int) (4 * scale);
             g.setColor(tabSeparatorColor);
             g.fillRect(width - tabSeparatorWidth, offset, tabSeparatorWidth, 
height - (offset * 2) - 1);
@@ -256,9 +288,11 @@ public class FlatViewTabDisplayerUI extends 
AbstractViewTabDisplayerUI {
         super.paintDisplayerBackground(g, c);
     }
 
-    private Color colorForState(int index, Color normal, Color active, Color 
selected, Color hover, Color attention) {
+    private Color colorForState(int index, Color normal, Color active, Color 
selected,
+            Color selectedHover, Color unselectedHover, Color attention)
+    {
         return isAttention(index) ? attention
-                : isMouseOver(index) ? hover
+                : isMouseOver(index) ? (isSelected(index) ? selectedHover : 
unselectedHover)
                 : isSelected(index) ? selected
                 : isActive() ? active
                 : normal;
@@ -294,12 +328,14 @@ public class FlatViewTabDisplayerUI extends 
AbstractViewTabDisplayerUI {
             activeBackground = Utils.getUIColor("ViewTab.activeBackground", 
background); // NOI18N
             selectedBackground = 
Utils.getUIColor("ViewTab.selectedBackground", activeBackground); // NOI18N
             hoverBackground = UIManager.getColor("ViewTab.hoverBackground"); 
// NOI18N
+            selectedHoverBackground = 
Utils.getUIColor("ViewTab.selectedHoverBackground", hoverBackground); // NOI18N
             attentionBackground = 
UIManager.getColor("ViewTab.attentionBackground"); // NOI18N
 
             foreground = Utils.getUIColor("ViewTab.foreground", 
"TabbedPane.foreground"); // NOI18N
             activeForeground = Utils.getUIColor("ViewTab.activeForeground", 
foreground); // NOI18N
             selectedForeground = 
Utils.getUIColor("ViewTab.selectedForeground", activeForeground); // NOI18N
             hoverForeground = Utils.getUIColor("ViewTab.hoverForeground", 
foreground); // NOI18N
+            selectedHoverForeground = 
Utils.getUIColor("ViewTab.selectedHoverForeground", hoverForeground); // NOI18N
             attentionForeground = 
Utils.getUIColor("ViewTab.attentionForeground", foreground); // NOI18N
 
             underlineColor = UIManager.getColor("ViewTab.underlineColor"); // 
NOI18N
@@ -316,6 +352,9 @@ public class FlatViewTabDisplayerUI extends 
AbstractViewTabDisplayerUI {
             tabInsets = UIScale.scale(tabInsets);
             underlineHeight = UIScale.scale(underlineHeight);
 
+            showSelectedTabBorder = 
Utils.getUIBoolean("ViewTab.showSelectedTabBorder", underlineAtTop); // NOI18N
+            unscaledBorders = Utils.getUIBoolean("ViewTab.unscaledBorders", 
false); // NOI18N
+
             colorsReady = true;
         }
     }
diff --git 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/Utils.java
 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/Utils.java
index c471a8bbcc..3aab25934b 100644
--- 
a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/Utils.java
+++ 
b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/ui/Utils.java
@@ -37,6 +37,16 @@ class Utils {
         return (color != null) ? color : UIManager.getColor(defaultKey);
     }
 
+    static int getUIInt(String key, int defaultValue) {
+        Object value = UIManager.get(key);
+        return (value instanceof Integer) ? ((Integer) value) : defaultValue;
+    }
+
+    static boolean getUIBoolean(String key, boolean defaultValue) {
+        Object value = UIManager.get(key);
+        return (value instanceof Boolean) ? ((Boolean) value) : defaultValue;
+    }
+
     /**
      * Sets rendering hints used for painting.
      */


---------------------------------------------------------------------
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