UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

531 lines (416 loc) 12.9 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) ************************************************************************ */ /** * This class represents a scroll able pane. This means that this widget * may contain content which is bigger than the available (inner) * dimensions of this widget. The widget also offer methods to control * the scrolling position. It can only have exactly one child. */ qx.Class.define("qx.ui.core.scroll.ScrollPane", { extend : qx.ui.core.Widget, /* ***************************************************************************** CONSTRUCTOR ***************************************************************************** */ construct : function() { this.base(arguments); this.set({ minWidth: 0, minHeight: 0 }); // Automatically configure a "fixed" grow layout. this._setLayout(new qx.ui.layout.Grow()); // Add resize listener to "translate" event this.addListener("resize", this._onUpdate); var contentEl = this.getContentElement(); // Synchronizes the DOM scroll position with the properties contentEl.addListener("scroll", this._onScroll, this); // Fixed some browser quirks e.g. correcting scroll position // to the previous value on re-display of a pane contentEl.addListener("appear", this._onAppear, this); }, /* ***************************************************************************** EVENTS ***************************************************************************** */ events : { /** Fired on resize of both the container or the content. */ update : "qx.event.type.Event", /** Fired on scroll animation end invoked by 'scroll*' methods. */ scrollAnimationEnd : "qx.event.type.Event" }, /* ***************************************************************************** PROPERTIES ***************************************************************************** */ properties : { /** The horizontal scroll position */ scrollX : { check : "qx.lang.Type.isNumber(value)&&value>=0&&value<=this.getScrollMaxX()", apply : "_applyScrollX", transform: "_transformScrollX", event : "scrollX", init : 0 }, /** The vertical scroll position */ scrollY : { check : "qx.lang.Type.isNumber(value)&&value>=0&&value<=this.getScrollMaxY()", apply : "_applyScrollY", transform: "_transformScrollY", event : "scrollY", init : 0 } }, /* ***************************************************************************** MEMBERS ***************************************************************************** */ members : { __frame : null, /* --------------------------------------------------------------------------- CONTENT MANAGEMENT --------------------------------------------------------------------------- */ /** * Configures the content of the scroll pane. Replaces any existing child * with the newly given one. * * @param widget {qx.ui.core.Widget?null} The content widget of the pane */ add : function(widget) { var old = this._getChildren()[0]; if (old) { this._remove(old); old.removeListener("resize", this._onUpdate, this); } if (widget) { this._add(widget); widget.addListener("resize", this._onUpdate, this); } }, /** * Removes the given widget from the content. The pane is empty * afterwards as only one child is supported by the pane. * * @param widget {qx.ui.core.Widget?null} The content widget of the pane */ remove : function(widget) { if (widget) { this._remove(widget); widget.removeListener("resize", this._onUpdate, this); } }, /** * Returns an array containing the current content. * * @return {Object[]} The content array */ getChildren : function() { return this._getChildren(); }, /* --------------------------------------------------------------------------- EVENT LISTENER --------------------------------------------------------------------------- */ /** * Event listener for resize event of content and container * * @param e {Event} Resize event object */ _onUpdate : function(e) { this.fireEvent("update"); }, /** * Event listener for scroll event of content * * @param e {qx.event.type.Event} Scroll event object */ _onScroll : function(e) { var contentEl = this.getContentElement(); this.setScrollX(contentEl.getScrollX()); this.setScrollY(contentEl.getScrollY()); }, /** * Event listener for appear event of content * * @param e {qx.event.type.Event} Appear event object */ _onAppear : function(e) { var contentEl = this.getContentElement(); var internalX = this.getScrollX(); var domX = contentEl.getScrollX(); if (internalX != domX) { contentEl.scrollToX(internalX); } var internalY = this.getScrollY(); var domY = contentEl.getScrollY(); if (internalY != domY) { contentEl.scrollToY(internalY); } }, /* --------------------------------------------------------------------------- ITEM LOCATION SUPPORT --------------------------------------------------------------------------- */ /** * Returns the top offset of the given item in relation to the * inner height of this widget. * * @param item {qx.ui.core.Widget} Item to query * @return {Integer} Top offset */ getItemTop : function(item) { var top = 0; do { top += item.getBounds().top; item = item.getLayoutParent(); } while (item && item !== this); return top; }, /** * Returns the top offset of the end of the given item in relation to the * inner height of this widget. * * @param item {qx.ui.core.Widget} Item to query * @return {Integer} Top offset */ getItemBottom : function(item) { return this.getItemTop(item) + item.getBounds().height; }, /** * Returns the left offset of the given item in relation to the * inner width of this widget. * * @param item {qx.ui.core.Widget} Item to query * @return {Integer} Top offset */ getItemLeft : function(item) { var left = 0; var parent; do { left += item.getBounds().left; parent = item.getLayoutParent(); if (parent) { left += parent.getInsets().left; } item = parent; } while (item && item !== this); return left; }, /** * Returns the left offset of the end of the given item in relation to the * inner width of this widget. * * @param item {qx.ui.core.Widget} Item to query * @return {Integer} Right offset */ getItemRight : function(item) { return this.getItemLeft(item) + item.getBounds().width; }, /* --------------------------------------------------------------------------- DIMENSIONS --------------------------------------------------------------------------- */ /** * The size (identical with the preferred size) of the content. * * @return {Map} Size of the content (keys: <code>width</code> and <code>height</code>) */ getScrollSize : function() { return this.getChildren()[0].getBounds(); }, /* --------------------------------------------------------------------------- SCROLL SUPPORT --------------------------------------------------------------------------- */ /** * The maximum horizontal scroll position. * * @return {Integer} Maximum horizontal scroll position. */ getScrollMaxX : function() { var paneSize = this.getInnerSize(); var scrollSize = this.getScrollSize(); if (paneSize && scrollSize) { return Math.max(0, scrollSize.width - paneSize.width); } return 0; }, /** * The maximum vertical scroll position. * * @return {Integer} Maximum vertical scroll position. */ getScrollMaxY : function() { var paneSize = this.getInnerSize(); var scrollSize = this.getScrollSize(); if (paneSize && scrollSize) { return Math.max(0, scrollSize.height - paneSize.height); } return 0; }, /** * Scrolls the element's content to the given left coordinate * * @param value {Integer} The vertical position to scroll to. * @param duration {Number?} The time in milliseconds the scroll to should take. */ scrollToX : function(value, duration) { var max = this.getScrollMaxX(); if (value < 0) { value = 0; } else if (value > max) { value = max; } this.stopScrollAnimation(); if (duration) { var from = this.getScrollX(); this.__frame = new qx.bom.AnimationFrame(); this.__frame.on("end", function() { this.setScrollX(value); this.__frame = null; this.fireEvent("scrollAnimationEnd"); }, this); this.__frame.on("frame", function(timePassed) { var newX = parseInt(timePassed/duration * (value - from) + from); this.setScrollX(newX); }, this); this.__frame.startSequence(duration); } else { this.setScrollX(value); } }, /** * Scrolls the element's content to the given top coordinate * * @param value {Integer} The horizontal position to scroll to. * @param duration {Number?} The time in milliseconds the scroll to should take. */ scrollToY : function(value, duration) { var max = this.getScrollMaxY(); if (value < 0) { value = 0; } else if (value > max) { value = max; } this.stopScrollAnimation(); if (duration) { var from = this.getScrollY(); this.__frame = new qx.bom.AnimationFrame(); this.__frame.on("end", function() { this.setScrollY(value); this.__frame = null; this.fireEvent("scrollAnimationEnd"); }, this); this.__frame.on("frame", function(timePassed) { var newY = parseInt(timePassed/duration * (value - from) + from); this.setScrollY(newY); }, this); this.__frame.startSequence(duration); } else { this.setScrollY(value); } }, /** * Scrolls the element's content horizontally by the given amount. * * @param x {Integer?0} Amount to scroll * @param duration {Number?} The time in milliseconds the scroll to should take. */ scrollByX : function(x, duration) { this.scrollToX(this.getScrollX() + x, duration); }, /** * Scrolls the element's content vertically by the given amount. * * @param y {Integer?0} Amount to scroll * @param duration {Number?} The time in milliseconds the scroll to should take. */ scrollByY : function(y, duration) { this.scrollToY(this.getScrollY() + y, duration); }, /** * If an scroll animation is running, it will be stopped with that method. */ stopScrollAnimation : function() { if (this.__frame) { this.__frame.cancelSequence(); this.__frame = null; } }, /* --------------------------------------------------------------------------- PROPERTY APPLY ROUTINES --------------------------------------------------------------------------- */ // property apply _applyScrollX : function(value) { this.getContentElement().scrollToX(value); }, /** * Transform property * * @param value {Number} Value to transform * @return {Number} Rounded value */ _transformScrollX: function(value) { return Math.round(value); }, // property apply _applyScrollY : function(value) { this.getContentElement().scrollToY(value); }, /** * Transform property * * @param value {Number} Value to transform * @return {Number} Rounded value */ _transformScrollY: function(value) { return Math.round(value); } } });