You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1007 lines
34 KiB
1007 lines
34 KiB
/*
|
|
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package javax.swing.plaf.synth;
|
|
|
|
import java.awt.event.*;
|
|
import java.awt.Graphics;
|
|
import java.awt.Dimension;
|
|
import java.awt.FontMetrics;
|
|
import java.awt.Rectangle;
|
|
import java.awt.Point;
|
|
import java.awt.Insets;
|
|
import java.beans.*;
|
|
import java.util.Dictionary;
|
|
import java.util.Enumeration;
|
|
import javax.swing.*;
|
|
import javax.swing.plaf.*;
|
|
import javax.swing.plaf.basic.BasicSliderUI;
|
|
import sun.swing.SwingUtilities2;
|
|
|
|
|
|
/**
|
|
* Provides the Synth L&F UI delegate for
|
|
* {@link JSlider}.
|
|
*
|
|
* @author Joshua Outwater
|
|
* @since 1.7
|
|
*/
|
|
public class SynthSliderUI extends BasicSliderUI
|
|
implements PropertyChangeListener, SynthUI {
|
|
private Rectangle valueRect = new Rectangle();
|
|
private boolean paintValue;
|
|
|
|
/**
|
|
* When a JSlider is used as a renderer in a JTable, its layout is not
|
|
* being recomputed even though the size is changing. Even though there
|
|
* is a ComponentListener installed, it is not being notified. As such,
|
|
* at times when being asked to paint the layout should first be redone.
|
|
* At the end of the layout method we set this lastSize variable, which
|
|
* represents the size of the slider the last time it was layed out.
|
|
*
|
|
* In the paint method we then check to see that this is accurate, that
|
|
* the slider has not changed sizes since being last layed out. If necessary
|
|
* we recompute the layout.
|
|
*/
|
|
private Dimension lastSize;
|
|
|
|
private int trackHeight;
|
|
private int trackBorder;
|
|
private int thumbWidth;
|
|
private int thumbHeight;
|
|
|
|
private SynthStyle style;
|
|
private SynthStyle sliderTrackStyle;
|
|
private SynthStyle sliderThumbStyle;
|
|
|
|
/** Used to determine the color to paint the thumb. */
|
|
private transient boolean thumbActive; //happens on rollover, and when pressed
|
|
private transient boolean thumbPressed; //happens when mouse was depressed while over thumb
|
|
|
|
///////////////////////////////////////////////////
|
|
// ComponentUI Interface Implementation methods
|
|
///////////////////////////////////////////////////
|
|
/**
|
|
* Creates a new UI object for the given component.
|
|
*
|
|
* @param c component to create UI object for
|
|
* @return the UI object
|
|
*/
|
|
public static ComponentUI createUI(JComponent c) {
|
|
return new SynthSliderUI((JSlider)c);
|
|
}
|
|
|
|
protected SynthSliderUI(JSlider c) {
|
|
super(c);
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected void installDefaults(JSlider slider) {
|
|
updateStyle(slider);
|
|
}
|
|
|
|
/**
|
|
* Uninstalls default setting. This method is called when a
|
|
* {@code LookAndFeel} is uninstalled.
|
|
*/
|
|
protected void uninstallDefaults(JSlider slider) {
|
|
SynthContext context = getContext(slider, ENABLED);
|
|
style.uninstallDefaults(context);
|
|
context.dispose();
|
|
style = null;
|
|
|
|
context = getContext(slider, Region.SLIDER_TRACK, ENABLED);
|
|
sliderTrackStyle.uninstallDefaults(context);
|
|
context.dispose();
|
|
sliderTrackStyle = null;
|
|
|
|
context = getContext(slider, Region.SLIDER_THUMB, ENABLED);
|
|
sliderThumbStyle.uninstallDefaults(context);
|
|
context.dispose();
|
|
sliderThumbStyle = null;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected void installListeners(JSlider slider) {
|
|
super.installListeners(slider);
|
|
slider.addPropertyChangeListener(this);
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected void uninstallListeners(JSlider slider) {
|
|
slider.removePropertyChangeListener(this);
|
|
super.uninstallListeners(slider);
|
|
}
|
|
|
|
private void updateStyle(JSlider c) {
|
|
SynthContext context = getContext(c, ENABLED);
|
|
SynthStyle oldStyle = style;
|
|
style = SynthLookAndFeel.updateStyle(context, this);
|
|
|
|
if (style != oldStyle) {
|
|
thumbWidth =
|
|
style.getInt(context, "Slider.thumbWidth", 30);
|
|
|
|
thumbHeight =
|
|
style.getInt(context, "Slider.thumbHeight", 14);
|
|
|
|
// handle scaling for sizeVarients for special case components. The
|
|
// key "JComponent.sizeVariant" scales for large/small/mini
|
|
// components are based on Apples LAF
|
|
String scaleKey = (String)slider.getClientProperty(
|
|
"JComponent.sizeVariant");
|
|
if (scaleKey != null){
|
|
if ("large".equals(scaleKey)){
|
|
thumbWidth *= 1.15;
|
|
thumbHeight *= 1.15;
|
|
} else if ("small".equals(scaleKey)){
|
|
thumbWidth *= 0.857;
|
|
thumbHeight *= 0.857;
|
|
} else if ("mini".equals(scaleKey)){
|
|
thumbWidth *= 0.784;
|
|
thumbHeight *= 0.784;
|
|
}
|
|
}
|
|
|
|
trackBorder =
|
|
style.getInt(context, "Slider.trackBorder", 1);
|
|
|
|
trackHeight = thumbHeight + trackBorder * 2;
|
|
|
|
paintValue = style.getBoolean(context,
|
|
"Slider.paintValue", true);
|
|
if (oldStyle != null) {
|
|
uninstallKeyboardActions(c);
|
|
installKeyboardActions(c);
|
|
}
|
|
}
|
|
context.dispose();
|
|
|
|
context = getContext(c, Region.SLIDER_TRACK, ENABLED);
|
|
sliderTrackStyle =
|
|
SynthLookAndFeel.updateStyle(context, this);
|
|
context.dispose();
|
|
|
|
context = getContext(c, Region.SLIDER_THUMB, ENABLED);
|
|
sliderThumbStyle =
|
|
SynthLookAndFeel.updateStyle(context, this);
|
|
context.dispose();
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected TrackListener createTrackListener(JSlider s) {
|
|
return new SynthTrackListener();
|
|
}
|
|
|
|
private void updateThumbState(int x, int y) {
|
|
setThumbActive(thumbRect.contains(x, y));
|
|
}
|
|
|
|
private void updateThumbState(int x, int y, boolean pressed) {
|
|
updateThumbState(x, y);
|
|
setThumbPressed(pressed);
|
|
}
|
|
|
|
private void setThumbActive(boolean active) {
|
|
if (thumbActive != active) {
|
|
thumbActive = active;
|
|
slider.repaint(thumbRect);
|
|
}
|
|
}
|
|
|
|
private void setThumbPressed(boolean pressed) {
|
|
if (thumbPressed != pressed) {
|
|
thumbPressed = pressed;
|
|
slider.repaint(thumbRect);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public int getBaseline(JComponent c, int width, int height) {
|
|
if (c == null) {
|
|
throw new NullPointerException("Component must be non-null");
|
|
}
|
|
if (width < 0 || height < 0) {
|
|
throw new IllegalArgumentException(
|
|
"Width and height must be >= 0");
|
|
}
|
|
if (slider.getPaintLabels() && labelsHaveSameBaselines()) {
|
|
// Get the insets for the track.
|
|
Insets trackInsets = new Insets(0, 0, 0, 0);
|
|
SynthContext trackContext = getContext(slider,
|
|
Region.SLIDER_TRACK);
|
|
style.getInsets(trackContext, trackInsets);
|
|
trackContext.dispose();
|
|
if (slider.getOrientation() == JSlider.HORIZONTAL) {
|
|
int valueHeight = 0;
|
|
if (paintValue) {
|
|
SynthContext context = getContext(slider);
|
|
valueHeight = context.getStyle().getGraphicsUtils(context).
|
|
getMaximumCharHeight(context);
|
|
context.dispose();
|
|
}
|
|
int tickHeight = 0;
|
|
if (slider.getPaintTicks()) {
|
|
tickHeight = getTickLength();
|
|
}
|
|
int labelHeight = getHeightOfTallestLabel();
|
|
int contentHeight = valueHeight + trackHeight +
|
|
trackInsets.top + trackInsets.bottom +
|
|
tickHeight + labelHeight + 4;
|
|
int centerY = height / 2 - contentHeight / 2;
|
|
centerY += valueHeight + 2;
|
|
centerY += trackHeight + trackInsets.top + trackInsets.bottom;
|
|
centerY += tickHeight + 2;
|
|
JComponent label = (JComponent) slider.getLabelTable().elements().nextElement();
|
|
Dimension pref = label.getPreferredSize();
|
|
return centerY + label.getBaseline(pref.width, pref.height);
|
|
}
|
|
else { // VERTICAL
|
|
Integer value = slider.getInverted() ? getLowestValue() :
|
|
getHighestValue();
|
|
if (value != null) {
|
|
int valueY = insetCache.top;
|
|
int valueHeight = 0;
|
|
if (paintValue) {
|
|
SynthContext context = getContext(slider);
|
|
valueHeight = context.getStyle().getGraphicsUtils(
|
|
context).getMaximumCharHeight(context);
|
|
context.dispose();
|
|
}
|
|
int contentHeight = height - insetCache.top -
|
|
insetCache.bottom;
|
|
int trackY = valueY + valueHeight;
|
|
int trackHeight = contentHeight - valueHeight;
|
|
int yPosition = yPositionForValue(value.intValue(), trackY,
|
|
trackHeight);
|
|
JComponent label = (JComponent) slider.getLabelTable().get(value);
|
|
Dimension pref = label.getPreferredSize();
|
|
return yPosition - pref.height / 2 +
|
|
label.getBaseline(pref.width, pref.height);
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public Dimension getPreferredSize(JComponent c) {
|
|
recalculateIfInsetsChanged();
|
|
Dimension d = new Dimension(contentRect.width, contentRect.height);
|
|
if (slider.getOrientation() == JSlider.VERTICAL) {
|
|
d.height = 200;
|
|
} else {
|
|
d.width = 200;
|
|
}
|
|
Insets i = slider.getInsets();
|
|
d.width += i.left + i.right;
|
|
d.height += i.top + i.bottom;
|
|
return d;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public Dimension getMinimumSize(JComponent c) {
|
|
recalculateIfInsetsChanged();
|
|
Dimension d = new Dimension(contentRect.width, contentRect.height);
|
|
if (slider.getOrientation() == JSlider.VERTICAL) {
|
|
d.height = thumbRect.height + insetCache.top + insetCache.bottom;
|
|
} else {
|
|
d.width = thumbRect.width + insetCache.left + insetCache.right;
|
|
}
|
|
return d;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected void calculateGeometry() {
|
|
calculateThumbSize();
|
|
layout();
|
|
calculateThumbLocation();
|
|
}
|
|
|
|
/**
|
|
* Lays out the slider.
|
|
*/
|
|
protected void layout() {
|
|
SynthContext context = getContext(slider);
|
|
SynthGraphicsUtils synthGraphics = style.getGraphicsUtils(context);
|
|
|
|
// Get the insets for the track.
|
|
Insets trackInsets = new Insets(0, 0, 0, 0);
|
|
SynthContext trackContext = getContext(slider, Region.SLIDER_TRACK);
|
|
style.getInsets(trackContext, trackInsets);
|
|
trackContext.dispose();
|
|
|
|
if (slider.getOrientation() == JSlider.HORIZONTAL) {
|
|
// Calculate the height of all the subcomponents so we can center
|
|
// them.
|
|
valueRect.height = 0;
|
|
if (paintValue) {
|
|
valueRect.height =
|
|
synthGraphics.getMaximumCharHeight(context);
|
|
}
|
|
|
|
trackRect.height = trackHeight;
|
|
|
|
tickRect.height = 0;
|
|
if (slider.getPaintTicks()) {
|
|
tickRect.height = getTickLength();
|
|
}
|
|
|
|
labelRect.height = 0;
|
|
if (slider.getPaintLabels()) {
|
|
labelRect.height = getHeightOfTallestLabel();
|
|
}
|
|
|
|
contentRect.height = valueRect.height + trackRect.height
|
|
+ trackInsets.top + trackInsets.bottom
|
|
+ tickRect.height + labelRect.height + 4;
|
|
contentRect.width = slider.getWidth() - insetCache.left
|
|
- insetCache.right;
|
|
|
|
// Check if any of the labels will paint out of bounds.
|
|
int pad = 0;
|
|
if (slider.getPaintLabels()) {
|
|
// Calculate the track rectangle. It is necessary for
|
|
// xPositionForValue to return correct values.
|
|
trackRect.x = insetCache.left;
|
|
trackRect.width = contentRect.width;
|
|
|
|
Dictionary dictionary = slider.getLabelTable();
|
|
if (dictionary != null) {
|
|
int minValue = slider.getMinimum();
|
|
int maxValue = slider.getMaximum();
|
|
|
|
// Iterate through the keys in the dictionary and find the
|
|
// first and last labels indices that fall within the
|
|
// slider range.
|
|
int firstLblIdx = Integer.MAX_VALUE;
|
|
int lastLblIdx = Integer.MIN_VALUE;
|
|
for (Enumeration keys = dictionary.keys();
|
|
keys.hasMoreElements(); ) {
|
|
int keyInt = ((Integer)keys.nextElement()).intValue();
|
|
if (keyInt >= minValue && keyInt < firstLblIdx) {
|
|
firstLblIdx = keyInt;
|
|
}
|
|
if (keyInt <= maxValue && keyInt > lastLblIdx) {
|
|
lastLblIdx = keyInt;
|
|
}
|
|
}
|
|
// Calculate the pad necessary for the labels at the first
|
|
// and last visible indices.
|
|
pad = getPadForLabel(firstLblIdx);
|
|
pad = Math.max(pad, getPadForLabel(lastLblIdx));
|
|
}
|
|
}
|
|
// Calculate the painting rectangles for each of the different
|
|
// slider areas.
|
|
valueRect.x = trackRect.x = tickRect.x = labelRect.x =
|
|
(insetCache.left + pad);
|
|
valueRect.width = trackRect.width = tickRect.width =
|
|
labelRect.width = (contentRect.width - (pad * 2));
|
|
|
|
int centerY = slider.getHeight() / 2 - contentRect.height / 2;
|
|
|
|
valueRect.y = centerY;
|
|
centerY += valueRect.height + 2;
|
|
|
|
trackRect.y = centerY + trackInsets.top;
|
|
centerY += trackRect.height + trackInsets.top + trackInsets.bottom;
|
|
|
|
tickRect.y = centerY;
|
|
centerY += tickRect.height + 2;
|
|
|
|
labelRect.y = centerY;
|
|
centerY += labelRect.height;
|
|
} else {
|
|
// Calculate the width of all the subcomponents so we can center
|
|
// them.
|
|
trackRect.width = trackHeight;
|
|
|
|
tickRect.width = 0;
|
|
if (slider.getPaintTicks()) {
|
|
tickRect.width = getTickLength();
|
|
}
|
|
|
|
labelRect.width = 0;
|
|
if (slider.getPaintLabels()) {
|
|
labelRect.width = getWidthOfWidestLabel();
|
|
}
|
|
|
|
valueRect.y = insetCache.top;
|
|
valueRect.height = 0;
|
|
if (paintValue) {
|
|
valueRect.height =
|
|
synthGraphics.getMaximumCharHeight(context);
|
|
}
|
|
|
|
// Get the max width of the min or max value of the slider.
|
|
FontMetrics fm = slider.getFontMetrics(slider.getFont());
|
|
valueRect.width = Math.max(
|
|
synthGraphics.computeStringWidth(context, slider.getFont(),
|
|
fm, "" + slider.getMaximum()),
|
|
synthGraphics.computeStringWidth(context, slider.getFont(),
|
|
fm, "" + slider.getMinimum()));
|
|
|
|
int l = valueRect.width / 2;
|
|
int w1 = trackInsets.left + trackRect.width / 2;
|
|
int w2 = trackRect.width / 2 + trackInsets.right +
|
|
tickRect.width + labelRect.width;
|
|
contentRect.width = Math.max(w1, l) + Math.max(w2, l) +
|
|
2 + insetCache.left + insetCache.right;
|
|
contentRect.height = slider.getHeight() -
|
|
insetCache.top - insetCache.bottom;
|
|
|
|
// Layout the components.
|
|
trackRect.y = tickRect.y = labelRect.y =
|
|
valueRect.y + valueRect.height;
|
|
trackRect.height = tickRect.height = labelRect.height =
|
|
contentRect.height - valueRect.height;
|
|
|
|
int startX = slider.getWidth() / 2 - contentRect.width / 2;
|
|
if (SynthLookAndFeel.isLeftToRight(slider)) {
|
|
if (l > w1) {
|
|
startX += (l - w1);
|
|
}
|
|
trackRect.x = startX + trackInsets.left;
|
|
|
|
startX += trackInsets.left + trackRect.width + trackInsets.right;
|
|
tickRect.x = startX;
|
|
labelRect.x = startX + tickRect.width + 2;
|
|
} else {
|
|
if (l > w2) {
|
|
startX += (l - w2);
|
|
}
|
|
labelRect.x = startX;
|
|
|
|
startX += labelRect.width + 2;
|
|
tickRect.x = startX;
|
|
trackRect.x = startX + tickRect.width + trackInsets.left;
|
|
}
|
|
}
|
|
context.dispose();
|
|
lastSize = slider.getSize();
|
|
}
|
|
|
|
/**
|
|
* Calculates the pad for the label at the specified index.
|
|
*
|
|
* @param i index of the label to calculate pad for.
|
|
* @return padding required to keep label visible.
|
|
*/
|
|
private int getPadForLabel(int i) {
|
|
int pad = 0;
|
|
|
|
JComponent c = (JComponent) slider.getLabelTable().get(i);
|
|
if (c != null) {
|
|
int centerX = xPositionForValue(i);
|
|
int cHalfWidth = c.getPreferredSize().width / 2;
|
|
if (centerX - cHalfWidth < insetCache.left) {
|
|
pad = Math.max(pad, insetCache.left - (centerX - cHalfWidth));
|
|
}
|
|
|
|
if (centerX + cHalfWidth > slider.getWidth() - insetCache.right) {
|
|
pad = Math.max(pad, (centerX + cHalfWidth) -
|
|
(slider.getWidth() - insetCache.right));
|
|
}
|
|
}
|
|
return pad;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected void calculateThumbLocation() {
|
|
super.calculateThumbLocation();
|
|
if (slider.getOrientation() == JSlider.HORIZONTAL) {
|
|
thumbRect.y += trackBorder;
|
|
} else {
|
|
thumbRect.x += trackBorder;
|
|
}
|
|
Point mousePosition = slider.getMousePosition();
|
|
if(mousePosition != null) {
|
|
updateThumbState(mousePosition.x, mousePosition.y);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void setThumbLocation(int x, int y) {
|
|
super.setThumbLocation(x, y);
|
|
// Value rect is tied to the thumb location. We need to repaint when
|
|
// the thumb repaints.
|
|
slider.repaint(valueRect.x, valueRect.y,
|
|
valueRect.width, valueRect.height);
|
|
setThumbActive(false);
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected int xPositionForValue(int value) {
|
|
int min = slider.getMinimum();
|
|
int max = slider.getMaximum();
|
|
int trackLeft = trackRect.x + thumbRect.width / 2 + trackBorder;
|
|
int trackRight = trackRect.x + trackRect.width - thumbRect.width / 2
|
|
- trackBorder;
|
|
int trackLength = trackRight - trackLeft;
|
|
double valueRange = (double)max - (double)min;
|
|
double pixelsPerValue = (double)trackLength / valueRange;
|
|
int xPosition;
|
|
|
|
if (!drawInverted()) {
|
|
xPosition = trackLeft;
|
|
xPosition += Math.round( pixelsPerValue * ((double)value - min));
|
|
} else {
|
|
xPosition = trackRight;
|
|
xPosition -= Math.round( pixelsPerValue * ((double)value - min));
|
|
}
|
|
|
|
xPosition = Math.max(trackLeft, xPosition);
|
|
xPosition = Math.min(trackRight, xPosition);
|
|
|
|
return xPosition;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected int yPositionForValue(int value, int trackY, int trackHeight) {
|
|
int min = slider.getMinimum();
|
|
int max = slider.getMaximum();
|
|
int trackTop = trackY + thumbRect.height / 2 + trackBorder;
|
|
int trackBottom = trackY + trackHeight - thumbRect.height / 2 -
|
|
trackBorder;
|
|
int trackLength = trackBottom - trackTop;
|
|
double valueRange = (double)max - (double)min;
|
|
double pixelsPerValue = (double)trackLength / valueRange;
|
|
int yPosition;
|
|
|
|
if (!drawInverted()) {
|
|
yPosition = trackTop;
|
|
yPosition += Math.round(pixelsPerValue * ((double)max - value));
|
|
} else {
|
|
yPosition = trackTop;
|
|
yPosition += Math.round(pixelsPerValue * ((double)value - min));
|
|
}
|
|
|
|
yPosition = Math.max(trackTop, yPosition);
|
|
yPosition = Math.min(trackBottom, yPosition);
|
|
|
|
return yPosition;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public int valueForYPosition(int yPos) {
|
|
int value;
|
|
int minValue = slider.getMinimum();
|
|
int maxValue = slider.getMaximum();
|
|
int trackTop = trackRect.y + thumbRect.height / 2 + trackBorder;
|
|
int trackBottom = trackRect.y + trackRect.height
|
|
- thumbRect.height / 2 - trackBorder;
|
|
int trackLength = trackBottom - trackTop;
|
|
|
|
if (yPos <= trackTop) {
|
|
value = drawInverted() ? minValue : maxValue;
|
|
} else if (yPos >= trackBottom) {
|
|
value = drawInverted() ? maxValue : minValue;
|
|
} else {
|
|
int distanceFromTrackTop = yPos - trackTop;
|
|
double valueRange = (double)maxValue - (double)minValue;
|
|
double valuePerPixel = valueRange / (double)trackLength;
|
|
int valueFromTrackTop =
|
|
(int)Math.round(distanceFromTrackTop * valuePerPixel);
|
|
value = drawInverted() ?
|
|
minValue + valueFromTrackTop : maxValue - valueFromTrackTop;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public int valueForXPosition(int xPos) {
|
|
int value;
|
|
int minValue = slider.getMinimum();
|
|
int maxValue = slider.getMaximum();
|
|
int trackLeft = trackRect.x + thumbRect.width / 2 + trackBorder;
|
|
int trackRight = trackRect.x + trackRect.width
|
|
- thumbRect.width / 2 - trackBorder;
|
|
int trackLength = trackRight - trackLeft;
|
|
|
|
if (xPos <= trackLeft) {
|
|
value = drawInverted() ? maxValue : minValue;
|
|
} else if (xPos >= trackRight) {
|
|
value = drawInverted() ? minValue : maxValue;
|
|
} else {
|
|
int distanceFromTrackLeft = xPos - trackLeft;
|
|
double valueRange = (double)maxValue - (double)minValue;
|
|
double valuePerPixel = valueRange / (double)trackLength;
|
|
int valueFromTrackLeft =
|
|
(int)Math.round(distanceFromTrackLeft * valuePerPixel);
|
|
value = drawInverted() ?
|
|
maxValue - valueFromTrackLeft : minValue + valueFromTrackLeft;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected Dimension getThumbSize() {
|
|
Dimension size = new Dimension();
|
|
|
|
if (slider.getOrientation() == JSlider.VERTICAL) {
|
|
size.width = thumbHeight;
|
|
size.height = thumbWidth;
|
|
} else {
|
|
size.width = thumbWidth;
|
|
size.height = thumbHeight;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected void recalculateIfInsetsChanged() {
|
|
SynthContext context = getContext(slider);
|
|
Insets newInsets = style.getInsets(context, null);
|
|
Insets compInsets = slider.getInsets();
|
|
newInsets.left += compInsets.left; newInsets.right += compInsets.right;
|
|
newInsets.top += compInsets.top; newInsets.bottom += compInsets.bottom;
|
|
if (!newInsets.equals(insetCache)) {
|
|
insetCache = newInsets;
|
|
calculateGeometry();
|
|
}
|
|
context.dispose();
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public SynthContext getContext(JComponent c) {
|
|
return getContext(c, SynthLookAndFeel.getComponentState(c));
|
|
}
|
|
|
|
private SynthContext getContext(JComponent c, int state) {
|
|
return SynthContext.getContext(c, style, state);
|
|
}
|
|
|
|
private SynthContext getContext(JComponent c, Region subregion) {
|
|
return getContext(c, subregion, getComponentState(c, subregion));
|
|
}
|
|
|
|
private SynthContext getContext(JComponent c, Region subregion, int state) {
|
|
SynthStyle style = null;
|
|
|
|
if (subregion == Region.SLIDER_TRACK) {
|
|
style = sliderTrackStyle;
|
|
} else if (subregion == Region.SLIDER_THUMB) {
|
|
style = sliderThumbStyle;
|
|
}
|
|
return SynthContext.getContext(c, subregion, style, state);
|
|
}
|
|
|
|
private int getComponentState(JComponent c, Region region) {
|
|
if (region == Region.SLIDER_THUMB && thumbActive &&c.isEnabled()) {
|
|
int state = thumbPressed ? PRESSED : MOUSE_OVER;
|
|
if (c.isFocusOwner()) state |= FOCUSED;
|
|
return state;
|
|
}
|
|
return SynthLookAndFeel.getComponentState(c);
|
|
}
|
|
|
|
/**
|
|
* Notifies this UI delegate to repaint the specified component.
|
|
* This method paints the component background, then calls
|
|
* the {@link #paint(SynthContext,Graphics)} method.
|
|
*
|
|
* <p>In general, this method does not need to be overridden by subclasses.
|
|
* All Look and Feel rendering code should reside in the {@code paint} method.
|
|
*
|
|
* @param g the {@code Graphics} object used for painting
|
|
* @param c the component being painted
|
|
* @see #paint(SynthContext,Graphics)
|
|
*/
|
|
@Override
|
|
public void update(Graphics g, JComponent c) {
|
|
SynthContext context = getContext(c);
|
|
SynthLookAndFeel.update(context, g);
|
|
context.getPainter().paintSliderBackground(context,
|
|
g, 0, 0, c.getWidth(), c.getHeight(),
|
|
slider.getOrientation());
|
|
paint(context, g);
|
|
context.dispose();
|
|
}
|
|
|
|
/**
|
|
* Paints the specified component according to the Look and Feel.
|
|
* <p>This method is not used by Synth Look and Feel.
|
|
* Painting is handled by the {@link #paint(SynthContext,Graphics)} method.
|
|
*
|
|
* @param g the {@code Graphics} object used for painting
|
|
* @param c the component being painted
|
|
* @see #paint(SynthContext,Graphics)
|
|
*/
|
|
@Override
|
|
public void paint(Graphics g, JComponent c) {
|
|
SynthContext context = getContext(c);
|
|
paint(context, g);
|
|
context.dispose();
|
|
}
|
|
|
|
/**
|
|
* Paints the specified component.
|
|
*
|
|
* @param context context for the component being painted
|
|
* @param g the {@code Graphics} object used for painting
|
|
* @see #update(Graphics,JComponent)
|
|
*/
|
|
protected void paint(SynthContext context, Graphics g) {
|
|
recalculateIfInsetsChanged();
|
|
recalculateIfOrientationChanged();
|
|
Rectangle clip = g.getClipBounds();
|
|
|
|
if (lastSize == null || !lastSize.equals(slider.getSize())) {
|
|
calculateGeometry();
|
|
}
|
|
|
|
if (paintValue) {
|
|
FontMetrics fm = SwingUtilities2.getFontMetrics(slider, g);
|
|
int labelWidth = context.getStyle().getGraphicsUtils(context).
|
|
computeStringWidth(context, g.getFont(), fm,
|
|
"" + slider.getValue());
|
|
valueRect.x = thumbRect.x + (thumbRect.width - labelWidth) / 2;
|
|
|
|
// For horizontal sliders, make sure value is not painted
|
|
// outside slider bounds.
|
|
if (slider.getOrientation() == JSlider.HORIZONTAL) {
|
|
if (valueRect.x + labelWidth > insetCache.left + contentRect.width) {
|
|
valueRect.x = (insetCache.left + contentRect.width) - labelWidth;
|
|
}
|
|
valueRect.x = Math.max(valueRect.x, 0);
|
|
}
|
|
|
|
g.setColor(context.getStyle().getColor(
|
|
context, ColorType.TEXT_FOREGROUND));
|
|
context.getStyle().getGraphicsUtils(context).paintText(
|
|
context, g, "" + slider.getValue(), valueRect.x,
|
|
valueRect.y, -1);
|
|
}
|
|
|
|
if (slider.getPaintTrack() && clip.intersects(trackRect)) {
|
|
SynthContext subcontext = getContext(slider, Region.SLIDER_TRACK);
|
|
paintTrack(subcontext, g, trackRect);
|
|
subcontext.dispose();
|
|
}
|
|
|
|
if (clip.intersects(thumbRect)) {
|
|
SynthContext subcontext = getContext(slider, Region.SLIDER_THUMB);
|
|
paintThumb(subcontext, g, thumbRect);
|
|
subcontext.dispose();
|
|
}
|
|
|
|
if (slider.getPaintTicks() && clip.intersects(tickRect)) {
|
|
paintTicks(g);
|
|
}
|
|
|
|
if (slider.getPaintLabels() && clip.intersects(labelRect)) {
|
|
paintLabels(g);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void paintBorder(SynthContext context, Graphics g, int x,
|
|
int y, int w, int h) {
|
|
context.getPainter().paintSliderBorder(context, g, x, y, w, h,
|
|
slider.getOrientation());
|
|
}
|
|
|
|
/**
|
|
* Paints the slider thumb.
|
|
*
|
|
* @param context context for the component being painted
|
|
* @param g {@code Graphics} object used for painting
|
|
* @param thumbBounds bounding box for the thumb
|
|
*/
|
|
protected void paintThumb(SynthContext context, Graphics g,
|
|
Rectangle thumbBounds) {
|
|
int orientation = slider.getOrientation();
|
|
SynthLookAndFeel.updateSubregion(context, g, thumbBounds);
|
|
context.getPainter().paintSliderThumbBackground(context, g,
|
|
thumbBounds.x, thumbBounds.y, thumbBounds.width,
|
|
thumbBounds.height, orientation);
|
|
context.getPainter().paintSliderThumbBorder(context, g,
|
|
thumbBounds.x, thumbBounds.y, thumbBounds.width,
|
|
thumbBounds.height, orientation);
|
|
}
|
|
|
|
/**
|
|
* Paints the slider track.
|
|
*
|
|
* @param context context for the component being painted
|
|
* @param g {@code Graphics} object used for painting
|
|
* @param trackBounds bounding box for the track
|
|
*/
|
|
protected void paintTrack(SynthContext context, Graphics g,
|
|
Rectangle trackBounds) {
|
|
int orientation = slider.getOrientation();
|
|
SynthLookAndFeel.updateSubregion(context, g, trackBounds);
|
|
context.getPainter().paintSliderTrackBackground(context, g,
|
|
trackBounds.x, trackBounds.y, trackBounds.width,
|
|
trackBounds.height, orientation);
|
|
context.getPainter().paintSliderTrackBorder(context, g,
|
|
trackBounds.x, trackBounds.y, trackBounds.width,
|
|
trackBounds.height, orientation);
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void propertyChange(PropertyChangeEvent e) {
|
|
if (SynthLookAndFeel.shouldUpdateStyle(e)) {
|
|
updateStyle((JSlider)e.getSource());
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
/// Track Listener Class
|
|
//////////////////////////////////////////////////
|
|
/**
|
|
* Track mouse movements.
|
|
*/
|
|
private class SynthTrackListener extends TrackListener {
|
|
|
|
@Override public void mouseExited(MouseEvent e) {
|
|
setThumbActive(false);
|
|
}
|
|
|
|
@Override public void mousePressed(MouseEvent e) {
|
|
super.mousePressed(e);
|
|
setThumbPressed(thumbRect.contains(e.getX(), e.getY()));
|
|
}
|
|
|
|
@Override public void mouseReleased(MouseEvent e) {
|
|
super.mouseReleased(e);
|
|
updateThumbState(e.getX(), e.getY(), false);
|
|
}
|
|
|
|
@Override public void mouseDragged(MouseEvent e) {
|
|
int thumbMiddle;
|
|
|
|
if (!slider.isEnabled()) {
|
|
return;
|
|
}
|
|
|
|
currentMouseX = e.getX();
|
|
currentMouseY = e.getY();
|
|
|
|
if (!isDragging()) {
|
|
return;
|
|
}
|
|
|
|
slider.setValueIsAdjusting(true);
|
|
|
|
switch (slider.getOrientation()) {
|
|
case JSlider.VERTICAL:
|
|
int halfThumbHeight = thumbRect.height / 2;
|
|
int thumbTop = e.getY() - offset;
|
|
int trackTop = trackRect.y;
|
|
int trackBottom = trackRect.y + trackRect.height
|
|
- halfThumbHeight - trackBorder;
|
|
int vMax = yPositionForValue(slider.getMaximum() -
|
|
slider.getExtent());
|
|
|
|
if (drawInverted()) {
|
|
trackBottom = vMax;
|
|
trackTop = trackTop + halfThumbHeight;
|
|
} else {
|
|
trackTop = vMax;
|
|
}
|
|
thumbTop = Math.max(thumbTop, trackTop - halfThumbHeight);
|
|
thumbTop = Math.min(thumbTop, trackBottom - halfThumbHeight);
|
|
|
|
setThumbLocation(thumbRect.x, thumbTop);
|
|
|
|
thumbMiddle = thumbTop + halfThumbHeight;
|
|
slider.setValue(valueForYPosition(thumbMiddle));
|
|
break;
|
|
case JSlider.HORIZONTAL:
|
|
int halfThumbWidth = thumbRect.width / 2;
|
|
int thumbLeft = e.getX() - offset;
|
|
int trackLeft = trackRect.x + halfThumbWidth + trackBorder;
|
|
int trackRight = trackRect.x + trackRect.width
|
|
- halfThumbWidth - trackBorder;
|
|
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);
|
|
|
|
thumbMiddle = thumbLeft + halfThumbWidth;
|
|
slider.setValue(valueForXPosition(thumbMiddle));
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if (slider.getValueIsAdjusting()) {
|
|
setThumbActive(true);
|
|
}
|
|
}
|
|
|
|
@Override public void mouseMoved(MouseEvent e) {
|
|
updateThumbState(e.getX(), e.getY());
|
|
}
|
|
}
|
|
}
|