mirror of https://github.com/weisJ/darklaf.git
weisj
5 years ago
37 changed files with 2870 additions and 18 deletions
@ -0,0 +1,69 @@
|
||||
package com.weis.darklaf.color; |
||||
|
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
|
||||
public class DarkColorModel { |
||||
|
||||
private final String prefix; |
||||
private final String[] labels; |
||||
|
||||
@Contract(pure = true) |
||||
public DarkColorModel(final String name, final String... labels) { |
||||
this.prefix = "ColorChooser." + name; |
||||
this.labels = labels; |
||||
} |
||||
|
||||
@Contract(pure = true) |
||||
public DarkColorModel() { |
||||
this("rgb", "Red", "Green", "Blue"); |
||||
} |
||||
|
||||
public int getCount() { |
||||
return this.labels.length; |
||||
} |
||||
|
||||
public int getMinimum(final int index) { |
||||
return 0; |
||||
} |
||||
|
||||
public int getMaximum(final int index) { |
||||
return 255; |
||||
} |
||||
|
||||
public int getDefault(final int index) { |
||||
return 0; |
||||
} |
||||
|
||||
public final String getText(@NotNull final Component component, final String suffix) { |
||||
return UIManager.getString(this.prefix + suffix + "Text", component.getLocale()); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "RGB"; |
||||
} |
||||
|
||||
public int getValueCount() { |
||||
return 3; |
||||
} |
||||
|
||||
public char[] getLabelDescriptorsBefore() { |
||||
return new char[]{'R', 'G', 'B'}; |
||||
} |
||||
|
||||
public char[] getLabelDescriptorsAfter() { |
||||
return new char[]{Character.MIN_VALUE, Character.MIN_VALUE, Character.MIN_VALUE, Character.MIN_VALUE}; |
||||
} |
||||
|
||||
public int[] getValuesFromColor(@NotNull final Color color) { |
||||
return new int[]{color.getRed(), color.getGreen(), color.getBlue()}; |
||||
} |
||||
|
||||
public Color getColorFromValues(@NotNull final int[] values) { |
||||
return new Color(values[0], values[1], values[2]); |
||||
} |
||||
} |
@ -0,0 +1,76 @@
|
||||
package com.weis.darklaf.color; |
||||
|
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.awt.*; |
||||
|
||||
public class DarkColorModelCMYK extends DarkColorModel { |
||||
|
||||
private static final int[] cmyk = new int[4]; |
||||
private static final int[] rgb = new int[3]; |
||||
|
||||
public DarkColorModelCMYK() { |
||||
super("cmyk", "Cyan", "Magenta", "Yellow", "Black"); |
||||
} |
||||
|
||||
@NotNull |
||||
private static int[] CMYKtoRGB(final double c, final double m, final double y, final double k) { |
||||
rgb[0] = (int) Math.round(255 * (1.0f + c * k - k - c)); |
||||
rgb[1] = (int) Math.round(255 * (1.0f + m * k - k - m)); |
||||
rgb[2] = (int) Math.round(255 * (1.0f + y * k - k - y)); |
||||
return rgb; |
||||
} |
||||
|
||||
@NotNull |
||||
private static int[] RGBtoCMYK(final int r, final int g, final int b) { |
||||
double max = DarkColorModelHSL.max(r / 255.0, g / 255.0, b / 255.0); |
||||
if (max > 0.0f) { |
||||
cmyk[0] = (int) Math.round((1.0f - (r / 255.0) / max) * 100); |
||||
cmyk[1] = (int) Math.round((1.0f - (g / 255.0) / max) * 100); |
||||
cmyk[2] = (int) Math.round((1.0f - (b / 255.0) / max) * 100); |
||||
} else { |
||||
cmyk[0] = 0; |
||||
cmyk[1] = 0; |
||||
cmyk[2] = 0; |
||||
} |
||||
cmyk[3] = (int) Math.round((1.0f - max) * 100); |
||||
return cmyk; |
||||
} |
||||
|
||||
@Override |
||||
public int getMaximum(final int index) { |
||||
return 100; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "CMYK"; |
||||
} |
||||
|
||||
@Override |
||||
public int getValueCount() { |
||||
return 4; |
||||
} |
||||
|
||||
@Override |
||||
public char[] getLabelDescriptorsBefore() { |
||||
return new char[]{'C', 'M', 'Y', 'K'}; |
||||
} |
||||
|
||||
@Override |
||||
public char[] getLabelDescriptorsAfter() { |
||||
return new char[]{'%', '%', '%', '%'}; |
||||
} |
||||
|
||||
@Override |
||||
public int[] getValuesFromColor(@NotNull final Color color) { |
||||
return RGBtoCMYK(color.getRed(), color.getGreen(), color.getBlue()); |
||||
} |
||||
|
||||
@Override |
||||
public Color getColorFromValues(@NotNull final int[] values) { |
||||
var rgb = CMYKtoRGB(values[0] / 100.0, values[1] / 100.0, values[2] / 100.0, values[3] / 100.0); |
||||
return new Color(rgb[0], rgb[1], rgb[2]); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,135 @@
|
||||
package com.weis.darklaf.color; |
||||
|
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.awt.*; |
||||
|
||||
public class DarkColorModelHSB extends DarkColorModel { |
||||
|
||||
private static final int[] hsb = new int[3]; |
||||
private static final int[] rgb = new int[3]; |
||||
|
||||
public DarkColorModelHSB() { |
||||
super("hsv", "Hue", "Saturation", "Brightness"); |
||||
} |
||||
|
||||
@Contract("_, _, _ -> new") |
||||
@NotNull |
||||
public static int[] RGBtoHSB(final int r, final int g, final int b) { |
||||
double hue, saturation, brightness; |
||||
int cmax = Math.max(r, g); |
||||
if (b > cmax) cmax = b; |
||||
int cmin = Math.min(r, g); |
||||
if (b < cmin) cmin = b; |
||||
|
||||
brightness = ((double) cmax) / 255.0f; |
||||
if (cmax != 0) { |
||||
saturation = ((double) (cmax - cmin)) / ((double) cmax); |
||||
} else { |
||||
saturation = 0; |
||||
} |
||||
if (saturation == 0) { |
||||
hue = 0; |
||||
} else { |
||||
double redc = ((double) (cmax - r)) / ((double) (cmax - cmin)); |
||||
double greenc = ((double) (cmax - g)) / ((double) (cmax - cmin)); |
||||
double bluec = ((double) (cmax - b)) / ((double) (cmax - cmin)); |
||||
if (r == cmax) { |
||||
hue = bluec - greenc; |
||||
} else if (g == cmax) { |
||||
hue = 2.0f + redc - bluec; |
||||
} else { |
||||
hue = 4.0f + greenc - redc; |
||||
} |
||||
hue = hue / 6.0f; |
||||
if (hue < 0) { |
||||
hue = hue + 1.0f; |
||||
} |
||||
} |
||||
hsb[0] = (int) Math.round(hue * 360); |
||||
hsb[1] = (int) Math.round(saturation * 100); |
||||
hsb[1] = (int) Math.round(brightness * 100); |
||||
return hsb; |
||||
} |
||||
|
||||
public static int[] HSBtoRGB(final double hue, final double saturation, final double brightness) { |
||||
double r = 0, g = 0, b = 0; |
||||
if (saturation == 0) { |
||||
r = g = b = (brightness * 255.0f + 0.5f); |
||||
} else { |
||||
double h = (hue - Math.floor(hue)) * 6.0f; |
||||
double f = h - Math.floor(h); |
||||
double p = brightness * (1.0f - saturation); |
||||
double q = brightness * (1.0f - saturation * f); |
||||
double t = brightness * (1.0f - (saturation * (1.0f - f))); |
||||
switch ((int) Math.round(h)) { |
||||
case 0: |
||||
r = (brightness * 255.0f + 0.5f); |
||||
g = (t * 255.0f + 0.5f); |
||||
b = (p * 255.0f + 0.5f); |
||||
break; |
||||
case 1: |
||||
r = (q * 255.0f + 0.5f); |
||||
g = (brightness * 255.0f + 0.5f); |
||||
b = (p * 255.0f + 0.5f); |
||||
break; |
||||
case 2: |
||||
r = (p * 255.0f + 0.5f); |
||||
g = (brightness * 255.0f + 0.5f); |
||||
b = (t * 255.0f + 0.5f); |
||||
break; |
||||
case 3: |
||||
r = (p * 255.0f + 0.5f); |
||||
g = (q * 255.0f + 0.5f); |
||||
b = (brightness * 255.0f + 0.5f); |
||||
break; |
||||
case 4: |
||||
r = (t * 255.0f + 0.5f); |
||||
g = (p * 255.0f + 0.5f); |
||||
b = (brightness * 255.0f + 0.5f); |
||||
break; |
||||
case 5: |
||||
r = (brightness * 255.0f + 0.5f); |
||||
g = (p * 255.0f + 0.5f); |
||||
b = (q * 255.0f + 0.5f); |
||||
break; |
||||
} |
||||
} |
||||
rgb[0] = ((int) Math.round(r)); |
||||
rgb[1] = ((int) Math.round(g)); |
||||
rgb[2] = ((int) Math.round(b)); |
||||
return rgb; |
||||
} |
||||
|
||||
@Override |
||||
public int getMaximum(final int index) { |
||||
return (index == 0) ? 359 : 100; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "HSB"; |
||||
} |
||||
|
||||
@Override |
||||
public char[] getLabelDescriptorsBefore() { |
||||
return new char[]{'H', 'S', 'B'}; |
||||
} |
||||
|
||||
@Override |
||||
public char[] getLabelDescriptorsAfter() { |
||||
return new char[]{'\u00B0', '%', '%'}; |
||||
} |
||||
|
||||
@Override |
||||
public int[] getValuesFromColor(@NotNull final Color color) { |
||||
return RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue()); |
||||
} |
||||
|
||||
@Override |
||||
public Color getColorFromValues(@NotNull final int[] values) { |
||||
var rgb = HSBtoRGB(values[0] / 360.0, values[1] / 100.0, values[2] / 100.0); |
||||
return new Color(rgb[0], rgb[1], rgb[2]); |
||||
} |
||||
} |
@ -0,0 +1,129 @@
|
||||
package com.weis.darklaf.color; |
||||
|
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.awt.*; |
||||
|
||||
public class DarkColorModelHSL extends DarkColorModel { |
||||
|
||||
private static final int[] hsl = new int[3]; |
||||
private static final int[] rgb = new int[3]; |
||||
|
||||
public DarkColorModelHSL() { |
||||
super("hsl", "Hue", "Saturation", "Lightness"); |
||||
} |
||||
|
||||
@NotNull |
||||
private static int[] HSLtoRGB(final double h, final double saturation, final double lightness) { |
||||
double hue = h; |
||||
|
||||
if (saturation > 0.0f) { |
||||
hue = (hue < 1.0f) ? hue * 6.0f : 0.0f; |
||||
double q = lightness + saturation * ((lightness > 0.5f) ? 1.0f - lightness : lightness); |
||||
double p = 2.0f * lightness - q; |
||||
rgb[0] = (int) Math.round(255 * normalize(q, p, (hue < 4.0f) ? (hue + 2.0f) : (hue - 4.0f))); |
||||
rgb[1] = (int) Math.round(255 * normalize(q, p, hue)); |
||||
rgb[2] = (int) Math.round(255 * normalize(q, p, (hue < 2.0f) ? (hue + 4.0f) : (hue - 2.0f))); |
||||
} else { |
||||
rgb[0] = (int) Math.round(255 * lightness); |
||||
rgb[1] = rgb[0]; |
||||
rgb[2] = rgb[0]; |
||||
} |
||||
return rgb; |
||||
} |
||||
|
||||
@NotNull |
||||
private static int[] RGBtoHSL(final int r, final int g, final int b) { |
||||
double max = max(r, g, b) / 255.0; |
||||
double min = min(r, g, b) / 255.0; |
||||
|
||||
double summa = (max + min); |
||||
double saturation = (max - min); |
||||
if (saturation > 0.0f) { |
||||
saturation /= (summa > 1.0f) |
||||
? 2.0f - summa |
||||
: summa; |
||||
} |
||||
hsl[0] = (int) Math.round(360 * getHue(r / 255.0, g / 255.0, b / 255.0, max, min)); |
||||
hsl[1] = (int) Math.round(100 * saturation); |
||||
hsl[2] = (int) Math.round(100 * (summa / 2.0)); |
||||
return hsl; |
||||
} |
||||
|
||||
protected static double min(final double red, final double green, final double blue) { |
||||
double min = Math.min(red, green); |
||||
return Math.min(min, blue); |
||||
} |
||||
|
||||
@Contract(pure = true) |
||||
protected static double max(final double red, final double green, final double blue) { |
||||
double max = Math.max(red, green); |
||||
return Math.max(max, blue); |
||||
} |
||||
|
||||
@Contract(pure = true) |
||||
private static double getHue(final double red, final double green, final double blue, |
||||
final double max, final double min) { |
||||
double hue = max - min; |
||||
if (hue > 0.0f) { |
||||
if (max == red) { |
||||
hue = (green - blue) / hue; |
||||
if (hue < 0.0f) { |
||||
hue += 6.0f; |
||||
} |
||||
} else if (max == green) { |
||||
hue = 2.0f + (blue - red) / hue; |
||||
} else /*max == blue*/ { |
||||
hue = 4.0f + (red - green) / hue; |
||||
} |
||||
hue /= 6.0f; |
||||
} |
||||
return hue; |
||||
} |
||||
|
||||
@Contract(pure = true) |
||||
private static double normalize(final double q, final double p, final double color) { |
||||
if (color < 1.0f) { |
||||
return p + (q - p) * color; |
||||
} |
||||
if (color < 3.0f) { |
||||
return q; |
||||
} |
||||
if (color < 4.0f) { |
||||
return p + (q - p) * (4.0f - color); |
||||
} |
||||
return p; |
||||
} |
||||
|
||||
@Override |
||||
public int getMaximum(final int index) { |
||||
return (index == 0) ? 359 : 100; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "HSL"; |
||||
} |
||||
|
||||
@Override |
||||
public char[] getLabelDescriptorsBefore() { |
||||
return new char[]{'H', 'S', 'L'}; |
||||
} |
||||
|
||||
@Override |
||||
public char[] getLabelDescriptorsAfter() { |
||||
return new char[]{'\u00B0', '%', '%'}; |
||||
} |
||||
|
||||
@Override |
||||
public int[] getValuesFromColor(@NotNull final Color color) { |
||||
return RGBtoHSL(color.getRed(), color.getGreen(), color.getBlue()); |
||||
} |
||||
|
||||
@Override |
||||
public Color getColorFromValues(@NotNull final int[] values) { |
||||
var rgb = HSLtoRGB(values[0] / 360.0, values[1] / 100.0, values[2] / 100.0); |
||||
return new Color(rgb[0], rgb[1], rgb[2]); |
||||
} |
||||
} |
@ -0,0 +1,172 @@
|
||||
package com.weis.darklaf.components.border; |
||||
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import com.weis.darklaf.util.GraphicsUtil; |
||||
import com.weis.darklaf.util.ImageUtil; |
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
import org.jetbrains.annotations.Nullable; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
import java.awt.geom.Area; |
||||
import java.awt.geom.Rectangle2D; |
||||
import java.awt.image.BufferedImage; |
||||
|
||||
import static com.weis.darklaf.util.GraphicsUtil.SCALE; |
||||
|
||||
/** |
||||
* @author Konstantin Bulenkov |
||||
*/ |
||||
public class ShadowPainter { |
||||
protected final Icon myTop; |
||||
protected final Icon myTopRight; |
||||
protected final Icon myRight; |
||||
protected final Icon myBottomRight; |
||||
protected final Icon myBottom; |
||||
protected final Icon myBottomLeft; |
||||
protected final Icon myLeft; |
||||
protected final Icon myTopLeft; |
||||
|
||||
private Icon myCroppedTop = null; |
||||
private Icon myCroppedRight = null; |
||||
private Icon myCroppedBottom = null; |
||||
private Icon myCroppedLeft = null; |
||||
|
||||
@Nullable |
||||
private Color myBorderColor; |
||||
|
||||
public ShadowPainter(final Icon top, final Icon topRight, final Icon right, |
||||
final Icon bottomRight, final Icon bottom, final Icon bottomLeft, |
||||
final Icon left, final Icon topLeft) { |
||||
myTop = top; |
||||
myTopRight = topRight; |
||||
myRight = right; |
||||
myBottomRight = bottomRight; |
||||
myBottom = bottom; |
||||
myBottomLeft = bottomLeft; |
||||
myLeft = left; |
||||
myTopLeft = topLeft; |
||||
updateIcons(); |
||||
} |
||||
|
||||
@Contract(pure = true) |
||||
public ShadowPainter(final Icon top, final Icon topRight, final Icon right, |
||||
final Icon bottomRight, final Icon bottom, final Icon bottomLeft, |
||||
final Icon left, final Icon topLeft, @Nullable final Color borderColor) { |
||||
this(top, topRight, right, bottomRight, bottom, bottomLeft, left, topLeft); |
||||
myBorderColor = borderColor; |
||||
} |
||||
|
||||
private static void fill(final Graphics g, final Icon pattern, final int x, final int y, final int from, |
||||
final int to, final boolean horizontally) { |
||||
double scale = SCALE; |
||||
if (GraphicsUtil.isHighDpiEnabled() && Math.ceil(scale) > scale) { |
||||
// Direct painting for fractional scale
|
||||
BufferedImage img = ImageUtil.toBufferedImage(ImageUtil.toImage(pattern)); |
||||
int patternSize = horizontally ? img.getWidth() : img.getHeight(); |
||||
Graphics2D g2d = (Graphics2D) g.create(); |
||||
try { |
||||
g2d.scale(1 / scale, 1 / scale); |
||||
g2d.translate(x * scale, y * scale); |
||||
for (int at = (int) Math.floor(from * scale); at < to * scale; at += patternSize) { |
||||
if (horizontally) { |
||||
g2d.drawImage(img, at, 0, null); |
||||
} else { |
||||
g2d.drawImage(img, 0, at, null); |
||||
} |
||||
} |
||||
} finally { |
||||
g2d.dispose(); |
||||
} |
||||
} else { |
||||
for (int at = from; at < to; at++) { |
||||
if (horizontally) { |
||||
pattern.paintIcon(null, g, x + at, y); |
||||
} else { |
||||
pattern.paintIcon(null, g, x, y + at); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void setBorderColor(@Nullable final Color borderColor) { |
||||
myBorderColor = borderColor; |
||||
} |
||||
|
||||
private void updateIcons() { |
||||
myCroppedTop = ImageUtil.cropIcon(myTop, 1, Integer.MAX_VALUE); |
||||
myCroppedRight = ImageUtil.cropIcon(myRight, Integer.MAX_VALUE, 1); |
||||
myCroppedBottom = ImageUtil.cropIcon(myBottom, 1, Integer.MAX_VALUE); |
||||
myCroppedLeft = ImageUtil.cropIcon(myLeft, Integer.MAX_VALUE, 1); |
||||
} |
||||
|
||||
public BufferedImage createShadow(@NotNull final JComponent c, final int width, final int height) { |
||||
final BufferedImage image = c.getGraphicsConfiguration() |
||||
.createCompatibleImage(width, height, Transparency.TRANSLUCENT); |
||||
final Graphics2D g = image.createGraphics(); |
||||
paintShadow(c, g, 0, 0, width, height); |
||||
g.dispose(); |
||||
return image; |
||||
} |
||||
|
||||
public void paintShadow(final Component c, final Graphics2D g, final int x, final int y, |
||||
final int width, final int height) { |
||||
final int leftSize = myCroppedLeft.getIconWidth(); |
||||
final int rightSize = myCroppedRight.getIconWidth(); |
||||
final int bottomSize = myCroppedBottom.getIconHeight(); |
||||
final int topSize = myCroppedTop.getIconHeight(); |
||||
// updateIcons();
|
||||
|
||||
int delta = myTopLeft.getIconHeight() + myBottomLeft.getIconHeight() - height; |
||||
if (delta > 0) { // Corner icons are overlapping. Need to handle this
|
||||
Shape clip = g.getClip(); |
||||
|
||||
int topHeight = myTopLeft.getIconHeight() - delta / 2; |
||||
Area top = new Area(new Rectangle2D.Float(x, y, width, topHeight)); |
||||
if (clip != null) { |
||||
top.intersect(new Area(clip)); |
||||
} |
||||
g.setClip(top); |
||||
|
||||
myTopLeft.paintIcon(c, g, x, y); |
||||
myTopRight.paintIcon(c, g, x + width - myTopRight.getIconWidth(), y); |
||||
|
||||
int bottomHeight = myBottomLeft.getIconHeight() - delta + delta / 2; |
||||
Area bottom = new Area(new Rectangle2D.Float(x, y + topHeight, width, bottomHeight)); |
||||
if (clip != null) { |
||||
bottom.intersect(new Area(clip)); |
||||
} |
||||
g.setClip(bottom); |
||||
|
||||
myBottomLeft.paintIcon(c, g, x, y + height - myBottomLeft.getIconHeight()); |
||||
myBottomRight.paintIcon(c, g, x + width - myBottomRight.getIconWidth(), |
||||
y + height - myBottomRight.getIconHeight()); |
||||
|
||||
g.setClip(clip); |
||||
} else { |
||||
myTopLeft.paintIcon(c, g, x, y); |
||||
myTopRight.paintIcon(c, g, x + width - myTopRight.getIconWidth(), y); |
||||
myBottomLeft.paintIcon(c, g, x, y + height - myBottomLeft.getIconHeight()); |
||||
myBottomRight.paintIcon(c, g, x + width - myBottomRight.getIconWidth(), |
||||
y + height - myBottomRight.getIconHeight()); |
||||
} |
||||
|
||||
fill(g, myCroppedTop, x, y, myTopLeft.getIconWidth(), |
||||
width - myTopRight.getIconWidth(), true); |
||||
fill(g, myCroppedBottom, x, y + height - bottomSize, myBottomLeft.getIconWidth(), |
||||
width - myBottomRight.getIconWidth(), true); |
||||
fill(g, myCroppedLeft, x, y, myTopLeft.getIconHeight(), |
||||
height - myBottomLeft.getIconHeight(), false); |
||||
fill(g, myCroppedRight, x + width - rightSize, y, myTopRight.getIconHeight(), |
||||
height - myBottomRight.getIconHeight(), false); |
||||
|
||||
if (myBorderColor != null) { |
||||
g.setColor(myBorderColor); |
||||
g.drawRect(x + leftSize - 1, y + topSize - 1, width - leftSize - rightSize + 1, |
||||
height - topSize - bottomSize + 1); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
package com.weis.darklaf.ui.colorchooser; |
||||
|
||||
import java.awt.*; |
||||
|
||||
public interface ColorListener { |
||||
|
||||
void colorChanged(final Color color, final Object source); |
||||
} |
@ -0,0 +1,38 @@
|
||||
package com.weis.darklaf.ui.colorchooser; |
||||
|
||||
/* |
||||
* Copyright 2000-2015 JetBrains s.r.o. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import com.intellij.openapi.Disposable; |
||||
import org.jetbrains.annotations.Nullable; |
||||
|
||||
import java.awt.*; |
||||
import java.awt.image.ImageObserver; |
||||
|
||||
public interface ColorPipette extends ImageObserver, Disposable { |
||||
void setInitialColor(@Nullable Color initialColor); |
||||
|
||||
@Nullable |
||||
Color getColor(); |
||||
|
||||
Window show(); |
||||
|
||||
void pickAndClose(); |
||||
|
||||
void cancelPipette(); |
||||
|
||||
boolean isAvailable(); |
||||
} |
@ -0,0 +1,204 @@
|
||||
package com.weis.darklaf.ui.colorchooser; |
||||
|
||||
import com.weis.darklaf.color.DarkColorModel; |
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.swing.*; |
||||
import javax.swing.text.AttributeSet; |
||||
import javax.swing.text.BadLocationException; |
||||
import javax.swing.text.DefaultFormatterFactory; |
||||
import javax.swing.text.DocumentFilter; |
||||
import java.awt.*; |
||||
import java.awt.event.FocusEvent; |
||||
import java.awt.event.FocusListener; |
||||
import java.text.ParseException; |
||||
|
||||
import static java.util.Locale.ENGLISH; |
||||
|
||||
public final class ColorValueFormatter extends JFormattedTextField.AbstractFormatter implements FocusListener { |
||||
|
||||
private final int fieldIndex; |
||||
private final int radix; |
||||
private final boolean hex; |
||||
private DarkColorModel model; |
||||
private boolean transparencyEnabled; |
||||
private JFormattedTextField text; |
||||
private final DocumentFilter filter = new DocumentFilter() { |
||||
@Override |
||||
public void remove(@NotNull final FilterBypass fb, final int offset, |
||||
final int length) throws BadLocationException { |
||||
if (isValid(fb.getDocument().getLength() - length)) { |
||||
fb.remove(offset, length); |
||||
commit(); |
||||
} else { |
||||
Toolkit.getDefaultToolkit().beep(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void replace(@NotNull final FilterBypass fb, final int offset, final int length, |
||||
@NotNull final String text, final AttributeSet set) throws BadLocationException { |
||||
if (isValid(fb.getDocument().getLength() + text.length() - length) && isValid(text)) { |
||||
var newText = new StringBuilder(fb.getDocument().getText(0, fb.getDocument().getLength())); |
||||
newText.replace(offset, offset + length, text); |
||||
if (hex || isValidValue(newText.toString())) { |
||||
fb.replace(offset, length, text.toUpperCase(ENGLISH), set); |
||||
commit(); |
||||
return; |
||||
} |
||||
} |
||||
Toolkit.getDefaultToolkit().beep(); |
||||
} |
||||
|
||||
@Override |
||||
public void insertString(@NotNull final FilterBypass fb, final int offset, |
||||
@NotNull final String text, final AttributeSet set) throws BadLocationException { |
||||
if (isValid(fb.getDocument().getLength() + text.length()) |
||||
&& isValid(text)) { |
||||
var newText = new StringBuilder(fb.getDocument().getText(0, fb.getDocument().getLength())); |
||||
newText.insert(offset, text); |
||||
if (hex || isValidValue(newText.toString())) { |
||||
fb.insertString(offset, text.toUpperCase(ENGLISH), set); |
||||
commit(); |
||||
return; |
||||
} |
||||
} |
||||
Toolkit.getDefaultToolkit().beep(); |
||||
} |
||||
}; |
||||
|
||||
private ColorValueFormatter(final DarkColorModel model, final int index, final boolean hex) { |
||||
this.model = model; |
||||
this.fieldIndex = index; |
||||
this.radix = hex ? 16 : 10; |
||||
this.hex = hex; |
||||
} |
||||
|
||||
@NotNull |
||||
static ColorValueFormatter init(final DarkColorModel model, final int index, |
||||
final boolean hex, @NotNull final JFormattedTextField text) { |
||||
ColorValueFormatter formatter = new ColorValueFormatter(model, index, hex); |
||||
text.setFormatterFactory(new DefaultFormatterFactory(formatter)); |
||||
text.setMinimumSize(text.getPreferredSize()); |
||||
text.addFocusListener(formatter); |
||||
return formatter; |
||||
} |
||||
|
||||
private void commit() { |
||||
SwingUtilities.invokeLater(() -> { |
||||
try { |
||||
if (text != null) { |
||||
text.commitEdit(); |
||||
} |
||||
} catch (ParseException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
public void setModel(final DarkColorModel model) { |
||||
this.model = model; |
||||
} |
||||
|
||||
public void setTransparencyEnabled(final boolean transparencyEnabled) { |
||||
this.transparencyEnabled = transparencyEnabled; |
||||
} |
||||
|
||||
@Override |
||||
public Object stringToValue(@NotNull final String text) throws ParseException { |
||||
try { |
||||
if (text.isEmpty()) { |
||||
return model.getDefault(fieldIndex); |
||||
} |
||||
if (hex) { |
||||
return String.format("%-8s", text).replace(' ', '0'); |
||||
} |
||||
var value = Integer.valueOf(text, this.radix); |
||||
var min = model.getMinimum(fieldIndex); |
||||
var max = model.getMaximum(fieldIndex); |
||||
if (value > max || value < min) { |
||||
throw new ParseException("Value not in range [" + min + "," + max + "]", 0); |
||||
} |
||||
return ((Number) value).intValue(); |
||||
} catch (NumberFormatException nfe) { |
||||
ParseException pe = new ParseException("illegal format", 0); |
||||
pe.initCause(nfe); |
||||
throw pe; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String valueToString(final Object object) throws ParseException { |
||||
if (object instanceof Integer) { |
||||
if (this.radix == 10) { |
||||
return object.toString(); |
||||
} |
||||
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); |
||||
} |
||||
|
||||
@Contract(pure = true) |
||||
@Override |
||||
protected DocumentFilter getDocumentFilter() { |
||||
return this.filter; |
||||
} |
||||
|
||||
public void focusGained(@NotNull final FocusEvent event) { |
||||
Object source = event.getSource(); |
||||
if (source instanceof JFormattedTextField) { |
||||
this.text = (JFormattedTextField) source; |
||||
SwingUtilities.invokeLater(() -> { |
||||
if (this.text != null) { |
||||
this.text.selectAll(); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
||||
public void focusLost(final FocusEvent event) { |
||||
SwingUtilities.invokeLater(() -> text.select(0, 0)); |
||||
} |
||||
|
||||
private int getLength() { |
||||
return hex ? getHexLength() : String.valueOf(model.getMaximum(fieldIndex)).length(); |
||||
} |
||||
|
||||
@Contract(pure = true) |
||||
private int getHexLength() { |
||||
return transparencyEnabled ? 8 : 6; |
||||
} |
||||
|
||||
@Contract(pure = true) |
||||
private boolean isValid(final int length) { |
||||
return (0 <= length) && (length <= getLength()); |
||||
} |
||||
|
||||
private boolean isValid(@NotNull final String text) { |
||||
int length = text.length(); |
||||
for (int i = 0; i < length; i++) { |
||||
char ch = text.charAt(i); |
||||
if (Character.digit(ch, this.radix) < 0) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
private boolean isValidValue(final String text) { |
||||
try { |
||||
stringToValue(text); |
||||
} catch (ParseException e) { |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
} |
@ -0,0 +1,185 @@
|
||||
package com.weis.darklaf.ui.colorchooser; |
||||
|
||||
|
||||
import com.weis.darklaf.util.ColorUtil; |
||||
import com.weis.darklaf.util.GraphicsContext; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
import java.awt.event.ComponentAdapter; |
||||
import java.awt.event.ComponentEvent; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
public class ColorWheel extends JComponent { |
||||
private static final int BORDER_SIZE = 5; |
||||
private final List<ColorListener> myListeners = new ArrayList<>(); |
||||
private float myBrightness = 1f; |
||||
private float myHue = 1f; |
||||
private float mySaturation = 0f; |
||||
private Image myImage; |
||||
private Rectangle myWheel; |
||||
private boolean myShouldInvalidate = true; |
||||
private Color myColor; |
||||
private int myOpacity; |
||||
private boolean pressedInside; |
||||
|
||||
public ColorWheel() { |
||||
setOpaque(true); |
||||
addComponentListener(new ComponentAdapter() { |
||||
@Override |
||||
public void componentResized(final ComponentEvent e) { |
||||
myShouldInvalidate = true; |
||||
} |
||||
}); |
||||
|
||||
addMouseMotionListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseDragged(final MouseEvent e) { |
||||
if (!pressedInside) return; |
||||
final int x = e.getX(); |
||||
final int y = e.getY(); |
||||
int mx = myWheel.x + myWheel.width / 2; |
||||
int my = myWheel.y + myWheel.height / 2; |
||||
double s; |
||||
double h; |
||||
s = Math.sqrt((x - mx) * (x - mx) + (y - my) * (y - my)) / (myWheel.height / 2.0); |
||||
h = -Math.atan2(y - my, x - mx) / (2 * Math.PI); |
||||
if (h < 0) h += 1.0; |
||||
if (s > 1) s = 1.0; |
||||
|
||||
setHSBValue((float) h, (float) s, myBrightness, myOpacity); |
||||
} |
||||
}); |
||||
|
||||
addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mousePressed(final MouseEvent e) { |
||||
final int x = e.getX(); |
||||
final int y = e.getY(); |
||||
int mx = myWheel.x + myWheel.width / 2; |
||||
int my = myWheel.y + myWheel.height / 2; |
||||
double s; |
||||
double h; |
||||
s = Math.sqrt((x - mx) * (x - mx) + (y - my) * (y - my)) / (myWheel.height / 2.0); |
||||
h = -Math.atan2(y - my, x - mx) / (2 * Math.PI); |
||||
if (h < 0) h += 1.0; |
||||
if (s <= 1) { |
||||
pressedInside = true; |
||||
setHSBValue((float) h, (float) s, myBrightness, myOpacity); |
||||
} else { |
||||
pressedInside = false; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void mouseReleased(final MouseEvent e) { |
||||
pressedInside = false; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
private void setHSBValue(final float h, final float s, final float b, final int opacity) { |
||||
Color rgb = new Color(Color.HSBtoRGB(h, s, b)); |
||||
setColor(ColorUtil.toAlpha(rgb, opacity), this, h, s, b); |
||||
} |
||||
|
||||
public void setColor(@NotNull final Color color, final Object source, |
||||
final float h, final float s, final float b) { |
||||
myColor = color; |
||||
myHue = h; |
||||
mySaturation = s; |
||||
myBrightness = b; |
||||
myOpacity = color.getAlpha(); |
||||
|
||||
fireColorChanged(source); |
||||
|
||||
repaint(); |
||||
} |
||||
|
||||
public void addListener(final ColorListener listener) { |
||||
myListeners.add(listener); |
||||
} |
||||
|
||||
private void fireColorChanged(final Object source) { |
||||
for (ColorListener listener : myListeners) { |
||||
listener.colorChanged(myColor, source); |
||||
} |
||||
} |
||||
|
||||
public void setBrightness(final float brightness) { |
||||
if (brightness != myBrightness) { |
||||
myImage = null; |
||||
setHSBValue(myHue, mySaturation, brightness, myOpacity); |
||||
} |
||||
} |
||||
|
||||
public void setOpacity(final int opacity) { |
||||
if (opacity != myOpacity) { |
||||
setHSBValue(myHue, mySaturation, myBrightness, opacity); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getPreferredSize() { |
||||
return getMinimumSize(); |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getMinimumSize() { |
||||
return new Dimension(300, 300); |
||||
} |
||||
|
||||
@Override |
||||
protected void paintComponent(final Graphics g) { |
||||
Graphics2D g2d = (Graphics2D) g; |
||||
|
||||
final Dimension size = getSize(); |
||||
int _size = Math.min(size.width, size.height); |
||||
_size = Math.min(_size, 600); |
||||
|
||||
if (myImage != null && myShouldInvalidate) { |
||||
if (myImage.getWidth(null) != _size) { |
||||
myImage = null; |
||||
} |
||||
} |
||||
|
||||
myShouldInvalidate = false; |
||||
|
||||
if (myImage == null) { |
||||
myImage = createImage(new ColorWheelImageProducer( |
||||
_size - BORDER_SIZE * 2, _size - BORDER_SIZE * 2, myBrightness)); |
||||
myWheel = new Rectangle(BORDER_SIZE, BORDER_SIZE, _size - BORDER_SIZE * 2, |
||||
_size - BORDER_SIZE * 2); |
||||
} |
||||
|
||||
g2d.setColor(UIManager.getColor("Panel.background")); |
||||
g2d.fillRect(0, 0, getWidth(), getHeight()); |
||||
|
||||
|
||||
var config = new GraphicsContext(g); |
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, ((float) myOpacity) / 255f)); |
||||
g2d.drawImage(myImage, myWheel.x, myWheel.y, null); |
||||
config.restore(); |
||||
|
||||
int mx = myWheel.x + myWheel.width / 2; |
||||
int my = myWheel.y + myWheel.height / 2; |
||||
int arcw = (int) (myWheel.width * mySaturation / 2); |
||||
int arch = (int) (myWheel.height * mySaturation / 2); |
||||
double th = myHue * 2 * Math.PI; |
||||
final int x = (int) (mx + arcw * Math.cos(th)); |
||||
final int y = (int) (my - arch * Math.sin(th)); |
||||
|
||||
g2d.setColor(UIManager.getColor("ColorChooser.colorWheelDropBackgroundColor")); |
||||
g2d.fillRect(x - 2, y - 2, 4, 4); |
||||
g2d.setColor(UIManager.getColor("ColorChooser.colorWheelDropBorderColor")); |
||||
g2d.drawRect(x - 2, y - 2, 4, 4); |
||||
} |
||||
|
||||
public void dropImage() { |
||||
myImage = null; |
||||
} |
||||
} |
@ -0,0 +1,82 @@
|
||||
package com.weis.darklaf.ui.colorchooser; |
||||
|
||||
import java.awt.*; |
||||
import java.awt.image.ColorModel; |
||||
import java.awt.image.MemoryImageSource; |
||||
|
||||
public class ColorWheelImageProducer extends MemoryImageSource { |
||||
|
||||
private final int[] myPixels; |
||||
private final int myWidth; |
||||
private final int myHeight; |
||||
private float myBrightness = 1f; |
||||
|
||||
private float[] myHues; |
||||
private float[] mySat; |
||||
private int[] myAlphas; |
||||
|
||||
public ColorWheelImageProducer(final int w, final int h, final float brightness) { |
||||
super(w, h, null, 0, w); |
||||
myPixels = new int[w * h]; |
||||
myWidth = w; |
||||
myHeight = h; |
||||
myBrightness = brightness; |
||||
generateLookupTables(); |
||||
newPixels(myPixels, ColorModel.getRGBdefault(), 0, w); |
||||
setAnimated(true); |
||||
generateColorWheel(); |
||||
} |
||||
|
||||
public int getRadius() { |
||||
return Math.min(myWidth, myHeight) / 2 - 2; |
||||
} |
||||
|
||||
private void generateLookupTables() { |
||||
mySat = new float[myWidth * myHeight]; |
||||
myHues = new float[myWidth * myHeight]; |
||||
myAlphas = new int[myWidth * myHeight]; |
||||
float radius = getRadius(); |
||||
|
||||
// blend is used to create a linear alpha gradient of two extra pixels
|
||||
float blend = (radius + 2f) / radius - 1f; |
||||
|
||||
// Center of the color wheel circle
|
||||
int cx = myWidth / 2; |
||||
int cy = myHeight / 2; |
||||
|
||||
for (int x = 0; x < myWidth; x++) { |
||||
int kx = x - cx; // cartesian coordinates of x
|
||||
int squarekx = kx * kx; // Square of cartesian x
|
||||
|
||||
for (int y = 0; y < myHeight; y++) { |
||||
int ky = cy - y; // cartesian coordinates of y
|
||||
|
||||
int index = x + y * myWidth; |
||||
mySat[index] = (float) Math.sqrt(squarekx + ky |
||||
* ky) |
||||
/ radius; |
||||
if (mySat[index] <= 1f) { |
||||
myAlphas[index] = 0xff000000; |
||||
} else { |
||||
myAlphas[index] = (int) ((blend - Math.min(blend, |
||||
mySat[index] - 1f)) * 255 / blend) << 24; |
||||
mySat[index] = 1f; |
||||
} |
||||
if (myAlphas[index] != 0) { |
||||
myHues[index] = (float) (Math.atan2(ky, kx) / Math.PI / 2d); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void generateColorWheel() { |
||||
for (int index = 0; index < myPixels.length; index++) { |
||||
if (myAlphas[index] != 0) { |
||||
myPixels[index] = myAlphas[index] | 0xffffff & Color.HSBtoRGB(myHues[index], |
||||
mySat[index], |
||||
myBrightness); |
||||
} |
||||
} |
||||
newPixels(); |
||||
} |
||||
} |
@ -0,0 +1,78 @@
|
||||
package com.weis.darklaf.ui.colorchooser; |
||||
|
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.swing.*; |
||||
import javax.swing.colorchooser.AbstractColorChooserPanel; |
||||
import java.awt.*; |
||||
|
||||
public class ColorWheelPanel extends JPanel { |
||||
private final ColorWheel myColorWheel; |
||||
private final SlideComponent myBrightnessComponent; |
||||
private final boolean opacityInPercent; |
||||
private SlideComponent myOpacityComponent = null; |
||||
private boolean enableOpacity; |
||||
|
||||
public ColorWheelPanel(final ColorListener colorListener, final boolean enableOpacity, |
||||
final boolean opacityInPercent) { |
||||
setLayout(new BorderLayout()); |
||||
setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0)); |
||||
|
||||
this.enableOpacity = enableOpacity; |
||||
this.opacityInPercent = opacityInPercent; |
||||
|
||||
myColorWheel = new ColorWheel(); |
||||
add(myColorWheel, BorderLayout.CENTER); |
||||
|
||||
myColorWheel.addListener(colorListener); |
||||
|
||||
myBrightnessComponent = new SlideComponent("Brightness", true); |
||||
myBrightnessComponent.setToolTipText("Brightness"); |
||||
myBrightnessComponent.addListener(value -> { |
||||
myColorWheel.setBrightness(1f - (value / 255f)); |
||||
myColorWheel.repaint(); |
||||
}); |
||||
|
||||
add(myBrightnessComponent, BorderLayout.EAST); |
||||
|
||||
if (enableOpacity) { |
||||
myOpacityComponent = new SlideComponent("Opacity", false); |
||||
myOpacityComponent.setUnits(opacityInPercent ? SlideComponent.Unit.PERCENT : SlideComponent.Unit.LEVEL); |
||||
myOpacityComponent.setToolTipText("Opacity"); |
||||
myOpacityComponent.addListener(integer -> { |
||||
myColorWheel.setOpacity(integer); |
||||
myColorWheel.repaint(); |
||||
}); |
||||
|
||||
add(myOpacityComponent, BorderLayout.SOUTH); |
||||
} |
||||
} |
||||
|
||||
public void setColor(@NotNull final Color color, final Object source) { |
||||
float[] hsb = new float[3]; |
||||
Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), hsb); |
||||
|
||||
myBrightnessComponent.setValue(255 - (int) (hsb[2] * 255)); |
||||
myBrightnessComponent.repaint(); |
||||
|
||||
myColorWheel.dropImage(); |
||||
if (myOpacityComponent != null && source instanceof AbstractColorChooserPanel) { |
||||
myOpacityComponent.setValue(color.getAlpha()); |
||||
myOpacityComponent.repaint(); |
||||
} |
||||
|
||||
myColorWheel.setColor(color, source, hsb[0], hsb[1], hsb[2]); |
||||
} |
||||
|
||||
public boolean isColorTransparencySelectionEnabled() { |
||||
return enableOpacity; |
||||
} |
||||
|
||||
public void setColorTransparencySelectionEnabled(final boolean b) { |
||||
if (b != enableOpacity) { |
||||
enableOpacity = b; |
||||
myOpacityComponent.setEnabled(b); |
||||
myOpacityComponent.setVisible(b); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,407 @@
|
||||
package com.weis.darklaf.ui.colorchooser; |
||||
|
||||
import com.weis.darklaf.color.DarkColorModel; |
||||
import com.weis.darklaf.util.ColorUtil; |
||||
import com.weis.darklaf.util.DefaultColorPipette; |
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.swing.*; |
||||
import javax.swing.colorchooser.AbstractColorChooserPanel; |
||||
import javax.swing.event.AncestorEvent; |
||||
import javax.swing.event.AncestorListener; |
||||
import java.awt.*; |
||||
|
||||
public class DarkColorChooserPanel extends AbstractColorChooserPanel implements ColorListener { |
||||
|
||||
private final ColorPipette pipette; |
||||
private final ColorWheelPanel colorWheelPanel; |
||||
private final JFormattedTextField textHex; |
||||
private final ColorValueFormatter hexFormatter; |
||||
private final JFormattedTextField[] valueFields; |
||||
private final ColorValueFormatter[] formatters; |
||||
|
||||
private final JComboBox<DarkColorModel> formatBox; |
||||
|
||||
private final ColorPreviewComponent previewComponent; |
||||
private final JLabel[] descriptors; |
||||
private final JLabel[] descriptorsAfter; |
||||
private final boolean doneInit; |
||||
private Color currentColor; |
||||
private boolean isChanging; |
||||
|
||||
@Contract("null -> fail") |
||||
public DarkColorChooserPanel(final DarkColorModel... colorModels) { |
||||
if (colorModels == null || colorModels.length == 0) { |
||||
throw new IllegalArgumentException("Must pass at least one valid colorModel"); |
||||
} |
||||
|
||||
previewComponent = new ColorPreviewComponent(); |
||||
colorWheelPanel = new ColorWheelPanel(this, true, true); |
||||
pipette = new DefaultColorPipette(this, colorWheelPanel::setColor); |
||||
|
||||
formatBox = new JComboBox<>(colorModels); |
||||
formatBox.addActionListener(e -> { |
||||
updateDescriptors(); |
||||
updateValueFields(); |
||||
updateFormatters(); |
||||
applyColorToFields(getColorFromModel()); |
||||
doLayout(); |
||||
}); |
||||
int record = 0; |
||||
DarkColorModel prototype = null; |
||||
for (var model : colorModels) { |
||||
record = Math.max(model.getValueCount(), record); |
||||
var name = model.toString(); |
||||
if (prototype == null || prototype.toString().length() < name.length()) { |
||||
prototype = model; |
||||
} |
||||
} |
||||
|
||||
formatBox.setPrototypeDisplayValue(prototype); |
||||
descriptors = new JLabel[record]; |
||||
descriptorsAfter = new JLabel[record]; |
||||
|
||||
textHex = createColorField(true); |
||||
textHex.addAncestorListener(new AncestorListener() { |
||||
@Override |
||||
public void ancestorAdded(final AncestorEvent event) { |
||||
textHex.requestFocus(); |
||||
textHex.removeAncestorListener(this); |
||||
} |
||||
|
||||
@Override |
||||
public void ancestorRemoved(final AncestorEvent event) { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void ancestorMoved(final AncestorEvent event) { |
||||
|
||||
} |
||||
}); |
||||
|
||||
hexFormatter = ColorValueFormatter.init(getDarkColorModel(), 0, true, textHex); |
||||
hexFormatter.setTransparencyEnabled(isColorTransparencySelectionEnabled()); |
||||
|
||||
valueFields = new JFormattedTextField[record]; |
||||
formatters = new ColorValueFormatter[record]; |
||||
|
||||
for (int i = 0; i < record; i++) { |
||||
descriptors[i] = new JLabel(); |
||||
descriptorsAfter[i] = new JLabel(); |
||||
valueFields[i] = createColorField(false); |
||||
formatters[i] = ColorValueFormatter.init(getDarkColorModel(), i, false, valueFields[i]); |
||||
} |
||||
|
||||
setLayout(new BorderLayout()); |
||||
setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5)); |
||||
add(buildTopPanel(true), BorderLayout.NORTH); |
||||
add(colorWheelPanel, BorderLayout.CENTER); |
||||
add(Box.createVerticalStrut(10), BorderLayout.SOUTH); |
||||
updateValueFields(); |
||||
updateDescriptors(); |
||||
doneInit = true; |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getPreferredSize() { |
||||
return new Dimension(520, 420); |
||||
} |
||||
|
||||
private void updateFormatters() { |
||||
for (ColorValueFormatter formatter : formatters) { |
||||
formatter.setModel(getDarkColorModel()); |
||||
} |
||||
} |
||||
|
||||
private void updateValueFields() { |
||||
var model = getDarkColorModel(); |
||||
int count = model.getValueCount(); |
||||
for (int i = 0; i < valueFields.length; i++) { |
||||
valueFields[i].setEnabled(i < count); |
||||
valueFields[i].setVisible(i < count); |
||||
} |
||||
} |
||||
|
||||
private void updateDescriptors() { |
||||
var desc = getDarkColorModel().getLabelDescriptorsBefore(); |
||||
var descAfter = getDarkColorModel().getLabelDescriptorsAfter(); |
||||
for (int i = 0; i < descriptors.length; i++) { |
||||
if (i < desc.length) { |
||||
descriptors[i].setText(desc[i] + ":"); |
||||
} else { |
||||
descriptors[i].setText(""); |
||||
} |
||||
if (i < descAfter.length) { |
||||
descriptorsAfter[i].setText(String.valueOf(descAfter[i])); |
||||
} else { |
||||
descriptorsAfter[i].setText(""); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@NotNull |
||||
private JComponent buildTopPanel(final boolean enablePipette) { |
||||
final JPanel result = new JPanel(new BorderLayout()); |
||||
|
||||
final JPanel previewPanel = new JPanel(new BorderLayout()); |
||||
if (enablePipette && pipette != null) { |
||||
JButton pipetteButton = new JButton(); |
||||
pipetteButton.putClientProperty("JButton.variant", "onlyLabel"); |
||||
pipetteButton.putClientProperty("JButton.thin", Boolean.TRUE); |
||||
pipetteButton.setRolloverEnabled(true); |
||||
pipetteButton.setIcon(getPipetteIcon()); |
||||
pipetteButton.setRolloverIcon(getPipetteRolloverIcon()); |
||||
pipetteButton.setDisabledIcon(getPipetteRolloverIcon()); |
||||
pipetteButton.setPressedIcon(getPipetteRolloverIcon()); |
||||
pipetteButton.setFocusable(false); |
||||
pipetteButton.addActionListener(e -> { |
||||
pipetteButton.setEnabled(false); |
||||
pipette.setInitialColor(getColorFromModel()); |
||||
pipette.show(); |
||||
}); |
||||
((DefaultColorPipette) pipette).setCloseAction(() -> pipetteButton.setEnabled(true)); |
||||
previewPanel.add(pipetteButton, BorderLayout.WEST); |
||||
} |
||||
previewPanel.add(previewComponent, BorderLayout.CENTER); |
||||
result.add(previewPanel, BorderLayout.NORTH); |
||||
|
||||
final JPanel valuePanel = new JPanel(); |
||||
valuePanel.setLayout(new BoxLayout(valuePanel, BoxLayout.X_AXIS)); |
||||
valuePanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0)); |
||||
|
||||
for (int i = 0; i < descriptorsAfter.length; i++) { |
||||
descriptorsAfter[i].setPreferredSize(new Dimension(14, -1)); |
||||
descriptors[i].setPreferredSize(new Dimension(14, -1)); |
||||
valuePanel.add(descriptors[i]); |
||||
valuePanel.add(valueFields[i]); |
||||
valuePanel.add(descriptorsAfter[i]); |
||||
if (i < descriptorsAfter.length - 1) { |
||||
valuePanel.add(Box.createHorizontalStrut(2)); |
||||
} |
||||
} |
||||
result.add(valuePanel, BorderLayout.WEST); |
||||
|
||||
final JPanel hexPanel = new JPanel(); |
||||
hexPanel.setLayout(new BoxLayout(hexPanel, BoxLayout.X_AXIS)); |
||||
hexPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0)); |
||||
|
||||
hexPanel.add(formatBox); |
||||
hexPanel.add(Box.createHorizontalStrut(2)); |
||||
hexPanel.add(new JLabel("#")); |
||||
hexPanel.add(textHex); |
||||
|
||||
result.add(hexPanel, BorderLayout.EAST); |
||||
return result; |
||||
} |
||||
|
||||
protected Icon getPipetteIcon() { |
||||
return UIManager.getIcon("ColorChooser.pipette.icon"); |
||||
} |
||||
|
||||
protected Icon getPipetteRolloverIcon() { |
||||
return UIManager.getIcon("ColorChooser.pipetteRollover.icon"); |
||||
} |
||||
|
||||
|
||||
@NotNull |
||||
private JFormattedTextField createColorField(final boolean hex) { |
||||
JFormattedTextField field = new JFormattedTextField(0); |
||||
field.setColumns(hex ? 8 : 4); |
||||
if (!hex) { |
||||
field.addPropertyChangeListener(e -> { |
||||
if ("value".equals(e.getPropertyName())) { |
||||
updatePreviewFromTextFields(); |
||||
} |
||||
}); |
||||
} else { |
||||
field.addPropertyChangeListener(e -> { |
||||
if ("value".equals(e.getPropertyName())) { |
||||
if (isChanging) return; |
||||
var hexStr = e.getNewValue().toString(); |
||||
var alpha = isColorTransparencySelectionEnabled() |
||||
? Integer.valueOf(hexStr.substring(6, 8), 16) : 255; |
||||
var c = new Color( |
||||
Integer.valueOf(hexStr.substring(0, 2), 16), |
||||
Integer.valueOf(hexStr.substring(2, 4), 16), |
||||
Integer.valueOf(hexStr.substring(4, 6), 16), |
||||
alpha); |
||||
colorWheelPanel.setColor(c, textHex); |
||||
} |
||||
}); |
||||
} |
||||
field.setFocusLostBehavior(JFormattedTextField.COMMIT_OR_REVERT); |
||||
return field; |
||||
} |
||||
|
||||
protected DarkColorModel getDarkColorModel() { |
||||
return (DarkColorModel) formatBox.getSelectedItem(); |
||||
} |
||||
|
||||
@Override |
||||
public void updateChooser() { |
||||
if (isChanging) return; |
||||
Color color = getColorFromModel(); |
||||
if (color != null) { |
||||
colorWheelPanel.setColor(color, this); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected void buildChooser() { |
||||
} |
||||
|
||||
@Override |
||||
public String getDisplayName() { |
||||
return "Test"; |
||||
} |
||||
|
||||
@Override |
||||
public int getMnemonic() { |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
public int getDisplayedMnemonicIndex() { |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isColorTransparencySelectionEnabled() { |
||||
return colorWheelPanel.isColorTransparencySelectionEnabled(); |
||||
} |
||||
|
||||
@Override |
||||
public void setColorTransparencySelectionEnabled(final boolean b) { |
||||
boolean oldValue = isColorTransparencySelectionEnabled(); |
||||
if (b != oldValue) { |
||||
var color = getColorFromModel(); |
||||
color = new Color(color.getRed(), color.getBlue(), color.getGreen()); |
||||
var model = getColorSelectionModel(); |
||||
if (model != null) { |
||||
model.setSelectedColor(color); |
||||
} |
||||
currentColor = color; |
||||
hexFormatter.setTransparencyEnabled(b); |
||||
applyColorToHEX(getColorFromModel()); |
||||
colorWheelPanel.setColorTransparencySelectionEnabled(b); |
||||
firePropertyChange(TRANSPARENCY_ENABLED_PROPERTY, |
||||
oldValue, b); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Icon getSmallDisplayIcon() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public Icon getLargeDisplayIcon() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
protected Color getColorFromModel() { |
||||
var c = super.getColorFromModel(); |
||||
return c == null ? currentColor : c; |
||||
} |
||||
|
||||
@Override |
||||
public void colorChanged(final Color color, final Object source) { |
||||
isChanging = true; |
||||
if (color != null && !color.equals(currentColor)) { |
||||
var newColor = !isColorTransparencySelectionEnabled() |
||||
? new Color(color.getRed(), color.getBlue(), color.getGreen()) : color; |
||||
var model = getColorSelectionModel(); |
||||
if (model != null) { |
||||
model.setSelectedColor(newColor); |
||||
} |
||||
currentColor = newColor; |
||||
previewComponent.setColor(newColor); |
||||
if (!(source instanceof JFormattedTextField)) { |
||||
applyColorToFields(newColor); |
||||
} |
||||
if (source != textHex) { |
||||
applyColorToHEX(newColor); |
||||
} |
||||
} |
||||
isChanging = false; |
||||
} |
||||
|
||||
private void applyColorToHEX(@NotNull final Color c) { |
||||
boolean transparencyEnabled = isColorTransparencySelectionEnabled(); |
||||
if (transparencyEnabled) { |
||||
textHex.setText(String.format("%02X%02X%02X%02X", c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha())); |
||||
} else { |
||||
textHex.setText(String.format("%02X%02X%02X", c.getRed(), c.getGreen(), c.getBlue())); |
||||
} |
||||
} |
||||
|
||||
private void applyColorToFields(final Color color) { |
||||
var model = getDarkColorModel(); |
||||
isChanging = true; |
||||
int[] values = model.getValuesFromColor(color); |
||||
for (int i = 0; i < values.length; i++) { |
||||
valueFields[i].setValue(values[i]); |
||||
} |
||||
isChanging = false; |
||||
} |
||||
|
||||
private void updatePreviewFromTextFields() { |
||||
if (!doneInit || isChanging) return; |
||||
isChanging = true; |
||||
int[] values = new int[valueFields.length]; |
||||
for (int i = 0; i < valueFields.length; i++) { |
||||
values[i] = (((Integer) valueFields[i].getValue())); |
||||
} |
||||
var color = getDarkColorModel().getColorFromValues(values); |
||||
|
||||
if (isColorTransparencySelectionEnabled()) { |
||||
color = ColorUtil.toAlpha(color, getColorFromModel().getAlpha()); |
||||
} |
||||
colorWheelPanel.setColor(color, valueFields[0]); |
||||
isChanging = false; |
||||
} |
||||
|
||||
private static final class ColorPreviewComponent extends JComponent { |
||||
private Color myColor; |
||||
|
||||
private ColorPreviewComponent() { |
||||
setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2)); |
||||
} |
||||
|
||||
@NotNull |
||||
@Contract(value = " -> new", pure = true) |
||||
@Override |
||||
public Dimension getPreferredSize() { |
||||
return new Dimension(100, 32); |
||||
} |
||||
|
||||
public void setColor(final Color c) { |
||||
myColor = c; |
||||
repaint(); |
||||
} |
||||
|
||||
@Override |
||||
protected void paintComponent(@NotNull final Graphics g) { |
||||
final Insets i = getInsets(); |
||||
final Rectangle r = getBounds(); |
||||
|
||||
final int width = r.width - i.left - i.right; |
||||
final int height = r.height - i.top - i.bottom; |
||||
|
||||
g.setColor(Color.WHITE); |
||||
g.fillRect(i.left, i.top, width, height); |
||||
|
||||
g.setColor(myColor); |
||||
g.fillRect(i.left + 1, i.top + 1, width - 2, height - 2); |
||||
|
||||
g.setColor(UIManager.getColor("ColorChooser.previewBorderColor")); |
||||
g.fillRect(i.left, i.top, width, 1); |
||||
g.fillRect(i.left, i.top, 1, height); |
||||
g.fillRect(i.left + width - 1, i.top, 1, height); |
||||
g.fillRect(i.left, i.top + height - 1, width, 1); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,73 @@
|
||||
package com.weis.darklaf.ui.colorchooser; |
||||
|
||||
import com.weis.darklaf.color.DarkColorModel; |
||||
import com.weis.darklaf.color.DarkColorModelCMYK; |
||||
import com.weis.darklaf.color.DarkColorModelHSL; |
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.swing.*; |
||||
import javax.swing.colorchooser.AbstractColorChooserPanel; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import javax.swing.plaf.basic.BasicColorChooserUI; |
||||
import java.awt.*; |
||||
import java.beans.PropertyChangeListener; |
||||
|
||||
public class DarkColorChooserUI extends BasicColorChooserUI { |
||||
|
||||
private final PropertyChangeListener propertyChangeListener = e -> { |
||||
if ("ancestor".equals(e.getPropertyName())) { |
||||
var pane = (JComponent) e.getNewValue(); |
||||
if (pane != null) { |
||||
pane = (JComponent) pane.getRootPane().getContentPane(); |
||||
} else { |
||||
return; |
||||
} |
||||
var children = pane.getComponents(); |
||||
if (children.length >= 2 && children[1] instanceof JComponent) { |
||||
var layout = ((JComponent) children[1]).getLayout(); |
||||
if (layout instanceof FlowLayout) { |
||||
((FlowLayout) layout).setAlignment(FlowLayout.TRAILING); |
||||
} |
||||
children[1].doLayout(); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
public DarkColorChooserUI() { |
||||
var c = new Color(25, 60, 2); |
||||
} |
||||
|
||||
@NotNull |
||||
@Contract("_ -> new") |
||||
public static ComponentUI createUI(final JComponent c) { |
||||
return new DarkColorChooserUI(); |
||||
} |
||||
|
||||
@Override |
||||
protected AbstractColorChooserPanel[] createDefaultChoosers() { |
||||
return new AbstractColorChooserPanel[]{ |
||||
new DarkColorChooserPanel(new DarkColorModel(), |
||||
new DarkColorModelHSL(), |
||||
// new DarkColorModelHSB(),
|
||||
new DarkColorModelCMYK())}; |
||||
} |
||||
|
||||
@Override |
||||
protected void installListeners() { |
||||
super.installListeners(); |
||||
chooser.addPropertyChangeListener(propertyChangeListener); |
||||
} |
||||
|
||||
@Override |
||||
protected void uninstallListeners() { |
||||
super.uninstallListeners(); |
||||
chooser.removePropertyChangeListener(propertyChangeListener); |
||||
} |
||||
|
||||
@Override |
||||
public void installUI(final JComponent c) { |
||||
super.installUI(c); |
||||
chooser.setPreviewPanel(new DarkPreviewPanel()); |
||||
} |
||||
} |
@ -0,0 +1,182 @@
|
||||
package com.weis.darklaf.ui.colorchooser; |
||||
|
||||
import org.jetbrains.annotations.NotNull; |
||||
import sun.swing.SwingUtilities2; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
|
||||
public class DarkPreviewPanel extends JPanel { |
||||
|
||||
private static final int SQUARE_SIZE = 28; |
||||
private static final int SQUARE_GAP = 4; |
||||
private static final int INNER_GAP = 4; |
||||
private static final int SWATCH_WIDTH = 75; |
||||
|
||||
|
||||
private static final int TEXT_GAP = 5; |
||||
private String sampleText; |
||||
|
||||
private Color oldColor = null; |
||||
|
||||
private JColorChooser getColorChooser() { |
||||
return (JColorChooser) SwingUtilities.getAncestorOfClass(JColorChooser.class, this); |
||||
} |
||||
|
||||
public Dimension getPreferredSize() { |
||||
JComponent host = getColorChooser(); |
||||
if (host == null) { |
||||
host = this; |
||||
} |
||||
FontMetrics fm = host.getFontMetrics(getFont()); |
||||
|
||||
int ascent = fm.getAscent(); |
||||
int height = fm.getHeight(); |
||||
int width = SwingUtilities2.stringWidth(host, fm, getSampleText()); |
||||
|
||||
int y = height * 3 + TEXT_GAP * 3; |
||||
int x = SQUARE_SIZE * 3 + SQUARE_GAP * 2 + SWATCH_WIDTH + width + TEXT_GAP * 3; |
||||
return new Dimension(x, y); |
||||
} |
||||
|
||||
public void paintComponent(final Graphics g) { |
||||
if (oldColor == null) oldColor = getForeground(); |
||||
|
||||
g.setColor(getBackground()); |
||||
g.fillRect(0, 0, getWidth(), getHeight()); |
||||
|
||||
if (this.getComponentOrientation().isLeftToRight()) { |
||||
int squareWidth = paintSquares(g, 0); |
||||
int textWidth = paintText(g, squareWidth); |
||||
paintSwatch(g, squareWidth + textWidth); |
||||
} else { |
||||
int swatchWidth = paintSwatch(g, 0); |
||||
int textWidth = paintText(g, swatchWidth); |
||||
paintSquares(g, swatchWidth + textWidth); |
||||
} |
||||
} |
||||
|
||||
private int paintSwatch(@NotNull final Graphics g, final int offsetX) { |
||||
g.setColor(oldColor); |
||||
g.fillRect(offsetX, 0, SWATCH_WIDTH, SQUARE_SIZE + SQUARE_GAP / 2); |
||||
g.setColor(getForeground()); |
||||
g.fillRect(offsetX, SQUARE_SIZE + SQUARE_GAP / 2, SWATCH_WIDTH, SQUARE_SIZE + SQUARE_GAP / 2); |
||||
return (offsetX + SWATCH_WIDTH); |
||||
} |
||||
|
||||
private int paintText(@NotNull final Graphics g, final int offsetX) { |
||||
g.setFont(getFont()); |
||||
JComponent host = getColorChooser(); |
||||
if (host == null) { |
||||
host = this; |
||||
} |
||||
FontMetrics fm = SwingUtilities2.getFontMetrics(host, g); |
||||
|
||||
int ascent = fm.getAscent(); |
||||
int height = fm.getHeight(); |
||||
int width = SwingUtilities2.stringWidth(host, fm, getSampleText()); |
||||
int textXOffset = offsetX + TEXT_GAP; |
||||
|
||||
Color color = getForeground(); |
||||
g.setColor(color); |
||||
SwingUtilities2.drawString(host, g, getSampleText(), textXOffset + (TEXT_GAP / 2), ascent); |
||||
|
||||
g.fillRect(textXOffset, (height) + TEXT_GAP, width + (TEXT_GAP), height + 2); |
||||
|
||||
g.setColor(Color.black); |
||||
SwingUtilities2.drawString(host, g, getSampleText(), textXOffset + (TEXT_GAP / 2), |
||||
height + ascent + TEXT_GAP + 2); |
||||
|
||||
|
||||
g.setColor(Color.white); |
||||
|
||||
g.fillRect(textXOffset, (height + TEXT_GAP) * 2, width + (TEXT_GAP), height + 2); |
||||
|
||||
g.setColor(color); |
||||
SwingUtilities2.drawString(host, g, getSampleText(), textXOffset + (TEXT_GAP / 2), |
||||
((height + TEXT_GAP) * 2) + ascent + 2); |
||||
|
||||
return width + TEXT_GAP * 3; |
||||
} |
||||
|
||||
private int paintSquares(@NotNull final Graphics g, final int offsetX) { |
||||
Color color = getForeground(); |
||||
|
||||
g.setColor(Color.white); |
||||
g.fillRect(offsetX, 0, SQUARE_SIZE, SQUARE_SIZE); |
||||
g.setColor(color); |
||||
g.fillRect(offsetX + INNER_GAP, |
||||
INNER_GAP, |
||||
SQUARE_SIZE - (INNER_GAP * 2), |
||||
SQUARE_SIZE - (INNER_GAP * 2)); |
||||
g.setColor(Color.white); |
||||
g.fillRect(offsetX + INNER_GAP * 2, |
||||
INNER_GAP * 2, |
||||
SQUARE_SIZE - (INNER_GAP * 4), |
||||
SQUARE_SIZE - (INNER_GAP * 4)); |
||||
|
||||
g.setColor(color); |
||||
g.fillRect(offsetX, SQUARE_SIZE + SQUARE_GAP, SQUARE_SIZE, SQUARE_SIZE); |
||||
|
||||
g.translate(SQUARE_SIZE + SQUARE_GAP, 0); |
||||
g.setColor(Color.black); |
||||
g.fillRect(offsetX, 0, SQUARE_SIZE, SQUARE_SIZE); |
||||
g.setColor(color); |
||||
g.fillRect(offsetX + INNER_GAP, |
||||
INNER_GAP, |
||||
SQUARE_SIZE - (INNER_GAP * 2), |
||||
SQUARE_SIZE - (INNER_GAP * 2)); |
||||
g.setColor(Color.white); |
||||
g.fillRect(offsetX + INNER_GAP * 2, |
||||
INNER_GAP * 2, |
||||
SQUARE_SIZE - (INNER_GAP * 4), |
||||
SQUARE_SIZE - (INNER_GAP * 4)); |
||||
g.translate(-(SQUARE_SIZE + SQUARE_GAP), 0); |
||||
|
||||
g.translate(SQUARE_SIZE + SQUARE_GAP, SQUARE_SIZE + SQUARE_GAP); |
||||
g.setColor(Color.white); |
||||
g.fillRect(offsetX, 0, SQUARE_SIZE, SQUARE_SIZE); |
||||
g.setColor(color); |
||||
g.fillRect(offsetX + INNER_GAP, |
||||
INNER_GAP, |
||||
SQUARE_SIZE - (INNER_GAP * 2), |
||||
SQUARE_SIZE - (INNER_GAP * 2)); |
||||
g.translate(-(SQUARE_SIZE + SQUARE_GAP), -(SQUARE_SIZE + SQUARE_GAP)); |
||||
|
||||
|
||||
g.translate((SQUARE_SIZE + SQUARE_GAP) * 2, 0); |
||||
g.setColor(Color.white); |
||||
g.fillRect(offsetX, 0, SQUARE_SIZE, SQUARE_SIZE); |
||||
g.setColor(color); |
||||
g.fillRect(offsetX + INNER_GAP, |
||||
INNER_GAP, |
||||
SQUARE_SIZE - (INNER_GAP * 2), |
||||
SQUARE_SIZE - (INNER_GAP * 2)); |
||||
g.setColor(Color.black); |
||||
g.fillRect(offsetX + INNER_GAP * 2, |
||||
INNER_GAP * 2, |
||||
SQUARE_SIZE - (INNER_GAP * 4), |
||||
SQUARE_SIZE - (INNER_GAP * 4)); |
||||
g.translate(-((SQUARE_SIZE + SQUARE_GAP) * 2), 0); |
||||
|
||||
g.translate((SQUARE_SIZE + SQUARE_GAP) * 2, (SQUARE_SIZE + SQUARE_GAP)); |
||||
g.setColor(Color.black); |
||||
g.fillRect(offsetX, 0, SQUARE_SIZE, SQUARE_SIZE); |
||||
g.setColor(color); |
||||
g.fillRect(offsetX + INNER_GAP, |
||||
INNER_GAP, |
||||
SQUARE_SIZE - (INNER_GAP * 2), |
||||
SQUARE_SIZE - (INNER_GAP * 2)); |
||||
g.translate(-((SQUARE_SIZE + SQUARE_GAP) * 2), -(SQUARE_SIZE + SQUARE_GAP)); |
||||
|
||||
return (SQUARE_SIZE * 3 + SQUARE_GAP * 2); |
||||
|
||||
} |
||||
|
||||
private String getSampleText() { |
||||
if (this.sampleText == null) { |
||||
this.sampleText = UIManager.getString("ColorChooser.sampleText", getLocale()); |
||||
} |
||||
return this.sampleText; |
||||
} |
||||
} |
@ -0,0 +1,31 @@
|
||||
package com.weis.darklaf.ui.colorchooser; |
||||
|
||||
import javax.swing.*; |
||||
import javax.swing.colorchooser.AbstractColorChooserPanel; |
||||
|
||||
public class DarkSwatchesChooserPanel extends AbstractColorChooserPanel { |
||||
@Override |
||||
public void updateChooser() { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
protected void buildChooser() { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public String getDisplayName() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public Icon getSmallDisplayIcon() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public Icon getLargeDisplayIcon() { |
||||
return null; |
||||
} |
||||
} |
@ -0,0 +1,281 @@
|
||||
package com.weis.darklaf.ui.colorchooser; |
||||
|
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
import java.awt.event.ComponentAdapter; |
||||
import java.awt.event.ComponentEvent; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.function.Consumer; |
||||
|
||||
/** |
||||
* @author Alexey Pegov |
||||
* @author Konstantin Bulenkov |
||||
*/ |
||||
class SlideComponent extends JComponent { |
||||
private static final int OFFSET = 11; |
||||
private final boolean myVertical; |
||||
private final String myTitle; |
||||
private final List<Consumer<Integer>> myListeners = new ArrayList<>(); |
||||
//Todo.
|
||||
// private LightweightHint myTooltipHint;
|
||||
private final JLabel myLabel = new JLabel(); |
||||
private int myPointerValue = 0; |
||||
private int myValue = 0; |
||||
private Unit myUnit = Unit.LEVEL; |
||||
|
||||
SlideComponent(final String title, final boolean vertical) { |
||||
myTitle = title; |
||||
myVertical = vertical; |
||||
|
||||
addMouseMotionListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseDragged(final MouseEvent e) { |
||||
processMouse(e); |
||||
} |
||||
}); |
||||
|
||||
addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mousePressed(final MouseEvent e) { |
||||
processMouse(e); |
||||
} |
||||
|
||||
@Override |
||||
public void mouseEntered(final MouseEvent e) { |
||||
updateBalloonText(); |
||||
} |
||||
|
||||
@Override |
||||
public void mouseMoved(final MouseEvent e) { |
||||
updateBalloonText(); |
||||
} |
||||
|
||||
@Override |
||||
public void mouseExited(final MouseEvent e) { |
||||
//Todo
|
||||
// if (myTooltipHint != null) {
|
||||
// myTooltipHint.hide();
|
||||
// myTooltipHint = null;
|
||||
// }
|
||||
} |
||||
}); |
||||
|
||||
addMouseWheelListener(event -> { |
||||
int units = event.getUnitsToScroll(); |
||||
if (units == 0) return; |
||||
int pointerValue = myPointerValue + units; |
||||
pointerValue = Math.max(pointerValue, OFFSET); |
||||
int size = myVertical ? getHeight() : getWidth(); |
||||
pointerValue = Math.min(pointerValue, (size - 12)); |
||||
|
||||
myPointerValue = pointerValue; |
||||
myValue = pointerValueToValue(myPointerValue); |
||||
|
||||
repaint(); |
||||
fireValueChanged(); |
||||
}); |
||||
|
||||
addComponentListener(new ComponentAdapter() { |
||||
@Override |
||||
public void componentResized(final ComponentEvent e) { |
||||
setValue(getValue()); |
||||
fireValueChanged(); |
||||
repaint(); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
private static void drawKnob(@NotNull final Graphics2D g2d, int x, int y, final boolean vertical) { |
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||
|
||||
//Todo Colors
|
||||
if (vertical) { |
||||
y -= 6; |
||||
|
||||
Polygon arrowShadow = new Polygon(); |
||||
arrowShadow.addPoint(x - 5, y + 1); |
||||
arrowShadow.addPoint(x + 7, y + 7); |
||||
arrowShadow.addPoint(x - 5, y + 13); |
||||
|
||||
g2d.setColor(new Color(0, 0, 0, 70)); |
||||
g2d.fill(arrowShadow); |
||||
|
||||
Polygon arrowHead = new Polygon(); |
||||
arrowHead.addPoint(x - 6, y); |
||||
arrowHead.addPoint(x + 6, y + 6); |
||||
arrowHead.addPoint(x - 6, y + 12); |
||||
|
||||
g2d.setColor(UIManager.getColor("ColorChooser.sliderKnobColor")); |
||||
g2d.fill(arrowHead); |
||||
} else { |
||||
x -= 6; |
||||
|
||||
Polygon arrowShadow = new Polygon(); |
||||
arrowShadow.addPoint(x + 1, y - 5); |
||||
arrowShadow.addPoint(x + 13, y - 5); |
||||
arrowShadow.addPoint(x + 7, y + 7); |
||||
|
||||
g2d.setColor(new Color(0, 0, 0, 70)); |
||||
g2d.fill(arrowShadow); |
||||
|
||||
Polygon arrowHead = new Polygon(); |
||||
arrowHead.addPoint(x, y - 6); |
||||
arrowHead.addPoint(x + 12, y - 6); |
||||
arrowHead.addPoint(x + 6, y + 6); |
||||
|
||||
g2d.setColor(UIManager.getColor("ColorChooser.sliderKnobColor")); |
||||
g2d.fill(arrowHead); |
||||
} |
||||
} |
||||
|
||||
void setUnits(final Unit unit) { |
||||
myUnit = unit; |
||||
} |
||||
|
||||
private void updateBalloonText() { |
||||
final Point point = myVertical ? new Point(0, myPointerValue) : new Point(myPointerValue, 0); |
||||
myLabel.setText(myTitle + ": " + Unit.formatValue(myValue, myUnit)); |
||||
//Todo
|
||||
// if (myTooltipHint == null) {
|
||||
// myTooltipHint = new LightweightHint(myLabel);
|
||||
// myTooltipHint.setCancelOnClickOutside(false);
|
||||
// myTooltipHint.setCancelOnOtherWindowOpen(false);
|
||||
//
|
||||
// final HintHint hint = new HintHint(this, point)
|
||||
// .setPreferredPosition(myVertical ? Balloon.Position.atLeft : Balloon
|
||||
// .Position.above)
|
||||
// .setBorderColor(Color.BLACK)
|
||||
// .setAwtTooltip(true)
|
||||
// .setFont(UIUtil.getLabelFont().deriveFont(Font.BOLD))
|
||||
// .setTextBg(HintUtil.getInformationColor())
|
||||
// .setShowImmediately(true);
|
||||
//
|
||||
// final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
|
||||
// myTooltipHint.show(this, point.x, point.y, owner instanceof JComponent ? (JComponent)owner : null, hint);
|
||||
// }
|
||||
// else {
|
||||
// myTooltipHint.setLocation(new RelativePoint(this, point));
|
||||
// }
|
||||
} |
||||
|
||||
@Override |
||||
protected void processMouseMotionEvent(final MouseEvent e) { |
||||
super.processMouseMotionEvent(e); |
||||
updateBalloonText(); |
||||
} |
||||
|
||||
private void processMouse(final MouseEvent e) { |
||||
int pointerValue = myVertical ? e.getY() : e.getX(); |
||||
pointerValue = Math.max(pointerValue, OFFSET); |
||||
int size = myVertical ? getHeight() : getWidth(); |
||||
pointerValue = Math.min(pointerValue, (size - 12)); |
||||
|
||||
myPointerValue = pointerValue; |
||||
|
||||
myValue = pointerValueToValue(myPointerValue); |
||||
|
||||
repaint(); |
||||
fireValueChanged(); |
||||
} |
||||
|
||||
public void addListener(final Consumer<Integer> listener) { |
||||
myListeners.add(listener); |
||||
} |
||||
|
||||
private void fireValueChanged() { |
||||
for (Consumer<Integer> listener : myListeners) { |
||||
listener.accept(myValue); |
||||
} |
||||
} |
||||
|
||||
public int getValue() { |
||||
return myValue; |
||||
} |
||||
|
||||
// 0 - 255
|
||||
public void setValue(final int value) { |
||||
myPointerValue = valueToPointerValue(value); |
||||
myValue = value; |
||||
} |
||||
|
||||
private int pointerValueToValue(int pointerValue) { |
||||
pointerValue -= OFFSET; |
||||
final int size = myVertical ? getHeight() : getWidth(); |
||||
float proportion = (size - 23) / 255f; |
||||
return Math.round((pointerValue / proportion)); |
||||
} |
||||
|
||||
private int valueToPointerValue(final int value) { |
||||
final int size = myVertical ? getHeight() : getWidth(); |
||||
float proportion = (size - 23) / 255f; |
||||
return OFFSET + (int) (value * proportion); |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getPreferredSize() { |
||||
return myVertical ? new Dimension(22, 100) |
||||
: new Dimension(100, 22); |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getMinimumSize() { |
||||
return myVertical ? new Dimension(22, 50) |
||||
: new Dimension(50, 22); |
||||
} |
||||
|
||||
@Override |
||||
public final void setToolTipText(final String text) { |
||||
//disable tooltips
|
||||
} |
||||
|
||||
@Override |
||||
protected void paintComponent(final Graphics g) { |
||||
final Graphics2D g2d = (Graphics2D) g; |
||||
|
||||
if (myVertical) { |
||||
g2d.setPaint(new GradientPaint(0f, 0f, Color.WHITE, 0f, getHeight(), Color.BLACK)); |
||||
g.fillRect(7, 10, 12, getHeight() - 20); |
||||
|
||||
g.setColor(UIManager.getColor("ColorChooser.sliderBorderColor")); |
||||
g.fillRect(7, 10, 12, 1); |
||||
g.fillRect(7, 10, 1, getHeight() - 20); |
||||
g.fillRect(7 + 12 - 1, 10, 1, getHeight() - 20); |
||||
g.fillRect(7, 10 + getHeight() - 20 - 1, 12, 1); |
||||
} else { |
||||
g2d.setPaint(new GradientPaint(0f, 0f, Color.WHITE, getWidth(), 0f, Color.BLACK)); |
||||
g.fillRect(10, 7, getWidth() - 20, 12); |
||||
|
||||
g.setColor(UIManager.getColor("ColorChooser.sliderBorderColor")); |
||||
g.fillRect(10, 7, 1, 12); |
||||
g.fillRect(10, 7, getWidth() - 20, 1); |
||||
g.fillRect(10, 7 + 12 - 1, getWidth() - 20, 1); |
||||
g.fillRect(10 + getWidth() - 20 - 1, 7, 1, 12); |
||||
} |
||||
|
||||
drawKnob(g2d, myVertical ? 7 : myPointerValue, myVertical ? myPointerValue : 7, myVertical); |
||||
} |
||||
|
||||
enum Unit { |
||||
PERCENT, |
||||
LEVEL; |
||||
|
||||
private static final float PERCENT_MAX_VALUE = 100f; |
||||
private static final float LEVEL_MAX_VALUE = 255f; |
||||
|
||||
@Contract(pure = true) |
||||
private static float getMaxValue(final Unit unit) { |
||||
return LEVEL.equals(unit) ? LEVEL_MAX_VALUE : PERCENT_MAX_VALUE; |
||||
} |
||||
|
||||
private static String formatValue(final int value, final Unit unit) { |
||||
return String.format("%d%s", (int) (getMaxValue(unit) / LEVEL_MAX_VALUE * value), |
||||
unit.equals(PERCENT) ? "%" : ""); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,252 @@
|
||||
package com.weis.darklaf.util; |
||||
|
||||
import com.bulenkov.iconloader.util.SystemInfo; |
||||
import com.weis.darklaf.ui.colorchooser.ColorListener; |
||||
import com.weis.darklaf.ui.colorchooser.ColorPipette; |
||||
import org.jetbrains.annotations.NotNull; |
||||
import org.jetbrains.annotations.Nullable; |
||||
|
||||
import javax.swing.*; |
||||
import javax.swing.plaf.basic.BasicRootPaneUI; |
||||
import java.awt.*; |
||||
import java.awt.event.AWTEventListener; |
||||
import java.awt.event.KeyEvent; |
||||
import java.awt.event.MouseEvent; |
||||
import java.awt.image.BufferedImage; |
||||
|
||||
public abstract class ColorPipetteBase implements ColorPipette, AWTEventListener { |
||||
protected final JComponent parent; |
||||
protected final Robot robot; |
||||
private final ColorListener colorListener; |
||||
private Runnable closeAction; |
||||
private JWindow pickerWindow; |
||||
private boolean keyDown; |
||||
private int downKeyCode; |
||||
|
||||
private Color currentColor; |
||||
private Color initialColor; |
||||
|
||||
public ColorPipetteBase(@NotNull final JComponent parent, @NotNull final ColorListener colorListener) { |
||||
this.parent = parent; |
||||
this.colorListener = colorListener; |
||||
robot = createRobot(); |
||||
} |
||||
|
||||
@Nullable |
||||
private static Robot createRobot() { |
||||
try { |
||||
return new Robot(); |
||||
} catch (AWTException e) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public boolean isKeyDown() { |
||||
return keyDown; |
||||
} |
||||
|
||||
public int getPressedKeyCode() { |
||||
return downKeyCode; |
||||
} |
||||
|
||||
public void setCloseAction(final Runnable closeAction) { |
||||
this.closeAction = closeAction; |
||||
} |
||||
|
||||
@Override |
||||
public void pickAndClose() { |
||||
PointerInfo pointerInfo = MouseInfo.getPointerInfo(); |
||||
Color pixelColor = getPixelColor(pointerInfo.getLocation()); |
||||
cancelPipette(); |
||||
notifyListener(pixelColor); |
||||
setInitialColor(pixelColor); |
||||
} |
||||
|
||||
protected Color getPixelColor(@NotNull final Point location) { |
||||
return robot.getPixelColor(location.x, location.y); |
||||
} |
||||
|
||||
@Nullable |
||||
protected Color getInitialColor() { |
||||
return initialColor; |
||||
} |
||||
|
||||
@Override |
||||
public void setInitialColor(@Nullable final Color initialColor) { |
||||
this.initialColor = initialColor; |
||||
setColor(initialColor); |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public Color getColor() { |
||||
return currentColor; |
||||
} |
||||
|
||||
protected void setColor(@Nullable final Color color) { |
||||
currentColor = color; |
||||
} |
||||
|
||||
@Override |
||||
public Window show() { |
||||
Window picker = getOrCreatePickerWindow(); |
||||
Toolkit.getDefaultToolkit().addAWTEventListener(this, |
||||
AWTEvent.MOUSE_MOTION_EVENT_MASK |
||||
| AWTEvent.MOUSE_EVENT_MASK |
||||
| AWTEvent.KEY_EVENT_MASK); |
||||
updateLocation(); |
||||
picker.setVisible(true); |
||||
return picker; |
||||
} |
||||
|
||||
@Nullable |
||||
protected Point updateLocation() { |
||||
PointerInfo pointerInfo = MouseInfo.getPointerInfo(); |
||||
if (pointerInfo == null) return null; |
||||
|
||||
Point mouseLocation = pointerInfo.getLocation(); |
||||
Window pickerWindow = getPickerWindow(); |
||||
if (pickerWindow != null && mouseLocation != null) { |
||||
pickerWindow.setLocation(adjustPickerLocation(mouseLocation, pickerWindow)); |
||||
} |
||||
return mouseLocation; |
||||
} |
||||
|
||||
protected Point adjustPickerLocation(@NotNull final Point mouseLocation, @NotNull final Window pickerWindow) { |
||||
return new Point(mouseLocation.x - pickerWindow.getWidth() / 2, |
||||
mouseLocation.y - pickerWindow.getHeight() / 2); |
||||
} |
||||
|
||||
@Nullable |
||||
protected Window getPickerWindow() { |
||||
return pickerWindow; |
||||
} |
||||
|
||||
@NotNull |
||||
protected Window getOrCreatePickerWindow() { |
||||
if (pickerWindow == null) { |
||||
Window owner = SwingUtilities.getWindowAncestor(parent); |
||||
pickerWindow = createPickerWindow(owner); |
||||
pickerWindow.setName("DarkLafPickerDialog"); |
||||
JRootPane rootPane = pickerWindow.getRootPane(); |
||||
rootPane.putClientProperty("Window.shadow", Boolean.FALSE); |
||||
} |
||||
return pickerWindow; |
||||
} |
||||
|
||||
@Override |
||||
public void eventDispatched(@NotNull final AWTEvent event) { |
||||
if (pickerWindow == null || !pickerWindow.isVisible()) return; |
||||
switch (event.getID()) { |
||||
case MouseEvent.MOUSE_PRESSED: |
||||
((MouseEvent) event).consume(); |
||||
pickAndClose(); |
||||
break; |
||||
case MouseEvent.MOUSE_CLICKED: |
||||
((MouseEvent) event).consume(); |
||||
break; |
||||
case KeyEvent.KEY_PRESSED: |
||||
downKeyCode = ((KeyEvent) event).getKeyCode(); |
||||
switch (downKeyCode) { |
||||
case KeyEvent.VK_ESCAPE: |
||||
cancelPipette(); |
||||
break; |
||||
case KeyEvent.VK_ENTER: |
||||
pickAndClose(); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
if (!keyDown) { |
||||
keyDown = true; |
||||
updatePipette(true); |
||||
} |
||||
break; |
||||
case KeyEvent.KEY_RELEASED: |
||||
keyDown = false; |
||||
var picker = getPickerWindow(); |
||||
if (picker != null) { |
||||
picker.repaint(); |
||||
} |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
|
||||
protected abstract void updatePipette(final boolean force); |
||||
|
||||
protected void notifyListener(@NotNull final Color c) { |
||||
colorListener.colorChanged(c, this); |
||||
} |
||||
|
||||
@Override |
||||
public boolean imageUpdate(final Image image, final int i, final int i1, |
||||
final int i2, final int i3, final int i4) { |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public void cancelPipette() { |
||||
Window pickerWindow = getPickerWindow(); |
||||
if (pickerWindow != null) { |
||||
pickerWindow.setVisible(false); |
||||
} |
||||
Color initialColor = getInitialColor(); |
||||
if (initialColor != null) { |
||||
notifyListener(initialColor); |
||||
} |
||||
if (closeAction != null) { |
||||
closeAction.run(); |
||||
} |
||||
Toolkit.getDefaultToolkit().removeAWTEventListener(this); |
||||
if (SystemInfo.isWindows) { |
||||
WindowsFrameUtil.User32dll.INSTANCE.ShowCursor(true); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void dispose() { |
||||
pickerWindow.dispose(); |
||||
pickerWindow = null; |
||||
setInitialColor(null); |
||||
setColor(null); |
||||
} |
||||
|
||||
protected JWindow createPickerWindow(final Window parent) { |
||||
return new PickerWindow(parent); |
||||
} |
||||
|
||||
protected static class PickerWindow extends JWindow { |
||||
|
||||
protected PickerWindow(final Window parent) { |
||||
super(parent); |
||||
setBackground(DarkUIUtil.TRANSPARENT_COLOR); |
||||
BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); |
||||
Cursor blankCursor = Toolkit.getDefaultToolkit() |
||||
.createCustomCursor(cursorImg, new Point(), "BlankCursor"); |
||||
setCursor(blankCursor); |
||||
} |
||||
|
||||
@NotNull |
||||
@Override |
||||
public Cursor getCursor() { |
||||
return super.getCursor(); |
||||
} |
||||
|
||||
@Override |
||||
protected JRootPane createRootPane() { |
||||
return new JRootPane() { |
||||
@Override |
||||
public void updateUI() { |
||||
setUI(new BasicRootPaneUI()); |
||||
} |
||||
|
||||
@Override |
||||
public int getWindowDecorationStyle() { |
||||
return NONE; |
||||
} |
||||
}; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,59 @@
|
||||
package com.weis.darklaf.util; |
||||
|
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.awt.*; |
||||
|
||||
public final class ColorUtil { |
||||
|
||||
@Contract(pure = true) |
||||
private ColorUtil() { |
||||
|
||||
} |
||||
|
||||
@Contract(pure = true) |
||||
private static int shift(final int colorComponent, final double d) { |
||||
int n = (int) ((double) colorComponent * d); |
||||
return n > 255 ? 255 : (Math.max(n, 0)); |
||||
} |
||||
|
||||
@NotNull |
||||
@Contract("_, _ -> new") |
||||
public static Color shift(@NotNull final Color c, final double d) { |
||||
return new Color(shift(c.getRed(), d), shift(c.getGreen(), d), shift(c.getBlue(), d), c.getAlpha()); |
||||
} |
||||
|
||||
@NotNull |
||||
@Contract("_, _ -> new") |
||||
public static Color toAlpha(final Color color, final int a) { |
||||
Color c = color != null ? color : Color.black; |
||||
return new Color(c.getRed(), c.getGreen(), c.getBlue(), a); |
||||
} |
||||
|
||||
@NotNull |
||||
public static Color fromHex(@NotNull String str) { |
||||
if (str.startsWith("#")) { |
||||
str = str.substring(1); |
||||
} |
||||
|
||||
if (str.length() == 3) { |
||||
return new Color(17 * Integer.valueOf(String.valueOf(str.charAt(0)), 16), |
||||
17 * Integer.valueOf(String.valueOf(str.charAt(1)), 16), |
||||
17 * Integer.valueOf(String.valueOf(str.charAt(2)), 16)); |
||||
} else if (str.length() == 6) { |
||||
return Color.decode("0x" + str); |
||||
} else { |
||||
throw new IllegalArgumentException("Should be String of 3 or 6 chars length."); |
||||
} |
||||
} |
||||
|
||||
public static Color fromHex(final String str, final Color defaultValue) { |
||||
try { |
||||
return fromHex(str); |
||||
} catch (Exception var3) { |
||||
return defaultValue; |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,203 @@
|
||||
package com.weis.darklaf.util; |
||||
|
||||
import com.weis.darklaf.ui.colorchooser.ColorListener; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
import java.awt.event.FocusAdapter; |
||||
import java.awt.event.FocusEvent; |
||||
import java.awt.event.KeyEvent; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
import java.awt.geom.Ellipse2D; |
||||
import java.awt.image.BufferedImage; |
||||
|
||||
public class DefaultColorPipette extends ColorPipetteBase { |
||||
private static final int SIZE = 36; |
||||
private static final int DIALOG_SIZE = 50; |
||||
private static final int MOUSE_OFF_X = 7; |
||||
private static final int MOUSE_OFF_Y = -7; |
||||
|
||||
private final Rectangle captureRect = new Rectangle(); |
||||
private final Point previousLocation = new Point(); |
||||
private final Timer timer; |
||||
private Graphics2D zoomGraphics; |
||||
private BufferedImage zoomImage; |
||||
|
||||
public DefaultColorPipette(@NotNull final JComponent parent, @NotNull final ColorListener colorListener) { |
||||
super(parent, colorListener); |
||||
timer = TimerUtil.createNamedTimer("DefaultColorPipette", 5, e -> updatePipette()); |
||||
} |
||||
|
||||
@Override |
||||
public Window show() { |
||||
Window picker = super.show(); |
||||
timer.start(); |
||||
return picker; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected Point adjustPickerLocation(@NotNull final Point mouseLocation, @NotNull final Window pickerWindow) { |
||||
var p = super.adjustPickerLocation(mouseLocation, pickerWindow); |
||||
p.x += DIALOG_SIZE / 2 - MOUSE_OFF_X; |
||||
p.y -= DIALOG_SIZE / 2 + MOUSE_OFF_Y; |
||||
return p; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isAvailable() { |
||||
if (robot != null) { |
||||
robot.createScreenCapture(new Rectangle(0, 0, 1, 1)); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
protected Color getPixelColor(@NotNull final Point location) { |
||||
return super.getPixelColor(getHotSPot(location)); |
||||
} |
||||
|
||||
protected Point getHotSPot(@NotNull final Point location) { |
||||
location.x -= MOUSE_OFF_X - 2; |
||||
location.y -= MOUSE_OFF_Y + 2; |
||||
return location; |
||||
} |
||||
|
||||
@Override |
||||
@NotNull |
||||
protected Window getOrCreatePickerWindow() { |
||||
Window pickerWindow = getPickerWindow(); |
||||
if (pickerWindow == null) { |
||||
pickerWindow = super.getOrCreatePickerWindow(); |
||||
pickerWindow.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseExited(final MouseEvent event) { |
||||
updatePipette(); |
||||
} |
||||
}); |
||||
pickerWindow.addMouseMotionListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseMoved(final MouseEvent e) { |
||||
updatePipette(); |
||||
} |
||||
}); |
||||
pickerWindow.addFocusListener(new FocusAdapter() { |
||||
@Override |
||||
public void focusLost(final FocusEvent e) { |
||||
pickAndClose(); |
||||
} |
||||
}); |
||||
|
||||
pickerWindow.setSize(DIALOG_SIZE, DIALOG_SIZE); |
||||
zoomImage = parent.getGraphicsConfiguration().createCompatibleImage(SIZE, SIZE, Transparency.TRANSLUCENT); |
||||
|
||||
zoomGraphics = (Graphics2D) zoomImage.getGraphics(); |
||||
zoomGraphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, |
||||
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); |
||||
} |
||||
|
||||
return pickerWindow; |
||||
} |
||||
|
||||
protected Icon getPipetteIcon() { |
||||
return UIManager.getIcon("ColorChooser.pipette.icon"); |
||||
} |
||||
|
||||
protected Color getPipetteBorderColor() { |
||||
return UIManager.getColor("ColorChooser.pipetteBorderColor"); |
||||
} |
||||
|
||||
protected void updatePipette() { |
||||
updatePipette(false); |
||||
} |
||||
|
||||
protected void updatePipette(final boolean force) { |
||||
Window pickerWindow = getPickerWindow(); |
||||
if (pickerWindow != null && pickerWindow.isShowing()) { |
||||
Point mouseLoc = updateLocation(); |
||||
if (mouseLoc == null) return; |
||||
final Color c = getPixelColor(mouseLoc); |
||||
if (!c.equals(getColor()) || !mouseLoc.equals(previousLocation) || force) { |
||||
setColor(c); |
||||
previousLocation.setLocation(mouseLoc); |
||||
|
||||
if (isKeyDown() && getPressedKeyCode() == KeyEvent.VK_SHIFT) { |
||||
var p = pickerWindow.getLocationOnScreen(); |
||||
p.y += pickerWindow.getHeight() - 2; |
||||
p.x += 2; |
||||
captureRect.setBounds(p.x - 9, p.y - 9, 18, 18); |
||||
|
||||
BufferedImage capture = robot.createScreenCapture(captureRect); |
||||
zoomGraphics.drawImage(capture, 0, 0, zoomImage.getWidth(), zoomImage.getHeight(), this); |
||||
} |
||||
pickerWindow.repaint(); |
||||
notifyListener(c); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void cancelPipette() { |
||||
timer.stop(); |
||||
super.cancelPipette(); |
||||
} |
||||
|
||||
@Override |
||||
public void dispose() { |
||||
timer.stop(); |
||||
super.dispose(); |
||||
if (zoomGraphics != null) { |
||||
zoomGraphics.dispose(); |
||||
} |
||||
zoomImage = null; |
||||
} |
||||
|
||||
@Override |
||||
protected JWindow createPickerWindow(final Window parent) { |
||||
return new DefaultPickerWindow(parent, this); |
||||
} |
||||
|
||||
protected static class DefaultPickerWindow extends PickerWindow { |
||||
|
||||
private final DefaultColorPipette pipette; |
||||
|
||||
protected DefaultPickerWindow(final Window parent, final DefaultColorPipette pipette) { |
||||
super(parent); |
||||
this.pipette = pipette; |
||||
} |
||||
|
||||
@Override |
||||
public void paint(final Graphics g2) { |
||||
super.paint(g2); |
||||
GraphicsUtil.setupStrokePainting(g2); |
||||
Graphics2D g = (Graphics2D) g2; |
||||
|
||||
//Draw region to be recognised as inside the window.
|
||||
g.setColor(Color.WHITE); |
||||
var config = GraphicsUtil.paintWithAlpha(g, 0.005f); |
||||
var p = MouseInfo.getPointerInfo().getLocation(); |
||||
SwingUtilities.convertPointFromScreen(p, this); |
||||
g.fillRect(p.x - 5, p.y - 5, 10, 10); |
||||
config.restore(); |
||||
|
||||
var icon = pipette.getPipetteIcon(); |
||||
if (pipette.isKeyDown() && pipette.getPressedKeyCode() == KeyEvent.VK_SHIFT) { |
||||
var oldCLip = g.getClip(); |
||||
var circ = new Ellipse2D.Float(icon.getIconWidth() - 4, 2, |
||||
getWidth() - icon.getIconWidth() - 2 + 4, |
||||
getHeight() - 2 - icon.getIconHeight() + 4); |
||||
g.setClip(circ); |
||||
g.drawImage(pipette.zoomImage, icon.getIconWidth() - 4, 2, null); |
||||
g.setClip(oldCLip); |
||||
|
||||
g.setColor(pipette.getPipetteBorderColor()); |
||||
g.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); |
||||
g.draw(circ); |
||||
} |
||||
icon.paintIcon(null, g, 0, getHeight() - icon.getIconHeight()); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,57 @@
|
||||
package com.weis.darklaf.util; |
||||
|
||||
import com.intellij.openapi.util.text.StringUtilRt; |
||||
import org.jetbrains.annotations.Contract; |
||||
|
||||
public final class StringUtil { |
||||
|
||||
@Contract(pure = true) |
||||
private StringUtil() { |
||||
} |
||||
|
||||
@Contract("null -> null; !null -> !null") |
||||
public static String toUpperCase(final String a) { |
||||
return a == null ? null : StringUtilRt.toUpperCase(a).toString(); |
||||
} |
||||
|
||||
@Contract("null -> fail") |
||||
public static CharSequence toUpperCase(final CharSequence s) { |
||||
if (s == null) { |
||||
throw new NullPointerException(); |
||||
} |
||||
|
||||
StringBuilder answer = null; |
||||
|
||||
for (int i = 0; i < s.length(); ++i) { |
||||
char c = s.charAt(i); |
||||
char upCased = toUpperCase(c); |
||||
if (answer == null && upCased != c) { |
||||
answer = new StringBuilder(s.length()); |
||||
answer.append(s.subSequence(0, i)); |
||||
} |
||||
|
||||
if (answer != null) { |
||||
answer.append(upCased); |
||||
} |
||||
} |
||||
return answer; |
||||
} |
||||
|
||||
@Contract(pure = true) |
||||
public static char toUpperCase(final char a) { |
||||
if (a < 'a') { |
||||
return a; |
||||
} else { |
||||
return a <= 'z' ? (char) (a + -32) : Character.toUpperCase(a); |
||||
} |
||||
} |
||||
|
||||
@Contract(pure = true) |
||||
public static char toLowerCase(final char a) { |
||||
if (a >= 'A' && (a < 'a' || a > 'z')) { |
||||
return a <= 'Z' ? (char) (a + 32) : Character.toLowerCase(a); |
||||
} else { |
||||
return a; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,32 @@
|
||||
package com.weis.darklaf.util; |
||||
|
||||
import org.jetbrains.annotations.Contract; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.event.ActionListener; |
||||
|
||||
public final class TimerUtil { |
||||
@Contract("_, _, _ -> new") |
||||
@NotNull |
||||
public static Timer createNamedTimer(@NotNull final String name, final int delay, |
||||
@NotNull final ActionListener listener) { |
||||
return new Timer(delay, listener) { |
||||
@Override |
||||
public String toString() { |
||||
return name; |
||||
} |
||||
}; |
||||
} |
||||
|
||||
@Contract("_, _ -> new") |
||||
@NotNull |
||||
public static Timer createNamedTimer(@NotNull final String name, final int delay) { |
||||
return new Timer(delay, null) { |
||||
@Override |
||||
public String toString() { |
||||
return name; |
||||
} |
||||
}; |
||||
} |
||||
} |
After Width: | Height: | Size: 658 B |
After Width: | Height: | Size: 658 B |
After Width: | Height: | Size: 686 B |
After Width: | Height: | Size: 658 B |
@ -0,0 +1,14 @@
|
||||
import com.weis.darklaf.LafManager; |
||||
|
||||
import javax.swing.*; |
||||
import java.awt.*; |
||||
|
||||
public final class ColorChooserDemo { |
||||
|
||||
public static void main(final String[] args) { |
||||
SwingUtilities.invokeLater(() -> { |
||||
LafManager.loadLaf(LafManager.Theme.Dark); |
||||
JColorChooser.showDialog(null, "Color Chooser with transparency", Color.RED, true); |
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,41 @@
|
||||
import com.weis.darklaf.color.DarkColorModel; |
||||
import com.weis.darklaf.color.DarkColorModelCMYK; |
||||
import com.weis.darklaf.color.DarkColorModelHSB; |
||||
import com.weis.darklaf.color.DarkColorModelHSL; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.awt.*; |
||||
import java.util.Arrays; |
||||
|
||||
public class ColorModelTest { |
||||
|
||||
public static void main(final String[] args) { |
||||
var color = new Color(10, 20, 30); |
||||
|
||||
var m1 = new DarkColorModel(); |
||||
var m2 = new DarkColorModelHSB(); |
||||
var m3 = new DarkColorModelHSL(); |
||||
var m4 = new DarkColorModelCMYK(); |
||||
// test(m1);
|
||||
test(m2); |
||||
// test(m3);
|
||||
// test(m4);
|
||||
} |
||||
|
||||
private static void test(@NotNull final DarkColorModel model) { |
||||
System.out.println("Testing " + model.toString()); |
||||
for (int r = model.getMinimum(0); r < model.getMaximum(0); r++) { |
||||
for (int g = model.getMinimum(1); g < model.getMaximum(1); g++) { |
||||
for (int b = model.getMinimum(2); b < model.getMaximum(2); b++) { |
||||
var c = new int[]{r, g, b}; |
||||
var interm = model.getColorFromValues(c); |
||||
var nc = model.getValuesFromColor(interm); |
||||
if (!Arrays.equals(c, nc)) { |
||||
throw new RuntimeException( |
||||
"Not equals " + Arrays.toString(c) + " : " + interm + " : " + Arrays.toString(nc)); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue