UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

385 lines (324 loc) 10.1 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2009 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: * Fabian Jakobs (fjakobs) ************************************************************************ */ /** * The scroll bar widget wraps the native browser scroll bars as a qooxdoo widget. * It can be uses instead of the styled qooxdoo scroll bars. * * Scroll bars are used by the {@link qx.ui.container.Scroll} container. Usually * a scroll bar is not used directly. * * *Example* * * Here is a little example of how to use the widget. * * <pre class='javascript'> * var scrollBar = new qx.ui.core.scroll.NativeScrollBar("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.NativeScrollBar", { extend: qx.ui.core.Widget, implement: qx.ui.core.scroll.IScrollBar, /** * @param orientation {String?"horizontal"} The initial scroll bar orientation */ construct(orientation) { super(); this.addState("native"); this.getContentElement().addListener("scroll", this._onScroll, this); this.addListener("pointerdown", this._stopPropagation, this); this.addListener("pointerup", this._stopPropagation, this); this.addListener("pointermove", this._stopPropagation, this); this.addListener("appear", this._onAppear, this); this.getContentElement().add(this._getScrollPaneElement()); this.getContentElement().setStyle("box-sizing", "content-box"); // Configure orientation if (orientation != null) { this.setOrientation(orientation); } else { this.initOrientation(); } // prevent drag & drop on scrolling this.addListener("track", e => { e.stopPropagation(); }); }, events: { /** * Fired as soon as the scroll animation ended. */ scrollAnimationEnd: "qx.event.type.Event" }, properties: { // overridden appearance: { refine: true, init: "scrollbar" }, // interface implementation orientation: { check: ["horizontal", "vertical"], init: "horizontal", apply: "_applyOrientation" }, // interface implementation maximum: { check: "PositiveInteger", apply: "_applyMaximum", init: 100 }, // interface implementation position: { check: "Number", init: 0, apply: "_applyPosition", event: "scroll" }, /** * Step size for each tap on the up/down or left/right buttons. */ singleStep: { check: "Integer", init: 20 }, // interface implementation knobFactor: { check: "PositiveNumber", nullable: true } }, members: { __isHorizontal: null, __scrollPaneElement: null, __requestId: null, __scrollAnimationframe: null, /** * Get the scroll pane html element. * * @return {qx.html.Element} The element */ _getScrollPaneElement() { if (!this.__scrollPaneElement) { this.__scrollPaneElement = new qx.html.Element(); } return this.__scrollPaneElement; }, /* --------------------------------------------------------------------------- WIDGET API --------------------------------------------------------------------------- */ // overridden renderLayout(left, top, width, height) { var changes = super.renderLayout(left, top, width, height); this._updateScrollBar(); return changes; }, // overridden _getContentHint() { var scrollbarWidth = qx.bom.element.Scroll.getScrollbarWidth(); return { width: this.__isHorizontal ? 100 : scrollbarWidth, maxWidth: this.__isHorizontal ? null : scrollbarWidth, minWidth: this.__isHorizontal ? null : scrollbarWidth, height: this.__isHorizontal ? scrollbarWidth : 100, maxHeight: this.__isHorizontal ? scrollbarWidth : null, minHeight: this.__isHorizontal ? scrollbarWidth : null }; }, // overridden _applyEnabled(value, old) { super._applyEnabled(value, old); this._updateScrollBar(); }, /* --------------------------------------------------------------------------- PROPERTY APPLY ROUTINES --------------------------------------------------------------------------- */ // property apply _applyMaximum(value) { this._updateScrollBar(); }, // property apply _applyPosition(value) { var content = this.getContentElement(); if (this.__isHorizontal) { content.scrollToX(value); } else { content.scrollToY(value); } }, // property apply _applyOrientation(value, old) { // ARIA attrs this.getContentElement().setAttribute("aria-orientation", value); var isHorizontal = (this.__isHorizontal = value === "horizontal"); this.set({ allowGrowX: isHorizontal, allowShrinkX: isHorizontal, allowGrowY: !isHorizontal, allowShrinkY: !isHorizontal }); if (isHorizontal) { this.replaceState("vertical", "horizontal"); } else { this.replaceState("horizontal", "vertical"); } this.getContentElement().setStyles({ overflowX: isHorizontal ? "scroll" : "hidden", overflowY: isHorizontal ? "hidden" : "scroll" }); // Update layout qx.ui.core.queue.Layout.add(this); }, /** * Update the scroll bar according to its current size, max value and * enabled state. */ _updateScrollBar() { var isHorizontal = this.__isHorizontal; var bounds = this.getBounds(); if (!bounds) { return; } if (this.isEnabled()) { var containerSize = isHorizontal ? bounds.width : bounds.height; var innerSize = this.getMaximum() + containerSize; } else { innerSize = 0; } // Scrollbars don't work properly in IE/Edge if the element with overflow has // exactly the size of the scrollbar. Thus we move the element one pixel // out of the view and increase the size by one. if ( qx.core.Environment.get("engine.name") == "mshtml" || qx.core.Environment.get("browser.name") == "edge" ) { var bounds = this.getBounds(); this.getContentElement().setStyles({ left: (isHorizontal ? bounds.left : bounds.left - 1) + "px", top: (isHorizontal ? bounds.top - 1 : bounds.top) + "px", width: (isHorizontal ? bounds.width : bounds.width + 1) + "px", height: (isHorizontal ? bounds.height + 1 : bounds.height) + "px" }); } this._getScrollPaneElement().setStyles({ left: 0, top: 0, width: (isHorizontal ? innerSize : 1) + "px", height: (isHorizontal ? 1 : innerSize) + "px" }); this.updatePosition(this.getPosition()); }, // interface implementation scrollTo(position, duration) { // if a user sets a new position, stop any animation this.stopScrollAnimation(); if (duration) { var from = this.getPosition(); this.__scrollAnimationframe = new qx.bom.AnimationFrame(); this.__scrollAnimationframe.on( "frame", function (timePassed) { var newPos = parseInt( (timePassed / duration) * (position - from) + from ); this.updatePosition(newPos); }, this ); this.__scrollAnimationframe.on( "end", function () { this.setPosition( Math.max(0, Math.min(this.getMaximum(), position)) ); this.__scrollAnimationframe = null; this.fireEvent("scrollAnimationEnd"); }, this ); this.__scrollAnimationframe.startSequence(duration); } else { this.updatePosition(position); } }, /** * Helper to set the new position taking care of min and max values. * @param position {Number} The new position. */ updatePosition(position) { this.setPosition(Math.max(0, Math.min(this.getMaximum(), position))); }, // interface implementation scrollBy(offset, duration) { this.scrollTo(this.getPosition() + offset, duration); }, // interface implementation scrollBySteps(steps, duration) { var size = this.getSingleStep(); this.scrollBy(steps * size, duration); }, /** * If a scroll animation is running, it will be stopped. */ stopScrollAnimation() { if (this.__scrollAnimationframe) { this.__scrollAnimationframe.cancelSequence(); this.__scrollAnimationframe = null; } }, /** * Scroll event handler * * @param e {qx.event.type.Event} the scroll event */ _onScroll(e) { var container = this.getContentElement(); var position = this.__isHorizontal ? container.getScrollX() : container.getScrollY(); this.setPosition(position); }, /** * Listener for appear which ensured the scroll bar is positioned right * on appear. * * @param e {qx.event.type.Data} Incoming event object */ _onAppear(e) { this._applyPosition(this.getPosition()); }, /** * Stops propagation on the given even * * @param e {qx.event.type.Event} the event */ _stopPropagation(e) { e.stopPropagation(); } }, destruct() { this._disposeObjects("__scrollPaneElement"); } });