config: Sliders

This commit is contained in:
Owain van Brakel
2021-02-25 06:18:15 +01:00
parent 8c4125573e
commit 28907f5340
3 changed files with 425 additions and 12 deletions

View File

@@ -68,8 +68,10 @@ import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
@@ -79,6 +81,7 @@ import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;
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;
@@ -509,6 +512,7 @@ class ConfigPanel extends PluginPanel
{
int value = Integer.parseInt(configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName()));
Units units = cid.getUnits();
Range range = cid.getRange();
int min = 0, max = Integer.MAX_VALUE;
if (range != null)
@@ -520,20 +524,122 @@ class ConfigPanel extends PluginPanel
// Config may previously have been out of range
value = Ints.constrainToRange(value, min, max);
SpinnerModel model = new SpinnerNumberModel(value, min, max, 1);
JSpinner spinner = new JSpinner(model);
Component editor = spinner.getEditor();
JFormattedTextField spinnerTextField = ((JSpinner.DefaultEditor) editor).getTextField();
spinnerTextField.setColumns(SPINNER_FIELD_WIDTH);
spinner.addChangeListener(ce -> changeConfiguration(spinner, cd, cid));
Units units = cid.getUnits();
if (units != null)
if (max < Integer.MAX_VALUE)
{
spinnerTextField.setFormatterFactory(new UnitFormatterFactory(units));
}
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());
}
else
{
sliderValueLabel.setText(String.valueOf(slider.getValue()));
}
slider.setPreferredSize(new Dimension(80, 25));
slider.addChangeListener((l) ->
{
if (units != null)
{
sliderValueLabel.setText(slider.getValue() + units.value());
}
else
{
sliderValueLabel.setText(String.valueOf(slider.getValue()));
}
item.add(spinner, BorderLayout.EAST);
if (!slider.getValueIsAdjusting())
{
changeConfiguration(slider, cd, cid);
}
}
);
SpinnerModel model = new SpinnerNumberModel(value, min, max, 1);
JSpinner spinner = new JSpinner(model);
Component editor = spinner.getEditor();
JFormattedTextField spinnerTextField = ((JSpinner.DefaultEditor) editor).getTextField();
spinnerTextField.setColumns(SPINNER_FIELD_WIDTH);
spinner.setUI(new BasicSpinnerUI()
{
protected Component createNextButton()
{
return null;
}
protected Component createPreviousButton()
{
return null;
}
});
JPanel subPanel = new JPanel();
subPanel.setPreferredSize(new Dimension(110, 25));
subPanel.setLayout(new BorderLayout());
spinner.addChangeListener((ce) ->
{
changeConfiguration(spinner, cd, cid);
if (units != null)
{
sliderValueLabel.setText(spinner.getValue() + units.value());
}
else
{
sliderValueLabel.setText(String.valueOf(spinner.getValue()));
}
slider.setValue((Integer) spinner.getValue());
subPanel.add(sliderValueLabel, BorderLayout.WEST);
subPanel.add(slider, BorderLayout.EAST);
subPanel.remove(spinner);
validate();
repaint();
});
sliderValueLabel.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
spinner.setValue(slider.getValue());
subPanel.remove(sliderValueLabel);
subPanel.remove(slider);
subPanel.add(spinner, BorderLayout.EAST);
validate();
repaint();
final JTextField tf = ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField();
tf.requestFocusInWindow();
SwingUtilities.invokeLater(tf::selectAll);
}
});
subPanel.add(sliderValueLabel, BorderLayout.WEST);
subPanel.add(slider, BorderLayout.EAST);
item.add(subPanel, BorderLayout.EAST);
}
else
{
SpinnerModel model = new SpinnerNumberModel(value, min, max, 1);
JSpinner spinner = new JSpinner(model);
Component editor = spinner.getEditor();
JFormattedTextField spinnerTextField = ((JSpinner.DefaultEditor) editor).getTextField();
spinnerTextField.setColumns(SPINNER_FIELD_WIDTH);
spinner.addChangeListener(ce -> changeConfiguration(spinner, cd, cid));
if (units != null)
{
spinnerTextField.setFormatterFactory(new UnitFormatterFactory(units));
}
item.add(spinner, BorderLayout.EAST);
}
}
if (cid.getType() == String.class)
@@ -895,6 +1001,11 @@ class ConfigPanel extends PluginPanel
JSpinner spinner = (JSpinner) component;
configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), "" + spinner.getValue());
}
else if (component instanceof JSlider)
{
JSlider slider = (JSlider) component;
configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), slider.getValue());
}
else if (component instanceof JTextComponent)
{
JTextComponent textField = (JTextComponent) component;

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

@@ -60,6 +60,7 @@ import lombok.extern.slf4j.Slf4j;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.components.CustomScrollBarUI;
import net.runelite.client.ui.components.SliderUI;
import org.pushingpixels.substance.internal.SubstanceSynapse;
/**
@@ -90,6 +91,7 @@ public class SwingUtil
UIManager.put("FormattedTextField.selectionForeground", Color.WHITE);
UIManager.put("TextArea.selectionBackground", ColorScheme.BRAND_BLUE_TRANSPARENT);
UIManager.put("TextArea.selectionForeground", Color.WHITE);
UIManager.put("SliderUI", SliderUI.class.getName());
// Do not render shadows under popups/tooltips.
// Fixes black boxes under popups that are above the game applet.