DevTools: Move legend into the PieChart control.

Today, each tool that uses the PieChart control has to implement its own
legend control. This change moves the legend into PieChart
and standardizes the look and feel.

Screenshots:

Application Storage tab: https://imgur.com/laaPFRO
Heap statistics: https://imgur.com/SNwpGXt
Profiler: https://imgur.com/FiKZtTx


Change-Id: I61a8adab0b58fbe49298804f97208da6a6bbbce6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1607140
Commit-Queue: Jeff Fisher <jeffish@microsoft.com>
Reviewed-by: Joel Einbinder <einbinder@chromium.org>
Reviewed-by: Alexei Filippov <alph@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#661950}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 9ef252cf698f59acd24f683cd5dcad9d34d33e88
This commit is contained in:
Jeff Fisher
2019-05-21 22:42:01 +00:00
committed by Commit Bot
parent fe680c83d7
commit 76f4942943
10 changed files with 134 additions and 132 deletions
+2 -1
View File
@@ -42,7 +42,8 @@ LayerViewer.PaintProfilerView = class extends UI.HBox {
this._canvasContainer = this.contentElement.createChild('div', 'paint-profiler-canvas-container');
this._progressBanner = this.contentElement.createChild('div', 'full-widget-dimmed-banner hidden');
this._progressBanner.textContent = Common.UIString('Profiling\u2026');
this._pieChart = new PerfUI.PieChart(55, this._formatPieChartTime.bind(this), true);
this._pieChart = new PerfUI.PieChart(
{chartName: ls`Profiling Results`, size: 55, formatter: this._formatPieChartTime.bind(this)});
this._pieChart.element.classList.add('paint-profiler-pie-chart');
this.contentElement.appendChild(this._pieChart.element);
+54 -13
View File
@@ -28,20 +28,25 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @typedef {{size: number, formatter: function(number):string, showLegend: (boolean|undefined), chartName: string}} */
PerfUI.PieChartOptions;
/**
* @unrestricted
*/
PerfUI.PieChart = class {
/**
* @param {number} size
* @param {function(number):string=} formatter
* @param {boolean=} showTotal
* @param {!PerfUI.PieChartOptions} options
*/
constructor(size, formatter, showTotal) {
constructor(options) {
const {size, formatter, showLegend, chartName} = options;
this.element = createElement('div');
this._shadowRoot = UI.createShadowRootWithCoreStyles(this.element, 'perf_ui/pieChart.css');
const root = this._shadowRoot.createChild('div', 'root');
const svg = this._createSVGChild(root, 'svg');
UI.ARIAUtils.markAsGroup(root);
UI.ARIAUtils.setAccessibleName(root, chartName);
this._chartRoot = root.createChild('div', 'chart-root');
const svg = this._createSVGChild(this._chartRoot, 'svg');
this._group = this._createSVGChild(svg, 'g');
this._innerR = 0.618;
const strokeWidth = 1 / size;
@@ -55,12 +60,13 @@ PerfUI.PieChart = class {
circle.setAttribute('stroke', 'hsl(0, 0%, 80%)');
circle.setAttribute('fill', 'transparent');
circle.setAttribute('stroke-width', strokeWidth);
this._foregroundElement = root.createChild('div', 'pie-chart-foreground');
if (showTotal)
this._totalElement = this._foregroundElement.createChild('div', 'pie-chart-total');
this._foregroundElement = this._chartRoot.createChild('div', 'pie-chart-foreground');
this._totalElement = this._foregroundElement.createChild('div', 'pie-chart-total');
this._formatter = formatter;
this._slices = [];
this._lastAngle = -Math.PI / 2;
if (showLegend)
this._legend = root.createChild('div', 'pie-chart-legend');
this._setSize(size);
}
@@ -78,8 +84,12 @@ PerfUI.PieChart = class {
totalString = this._formatter ? this._formatter(totalValue) : totalValue;
else
totalString = '';
if (this._totalElement)
this._totalElement.textContent = totalString;
this._totalElement.textContent = totalString;
if (this._legend) {
this._legend.removeChildren();
const legendItem = this._addLegendItem(totalValue, ls`Total`);
UI.ARIAUtils.setLabelledBy(this._totalElement, legendItem);
}
}
/**
@@ -88,15 +98,16 @@ PerfUI.PieChart = class {
_setSize(value) {
this._group.setAttribute('transform', 'scale(' + (value / 2) + ') translate(1, 1) scale(0.99, 0.99)');
const size = value + 'px';
this.element.style.width = size;
this.element.style.height = size;
this._chartRoot.style.width = size;
this._chartRoot.style.height = size;
}
/**
* @param {number} value
* @param {string} color
* @param {string=} name
*/
addSlice(value, color) {
addSlice(value, color, name) {
let sliceAngle = value / this._totalValue * 2 * Math.PI;
if (!isFinite(sliceAngle))
return;
@@ -117,6 +128,10 @@ PerfUI.PieChart = class {
`M${x1},${y1} A1,1,0,${largeArc},1,${x2},${y2} L${x3},${y3} A${r2},${r2},0,${largeArc},0,${x4},${y4} Z`);
path.setAttribute('fill', color);
this._slices.push(path);
if (this._legend) {
const legendItem = this._addLegendItem(value, name, color);
UI.ARIAUtils.setLabelledBy(path, legendItem);
}
}
/**
@@ -129,4 +144,30 @@ PerfUI.PieChart = class {
parent.appendChild(child);
return child;
}
/**
* @param {number} value
* @param {string=} name
* @param {string=} color
* @returns {!Element}
*/
_addLegendItem(value, name, color) {
const node = this._legend.ownerDocument.createElement('div');
node.className = 'pie-chart-legend-row';
// make sure total always appears at the bottom
if (this._legend.childElementCount)
this._legend.insertBefore(node, this._legend.lastElementChild);
else
this._legend.appendChild(node);
const sizeDiv = node.createChild('div', 'pie-chart-size');
const swatchDiv = node.createChild('div', 'pie-chart-swatch');
const nameDiv = node.createChild('div', 'pie-chart-name');
if (color)
swatchDiv.style.backgroundColor = color;
else
swatchDiv.classList.add('pie-chart-empty-swatch');
nameDiv.textContent = name;
sizeDiv.textContent = this._formatter ? this._formatter(value) : value;
return node;
}
};
+43 -2
View File
@@ -5,9 +5,15 @@
*/
.root {
align-items: center;
display: flex;
min-width: fit-content;
white-space: nowrap;
}
.chart-root {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
.pie-chart-foreground {
@@ -17,10 +23,45 @@
z-index: 10;
top: 0;
display: flex;
pointer-events: none;
}
.pie-chart-total {
margin: auto;
padding: 2px 5px;
background-color: rgba(255, 255, 255, 0.6);
pointer-events: auto;
}
.pie-chart-legend {
margin-left: 30px;
}
.pie-chart-legend-row {
margin: 5px auto;
padding-right: 25px;
}
.pie-chart-swatch {
display: inline-block;
width: 11px;
height: 11px;
margin: 0 6px;
top: 1px;
position: relative;
border: 1px solid rgba(100, 100, 100, 0.2);
}
.pie-chart-swatch.pie-chart-empty-swatch {
border: none;
}
.pie-chart-name {
display: inline-block;
}
.pie-chart-size {
display: inline-block;
text-align: right;
width: 70px;
}
+8 -17
View File
@@ -292,7 +292,6 @@ Profiler.HeapSnapshotView = class extends UI.SimpleView {
this._statisticsView.addRecord(statistics.jsArrays, Common.UIString('JS Arrays'), '#7af');
this._statisticsView.addRecord(statistics.native, Common.UIString('Typed Arrays'), '#fc5');
this._statisticsView.addRecord(statistics.system, Common.UIString('System Objects'), '#98f');
this._statisticsView.addRecord(statistics.total, Common.UIString('Total'));
return statistics;
}
@@ -1611,10 +1610,14 @@ Profiler.HeapSnapshotStatisticsView = class extends UI.VBox {
constructor() {
super();
this.element.classList.add('heap-snapshot-statistics-view');
this._pieChart = new PerfUI.PieChart(150, Profiler.HeapSnapshotStatisticsView._valueFormatter, true);
this._pieChart = new PerfUI.PieChart({
chartName: ls`Heap memory usage`,
size: 150,
formatter: Profiler.HeapSnapshotStatisticsView._valueFormatter,
showLegend: true
});
this._pieChart.element.classList.add('heap-snapshot-stats-pie-chart');
this.element.appendChild(this._pieChart.element);
this._labels = this.element.createChild('div', 'heap-snapshot-stats-legend');
}
/**
@@ -1635,22 +1638,10 @@ Profiler.HeapSnapshotStatisticsView = class extends UI.VBox {
/**
* @param {number} value
* @param {string} name
* @param {string=} color
* @param {string} color
*/
addRecord(value, name, color) {
if (color)
this._pieChart.addSlice(value, color);
const node = this._labels.createChild('div');
const swatchDiv = node.createChild('div', 'heap-snapshot-stats-swatch');
const nameDiv = node.createChild('div', 'heap-snapshot-stats-name');
const sizeDiv = node.createChild('div', 'heap-snapshot-stats-size');
if (color)
swatchDiv.style.backgroundColor = color;
else
swatchDiv.classList.add('heap-snapshot-stats-empty-swatch');
nameDiv.textContent = name;
sizeDiv.textContent = Profiler.HeapSnapshotStatisticsView._valueFormatter(value);
this._pieChart.addSlice(value, color, name);
}
};
-32
View File
@@ -182,38 +182,6 @@
flex-shrink: 0;
}
.heap-snapshot-stats-legend {
margin-left: 24px;
flex-shrink: 0;
}
.heap-snapshot-stats-legend > div {
margin-top: 1px;
width: 170px;
}
.heap-snapshot-stats-swatch {
display: inline-block;
width: 10px;
height: 10px;
border: 1px solid rgba(100, 100, 100, 0.3);
}
.heap-snapshot-stats-swatch.heap-snapshot-stats-empty-swatch {
border: none;
}
.heap-snapshot-stats-name,
.heap-snapshot-stats-size {
display: inline-block;
margin-left: 6px;
}
.heap-snapshot-stats-size {
float: right;
text-align: right;
}
.heap-allocation-stack .stack-frame {
display: flex;
justify-content: space-between;
+3 -9
View File
@@ -40,12 +40,11 @@ Resources.ClearStorageView = class extends UI.ThrottledWidget {
ls`Learn more`);
learnMoreRow.appendChild(learnMore);
this._quotaUsage = null;
this._pieChart = new PerfUI.PieChart(110, Number.bytesToString, true);
this._pieChartLegend = createElement('div');
this._pieChart = new PerfUI.PieChart(
{chartName: ls`Storage Usage`, size: 110, formatter: Number.bytesToString, showLegend: true});
const usageBreakdownRow = quota.appendRow();
usageBreakdownRow.classList.add('usage-breakdown-row');
usageBreakdownRow.appendChild(this._pieChart.element);
usageBreakdownRow.appendChild(this._pieChartLegend);
const clearButtonSection = this._reportView.appendSection('', 'clear-storage-button').appendRow();
this._clearButton = UI.createTextButton(ls`Clear site data`, this._clear.bind(this));
@@ -234,11 +233,7 @@ Resources.ClearStorageView = class extends UI.ThrottledWidget {
continue;
const title = this._getStorageTypeName(usageForType.storageType);
const color = this._pieColors.get(usageForType.storageType) || '#ccc';
this._pieChart.addSlice(value, color);
const rowElement = this._pieChartLegend.createChild('div', 'usage-breakdown-legend-row');
rowElement.createChild('span', 'usage-breakdown-legend-value').textContent = Number.bytesToString(value);
rowElement.createChild('span', 'usage-breakdown-legend-swatch').style.backgroundColor = color;
rowElement.createChild('span', 'usage-breakdown-legend-title').textContent = title;
this._pieChart.addSlice(value, color, title);
}
}
@@ -251,7 +246,6 @@ Resources.ClearStorageView = class extends UI.ThrottledWidget {
*/
_resetPieChart(total) {
this._pieChart.setTotal(total);
this._pieChartLegend.removeChildren();
}
/**
-24
View File
@@ -26,27 +26,3 @@
.usage-breakdown-row {
min-width: fit-content;
}
.usage-breakdown-legend-row {
margin: 5px auto;
}
.usage-breakdown-legend-title {
display: inline-block;
}
.usage-breakdown-legend-value {
display: inline-block;
width: 70px;
text-align: right;
}
.usage-breakdown-legend-swatch {
display: inline-block;
width: 11px;
height: 11px;
margin: 0 6px;
position: relative;
top: 1px;
border: 1px solid rgba(0, 0, 0, 0.2);
}
+7 -8
View File
@@ -1523,12 +1523,16 @@ Timeline.TimelineUIUtils = class {
total += aggregatedStats[categoryName];
const element = createElementWithClass('div', 'timeline-details-view-pie-chart-wrapper hbox');
const pieChart = new PerfUI.PieChart(110, value => Number.preciseMillisToString(value), true);
const pieChart = new PerfUI.PieChart({
chartName: ls`Time spent in rendering`,
size: 110,
formatter: value => Number.preciseMillisToString(value),
showLegend: true,
});
pieChart.element.classList.add('timeline-details-view-pie-chart');
pieChart.setTotal(total);
const pieChartContainer = element.createChild('div', 'vbox');
pieChartContainer.appendChild(pieChart.element);
const footerElement = element.createChild('div', 'timeline-aggregated-info-legend');
/**
* @param {string} name
@@ -1539,12 +1543,7 @@ Timeline.TimelineUIUtils = class {
function appendLegendRow(name, title, value, color) {
if (!value)
return;
pieChart.addSlice(value, color);
const rowElement = footerElement.createChild('div');
rowElement.createChild('span', 'timeline-aggregated-legend-value').textContent =
Number.preciseMillisToString(value, 1);
rowElement.createChild('span', 'timeline-aggregated-legend-swatch').style.backgroundColor = color;
rowElement.createChild('span', 'timeline-aggregated-legend-title').textContent = title;
pieChart.addSlice(value, color, title);
}
// In case of self time, first add self, then children of the same category.
-26
View File
@@ -172,26 +172,6 @@
height: 100%;
}
.timeline-aggregated-legend-title {
display: inline-block;
}
.timeline-aggregated-legend-value {
display: inline-block;
width: 70px;
text-align: right;
}
.timeline-aggregated-legend-swatch {
display: inline-block;
width: 11px;
height: 11px;
margin: 0 6px;
position: relative;
top: 1px;
border: 1px solid rgba(0, 0, 0, 0.2);
}
.popover ul {
margin: 0;
padding: 0;
@@ -385,12 +365,6 @@
line-height: 12px;
}
.timeline-aggregated-info-legend > div {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.timeline-flamechart {
overflow: hidden;
}
+17
View File
@@ -109,6 +109,14 @@ UI.ARIAUtils.markAsPresentation = function(element) {
element.setAttribute('role', 'presentation');
};
/**
* @param {!Element} element
*/
UI.ARIAUtils.ensureId = function(element) {
if (!element.id)
element.id = UI.ARIAUtils.nextId('ariaElement');
};
/**
* @param {string} prefix
* @return {string}
@@ -186,6 +194,15 @@ UI.ARIAUtils.setAccessibleName = function(element, name) {
element.setAttribute('aria-label', name);
};
/**
* @param {!Element} element
* @param {!Element} labelElement
*/
UI.ARIAUtils.setLabelledBy = function(element, labelElement) {
UI.ARIAUtils.ensureId(labelElement);
element.setAttribute('aria-labelledby', labelElement.id);
};
/**
* @param {string} message
* @param {!Element} element