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_PROPERTY_PATH = "properties/";
private static final String FONT_SIZE_DEFAULTS_NAME = "font_sizes"; private static final String FONT_SIZE_DEFAULTS_NAME = "font_sizes";
private static final String FONT_DEFAULTS_NAME = "font"; 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__"; private static final String ALL_FONTS = "__all__";
@ -85,19 +86,23 @@ public class FontDefaultsInitTask implements DefaultsInitTask {
} }
if (systemKerningEnabled()) { 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()) { if (!allowedFonts.isEmpty()) {
Set<String> kerningFonts = new HashSet<>(kerningFontsList); Set<String> allowedFontsSet = new HashSet<>(allowedFonts);
boolean enabledAll = ALL_FONTS.equals(kerningFontsList.get(0)); 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); applyFontRule(currentTheme, defaults);
setupRenderingHints(defaults); setupRenderingHints(defaults);
defaults.remove(KERNING_LIST); defaults.remove(KERNING_ALLOW_LIST);
defaults.remove(KERNING_BLOCK_LIST);
} }
private boolean systemKerningEnabled() { 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 java.awt.*;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.table.TableCellRenderer; import javax.swing.table.TableCellRenderer;
import javax.swing.tree.TreeCellRenderer; import javax.swing.tree.TreeCellRenderer;
@ -41,6 +42,7 @@ public class DarkCellRendererToggleButton<T extends JToggleButton & CellEditorTo
implements TableCellRenderer, TreeCellRenderer, SwingConstants { implements TableCellRenderer, TreeCellRenderer, SwingConstants {
private final T toggleButton; private final T toggleButton;
private final Border border = new DarkCellBorder();
public DarkCellRendererToggleButton(final T toggleButton) { public DarkCellRendererToggleButton(final T toggleButton) {
this.toggleButton = toggleButton; this.toggleButton = toggleButton;
@ -54,6 +56,7 @@ public class DarkCellRendererToggleButton<T extends JToggleButton & CellEditorTo
toggleButton.setSelected((Boolean) value); toggleButton.setSelected((Boolean) value);
} }
toggleButton.setHasFocus(focus); toggleButton.setHasFocus(focus);
toggleButton.setBorder(border);
return toggleButton; return toggleButton;
} }
@ -85,6 +88,7 @@ public class DarkCellRendererToggleButton<T extends JToggleButton & CellEditorTo
public CellCheckBox(final boolean opaque) { public CellCheckBox(final boolean opaque) {
setOpaque(opaque); setOpaque(opaque);
setHorizontalAlignment(CENTER);
putClientProperty(ToggleButtonConstants.KEY_IS_TREE_EDITOR, true); putClientProperty(ToggleButtonConstants.KEY_IS_TREE_EDITOR, true);
putClientProperty(ToggleButtonConstants.KEY_IS_TABLE_EDITOR, true); putClientProperty(ToggleButtonConstants.KEY_IS_TABLE_EDITOR, true);
putClientProperty(ToggleButtonConstants.KEY_VERTICAL_ICON_OFFSET, 0); putClientProperty(ToggleButtonConstants.KEY_VERTICAL_ICON_OFFSET, 0);
@ -111,6 +115,7 @@ public class DarkCellRendererToggleButton<T extends JToggleButton & CellEditorTo
public CellRadioButton(final boolean opaque) { public CellRadioButton(final boolean opaque) {
setOpaque(opaque); setOpaque(opaque);
setHorizontalAlignment(CENTER);
putClientProperty(ToggleButtonConstants.KEY_IS_TREE_EDITOR, true); putClientProperty(ToggleButtonConstants.KEY_IS_TREE_EDITOR, true);
putClientProperty(ToggleButtonConstants.KEY_IS_TABLE_EDITOR, true); putClientProperty(ToggleButtonConstants.KEY_IS_TABLE_EDITOR, true);
putClientProperty(ToggleButtonConstants.KEY_VERTICAL_ICON_OFFSET, 0); 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(); Component renderer = cellHintPopupListener.getRenderer();
if (rendererBounds != null && renderer != null) { if (rendererBounds != null && renderer != null) {
Color bg = cellHintPopupListener.getBackground(renderer); 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.setColor(bg);
g.fillRect(0, 0, getWidth(), getHeight()); g.fillRect(0, 0, getWidth(), getHeight());
}
g.translate(-rendererBounds.x, -rendererBounds.y); g.translate(-rendererBounds.x, -rendererBounds.y);
// If the renderer is an editor we need to restore the bounds. // 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); 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) { default Component getEffectiveCellRendererComponent(final I position, final boolean isEditing) {
if (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 sun.swing.SwingUtilities2;
import com.github.weisj.darklaf.components.OverlayScrollPane; 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.CellUtil;
import com.github.weisj.darklaf.ui.cell.DarkCellRendererPane; 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.DarkUIUtil;
import com.github.weisj.darklaf.util.PropertyKey; import com.github.weisj.darklaf.util.PropertyKey;
import com.github.weisj.darklaf.util.PropertyUtil; import com.github.weisj.darklaf.util.PropertyUtil;
@ -205,8 +209,9 @@ public class DarkTableUI extends DarkTableUIBridge implements TableConstants {
protected boolean scrollBarVisible() { protected boolean scrollBarVisible() {
JScrollPane comp = DarkUIUtil.getParentOfType(JScrollPane.class, table, 2); JScrollPane comp = DarkUIUtil.getParentOfType(JScrollPane.class, table, 2);
if (comp == null) return false;
OverlayScrollPane overlayScrollPane = DarkUIUtil.getParentOfType(OverlayScrollPane.class, table, 3); 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, 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; int tableHeight = getPreferredSize(table).height;
g.setColor(parent.getBackground()); 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. // Move to the where the cell has been dragged.
vacatedColumnRect.x += dist; vacatedColumnRect.x += dist;
g.setColor(Color.RED);
PaintUtil.drawRect(g, vacatedColumnRect);
boolean ltr = table.getComponentOrientation().isLeftToRight(); boolean ltr = table.getComponentOrientation().isLeftToRight();
// Fill the background. // Fill the background.
@ -315,9 +326,8 @@ public class DarkTableUI extends DarkTableUIBridge implements TableConstants {
// Render the cell value // Render the cell value
Rectangle r = table.getCellRect(row, draggedColumnIndex, false); Rectangle r = table.getCellRect(row, draggedColumnIndex, false);
r.x += dist; 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()) { if (table.getShowHorizontalLines()) {
g.setColor(table.getGridColor()); g.setColor(table.getGridColor());
Rectangle rcr = table.getCellRect(row, draggedColumnIndex, true); 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) { public static boolean ignoreKeyCodeOnEdit(final KeyEvent event, final JTable table) {
if (event != null) { if (event != null) {
int keyCode = event.getKeyCode(); int keyCode = event.getKeyCode();
@ -364,74 +392,43 @@ public class DarkTableUI extends DarkTableUIBridge implements TableConstants {
return false; return false;
} }
@Override protected void paintCell(final Graphics g, final Rectangle r, final int row, final int column,
protected void paintCell(final Graphics g, final Rectangle cellRect, final int row, final int column) { final int cMin, final int cMax) {
Rectangle bounds = table.getVisibleRect(); // if (true) return;
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();
boolean isEditorCell = table.isEditing() && table.getEditingRow() == row && table.getEditingColumn() == column; boolean isEditorCell = table.isEditing() && table.getEditingRow() == row && table.getEditingColumn() == column;
JTableHeader header = table.getTableHeader(); int x = r.x;
int draggedIndex = header != null ? viewIndexForColumn(header.getDraggedColumn()) int y = r.y;
: -1; int w = r.width;
int dist = header != null ? adjustDistance(header.getDraggedDistance(), int h = r.height;
table.getCellRect(row, draggedIndex, true),
table) if (table.getShowVerticalLines() && !scrollBarVisible()) {
: 0; if (column == table.getColumnCount() - 1) {
boolean isDragged = column == draggedIndex && dist != 0; w++;
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;
}
} }
} }
if (isEditorCell) { if (isEditorCell) {
if (!table.getShowVerticalLines()) { if (!table.getShowVerticalLines()) {
if (column > cMin) r.x -= 1; if (column > cMin) x--;
if (column > cMin && column < cMax) r.width += 1; if (column > cMin && column < cMax) w++;
} }
} }
if (isEditorCell) { if (isEditorCell) {
Component component = getCellEditorComponent(); Component component = getCellEditorComponent();
component.setBounds(r); component.setBounds(x, y, w, h);
component.validate(); component.validate();
} else { } else {
TableCellRenderer renderer = getCellRenderer(row, column); TableCellRenderer renderer = getCellRenderer(row, column);
Component component = table.prepareRenderer(renderer, row, column); Component component = table.prepareRenderer(renderer, row, column);
CellUtil.setSelectedFlag(component, table.isCellSelected(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) { protected TableCellRenderer getCellRenderer(final int row, final int column) {
TableCellRenderer renderer = table.getCellRenderer(row, column); TableCellRenderer renderer = table.getCellRenderer(row, column);
if (renderer instanceof DarkTableCellRendererDelegate) return renderer;
if (rendererDelegate == null) { if (rendererDelegate == null) {
rendererDelegate = new DarkTableCellRendererDelegate(renderer); rendererDelegate = new DarkTableCellRendererDelegate(renderer);
} }
@ -455,7 +452,7 @@ public class DarkTableUI extends DarkTableUIBridge implements TableConstants {
final JTable comp) { final JTable comp) {
int dist = distance; int dist = distance;
int min = 0; int min = 0;
int max = comp.getX() + comp.getWidth(); int max = comp.getWidth();
if (rect.x + dist <= min) { if (rect.x + dist <= min) {
dist = min - rect.x; 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() { protected JFileChooser getFileChooser() {
Object obj = PropertyUtil.getObject(table, DarkTableUI.KEY_FILE_CHOOSER_PARENT, Supplier.class, Object::new) Object obj = PropertyUtil.getObject(table, DarkTableUI.KEY_FILE_CHOOSER_PARENT, Supplier.class, Object::new)
.get(); .get();
@ -540,7 +529,8 @@ public class DarkTableUI extends DarkTableUIBridge implements TableConstants {
table.setRowMargin(b ? 1 : 0); table.setRowMargin(b ? 1 : 0);
} else if (KEY_VERTICAL_LINES.equals(key)) { } else if (KEY_VERTICAL_LINES.equals(key)) {
boolean b = Boolean.TRUE.equals(e.getNewValue()); 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)) { } else if (PropertyKey.ANCESTOR.equals(key)) {
Object oldVal = e.getOldValue(); Object oldVal = e.getOldValue();
Object newVal = e.getNewValue(); 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(); columnWidth = aColumn.getWidth();
cellRect.width = columnWidth - columnMargin; cellRect.width = columnWidth - columnMargin;
if (aColumn != draggedColumn) { if (aColumn != draggedColumn) {
paintCell(g, cellRect, row, column); paintCell(g, cellRect, row, column, rMin, rMax);
} }
cellRect.x += columnWidth; cellRect.x += columnWidth;
} }
@ -227,7 +227,7 @@ public abstract class DarkTableUIBridge extends TableUIBridge {
if (aColumn != draggedColumn) { if (aColumn != draggedColumn) {
columnWidth = aColumn.getWidth(); columnWidth = aColumn.getWidth();
cellRect.width = columnWidth - columnMargin; cellRect.width = columnWidth - columnMargin;
paintCell(g, cellRect, row, cMin); paintCell(g, cellRect, row, cMin, cMin, cMax);
} }
for (int column = cMin + 1; column <= cMax; column++) { for (int column = cMin + 1; column <= cMax; column++) {
aColumn = cm.getColumn(column); aColumn = cm.getColumn(column);
@ -235,7 +235,7 @@ public abstract class DarkTableUIBridge extends TableUIBridge {
cellRect.width = columnWidth - columnMargin; cellRect.width = columnWidth - columnMargin;
cellRect.x -= columnWidth; cellRect.x -= columnWidth;
if (aColumn != draggedColumn) { 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(); rendererPane.removeAll();
} }
@Override @Deprecated
protected abstract void paintCell(final Graphics g, final Rectangle cellRect, final int row, final int column); 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 @Override
protected int viewIndexForColumn(final TableColumn aColumn) { 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 Color background;
protected int defaultHeight; protected int defaultHeight;
protected DarkTableHeaderRendererDelegate rendererDelegate; protected DarkTableHeaderRendererDelegate rendererDelegate;
private int lastMaxVisible = -1;
public static ComponentUI createUI(final JComponent c) { public static ComponentUI createUI(final JComponent c) {
return new DarkTableHeaderUI(); return new DarkTableHeaderUI();
@ -110,21 +111,28 @@ public class DarkTableHeaderUI extends BasicTableHeaderUI {
boolean ltr = header.getComponentOrientation().isLeftToRight(); boolean ltr = header.getComponentOrientation().isLeftToRight();
Rectangle clip = g.getClipBounds(); Rectangle clip = g.getClipBounds();
Point left = clip.getLocation(); Point leftClip = clip.getLocation();
Point right = new Point(clip.x + clip.width - 1, clip.y); Point rightCip = new Point(clip.x + clip.width - 1, clip.y);
TableColumnModel cm = header.getColumnModel(); TableColumnModel cm = header.getColumnModel();
int cMin = header.columnAtPoint(ltr ? left : right); int cMin = Math.max(header.columnAtPoint(ltr ? leftClip : rightCip), 0);
int cMax = header.columnAtPoint(ltr ? right : left); int cMax = header.columnAtPoint(ltr ? rightCip : leftClip);
// This should never happen.
if (cMin == -1) {
cMin = 0;
}
// If the table does not have enough columns to fill the view we'll get -1. // 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. // Replace this with the index of the last column.
if (cMax == -1) { if (cMax == -1) {
cMax = cm.getColumnCount() - 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 Color borderColor = c.getBorder() instanceof DarkTableScrollPaneBorder
? ((DarkTableScrollPaneBorder) c.getBorder()).getBorderColor() ? ((DarkTableScrollPaneBorder) c.getBorder()).getBorderColor()
: getBorderColor(); : getBorderColor();
@ -132,7 +140,7 @@ public class DarkTableHeaderUI extends BasicTableHeaderUI {
TableColumn draggedColumn = header.getDraggedColumn(); TableColumn draggedColumn = header.getDraggedColumn();
Rectangle cellRect = header.getHeaderRect(ltr ? cMin : cMax); 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. // Paint the dragged column if we are dragging.
if (draggedColumn != null) { if (draggedColumn != null) {
@ -142,26 +150,42 @@ public class DarkTableHeaderUI extends BasicTableHeaderUI {
// Remove all components in the rendererPane. // Remove all components in the rendererPane.
rendererPane.removeAll(); rendererPane.removeAll();
config.restore(); 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, public void paintCells(final Graphics2D g, final int h, final boolean ltr,
final TableColumnModel cm, 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) { final TableColumn draggedColumn, final Rectangle cellRect) {
if (ltr) { if (ltr) {
for (int column = cMin; column <= cMax; column++) { 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 { } else {
for (int column = cMax; column >= cMin; column--) { 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, public void paintSingleCell(final Graphics2D g, final int h, final TableColumnModel cm,
final int cMax, final Color borderColor, final int cMinVisible, final int cMaxVisible,
final TableColumn draggedColumn, final Color borderColor, final TableColumn draggedColumn,
final Rectangle cellRect, final int column) { final Rectangle cellRect, final int column) {
TableColumn aColumn; TableColumn aColumn;
int columnWidth; int columnWidth;
@ -171,11 +195,11 @@ public class DarkTableHeaderUI extends BasicTableHeaderUI {
if (aColumn != draggedColumn) { if (aColumn != draggedColumn) {
paintCell(g, cellRect, column); paintCell(g, cellRect, column);
} }
cellRect.x += columnWidth;
if (column != cMax) {
g.setColor(borderColor); 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, 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 dateEditor.get();
} }
} }
return getDelegate(); return null;
} }
@Override @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) { 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, protected static void setupEditorComponent(final Component editorComponent, final Object value,
final Component rendererComp) { final Component rendererComp) {
if (editorComponent instanceof JSpinner) { 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) { if (rendererComp instanceof JTextField) {
((JComponent) editorComponent).putClientProperty(DarkSpinnerUI.KEY_EDITOR_ALIGNMENT, return ((JTextField) rendererComp).getHorizontalAlignment();
((JTextField) rendererComp).getHorizontalAlignment());
} else if (rendererComp instanceof JLabel) { } else if (rendererComp instanceof JLabel) {
((JComponent) editorComponent).putClientProperty(DarkSpinnerUI.KEY_EDITOR_ALIGNMENT, return ((JLabel) rendererComp).getHorizontalAlignment();
((JLabel) rendererComp).getHorizontalAlignment());
}
} }
return -1;
} }
protected static Component applyRendererIcon(final Component component, final Component rendererComp) { 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 java.util.EventObject;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellEditor;
import com.github.weisj.darklaf.ui.table.DarkTableCellBorder;
/** /**
* @author vincencopalazzo * @author vincencopalazzo
* @author atarw * @author atarw
@ -38,6 +41,7 @@ import javax.swing.table.TableCellEditor;
public class DarkTableCellEditorToggleButton extends AbstractCellEditor implements TableCellEditor, SwingConstants { public class DarkTableCellEditorToggleButton extends AbstractCellEditor implements TableCellEditor, SwingConstants {
private final JToggleButton toggleButton; private final JToggleButton toggleButton;
private final Border editorBorder = new DarkTableCellBorder();
public DarkTableCellEditorToggleButton(final JToggleButton toggleButton) { public DarkTableCellEditorToggleButton(final JToggleButton toggleButton) {
this.toggleButton = toggleButton; this.toggleButton = toggleButton;
@ -50,6 +54,7 @@ public class DarkTableCellEditorToggleButton extends AbstractCellEditor implemen
if (value instanceof Boolean) { if (value instanceof Boolean) {
toggleButton.setSelected((Boolean) value); toggleButton.setSelected((Boolean) value);
} }
toggleButton.setBorder(editorBorder);
return toggleButton; 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, public Component getTableCellRendererComponent(final JTable table, final Object value,
final boolean isSelected, final boolean hasFocus, final boolean isSelected, final boolean hasFocus,
final int row, final int column) { final int row, final int column) {
TableCellRenderer renderer = TableConstants.useBooleanEditorForValue(value, table, column) boolean booleanRenderer = TableConstants.useBooleanEditorForValue(value, table, column);
? getBooleanRenderer(table) TableCellRenderer renderer = booleanRenderer ? getBooleanRenderer(table) : super.getDelegate();
: super.getDelegate();
Component component = renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); Component component = renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
boolean isRowFocus = DarkTableCellFocusBorder.isRowFocusBorder(table); 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 boundsX = cellBounds.x;
final int boundsWidth = cellBounds.width; final int boundsWidth = cellBounds.width;
final boolean selected = tree.isPathSelected(path);
cellBounds.x = xOffset; cellBounds.x = xOffset;
cellBounds.width = containerWidth; cellBounds.width = containerWidth;
paintRowBackground(g, paintBounds, cellBounds, path, row); paintRowBackground(g, cellBounds, path, row, selected);
cellBounds.x = boundsX; cellBounds.x = boundsX;
cellBounds.width = boundsWidth; cellBounds.width = boundsWidth;
@ -466,24 +468,22 @@ public class DarkTreeUI extends BasicTreeUI implements PropertyChangeListener, C
hasBeenExpanded, isLeaf); hasBeenExpanded, isLeaf);
} }
paintRow(g, paintBounds, insets, cellBounds, path, row, isExpanded, 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; return (cellBounds.y + cellBounds.height) < paintBounds.y + paintBounds.height;
} }
protected void paintRowBackground(final Graphics g, final Rectangle clipBounds, protected void paintRowBackground(final Graphics g, final Rectangle bounds, final TreePath path,
final Rectangle bounds, final TreePath path, final int row, final boolean selected) {
final int row) {
if (path != null) { if (path != null) {
boolean selected = tree.isPathSelected(path); g.setColor(CellUtil.getTreeBackground(tree, selected, row));
Graphics2D rowGraphics = (Graphics2D) g.create(); g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
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();
} }
} }

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) Menu.acceleratorFont = withSize(%fontSize.accelerator)withStyle(0)
CheckBoxMenuItem.acceleratorFont = withSize(%fontSize.accelerator)withStyle(0) CheckBoxMenuItem.acceleratorFont = withSize(%fontSize.accelerator)withStyle(0)
InternalFrame.titleFont = withSize(%fontSize.title)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 Table.alternateRowColor = false
Tree.alternateRowColor = false Tree.alternateRowColor = false
List.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.icon = navigation/arrowDown.svg[themed]
InternalFrame.useExternalMenuBar = true InternalFrame.useExternalMenuBar = true
kerning.blockList = {Table.font}
fontList.kerningEnabled = {__all__}

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

@ -32,37 +32,11 @@ Tree.alternateRowColor = false
List.alternateRowColor = false List.alternateRowColor = false
PopupMenu.defaultLightWeightPopups.windows = false PopupMenu.defaultLightWeightPopups.windows = false
fontList.kerningEnabled = {Button.font;\ kerning.blockList = {NumberingPane.font;\
CheckBox.font;\ FormattedTextField.font;\
CheckBoxMenuItem.font;\ EditorPane.font;\
ColorChooser.font;\ PasswordField.font;\
DesktopIcon.font;\ TextPane.font;\
IconButton.font;\ TextArea.font;\
Label.font;\ TextField.font;\
List.font;\ Table.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}

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

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

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

@ -24,7 +24,10 @@
*/ */
package ui.table; package ui.table;
import java.awt.*;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import ui.ComponentDemo; import ui.ComponentDemo;
@ -42,7 +45,21 @@ public class LargeTableDemo implements ComponentDemo {
public JComponent createComponent() { public JComponent createComponent() {
JPanel holder = new JPanel(); JPanel holder = new JPanel();
holder.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 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.setModel(new DarkTableModel(CELL_COUNT / COLUMN_COUNT, COLUMN_COUNT));
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
holder.add(new JScrollPane(table)); 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 java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.*; import javax.swing.*;
import javax.swing.table.JTableHeader; import javax.swing.table.*;
import javax.swing.table.TableCellEditor;
import ui.ComponentDemo; import ui.ComponentDemo;
import ui.DemoPanel; 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.DarkTableUI;
import com.github.weisj.darklaf.ui.table.renderer.DarkTableCellEditor; 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.PropertyKey;
import com.github.weisj.darklaf.util.PropertyUtil; import com.github.weisj.darklaf.util.PropertyUtil;
@ -47,15 +49,47 @@ public class TableDemo implements ComponentDemo {
@Override @Override
public JComponent createComponent() { 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}, {2, "Rambo", 70.0, false, 10},
{3, "Zorro", 60.0, true, "cell"}}; {3, "Zorro", 60.0, true, "cell"}};
Class<?>[] columnClasses = {Integer.class, String.class, Double.class, Boolean.class, Object.class};
AtomicBoolean editable = new AtomicBoolean(true); AtomicBoolean editable = new AtomicBoolean(true);
JTable table = new JTable(data, columns) { TableModel model = new DefaultTableModel() {
final Class<?>[] columnClasses = {Integer.class, String.class, Double.class, Boolean.class, Object.class};
@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 comboEditor = new DarkTableCellEditor(new JComboBox<>());
final TableCellEditor spinnerEditor = new DarkTableCellEditor(new JSpinner()); 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 @Override
public Class<?> getColumnClass(final int column) { public TableCellRenderer getCellRenderer(final int row, final int column) {
return columnClasses[column]; return renderer;
} }
}; };
JTableHeader header = table.getTableHeader(); JTableHeader header = table.getTableHeader();
JScrollPane scrollPane = new JScrollPane(table); JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setBorder(BorderFactory.createEmptyBorder()); 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); JPanel controlPanel = panel.addControls(3);
controlPanel.add(new JCheckBox("enabled") { 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.icons.StateIcon;
import com.github.weisj.darklaf.uiresource.DarkColorUIResource; import com.github.weisj.darklaf.uiresource.DarkColorUIResource;
import com.github.weisj.darklaf.uiresource.DarkFontUIResource; 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 * @author Konstantin Bulenkov
@ -63,6 +66,7 @@ public final class PropertyLoader {
private static final String ICON_EMPTY = "empty"; private static final String ICON_EMPTY = "empty";
private static final char REFERENCE_PREFIX = '%'; private static final char REFERENCE_PREFIX = '%';
private static final String FALLBACK_PREFIX = "?:";
private static final String FONT_FROM = "from"; private static final String FONT_FROM = "from";
private static final String FONT_SIZE = "withSize"; private static final String FONT_SIZE = "withSize";
@ -126,6 +130,8 @@ public final class PropertyLoader {
final String value = properties.get(key).toString(); final String value = properties.get(key).toString();
Object parsed = parseValue(key, value, accumulator, currentDefaults, iconLoader); Object parsed = parseValue(key, value, accumulator, currentDefaults, iconLoader);
if (parsed != null) { if (parsed != null) {
String k = parseKey(key);
if (parsed instanceof FallbackValue && accumulator.containsKey(k)) continue;
accumulator.put(parseKey(key), parsed); accumulator.put(parseKey(key), parsed);
} else { } else {
currentDefaults.remove(parseKey(key)); currentDefaults.remove(parseKey(key));
@ -168,18 +174,21 @@ public final class PropertyLoader {
return key.startsWith(String.valueOf(REFERENCE_PREFIX)) ? key.substring(1) : key; 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 Map<Object, Object> accumulator,
final UIDefaults currentDefaults, final IconLoader iconLoader) { final UIDefaults currentDefaults, final IconLoader iconLoader) {
if (value == null || PropertyValue.NULL.equals(value)) { if (val == null || PropertyValue.NULL.equals(val)) {
return null; return null;
} }
String key = propertyKey; String key = propertyKey;
boolean isFallback = val.startsWith(FALLBACK_PREFIX);
String value = !isFallback ? val : val.substring(FALLBACK_PREFIX.length());
boolean skipObjects = false; boolean skipObjects = false;
if (key.startsWith(String.valueOf(REFERENCE_PREFIX))) { if (key.startsWith(String.valueOf(REFERENCE_PREFIX))) {
key = parseKey(key);
skipObjects = true; skipObjects = true;
} }
key = parseKey(key);
final Color color = ColorUtil.fromHex(value, null); final Color color = ColorUtil.fromHex(value, null);
final Integer invVal = getInteger(value); final Integer invVal = getInteger(value);
@ -187,11 +196,11 @@ public final class PropertyLoader {
? Boolean.TRUE ? Boolean.TRUE
: PropertyValue.FALSE.equalsIgnoreCase(value) ? Boolean.FALSE : null; : PropertyValue.FALSE.equalsIgnoreCase(value) ? Boolean.FALSE : null;
if (color != null && (value.length() == 6 || value.length() == 8)) { if (color != null && (value.length() == 6 || value.length() == 8)) {
return new DarkColorUIResource(color); return maybeWrap(new DarkColorUIResource(color), isFallback);
} else if (invVal != null) { } else if (invVal != null) {
return invVal; return maybeWrap(invVal, isFallback);
} else if (boolVal != null) { } else if (boolVal != null) {
return boolVal; return maybeWrap(boolVal, isFallback);
} }
Object returnVal = new LoadError(); Object returnVal = new LoadError();
@ -201,9 +210,9 @@ public final class PropertyLoader {
&& (key.endsWith("Border") && (key.endsWith("Border")
|| key.endsWith(".border") || key.endsWith(".border")
|| key.endsWith("Renderer"))) { || 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")) { } 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")) { } else if (key.toLowerCase().endsWith("font")) {
returnVal = parseFont(key, value, accumulator, currentDefaults); returnVal = parseFont(key, value, accumulator, currentDefaults);
} else if (key.endsWith(".icon") || key.endsWith("Icon") || key.endsWith("Image")) { } 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))) { } else if (value.startsWith(String.valueOf(REFERENCE_PREFIX))) {
returnVal = parseReference(key, value, accumulator, currentDefaults); returnVal = parseReference(key, value, accumulator, currentDefaults);
} }
if (!(returnVal instanceof LoadError)) return returnVal; if (!(returnVal instanceof LoadError)) return maybeWrap(returnVal, isFallback);
return value; 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, private static <T> Pair<T, T> parsePair(final ParseFunction<T> mapper,
@ -524,4 +537,12 @@ public final class PropertyLoader {
return getSecond(); return getSecond();
} }
} }
private static class FallbackValue {
private final Object value;
private FallbackValue(final Object value) {
this.value = value;
}
}
} }

Loading…
Cancel
Save