Browse Source

Made svg icon load the svg model load lazily.

MAde ThemedIcon patch colors lazily.
Added TristateCheckBox.
Fixed NPE during titlePane uninstall process.
Added missing properties for striped table/tree.
pull/15/head
weisj 5 years ago
parent
commit
820eb689f0
  1. 3
      src/main/java/com/weis/darklaf/DarkLaf.java
  2. 78
      src/main/java/com/weis/darklaf/components/tristate/TristateButtonModel.java
  3. 100
      src/main/java/com/weis/darklaf/components/tristate/TristateCheckBox.java
  4. 21
      src/main/java/com/weis/darklaf/components/tristate/TristateState.java
  5. 19
      src/main/java/com/weis/darklaf/icons/DarkSVGIcon.java
  6. 9
      src/main/java/com/weis/darklaf/icons/IconColorMapper.java
  7. 17
      src/main/java/com/weis/darklaf/icons/IconLoader.java
  8. 31
      src/main/java/com/weis/darklaf/icons/ThemedSVGIcon.java
  9. 2
      src/main/java/com/weis/darklaf/theme/Theme.java
  10. 2
      src/main/java/com/weis/darklaf/ui/rootpane/DarkTitlePane.java
  11. 1
      src/main/java/com/weis/darklaf/ui/tree/DarkTreeUI.java
  12. 46
      src/main/java/com/weis/darklaf/ui/tristate/DarkTristateCheckBoxUI.java
  13. 28
      src/main/java/com/weis/darklaf/util/LafUtil.java
  14. 3
      src/main/resources/com/weis/darklaf/properties/platform/mac.properties
  15. 3
      src/main/resources/com/weis/darklaf/properties/ui/table.properties
  16. 3
      src/main/resources/com/weis/darklaf/properties/ui/tree.properties
  17. 2
      src/main/resources/com/weis/darklaf/properties/ui/tristate.properties
  18. 2
      src/test/java/ColorChooserDemo.java
  19. 10
      src/test/java/UIDemo.java

3
src/main/java/com/weis/darklaf/DarkLaf.java

@ -3,7 +3,6 @@ package com.weis.darklaf;
import com.weis.darklaf.platform.windows.JNIDecorations;
import com.weis.darklaf.theme.Theme;
import com.weis.darklaf.ui.menu.DarkPopupMenuUI;
import com.weis.darklaf.util.LafUtil;
import com.weis.darklaf.util.SystemInfo;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@ -203,8 +202,6 @@ public class DarkLaf extends BasicLookAndFeel {
StyleSheet styleSheet = currentTheme.loadStyleSheet();
new HTMLEditorKit().setStyleSheet(styleSheet);
LafUtil.patchIcons(defaults);
setDecorationsEnabled(currentTheme.useCustomDecorations());
}

78
src/main/java/com/weis/darklaf/components/tristate/TristateButtonModel.java

@ -0,0 +1,78 @@
package com.weis.darklaf.components.tristate;
import javax.swing.*;
import java.awt.event.ItemEvent;
public class TristateButtonModel extends JToggleButton.ToggleButtonModel {
private TristateState state = TristateState.DESELECTED;
public TristateButtonModel(final TristateState state) {
setState(state);
}
public TristateButtonModel() {
this(TristateState.DESELECTED);
}
public void setIndeterminate() {
setState(TristateState.INDETERMINATE);
}
public boolean isIndeterminate() {
return state == TristateState.INDETERMINATE;
}
// Overrides of superclass methods
public void setEnabled(final boolean enabled) {
super.setEnabled(enabled);
// Restore state display
displayState();
}
public void setSelected(final boolean selected) {
setState(selected ? TristateState.SELECTED : TristateState.DESELECTED);
}
@Override
public boolean isSelected() {
return state == TristateState.SELECTED;
}
// Empty overrides of superclass methods
public void setArmed(final boolean b) {
}
public void setPressed(final boolean b) {
}
protected void iterateState() {
setState(state.next());
}
public void setState(final TristateState state) {
//Set internal state
this.state = state;
displayState();
if (state == TristateState.INDETERMINATE && isEnabled()) {
// force the events to fire
// Send ChangeEvent
fireStateChanged();
// Send ItemEvent
int indeterminate = 3;
//noinspection MagicConstant
fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, this, indeterminate));
}
}
protected void displayState() {
super.setSelected(state != TristateState.DESELECTED);
super.setArmed(state == TristateState.INDETERMINATE);
super.setPressed(state == TristateState.INDETERMINATE);
}
public TristateState getState() {
return state;
}
}

100
src/main/java/com/weis/darklaf/components/tristate/TristateCheckBox.java

@ -0,0 +1,100 @@
package com.weis.darklaf.components.tristate;
import com.weis.darklaf.DarkLaf;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ActionMapUIResource;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class TristateCheckBox extends JCheckBox {
private final ChangeListener enableListener = e -> TristateCheckBox.this.setFocusable(getModel().isEnabled());
public TristateCheckBox(final String text) {
this(text, null, TristateState.DESELECTED);
}
@NotNull
@Contract(pure = true)
public String getUIClassID() {
if (UIManager.getLookAndFeel() instanceof DarkLaf) {
return "TristateCheckBoxUI";
} else {
return super.getUIClassID();
}
}
public TristateCheckBox(final String text, final Icon icon, final TristateState initial) {
super(text, icon);
setModel(new TristateButtonModel(initial));
// override action behaviour
super.addMouseListener(new MouseAdapter() {
public void mousePressed(final MouseEvent e) {
System.out.println(getState());
TristateCheckBox.this.iterateState();
}
});
ActionMap actions = new ActionMapUIResource();
actions.put("pressed", new AbstractAction() {
public void actionPerformed(final ActionEvent e) {
TristateCheckBox.this.iterateState();
}
});
actions.put("released", null);
SwingUtilities.replaceUIActionMap(this, actions);
}
public void setIndeterminate() {
getTristateModel().setIndeterminate();
}
public void setState(final TristateState state) {
getTristateModel().setState(state);
}
public boolean isIndeterminate() {
return getTristateModel().isIndeterminate();
}
public TristateState getState() {
return getTristateModel().getState();
}
@Override
public void setModel(final ButtonModel newModel) {
super.setModel(newModel);
if (model instanceof TristateButtonModel) {
model.addChangeListener(enableListener);
}
}
private void iterateState() {
if (!getModel().isEnabled()) return;
grabFocus();
getTristateModel().iterateState();
repaint();
int modifiers = 0;
AWTEvent currentEvent = EventQueue.getCurrentEvent();
if (currentEvent instanceof InputEvent) {
modifiers = ((InputEvent) currentEvent).getModifiersEx();
} else if (currentEvent instanceof ActionEvent) {
modifiers = ((ActionEvent) currentEvent).getModifiers();
}
fireActionPerformed(new ActionEvent(this,
ActionEvent.ACTION_PERFORMED, getText(),
System.currentTimeMillis(), modifiers));
}
public TristateButtonModel getTristateModel() {
return (TristateButtonModel) super.getModel();
}
}

21
src/main/java/com/weis/darklaf/components/tristate/TristateState.java

@ -0,0 +1,21 @@
package com.weis.darklaf.components.tristate;
public enum TristateState {
SELECTED {
public TristateState next() {
return INDETERMINATE;
}
},
INDETERMINATE {
public TristateState next() {
return DESELECTED;
}
},
DESELECTED {
public TristateState next() {
return SELECTED;
}
};
public abstract TristateState next();
}

19
src/main/java/com/weis/darklaf/icons/DarkSVGIcon.java

@ -9,6 +9,7 @@ import java.awt.*;
import java.awt.geom.AffineTransform;
import java.io.Serializable;
import java.net.URI;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Icon from SVG image.
@ -20,6 +21,8 @@ public class DarkSVGIcon implements Icon, Serializable {
private final Dimension size;
private final SVGIcon icon;
private final URI uri;
private final AtomicBoolean loaded;
/**
* Method to fetch the SVG icon from a url.
@ -29,18 +32,20 @@ public class DarkSVGIcon implements Icon, Serializable {
* @param displayHeight display height of icon.
*/
public DarkSVGIcon(@NotNull final URI uri, final int displayWidth, final int displayHeight) {
this.uri = uri;
size = new Dimension(displayWidth, displayHeight);
icon = new SVGIcon();
icon.setSvgURI(uri);
icon.setScaleToFit(true);
icon.setAntiAlias(true);
loaded = new AtomicBoolean(false);
}
@Contract(pure = true)
private DarkSVGIcon(final int width, final int height, @NotNull final DarkSVGIcon icon) {
this.size = new Dimension(width, height);
this.icon = icon.icon;
this.uri = icon.uri;
this.loaded = icon.loaded;
}
public DarkSVGIcon derive(final int width, final int height) {
@ -49,9 +54,17 @@ public class DarkSVGIcon implements Icon, Serializable {
@Override
public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
ensureLoaded();
paintIcon(c, g, x, y, 0);
}
private void ensureLoaded() {
if (!loaded.get()) {
icon.setSvgURI(uri);
loaded.set(true);
}
}
/**
* Paint the icon with rotation.
*
@ -63,6 +76,7 @@ public class DarkSVGIcon implements Icon, Serializable {
*/
public void paintIcon(final Component c, @NotNull final Graphics g, final int x, final int y,
final double rotation) {
ensureLoaded();
var g2 = (Graphics2D) g.create();
g2.translate(x, y);
if (rotation != 0) {
@ -85,6 +99,7 @@ public class DarkSVGIcon implements Icon, Serializable {
}
public SVGIcon getSVGIcon() {
ensureLoaded();
return icon;
}
}

9
src/main/java/com/weis/darklaf/icons/IconColorMapper.java

@ -16,18 +16,17 @@ import java.util.logging.Logger;
public final class IconColorMapper {
private static final Logger LOGGER = Logger.getLogger(IconLoader.class.getName());
public static void patchColors(@NotNull final SVGIcon svgIcon, @NotNull final UIDefaults defaults) {
public static void patchColors(@NotNull final SVGIcon svgIcon) {
var universe = svgIcon.getSvgUniverse();
SVGDiagram diagram = universe.getDiagram(svgIcon.getSvgURI());
try {
loadColors(diagram, defaults);
loadColors(diagram);
} catch (SVGElementException e) {
LOGGER.log(Level.SEVERE, "Failed patching colors. " + e.getMessage(), e.getStackTrace());
}
}
private static void loadColors(@NotNull final SVGDiagram diagram,
@NotNull final UIDefaults defaults) throws SVGElementException {
private static void loadColors(@NotNull final SVGDiagram diagram) throws SVGElementException {
var root = diagram.getRoot();
var defs = diagram.getElement("colors");
if (defs == null) return;
@ -41,7 +40,7 @@ public final class IconColorMapper {
for (var child : children) {
if (child instanceof LinearGradient) {
var id = ((LinearGradient) child).getId();
var c = defaults.getColor(id);
var c = UIManager.getColor(id);
if (c == null) {
c = Color.RED;
LOGGER.warning("Could not load color with id'" + id + "'. Using Color.RED instead.");

17
src/main/java/com/weis/darklaf/icons/IconLoader.java

@ -19,6 +19,7 @@ public final class IconLoader {
private final Class<?> parentClass;
private final Map<IconKey, UIAwareIcon> awareIconMap = new HashMap<>();
private final Map<IconKey, Icon> iconMap = new HashMap<>();
@Contract(pure = true)
private IconLoader(final Class<?> parentClass) {
this.parentClass = parentClass;
@ -67,6 +68,10 @@ public final class IconLoader {
}
public Icon getIcon(final String path, final int w, final int h) {
return getIcon(path, w, h, false);
}
public Icon getIcon(final String path, final int w, final int h, final boolean themed) {
IconKey key = new IconKey(path, w, h);
if (iconMap.containsKey(key)) {
return iconMap.get(key);
@ -87,7 +92,7 @@ public final class IconLoader {
}
key.w = w; //Restore key.
if (path.endsWith(".svg")) {
Icon icon = loadIcon(path, w, h);
Icon icon = loadSVGIcon(path, w, h, themed);
iconMap.put(key, icon);
return icon;
} else {
@ -100,10 +105,14 @@ public final class IconLoader {
@NotNull
@Contract("_, _, _ -> new")
public Icon loadIcon(@NotNull final String name, final int w, final int h) {
public Icon loadSVGIcon(@NotNull final String name, final int w, final int h, final boolean themed) {
try {
LOGGER.info("Loading icon '" + name + "'. Resolving from " + parentClass);
if (themed) {
return new ThemedSVGIcon(Objects.requireNonNull(parentClass.getResource(name).toURI()), w, h);
} else {
return new DarkSVGIcon(Objects.requireNonNull(parentClass.getResource(name).toURI()), w, h);
}
} catch (NullPointerException | URISyntaxException e) {
LOGGER.log(Level.SEVERE, "Exception while loading '" + name + "'" + ". Resolving from " + parentClass,
e.getStackTrace());
@ -112,8 +121,8 @@ public final class IconLoader {
}
@NotNull
public Icon loadIcon(@NotNull final String name) {
return loadIcon(name, 16, 16);
public Icon loadSVGIcon(@NotNull final String name, final boolean themed) {
return loadSVGIcon(name, 16, 16, themed);
}
@Nullable

31
src/main/java/com/weis/darklaf/icons/ThemedSVGIcon.java

@ -0,0 +1,31 @@
package com.weis.darklaf.icons;
import com.weis.darklaf.LafManager;
import com.weis.darklaf.theme.Theme;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.net.URI;
public class ThemedSVGIcon extends DarkSVGIcon {
private Theme currentTheme;
public ThemedSVGIcon(@NotNull final URI uri, final int displayWidth, final int displayHeight) {
super(uri, displayWidth, displayHeight);
}
@Override
public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
ensureTheme();
super.paintIcon(c, g, x, y);
}
private void ensureTheme() {
var theme = LafManager.getTheme();
if (currentTheme != theme) {
IconColorMapper.patchColors(getSVGIcon());
currentTheme = theme;
}
}
}

2
src/main/java/com/weis/darklaf/theme/Theme.java

@ -21,7 +21,7 @@ import java.util.logging.Logger;
public abstract class Theme {
private static final Logger LOGGER = Logger.getLogger(Theme.class.getName());
private static final String[] UI_PROPERTIES = new String[]{
"borders", "button", "checkBox", "colorChooser", "comboBox", "fileChooser",
"borders", "button", "checkBox", "colorChooser", "comboBox", "fileChooser", "tristate",
"internalFrame", "label", "list", "menu", "menuBar", "menuItem", "optionPane", "panel", "popupMenu",
"progressBar", "radioButton", "rootPane", "scrollBar", "scrollPane", "separator", "slider", "spinner",
"splitPane", "statusBar", "tabbedPane", "table", "text", "toggleButton", "toolBar", "toolTip", "tree",

2
src/main/java/com/weis/darklaf/ui/rootpane/DarkTitlePane.java

@ -166,8 +166,10 @@ public class DarkTitlePane extends JComponent {
windowHandle = 0;
rootPane.removeContainerListener(rootPaneContainerListener);
rootPane.getLayeredPane().removeContainerListener(layeredPaneContainerListener);
if (menuBar != null) {
menuBar.setPreferredSize(null);
rootPane.setJMenuBar(menuBar);
}
removeAll();
}

1
src/main/java/com/weis/darklaf/ui/tree/DarkTreeUI.java

@ -105,6 +105,7 @@ public class DarkTreeUI extends BasicTreeUI {
myOldRepaintAllRowValue = UIManager.getBoolean("Tree.repaintWholeRow");
UIManager.put("Tree.repaintWholeRow", true);
tree.putClientProperty("JTree.alternateRowColor", UIManager.getBoolean("Tree.alternateRowColor"));
tree.setShowsRootHandles(true);
tree.addMouseListener(mySelectionListener);

46
src/main/java/com/weis/darklaf/ui/tristate/DarkTristateCheckBoxUI.java

@ -0,0 +1,46 @@
package com.weis.darklaf.ui.tristate;
import com.weis.darklaf.components.tristate.TristateCheckBox;
import com.weis.darklaf.components.tristate.TristateState;
import com.weis.darklaf.ui.checkbox.DarkCheckBoxUI;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
public class DarkTristateCheckBoxUI extends DarkCheckBoxUI {
@NotNull
@Contract("_ -> new")
public static ComponentUI createUI(final JComponent c) {
return new DarkTristateCheckBoxUI();
}
private Icon checkBoxIndeterminateIcon;
private Icon checkBoxIndeterminateDisabledIcon;
private Icon checkBoxIndeterminateFocusedIcon;
@Override
public void installDefaults(final AbstractButton b) {
super.installDefaults(b);
checkBoxIndeterminateIcon = UIManager.getIcon("CheckBox.indeterminate.icon");
checkBoxIndeterminateDisabledIcon = UIManager.getIcon("CheckBox.indeterminateDisabled.icon");
checkBoxIndeterminateFocusedIcon = UIManager.getIcon("CheckBox.indeterminateFocused.icon");
}
@Override
protected Icon getCheckIcon(@NotNull final AbstractButton b) {
if (b instanceof TristateCheckBox) {
var state = ((TristateCheckBox) b).getState();
if (state == TristateState.INDETERMINATE) {
if (b.isEnabled()) {
return b.hasFocus() ? checkBoxIndeterminateFocusedIcon : checkBoxIndeterminateIcon;
} else {
return checkBoxIndeterminateDisabledIcon;
}
}
}
return super.getCheckIcon(b);
}
}

28
src/main/java/com/weis/darklaf/util/LafUtil.java

@ -1,9 +1,6 @@
package com.weis.darklaf.util;
import com.kitfox.svg.app.beans.SVGIcon;
import com.weis.darklaf.icons.DarkSVGIcon;
import com.weis.darklaf.icons.EmptyIcon;
import com.weis.darklaf.icons.IconColorMapper;
import com.weis.darklaf.icons.IconLoader;
import com.weis.darklaf.icons.UIAwareIcon;
import org.jetbrains.annotations.Contract;
@ -18,11 +15,9 @@ import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -33,8 +28,6 @@ public final class LafUtil {
private static final String AWARE_KEY = "[aware]";
private static final String PATCH_KEY = "[patch]";
private static final Set<SVGIcon> resolveSet = new HashSet<>();
@NotNull
public static Properties loadProperties(@NotNull final Class<?> clazz, final String name, final String path) {
final Properties properties = new Properties();
@ -46,18 +39,6 @@ public final class LafUtil {
return properties;
}
public static void patchIcons(@NotNull final UIDefaults defaults) {
new SwingWorker<>() {
@Override
protected Object doInBackground() {
resolveSet.forEach(icon -> IconColorMapper.patchColors(icon, defaults));
resolveSet.clear();
return null;
}
}.doInBackground();
}
@Nullable
public static Object parseValue(@NotNull final String key, @NotNull final String value,
final Map<Object, Object> defaults) {
@ -164,14 +145,7 @@ public final class LafUtil {
}
var iconPath = path.substring(0, path.length() - tag.length());
if (tag.equals(PATCH_KEY)) {
Icon icon = ICON_LOADER.getIcon(iconPath, dim.width, dim.height);
if (icon instanceof DarkSVGIcon) {
resolveSet.add(((DarkSVGIcon) icon).getSVGIcon());
return icon;
} else {
LOGGER.warning("Could not patch colours for icon '" + iconPath + "'. Not a SVG icon.");
return icon;
}
return ICON_LOADER.getIcon(iconPath, dim.width, dim.height, true);
} else {
UIAwareIcon icon = ICON_LOADER.getUIAwareIcon(iconPath, dim.width, dim.height);
if (tag.equals(DUAL_KEY)) {

3
src/main/resources/com/weis/darklaf/properties/platform/mac.properties

@ -19,4 +19,5 @@ darcula.selectionBackground = 2F65CA
MenuBar.border = com.bulenkov.darcula.ui.DarculaMenuBarBorder
FileChooserUI = javax.swing.plaf.metal.MetalFileChooserUI
#Table.alternateRowColor
Table.alternateRowColor = true
Table.alternateRowBackground = %backgroundAlternative

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

@ -22,6 +22,9 @@ Table.focusSelectionBackground = %highlightFillFocus
Table.selectionNoFocusBackground = %highlightFill
Table.selectionBackground = %highlightFillFocus
Table.alternateRowColor = false
Table.alternateRowBackground = %backgroundAlternative
Table.renderBooleanAsCheckBox = true
Table.booleanRenderType = checkBox

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

@ -18,6 +18,9 @@ Tree.textBackground = %textBackground
Tree.rowHeight = 22
Tree.dropLineColor = %dropForeground
Tree.alternateRowColor = false
Tree.alternateRowBackground = %backgroundAlternative
Tree.renderBooleanAsCheckBox = true
Tree.booleanRenderType = checkBox

2
src/main/resources/com/weis/darklaf/properties/ui/tristate.properties

@ -0,0 +1,2 @@
# suppress inspection "UnusedProperty" for whole file
TristateCheckBoxUI = com.weis.darklaf.ui.tristate.DarkTristateCheckBoxUI

2
src/test/java/ColorChooserDemo.java

@ -1,5 +1,4 @@
import com.weis.darklaf.LafManager;
import com.weis.darklaf.theme.Theme;
import javax.swing.*;
import java.awt.*;
@ -11,7 +10,6 @@ public final class ColorChooserDemo {
LafManager.install();
JColorChooser.showDialog(null, "Color Chooser with transparency",
Color.RED, true);
// JOptionPane.showMessageDialog(null, "This is a test!","THis is a test!", JOptionPane.ERROR_MESSAGE);
});
}
}

10
src/test/java/UIDemo.java

@ -1,5 +1,6 @@
import com.weis.darklaf.LafManager;
import com.weis.darklaf.components.TextFieldHistory;
import com.weis.darklaf.components.tristate.TristateCheckBox;
import com.weis.darklaf.icons.IconLoader;
import org.jdesktop.swingx.JXStatusBar;
import org.jdesktop.swingx.JXTaskPane;
@ -27,7 +28,7 @@ public final class UIDemo {
taskpanecontainer.add(taskpane);
JFrame frame = new JFrame("UIDemo");
Icon folderIcon = IconLoader.get().getUIAwareIcon("control/checkBoxFocused.svg", 19, 19);
Icon folderIcon = IconLoader.get().getUIAwareIcon("files/folder.svg", 19, 19);
var panel = new JPanel(new GridLayout(2, 5));
var content = new JPanel(new BorderLayout());
@ -58,6 +59,13 @@ public final class UIDemo {
}});
add(new JRadioButton("enabled"));
}});
add(new JPanel(new FlowLayout(FlowLayout.LEFT)) {{
add(new TristateCheckBox("disabled") {{
setIndeterminate();
setEnabled(false);
}});
add(new TristateCheckBox("enabled"));
}});
add(new JPanel(new FlowLayout(FlowLayout.LEFT)) {{
add(new JButton("IconButton", folderIcon) {{
setRolloverEnabled(true);

Loading…
Cancel
Save