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

Unified Diff: client/layout/GridLayout.dart

Issue 9382027: Move client/{base, observable, layout, touch, util, view} to samples/ui_lib . (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: '' Created 8 years, 10 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 | « client/base/base.dart ('k') | client/layout/GridLayoutParams.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: client/layout/GridLayout.dart
===================================================================
--- client/layout/GridLayout.dart (revision 4144)
+++ client/layout/GridLayout.dart (working copy)
@@ -1,520 +0,0 @@
-// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/**
- * Implements a grid-based layout system based on:
- * [http://dev.w3.org/csswg/css3-grid-align/]
- *
- * This layout is designed to support animations and work on browsers that
- * don't support grid natively. As such, we implement it on top of absolute
- * positioning.
- */
-// TODO(jmesserly): the DOM integration still needs work:
-// - The grid assumes it is absolutely positioned in its container.
-// Becasue of that, the grid doesn't work right unless it has at least one
-// fractional size in each dimension. In other words, only "top down" grids
-// work at the moment, because the grid can't determine its own size.
-// The core algorithm supports computing min breadth; the issue is about how
-// to integrate it into our View layer.
-// - Unless a child element is "display: inline-block" we can't get its
-// horizontal content size.
-// - Once we set an element's size to "position: absolute", we lose the
-// ability to get its original content size. If the width or height gets
-// set to something other than the content size, we can't recover the
-// original content size.
-// - There's some rounding to ints when we want to set the positions of our
-// tracks. I don't think we necessarily need to do that.
-//
-// TODO(jmesserly): Some features of the spec are unimplemented:
-// - grid-flow & items that have row and column set to 'auto'.
-// - Grid writing modes (right to left languages, etc)
-// - We don't do a second calculation pass if min content size of a grid-item
-// changes due to column width.
-// - The CSS parsing is not 100% complete, see the parser TODOs.
-// - We don't implement error recovery for invalid combinations of CSS
-// properties, or invalid CSS property values. Instead we throw an error.
-//
-// TODO(jmesserly): high level performance optimizations we could do:
-// - Optimize for the common case of spanCount = 1
-// - Optimize for the vbox/hbox case (1 row or 1 column)
-// - Optimize for the case of no content sized tracks
-// - Optimize for the "incremental update" cases
-class GridLayout extends ViewLayout {
-
- /** Configuration parameters defined in CSS. */
- final GridTrackList rows;
- final GridTrackList columns;
- final GridTemplate template;
-
- /** The default sizing for new rows. */
- final TrackSizing rowSizing;
-
- /** The default sizing for new columns. */
- final TrackSizing columnSizing;
-
- /**
- * This stores the grid's size during a layout.
- * Used for rows/columns with % or fr units.
- */
- int _gridWidth, _gridHeight;
-
- /**
- * During a layout, this stores all row/column size information.
- * Because grid-items can implicitly specify their own rows/columns, we can't
- * compute this until we know the set of items.
- */
- List<GridTrack> _rowTracks, _columnTracks;
-
- /** During a layout, tracks which dimension we're processing. */
- Dimension _dimension;
-
- GridLayout(Positionable view)
- : super(view),
- rows = _GridTrackParser.parse(view.customStyle['grid-rows']),
- columns = _GridTrackParser.parse(view.customStyle['grid-columns']),
- template = _GridTemplateParser.parse(view.customStyle['grid-template']),
-
- rowSizing = _GridTrackParser.parseTrackSizing(
- view.customStyle['grid-row-sizing']),
-
- columnSizing = _GridTrackParser.parseTrackSizing(
- view.customStyle['grid-column-sizing']) {
-
- _rowTracks = rows != null ? rows.tracks : new List<GridTrack>();
- _columnTracks = columns != null ? columns.tracks : new List<GridTrack>();
- }
-
-
- int get currentWidth() => _gridWidth;
- int get currentHeight() => _gridHeight;
-
- void cacheExistingBrowserLayout() {
- // We don't need to do anything as we don't rely on the _cachedViewRect
- // when the grid layout is used.
- }
-
- // TODO(jacobr): cleanup this method so that it returns a Future
- // rather than taking a Completer as an argument.
- /** The main entry point for layout computation. */
- void measureLayout(Future<Size> size, Completer<bool> changed) {
- _ensureAllTracks();
- window.requestLayoutFrame(() {
- _gridWidth = size.value.width;
- _gridHeight = size.value.height;
-
- if (_rowTracks.length > 0 && _columnTracks.length > 0) {
- _measureTracks();
- _setBoundsOfChildren();
- if (changed != null) {
- changed.complete(true);
- }
- }
- });
- }
-
- /**
- * The top level measurement function.
- * [http://dev.w3.org/csswg/css3-grid-align/#calculating-size-of-grid-tracks]
- */
- void _measureTracks() {
- // Resolve logical width, then height. Width comes first so we can use
- // the width when determining the content-sized height.
- try {
- _dimension = Dimension.WIDTH;
- _computeUsedBreadthOfTracks(_columnTracks);
- _dimension = Dimension.HEIGHT;
- _computeUsedBreadthOfTracks(_rowTracks);
- } finally {
- _dimension = null;
- }
-
- // TODO(jmesserly): we're supposed to detect a min-content size change
- // due to our computed width and trigger a new layout.
- // How do we implement that?
- }
-
- num _getRemainingSpace(List<GridTrack> tracks) {
- num remaining = _getGridContentSize();
- remaining -= CollectionUtils.sum(tracks, (t) => t.usedBreadth);
- return Math.max(0, remaining);
- }
-
- /**
- * This is the core Grid Track sizing algorithm. It is run for Grid columns
- * and Grid rows. The goal of the function is to ensure:
- * 1. That each Grid Track satisfies its minSizing
- * 2. That each Grid Track grows from the breadth which satisfied its
- * minSizing to a breadth which satifies its
- * maxSizing, subject to RemainingSpace.
- */
- // Note: spec does not correctly doc all the parameters to this function.
- void _computeUsedBreadthOfTracks(List<GridTrack> tracks) {
-
- // TODO(jmesserly): as a performance optimization we could cache this
- final items = CollectionUtils.map(view.childViews, (view_) => view_.layout);
- CollectionUtils.sortBy(items, (item) => _getSpanCount(item));
-
- // 1. Initialize per Grid Track variables
- for (final t in tracks) {
- // percentage or length sizing functions will return a value
- // min-content, max-content, or a fraction will be set to 0
- t.usedBreadth = t.minSizing.resolveLength(_getGridContentSize());
- t.maxBreadth = t.maxSizing.resolveLength(_getGridContentSize());
- t.updatedBreadth = 0;
- }
-
- // 2. Resolve content-based MinTrackSizingFunctions
- final USED_BREADTH = const _UsedBreadthAccumulator();
- final MAX_BREADTH = const _MaxBreadthAccumulator();
-
- _distributeSpaceBySpanCount(items, ContentSizeMode.MIN, USED_BREADTH);
-
- _distributeSpaceBySpanCount(items, ContentSizeMode.MAX, USED_BREADTH);
-
- // 3. Ensure that maxBreadth is as big as usedBreadth for each track
- for (final t in tracks) {
- if (t.maxBreadth < t.usedBreadth) {
- t.maxBreadth = t.usedBreadth;
- }
- }
-
- // 4. Resolve content-based MaxTrackSizingFunctions
- _distributeSpaceBySpanCount(items, ContentSizeMode.MIN, MAX_BREADTH);
-
- _distributeSpaceBySpanCount(items, ContentSizeMode.MAX, MAX_BREADTH);
-
- // 5. Grow all Grid Tracks in GridTracks from their usedBreadth up to their
- // maxBreadth value until RemainingSpace is exhausted.
- // Note: it's not spec'd what to pass as the accumulator, but usedBreadth
- // seems right.
- _distributeSpaceToTracks(tracks, _getRemainingSpace(tracks),
- USED_BREADTH, false);
-
- // Spec wording is confusing about which direction this assignment happens,
- // but this is the way that makes sense.
- for (final t in tracks) {
- t.usedBreadth = t.updatedBreadth;
- }
-
- // 6. Grow all Grid Tracks having a fraction as their maxSizing
- final tempBreadth = _calcNormalizedFractionBreadth(tracks);
- for (final t in tracks) {
- t.usedBreadth = Math.max(t.usedBreadth,
- tempBreadth * t.maxSizing.fractionValue);
- }
-
- _computeTrackPositions(tracks);
- }
-
- /**
- * Final steps to finish positioning tracks. Takes the track size and uses
- * it to get start and end positions. Also rounds the positions to integers.
- */
- void _computeTrackPositions(List<GridTrack> tracks) {
- // Compute start positions of tracks, as well as the final position
-
- num position = 0;
- for (final t in tracks) {
- t.start = position;
- position += t.usedBreadth;
- }
-
- // Now, go through and round each position to an integer. Then
- // compute the sizes based on those integers.
- num finalPosition = position;
-
- for (int i = 0; i < tracks.length; i++) {
- int startEdge = tracks[i].start;
- int endEdge;
- if (i < tracks.length - 1) {
- endEdge = tracks[i + 1].start.round().toInt();
- tracks[i + 1].start = endEdge;
- } else {
- endEdge = finalPosition.round().toInt();
- }
- int breadth = endEdge - startEdge;
-
- // check that we're not off by >= 1px.
- assert((endEdge - startEdge - tracks[i].usedBreadth).abs() < 1);
-
- tracks[i].usedBreadth = breadth;
- }
- }
-
- /**
- * This method computes a '1fr' value, referred to as the
- * tempBreadth, for a set of Grid Tracks. The value computed
- * will ensure that when the tempBreadth is multiplied by the
- * fractions associated with tracks, that the UsedBreadths of tracks
- * will increase by an amount equal to the maximum of zero and the specified
- * freeSpace less the sum of the current UsedBreadths.
- */
- num _calcNormalizedFractionBreadth(List<GridTrack> tracks) {
-
- final fractionTracks = tracks.filter((t) => t.maxSizing.isFraction);
-
- // Note: the spec has various bugs in this function, such as mismatched
- // identifiers and names that aren't defined. For the most part it's
- // possible to figure out the meaning. It's also a bit confused about
- // how to compute spaceNeededFromFractionTracks, but that should just be the
- // set to the remaining free space after usedBreadth is accounted for.
-
- // We use the tempBreadth field to store the normalized fraction breadth
- for (final t in fractionTracks) {
- t.tempBreadth = t.usedBreadth / t.maxSizing.fractionValue;
- }
-
- CollectionUtils.sortBy(fractionTracks, (t) => t.tempBreadth);
-
- num spaceNeededFromFractionTracks = _getRemainingSpace(tracks);
- num currentBandFractionBreadth = 0;
- num accumulatedFractions = 0;
- for (final t in fractionTracks) {
- if (t.tempBreadth != currentBandFractionBreadth) {
- if (t.tempBreadth * accumulatedFractions >
- spaceNeededFromFractionTracks) {
- break;
- }
- currentBandFractionBreadth = t.tempBreadth;
- }
- accumulatedFractions += t.maxSizing.fractionValue;
- spaceNeededFromFractionTracks += t.usedBreadth;
- }
- return spaceNeededFromFractionTracks / accumulatedFractions;
- }
-
- /**
- * Ensures that for each Grid Track in tracks, a value will be
- * computed, updatedBreadth, that represents the Grid Track's share of
- * freeSpace.
- */
- void _distributeSpaceToTracks(List<GridTrack> tracks, num freeSpace,
- _BreadthAccumulator breadth, bool ignoreMaxBreadth) {
-
- // TODO(jmesserly): in some cases it would be safe to sort the passed in
- // list in place. Not always though.
- tracks = CollectionUtils.orderBy(tracks,
- (t) => t.maxBreadth - breadth.getSize(t));
-
- // Give each Grid Track an equal share of the space, but without exceeding
- // their maxBreadth values. Because there are different MaxBreadths
- // assigned to the different Grid Tracks, this can result in uneven growth.
- for (int i = 0; i < tracks.length; i++) {
- num share = freeSpace / (tracks.length - i);
- share = Math.min(share, tracks[i].maxBreadth);
- tracks[i].tempBreadth = share;
- freeSpace -= share;
- }
-
- // If the first loop completed having grown every Grid Track to its
- // maxBreadth, and there is still freeSpace, then divide that space
- // evenly and assign it to each Grid Track without regard for its
- // maxBreadth. This phase of growth will always be even, but only occurs
- // when the ignoreMaxBreadth flag is true.
- if (freeSpace > 0 && ignoreMaxBreadth) {
- for (int i = 0; i < tracks.length; i++) {
- final share = freeSpace / (tracks.length - i);
- tracks[i].tempBreadth += share;
- freeSpace -= share;
- }
- }
-
- // Note: the spec has us updating all grid tracks, not just the passed in
- // tracks, but I think that's a spec bug.
- for (final t in tracks) {
- t.updatedBreadth = Math.max(t.updatedBreadth, t.tempBreadth);
- }
- }
-
- /**
- * This function prioritizes the distribution of space driven by Grid Items
- * in content-sized Grid Tracks by the Grid Item's spanCount. That is, Grid
- * Items having a lower spanCount have an opportunity to increase the size of
- * the Grid Tracks they cover before those with larger SpanCounts.
- *
- * Note: items are assumed to be already sorted in increasing span count
- */
- void _distributeSpaceBySpanCount(List<ViewLayout> items,
- ContentSizeMode sizeMode, _BreadthAccumulator breadth) {
-
- items = items.filter((item) =>
- _hasContentSizedTracks(_getTracks(item), sizeMode, breadth));
-
- var tracks = [];
-
- for (int i = 0; i < items.length; i++) {
- final item = items[i];
-
- final itemTargetSize = item.measureContent(this, _dimension, sizeMode);
-
- final spannedTracks = _getTracks(item);
- _distributeSpaceToTracks(spannedTracks, itemTargetSize, breadth, true);
-
- // Remember that we need to update the sizes on these tracks
- tracks.addAll(spannedTracks);
-
- // Each time we transition to a new spanCount, update any modified tracks
- bool spanCountFinished = false;
- if (i + 1 == items.length) {
- spanCountFinished = true;
- } else if (_getSpanCount(item) != _getSpanCount(items[i + 1])) {
- spanCountFinished = true;
- }
-
- if (spanCountFinished) {
- for (final t in tracks) {
- breadth.setSize(t,
- Math.max(breadth.getSize(t), t.updatedBreadth));
- }
- tracks = [];
- }
- }
- }
-
- /**
- * Returns true if we have an appropriate content sized dimension, and don't
- * cross a fractional track.
- */
- static bool _hasContentSizedTracks(Collection<GridTrack> tracks,
- ContentSizeMode sizeMode, _BreadthAccumulator breadth) {
-
- for (final t in tracks) {
- final fn = breadth.getSizingFunction(t);
- if (sizeMode == ContentSizeMode.MAX && fn.isMaxContentSized ||
- sizeMode == ContentSizeMode.MIN && fn.isContentSized) {
-
- // Make sure we don't cross a fractional track
- return tracks.length == 1 || !tracks.some((t_) => t_.isFractional);
- }
- }
- return false;
- }
-
- /** Ensures that the numbered track exists. */
- void _ensureTrack(List<GridTrack> tracks, TrackSizing sizing,
- int start, int span) {
- // Start is 1-based. Make it 0-based.
- start -= 1;
-
- // Grow the list if needed
- int length = start + span;
- int first = Math.min(start, tracks.length);
- tracks.length = Math.max(tracks.length, length);
-
- // Fill in tracks
- for (int i = first; i < length; i++) {
- if (tracks[i] == null) {
- tracks[i] = new GridTrack(sizing);
- }
- }
- }
-
- /**
- * Scans children creating GridLayoutParams as needed, and creates all of the
- * rows and columns that we will need.
- *
- * Note: this can potentially create new rows/columns, so this needs to be
- * run before the track sizing algorithm.
- */
- void _ensureAllTracks() {
- final items = CollectionUtils.map(view.childViews, (view_) => view_.layout);
-
- for (final child in items) {
- if (child.layoutParams == null) {
- final p = new GridLayoutParams(child.view, this);
- _ensureTrack(_rowTracks, rowSizing, p.row, p.rowSpan);
- _ensureTrack(_columnTracks, columnSizing, p.column, p.columnSpan);
- child.layoutParams = p;
- }
- child.cacheExistingBrowserLayout();
- }
- }
-
- /**
- * Given the track sizes that were computed, position children in the grid.
- */
- void _setBoundsOfChildren() {
- final items = CollectionUtils.map(view.childViews, (view_) => view_.layout);
-
- for (final item in items) {
- GridLayoutParams childLayout = item.layoutParams;
- var xPos = _getTrackLocationX(childLayout);
- var yPos = _getTrackLocationY(childLayout);
-
- int left = xPos.start, width = xPos.length;
- int top = yPos.start, height = yPos.length;
-
- // Somewhat counterintuitively (at least to me):
- // grid-col-align is the horizontal alignment
- // grid-row-align is the vertical alignment
- xPos = childLayout.columnAlign.align(xPos, item.currentWidth);
- yPos = childLayout.rowAlign.align(yPos, item.currentHeight);
-
- item.setBounds(xPos.start, yPos.start, xPos.length, yPos.length);
- }
- }
-
- num _getGridContentSize() {
- switch (_dimension) {
- case Dimension.WIDTH:
- return _gridWidth;
- case Dimension.HEIGHT:
- return _gridHeight;
- }
- }
-
- _GridLocation _getTrackLocationX(GridLayoutParams childLayout) {
- int start = childLayout.column - 1;
- int end = start + childLayout.columnSpan - 1;
-
- start = _columnTracks[start].start;
- end = _columnTracks[end].end;
-
- return new _GridLocation(start, end - start);
- }
-
- _GridLocation _getTrackLocationY(GridLayoutParams childLayout) {
- int start = childLayout.row - 1;
- int end = start + childLayout.rowSpan - 1;
-
- start = _rowTracks[start].start;
- end = _rowTracks[end].end;
-
- return new _GridLocation(start, end - start);
- }
-
- /** Gets the tracks that this item crosses. */
- // TODO(jmesserly): might be better to return an iterable
- List<GridTrack> _getTracks(ViewLayout item) {
- GridLayoutParams childLayout = item.layoutParams;
-
- int start, span;
- List<GridTrack> tracks;
- switch (_dimension) {
- case Dimension.WIDTH:
- start = childLayout.column - 1;
- span = childLayout.columnSpan;
- tracks = _columnTracks;
- break;
- case Dimension.HEIGHT:
- start = childLayout.row - 1;
- span = childLayout.rowSpan;
- tracks = _rowTracks;
- }
-
- assert(start >= 0 && span >= 1);
-
- final result = new List<GridTrack>(span);
- for (int i = 0; i < span; i++) {
- result[i] = tracks[start + i];
- }
- return result;
- }
-
- int _getSpanCount(ViewLayout item) {
- GridLayoutParams childLayout = item.layoutParams;
- return (_dimension == Dimension.WIDTH ?
- childLayout.columnSpan : childLayout.rowSpan);
- }
-}
« no previous file with comments | « client/base/base.dart ('k') | client/layout/GridLayoutParams.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698