Browse Source

Fixed an issue where the cursor wasn't correctly displayed in OverlayScrollPanes.

Added convenience class for tooltip alignment.
Fixed an issue where clicking on a JToggleButton would not be recognized.
Fixed an issue where JToggleButtons were too wide.
Fixed an issue where the arrowIcon wouldn't be displayed in menus.
Added ui for fileChooser.
Fixed issue where the tooltip border was too small.
pull/15/head
weisj 5 years ago
parent
commit
86215082cb
  1. 3
      build.gradle
  2. 99
      src/main/java/com/weis/darklaf/components/OverlayScrollPane.java
  3. 46
      src/main/java/com/weis/darklaf/components/ShadowButton.java
  4. 68
      src/main/java/com/weis/darklaf/components/TooltipAwareButton.java
  5. 68
      src/main/java/com/weis/darklaf/components/TooltipAwareToggleButton.java
  6. 77
      src/main/java/com/weis/darklaf/ui/button/DarkToggleButtonUI.java
  7. 2
      src/main/java/com/weis/darklaf/ui/checkbox/DarkCheckBoxMenuItemUI.java
  8. 41
      src/main/java/com/weis/darklaf/ui/filechooser/DarkFileChooserListViewBorder.java
  9. 294
      src/main/java/com/weis/darklaf/ui/filechooser/DarkFileChooserUI.java
  10. 1390
      src/main/java/com/weis/darklaf/ui/filechooser/DarkFileChooserUIBridge.java
  11. 434
      src/main/java/com/weis/darklaf/ui/filechooser/DarkFilePane.java
  12. 2060
      src/main/java/com/weis/darklaf/ui/filechooser/DarkFilePaneUIBridge.java
  13. 41
      src/main/java/com/weis/darklaf/ui/list/DarkListCellFocusBorder.java
  14. 44
      src/main/java/com/weis/darklaf/ui/list/DarkListCellRenderer.java
  15. 103
      src/main/java/com/weis/darklaf/ui/list/DarkListUI.java
  16. 2257
      src/main/java/com/weis/darklaf/ui/list/DarkListUIBridge.java
  17. 238
      src/main/java/com/weis/darklaf/ui/menu/DarkMenuItemUIBase.java
  18. 805
      src/main/java/com/weis/darklaf/ui/menu/DarkMenuUI.java
  19. 26
      src/main/java/com/weis/darklaf/ui/scrollpane/DarkScrollBarUI.java
  20. 1
      src/main/java/com/weis/darklaf/ui/tabframe/DarkTabFrameTabLabelUI.java
  21. 2189
      src/main/java/com/weis/darklaf/ui/table/BasicTableUIBridge.java
  22. 78
      src/main/java/com/weis/darklaf/ui/table/DarkTableCellEditor.java
  23. 32
      src/main/java/com/weis/darklaf/ui/table/DarkTableCellFocusBorder.java
  24. 24
      src/main/java/com/weis/darklaf/ui/table/DarkTableCellRenderer.java
  25. 6
      src/main/java/com/weis/darklaf/ui/table/DarkTableHeaderUI.java
  26. 341
      src/main/java/com/weis/darklaf/ui/table/DarkTableUI.java
  27. 21
      src/main/java/com/weis/darklaf/ui/table/DarkTableUIBridge.java
  28. 57
      src/main/java/com/weis/darklaf/ui/table/TextFieldTableCellEditorBorder.java
  29. 16
      src/main/java/com/weis/darklaf/ui/text/DarkTextBorder.java
  30. 7
      src/main/java/com/weis/darklaf/ui/text/DarkTextFieldUI.java
  31. 2
      src/main/java/com/weis/darklaf/ui/tooltip/DarkTooltipBorder.java
  32. 63
      src/main/java/com/weis/darklaf/util/DarkUIUtil.java
  33. 9
      src/main/resources/com/weis/darklaf/icons/dark/files/drive.svg
  34. 6
      src/main/resources/com/weis/darklaf/icons/dark/menu/down.svg
  35. 4
      src/main/resources/com/weis/darklaf/icons/dark/menu/save.svg
  36. 5
      src/main/resources/com/weis/darklaf/icons/dark/menu/up.svg
  37. 3
      src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowDownHover.svg
  38. 3
      src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowDownSelected.svg
  39. 3
      src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowLeftHover.svg
  40. 3
      src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowLeftSelected.svg
  41. 4
      src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowRightHover.svg
  42. 4
      src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowRightSelected.svg
  43. 4
      src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowUpHover.svg
  44. 3
      src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowUpSelected.svg
  45. 9
      src/main/resources/com/weis/darklaf/icons/light/files/drive.svg
  46. 4
      src/main/resources/com/weis/darklaf/icons/light/files/folder.svg
  47. 6
      src/main/resources/com/weis/darklaf/icons/light/menu/down.svg
  48. 4
      src/main/resources/com/weis/darklaf/icons/light/menu/save.svg
  49. 5
      src/main/resources/com/weis/darklaf/icons/light/menu/up.svg
  50. 3
      src/main/resources/com/weis/darklaf/icons/light/navigation/arrowDownHover.svg
  51. 3
      src/main/resources/com/weis/darklaf/icons/light/navigation/arrowDownSelected.svg
  52. 3
      src/main/resources/com/weis/darklaf/icons/light/navigation/arrowLeftHover.svg
  53. 3
      src/main/resources/com/weis/darklaf/icons/light/navigation/arrowLeftSelected.svg
  54. 4
      src/main/resources/com/weis/darklaf/icons/light/navigation/arrowRightHover.svg
  55. 4
      src/main/resources/com/weis/darklaf/icons/light/navigation/arrowRightSelected.svg
  56. 3
      src/main/resources/com/weis/darklaf/icons/light/navigation/arrowUpHover.svg
  57. 3
      src/main/resources/com/weis/darklaf/icons/light/navigation/arrowUpSelected.svg
  58. 2
      src/main/resources/com/weis/darklaf/properties/platform/windows.properties
  59. 1
      src/main/resources/com/weis/darklaf/properties/ui/comboBox.properties
  60. 32
      src/main/resources/com/weis/darklaf/properties/ui/fileChooser.properties
  61. 5
      src/main/resources/com/weis/darklaf/properties/ui/list.properties
  62. 5
      src/main/resources/com/weis/darklaf/properties/ui/menu.properties
  63. 8
      src/main/resources/com/weis/darklaf/properties/ui/table.properties
  64. 5
      src/main/resources/com/weis/darklaf/properties/ui/toggleButton.properties
  65. 4
      src/main/resources/com/weis/darklaf/properties/ui/tree.properties
  66. BIN
      src/main/resources/library/x64/jniplatform.dll
  67. BIN
      src/main/resources/library/x86/jniplatform.dll
  68. 7
      src/test/java/FileChooserDemo.java
  69. 4
      src/test/java/ToolTipDemo.java
  70. 8
      src/test/java/UIDemo.java
  71. 3
      src/test/java/UIManagerDefaults.java

3
build.gradle

@ -82,7 +82,8 @@ tasks.withType(JavaCompile) {
options.compilerArgs += [
'--add-exports=java.desktop/sun.awt=ALL-UNNAMED',
'--add-exports=java.desktop/com.sun.java.swing=ALL-UNNAMED',
'--add-exports=java.desktop/sun.swing=ALL-UNNAMED'
'--add-exports=java.desktop/sun.swing=ALL-UNNAMED',
'--add-exports=java.desktop/sun.awt.shell=ALL-UNNAMED'
]
}
}

99
src/main/java/com/weis/darklaf/components/OverlayScrollPane.java

@ -40,7 +40,6 @@ public class OverlayScrollPane extends JLayeredPane {
protected final OScrollPane scrollPane;
private final ControlPanel controlPanel;
private Insets barInsets;
/**
* Creates a <code>JScrollIndicator</code> that displays the contents of the specified component,
@ -76,9 +75,7 @@ public class OverlayScrollPane extends JLayeredPane {
* @param hsbPolicy an integer that specifies the horizontal scrollbar policy
*/
public OverlayScrollPane(final JComponent view, final int vsbPolicy, final int hsbPolicy) {
setBorder(null);
scrollPane = createScrollPane(view, vsbPolicy, hsbPolicy);
scrollPane.setBorder(BorderFactory.createEmptyBorder());
add(scrollPane, JLayeredPane.DEFAULT_LAYER);
controlPanel = new ControlPanel(scrollPane);
@ -106,47 +103,32 @@ public class OverlayScrollPane extends JLayeredPane {
scrollPane.setSize(getSize());
controlPanel.setSize(getSize());
scrollPane.doLayout();
} @Override
public Dimension getPreferredSize() {
return scrollPane.getPreferredSize();
}
public void setVerticalScrollBarPolicy(final int policy) {
scrollPane.setVerticalScrollBarPolicy(policy);
controlPanel.showVerticalScrollBar(policy != JScrollPane.VERTICAL_SCROLLBAR_NEVER);
} @Override
public void setPreferredSize(final Dimension preferredSize) {
super.setPreferredSize(preferredSize);
scrollPane.setPreferredSize(preferredSize);
}
public void setHorizontalScrollBarPolicy(final int policy) {
scrollPane.setHorizontalScrollBarPolicy(policy);
controlPanel.showHorizontalScrollBar(policy != JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
}
@Contract(pure = true)
@NotNull
public JScrollBar getVerticalScrollBar() {
return scrollPane.verticalScrollBar;
}
@Contract(pure = true)
@NotNull
public JScrollBar getHorizontalScrollBar() {
return scrollPane.horizontalScrollBar;
}
public void setViewportView(final Component c) {
scrollPane.setViewportView(c);
public Dimension getPreferredSize() {
return scrollPane.getPreferredSize();
}
private static final class PopupScrollBar extends JScrollBar {
private PopupScrollBar(final int direction) {
private final JScrollPane pane;
private PopupScrollBar(final int direction, final JScrollPane pane) {
super(direction);
this.pane = pane;
putClientProperty("JScrollBar.fastWheelScrolling", true);
setOpaque(false);
}
@Contract(pure = true)
@Override
public boolean isOpaque() {
return false;
}
}
protected static class OScrollPane extends JScrollPane {
@ -218,10 +200,12 @@ public class OverlayScrollPane extends JLayeredPane {
*/
public void setUI(final ScrollPaneUI ui) {
if (verticalScrollBar == null) {
verticalScrollBar = new PopupScrollBar(JScrollBar.VERTICAL);
verticalScrollBar = new PopupScrollBar(JScrollBar.VERTICAL, this);
verticalScrollBar.putClientProperty("JScrollBar.scrollPaneParent", this);
}
if (horizontalScrollBar == null) {
horizontalScrollBar = new PopupScrollBar(JScrollBar.HORIZONTAL);
horizontalScrollBar = new PopupScrollBar(JScrollBar.HORIZONTAL, this);
horizontalScrollBar.putClientProperty("JScrollBar.scrollPaneParent", this);
}
super.setUI(ui);
SwingUtilities.invokeLater(() -> {
@ -243,6 +227,33 @@ public class OverlayScrollPane extends JLayeredPane {
}
}
@Override
public void setPreferredSize(final Dimension preferredSize) {
super.setPreferredSize(preferredSize);
scrollPane.setPreferredSize(preferredSize);
}
public void setHorizontalScrollBarPolicy(final int policy) {
scrollPane.setHorizontalScrollBarPolicy(policy);
controlPanel.showHorizontalScrollBar(policy != JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
}
@Contract(pure = true)
@NotNull
public JScrollBar getVerticalScrollBar() {
return scrollPane.verticalScrollBar;
}
@Contract(pure = true)
@NotNull
public JScrollBar getHorizontalScrollBar() {
return scrollPane.horizontalScrollBar;
}
public void setViewportView(final Component c) {
scrollPane.setViewportView(c);
}
private final class ControlPanel extends JPanel {
private boolean showVertical;
@ -281,20 +292,14 @@ public class OverlayScrollPane extends JLayeredPane {
scrollPane.horizontalScrollBar.setVisible(show);
}
@NotNull
private Rectangle getVerticalBounds() {
var bounds = OverlayScrollPane.this.getBounds();
var verticalSize = scrollPane.verticalScrollBar.getPreferredSize();
return new Rectangle(bounds.width - verticalSize.width, 0,
verticalSize.width, bounds.height);
}
@NotNull
private Rectangle getHorizontalBounds() {
var bounds = OverlayScrollPane.this.getBounds();
var horizontalSize = scrollPane.horizontalScrollBar.getPreferredSize();
return new Rectangle(0, bounds.height - horizontalSize.height,
bounds.width, horizontalSize.height);
@Override
public boolean contains(final int x, final int y) {
if (scrollPane.horizontalScrollBar.isVisible()
&& scrollPane.horizontalScrollBar.getBounds().contains(x, y)) {
return true;
}
return scrollPane.verticalScrollBar.isVisible()
&& scrollPane.verticalScrollBar.getBounds().contains(x, y);
}
}

46
src/main/java/com/weis/darklaf/components/ShadowButton.java

@ -0,0 +1,46 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.components;
import javax.swing.*;
public class ShadowButton extends JButton {
public ShadowButton(final Icon icon) {
super(icon);
init();
}
private void init() {
setRolloverEnabled(true);
setOpaque(false);
putClientProperty("JButton.variant", "shadow");
putClientProperty("JButton.square", Boolean.TRUE);
}
public ShadowButton(final Action action) {
super(action);
init();
}
}

68
src/main/java/com/weis/darklaf/components/TooltipAwareButton.java

@ -0,0 +1,68 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.components;
import com.weis.darklaf.components.alignment.Alignment;
import com.weis.darklaf.components.tooltip.ToolTipContext;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
public class TooltipAwareButton extends JButton {
private final ToolTipContext context = new ToolTipContext(this)
.setAlignment(Alignment.CENTER)
.setCenterAlignment(Alignment.SOUTH);
public TooltipAwareButton() {
this(null, null);
}
public TooltipAwareButton(final String text, final Icon icon) {
super(text, icon);
}
public TooltipAwareButton(final Icon icon) {
this(null, icon);
}
public TooltipAwareButton(final String text) {
this(text, null);
}
public TooltipAwareButton(final Action a) {
super(a);
}
@Override
public Point getToolTipLocation(final MouseEvent event) {
return context.getToolTipLocation(event);
}
@Override
public JToolTip createToolTip() {
return context.getToolTip();
}
}

68
src/main/java/com/weis/darklaf/components/TooltipAwareToggleButton.java

@ -0,0 +1,68 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.components;
import com.weis.darklaf.components.alignment.Alignment;
import com.weis.darklaf.components.tooltip.ToolTipContext;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
public class TooltipAwareToggleButton extends JToggleButton {
private final ToolTipContext context = new ToolTipContext(this)
.setAlignment(Alignment.CENTER)
.setCenterAlignment(Alignment.SOUTH);
public TooltipAwareToggleButton() {
this(null, null);
}
public TooltipAwareToggleButton(final String text, final Icon icon) {
super(text, icon);
}
public TooltipAwareToggleButton(final Icon icon) {
this(null, icon);
}
public TooltipAwareToggleButton(final String text) {
this(text, null);
}
public TooltipAwareToggleButton(final Action a) {
super(a);
}
@Override
public Point getToolTipLocation(final MouseEvent event) {
return context.getToolTipLocation(event);
}
@Override
public JToolTip createToolTip() {
return context.getToolTip();
}
}

77
src/main/java/com/weis/darklaf/ui/button/DarkToggleButtonUI.java

@ -82,7 +82,9 @@ public class DarkToggleButtonUI extends DarkButtonUI {
public Dimension getPreferredSize(final JComponent c) {
Dimension d = super.getPreferredSize(c);
d.width += SLIDER_WIDTH + DarkButtonBorder.BORDER_SIZE;
if (isSlider(c)) {
d.width += SLIDER_WIDTH + DarkButtonBorder.BORDER_SIZE;
}
return d;
}
@ -129,20 +131,43 @@ public class DarkToggleButtonUI extends DarkButtonUI {
}
}
protected Color getBackgroundColor(@NotNull final JComponent c) {
if ((c instanceof JToggleButton && ((JToggleButton) c).isSelected())) {
return UIManager.getColor("Button.activeFillColor");
private void paintSlider(@NotNull final Graphics2D g, final AbstractButton c) {
var bounds = getSliderBounds(c);
g.translate(bounds.x, bounds.y);
Shape slider = new RoundRectangle2D.Float(0, 0, bounds.width, bounds.height,
bounds.height, bounds.height);
if (c.hasFocus()) {
g.translate(-BSIZE, -BSIZE);
DarkUIUtil.paintFocusBorder(g, bounds.width + 2 * BSIZE, bounds.height + 2 * BSIZE,
(float) ((bounds.height + 2 * BSIZE) / 2.0 + 2), true);
g.translate(BSIZE, BSIZE);
}
g.setColor(getBackgroundColor(c));
g.fill(slider);
g.setColor(getToggleBorderColor(c));
g.draw(slider);
g.setColor(getSliderColor(c));
if (c.isSelected()) {
g.fill(new Ellipse2D.Float(
bounds.width - bounds.height + 1, 1, bounds.height - 1.5f, bounds.height - 1.5f));
} else {
return UIManager.getColor("Button.inactiveFillColor");
g.fill(new Ellipse2D.Float(1, 1, bounds.height - 1.5f, bounds.height - 1.5f));
}
g.translate(-bounds.x, -bounds.y);
}
@Override
public boolean contains(@NotNull final JComponent c, final int x, final int y) {
if (!(x >= 0 && x <= c.getWidth() && y >= 0 && y <= c.getHeight())) return false;
var bounds = getSliderBounds(c);
return new RoundRectangle2D.Float(bounds.x, bounds.y, bounds.width, bounds.height,
bounds.height, bounds.height).contains(x, y);
protected Color getBackgroundColor(@NotNull final JComponent c) {
if (c instanceof JToggleButton && c.isEnabled()) {
if (((JToggleButton) c).isSelected()) {
return UIManager.getColor("ToggleButton.activeFillColor");
} else {
return UIManager.getColor("ToggleButton.inactiveFillColor");
}
}
return super.getBackgroundColor(c);
}
@NotNull
@ -162,31 +187,13 @@ public class DarkToggleButtonUI extends DarkButtonUI {
&& "slider".equals(c.getClientProperty("ToggleButton.variant"));
}
private void paintSlider(@NotNull final Graphics2D g, final AbstractButton c) {
@Override
public boolean contains(@NotNull final JComponent c, final int x, final int y) {
if (!isSlider(c)) return super.contains(c, x, y);
if (!(x >= 0 && x <= c.getWidth() && y >= 0 && y <= c.getHeight())) return false;
var bounds = getSliderBounds(c);
g.translate(bounds.x, bounds.y);
Shape slider = new RoundRectangle2D.Float(0, 0, bounds.width, bounds.height,
bounds.height, bounds.height);
g.setColor(getBackgroundColor(c));
g.fill(slider);
if (c.hasFocus()) {
g.translate(-BSIZE, -BSIZE);
DarkUIUtil.paintFocusBorder(g, bounds.width + 2 * BSIZE, bounds.height + 2 * BSIZE,
(float) (bounds.height / 2.0) + 2, true);
g.translate(BSIZE, BSIZE);
}
g.setColor(getToggleBorderColor(c));
g.draw(slider);
g.setColor(getSliderColor(c));
if (c.isSelected()) {
g.fill(new Ellipse2D.Float(
bounds.width - bounds.height + 1, 1, bounds.height - 1.5f, bounds.height - 1.5f));
} else {
g.fill(new Ellipse2D.Float(1, 1, bounds.height - 1.5f, bounds.height - 1.5f));
}
g.translate(-bounds.x, -bounds.y);
return new RoundRectangle2D.Float(bounds.x, bounds.y, bounds.width, bounds.height,
bounds.height, bounds.height).contains(x, y);
}
private static Color getToggleBorderColor(@NotNull final AbstractButton b) {

2
src/main/java/com/weis/darklaf/ui/checkbox/DarkCheckBoxMenuItemUI.java

@ -56,7 +56,7 @@ public class DarkCheckBoxMenuItemUI extends DarkMenuItemUIBase {
Graphics2D g = (Graphics2D) g2;
GraphicsContext config = GraphicsUtil.setupStrokePainting(g);
var rect = lr.getCheckRect();
getCheckBoxIcon(lh.getMenuItem()).paintIcon(lh.getMenuItem(), g2, rect.x, rect.y);
getCheckBoxIcon(lh.getMenuItem()).paintIcon(lh.getMenuItem(), g2, rect.x - 1, rect.y);
config.restore();
g.setColor(foreground);
}

41
src/main/java/com/weis/darklaf/ui/filechooser/DarkFileChooserListViewBorder.java

@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.ui.filechooser;
import com.weis.darklaf.components.border.MutableLineBorder;
import javax.swing.*;
import java.awt.*;
public class DarkFileChooserListViewBorder extends MutableLineBorder.UIResource {
public DarkFileChooserListViewBorder() {
super(1, 1, 1, 1, Color.BLACK);
}
@Override
protected Color getColor() {
return UIManager.getColor("FileChooser.borderColor");
}
}

294
src/main/java/com/weis/darklaf/ui/filechooser/DarkFileChooserUI.java

@ -0,0 +1,294 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.ui.filechooser;
import com.weis.darklaf.components.TooltipAwareButton;
import com.weis.darklaf.components.TooltipAwareToggleButton;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import sun.swing.FilePane;
import javax.accessibility.AccessibleContext;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.filechooser.FileSystemView;
import javax.swing.plaf.ComponentUI;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.io.File;
public class DarkFileChooserUI extends DarkFileChooserUIBridge {
public DarkFileChooserUI(final JFileChooser b) {
super(b);
}
@NotNull
@Contract("_ -> new")
public static ComponentUI createUI(final JComponent c) {
return new DarkFileChooserUI((JFileChooser) c);
}
@Override
public void installComponents(final JFileChooser fc) {
FileSystemView fsv = fc.getFileSystemView();
fc.setBorder(new EmptyBorder(12, 12, 11, 11));
fc.setLayout(new BorderLayout(0, 11));
filePane = new DarkFilePane(new MetalFileChooserUIAccessor());
fc.addPropertyChangeListener(filePane);
// ********************************* //
// **** Construct the top panel **** //
// ********************************* //
// Directory manipulation buttons
JPanel topPanel = new JPanel(new BorderLayout(11, 0));
JPanel topButtonPanel = new JPanel();
topButtonPanel.setLayout(new BoxLayout(topButtonPanel, BoxLayout.LINE_AXIS));
topPanel.add(topButtonPanel, BorderLayout.AFTER_LINE_ENDS);
// Add the top panel to the fileChooser
fc.add(topPanel, BorderLayout.NORTH);
// ComboBox Label
lookInLabel = new JLabel(lookInLabelText);
lookInLabel.setDisplayedMnemonic(lookInLabelMnemonic);
topPanel.add(lookInLabel, BorderLayout.BEFORE_LINE_BEGINS);
// CurrentDir ComboBox
directoryComboBox = new JComboBox<>() {
public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
// Must be small enough to not affect total width.
d.width = 150;
return d;
}
};
directoryComboBox.putClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY,
lookInLabelText);
lookInLabel.setLabelFor(directoryComboBox);
directoryComboBoxModel = createDirectoryComboBoxModel(fc);
directoryComboBox.setModel(directoryComboBoxModel);
directoryComboBox.addActionListener(directoryComboBoxAction);
directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc));
directoryComboBox.setAlignmentX(JComponent.LEFT_ALIGNMENT);
directoryComboBox.setAlignmentY(JComponent.TOP_ALIGNMENT);
directoryComboBox.setMaximumRowCount(8);
topPanel.add(directoryComboBox, BorderLayout.CENTER);
// Up Button
JButton upFolderButton = new TooltipAwareButton(getChangeToParentDirectoryAction());
upFolderButton.setText(null);
upFolderButton.setIcon(upFolderIcon);
upFolderButton.setToolTipText(upFolderToolTipText);
upFolderButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
upFolderAccessibleName);
upFolderButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
upFolderButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
upFolderButton.setMargin(shrinkwrap);
topButtonPanel.add(upFolderButton);
topButtonPanel.add(Box.createRigidArea(hstrut5));
// Home Button
File homeDir = fsv.getHomeDirectory();
String toolTipText = homeFolderToolTipText;
JButton b = new TooltipAwareButton(homeFolderIcon);
b.setToolTipText(toolTipText);
b.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
homeFolderAccessibleName);
b.setAlignmentX(JComponent.LEFT_ALIGNMENT);
b.setAlignmentY(JComponent.CENTER_ALIGNMENT);
b.setMargin(shrinkwrap);
b.addActionListener(getGoHomeAction());
topButtonPanel.add(b);
topButtonPanel.add(Box.createRigidArea(hstrut5));
// New Directory Button
if (!UIManager.getBoolean("FileChooser.readOnly")) {
b = new TooltipAwareButton(filePane.getNewFolderAction());
b.setText(null);
b.setIcon(newFolderIcon);
b.setToolTipText(newFolderToolTipText);
b.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
newFolderAccessibleName);
b.setAlignmentX(JComponent.LEFT_ALIGNMENT);
b.setAlignmentY(JComponent.CENTER_ALIGNMENT);
b.setMargin(shrinkwrap);
}
topButtonPanel.add(b);
topButtonPanel.add(Box.createRigidArea(hstrut5));
// View button group
ButtonGroup viewButtonGroup = new ButtonGroup();
// List Button
listViewButton = new TooltipAwareToggleButton(listViewIcon);
listViewButton.putClientProperty("JButton.square", Boolean.TRUE);
listViewButton.setToolTipText(listViewButtonToolTipText);
listViewButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
listViewButtonAccessibleName);
listViewButton.setSelected(true);
listViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
listViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
listViewButton.setMargin(shrinkwrap);
listViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_LIST));
topButtonPanel.add(listViewButton);
topButtonPanel.add(Box.createRigidArea(hstrut5));
viewButtonGroup.add(listViewButton);
// Details Button
detailsViewButton = new TooltipAwareToggleButton(detailsViewIcon);
detailsViewButton.putClientProperty("JButton.square", Boolean.TRUE);
detailsViewButton.setToolTipText(detailsViewButtonToolTipText);
detailsViewButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
detailsViewButtonAccessibleName);
detailsViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
detailsViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
detailsViewButton.setMargin(shrinkwrap);
detailsViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_DETAILS));
topButtonPanel.add(detailsViewButton);
viewButtonGroup.add(detailsViewButton);
topButtonPanel.add(Box.createGlue());
filePane.addPropertyChangeListener(e -> {
if ("viewType".equals(e.getPropertyName())) {
int viewType = filePane.getViewType();
switch (viewType) {
case FilePane.VIEWTYPE_LIST:
listViewButton.setSelected(true);
break;
case FilePane.VIEWTYPE_DETAILS:
detailsViewButton.setSelected(true);
break;
}
}
});
// ************************************** //
// ******* Add the directory pane ******* //
// ************************************** //
fc.add(getAccessoryPanel(), BorderLayout.AFTER_LINE_ENDS);
JComponent accessory = fc.getAccessory();
if (accessory != null) {
getAccessoryPanel().add(accessory);
}
filePane.setPreferredSize(LIST_PREF_SIZE);
fc.add(filePane, BorderLayout.CENTER);
// ********************************** //
// **** Construct the bottom panel ** //
// ********************************** //
JPanel bottomPanel = getBottomPanel();
bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS));
fc.add(bottomPanel, BorderLayout.SOUTH);
// FileName label and textfield
JPanel fileNamePanel = new JPanel();
fileNamePanel.setLayout(new BoxLayout(fileNamePanel, BoxLayout.LINE_AXIS));
bottomPanel.add(fileNamePanel);
bottomPanel.add(Box.createRigidArea(vstrut5));
fileNameLabel = new AlignedLabel();
populateFileNameLabel();
fileNamePanel.add(fileNameLabel);
@SuppressWarnings("serial") // anonymous class
JTextField tmp2 = new JTextField(35) {
public Dimension getMaximumSize() {
return new Dimension(Short.MAX_VALUE, super.getPreferredSize().height);
}
};
fileNameTextField = tmp2;
fileNamePanel.add(fileNameTextField);
fileNameLabel.setLabelFor(fileNameTextField);
fileNameTextField.addFocusListener(
new FocusAdapter() {
public void focusGained(final FocusEvent e) {
if (!getFileChooser().isMultiSelectionEnabled()) {
filePane.clearSelection();
}
}
}
);
if (fc.isMultiSelectionEnabled()) {
setFileName(fileNameString(fc.getSelectedFiles()));
} else {
setFileName(fileNameString(fc.getSelectedFile()));
}
// Filetype label and combobox
JPanel filesOfTypePanel = new JPanel();
filesOfTypePanel.setLayout(new BoxLayout(filesOfTypePanel, BoxLayout.LINE_AXIS));
bottomPanel.add(filesOfTypePanel);
AlignedLabel filesOfTypeLabel = new AlignedLabel(filesOfTypeLabelText);
filesOfTypeLabel.setDisplayedMnemonic(filesOfTypeLabelMnemonic);
filesOfTypePanel.add(filesOfTypeLabel);
filterComboBoxModel = createFilterComboBoxModel();
fc.addPropertyChangeListener(filterComboBoxModel);
filterComboBox = new JComboBox<>(filterComboBoxModel);
if (filterComboBox.getItemCount() == 0) {
filterComboBox.setEnabled(false);
}
filterComboBox.putClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY,
filesOfTypeLabelText);
filesOfTypeLabel.setLabelFor(filterComboBox);
filterComboBox.setRenderer(createFilterComboBoxRenderer());
filesOfTypePanel.add(filterComboBox);
// buttons
getButtonPanel().setLayout(new ButtonAreaLayout());
approveButton = new TooltipAwareButton(getApproveButtonText(fc));
// Note: Metal does not use mnemonics for approve and cancel
approveButton.addActionListener(getApproveSelectionAction());
approveButton.setToolTipText(getApproveButtonToolTipText(fc));
getButtonPanel().add(approveButton);
cancelButton = new TooltipAwareButton(cancelButtonText);
cancelButton.setToolTipText(cancelButtonToolTipText);
cancelButton.addActionListener(getCancelSelectionAction());
getButtonPanel().add(cancelButton);
if (fc.getControlButtonsAreShown()) {
addControlButtons();
}
groupLabels(new AlignedLabel[]{fileNameLabel, filesOfTypeLabel});
}
}

1390
src/main/java/com/weis/darklaf/ui/filechooser/DarkFileChooserUIBridge.java

File diff suppressed because it is too large Load Diff

434
src/main/java/com/weis/darklaf/ui/filechooser/DarkFilePane.java

@ -0,0 +1,434 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.ui.filechooser;
import com.weis.darklaf.components.OverlayScrollPane;
import com.weis.darklaf.ui.table.TextFieldTableCellEditorBorder;
import com.weis.darklaf.util.DarkUIUtil;
import sun.awt.AWTAccessor;
import sun.swing.SwingUtilities2;
import javax.accessibility.AccessibleContext;
import javax.swing.*;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableCellRenderer;
import javax.swing.text.Position;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.function.Supplier;
public class DarkFilePane extends DarkFilePaneUIBridge {
public DarkFilePane(final FileChooserUIAccessor fileChooserUIAccessor) {
super(fileChooserUIAccessor);
}
@Override
protected void installDefaults() {
super.installDefaults();
kiloByteString = UIManager.getString("FileChooser.fileSizeKiloBytes");
megaByteString = UIManager.getString("FileChooser.fileSizeMegaBytes");
gigaByteString = UIManager.getString("FileChooser.fileSizeGigaBytes");
editCell = new JTextField();
editCell.setBorder(new TextFieldTableCellEditorBorder());
editCell.putClientProperty("JTextField.listCellEditor", true);
}
public JPanel createList() {
JPanel p = new JPanel(new BorderLayout());
final JFileChooser fileChooser = getFileChooser();
@SuppressWarnings("serial") // anonymous class
final JList<Object> list = new JList<>() {
public int getNextMatch(final String prefix, final int startIndex, final Position.Bias bias) {
ListModel<?> model = getModel();
int max = model.getSize();
if (prefix == null || startIndex < 0 || startIndex >= max) {
throw new IllegalArgumentException();
}
// start search from the next element before/after the selected element
boolean backwards = (bias == Position.Bias.Backward);
for (int i = startIndex; backwards ? i >= 0 : i < max; i += (backwards ? -1 : 1)) {
String filename = fileChooser.getName((File) model.getElementAt(i));
if (filename.regionMatches(true, 0, prefix, 0, prefix.length())) {
return i;
}
}
return -1;
}
};
list.setCellRenderer(new FileRenderer());
list.setLayoutOrientation(JList.VERTICAL_WRAP);
// 4835633 : tell BasicListUI that this is a file list
list.putClientProperty("List.isFileList", Boolean.TRUE);
list.putClientProperty("JList.fullRowSelection", fullRowSelection);
if (listViewWindowsStyle) {
list.addFocusListener(repaintListener);
}
updateListRowCount(list);
getModel().addListDataListener(new ListDataListener() {
public void intervalAdded(final ListDataEvent e) {
updateListRowCount(list);
}
public void intervalRemoved(final ListDataEvent e) {
updateListRowCount(list);
}
public void contentsChanged(final ListDataEvent e) {
if (isShowing()) {
clearSelection();
}
updateListRowCount(list);
}
});
getModel().addPropertyChangeListener(this);
if (fileChooser.isMultiSelectionEnabled()) {
list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
} else {
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
list.setModel(new SortableListModel());
list.addListSelectionListener(createListSelectionListener());
list.addMouseListener(getMouseHandler());
OverlayScrollPane overlayScrollPane = new OverlayScrollPane(list);
JScrollPane scrollPane = overlayScrollPane.getScrollPane();
if (listViewBackground != null) {
list.setBackground(listViewBackground);
}
if (listViewBorder != null) {
scrollPane.setBorder(listViewBorder);
}
list.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, filesListAccessibleName);
p.add(overlayScrollPane, BorderLayout.CENTER);
return p;
}
@Override
public JPanel createDetailsView() {
final JFileChooser chooser = getFileChooser();
JPanel p = new JPanel(new BorderLayout());
@SuppressWarnings("serial") // anonymous class
final JTable detailsTable = new JTable(getDetailsTableModel()) {
public void tableChanged(final TableModelEvent e) {
super.tableChanged(e);
if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {
// update header with possibly changed column set
updateDetailsColumnModel(this);
}
}
// Handle Escape key events here
protected boolean processKeyBinding(final KeyStroke ks, final KeyEvent e,
final int condition, final boolean pressed) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE && getCellEditor() == null) {
// We are not editing, forward to filechooser.
chooser.dispatchEvent(e);
return true;
}
return super.processKeyBinding(ks, e, condition, pressed);
}
};
int rowHeight = UIManager.getInt("FileChooser.rowHeight");
if (rowHeight > 0) {
detailsTable.setRowHeight(rowHeight);
}
detailsTable.setRowSorter(getRowSorter());
detailsTable.setAutoCreateColumnsFromModel(false);
detailsTable.setComponentOrientation(chooser.getComponentOrientation());
detailsTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
detailsTable.setRowSelectionAllowed(true);
detailsTable.setShowGrid(false);
detailsTable.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
detailsTable.addKeyListener(detailsKeyListener);
detailsTable.putClientProperty("JTable.rowFocusBorder", true);
detailsTable.putClientProperty("JTable.fileChooserParent", (Supplier<JFileChooser>) this::getFileChooser);
detailsTable.putClientProperty("JTable.fileNameColumnIndex", COLUMN_FILENAME);
Font font = list.getFont();
detailsTable.setFont(font);
TableCellRenderer headerRenderer =
new AlignableTableHeaderRenderer(detailsTable.getTableHeader().getDefaultRenderer());
detailsTable.getTableHeader().setDefaultRenderer(headerRenderer);
TableCellRenderer cellRenderer = new DetailsTableCellRenderer(chooser);
detailsTable.setDefaultRenderer(Object.class, cellRenderer);
if (getFileChooser().isMultiSelectionEnabled()) {
detailsTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
} else {
detailsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
detailsTable.addMouseListener(getMouseHandler());
// 4835633 : tell BasicTableUI that this is a file list
detailsTable.putClientProperty("Table.isFileList", Boolean.TRUE);
if (listViewWindowsStyle) {
detailsTable.addFocusListener(repaintListener);
}
// TAB/SHIFT-TAB should transfer focus and ENTER should select an item.
// We don't want them to navigate within the table
ActionMap am = SwingUtilities.getUIActionMap(detailsTable);
am.remove("selectNextRowCell");
am.remove("selectPreviousRowCell");
am.remove("selectNextColumnCell");
am.remove("selectPreviousColumnCell");
detailsTable.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
null);
detailsTable.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
null);
OverlayScrollPane overlayScrollPane = new OverlayScrollPane(detailsTable);
JScrollPane scrollPane = overlayScrollPane.getScrollPane();
scrollPane.setComponentOrientation(chooser.getComponentOrientation());
LookAndFeel.installColors(scrollPane.getViewport(), "Table.background", "Table.foreground");
// Adjust width of first column so the table fills the viewport when
// first displayed (temporary listener).
scrollPane.addComponentListener(new ComponentAdapter() {
public void componentResized(final ComponentEvent e) {
JScrollPane sp = (JScrollPane) e.getComponent();
fixNameColumnWidth(sp.getViewport().getSize().width);
sp.removeComponentListener(this);
}
});
detailsTable.setForeground(list.getForeground());
detailsTable.setBackground(list.getBackground());
if (listViewBorder != null) {
scrollPane.setBorder(listViewBorder);
}
p.add(overlayScrollPane, BorderLayout.CENTER);
detailsTableModel.fireTableStructureChanged();
detailsTable.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, filesDetailsAccessibleName);
return p;
}
protected void cancelEdit() {
if (editFile != null) {
editFile = null;
list.remove(editCell);
list.putClientProperty("JList.isEditing", false);
repaint();
} else if (detailsTable != null && detailsTable.isEditing()) {
detailsTable.getCellEditor().cancelCellEditing();
}
}
protected void editFileName(final int index) {
JFileChooser chooser = getFileChooser();
File currentDirectory = chooser.getCurrentDirectory();
if (readOnly || !canWrite(currentDirectory, chooser)) {
return;
}
ensureIndexIsVisible(index);
switch (viewType) {
case VIEWTYPE_LIST:
editFile = (File) getModel().getElementAt(getRowSorter().convertRowIndexToModel(index));
Rectangle r = list.getCellBounds(index, index);
if (editCell == null) {
editCell = new JTextField();
editCell.setName("Tree.cellEditor");
editCell.addActionListener(new EditActionListener());
editCell.addFocusListener(editorFocusListener);
editCell.setNextFocusableComponent(list);
}
list.add(editCell);
editCell.setText(chooser.getName(editFile));
ComponentOrientation orientation = list.getComponentOrientation();
editCell.setComponentOrientation(orientation);
Icon icon = chooser.getIcon(editFile);
// PENDING - grab padding (4) below from defaults table.
int editX = icon == null ? 20 : icon.getIconWidth() + 4;
int gap = 0;
var renderer = list.getCellRenderer();
if (renderer instanceof JLabel) {
gap = ((JLabel) renderer).getIconTextGap() - 1;
}
if (orientation.isLeftToRight()) {
editCell.setBounds(editX + r.x + gap, r.y, r.width - editX - gap, r.height);
} else {
editCell.setBounds(r.x, r.y, r.width - editX - gap, r.height);
}
list.putClientProperty("JList.isEditing", true);
editCell.requestFocus();
editCell.selectAll();
break;
case VIEWTYPE_DETAILS:
detailsTable.editCellAt(index, COLUMN_FILENAME);
break;
}
}
public JPopupMenu getComponentPopupMenu() {
JPopupMenu popupMenu = getFileChooser().getComponentPopupMenu();
if (popupMenu != null) {
return popupMenu;
}
JMenu viewMenu = getViewMenu();
if (contextMenu == null) {
contextMenu = new JPopupMenu();
if (viewMenu != null) {
contextMenu.add(viewMenu);
if (listViewWindowsStyle) {
contextMenu.addSeparator();
}
}
ActionMap actionMap = getActionMap();
Action refreshAction = actionMap.get(ACTION_REFRESH);
Action newFolderAction = actionMap.get(ACTION_NEW_FOLDER);
Action renameAction = actionMap.get(ACTION_EDIT_FILE_NAME);
if (refreshAction != null) {
contextMenu.add(refreshAction);
}
if (renameAction != null) {
var menuItem = new JMenuItem(renameAction);
menuItem.setText("Rename");
contextMenu.add(menuItem);
}
if (newFolderAction != null) {
contextMenu.add(newFolderAction);
}
}
if (viewMenu != null) {
viewMenu.getPopupMenu().setInvoker(viewMenu);
}
return contextMenu;
}
@Override
protected Handler getMouseHandler() {
if (handler == null) {
handler = new DarkHandler();
}
return handler;
}
protected class DarkHandler extends Handler {
@Override
public void mouseClicked(MouseEvent evt) {
JComponent source = (JComponent) evt.getSource();
int index;
if (source instanceof JList) {
index = list.locationToIndex(evt.getPoint());
} else if (source instanceof JTable) {
JTable table = (JTable) source;
Point p = evt.getPoint();
index = table.rowAtPoint(p);
boolean pointOutsidePrefSize = SwingUtilities2.pointOutsidePrefSize(table, index,
table.columnAtPoint(p), p);
if (pointOutsidePrefSize && !fullRowSelection) {
return;
}
// Translate point from table to list
if (index >= 0 && list != null && listSelectionModel.isSelectedIndex(index)) {
// Make a new event with the list as source, placing the
// click in the corresponding list cell.
Rectangle r = list.getCellBounds(index, index);
MouseEvent newEvent = new MouseEvent(list, evt.getID(),
evt.getWhen(), evt.getModifiersEx(),
r.x + 1, r.y + r.height / 2,
evt.getXOnScreen(),
evt.getYOnScreen(),
evt.getClickCount(), evt.isPopupTrigger(),
evt.getButton());
AWTAccessor.MouseEventAccessor meAccessor = AWTAccessor.getMouseEventAccessor();
meAccessor.setCausedByTouchEvent(newEvent,
meAccessor.isCausedByTouchEvent(evt));
evt = newEvent;
}
} else {
return;
}
if (index >= 0 && SwingUtilities.isLeftMouseButton(evt)) {
JFileChooser fc = getFileChooser();
// For single click, we handle editing file name
if (evt.getClickCount() == 1 && source instanceof JList) {
if ((!fc.isMultiSelectionEnabled() || fc.getSelectedFiles().length <= 1)
&& listSelectionModel.isSelectedIndex(index)
&& getEditIndex() == index && editFile == null
&& DarkUIUtil.isOverText(evt, index, list)) {
editFileName(index);
} else {
setEditIndex(index);
}
} else if (evt.getClickCount() == 2) {
// on double click (open or drill down one directory) be
// sure to clear the edit index
resetEditIndex();
}
}
// Forward event to Basic
if (getDoubleClickListener() != null) {
list.putClientProperty("List.isFileList", false);
getDoubleClickListener().mouseClicked(evt);
list.putClientProperty("List.isFileList", true);
}
}
}
}

2060
src/main/java/com/weis/darklaf/ui/filechooser/DarkFilePaneUIBridge.java

File diff suppressed because it is too large Load Diff

41
src/main/java/com/weis/darklaf/ui/list/DarkListCellFocusBorder.java

@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.ui.list;
import com.weis.darklaf.ui.cell.DarkCellBorder;
import com.weis.darklaf.util.DarkUIUtil;
import javax.swing.*;
import java.awt.*;
public class DarkListCellFocusBorder extends DarkCellBorder {
@Override
public void paintBorder(final Component c, final Graphics g, final int x, final int y,
final int width, final int height) {
super.paintBorder(c, g, x, y, width, height);
g.setColor(UIManager.getColor("List.focusBorderColor"));
DarkUIUtil.drawRect(g, 0, 0, width, height, 1);
}
}

44
src/main/java/com/weis/darklaf/ui/list/DarkListCellRenderer.java

@ -0,0 +1,44 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.ui.list;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
public class DarkListCellRenderer extends DefaultListCellRenderer {
@Override
public Component getListCellRendererComponent(@NotNull final JList<?> list, final Object value,
final int index, final boolean isSelected,
final boolean cellHasFocus) {
if (Boolean.TRUE.equals(list.getClientProperty("JList.isEditing"))) {
if (list.getSelectionModel().getLeadSelectionIndex() == index) {
return super.getListCellRendererComponent(list, value, index, false, false);
}
}
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
}

103
src/main/java/com/weis/darklaf/ui/list/DarkListUI.java

@ -1,20 +1,119 @@
package com.weis.darklaf.ui.list;
import com.weis.darklaf.util.DarkUIUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicListUI;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
/**
* @author Jannis Weis
*/
public class DarkListUI extends BasicListUI {
public class DarkListUI extends DarkListUIBridge {
@NotNull
@Contract("_ -> new")
public static ComponentUI createUI(final JComponent list) {
return new DarkListUI();
}
protected void paintCell(final Graphics g, final int row, final Rectangle rowBounds,
final ListCellRenderer<Object> cellRenderer, final ListModel<Object> dataModel,
final ListSelectionModel selModel, final int leadIndex) {
Object value = dataModel.getElementAt(row);
boolean cellHasFocus = list.hasFocus() && (row == leadIndex);
boolean isSelected = selModel.isSelectedIndex(row);
Component rendererComponent =
cellRenderer.getListCellRendererComponent(list, value, row, isSelected, cellHasFocus);
int cx = rowBounds.x;
int cy = rowBounds.y;
int cw = rowBounds.width;
int ch = rowBounds.height;
if (Boolean.TRUE.equals(list.getClientProperty("JList.shrinkWrap"))) {
// Shrink renderer to preferred size. This is mostly used on Windows
// where selection is only shown around the file name, instead of
// across the whole list cell.
int w = Math.min(cw, rendererComponent.getPreferredSize().width + 4);
if (!list.getComponentOrientation().isLeftToRight()) {
cx += (cw - w);
}
cw = w;
}
rendererPane.paintComponent(g, rendererComponent, list, cx, cy, cw, ch, true);
}
@Override
protected void installListeners() {
super.installListeners();
list.addMouseListener(new MouseAdapter() {
});
}
@Override
protected Handler getHandler() {
if (handler == null) {
handler = new DarkHandler();
}
return handler;
}
protected class DarkHandler extends Handler {
@Override
protected void adjustSelection(final MouseEvent e) {
int row = list.locationToIndex(e.getPoint());
if (row < 0) {
// If shift is down in multi-select, we should do nothing.
// For single select or non-shift-click, clear the selection
if (isFileList && !Boolean.TRUE.equals(list.getClientProperty("JList.fullRowSelection"))
&& e.getID() == MouseEvent.MOUSE_PRESSED &&
(!e.isShiftDown() || list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)) {
list.clearSelection();
}
} else {
int anchorIndex = adjustIndex(list.getAnchorSelectionIndex(), list);
boolean anchorSelected;
if (anchorIndex == -1) {
anchorIndex = 0;
anchorSelected = false;
} else {
anchorSelected = list.isSelectedIndex(anchorIndex);
}
if (DarkUIUtil.isMenuShortcutKeyDown(e)) {
if (e.isShiftDown()) {
if (anchorSelected) {
list.addSelectionInterval(anchorIndex, row);
} else {
list.removeSelectionInterval(anchorIndex, row);
if (isFileList) {
list.addSelectionInterval(row, row);
list.getSelectionModel().setAnchorSelectionIndex(anchorIndex);
}
}
} else if (list.isSelectedIndex(row)) {
list.removeSelectionInterval(row, row);
} else {
list.addSelectionInterval(row, row);
}
} else if (e.isShiftDown()) {
list.setSelectionInterval(anchorIndex, row);
} else {
list.setSelectionInterval(row, row);
}
}
}
}
}

2257
src/main/java/com/weis/darklaf/ui/list/DarkListUIBridge.java

File diff suppressed because it is too large Load Diff

238
src/main/java/com/weis/darklaf/ui/menu/DarkMenuItemUIBase.java

@ -27,27 +27,15 @@ import com.weis.darklaf.util.DarkUIUtil;
import com.weis.darklaf.util.LazyActionMap;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import sun.swing.MenuItemCheckIconFactory;
import sun.swing.MenuItemLayoutHelper;
import sun.swing.SwingUtilities2;
import sun.swing.UIAction;
import javax.swing.*;
import javax.swing.event.MenuDragMouseEvent;
import javax.swing.event.MenuDragMouseListener;
import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentInputMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicMenuItemUI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Objects;
/**
* @author Konstantin Bulenkov
@ -55,8 +43,6 @@ import java.util.Objects;
*/
public class DarkMenuItemUIBase extends BasicMenuItemUI {
protected Handler handler;
@NotNull
@Contract("_ -> new")
public static ComponentUI createUI(final JComponent c) {
@ -256,103 +242,8 @@ public class DarkMenuItemUIBase extends BasicMenuItemUI {
}
}
/*
* Code from BasicMenuItemUI.
*/
protected DarkMenuItemUIBase.Handler getHandler() {
if (handler == null) {
handler = new DarkMenuItemUIBase.Handler();
}
return handler;
}
protected void updateAcceleratorBinding() {
KeyStroke accelerator = menuItem.getAccelerator();
InputMap windowInputMap = SwingUtilities.getUIInputMap(menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW);
if (windowInputMap != null) {
windowInputMap.clear();
}
if (accelerator != null) {
if (windowInputMap == null) {
windowInputMap = createInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
SwingUtilities.replaceUIInputMap(menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW, windowInputMap);
}
windowInputMap.put(accelerator, "doClick");
int modifiers = accelerator.getModifiers();
if (((modifiers & InputEvent.ALT_DOWN_MASK) != 0) &&
((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0)) {
//When both ALT and ALT_GRAPH are set, add the ALT only
// modifier keystroke which is used for left ALT key.
// Unsetting the ALT_GRAPH will do that as ALT is already set
modifiers &= ~InputEvent.ALT_GRAPH_DOWN_MASK;
modifiers &= ~InputEvent.ALT_GRAPH_MASK;
KeyStroke keyStroke = KeyStroke.getKeyStroke(accelerator.getKeyCode(),
modifiers, accelerator.isOnKeyRelease());
windowInputMap.put(keyStroke, "doClick");
} else if (((modifiers & InputEvent.ALT_DOWN_MASK) != 0) && (
(modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) == 0)) {
//When only ALT modifier is set, add the ALT + ALT_GRAPH
// modifier keystroke which is used for right ALT key
modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;
KeyStroke keyStroke = KeyStroke.getKeyStroke(accelerator.getKeyCode(),
modifiers, accelerator.isOnKeyRelease());
windowInputMap.put(keyStroke, "doClick");
} else if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) {
//When only ALT_GRAPH is set, remove the ALT_GRAPH only
// modifier and add the ALT and ALT+ALT_GRAPH modifiers
// keystroke which are used for left ALT key and right ALT
// respectively
modifiers &= ~InputEvent.ALT_GRAPH_DOWN_MASK;
modifiers &= ~InputEvent.ALT_GRAPH_MASK;
modifiers |= InputEvent.ALT_DOWN_MASK;
KeyStroke keyStroke = KeyStroke.getKeyStroke(accelerator.getKeyCode(),
modifiers, accelerator.isOnKeyRelease());
windowInputMap.put(keyStroke, "doClick");
//Add ALT+ALT_GRAPH modifier which is used for right ALT key
modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;
keyStroke = KeyStroke.getKeyStroke(accelerator.getKeyCode(),
modifiers, accelerator.isOnKeyRelease());
windowInputMap.put(keyStroke, "doClick");
}
}
}
protected InputMap createInputMap(final int condition) {
if (condition == JComponent.WHEN_IN_FOCUSED_WINDOW) {
return new ComponentInputMapUIResource(menuItem);
}
return null;
}
protected void updateCheckIcon() {
String prefix = getPropertyPrefix();
if (checkIcon == null || checkIcon instanceof UIResource) {
checkIcon = UIManager.getIcon(prefix + ".checkIcon");
//In case of column layout, .checkIconFactory is defined for this UI,
//the icon is compatible with it and useCheckAndArrow() is true,
//then the icon is handled by the checkIcon.
boolean isColumnLayout = MenuItemLayoutHelper.isColumnLayout(
menuItem.getComponentOrientation().isLeftToRight(), menuItem);
if (isColumnLayout) {
MenuItemCheckIconFactory iconFactory =
(MenuItemCheckIconFactory) UIManager.get(prefix + ".checkIconFactory");
if (iconFactory != null
&& MenuItemLayoutHelper.useCheckAndArrow(menuItem)
&& iconFactory.isCompatible(checkIcon, prefix)) {
checkIcon = iconFactory.getIcon(menuItem);
}
}
}
}
private static class Actions extends UIAction {
private static final String CLICK = "doClick";
protected static class Actions extends UIAction {
protected static final String CLICK = "doClick";
Actions(final String key) {
super(key);
@ -364,129 +255,4 @@ public class DarkMenuItemUIBase extends BasicMenuItemUI {
mi.doClick();
}
}
protected class Handler implements MenuDragMouseListener, MouseInputListener, PropertyChangeListener {
//
// MouseInputListener
//
public void mouseClicked(final MouseEvent e) {
}
public void mousePressed(final MouseEvent e) {
}
public void mouseReleased(final MouseEvent e) {
if (!menuItem.isEnabled()) {
return;
}
MenuSelectionManager manager =
MenuSelectionManager.defaultManager();
Point p = e.getPoint();
if (p.x >= 0 && p.x < menuItem.getWidth() &&
p.y >= 0 && p.y < menuItem.getHeight()) {
doClick(manager);
} else {
manager.processMouseEvent(e);
}
}
@SuppressWarnings("deprecation")
public void mouseEntered(@NotNull final MouseEvent e) {
MenuSelectionManager manager = MenuSelectionManager.defaultManager();
int modifiers = e.getModifiers();
// 4188027: drag enter/exit added in JDK 1.1.7A, JDK1.2
if ((modifiers & (InputEvent.BUTTON1_MASK |
InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK)) != 0) {
MenuSelectionManager.defaultManager().processMouseEvent(e);
} else {
manager.setSelectedPath(getPath());
}
}
@SuppressWarnings("deprecation")
public void mouseExited(@NotNull final MouseEvent e) {
MenuSelectionManager manager = MenuSelectionManager.defaultManager();
int modifiers = e.getModifiers();
// 4188027: drag enter/exit added in JDK 1.1.7A, JDK1.2
if ((modifiers & (InputEvent.BUTTON1_MASK |
InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK)) != 0) {
MenuSelectionManager.defaultManager().processMouseEvent(e);
} else {
MenuElement[] path = manager.getSelectedPath();
if (path.length > 1 && path[path.length - 1] == menuItem) {
MenuElement[] newPath = new MenuElement[path.length - 1];
int i, c;
for (i = 0, c = path.length - 1; i < c; i++) { newPath[i] = path[i]; }
manager.setSelectedPath(newPath);
}
}
}
public void mouseDragged(final MouseEvent e) {
MenuSelectionManager.defaultManager().processMouseEvent(e);
}
public void mouseMoved(final MouseEvent e) {
}
//
// MenuDragListener
//
public void menuDragMouseEntered(@NotNull final MenuDragMouseEvent e) {
MenuSelectionManager manager = e.getMenuSelectionManager();
MenuElement[] path = e.getPath();
manager.setSelectedPath(path);
}
public void menuDragMouseExited(final MenuDragMouseEvent e) {
}
public void menuDragMouseDragged(@NotNull final MenuDragMouseEvent e) {
MenuSelectionManager manager = e.getMenuSelectionManager();
MenuElement[] path = e.getPath();
manager.setSelectedPath(path);
}
public void menuDragMouseReleased(final MenuDragMouseEvent e) {
if (!menuItem.isEnabled()) {
return;
}
MenuSelectionManager manager = e.getMenuSelectionManager();
e.getPath();
Point p = e.getPoint();
if (p.x >= 0 && p.x < menuItem.getWidth() &&
p.y >= 0 && p.y < menuItem.getHeight()) {
doClick(manager);
} else {
manager.clearSelectedPath();
}
}
//
// PropertyChangeListener
//
public void propertyChange(final PropertyChangeEvent e) {
String name = e.getPropertyName();
if (Objects.equals(name, "labelFor") || Objects.equals(name, "displayedMnemonic") ||
Objects.equals(name, "accelerator")) {
updateAcceleratorBinding();
} else if (Objects.equals(name, "text") || "font".equals(name) || "foreground".equals(name)
|| SwingUtilities2.isScaleChanged(e)) {
// remove the old html view client property if one
// existed, and install a new one if the text installed
// into the JLabel is html source.
JMenuItem lbl = ((JMenuItem) e.getSource());
String text = lbl.getText();
BasicHTML.updateRenderer(lbl, text);
} else if (Objects.equals(name, "iconTextGap")) {
defaultTextIconGap = ((Number) e.getNewValue()).intValue();
} else if (Objects.equals(name, "horizontalTextPosition")) {
updateCheckIcon();
}
}
}
}

805
src/main/java/com/weis/darklaf/ui/menu/DarkMenuUI.java

@ -1,688 +1,245 @@
/*
* MIT License
*
* Copyright (c) 2019 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.weis.darklaf.ui.menu;
import com.weis.darklaf.util.DarkUIUtil;
import com.weis.darklaf.util.LazyActionMap;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import sun.swing.DefaultLookup;
import sun.swing.UIAction;
import sun.swing.MenuItemLayoutHelper;
import sun.swing.SwingUtilities2;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.MenuDragMouseEvent;
import javax.swing.event.MenuDragMouseListener;
import javax.swing.event.MenuKeyEvent;
import javax.swing.event.MenuKeyListener;
import javax.swing.event.MenuListener;
import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicMenuUI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
/**
* Code taken from {@link BasicMenuUI}
*/
public class DarkMenuUI extends DarkMenuItemUIBase {
public class DarkMenuUI extends BasicMenuUI {
/* diagnostic aids -- should be false for production builds. */
private static final boolean TRACE = false; // trace creates and disposes
private static final boolean VERBOSE = false; // show reuse hits/misses
private static final boolean DEBUG = false; // show bad params, misc.
private static boolean crossMenuMnemonic = true;
/**
* The instance of {@code ChangeListener}.
*/
protected ChangeListener changeListener;
/**
* The instance of {@code MenuListener}.
*/
protected MenuListener menuListener;
private int lastMnemonic = 0;
/**
* Uses as the parent of the windowInputMap when selected.
*/
private InputMap selectedWindowInputMap;
protected Icon arrowIconHover;
/**
* Constructs a new instance of {@code BasicMenuUI}.
*
* @param x a component
* @return a new instance of {@code BasicMenuUI}
*/
@NotNull
@Contract(value = "_ -> new", pure = true)
public static ComponentUI createUI(final JComponent x) {
return new DarkMenuUI();
}
protected static void loadActionMap(@NotNull final LazyActionMap map) {
loadActionMap(map);
map.put(new Actions(Actions.SELECT, null, true));
}
private static void appendPath(@NotNull final MenuElement[] path, final MenuElement elem) {
MenuElement[] newPath = new MenuElement[path.length + 1];
System.arraycopy(path, 0, newPath, 0, path.length);
newPath[path.length] = elem;
MenuSelectionManager.defaultManager().setSelectedPath(newPath);
}
@NotNull
protected static java.util.List<JPopupMenu> getPopups() {
MenuSelectionManager msm = MenuSelectionManager.defaultManager();
MenuElement[] p = msm.getSelectedPath();
java.util.List<JPopupMenu> list = new ArrayList<JPopupMenu>(p.length);
for (MenuElement element : p) {
if (element instanceof JPopupMenu) {
list.add((JPopupMenu) element);
}
}
return list;
}
protected static JPopupMenu getLastPopup() {
MenuSelectionManager msm = MenuSelectionManager.defaultManager();
MenuElement[] p = msm.getSelectedPath();
JPopupMenu popup = null;
for (int i = p.length - 1; popup == null && i >= 0; i--) {
if (p[i] instanceof JPopupMenu) { popup = (JPopupMenu) p[i]; }
}
return popup;
}
@Override
protected void installDefaults() {
super.installDefaults();
updateDefaultBackgroundColor();
((JMenu) menuItem).setDelay(200);
crossMenuMnemonic = UIManager.getBoolean("Menu.crossMenuMnemonic");
}
protected String getPropertyPrefix() {
return "Menu";
}
protected void installListeners() {
super.installListeners();
if (changeListener == null) { changeListener = createChangeListener(menuItem); }
if (changeListener != null) { menuItem.addChangeListener(changeListener); }
if (menuListener == null) { menuListener = createMenuListener(menuItem); }
if (menuListener != null) { ((JMenu) menuItem).addMenuListener(menuListener); }
}
protected void installKeyboardActions() {
super.installKeyboardActions();
updateMnemonicBinding();
}
@SuppressWarnings("deprecation")
void updateMnemonicBinding() {
int mnemonic = menuItem.getModel().getMnemonic();
int[] shortcutKeys = (int[]) DefaultLookup.get(menuItem, this,
"Menu.shortcutKeys");
if (shortcutKeys == null) {
shortcutKeys = new int[]{KeyEvent.ALT_MASK,
KeyEvent.ALT_MASK | KeyEvent.ALT_GRAPH_MASK};
}
if (mnemonic == lastMnemonic) {
return;
}
InputMap windowInputMap = SwingUtilities.getUIInputMap(
menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW);
if (lastMnemonic != 0 && windowInputMap != null) {
for (int shortcutKey : shortcutKeys) {
windowInputMap.remove(KeyStroke.getKeyStroke
(lastMnemonic, shortcutKey, false));
}
}
if (mnemonic != 0) {
if (windowInputMap == null) {
windowInputMap = createInputMap(JComponent.
WHEN_IN_FOCUSED_WINDOW);
SwingUtilities.replaceUIInputMap(menuItem, JComponent.
WHEN_IN_FOCUSED_WINDOW, windowInputMap);
}
for (int shortcutKey : shortcutKeys) {
windowInputMap.put(KeyStroke.getKeyStroke(mnemonic,
shortcutKey, false), "selectMenu");
}
}
lastMnemonic = mnemonic;
}
protected void uninstallDefaults() {
menuItem.setArmed(false);
menuItem.setSelected(false);
menuItem.resetKeyboardActions();
super.uninstallDefaults();
acceleratorFont = UIManager.getFont("MenuItem.font");
acceleratorForeground = UIManager.getColor("MenuItem.foreground");
acceleratorSelectionForeground = UIManager.getColor("MenuItem.selectionForeground");
arrowIconHover = UIManager.getIcon("MenuItem.arrowHover.icon");
}
protected void uninstallListeners() {
super.uninstallListeners();
if (changeListener != null) { menuItem.removeChangeListener(changeListener); }
if (menuListener != null) { ((JMenu) menuItem).removeMenuListener(menuListener); }
changeListener = null;
menuListener = null;
handler = null;
}
protected void uninstallKeyboardActions() {
super.uninstallKeyboardActions();
lastMnemonic = 0;
}
protected MouseInputListener createMouseInputListener(final JComponent c) {
return getHandler();
}
protected DarkMenuItemUIBase.Handler getHandler() {
if (handler == null) {
handler = new DarkMenuUI.Handler();
}
return handler;
}
protected MenuDragMouseListener createMenuDragMouseListener(final JComponent c) {
return getHandler();
}
protected MenuKeyListener createMenuKeyListener(final JComponent c) {
return (MenuKeyListener) getHandler();
}
protected PropertyChangeListener createPropertyChangeListener(final JComponent c) {
return getHandler();
}
public Dimension getMinimumSize(final JComponent c) {
return (((JMenu) menuItem).isTopLevelMenu()) ?
c.getPreferredSize() : null;
@Override
public void paint(final Graphics g, final JComponent c) {
paintMenuItem(g, c, checkIcon, getArrowIcon(),
selectionBackground, selectionForeground,
defaultTextIconGap);
}
public Dimension getMaximumSize(final JComponent c) {
if (((JMenu) menuItem).isTopLevelMenu()) {
Dimension d = c.getPreferredSize();
return new Dimension(d.width, Short.MAX_VALUE);
}
return null;
}
/**
* Returns an instance of {@code ChangeListener}.
*
* @param c a component
* @return an instance of {@code ChangeListener}
*/
protected ChangeListener createChangeListener(final JComponent c) {
return null;
}
/**
* Returns an instance of {@code MenuListener}.
*
* @param c a component
* @return an instance of {@code MenuListener}
*/
protected MenuListener createMenuListener(final JComponent c) {
return null;
protected Icon getArrowIcon() {
boolean hover = menuItem.getModel().isArmed()
|| (menuItem instanceof JMenu && menuItem.getModel().isSelected());
return hover ? arrowIconHover : arrowIcon;
}
/*
* Set the background color depending on whether this is a toplevel menu
* in a menubar or a submenu of another menu.
*/
private void updateDefaultBackgroundColor() {
if (!UIManager.getBoolean("Menu.useMenuBarBackgroundForTopLevel")) {
return;
}
JMenu menu = (JMenu) menuItem;
if (menu.getBackground() instanceof UIResource) {
if (menu.isTopLevelMenu()) {
menu.setBackground(UIManager.getColor("MenuBar.background"));
protected void paintCheckIcon(final Graphics g, @NotNull final MenuItemLayoutHelper lh,
final MenuItemLayoutHelper.LayoutResult lr,
final Color holdc, final Color foreground) {
if (lh.getCheckIcon() != null) {
ButtonModel model = lh.getMenuItem().getModel();
if (model.isArmed() || (lh.getMenuItem() instanceof JMenu
&& model.isSelected())) {
g.setColor(foreground);
} else {
menu.setBackground(UIManager.getColor(getPropertyPrefix() + ".background"));
g.setColor(holdc);
}
if (lh.useCheckAndArrow()) {
lh.getCheckIcon().paintIcon(lh.getMenuItem(), g,
lr.getCheckRect().x, lr.getCheckRect().y);
}
g.setColor(holdc);
}
}
void installLazyActionMap() {
LazyActionMap.installLazyActionMap(menuItem, BasicMenuUI.class,
getPropertyPrefix() + ".actionMap");
}
/**
* Sets timer to the {@code menu}.
*
* @param menu an instance of {@code JMenu}.
*/
protected void setupPostTimer(@NotNull final JMenu menu) {
Timer timer = new Timer(menu.getDelay(), new Actions(Actions.SELECT, menu, false));
timer.setRepeats(false);
timer.start();
}
private static class Actions extends UIAction {
private static final String SELECT = "selectMenu";
// NOTE: This will be null if the action is registered in the
// ActionMap. For the timer use it will be non-null.
private JMenu menu;
private boolean force = false;
Actions(final String key, final JMenu menu, final boolean shouldForce) {
super(key);
this.menu = menu;
this.force = shouldForce;
}
public void actionPerformed(final ActionEvent e) {
JMenu menu = getMenu(e);
if (!crossMenuMnemonic) {
JPopupMenu pm = getLastPopup();
if (pm != null && pm != menu.getParent()) {
return;
}
}
final MenuSelectionManager defaultManager = MenuSelectionManager.defaultManager();
if (force) {
Container cnt = menu.getParent();
if (cnt instanceof JMenuBar) {
MenuElement[] me;
MenuElement[] subElements;
subElements = menu.getPopupMenu().getSubElements();
if (subElements.length > 0) {
me = new MenuElement[4];
me[0] = (MenuElement) cnt;
me[1] = menu;
me[2] = menu.getPopupMenu();
me[3] = subElements[0];
} else {
me = new MenuElement[3];
me[0] = (MenuElement) cnt;
me[1] = menu;
me[2] = menu.getPopupMenu();
}
defaultManager.setSelectedPath(me);
protected void paintIcon(final Graphics g, @NotNull final MenuItemLayoutHelper lh,
final MenuItemLayoutHelper.LayoutResult lr, final Color holdc) {
if (lh.getIcon() != null) {
Icon icon;
ButtonModel model = lh.getMenuItem().getModel();
if (!model.isEnabled()) {
icon = lh.getMenuItem().getDisabledIcon();
} else if (model.isPressed() && model.isArmed()) {
icon = lh.getMenuItem().getPressedIcon();
if (icon == null) {
// Use default icon
icon = lh.getMenuItem().getIcon();
}
} else {
MenuElement[] path = defaultManager.getSelectedPath();
if (path.length > 0 && path[path.length - 1] == menu) {
appendPath(path, menu.getPopupMenu());
}
icon = lh.getMenuItem().getIcon();
}
}
private JMenu getMenu(final ActionEvent e) {
if (e.getSource() instanceof JMenu) {
return (JMenu) e.getSource();
if (icon != null) {
icon.paintIcon(lh.getMenuItem(), g, lr.getIconRect().x, lr.getIconRect().y);
g.setColor(holdc);
}
return menu;
}
@Override
public boolean accept(final Object c) {
if (c instanceof JMenu) {
return ((JMenu) c).isEnabled();
}
return true;
}
}
/**
* Instantiated and used by a menu item to handle the current menu selection
* from mouse events. A MouseInputHandler processes and forwards all mouse events
* to a shared instance of the MenuSelectionManager.
* <p>
* This class is protected so that it can be subclassed by other look and
* feels to implement their own mouse handling behavior. All overridden
* methods should call the parent methods so that the menu selection
* is correct.
*
* @see javax.swing.MenuSelectionManager
* @since 1.4
*/
protected class MouseInputHandler implements MouseInputListener {
// NOTE: This class exists only for backward compatibility. All
// its functionality has been moved into Handler. If you need to add
// new functionality add it to the Handler, but make sure this
// class calls into the Handler.
public void mouseClicked(final MouseEvent e) {
getHandler().mouseClicked(e);
}
/**
* Invoked when the mouse has been clicked on the menu. This
* method clears or sets the selection path of the
* MenuSelectionManager.
*
* @param e the mouse event
*/
public void mousePressed(final MouseEvent e) {
getHandler().mousePressed(e);
}
/**
* Invoked when the mouse has been released on the menu. Delegates the
* mouse event to the MenuSelectionManager.
*
* @param e the mouse event
*/
public void mouseReleased(final MouseEvent e) {
getHandler().mouseReleased(e);
}
/**
* Invoked when the cursor enters the menu. This method sets the selected
* path for the MenuSelectionManager and handles the case
* in which a menu item is used to pop up an additional menu, as in a
* hierarchical menu system.
*
* @param e the mouse event; not used
*/
public void mouseEntered(final MouseEvent e) {
getHandler().mouseEntered(e);
}
public void mouseExited(final MouseEvent e) {
getHandler().mouseExited(e);
}
/**
* Invoked when a mouse button is pressed on the menu and then dragged.
* Delegates the mouse event to the MenuSelectionManager.
*
* @param e the mouse event
* @see java.awt.event.MouseMotionListener#mouseDragged
*/
public void mouseDragged(final MouseEvent e) {
getHandler().mouseDragged(e);
}
public void mouseMoved(final MouseEvent e) {
getHandler().mouseMoved(e);
}
}
/**
* As of Java 2 platform 1.4, this previously undocumented class
* is now obsolete. KeyBindings are now managed by the popup menu.
*/
public class ChangeHandler implements ChangeListener {
/**
* The instance of {@code JMenu}.
*/
public JMenu menu;
/**
* The instance of {@code BasicMenuUI}.
*/
public BasicMenuUI ui;
/**
* {@code true} if an item of popup menu is selected.
*/
public boolean isSelected = false;
/**
* The component that was focused.
*/
public Component wasFocused;
/**
* Constructs a new instance of {@code ChangeHandler}.
*
* @param m an instance of {@code JMenu}
* @param ui an instance of {@code BasicMenuUI}
*/
public ChangeHandler(final JMenu m, final BasicMenuUI ui) {
menu = m;
this.ui = ui;
}
public void stateChanged(final ChangeEvent e) {
protected void paintText(final Graphics g, @NotNull final MenuItemLayoutHelper lh,
final MenuItemLayoutHelper.LayoutResult lr) {
if (!lh.getText().isBlank()) {
if (lh.getHtmlView() != null) {
// Text is HTML
lh.getHtmlView().paint(g, lr.getTextRect());
} else {
// Text isn't HTML
paintText(g, lh.getMenuItem(), lr.getTextRect(), lh.getText());
}
}
}
private class Handler extends DarkMenuItemUIBase.Handler implements MenuKeyListener {
//
// MouseInputListener
//
public void mouseClicked(final MouseEvent e) {
}
/**
* Invoked when the mouse has been clicked on the menu. This
* method clears or sets the selection path of the
* MenuSelectionManager.
*
* @param e the mouse event
*/
public void mousePressed(final MouseEvent e) {
JMenu menu = (JMenu) menuItem;
if (!menu.isEnabled()) { return; }
MenuSelectionManager manager = MenuSelectionManager.defaultManager();
if (menu.isTopLevelMenu()) {
if (menu.isSelected() && menu.getPopupMenu().isShowing()) {
manager.clearSelectedPath();
protected void paintAccText(final Graphics g, final MenuItemLayoutHelper lh,
final MenuItemLayoutHelper.LayoutResult lr) {
rightAlignAccText(lh, lr);
if (!lh.getAccText().isBlank()) {
ButtonModel model = lh.getMenuItem().getModel();
g.setFont(lh.getAccFontMetrics().getFont());
if (!model.isEnabled()) {
// *** paint the accText disabled
if (disabledForeground != null) {
g.setColor(disabledForeground);
SwingUtilities2.drawString(lh.getMenuItem(), g,
lh.getAccText(), lr.getAccRect().x,
lr.getAccRect().y + lh.getAccFontMetrics().getAscent());
} else {
Container cnt = menu.getParent();
if (cnt instanceof JMenuBar) {
MenuElement[] me = new MenuElement[2];
me[0] = (MenuElement) cnt;
me[1] = menu;
manager.setSelectedPath(me);
}
g.setColor(lh.getMenuItem().getBackground().brighter());
SwingUtilities2.drawString(lh.getMenuItem(), g,
lh.getAccText(), lr.getAccRect().x,
lr.getAccRect().y + lh.getAccFontMetrics().getAscent());
g.setColor(lh.getMenuItem().getBackground().darker());
SwingUtilities2.drawString(lh.getMenuItem(), g,
lh.getAccText(), lr.getAccRect().x - 1,
lr.getAccRect().y + lh.getFontMetrics().getAscent() - 1);
}
}
MenuElement[] selectedPath = manager.getSelectedPath();
if (selectedPath.length > 0 && selectedPath[selectedPath.length - 1] != menu.getPopupMenu()) {
if (menu.isTopLevelMenu() || menu.getDelay() == 0) {
appendPath(selectedPath, menu.getPopupMenu());
} else {
// *** paint the accText normally
if (model.isArmed()
|| (lh.getMenuItem() instanceof JMenu
&& model.isSelected())) {
g.setColor(acceleratorSelectionForeground);
} else {
setupPostTimer(menu);
g.setColor(acceleratorForeground);
}
SwingUtilities2.drawString(lh.getMenuItem(), g, lh.getAccText(),
lr.getAccRect().x, lr.getAccRect().y +
lh.getAccFontMetrics().getAscent());
}
}
}
/**
* Invoked when the mouse has been released on the menu. Delegates the
* mouse event to the MenuSelectionManager.
*
* @param e the mouse event
*/
public void mouseReleased(final MouseEvent e) {
JMenu menu = (JMenu) menuItem;
if (!menu.isEnabled()) { return; }
MenuSelectionManager manager = MenuSelectionManager.defaultManager();
manager.processMouseEvent(e);
if (!e.isConsumed()) { manager.clearSelectedPath(); }
}
/**
* Invoked when the cursor enters the menu. This method sets the selected
* path for the MenuSelectionManager and handles the case
* in which a menu item is used to pop up an additional menu, as in a
* hierarchical menu system.
*
* @param e the mouse event; not used
*/
public void mouseEntered(final MouseEvent e) {
JMenu menu = (JMenu) menuItem;
// only disable the menu highlighting if it's disabled and the property isn't
// true. This allows disabled rollovers to work in WinL&F
if (!menu.isEnabled() && !UIManager.getBoolean("MenuItem.disabledAreNavigable")) {
return;
protected void paintArrowIcon(final Graphics g, @NotNull final MenuItemLayoutHelper lh,
final MenuItemLayoutHelper.LayoutResult lr,
final Color foreground) {
if (lh.getArrowIcon() != null) {
ButtonModel model = lh.getMenuItem().getModel();
if (model.isArmed() || (lh.getMenuItem() instanceof JMenu
&& model.isSelected())) {
g.setColor(foreground);
}
MenuSelectionManager manager = MenuSelectionManager.defaultManager();
MenuElement[] selectedPath = manager.getSelectedPath();
if (!menu.isTopLevelMenu()) {
if (!(selectedPath.length > 0 && selectedPath[selectedPath.length - 1] == menu.getPopupMenu())) {
if (menu.getDelay() == 0) {
appendPath(getPath(), menu.getPopupMenu());
} else {
manager.setSelectedPath(getPath());
setupPostTimer(menu);
}
}
} else {
if (selectedPath.length > 0 && selectedPath[0] == menu.getParent()) {
MenuElement[] newPath = new MenuElement[3];
// A top level menu's parent is by definition
// a JMenuBar
newPath[0] = (MenuElement) menu.getParent();
newPath[1] = menu;
if (getLastPopup() != null) {
newPath[2] = menu.getPopupMenu();
}
manager.setSelectedPath(newPath);
}
if (lh.useCheckAndArrow()) {
lh.getArrowIcon().paintIcon(lh.getMenuItem(), g,
lr.getArrowRect().x, lr.getArrowRect().y);
}
}
}
public void mouseExited(final MouseEvent e) {
}
/**
* Invoked when a mouse button is pressed on the menu and then dragged.
* Delegates the mouse event to the MenuSelectionManager.
*
* @param e the mouse event
* @see java.awt.event.MouseMotionListener#mouseDragged
*/
public void mouseDragged(final MouseEvent e) {
JMenu menu = (JMenu) menuItem;
if (!menu.isEnabled()) { return; }
MenuSelectionManager.defaultManager().processMouseEvent(e);
private static void rightAlignAccText(@NotNull final MenuItemLayoutHelper lh,
@NotNull final MenuItemLayoutHelper.LayoutResult lr) {
var accRect = lr.getAccRect();
ButtonModel model = lh.getMenuItem().getModel();
if (model.isEnabled()) {
accRect.x = lh.getViewRect().x + lh.getViewRect().width
- lh.getMenuItem().getIconTextGap() - lr.getAccRect().width;
}
}
public void mouseMoved(final MouseEvent e) {
}
protected void paintMenuItem(@NotNull final Graphics g, final JComponent c,
final Icon checkIcon, final Icon arrowIcon,
final Color background, final Color foreground,
final int defaultTextIconGap) {
// Save original graphics font and color
Font holdf = g.getFont();
Color holdc = g.getColor();
//
// MenuDragHandler
//
public void menuDragMouseEntered(final MenuDragMouseEvent e) {
}
JMenuItem mi = (JMenuItem) c;
g.setFont(mi.getFont());
public void menuDragMouseDragged(final MenuDragMouseEvent e) {
if (!menuItem.isEnabled()) return;
Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight());
DarkUIUtil.applyInsets(viewRect, mi.getInsets());
MenuSelectionManager manager = e.getMenuSelectionManager();
MenuElement[] path = e.getPath();
MenuItemLayoutHelper lh = new MenuItemLayoutHelper(mi, checkIcon,
arrowIcon, viewRect, defaultTextIconGap, acceleratorDelimiter,
mi.getComponentOrientation().isLeftToRight(), mi.getFont(),
acceleratorFont, MenuItemLayoutHelper.useCheckAndArrow(menuItem),
getPropertyPrefix());
MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem();
Point p = e.getPoint();
if (p.x >= 0 && p.x < menuItem.getWidth() &&
p.y >= 0 && p.y < menuItem.getHeight()) {
JMenu menu = (JMenu) menuItem;
MenuElement[] selectedPath = manager.getSelectedPath();
if (!(selectedPath.length > 0 &&
selectedPath[selectedPath.length - 1] ==
menu.getPopupMenu())) {
if (menu.isTopLevelMenu() ||
menu.getDelay() == 0 ||
e.getID() == MouseEvent.MOUSE_DRAGGED) {
appendPath(path, menu.getPopupMenu());
} else {
manager.setSelectedPath(path);
setupPostTimer(menu);
}
}
} else if (e.getID() == MouseEvent.MOUSE_RELEASED) {
Component comp = manager.componentForPoint(e.getComponent(), e.getPoint());
if (comp == null) { manager.clearSelectedPath(); }
}
}
paintBackground(g, mi, background);
paintCheckIcon(g, lh, lr, holdc, foreground);
paintIcon(g, lh, lr, holdc);
g.setColor(foreground);
paintText(g, lh, lr);
paintAccText(g, lh, lr);
paintArrowIcon(g, lh, lr, foreground);
public void menuDragMouseExited(final MenuDragMouseEvent e) {
}
// Restore original graphics font and color
g.setColor(holdc);
g.setFont(holdf);
}
public void menuDragMouseReleased(final MenuDragMouseEvent e) {
}
@Override
protected void paintBackground(@NotNull final Graphics g, @NotNull final JMenuItem menuItem, final Color bgColor) {
ButtonModel model = menuItem.getModel();
Color oldColor = g.getColor();
int menuWidth = menuItem.getWidth();
int menuHeight = menuItem.getHeight() + 1;
//
// PropertyChangeListener
//
public void propertyChange(final PropertyChangeEvent e) {
if (Objects.equals(e.getPropertyName(), AbstractButton.
MNEMONIC_CHANGED_PROPERTY)) {
updateMnemonicBinding();
boolean parentOpaque = menuItem.getParent().isOpaque();
if (menuItem.isOpaque() && parentOpaque) {
if (model.isArmed() || (menuItem instanceof JMenu && model.isSelected())) {
g.setColor(bgColor);
g.fillRect(0, 0, menuWidth, menuHeight);
} else {
if (e.getPropertyName().equals("ancestor")) {
updateDefaultBackgroundColor();
}
super.propertyChange(e);
}
}
//
// MenuKeyListener
//
/**
* Open the Menu
*/
public void menuKeyTyped(final MenuKeyEvent e) {
if (!crossMenuMnemonic && getLastPopup() != null) {
// when crossMenuMnemonic is not set, we don't open a toplevel
// menu if another toplevel menu is already open
return;
}
if (getPopups().size() != 0) {
//Fix 6939261: to return in case not on the main menu
//and has a pop-up.
//after return code will be handled in BasicPopupMenuUI.java
return;
}
char key = Character.toLowerCase((char) menuItem.getMnemonic());
MenuElement[] path = e.getPath();
if (key == Character.toLowerCase(e.getKeyChar())) {
JPopupMenu popupMenu = ((JMenu) menuItem).getPopupMenu();
ArrayList<MenuElement> newList = new ArrayList<>(Arrays.asList(path));
newList.add(popupMenu);
MenuElement[] subs = popupMenu.getSubElements();
MenuElement sub = DarkUIUtil.findEnabledChild(subs, -1, true);
if (sub != null) {
newList.add(sub);
}
MenuSelectionManager manager = e.getMenuSelectionManager();
MenuElement[] newPath = new MenuElement[0];
newPath = newList.toArray(newPath);
manager.setSelectedPath(newPath);
e.consume();
}
}
public void menuKeyPressed(final MenuKeyEvent e) {
}
public void menuKeyReleased(final MenuKeyEvent e) {
g.setColor(menuItem.getBackground());
g.fillRect(0, 0, menuWidth, menuHeight);
}
g.setColor(oldColor);
} else if (model.isArmed() || (menuItem instanceof JMenu &&
model.isSelected())) {
g.setColor(bgColor);
g.fillRect(0, 0, menuWidth, menuHeight);
g.setColor(oldColor);
}
}
}

26
src/main/java/com/weis/darklaf/ui/scrollpane/DarkScrollBarUI.java

@ -28,7 +28,6 @@ import com.weis.darklaf.util.Animator;
import com.weis.darklaf.util.DarkUIUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
@ -71,9 +70,13 @@ public class DarkScrollBarUI extends BasicScrollBarUI {
if (scrollbar.getOrientation() == VERTICAL && !e.isShiftDown()
|| scrollbar.getOrientation() == HORIZONTAL && e.isShiftDown()) {
scrollbar.setValueIsAdjusting(true);
var sp = scrollbar.getClientProperty("JScrollBar.scrollPaneParent");
if (scrollbar.getParent() instanceof JScrollPane) {
doScroll(scrollbar, ((JScrollPane) scrollbar.getParent()).getViewport(), e,
scrollbar.getParent().getComponentOrientation().isLeftToRight());
} else if (sp instanceof JScrollPane) {
doScroll(scrollbar, ((JScrollPane) sp).getViewport(), e,
scrollbar.getParent().getComponentOrientation().isLeftToRight());
} else {
doScroll(scrollbar, null, e, scrollbar.getComponentOrientation().isLeftToRight());
}
@ -88,18 +91,15 @@ public class DarkScrollBarUI extends BasicScrollBarUI {
private Animator thumbFadeinAnimator;
private boolean mouseOverTrack = false;
private boolean mouseOverThumb = false;
private final MouseMotionListener mouseMotionListener = new MouseMovementListener() {
@Override
public void mouseMoved(@Nullable final MouseEvent e) {
if (e == null) {
return;
}
boolean overThumb = isOverThumb(e.getPoint());
if (overThumb != mouseOverThumb) {
mouseOverThumb = overThumb;
if (!scrollbar.getValueIsAdjusting()) {
resetThumbAnimator();
}
private final MouseMotionListener mouseMotionListener = (MouseMovementListener) e -> {
if (e == null) {
return;
}
boolean overThumb = isOverThumb(e.getPoint());
if (overThumb != mouseOverThumb) {
mouseOverThumb = overThumb;
if (!scrollbar.getValueIsAdjusting()) {
resetThumbAnimator();
}
}
};

1
src/main/java/com/weis/darklaf/ui/tabframe/DarkTabFrameTabLabelUI.java

@ -151,7 +151,6 @@ public class DarkTabFrameTabLabelUI extends DarkLabelUI implements PropertyChang
if (tabFrame == null) return;
int acc = tabComponent.getAccelerator();
if (acc < 0) return;
System.out.println("install");
tabFrame.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
.put(KeyStroke.getKeyStroke(UIManager.getString("TabFrame.acceleratorKeyCode") + " " + acc),
"accelerator_" + acc);

2189
src/main/java/com/weis/darklaf/ui/table/BasicTableUIBridge.java

File diff suppressed because it is too large Load Diff

78
src/main/java/com/weis/darklaf/ui/table/DarkTableCellEditor.java

@ -23,6 +23,7 @@ import java.util.EventObject;
public class DarkTableCellEditor extends DefaultCellEditor {
private static final JCheckBox dummyCheckBox = new JCheckBox();
private static final IconWrapper iconWrapper = new IconWrapper();
private final DarkTableCellEditorToggleButton checkBoxEditor =
new DarkTableCellEditorToggleButton(this, new DarkTableCellEditorToggleButton.CellCheckBox());
@ -39,6 +40,7 @@ public class DarkTableCellEditor extends DefaultCellEditor {
public DarkTableCellEditor(final JTextField textField) {
super(textField);
textField.setBorder(new TextFieldTableCellEditorBorder());
textField.putClientProperty("JTextField.isCellEditor", Boolean.TRUE);
setClickCountToStart(2);
}
@ -148,7 +150,8 @@ public class DarkTableCellEditor extends DefaultCellEditor {
}
@Override
public boolean isCellEditable(@NotNull final EventObject anEvent) {
public boolean isCellEditable(final EventObject anEvent) {
if (anEvent == null) return super.isCellEditable(anEvent);
var table = ((JTable) anEvent.getSource());
if (DarkTableCellRenderer.isBooleanRenderingEnabled(table) && anEvent instanceof MouseEvent) {
var p = ((MouseEvent) anEvent).getPoint();
@ -191,6 +194,8 @@ public class DarkTableCellEditor extends DefaultCellEditor {
delegate.setValue(value);
var comp = editorComponent;
if (editorComponent instanceof JComboBox) {
((JComboBox<?>) editorComponent).removeAllItems();
((JComboBox<Object>) editorComponent).addItem(value);
@ -207,19 +212,30 @@ public class DarkTableCellEditor extends DefaultCellEditor {
}
}
var rendererComp = table.getCellRenderer(row, column)
.getTableCellRendererComponent(table, value, isSelected, false, row, column);
if (rendererComp instanceof JLabel) {
var icon = ((JLabel) rendererComp).getIcon();
if (icon != null) {
comp = iconWrapper;
iconWrapper.init(editorComponent, icon, rendererComp.getComponentOrientation().isLeftToRight());
iconWrapper.setIconGap(((JLabel) rendererComp).getIconTextGap() - 1);
}
}
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);
comp.setBackground(alternativeRowColor);
} else {
editorComponent.setBackground(normalColor);
comp.setBackground(normalColor);
}
}
}
return editorComponent;
return comp;
}
@Contract("null, _ -> false")
@ -234,4 +250,58 @@ public class DarkTableCellEditor extends DefaultCellEditor {
}
return checkBoxEditor;
}
protected static class IconWrapper extends JPanel {
private final JLabel label;
private JComponent c;
private int iconGap;
protected IconWrapper() {
setLayout(null);
label = new JLabel();
label.setIconTextGap(0);
add(label);
}
protected void setIconGap(final int iconGap) {
this.iconGap = iconGap;
}
protected void init(@NotNull final JComponent component, final Icon icon, final boolean ltr) {
setComponentOrientation(ltr ? ComponentOrientation.LEFT_TO_RIGHT : ComponentOrientation.RIGHT_TO_LEFT);
if (c != null) {
remove(c);
}
add(component);
this.c = component;
label.setIcon(icon);
}
@Override
public void doLayout() {
if (c == null) return;
int w = getWidth();
int h = getHeight();
var b = c.getBorder();
var ins = new Insets(0, 0, 0, 0);
var labelSize = label.getPreferredSize();
int gap = getIconCompGap();
if (b != null) {
ins = b.getBorderInsets(c);
}
if (getComponentOrientation().isLeftToRight()) {
label.setBounds(ins.left + gap, 0, labelSize.width + 1, h);
c.setBounds(ins.left + labelSize.width + 2 * gap - 1, 0,
w - ins.left - labelSize.width - 2 * gap + 1, h);
} else {
c.setBounds(0, 0, w - ins.right - labelSize.width - gap - 1, h);
label.setBounds(w - ins.right - labelSize.width - gap - 1, 0, labelSize.width + 1, h);
}
}
public int getIconCompGap() {
return iconGap;
}
}
}

32
src/main/java/com/weis/darklaf/ui/table/DarkTableCellFocusBorder.java

@ -15,7 +15,35 @@ public class DarkTableCellFocusBorder extends DarkCellBorder {
public void paintBorder(final Component c, final Graphics g, final int x, final int y,
final int width, final int height) {
super.paintBorder(c, g, x, y, width, height);
g.setColor(UIManager.getColor("Table.focusBorderColor"));
DarkUIUtil.drawRect(g, 0, 0, width, height, 1);
if (isRowFocusBorder(c)) {
g.setColor(UIManager.getColor("Table.focusRowBorderColor"));
((Graphics2D) g).scale(0.5, 0.5);
g.drawRect(0, 0, 2 * width, 1);
g.drawRect(0, 2 * height - 2, 2 * width, 1);
if (forcePaintLeft(c)) {
g.drawRect(0, 0, 1, 2 * height);
}
if (forcePaintRight(c)) {
g.drawRect(2 * width - 2, 0, 1, 2 * height);
}
} else {
g.setColor(UIManager.getColor("Table.focusBorderColor"));
DarkUIUtil.drawRect(g, 0, 0, width, height, 1);
}
}
protected static boolean isRowFocusBorder(final Component c) {
return c instanceof JComponent
&& Boolean.TRUE.equals(((JComponent) c).getClientProperty("JTable.rowFocusBorder"));
}
protected static boolean forcePaintLeft(final Component c) {
return c instanceof JComponent
&& Boolean.TRUE.equals(((JComponent) c).getClientProperty("JTable.forcePaintLeft"));
}
protected static boolean forcePaintRight(final Component c) {
return c instanceof JComponent
&& Boolean.TRUE.equals(((JComponent) c).getClientProperty("JTable.forcePaintRight"));
}
}

24
src/main/java/com/weis/darklaf/ui/table/DarkTableCellRenderer.java

@ -1,11 +1,14 @@
package com.weis.darklaf.ui.table;
import com.weis.darklaf.ui.cell.DarkCellRendererToggleButton;
import com.weis.darklaf.util.DarkUIUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import java.awt.*;
/**
@ -34,6 +37,27 @@ public class DarkTableCellRenderer extends DefaultTableCellRenderer {
this.setVerticalAlignment(SwingConstants.CENTER);
setHorizontalAlignment(table.getComponentOrientation().isLeftToRight() ? LEFT : RIGHT);
if (DarkTableCellFocusBorder.isRowFocusBorder(table)
&& table.getSelectionModel().getLeadSelectionIndex() == row
&& !table.isEditing()
&& DarkUIUtil.hasFocus(table)) {
component.setBorder(UIManager.getBorder("Table.focusSelectedCellHighlightBorder"));
component.putClientProperty("JTable.rowFocusBorder", true);
JTableHeader header = table.getTableHeader();
TableColumn draggedColumn = (header == null) ? null : header.getDraggedColumn();
boolean forceLeft = false;
boolean forceRight = false;
if (draggedColumn != null) {
int index = DarkTableUI.viewIndexForColumn(draggedColumn, table);
forceLeft = column == index + 1 || column == index;
forceRight = column == index - 1 || column == index;
}
component.putClientProperty("JTable.forcePaintRight", forceRight);
component.putClientProperty("JTable.forcePaintLeft", forceLeft);
} else {
component.putClientProperty("JTable.rowFocusBorder", false);
}
boolean alternativeRow = UIManager.getBoolean("Table.alternateRowColor");
Color alternativeRowColor = UIManager.getColor("Table.alternateRowBackground");
Color normalColor = UIManager.getColor("Table.background");

6
src/main/java/com/weis/darklaf/ui/table/DarkTableHeaderUI.java

@ -164,6 +164,9 @@ public class DarkTableHeaderUI extends DarkTableHeaderUIBridge {
if (draggedColumnIndex != cMax) {
g.fillRect(draggedCellRect.x + draggedCellRect.width - 1, draggedCellRect.y,
1, draggedCellRect.height);
} else {
g.fillRect(draggedCellRect.x + draggedCellRect.width, draggedCellRect.y,
1, draggedCellRect.height);
}
if (draggedColumnIndex == cMin) {
g.fillRect(draggedCellRect.x, draggedCellRect.y, 1, draggedCellRect.height);
@ -188,6 +191,9 @@ public class DarkTableHeaderUI extends DarkTableHeaderUIBridge {
if (draggedColumnIndex != cMax) {
g.fillRect(draggedCellRect.x + draggedCellRect.width - 1, draggedCellRect.y,
1, draggedCellRect.height);
} else {
g.fillRect(draggedCellRect.x + draggedCellRect.width, draggedCellRect.y,
1, draggedCellRect.height);
}
} else {
if (draggedColumnIndex != cMin) {

341
src/main/java/com/weis/darklaf/ui/table/DarkTableUI.java

@ -1,5 +1,6 @@
package com.weis.darklaf.ui.table;
import com.weis.darklaf.util.DarkUIUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import sun.swing.SwingUtilities2;
@ -11,9 +12,12 @@ import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;
import java.util.function.Supplier;
/**
* @author Jannis Weis
@ -52,6 +56,22 @@ public class DarkTableUI extends DarkTableUIBridge {
} else if ("showVerticalLines".equals(key)) {
var b = (boolean) e.getNewValue();
table.getColumnModel().setColumnMargin(b ? 1 : 0);
} else if ("ancestor".equals(key)) {
var oldVal = e.getOldValue();
var newVal = e.getNewValue();
if (oldVal instanceof Component) {
var oldUnwrapped = SwingUtilities.getUnwrappedParent((Component) oldVal);
if ((oldUnwrapped instanceof JScrollPane)
&& ((JComponent) oldUnwrapped).getBorder() instanceof UIResource) {
LookAndFeel.uninstallBorder((JComponent) oldUnwrapped);
}
}
if (newVal instanceof Component) {
var newUnwrapped = SwingUtilities.getUnwrappedParent((Component) newVal);
if ((newUnwrapped instanceof JScrollPane)) {
LookAndFeel.installBorder((JComponent) newUnwrapped, "Table.scrollPaneBorder");
}
}
}
};
@ -61,53 +81,6 @@ public class DarkTableUI extends DarkTableUIBridge {
return new DarkTableUI();
}
@Override
protected void installDefaults() {
super.installDefaults();
table.setRowHeight(ROW_HEIGHT);
table.setDefaultEditor(Object.class, new DarkTableCellEditor());
table.putClientProperty("JTable.renderBooleanAsCheckBox",
UIManager.getBoolean("Table.renderBooleanAsCheckBox"));
table.putClientProperty("JTable.booleanRenderType", UIManager.getString("Table.booleanRenderType"));
setupRendererComponents(table);
}
protected static void setupRendererComponents(@NotNull final JTable table) {
var cellRenderer = new DarkTableCellRenderer();
var cellEditor = new DarkTableCellEditor();
var colorRendererEditor = new DarkColorTableCellRendererEditor();
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);
table.setDefaultRenderer(Color.class, colorRendererEditor);
table.setDefaultEditor(Object.class, cellEditor);
table.setDefaultEditor(String.class, cellEditor);
table.setDefaultEditor(Integer.class, cellEditor);
table.setDefaultEditor(Double.class, cellEditor);
table.setDefaultEditor(Float.class, cellEditor);
table.setDefaultEditor(Boolean.class, cellEditor);
table.setDefaultEditor(Color.class, colorRendererEditor);
}
@Override
protected void installListeners() {
super.installListeners();
table.addFocusListener(focusListener);
table.addPropertyChangeListener(propertyChangeListener);
}
@Override
protected void uninstallListeners() {
super.uninstallListeners();
table.removeFocusListener(focusListener);
table.removePropertyChangeListener(propertyChangeListener);
}
@Override
protected void paintGrid(@NotNull final Graphics g,
final int rMin, final int rMax, final int cMin, final int cMax) {
@ -140,7 +113,7 @@ public class DarkTableUI extends DarkTableUIBridge {
boolean scrollVisible = scrollBarVisible();
if (table.getShowVerticalLines()) {
TableColumnModel cm = table.getColumnModel();
int tableHeight = damagedArea.y + damagedArea.height;
int tableHeight = getPreferredSize(table).height;
int x;
boolean ltr = table.getComponentOrientation().isLeftToRight();
if (ltr) {
@ -169,16 +142,129 @@ public class DarkTableUI extends DarkTableUIBridge {
}
}
}
if (!table.getShowHorizontalLines() && table.getRowCount() != 0 && !scrollVisible) {
g.setColor(getBorderColor());
var clip = g.getClipBounds();
clip.height += 1;
g.setClip(clip);
int y = table.getHeight();
g.fillRect(0, y, table.getWidth(), 1);
}
protected static void setupRendererComponents(@NotNull final JTable table) {
var cellRenderer = new DarkTableCellRenderer();
var cellEditor = new DarkTableCellEditor();
var colorRendererEditor = new DarkColorTableCellRendererEditor();
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);
table.setDefaultRenderer(Color.class, colorRendererEditor);
table.setDefaultEditor(Object.class, cellEditor);
table.setDefaultEditor(String.class, cellEditor);
table.setDefaultEditor(Integer.class, cellEditor);
table.setDefaultEditor(Double.class, cellEditor);
table.setDefaultEditor(Float.class, cellEditor);
table.setDefaultEditor(Boolean.class, cellEditor);
table.setDefaultEditor(Color.class, colorRendererEditor);
}
@Override
protected void paintDraggedArea(@NotNull final Graphics g, final int rMin, final int rMax,
final int cMin, final int cMax,
final TableColumn draggedColumn, final int distance) {
int draggedColumnIndex = viewIndexForColumn(draggedColumn);
Rectangle minCell = table.getCellRect(rMin, draggedColumnIndex, true);
Rectangle maxCell = table.getCellRect(rMax, draggedColumnIndex, true);
Rectangle vacatedColumnRect = minCell.union(maxCell);
int dist = adjustDistance(distance, vacatedColumnRect, table);
// Paint a gray well in place of the moving column.
g.setColor(table.getParent().getBackground());
g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
vacatedColumnRect.width - 1, vacatedColumnRect.height);
// Move to the where the cell has been dragged.
vacatedColumnRect.x += dist;
boolean scrollVisible = scrollBarVisible();
boolean drawBottomBorder = !table.getShowHorizontalLines() && !scrollVisible && table.getShowVerticalLines();
boolean ltr = table.getComponentOrientation().isLeftToRight();
// Fill the background.
g.setColor(table.getBackground());
g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
vacatedColumnRect.width, vacatedColumnRect.height);
// Paint the vertical grid lines if necessary.
if (table.getShowVerticalLines()) {
g.setColor(table.getGridColor());
int x1 = vacatedColumnRect.x;
int y1 = vacatedColumnRect.y;
int x2 = x1 + vacatedColumnRect.width - 1;
int y2 = y1 + vacatedColumnRect.height - 1;
boolean onLeftEdge = ltr ? draggedColumnIndex == cMin : draggedColumnIndex == cMax;
boolean onRightEdge = ltr ? draggedColumnIndex == cMax : draggedColumnIndex == cMin;
if (scrollBarVisible()) {
if (isScrollPaneRtl()) {
onLeftEdge = false;
} else {
onRightEdge = false;
}
}
// Left
if (dist != 0 || !onLeftEdge) {
if (draggedColumnIndex == cMin && scrollBarVisible() && isScrollPaneRtl()) x1++;
g.fillRect(x1 - 1, y1, 1, y2 - y1);
}
// Right
if (dist != 0 || !onRightEdge) {
g.fillRect(x2, y1, 1, y2 - y1);
}
}
for (int row = rMin; row <= rMax; row++) {
// Render the cell value
Rectangle r = table.getCellRect(row, draggedColumnIndex, false);
r.x += dist;
paintCell(g, r, row, draggedColumnIndex);
// Paint the (lower) horizontal grid line if necessary.
if (table.getShowHorizontalLines() || (!scrollVisible && row == rMax)) {
g.setColor(table.getGridColor());
Rectangle rcr = table.getCellRect(row, draggedColumnIndex, true);
rcr.x += dist;
int x1 = rcr.x - 1;
int y1 = rcr.y;
int x2 = x1 + rcr.width + 1;
int y2 = y1 + rcr.height - 1;
g.fillRect(x1, y2, x2 - x1, 1);
}
}
}
@Override
protected void installListeners() {
super.installListeners();
table.addFocusListener(focusListener);
table.addPropertyChangeListener(propertyChangeListener);
}
@Override
protected void uninstallListeners() {
super.uninstallListeners();
table.removeFocusListener(focusListener);
table.removePropertyChangeListener(propertyChangeListener);
}
protected boolean pointOutsidePrefSize(final int row, final int column, final Point p) {
return false;
}
protected boolean isScrollPaneRtl() {
if (!isInScrollPane()) return false;
Container comp = SwingUtilities.getUnwrappedParent(table).getParent();
@ -300,91 +386,94 @@ public class DarkTableUI extends DarkTableUIBridge {
}
@Override
protected void paintDraggedArea(@NotNull final Graphics g, final int rMin, final int rMax,
final int cMin, final int cMax,
final TableColumn draggedColumn, final int distance) {
int draggedColumnIndex = viewIndexForColumn(draggedColumn);
Rectangle minCell = table.getCellRect(rMin, draggedColumnIndex, true);
Rectangle maxCell = table.getCellRect(rMax, draggedColumnIndex, true);
Rectangle vacatedColumnRect = minCell.union(maxCell);
int dist = adjustDistance(distance, vacatedColumnRect, table);
// Paint a gray well in place of the moving column.
g.setColor(table.getParent().getBackground());
g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
vacatedColumnRect.width - 1, vacatedColumnRect.height);
// Move to the where the cell has been dragged.
vacatedColumnRect.x += dist;
protected Handler getHandler() {
if (handler == null) {
handler = new DarkHandler();
}
return handler;
}
boolean scrollVisible = scrollBarVisible();
boolean drawBottomBorder = !table.getShowHorizontalLines() && !scrollVisible;
boolean ltr = table.getComponentOrientation().isLeftToRight();
@Override
protected void installDefaults() {
super.installDefaults();
int rowHeight = UIManager.getInt("Table.rowHeight");
if (rowHeight > 0) {
table.setRowHeight(ROW_HEIGHT);
}
table.setDefaultEditor(Object.class, new DarkTableCellEditor());
table.putClientProperty("JTable.renderBooleanAsCheckBox",
UIManager.getBoolean("Table.renderBooleanAsCheckBox"));
table.putClientProperty("JTable.booleanRenderType", UIManager.getString("Table.booleanRenderType"));
setupRendererComponents(table);
}
// Fill the background.
g.setColor(table.getBackground());
g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
vacatedColumnRect.width, vacatedColumnRect.height);
@Override
public Dimension getPreferredSize(final JComponent c) {
var prefSize = super.getPreferredSize(c);
if (!isInScrollPane()) {
return prefSize;
} else {
var dim = SwingUtilities.getUnwrappedParent(table).getSize();
if (dim.width < prefSize.width || dim.height < prefSize.height) {
return prefSize;
} else {
return dim;
}
}
}
protected class DarkHandler extends Handler {
// Paint the vertical grid lines if necessary.
if (table.getShowVerticalLines()) {
g.setColor(table.getGridColor());
int x1 = vacatedColumnRect.x;
int y1 = vacatedColumnRect.y;
int x2 = x1 + vacatedColumnRect.width - 1;
int y2 = y1 + vacatedColumnRect.height - 1;
protected int lastIndex = -1;
boolean onLeftEdge = ltr ? draggedColumnIndex == cMin : draggedColumnIndex == cMax;
boolean onRightEdge = ltr ? draggedColumnIndex == cMax : draggedColumnIndex == cMin;
if (scrollBarVisible()) {
if (isScrollPaneRtl()) {
onLeftEdge = false;
@Override
public void mouseClicked(final MouseEvent e) {
super.mouseClicked(e);
if (isFileList) {
int row = table.rowAtPoint(e.getPoint());
JFileChooser fc = getFileChooser();
if (row < 0 || fc == null) return;
int column = getFileNameColumnIndex();
boolean isSelected = table.getSelectionModel().getLeadSelectionIndex() == row
&& table.getColumnModel().getSelectionModel().getLeadSelectionIndex() == column;
if ((!fc.isMultiSelectionEnabled() || fc.getSelectedFiles().length <= 1)
&& isSelected && lastIndex == row
&& DarkUIUtil.isOverText(e, row, column, table)) {
startEditing(row, column);
} else {
onRightEdge = false;
lastIndex = row;
}
}
// Left
if (dist != 0 || !onLeftEdge) {
if (draggedColumnIndex == cMin && scrollBarVisible() && isScrollPaneRtl()) x1++;
g.fillRect(x1 - 1, y1, 1, y2 - y1);
}
// Right
if (dist != 0 || !onRightEdge) {
g.fillRect(x2, y1, 1, y2 - y1);
}
protected JFileChooser getFileChooser() {
var obj = table.getClientProperty("JTable.fileChooserParent");
if (obj instanceof Supplier) {
var supplied = ((Supplier) obj).get();
return supplied instanceof JFileChooser ? (JFileChooser) supplied : null;
}
return null;
}
for (int row = rMin; row <= rMax; row++) {
// Render the cell value
Rectangle r = table.getCellRect(row, draggedColumnIndex, false);
r.x += dist;
paintCell(g, r, row, draggedColumnIndex);
protected Integer getFileNameColumnIndex() {
var obj = table.getClientProperty("JTable.fileNameColumnIndex");
return obj instanceof Integer ? (Integer) obj : 0;
}
// Paint the (lower) horizontal grid line if necessary.
if (table.getShowHorizontalLines() || (!scrollVisible && row == rMax)) {
g.setColor(table.getGridColor());
if (drawBottomBorder) {
g.setColor(getBorderColor());
}
Rectangle rcr = table.getCellRect(row, draggedColumnIndex, true);
rcr.x += dist;
int x1 = rcr.x - 1;
int y1 = rcr.y;
int x2 = x1 + rcr.width + 1;
int y2 = y1 + rcr.height - 1;
g.fillRect(x1, y2, x2 - x1, 1);
protected void startEditing(final int row, final int column) {
table.editCellAt(row, column, null);
Component editorComponent = table.getEditorComponent();
if (editorComponent != null && !editorComponent.hasFocus()) {
SwingUtilities2.compositeRequestFocus(editorComponent);
}
}
if (drawBottomBorder) {
var rect = table.getCellRect(rMax, draggedColumnIndex, true);
int y = rect.y + rect.height - 1;
g.setColor(getBorderColor());
g.fillRect(rect.x, y, rect.width, 1);
@Override
protected void maybeStartTimer() {
}
@Override
public void actionPerformed(final ActionEvent ae) {
}
}
}

21
src/main/java/com/weis/darklaf/ui/table/DarkTableUIBridge.java

@ -27,7 +27,6 @@ import org.jetbrains.annotations.NotNull;
import sun.swing.SwingUtilities2;
import javax.swing.*;
import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
@ -37,21 +36,7 @@ import java.awt.*;
/**
* @author Jannis Weis
*/
public class DarkTableUIBridge extends BasicTableUI {
protected static int getAdjustedLead(final JTable table, final boolean row) {
return row ? getAdjustedLead(table, true, table.getSelectionModel())
: getAdjustedLead(table, false, table.getColumnModel().getSelectionModel());
}
protected static int getAdjustedLead(final JTable table,
final boolean row,
final ListSelectionModel model) {
int index = model.getLeadSelectionIndex();
int compare = row ? table.getRowCount() : table.getColumnCount();
return index < compare ? index : -1;
}
public class DarkTableUIBridge extends BasicTableUIBridge {
/**
* Paint a representation of the <code>table</code> instance
@ -446,6 +431,10 @@ public class DarkTableUIBridge extends BasicTableUI {
}
protected int viewIndexForColumn(final TableColumn aColumn) {
return viewIndexForColumn(aColumn, table);
}
public static int viewIndexForColumn(final TableColumn aColumn, final JTable table) {
TableColumnModel cm = table.getColumnModel();
for (int column = 0; column < cm.getColumnCount(); column++) {
if (cm.getColumn(column) == aColumn) {

57
src/main/java/com/weis/darklaf/ui/table/TextFieldTableCellEditorBorder.java

@ -1,6 +1,8 @@
package com.weis.darklaf.ui.table;
import com.weis.darklaf.ui.text.DarkTextFieldUI;
import com.weis.darklaf.util.DarkUIUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@ -12,12 +14,11 @@ import java.awt.*;
public class TextFieldTableCellEditorBorder extends DarkTableCellBorder {
@Override
public void paintBorder(final Component c, @NotNull final Graphics g, final int x, final int y,
public void paintBorder(@NotNull final Component c, @NotNull final Graphics g, final int x, final int y,
final int width, final int height) {
g.setColor(DarkTextFieldUI.getBorderColor(c));
var parent = c.getParent();
if (parent instanceof JTable) {
var table = ((JTable) parent);
g.setColor(DarkTextFieldUI.getBorderColor(false, false, true, true));
var table = DarkUIUtil.getParentOfType(JTable.class, c);
if (table != null) {
if (!table.getShowHorizontalLines()) {
g.fillRect(0, 0, width, 1);
g.fillRect(0, height - 1, width, 1);
@ -25,7 +26,53 @@ public class TextFieldTableCellEditorBorder extends DarkTableCellBorder {
if (!table.getShowVerticalLines()) {
g.fillRect(0, 0, 1, height);
g.fillRect(width - 1, 0, 1, height);
} else if (isInWrapper(c)) {
if (c.getParent().getComponentOrientation().isLeftToRight()) {
g.fillRect(0, 0, 1, height);
} else {
g.fillRect(width - 1, 0, 1, height);
}
}
} else {
DarkUIUtil.drawRect(g, x, y, width, height, 1);
}
}
protected static boolean isInWrapper(@NotNull final Component c) {
return c.getParent() instanceof DarkTableCellEditor.IconWrapper;
}
@Override
public Insets getBorderInsets(final Component c) {
var ins = super.getBorderInsets();
if (isInWrapper(c)) {
if (parentLTR(c)) {
ins.left -= ((DarkTableCellEditor.IconWrapper) c.getParent()).getIconCompGap();
} else {
ins.right -= ((DarkTableCellEditor.IconWrapper) c.getParent()).getIconCompGap();
}
} else if (isListEditor(c)) {
var renderer = ((JList) c.getParent()).getCellRenderer();
if (renderer instanceof JLabel) {
if (parentLTR(c)) {
ins.left -= ((JLabel) renderer).getIconTextGap() - 1;
} else {
ins.right -= ((JLabel) renderer).getIconTextGap() - 1;
}
}
}
return ins;
}
protected static boolean parentLTR(@NotNull final Component c) {
return c.getParent().getComponentOrientation().isLeftToRight();
}
@Contract("null -> false")
protected static boolean isListEditor(final Component c) {
return c instanceof JComponent
&& Boolean.TRUE.equals(((JComponent) c).getClientProperty("JTextField.listCellEditor"))
&& c.getParent() instanceof JList;
}
}

16
src/main/java/com/weis/darklaf/ui/text/DarkTextBorder.java

@ -23,6 +23,7 @@
*/
package com.weis.darklaf.ui.text;
import com.weis.darklaf.ui.table.TextFieldTableCellEditorBorder;
import com.weis.darklaf.util.DarkUIUtil;
import com.weis.darklaf.util.GraphicsContext;
import com.weis.darklaf.util.GraphicsUtil;
@ -38,11 +39,18 @@ import java.awt.*;
*/
public class DarkTextBorder implements Border, UIResource {
private static final Border editorBorder = new TextFieldTableCellEditorBorder();
public final static int BORDER_SIZE = 3;
public final static int PADDING = 4;
public void paintBorder(final Component c, final Graphics g2, final int x, final int y,
final int width, final int height) {
if (isCellEditor(c)) {
editorBorder.paintBorder(c, g2, x, y, width, height);
return;
}
Graphics2D g = (Graphics2D) g2;
g.translate(x, y);
GraphicsContext config = GraphicsUtil.setupStrokePainting(g);
@ -76,8 +84,16 @@ public class DarkTextBorder implements Border, UIResource {
config.restore();
}
protected static boolean isCellEditor(final Component c) {
return c instanceof JComponent
&& Boolean.TRUE.equals(((JComponent) c).getClientProperty("JTextField.cellEditor"));
}
@Override
public Insets getBorderInsets(final Component c) {
if (isCellEditor(c)) {
return editorBorder.getBorderInsets(c);
}
Insets insets = new Insets(BORDER_SIZE + PADDING, BORDER_SIZE + PADDING,
BORDER_SIZE + PADDING, BORDER_SIZE + PADDING);
if (DarkTextFieldUI.isSearchField(c)) {

7
src/main/java/com/weis/darklaf/ui/text/DarkTextFieldUI.java

@ -95,6 +95,11 @@ public class DarkTextFieldUI extends DarkTextFieldUIBridge implements PropertyCh
boolean editable = !(c instanceof JTextComponent) || ((JTextComponent) c).isEditable();
boolean focus = DarkUIUtil.hasFocus(c);
boolean error = hasError(c);
return getBorderColor(focus, error, editable, c.isEnabled());
}
public static Color getBorderColor(@NotNull final boolean focus, final boolean error,
final boolean editable, final boolean enabled) {
if (focus) {
if (error) {
return UIManager.getColor("TextField.border.focusError");
@ -104,7 +109,7 @@ public class DarkTextFieldUI extends DarkTextFieldUIBridge implements PropertyCh
} else if (error) {
return UIManager.getColor("TextField.border.error");
}
return c.isEnabled() && editable
return enabled && editable
? UIManager.getColor("TextField.border.enabled")
: UIManager.getColor("TextField.border.disabled");
}

2
src/main/java/com/weis/darklaf/ui/tooltip/DarkTooltipBorder.java

@ -82,6 +82,8 @@ public class DarkTooltipBorder implements Border, UIResource {
si.right = 0;
} else if (align == Alignment.WEST) {
si.left = 0;
} else if (align == Alignment.NORTH_EAST || align == Alignment.NORTH || align == Alignment.NORTH_WEST) {
si.bottom = 0;
}
}

63
src/main/java/com/weis/darklaf/util/DarkUIUtil.java

@ -37,6 +37,8 @@ import javax.swing.table.TableCellRenderer;
import javax.swing.tree.TreeCellRenderer;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D;
@ -166,13 +168,7 @@ public final class DarkUIUtil {
return new Color(redPart, greenPart, bluePart);
}
public static void drawRect(final Graphics g, final int x, final int y, final int width, final int height,
final int thickness) {
g.fillRect(x, y, width, thickness);
g.fillRect(x, y, thickness, height);
g.fillRect(x + width - thickness, y, thickness, height);
g.fillRect(x, y + height - thickness, width, thickness);
}
private static final Rectangle iconRect = new Rectangle();
public static void applyInsets(final Rectangle rect, final Insets insets) {
if (insets != null && rect != null) {
@ -316,6 +312,59 @@ public final class DarkUIUtil {
return null;
}
private static final Rectangle textRect = new Rectangle();
public static void drawRect(@NotNull final Graphics g, final int x, final int y,
final int width, final int height, final int thickness) {
g.fillRect(x, y, width, thickness);
g.fillRect(x, y + thickness, thickness, height - 2 * thickness);
g.fillRect(x + width - thickness, y + thickness, thickness, height - 2 * thickness);
g.fillRect(x, y + height - thickness, width, thickness);
}
public static boolean isOverText(@NotNull final MouseEvent e, final int index, final JList list) {
var bounds = list.getCellBounds(index, index);
if (!bounds.contains(e.getPoint())) return false;
//noinspection unchecked
var cellRenderer = ((ListCellRenderer<Object>) list.getCellRenderer())
.getListCellRendererComponent(list, list.getModel().getElementAt(index),
index, false, false);
if (cellRenderer instanceof JLabel) {
return isOverText((JLabel) cellRenderer, bounds, e.getPoint());
} else {
return true;
}
}
public static boolean isOverText(final JLabel label, final Rectangle bounds, final Point p) {
textRect.setBounds(0, 0, 0, 0);
iconRect.setBounds(0, 0, 0, 0);
SwingUtilities.layoutCompoundLabel(label, label.getFontMetrics(label.getFont()), label.getText(),
label.getIcon(), label.getVerticalAlignment(),
label.getHorizontalAlignment(),
label.getVerticalTextPosition(), label.getHorizontalTextPosition(),
bounds, iconRect, textRect, label.getIconTextGap());
return textRect.contains(p);
}
public static boolean isOverText(@NotNull final MouseEvent e, final int row, final int column,
final JTable table) {
var bounds = table.getCellRect(row, column, false);
if (!bounds.contains(e.getPoint())) return false;
var cellRenderer = table.getCellRenderer(row, column).getTableCellRendererComponent(
table, table.getValueAt(row, column), false, false, row, column);
if (cellRenderer instanceof JLabel) {
return isOverText((JLabel) cellRenderer, bounds, e.getPoint());
} else {
return true;
}
}
public static boolean isMenuShortcutKeyDown(final InputEvent event) {
return (event.getModifiersEx() &
Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()) != 0;
}
public enum Outline {
error {
@Override

9
src/main/resources/com/weis/darklaf/icons/dark/files/drive.svg

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd">
<rect x="2" y="9" width="12" height="4" fill="#9AA7B0" fill-opacity=".8"/>
<rect x="11" y="10" width="2" height="2" fill="#499C54"/>
<rect x="2" y="4" width="12" height="4" fill="#9AA7B0" fill-opacity=".8"/>
<rect x="11" y="5" width="2" height="2" fill="#499C54"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 438 B

6
src/main/resources/com/weis/darklaf/icons/dark/menu/down.svg

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="16" viewBox="0 0 8 16">
<g fill="none" fill-rule="evenodd" transform="translate(-7.5,0)">
<rect width="1" height="7" x="11" y="3" fill="#AFB1B3"/>
<polygon fill="#AFB1B3" points="11.5 10 14 13 9 13" transform="matrix(1 0 0 -1 0 23)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 329 B

4
src/main/resources/com/weis/darklaf/icons/dark/menu/save.svg

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="#AFB1B3" fill-rule="evenodd"
d="M11,14 L11,11 L5,11 L5,14 L2,14 L2,2 L14,2 L14,14 L11,14 Z M4,4 L4,8 L12,8 L12,4 L4,4 Z M6,12 L10,12 L10,14 L6,14 L6,12 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 274 B

5
src/main/resources/com/weis/darklaf/icons/dark/menu/up.svg

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="16" viewBox="0 0 8 16">
<g fill="none" fill-rule="evenodd" transform="translate(-0.5,0)">
<path fill="#AFB1B3" d="M4,6 L2,6 L4.5,3 L7,6 L5,6 L5,13 L4,13 L4,6 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 249 B

3
src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowDownHover.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#dddddd" fill-rule="evenodd" points="8 6 12.5 11 3.5 11" transform="matrix(1 0 0 -1 0 17)"/>
</svg>

After

Width:  |  Height:  |  Size: 203 B

3
src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowDownSelected.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#AFB1B3" fill-rule="evenodd" points="8 6 12.5 11 3.5 11" transform="matrix(1 0 0 -1 0 17)"/>
</svg>

After

Width:  |  Height:  |  Size: 203 B

3
src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowLeftHover.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#dddddd" fill-rule="evenodd" points="7.5 5.5 12 10.5 3 10.5" transform="rotate(-90 7.5 8)"/>
</svg>

After

Width:  |  Height:  |  Size: 203 B

3
src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowLeftSelected.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#AFB1B3" fill-rule="evenodd" points="7.5 5.5 12 10.5 3 10.5" transform="rotate(-90 7.5 8)"/>
</svg>

After

Width:  |  Height:  |  Size: 203 B

4
src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowRightHover.svg

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#dddddd" fill-rule="evenodd" points="8.5 5.5 13 10.5 4 10.5"
transform="matrix(0 -1 -1 0 16.5 16.5)"/>
</svg>

After

Width:  |  Height:  |  Size: 226 B

4
src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowRightSelected.svg

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#AFB1B3" fill-rule="evenodd" points="8.5 5.5 13 10.5 4 10.5"
transform="matrix(0 -1 -1 0 16.5 16.5)"/>
</svg>

After

Width:  |  Height:  |  Size: 226 B

4
src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowUpHover.svg

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#dddddd" fill-rule="evenodd" points="8 5 12.5 10 3.5 10"/>
</svg>

After

Width:  |  Height:  |  Size: 170 B

3
src/main/resources/com/weis/darklaf/icons/dark/navigation/arrowUpSelected.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#AFB1B3" fill-rule="evenodd" points="8 5 12.5 10 3.5 10"/>
</svg>

After

Width:  |  Height:  |  Size: 169 B

9
src/main/resources/com/weis/darklaf/icons/light/files/drive.svg

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd">
<rect x="2" y="9" width="12" height="4" fill="#9AA7B0" fill-opacity=".8"/>
<rect x="11" y="10" width="2" height="2" fill="#59A869"/>
<rect x="2" y="4" width="12" height="4" fill="#9AA7B0" fill-opacity=".8"/>
<rect x="11" y="5" width="2" height="2" fill="#59A869"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 438 B

4
src/main/resources/com/weis/darklaf/icons/light/files/folder.svg

@ -1,7 +1,3 @@
<!--<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">-->
<!-- <path fill="#6E6E6E" fill-opacity=".7"-->
<!-- d="M1,13 L15,13 L15,4 L7.98457,4 L6.69633,2.71149 C6.22161957,2.28559443 5.61570121,2.03457993 4.97888,2 L1.05128,2 C1.02295884,2 1,2.02295884 1,2.05128 L1,13 Z"/>-->
<!--</svg>-->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="#9AA7B0" fill-opacity=".8"
d="M1,13 L15,13 L15,4 L7.98457,4 L6.69633,2.71149 C6.22161957,2.28559443 5.61570121,2.03457993 4.97888,2 L1.05128,2 C1.02295884,2 1,2.02295884 1,2.05128 L1,13 Z"/>

Before

Width:  |  Height:  |  Size: 644 B

After

Width:  |  Height:  |  Size: 308 B

6
src/main/resources/com/weis/darklaf/icons/light/menu/down.svg

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="16" viewBox="0 0 8 16">
<g fill="none" fill-rule="evenodd" transform="translate(-7.5,0)">
<rect width="1" height="7" x="11" y="3" fill="#6E6E6E"/>
<polygon fill="#6E6E6E" points="11.5 10 14 13 9 13" transform="matrix(1 0 0 -1 0 23)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 329 B

4
src/main/resources/com/weis/darklaf/icons/light/menu/save.svg

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="#6E6E6E" fill-rule="evenodd"
d="M11,14 L11,11 L5,11 L5,14 L2,14 L2,2 L14,2 L14,14 L11,14 Z M4,4 L4,8 L12,8 L12,4 L4,4 Z M6,12 L10,12 L10,14 L6,14 L6,12 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 274 B

5
src/main/resources/com/weis/darklaf/icons/light/menu/up.svg

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="16" viewBox="0 0 8 16">
<g fill="none" fill-rule="evenodd" transform="translate(-0.5,0)">
<path fill="#6E6E6E" d="M4,6 L2,6 L4.5,3 L7,6 L5,6 L5,13 L4,13 L4,6 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 249 B

3
src/main/resources/com/weis/darklaf/icons/light/navigation/arrowDownHover.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#6E6E6E" fill-rule="evenodd" points="8 6 12.5 11 3.5 11" transform="matrix(1 0 0 -1 0 17)"/>
</svg>

After

Width:  |  Height:  |  Size: 203 B

3
src/main/resources/com/weis/darklaf/icons/light/navigation/arrowDownSelected.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#FFF" fill-rule="evenodd" points="8 6 12.5 11 3.5 11" transform="matrix(1 0 0 -1 0 17)"/>
</svg>

After

Width:  |  Height:  |  Size: 200 B

3
src/main/resources/com/weis/darklaf/icons/light/navigation/arrowLeftHover.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#6E6E6E" fill-rule="evenodd" points="7.5 5.5 12 10.5 3 10.5" transform="rotate(-90 7.5 8)"/>
</svg>

After

Width:  |  Height:  |  Size: 203 B

3
src/main/resources/com/weis/darklaf/icons/light/navigation/arrowLeftSelected.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#FFF" fill-rule="evenodd" points="7.5 5.5 12 10.5 3 10.5" transform="rotate(-90 7.5 8)"/>
</svg>

After

Width:  |  Height:  |  Size: 200 B

4
src/main/resources/com/weis/darklaf/icons/light/navigation/arrowRightHover.svg

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#6E6E6E" fill-rule="evenodd" points="8.5 5.5 13 10.5 4 10.5"
transform="matrix(0 -1 -1 0 16.5 16.5)"/>
</svg>

After

Width:  |  Height:  |  Size: 226 B

4
src/main/resources/com/weis/darklaf/icons/light/navigation/arrowRightSelected.svg

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#FFF" fill-rule="evenodd" points="8.5 5.5 13 10.5 4 10.5"
transform="matrix(0 -1 -1 0 16.5 16.5)"/>
</svg>

After

Width:  |  Height:  |  Size: 223 B

3
src/main/resources/com/weis/darklaf/icons/light/navigation/arrowUpHover.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#6E6E6E" fill-rule="evenodd" points="8 5 12.5 10 3.5 10"/>
</svg>

After

Width:  |  Height:  |  Size: 169 B

3
src/main/resources/com/weis/darklaf/icons/light/navigation/arrowUpSelected.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#FFF" fill-rule="evenodd" points="8 5 12.5 10 3.5 10"/>
</svg>

After

Width:  |  Height:  |  Size: 166 B

2
src/main/resources/com/weis/darklaf/properties/platform/windows.properties

@ -62,3 +62,5 @@ ToolBar.borderColor = %border
#MenuBar
MenuBar.border = com.weis.darklaf.ui.menu.DarkMenuBarBorder
MenuBar.borderColor = %border
FileChooser.listViewWindowsStyle = true

1
src/main/resources/com/weis/darklaf/properties/ui/comboBox.properties

@ -29,3 +29,4 @@ ComboBox.focusBorderColor = %glowFocusLine
ComboBox.inactiveBackground = %widgetFillInactive
ComboBox.activeBackground = %background
ComboBox.disabledForeground = %textForegroundInactive
ComboBox.disabledBackground = %widgetFillInactive

32
src/main/resources/com/weis/darklaf/properties/ui/fileChooser.properties

@ -21,12 +21,28 @@
#SOFTWARE.
#
# suppress inspection "UnusedProperty" for whole file
FileChooser.newFolderIcon = files/newFolder.svg[aware]
FileChooser.upFolderIcon = files/upFolder.svg[aware]
FileChooser.homeFolderIcon = files/homeFolder.svg[aware]
FileChooserUI = com.weis.darklaf.ui.filechooser.DarkFileChooserUI
FileChooser.newFolderIcon = files/newFolder.svg[aware]
FileChooser.upFolderIcon = files/upFolder.svg[aware]
FileChooser.homeFolderIcon = files/homeFolder.svg[aware]
FileChooser.listViewBorder = com.weis.darklaf.ui.filechooser.DarkFileChooserListViewBorder
FileView.fileIcon = files/general.svg[aware]
FileView.directoryIcon = files/folder.svg[aware]
FileChooser.detailsViewIcon = menu/listFiles.svg[aware]
FileChooser.listViewIcon = menu/groupBy.svg[aware]
FileView.computerIcon = files/desktop.svg[aware]
FileChooser.detailsViewIcon = menu/listFiles.svg[aware]
FileChooser.listViewIcon = menu/groupBy.svg[aware]
FileChooser.borderColor = %borderSecondary
FileChooser.rowHeight = 20
FileChooser.minEditDelay = 200
FileChooser.maxEditDelay = 600
FileView.fullRowSelection = true
FileChooser.listViewWindowsStyle = false
FileChooser.fileSizeKiloBytes = {0} kb
FileChooser.fileSizeMegaBytes = {0} mb
FileChooser.fileSizeGigaBytes = {0} gb
FileChooser.readOnly = false
FileView.fileIcon = files/general.svg[aware]
FileView.directoryIcon = files/folder.svg[aware]
FileView.computerIcon = files/desktop.svg[aware]
FileView.floppyDriveIcon = menu/save.svg[aware]
FileView.hardDriveIcon = files/drive.svg[aware]

5
src/main/resources/com/weis/darklaf/properties/ui/list.properties

@ -22,9 +22,12 @@
#
# suppress inspection "UnusedProperty" for whole file
ListUI = com.weis.darklaf.ui.list.DarkListUI
List.cellRenderer = com.weis.darklaf.ui.list.DarkListCellRenderer
List.border = null
List.background = %background
List.focusSelectedCellHighlightBorder = com.weis.darklaf.ui.list.DarkListCellBorder
List.focusSelectedCellHighlightBorder = com.weis.darklaf.ui.list.DarkListCellFocusBorder
List.focusCellHighlightBorder = com.weis.darklaf.ui.list.DarkListCellBorder
List.cellNoFocusBorder = com.weis.darklaf.ui.list.DarkListCellBorder
List.dropLineColor = %dropForeground
List.selectionBackground = %highlightFillFocus
List.focusBorderColor = %borderFocus

5
src/main/resources/com/weis/darklaf/properties/ui/menu.properties

@ -27,6 +27,9 @@ Menu.border = com.weis.darklaf.ui.menu.DarkMenuItemBorde
Menu.selectionBackground = %highlightFillFocus
Menu.acceleratorForeground = %acceleratorForeground
Menu.acceleratorSelectionForeground = %acceleratorForeground
Menu.submenuPopupOffsetX = -4
Menu.submenuPopupOffsetY = -2
#Icons
Menu.arrowIcon = navigation/arrowRight.svg[aware]
Menu.arrowIcon = navigation/arrowRight.svg[aware]
MenuItem.arrowHover.icon = navigation/arrowRightHover.svg[aware]

8
src/main/resources/com/weis/darklaf/properties/ui/table.properties

@ -37,10 +37,13 @@ Table.cellEditorBorder = com.weis.darklaf.ui.table.DarkTableCell
Table.scrollPaneBorder = com.weis.darklaf.ui.table.DarkTableBorder
Table.background = %background
Table.focusBorderColor = %borderFocus
Table.focusRowBorderColor = %borderFocus
Table.gridColor = %gridLine
Table.dropLineColor = %dropForeground
Table.dropLineShortColor = %dropForeground
Table.focusSelectionBackground = %highlightFillFocus
Table.focusCellBackground = %background
Table.focusCellForeground = %textForeground
Table.selectionNoFocusBackground = %highlightFill
Table.selectionBackground = %highlightFillFocus
@ -49,7 +52,8 @@ Table.alternateRowBackground = %backgroundAlternative
Table.renderBooleanAsCheckBox = true
Table.booleanRenderType = checkBox
Table.rowHeight = 22
#Icons
Table.ascendingSortIcon = menu/upDown.svg[aware]
Table.descendingSortIcon = menu/upDown.svg[aware]
Table.ascendingSortIcon = menu/up.svg[aware](8,16)
Table.descendingSortIcon = menu/down.svg[aware](8,16)

5
src/main/resources/com/weis/darklaf/properties/ui/toggleButton.properties

@ -27,4 +27,7 @@ ToggleButton.sliderBorderColor = %widgetBorder
ToggleButton.disabledSliderBorderColor = %widgetBorderInactive
ToggleButton.focusedSliderBorderColor = %glowFocusLine
ToggleButton.sliderColor = %controlFill
ToggleButton.disabledSliderColor = %controlFillDisabled
ToggleButton.disabledSliderColor = %controlFillDisabled
ToggleButton.inactiveFillColor = %widgetFill
ToggleButton.activeFillColor = %backgroundHoverSecondary

4
src/main/resources/com/weis/darklaf/properties/ui/tree.properties

@ -52,12 +52,12 @@ Tree.expandedIcon = navigation/arrowDown.svg[aware]
Tree.closedIcon = files/folder.svg[aware]
Tree.openIcon = files/folder.svg[aware]
Tree.leafIcon = files/general.svg[aware]
Tree.collapsed.selected.focused.icon = navigation/arrowRight.svg[aware]
Tree.collapsed.selected.focused.icon = navigation/arrowRightSelected.svg[aware]
Tree.collapsed.selected.unfocused.icon = navigation/arrowRight.svg[aware]
Tree.collapsed.unselected.focused.icon = navigation/arrowRight.svg[aware]
Tree.collapsed.unselected.unfocused.icon = navigation/arrowRight.svg[aware]
Tree.expanded.selected.focused.icon = navigation/arrowDown.svg[aware]
Tree.expanded.selected.focused.icon = navigation/arrowDownSelected.svg[aware]
Tree.expanded.selected.unfocused.icon = navigation/arrowDown.svg[aware]
Tree.expanded.unselected.focused.icon = navigation/arrowDown.svg[aware]
Tree.expanded.unselected.unfocused.icon = navigation/arrowDown.svg[aware]

BIN
src/main/resources/library/x64/jniplatform.dll

Binary file not shown.

BIN
src/main/resources/library/x86/jniplatform.dll

Binary file not shown.

7
src/test/java/FileChooserDemo.java

@ -1,5 +1,4 @@
import com.weis.darklaf.LafManager;
import com.weis.darklaf.theme.Theme;
import javax.swing.*;
@ -9,11 +8,11 @@ public final class FileChooserDemo {
SwingUtilities.invokeLater(() -> {
LafManager.install();
var chooser = new JFileChooser(System.getProperty("user.home"));
chooser.setMultiSelectionEnabled(true);
var frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(100, 100);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
chooser.showOpenDialog(frame);
});
}

4
src/test/java/ToolTipDemo.java

@ -1,4 +1,5 @@
import com.weis.darklaf.LafManager;
import com.weis.darklaf.components.alignment.Alignment;
import com.weis.darklaf.components.tooltip.ToolTipContext;
import javax.swing.*;
@ -13,7 +14,8 @@ public class ToolTipDemo {
JFrame f = new JFrame();
var p = new JPanel();
p.add(new JButton("Button with very very long text") {
private final ToolTipContext context = new ToolTipContext(this);
private final ToolTipContext context = new ToolTipContext(this).setAlignment(Alignment.CENTER)
.setCenterAlignment(Alignment.SOUTH);
{
setToolTipText("ToolTip \n multiline \n third line's a charm");

8
src/test/java/UIDemo.java

@ -391,7 +391,13 @@ public final class UIDemo {
var menuBar = new JMenuBar();
var menu = new JMenu("test");
menu.add(new JMenuItem("item"));
menu.add(new JMenu("submenu") {{
add(new JMenuItem("item1"));
add(new JMenuItem("item2"));
add(new JMenuItem("item3"));
add(new JMenuItem("item4"));
add(new JMenuItem("item5"));
}});
menu.addSeparator();
menu.add(new JRadioButtonMenuItem("radioButton"));
menu.add(new JCheckBoxMenuItem("checkBox"));

3
src/test/java/UIManagerDefaults.java

@ -5,6 +5,7 @@
import com.weis.darklaf.DarkLafInfo;
import com.weis.darklaf.LafManager;
import com.weis.darklaf.components.OverlayScrollPane;
import com.weis.darklaf.ui.cell.DarkCellRendererToggleButton;
import com.weis.darklaf.ui.table.DarkColorTableCellRendererEditor;
import org.jetbrains.annotations.Contract;
@ -211,7 +212,7 @@ public class UIManagerDefaults implements ItemListener {
d.height = 350;
table.setPreferredScrollableViewportSize(d);
return new JScrollPane(table);
return new OverlayScrollPane(table);
}
/*

Loading…
Cancel
Save