UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

367 lines (302 loc) 9.38 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2004-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) * Jonathan Weiß (jonathan_rass) ************************************************************************ */ /** * EXPERIMENTAL! * * The WidgetCell layer renders each cell with a qooxdoo widget. The concrete * widget instance for each cell is provided by a cell provider. */ qx.Class.define("qx.ui.virtual.layer.WidgetCell", { extend : qx.ui.virtual.layer.Abstract, include : [ qx.ui.core.MChildrenHandling ], /** * @param widgetCellProvider {qx.ui.virtual.core.IWidgetCellProvider} This * class manages the life cycle of the cell widgets. */ construct : function(widgetCellProvider) { this.base(arguments); this.setZIndex(12); if (qx.core.Environment.get("qx.debug")) { this.assertInterface( widgetCellProvider, qx.ui.virtual.core.IWidgetCellProvider ); } this._cellProvider = widgetCellProvider; this.__spacerPool = []; }, /* ***************************************************************************** PROPERTIES ***************************************************************************** */ properties : { // overridden anonymous : { refine: true, init: false } }, events : { /** * Is fired when the {@link #_fullUpdate} or the * {@link #_updateLayerWindow} is finished. */ updated : "qx.event.type.Event" }, /* ***************************************************************************** MEMBERS ***************************************************************************** */ members : { __spacerPool : null, /** * Returns the widget used to render the given cell. May return null if the * cell isn’t rendered currently rendered. * * @param row {Integer} The cell's row index * @param column {Integer} The cell's column index * @return {qx.ui.core.LayoutItem|null} the widget used to render the given * cell or <code>null</code> */ getRenderedCellWidget : function(row, column) { if (this._getChildren().length === 0) { return null; } var columnCount = this.getColumnSizes().length; var rowCount = this.getRowSizes().length; var firstRow = this.getFirstRow(); var firstColumn = this.getFirstColumn(); if ( row < firstRow || row >= firstRow + rowCount || column < firstColumn || column >= firstColumn + columnCount ) { return null; } var childIndex = (column - firstColumn) + (row - firstRow) * columnCount; var widget = this._getChildren()[childIndex]; if (!widget || widget.getUserData("cell.empty")) { return null; } else { return widget; } }, /** * Get the spacer widget, for empty cells * * @return {qx.ui.core.Spacer} The spacer widget. */ _getSpacer : function() { var spacer = this.__spacerPool.pop(); if (!spacer) { spacer = new qx.ui.core.Spacer(); spacer.setUserData("cell.empty", 1); } return spacer; }, /** * Activates one of the still not empty items. * @param elementToPool {qx.ui.core.Widget} The widget which gets pooled. */ _activateNotEmptyChild : function(elementToPool) { // get the current active element var active = qx.ui.core.FocusHandler.getInstance().getActiveWidget(); // if the element to pool is active or one of its children if (active == elementToPool || qx.ui.core.Widget.contains(elementToPool, active)) { // search for a new child to activate var children = this._getChildren(); for (var i = children.length - 1; i >= 0; i--) { if (!children[i].getUserData("cell.empty")) { children[i].activate(); break; } }; } }, // overridden _fullUpdate : function(firstRow, firstColumn, rowSizes, columnSizes) { var cellProvider = this._cellProvider; var children = this._getChildren().concat(); for (var i=0; i<children.length; i++) { var child = children[i]; if (child.getUserData("cell.empty")) { this.__spacerPool.push(child); } else { this._activateNotEmptyChild(child); cellProvider.poolCellWidget(child); } } var top = 0; var left = 0; var visibleItems = []; for (var y=0; y<rowSizes.length; y++) { for (var x=0; x<columnSizes.length; x++) { var row = firstRow + y; var column = firstColumn + x; var item = cellProvider.getCellWidget(row, column) || this._getSpacer(); visibleItems.push(item); item.setUserBounds(left, top, columnSizes[x], rowSizes[y]); item.setUserData("cell.row", row); item.setUserData("cell.column", column); this._add(item); left += columnSizes[x]; } top += rowSizes[y]; left = 0; } children.forEach(function(child){ if (visibleItems.indexOf(child) === -1) { this._remove(child); } }.bind(this)); this.fireEvent("updated"); }, _updateLayerWindow : function( firstRow, firstColumn, rowSizes, columnSizes ) { // compute overlap of old and new window // // +---+ // | ##--+ // | ## | // +--## | // +---+ // if (qx.core.Environment.get("qx.debug")) { this.assertPositiveInteger(firstRow); this.assertPositiveInteger(firstColumn); this.assertArray(rowSizes); this.assertArray(columnSizes); } var lastRow = firstRow + rowSizes.length - 1; var lastColumn = firstColumn + columnSizes.length - 1; var overlap = { firstRow: Math.max(firstRow, this.getFirstRow()), lastRow: Math.min(lastRow, this._lastRow), firstColumn: Math.max(firstColumn, this.getFirstColumn()), lastColumn: Math.min(lastColumn, this._lastColumn) }; this._lastColumn = lastColumn; this._lastRow = lastRow; if ( overlap.firstRow > overlap.lastRow || overlap.firstColumn > overlap.lastColumn ) { return this._fullUpdate( firstRow, firstColumn, rowSizes, columnSizes ); } // collect the widgets to move var children = this._getChildren(); var lineLength = this.getColumnSizes().length; var widgetsToMove = []; var widgetsToMoveIndexes = {}; for (var row=firstRow; row<=lastRow; row++) { widgetsToMove[row] = []; for (var column=firstColumn; column<=lastColumn; column++) { if ( row >= overlap.firstRow && row <= overlap.lastRow && column >= overlap.firstColumn && column <= overlap.lastColumn ) { var x = column - this.getFirstColumn(); var y = row - this.getFirstRow(); var index = y*lineLength + x; widgetsToMove[row][column] = children[index]; widgetsToMoveIndexes[index] = true; } } } var cellProvider = this._cellProvider; // pool widgets var children = this._getChildren().concat(); for (var i=0; i<children.length; i++) { if (!widgetsToMoveIndexes[i]) { var child = children[i]; if (child.getUserData("cell.empty")) { this.__spacerPool.push(child); } else { this._activateNotEmptyChild(child); cellProvider.poolCellWidget(child); } } } var top = 0; var left = 0; var visibleItems = []; for (var y=0; y<rowSizes.length; y++) { for (var x=0; x<columnSizes.length; x++) { var row = firstRow + y; var column = firstColumn + x; var item = widgetsToMove[row][column] || cellProvider.getCellWidget(row, column) || this._getSpacer(); visibleItems.push(item); item.setUserBounds(left, top, columnSizes[x], rowSizes[y]); item.setUserData("cell.row", row); item.setUserData("cell.column", column); this._add(item); left += columnSizes[x]; } top += rowSizes[y]; left = 0; } children.forEach(function(child){ if (visibleItems.indexOf(child) === -1) { this._remove(child); } }.bind(this)); this.fireEvent("updated"); } }, destruct : function() { var children = this._getChildren(); for (var i=0; i<children.length; i++) { children[i].dispose(); } this._cellProvider = this.__spacerPool = null; } });