Commit 27970bd8 authored by Adam Barth's avatar Adam Barth Committed by GitHub

Remove package:flutter/cassowary.dart (#7350)

We didn't end up using this mechanism.
parent cd09370c
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This example shows how to use the Cassowary autolayout system directly in the
// underlying render tree.
import 'package:flutter/cassowary.dart' as al;
import 'package:flutter/rendering.dart';
class _MyAutoLayoutDelegate extends AutoLayoutDelegate {
AutoLayoutRect p1 = new AutoLayoutRect();
AutoLayoutRect p2 = new AutoLayoutRect();
AutoLayoutRect p3 = new AutoLayoutRect();
AutoLayoutRect p4 = new AutoLayoutRect();
@override
List<al.Constraint> getConstraints(AutoLayoutRect parent) {
return <al.Constraint>[
// Sum of widths of each box must be equal to that of the container
parent.width.equals(p1.width + p2.width + p3.width),
// The boxes must be stacked left to right
p1.right <= p2.left,
p2.right <= p3.left,
// The widths of the first and the third boxes should be equal
p1.width.equals(p3.width),
// The width of the first box should be twice as much as that of the second
p1.width.equals(p2.width * al.cm(2.0)),
// The height of the three boxes should be equal to that of the container
p1.height.equals(p2.height),
p2.height.equals(p3.height),
p3.height.equals(parent.height),
// The fourth box should be half as wide as the second and must be attached
// to the right edge of the same (by its center)
p4.width.equals(p2.width / al.cm(2.0)),
p4.height.equals(al.cm(50.0)),
p4.horizontalCenter.equals(p2.right),
p4.verticalCenter.equals(p2.height / al.cm(2.0)),
];
}
@override
bool shouldUpdateConstraints(AutoLayoutDelegate oldDelegate) => true;
}
void main() {
RenderDecoratedBox c1 = new RenderDecoratedBox(
decoration: new BoxDecoration(backgroundColor: const Color(0xFFFF0000))
);
RenderDecoratedBox c2 = new RenderDecoratedBox(
decoration: new BoxDecoration(backgroundColor: const Color(0xFF00FF00))
);
RenderDecoratedBox c3 = new RenderDecoratedBox(
decoration: new BoxDecoration(backgroundColor: const Color(0xFF0000FF))
);
RenderDecoratedBox c4 = new RenderDecoratedBox(
decoration: new BoxDecoration(backgroundColor: const Color(0xFFFFFFFF))
);
_MyAutoLayoutDelegate delegate = new _MyAutoLayoutDelegate();
RenderAutoLayout root = new RenderAutoLayout(
delegate: delegate,
children: <RenderBox>[c1, c2, c3, c4]
);
AutoLayoutParentData parentData1 = c1.parentData;
AutoLayoutParentData parentData2 = c2.parentData;
AutoLayoutParentData parentData3 = c3.parentData;
AutoLayoutParentData parentData4 = c4.parentData;
parentData1.rect = delegate.p1;
parentData2.rect = delegate.p2;
parentData3.rect = delegate.p3;
parentData4.rect = delegate.p4;
new RenderingFlutterBinding(root: root);
}
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This example shows how to use the Cassowary autolayout system with widgets.
import 'package:flutter/cassowary.dart' as al;
import 'package:flutter/widgets.dart';
class _MyAutoLayoutDelegate extends AutoLayoutDelegate {
AutoLayoutRect p1 = new AutoLayoutRect();
AutoLayoutRect p2 = new AutoLayoutRect();
AutoLayoutRect p3 = new AutoLayoutRect();
AutoLayoutRect p4 = new AutoLayoutRect();
@override
List<al.Constraint> getConstraints(AutoLayoutRect parent) {
return <al.Constraint>[
// Sum of widths of each box must be equal to that of the container
parent.width.equals(p1.width + p2.width + p3.width),
// The boxes must be stacked left to right
p1.right <= p2.left,
p2.right <= p3.left,
// The widths of the first and the third boxes should be equal
p1.width.equals(p3.width),
// The width of the first box should be twice as much as that of the second
p1.width.equals(p2.width * al.cm(2.0)),
// The height of the three boxes should be equal to that of the container
p1.height.equals(p2.height),
p2.height.equals(p3.height),
p3.height.equals(parent.height),
// The fourth box should be half as wide as the second and must be attached
// to the right edge of the same (by its center)
p4.width.equals(p2.width / al.cm(2.0)),
p4.height.equals(al.cm(50.0)),
p4.horizontalCenter.equals(p2.right),
p4.verticalCenter.equals(p2.height / al.cm(2.0)),
];
}
@override
bool shouldUpdateConstraints(_MyAutoLayoutDelegate oldDelegate) => true;
}
class ColoredBoxes extends StatefulWidget {
@override
_ColoredBoxesState createState() => new _ColoredBoxesState();
}
class _ColoredBoxesState extends State<ColoredBoxes> {
final _MyAutoLayoutDelegate delegate = new _MyAutoLayoutDelegate();
@override
Widget build(BuildContext context) {
return new AutoLayout(
delegate: delegate,
children: <Widget>[
new AutoLayoutChild(
rect: delegate.p1,
child: new DecoratedBox(
decoration: new BoxDecoration(backgroundColor: const Color(0xFFFF0000))
)
),
new AutoLayoutChild(
rect: delegate.p2,
child: new DecoratedBox(
decoration: new BoxDecoration(backgroundColor: const Color(0xFF00FF00))
)
),
new AutoLayoutChild(
rect: delegate.p3,
child: new DecoratedBox(
decoration: new BoxDecoration(backgroundColor: const Color(0xFF0000FF))
)
),
new AutoLayoutChild(
rect: delegate.p4,
child: new DecoratedBox(
decoration: new BoxDecoration(backgroundColor: const Color(0xFFFFFFFF))
)
),
]
);
}
}
void main() {
runApp(new ColoredBoxes());
}
......@@ -8,7 +8,6 @@
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/animation/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/cassowary/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/cupertino/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/engine/packages" />
<excludeFolder url="file://$MODULE_DIR$/test/examples/packages" />
......@@ -30,4 +29,4 @@
<orderEntry type="library" name="Dart SDK" level="application" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>
\ No newline at end of file
</module>
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// An implementation of the Cassowary constraint solving algorithm in Dart.
///
/// To use, import `package:flutter/cassowary.dart`.
///
/// This is used by the [RenderAutoLayout] render object in the rendering
/// library and by the [AutoLayout] widget in the widget library.
///
/// See also:
///
/// * <https://en.wikipedia.org/wiki/Cassowary_(software)>
/// * <https://constraints.cs.washington.edu/solvers/cassowary-tochi.pdf>
library cassowary;
export 'src/cassowary/constraint.dart';
export 'src/cassowary/expression.dart';
export 'src/cassowary/term.dart';
export 'src/cassowary/equation_member.dart';
export 'src/cassowary/constant_member.dart';
export 'src/cassowary/solver.dart';
export 'src/cassowary/result.dart';
export 'src/cassowary/parser_exception.dart';
export 'src/cassowary/param.dart';
export 'src/cassowary/priority.dart';
......@@ -23,7 +23,6 @@
library rendering;
export 'src/rendering/animated_size.dart';
export 'src/rendering/auto_layout.dart';
export 'src/rendering/binding.dart';
export 'src/rendering/block.dart';
export 'src/rendering/box.dart';
......
Cassowary in Dart
=================
This is an implementation of the Cassowary constraint solving algorithm in Dart. The initial implementation was based on the [Kiwi toolkit](https://github.com/nucleic/kiwi) written in C++. Implements a subset of the functionality described in the [Cassowary paper](https://constraints.cs.washington.edu/solvers/cassowary-tochi.pdf) and makes specific affordances for the needs of [Flutter](http://flutter.io/)
# The Solver
A solver is the object that accepts constraints and updates member variables in an attempt to satisfy the same. It is rarely the case that the user will have to create a solver. Instead, one will be vended by Flutter.
# Parameters
In order to create constraints, the user needs to take specific parameter objects vended by elements in the view hierarchy and create expression from these. Constraints can then be obtained from these expressions. If the solver needs to update these parameters to satisfy constraints, it will call callbacks on these parameters.
# Constructing Constraints
A constraint is a linear equation of the form `ax + by + cz + ... + k == 0`. Constraints need not be equality relationships. Less/greater-than-or-equal-to (`<=` or `>=`) relationships can also be specified. In addition, each constraint is specified at a given priority to help resolve constraint ambiguities. A system can also be overconstrained.
The constraint as a whole is represented by an instance of the `Constraint` object. This in turn references an instance of an `Expression` (`ax + by + cz + ... k`), a realtionship and the finally the priority.
Each expression in turn is made up of a list of `Term`s and a constant. The term `ax` has the coefficient `a` and `Variable` `x`. The `Param` that is vended to the user is nothing but a wrapper for this variable and deals with detecting changes to it and updating the underlying view in the hierarchy.
Once the user obtains specific parameter objects, it is straightforward to create constraints. The following example sets up constraints that specify that the width of the element must be at least 100 units. It is assumed that the `left` and `right` `Param` objects have been obtained from the view in question.
```
Constraint widthAtLeast100 = right - left >= CM(100.0)
```
Lets go over this one step at a time: The expression `right - left` creates an instance of an `Expression` object. The expression consists of two terms. The `right` and `left` params wrap variables. The coefficients are 1.0 and -1.0 respectively and the constant -100.0. Constants need to be decorated with `CM` to aid with the operator overloading mechanism in Dart.
All variables are unrestricted. So there is nothing preventing the solver from making the left and right edges negative. We can specify our preference against this by specifying another constraint like so:
```
Constraint edgesPositive = (left >= CM(0.0))
```
When we construct these constraints for the solver, they are created at the default `Priority.required`. This means that the solver will resist adding constraints where there are ambiguities between two required constraints. To specify a weaker priority, you can use the `priority` setter or use the `|` symbol with the priority while constructing the constraint. Like so:
```
Constraint edgesPositive = (left >= CM(0.0) | Priority.weak)
```
Once the set of constraints are constructed, they are added to the solver and the results of the solution flushed out.
```
solver.addConstraints([widthAtLeast100, edgesPositive])
..flushVariableUpdates();
```
# Edit Constraints
When updates need to be applied to parameters that are a part of the solver, edit variables may be used. To illustrate this, we try to express the following case in terms of constraints and their update: On mouse down, we want to update the midpoint of our view and have the `left` and `right` parameters automatically updated (subject to the constraints already setup).
We create a parameter that we will use to represent the mouse coordinate.
```
Param mid = new Param(coordinate);
```
Then, we add a constraint that expresses the midpoint in terms of the parameters we already have.
```
solver.addConstraint(left + right == mid * CM(2.0));
```
Then, we specify that we intend to edit the midpoint. As we get updates, we tell the solver to satisfy all other constraints (admittedly our example is trivial).
```
solver.addEditVariable(mid, Priority.strong);
```
and finally
```
solver.flushVariableUpdates();
```
// Copyright 2016 The Chromium 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 'expression.dart';
import 'equation_member.dart';
import 'term.dart';
/// A member of a [Constraint] [Expression] that represent a constant at the
/// time the [Constraint] is added to the solver.
class ConstantMember extends EquationMember {
/// Creates a [ConstantMember] object.
///
/// The [cm] convenience method may be a more convenient way to create a
/// [ConstantMember] object.
ConstantMember(this.value);
@override
Expression asExpression() => new Expression(<Term>[], this.value);
@override
final double value;
@override
bool get isConstant => true;
}
/// Creates a [ConstantMember].
///
/// This is a convenience method to make cassowary expressions less verbose.
ConstantMember cm(double value) => new ConstantMember(value);
// Copyright 2016 The Chromium 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 'priority.dart';
import 'expression.dart';
/// Relationships between [Constraint] expressions.
///
/// A [Constraint] is created by specifying a relationship between two
/// expressions. The [Solver] tries to satisfy this relationship after the
/// [Constraint] has been added to it at a set priority.
enum Relation {
/// The relationship between the left and right hand sides of the expression
/// is `==`, (lhs == rhs).
equalTo,
/// The relationship between the left and right hand sides of the expression
/// is `<=`, (lhs <= rhs).
lessThanOrEqualTo,
/// The relationship between the left and right hand sides of the expression
/// is `>=`, (lhs => rhs).
greaterThanOrEqualTo,
}
/// A relationship between two expressions (represented by [Expression]) that
/// the [Solver] tries to hold true. In case of ambiguities, the [Solver] will
/// use priorities to determine [Constraint] precedence. Once a [Constraint] is
/// added to the [Solver], this [Priority] cannot be changed.
class Constraint {
/// Creates a new [Constraint] by specifying a single [Expression]. This
/// assumes that the right hand side [Expression] is the constant zero.
/// (`<expression> <relation> <0>`)
Constraint(this.expression, this.relation);
/// The [Relation] between a [Constraint] [Expression] and zero.
final Relation relation;
/// The [Constraint] [Expression]. The [Expression] on the right hand side of
/// constraint must be zero. If the [Expression] on the right is not zero,
/// it must be negated from the left hand [Expression] before a [Constraint]
/// can be created.
final Expression expression;
/// The [Constraint] [Priority]. The [Priority] can only be modified when the
/// [Constraint] is being created. Once it is added to the solver,
/// modifications to the [Constraint] [Priority] will have no effect on the
/// how the solver evaluates the constraint.
double priority = Priority.required;
/// The operator `|` is overloaded as a convenience so that constraint
/// priorities can be specifed along with the [Constraint] expression.
///
/// For example: `ax + by + cx <= 0 | Priority.weak`. See [Priority].
Constraint operator |(double p) => this..priority = p;
@override
String toString() {
StringBuffer buffer = new StringBuffer();
buffer.write(expression.toString());
switch (relation) {
case Relation.equalTo:
buffer.write(' == 0 ');
break;
case Relation.greaterThanOrEqualTo:
buffer.write(' >= 0 ');
break;
case Relation.lessThanOrEqualTo:
buffer.write(' <= 0 ');
break;
}
buffer.write(' | priority = $priority');
if (priority == Priority.required)
buffer.write(' (required)');
return buffer.toString();
}
}
// Copyright 2016 The Chromium 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 'expression.dart';
import 'constraint.dart';
/// A member that can be used to construct an [Expression] that may be
/// used to create a constraint. This is to facilitate the easy creation of
/// constraints. The use of the operator overloads is completely optional and
/// is only meant as a convenience. The [Constraint] expressions can be created
/// by manually creating instance of [Constraint] variables, then terms and
/// combining those to create expression.
abstract class EquationMember {
/// The representation of this member after it is hoisted to be an
/// expression.
Expression asExpression();
/// Returns if this member is a constant. Constant members can be combined
/// more easily without making the expression non-linear. This makes them
/// easier to use with multiplication and division operators. Constant
/// expression that have zero value may also eliminate other expressions from
/// the solver when used with the multiplication operator.
bool get isConstant;
/// The current constant value of this member. After a [Solver] flush, this is
/// value read by entities outside the [Solver].
double get value;
/// Creates a [Constraint] by using this member as the left hand side
/// expression and the argument as the right hand side [Expression] of a
/// [Constraint] with a [Relation.greaterThanOrEqualTo] relationship between
/// the two.
///
/// For example: `right - left >= cm(200.0)` would read, "the width of the
/// object is at least 200."
Constraint operator >=(EquationMember m) => asExpression() >= m;
/// Creates a [Constraint] by using this member as the left hand side
/// expression and the argument as the right hand side [Expression] of a
/// [Constraint] with a [Relation.lessThanOrEqualTo] relationship between the
/// two.
///
/// For example: `rightEdgeOfA <= leftEdgeOfB` would read, "the entities A and
/// B are stacked left to right."
Constraint operator <=(EquationMember m) => asExpression() <= m;
/// Creates a [Constraint] by using this member as the left hand side
/// expression and the argument as the right hand side [Expression] of a
/// [Constraint] with a [Relation.equalTo] relationship between the two.
///
/// For example: `topEdgeOfBoxA + cm(10.0) == topEdgeOfBoxB` woud read,
/// "the entities A and B have a padding on top of 10."
Constraint equals(EquationMember m) => asExpression().equals(m);
/// Creates a [Expression] by adding this member with the argument. Both
/// members may need to be hoisted to expressions themselves before this can
/// occur.
///
/// For example: `(left + right) / cm(2.0)` can be used as an [Expression]
/// equivalent of the `midPointX` property.
Expression operator +(EquationMember m) => asExpression() + m;
/// Creates a [Expression] by subtracting the argument from this member. Both
/// members may need to be hoisted to expressions themselves before this can
/// occur.
///
/// For example: `right - left` can be used as an [Expression]
/// equivalent of the `width` property.
Expression operator -(EquationMember m) => asExpression() - m;
/// Creates a [Expression] by multiplying this member with the argument. Both
/// members may need to be hoisted to expressions themselves before this can
/// occur.
///
/// Warning: This operation may throw a [ParserException] if the resulting
/// expression is no longer linear. This is because a non-linear [Expression]
/// may not be used to create a constraint. At least one of the [Expression]
/// members must evaluate to a constant.
///
/// For example: `((left + right) >= (cm(2.0) * mid)` declares a `midpoint`
/// constraint. Notice that at least one the members of the right hand
/// `Expression` is a constant.
Expression operator *(EquationMember m) => asExpression() * m;
/// Creates a [Expression] by dividing this member by the argument. Both
/// members may need to be hoisted to expressions themselves before this can
/// occur.
///
/// Warning: This operation may throw a [ParserException] if the resulting
/// expression is no longer linear. This is because a non-linear [Expression]
/// may not be used to create a constraint. The divisor (i.e. the argument)
/// must evaluate to a constant.
///
/// For example: `((left + right) / cm(2.0) >= mid` declares a `midpoint`
/// constraint. Notice that the divisor of the left hand [Expression] is a
/// constant.
Expression operator /(EquationMember m) => asExpression() / m;
}
// Copyright 2016 The Chromium 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 'constant_member.dart';
import 'constraint.dart';
import 'equation_member.dart';
import 'param.dart';
import 'parser_exception.dart';
import 'term.dart';
class _Multiplication {
const _Multiplication(this.multiplier, this.multiplicand);
final Expression multiplier;
final double multiplicand;
}
/// The representation of a linear [Expression] that can be used to create a
/// constraint.
class Expression extends EquationMember {
/// Creates a new linear [Expression] using the given terms and constant.
Expression(this.terms, this.constant);
/// Creates a new linear [Expression] by copying the terms and constant of
/// another expression.
Expression.fromExpression(Expression expr)
: this.terms = new List<Term>.from(expr.terms),
this.constant = expr.constant;
/// The list of terms in this linear expression. Terms in a an [Expression]
/// must have only one [Variable] (indeterminate) and a degree of 1.
final List<Term> terms;
/// The constant portion of this linear expression. This is just another
/// [Term] with no [Variable].
final double constant;
@override
Expression asExpression() => this;
@override
bool get isConstant => terms.isEmpty;
@override
double get value => terms.fold(constant, (double value, Term term) => value + term.value);
@override
Constraint operator >=(EquationMember value) {
return _createConstraint(value, Relation.greaterThanOrEqualTo);
}
@override
Constraint operator <=(EquationMember value) {
return _createConstraint(value, Relation.lessThanOrEqualTo);
}
@override
Constraint equals(EquationMember value) {
return _createConstraint(value, Relation.equalTo);
}
Constraint _createConstraint(EquationMember /* rhs */ value, Relation relation) {
if (value is ConstantMember) {
return new Constraint(
new Expression(new List<Term>.from(terms), constant - value.value),
relation
);
}
if (value is Param) {
List<Term> newTerms = new List<Term>.from(terms)
..add(new Term(value.variable, -1.0));
return new Constraint(new Expression(newTerms, constant), relation);
}
if (value is Term) {
List<Term> newTerms = new List<Term>.from(terms)
..add(new Term(value.variable, -value.coefficient));
return new Constraint(new Expression(newTerms, constant), relation);
}
if (value is Expression) {
List<Term> newTerms = value.terms.fold(
new List<Term>.from(terms),
(List<Term> list, Term t) {
return list..add(new Term(t.variable, -t.coefficient));
}
);
return new Constraint(
new Expression(newTerms, constant - value.constant),
relation
);
}
assert(false);
return null;
}
@override
Expression operator +(EquationMember m) {
if (m is ConstantMember)
return new Expression(new List<Term>.from(terms), constant + m.value);
if (m is Param) {
return new Expression(
new List<Term>.from(terms)..add(new Term(m.variable, 1.0)),
constant
);
}
if (m is Term)
return new Expression(new List<Term>.from(terms)..add(m), constant);
if (m is Expression) {
return new Expression(
new List<Term>.from(terms)..addAll(m.terms),
constant + m.constant
);
}
assert(false);
return null;
}
@override
Expression operator -(EquationMember m) {
if (m is ConstantMember)
return new Expression(new List<Term>.from(terms), constant - m.value);
if (m is Param) {
return new Expression(
new List<Term>.from(terms)..add(new Term(m.variable, -1.0)),
constant
);
}
if (m is Term) {
return new Expression(new List<Term>.from(terms)
..add(new Term(m.variable, -m.coefficient)), constant);
}
if (m is Expression) {
List<Term> copiedTerms = new List<Term>.from(terms);
for (Term t in m.terms)
copiedTerms.add(new Term(t.variable, -t.coefficient));
return new Expression(copiedTerms, constant - m.constant);
}
assert(false);
return null;
}
@override
Expression operator *(EquationMember m) {
_Multiplication args = _findMulitplierAndMultiplicand(m);
if (args == null) {
throw new ParserException(
'Could not find constant multiplicand or multiplier',
<EquationMember>[this, m]
);
}
return args.multiplier._applyMultiplicand(args.multiplicand);
}
@override
Expression operator /(EquationMember m) {
if (!m.isConstant) {
throw new ParserException(
'The divisor was not a constant expression', <EquationMember>[this, m]);
}
return this._applyMultiplicand(1.0 / m.value);
}
_Multiplication _findMulitplierAndMultiplicand(EquationMember m) {
// At least one of the the two members must be constant for the resulting
// expression to be linear
if (!this.isConstant && !m.isConstant)
return null;
if (this.isConstant)
return new _Multiplication(m.asExpression(), this.value);
if (m.isConstant)
return new _Multiplication(this.asExpression(), m.value);
assert(false);
return null;
}
Expression _applyMultiplicand(double m) {
List<Term> newTerms = terms.fold(
new List<Term>(),
(List<Term> list, Term term) {
return list..add(new Term(term.variable, term.coefficient * m));
}
);
return new Expression(newTerms, constant * m);
}
@override
String toString() {
StringBuffer buffer = new StringBuffer();
terms.forEach((Term t) => buffer.write('$t'));
if (constant != 0.0) {
buffer.write(constant.sign > 0.0 ? '+' : '-');
buffer.write(constant.abs());
}
return buffer.toString();
}
}
// Copyright 2016 The Chromium 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 'equation_member.dart';
import 'expression.dart';
import 'term.dart';
/// A [Variable] inside the layout [Solver]. It represents an indeterminate
/// in the [Expression] that is used to create the [Constraint]. If any entity
/// is interested in watching updates to the value of this indeterminate,
/// it can assign a watcher as the `owner`.
class Variable {
/// Creates a new [Variable] with the given constant value.
Variable(this.value);
/// The current value of the variable.
double value;
/// An optional name given to the variable. This is useful in debugging
/// [Solver] state.
String name;
/// Variables represent state inside the solver. This state is usually of
/// interest to some entity outside the solver. Such entities can (optionally)
/// associate themselves with these variables. This means that when solver
/// is flushed, it is easy to obtain a reference to the entity the variable
/// is associated with.
Param get owner => _owner;
Param _owner;
/// Used by the [Solver] to apply updates to this variable. Only updated
/// variables show up in [Solver] flush results.
bool applyUpdate(double updated) {
bool res = updated != value;
value = updated;
return res;
}
}
/// A [Param] wraps a [Variable] and makes it suitable to be used in an
/// expression.
class Param extends EquationMember {
/// Creates a new [Param] with the specified constant value.
Param([double value = 0.0]) : variable = new Variable(value) {
variable._owner = this;
}
/// Creates a new [Param] with the specified constant value that is tied
/// to some object outside the solver.
Param.withContext(dynamic context, [double value = 0.0])
: variable = new Variable(value),
context = context {
variable._owner = this;
}
/// The [Variable] associated with this [Param].
final Variable variable;
/// Some object outside the [Solver] that is associated with this Param.
dynamic context;
@override
Expression asExpression() => new Expression(<Term>[new Term(variable, 1.0)], 0.0);
@override
bool get isConstant => false;
@override
double get value => variable.value;
/// The name of the [Variable] associated with this [Param].
String get name => variable.name;
/// Set the name of the [Variable] associated with this [Param].
set name(String name) { variable.name = name; }
}
// Copyright 2016 The Chromium 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 'equation_member.dart';
/// Exception thrown when attempting to create a non-linear expression.
///
/// During the creation of constraints or expressions using the overloaded
/// operators, it may be possible to end up with non-linear expressions. Such
/// expressions are not suitable for [Constraint] creation because the [Solver]
/// will reject the same. A [ParserException] is thrown when a developer tries
/// to create such an expression.
///
/// The only cases where this is possible is when trying to multiply two
/// expressions where at least one of them is not a constant expression, or,
/// when trying to divide two expressions where the divisor is not constant.
class ParserException implements Exception {
/// Creates a new [ParserException] with a given message and a list of the
/// offending member for debugging purposes.
ParserException(this.message, this.members);
/// A detailed message describing the exception.
final String message;
/// The members that caused the exception.
List<EquationMember> members;
@override
String toString() {
if (message == null)
return 'Error while parsing constraint or expression';
return 'Error: "$message" while trying to parse constraint or expression';
}
}
// Copyright 2016 The Chromium 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';
/// Utility functions for managing cassowary priorities.
///
/// Priorities in cassowary expressions are internally expressed as a number
/// between 0 and 1,000,000,000. These numbers can be created by using the
/// [Priority.create] static method.
class Priority {
/// The [Priority] level that, by convention, is the highest allowed
/// [Priority] level (1,000,000,000).
static final double required = create(1e3, 1e3, 1e3);
/// A [Priority] level that is below the [required] level but still near it
/// (1,000,000).
static final double strong = create(1.0, 0.0, 0.0);
/// A [Priority] level logarithmically in the middle of [strong] and [weak]
/// (1,000).
static final double medium = create(0.0, 1.0, 0.0);
/// A [Priority] level that, by convention, is the lowest allowed [Priority]
/// level (1).
static final double weak = create(0.0, 0.0, 1.0);
/// Computes a [Priority] level by combining three numbers in the range
/// 0..1000.
///
/// The first number is a multiple of [strong].
///
/// The second number is a multiple of [medium].
///
/// The third number is a multiple of [weak].
///
/// By convention, at least one of these numbers should be equal to or greater
/// than 1.
static double create(double a, double b, double c) {
double result = 0.0;
result += max(0.0, min(1e3, a)) * 1e6;
result += max(0.0, min(1e3, b)) * 1e3;
result += max(0.0, min(1e3, c));
return result;
}
}
// Copyright 2016 The Chromium 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 'solver.dart';
/// Return values used by methods on the cassowary [Solver].
class Result {
const Result._(this.message, { bool isError: true }) : error = isError;
/// The human-readable string associated with this result.
///
/// This message is typically brief and intended for developers to help debug
/// erroneous expressions.
final String message;
/// Whether this [Result] represents an error (true) or not (false).
final bool error;
/// The result when the operation was successful.
static const Result success =
const Result._('Success', isError: false);
/// The result when the [Constraint] could not be added to the [Solver]
/// because it was already present in the solver.
static const Result duplicateConstraint =
const Result._('Duplicate constraint');
/// The result when the [Constraint] could not be added to the [Solver]
/// because it was unsatisfiable. Try lowering the [Priority] of the
/// [Constraint] and try again.
static const Result unsatisfiableConstraint =
const Result._('Unsatisfiable constraint');
/// The result when the [Constraint] could not be removed from the solver
/// because it was not present in the [Solver] to begin with.
static const Result unknownConstraint =
const Result._('Unknown constraint');
/// The result when could not add the edit [Variable] to the [Solver] because
/// it was already added to the [Solver] previously.
static const Result duplicateEditVariable =
const Result._('Duplicate edit variable');
/// The result when the [Constraint] constraint was added at an invalid
/// priority or an edit [Variable] was added at an invalid or required
/// priority.
static const Result badRequiredStrength =
const Result._('Bad required strength');
/// The result when the edit [Variable] could not be removed from the solver
/// because it was not present in the [Solver] to begin with.
static const Result unknownEditVariable =
const Result._('Unknown edit variable');
}
This diff is collapsed.
// Copyright 2016 The Chromium 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 'equation_member.dart';
import 'expression.dart';
import 'param.dart';
/// Represents a single term in an expression. This term contains a single
/// indeterminate and has degree 1.
class Term extends EquationMember {
/// Creates term with the given [Variable] and coefficient.
Term(this.variable, this.coefficient);
/// The [Variable] (or indeterminate) portion of this term. Variables are
/// usually tied to an opaque object (via its `context` property). On a
/// [Solver] flush, these context objects of updated variables are returned by
/// the solver. An external entity can then choose to interpret these values
/// in what manner it sees fit.
final Variable variable;
/// The coefficient of this term. Before addition of the [Constraint] to the
/// solver, terms with a zero coefficient are dropped.
final double coefficient;
@override
Expression asExpression() =>
new Expression(<Term>[new Term(this.variable, this.coefficient)], 0.0);
@override
bool get isConstant => false;
@override
double get value => coefficient * variable.value;
@override
String toString() {
StringBuffer buffer = new StringBuffer();
buffer.write(coefficient.sign > 0.0 ? "+" : "-");
if (coefficient.abs() != 1.0) {
buffer.write(coefficient.abs());
buffer.write("*");
}
buffer.write(variable);
return buffer.toString();
}
}
This diff is collapsed.
// Copyright 2015 The Chromium 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 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'framework.dart';
export 'package:flutter/rendering.dart' show
AutoLayoutRect,
AutoLayoutDelegate;
/// A widget that uses the cassowary constraint solver to automatically size and position children.
class AutoLayout extends MultiChildRenderObjectWidget {
/// Creates a widget that uses the cassowary constraint solver to automatically size and position children.
AutoLayout({
Key key,
this.delegate,
List<Widget> children: const <Widget>[]
}) : super(key: key, children: children);
/// The delegate that generates constraints for the layout.
///
/// If the delgate is null, the layout is unconstrained.
final AutoLayoutDelegate delegate;
@override
RenderAutoLayout createRenderObject(BuildContext context) => new RenderAutoLayout(delegate: delegate);
@override
void updateRenderObject(BuildContext context, RenderAutoLayout renderObject) {
renderObject.delegate = delegate;
}
}
/// A widget that provides constraints for a child of an [AutoLayout] widget.
///
/// An [AutoLayoutChild] widget must be a descendant of an [AutoLayout], and
/// the path from the [AutoLayoutChild] widget to its enclosing [AutoLayout]
/// must contain only [StatelessWidget]s or [StatefulWidget]s (not other kinds
/// of widgets, like [RenderObjectWidget]s).
class AutoLayoutChild extends ParentDataWidget<AutoLayout> {
/// Creates a widget that provides constraints for a child of an [AutoLayout] widget.
///
/// The object identity of the [rect] argument must be unique among children
/// of a given [AutoLayout] widget.
AutoLayoutChild({
AutoLayoutRect rect,
@required Widget child
}) : rect = rect,
super(key: rect != null ? new ObjectKey(rect) : null, child: child);
/// The constraints to use for this child.
///
/// The object identity of the [rect] object must be unique among children of
/// a given [AutoLayout] widget.
///
/// If null, the child's size and position are unconstrained.
final AutoLayoutRect rect;
@override
void applyParentData(RenderObject renderObject) {
assert(renderObject.parentData is AutoLayoutParentData);
final AutoLayoutParentData parentData = renderObject.parentData;
// AutoLayoutParentData filters out redundant writes and marks needs layout
// as appropriate.
parentData.rect = rect;
}
}
......@@ -10,7 +10,6 @@ library widgets;
export 'src/widgets/animated_cross_fade.dart';
export 'src/widgets/animated_size.dart';
export 'src/widgets/app.dart';
export 'src/widgets/auto_layout.dart';
export 'src/widgets/banner.dart';
export 'src/widgets/basic.dart';
export 'src/widgets/binding.dart';
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment