blob: a3cdf0b59a82649e7b0c59ac65986cfeb61b894d [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math';
import 'package:flutter/material.dart';
import 'utils.dart';
enum TileType { content, row, column, custom }
/// Defines a model for a tile. It is a tree data structure where each tile
/// model holds a reference to its parent and a list of children. If the type
/// of tile, [TileType] is [TileType.content], it is a leaf node tile.
///
/// The [content] of tile holds a reference to an arbitrary object, that is
/// passed to the caller during the construction of the tile's chrome widget.
class TileModel<T> extends ChangeNotifier {
TileModel<T> parent;
TileType _type;
T content;
double flex;
Offset position;
Size _minSize;
List<TileModel<T>> tiles;
TileModel({
@required TileType type,
this.parent,
this.content,
this.tiles,
this.flex = 1,
this.position = Offset.zero,
Size minSize = Size.zero,
}) : _minSize = minSize,
_type = type {
tiles ??= <TileModel<T>>[];
traverse(tile: this, callback: (tile, parent) => tile.parent = parent);
}
TileType get type => _type;
set type(TileType value) {
_type = value;
notifyListeners();
}
bool get isContent => type == TileType.content;
bool get isFloating => parent == null;
bool get isRow => type == TileType.row;
bool get isColumn => type == TileType.column;
bool get isCustom => type == TileType.custom;
bool get isCollection => isRow || isColumn || isCustom;
bool get isEmpty =>
isCollection && tiles.isEmpty || isContent && content == null;
double _width = 0;
double get width => parent?.type == TileType.column ? _width * flex : _width;
set width(double value) => _width = value;
double _height = 0;
double get height => parent?.type == TileType.row ? _height * flex : _height;
set height(double value) => _height = value;
void insert(int index, TileModel<T> tile) {
tile.parent = this;
tiles.insert(index, tile);
notifyListeners();
}
void add(TileModel<T> tile) => insert(tiles.length, tile);
void remove(TileModel<T> tile) {
assert(tiles.contains(tile));
tile.parent = null;
tiles.remove(tile);
notifyListeners();
}
void resize(double delta) {
parent.type == TileType.column
? flex += flex / width * delta
: flex += flex / height * delta;
notifyListeners();
}
Size get minSize => isContent
? _minSize
: tiles.map((tile) => tile.minSize).reduce(
(s1, s2) => Size(max(s1.width, s2.width), max(s1.height, s2.height)));
bool get overflowed => parent.type == TileType.column
? width < minSize.width
: height < minSize.height;
void notify() => notifyListeners();
@override
String toString() => isContent ? '$content' : '$type $tiles';
}