com.bearsoft.gwt.ui.widgets.progress.SliderBar.java Source code

Java tutorial

Introduction

Here is the source code for com.bearsoft.gwt.ui.widgets.progress.SliderBar.java

Source

/*
 * Copyright 2008 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.bearsoft.gwt.ui.widgets.progress;

import com.bearsoft.gwt.ui.XElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.HasEnabled;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.RequiresResize;

import java.util.ArrayList;
import java.util.List;

/**
 * A widget that allows the user to select a value within a range of possible
 * values using a sliding bar that responds to mouse events.
 *
 * <h3>Keyboard Events</h3>
 * <p>
 * SliderBar listens for the following key events. Holding down a key will
 * repeat the action until the key is released. <ul class='css'>
 * <li>left arrow - shift left one step</li>
 * <li>right arrow - shift right one step</li>
 * <li>ctrl+left arrow - jump left 10% of the distance</li>
 * <li>ctrl+right arrow - jump right 10% of the distance</li>
 * <li>home - jump to min value</li>
 * <li>end - jump to max value</li>
 * <li>space - jump to middle value</li>
 * </ul>
 * </p>
 *
 * <h3>CSS Style Rules</h3> <ul class='css'> <li>.gwt-SliderBar-shell { primary
 * style }</li> <li>.gwt-SliderBar-shell-focused { primary style when focused
 * }</li>
 * <li>.gwt-SliderBar-shell gwt-SliderBar-line { the line that the knob moves
 * along }</li> <li>.gwt-SliderBar-shell gwt-SliderBar-line-sliding { the line
 * that the knob moves along when sliding }</li> <li>.gwt-SliderBar-shell
 * .gwt-SliderBar-knob { the sliding knob }</li> <li>.gwt-SliderBar-shell
 * .gwt-SliderBar-knob-sliding { the sliding knob when sliding }</li> <li>
 * .gwt-SliderBar-shell .gwt-SliderBar-tick { the ticks along the line }</li>
 * <li>.gwt-SliderBar-shell .gwt-SliderBar-label { the text labels along the
 * line }</li> </ul>
 */
public class SliderBar extends FocusPanel implements RequiresResize, HasValue<Double>, HasEnabled {

    /**
     * The timer used to continue to shift the knob as the user holds down one
     * of the left/right arrow keys. Only IE auto-repeats, so we just keep
     * catching the events.
     */
    private class KeyTimer extends Timer {

        /**
         * A bit indicating that this is the first run.
         */
        private boolean firstRun = true;

        /**
         * The delay between shifts, which shortens as the user holds down the
         * button.
         */
        private int repeatDelay = 30;

        /**
         * A bit indicating whether we are shifting to a higher or lower value.
         */
        private boolean shiftRight;

        /**
         * The number of steps to shift with each press.
         */
        private int multiplier = 1;

        /**
         * This method will be called when a timer fires. Override it to
         * implement the timer's logic.
         */
        @Override
        public void run() {
            // Highlight the knob on first run
            if (firstRun) {
                firstRun = false;
                startSliding(true);
            }

            // Slide the slider bar
            if (shiftRight) {
                setValue((value != null ? value : 0) + multiplier * stepSize);
            } else {
                setValue((value != null ? value : 0) - multiplier * stepSize);
            }

            // Repeat this timer until cancelled by keyup event
            schedule(repeatDelay);
        }

        /**
         * Schedules a timer to elapse in the future.
         *
         * @param delayMillis how long to wait before the timer elapses, in
         * milliseconds
         * @param aShiftRight whether to shift up or not
         * @param aMultiplier the number of steps to shift
         */
        public void schedule(int delayMillis, boolean aShiftRight, int aMultiplier) {
            firstRun = true;
            shiftRight = aShiftRight;
            multiplier = aMultiplier;
            super.schedule(delayMillis);
        }
    }

    /**
     * A formatter used to format the labels displayed in the widget.
     */
    public static interface LabelFormatter {

        /**
         * Generate the text to display in each label based on the label's
         * value.
         *
         * Override this method to change the text displayed within the
         * SliderBar.
         *
         * @param slider the Slider bar
         * @param value the value the label displays
         * @return the text to display for the label
         */
        String formatLabel(SliderBar slider, double value);
    }

    /**
     * The current value.
     */
    private Double value;

    /**
     * The knob that slides across the line.
     */
    private final Element knobElement = Document.get().createDivElement();

    /**
     * The timer used to continue to shift the knob if the user holds down a
     * key.
     */
    private final KeyTimer keyTimer = new KeyTimer();

    /**
     * The elements used to display labels above the ticks.
     */
    private final List<Element> labelElements = new ArrayList<>();

    /**
     * The formatter used to generate label text.
     */
    private LabelFormatter labelFormatter;

    /**
     * The line that the knob moves over.
     */
    private Element lineElement;

    /**
     * The area of a line that lies before the knob.
     */
    private Element coverElement;

    /**
     * The offset between the edge of the shell and the line.
     */
    private int lineLeftOffset;

    /**
     * The maximum slider value.
     */
    private double maxValue;

    /**
     * The minimum slider value.
     */
    private double minValue;

    /**
     * The number of labels to show.
     */
    private int numLabels;

    /**
     * The number of tick marks to show.
     */
    private int numTicks;

    /**
     * A bit indicating whether or not we are currently sliding the slider bar
     * due to keyboard events.
     */
    private boolean slidingKeyboard;

    /**
     * A bit indicating whether or not we are currently sliding the slider bar
     * due to mouse events.
     */
    private boolean slidingMouse;

    /**
     * A bit indicating whether or not the slider is enabled
     */
    private boolean enabled = true;

    /**
     * The size of the increments between knob positions.
     */
    private double stepSize = 1.0;

    /**
     * The elements used to display tick marks, which are the vertical lines
     * along the slider bar.
     */
    private final List<Element> tickElements = new ArrayList<>();

    /**
     * Create a slider bar.
     *
     * @param aMinValue the minimum value in the range
     * @param aMaxValue the maximum value in the range
     */
    public SliderBar(double aMinValue, double aMaxValue) {
        this(aMinValue, aMaxValue, null);
    }

    /**
     * Create a slider bar.
     *
     * @param aMinValue the minimum value in the range
     * @param aMaxValue the maximum value in the range
     * @param aLabelFormatter the label formatter
     */
    public SliderBar(double aMinValue, double aMaxValue, LabelFormatter aLabelFormatter) {
        super();
        minValue = aMinValue;
        maxValue = aMaxValue;
        setLabelFormatter(aLabelFormatter);

        // Create the outer shell
        getElement().getStyle().setDisplay(Style.Display.INLINE_BLOCK);
        getElement().getStyle().setPosition(Style.Position.RELATIVE);
        // default preferred size
        getElement().getStyle().setWidth(150, Style.Unit.PX);
        getElement().getStyle().setHeight(35, Style.Unit.PX);
        setStyleName("gwt-SliderBar-shell");

        // Create the line
        lineElement = DOM.createDiv();
        getElement().appendChild(lineElement);
        lineElement.getStyle().setPosition(Style.Position.ABSOLUTE);
        lineElement.setClassName("gwt-SliderBar-line");
        coverElement = DOM.createDiv();
        lineElement.appendChild(coverElement);
        coverElement.getStyle().setPosition(Style.Position.ABSOLUTE);
        coverElement.getStyle().setLeft(0, Style.Unit.PX);
        coverElement.getStyle().setTop(0, Style.Unit.PX);
        coverElement.getStyle().setBottom(0, Style.Unit.PX);
        coverElement.setClassName("gwt-SliderBar-line-before-knob");

        // Create the knob
        knobElement.getStyle().setDisplay(Style.Display.INLINE_BLOCK);
        knobElement.getStyle().setPosition(Style.Position.ABSOLUTE);
        knobElement.setClassName("gwt-SliderBar-knob");
        knobElement.addClassName("gwt-SliderBar-knob-enabled");
        getElement().appendChild(knobElement);

        sinkEvents(Event.MOUSEEVENTS | Event.KEYEVENTS | Event.FOCUSEVENTS);
        getElement().<XElement>cast().addResizingTransitionEnd(this);
    }

    /**
     * Return the current value.
     *
     * @return the current value
     */
    @Override
    public Double getValue() {
        return value;
    }

    /**
     * Return the label formatter.
     *
     * @return the label formatter
     */
    public LabelFormatter getLabelFormatter() {
        return labelFormatter;
    }

    /**
     * Return the max value.
     *
     * @return the max value
     */
    public double getMaxValue() {
        return maxValue;
    }

    /**
     * Return the minimum value.
     *
     * @return the minimum value
     */
    public double getMinValue() {
        return minValue;
    }

    /**
     * Return the number of labels.
     *
     * @return the number of labels
     */
    public int getNumLabels() {
        return numLabels;
    }

    /**
     * Return the number of ticks.
     *
     * @return the number of ticks
     */
    public int getNumTicks() {
        return numTicks;
    }

    /**
     * Return the step size.
     *
     * @return the step size
     */
    public double getStepSize() {
        return stepSize;
    }

    /**
     * Return the total range between the minimum and maximum values.
     *
     * @return the total range
     */
    public double getTotalRange() {
        if (minValue > maxValue) {
            return 0;
        } else {
            return maxValue - minValue;
        }
    }

    /**
     * @return Gets whether this widget is enabled
     */
    @Override
    public boolean isEnabled() {
        return enabled;
    }

    /**
     * Listen for events that will move the knob.
     *
     * @param event the event that occurred
     */
    @Override
    public void onBrowserEvent(Event event) {
        super.onBrowserEvent(event);
        if (enabled) {
            switch (DOM.eventGetType(event)) {
            // Unhighlight and cancel keyboard events
            case Event.ONBLUR:
                keyTimer.cancel();
                if (slidingMouse) {
                    DOM.releaseCapture(getElement());
                    slidingMouse = false;
                    slideKnob(event);
                    stopSliding(true);
                } else if (slidingKeyboard) {
                    slidingKeyboard = false;
                    stopSliding(true);
                }
                unhighlightFocus();
                break;

            // Highlight on focus
            case Event.ONFOCUS:
                highlightFocus();
                break;

            // Mousewheel events
            case Event.ONMOUSEWHEEL:
                int velocityY = event.getMouseWheelVelocityY();
                event.preventDefault();
                if (velocityY > 0) {
                    shiftRight(1);
                } else {
                    shiftLeft(1);
                }
                break;

            // Shift left or right on key press
            case Event.ONKEYDOWN:
                if (!slidingKeyboard) {
                    int multiplier = 1;
                    if (event.getCtrlKey()) {
                        multiplier = (int) (getTotalRange() / stepSize / 10);
                    }

                    switch (event.getKeyCode()) {
                    case KeyCodes.KEY_HOME:
                        event.preventDefault();
                        setValue(minValue, true);
                        break;
                    case KeyCodes.KEY_END:
                        event.preventDefault();
                        setValue(maxValue, true);
                        break;
                    case KeyCodes.KEY_LEFT:
                        event.preventDefault();
                        slidingKeyboard = true;
                        startSliding(false);
                        shiftLeft(multiplier);
                        keyTimer.schedule(400, false, multiplier);
                        break;
                    case KeyCodes.KEY_RIGHT:
                        event.preventDefault();
                        slidingKeyboard = true;
                        startSliding(false);
                        shiftRight(multiplier);
                        keyTimer.schedule(400, true, multiplier);
                        break;
                    case KeyCodes.KEY_SPACE:
                        event.preventDefault();
                        setValue(minValue + getTotalRange() / 2, true);
                        break;
                    }
                }
                break;
            // Stop shifting on key up
            case Event.ONKEYUP:
                keyTimer.cancel();
                if (slidingKeyboard) {
                    slidingKeyboard = false;
                    stopSliding(true);
                }
                break;

            // Mouse Events
            case Event.ONMOUSEDOWN:
                setFocus(true);
                slidingMouse = true;
                DOM.setCapture(getElement());
                startSliding(true);
                event.preventDefault();
                //slideKnob(event);
                break;
            case Event.ONMOUSEUP:
                if (slidingMouse) {
                    DOM.releaseCapture(getElement());
                    slidingMouse = false;
                    slideKnob(event);
                    stopSliding(true);
                }
                break;
            case Event.ONMOUSEMOVE:
                if (slidingMouse) {
                    slideKnob(event);
                }
                break;
            }
        }
    }

    /**
     * This method is called when the dimensions of the parent element change.
     * Subclasses should override this method as needed.
     */
    @Override
    public void onResize() {
        // Center the line in the shell
        int width = getElement().getClientWidth();
        int height = getElement().getClientHeight();
        int lineWidth = lineElement.getOffsetWidth();
        int lineHeight = lineElement.getOffsetHeight();
        lineLeftOffset = (width / 2) - (lineWidth / 2);
        lineElement.getStyle().setLeft(lineLeftOffset, Style.Unit.PX);
        lineElement.getStyle().setTop((height - lineHeight) / 2, Style.Unit.PX);
        int knobHeight = knobElement.getOffsetHeight();
        knobElement.getStyle().setTop((height - knobHeight) / 2, Style.Unit.PX);
        // Draw the other components
        drawLabels();
        drawTicks();
        drawKnob();
    }

    /**
     * Redraw the progress bar when something changes the layout.
     */
    public void redraw() {
        if (isAttached()) {
            onResize();
        }
    }

    /**
     * Set the current value and fire the onValueChange event.
     *
     * @param aValue the current value
     */
    @Override
    public void setValue(Double aValue) {
        setValue(aValue, false);
    }

    /**
     * Set the current value and optionally fire the ValueChangeEvent.
     *
     * @param aValue the current value
     * @param fireEvents fire the onValue change events if true
     */
    @Override
    public void setValue(Double aValue, boolean fireEvents) {
        // Confine the value to the range
        value = Math.max(minValue, Math.min(maxValue, (aValue != null ? aValue : 0)));
        double remainder = (value - minValue) % stepSize;
        value -= remainder;

        // Go to next step if more than halfway there
        if ((remainder > (stepSize / 2)) && ((value + stepSize) <= maxValue)) {
            value += stepSize;
        }

        // Redraw the knob
        drawKnob();

        // Fire the onValueChange event
        if (fireEvents) {
            ValueChangeEvent.fire(SliderBar.this, getValue());
        }
    }

    @Override
    public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Double> handler) {
        return addHandler(handler, ValueChangeEvent.getType());
    }

    /**
     * Sets whether this widget is enabled.
     *
     * @param aValue true to enable the widget, false to disable it
     */
    @Override
    public void setEnabled(boolean aValue) {
        enabled = aValue;
        if (aValue) {
            knobElement.removeClassName("gwt-SliderBar-knob-disabled");
            lineElement.removeClassName("gwt-SliderBar-line-disabled");
            knobElement.addClassName("gwt-SliderBar-knob-enabled");
            lineElement.addClassName("gwt-SliderBar-line-enabled");
        } else {
            knobElement.removeClassName("gwt-SliderBar-knob-enabled");
            lineElement.removeClassName("gwt-SliderBar-line-enabled");
            knobElement.addClassName("gwt-SliderBar-knob-disabled");
            lineElement.addClassName("gwt-SliderBar-line-disabled");
        }
        redraw();
    }

    /**
     * Set the label formatter.
     *
     * @param aFormatter the label formatter
     */
    public void setLabelFormatter(LabelFormatter aFormatter) {
        labelFormatter = aFormatter;
    }

    /**
     * Set the max value.
     *
     * @param aValue the current value
     */
    public void setMaxValue(double aValue) {
        maxValue = aValue;
        drawLabels();
        resetCurrentValue();
    }

    /**
     * Set the minimum value.
     *
     * @param aValue the current value
     */
    public void setMinValue(double aValue) {
        minValue = aValue;
        drawLabels();
        resetCurrentValue();
    }

    /**
     * Set the number of labels to show on the line. Labels indicate the value
     * of the slider at that point. Use this method to enable labels.
     *
     * If you set the number of labels equal to the total range divided by the
     * step size, you will get a properly aligned "jumping" effect where the
     * knob jumps between labels.
     *
     * Note that the number of labels displayed will be one more than the number
     * you specify, so specify 1 labels to show labels on either end of the
     * line. In other words, numLabels is really the number of slots between the
     * labels.
     *
     * setNumLabels(0) will disable labels.
     *
     * @param aValue the number of labels to show
     */
    public void setNumLabels(int aValue) {
        numLabels = aValue;
        drawLabels();
    }

    /**
     * Set the number of ticks to show on the line. A tick is a vertical line
     * that represents a division of the overall line. Use this method to enable
     * ticks.
     *
     * If you set the number of ticks equal to the total range divided by the
     * step size, you will get a properly aligned "jumping" effect where the
     * knob jumps between ticks.
     *
     * Note that the number of ticks displayed will be one more than the number
     * you specify, so specify 1 tick to show ticks on either end of the line.
     * In other words, numTicks is really the number of slots between the ticks.
     *
     * setNumTicks(0) will disable ticks.
     *
     * @param aValue the number of ticks to show
     */
    public void setNumTicks(int aValue) {
        numTicks = aValue;
        drawTicks();
    }

    /**
     * Set the step size.
     *
     * @param aValue the current value
     */
    public void setStepSize(double aValue) {
        stepSize = aValue;
        resetCurrentValue();
    }

    /**
     * Shift to the left (smaller value).
     *
     * @param numSteps the number of steps to shift
     */
    public void shiftLeft(int numSteps) {
        Double oldValue = getValue();
        setValue((oldValue != null ? oldValue : 0) - numSteps * stepSize, true);
    }

    /**
     * Shift to the right (greater value).
     *
     * @param numSteps the number of steps to shift
     */
    public void shiftRight(int numSteps) {
        Double oldValue = getValue();
        setValue((oldValue != null ? oldValue : 0) + numSteps * stepSize, true);
    }

    /**
     * Format the label to display above the ticks
     *
     * Override this method in a subclass to customize the format. By default,
     * this method returns the integer portion of the value.
     *
     * @param value the value at the label
     * @return the text to put in the label
     */
    protected String formatLabel(double value) {
        if (labelFormatter != null) {
            return labelFormatter.formatLabel(this, value);
        } else {
            return (int) (10 * value) / 10.0 + "";
        }
    }

    /**
     * Get the percentage of the knob's position relative to the size of the
     * line. The return value will be between 0.0 and 1.0.
     *
     * @return the current percent complete
     */
    protected double getKnobPercent() {
        // If we have no range
        if (maxValue > minValue) {
            // Calculate the relative progress
            double percent = ((value != null ? value : 0) - minValue) / (maxValue - minValue);
            return Math.max(0.0, Math.min(1.0, percent));
        } else {
            return 0;
        }
    }

    @Override
    protected void onAttach() {
        super.onAttach();
        redraw();
    }

    /**
     * Draw the knob where it is supposed to be relative to the line.
     */
    private void drawKnob() {
        // Proceed only if attached
        if (isAttached()) {
            // Move the knob to the correct position
            int lineWidth = lineElement.getOffsetWidth();
            int knobWidth = knobElement.getOffsetWidth();
            int knobLeftOffset = (int) (lineLeftOffset + getKnobPercent() * lineWidth - knobWidth / 2);
            knobLeftOffset = Math.min(knobLeftOffset, lineLeftOffset + lineWidth - knobWidth / 2 - 1);
            knobElement.getStyle().setLeft(knobLeftOffset, Style.Unit.PX);
            coverElement.getStyle().setWidth(getKnobPercent() * lineWidth, Style.Unit.PX);
        }
    }

    /**
     * Draw the labels along the line.
     */
    private void drawLabels() {
        if (isAttached()) {
            // Draw the labels
            int lineWidth = lineElement.getOffsetWidth();
            if (numLabels > 0) {
                // Create the labels or make them visible
                for (int i = 0; i <= numLabels; i++) {
                    Element label;
                    if (i < labelElements.size()) {
                        label = labelElements.get(i);
                    } else { // Create the new label
                        label = DOM.createDiv();
                        label.addClassName("gwt-SliderBar-label");
                        label.getStyle().setPosition(Style.Position.ABSOLUTE);
                        label.getStyle().setDisplay(Style.Display.NONE);
                        getElement().appendChild(label);
                        labelElements.add(label);
                    }
                    if (enabled) {
                        label.removeClassName("gwt-SliderBar-label-disabled");
                        label.addClassName("gwt-SliderBar-label-enabled");
                    } else {
                        label.removeClassName("gwt-SliderBar-label-enabled");
                        label.addClassName("gwt-SliderBar-label-disabled");
                    }

                    // Set the label text
                    double value4Formatting = minValue + (getTotalRange() * i / numLabels);
                    label.getStyle().setVisibility(Style.Visibility.HIDDEN);
                    label.getStyle().clearDisplay();
                    label.setInnerHTML(formatLabel(value4Formatting));

                    // Move to the left so the label width is not clipped by the shell
                    label.getStyle().setLeft(0, Style.Unit.PX);

                    // Position the label and make it visible
                    int labelWidth = label.getOffsetWidth();
                    int labelLeftOffset = lineLeftOffset + (lineWidth * i / numLabels) - (labelWidth / 2);
                    labelLeftOffset = Math.min(labelLeftOffset, lineLeftOffset + lineWidth - labelWidth);
                    labelLeftOffset = Math.max(labelLeftOffset, lineLeftOffset);
                    label.getStyle().setLeft(labelLeftOffset, Style.Unit.PX);
                    label.getStyle().setVisibility(Style.Visibility.VISIBLE);
                }
                // Hide unused labels
                for (int i = (numLabels + 1); i < labelElements.size(); i++) {
                    labelElements.get(i).getStyle().setDisplay(Style.Display.NONE);
                }
            } else { // Hide all labels
                for (Element elem : labelElements) {
                    elem.getStyle().setDisplay(Style.Display.NONE);
                }
            }
        }
    }

    /**
     * Draw the tick along the line.
     */
    private void drawTicks() {
        // Abort if not attached
        if (isAttached()) {
            // Draw the ticks
            int lineWidth = lineElement.getOffsetWidth();
            if (numTicks > 0) {
                // Create the ticks or make them visible
                for (int i = 0; i <= numTicks; i++) {
                    Element tick;
                    if (i < tickElements.size()) {
                        tick = tickElements.get(i);
                    } else { // Create the new tick
                        tick = DOM.createDiv();
                        tick.addClassName("gwt-SliderBar-tick");
                        tick.getStyle().setPosition(Style.Position.ABSOLUTE);
                        tick.getStyle().setDisplay(Style.Display.NONE);
                        DOM.appendChild(getElement(), tick);
                        tickElements.add(tick);
                    }
                    // Position the tick and make it visible
                    tick.getStyle().setVisibility(Style.Visibility.HIDDEN);
                    tick.getStyle().clearDisplay();
                    int tickWidth = tick.getOffsetWidth();
                    int tickLeftOffset = lineLeftOffset + (lineWidth * i / numTicks) - (tickWidth / 2);
                    tickLeftOffset = Math.min(tickLeftOffset, lineLeftOffset + lineWidth - tickWidth);
                    tick.getStyle().setLeft(tickLeftOffset, Style.Unit.PX);
                    tick.getStyle().setVisibility(Style.Visibility.VISIBLE);
                    if (enabled) {
                        tick.removeClassName("gwt-SliderBar-tick-disabled");
                        tick.addClassName("gwt-SliderBar-tick-enabled");
                    } else {
                        tick.removeClassName("gwt-SliderBar-tick-enabled");
                        tick.addClassName("gwt-SliderBar-tick-disabled");
                    }
                }

                // Hide unused ticks
                for (int i = (numTicks + 1); i < tickElements.size(); i++) {
                    tickElements.get(i).getStyle().setDisplay(Style.Display.NONE);
                }
            } else { // Hide all ticks
                for (Element elem : tickElements) {
                    elem.getStyle().setDisplay(Style.Display.NONE);
                }
            }
        }
    }

    /**
     * Highlight this widget.
     */
    private void highlightFocus() {
        String styleName = getStylePrimaryName();
        getElement().addClassName(styleName + "-focused");
    }

    /**
     * Reset the progress to constrain the progress to the current range and
     * redraw the knob as needed.
     */
    private void resetCurrentValue() {
        setValue(getValue());
    }

    /**
     * Slide the knob to a new location.
     *
     * @param event the mouse event
     */
    private void slideKnob(Event event) {
        int x = event.getClientX();
        if (x > 0) {
            int lineWidth = lineElement.getOffsetWidth();
            int lineLeft = lineElement.getAbsoluteLeft();
            double percent = (double) (x - lineLeft) / lineWidth * 1.0;
            setValue(getTotalRange() * percent + minValue, true);
        }
    }

    /**
     * Start sliding the knob.
     *
     * @param highlight true to change the style
     * @param fireEvent true to fire the event
     */
    private void startSliding(boolean highlight) {
        if (highlight) {
            lineElement.addClassName("gwt-SliderBar-line-sliding");
            knobElement.addClassName("gwt-SliderBar-knob-sliding");
        }
    }

    /**
     * Stop sliding the knob.
     *
     * @param unhighlight true to change the style
     * @param fireEvent true to fire the event
     */
    private void stopSliding(boolean unhighlight) {
        if (unhighlight) {
            lineElement.removeClassName("gwt-SliderBar-line-sliding");
            knobElement.removeClassName("gwt-SliderBar-knob-sliding");
        }
    }

    /**
     * Unhighlight this widget.
     */
    private void unhighlightFocus() {
        String styleName = getStylePrimaryName();
        getElement().removeClassName(styleName + "-focused");
    }
}