| /* |
| * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. |
| * Copyright (C) 2017 Sony Interactive Entertainment Inc. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| WI.LayerDetailsSidebarPanel = class LayerDetailsSidebarPanel extends WI.DetailsSidebarPanel |
| { |
| constructor() |
| { |
| super("layer", WI.UIString("All Layers")); |
| |
| this.element.classList.add("layer"); |
| |
| this._layers = []; |
| this._layerIdToSelect = null; |
| |
| this._dataGrid = null; |
| this._hoveredDataGridNode = null; |
| this._dataGridNodesByLayerId = new Map; |
| |
| this._bottomBar = null; |
| this._bottomBarWidth = NaN; |
| this._layersCountLabel = null; |
| this._layersMemoryLabel = null; |
| } |
| |
| // Public |
| |
| get minimumWidth() |
| { |
| let minimumWidth = super.minimumWidth; |
| |
| if (isNaN(this._bottomBarWidth) && this._layersCountLabel && this._layersMemoryLabel) |
| this._bottomBarWidth = this._layersCountLabel.realOffsetWidth + this._layersMemoryLabel.realOffsetWidth; |
| if (!isNaN(this._bottomBarWidth)) |
| minimumWidth = Math.max(minimumWidth, this._bottomBarWidth); |
| |
| return minimumWidth; |
| } |
| |
| inspect(objects) |
| { |
| if (!(objects instanceof Array)) |
| objects = [objects]; |
| |
| let layers = objects.filter((object) => object instanceof WI.Layer); |
| this._updateLayers(layers); |
| |
| return !!layers.length; |
| } |
| |
| selectNodeByLayerId(layerId) |
| { |
| this._layerIdToSelect = null; |
| |
| let node = this._dataGridNodesByLayerId.get(layerId); |
| if (node === this._dataGrid.selectedNode) |
| return; |
| |
| const suppressEvent = true; |
| if (node) |
| node.revealAndSelect(suppressEvent); |
| else if (this._dataGrid.selectedNode) |
| this._dataGrid.selectedNode.deselect(suppressEvent); |
| else |
| this._layerIdToSelect = layerId; |
| } |
| |
| // Private |
| |
| _buildDataGrid() |
| { |
| const columns = { |
| name: { |
| title: WI.UIString("Node"), |
| sortable: false, |
| }, |
| paintCount: { |
| title: WI.UIString("Paints"), |
| sortable: true, |
| aligned: "right", |
| width: "70px", |
| }, |
| memory: { |
| title: WI.UIString("Memory"), |
| sortable: true, |
| aligned: "right", |
| width: "70px", |
| } |
| }; |
| |
| this._dataGrid = new WI.DataGrid(columns); |
| this._dataGrid.addEventListener(WI.DataGrid.Event.SortChanged, this._sortDataGrid, this); |
| this._dataGrid.addEventListener(WI.DataGrid.Event.SelectedNodeChanged, this._dataGridSelectedNodeChanged, this); |
| |
| this._dataGrid.sortColumnIdentifier = "memory"; |
| this._dataGrid.sortOrder = WI.DataGrid.SortOrder.Descending; |
| this._dataGrid.createSettings("layer-details-sidebar-panel"); |
| |
| this._dataGrid.element.addEventListener("mousemove", this._dataGridMouseMove.bind(this)); |
| this._dataGrid.element.addEventListener("mouseleave", this._dataGridMouseLeave.bind(this)); |
| |
| // FIXME: We can't use virtualized rows until DataGrid is able to scroll them programmatically. |
| // See TreeElement#reveal -> TreeOutline#updateVirtualizedElements for an analogy. |
| this._dataGrid.inline = true; |
| this._dataGrid.element.classList.remove("inline"); |
| |
| this.contentView.addSubview(this._dataGrid); |
| } |
| |
| _buildBottomBar() |
| { |
| this._bottomBar = this.element.appendChild(document.createElement("div")); |
| this._bottomBar.className = "bottom-bar"; |
| |
| this._layersCountLabel = this._bottomBar.appendChild(document.createElement("div")); |
| this._layersCountLabel.className = "layers-count-label"; |
| |
| this._layersMemoryLabel = this._bottomBar.appendChild(document.createElement("div")); |
| this._layersMemoryLabel.className = "layers-memory-label"; |
| |
| this._bottomBarWidth = NaN; |
| } |
| |
| _sortDataGrid() |
| { |
| let sortColumnIdentifier = this._dataGrid.sortColumnIdentifier; |
| |
| function comparator(a, b) { |
| let itemA = a.layer[sortColumnIdentifier] || 0; |
| let itemB = b.layer[sortColumnIdentifier] || 0; |
| return itemA - itemB; |
| } |
| |
| this._dataGrid.sortNodes(comparator); |
| } |
| |
| _dataGridSelectedNodeChanged() |
| { |
| let layerId = this._dataGrid.selectedNode ? this._dataGrid.selectedNode.layer.layerId : null; |
| this.dispatchEventToListeners(WI.LayerDetailsSidebarPanel.Event.SelectedLayerChanged, {layerId}); |
| } |
| |
| _dataGridMouseMove(event) |
| { |
| let dataGridNode = this._dataGrid.dataGridNodeFromNode(event.target); |
| if (dataGridNode === this._hoveredDataGridNode) |
| return; |
| |
| if (!dataGridNode) { |
| this._hideDOMNodeHighlight(); |
| return; |
| } |
| |
| this._hoveredDataGridNode = dataGridNode; |
| |
| let layer = dataGridNode.layer; |
| |
| if (layer.isGeneratedContent || layer.isReflection || layer.isAnonymous) { |
| const usePageCoordinates = true; |
| WI.domManager.highlightRect(layer.bounds, usePageCoordinates); |
| } else { |
| let domNode = WI.domManager.nodeForId(layer.nodeId); |
| if (domNode) |
| domNode.highlight(); |
| else |
| WI.domManager.hideDOMNodeHighlight(); |
| } |
| } |
| |
| _dataGridMouseLeave(event) |
| { |
| this._hideDOMNodeHighlight(); |
| } |
| |
| _hideDOMNodeHighlight() |
| { |
| WI.domManager.hideDOMNodeHighlight(); |
| this._hoveredDataGridNode = null; |
| } |
| |
| _updateLayers(newLayers) |
| { |
| this._updateDataGrid(newLayers); |
| this._updateBottomBar(newLayers); |
| |
| this._layers = newLayers; |
| } |
| |
| _updateDataGrid(newLayers) |
| { |
| if (!this._dataGrid) |
| this._buildDataGrid(); |
| |
| let {removals, additions, preserved} = WI.layerTreeManager.layerTreeMutations(this._layers, newLayers); |
| |
| removals.forEach((layer) => { |
| let node = this._dataGridNodesByLayerId.get(layer.layerId); |
| this._dataGrid.removeChild(node); |
| this._dataGridNodesByLayerId.delete(layer.layerId); |
| }); |
| |
| additions.forEach((layer) => { |
| let node = new WI.LayerTreeDataGridNode(layer); |
| this._dataGridNodesByLayerId.set(layer.layerId, node); |
| this._dataGrid.appendChild(node); |
| }); |
| |
| preserved.forEach((layer) => { |
| let node = this._dataGridNodesByLayerId.get(layer.layerId); |
| node.layer = layer; |
| }); |
| |
| this._sortDataGrid(); |
| |
| if (this._layerIdToSelect) |
| this.selectNodeByLayerId(this._layerIdToSelect); |
| } |
| |
| _updateBottomBar(newLayers) |
| { |
| if (!this._bottomBar) |
| this._buildBottomBar(); |
| |
| this._layersCountLabel.textContent = WI.UIString("Layer Count: %d").format(newLayers.length); |
| |
| let totalMemory = newLayers.reduce((total, layer) => total + (layer.memory || 0), 0); |
| this._layersMemoryLabel.textContent = WI.UIString("Memory: %s").format(Number.bytesToString(totalMemory)); |
| |
| this._bottomBarWidth = NaN; |
| } |
| }; |
| |
| WI.LayerDetailsSidebarPanel.Event = { |
| SelectedLayerChanged: "selected-layer-changed" |
| }; |