Browse Source

Improved value storage for ColorChooser.

Made invalid input signal for ColorChooser less intrusive.
Added error visualization for FormattedTextField.
Fixed error where ListCellRenderer could not be loaded from defaults.
pull/15/head
weisj 5 years ago
parent
commit
d46d8a3a89
  1. 61
      src/main/java/com/weis/darklaf/ui/colorchooser/ColorValueFormatter.java
  2. 4
      src/main/java/com/weis/darklaf/ui/colorchooser/DarkColorChooserPanel.java
  3. 27
      src/main/java/com/weis/darklaf/ui/filechooser/DarkFileChooserUI.java
  4. 9
      src/main/java/com/weis/darklaf/ui/filechooser/DarkFileChooserUIBridge.java
  5. 4
      src/main/java/com/weis/darklaf/ui/filechooser/DarkFilePaneUIBridge.java
  6. 39
      src/main/java/com/weis/darklaf/ui/filechooser/FileTextField.java
  7. 4
      src/main/java/com/weis/darklaf/ui/list/DarkListUI.java
  8. 72
      src/main/java/com/weis/darklaf/ui/text/DarkFormattedTextFieldUI.java
  9. 2
      src/main/java/com/weis/darklaf/ui/text/DarkTextFieldUI.java
  10. 2
      src/main/resources/com/weis/darklaf/properties/ui/colorChooser.properties
  11. 1
      src/main/resources/com/weis/darklaf/properties/ui/list.properties
  12. 2
      src/main/resources/com/weis/darklaf/theme/darcula/darcula_defaults.properties
  13. 15
      src/test/java/FileChooserDemo.java

61
src/main/java/com/weis/darklaf/ui/colorchooser/ColorValueFormatter.java

@ -33,6 +33,8 @@ import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultFormatterFactory; import javax.swing.text.DefaultFormatterFactory;
import javax.swing.text.DocumentFilter; import javax.swing.text.DocumentFilter;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.FocusListener; import java.awt.event.FocusListener;
import java.text.ParseException; import java.text.ParseException;
@ -42,7 +44,8 @@ import static java.util.Locale.ENGLISH;
/** /**
* @author Jannis Weis * @author Jannis Weis
*/ */
public final class ColorValueFormatter extends JFormattedTextField.AbstractFormatter implements FocusListener { public final class ColorValueFormatter extends JFormattedTextField.AbstractFormatter implements FocusListener,
ActionListener {
private final int fieldIndex; private final int fieldIndex;
private final int radix; private final int radix;
@ -50,6 +53,7 @@ public final class ColorValueFormatter extends JFormattedTextField.AbstractForma
private DarkColorModel model; private DarkColorModel model;
private boolean transparencyEnabled; private boolean transparencyEnabled;
private JFormattedTextField text; private JFormattedTextField text;
private final Timer errorTimer;
private final DocumentFilter filter = new DocumentFilter() { private final DocumentFilter filter = new DocumentFilter() {
@Override @Override
public void remove(@NotNull final FilterBypass fb, final int offset, public void remove(@NotNull final FilterBypass fb, final int offset,
@ -58,7 +62,7 @@ public final class ColorValueFormatter extends JFormattedTextField.AbstractForma
fb.remove(offset, length); fb.remove(offset, length);
commit(); commit();
} else { } else {
Toolkit.getDefaultToolkit().beep(); error();
} }
} }
@ -75,7 +79,7 @@ public final class ColorValueFormatter extends JFormattedTextField.AbstractForma
return; return;
} }
} }
Toolkit.getDefaultToolkit().beep(); error();
} }
@Override @Override
@ -90,7 +94,7 @@ public final class ColorValueFormatter extends JFormattedTextField.AbstractForma
return; return;
} }
} }
Toolkit.getDefaultToolkit().beep(); error();
} }
}; };
@ -99,6 +103,20 @@ public final class ColorValueFormatter extends JFormattedTextField.AbstractForma
this.fieldIndex = index; this.fieldIndex = index;
this.radix = hex ? 16 : 10; this.radix = hex ? 16 : 10;
this.hex = hex; this.hex = hex;
this.errorTimer = new Timer(UIManager.getInt("ColorChooser.errorDelay"), this);
errorTimer.setRepeats(false);
}
protected void error() {
text.putClientProperty("JTextField.hasError", true);
text.repaint();
errorTimer.restart();
}
@Override
public void actionPerformed(final ActionEvent e) {
text.putClientProperty("JTextField.hasError", false);
text.repaint();
} }
@NotNull @NotNull
@ -112,6 +130,8 @@ public final class ColorValueFormatter extends JFormattedTextField.AbstractForma
} }
private void commit() { private void commit() {
text.putClientProperty("JTextField.hasError", false);
text.repaint();
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
try { try {
if (text != null) { if (text != null) {
@ -179,10 +199,7 @@ public final class ColorValueFormatter extends JFormattedTextField.AbstractForma
return model.getDefault(fieldIndex); return model.getDefault(fieldIndex);
} }
if (hex) { if (hex) {
var hexStr = text; var hexStr = String.format("%1$-" + getHexLength() + "s", text).replaceAll(" ", "F");
if (hexStr.length() == 6) {
hexStr += "FF";
}
int r = Integer.valueOf(hexStr.substring(0, 2), 16); int r = Integer.valueOf(hexStr.substring(0, 2), 16);
checkRange(r, 0, 255); checkRange(r, 0, 255);
int g = Integer.valueOf(hexStr.substring(2, 4), 16); int g = Integer.valueOf(hexStr.substring(2, 4), 16);
@ -191,7 +208,7 @@ public final class ColorValueFormatter extends JFormattedTextField.AbstractForma
checkRange(b, 0, 255); checkRange(b, 0, 255);
int alpha = Integer.valueOf(hexStr.substring(6, 8), 16); int alpha = Integer.valueOf(hexStr.substring(6, 8), 16);
checkRange(alpha, 0, 255); checkRange(alpha, 0, 255);
return Integer.valueOf(text, radix); return new Color(r, g, b, alpha);
} else { } else {
var value = Integer.valueOf(text, this.radix); var value = Integer.valueOf(text, this.radix);
var min = model.getMinimum(fieldIndex); var min = model.getMinimum(fieldIndex);
@ -215,27 +232,19 @@ public final class ColorValueFormatter extends JFormattedTextField.AbstractForma
@Contract("null -> fail") @Contract("null -> fail")
@Override @Override
public String valueToString(final Object object) throws ParseException { public String valueToString(final Object object) throws ParseException {
if (object instanceof Integer) { if (object instanceof Integer && !hex) {
if (radix == 10) {
return object.toString(); return object.toString();
} } else if (object instanceof Color && hex) {
if (hex) { var c = (Color) object;
int r = c.getRed();
int g = c.getGreen();
int b = c.getBlue();
int a = c.getAlpha();
if (getHexLength() == 8) { if (getHexLength() == 8) {
var hexStr = String.format("%08X", object); return String.format("%02X%02X%02X%02X", r, g, b, a);
hexStr = hexStr.substring(2) + hexStr.substring(0, 2);
return hexStr;
} else { } else {
return String.format("%06X", (0xFFFFFF & (Integer) object)); return String.format("%02X%02X%02X", r, g, b);
}
}
int value = (Integer) object;
int index = getLength();
char[] array = new char[index];
while (0 < index--) {
array[index] = Character.forDigit(value & 0x0F, this.radix);
value >>= 4;
} }
return new String(array).toUpperCase(ENGLISH);
} }
throw new ParseException("illegal object", 0); throw new ParseException("illegal object", 0);
} }

4
src/main/java/com/weis/darklaf/ui/colorchooser/DarkColorChooserPanel.java

@ -375,9 +375,9 @@ public class DarkColorChooserPanel extends AbstractColorChooserPanel implements
isChanging = true; isChanging = true;
boolean transparencyEnabled = isColorTransparencySelectionEnabled(); boolean transparencyEnabled = isColorTransparencySelectionEnabled();
if (transparencyEnabled) { if (transparencyEnabled) {
textHex.setValue(c.getRGB()); textHex.setValue(c);
} else { } else {
textHex.setValue(new Color(c.getRed(), c.getGreen(), c.getBlue()).getRGB()); textHex.setValue(ColorUtil.removeAlpha(c));
} }
isChanging = changingOld; isChanging = changingOld;
} }

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

@ -32,6 +32,8 @@ import sun.swing.FilePane;
import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleContext;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.filechooser.FileSystemView; import javax.swing.filechooser.FileSystemView;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import java.awt.*; import java.awt.*;
@ -225,13 +227,7 @@ public class DarkFileChooserUI extends DarkFileChooserUIBridge {
populateFileNameLabel(); populateFileNameLabel();
fileNamePanel.add(fileNameLabel); fileNamePanel.add(fileNameLabel);
@SuppressWarnings("serial") // anonymous class fileNameTextField = new FileTextField();
JTextField tmp2 = new JTextField(35) {
public Dimension getMaximumSize() {
return new Dimension(Short.MAX_VALUE, super.getPreferredSize().height);
}
};
fileNameTextField = tmp2;
fileNamePanel.add(fileNameTextField); fileNamePanel.add(fileNameTextField);
fileNameLabel.setLabelFor(fileNameTextField); fileNameLabel.setLabelFor(fileNameTextField);
fileNameTextField.addFocusListener( fileNameTextField.addFocusListener(
@ -262,9 +258,22 @@ public class DarkFileChooserUI extends DarkFileChooserUIBridge {
filterComboBoxModel = createFilterComboBoxModel(); filterComboBoxModel = createFilterComboBoxModel();
fc.addPropertyChangeListener(filterComboBoxModel); fc.addPropertyChangeListener(filterComboBoxModel);
filterComboBox = new JComboBox<>(filterComboBoxModel); filterComboBox = new JComboBox<>(filterComboBoxModel);
if (filterComboBox.getItemCount() == 0) { filterComboBoxModel.addListDataListener(new ListDataListener() {
filterComboBox.setEnabled(false); @Override
public void intervalAdded(final ListDataEvent e) {
filterComboBox.setEnabled(filterComboBox.getItemCount() > 1);
}
@Override
public void intervalRemoved(final ListDataEvent e) {
filterComboBox.setEnabled(filterComboBox.getItemCount() > 1);
} }
@Override
public void contentsChanged(final ListDataEvent e) {
filterComboBox.setEnabled(filterComboBox.getItemCount() > 1);
}
});
filterComboBox.putClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY, filterComboBox.putClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY,
filesOfTypeLabelText); filesOfTypeLabelText);
filesOfTypeLabel.setLabelFor(filterComboBox); filesOfTypeLabel.setLabelFor(filterComboBox);

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

@ -52,6 +52,7 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
import java.util.Objects;
import java.util.Vector; import java.util.Vector;
@ -475,7 +476,7 @@ public class DarkFileChooserUIBridge extends BasicFileChooserUI {
if (o != e.getOldValue()) { if (o != e.getOldValue()) {
cc.applyComponentOrientation(o); cc.applyComponentOrientation(o);
} }
} else if (s == "FileChooser.useShellFolder") { } else if (Objects.equals(s, "FileChooser.useShellFolder")) {
doDirectoryChanged(e); doDirectoryChanged(e);
} else if (s.equals("ancestor")) { } else if (s.equals("ancestor")) {
if (e.getOldValue() == null && e.getNewValue() != null) { if (e.getOldValue() == null && e.getNewValue() != null) {
@ -1269,10 +1270,10 @@ public class DarkFileChooserUIBridge extends BasicFileChooserUI {
public void propertyChange(final PropertyChangeEvent e) { public void propertyChange(final PropertyChangeEvent e) {
String prop = e.getPropertyName(); String prop = e.getPropertyName();
if (prop == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) { if (Objects.equals(prop, JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY)) {
filters = (FileFilter[]) e.getNewValue(); filters = (FileFilter[]) e.getNewValue();
fireContentsChanged(this, -1, -1); fireContentsChanged(this, -1, -1);
} else if (prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY) { } else if (Objects.equals(prop, JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) {
fireContentsChanged(this, -1, -1); fireContentsChanged(this, -1, -1);
} }
} }
@ -1298,7 +1299,7 @@ public class DarkFileChooserUIBridge extends BasicFileChooserUI {
found = true; found = true;
} }
} }
if (found == false) { if (!found) {
getFileChooser().addChoosableFileFilter(currentFilter); getFileChooser().addChoosableFileFilter(currentFilter);
} }
} }

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

@ -486,7 +486,7 @@ public class DarkFilePaneUIBridge extends JPanel implements PropertyChangeListen
listViewWindowsStyle = UIManager.getBoolean("FileChooser.listViewWindowsStyle"); listViewWindowsStyle = UIManager.getBoolean("FileChooser.listViewWindowsStyle");
readOnly = UIManager.getBoolean("FileChooser.readOnly"); readOnly = UIManager.getBoolean("FileChooser.readOnly");
// TODO: On windows, get the following localized strings from the OS // TUDU: On windows, get the following localized strings from the OS
viewMenuLabelText = viewMenuLabelText =
UIManager.getString("FileChooser.viewMenuLabelText", l); UIManager.getString("FileChooser.viewMenuLabelText", l);
@ -1797,7 +1797,7 @@ public class DarkFilePaneUIBridge extends JPanel implements PropertyChangeListen
setHorizontalAlignment(alignment); setHorizontalAlignment(alignment);
// formatting cell text // formatting cell text
// TODO: it's rather a temporary trick, to be revised // TUDU: it's rather a temporary trick, to be revised
String text; String text;
if (value == null) { if (value == null) {
text = ""; text = "";

39
src/main/java/com/weis/darklaf/ui/filechooser/FileTextField.java

@ -0,0 +1,39 @@
/*
* 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 javax.swing.*;
import java.awt.*;
public class FileTextField extends JTextField {
public FileTextField() {
super(35);
}
public Dimension getMaximumSize() {
return new Dimension(Short.MAX_VALUE, super.getPreferredSize().height);
}
}

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

@ -15,6 +15,10 @@ import java.awt.event.MouseEvent;
*/ */
public class DarkListUI extends DarkListUIBridge { public class DarkListUI extends DarkListUIBridge {
static {
UIManager.put("List.cellRenderer", new DarkListCellRenderer());
}
@NotNull @NotNull
@Contract("_ -> new") @Contract("_ -> new")
public static ComponentUI createUI(final JComponent list) { public static ComponentUI createUI(final JComponent list) {

72
src/main/java/com/weis/darklaf/ui/text/DarkFormattedTextFieldUI.java

@ -27,16 +27,86 @@ import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.text.Document;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.ParseException;
/** /**
* @author Jannis Weis * @author Jannis Weis
*/ */
public class DarkFormattedTextFieldUI extends DarkTextFieldUI { public class DarkFormattedTextFieldUI extends DarkTextFieldUI implements PropertyChangeListener, DocumentListener {
private JFormattedTextField textField;
@NotNull @NotNull
@Contract("_ -> new") @Contract("_ -> new")
public static ComponentUI createUI(final JComponent c) { public static ComponentUI createUI(final JComponent c) {
return new DarkFormattedTextFieldUI(); return new DarkFormattedTextFieldUI();
} }
protected String getPropertyPrefix() {
return "FormattedTextField";
}
@Override
public void installUI(final JComponent c) {
textField = (JFormattedTextField) c;
super.installUI(c);
}
@Override
public void propertyChange(final PropertyChangeEvent evt) {
super.propertyChange(evt);
if ("document".equals(evt.getPropertyName())) {
var oldDoc = evt.getOldValue();
var newDoc = evt.getNewValue();
if (oldDoc instanceof Document) {
((Document) oldDoc).removeDocumentListener(this);
}
if (newDoc instanceof Document) {
((Document) newDoc).addDocumentListener(this);
}
}
}
@Override
protected void installListeners() {
super.installListeners();
textField.getDocument().addDocumentListener(this);
}
@Override
protected void uninstallListeners() {
super.uninstallListeners();
textField.getDocument().removeDocumentListener(this);
}
@Override
public void insertUpdate(final DocumentEvent e) {
update();
}
@Override
public void removeUpdate(final DocumentEvent e) {
update();
}
@Override
public void changedUpdate(final DocumentEvent e) {
update();
}
protected void update() {
if (textField == null) return;
try {
textField.getFormatter().stringToValue(textField.getText());
textField.putClientProperty("JTextField.hasError", false);
} catch (ParseException e) {
textField.putClientProperty("JTextField.hasError", true);
}
}
} }

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

@ -116,7 +116,7 @@ public class DarkTextFieldUI extends DarkTextFieldUIBridge implements PropertyCh
@Contract("null -> false") @Contract("null -> false")
protected static boolean hasError(final Component c) { protected static boolean hasError(final Component c) {
return c instanceof JComponent && Boolean.TRUE.equals(((JComponent) c).getClientProperty("error")); return c instanceof JComponent && Boolean.TRUE.equals(((JComponent) c).getClientProperty("JTextField.hasError"));
} }
public static Color getBackgroundColor(@NotNull final JTextComponent c) { public static Color getBackgroundColor(@NotNull final JTextComponent c) {

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

@ -35,7 +35,7 @@ ColorChooser.swatchesSwatchSize = 15,15
ColorChooser.swatchBorderColor = %borderSecondary ColorChooser.swatchBorderColor = %borderSecondary
ColorChooser.swatchGridColor = %gridLine ColorChooser.swatchGridColor = %gridLine
ColorChooser.sliderShadow = %shadow ColorChooser.sliderShadow = %shadow
ColorChooser.errorDelay = 600
#Icons #Icons
ColorChooser.pipette.icon = misc/pipette.svg[aware] ColorChooser.pipette.icon = misc/pipette.svg[aware]
ColorChooser.pipetteRollover.icon = misc/pipette_rollover.svg[aware] ColorChooser.pipetteRollover.icon = misc/pipette_rollover.svg[aware]

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

@ -22,7 +22,6 @@
# #
# suppress inspection "UnusedProperty" for whole file # suppress inspection "UnusedProperty" for whole file
ListUI = com.weis.darklaf.ui.list.DarkListUI ListUI = com.weis.darklaf.ui.list.DarkListUI
List.cellRenderer = com.weis.darklaf.ui.list.DarkListCellRenderer
List.border = null List.border = null
List.background = %background List.background = %background
List.focusSelectedCellHighlightBorder = com.weis.darklaf.ui.list.DarkListCellFocusBorder List.focusSelectedCellHighlightBorder = com.weis.darklaf.ui.list.DarkListCellFocusBorder

2
src/main/resources/com/weis/darklaf/theme/darcula/darcula_defaults.properties

@ -118,7 +118,7 @@ glowError = cf6767
glowErrorLine = cf6767 glowErrorLine = cf6767
glowFocusError = c2413c glowFocusError = c2413c
glowFocusErrorLine = c2413c glowFocusErrorLine = cf6767
glowWarning = cf6b30 glowWarning = cf6b30
glowWarningLine = cf6b30 glowWarningLine = cf6b30

15
src/test/java/FileChooserDemo.java

@ -1,6 +1,9 @@
import com.weis.darklaf.LafManager; import com.weis.darklaf.LafManager;
import javax.swing.*; import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.io.File;
public final class FileChooserDemo { public final class FileChooserDemo {
@ -8,6 +11,18 @@ public final class FileChooserDemo {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
LafManager.install(); LafManager.install();
var chooser = new JFileChooser(System.getProperty("user.home")); var chooser = new JFileChooser(System.getProperty("user.home"));
var filter = new FileNameExtensionFilter("JSON files", "json");
chooser.addChoosableFileFilter(new FileFilter() {
@Override
public boolean accept(final File f) {
return f.isDirectory() || filter.accept(f);
}
@Override
public String getDescription() {
return filter.getDescription();
}
});
chooser.setMultiSelectionEnabled(true); chooser.setMultiSelectionEnabled(true);
var frame = new JFrame(); var frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

Loading…
Cancel
Save