/**
   * Initializes the ScrollBarSkin. Creates the scene and sets up all the bindings for the group.
   */
  private void initialize() {

    track = new StackPane();
    track.getStyleClass().setAll("track");

    trackBackground = new StackPane();
    trackBackground.getStyleClass().setAll("track-background");

    thumb =
        new StackPane() {
          @Override
          public Object queryAccessibleAttribute(
              AccessibleAttribute attribute, Object... parameters) {
            switch (attribute) {
              case VALUE:
                return getSkinnable().getValue();
              default:
                return super.queryAccessibleAttribute(attribute, parameters);
            }
          }
        };
    thumb.getStyleClass().setAll("thumb");
    thumb.setAccessibleRole(AccessibleRole.THUMB);

    if (!IS_TOUCH_SUPPORTED) {

      incButton =
          new EndButton("increment-button", "increment-arrow") {
            @Override
            public void executeAccessibleAction(AccessibleAction action, Object... parameters) {
              switch (action) {
                case FIRE:
                  getSkinnable().increment();
                  break;
                default:
                  super.executeAccessibleAction(action, parameters);
              }
            }
          };
      incButton.setAccessibleRole(AccessibleRole.INCREMENT_BUTTON);
      incButton.setOnMousePressed(
          me -> {
            /*
             ** if the tracklenght isn't greater than do nothing....
             */
            if (!thumb.isVisible() || trackLength > thumbLength) {
              getBehavior().incButtonPressed();
            }
            me.consume();
          });
      incButton.setOnMouseReleased(
          me -> {
            /*
             ** if the tracklenght isn't greater than do nothing....
             */
            if (!thumb.isVisible() || trackLength > thumbLength) {
              getBehavior().incButtonReleased();
            }
            me.consume();
          });

      decButton =
          new EndButton("decrement-button", "decrement-arrow") {
            @Override
            public void executeAccessibleAction(AccessibleAction action, Object... parameters) {
              switch (action) {
                case FIRE:
                  getSkinnable().decrement();
                  break;
                default:
                  super.executeAccessibleAction(action, parameters);
              }
            }
          };
      decButton.setAccessibleRole(AccessibleRole.DECREMENT_BUTTON);
      decButton.setOnMousePressed(
          me -> {
            /*
             ** if the tracklenght isn't greater than do nothing....
             */
            if (!thumb.isVisible() || trackLength > thumbLength) {
              getBehavior().decButtonPressed();
            }
            me.consume();
          });
      decButton.setOnMouseReleased(
          me -> {
            /*
             ** if the tracklenght isn't greater than do nothing....
             */
            if (!thumb.isVisible() || trackLength > thumbLength) {
              getBehavior().decButtonReleased();
            }
            me.consume();
          });
    }

    track.setOnMousePressed(
        me -> {
          if (!thumb.isPressed() && me.getButton() == MouseButton.PRIMARY) {
            if (getSkinnable().getOrientation() == Orientation.VERTICAL) {
              if (trackLength != 0) {
                getBehavior().trackPress(me.getY() / trackLength);
                me.consume();
              }
            } else {
              if (trackLength != 0) {
                getBehavior().trackPress(me.getX() / trackLength);
                me.consume();
              }
            }
          }
        });

    track.setOnMouseReleased(
        me -> {
          getBehavior().trackRelease();
          me.consume();
        });

    thumb.setOnMousePressed(
        me -> {
          if (me.isSynthesized()) {
            // touch-screen events handled by Scroll handler
            me.consume();
            return;
          }
          /*
           ** if max isn't greater than min then there is nothing to do here
           */
          if (getSkinnable().getMax() > getSkinnable().getMin()) {
            dragStart = thumb.localToParent(me.getX(), me.getY());
            double clampedValue =
                Utils.clamp(
                    getSkinnable().getMin(), getSkinnable().getValue(), getSkinnable().getMax());
            preDragThumbPos =
                (clampedValue - getSkinnable().getMin())
                    / (getSkinnable().getMax() - getSkinnable().getMin());
            me.consume();
          }
        });

    thumb.setOnMouseDragged(
        me -> {
          if (me.isSynthesized()) {
            // touch-screen events handled by Scroll handler
            me.consume();
            return;
          }
          /*
           ** if max isn't greater than min then there is nothing to do here
           */
          if (getSkinnable().getMax() > getSkinnable().getMin()) {
            /*
             ** if the tracklength isn't greater then do nothing....
             */
            if (trackLength > thumbLength) {
              Point2D cur = thumb.localToParent(me.getX(), me.getY());
              if (dragStart == null) {
                // we're getting dragged without getting a mouse press
                dragStart = thumb.localToParent(me.getX(), me.getY());
              }
              double dragPos =
                  getSkinnable().getOrientation() == Orientation.VERTICAL
                      ? cur.getY() - dragStart.getY()
                      : cur.getX() - dragStart.getX();
              getBehavior().thumbDragged(preDragThumbPos + dragPos / (trackLength - thumbLength));
            }

            me.consume();
          }
        });

    thumb.setOnScrollStarted(
        se -> {
          if (se.isDirect()) {
            /*
             ** if max isn't greater than min then there is nothing to do here
             */
            if (getSkinnable().getMax() > getSkinnable().getMin()) {
              dragStart = thumb.localToParent(se.getX(), se.getY());
              double clampedValue =
                  Utils.clamp(
                      getSkinnable().getMin(), getSkinnable().getValue(), getSkinnable().getMax());
              preDragThumbPos =
                  (clampedValue - getSkinnable().getMin())
                      / (getSkinnable().getMax() - getSkinnable().getMin());
              se.consume();
            }
          }
        });

    thumb.setOnScroll(
        event -> {
          if (event.isDirect()) {
            /*
             ** if max isn't greater than min then there is nothing to do here
             */
            if (getSkinnable().getMax() > getSkinnable().getMin()) {
              /*
               ** if the tracklength isn't greater then do nothing....
               */
              if (trackLength > thumbLength) {
                Point2D cur = thumb.localToParent(event.getX(), event.getY());
                if (dragStart == null) {
                  // we're getting dragged without getting a mouse press
                  dragStart = thumb.localToParent(event.getX(), event.getY());
                }
                double dragPos =
                    getSkinnable().getOrientation() == Orientation.VERTICAL
                        ? cur.getY() - dragStart.getY()
                        : cur.getX() - dragStart.getX();
                getBehavior()
                    .thumbDragged(/*todo*/ preDragThumbPos + dragPos / (trackLength - thumbLength));
              }

              event.consume();
              return;
            }
          }
        });

    getSkinnable()
        .addEventHandler(
            ScrollEvent.SCROLL,
            event -> {
              /*
               ** if the tracklength isn't greater then do nothing....
               */
              if (trackLength > thumbLength) {

                double dx = event.getDeltaX();
                double dy = event.getDeltaY();

                /*
                 ** in 2.0 a horizontal scrollbar would scroll on a vertical
                 ** drag on a tracker-pad. We need to keep this behavior.
                 */
                dx = (Math.abs(dx) < Math.abs(dy) ? dy : dx);

                /*
                 ** we only consume an event that we've used.
                 */
                ScrollBar sb = (ScrollBar) getSkinnable();

                double delta = (getSkinnable().getOrientation() == Orientation.VERTICAL ? dy : dx);

                /*
                 ** RT-22941 - If this is either a touch or inertia scroll
                 ** then we move to the position of the touch point.
                 *
                 * TODO: this fix causes RT-23406 ([ScrollBar, touch] Dragging scrollbar from the
                 * track on touchscreen causes flickering)
                 */
                if (event.isDirect()) {
                  if (trackLength > thumbLength) {
                    getBehavior()
                        .thumbDragged(
                            (getSkinnable().getOrientation() == Orientation.VERTICAL
                                    ? event.getY()
                                    : event.getX())
                                / trackLength);
                    event.consume();
                  }
                } else {
                  if (delta > 0.0 && sb.getValue() > sb.getMin()) {
                    sb.decrement();
                    event.consume();
                  } else if (delta < 0.0 && sb.getValue() < sb.getMax()) {
                    sb.increment();
                    event.consume();
                  }
                }
              }
            });

    getChildren().clear();
    if (!IS_TOUCH_SUPPORTED) {
      getChildren().addAll(trackBackground, incButton, decButton, track, thumb);
    } else {
      getChildren().addAll(track, thumb);
    }
  }