// 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. // @dart = 2.9 part of layout; // This file has classes representing the grid tracks and grid template /** * The data structure representing the grid-rows or grid-columns * properties. */ class GridTrackList { /** The set of tracks defined in CSS via grid-rows and grid-columns */ final List tracks; /** * Maps edge names to the corresponding track. Depending on whether the index * is used as a start or end, it might be interpreted exclusively or * inclusively. */ final Map lineNames; GridTrackList(this.tracks, this.lineNames) {} } /** Represents a row or a column. */ class GridTrack { /** * The start position of this track. Equal to the sum of previous track's * usedBreadth. */ num start; /** The final computed breadth of this track. */ num usedBreadth; // Fields used internally by the sizing algorithm num maxBreadth; num updatedBreadth; num tempBreadth; final TrackSizing sizing; GridTrack(this.sizing) {} /** * Support for the feature that repeats rows and columns, e.g. * [:grid-columns: 10px ("content" 250px 10px)[4]:] */ GridTrack clone() => new GridTrack(sizing.clone()); /** The min sizing function for the track. */ SizingFunction get minSizing => sizing.min; /** The min sizing function for the track. */ SizingFunction get maxSizing => sizing.max; num get end => start + usedBreadth; bool get isFractional => minSizing.isFraction || maxSizing.isFraction; } /** Represents the grid-row-align or grid-column-align. */ class GridItemAlignment { // TODO(jmesserly): should this be stored as an int for performance? final String value; // 'start' | 'end' | 'center' | 'stretch' GridItemAlignment.fromString(String value) : this.value = (value == null) ? 'stretch' : value { switch (this.value) { case 'start': case 'end': case 'center': case 'stretch': break; default: throw new UnsupportedError('invalid row/column alignment "$value"'); } } _GridLocation align(_GridLocation span, int size) { switch (value) { case 'start': return new _GridLocation(span.start, size); case 'end': return new _GridLocation(span.end - size, size); case 'center': size = Math.min(size, span.length); num center = span.start + span.length / 2; num left = center - size / 2; return new _GridLocation(left.round(), size); case 'stretch': return span; } } } /** * Represents a grid-template. Used in conjunction with a grid-cell to * place cells in the grid, without needing to specify the exact row/column. */ class GridTemplate { final Map _rects; final int _numRows; GridTemplate(List rows) : _rects = new Map(), _numRows = rows.length { _buildRects(rows); } /** Scans the template strings and computes bounds for each one. */ void _buildRects(List templateRows) { for (int r = 0; r < templateRows.length; r++) { String row = templateRows[r]; for (int c = 0; c < row.length; c++) { int cell = row.codeUnitAt(c); final rect = _rects[cell]; if (rect != null) { rect.add(r + 1, c + 1); } else { _rects[cell] = new _GridTemplateRect(cell, r + 1, c + 1); } } } // Finally, check that each rectangle is valid (i.e. all spaces filled) for (final rect in _rects.values) { rect.checkValid(); } } /** * Looks up the given cell in the template, and returns the rect. */ _GridTemplateRect lookupCell(String cell) { if (cell.length != 1) { throw new UnsupportedError( 'grid-cell "$cell" must be a one character string'); } final rect = _rects[cell.codeUnitAt(0)]; if (rect == null) { throw new UnsupportedError( 'grid-cell "$cell" not found in parent\'s grid-template'); } return rect; } } /** Used by GridTemplate to track a single cell's bounds. */ class _GridTemplateRect { int row, column, rowSpan, columnSpan, _count, _char; _GridTemplateRect(this._char, this.row, this.column) : rowSpan = 1, columnSpan = 1, _count = 1 {} void add(int r, int c) { assert(r >= row && c >= column); _count++; rowSpan = Math.max(rowSpan, r - row + 1); columnSpan = Math.max(columnSpan, c - column + 1); } void checkValid() { int expected = rowSpan * columnSpan; if (expected != _count) { // TODO(jmesserly): not sure if we should throw here, due to CSS's // permissiveness. At the moment we're noisy about errors. String cell = new String.fromCharCodes([_char]); throw new UnsupportedError('grid-template "$cell"' ' is not square, expected $expected cells but got $_count'); } } } /** * Used to return a row/column and span during parsing of grid-row and * grid-column during parsing. */ class _GridLocation { final int start, length; _GridLocation(this.start, this.length) {} int get end => start + length; }