Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(270)

Unified Diff: charted/lib/charts/behaviors/hovercard.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « charted/lib/charts/behaviors/chart_tooltip.dart ('k') | charted/lib/charts/behaviors/line_marker.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: charted/lib/charts/behaviors/hovercard.dart
diff --git a/charted/lib/charts/behaviors/hovercard.dart b/charted/lib/charts/behaviors/hovercard.dart
deleted file mode 100644
index f01d2ac7292919990e427d4b0d3397b961ca9e3a..0000000000000000000000000000000000000000
--- a/charted/lib/charts/behaviors/hovercard.dart
+++ /dev/null
@@ -1,404 +0,0 @@
-//
-// Copyright 2014 Google Inc. All rights reserved.
-//
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file or at
-// https://developers.google.com/open-source/licenses/bsd
-//
-
-part of charted.charts;
-
-typedef Element HovercardBuilder(int column, int row);
-
-///
-/// Subscribe to events on the chart and display more information about the
-/// visualization that is hovered or highlighted.
-///
-/// This behavior supports two event modes:
-/// (1) State change: Subscribes to changes on ChartState
-/// (2) Mouse tracking: Subscribe to onValueMouseOver and onValueMouseOut
-///
-/// Supports two placement modes for mouse-tracking:
-/// (1) Place relative to mouse position
-/// (2) Place relative to the visualized measure value.
-///
-/// Supports two modes for displayed content:
-/// (1) Show all measure values at the current dimension value.
-/// (2) Show the hovered value only
-///
-/// Optionally, takes a builder that is passed row, column values that
-/// can be used to build custom tooltip
-///
-/// What makes the positioning logic complex?
-/// (1) Is this a CartesianArea?
-/// (2) Does the CartesianArea use two dimensions or just one?
-/// (3) Does the CartesianArea use "bands" along the axes?
-/// (4) How does measure correspond to positioning? Are the bars stacked?
-///
-/// So, how is the position computed?
-/// (1) Uses ChartConfig to figure out which renderers are being used, asks
-/// for extent of the row to roughly get the position along measure axis.
-/// (2) Position along dimension axes is computed separately based on how
-/// many dimensions are being used and if any of them use bands.
-///
-/// Constraints and known issues:
-/// (0) The implementation isn't complete yet! Specifically for CartesianArea
-/// that uses two axes.
-/// (1) Even with all the logic, single value mode does not work well
-/// with StackedBarChartRenderer
-/// (2) Only mouse relative positioning is supported on LayoutArea
-/// (3) Positioning only works for renderers that determine extent given a
-/// single row. Eg: Would not work with a water-fall chart.
-///
-class Hovercard implements ChartBehavior {
- final HovercardBuilder builder;
-
- bool _isMouseTracking;
- bool _isMultiValue;
- bool _showDimensionTitle;
- Iterable<int> _columnsToShow;
-
- Iterable placementOrder =
- const['orientation', 'top', 'right', 'bottom', 'left', 'orientation'];
- int offset = 20;
-
- ChartArea _area;
- ChartState _state;
- SubscriptionsDisposer _disposer = new SubscriptionsDisposer();
-
- Element _hovercardRoot;
-
- Hovercard({
- bool isMouseTracking,
- bool isMultiValue: false,
- bool showDimensionTitle: false,
- List columnsToShow: const [],
- this.builder}) {
- _isMouseTracking = isMouseTracking;
- _isMultiValue = isMultiValue;
- _showDimensionTitle = showDimensionTitle;
- _columnsToShow = columnsToShow;
- }
-
- void init(ChartArea area, Selection _, Selection __) {
- _area = area;
- _state = area.state;
-
- // If we don't have state, fall back to mouse events.
- _isMouseTracking =
- _isMouseTracking == true || _state == null || _area is LayoutArea;
-
- // Subscribe to events.
- if (_isMouseTracking) {
- _disposer.addAll([
- _area.onValueMouseOver.listen(_handleMouseOver),
- _area.onValueMouseOut.listen(_handleMouseOut)
- ]);
- } else {
- _disposer.add(_state.changes.listen(_handleStateChange));
- }
- }
-
- void dispose() {
- _disposer.dispose();
- if (_hovercardRoot != null) _hovercardRoot.remove();
- }
-
- void _handleMouseOver(ChartEvent e) {
- _ensureHovercard();
- _hovercardRoot.children.clear();
- _hovercardRoot.append(builder != null
- ? builder(e.column, e.row)
- : _createTooltip(e.column, e.row));
- _hovercardRoot.style
- ..visibility = 'visible'
- ..opacity = '1.0';
- _updateTooltipPosition(evt: e);
- }
-
- void _handleMouseOut(ChartEvent e) {
- _ensureHovercard();
- _hovercardRoot.style
- ..visibility = 'hidden'
- ..opacity = '$EPSILON';
- }
-
- void _handleStateChange(Iterable<ChangeRecord> changes) {
- _ensureHovercard();
-
- var value = _state.hovered;
- if (_state.highlights.length == 1) {
- value = _state.highlights.first;
- }
-
- if (value == null) {
- _hovercardRoot.style
- ..visibility = 'hidden'
- ..opacity = '$EPSILON';
- } else {
- _hovercardRoot.children.clear();
- _hovercardRoot.append(builder != null
- ? builder(value.first, value.last)
- : _createTooltip(value.first, value.last));
- _hovercardRoot.style
- ..visibility = 'visible'
- ..opacity = '1.0';
- _updateTooltipPosition(column: value.first, row: value.last);
- }
- }
-
- void _ensureHovercard() {
- if (_hovercardRoot != null) return;
- _hovercardRoot = new Element.div();
- _hovercardRoot.classes.add('hovercard');
- if (_area.config.isRTL) {
- _hovercardRoot.attributes['dir'] = 'rtl';
- _hovercardRoot.classes.add('rtl');
- }
- _area.host.style.position = 'relative';
- _area.host.append(_hovercardRoot);
- }
-
- void _updateTooltipPosition({ChartEvent evt, int column, int row}) {
- assert(evt != null || column != null && row != null);
- if (_isMouseTracking && evt != null) {
- _positionAtMousePointer(evt);
- } else if (_area is CartesianArea) {
- if ((_area as CartesianArea).useTwoDimensionAxes) {
- _positionOnTwoDimensionCartesian(column, row);
- } else {
- _positionOnSingleDimensionCartesian(column, row);
- }
- } else {
- _positionOnLayout(column, row);
- }
- }
-
- void _positionAtMousePointer(ChartEvent e) =>
- _positionAtPoint(e.chartX, e.chartY, offset, offset, false, false);
-
- void _positionOnLayout(column, row) {
- // Currently for layouts, when hovercard is triggered due to change
- // in ChartState, we render hovercard in the middle of layout.
- // TODO: Get bounding rect from LayoutRenderer and position relative to it.
- }
-
- void _positionOnTwoDimensionCartesian(int column, int row) {
- // TODO: Implement multi dimension positioning.
- }
-
- void _positionOnSingleDimensionCartesian(int column, int row) {
- CartesianArea area = _area;
- var dimensionCol = area.config.dimensions.first,
- dimensionScale = area.dimensionScales.first,
- measureScale = _getScaleForColumn(column),
- dimensionOffset = this.offset,
- dimensionCenterOffset = 0;
-
- // If we are using bands on the one axis that is shown
- // update position and offset accordingly.
- if (area.dimensionsUsingBands.contains(dimensionCol)) {
- assert(dimensionScale is OrdinalScale);
- dimensionOffset = (dimensionScale as OrdinalScale).rangeBand / 2;
- dimensionCenterOffset = dimensionOffset;
- }
-
- var rowData = area.data.rows.elementAt(row),
- measurePosition = 0,
- isNegative = false,
- dimensionPosition = dimensionScale.scale(
- rowData.elementAt(dimensionCol)) + dimensionCenterOffset;
-
- if (_isMultiValue) {
- var max = SMALL_INT_MIN,
- min = SMALL_INT_MAX;
- area.config.series.forEach((ChartSeries series) {
- CartesianRenderer renderer = series.renderer;
- Extent extent = renderer.extentForRow(rowData);
- if (extent.min < min) min = extent.min;
- if (extent.max > max) max = extent.max;
- measurePosition = measureScale.scale(max);
- isNegative = max < 0;
- });
- } else {
- var value = rowData.elementAt(column);
- isNegative = value < 0;
- measurePosition = measureScale.scale(rowData.elementAt(column));
- }
-
- if (area.config.isLeftAxisPrimary) {
- _positionAtPoint(measurePosition, dimensionPosition,
- 0, dimensionOffset, isNegative, true);
- } else {
- _positionAtPoint(dimensionPosition, measurePosition,
- dimensionOffset, 0, isNegative, false);
- }
- }
-
- void _positionAtPoint(num x, num y,
- num xBand, num yBand, bool negative, [bool isLeftPrimary = false]) {
- var rect = _hovercardRoot.getBoundingClientRect(),
- width = rect.width,
- height = rect.height,
- scaleToHostY =
- (_area.theme.padding != null ? _area.theme.padding.top : 0) +
- (_area.layout.renderArea.y),
- scaleToHostX =
- (_area.theme.padding != null ? _area.theme.padding.start: 0) +
- (_area.layout.renderArea.x),
- renderAreaHeight = _area.layout.renderArea.height,
- renderAreaWidth = _area.layout.renderArea.width;
-
- if (scaleToHostY < 0) scaleToHostY = 0;
- if (scaleToHostX < 0) scaleToHostX = 0;
-
- num top = 0, left = 0;
- for (int i = 0, len = placementOrder.length; i < len; ++i) {
- String placement = placementOrder.elementAt(i);
-
- // Place the popup based on the orientation.
- if (placement == 'orientation') {
- placement = isLeftPrimary ? 'right' : 'top';
- }
-
- if (placement == 'top') {
- top = negative ? y + yBand : y - (height + yBand);
- left = isLeftPrimary ? x - width : x - width / 2;
- }
- if (placement == 'right') {
- top = isLeftPrimary ? y - height / 2 : y;
- left = negative ? x - (width + xBand) : x + xBand;
- }
- if (placement == 'left') {
- top = isLeftPrimary ? y - height / 2 : y;
- left = negative ? x + xBand : x - (width + xBand);
- }
- if (placement == 'bottom') {
- top = negative ? y - (height + yBand) : y + yBand;
- left = isLeftPrimary ? x - width : x - width / 2;
- }
-
- // Check if the popup is contained in the RenderArea.
- // If not, try other placements.
- if (top > 0 && left > 0 &&
- top + height < renderAreaHeight && left + width < renderAreaWidth) {
- break;
- }
- }
-
- _hovercardRoot.style
- ..top = '${top + scaleToHostY}px'
- ..left = '${left + scaleToHostX}px';
- }
-
- Element _createTooltip(int column, int row) {
- var element = new Element.div();
- if (_showDimensionTitle) {
- var titleElement = new Element.div()
- ..className = 'hovercard-title'
- ..text = _getDimensionTitle(column, row);
- element.append(titleElement);
- }
-
- var measureVals = _getMeasuresData(column, row);
- measureVals.forEach((ChartLegendItem item) {
- var labelElement = new Element.div()
- ..className = 'hovercard-measure-label'
- ..text = item.label,
- valueElement = new Element.div()
- ..style.color = item.color
- ..className = 'hovercard-measure-value'
- ..text = item.value,
- measureElement = new Element.div()
- ..append(labelElement)
- ..append(valueElement);
-
- measureElement.className = _columnsToShow.length > 1 || _isMultiValue
- ? 'hovercard-measure hovercard-multi'
- : 'hovercard-measure hovercard-single';
- element.append(measureElement);
- });
-
- return element;
- }
-
- Iterable<ChartLegendItem> _getMeasuresData(int column, int row) {
- var measureVals = <ChartLegendItem>[];
-
- if (_columnsToShow.isNotEmpty) {
- _columnsToShow.forEach((int column) {
- measureVals.add(_createHovercardItem(column, row));
- });
- } else if (_columnsToShow.length > 1 || _isMultiValue) {
- var displayedCols = [];
- _area.config.series.forEach((ChartSeries series) {
- series.measures.forEach((int column) {
- if (!displayedCols.contains(column)) displayedCols.add(column);
- });
- });
- displayedCols.sort();
- displayedCols.forEach((int column) {
- measureVals.add(_createHovercardItem(column, row));
- });
- } else {
- measureVals.add(_createHovercardItem(column, row));
- }
-
- return measureVals;
- }
-
- ChartLegendItem _createHovercardItem(int column, int row) {
- var rowData = _area.data.rows.elementAt(row),
- columns = _area.data.columns,
- spec = columns.elementAt(column),
- colorKey = _area.useRowColoring ? row : column,
- formatter = _getFormatterForColumn(column),
- label = _area.useRowColoring
- ? rowData.elementAt(_area.config.dimensions.first)
- : spec.label;
- return new ChartLegendItem(
- label: label,
- value: formatter(rowData.elementAt(column)),
- color: _area.theme.getColorForKey(colorKey));
- }
-
- String _getDimensionTitle(int column, int row) {
- var rowData = _area.data.rows.elementAt(row),
- colSpec = _area.data.columns.elementAt(column);
- if (_area.useRowColoring) {
- return colSpec.label;
- } else {
- var count = (_area as CartesianArea).useTwoDimensionAxes ? 2 : 1,
- dimensions = _area.config.dimensions.take(count);
- return dimensions.map(
- (int c) =>
- _getFormatterForColumn(c)(rowData.elementAt(c))).join(', ');
- }
- }
-
- // TODO: Move this to a common place?
- Scale _getScaleForColumn(int column) {
- var series = _area.config.series.firstWhere(
- (ChartSeries x) => x.measures.contains(column), orElse: () => null);
- return series != null
- ? (_area as CartesianArea).measureScales(series).first
- : null;
- }
-
- // TODO: Move this to a common place?
- FormatFunction _getFormatterForColumn(int column) {
- var formatter = _area.data.columns.elementAt(column).formatter;
- if (formatter == null && _area is CartesianArea) {
- var scale = _getScaleForColumn(column);
- if (scale != null) {
- formatter = scale.createTickFormatter();
- }
- }
- if (formatter == null) {
- // Formatter function must return String. Default to identity function
- // but return the toString() instead.
- formatter = (x) => x.toString();
- }
- return formatter;
- }
-}
« no previous file with comments | « charted/lib/charts/behaviors/chart_tooltip.dart ('k') | charted/lib/charts/behaviors/line_marker.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698