提交 2f510d9f 编写于 作者: H Hixie

A proof of concept for annular sector layout.

Could be useful for watch faces. :-)
Includes some minor fixes to layout2.dart.
Includes adding a very basic path API.

R=abarth@chromium.org

Review URL: https://codereview.chromium.org/1152383002
上级 058f0222
......@@ -835,6 +835,8 @@ sky_core_files = [
"page/SpellCheckerClient.h",
"painting/Canvas.cpp",
"painting/Canvas.h",
"painting/CanvasPath.cpp",
"painting/CanvasPath.h",
"painting/Paint.cpp",
"painting/Paint.h",
"painting/PaintingCallback.cpp",
......@@ -1111,6 +1113,7 @@ core_idl_files = get_path_info([
"painting/Paint.idl",
"painting/PaintingCallback.idl",
"painting/PaintingContext.idl",
"painting/Path.idl",
"painting/Picture.idl",
"painting/PictureRecorder.idl",
"view/BeginFrameCallback.idl",
......
......@@ -145,6 +145,16 @@ void Canvas::drawCircle(float x, float y, float radius, const Paint* paint)
m_canvas->drawCircle(x, y, radius, paint->paint());
}
void Canvas::drawPath(const CanvasPath* path, const Paint* paint)
{
if (!m_canvas)
return;
ASSERT(path);
ASSERT(paint);
ASSERT(m_displayList->isRecording());
m_canvas->drawPath(path->path(), paint->paint());
}
PassRefPtr<DisplayList> Canvas::finishRecording()
{
if (!isRecording())
......
......@@ -5,6 +5,7 @@
#ifndef SKY_ENGINE_CORE_PAINTING_CANVAS_H_
#define SKY_ENGINE_CORE_PAINTING_CANVAS_H_
#include "sky/engine/core/painting/CanvasPath.h"
#include "sky/engine/core/painting/Paint.h"
#include "sky/engine/core/painting/Picture.h"
#include "sky/engine/core/painting/Rect.h"
......@@ -44,6 +45,7 @@ public:
void drawRect(const Rect& rect, const Paint* paint);
void drawOval(const Rect& rect, const Paint* paint);
void drawCircle(float x, float y, float radius, const Paint* paint);
void drawPath(const CanvasPath* path, const Paint* paint);
SkCanvas* skCanvas() { return m_canvas; }
......
......@@ -27,4 +27,5 @@ interface Canvas {
void drawRect(Rect rect, Paint paint);
void drawOval(Rect rect, Paint paint);
void drawCircle(float x, float y, float radius, Paint paint);
void drawPath(Path path, Paint paint);
};
// 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.
#include "sky/engine/config.h"
#include "sky/engine/core/painting/CanvasPath.h"
namespace blink {
CanvasPath::CanvasPath()
{
}
CanvasPath::~CanvasPath()
{
}
} // namespace blink
// 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.
#ifndef SKY_ENGINE_CORE_PAINTING_PATH_H_
#define SKY_ENGINE_CORE_PAINTING_PATH_H_
#include "sky/engine/core/painting/Rect.h"
#include "sky/engine/tonic/dart_wrappable.h"
#include "sky/engine/wtf/PassRefPtr.h"
#include "sky/engine/wtf/RefCounted.h"
#include "third_party/skia/include/core/SkPath.h"
// Note: There's a very similar class in ../../platform/graphics/Path.h
// We should probably rationalise these two.
// (The existence of that class is why this is CanvasPath and not just Path.)
namespace blink {
class CanvasPath : public RefCounted<CanvasPath>, public DartWrappable {
DEFINE_WRAPPERTYPEINFO();
public:
~CanvasPath() override;
static PassRefPtr<CanvasPath> create()
{
return adoptRef(new CanvasPath);
}
void moveTo(float x, float y)
{
m_path.moveTo(x, y);
}
void lineTo(float x, float y)
{
m_path.lineTo(x, y);
}
void arcTo(const Rect& rect, float startAngle, float sweepAngle, bool forceMoveTo)
{
m_path.arcTo(rect.sk_rect, startAngle, sweepAngle, forceMoveTo);
}
void close()
{
m_path.close();
}
const SkPath& path() const { return m_path; }
private:
CanvasPath();
SkPath m_path;
};
} // namespace blink
#endif // SKY_ENGINE_CORE_PAINTING_PATH_H_
// 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.
[
Constructor(),
ImplementedAs=CanvasPath,
] interface Path {
void moveTo(float x, float y);
void lineTo(float x, float y);
void arcTo(Rect rect, float startAngle, float sweepAngle, boolean forceMoveTo);
void close();
};
// 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 'dart:math' as math;
import 'dart:sky' as sky;
import 'package:sky/framework/layout2.dart';
const double kTwoPi = 2 * math.PI;
double deg(double radians) => radians * 180.0 / math.PI;
class SectorConstraints {
const SectorConstraints({
this.minDeltaRadius: 0.0,
this.maxDeltaRadius: double.INFINITY,
this.minDeltaTheta: 0.0,
this.maxDeltaTheta: kTwoPi});
const SectorConstraints.tight({ double deltaRadius: 0.0, double deltaTheta: 0.0 })
: minDeltaRadius = deltaRadius,
maxDeltaRadius = deltaRadius,
minDeltaTheta = deltaTheta,
maxDeltaTheta = deltaTheta;
final double minDeltaRadius;
final double maxDeltaRadius;
final double minDeltaTheta;
final double maxDeltaTheta;
double constrainDeltaRadius(double deltaRadius) {
return clamp(min: minDeltaRadius, max: maxDeltaRadius, value: deltaRadius);
}
double constrainDeltaTheta(double deltaTheta) {
return clamp(min: minDeltaTheta, max: maxDeltaTheta, value: deltaTheta);
}
}
class SectorDimensions {
const SectorDimensions({ this.deltaRadius: 0.0, this.deltaTheta: 0.0 });
factory SectorDimensions.withConstraints(
SectorConstraints constraints,
{ double deltaRadius: 0.0, double deltaTheta: 0.0 }
) {
return new SectorDimensions(
deltaRadius: constraints.constrainDeltaRadius(deltaRadius),
deltaTheta: constraints.constrainDeltaTheta(deltaTheta)
);
}
final double deltaRadius;
final double deltaTheta;
}
class SectorParentData extends ParentData {
double radius = 0.0;
double theta = 0.0;
}
abstract class RenderSector extends RenderNode {
void setParentData(RenderNode child) {
if (child.parentData is! SectorParentData)
child.parentData = new SectorParentData();
}
SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) {
return new SectorDimensions.withConstraints(constraints);
}
void layout(SectorConstraints constraints, double radius, { RenderNode relayoutSubtreeRoot }) {
deltaRadius = constraints.constrainDeltaRadius(0.0);
deltaTheta = constraints.constrainDeltaTheta(0.0);
layoutDone();
}
double deltaRadius;
double deltaTheta;
}
class RenderDecoratedSector extends RenderSector {
BoxDecoration _decoration;
RenderDecoratedSector(BoxDecoration decoration) : _decoration = decoration;
void setBoxDecoration(BoxDecoration decoration) {
if (_decoration == decoration)
return;
_decoration = decoration;
markNeedsPaint();
}
// origin must be set to the center of the circle
void paint(RenderNodeDisplayList canvas) {
assert(deltaRadius != null);
assert(deltaTheta != null);
assert(parentData is SectorParentData);
if (_decoration == null)
return;
if (_decoration.backgroundColor != null) {
sky.Paint paint = new sky.Paint()..color = _decoration.backgroundColor;
sky.Path path = new sky.Path();
double outerRadiusOver2 = (parentData.radius + deltaRadius) / 2.0;
sky.Rect outerBounds = new sky.Rect()..setLTRB(-outerRadiusOver2, -outerRadiusOver2, outerRadiusOver2, outerRadiusOver2);
path.arcTo(outerBounds, deg(parentData.theta), deg(deltaTheta), true);
double innerRadiusOver2 = parentData.radius / 2.0;
sky.Rect innerBounds = new sky.Rect()..setLTRB(-innerRadiusOver2, -innerRadiusOver2, innerRadiusOver2, innerRadiusOver2);
path.arcTo(innerBounds, deg(parentData.theta + deltaTheta), deg(-deltaTheta), false);
path.close();
canvas.drawPath(path, paint);
}
}
}
class SectorChildListParentData extends SectorParentData with ContainerParentDataMixin<RenderSector> { }
class RenderSectorRing extends RenderDecoratedSector with ContainerRenderNodeMixin<RenderSector, SectorChildListParentData> {
// lays out RenderSector children in a ring
RenderSectorRing({
BoxDecoration decoration,
double deltaRadius: double.INFINITY,
double padding: 0.0
}) : super(decoration), _padding = padding, _desiredDeltaRadius = deltaRadius;
double _desiredDeltaRadius;
double get desiredDeltaRadius => _desiredDeltaRadius;
void set desiredDeltaRadius(double value) {
assert(value != null);
if (_desiredDeltaRadius != value) {
_desiredDeltaRadius = value;
markNeedsLayout();
}
}
double _padding;
double get padding => _padding;
void set padding(double value) {
assert(value != null);
if (_padding != value) {
_padding = value;
markNeedsLayout();
}
}
void setParentData(RenderNode child) {
if (child.parentData is! SectorChildListParentData)
child.parentData = new SectorChildListParentData();
}
SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) {
double outerDeltaRadius = constraints.constrainDeltaRadius(desiredDeltaRadius);
double innerDeltaRadius = outerDeltaRadius - padding * 2.0;
double childRadius = radius + padding;
double paddingTheta = math.atan(padding / (radius + outerDeltaRadius));
double innerTheta = paddingTheta; // increments with each child
double remainingTheta = constraints.maxDeltaTheta - (innerTheta + paddingTheta);
RenderSector child = firstChild;
while (child != null) {
SectorConstraints innerConstraints = new SectorConstraints(
maxDeltaRadius: innerDeltaRadius,
maxDeltaTheta: remainingTheta
);
SectorDimensions childDimensions = child.getIntrinsicDimensions(innerConstraints, childRadius);
innerTheta += childDimensions.deltaTheta;
remainingTheta -= childDimensions.deltaTheta;
assert(child.parentData is SectorChildListParentData);
child = child.parentData.nextSibling;
if (child != null) {
innerTheta += paddingTheta;
remainingTheta -= paddingTheta;
}
}
return new SectorDimensions.withConstraints(constraints,
deltaRadius: outerDeltaRadius,
deltaTheta: innerTheta);
}
SectorConstraints _constraints;
void layout(SectorConstraints constraints, double radius, { RenderNode relayoutSubtreeRoot }) {
if (relayoutSubtreeRoot != null)
saveRelayoutSubtreeRoot(relayoutSubtreeRoot);
relayoutSubtreeRoot = relayoutSubtreeRoot == null ? this : relayoutSubtreeRoot;
deltaRadius = constraints.constrainDeltaRadius(desiredDeltaRadius);
assert(deltaRadius < double.INFINITY);
_constraints = constraints;
internalLayout(radius, relayoutSubtreeRoot);
}
void relayout() {
assert(parentData is SectorParentData);
internalLayout(parentData.radius, this);
}
void internalLayout(double radius, RenderNode relayoutSubtreeRoot) {
double innerDeltaRadius = deltaRadius - padding * 2.0;
double childRadius = radius + padding;
double paddingTheta = math.atan(padding / (radius + deltaRadius));
double innerTheta = paddingTheta; // increments with each child
double remainingTheta = _constraints.maxDeltaTheta - (innerTheta + paddingTheta);
RenderSector child = firstChild;
while (child != null) {
SectorConstraints innerConstraints = new SectorConstraints(
maxDeltaRadius: innerDeltaRadius,
maxDeltaTheta: remainingTheta
);
child.layout(innerConstraints, childRadius, relayoutSubtreeRoot: relayoutSubtreeRoot);
assert(child.parentData is SectorParentData);
child.parentData.theta = innerTheta;
child.parentData.radius = childRadius;
innerTheta += child.deltaTheta;
remainingTheta -= child.deltaTheta;
assert(child.parentData is SectorChildListParentData);
child = child.parentData.nextSibling;
if (child != null) {
innerTheta += paddingTheta;
remainingTheta -= paddingTheta;
}
}
deltaTheta = innerTheta;
}
// TODO(ianh): hit testing et al is pending on adam's patch
// paint origin is 0,0 of our circle
// each sector then knows how to paint itself at its location
void paint(RenderNodeDisplayList canvas) {
super.paint(canvas);
RenderSector child = firstChild;
while (child != null) {
assert(child.parentData is SectorChildListParentData);
canvas.paintChild(child, 0.0, 0.0);
child = child.parentData.nextSibling;
}
}
}
class RenderBoxToRenderSectorAdapter extends RenderBox {
RenderBoxToRenderSectorAdapter({ double innerRadius: 0.0, RenderSector child }) :
_innerRadius = innerRadius {
_child = child;
adoptChild(_child);
}
double _innerRadius;
double get innerRadius => _innerRadius;
void set innerRadius(double value) {
_innerRadius = value;
markNeedsLayout();
}
RenderSector _child;
RenderSector get child => _child;
void set child(RenderSector value) {
if (_child != null)
dropChild(_child);
_child = value;
adoptChild(_child);
markNeedsLayout();
}
void setParentData(RenderNode child) {
if (child.parentData is! SectorParentData)
child.parentData = new SectorParentData();
}
BoxDimensions getIntrinsicDimensions(BoxConstraints constraints) {
if (child == null)
return new BoxDimensions.withConstraints(constraints, width: 0.0, height: 0.0);
assert(child is RenderSector);
assert(child.parentData is SectorParentData);
assert(!constraints.isInfinite);
double maxChildDeltaRadius = math.max(constraints.maxWidth, constraints.maxHeight) / 2.0 - innerRadius;
SectorDimensions childDimensions = child.getIntrinsicDimensions(new SectorConstraints(maxDeltaRadius: maxChildDeltaRadius), innerRadius);
double dimension = (innerRadius + childDimensions.deltaRadius) * 2.0;
return new BoxDimensions.withConstraints(constraints, width: dimension, height: dimension);
}
void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) {
if (relayoutSubtreeRoot != null)
saveRelayoutSubtreeRoot(relayoutSubtreeRoot);
relayoutSubtreeRoot = relayoutSubtreeRoot == null ? this : relayoutSubtreeRoot;
BoxDimensions ourDimensions;
if (child == null) {
ourDimensions = new BoxDimensions.withConstraints(constraints, width: 0.0, height: 0.0);
} else {
assert(child is RenderSector);
assert(child.parentData is SectorParentData);
assert(!constraints.isInfinite);
double maxChildDeltaRadius = math.min(constraints.maxWidth, constraints.maxHeight) / 2.0 - innerRadius;
child.layout(new SectorConstraints(maxDeltaRadius: maxChildDeltaRadius), innerRadius, relayoutSubtreeRoot: relayoutSubtreeRoot);
double dimension = (innerRadius + child.deltaRadius) * 2.0;
ourDimensions = new BoxDimensions.withConstraints(constraints, width: dimension, height: dimension);
}
width = ourDimensions.width;
height = ourDimensions.height;
print("adapter is: ${width}x${height}");
layoutDone();
}
double width;
double height;
// TODO(ianh): hit testing et al is pending on adam's patch
// paint origin is 0,0 of our circle
void paint(RenderNodeDisplayList canvas) {
super.paint(canvas);
if (child != null) {
print("painting child at ${width/2.0},${height/2.0}");
sky.Paint paint;
paint = new sky.Paint()..color = 0xFF474700;
canvas.drawRect(new sky.Rect()..setLTRB(0.0, 0.0, width, height), paint);
paint = new sky.Paint()..color = 0xFFF7F700;
canvas.drawRect(new sky.Rect()..setLTRB(10.0, 10.0, width-10.0, height-10.0), paint);
paint = new sky.Paint()..color = 0xFFFFFFFF;
canvas.drawRect(new sky.Rect()..setLTRB(width/2.0-5.0, height/2.0-5.0, width/2.0+5.0, height/2.0+5.0), paint);
canvas.paintChild(child, width/2.0, height/2.0);
}
}
}
class RenderSolidColor extends RenderDecoratedSector {
final int backgroundColor;
RenderSolidColor(int backgroundColor)
: super(new BoxDecoration(backgroundColor: backgroundColor)),
backgroundColor = backgroundColor;
SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) {
return new SectorDimensions.withConstraints(constraints, deltaTheta: 1.0); // 1.0 radians
}
void layout(SectorConstraints constraints, double radius, { RenderNode relayoutSubtreeRoot }) {
deltaRadius = constraints.constrainDeltaRadius(constraints.maxDeltaRadius);
deltaTheta = constraints.constrainDeltaTheta(1.0); // 1.0 radians
layoutDone();
}
}
RenderView renderView;
void beginFrame(double timeStamp) {
RenderNode.flushLayout();
renderView.paintFrame();
}
bool handleEvent(sky.Event event) {
if (event is! sky.PointerEvent)
return false;
return renderView.handlePointer(event, x: event.x, y: event.y);
}
void main() {
print("test...");
sky.view.setEventCallback(handleEvent);
sky.view.setBeginFrameCallback(beginFrame);
var rootCircle = new RenderSectorRing(padding: 10.0);
rootCircle.add(new RenderSolidColor(0xFF00FF00));
rootCircle.add(new RenderSolidColor(0xFF0000FF));
var root = new RenderBoxToRenderSectorAdapter(innerRadius: 50.0, child: rootCircle);
renderView = new RenderView(root: root);
renderView.layout(newWidth: sky.view.width, newHeight: sky.view.height);
sky.view.scheduleFrame();
print("window is ${sky.view.width}x${sky.view.height}");
}
......@@ -79,7 +79,7 @@ abstract class RenderNode extends AbstractNode {
void saveRelayoutSubtreeRoot(RenderNode relayoutSubtreeRoot) {
_relayoutSubtreeRoot = relayoutSubtreeRoot;
assert(_relayoutSubtreeRoot == null || _relayoutSubtreeRoot._relayoutSubtreeRoot == null);
assert(_relayoutSubtreeRoot == null || _relayoutSubtreeRoot == parent || _relayoutSubtreeRoot == parent._relayoutSubtreeRoot);
assert(_relayoutSubtreeRoot == null || _relayoutSubtreeRoot == parent || (parent is RenderNode && _relayoutSubtreeRoot == parent._relayoutSubtreeRoot));
}
bool debugAncestorsAlreadyMarkedNeedsLayout() {
if (_relayoutSubtreeRoot == null)
......@@ -103,6 +103,7 @@ abstract class RenderNode extends AbstractNode {
return;
}
_needsLayout = true;
assert(parent is RenderNode);
if (_relayoutSubtreeRoot != null)
parent.markNeedsLayout();
else
......@@ -402,7 +403,7 @@ class BoxConstraints {
this.minHeight: 0.0,
this.maxHeight: double.INFINITY});
const BoxConstraints.tight({ width: width, height: height })
const BoxConstraints.tight({ double width: 0.0, double height: 0.0 })
: minWidth = width,
maxWidth = width,
minHeight = height,
......@@ -420,14 +421,17 @@ class BoxConstraints {
double constrainHeight(double height) {
return clamp(min: minHeight, max: maxHeight, value: height);
}
bool get isInfinite => maxWidth >= double.INFINITY || maxHeight >= double.INFINITY;
}
class BoxDimensions {
const BoxDimensions({this.width, this.height});
const BoxDimensions({ this.width: 0.0, this.height: 0.0 });
BoxDimensions.withConstraints(
BoxConstraints constraints, {double width: 0.0, double height: 0.0})
: width = constraints.constrainWidth(width),
BoxConstraints constraints,
{ double width: 0.0, double height: 0.0 }
) : width = constraints.constrainWidth(width),
height = constraints.constrainHeight(height);
final double width;
......@@ -661,7 +665,7 @@ class RenderBlock extends RenderDecoratedBox with ContainerRenderNodeMixin<Rende
double outerWidth = constraints.constrainWidth(constraints.maxWidth);
assert(outerWidth < double.INFINITY);
double innerWidth = outerWidth - (_padding.left + _padding.right);
RenderBox child = _firstChild;
RenderBox child = firstChild;
BoxConstraints innerConstraints = new BoxConstraints(minWidth: innerWidth,
maxWidth: innerWidth);
while (child != null) {
......@@ -696,7 +700,7 @@ class RenderBlock extends RenderDecoratedBox with ContainerRenderNodeMixin<Rende
assert(_maxHeight != null);
double y = _padding.top;
double innerWidth = width - (_padding.left + _padding.right);
RenderBox child = _firstChild;
RenderBox child = firstChild;
while (child != null) {
child.layout(new BoxConstraints(minWidth: innerWidth, maxWidth: innerWidth),
relayoutSubtreeRoot: relayoutSubtreeRoot);
......@@ -776,21 +780,19 @@ class RenderFlex extends RenderDecoratedBox with ContainerRenderNodeMixin<Render
int _getFlex(RenderBox child) {
assert(child.parentData is FlexBoxParentData);
return (child.parentData.flex != null ? child.parentData.flex : 0);
return child.parentData.flex != null ? child.parentData.flex : 0;
}
void internalLayout(RenderNode relayoutSubtreeRoot) {
// Based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexible Lengths
// Steps 1-3. Determine used flex factor, size inflexible items, calculate free space
int numFlexibleChildren = 0;
int totalFlex = 0;
assert(_constraints != null);
double freeSpace = (_direction == FlexDirection.Horizontal) ? _constraints.maxWidth : _constraints.maxHeight;
RenderBox child = _firstChild;
RenderBox child = firstChild;
while (child != null) {
int flex = _getFlex(child);
if (flex > 0) {
numFlexibleChildren++;
totalFlex += child.parentData.flex;
} else {
BoxConstraints constraints = new BoxConstraints(maxHeight: _constraints.maxHeight,
......@@ -805,7 +807,7 @@ class RenderFlex extends RenderDecoratedBox with ContainerRenderNodeMixin<Render
// Steps 4-5. Distribute remaining space to flexible children.
double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0;
double usedSpace = 0.0;
child = _firstChild;
child = firstChild;
while (child != null) {
int flex = _getFlex(child);
if (flex > 0) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册