| <!DOCTYPE html> |
| <!-- |
| Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| Use of this source code is governed by a BSD-style license that can be |
| found in the LICENSE file. |
| --> |
| |
| <link rel="import" href="/tracing/base/utils.html"> |
| <link rel="import" href="/tracing/model/model_settings.html"> |
| <link rel="import" href="/tracing/ui/base/ui.html"> |
| <link rel="import" href="/tracing/ui/tracks/container_track.html"> |
| |
| <script> |
| 'use strict'; |
| |
| tr.exportTo('tr.ui.tracks', function() { |
| /** |
| * A track that displays a group of objects in multiple rows. |
| * @constructor |
| * @extends {ContainerTrack} |
| */ |
| const MultiRowTrack = tr.ui.b.define( |
| 'multi-row-track', tr.ui.tracks.ContainerTrack); |
| |
| MultiRowTrack.prototype = { |
| |
| __proto__: tr.ui.tracks.ContainerTrack.prototype, |
| |
| decorate(viewport) { |
| tr.ui.tracks.ContainerTrack.prototype.decorate.call(this, viewport); |
| this.tooltip_ = ''; |
| this.heading_ = ''; |
| |
| this.groupingSource_ = undefined; |
| this.itemsToGroup_ = undefined; |
| |
| this.defaultToCollapsedWhenSubRowCountMoreThan = 1; |
| |
| this.currentSubRowsWithHeadings_ = undefined; |
| this.expanded_ = true; |
| }, |
| |
| get itemsToGroup() { |
| return this.itemsToGroup_; |
| }, |
| |
| setItemsToGroup(itemsToGroup, opt_groupingSource) { |
| this.itemsToGroup_ = itemsToGroup; |
| this.groupingSource_ = opt_groupingSource; |
| this.currentSubRowsWithHeadings_ = undefined; |
| this.updateContents_(); |
| this.updateExpandedStateFromGroupingSource_(); |
| }, |
| |
| /** |
| * Opt-out from using buildSubRows_() and provide prebuilt rows. |
| * Array of {row: [rowItems...], heading} dicts is expected as an argument. |
| */ |
| setPrebuiltSubRows(groupingSource, subRowsWithHeadings) { |
| this.itemsToGroup_ = undefined; |
| this.groupingSource_ = groupingSource; |
| this.currentSubRowsWithHeadings_ = subRowsWithHeadings; |
| this.updateContents_(); |
| this.updateExpandedStateFromGroupingSource_(); |
| }, |
| |
| get heading() { |
| return this.heading_; |
| }, |
| |
| set heading(h) { |
| this.heading_ = h; |
| this.updateHeadingAndTooltip_(); |
| }, |
| |
| get tooltip() { |
| return this.tooltip_; |
| }, |
| |
| set tooltip(t) { |
| this.tooltip_ = t; |
| this.updateHeadingAndTooltip_(); |
| }, |
| |
| get subRows() { |
| return this.currentSubRowsWithHeadings_.map(elem => elem.row); |
| }, |
| |
| get hasVisibleContent() { |
| return this.children.length > 0; |
| }, |
| |
| get expanded() { |
| return this.expanded_; |
| }, |
| |
| set expanded(expanded) { |
| if (this.expanded_ === expanded) return; |
| |
| this.expanded_ = expanded; |
| this.expandedStateChanged_(); |
| }, |
| |
| onHeadingClicked_(e) { |
| if (this.subRows.length <= 1) return; |
| |
| this.expanded = !this.expanded; |
| |
| if (this.groupingSource_) { |
| const modelSettings = new tr.model.ModelSettings( |
| this.groupingSource_.model); |
| modelSettings.setSettingFor(this.groupingSource_, 'expanded', |
| this.expanded); |
| } |
| |
| e.stopPropagation(); |
| }, |
| |
| updateExpandedStateFromGroupingSource_() { |
| if (this.groupingSource_) { |
| const numSubRows = this.subRows.length; |
| const modelSettings = new tr.model.ModelSettings( |
| this.groupingSource_.model); |
| if (numSubRows > 1) { |
| let defaultExpanded; |
| if (numSubRows > this.defaultToCollapsedWhenSubRowCountMoreThan) { |
| defaultExpanded = false; |
| } else { |
| defaultExpanded = true; |
| } |
| this.expanded = modelSettings.getSettingFor( |
| this.groupingSource_, 'expanded', defaultExpanded); |
| } else { |
| this.expanded = undefined; |
| } |
| } |
| }, |
| |
| expandedStateChanged_() { |
| const children = this.children; |
| const minH = Math.max(2, Math.ceil(18 / children.length)); |
| const h = (this.expanded_ ? 18 : minH) + 'px'; |
| |
| for (let i = 0; i < children.length; i++) { |
| children[i].height = h; |
| if (i === 0) { |
| children[i].arrowVisible = true; |
| } |
| children[i].expanded = this.expanded; |
| } |
| |
| if (children.length === 1) { |
| children[0].expanded = true; |
| children[0].arrowVisible = false; |
| } |
| }, |
| |
| updateContents_() { |
| tr.ui.tracks.ContainerTrack.prototype.updateContents_.call(this); |
| this.detach(); // Clear sub-tracks. |
| |
| if (this.currentSubRowsWithHeadings_ === undefined) { |
| // No prebuilt rows, build it. |
| if (this.itemsToGroup_ === undefined) { |
| return; |
| } |
| const subRows = this.buildSubRows_(this.itemsToGroup_); |
| this.currentSubRowsWithHeadings_ = subRows.map(row => { |
| return {row, heading: undefined}; |
| }); |
| } |
| if (this.currentSubRowsWithHeadings_ === undefined || |
| this.currentSubRowsWithHeadings_.length === 0) { |
| return; |
| } |
| |
| const addSubTrackEx = (items, opt_heading) => { |
| const track = this.addSubTrack_(items); |
| if (opt_heading !== undefined) { |
| track.heading = opt_heading; |
| } |
| track.addEventListener( |
| 'heading-clicked', this.onHeadingClicked_.bind(this)); |
| }; |
| |
| if (this.currentSubRowsWithHeadings_[0].heading !== undefined && |
| this.currentSubRowsWithHeadings_[0].heading !== this.heading_) { |
| // Create an empty row to render the group's title there. |
| addSubTrackEx([]); |
| } |
| |
| for (const subRowWithHeading of this.currentSubRowsWithHeadings_) { |
| const subRow = subRowWithHeading.row; |
| if (subRow.length === 0) { |
| continue; |
| } |
| addSubTrackEx(subRow, subRowWithHeading.heading); |
| } |
| |
| this.updateHeadingAndTooltip_(); |
| this.expandedStateChanged_(); |
| }, |
| |
| updateHeadingAndTooltip_() { |
| if (!Polymer.dom(this).firstChild) return; |
| |
| Polymer.dom(this).firstChild.heading = this.heading_; |
| Polymer.dom(this).firstChild.tooltip = this.tooltip_; |
| }, |
| |
| /** |
| * Breaks up the list of slices into N rows, each of which is a list of |
| * slices that are non overlapping. |
| */ |
| buildSubRows_(itemsToGroup) { |
| throw new Error('Not implemented'); |
| }, |
| |
| addSubTrack_(subRowItems) { |
| throw new Error('Not implemented'); |
| }, |
| |
| areArrayContentsSame_(a, b) { |
| if (!a || !b) return false; |
| |
| if (!a.length || !b.length) return false; |
| |
| if (a.length !== b.length) return false; |
| |
| for (let i = 0; i < a.length; ++i) { |
| if (a[i] !== b[i]) return false; |
| } |
| return true; |
| } |
| }; |
| |
| return { |
| MultiRowTrack, |
| }; |
| }); |
| </script> |