You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by lk...@apache.org on 2020/01/19 00:44:18 UTC

[netbeans] branch master updated: [NETBEANS-3428] FlatLaf: improving editor and view tabs in main window

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

lkishalmi 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 ab0fedf  [NETBEANS-3428] FlatLaf: improving editor and view tabs in main window
ab0fedf is described below

commit ab0fedf0a11e6b428e062d6538c209b3b2740f17
Author: Karl Tauber <ka...@jformdesigner.com>
AuthorDate: Sat Jan 18 17:40:20 2020 +0100

    [NETBEANS-3428] FlatLaf: improving editor and view tabs in main window
    
    Editor and View tabs:
    - tab separators
    - tab text moved up 1px
    - smaller tab insets
    - minimize icon improved
    
    View tabs only:
    - minimized space between tab text and close button
    - center tab text if close button is hidden
    - make sure that as much tab text as possible is shown by dynamically reducing left/right margins
    - avoid empty tabs (truncate text if necessary)
---
 .../netbeans/swing/laf/flatlaf/FlatLaf.properties  |  8 ++-
 .../laf/flatlaf/ui/FlatEditorTabCellRenderer.java  | 29 +++++++--
 .../laf/flatlaf/ui/FlatEditorTabDisplayerUI.java   | 10 +++
 .../swing/laf/flatlaf/ui/FlatTabControlIcon.java   | 12 ++--
 .../laf/flatlaf/ui/FlatViewTabDisplayerUI.java     | 74 ++++++++++++++++++----
 5 files changed, 106 insertions(+), 27 deletions(-)

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 88ceb0b..affe2e0 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
@@ -34,16 +34,20 @@ TabbedContainer.view.contentBorderColor=$Component.borderColor
 
 #---- EditorTab ----
 
-EditorTab.tabInsets=6,8,6,8
+EditorTab.tabInsets=5,6,7,6
 EditorTab.underlineHeight=3
 #EditorTab.underlineAtTop=true
+EditorTab.tabSeparatorColor=$Component.borderColor
+EditorTab.showTabSeparators=true
 
 
 #---- ViewTab ----
 
-ViewTab.tabInsets=6,8,6,8
+ViewTab.tabInsets=5,6,7,0
 ViewTab.underlineHeight=3
 #ViewTab.underlineAtTop=true
+ViewTab.tabSeparatorColor=$Component.borderColor
+ViewTab.showTabSeparators=true
 
 
 #---- Multi-tabs ----
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 a4048aa..db7b332 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
@@ -58,14 +58,18 @@ public class FlatEditorTabCellRenderer extends AbstractTabCellRenderer {
 
     private static final Color underlineColor = UIManager.getColor("EditorTab.underlineColor"); // NOI18N
     private static final Color inactiveUnderlineColor = UIManager.getColor("EditorTab.inactiveUnderlineColor"); // NOI18N
+    private static final Color tabSeparatorColor = UIManager.getColor("EditorTab.tabSeparatorColor"); // NOI18N
     private static final Color contentBorderColor = UIManager.getColor("TabbedContainer.editor.contentBorderColor"); // NOI18N
 
     private static final Insets tabInsets = UIScale.scale(UIManager.getInsets("EditorTab.tabInsets")); // NOI18N
     private static final int underlineHeight = UIScale.scale(UIManager.getInt("EditorTab.underlineHeight")); // NOI18N
     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();
 
+    boolean nextTabSelected;
+
     public FlatEditorTabCellRenderer() {
         super(painter, new Dimension(tabInsets.left + tabInsets.right, tabInsets.top + tabInsets.bottom));
     }
@@ -198,25 +202,40 @@ public class FlatEditorTabCellRenderer extends AbstractTabCellRenderer {
             FlatEditorTabCellRenderer ren = (FlatEditorTabCellRenderer) c;
             boolean selected = ren.isSelected();
 
+            // get background color
+            Color bg = ren.colorForState(
+                    background, activeBackground, selectedBackground,
+                    hoverBackground, attentionBackground);
+
+            boolean showSeparator = showTabSeparators && !selected && !ren.nextTabSelected;
+
+            // do not round tab separator width to get nice small lines at 125%, 150% and 175%
+            int tabSeparatorWidth = showSeparator ? (int) (1 * scale) : 0;
+
             // paint background
-            g.setColor(ren.colorForState(background, activeBackground, selectedBackground,
-                    hoverBackground, attentionBackground));
-            g.fillRect(0, 0, width, height);
+            g.setColor(bg);
+            g.fillRect(0, 0, width - (bg != background ? tabSeparatorWidth : 0), height);
 
             if (selected && underlineHeight > 0) {
                 // paint underline if tab is selected
                 int underlineHeight = (int) Math.round(FlatEditorTabCellRenderer.underlineHeight * scale);
                 g.setColor(ren.isActive() ? underlineColor : inactiveUnderlineColor);
                 if (underlineAtTop)
-                    g.fillRect(0, 0, width, underlineHeight);
+                    g.fillRect(0, 0, width - tabSeparatorWidth, underlineHeight);
                 else
-                    g.fillRect(0, height - underlineHeight, width, underlineHeight);
+                    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 (showSeparator) {
+                int offset = (int) (4 * scale);
+                g.setColor(tabSeparatorColor);
+                g.fillRect(width - tabSeparatorWidth, offset, tabSeparatorWidth, height - (offset * 2) - 1);
+            }
         }
 
         private void paintCloseButton(Graphics g, FlatEditorTabCellRenderer ren) {
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 1818953..c20c68b 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
@@ -36,6 +36,7 @@ import org.netbeans.swing.laf.flatlaf.HiDPIUtils;
 import org.netbeans.swing.tabcontrol.TabDisplayer;
 import org.netbeans.swing.tabcontrol.plaf.BasicScrollingTabDisplayerUI;
 import org.netbeans.swing.tabcontrol.plaf.TabCellRenderer;
+import org.netbeans.swing.tabcontrol.plaf.TabState;
 
 /**
  * Tab displayer UI for FlatLaf look and feel
@@ -77,6 +78,15 @@ public class FlatEditorTabDisplayerUI extends BasicScrollingTabDisplayerUI {
     }
 
     @Override
+    public TabCellRenderer getTabCellRenderer(int tab) {
+        TabCellRenderer ren = super.getTabCellRenderer(tab);
+        if (ren instanceof FlatEditorTabCellRenderer && tab + 1 < displayer.getModel().size()) {
+            ((FlatEditorTabCellRenderer)ren).nextTabSelected = (tabState.getState(tab + 1) & TabState.SELECTED) != 0;
+        }
+        return ren;
+    }
+
+    @Override
     public Insets getTabAreaInsets() {
         return new Insets(0, 0, 0, getControlButtons().getPreferredSize().width + ICON_X_PAD);
     }
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 9b81fda..c506498 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
@@ -220,17 +220,17 @@ public final class FlatTabControlIcon extends VectorIcon {
             g.fill(win1);
             g.fill(win2);
         } else if (buttonId == TabControlButton.ID_MAXIMIZE_BUTTON) {
-            int xy = round(3 * scaling);
-            int wh = round(10 * scaling);
+            int xy = (int) (3 * scaling);
+            int wh = width - (2 * xy);
             /* Draw one larger window. The getWindowSymbol method ensures we are using the same
             window border thickness as for ID_RESTORE_BUTTON. */
             g.fill(getWindowSymbol(scaling, xy, xy, wh, wh));
         } else if (buttonId == TabControlButton.ID_SLIDE_GROUP_BUTTON) {
             // Draw a simple bar towards the bottom of the icon.
-            int barX = round(3 * scaling);
-            int barY = round(7 * scaling);
-            int barWidth = round(10 * scaling);
-            int barThickness = round(2 * scaling);
+            int barX = (int) (3 * scaling);
+            int barY = round(11 * scaling);
+            int barWidth = width - (2 * barX);
+            int barThickness = (int) (1 * scaling);
             g.fill(new Rectangle2D.Double(barX, barY, barWidth, barThickness));
         } else if (buttonId == TabControlButton.ID_DROP_DOWN_BUTTON ||
                    buttonId == TabControlButton.ID_SCROLL_LEFT_BUTTON ||
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 89a86e0..d8d972d 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
@@ -72,11 +72,13 @@ public class FlatViewTabDisplayerUI extends AbstractViewTabDisplayerUI {
 
             underlineColor,         // underline color of selected active tabs
             inactiveUnderlineColor, // underline color of selected inactive tabs
+            tabSeparatorColor,      // tab separator color
             contentBorderColor;     // bottom border color
 
     private static Insets tabInsets;
     private static int underlineHeight;     // height of "underline" painted at bottom of tab to indicate selection
     private static boolean underlineAtTop;  // paint "underline" at top of tab
+    private static boolean showTabSeparators; // paint tab separators
 
     private Font font;
 
@@ -115,7 +117,7 @@ public class FlatViewTabDisplayerUI extends AbstractViewTabDisplayerUI {
         FontMetrics fm = getTxtFontMetrics();
         // setting font already here to compute string width correctly
         g.setFont(getTxtFont());
-        int txtWidth = width;
+        int availTxtWidth = width - (txtLeftPad + txtRightPad);
         if (isSelected(index)) {
             // layout buttons
             Component buttons = getControlButtons();
@@ -125,20 +127,47 @@ public class FlatViewTabDisplayerUI extends AbstractViewTabDisplayerUI {
                     buttons.setVisible(false);
                 } else {
                     buttons.setVisible(true);
-                    txtWidth = width - (buttonsSize.width + ICON_X_PAD + txtLeftPad + txtRightPad);
+                    availTxtWidth -= (buttonsSize.width + ICON_X_PAD);
                     buttons.setLocation(x + width - buttonsSize.width - ICON_X_PAD, y + (height - buttonsSize.height) / 2);
                 }
             }
-        } else {
-            txtWidth = width - (txtLeftPad + txtRightPad);
         }
 
         // paint busy icon
         if (isTabBusy(index)) {
             Icon busyIcon = BusyTabsSupport.getDefault().getBusyIcon(isSelected(index));
-            txtWidth -= busyIcon.getIconWidth() - UIScale.scale(3) - txtLeftPad;
+            availTxtWidth -= busyIcon.getIconWidth() - UIScale.scale(3) - txtLeftPad;
             busyIcon.paintIcon(displayer, g, x + txtLeftPad, y + (height - busyIcon.getIconHeight()) / 2);
-            x += busyIcon.getIconWidth() + UIScale.scale(3);
+            int busyWidth = busyIcon.getIconWidth() + UIScale.scale(3);
+            x += busyWidth;
+            width -= busyWidth;
+        }
+
+        // make sure that as much text as possible is shown (and avoid empty tabs)
+        int realTxtWidth = (text.startsWith("<html") || text.startsWith("<HTML")) //NOI18N
+                ? (int) HtmlRenderer.renderString(text, g, 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE,
+                            getTxtFont(), foreground, HtmlRenderer.STYLE_TRUNCATE, false)
+                : fm.stringWidth(text);
+        if (realTxtWidth > availTxtWidth) {
+            // add left and right insets to available width
+            int left = Math.min(txtLeftPad - 1, realTxtWidth - availTxtWidth);
+            availTxtWidth += left + txtRightPad;
+            txtLeftPad -= left;
+
+            // Truncate text here because HtmlRenderer.renderString() does not paint any text
+            // if it is longer that 3 characters and the available width is smaller
+            // than the width of the first 3 characters plus "…".
+            if (realTxtWidth > availTxtWidth && text.length() > 3) {
+                int minWidth = fm.stringWidth(text.substring(0, 3) + "…"); //NOI18N
+                if (minWidth > availTxtWidth) {
+                    // truncate text; in the worst case, text becomes "…" only
+                    for (int i = 2; i >= 0; i--) {
+                        text = text.substring(0, i) + "…"; //NOI18N
+                        if (fm.stringWidth(text) < availTxtWidth)
+                            break;
+                    }
+                }
+            }
         }
 
         // text color
@@ -146,14 +175,19 @@ public class FlatViewTabDisplayerUI extends AbstractViewTabDisplayerUI {
                 hoverForeground, attentionForeground);
 
         // paint text
-        int txtX = x + tabInsets.left;
+        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;
         }
-        HtmlRenderer.renderString(text, g, txtX, txtY, txtWidth, height,
-                getTxtFont(), c, HtmlRenderer.STYLE_TRUNCATE, true);
+        int style = HtmlRenderer.STYLE_TRUNCATE;
+        if (!isSelected(index)) {
+            // center text of unselected tabs
+            txtX = Math.max(x + 1, x + ((width - realTxtWidth) / 2));
+        }
+        HtmlRenderer.renderString(text, g, txtX, txtY, availTxtWidth, height,
+                getTxtFont(), c, style, true);
     }
 
     @Override
@@ -173,19 +207,23 @@ public class FlatViewTabDisplayerUI extends AbstractViewTabDisplayerUI {
     }
 
     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;
+
         // paint background
-        g.setColor(colorForState(index, background, activeBackground, selectedBackground,
-                hoverBackground, attentionBackground));
-        g.fillRect(0, 0, width, height);
+        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) {
-                g.fillRect(0, 0, width, underlineHeight);
+                g.fillRect(0, 0, width - tabSeparatorWidth, underlineHeight);
             } else {
-                g.fillRect(0, height - underlineHeight, width, underlineHeight);
+                g.fillRect(0, height - underlineHeight, width - tabSeparatorWidth, underlineHeight);
             }
         } else {
             // paint bottom border
@@ -193,6 +231,12 @@ public class FlatViewTabDisplayerUI extends AbstractViewTabDisplayerUI {
             g.setColor(contentBorderColor);
             g.fillRect(0, height - contentBorderWidth, width, contentBorderWidth);
         }
+
+        if (showTabSeparators && index >= 0) {
+            int offset = (int) (4 * scale);
+            g.setColor(tabSeparatorColor);
+            g.fillRect(width - tabSeparatorWidth, offset, tabSeparatorWidth, height - (offset * 2) - 1);
+        }
     }
 
     @Override
@@ -251,11 +295,13 @@ public class FlatViewTabDisplayerUI extends AbstractViewTabDisplayerUI {
 
             underlineColor = UIManager.getColor("ViewTab.underlineColor"); // NOI18N
             inactiveUnderlineColor = UIManager.getColor("ViewTab.inactiveUnderlineColor"); // NOI18N
+            tabSeparatorColor = UIManager.getColor("ViewTab.tabSeparatorColor"); // NOI18N
             contentBorderColor = UIManager.getColor("TabbedContainer.view.contentBorderColor"); // NOI18N
 
             tabInsets = UIManager.getInsets("ViewTab.tabInsets"); // NOI18N
             underlineHeight = UIManager.getInt("ViewTab.underlineHeight"); // NOI18N
             underlineAtTop = UIManager.getBoolean("ViewTab.underlineAtTop"); // NOI18N
+            showTabSeparators = UIManager.getBoolean("ViewTab.showTabSeparators"); // NOI18N
 
             // scale on Java 8 and Linux
             tabInsets = UIScale.scale(tabInsets);


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

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