blob: fe866d785ff5fd6d20a7b0703c690c8c27075428 [file] [log] [blame]
<!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>