From e5aefdb02f73068363363990d02c0737ea21140b Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Wed, 1 May 2013 15:24:41 +0200 Subject: [PATCH] fix toolbar and menu on windows --- .idea/misc.xml | 2 +- src/com/bulenkov/darcula/DarculaLaf.java | 15 +- src/com/bulenkov/darcula/darcula.properties | 5 + .../bulenkov/darcula/darcula_linux.properties | 3 + .../darcula/darcula_windows.properties | 7 +- .../bulenkov/darcula/ui/DarculaMenuBarUI.java | 22 +++ .../bulenkov/darcula/ui/DarculaToolBarUI.java | 22 +++ src/com/bulenkov/darcula/util/ColorUtil.java | 11 ++ src/com/bulenkov/darcula/util/StringUtil.java | 100 +++++++++++++ .../bulenkov/darcula/util/UIManagerUtil.java | 138 ++++++++++++++++++ 10 files changed, 322 insertions(+), 3 deletions(-) create mode 100644 src/com/bulenkov/darcula/ui/DarculaMenuBarUI.java create mode 100644 src/com/bulenkov/darcula/ui/DarculaToolBarUI.java create mode 100644 src/com/bulenkov/darcula/util/UIManagerUtil.java diff --git a/.idea/misc.xml b/.idea/misc.xml index a0db047..75af768 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,7 +6,7 @@ - + diff --git a/src/com/bulenkov/darcula/DarculaLaf.java b/src/com/bulenkov/darcula/DarculaLaf.java index 0fb3887..74d198b 100644 --- a/src/com/bulenkov/darcula/DarculaLaf.java +++ b/src/com/bulenkov/darcula/DarculaLaf.java @@ -17,6 +17,7 @@ package com.bulenkov.darcula; import com.bulenkov.darcula.util.ColorUtil; import com.bulenkov.darcula.util.StringUtil; +import com.bulenkov.darcula.util.UIManagerUtil; import sun.awt.AppContext; import javax.swing.*; @@ -29,6 +30,8 @@ import javax.swing.text.DefaultEditorKit; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.StyleSheet; import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.io.*; @@ -72,11 +75,21 @@ public final class DarculaLaf extends BasicLookAndFeel { @SuppressWarnings("UnusedParameters") private static void log(Exception e) { //everything is gonna be alright - //e.printStackTrace(); + e.printStackTrace(); } @Override public UIDefaults getDefaults() { + final Timer[] timer = new Timer[1]; + timer[0] = new Timer(2000, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + UIManagerUtil.showInfo(); + timer[0].stop(); + } + }); + timer[0].start(); + try { final Method superMethod = BasicLookAndFeel.class.getDeclaredMethod("getDefaults"); superMethod.setAccessible(true); diff --git a/src/com/bulenkov/darcula/darcula.properties b/src/com/bulenkov/darcula/darcula.properties index 8a33709..92a2c96 100644 --- a/src/com/bulenkov/darcula/darcula.properties +++ b/src/com/bulenkov/darcula/darcula.properties @@ -5,9 +5,14 @@ darcula.foreground=bbbbbb darcula.textForeground=bbbbbb darcula.caretForeground=bbbbbb darcula.inactiveBackground=3C3F41 +darcula.highlight=3C3F41 +darcula.light=3C3F41 window=3c3f41 info=3c3f41 controlLtHighlight=3c3f41 +controlHighlight=3c3f41 +menu=3c3f41 +desktop=3c3f41 text=bbbbbb textText=bbbbbb diff --git a/src/com/bulenkov/darcula/darcula_linux.properties b/src/com/bulenkov/darcula/darcula_linux.properties index aef0536..d523622 100644 --- a/src/com/bulenkov/darcula/darcula_linux.properties +++ b/src/com/bulenkov/darcula/darcula_linux.properties @@ -4,4 +4,7 @@ darcula.selectionForeground=bbbbbb PopupMenu.border=com.bulenkov.darcula.ui.DarculaPopupMenuBorder +MenuBarUI=com.bulenkov.darcula.ui.DarculaMenuBarUI MenuBar.border=com.bulenkov.darcula.ui.DarculaMenuBarBorder + +ToolBarUI=com.bulenkov.darcula.ui.DarculaToolBarUI diff --git a/src/com/bulenkov/darcula/darcula_windows.properties b/src/com/bulenkov/darcula/darcula_windows.properties index 8e38720..df0a46b 100644 --- a/src/com/bulenkov/darcula/darcula_windows.properties +++ b/src/com/bulenkov/darcula/darcula_windows.properties @@ -1,9 +1,14 @@ # suppress inspection "UnusedProperty" for whole file darcula.selectionBackground=4B6EAF darcula.selectionForeground=bbbbbb +darcula.select=0D293E PopupMenu.border=com.bulenkov.darcula.ui.DarculaPopupMenuBorder +MenuBarUI=com.bulenkov.darcula.ui.DarculaMenuBarUI MenuBar.border=com.bulenkov.darcula.ui.DarculaMenuBarBorder -InternalFrameUI=com.bulenkov.darcula.ui.win.DarculaWindowsInternalFrameUI \ No newline at end of file +ToolBarUI=com.bulenkov.darcula.ui.DarculaToolBarUI +ToggleButton.shadow=0d293e + +#InternalFrameUI=com.bulenkov.darcula.ui.win.DarculaWindowsInternalFrameUI \ No newline at end of file diff --git a/src/com/bulenkov/darcula/ui/DarculaMenuBarUI.java b/src/com/bulenkov/darcula/ui/DarculaMenuBarUI.java new file mode 100644 index 0000000..9111018 --- /dev/null +++ b/src/com/bulenkov/darcula/ui/DarculaMenuBarUI.java @@ -0,0 +1,22 @@ +package com.bulenkov.darcula.ui; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.metal.MetalMenuBarUI; +import java.awt.*; + +/** + * @author Konstantin Bulenkov + */ +public class DarculaMenuBarUI extends MetalMenuBarUI { + @SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass", "UnusedDeclaration"}) + public static ComponentUI createUI(JComponent c) { + return new DarculaMenuBarUI(); + } + + @Override + public void paint(Graphics g, JComponent c) { + g.setColor(UIManager.getColor("MenuItem.background")); + g.fillRect(0, 0, c.getWidth(), c.getHeight()); + } +} diff --git a/src/com/bulenkov/darcula/ui/DarculaToolBarUI.java b/src/com/bulenkov/darcula/ui/DarculaToolBarUI.java new file mode 100644 index 0000000..908e7ce --- /dev/null +++ b/src/com/bulenkov/darcula/ui/DarculaToolBarUI.java @@ -0,0 +1,22 @@ +package com.bulenkov.darcula.ui; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.metal.MetalToolBarUI; +import java.awt.*; + +/** + * @author Konstantin Bulenkov + */ +public class DarculaToolBarUI extends MetalToolBarUI { + @SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass", "UnusedDeclaration"}) + public static ComponentUI createUI(JComponent c) { + return new DarculaToolBarUI(); + } + + @Override + public void paint(Graphics g, JComponent c) { + g.setColor(UIManager.getColor("ToolBar.background")); + g.fillRect(0, 0, c.getWidth(), c.getHeight()); + } +} diff --git a/src/com/bulenkov/darcula/util/ColorUtil.java b/src/com/bulenkov/darcula/util/ColorUtil.java index 5c0c2b9..776be08 100644 --- a/src/com/bulenkov/darcula/util/ColorUtil.java +++ b/src/com/bulenkov/darcula/util/ColorUtil.java @@ -54,4 +54,15 @@ public class ColorUtil { } } + public static boolean isDark(final Color c) { + // based on perceptional luminosity, see + return (1 - (0.299 * c.getRed() + 0.587 * c.getGreen() + 0.114 * c.getBlue()) / 255) >= 0.5; + } + + public static String toHex(final Color c) { + final String R = Integer.toHexString(c.getRed()); + final String G = Integer.toHexString(c.getGreen()); + final String B = Integer.toHexString(c.getBlue()); + return (R.length() < 2 ? "0" : "") + R + (G.length() < 2 ? "0" : "") + G + (B.length() < 2 ? "0" : "") + B; + } } diff --git a/src/com/bulenkov/darcula/util/StringUtil.java b/src/com/bulenkov/darcula/util/StringUtil.java index 606c551..93634ba 100644 --- a/src/com/bulenkov/darcula/util/StringUtil.java +++ b/src/com/bulenkov/darcula/util/StringUtil.java @@ -39,4 +39,104 @@ public class StringUtil { } return result; } + + public static int naturalCompare(String string1, String string2) { + return naturalCompare(string1, string2, false); + } + + private static int naturalCompare(String string1, String string2, boolean caseSensitive) { + final int string1Length = string1.length(); + final int string2Length = string2.length(); + for (int i = 0, j = 0; i < string1Length && j < string2Length; i++, j++) { + char ch1 = string1.charAt(i); + char ch2 = string2.charAt(j); + if ((isDigit(ch1) || ch1 == ' ') && (isDigit(ch2) || ch2 == ' ')) { + int startNum1 = i; + while (ch1 == ' ' || ch1 == '0') { // skip leading spaces and zeros + startNum1++; + if (startNum1 >= string1Length) break; + ch1 = string1.charAt(startNum1); + } + int startNum2 = j; + while (ch2 == ' ' || ch2 == '0') { + startNum2++; + if (startNum2 >= string2Length) break; + ch2 = string2.charAt(startNum2); + } + i = startNum1; + j = startNum2; + while (i < string1Length && isDigit(string1.charAt(i))) i++; + while (j < string2Length && isDigit(string2.charAt(j))) j++; + String digits1 = string1.substring(startNum1, i); + String digits2 = string2.substring(startNum2, j); + if (digits1.length() != digits2.length()) { + return digits1.length() - digits2.length(); + } + int numberDiff = digits1.compareTo(digits2); + if (numberDiff != 0) { + return numberDiff; + } + i--; + j--; + final int lengthDiff = (i - startNum1) - (j - startNum2); + if (lengthDiff != 0) { + return lengthDiff; + } + for (; startNum1 < i; startNum1++, startNum2++) { + final int diff = string1.charAt(startNum1) - string2.charAt(startNum2); + if (diff != 0) { + return diff; + } + } + } + else { + if (caseSensitive) { + return ch1 - ch2; + } + else { + // similar logic to charsMatch() below + if (ch1 != ch2) { + final int diff1 = toUpperCase(ch1) - toUpperCase(ch2); + if (diff1 != 0) { + final int diff2 = toLowerCase(ch1) - toLowerCase(ch2); + if (diff2 != 0) { + return diff2; + } + } + } + } + } + } + if (!caseSensitive && string1Length == string2Length) { + // do case sensitive compare if case insensitive strings are equal + return naturalCompare(string1, string2, true); + } + return string1Length - string2Length; + } + + public static char toUpperCase(char a) { + if (a < 'a') { + return a; + } + if (a <= 'z') { + return (char)(a + ('A' - 'a')); + } + return Character.toUpperCase(a); + } + + public static char toLowerCase(char a) { + if (a < 'A' || a >= 'a' && a <= 'z') { + return a; + } + + if (a <= 'Z') { + return (char)(a + ('a' - 'A')); + } + + return Character.toLowerCase(a); + } + + private static boolean isDigit(char c) { + return c >= '0' && c <= '9'; + } } diff --git a/src/com/bulenkov/darcula/util/UIManagerUtil.java b/src/com/bulenkov/darcula/util/UIManagerUtil.java new file mode 100644 index 0000000..7778f0d --- /dev/null +++ b/src/com/bulenkov/darcula/util/UIManagerUtil.java @@ -0,0 +1,138 @@ +package com.bulenkov.darcula.util; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.ColorUIResource; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.EventObject; + +/** + * @author Konstantin Bulenkov + */ +public class UIManagerUtil { + + public static void showInfo() { + JFrame frame = new JFrame(); + frame.setSize(500, 800); + frame.setTitle("Edit LaF Defaults"); + + final UIDefaults defaults = UIManager.getDefaults(); + Enumeration keys = defaults.keys(); + final Object[][] data = new Object[defaults.size()][2]; + int i = 0; + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + data[i][0] = key; + data[i][1] = defaults.get(key); + i++; + } + + Arrays.sort(data, new Comparator() { + @Override + public int compare(Object[] o1, Object[] o2) { + return StringUtil.naturalCompare(o1[0].toString(), o2[0].toString()); + } + }); + + + JTable table = new JTable(new DefaultTableModel(data, new Object[]{"Name", "Value"}) { + @Override + public boolean isCellEditable(int row, int column) { + return column == 1 && getValueAt(row, column) instanceof Color; + } + }) { + @Override + public boolean editCellAt(int row, int column, EventObject e) { + if (isCellEditable(row, column) && e instanceof MouseEvent) { + final Object color = getValueAt(row, column); + final Color newColor = JColorChooser.showDialog(null, "Set Color", (Color)color); + if (newColor != null) { + final ColorUIResource colorUIResource = new ColorUIResource(newColor); + final Object key = getValueAt(row, 0); + UIManager.put(key, colorUIResource); + setValueAt(colorUIResource, row, column); + } + } + return false; + } + }; + table.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() { + @Override + public Component getTableCellRendererComponent(JTable table, + Object value, + boolean isSelected, + boolean hasFocus, + int row, + int column) { + final JPanel panel = new JPanel(new BorderLayout()); + final JLabel label = new JLabel(value == null ? "" : value.toString()); + panel.add(label, BorderLayout.CENTER); + if (value instanceof Color) { + final Color c = (Color) value; + label.setText(String.format("[r=%d,g=%d,b=%d] hex=0x%s", c.getRed(), c.getGreen(), c.getBlue(), ColorUtil.toHex(c))); + label.setForeground(ColorUtil.isDark(c) ? Color.white : Color.black); + panel.setBackground(c); + return panel; + } else if (value instanceof Icon) { + try { + final Icon icon = new IconWrap((Icon) value); + if (icon.getIconHeight() <= 20) { + label.setIcon(icon); + } + label.setText(String.format("(%dx%d) %s)", icon.getIconWidth(), icon.getIconHeight(), label.getText())); + } catch (Throwable e1) {// + } + return panel; + } else if (value instanceof Border) { + try { + final Insets i = ((Border) value).getBorderInsets(null); + label.setText(String.format("[%d, %d, %d, %d] %s", i.top, i.left, i.bottom, i.right, label.getText())); + return panel; + } catch (Exception ignore) { + } + } + return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + } + }); + final JScrollPane pane = new JScrollPane(table); + table.setShowGrid(false); + final JPanel panel = new JPanel(new BorderLayout()); + panel.add(pane, BorderLayout.CENTER); + + frame.getContentPane().add(panel); + frame.setVisible(true); + } + + private static class IconWrap implements Icon { + private final Icon myIcon; + + public IconWrap(Icon icon) { + myIcon = icon; + } + + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + try { + myIcon.paintIcon(c, g, x, y); + } catch (Exception e) { + } + } + + @Override + public int getIconWidth() { + return myIcon.getIconWidth(); + } + + @Override + public int getIconHeight() { + return myIcon.getIconHeight(); + } + } +}