Browse Source

Improve performance of JTable by disabling kerning on table font.

Ensure lead selection border of tree cell  is painted last.
Added option to declare fallback values in properties that are only applied if the key doesn't have a value associated yet.
Ensure boolean renderers use the correct cell border.
Center boolean renderers.
Ensure DarkMultiCellEditor uses the correct delegate.
Fixed missing border on table header cell after scrolling.
pull/198/head
weisj 5 years ago
parent
commit
1c8b4f42a2
  1. 19
      core/src/main/java/com/github/weisj/darklaf/task/FontDefaultsInitTask.java
  2. 5
      core/src/main/java/com/github/weisj/darklaf/ui/cell/DarkCellRendererToggleButton.java
  3. 4
      core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/CellHintPopupListener.java
  4. 4
      core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/IndexedCellContainer.java
  5. 116
      core/src/main/java/com/github/weisj/darklaf/ui/table/DarkTableUI.java
  6. 13
      core/src/main/java/com/github/weisj/darklaf/ui/table/DarkTableUIBridge.java
  7. 58
      core/src/main/java/com/github/weisj/darklaf/ui/table/header/DarkTableHeaderUI.java
  8. 2
      core/src/main/java/com/github/weisj/darklaf/ui/table/renderer/DarkMultiCellEditor.java
  9. 4
      core/src/main/java/com/github/weisj/darklaf/ui/table/renderer/DarkTableCellEditor.java
  10. 20
      core/src/main/java/com/github/weisj/darklaf/ui/table/renderer/DarkTableCellEditorDelegate.java
  11. 5
      core/src/main/java/com/github/weisj/darklaf/ui/table/renderer/DarkTableCellEditorToggleButton.java
  12. 5
      core/src/main/java/com/github/weisj/darklaf/ui/table/renderer/DarkTableCellRendererDelegate.java
  13. 30
      core/src/main/java/com/github/weisj/darklaf/ui/tree/DarkTreeUI.java
  14. 3
      core/src/main/resources/com/github/weisj/darklaf/properties/font.properties
  15. 2
      core/src/main/resources/com/github/weisj/darklaf/properties/platform/linux.properties
  16. 3
      core/src/main/resources/com/github/weisj/darklaf/properties/platform/mac.properties
  17. 42
      core/src/main/resources/com/github/weisj/darklaf/properties/platform/windows.properties
  18. 2
      core/src/main/resources/com/github/weisj/darklaf/properties/ui/cell.properties
  19. 19
      core/src/test/java/ui/table/LargeTableDemo.java
  20. 54
      core/src/test/java/ui/table/TableDemo.java
  21. 43
      property-loader/src/main/java/com/github/weisj/darklaf/PropertyLoader.java

19
core/src/main/java/com/github/weisj/darklaf/task/FontDefaultsInitTask.java

@ -52,7 +52,8 @@ public class FontDefaultsInitTask implements DefaultsInitTask {
private static final String FONT_PROPERTY_PATH = "properties/";
private static final String FONT_SIZE_DEFAULTS_NAME = "font_sizes";
private static final String FONT_DEFAULTS_NAME = "font";
private static final String KERNING_LIST = "fontList.kerningEnabled";
private static final String KERNING_ALLOW_LIST = "kerning.allowList";
private static final String KERNING_BLOCK_LIST = "kerning.blockList";
private static final String ALL_FONTS = "__all__";
@ -85,19 +86,23 @@ public class FontDefaultsInitTask implements DefaultsInitTask {
}
if (systemKerningEnabled()) {
List<String> kerningFontsList = PropertyUtil.getList(defaults, KERNING_LIST, String.class);
List<String> allowedFonts = PropertyUtil.getList(defaults, KERNING_ALLOW_LIST, String.class);
List<String> blockedFonts = PropertyUtil.getList(defaults, KERNING_BLOCK_LIST, String.class);
if (!kerningFontsList.isEmpty()) {
Set<String> kerningFonts = new HashSet<>(kerningFontsList);
boolean enabledAll = ALL_FONTS.equals(kerningFontsList.get(0));
if (!allowedFonts.isEmpty()) {
Set<String> allowedFontsSet = new HashSet<>(allowedFonts);
Set<String> blockedFontSet = new HashSet<>(blockedFonts);
boolean enabledAll = ALL_FONTS.equals(allowedFonts.get(0));
setupKerningPerFont(defaults, key -> enabledAll || kerningFonts.contains(key));
setupKerningPerFont(defaults, key -> (enabledAll || allowedFontsSet.contains(key))
&& !blockedFontSet.contains(key));
}
}
applyFontRule(currentTheme, defaults);
setupRenderingHints(defaults);
defaults.remove(KERNING_LIST);
defaults.remove(KERNING_ALLOW_LIST);
defaults.remove(KERNING_BLOCK_LIST);
}
private boolean systemKerningEnabled() {

5
core/src/main/java/com/github/weisj/darklaf/ui/cell/DarkCellRendererToggleButton.java

@ -27,6 +27,7 @@ package com.github.weisj.darklaf.ui.cell;
import java.awt.*;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.table.TableCellRenderer;
import javax.swing.tree.TreeCellRenderer;
@ -41,6 +42,7 @@ public class DarkCellRendererToggleButton<T extends JToggleButton & CellEditorTo
implements TableCellRenderer, TreeCellRenderer, SwingConstants {
private final T toggleButton;
private final Border border = new DarkCellBorder();
public DarkCellRendererToggleButton(final T toggleButton) {
this.toggleButton = toggleButton;
@ -54,6 +56,7 @@ public class DarkCellRendererToggleButton<T extends JToggleButton & CellEditorTo
toggleButton.setSelected((Boolean) value);
}
toggleButton.setHasFocus(focus);
toggleButton.setBorder(border);
return toggleButton;
}
@ -85,6 +88,7 @@ public class DarkCellRendererToggleButton<T extends JToggleButton & CellEditorTo
public CellCheckBox(final boolean opaque) {
setOpaque(opaque);
setHorizontalAlignment(CENTER);
putClientProperty(ToggleButtonConstants.KEY_IS_TREE_EDITOR, true);
putClientProperty(ToggleButtonConstants.KEY_IS_TABLE_EDITOR, true);
putClientProperty(ToggleButtonConstants.KEY_VERTICAL_ICON_OFFSET, 0);
@ -111,6 +115,7 @@ public class DarkCellRendererToggleButton<T extends JToggleButton & CellEditorTo
public CellRadioButton(final boolean opaque) {
setOpaque(opaque);
setHorizontalAlignment(CENTER);
putClientProperty(ToggleButtonConstants.KEY_IS_TREE_EDITOR, true);
putClientProperty(ToggleButtonConstants.KEY_IS_TABLE_EDITOR, true);
putClientProperty(ToggleButtonConstants.KEY_VERTICAL_ICON_OFFSET, 0);

4
core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/CellHintPopupListener.java

@ -355,8 +355,12 @@ public class CellHintPopupListener<T extends JComponent, I> extends MouseInputAd
Component renderer = cellHintPopupListener.getRenderer();
if (rendererBounds != null && renderer != null) {
Color bg = cellHintPopupListener.getBackground(renderer);
if (bg == null) bg = cellHintPopupListener.cellContainer.getComponent().getBackground();
if (bg == null) bg = getBackground();
if (bg != null) {
g.setColor(bg);
g.fillRect(0, 0, getWidth(), getHeight());
}
g.translate(-rendererBounds.x, -rendererBounds.y);
// If the renderer is an editor we need to restore the bounds.

4
core/src/main/java/com/github/weisj/darklaf/ui/cell/hint/IndexedCellContainer.java

@ -38,7 +38,9 @@ public interface IndexedCellContainer<T extends JComponent, I> extends CellConta
I getCellPosition(final Point p);
Color getBackgroundAt(final I position, final Component renderer);
default Color getBackgroundAt(final I position, final Component renderer) {
return renderer != null ? renderer.getBackground() : null;
}
default Component getEffectiveCellRendererComponent(final I position, final boolean isEditing) {
if (isEditing) {

116
core/src/main/java/com/github/weisj/darklaf/ui/table/DarkTableUI.java

@ -38,9 +38,13 @@ import javax.swing.table.*;
import sun.swing.SwingUtilities2;
import com.github.weisj.darklaf.components.OverlayScrollPane;
import com.github.weisj.darklaf.graphics.PaintUtil;
import com.github.weisj.darklaf.ui.cell.CellUtil;
import com.github.weisj.darklaf.ui.cell.DarkCellRendererPane;
import com.github.weisj.darklaf.ui.table.renderer.*;
import com.github.weisj.darklaf.ui.table.renderer.DarkColorTableCellRendererEditor;
import com.github.weisj.darklaf.ui.table.renderer.DarkTableCellEditorDelegate;
import com.github.weisj.darklaf.ui.table.renderer.DarkTableCellRenderer;
import com.github.weisj.darklaf.ui.table.renderer.DarkTableCellRendererDelegate;
import com.github.weisj.darklaf.util.DarkUIUtil;
import com.github.weisj.darklaf.util.PropertyKey;
import com.github.weisj.darklaf.util.PropertyUtil;
@ -205,8 +209,9 @@ public class DarkTableUI extends DarkTableUIBridge implements TableConstants {
protected boolean scrollBarVisible() {
JScrollPane comp = DarkUIUtil.getParentOfType(JScrollPane.class, table, 2);
if (comp == null) return false;
OverlayScrollPane overlayScrollPane = DarkUIUtil.getParentOfType(OverlayScrollPane.class, table, 3);
return comp != null && overlayScrollPane == null && comp.getVerticalScrollBar().isVisible();
return overlayScrollPane == null && comp.getVerticalScrollBar().isVisible();
}
protected boolean showVerticalLine(final boolean ltr, final boolean scrollVisible,
@ -272,11 +277,17 @@ public class DarkTableUI extends DarkTableUIBridge implements TableConstants {
}
int tableHeight = getPreferredSize(table).height;
g.setColor(parent.getBackground());
g.fillRect(vacatedColumnRect.x, 0, vacatedColumnRect.width - 1, tableHeight);
int width = vacatedColumnRect.width;
if (draggedColumnIndex < cMax) width--;
g.fillRect(vacatedColumnRect.x, 0, width, tableHeight);
// Move to the where the cell has been dragged.
vacatedColumnRect.x += dist;
g.setColor(Color.RED);
PaintUtil.drawRect(g, vacatedColumnRect);
boolean ltr = table.getComponentOrientation().isLeftToRight();
// Fill the background.
@ -315,9 +326,8 @@ public class DarkTableUI extends DarkTableUIBridge implements TableConstants {
// Render the cell value
Rectangle r = table.getCellRect(row, draggedColumnIndex, false);
r.x += dist;
paintCell(g, r, row, draggedColumnIndex);
paintCell(g, r, row, draggedColumnIndex, cMin, cMax);
// Paint the (lower) horizontal grid line if necessary.
if (table.getShowHorizontalLines()) {
g.setColor(table.getGridColor());
Rectangle rcr = table.getCellRect(row, draggedColumnIndex, true);
@ -331,6 +341,24 @@ public class DarkTableUI extends DarkTableUIBridge implements TableConstants {
}
}
@Override
public void paint(final Graphics g, final JComponent c) {
/*
* JTable always subtracts the cell margins even if for the last column. This results in
* part of the cell not being repainted. Dispatch repaint here manually.
*/
if (table.getShowVerticalLines()) {
Rectangle r = g.getClipBounds();
int spacing = table.getColumnModel().getColumnMargin();
if (r.x + r.width == c.getWidth() - spacing) {
r.x = r.x + r.width;
r.width = spacing;
c.repaint(r);
}
}
super.paint(g, c);
}
public static boolean ignoreKeyCodeOnEdit(final KeyEvent event, final JTable table) {
if (event != null) {
int keyCode = event.getKeyCode();
@ -364,74 +392,43 @@ public class DarkTableUI extends DarkTableUIBridge implements TableConstants {
return false;
}
@Override
protected void paintCell(final Graphics g, final Rectangle cellRect, final int row, final int column) {
Rectangle bounds = table.getVisibleRect();
Point upperLeft = bounds.getLocation();
Point lowerRight = new Point(upperLeft.x + bounds.width - 1, upperLeft.y + bounds.height - 1);
int cMin = table.columnAtPoint(upperLeft);
int cMax = table.columnAtPoint(lowerRight);
boolean scrollLtR = !isScrollPaneRtl();
boolean ltr = table.getComponentOrientation().isLeftToRight();
protected void paintCell(final Graphics g, final Rectangle r, final int row, final int column,
final int cMin, final int cMax) {
// if (true) return;
boolean isEditorCell = table.isEditing() && table.getEditingRow() == row && table.getEditingColumn() == column;
JTableHeader header = table.getTableHeader();
int draggedIndex = header != null ? viewIndexForColumn(header.getDraggedColumn())
: -1;
int dist = header != null ? adjustDistance(header.getDraggedDistance(),
table.getCellRect(row, draggedIndex, true),
table)
: 0;
boolean isDragged = column == draggedIndex && dist != 0;
Rectangle rectWithSpacing = table.getCellRect(row, cMin, true);
Rectangle r = new Rectangle(cellRect);
r.y = rectWithSpacing.y;
r.height = rectWithSpacing.height;
if (table.getShowHorizontalLines()) {
r.height--;
}
if (!scrollBarVisible()) {
if (ltr) {
if (column == cMax && !isDragged) r.width += 1;
} else {
if (column == cMin && !isDragged) r.width += 1;
}
} else if (!scrollLtR) {
if (ltr) {
if (column == cMax && !isDragged) r.width += 1;
if (column == cMin && !isDragged) {
r.width -= 1;
r.x += 1;
}
} else {
if (column == cMin && !isDragged) r.width += 1;
if (column == cMax && !isDragged) {
r.width -= 1;
r.x += 1;
}
int x = r.x;
int y = r.y;
int w = r.width;
int h = r.height;
if (table.getShowVerticalLines() && !scrollBarVisible()) {
if (column == table.getColumnCount() - 1) {
w++;
}
}
if (isEditorCell) {
if (!table.getShowVerticalLines()) {
if (column > cMin) r.x -= 1;
if (column > cMin && column < cMax) r.width += 1;
if (column > cMin) x--;
if (column > cMin && column < cMax) w++;
}
}
if (isEditorCell) {
Component component = getCellEditorComponent();
component.setBounds(r);
component.setBounds(x, y, w, h);
component.validate();
} else {
TableCellRenderer renderer = getCellRenderer(row, column);
Component component = table.prepareRenderer(renderer, row, column);
CellUtil.setSelectedFlag(component, table.isCellSelected(row, column));
rendererPane.paintComponent(g, component, table, r.x, r.y, r.width, r.height, true);
rendererPane.paintComponent(g, component, table, x, y, w, h, true);
}
}
protected TableCellRenderer getCellRenderer(final int row, final int column) {
TableCellRenderer renderer = table.getCellRenderer(row, column);
if (renderer instanceof DarkTableCellRendererDelegate) return renderer;
if (rendererDelegate == null) {
rendererDelegate = new DarkTableCellRendererDelegate(renderer);
}
@ -455,7 +452,7 @@ public class DarkTableUI extends DarkTableUIBridge implements TableConstants {
final JTable comp) {
int dist = distance;
int min = 0;
int max = comp.getX() + comp.getWidth();
int max = comp.getWidth();
if (rect.x + dist <= min) {
dist = min - rect.x;
}
@ -493,14 +490,6 @@ public class DarkTableUI extends DarkTableUIBridge implements TableConstants {
}
}
@Override
public void mousePressed(final MouseEvent e) {
super.mousePressed(e);
if (SwingUtilities.isLeftMouseButton(e)) {
table.repaint();
}
}
protected JFileChooser getFileChooser() {
Object obj = PropertyUtil.getObject(table, DarkTableUI.KEY_FILE_CHOOSER_PARENT, Supplier.class, Object::new)
.get();
@ -540,7 +529,8 @@ public class DarkTableUI extends DarkTableUIBridge implements TableConstants {
table.setRowMargin(b ? 1 : 0);
} else if (KEY_VERTICAL_LINES.equals(key)) {
boolean b = Boolean.TRUE.equals(e.getNewValue());
table.getColumnModel().setColumnMargin(b ? 1 : 0);
TableColumnModel cm = table.getColumnModel();
cm.setColumnMargin(b ? 1 : 0);
} else if (PropertyKey.ANCESTOR.equals(key)) {
Object oldVal = e.getOldValue();
Object newVal = e.getNewValue();

13
core/src/main/java/com/github/weisj/darklaf/ui/table/DarkTableUIBridge.java

@ -215,7 +215,7 @@ public abstract class DarkTableUIBridge extends TableUIBridge {
columnWidth = aColumn.getWidth();
cellRect.width = columnWidth - columnMargin;
if (aColumn != draggedColumn) {
paintCell(g, cellRect, row, column);
paintCell(g, cellRect, row, column, rMin, rMax);
}
cellRect.x += columnWidth;
}
@ -227,7 +227,7 @@ public abstract class DarkTableUIBridge extends TableUIBridge {
if (aColumn != draggedColumn) {
columnWidth = aColumn.getWidth();
cellRect.width = columnWidth - columnMargin;
paintCell(g, cellRect, row, cMin);
paintCell(g, cellRect, row, cMin, cMin, cMax);
}
for (int column = cMin + 1; column <= cMax; column++) {
aColumn = cm.getColumn(column);
@ -235,7 +235,7 @@ public abstract class DarkTableUIBridge extends TableUIBridge {
cellRect.width = columnWidth - columnMargin;
cellRect.x -= columnWidth;
if (aColumn != draggedColumn) {
paintCell(g, cellRect, row, column);
paintCell(g, cellRect, row, column, cMin, cMax);
}
}
}
@ -250,8 +250,11 @@ public abstract class DarkTableUIBridge extends TableUIBridge {
rendererPane.removeAll();
}
@Override
protected abstract void paintCell(final Graphics g, final Rectangle cellRect, final int row, final int column);
@Deprecated
protected final void paintCell(final Graphics g, final Rectangle cellRect, final int row, final int column) {}
protected abstract void paintCell(final Graphics g, final Rectangle cellRect, final int row, final int column,
final int cMin, final int cMax);
@Override
protected int viewIndexForColumn(final TableColumn aColumn) {

58
core/src/main/java/com/github/weisj/darklaf/ui/table/header/DarkTableHeaderUI.java

@ -48,6 +48,7 @@ public class DarkTableHeaderUI extends BasicTableHeaderUI {
protected Color background;
protected int defaultHeight;
protected DarkTableHeaderRendererDelegate rendererDelegate;
private int lastMaxVisible = -1;
public static ComponentUI createUI(final JComponent c) {
return new DarkTableHeaderUI();
@ -110,21 +111,28 @@ public class DarkTableHeaderUI extends BasicTableHeaderUI {
boolean ltr = header.getComponentOrientation().isLeftToRight();
Rectangle clip = g.getClipBounds();
Point left = clip.getLocation();
Point right = new Point(clip.x + clip.width - 1, clip.y);
Point leftClip = clip.getLocation();
Point rightCip = new Point(clip.x + clip.width - 1, clip.y);
TableColumnModel cm = header.getColumnModel();
int cMin = header.columnAtPoint(ltr ? left : right);
int cMax = header.columnAtPoint(ltr ? right : left);
// This should never happen.
if (cMin == -1) {
cMin = 0;
}
int cMin = Math.max(header.columnAtPoint(ltr ? leftClip : rightCip), 0);
int cMax = header.columnAtPoint(ltr ? rightCip : leftClip);
// If the table does not have enough columns to fill the view we'll get -1.
// Replace this with the index of the last column.
if (cMax == -1) {
cMax = cm.getColumnCount() - 1;
}
Rectangle bounds = c.getVisibleRect();
Point left = bounds.getLocation();
Point right = new Point(bounds.x + bounds.width - 1, bounds.y);
int cMinVisible = Math.max(header.columnAtPoint(ltr ? left : right), 0);
int cMaxVisible = header.columnAtPoint(ltr ? right : left);
if (cMaxVisible == -1) {
cMaxVisible = cm.getColumnCount() - 1;
}
Color borderColor = c.getBorder() instanceof DarkTableScrollPaneBorder
? ((DarkTableScrollPaneBorder) c.getBorder()).getBorderColor()
: getBorderColor();
@ -132,7 +140,7 @@ public class DarkTableHeaderUI extends BasicTableHeaderUI {
TableColumn draggedColumn = header.getDraggedColumn();
Rectangle cellRect = header.getHeaderRect(ltr ? cMin : cMax);
paintCells(g, h, ltr, cm, cMin, cMax, borderColor, draggedColumn, cellRect);
paintCells(g, h, ltr, cm, cMin, cMax, cMinVisible, cMaxVisible, borderColor, draggedColumn, cellRect);
// Paint the dragged column if we are dragging.
if (draggedColumn != null) {
@ -142,26 +150,42 @@ public class DarkTableHeaderUI extends BasicTableHeaderUI {
// Remove all components in the rendererPane.
rendererPane.removeAll();
config.restore();
/*
* If the table is in a scroll pane which uses buffer backed viewports scrolling leads to the previously
* last cell missing part of its border. We manually repaint it to prevent the appearance of joint cells.
*/
if (lastMaxVisible != cMaxVisible) {
if (lastMaxVisible >= 0) {
Rectangle r = header.getHeaderRect(lastMaxVisible);
SwingUtilities.invokeLater(() -> header.repaint(r));
}
lastMaxVisible = cMaxVisible;
}
}
public void paintCells(final Graphics2D g, final int h, final boolean ltr,
final TableColumnModel cm,
final int cMin, final int cMax, final Color borderColor,
final int cMin, final int cMax,
final int cMinVisible, final int cMaxVisible,
final Color borderColor,
final TableColumn draggedColumn, final Rectangle cellRect) {
if (ltr) {
for (int column = cMin; column <= cMax; column++) {
paintSingleCell(g, h, cm, cMax, borderColor, draggedColumn, cellRect, column);
paintSingleCell(g, h, cm, cMinVisible, cMaxVisible,
borderColor, draggedColumn, cellRect, column);
}
} else {
for (int column = cMax; column >= cMin; column--) {
paintSingleCell(g, h, cm, cMin, borderColor, draggedColumn, cellRect, column);
paintSingleCell(g, h, cm, cMinVisible, cMaxVisible,
borderColor, draggedColumn, cellRect, column);
}
}
}
public void paintSingleCell(final Graphics2D g, final int h, final TableColumnModel cm,
final int cMax, final Color borderColor,
final TableColumn draggedColumn,
final int cMinVisible, final int cMaxVisible,
final Color borderColor, final TableColumn draggedColumn,
final Rectangle cellRect, final int column) {
TableColumn aColumn;
int columnWidth;
@ -171,11 +195,11 @@ public class DarkTableHeaderUI extends BasicTableHeaderUI {
if (aColumn != draggedColumn) {
paintCell(g, cellRect, column);
}
cellRect.x += columnWidth;
if (column != cMax) {
g.setColor(borderColor);
g.fillRect(cellRect.x - 1, 0, 1, h);
if (column < cMaxVisible) {
g.fillRect(cellRect.x + cellRect.width - 1, 0, 1, h);
}
cellRect.x += cellRect.width;
}
public void paintDraggedArea(final Graphics2D g, final boolean ltr,

2
core/src/main/java/com/github/weisj/darklaf/ui/table/renderer/DarkMultiCellEditor.java

@ -59,7 +59,7 @@ public class DarkMultiCellEditor extends TableCellEditorDelegate {
return dateEditor.get();
}
}
return getDelegate();
return null;
}
@Override

4
core/src/main/java/com/github/weisj/darklaf/ui/table/renderer/DarkTableCellEditor.java

@ -130,7 +130,9 @@ public class DarkTableCellEditor extends DefaultCellEditor {
}
}
}
} catch (ParseException ignore) {}
} catch (ParseException e) {
e.printStackTrace();
}
}
public boolean shouldSelectCell(final EventObject anEvent) {

20
core/src/main/java/com/github/weisj/darklaf/ui/table/renderer/DarkTableCellEditorDelegate.java

@ -126,14 +126,24 @@ public class DarkTableCellEditorDelegate extends TableCellEditorDelegate {
protected static void setupEditorComponent(final Component editorComponent, final Object value,
final Component rendererComp) {
if (editorComponent instanceof JSpinner) {
int alignment = getHorizontalAlignment(rendererComp);
if (alignment >= 0) {
((JComponent) editorComponent).putClientProperty(DarkSpinnerUI.KEY_EDITOR_ALIGNMENT, alignment);
}
}
if (editorComponent instanceof JTextField) {
int alignment = getHorizontalAlignment(rendererComp);
if (alignment >= 0) ((JTextField) editorComponent).setHorizontalAlignment(alignment);
}
}
protected static int getHorizontalAlignment(final Component rendererComp) {
if (rendererComp instanceof JTextField) {
((JComponent) editorComponent).putClientProperty(DarkSpinnerUI.KEY_EDITOR_ALIGNMENT,
((JTextField) rendererComp).getHorizontalAlignment());
return ((JTextField) rendererComp).getHorizontalAlignment();
} else if (rendererComp instanceof JLabel) {
((JComponent) editorComponent).putClientProperty(DarkSpinnerUI.KEY_EDITOR_ALIGNMENT,
((JLabel) rendererComp).getHorizontalAlignment());
}
return ((JLabel) rendererComp).getHorizontalAlignment();
}
return -1;
}
protected static Component applyRendererIcon(final Component component, final Component rendererComp) {

5
core/src/main/java/com/github/weisj/darklaf/ui/table/renderer/DarkTableCellEditorToggleButton.java

@ -28,8 +28,11 @@ import java.awt.*;
import java.util.EventObject;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.table.TableCellEditor;
import com.github.weisj.darklaf.ui.table.DarkTableCellBorder;
/**
* @author vincencopalazzo
* @author atarw
@ -38,6 +41,7 @@ import javax.swing.table.TableCellEditor;
public class DarkTableCellEditorToggleButton extends AbstractCellEditor implements TableCellEditor, SwingConstants {
private final JToggleButton toggleButton;
private final Border editorBorder = new DarkTableCellBorder();
public DarkTableCellEditorToggleButton(final JToggleButton toggleButton) {
this.toggleButton = toggleButton;
@ -50,6 +54,7 @@ public class DarkTableCellEditorToggleButton extends AbstractCellEditor implemen
if (value instanceof Boolean) {
toggleButton.setSelected((Boolean) value);
}
toggleButton.setBorder(editorBorder);
return toggleButton;
}

5
core/src/main/java/com/github/weisj/darklaf/ui/table/renderer/DarkTableCellRendererDelegate.java

@ -55,9 +55,8 @@ public class DarkTableCellRendererDelegate extends TableCellRendererDelegate imp
public Component getTableCellRendererComponent(final JTable table, final Object value,
final boolean isSelected, final boolean hasFocus,
final int row, final int column) {
TableCellRenderer renderer = TableConstants.useBooleanEditorForValue(value, table, column)
? getBooleanRenderer(table)
: super.getDelegate();
boolean booleanRenderer = TableConstants.useBooleanEditorForValue(value, table, column);
TableCellRenderer renderer = booleanRenderer ? getBooleanRenderer(table) : super.getDelegate();
Component component = renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
boolean isRowFocus = DarkTableCellFocusBorder.isRowFocusBorder(table);

30
core/src/main/java/com/github/weisj/darklaf/ui/tree/DarkTreeUI.java

@ -447,9 +447,11 @@ public class DarkTreeUI extends BasicTreeUI implements PropertyChangeListener, C
final int boundsX = cellBounds.x;
final int boundsWidth = cellBounds.width;
final boolean selected = tree.isPathSelected(path);
cellBounds.x = xOffset;
cellBounds.width = containerWidth;
paintRowBackground(g, paintBounds, cellBounds, path, row);
paintRowBackground(g, cellBounds, path, row, selected);
cellBounds.x = boundsX;
cellBounds.width = boundsWidth;
@ -466,24 +468,22 @@ public class DarkTreeUI extends BasicTreeUI implements PropertyChangeListener, C
hasBeenExpanded, isLeaf);
}
paintRow(g, paintBounds, insets, cellBounds, path, row, isExpanded, hasBeenExpanded, isLeaf);
if (!selected && tree.getLeadSelectionRow() == row && tree.hasFocus()) {
g.setColor(CellUtil.getTreeBackground(tree, true, row));
cellBounds.x = xOffset;
cellBounds.width = containerWidth;
PaintUtil.drawRect(g, cellBounds, leadSelectionBorderInsets);
}
return (cellBounds.y + cellBounds.height) < paintBounds.y + paintBounds.height;
}
protected void paintRowBackground(final Graphics g, final Rectangle clipBounds,
final Rectangle bounds, final TreePath path,
final int row) {
protected void paintRowBackground(final Graphics g, final Rectangle bounds, final TreePath path,
final int row, final boolean selected) {
if (path != null) {
boolean selected = tree.isPathSelected(path);
Graphics2D rowGraphics = (Graphics2D) g.create();
rowGraphics.setClip(clipBounds);
rowGraphics.setColor(CellUtil.getTreeBackground(tree, selected, row));
rowGraphics.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
if (!selected && tree.getLeadSelectionRow() == row && tree.hasFocus()) {
rowGraphics.setColor(CellUtil.getTreeBackground(tree, true, row));
PaintUtil.drawRect(rowGraphics, bounds, leadSelectionBorderInsets);
}
rowGraphics.dispose();
g.setColor(CellUtil.getTreeBackground(tree, selected, row));
g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
}
}

3
core/src/main/resources/com/github/weisj/darklaf/properties/font.properties

@ -67,3 +67,6 @@ RadioButtonMenuItem.acceleratorFont = withSize(%fontSize.accelerator)withStyle(0
Menu.acceleratorFont = withSize(%fontSize.accelerator)withStyle(0)
CheckBoxMenuItem.acceleratorFont = withSize(%fontSize.accelerator)withStyle(0)
InternalFrame.titleFont = withSize(%fontSize.title)withStyle(0)
kerning.allowList = ?:{__all__}
kerning.blockList = ?:{}

2
core/src/main/resources/com/github/weisj/darklaf/properties/platform/linux.properties

@ -28,3 +28,5 @@ FileChooser.listViewWindowsStyle = false
Table.alternateRowColor = false
Tree.alternateRowColor = false
List.alternateRowColor = false
kerning.blockList = {Table.font}

3
core/src/main/resources/com/github/weisj/darklaf/properties/platform/mac.properties

@ -40,5 +40,4 @@ ToolTip.paintShadow = false
InternalFrame.icon = navigation/arrowDown.svg[themed]
InternalFrame.useExternalMenuBar = true
fontList.kerningEnabled = {__all__}
kerning.blockList = {Table.font}

42
core/src/main/resources/com/github/weisj/darklaf/properties/platform/windows.properties

@ -32,37 +32,11 @@ Tree.alternateRowColor = false
List.alternateRowColor = false
PopupMenu.defaultLightWeightPopups.windows = false
fontList.kerningEnabled = {Button.font;\
CheckBox.font;\
CheckBoxMenuItem.font;\
ColorChooser.font;\
DesktopIcon.font;\
IconButton.font;\
Label.font;\
List.font;\
Menu.font;\
MenuBar.font;\
MenuItem.font;\
OptionPane.font;\
Panel.font;\
PopupMenu.font;\
ProgressBar.font;\
RadioButton.font;\
RadioButtonMenuItem.font;\
ScrollPane.font;\
Slider.font;\
TabbedPane.font;\
TabFrameTab.font;\
TableHeader.font;\
Table.font;\
TitledBorder.font;\
ToggleButton.font;\
ToolBar.font;\
ToolTip.font;\
Tree.font;\
Viewport.font;\
MenuItem.acceleratorFont;\
RadioButtonMenuItem.acceleratorFont;\
Menu.acceleratorFont;\
CheckBoxMenuItem.acceleratorFont;\
InternalFrame.titleFont}
kerning.blockList = {NumberingPane.font;\
FormattedTextField.font;\
EditorPane.font;\
PasswordField.font;\
TextPane.font;\
TextArea.font;\
TextField.font;\
Table.font}

2
core/src/main/resources/com/github/weisj/darklaf/properties/ui/cell.properties

@ -43,7 +43,7 @@ Cell.backgroundSelectedNoFocus = %highlightFill
Cell.inactiveBackground = %backgroundContainer
Cell.inactiveBackgroundAlternative = %backgroundAlternative
Cell.inactiveBackgroundSelected = %highlightFill
Cell.inactiveBackgroundSelected = %highlightFillFocus
Cell.inactiveBackgroundNoFocus = %backgroundContainer
Cell.inactiveBackgroundNoFocusAlternative = %backgroundAlternative
Cell.inactiveBackgroundSelectedNoFocus = %highlightFill

19
core/src/test/java/ui/table/LargeTableDemo.java

@ -24,7 +24,10 @@
*/
package ui.table;
import java.awt.*;
import javax.swing.*;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.table.AbstractTableModel;
import ui.ComponentDemo;
@ -42,7 +45,21 @@ public class LargeTableDemo implements ComponentDemo {
public JComponent createComponent() {
JPanel holder = new JPanel();
holder.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JTable table = new JTable();
JTable table = new JTable() {
@Override
public void columnMoved(final TableColumnModelEvent e) {
int fromIndex = e.getFromIndex();
int toIndex = e.getToIndex();
int start = Math.max(Math.min(fromIndex, toIndex) - 1, 0);
int end = Math.min(Math.max(fromIndex, toIndex) + 1, getColumnCount() - 1);
Rectangle visible = getVisibleRect();
for (int i = start; i <= end; i++) {
Rectangle cell = getCellRect(0, i, true);
cell.height = visible.height;
repaint(cell);
}
}
};
table.setModel(new DarkTableModel(CELL_COUNT / COLUMN_COUNT, COLUMN_COUNT));
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
holder.add(new JScrollPane(table));

54
core/src/test/java/ui/table/TableDemo.java

@ -28,14 +28,16 @@ import java.awt.*;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.*;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellEditor;
import javax.swing.table.*;
import ui.ComponentDemo;
import ui.DemoPanel;
import com.github.weisj.darklaf.components.OverlayScrollPane;
import com.github.weisj.darklaf.ui.table.DarkTableUI;
import com.github.weisj.darklaf.ui.table.renderer.DarkTableCellEditor;
import com.github.weisj.darklaf.ui.table.renderer.DarkTableCellRenderer;
import com.github.weisj.darklaf.ui.table.renderer.DarkTableCellRendererDelegate;
import com.github.weisj.darklaf.util.PropertyKey;
import com.github.weisj.darklaf.util.PropertyUtil;
@ -47,15 +49,47 @@ public class TableDemo implements ComponentDemo {
@Override
public JComponent createComponent() {
String[] columns = new String[]{"Id", "Name", "Hourly Rate", "Part Time", "Components"};
String[] columns = {"Id", "Name", "Hourly Rate", "Part Time", "Components"};
Object[][] data = new Object[][]{{1, "John", 40.0, false, "Item"},
Object[][] data = {{1, "John", 40.0, false, "Item"},
{2, "Rambo", 70.0, false, 10},
{3, "Zorro", 60.0, true, "cell"}};
Class<?>[] columnClasses = {Integer.class, String.class, Double.class, Boolean.class, Object.class};
AtomicBoolean editable = new AtomicBoolean(true);
JTable table = new JTable(data, columns) {
final Class<?>[] columnClasses = {Integer.class, String.class, Double.class, Boolean.class, Object.class};
TableModel model = new DefaultTableModel() {
@Override
public int getColumnCount() {
return columns.length;
}
@Override
public int getRowCount() {
return 40;
}
@Override
public Class<?> getColumnClass(final int columnIndex) {
return columnIndex < columnClasses.length ? columnClasses[columnIndex] : Object.class;
}
@Override
public Object getValueAt(final int row, final int column) {
return data[Math.min(row, data.length - 1)][Math.min(column, columns.length - 1)];
}
@Override
public void setValueAt(final Object aValue, final int row, final int column) {
data[Math.min(row, data.length - 1)][Math.min(column, columns.length - 1)] = aValue;
}
@Override
public String getColumnName(final int column) {
return columns[column];
}
};
JTable table = new JTable(model) {
final TableCellEditor comboEditor = new DarkTableCellEditor(new JComboBox<>());
final TableCellEditor spinnerEditor = new DarkTableCellEditor(new JSpinner());
@ -75,15 +109,17 @@ public class TableDemo implements ComponentDemo {
}
}
private final TableCellRenderer renderer = new DarkTableCellRendererDelegate(new DarkTableCellRenderer());
@Override
public Class<?> getColumnClass(final int column) {
return columnClasses[column];
public TableCellRenderer getCellRenderer(final int row, final int column) {
return renderer;
}
};
JTableHeader header = table.getTableHeader();
JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setBorder(BorderFactory.createEmptyBorder());
DemoPanel panel = new DemoPanel(scrollPane, new BorderLayout(), 10);
DemoPanel panel = new DemoPanel(new OverlayScrollPane(scrollPane), new BorderLayout(), 10);
JPanel controlPanel = panel.addControls(3);
controlPanel.add(new JCheckBox("enabled") {

43
property-loader/src/main/java/com/github/weisj/darklaf/PropertyLoader.java

@ -46,7 +46,10 @@ import com.github.weisj.darklaf.icons.IconLoader;
import com.github.weisj.darklaf.icons.StateIcon;
import com.github.weisj.darklaf.uiresource.DarkColorUIResource;
import com.github.weisj.darklaf.uiresource.DarkFontUIResource;
import com.github.weisj.darklaf.util.*;
import com.github.weisj.darklaf.util.ColorUtil;
import com.github.weisj.darklaf.util.LogUtil;
import com.github.weisj.darklaf.util.Pair;
import com.github.weisj.darklaf.util.PropertyValue;
/**
* @author Konstantin Bulenkov
@ -63,6 +66,7 @@ public final class PropertyLoader {
private static final String ICON_EMPTY = "empty";
private static final char REFERENCE_PREFIX = '%';
private static final String FALLBACK_PREFIX = "?:";
private static final String FONT_FROM = "from";
private static final String FONT_SIZE = "withSize";
@ -126,6 +130,8 @@ public final class PropertyLoader {
final String value = properties.get(key).toString();
Object parsed = parseValue(key, value, accumulator, currentDefaults, iconLoader);
if (parsed != null) {
String k = parseKey(key);
if (parsed instanceof FallbackValue && accumulator.containsKey(k)) continue;
accumulator.put(parseKey(key), parsed);
} else {
currentDefaults.remove(parseKey(key));
@ -168,18 +174,21 @@ public final class PropertyLoader {
return key.startsWith(String.valueOf(REFERENCE_PREFIX)) ? key.substring(1) : key;
}
public static Object parseValue(final String propertyKey, final String value,
public static Object parseValue(final String propertyKey, final String val,
final Map<Object, Object> accumulator,
final UIDefaults currentDefaults, final IconLoader iconLoader) {
if (value == null || PropertyValue.NULL.equals(value)) {
if (val == null || PropertyValue.NULL.equals(val)) {
return null;
}
String key = propertyKey;
boolean isFallback = val.startsWith(FALLBACK_PREFIX);
String value = !isFallback ? val : val.substring(FALLBACK_PREFIX.length());
boolean skipObjects = false;
if (key.startsWith(String.valueOf(REFERENCE_PREFIX))) {
key = parseKey(key);
skipObjects = true;
}
key = parseKey(key);
final Color color = ColorUtil.fromHex(value, null);
final Integer invVal = getInteger(value);
@ -187,11 +196,11 @@ public final class PropertyLoader {
? Boolean.TRUE
: PropertyValue.FALSE.equalsIgnoreCase(value) ? Boolean.FALSE : null;
if (color != null && (value.length() == 6 || value.length() == 8)) {
return new DarkColorUIResource(color);
return maybeWrap(new DarkColorUIResource(color), isFallback);
} else if (invVal != null) {
return invVal;
return maybeWrap(invVal, isFallback);
} else if (boolVal != null) {
return boolVal;
return maybeWrap(boolVal, isFallback);
}
Object returnVal = new LoadError();
@ -201,9 +210,9 @@ public final class PropertyLoader {
&& (key.endsWith("Border")
|| key.endsWith(".border")
|| key.endsWith("Renderer"))) {
return (UIDefaults.LazyValue) def -> parseObject(value);
return maybeWrap((UIDefaults.LazyValue) def -> parseObject(value), isFallback);
} else if (key.endsWith(".component") || key.endsWith("Component")) {
return (UIDefaults.ActiveValue) (def) -> parseObject(value);
return maybeWrap((UIDefaults.ActiveValue) (def) -> parseObject(value), isFallback);
} else if (key.toLowerCase().endsWith("font")) {
returnVal = parseFont(key, value, accumulator, currentDefaults);
} else if (key.endsWith(".icon") || key.endsWith("Icon") || key.endsWith("Image")) {
@ -226,8 +235,12 @@ public final class PropertyLoader {
} else if (value.startsWith(String.valueOf(REFERENCE_PREFIX))) {
returnVal = parseReference(key, value, accumulator, currentDefaults);
}
if (!(returnVal instanceof LoadError)) return returnVal;
return value;
if (!(returnVal instanceof LoadError)) return maybeWrap(returnVal, isFallback);
return maybeWrap(value, isFallback);
}
private static Object maybeWrap(final Object value, final boolean isDefaultValue) {
return !isDefaultValue ? value : new FallbackValue(value);
}
private static <T> Pair<T, T> parsePair(final ParseFunction<T> mapper,
@ -524,4 +537,12 @@ public final class PropertyLoader {
return getSecond();
}
}
private static class FallbackValue {
private final Object value;
private FallbackValue(final Object value) {
this.value = value;
}
}
}

Loading…
Cancel
Save