Merge pull request #7039 from raiyni/pr-6414
Add custom RuneLite Color picker
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.runelite.client.config;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Used with ConfigItem, determines if to use alpha slider on colors
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface Alpha
|
||||
{
|
||||
}
|
||||
@@ -32,4 +32,5 @@ public class ConfigItemDescriptor
|
||||
private final ConfigItem item;
|
||||
private final Class<?> type;
|
||||
private final Range range;
|
||||
private final Alpha alpha;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ import net.runelite.api.events.ConfigChanged;
|
||||
import net.runelite.client.RuneLite;
|
||||
import net.runelite.client.account.AccountSession;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import net.runelite.client.util.ColorUtil;
|
||||
import net.runelite.http.api.config.ConfigClient;
|
||||
import net.runelite.http.api.config.ConfigEntry;
|
||||
import net.runelite.http.api.config.Configuration;
|
||||
@@ -386,7 +387,8 @@ public class ConfigManager
|
||||
.map(m -> new ConfigItemDescriptor(
|
||||
m.getDeclaredAnnotation(ConfigItem.class),
|
||||
m.getReturnType(),
|
||||
m.getDeclaredAnnotation(Range.class)
|
||||
m.getDeclaredAnnotation(Range.class),
|
||||
m.getDeclaredAnnotation(Alpha.class)
|
||||
))
|
||||
.sorted((a, b) -> ComparisonChain.start()
|
||||
.compare(a.getItem().position(), b.getItem().position())
|
||||
@@ -481,7 +483,7 @@ public class ConfigManager
|
||||
}
|
||||
if (type == Color.class)
|
||||
{
|
||||
return Color.decode(str);
|
||||
return ColorUtil.fromString(str);
|
||||
}
|
||||
if (type == Dimension.class)
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
package net.runelite.client.plugins.cannon;
|
||||
|
||||
import java.awt.Color;
|
||||
import net.runelite.client.config.Alpha;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
@@ -62,6 +63,7 @@ public interface CannonConfig extends Config
|
||||
return false;
|
||||
}
|
||||
|
||||
@Alpha
|
||||
@ConfigItem(
|
||||
keyName = "highlightDoubleHitColor",
|
||||
name = "Color of double hit spots",
|
||||
|
||||
@@ -48,11 +48,8 @@ import javax.swing.BorderFactory;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JColorChooser;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JFormattedTextField;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
@@ -63,6 +60,7 @@ import javax.swing.JTextArea;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.SpinnerModel;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
@@ -90,6 +88,8 @@ import net.runelite.client.ui.PluginPanel;
|
||||
import net.runelite.client.ui.components.ComboBoxListRenderer;
|
||||
import net.runelite.client.ui.components.IconButton;
|
||||
import net.runelite.client.ui.components.IconTextField;
|
||||
import net.runelite.client.ui.components.colorpicker.RuneliteColorPicker;
|
||||
import net.runelite.client.util.ColorUtil;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
import net.runelite.client.util.Text;
|
||||
|
||||
@@ -400,47 +400,48 @@ public class ConfigPanel extends PluginPanel
|
||||
String existing = configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName());
|
||||
|
||||
Color existingColor;
|
||||
JButton colorPicker;
|
||||
JButton colorPickerBtn;
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
existingColor = Color.BLACK;
|
||||
colorPicker = new JButton("Pick a color");
|
||||
colorPickerBtn = new JButton("Pick a color");
|
||||
}
|
||||
else
|
||||
{
|
||||
existingColor = Color.decode(existing);
|
||||
colorPicker = new JButton("#" + Integer.toHexString(existingColor.getRGB()).substring(2).toUpperCase());
|
||||
existingColor = ColorUtil.fromString(existing);
|
||||
colorPickerBtn = new JButton(ColorUtil.toHexColor(existingColor).toUpperCase());
|
||||
}
|
||||
|
||||
colorPicker.setFocusable(false);
|
||||
colorPicker.setBackground(existingColor);
|
||||
colorPicker.addMouseListener(new MouseAdapter()
|
||||
colorPickerBtn.setFocusable(false);
|
||||
colorPickerBtn.setBackground(existingColor);
|
||||
colorPickerBtn.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e)
|
||||
{
|
||||
final JFrame parent = new JFrame();
|
||||
JColorChooser jColorChooser = new JColorChooser(existingColor);
|
||||
jColorChooser.getSelectionModel().addChangeListener(e1 ->
|
||||
RuneliteColorPicker colorPicker = new RuneliteColorPicker(SwingUtilities.windowForComponent(ConfigPanel.this),
|
||||
colorPickerBtn.getBackground(), cid.getItem().name(), cid.getAlpha() == null);
|
||||
colorPicker.setLocation(getLocationOnScreen());
|
||||
colorPicker.setOnColorChange(c ->
|
||||
{
|
||||
colorPicker.setBackground(jColorChooser.getColor());
|
||||
colorPicker.setText("#" + Integer.toHexString(jColorChooser.getColor().getRGB()).substring(2).toUpperCase());
|
||||
colorPickerBtn.setBackground(c);
|
||||
colorPickerBtn.setText(ColorUtil.toHexColor(c).toUpperCase());
|
||||
});
|
||||
parent.addWindowListener(new WindowAdapter()
|
||||
|
||||
colorPicker.addWindowListener(new WindowAdapter()
|
||||
{
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e)
|
||||
{
|
||||
changeConfiguration(listItem, config, jColorChooser, cd, cid);
|
||||
changeConfiguration(listItem, config, colorPicker, cd, cid);
|
||||
}
|
||||
});
|
||||
parent.add(jColorChooser);
|
||||
parent.pack();
|
||||
parent.setVisible(true);
|
||||
colorPicker.setVisible(true);
|
||||
}
|
||||
});
|
||||
item.add(colorPicker, BorderLayout.EAST);
|
||||
|
||||
item.add(colorPickerBtn, BorderLayout.EAST);
|
||||
}
|
||||
|
||||
if (cid.getType() == Dimension.class)
|
||||
@@ -556,7 +557,7 @@ public class ConfigPanel extends PluginPanel
|
||||
scrollPane.getVerticalScrollBar().setValue(0);
|
||||
}
|
||||
|
||||
private void changeConfiguration(PluginListItem listItem, Config config, JComponent component, ConfigDescriptor cd, ConfigItemDescriptor cid)
|
||||
private void changeConfiguration(PluginListItem listItem, Config config, Component component, ConfigDescriptor cd, ConfigItemDescriptor cid)
|
||||
{
|
||||
final ConfigItem configItem = cid.getItem();
|
||||
|
||||
@@ -588,10 +589,10 @@ public class ConfigPanel extends PluginPanel
|
||||
JTextComponent textField = (JTextComponent) component;
|
||||
configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), textField.getText());
|
||||
}
|
||||
else if (component instanceof JColorChooser)
|
||||
else if (component instanceof RuneliteColorPicker)
|
||||
{
|
||||
JColorChooser jColorChooser = (JColorChooser) component;
|
||||
configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), String.valueOf(jColorChooser.getColor().getRGB()));
|
||||
RuneliteColorPicker colorPicker = (RuneliteColorPicker) component;
|
||||
configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), colorPicker.getSelectedColor().getRGB() + "");
|
||||
}
|
||||
else if (component instanceof JComboBox)
|
||||
{
|
||||
|
||||
@@ -26,15 +26,16 @@
|
||||
|
||||
package net.runelite.client.plugins.groundmarkers;
|
||||
|
||||
import java.awt.Color;
|
||||
import net.runelite.client.config.Alpha;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
@ConfigGroup("groundMarker")
|
||||
public interface GroundMarkerConfig extends Config
|
||||
{
|
||||
@Alpha
|
||||
@ConfigItem(
|
||||
keyName = "markerColor",
|
||||
name = "Color of the tile",
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
package net.runelite.client.plugins.objectindicators;
|
||||
|
||||
import java.awt.Color;
|
||||
import net.runelite.client.config.Alpha;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
@@ -33,6 +34,7 @@ import net.runelite.client.config.ConfigItem;
|
||||
@ConfigGroup("objectindicators")
|
||||
public interface ObjectIndicatorsConfig extends Config
|
||||
{
|
||||
@Alpha
|
||||
@ConfigItem(
|
||||
keyName = "markerColor",
|
||||
name = "Marker color",
|
||||
|
||||
@@ -36,14 +36,13 @@ import java.awt.event.WindowEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JColorChooser;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.SpinnerModel;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
@@ -53,6 +52,7 @@ import net.runelite.client.plugins.screenmarkers.ScreenMarkerPlugin;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import net.runelite.client.ui.components.FlatTextField;
|
||||
import net.runelite.client.ui.components.colorpicker.RuneliteColorPicker;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
|
||||
class ScreenMarkerPanel extends JPanel
|
||||
@@ -489,17 +489,16 @@ class ScreenMarkerPanel extends JPanel
|
||||
|
||||
private void openFillColorPicker()
|
||||
{
|
||||
final JFrame parent = new JFrame();
|
||||
JColorChooser jColorChooser = new JColorChooser(marker.getMarker().getFill());
|
||||
|
||||
jColorChooser.getSelectionModel().addChangeListener(e1 ->
|
||||
RuneliteColorPicker colorPicker = new RuneliteColorPicker(SwingUtilities.windowForComponent(this),
|
||||
marker.getMarker().getFill(), marker.getMarker().getName() + " Fill", false);
|
||||
colorPicker.setLocation(getLocationOnScreen());
|
||||
colorPicker.setOnColorChange(c ->
|
||||
{
|
||||
Color chosen = jColorChooser.getColor();
|
||||
marker.getMarker().setFill(new Color(chosen.getRed(), chosen.getGreen(), chosen.getBlue(), DEFAULT_FILL_OPACITY));
|
||||
marker.getMarker().setFill(c);
|
||||
updateFill();
|
||||
});
|
||||
|
||||
parent.addWindowListener(new WindowAdapter()
|
||||
colorPicker.addWindowListener(new WindowAdapter()
|
||||
{
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e)
|
||||
@@ -507,25 +506,21 @@ class ScreenMarkerPanel extends JPanel
|
||||
plugin.updateConfig();
|
||||
}
|
||||
});
|
||||
|
||||
parent.add(jColorChooser);
|
||||
parent.pack();
|
||||
parent.setLocationRelativeTo(null);
|
||||
parent.setVisible(true);
|
||||
colorPicker.setVisible(true);
|
||||
}
|
||||
|
||||
private void openBorderColorPicker()
|
||||
{
|
||||
final JFrame parent = new JFrame();
|
||||
JColorChooser jColorChooser = new JColorChooser(marker.getMarker().getColor());
|
||||
|
||||
jColorChooser.getSelectionModel().addChangeListener(e1 ->
|
||||
RuneliteColorPicker colorPicker = new RuneliteColorPicker(SwingUtilities.windowForComponent(this),
|
||||
marker.getMarker().getColor(), marker.getMarker().getName() + " Border", false);
|
||||
colorPicker.setLocation(getLocationOnScreen());
|
||||
colorPicker.setOnColorChange(c ->
|
||||
{
|
||||
marker.getMarker().setColor(jColorChooser.getColor());
|
||||
marker.getMarker().setColor(c);
|
||||
updateBorder();
|
||||
});
|
||||
|
||||
parent.addWindowListener(new WindowAdapter()
|
||||
colorPicker.addWindowListener(new WindowAdapter()
|
||||
{
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e)
|
||||
@@ -533,10 +528,6 @@ class ScreenMarkerPanel extends JPanel
|
||||
plugin.updateConfig();
|
||||
}
|
||||
});
|
||||
|
||||
parent.add(jColorChooser);
|
||||
parent.pack();
|
||||
parent.setLocationRelativeTo(null);
|
||||
parent.setVisible(true);
|
||||
colorPicker.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
package net.runelite.client.plugins.tileindicators;
|
||||
|
||||
import java.awt.Color;
|
||||
import net.runelite.client.config.Alpha;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
@@ -32,6 +33,7 @@ import net.runelite.client.config.ConfigItem;
|
||||
@ConfigGroup("tileindicators")
|
||||
public interface TileIndicatorsConfig extends Config
|
||||
{
|
||||
@Alpha
|
||||
@ConfigItem(
|
||||
keyName = "highlightDestinationColor",
|
||||
name = "Color of current destination highlighting",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
package net.runelite.client.plugins.xpglobes;
|
||||
|
||||
import java.awt.Color;
|
||||
import net.runelite.client.config.Alpha;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
@@ -65,6 +66,7 @@ public interface XpGlobesConfig extends Config
|
||||
return false;
|
||||
}
|
||||
|
||||
@Alpha
|
||||
@ConfigItem(
|
||||
keyName = "Progress arc color",
|
||||
name = "Progress arc color",
|
||||
@@ -76,6 +78,7 @@ public interface XpGlobesConfig extends Config
|
||||
return Color.ORANGE;
|
||||
}
|
||||
|
||||
@Alpha
|
||||
@ConfigItem(
|
||||
keyName = "Progress orb outline color",
|
||||
name = "Progress orb outline color",
|
||||
@@ -87,6 +90,7 @@ public interface XpGlobesConfig extends Config
|
||||
return Color.BLACK;
|
||||
}
|
||||
|
||||
@Alpha
|
||||
@ConfigItem(
|
||||
keyName = "Progress orb background color",
|
||||
name = "Progress orb background color",
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.ui.components.colorpicker;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GradientPaint;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseMotionAdapter;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.function.Consumer;
|
||||
import javax.swing.JPanel;
|
||||
import lombok.Setter;
|
||||
|
||||
public class ColorPanel extends JPanel
|
||||
{
|
||||
private static final int SELECTOR_RADIUS = 7;
|
||||
|
||||
private final int size;
|
||||
|
||||
private BufferedImage image;
|
||||
private Point targetPosition;
|
||||
private int selectedY;
|
||||
private boolean forceRedraw;
|
||||
|
||||
@Setter
|
||||
private Consumer<Color> onColorChange;
|
||||
|
||||
ColorPanel(int size)
|
||||
{
|
||||
this.size = size;
|
||||
this.image = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
|
||||
this.targetPosition = new Point(size, 0);
|
||||
setPreferredSize(new Dimension(size, size));
|
||||
|
||||
addMouseMotionListener(new MouseMotionAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent me)
|
||||
{
|
||||
moveTarget(me.getX(), me.getY(), true);
|
||||
}
|
||||
});
|
||||
|
||||
addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent me)
|
||||
{
|
||||
moveTarget(me.getX(), me.getY(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent me)
|
||||
{
|
||||
moveTarget(me.getX(), me.getY(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the gradient's base hue index.
|
||||
*/
|
||||
void setBaseColor(int selectedY)
|
||||
{
|
||||
if (this.selectedY == selectedY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedY = selectedY;
|
||||
redrawGradient();
|
||||
|
||||
if (onColorChange != null)
|
||||
{
|
||||
onColorChange.accept(colorAt(targetPosition.x, targetPosition.y));
|
||||
}
|
||||
|
||||
paintImmediately(0, 0, size, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Move the indicator to the closest color without firing change event.
|
||||
*/
|
||||
void moveToClosestColor(int y, Color color)
|
||||
{
|
||||
Point closest = closestPointToColor(color);
|
||||
if (this.selectedY == y && closest.x == targetPosition.x && closest.y == targetPosition.y)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedY = y;
|
||||
redrawGradient();
|
||||
moveTarget(closest.x, closest.y, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the closest point to a given color.
|
||||
*/
|
||||
private Point closestPointToColor(Color target)
|
||||
{
|
||||
float[] hsb = Color.RGBtoHSB(target.getRed(), target.getGreen(), target.getBlue(), null);
|
||||
int offSize = size - 1;
|
||||
|
||||
return new Point((int) (hsb[1] * offSize), offSize - (int) (hsb[2] * offSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the target (selector) to a specified x,y coordinates.
|
||||
*/
|
||||
private void moveTarget(int x, int y, boolean shouldUpdate)
|
||||
{
|
||||
if (targetPosition.x == x && targetPosition.y == y && !forceRedraw)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
x = Ints.constrainToRange(x, 0, size - 1);
|
||||
y = Ints.constrainToRange(y, 0, size - 1);
|
||||
|
||||
targetPosition = new Point(x, y);
|
||||
paintImmediately(0, 0, size, size);
|
||||
|
||||
if (onColorChange != null && shouldUpdate)
|
||||
{
|
||||
onColorChange.accept(colorAt(x, y));
|
||||
}
|
||||
forceRedraw = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
// Paint the gradient
|
||||
g.drawImage(this.image, 0, 0, null);
|
||||
|
||||
int targetX = targetPosition.x - (SELECTOR_RADIUS / 2);
|
||||
int targetY = targetPosition.y - (SELECTOR_RADIUS / 2);
|
||||
|
||||
// Paint the target (selector)
|
||||
g.setColor(Color.WHITE);
|
||||
g.fillOval(targetX, targetY, SELECTOR_RADIUS, SELECTOR_RADIUS);
|
||||
g.setColor(Color.BLACK);
|
||||
g.drawOval(targetX, targetY, SELECTOR_RADIUS, SELECTOR_RADIUS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Draws a 3-color gradient based on white, black, and current hue index.
|
||||
*/
|
||||
private void redrawGradient()
|
||||
{
|
||||
Color primaryRight = Color.getHSBColor(1f - this.selectedY / (float) (size - 1), 1, 1);
|
||||
Graphics2D g = image.createGraphics();
|
||||
GradientPaint primary = new GradientPaint(
|
||||
0f, 0f, Color.WHITE,
|
||||
size - 1, 0f, primaryRight);
|
||||
GradientPaint shade = new GradientPaint(
|
||||
0f, 0f, new Color(0, 0, 0, 0),
|
||||
0f, size - 1, Color.BLACK);
|
||||
g.setPaint(primary);
|
||||
g.fillRect(0, 0, size, size);
|
||||
g.setPaint(shade);
|
||||
g.fillRect(0, 0, size, size);
|
||||
|
||||
g.dispose();
|
||||
forceRedraw = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines which color is displayed on the gradient at x,y.
|
||||
*/
|
||||
private Color colorAt(int x, int y)
|
||||
{
|
||||
x = Ints.constrainToRange(x, 0, size - 1);
|
||||
y = Ints.constrainToRange(y, 0, size - 1);
|
||||
return new Color(image.getRGB(x, y));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.ui.components.colorpicker;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.FocusAdapter;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.util.function.Consumer;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.text.AbstractDocument;
|
||||
import javax.swing.text.AttributeSet;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.DocumentFilter;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.util.ColorUtil;
|
||||
|
||||
public class ColorValuePanel extends JPanel
|
||||
{
|
||||
private static final int DEFAULT_VALUE = ColorUtil.MAX_RGB_VALUE;
|
||||
|
||||
private ColorValueSlider slider = new ColorValueSlider();
|
||||
private JTextField input = new JTextField();
|
||||
|
||||
private Consumer<Integer> onValueChanged;
|
||||
|
||||
void setOnValueChanged(Consumer<Integer> c)
|
||||
{
|
||||
onValueChanged = c;
|
||||
slider.setOnValueChanged(c);
|
||||
}
|
||||
|
||||
ColorValuePanel(String labelText)
|
||||
{
|
||||
setLayout(new BorderLayout(10, 0));
|
||||
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
|
||||
input.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
input.setPreferredSize(new Dimension(35, 30));
|
||||
input.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
((AbstractDocument) input.getDocument()).setDocumentFilter(new DocumentFilter()
|
||||
{
|
||||
@Override
|
||||
public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String str, AttributeSet attrs)
|
||||
throws BadLocationException
|
||||
{
|
||||
try
|
||||
{
|
||||
String text = RuneliteColorPicker.getReplacedText(fb, offset, length, str);
|
||||
|
||||
int value = Integer.parseInt(text);
|
||||
if (value < ColorUtil.MIN_RGB_VALUE || value > ColorUtil.MAX_RGB_VALUE)
|
||||
{
|
||||
Toolkit.getDefaultToolkit().beep();
|
||||
return;
|
||||
}
|
||||
|
||||
super.replace(fb, offset, length, str, attrs);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
Toolkit.getDefaultToolkit().beep();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
input.addFocusListener(new FocusAdapter()
|
||||
{
|
||||
@Override
|
||||
public void focusLost(FocusEvent e)
|
||||
{
|
||||
updateText();
|
||||
}
|
||||
});
|
||||
|
||||
input.addActionListener(a -> updateText());
|
||||
|
||||
JLabel label = new JLabel(labelText);
|
||||
label.setPreferredSize(new Dimension(45, 0));
|
||||
label.setForeground(Color.WHITE);
|
||||
|
||||
slider.setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
slider.setBorder(new EmptyBorder(0, 0, 5, 0));
|
||||
slider.setPreferredSize(new Dimension(ColorUtil.MAX_RGB_VALUE + ColorValueSlider.KNOB_WIDTH, 30));
|
||||
|
||||
update(DEFAULT_VALUE);
|
||||
add(label, BorderLayout.WEST);
|
||||
add(slider, BorderLayout.CENTER);
|
||||
add(input, BorderLayout.EAST);
|
||||
}
|
||||
|
||||
private void updateText()
|
||||
{
|
||||
int value = Integer.parseInt(input.getText());
|
||||
|
||||
update(value);
|
||||
if (onValueChanged != null)
|
||||
{
|
||||
onValueChanged.accept(getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void update(int value)
|
||||
{
|
||||
value = ColorUtil.constrainValue(value);
|
||||
|
||||
slider.setValue(value);
|
||||
input.setText(value + "");
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return slider.getValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.ui.components.colorpicker;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseMotionAdapter;
|
||||
import java.util.function.Consumer;
|
||||
import javax.swing.JPanel;
|
||||
import lombok.Setter;
|
||||
import net.runelite.client.util.ColorUtil;
|
||||
|
||||
public class ColorValueSlider extends JPanel
|
||||
{
|
||||
static final int KNOB_WIDTH = 4;
|
||||
|
||||
private static final int KNOB_HEIGHT = 14;
|
||||
private static final Color TRACK_COLOR = new Color(20, 20, 20);
|
||||
private static final Color KNOB_COLOR = new Color(150, 150, 150);
|
||||
|
||||
private int value = ColorUtil.MAX_RGB_VALUE + KNOB_WIDTH;
|
||||
|
||||
@Setter
|
||||
private Consumer<Integer> onValueChanged;
|
||||
|
||||
ColorValueSlider()
|
||||
{
|
||||
addMouseMotionListener(new MouseMotionAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent me)
|
||||
{
|
||||
moveTarget(me.getX(), true);
|
||||
}
|
||||
});
|
||||
|
||||
addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent me)
|
||||
{
|
||||
moveTarget(me.getX(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent me)
|
||||
{
|
||||
moveTarget(me.getX(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setValue(int x)
|
||||
{
|
||||
moveTarget(x + KNOB_WIDTH, false);
|
||||
}
|
||||
|
||||
private void moveTarget(int x, boolean shouldUpdate)
|
||||
{
|
||||
value = Ints.constrainToRange(x, ColorUtil.MIN_RGB_VALUE + KNOB_WIDTH, ColorUtil.MAX_RGB_VALUE + KNOB_WIDTH);
|
||||
paintImmediately(0, 0, this.getWidth(), this.getHeight());
|
||||
|
||||
if (shouldUpdate && onValueChanged != null)
|
||||
{
|
||||
onValueChanged.accept(getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
super.paint(g);
|
||||
|
||||
g.setColor(TRACK_COLOR);
|
||||
g.fillRect(0, this.getHeight() / 2 - 2, this.getWidth() - KNOB_WIDTH, 5);
|
||||
|
||||
g.setColor(KNOB_COLOR);
|
||||
g.fillRect(value - KNOB_WIDTH / 2, this.getHeight() / 2 - KNOB_HEIGHT / 2, KNOB_WIDTH, KNOB_HEIGHT);
|
||||
}
|
||||
|
||||
int getValue()
|
||||
{
|
||||
return value - KNOB_WIDTH;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.ui.components.colorpicker;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseMotionAdapter;
|
||||
import java.util.function.Consumer;
|
||||
import javax.swing.JPanel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class HuePanel extends JPanel
|
||||
{
|
||||
private static final int PANEL_WIDTH = 15;
|
||||
private static final int KNOB_HEIGHT = 4;
|
||||
|
||||
private final int height;
|
||||
|
||||
@Getter
|
||||
private int selectedY;
|
||||
|
||||
@Setter
|
||||
private Consumer<Integer> onColorChange;
|
||||
|
||||
HuePanel(int height)
|
||||
{
|
||||
this.height = height;
|
||||
setPreferredSize(new Dimension(PANEL_WIDTH, height));
|
||||
|
||||
addMouseMotionListener(new MouseMotionAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent me)
|
||||
{
|
||||
moveSelector(me.getY());
|
||||
}
|
||||
});
|
||||
|
||||
addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent me)
|
||||
{
|
||||
moveSelector(me.getY());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent me)
|
||||
{
|
||||
moveSelector(me.getY());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Repaint slider with closest guess for y value on slider.
|
||||
*/
|
||||
public void select(Color color)
|
||||
{
|
||||
this.selectedY = closestYToColor(color);
|
||||
paintImmediately(0, 0, PANEL_WIDTH, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the selector to a specified y coordinate.
|
||||
*/
|
||||
private void moveSelector(int y)
|
||||
{
|
||||
y = Ints.constrainToRange(y, 0, height - 1);
|
||||
if (y == this.selectedY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedY = y;
|
||||
paintImmediately(0, 0, PANEL_WIDTH, height);
|
||||
if (this.onColorChange != null)
|
||||
{
|
||||
this.onColorChange.accept(y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a close y value for the given target color.
|
||||
*/
|
||||
private int closestYToColor(Color target)
|
||||
{
|
||||
float[] hsb = Color.RGBtoHSB(target.getRed(), target.getGreen(), target.getBlue(), null);
|
||||
float hue = hsb[0];
|
||||
|
||||
int offHeight = height - 1;
|
||||
|
||||
return Math.round(offHeight - hue * offHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
// Paint the gradient
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
g.setColor(colorAt(y));
|
||||
g.fillRect(0, y, PANEL_WIDTH, 1);
|
||||
}
|
||||
|
||||
final int halfKnob = KNOB_HEIGHT / 2;
|
||||
|
||||
// Paint the selector
|
||||
g.setColor(Color.WHITE);
|
||||
g.fillRect(0, selectedY - 1, PANEL_WIDTH, KNOB_HEIGHT);
|
||||
g.setColor(Color.BLACK);
|
||||
g.drawLine(0, selectedY - halfKnob, PANEL_WIDTH, selectedY - halfKnob);
|
||||
g.drawLine(0, selectedY + halfKnob, PANEL_WIDTH, selectedY + halfKnob);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate hue color for current hue index.
|
||||
*/
|
||||
private Color colorAt(int y)
|
||||
{
|
||||
return Color.getHSBColor(1 - (float) y / (height - 1), 1, 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.runelite.client.ui.components.colorpicker;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import javax.swing.JPanel;
|
||||
import lombok.Getter;
|
||||
|
||||
class PreviewPanel extends JPanel
|
||||
{
|
||||
private static final int CHECKER_SIZE = 10;
|
||||
|
||||
@Getter
|
||||
private Color color;
|
||||
|
||||
void setColor(Color c)
|
||||
{
|
||||
this.color = c;
|
||||
this.paintImmediately(0, 0, this.getWidth(), this.getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
super.paint(g);
|
||||
|
||||
for (int x = 0; x < getWidth(); x += CHECKER_SIZE)
|
||||
{
|
||||
for (int y = 0; y < getHeight(); y += CHECKER_SIZE)
|
||||
{
|
||||
int val = (x / CHECKER_SIZE + y / CHECKER_SIZE) % 2;
|
||||
g.setColor(val == 0 ? Color.LIGHT_GRAY : Color.WHITE);
|
||||
g.fillRect(x, y, CHECKER_SIZE, CHECKER_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
g.setColor(color);
|
||||
g.fillRect(0, 0, this.getWidth(), this.getHeight());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.ui.components.colorpicker;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.FocusAdapter;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.function.Consumer;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.text.AbstractDocument;
|
||||
import javax.swing.text.AttributeSet;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.DocumentFilter;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.util.ColorUtil;
|
||||
import org.pushingpixels.substance.internal.SubstanceSynapse;
|
||||
|
||||
public class RuneliteColorPicker extends JDialog
|
||||
{
|
||||
private final static int FRAME_WIDTH = 400;
|
||||
private final static int FRAME_HEIGHT = 380;
|
||||
private final static int TONE_PANEL_SIZE = 160;
|
||||
|
||||
private final static String BLANK_HEX = "#000";
|
||||
|
||||
private final ColorPanel colorPanel = new ColorPanel(TONE_PANEL_SIZE);
|
||||
private final HuePanel huePanel = new HuePanel(TONE_PANEL_SIZE);
|
||||
private final PreviewPanel afterPanel = new PreviewPanel();
|
||||
|
||||
private final ColorValuePanel redSlider = new ColorValuePanel("Red");
|
||||
private final ColorValuePanel greenSlider = new ColorValuePanel("Green");
|
||||
private final ColorValuePanel blueSlider = new ColorValuePanel("Blue");
|
||||
private final ColorValuePanel alphaSlider = new ColorValuePanel("Opacity");
|
||||
|
||||
private final JTextField hexInput = new JTextField();
|
||||
|
||||
private final boolean alphaHidden;
|
||||
|
||||
@Getter
|
||||
private Color selectedColor;
|
||||
|
||||
@Setter
|
||||
private Consumer<Color> onColorChange;
|
||||
|
||||
public RuneliteColorPicker(Window parent, Color previousColor, String title, boolean alphaHidden)
|
||||
{
|
||||
super(parent, "RuneLite Color Picker - " + title, ModalityType.MODELESS);
|
||||
|
||||
this.selectedColor = previousColor;
|
||||
|
||||
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||
setResizable(false);
|
||||
setSize(FRAME_WIDTH, FRAME_HEIGHT);
|
||||
setBackground(ColorScheme.PROGRESS_COMPLETE_COLOR);
|
||||
|
||||
JPanel content = new JPanel(new BorderLayout());
|
||||
content.putClientProperty(SubstanceSynapse.COLORIZATION_FACTOR, 1.0);
|
||||
content.setBorder(new EmptyBorder(15, 15, 15, 15));
|
||||
|
||||
JPanel colorSelection = new JPanel(new BorderLayout(15, 0));
|
||||
|
||||
JPanel leftPanel = new JPanel(new BorderLayout(15, 0));
|
||||
leftPanel.add(huePanel, BorderLayout.WEST);
|
||||
leftPanel.add(colorPanel, BorderLayout.CENTER);
|
||||
|
||||
JPanel rightPanel = new JPanel();
|
||||
rightPanel.setLayout(new GridBagLayout());
|
||||
GridBagConstraints cx = new GridBagConstraints();
|
||||
|
||||
|
||||
cx.insets = new Insets(0, 0, 0, 0);
|
||||
JLabel old = new JLabel("Previous");
|
||||
old.setHorizontalAlignment(JLabel.CENTER);
|
||||
|
||||
JLabel next = new JLabel(" Current ");
|
||||
next.setHorizontalAlignment(JLabel.CENTER);
|
||||
|
||||
PreviewPanel beforePanel = new PreviewPanel();
|
||||
beforePanel.setColor(previousColor);
|
||||
afterPanel.setColor(previousColor);
|
||||
|
||||
JPanel hexContainer = new JPanel(new GridBagLayout());
|
||||
|
||||
JLabel hexLabel = new JLabel("#");
|
||||
hexInput.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
JLabel label = new JLabel("Hex color");
|
||||
label.setVerticalAlignment(JLabel.BOTTOM);
|
||||
|
||||
cx.weightx = 0;
|
||||
cx.fill = GridBagConstraints.BOTH;
|
||||
cx.insets = new Insets(0, 1, 0, 1);
|
||||
hexContainer.add(hexLabel, cx);
|
||||
|
||||
cx.weightx = 1;
|
||||
cx.fill = GridBagConstraints.HORIZONTAL;
|
||||
cx.gridwidth = GridBagConstraints.REMAINDER;
|
||||
hexContainer.add(hexInput, cx);
|
||||
|
||||
cx.fill = GridBagConstraints.BOTH;
|
||||
cx.gridwidth = GridBagConstraints.RELATIVE;
|
||||
cx.weightx = 1;
|
||||
cx.weighty = 1;
|
||||
cx.gridy = 0;
|
||||
cx.gridx = 0;
|
||||
rightPanel.add(old, cx);
|
||||
|
||||
cx.gridx++;
|
||||
rightPanel.add(next, cx);
|
||||
|
||||
cx.gridx = 0;
|
||||
cx.gridy++;
|
||||
cx.gridwidth = GridBagConstraints.RELATIVE;
|
||||
cx.fill = GridBagConstraints.BOTH;
|
||||
rightPanel.add(beforePanel, cx);
|
||||
|
||||
cx.gridx++;
|
||||
rightPanel.add(afterPanel, cx);
|
||||
|
||||
cx.gridwidth = GridBagConstraints.REMAINDER;
|
||||
cx.gridx = 0;
|
||||
cx.gridy++;
|
||||
rightPanel.add(label, cx);
|
||||
|
||||
cx.gridy++;
|
||||
cx.fill = GridBagConstraints.HORIZONTAL;
|
||||
rightPanel.add(hexContainer, cx);
|
||||
|
||||
JPanel slidersContainer = new JPanel(new GridLayout(4, 1, 0, 10));
|
||||
slidersContainer.setBorder(new EmptyBorder(15, 0, 0, 0));
|
||||
|
||||
slidersContainer.add(redSlider);
|
||||
slidersContainer.add(greenSlider);
|
||||
slidersContainer.add(blueSlider);
|
||||
slidersContainer.add(alphaSlider);
|
||||
|
||||
this.alphaHidden = alphaHidden;
|
||||
if (alphaHidden)
|
||||
{
|
||||
alphaSlider.setVisible(false);
|
||||
setSize(FRAME_WIDTH, FRAME_HEIGHT - 40);
|
||||
}
|
||||
|
||||
colorSelection.add(leftPanel, BorderLayout.WEST);
|
||||
colorSelection.add(rightPanel, BorderLayout.CENTER);
|
||||
colorSelection.add(slidersContainer, BorderLayout.SOUTH);
|
||||
|
||||
content.add(colorSelection, BorderLayout.NORTH);
|
||||
|
||||
setContentPane(content);
|
||||
|
||||
// Reset selected color when clicking the old color
|
||||
beforePanel.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e)
|
||||
{
|
||||
if (!alphaHidden)
|
||||
{
|
||||
alphaSlider.update(beforePanel.getColor().getAlpha());
|
||||
}
|
||||
colorChange(beforePanel.getColor());
|
||||
updatePanels();
|
||||
}
|
||||
});
|
||||
|
||||
huePanel.setOnColorChange((y) ->
|
||||
{
|
||||
colorPanel.setBaseColor(y);
|
||||
updateText();
|
||||
});
|
||||
|
||||
colorPanel.setOnColorChange(this::colorChange);
|
||||
|
||||
((AbstractDocument) hexInput.getDocument()).setDocumentFilter(new DocumentFilter()
|
||||
{
|
||||
@Override
|
||||
public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String str, AttributeSet attrs)
|
||||
throws BadLocationException
|
||||
{
|
||||
str = str.replaceAll("#|0x", "");
|
||||
String text = RuneliteColorPicker.getReplacedText(fb, offset, length, str);
|
||||
|
||||
if (!ColorUtil.isHex(text))
|
||||
{
|
||||
Toolkit.getDefaultToolkit().beep();
|
||||
return;
|
||||
}
|
||||
|
||||
super.replace(fb, offset, length, str, attrs);
|
||||
}
|
||||
});
|
||||
hexInput.addFocusListener(new FocusAdapter()
|
||||
{
|
||||
@Override
|
||||
public void focusLost(FocusEvent e)
|
||||
{
|
||||
updateHex();
|
||||
}
|
||||
});
|
||||
hexInput.addActionListener((ActionEvent e) -> updateHex());
|
||||
|
||||
redSlider.setOnValueChanged(i ->
|
||||
{
|
||||
colorChange(new Color(i, selectedColor.getGreen(), selectedColor.getBlue()));
|
||||
updatePanels();
|
||||
});
|
||||
|
||||
greenSlider.setOnValueChanged(i ->
|
||||
{
|
||||
colorChange(new Color(selectedColor.getRed(), i, selectedColor.getBlue()));
|
||||
updatePanels();
|
||||
});
|
||||
|
||||
blueSlider.setOnValueChanged(i ->
|
||||
{
|
||||
colorChange(new Color(selectedColor.getRed(), selectedColor.getGreen(), i));
|
||||
updatePanels();
|
||||
});
|
||||
|
||||
alphaSlider.setOnValueChanged(i ->
|
||||
colorChange(new Color(selectedColor.getRed(), selectedColor.getGreen(), selectedColor.getBlue(), i)));
|
||||
|
||||
updatePanels();
|
||||
updateText();
|
||||
}
|
||||
|
||||
private void updatePanels()
|
||||
{
|
||||
huePanel.select(selectedColor);
|
||||
colorPanel.moveToClosestColor(huePanel.getSelectedY(), selectedColor);
|
||||
}
|
||||
|
||||
private void updateText()
|
||||
{
|
||||
String hex = alphaHidden ? ColorUtil.colorToHexCode(this.getSelectedColor()) : ColorUtil.colorToAlphaHexCode(this.getSelectedColor());
|
||||
hexInput.setText(hex.toUpperCase());
|
||||
afterPanel.setColor(selectedColor);
|
||||
|
||||
redSlider.update(this.selectedColor.getRed());
|
||||
greenSlider.update(this.selectedColor.getGreen());
|
||||
blueSlider.update(this.selectedColor.getBlue());
|
||||
if (!alphaHidden)
|
||||
{
|
||||
alphaSlider.update(this.selectedColor.getAlpha());
|
||||
}
|
||||
}
|
||||
|
||||
private void colorChange(Color newColor)
|
||||
{
|
||||
if (newColor == this.selectedColor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedColor = newColor;
|
||||
|
||||
// Finally, before firing the color changed event, apply any transparency
|
||||
// that might have been edited by the user
|
||||
if (this.selectedColor.getAlpha() != this.alphaSlider.getValue())
|
||||
{
|
||||
this.selectedColor = new Color(
|
||||
selectedColor.getRed(),
|
||||
selectedColor.getGreen(),
|
||||
selectedColor.getBlue(),
|
||||
alphaSlider.getValue());
|
||||
}
|
||||
|
||||
updateText();
|
||||
|
||||
if (onColorChange != null)
|
||||
{
|
||||
onColorChange.accept(this.selectedColor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the hex input, updating the color with the new values.
|
||||
*/
|
||||
private void updateHex()
|
||||
{
|
||||
String hex = hexInput.getText();
|
||||
if (Strings.isNullOrEmpty(hex))
|
||||
{
|
||||
hex = BLANK_HEX;
|
||||
}
|
||||
|
||||
Color color = ColorUtil.fromHex(hex);
|
||||
if (color == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!alphaHidden && ColorUtil.isAlphaHex(hex))
|
||||
{
|
||||
alphaSlider.update(color.getAlpha());
|
||||
}
|
||||
|
||||
colorChange(color);
|
||||
updatePanels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the whole string from the passed DocumentFilter replace.
|
||||
*/
|
||||
static String getReplacedText(DocumentFilter.FilterBypass fb, int offset, int length, String str)
|
||||
throws BadLocationException
|
||||
{
|
||||
Document doc = fb.getDocument();
|
||||
StringBuilder sb = new StringBuilder(doc.getText(0, doc.getLength()));
|
||||
sb.replace(offset, offset + length, str);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -24,13 +24,19 @@
|
||||
*/
|
||||
package net.runelite.client.util;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import java.awt.Color;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ColorUtil
|
||||
{
|
||||
public static final int MAX_RGB_VALUE = 255;
|
||||
public static final int MIN_RGB_VALUE = 0;
|
||||
private static final String OPENING_COLOR_TAG_START = "<col=";
|
||||
private static final String OPENING_COLOR_TAG_END = ">";
|
||||
private static final String CLOSING_COLOR_TAG = "</col>";
|
||||
private final static Pattern ALPHA_HEX_PATTERN = Pattern.compile("^(#|0x)?[0-9a-fA-F]{7,8}");
|
||||
private final static Pattern HEX_PATTERN = Pattern.compile("^(#|0x)?[0-9a-fA-F]{1,8}");
|
||||
|
||||
/**
|
||||
* Creates a color tag from the given color.
|
||||
@@ -108,11 +114,22 @@ public class ColorUtil
|
||||
* @param color The color to get a hex code from.
|
||||
* @return A lower-cased string of the RGB hex code of color.
|
||||
*/
|
||||
static String colorToHexCode(final Color color)
|
||||
public static String colorToHexCode(final Color color)
|
||||
{
|
||||
return String.format("%06x", color.getRGB() & 0xFFFFFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ARGB hex color code of the passed color.
|
||||
*
|
||||
* @param color The color to get a hex code from.
|
||||
* @return A lower-cased string of the ARGB hex code of color.
|
||||
*/
|
||||
public static String colorToAlphaHexCode(final Color color)
|
||||
{
|
||||
return String.format("%08x", color.getRGB());
|
||||
}
|
||||
|
||||
static boolean isFullyTransparent(final Color color)
|
||||
{
|
||||
return color.getAlpha() == 0;
|
||||
@@ -122,4 +139,91 @@ public class ColorUtil
|
||||
{
|
||||
return !isFullyTransparent(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the passed hex string is an alpha hex color.
|
||||
*
|
||||
* @param hex The hex to test.
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean isAlphaHex(String hex)
|
||||
{
|
||||
return ALPHA_HEX_PATTERN.matcher(hex).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the passed hex string is a hex color.
|
||||
*
|
||||
* @param hex The hex to test.
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean isHex(String hex)
|
||||
{
|
||||
return HEX_PATTERN.matcher(hex).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Limits an int to the rgba value range (0-255)
|
||||
*
|
||||
* @param value The value for the r, g, b, or a.
|
||||
* @return An int between 0 - 255.
|
||||
*/
|
||||
public static int constrainValue(int value)
|
||||
{
|
||||
return Ints.constrainToRange(value, MIN_RGB_VALUE, MAX_RGB_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Color from the passed int string.
|
||||
*
|
||||
* @param string The int to get a Color object from.
|
||||
* @return A Color of the int of color.
|
||||
*/
|
||||
public static Color fromString(String string)
|
||||
{
|
||||
try
|
||||
{
|
||||
int i = Integer.decode(string);
|
||||
return new Color(i, true);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Color from the passed hex string.
|
||||
*
|
||||
* @param hex The hex to get a Color object from.
|
||||
* @return A Color of the hex code of color.
|
||||
*/
|
||||
public static Color fromHex(String hex)
|
||||
{
|
||||
if (!hex.startsWith("#") && !hex.startsWith("0x"))
|
||||
{
|
||||
hex = "#" + hex;
|
||||
}
|
||||
|
||||
if ((hex.length() <= 7 && hex.startsWith("#")) || (hex.length() <= 8 && hex.startsWith("0x")))
|
||||
{
|
||||
try
|
||||
{
|
||||
return Color.decode(hex);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return new Color(Long.decode(hex).intValue(), true);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user