/*******************************************************************************
* Copyright (c) 2010 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
******************************************************************************/
package org.csstudio.swt.widgets.figures;
import java.util.ArrayList;
import java.util.List;
import org.csstudio.swt.widgets.datadefinition.IManualValueChangeListener;
import org.csstudio.swt.widgets.figureparts.AlphaLabel;
import org.csstudio.swt.widgets.util.GraphicsUtil;
import org.csstudio.swt.widgets.util.RepeatFiringBehavior;
import org.csstudio.swt.xygraph.linearscale.AbstractScale.LabelSide;
import org.csstudio.swt.xygraph.linearscale.LinearScale;
import org.csstudio.swt.xygraph.linearscale.LinearScale.Orientation;
import org.csstudio.swt.xygraph.linearscale.LinearScaledMarker;
import org.csstudio.swt.xygraph.util.XYGraphMediaFactory;
import org.eclipse.draw2d.AbstractLayout;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Cursors;
import org.eclipse.draw2d.FigureListener;
import org.eclipse.draw2d.FigureUtilities;
import org.eclipse.draw2d.FocusEvent;
import org.eclipse.draw2d.FocusListener;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.KeyEvent;
import org.eclipse.draw2d.KeyListener;
import org.eclipse.draw2d.LineBorder;
import org.eclipse.draw2d.MouseEvent;
import org.eclipse.draw2d.MouseListener;
import org.eclipse.draw2d.MouseMotionListener;
import org.eclipse.draw2d.Polygon;
import org.eclipse.draw2d.RectangleFigure;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Pattern;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
/**
* A Scaled Slider figure
* @author Xihui Chen
*
*/
public class ScaledSliderFigure extends AbstractLinearMarkedFigure {
private Color thumbColor = XYGraphMediaFactory.getInstance().getColor(
new RGB(172,172, 172));
private boolean effect3D = true;
private boolean horizontal = false;
private final Color WHITE_COLOR = XYGraphMediaFactory.getInstance().getColor(
XYGraphMediaFactory.COLOR_WHITE);
//border color for track and thumb
private final Color GRAY_COLOR = XYGraphMediaFactory.getInstance().getColor(
XYGraphMediaFactory.COLOR_GRAY);
private final Color GREEN_COLOR = XYGraphMediaFactory.getInstance().getColor(
XYGraphMediaFactory.COLOR_GREEN);
private final Color BLUE_COLOR = XYGraphMediaFactory.getInstance().getColor(
XYGraphMediaFactory.COLOR_BLUE);
private final Color LABEL_COLOR = XYGraphMediaFactory.getInstance().getColor(
new RGB(255, 255, 150));
private Color fillColor = BLUE_COLOR;
private Color fillBackgroundColor = GRAY_COLOR;
/** The alpha (0 is transparency and 255 is opaque) for disabled paint */
private static final int DISABLED_ALPHA = 100;
private Track track;
private Thumb thumb;
private AlphaLabel label;
private double stepIncrement = 1;
private double pageIncrement = 10;
/**
* Listeners that react on slider events.
*/
private List<IManualValueChangeListener> listeners =
new ArrayList<IManualValueChangeListener>();
public ScaledSliderFigure() {
super();
scale.setScaleLineVisible(false);
scale.setTickLabelSide(LabelSide.Secondary);
if(horizontal) {
((LinearScale)scale).setOrientation(Orientation.HORIZONTAL);
scale.setTickLabelSide(LabelSide.Primary);
marker.setLabelSide(LabelSide.Secondary);
}else {
((LinearScale)scale).setOrientation(Orientation.VERTICAL);
scale.setTickLabelSide(LabelSide.Secondary);
marker.setLabelSide(LabelSide.Primary);
}
track = new Track();
thumb = new Thumb();
label = new AlphaLabel();
label.setBackgroundColor(LABEL_COLOR);
//label.setOpaque(true);
label.setBorder(new LineBorder(GRAY_COLOR));
label.setVisible(false);
setLayoutManager(new XSliderLayout());
add(scale, XSliderLayout.SCALE);
add(marker, XSliderLayout.MARKERS);
add(track, XSliderLayout.TRACK);
add(thumb, XSliderLayout.THUMB);
add(label, "label");
addFigureListener(new FigureListener() {
@Override
public void figureMoved(IFigure source) {
revalidate();
}
});
setRequestFocusEnabled(true);
setFocusTraversable(true);
addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent ke) {
if((ke.keycode == SWT.ARROW_DOWN && !horizontal) ||
(ke.keycode == SWT.ARROW_LEFT && horizontal) )
stepDown();
else if((ke.keycode == SWT.ARROW_UP && !horizontal) ||
(ke.keycode == SWT.ARROW_RIGHT && horizontal) )
stepUp();
else if(ke.keycode == SWT.PAGE_UP)
pageUp();
else if(ke.keycode == SWT.PAGE_DOWN)
pageDown();
}
@Override
public void keyReleased(KeyEvent ke) {
}
});
addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent fe) {
repaint();
}
@Override
public void focusLost(FocusEvent fe) {
repaint();
}
});
}
/**
* Add a slider listener.
*
* @param listener
* The slider listener to add.
*/
public void addManualValueChangeListener(final IManualValueChangeListener listener) {
listeners.add(listener);
}
/**Remove a manual value change listener.
* @param listener the listner to be removed.
*/
public void removeManualValueChangeListener(final IManualValueChangeListener listener){
if(listeners.contains(listener))
listeners.remove(listener);
}
/**Convert the difference of two points to the corresponding value to be changed.
* @param difference the difference between two points.
* difference = endPoint - startPoint
* @param oldValue the old value before this change
* @return the value to be changed
*/
private double calcValueChange(Dimension difference, double oldValue) {
double change;
double dragRange = ((LinearScale)scale).getTickLength();
if(scale.isLogScaleEnabled()) {
double c = dragRange/(
Math.log10(scale.getRange().getUpper()) -
Math.log10(scale.getRange().getLower()));
if(horizontal)
change = oldValue * (Math.pow(10, difference.width/c) - 1);
else
change = oldValue * (Math.pow(10, -difference.height/c) - 1);
} else {
if(horizontal)
change = (scale.getRange().getUpper() - scale.getRange().getLower())
* difference.width / dragRange;
else
change = -(scale.getRange().getUpper() - scale.getRange().getLower())
* difference.height / dragRange;
}
return change;
}
/**
* Inform all slider listeners, that the manual value has changed.
*
* @param newManualValue
* the new manual value
*/
private void fireManualValueChange(final double newManualValue) {
for (IManualValueChangeListener l : listeners) {
l.manualValueChanged(newManualValue);
}
}
/**
* @return the fillBackgroundColor
*/
public Color getFillBackgroundColor() {
return fillBackgroundColor;
}
/**
* @return the fillColor
*/
public Color getFillColor() {
return fillColor;
}
/**
* @return the pageIncrement
*/
public double getPageIncrement() {
return pageIncrement;
}
/**
* @return the stepIncrement
*/
public double getStepIncrement() {
return stepIncrement;
}
/**
* @return the thumbColor
*/
public Color getThumbColor() {
return thumbColor;
}
/**
* @return the effect3D
*/
public boolean isEffect3D() {
return effect3D;
}
/**
* @return the horizontal
*/
public boolean isHorizontal() {
return horizontal;
}
//override this to avoid revalidate the whole FigureCanvas.
@Override
public void revalidate() {
invalidate();
getUpdateManager().addInvalidFigure(this);
}
@Override
public boolean isOpaque() {
return false;
}
/**Set Value from manual control of the widget. Value will be coerced in range.
* @param value
*/
public void manualSetValue(double value){
setValue(getCoercedValue(value));
// value < minimum ? minimum : (value > maximum ? maximum : value));
}
public void pageDown(){
manualSetValue(getValue() - pageIncrement);
fireManualValueChange(getValue());
}
public void pageUp(){
manualSetValue(getValue() + pageIncrement);
fireManualValueChange(getValue());
}
private boolean drawFocus = true;
public void setDrawFocus(boolean drawFocus) {
this.drawFocus = drawFocus;
}
@Override
protected void paintClientArea(Graphics graphics) {
super.paintClientArea(graphics);
if(hasFocus() && drawFocus){
graphics.setForegroundColor(ColorConstants.black);
graphics.setBackgroundColor(ColorConstants.white);
Rectangle area = getClientArea();
graphics.drawFocus(area.x, area.y, area.width-1, area.height-1);
}
if(!isEnabled()) {
graphics.setAlpha(DISABLED_ALPHA);
graphics.setBackgroundColor(GRAY_COLOR);
graphics.fillRectangle(bounds);
}
}
/**
* @param effect3D the effect3D to set
*/
public void setEffect3D(boolean effect3D) {
if(this.effect3D == effect3D)
return;
this.effect3D = effect3D;
repaint();
}
@Override
public void setEnabled(boolean value) {
super.setEnabled(value);
repaint();
}
/**
* @param fillBackgroundColor the fillBackgroundColor to set
*/
public void setFillBackgroundColor(Color fillBackgroundColor) {
if(this.fillBackgroundColor !=null && this.fillBackgroundColor.equals(fillBackgroundColor))
return;
this.fillBackgroundColor = fillBackgroundColor;
repaint();
}
/**
* @param fillColor the fillColor to set
*/
public void setFillColor(Color fillColor) {
if(this.fillColor != null && this.fillColor.equals(fillColor))
return;
this.fillColor = fillColor;
repaint();
}
/**
* @param horizontal the horizontal to set
*/
public void setHorizontal(boolean horizontal) {
if(this.horizontal == horizontal)
return;
this.horizontal = horizontal;
if(horizontal) {
((LinearScale)scale).setOrientation(Orientation.HORIZONTAL);
scale.setTickLabelSide(LabelSide.Primary);
marker.setLabelSide(LabelSide.Secondary);
}else {
((LinearScale)scale).setOrientation(Orientation.VERTICAL);
scale.setTickLabelSide(LabelSide.Secondary);
marker.setLabelSide(LabelSide.Primary);
}
revalidate();
}
public void setPageIncrement(double pageIncrement) {
this.pageIncrement = pageIncrement;
}
/**
* @param stepIncrement the stepIncrement to set
*/
public void setStepIncrement(double stepIncrement) {
this.stepIncrement = stepIncrement;
}
/**
* @param thumbColor the thumbColor to set
*/
public void setThumbColor(Color thumbColor) {
if(this.thumbColor != null && this.thumbColor.equals(thumbColor))
return;
this.thumbColor = thumbColor;
repaint();
}
@Override
public void setValue(double value) {
super.setValue(value);
revalidate();
}
public void stepDown(){
manualSetValue(getValue() - stepIncrement);
fireManualValueChange(getValue());
}
public void stepUp(){
manualSetValue(getValue() + stepIncrement);
fireManualValueChange(getValue());
}
class Thumb extends Polygon {
class ThumbDragger
extends MouseMotionListener.Stub
implements MouseListener {
protected Point start;
protected boolean armed;
@Override
public void mouseDoubleClicked(MouseEvent me) {
}
@Override
public void mouseDragged(MouseEvent me) {
if (!armed)
return;
Dimension difference = me.getLocation().getDifference(start);
double valueChange = calcValueChange(difference, value);
double oldValue = value;
if(stepIncrement <= 0 || Math.abs(valueChange) > stepIncrement/2.0) {
if(stepIncrement > 0)
manualSetValue(value + stepIncrement * Math.round(valueChange/stepIncrement));
else
manualSetValue(value + valueChange);
label.setVisible(true);
double valuePosition =
((LinearScale)scale).getValuePosition(getCoercedValue(), false);
if(value != oldValue){
fireManualValueChange(value);
}
start = new Point(
horizontal? valuePosition: 0,
horizontal ? 0 : valuePosition);
}
me.consume();
}
@Override
public void mouseEntered(MouseEvent me) {
temp = thumbColor;
thumbColor = GREEN_COLOR;
repaint();
}
@Override
public void mouseExited(MouseEvent me) {
thumbColor = temp;
label.setVisible(false);
repaint();
}
@Override
public void mousePressed(MouseEvent me) {
if(me.button != 1)
return;
armed = true;
double valuePosition =
((LinearScale)scale).getValuePosition(getCoercedValue(), false);
start = new Point(
horizontal? valuePosition: 0,
horizontal ? 0 : valuePosition);
label.setVisible(true);
if(!ScaledSliderFigure.this.hasFocus()){
ScaledSliderFigure.this.requestFocus();
}
me.consume();
}
@Override
public void mouseReleased(MouseEvent me) {
if(me.button != 1)
return;
if (!armed)
return;
armed = false;
me.consume();
}
}
public static final int LENGTH = 20;
public static final int BREADTH = 11;
public final PointList horizontalThumbPointList = new PointList(new int[] {
0,0, 0, BREADTH, LENGTH*4/5, BREADTH, LENGTH, BREADTH/2,
LENGTH*4/5, 0}) ;
public final PointList verticalThumbPointList = new PointList(new int[] {
0,0, 0, LENGTH*4/5, BREADTH/2, LENGTH, BREADTH, LENGTH*4/5, BREADTH,
0}) ;
private Color temp;
public Thumb() {
super();
setOutline(true);
setFill(true);
setCursor(Cursors.HAND);
setForegroundColor(GRAY_COLOR);
setLineWidth(1);
ThumbDragger thumbDragger = new ThumbDragger();
addMouseMotionListener(thumbDragger);
addMouseListener(thumbDragger);
}
@Override
protected void fillShape(Graphics g) {
g.setAntialias(SWT.ON);
g.setClip(new Rectangle(getBounds().x, getBounds().y, getBounds().width, getBounds().height));
g.setBackgroundColor(WHITE_COLOR);
super.fillShape(g);
Point leftPoint = getPoints().getPoint(0);
Point rightPoint;
if(horizontal)
rightPoint = getPoints().getPoint(4);
else
rightPoint = getPoints().getPoint(1);//.translate(0, -BREADTH/2);
Pattern thumbPattern = null;
boolean support3D = GraphicsUtil.testPatternSupported(g);
if(effect3D && support3D) {
thumbPattern = new Pattern(Display.getCurrent(),
leftPoint.x, leftPoint.y, rightPoint.x, rightPoint.y, WHITE_COLOR, 0,
thumbColor, 255);
g.setBackgroundPattern(thumbPattern);
}else
g.setBackgroundColor(thumbColor);
g.fillPolygon(getPoints());
if(effect3D && support3D)
thumbPattern.dispose();
}
}
class Track extends RectangleFigure {
public static final int TRACK_BREADTH = 6;
private RepeatFiringBehavior behavior = new RepeatFiringBehavior();
private double pressedValue;
private boolean pageUp;
public Track() {
super();
setOutline(false);
setForegroundColor(GRAY_COLOR);
setCursor(Cursors.HAND);
behavior.setRunTask(new Runnable() {
@Override
public void run() {
if(pageUp){
if(getValue() >=pressedValue)
behavior.suspend();
else
pageUp();
}else{
if(getValue() <= pressedValue)
behavior.suspend();
else
pageDown();
}
}
});
addMouseListener(new MouseListener.Stub(){
@Override
public void mousePressed(MouseEvent me) {
if(me.button != 1)
return;
Point start = me.getLocation();
if(horizontal)
start.x = start.x + thumb.getBounds().width/2;
else
start.y = start.y + thumb.getBounds().height/2;
pressedValue = ((LinearScale)scale).getPositionValue(horizontal? start.x : start.y, false);
if(pressedValue > getValue())
pageUp = true;
else
pageUp = false;
behavior.pressed();
if(!ScaledSliderFigure.this.hasFocus()){
ScaledSliderFigure.this.requestFocus();
}
me.consume();
}
@Override
public void mouseReleased(MouseEvent me) {
if(me.button != 1)
return;
behavior.released();
}
});
}
@Override
protected void fillShape(Graphics graphics) {
graphics.setAntialias(SWT.ON);
int valuePosition = ((LinearScale) scale).getValuePosition(getCoercedValue(), false);
boolean support3D = GraphicsUtil.testPatternSupported(graphics);
if(effect3D && support3D) {
//fill background
graphics.setBackgroundColor(fillBackgroundColor);
super.fillShape(graphics);
Pattern backGroundPattern;
if(horizontal)
backGroundPattern= new Pattern(Display.getCurrent(),
bounds.x, bounds.y,
bounds.x, bounds.y + bounds.height,
WHITE_COLOR, 255,
fillBackgroundColor, 0);
else
backGroundPattern= new Pattern(Display.getCurrent(),
bounds.x, bounds.y,
bounds.x + bounds.width, bounds.y,
WHITE_COLOR, 255,
fillBackgroundColor, 0);
graphics.setBackgroundPattern(backGroundPattern);
super.fillShape(graphics);
graphics.setForegroundColor(fillBackgroundColor);
outlineShape(graphics);
backGroundPattern.dispose();
//fill value
if(horizontal)
backGroundPattern = new Pattern(Display.getCurrent(),
bounds.x, bounds.y,
bounds.x, bounds.y + bounds.height,
WHITE_COLOR, 255,
fillColor, 0);
else
backGroundPattern = new Pattern(Display.getCurrent(),
bounds.x, bounds.y,
bounds.x + bounds.width, bounds.y,
WHITE_COLOR, 255,
fillColor, 0);
graphics.setBackgroundColor(fillColor);
graphics.setForegroundColor(fillColor);
if(horizontal){
int fillWidth = valuePosition - bounds.x;
graphics.fillRectangle(new Rectangle(bounds.x,
bounds.y, fillWidth, bounds.height));
graphics.setBackgroundPattern(backGroundPattern);
graphics.fillRectangle(new Rectangle(bounds.x,
bounds.y, fillWidth, bounds.height));
graphics.drawRectangle(new Rectangle(bounds.x + lineWidth / 2,
bounds.y + lineWidth / 2,
fillWidth - Math.max(1, lineWidth),
bounds.height - Math.max(1, lineWidth)));
}else {
int fillHeight = bounds.height - (valuePosition - bounds.y);
graphics.fillRectangle(new Rectangle(bounds.x,
valuePosition, bounds.width, fillHeight));
graphics.setBackgroundPattern(backGroundPattern);
graphics.fillRectangle(new Rectangle(bounds.x,
valuePosition, bounds.width, fillHeight));
graphics.drawRectangle(new Rectangle(bounds.x + lineWidth / 2,
valuePosition+ lineWidth / 2,
bounds.width- Math.max(1, lineWidth),
fillHeight - Math.max(1, lineWidth)));
}
backGroundPattern.dispose();
}else {
graphics.setBackgroundColor(fillBackgroundColor);
super.fillShape(graphics);
graphics.setBackgroundColor(fillColor);
if(horizontal)
graphics.fillRectangle(new Rectangle(bounds.x,
bounds.y,
valuePosition - bounds.x,
bounds.height));
else
graphics.fillRectangle(new Rectangle(bounds.x,
valuePosition,
bounds.width,
bounds.height - (valuePosition - bounds.y)));
// graphics.setForegroundColor(outlineColor);
}
}
}
class XSliderLayout extends AbstractLayout {
private static final int GAP_BTW_THUMB_SCALE = 5;
private static final int ADDITIONAL_MARGIN = 3;
private static final int LABEL_MARGIN = 3;
/** Used as a constraint for the scale. */
public static final String SCALE = "scale"; //$NON-NLS-1$
/** Used as a constraint for the pipe indicator. */
public static final String TRACK = "track"; //$NON-NLS-1$
/** Used as a constraint for the alarm ticks */
public static final String MARKERS = "markers"; //$NON-NLS-1$
/** Used as a constraint for the thumb */
public static final String THUMB = "thumb"; //$NON-NLS-1$
/** Used as a constraint for the label*/
public static final String LABEL = "label"; //$NON-NLS-1$
private LinearScale scale;
private LinearScaledMarker marker;
private Track track;
private Thumb thumb;
private AlphaLabel label;
@Override
protected Dimension calculatePreferredSize(IFigure container, int w,
int h) {
Insets insets = container.getInsets();
Dimension d = new Dimension(64, 4*64);
d.expand(insets.getWidth(), insets.getHeight());
return d;
}
private void horizontalLayout(IFigure container) {
Rectangle area = container.getClientArea().getCopy();
area.x += ADDITIONAL_MARGIN;
area.width -= 2*ADDITIONAL_MARGIN;
Dimension scaleSize;
Dimension markerSize;
if(scale != null) {
scaleSize = new Dimension(area.width, 0);
if(scale.isVisible())
scaleSize = scale.getPreferredSize(area.width, -1);
scale.setBounds(new Rectangle(area.x,
area.y + area.height/2 + Thumb.LENGTH/2 + GAP_BTW_THUMB_SCALE,
scaleSize.width, scaleSize.height));
}
if(marker != null && marker.isVisible()) {
markerSize = marker.getPreferredSize();
marker.setBounds(new Rectangle(marker.getScale().getBounds().x,
area.y + area.height/2 - markerSize.height - Thumb.LENGTH/2 - GAP_BTW_THUMB_SCALE,
markerSize.width, markerSize.height));
}
if(track != null) {
track.setBounds(new Rectangle(
scale.getValuePosition(scale.getRange().getLower(), false) - track.getLineWidth(),
area.y + area.height/2 - Track.TRACK_BREADTH/2,
scale.getTickLength()+ 2*track.getLineWidth(),
Track.TRACK_BREADTH));
}
if(thumb != null) {
PointList newPointList = thumb.verticalThumbPointList.getCopy();
newPointList.translate(scale.getValuePosition(getCoercedValue(), false) - Thumb.BREADTH/2,
area.y + area.height/2 - Thumb.LENGTH/2 + 1
);
thumb.setPoints(newPointList);
}
if(label != null && label.isVisible())
setLabel();
}
@Override
public void layout(IFigure container) {
if(horizontal)
horizontalLayout(container);
else
verticalLayout(container);
}
@Override
public void setConstraint(IFigure child, Object constraint) {
if(constraint.equals(SCALE))
scale = (LinearScale)child;
else if (constraint.equals(MARKERS))
marker = (LinearScaledMarker) child;
else if (constraint.equals(TRACK))
track = (Track) child;
else if (constraint.equals(THUMB))
thumb = (Thumb) child;
else if (constraint.equals(LABEL))
label = (AlphaLabel) child;
}
private void setLabel() {
String text = getValueText();
Dimension textSize = FigureUtilities.getStringExtents(text, label.getFont());
label.setText(text);
if(horizontal)
label.setBounds(new Rectangle(
thumb.getBounds().x + thumb.getBounds().width/2
- (textSize.width + 2*LABEL_MARGIN)/2,
thumb.getBounds().y - textSize.height - 2*LABEL_MARGIN,
textSize.width + 2 * LABEL_MARGIN, textSize.height+LABEL_MARGIN));
else
label.setBounds(new Rectangle(
thumb.getBounds().x - textSize.width - 3*LABEL_MARGIN,
thumb.getBounds().y + thumb.getBounds().height/2
- (textSize.height + LABEL_MARGIN)/2,
textSize.width + 2 * LABEL_MARGIN, textSize.height+LABEL_MARGIN));
}
private void verticalLayout(IFigure container) {
Rectangle area = container.getClientArea();
Dimension scaleSize;
Dimension markerSize;
if(scale != null) {
scaleSize = new Dimension(0, area.height);
if(scale.isVisible())
scaleSize = scale.getPreferredSize(-1, area.height);
scale.setBounds(new Rectangle(area.x + area.width/2 + Thumb.LENGTH/2 + GAP_BTW_THUMB_SCALE,
area.y,
scaleSize.width, scaleSize.height));
}
if(marker != null && marker.isVisible()) {
markerSize = marker.getPreferredSize();
marker.setBounds(new Rectangle(
area.x + area.width/2 - markerSize.width - Thumb.LENGTH/2 - GAP_BTW_THUMB_SCALE,
marker.getScale().getBounds().y, markerSize.width, markerSize.height));
}
if(track != null) {
track.setBounds(new Rectangle(
area.x + area.width/2 - Track.TRACK_BREADTH/2,
scale.getValuePosition(scale.getRange().getUpper(), false) - track.getLineWidth(),
Track.TRACK_BREADTH,
scale.getTickLength()+ 2*track.getLineWidth()));
}
if(thumb != null) {
PointList newPointList = thumb.horizontalThumbPointList.getCopy();
newPointList.translate(area.x + area.width/2 - Thumb.LENGTH/2 + 1,
scale.getValuePosition(getCoercedValue(), false) - Thumb.BREADTH/2);
thumb.setPoints(newPointList);
}
if(label != null && label.isVisible())
setLabel();
}
}
}