mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
We'll need this for compositing because we need to switch out the sky.Canvas when we switch compositing layers.
230 lines
7.5 KiB
Dart
230 lines
7.5 KiB
Dart
// 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:cassowary/cassowary.dart' as al;
|
|
import 'package:sky/rendering/box.dart';
|
|
import 'package:sky/rendering/object.dart';
|
|
|
|
/// Hosts the edge parameters and vends useful methods to construct expressions
|
|
/// for constraints. Also sets up and manages implicit constraints and edit
|
|
/// variables. Used as a mixin by layout containers and parent data instances
|
|
/// of render boxes taking part in auto layout
|
|
abstract class _AutoLayoutParamMixin {
|
|
// Ideally, the edges would all be final, but then they would have to be
|
|
// initialized before the constructor. Not sure how to do that using a Mixin
|
|
al.Param _leftEdge;
|
|
al.Param _rightEdge;
|
|
al.Param _topEdge;
|
|
al.Param _bottomEdge;
|
|
|
|
List<al.Constraint> _implicitConstraints;
|
|
|
|
al.Param get leftEdge => _leftEdge;
|
|
al.Param get rightEdge => _rightEdge;
|
|
al.Param get topEdge => _topEdge;
|
|
al.Param get bottomEdge => _bottomEdge;
|
|
|
|
al.Expression get width => _rightEdge - _leftEdge;
|
|
al.Expression get height => _bottomEdge - _topEdge;
|
|
|
|
al.Expression get horizontalCenter => (_leftEdge + _rightEdge) / al.cm(2.0);
|
|
al.Expression get verticalCenter => (_topEdge + _bottomEdge) / al.cm(2.0);
|
|
|
|
void _setupLayoutParameters(dynamic context) {
|
|
_leftEdge = new al.Param.withContext(context);
|
|
_rightEdge = new al.Param.withContext(context);
|
|
_topEdge = new al.Param.withContext(context);
|
|
_bottomEdge = new al.Param.withContext(context);
|
|
}
|
|
|
|
void _setupEditVariablesInSolver(al.Solver solver, double priority) {
|
|
solver.addEditVariables([
|
|
_leftEdge.variable,
|
|
_rightEdge.variable,
|
|
_topEdge.variable,
|
|
_bottomEdge.variable], priority);
|
|
}
|
|
|
|
void _applyEditsAtSize(al.Solver solver, Size size) {
|
|
solver.suggestValueForVariable(_leftEdge.variable, 0.0);
|
|
solver.suggestValueForVariable(_topEdge.variable, 0.0);
|
|
solver.suggestValueForVariable(_bottomEdge.variable, size.height);
|
|
solver.suggestValueForVariable(_rightEdge.variable, size.width);
|
|
}
|
|
|
|
/// Called when the solver has updated at least one of the layout parameters
|
|
/// of this object. The object is now responsible for applying this update to
|
|
/// it other properties (if necessary)
|
|
void _applyAutolayoutParameterUpdates();
|
|
|
|
/// Returns the set of implicit constraints that need to be applied to all
|
|
/// instances of this class when they are moved into a render object with an
|
|
/// active solver. If no implicit constraints needs to be applied, the object
|
|
/// may return null.
|
|
List<al.Constraint> _constructImplicitConstraints();
|
|
|
|
void _setupImplicitConstraints(al.Solver solver) {
|
|
List<al.Constraint> implicit = _constructImplicitConstraints();
|
|
|
|
if (implicit == null || implicit.length == 0) {
|
|
return;
|
|
}
|
|
|
|
al.Result result = solver.addConstraints(implicit);
|
|
assert(result == al.Result.success);
|
|
|
|
_implicitConstraints = implicit;
|
|
}
|
|
|
|
void _removeImplicitConstraints(al.Solver solver) {
|
|
if (_implicitConstraints == null || _implicitConstraints.length == 0) {
|
|
return;
|
|
}
|
|
|
|
al.Result result = solver.removeConstraints(_implicitConstraints);
|
|
assert(result == al.Result.success);
|
|
|
|
_implicitConstraints = null;
|
|
}
|
|
}
|
|
|
|
class AutoLayoutParentData extends BoxParentData
|
|
with ContainerParentDataMixin<RenderBox>, _AutoLayoutParamMixin {
|
|
|
|
AutoLayoutParentData(this._renderBox) {
|
|
_setupLayoutParameters(this);
|
|
}
|
|
|
|
final RenderBox _renderBox;
|
|
|
|
void _applyAutolayoutParameterUpdates() {
|
|
// This is called by the parent's layout function
|
|
// to lay our box out.
|
|
assert(_renderBox.parentData == this);
|
|
assert(_renderBox.parent is RenderAutoLayout);
|
|
assert((_renderBox.parent as RenderAutoLayout).debugDoingThisLayout); // TODO(ianh): Remove cast once the analyzer is cleverer
|
|
BoxConstraints size = new BoxConstraints.tightFor(
|
|
width: _rightEdge.value - _leftEdge.value,
|
|
height: _bottomEdge.value - _topEdge.value
|
|
);
|
|
_renderBox.layout(size);
|
|
position = new Point(_leftEdge.value, _topEdge.value);
|
|
}
|
|
|
|
List<al.Constraint> _constructImplicitConstraints() {
|
|
return [
|
|
_leftEdge >= al.cm(0.0), // The left edge must be positive.
|
|
_rightEdge >= _leftEdge, // Width must be positive.
|
|
];
|
|
}
|
|
|
|
}
|
|
|
|
class RenderAutoLayout extends RenderBox
|
|
with ContainerRenderObjectMixin<RenderBox, AutoLayoutParentData>,
|
|
RenderBoxContainerDefaultsMixin<RenderBox, AutoLayoutParentData>,
|
|
_AutoLayoutParamMixin {
|
|
|
|
RenderAutoLayout({ List<RenderBox> children }) {
|
|
_setupLayoutParameters(this);
|
|
_setupEditVariablesInSolver(_solver, al.Priority.required - 1);
|
|
addAll(children);
|
|
}
|
|
|
|
final al.Solver _solver = new al.Solver();
|
|
List<al.Constraint> _explicitConstraints = new List<al.Constraint>();
|
|
|
|
/// Adds all the given constraints to the solver. Either all constraints are
|
|
/// added or none
|
|
al.Result addConstraints(List<al.Constraint> constraints) {
|
|
al.Result result = _solver.addConstraints(constraints);
|
|
if (result == al.Result.success) {
|
|
markNeedsLayout();
|
|
_explicitConstraints.addAll(constraints);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// Add the given constraint to the solver.
|
|
al.Result addConstraint(al.Constraint constraint) {
|
|
al.Result result = _solver.addConstraint(constraint);
|
|
|
|
if (result == al.Result.success) {
|
|
markNeedsLayout();
|
|
_explicitConstraints.add(constraint);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// Removes all explicitly added constraints.
|
|
al.Result clearAllConstraints() {
|
|
al.Result result = _solver.removeConstraints(_explicitConstraints);
|
|
|
|
if (result == al.Result.success) {
|
|
markNeedsLayout();
|
|
_explicitConstraints = new List<al.Constraint>();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void adoptChild(RenderObject child) {
|
|
// Make sure to call super first to setup the parent data
|
|
super.adoptChild(child);
|
|
child.parentData._setupImplicitConstraints(_solver);
|
|
}
|
|
|
|
void dropChild(RenderObject child) {
|
|
child.parentData._removeImplicitConstraints(_solver);
|
|
super.dropChild(child);
|
|
}
|
|
|
|
void setupParentData(RenderObject child) {
|
|
if (child.parentData is! AutoLayoutParentData)
|
|
child.parentData = new AutoLayoutParentData(child);
|
|
}
|
|
|
|
bool get sizedByParent => true;
|
|
|
|
void performResize() {
|
|
size = constraints.biggest;
|
|
}
|
|
|
|
void performLayout() {
|
|
// Step 1: Update dimensions of self
|
|
_applyEditsAtSize(_solver, size);
|
|
|
|
// Step 2: Resolve solver updates and flush parameters
|
|
|
|
// We don't iterate over the children, instead, we ask the solver to tell
|
|
// us the updated parameters. Attached to the parameters (via the context)
|
|
// are the _AutoLayoutParamMixin instances.
|
|
for (_AutoLayoutParamMixin update in _solver.flushUpdates()) {
|
|
update._applyAutolayoutParameterUpdates();
|
|
}
|
|
}
|
|
|
|
void _applyAutolayoutParameterUpdates() {
|
|
// Nothing to do since the size update has already been presented to the
|
|
// solver as an edit variable modification. The invokation of this method
|
|
// only indicates that the value has been flushed to the variable.
|
|
}
|
|
|
|
void hitTestChildren(HitTestResult result, {Point position}) {
|
|
defaultHitTestChildren(result, position: position);
|
|
}
|
|
|
|
void paint(PaintingContext context, Offset offset) {
|
|
defaultPaint(context, offset);
|
|
}
|
|
|
|
List<al.Constraint> _constructImplicitConstraints() {
|
|
// Only edits variables are present on layout containers. If, in the future,
|
|
// implicit constraints (for say margins, padding, etc.) need to be added,
|
|
// they must be returned from here.
|
|
return null;
|
|
}
|
|
}
|