UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

442 lines (379 loc) 12.2 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de License: MIT: https://opensource.org/licenses/MIT See the LICENSE file in the project's top-level directory for details. Authors: * Sebastian Werner (wpbasti) * Fabian Jakobs (fjakobs) ************************************************************************ */ /** * The scroll bar widget, is a special slider, which is used in qooxdoo instead * of the native browser scroll bars. * * Scroll bars are used by the {@link qx.ui.container.Scroll} container. Usually * a scroll bar is not used directly. * * @childControl slider {qx.ui.core.scroll.ScrollSlider} scroll slider component * @childControl button-begin {qx.ui.form.RepeatButton} button to scroll to top * @childControl button-end {qx.ui.form.RepeatButton} button to scroll to bottom * * *Example* * * Here is a little example of how to use the widget. * * <pre class='javascript'> * var scrollBar = new qx.ui.core.scroll.ScrollBar("horizontal"); * scrollBar.set({ * maximum: 500 * }) * this.getRoot().add(scrollBar); * </pre> * * This example creates a horizontal scroll bar with a maximum value of 500. * * *External Documentation* * * <a href='http://qooxdoo.org/docs/#desktop/widget/scrollbar.md' target='_blank'> * Documentation of this widget in the qooxdoo manual.</a> */ qx.Class.define("qx.ui.core.scroll.ScrollBar", { extend: qx.ui.core.Widget, implement: qx.ui.core.scroll.IScrollBar, /* ***************************************************************************** CONSTRUCTOR ***************************************************************************** */ /** * @param orientation {String?"horizontal"} The initial scroll bar orientation */ construct(orientation) { super(); // Create child controls this._createChildControl("button-begin"); this._createChildControl("slider").addListener( "resize", this._onResizeSlider, this ); this._createChildControl("button-end"); // Configure orientation if (orientation != null) { this.setOrientation(orientation); } else { this.initOrientation(); } // prevent drag & drop on scrolling this.addListener("track", e => { e.stopPropagation(); }); }, events: { /** Change event for the value. */ scrollAnimationEnd: "qx.event.type.Event" }, /* ***************************************************************************** PROPERTIES ***************************************************************************** */ properties: { // overridden appearance: { refine: true, init: "scrollbar" }, /** * The scroll bar orientation */ orientation: { check: ["horizontal", "vertical"], init: "horizontal", apply: "_applyOrientation" }, /** * The maximum value (difference between available size and * content size). */ maximum: { check: "PositiveInteger", apply: "_applyMaximum", init: 100 }, /** * Position of the scrollbar (which means the scroll left/top of the * attached area's pane) * * Strictly validates according to {@link #maximum}. * Does not apply any correction to the incoming value. If you depend * on this, please use {@link #scrollTo} instead. */ position: { check: "qx.lang.Type.isNumber(value)&&value>=0&&value<=this.getMaximum()", init: 0, apply: "_applyPosition", event: "scroll" }, /** * Step size for each tap on the up/down or left/right buttons. */ singleStep: { check: "Integer", init: 20 }, /** * The amount to increment on each event. Typically corresponds * to the user pressing <code>PageUp</code> or <code>PageDown</code>. */ pageStep: { check: "Integer", init: 10, apply: "_applyPageStep" }, /** * Factor to apply to the width/height of the knob in relation * to the dimension of the underlying area. */ knobFactor: { check: "PositiveNumber", apply: "_applyKnobFactor", nullable: true } }, /* ***************************************************************************** MEMBERS ***************************************************************************** */ members: { __offset: 2, __originalMinSize: 0, // overridden _computeSizeHint() { var hint = super._computeSizeHint(); if (this.getOrientation() === "horizontal") { this.__originalMinSize = hint.minWidth; hint.minWidth = 0; } else { this.__originalMinSize = hint.minHeight; hint.minHeight = 0; } return hint; }, // overridden renderLayout(left, top, width, height) { var changes = super.renderLayout(left, top, width, height); var horizontal = this.getOrientation() === "horizontal"; if (this.__originalMinSize >= (horizontal ? width : height)) { this.getChildControl("button-begin").setVisibility("hidden"); this.getChildControl("button-end").setVisibility("hidden"); } else { this.getChildControl("button-begin").setVisibility("visible"); this.getChildControl("button-end").setVisibility("visible"); } return changes; }, // overridden _createChildControlImpl(id, hash) { var control; switch (id) { case "slider": control = new qx.ui.core.scroll.ScrollSlider(); control.setPageStep(100); control.setFocusable(false); control.addListener("changeValue", this._onChangeSliderValue, this); control.addListener( "slideAnimationEnd", this._onSlideAnimationEnd, this ); this._add(control, { flex: 1 }); break; case "button-begin": // Top/Left Button control = new qx.ui.form.RepeatButton(); control.setFocusable(false); control.addListener("execute", this._onExecuteBegin, this); this._add(control); break; case "button-end": // Bottom/Right Button control = new qx.ui.form.RepeatButton(); control.setFocusable(false); control.addListener("execute", this._onExecuteEnd, this); this._add(control); break; } return control || super._createChildControlImpl(id); }, /* --------------------------------------------------------------------------- PROPERTY APPLY ROUTINES --------------------------------------------------------------------------- */ // property apply _applyMaximum(value) { this.getChildControl("slider").setMaximum(value); }, // property apply _applyPosition(value) { this.getChildControl("slider").setValue(value); }, // property apply _applyKnobFactor(value) { this.getChildControl("slider").setKnobFactor(value); }, // property apply _applyPageStep(value) { this.getChildControl("slider").setPageStep(value); }, // property apply _applyOrientation(value, old) { // ARIA attrs this.getContentElement().setAttribute("aria-orientation", value); // Dispose old layout var oldLayout = this._getLayout(); if (oldLayout) { oldLayout.dispose(); } // Reconfigure if (value === "horizontal") { this._setLayout(new qx.ui.layout.HBox()); this.setAllowStretchX(true); this.setAllowStretchY(false); this.replaceState("vertical", "horizontal"); this.getChildControl("button-begin").replaceState("up", "left"); this.getChildControl("button-end").replaceState("down", "right"); } else { this._setLayout(new qx.ui.layout.VBox()); this.setAllowStretchX(false); this.setAllowStretchY(true); this.replaceState("horizontal", "vertical"); this.getChildControl("button-begin").replaceState("left", "up"); this.getChildControl("button-end").replaceState("right", "down"); } // Sync slider orientation this.getChildControl("slider").setOrientation(value); }, /* --------------------------------------------------------------------------- METHOD REDIRECTION TO SLIDER --------------------------------------------------------------------------- */ /** * Scrolls to the given position. * * This method automatically corrects the given position to respect * the {@link #maximum}. * * @param position {Integer} Scroll to this position. Must be greater zero. * @param duration {Number} The time in milliseconds the slide to should take. */ scrollTo(position, duration) { this.getChildControl("slider").slideTo(position, duration); }, /** * Scrolls by the given offset. * * This method automatically corrects the given position to respect * the {@link #maximum}. * * @param offset {Integer} Scroll by this offset * @param duration {Number} The time in milliseconds the slide to should take. */ scrollBy(offset, duration) { this.getChildControl("slider").slideBy(offset, duration); }, /** * Scrolls by the given number of steps. * * This method automatically corrects the given position to respect * the {@link #maximum}. * * @param steps {Integer} Number of steps * @param duration {Number} The time in milliseconds the slide to should take. */ scrollBySteps(steps, duration) { var size = this.getSingleStep(); this.getChildControl("slider").slideBy(steps * size, duration); }, /** * Updates the position property considering the minimum and maximum values. * @param position {Number} The new position. */ updatePosition(position) { this.getChildControl("slider").updatePosition(position); }, /** * If a scroll animation is running, it will be stopped. */ stopScrollAnimation() { this.getChildControl("slider").stopSlideAnimation(); }, /* --------------------------------------------------------------------------- EVENT LISTENER --------------------------------------------------------------------------- */ /** * Executed when the up/left button is executed (pressed) * * @param e {qx.event.type.Event} Execute event of the button */ _onExecuteBegin(e) { this.scrollBy(-this.getSingleStep(), 50); }, /** * Executed when the down/right button is executed (pressed) * * @param e {qx.event.type.Event} Execute event of the button */ _onExecuteEnd(e) { this.scrollBy(this.getSingleStep(), 50); }, /** * Change listener for slider animation end. */ _onSlideAnimationEnd() { this.fireEvent("scrollAnimationEnd"); }, /** * Change listener for slider value changes. * * @param e {qx.event.type.Data} The change event object */ _onChangeSliderValue(e) { this.setPosition(e.getData()); }, /** * Hide the knob of the slider if the slidebar is too small or show it * otherwise. * * @param e {qx.event.type.Data} event object */ _onResizeSlider(e) { var knob = this.getChildControl("slider").getChildControl("knob"); var knobHint = knob.getSizeHint(); var hideKnob = false; var sliderSize = this.getChildControl("slider").getInnerSize(); if (this.getOrientation() == "vertical") { if (sliderSize.height < knobHint.minHeight + this.__offset) { hideKnob = true; } } else { if (sliderSize.width < knobHint.minWidth + this.__offset) { hideKnob = true; } } if (hideKnob) { knob.exclude(); } else { knob.show(); } } } });