From b077e3f741c3c48e23bdaf7565a2e7fd1e9107ec Mon Sep 17 00:00:00 2001 From: weisj <31143295+weisJ@users.noreply.github.com> Date: Wed, 3 Mar 2021 17:45:00 +0100 Subject: [PATCH] CellHintPopup: Ensure component are children of their respective cell containers/renderer panes, when calculating their preferred/minimum size or during painting. This ensures they have the correct GraphicsConfiguration set, which is used e.g. when retrieving the FontRendererContext/FontMetrics. --- .../weisj/darklaf/ui/HasRendererPane.java | 29 ++++++++++ .../ui/cell/DarkBooleanCellRenderer.java | 7 ++- .../hint/AbstractIndexedCellContainer.java | 57 +++++++++++++++++++ .../darklaf/ui/cell/hint/CellContainer.java | 2 + .../ui/cell/hint/CellHintPopupListener.java | 14 ++++- .../ui/cell/hint/IndexedCellContainer.java | 2 + .../weisj/darklaf/ui/table/DarkTableUI.java | 8 ++- .../darklaf/ui/table/TableCellContainer.java | 7 +-- .../weisj/darklaf/ui/tree/DarkTreeUI.java | 8 ++- .../darklaf/ui/tree/TreeCellContainer.java | 7 +-- .../ui/tree/TreeRendererComponent.java | 43 ++++++++------ 11 files changed, 153 insertions(+), 31 deletions(-) create mode 100644 core/src/main/java/com/github/weisj/darklaf/ui/HasRendererPane.java create mode 100644 core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/AbstractIndexedCellContainer.java diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/HasRendererPane.java b/core/src/main/java/com/github/weisj/darklaf/ui/HasRendererPane.java new file mode 100644 index 000000000..6675ee253 --- /dev/null +++ b/core/src/main/java/com/github/weisj/darklaf/ui/HasRendererPane.java @@ -0,0 +1,29 @@ +/* + * MIT License + * + * Copyright (c) 2021 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package com.github.weisj.darklaf.ui; + +import java.awt.Container; + +public interface HasRendererPane { + + Container getRendererPane(); +} diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/cell/DarkBooleanCellRenderer.java b/core/src/main/java/com/github/weisj/darklaf/ui/cell/DarkBooleanCellRenderer.java index f61f5acda..9eb0653ef 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/cell/DarkBooleanCellRenderer.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/cell/DarkBooleanCellRenderer.java @@ -44,15 +44,16 @@ public DarkBooleanCellRenderer(final boolean opaque) { @Override public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) { - return getBooleanRenderer(table).getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + return getBooleanRenderer(table) + .getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); } @Override public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean selected, final boolean expanded, final boolean leaf, final int row, final boolean hasFocus) { - return getBooleanRenderer(tree).getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, - hasFocus); + return getBooleanRenderer(tree) + .getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); } protected ComponentBasedTableCellRenderer getBooleanRenderer(final JTable table) { diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/AbstractIndexedCellContainer.java b/core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/AbstractIndexedCellContainer.java new file mode 100644 index 000000000..2bd524d62 --- /dev/null +++ b/core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/AbstractIndexedCellContainer.java @@ -0,0 +1,57 @@ +/* + * MIT License + * + * Copyright (c) 2021 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package com.github.weisj.darklaf.ui.cell.hint; + +import java.awt.Component; +import java.awt.Dimension; + +import javax.swing.JComponent; + +import com.github.weisj.darklaf.ui.HasRendererPane; + +public abstract class AbstractIndexedCellContainer + implements IndexedCellContainer { + + protected final UI ui; + + protected AbstractIndexedCellContainer(final UI ui) { + this.ui = ui; + } + + @Override + public void addRenderer(final Component renderer) { + ui.getRendererPane().add(renderer); + } + + @Override + public Dimension getRequiredCellSize(final I lastIndex, final Component comp) { + // Components without a parent may report incorrect preferred/minimum size as the + // associated FontMetrics have a missing default transform. This is bad for + // determining the actual needed size on screen. + // We add the component to the renderer pane to circumvent the issue. + boolean hasParent = comp.getParent() != null; + if (!hasParent) { + addRenderer(comp); + } + return comp.getMinimumSize(); + } +} diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/CellContainer.java b/core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/CellContainer.java index 6f1d71a7d..8cb626b09 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/CellContainer.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/CellContainer.java @@ -34,4 +34,6 @@ public interface CellContainer { default Rectangle getAllocation() { return getComponent().getVisibleRect(); } + + void addRenderer(final Component renderer); } diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/CellHintPopupListener.java b/core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/CellHintPopupListener.java index 97af19c66..53553a5f4 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/CellHintPopupListener.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/CellHintPopupListener.java @@ -136,7 +136,12 @@ private boolean fitsInside(final Dimension size, final Rectangle bounds) { } private Dimension getPreferredSize(final boolean isEditing, final Component comp) { - final Dimension prefSize = isEditing ? comp.getBounds().getSize() : comp.getMinimumSize(); + Dimension prefSize; + if (isEditing) { + prefSize = comp.getBounds().getSize(); + } else { + prefSize = cellContainer.getRequiredCellSize(lastIndex, comp); + } if (comp instanceof JComponent) { // Avoid showing the popup if only the border is obscured. Border border = ((JComponent) comp).getBorder(); @@ -245,7 +250,7 @@ public void repaint() { } private void enter(final I index, final Rectangle bounds, final Rectangle rendererBounds) { - LOGGER.log(popupComponent.isShowing() ? Level.FINER : Level.FINE, "Showing cell popup at index " + index); + LOGGER.log(popupComponent.isShowing() ? Level.FINEST : Level.FINE, "Showing cell popup at index " + index); if (index != null) { lastIndex = index; popupComponent.setPreferredSize(bounds.getSize()); @@ -414,6 +419,11 @@ public void paint(final Graphics g) { if (renderer instanceof JComponent) { ((JComponent) renderer).setDoubleBuffered(false); } + if (!cellHintPopupListener.isCurrentCellEditing()) { + // Ensure correct parent. Without the component may use an incorrect + // font rendering context. + cellHintPopupListener.cellContainer.addRenderer(renderer); + } renderer.setBounds(0, 0, rendererBounds.width, rendererBounds.height); renderer.doLayout(); renderer.paint(g); diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/IndexedCellContainer.java b/core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/IndexedCellContainer.java index 35cb1b120..e5ddb8fee 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/IndexedCellContainer.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/IndexedCellContainer.java @@ -52,4 +52,6 @@ default Component getEffectiveCellRendererComponent(final I position, final bool Component getCellRendererComponent(final I position); Component getCellEditorComponent(final I position); + + Dimension getRequiredCellSize(final I lastIndex, final Component comp); } diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/table/DarkTableUI.java b/core/src/main/java/com/github/weisj/darklaf/ui/table/DarkTableUI.java index 4e2bdaa9f..f05450d25 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/table/DarkTableUI.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/table/DarkTableUI.java @@ -36,6 +36,7 @@ import com.github.weisj.darklaf.components.OverlayScrollPane; import com.github.weisj.darklaf.graphics.PaintUtil; +import com.github.weisj.darklaf.ui.HasRendererPane; import com.github.weisj.darklaf.ui.cell.CellUtil; import com.github.weisj.darklaf.ui.cell.DarkBooleanCellRenderer; import com.github.weisj.darklaf.ui.cell.DarkCellRendererPane; @@ -48,7 +49,7 @@ import com.github.weisj.darklaf.util.PropertyUtil; /** @author Jannis Weis */ -public class DarkTableUI extends DarkTableUIBridge implements TableConstants { +public class DarkTableUI extends DarkTableUIBridge implements TableConstants, HasRendererPane { private static final int ROW_HEIGHT_FALLBACK = 22; protected Color selectionBackgroundNoFocus; @@ -451,6 +452,11 @@ public static int adjustDistance(final int distance, final Rectangle rect, final return dist; } + @Override + public Container getRendererPane() { + return rendererPane; + } + protected class DarkHandler extends Handler { protected int lastIndex = -1; diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/table/TableCellContainer.java b/core/src/main/java/com/github/weisj/darklaf/ui/table/TableCellContainer.java index 355b94ec7..da722e4fa 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/table/TableCellContainer.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/table/TableCellContainer.java @@ -25,17 +25,16 @@ import javax.swing.*; -import com.github.weisj.darklaf.ui.cell.hint.IndexedCellContainer; +import com.github.weisj.darklaf.ui.cell.hint.AbstractIndexedCellContainer; import com.github.weisj.darklaf.util.Pair; -public class TableCellContainer implements IndexedCellContainer> { +public class TableCellContainer extends AbstractIndexedCellContainer, DarkTableUI> { private final JTable table; - private final DarkTableUI ui; public TableCellContainer(final JTable table, final DarkTableUI ui) { + super(ui); this.table = table; - this.ui = ui; } @Override diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/tree/DarkTreeUI.java b/core/src/main/java/com/github/weisj/darklaf/ui/tree/DarkTreeUI.java index 98017c082..83785b701 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/tree/DarkTreeUI.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/tree/DarkTreeUI.java @@ -39,6 +39,7 @@ import com.github.weisj.darklaf.graphics.PaintUtil; import com.github.weisj.darklaf.icons.RotatableIcon; +import com.github.weisj.darklaf.ui.HasRendererPane; import com.github.weisj.darklaf.ui.cell.CellConstants; import com.github.weisj.darklaf.ui.cell.CellUtil; import com.github.weisj.darklaf.ui.cell.DarkCellRendererPane; @@ -51,7 +52,7 @@ * @author Konstantin Bulenkov * @author Jannis Weis */ -public class DarkTreeUI extends BasicTreeUI implements PropertyChangeListener, CellConstants { +public class DarkTreeUI extends BasicTreeUI implements PropertyChangeListener, CellConstants, HasRendererPane { protected static final String KEY_PREFIX = "JTree."; public static final String KEY_ALTERNATE_ROW_COLOR = KEY_PREFIX + "alternateRowColor"; @@ -606,6 +607,11 @@ protected static boolean isLeaf(final JTree tree, final int row) { return true; } + @Override + public Container getRendererPane() { + return rendererPane; + } + protected static class TreeUIAction extends AbstractAction implements UIResource { private final ActionListener actionListener; diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/tree/TreeCellContainer.java b/core/src/main/java/com/github/weisj/darklaf/ui/tree/TreeCellContainer.java index 08dcb6050..5267ea3b4 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/tree/TreeCellContainer.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/tree/TreeCellContainer.java @@ -28,16 +28,15 @@ import javax.swing.tree.TreePath; import com.github.weisj.darklaf.ui.cell.CellUtil; -import com.github.weisj.darklaf.ui.cell.hint.IndexedCellContainer; +import com.github.weisj.darklaf.ui.cell.hint.AbstractIndexedCellContainer; -public class TreeCellContainer implements IndexedCellContainer { +public class TreeCellContainer extends AbstractIndexedCellContainer { private final JTree tree; - private final DarkTreeUI ui; public TreeCellContainer(final JTree tree, final DarkTreeUI ui) { + super(ui); this.tree = tree; - this.ui = ui; } @Override diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/tree/TreeRendererComponent.java b/core/src/main/java/com/github/weisj/darklaf/ui/tree/TreeRendererComponent.java index 68083ec3c..f0c34e975 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/tree/TreeRendererComponent.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/tree/TreeRendererComponent.java @@ -25,7 +25,13 @@ import javax.swing.*; -/** @author Jannis Weis */ + +/** + * Component which wraps a non standard cell renderer and provides it with the capability to display + * the nodes icon. + * + * @author Jannis Weis + */ public class TreeRendererComponent extends Container { private static final int PAD = 5; @@ -47,6 +53,10 @@ public void setRenderComponent(final Component renderComponent) { add(renderComponent); } + public Component getRenderComponent() { + return renderComponent; + } + @Override public boolean isShowing() { return true; @@ -56,7 +66,7 @@ public boolean isShowing() { public void doLayout() { if (renderComponent != null) { int offset = getOffset(); - int width = renderComponent.getPreferredSize().width; + int width = Math.min(renderComponent.getPreferredSize().width, getWidth()); int height = getHeight(); if (getComponentOrientation().isLeftToRight()) { renderComponent.setBounds(offset, 0, width, height); @@ -76,12 +86,11 @@ private int getOffset() { @Override public Dimension getPreferredSize() { if (defaultRenderer != null) { - Dimension pSize = renderComponent.getPreferredSize(); - pSize.width += getOffset() + PAD; - Dimension rSize = defaultRenderer.getPreferredSize(); + Dimension actualRendererPreferredSize = renderComponent.getPreferredSize(); + Dimension rendererSize = defaultRenderer.getPreferredSize(); - addIconSize(pSize, rSize); - return pSize; + addIconSize(actualRendererPreferredSize, rendererSize); + return actualRendererPreferredSize; } return new Dimension(0, 0); } @@ -89,23 +98,25 @@ public Dimension getPreferredSize() { @Override public Dimension getMinimumSize() { if (defaultRenderer != null) { - Dimension pSize = renderComponent.getMinimumSize(); - pSize.width += getOffset() + PAD; - Dimension rSize = defaultRenderer.getMinimumSize(); + Dimension actualRendererMinimumSize = renderComponent.getMinimumSize(); + Dimension renderSize = defaultRenderer.getMinimumSize(); - addIconSize(pSize, rSize); - return pSize; + addIconSize(actualRendererMinimumSize, renderSize); + return actualRendererMinimumSize; } return new Dimension(0, 0); } - private void addIconSize(final Dimension pSize, final Dimension rSize) { + private void addIconSize(final Dimension actualSize, final Dimension rendererSize) { Icon icon = defaultRenderer.getIcon(); - if (rSize != null) { - pSize.height = Math.max(pSize.height, rSize.height); + if (icon != null) { + actualSize.width += getOffset() + PAD; + } + if (rendererSize != null) { + actualSize.height = Math.max(actualSize.height, rendererSize.height); } if (icon != null) { - pSize.height = Math.max(pSize.height, icon.getIconHeight()); + actualSize.height = Math.max(actualSize.height, icon.getIconHeight()); } }