From 5641b4f07ce4cc5e6426e6a635795d9da11febec Mon Sep 17 00:00:00 2001 From: weisj Date: Wed, 25 Sep 2019 01:29:56 +0200 Subject: [PATCH] Improved table cell rendering for booleans. --- .../weis/darklaf/decorators/CellRenderer.java | 4 + .../ui/checkbox/DarkCheckBoxBorder.java | 15 ++- .../darklaf/ui/checkbox/DarkCheckBoxUI.java | 1 + .../darklaf/ui/table/DarkTableCellEditor.java | 92 +++++++++++++++++++ .../ui/table/DarkTableCellEditorCheckBox.java | 77 ++++++++++++++++ .../ui/table/DarkTableCellRenderer.java | 68 ++++++++++++++ .../table/DarkTableCellRendererCheckBox.java | 62 +++++++++++++ .../weis/darklaf/ui/table/DarkTableUI.java | 37 ++++---- .../weis/darklaf/ui/text/DarkTextAreaUI.java | 2 +- .../ui/text/DarkTextFieldUIBridge.java | 2 +- src/test/java/UIManagerDefaults.java | 32 ++++--- 11 files changed, 357 insertions(+), 35 deletions(-) create mode 100644 src/main/java/com/weis/darklaf/decorators/CellRenderer.java create mode 100644 src/main/java/com/weis/darklaf/ui/table/DarkTableCellEditor.java create mode 100644 src/main/java/com/weis/darklaf/ui/table/DarkTableCellEditorCheckBox.java create mode 100644 src/main/java/com/weis/darklaf/ui/table/DarkTableCellRenderer.java create mode 100644 src/main/java/com/weis/darklaf/ui/table/DarkTableCellRendererCheckBox.java diff --git a/src/main/java/com/weis/darklaf/decorators/CellRenderer.java b/src/main/java/com/weis/darklaf/decorators/CellRenderer.java new file mode 100644 index 00000000..911ab623 --- /dev/null +++ b/src/main/java/com/weis/darklaf/decorators/CellRenderer.java @@ -0,0 +1,4 @@ +package com.weis.darklaf.decorators; + +public interface CellRenderer { +} diff --git a/src/main/java/com/weis/darklaf/ui/checkbox/DarkCheckBoxBorder.java b/src/main/java/com/weis/darklaf/ui/checkbox/DarkCheckBoxBorder.java index 9cb7b983..933d049a 100644 --- a/src/main/java/com/weis/darklaf/ui/checkbox/DarkCheckBoxBorder.java +++ b/src/main/java/com/weis/darklaf/ui/checkbox/DarkCheckBoxBorder.java @@ -1,12 +1,14 @@ package com.weis.darklaf.ui.checkbox; import com.bulenkov.iconloader.util.SystemInfo; +import com.weis.darklaf.decorators.CellRenderer; import com.weis.darklaf.util.DarkUIUtil; import javax.swing.*; import javax.swing.border.Border; import javax.swing.plaf.InsetsUIResource; import javax.swing.plaf.UIResource; +import javax.swing.table.TableCellRenderer; import java.awt.*; public class DarkCheckBoxBorder implements Border, UIResource { @@ -18,11 +20,18 @@ public class DarkCheckBoxBorder implements Border, UIResource { @Override public Insets getBorderInsets(final Component c) { - final int a = SystemInfo.isMac || DarkUIUtil.getParentOfType(CellRendererPane.class, c) != null ? 2 : 4; - return new InsetsUIResource(a, a, a, a); + if (DarkUIUtil.getParentOfType(CellRendererPane.class, c) != null + || DarkUIUtil.getParentOfType(TableCellRenderer.class, c) != null + || DarkUIUtil.getParentOfType(CellRenderer.class, c) != null + || DarkUIUtil.getParentOfType(CellEditor.class, c) != null) { + return new Insets(2, 5, 2, 5); + } + final int a = SystemInfo.isMac ? 2 : 4; + return new InsetsUIResource(a, a, a, a); } @Override public boolean isBorderOpaque() { return false; - }} + } +} diff --git a/src/main/java/com/weis/darklaf/ui/checkbox/DarkCheckBoxUI.java b/src/main/java/com/weis/darklaf/ui/checkbox/DarkCheckBoxUI.java index 5a6acbc9..d4daa9bb 100644 --- a/src/main/java/com/weis/darklaf/ui/checkbox/DarkCheckBoxUI.java +++ b/src/main/java/com/weis/darklaf/ui/checkbox/DarkCheckBoxUI.java @@ -81,6 +81,7 @@ public class DarkCheckBoxUI extends MetalCheckBoxUI { GraphicsContext config = new GraphicsContext(g); boolean enabled = b.isEnabled(); g.translate(iconRect.x + ICON_OFF, iconRect.y + ICON_OFF); + paintCheckBorder(g, enabled, b.hasFocus()); if (b.isSelected()) { paintCheckArrow(g, enabled); diff --git a/src/main/java/com/weis/darklaf/ui/table/DarkTableCellEditor.java b/src/main/java/com/weis/darklaf/ui/table/DarkTableCellEditor.java new file mode 100644 index 00000000..593edcbe --- /dev/null +++ b/src/main/java/com/weis/darklaf/ui/table/DarkTableCellEditor.java @@ -0,0 +1,92 @@ +package com.weis.darklaf.ui.table; + +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.util.EventObject; + +/** + * @author vincencopalazzo + * @author atarw + */ +public class DarkTableCellEditor extends DefaultCellEditor { + + private final DarkTableCellEditorCheckBox booleanEditor = new DarkTableCellEditorCheckBox(this); + private boolean value; + private boolean isBooleanEditor; + + public DarkTableCellEditor() { + this(new JTextField()); + } + + public DarkTableCellEditor(final JComboBox comboBox) { + super(comboBox); + } + + public DarkTableCellEditor(final JCheckBox checkBox) { + super(checkBox); + } + + public DarkTableCellEditor(final JTextField textField) { + super(textField); + textField.setBorder(new DarkTableCellBorder()); + } + + protected void setValue(final Object value) { + delegate.setValue(value); + if (value instanceof Boolean) { + this.value = (boolean) value; + } else { + isBooleanEditor = false; + } + } + + @Override + public Component getTableCellEditorComponent(final JTable table, final Object value, + final boolean isSelected, final int row, final int column) { + + if (value instanceof Boolean && DarkTableCellRenderer.isBooleanRenderingEnabled(table)) { + isBooleanEditor = true; + return booleanEditor.getTableCellEditorComponent(table, value, isSelected, row, column); + } else { + isBooleanEditor = false; + } + + super.getTableCellEditorComponent(table, value, isSelected, row, column); + boolean alternativeRow = UIManager.getBoolean("Table.alternateRowColor"); + Color alternativeRowColor = UIManager.getColor("Table.alternateRowBackground"); + Color normalColor = UIManager.getColor("Table.background"); + if (alternativeRow) { + if (!isSelected) { + if (row % 2 == 1) { + editorComponent.setBackground(alternativeRowColor); + } else { + editorComponent.setBackground(normalColor); + } + } + } + return editorComponent; + } + + @Override + public Object getCellEditorValue() { + if (isBooleanEditor) { + return value; + } else { + return super.getCellEditorValue(); + } + } + + @Override + public boolean isCellEditable(@NotNull final EventObject anEvent) { + var table = ((JTable) anEvent.getSource()); + if (DarkTableCellRenderer.isBooleanRenderingEnabled(table)) { + var p = MouseInfo.getPointerInfo().getLocation(); + SwingUtilities.convertPointFromScreen(p, table); + var value = table.getValueAt(table.rowAtPoint(p), table.columnAtPoint(p)); + if (value instanceof Boolean) return true; + } + return super.isCellEditable(anEvent); + } +} diff --git a/src/main/java/com/weis/darklaf/ui/table/DarkTableCellEditorCheckBox.java b/src/main/java/com/weis/darklaf/ui/table/DarkTableCellEditorCheckBox.java new file mode 100644 index 00000000..1bd28726 --- /dev/null +++ b/src/main/java/com/weis/darklaf/ui/table/DarkTableCellEditorCheckBox.java @@ -0,0 +1,77 @@ +package com.weis.darklaf.ui.table; + +import com.weis.darklaf.decorators.CellRenderer; + +import javax.swing.*; +import javax.swing.table.TableCellEditor; +import java.awt.*; +import java.util.EventObject; + +/** + * @author vincencopalazzo + * @author atarw + */ +public class DarkTableCellEditorCheckBox extends AbstractCellEditor implements TableCellEditor, SwingConstants { + + private final JCheckBox checkBox; + + public DarkTableCellEditorCheckBox(final DarkTableCellEditor delegate) { + checkBox = new CellEditorCheckBox(); + checkBox.addChangeListener(e -> delegate.setValue(checkBox.isSelected())); + } + + + @Override + public Component getTableCellEditorComponent(final JTable table, final Object value, + final boolean isSelected, final int row, final int column) { + if (value instanceof Boolean) { + checkBox.setSelected((Boolean) value); + } + checkBox.setHorizontalAlignment(table.getComponentOrientation().isLeftToRight() ? LEFT : RIGHT); + + boolean alternativeRow = UIManager.getBoolean("Table.alternateRowColor"); + Color alternativeRowColor = UIManager.getColor("Table.alternateRowBackground"); + Color normalColor = UIManager.getColor("Table.background"); + if (alternativeRow) { + if (!isSelected) { + if (row % 2 == 1) { + checkBox.setBackground(alternativeRowColor); + } else { + checkBox.setBackground(normalColor); + } + checkBox.setForeground(table.getForeground()); + } else { + checkBox.setForeground(table.getSelectionForeground()); + checkBox.setBackground(table.getSelectionBackground()); + } + } + return checkBox; + } + + @Override + public Object getCellEditorValue() { + return checkBox.isSelected(); + } + + @Override + public boolean isCellEditable(final EventObject anEvent) { + return true; + } + + @Override + public boolean shouldSelectCell(final EventObject anEvent) { + return false; + } + + private static class CellEditorCheckBox extends JCheckBox implements CellRenderer { + @Override + public boolean hasFocus() { + return true; + } + + @Override + public boolean isFocusOwner() { + return super.hasFocus(); + } + } +} diff --git a/src/main/java/com/weis/darklaf/ui/table/DarkTableCellRenderer.java b/src/main/java/com/weis/darklaf/ui/table/DarkTableCellRenderer.java new file mode 100644 index 00000000..b7a4e01f --- /dev/null +++ b/src/main/java/com/weis/darklaf/ui/table/DarkTableCellRenderer.java @@ -0,0 +1,68 @@ +package com.weis.darklaf.ui.table; + +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.table.DefaultTableCellRenderer; +import java.awt.*; + +/** + * @author vincencopalazzo + * @author atarw + */ +public class DarkTableCellRenderer extends DefaultTableCellRenderer { + + private final DarkTableCellRendererCheckBox booleanRenderer = new DarkTableCellRendererCheckBox(); + + protected static boolean isBooleanRenderingEnabled(@NotNull final JTable table) { + return Boolean.TRUE.equals(table.getClientProperty("JTable.renderBooleanAsCheckBox")); + } + + @Override + public Component getTableCellRendererComponent(final JTable table, final Object value, + final boolean isSelected, final boolean hasFocus, final int row, + final int column) { + if (value instanceof Boolean && isBooleanRenderingEnabled(table)) { + return booleanRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + } + + JComponent component = (JComponent) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, + row, column); + this.setVerticalAlignment(SwingConstants.CENTER); + setHorizontalAlignment(table.getComponentOrientation().isLeftToRight() ? LEFT : RIGHT); + + boolean alternativeRow = UIManager.getBoolean("Table.alternateRowColor"); + Color alternativeRowColor = UIManager.getColor("Table.alternateRowBackground"); + Color normalColor = UIManager.getColor("Table.background"); + if (alternativeRow) { + if (!isSelected) { + if (row % 2 == 1) { + component.setBackground(alternativeRowColor); + setDefaultCellRenderWithAllType(table, value, isSelected, hasFocus, row, column, + alternativeRowColor); + } else { + component.setBackground(normalColor); + setDefaultCellRenderWithAllType(table, value, isSelected, hasFocus, row, column, normalColor); + } + component.setForeground(table.getSelectionForeground()); + } + } + return component; + } + + // This method setting a MaterialCellRender at the particular class + // With this class not working correctly the color alternate in the Jtable + // in particular the IconImage without this code the cell is painted not correctly or + // in the cell did print the path of the image + protected void setDefaultCellRenderWithAllType(final JTable table, final Object value, final boolean isSelected, + final boolean hasFocus, final int row, final int column, + final Color color) { + if (table == null) { + throw new IllegalArgumentException("Table is null"); + } + + Component component = table.getDefaultRenderer(ImageIcon.class) + .getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + component.setBackground(color); + } +} diff --git a/src/main/java/com/weis/darklaf/ui/table/DarkTableCellRendererCheckBox.java b/src/main/java/com/weis/darklaf/ui/table/DarkTableCellRendererCheckBox.java new file mode 100644 index 00000000..bd7e0a12 --- /dev/null +++ b/src/main/java/com/weis/darklaf/ui/table/DarkTableCellRendererCheckBox.java @@ -0,0 +1,62 @@ +package com.weis.darklaf.ui.table; + +import javax.swing.*; +import javax.swing.table.TableCellRenderer; +import java.awt.*; + +/** + * @author vincencopalazzo + * @author atarw + */ +public class DarkTableCellRendererCheckBox extends JCheckBox implements TableCellRenderer, SwingConstants { + + private boolean hasFocus; + + public DarkTableCellRendererCheckBox() { + this(false); + } + + + public DarkTableCellRendererCheckBox(final boolean selected) { + setSelected(selected); + } + + @Override + public boolean hasFocus() { + return hasFocus || super.hasFocus(); + } + + @Override + public boolean isFocusOwner() { + return super.hasFocus(); + } + + @Override + public Component getTableCellRendererComponent(final JTable table, final Object value, + final boolean isSelected, final boolean focus, + final int row, final int column) { + if (value instanceof Boolean) { + setSelected((Boolean) value); + } + setHorizontalAlignment(table.getComponentOrientation().isLeftToRight() ? LEFT : RIGHT); + hasFocus = focus; + + boolean alternativeRow = UIManager.getBoolean("Table.alternateRowColor"); + Color alternativeRowColor = UIManager.getColor("Table.alternateRowBackground"); + Color normalColor = UIManager.getColor("Table.background"); + if (alternativeRow) { + if (!isSelected) { + if (row % 2 == 1) { + this.setBackground(alternativeRowColor); + } else { + this.setBackground(normalColor); + } + this.setForeground(table.getForeground()); + } else { + this.setForeground(table.getSelectionForeground()); + this.setBackground(table.getSelectionBackground()); + } + } + return this; + } +} diff --git a/src/main/java/com/weis/darklaf/ui/table/DarkTableUI.java b/src/main/java/com/weis/darklaf/ui/table/DarkTableUI.java index 71aec11f..22ac985e 100644 --- a/src/main/java/com/weis/darklaf/ui/table/DarkTableUI.java +++ b/src/main/java/com/weis/darklaf/ui/table/DarkTableUI.java @@ -7,7 +7,6 @@ import sun.swing.SwingUtilities2; import javax.swing.*; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; -import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import java.awt.*; @@ -31,7 +30,11 @@ public class DarkTableUI extends DarkTableUIBridge { public void focusLost(final FocusEvent e) { var bg = table.getSelectionBackground(); if (bg instanceof UIResource) { - table.setSelectionBackground(UIManager.getColor("Table.selectionBackground")); + if (table.isEditing()) { + table.setSelectionBackground(table.getBackground()); + } else { + table.setSelectionBackground(UIManager.getColor("Table.selectionBackground")); + } } table.repaint(); } @@ -57,10 +60,22 @@ public class DarkTableUI extends DarkTableUIBridge { return dist; } + protected static void setupRendererComponents(@NotNull final JTable table) { + var cellRenderer = new DarkTableCellRenderer(); + table.setDefaultRenderer(Object.class, cellRenderer); + table.setDefaultRenderer(String.class, cellRenderer); + table.setDefaultRenderer(Integer.class, cellRenderer); + table.setDefaultRenderer(Double.class, cellRenderer); + table.setDefaultRenderer(Float.class, cellRenderer); + table.setDefaultRenderer(Boolean.class, cellRenderer); + } + @Override protected void installDefaults() { super.installDefaults(); table.setRowHeight(ROW_HEIGHT); + table.setDefaultEditor(Object.class, new DarkTableCellEditor()); + setupRendererComponents(table); } @Override @@ -320,22 +335,6 @@ public class DarkTableUI extends DarkTableUIBridge { } } } - - if (table.isEditing() && table.getEditingRow() == row && - table.getEditingColumn() == column) { - Component component = table.getEditorComponent(); - if (component instanceof JComponent) { - //Todo - var border = UIManager.getBorder("Table.cellEditorBorder"); - ((JComponent) component).setBorder(border); - } - component.setBounds(r); - component.validate(); - } else { - TableCellRenderer renderer = table.getCellRenderer(row, column); - Component component = table.prepareRenderer(renderer, row, column); - rendererPane.paintComponent(g, component, table, r.x, r.y, - r.width, r.height, true); - } + super.paintCell(g, r, row, column, cMin, cMax); } } diff --git a/src/main/java/com/weis/darklaf/ui/text/DarkTextAreaUI.java b/src/main/java/com/weis/darklaf/ui/text/DarkTextAreaUI.java index c62061ed..35a327b7 100644 --- a/src/main/java/com/weis/darklaf/ui/text/DarkTextAreaUI.java +++ b/src/main/java/com/weis/darklaf/ui/text/DarkTextAreaUI.java @@ -1,6 +1,6 @@ package com.weis.darklaf.ui.text; -import com.weis.darklaf.DarkHTML; +import com.weis.darklaf.ui.html.DarkHTML; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/weis/darklaf/ui/text/DarkTextFieldUIBridge.java b/src/main/java/com/weis/darklaf/ui/text/DarkTextFieldUIBridge.java index b8515ad2..a81115df 100644 --- a/src/main/java/com/weis/darklaf/ui/text/DarkTextFieldUIBridge.java +++ b/src/main/java/com/weis/darklaf/ui/text/DarkTextFieldUIBridge.java @@ -1,6 +1,6 @@ package com.weis.darklaf.ui.text; -import com.weis.darklaf.DarkHTML; +import com.weis.darklaf.ui.html.DarkHTML; import javax.swing.*; import javax.swing.event.DocumentEvent; diff --git a/src/test/java/UIManagerDefaults.java b/src/test/java/UIManagerDefaults.java index 66f06964..989e7d8b 100644 --- a/src/test/java/UIManagerDefaults.java +++ b/src/test/java/UIManagerDefaults.java @@ -5,6 +5,7 @@ import com.weis.darklaf.DarkLafInfo; import com.weis.darklaf.LafManager; +import com.weis.darklaf.ui.table.DarkTableCellRendererCheckBox; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,6 +24,7 @@ import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.lang.reflect.InvocationTargetException; +import java.util.EventObject; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -108,6 +110,7 @@ public class UIManagerDefaults implements ActionListener, ItemListener { @NotNull private JComponent buildNorthComponent() { comboBox = new JComboBox<>(); + comboBox.setPreferredSize(new Dimension(200, comboBox.getPreferredSize().height)); final JLabel label = new JLabel("Select Item:"); label.setDisplayedMnemonic('S'); @@ -147,17 +150,17 @@ public class UIManagerDefaults implements ActionListener, ItemListener { table.getColumnModel().getColumn(1).setPreferredWidth(500); table.getColumnModel().getColumn(2).setPreferredWidth(100); table.getColumnModel().getColumn(2).setCellRenderer(new SampleRenderer()); + table.getColumnModel().getColumn(2).setCellEditor(new DefaultCellEditor(new JTextField()) { + @Override + public boolean isCellEditable(final EventObject anEvent) { + return false; + } + }); final Dimension d = table.getPreferredSize(); d.height = 350; table.setPreferredScrollableViewportSize(d); -// table.setShowHorizontalLines(false); -// table.setShowVerticalLines(false); -// table.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); - - return new JScrollPane(table) {{ - setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); - }}; + return new JScrollPane(table); } /* @@ -180,7 +183,6 @@ public class UIManagerDefaults implements ActionListener, ItemListener { comboBox.setModel(new DefaultComboBoxModel<>(comboBoxItems)); comboBox.setSelectedIndex(-1); comboBox.addItemListener(this); - comboBox.requestFocusInWindow(); if (selectedItem != null) { comboBox.setSelectedItem(selectedItem); @@ -192,7 +194,7 @@ public class UIManagerDefaults implements ActionListener, ItemListener { * items for each attribute type. */ @NotNull - private TreeMap buildItemsMap() { + private TreeMap> buildItemsMap() { final UIDefaults defaults = UIManager.getLookAndFeelDefaults(); // Build of Map of items and a Map of attributes for each item for (final Object key : new HashSet<>(defaults.keySet())) { @@ -362,8 +364,11 @@ public class UIManagerDefaults implements ActionListener, ItemListener { */ public void actionPerformed(final ActionEvent e) { selectedItem = null; + if (table.isEditing()) { + table.getCellEditor().stopCellEditing(); + } + table.clearSelection(); resetComponents(); - comboBox.requestFocusInWindow(); } /* @@ -396,7 +401,7 @@ public class UIManagerDefaults implements ActionListener, ItemListener { final Vector row = new Vector<>(3); row.add(attribute); if (value != null) { - row.add(value.toString()); + row.add(value instanceof Boolean ? value : value.toString()); if (value instanceof Icon) { value = new SafeIcon((Icon) value); } @@ -540,6 +545,9 @@ public class UIManagerDefaults implements ActionListener, ItemListener { * Render the value based on its class. */ private static final class SampleRenderer extends JLabel implements TableCellRenderer { + + private final DarkTableCellRendererCheckBox booleanRenderer = new DarkTableCellRendererCheckBox(); + private SampleRenderer() { super(); setHorizontalAlignment(SwingConstants.CENTER); @@ -569,6 +577,8 @@ public class UIManagerDefaults implements ActionListener, ItemListener { setFont((Font) sample); } else if (sample instanceof Icon) { setIcon((Icon) sample); + } else if (sample instanceof Boolean) { + return booleanRenderer.getTableCellRendererComponent(table, sample, isSelected, hasFocus, row, column); } return this; }