Merge pull request #2688 from Owain94/SliderUI

client: Custom slider
This commit is contained in:
Owain van Brakel
2020-06-15 00:05:02 +02:00
committed by GitHub
3 changed files with 310 additions and 8 deletions

View File

@@ -83,6 +83,7 @@ import javax.swing.event.ChangeListener;
import javax.swing.plaf.basic.BasicSpinnerUI;
import javax.swing.text.JTextComponent;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.events.ConfigButtonClicked;
import net.runelite.api.util.Text;
import net.runelite.client.RuneLite;
import net.runelite.client.config.Button;
@@ -97,7 +98,6 @@ import net.runelite.client.config.ModifierlessKeybind;
import net.runelite.client.config.Range;
import net.runelite.client.config.Units;
import net.runelite.client.eventbus.EventBus;
import net.runelite.api.events.ConfigButtonClicked;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.events.PluginChanged;
import net.runelite.client.plugins.ExternalPluginManager;
@@ -162,13 +162,13 @@ class ConfigPanel extends PluginPanel
BACK_ICON = new ImageIcon(backIcon);
BACK_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(backIcon, -100));
final BufferedImage orangeBackIcon = ImageUtil.fillImage(backIcon, ColorScheme.BRAND_BLUE);
final BufferedImage blueBackIcon = ImageUtil.fillImage(backIcon, ColorScheme.BRAND_BLUE);
final BufferedImage sectionRetractIcon = ImageUtil.rotateImage(orangeBackIcon, Math.PI * 1.5);
final BufferedImage sectionRetractIcon = ImageUtil.rotateImage(blueBackIcon, Math.PI * 1.5);
SECTION_RETRACT_ICON = new ImageIcon(sectionRetractIcon);
SECTION_RETRACT_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(sectionRetractIcon, -100));
final BufferedImage sectionExpandIcon = ImageUtil.rotateImage(orangeBackIcon, Math.PI);
final BufferedImage sectionExpandIcon = ImageUtil.rotateImage(blueBackIcon, Math.PI);
SECTION_EXPAND_ICON = new ImageIcon(sectionExpandIcon);
SECTION_EXPAND_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(sectionExpandIcon, -100));
}
@@ -648,21 +648,21 @@ class ConfigPanel extends PluginPanel
{
JLabel sliderValueLabel = new JLabel();
JSlider slider = new JSlider(min, max, value);
slider.setBackground(ColorScheme.DARK_GRAY_COLOR);
if (units != null)
{
sliderValueLabel.setText(slider.getValue() + " " + units.value());
sliderValueLabel.setText(slider.getValue() + units.value());
}
else
{
sliderValueLabel.setText(String.valueOf(slider.getValue()));
}
slider.setPreferredSize(new Dimension(80, 25));
slider.setBackground(Color.WHITE);
slider.addChangeListener((l) ->
{
if (units != null)
{
sliderValueLabel.setText(slider.getValue() + " " + units.value());
sliderValueLabel.setText(slider.getValue() + units.value());
}
else
{
@@ -704,7 +704,7 @@ class ConfigPanel extends PluginPanel
if (units != null)
{
sliderValueLabel.setText(spinner.getValue() + " " + units.value());
sliderValueLabel.setText(spinner.getValue() + units.value());
}
else
{

View File

@@ -0,0 +1,300 @@
package net.runelite.client.ui.components;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import javax.swing.JComponent;
import javax.swing.JSlider;
import javax.swing.SwingConstants;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicSliderUI;
import net.runelite.client.ui.ColorScheme;
public class SliderUI extends BasicSliderUI
{
private final Color rangeColor = ColorScheme.BRAND_BLUE;
private final BasicStroke stroke = new BasicStroke(2f);
private transient boolean upperDragging;
public SliderUI(JSlider b)
{
super(b);
}
public static ComponentUI createUI(JComponent c)
{
return new SliderUI((JSlider) c);
}
@Override
protected void calculateThumbSize()
{
super.calculateThumbSize();
thumbRect.setSize(thumbRect.width, thumbRect.height);
}
/**
* Creates a listener to handle track events in the specified slider.
*/
@Override
protected TrackListener createTrackListener(JSlider slider)
{
return new RangeTrackListener();
}
@Override
protected void calculateThumbLocation()
{
// Call superclass method for lower thumb location.
super.calculateThumbLocation();
// Adjust upper value to snap to ticks if necessary.
if (slider.getSnapToTicks())
{
int upperValue = slider.getValue() + slider.getExtent();
int snappedValue = upperValue;
int majorTickSpacing = slider.getMajorTickSpacing();
int minorTickSpacing = slider.getMinorTickSpacing();
int tickSpacing = 0;
if (minorTickSpacing > 0)
{
tickSpacing = minorTickSpacing;
}
else if (majorTickSpacing > 0)
{
tickSpacing = majorTickSpacing;
}
if (tickSpacing != 0)
{
// If it's not on a tick, change the value
if ((upperValue - slider.getMinimum()) % tickSpacing != 0)
{
float temp = (float) (upperValue - slider.getMinimum()) / (float) tickSpacing;
int whichTick = Math.round(temp);
snappedValue = slider.getMinimum() + (whichTick * tickSpacing);
}
if (snappedValue != upperValue)
{
slider.setExtent(snappedValue - slider.getValue());
}
}
}
// Calculate upper thumb location. The thumb is centered over its
// value on the track.
if (slider.getOrientation() == JSlider.HORIZONTAL)
{
int upperPosition = xPositionForValue(slider.getValue() + slider.getExtent());
thumbRect.x = upperPosition - (thumbRect.width / 2);
thumbRect.y = trackRect.y;
}
else
{
int upperPosition = yPositionForValue(slider.getValue() + slider.getExtent());
thumbRect.x = trackRect.x;
thumbRect.y = upperPosition - (thumbRect.height / 2);
}
slider.repaint();
}
/**
* Returns the size of a thumb.
* Parent method not use size from LaF
*
* @return size of trumb
*/
@Override
protected Dimension getThumbSize()
{
return new Dimension(16, 16);
}
private Shape createThumbShape(int width, int height)
{
return new Ellipse2D.Double(0, 0, width, height);
}
@Override
public void paintTrack(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
Stroke old = g2d.getStroke();
g2d.setStroke(stroke);
g2d.setPaint(ColorScheme.LIGHT_GRAY_COLOR);
Color oldColor = ColorScheme.LIGHT_GRAY_COLOR;
Rectangle trackBounds = trackRect;
if (slider.getOrientation() == SwingConstants.HORIZONTAL)
{
g2d.drawLine(trackRect.x, trackRect.y + trackRect.height / 2,
trackRect.x + trackRect.width, trackRect.y + trackRect.height / 2);
int lowerX = thumbRect.width / 2;
int upperX = thumbRect.x + (thumbRect.width / 2);
int cy = (trackBounds.height / 2) - 2;
g2d.translate(trackBounds.x, trackBounds.y + cy);
g2d.setColor(rangeColor);
g2d.drawLine(lowerX - trackBounds.x, 2, upperX - trackBounds.x, 2);
g2d.translate(-trackBounds.x, -(trackBounds.y + cy));
g2d.setColor(oldColor);
}
g2d.setStroke(old);
}
/**
* Overrides superclass method to do nothing. Thumb painting is handled
* within the <code>paint()</code> method.
*/
@Override
public void paintThumb(Graphics g)
{
Rectangle knobBounds = thumbRect;
int w = knobBounds.width;
int h = knobBounds.height;
Graphics2D g2d = (Graphics2D) g.create();
Shape thumbShape = createThumbShape(w - 1, h - 1);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.translate(knobBounds.x, knobBounds.y);
g2d.setColor(ColorScheme.BRAND_BLUE);
g2d.fill(thumbShape);
g2d.setColor(ColorScheme.DARK_GRAY_COLOR);
g2d.draw(thumbShape);
g2d.dispose();
}
/**
* Listener to handle mouse movements in the slider track.
*/
public class RangeTrackListener extends TrackListener
{
@Override
public void mouseClicked(MouseEvent e)
{
if (!slider.isEnabled())
{
return;
}
currentMouseX -= thumbRect.width / 2; // Because we want the mouse location correspond to middle of the "thumb", not left side of it.
moveUpperThumb();
}
public void mousePressed(MouseEvent e)
{
if (!slider.isEnabled())
{
return;
}
currentMouseX = e.getX();
currentMouseY = e.getY();
if (slider.isRequestFocusEnabled())
{
slider.requestFocus();
}
boolean upperPressed = false;
if (thumbRect.contains(currentMouseX, currentMouseY))
{
upperPressed = true;
}
if (upperPressed)
{
switch (slider.getOrientation())
{
case JSlider.VERTICAL:
offset = currentMouseY - thumbRect.y;
break;
case JSlider.HORIZONTAL:
offset = currentMouseX - thumbRect.x;
break;
}
//upperThumbSelected = true;
upperDragging = true;
return;
}
upperDragging = false;
}
@Override
public void mouseReleased(MouseEvent e)
{
upperDragging = false;
slider.setValueIsAdjusting(false);
super.mouseReleased(e);
}
@Override
public void mouseDragged(MouseEvent e)
{
if (!slider.isEnabled())
{
return;
}
currentMouseX = e.getX();
currentMouseY = e.getY();
if (upperDragging)
{
slider.setValueIsAdjusting(true);
moveUpperThumb();
}
}
@Override
public boolean shouldScroll(int direction)
{
return false;
}
/**
* Moves the location of the upper thumb, and sets its corresponding value in the slider.
*/
public void moveUpperThumb()
{
int thumbMiddle;
if (slider.getOrientation() == JSlider.HORIZONTAL)
{
int halfThumbWidth = thumbRect.width / 2;
int thumbLeft = currentMouseX - offset;
int trackLeft = trackRect.x;
int trackRight = trackRect.x + (trackRect.width - 1);
int hMax = xPositionForValue(slider.getMaximum() -
slider.getExtent());
if (drawInverted())
{
trackLeft = hMax;
}
else
{
trackRight = hMax;
}
thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth);
thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth);
setThumbLocation(thumbLeft, thumbRect.y);//setThumbLocation
thumbMiddle = thumbLeft + halfThumbWidth;
slider.setValue(valueForXPosition(thumbMiddle));
}
}
}
}

View File

@@ -74,6 +74,7 @@ import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.components.CustomScrollBarUI;
import net.runelite.client.ui.components.SliderUI;
import net.runelite.client.ui.skin.SubstanceRuneLiteLookAndFeel;
import org.pushingpixels.substance.internal.SubstanceSynapse;
@@ -115,6 +116,7 @@ public class SwingUtil
UIManager.put("ProgressBar.verticalSize", new Dimension(12, 10));
UIManager.put("ProgressBar.horizontalSize", new Dimension(10, 12));
UIManager.put("ProgressBarUI", BasicProgressBarUI.class.getName());
UIManager.put("SliderUI", SliderUI.class.getName());
// Do not render shadows under popups/tooltips.
// Fixes black boxes under popups that are above the game applet.