OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 import 'dart:math' as math; |
| 6 import 'dart:sky' as sky; |
| 7 import 'package:sky/framework/layout2.dart'; |
| 8 |
| 9 const double kTwoPi = 2 * math.PI; |
| 10 |
| 11 double deg(double radians) => radians * 180.0 / math.PI; |
| 12 |
| 13 class SectorConstraints { |
| 14 const SectorConstraints({ |
| 15 this.minDeltaRadius: 0.0, |
| 16 this.maxDeltaRadius: double.INFINITY, |
| 17 this.minDeltaTheta: 0.0, |
| 18 this.maxDeltaTheta: kTwoPi}); |
| 19 |
| 20 const SectorConstraints.tight({ double deltaRadius: 0.0, double deltaTheta: 0.
0 }) |
| 21 : minDeltaRadius = deltaRadius, |
| 22 maxDeltaRadius = deltaRadius, |
| 23 minDeltaTheta = deltaTheta, |
| 24 maxDeltaTheta = deltaTheta; |
| 25 |
| 26 final double minDeltaRadius; |
| 27 final double maxDeltaRadius; |
| 28 final double minDeltaTheta; |
| 29 final double maxDeltaTheta; |
| 30 |
| 31 double constrainDeltaRadius(double deltaRadius) { |
| 32 return clamp(min: minDeltaRadius, max: maxDeltaRadius, value: deltaRadius); |
| 33 } |
| 34 |
| 35 double constrainDeltaTheta(double deltaTheta) { |
| 36 return clamp(min: minDeltaTheta, max: maxDeltaTheta, value: deltaTheta); |
| 37 } |
| 38 } |
| 39 |
| 40 class SectorDimensions { |
| 41 const SectorDimensions({ this.deltaRadius: 0.0, this.deltaTheta: 0.0 }); |
| 42 |
| 43 factory SectorDimensions.withConstraints( |
| 44 SectorConstraints constraints, |
| 45 { double deltaRadius: 0.0, double deltaTheta: 0.0 } |
| 46 ) { |
| 47 return new SectorDimensions( |
| 48 deltaRadius: constraints.constrainDeltaRadius(deltaRadius), |
| 49 deltaTheta: constraints.constrainDeltaTheta(deltaTheta) |
| 50 ); |
| 51 } |
| 52 |
| 53 final double deltaRadius; |
| 54 final double deltaTheta; |
| 55 } |
| 56 |
| 57 class SectorParentData extends ParentData { |
| 58 double radius = 0.0; |
| 59 double theta = 0.0; |
| 60 } |
| 61 |
| 62 abstract class RenderSector extends RenderNode { |
| 63 |
| 64 void setParentData(RenderNode child) { |
| 65 if (child.parentData is! SectorParentData) |
| 66 child.parentData = new SectorParentData(); |
| 67 } |
| 68 |
| 69 SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double
radius) { |
| 70 return new SectorDimensions.withConstraints(constraints); |
| 71 } |
| 72 |
| 73 void layout(SectorConstraints constraints, double radius, { RenderNode relayou
tSubtreeRoot }) { |
| 74 deltaRadius = constraints.constrainDeltaRadius(0.0); |
| 75 deltaTheta = constraints.constrainDeltaTheta(0.0); |
| 76 layoutDone(); |
| 77 } |
| 78 |
| 79 double deltaRadius; |
| 80 double deltaTheta; |
| 81 } |
| 82 |
| 83 class RenderDecoratedSector extends RenderSector { |
| 84 BoxDecoration _decoration; |
| 85 |
| 86 RenderDecoratedSector(BoxDecoration decoration) : _decoration = decoration; |
| 87 |
| 88 void setBoxDecoration(BoxDecoration decoration) { |
| 89 if (_decoration == decoration) |
| 90 return; |
| 91 _decoration = decoration; |
| 92 markNeedsPaint(); |
| 93 } |
| 94 |
| 95 // origin must be set to the center of the circle |
| 96 void paint(RenderNodeDisplayList canvas) { |
| 97 assert(deltaRadius != null); |
| 98 assert(deltaTheta != null); |
| 99 assert(parentData is SectorParentData); |
| 100 |
| 101 if (_decoration == null) |
| 102 return; |
| 103 |
| 104 if (_decoration.backgroundColor != null) { |
| 105 sky.Paint paint = new sky.Paint()..color = _decoration.backgroundColor; |
| 106 sky.Path path = new sky.Path(); |
| 107 double outerRadiusOver2 = (parentData.radius + deltaRadius) / 2.0; |
| 108 sky.Rect outerBounds = new sky.Rect()..setLTRB(-outerRadiusOver2, -outerRa
diusOver2, outerRadiusOver2, outerRadiusOver2); |
| 109 path.arcTo(outerBounds, deg(parentData.theta), deg(deltaTheta), true); |
| 110 double innerRadiusOver2 = parentData.radius / 2.0; |
| 111 sky.Rect innerBounds = new sky.Rect()..setLTRB(-innerRadiusOver2, -innerRa
diusOver2, innerRadiusOver2, innerRadiusOver2); |
| 112 path.arcTo(innerBounds, deg(parentData.theta + deltaTheta), deg(-deltaThet
a), false); |
| 113 path.close(); |
| 114 canvas.drawPath(path, paint); |
| 115 } |
| 116 } |
| 117 } |
| 118 |
| 119 class SectorChildListParentData extends SectorParentData with ContainerParentDat
aMixin<RenderSector> { } |
| 120 |
| 121 class RenderSectorRing extends RenderDecoratedSector with ContainerRenderNodeMix
in<RenderSector, SectorChildListParentData> { |
| 122 // lays out RenderSector children in a ring |
| 123 |
| 124 RenderSectorRing({ |
| 125 BoxDecoration decoration, |
| 126 double deltaRadius: double.INFINITY, |
| 127 double padding: 0.0 |
| 128 }) : super(decoration), _padding = padding, _desiredDeltaRadius = deltaRadius; |
| 129 |
| 130 double _desiredDeltaRadius; |
| 131 double get desiredDeltaRadius => _desiredDeltaRadius; |
| 132 void set desiredDeltaRadius(double value) { |
| 133 assert(value != null); |
| 134 if (_desiredDeltaRadius != value) { |
| 135 _desiredDeltaRadius = value; |
| 136 markNeedsLayout(); |
| 137 } |
| 138 } |
| 139 |
| 140 double _padding; |
| 141 double get padding => _padding; |
| 142 void set padding(double value) { |
| 143 assert(value != null); |
| 144 if (_padding != value) { |
| 145 _padding = value; |
| 146 markNeedsLayout(); |
| 147 } |
| 148 } |
| 149 |
| 150 void setParentData(RenderNode child) { |
| 151 if (child.parentData is! SectorChildListParentData) |
| 152 child.parentData = new SectorChildListParentData(); |
| 153 } |
| 154 |
| 155 SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double
radius) { |
| 156 double outerDeltaRadius = constraints.constrainDeltaRadius(desiredDeltaRadiu
s); |
| 157 double innerDeltaRadius = outerDeltaRadius - padding * 2.0; |
| 158 double childRadius = radius + padding; |
| 159 double paddingTheta = math.atan(padding / (radius + outerDeltaRadius)); |
| 160 double innerTheta = paddingTheta; // increments with each child |
| 161 double remainingTheta = constraints.maxDeltaTheta - (innerTheta + paddingThe
ta); |
| 162 RenderSector child = firstChild; |
| 163 while (child != null) { |
| 164 SectorConstraints innerConstraints = new SectorConstraints( |
| 165 maxDeltaRadius: innerDeltaRadius, |
| 166 maxDeltaTheta: remainingTheta |
| 167 ); |
| 168 SectorDimensions childDimensions = child.getIntrinsicDimensions(innerConst
raints, childRadius); |
| 169 innerTheta += childDimensions.deltaTheta; |
| 170 remainingTheta -= childDimensions.deltaTheta; |
| 171 assert(child.parentData is SectorChildListParentData); |
| 172 child = child.parentData.nextSibling; |
| 173 if (child != null) { |
| 174 innerTheta += paddingTheta; |
| 175 remainingTheta -= paddingTheta; |
| 176 } |
| 177 } |
| 178 return new SectorDimensions.withConstraints(constraints, |
| 179 deltaRadius: outerDeltaRadius, |
| 180 deltaTheta: innerTheta); |
| 181 } |
| 182 |
| 183 SectorConstraints _constraints; |
| 184 void layout(SectorConstraints constraints, double radius, { RenderNode relayou
tSubtreeRoot }) { |
| 185 if (relayoutSubtreeRoot != null) |
| 186 saveRelayoutSubtreeRoot(relayoutSubtreeRoot); |
| 187 relayoutSubtreeRoot = relayoutSubtreeRoot == null ? this : relayoutSubtreeRo
ot; |
| 188 deltaRadius = constraints.constrainDeltaRadius(desiredDeltaRadius); |
| 189 assert(deltaRadius < double.INFINITY); |
| 190 _constraints = constraints; |
| 191 internalLayout(radius, relayoutSubtreeRoot); |
| 192 } |
| 193 |
| 194 void relayout() { |
| 195 assert(parentData is SectorParentData); |
| 196 internalLayout(parentData.radius, this); |
| 197 } |
| 198 |
| 199 void internalLayout(double radius, RenderNode relayoutSubtreeRoot) { |
| 200 double innerDeltaRadius = deltaRadius - padding * 2.0; |
| 201 double childRadius = radius + padding; |
| 202 double paddingTheta = math.atan(padding / (radius + deltaRadius)); |
| 203 double innerTheta = paddingTheta; // increments with each child |
| 204 double remainingTheta = _constraints.maxDeltaTheta - (innerTheta + paddingTh
eta); |
| 205 RenderSector child = firstChild; |
| 206 while (child != null) { |
| 207 SectorConstraints innerConstraints = new SectorConstraints( |
| 208 maxDeltaRadius: innerDeltaRadius, |
| 209 maxDeltaTheta: remainingTheta |
| 210 ); |
| 211 child.layout(innerConstraints, childRadius, relayoutSubtreeRoot: relayoutS
ubtreeRoot); |
| 212 assert(child.parentData is SectorParentData); |
| 213 child.parentData.theta = innerTheta; |
| 214 child.parentData.radius = childRadius; |
| 215 innerTheta += child.deltaTheta; |
| 216 remainingTheta -= child.deltaTheta; |
| 217 assert(child.parentData is SectorChildListParentData); |
| 218 child = child.parentData.nextSibling; |
| 219 if (child != null) { |
| 220 innerTheta += paddingTheta; |
| 221 remainingTheta -= paddingTheta; |
| 222 } |
| 223 } |
| 224 deltaTheta = innerTheta; |
| 225 } |
| 226 |
| 227 // TODO(ianh): hit testing et al is pending on adam's patch |
| 228 |
| 229 // paint origin is 0,0 of our circle |
| 230 // each sector then knows how to paint itself at its location |
| 231 void paint(RenderNodeDisplayList canvas) { |
| 232 super.paint(canvas); |
| 233 RenderSector child = firstChild; |
| 234 while (child != null) { |
| 235 assert(child.parentData is SectorChildListParentData); |
| 236 canvas.paintChild(child, 0.0, 0.0); |
| 237 child = child.parentData.nextSibling; |
| 238 } |
| 239 } |
| 240 |
| 241 } |
| 242 |
| 243 class RenderBoxToRenderSectorAdapter extends RenderBox { |
| 244 |
| 245 RenderBoxToRenderSectorAdapter({ double innerRadius: 0.0, RenderSector child }
) : |
| 246 _innerRadius = innerRadius { |
| 247 _child = child; |
| 248 adoptChild(_child); |
| 249 } |
| 250 |
| 251 double _innerRadius; |
| 252 double get innerRadius => _innerRadius; |
| 253 void set innerRadius(double value) { |
| 254 _innerRadius = value; |
| 255 markNeedsLayout(); |
| 256 } |
| 257 |
| 258 RenderSector _child; |
| 259 RenderSector get child => _child; |
| 260 void set child(RenderSector value) { |
| 261 if (_child != null) |
| 262 dropChild(_child); |
| 263 _child = value; |
| 264 adoptChild(_child); |
| 265 markNeedsLayout(); |
| 266 } |
| 267 |
| 268 void setParentData(RenderNode child) { |
| 269 if (child.parentData is! SectorParentData) |
| 270 child.parentData = new SectorParentData(); |
| 271 } |
| 272 |
| 273 BoxDimensions getIntrinsicDimensions(BoxConstraints constraints) { |
| 274 if (child == null) |
| 275 return new BoxDimensions.withConstraints(constraints, width: 0.0, height:
0.0); |
| 276 assert(child is RenderSector); |
| 277 assert(child.parentData is SectorParentData); |
| 278 assert(!constraints.isInfinite); |
| 279 double maxChildDeltaRadius = math.max(constraints.maxWidth, constraints.maxH
eight) / 2.0 - innerRadius; |
| 280 SectorDimensions childDimensions = child.getIntrinsicDimensions(new SectorCo
nstraints(maxDeltaRadius: maxChildDeltaRadius), innerRadius); |
| 281 double dimension = (innerRadius + childDimensions.deltaRadius) * 2.0; |
| 282 return new BoxDimensions.withConstraints(constraints, width: dimension, heig
ht: dimension); |
| 283 } |
| 284 |
| 285 void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) { |
| 286 if (relayoutSubtreeRoot != null) |
| 287 saveRelayoutSubtreeRoot(relayoutSubtreeRoot); |
| 288 relayoutSubtreeRoot = relayoutSubtreeRoot == null ? this : relayoutSubtreeRo
ot; |
| 289 BoxDimensions ourDimensions; |
| 290 if (child == null) { |
| 291 ourDimensions = new BoxDimensions.withConstraints(constraints, width: 0.0,
height: 0.0); |
| 292 } else { |
| 293 assert(child is RenderSector); |
| 294 assert(child.parentData is SectorParentData); |
| 295 assert(!constraints.isInfinite); |
| 296 double maxChildDeltaRadius = math.min(constraints.maxWidth, constraints.ma
xHeight) / 2.0 - innerRadius; |
| 297 child.layout(new SectorConstraints(maxDeltaRadius: maxChildDeltaRadius), i
nnerRadius, relayoutSubtreeRoot: relayoutSubtreeRoot); |
| 298 double dimension = (innerRadius + child.deltaRadius) * 2.0; |
| 299 ourDimensions = new BoxDimensions.withConstraints(constraints, width: dime
nsion, height: dimension); |
| 300 } |
| 301 width = ourDimensions.width; |
| 302 height = ourDimensions.height; |
| 303 print("adapter is: ${width}x${height}"); |
| 304 layoutDone(); |
| 305 } |
| 306 |
| 307 double width; |
| 308 double height; |
| 309 |
| 310 // TODO(ianh): hit testing et al is pending on adam's patch |
| 311 |
| 312 // paint origin is 0,0 of our circle |
| 313 void paint(RenderNodeDisplayList canvas) { |
| 314 super.paint(canvas); |
| 315 if (child != null) { |
| 316 print("painting child at ${width/2.0},${height/2.0}"); |
| 317 sky.Paint paint; |
| 318 paint = new sky.Paint()..color = 0xFF474700; |
| 319 canvas.drawRect(new sky.Rect()..setLTRB(0.0, 0.0, width, height), paint); |
| 320 paint = new sky.Paint()..color = 0xFFF7F700; |
| 321 canvas.drawRect(new sky.Rect()..setLTRB(10.0, 10.0, width-10.0, height-10.
0), paint); |
| 322 paint = new sky.Paint()..color = 0xFFFFFFFF; |
| 323 canvas.drawRect(new sky.Rect()..setLTRB(width/2.0-5.0, height/2.0-5.0, wid
th/2.0+5.0, height/2.0+5.0), paint); |
| 324 canvas.paintChild(child, width/2.0, height/2.0); |
| 325 } |
| 326 } |
| 327 |
| 328 } |
| 329 |
| 330 class RenderSolidColor extends RenderDecoratedSector { |
| 331 final int backgroundColor; |
| 332 |
| 333 RenderSolidColor(int backgroundColor) |
| 334 : super(new BoxDecoration(backgroundColor: backgroundColor)), |
| 335 backgroundColor = backgroundColor; |
| 336 |
| 337 SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double
radius) { |
| 338 return new SectorDimensions.withConstraints(constraints, deltaTheta: 1.0); /
/ 1.0 radians |
| 339 } |
| 340 |
| 341 void layout(SectorConstraints constraints, double radius, { RenderNode relayou
tSubtreeRoot }) { |
| 342 deltaRadius = constraints.constrainDeltaRadius(constraints.maxDeltaRadius); |
| 343 deltaTheta = constraints.constrainDeltaTheta(1.0); // 1.0 radians |
| 344 layoutDone(); |
| 345 } |
| 346 } |
| 347 |
| 348 RenderView renderView; |
| 349 |
| 350 void beginFrame(double timeStamp) { |
| 351 RenderNode.flushLayout(); |
| 352 |
| 353 renderView.paintFrame(); |
| 354 } |
| 355 |
| 356 bool handleEvent(sky.Event event) { |
| 357 if (event is! sky.PointerEvent) |
| 358 return false; |
| 359 return renderView.handlePointer(event, x: event.x, y: event.y); |
| 360 } |
| 361 |
| 362 void main() { |
| 363 print("test..."); |
| 364 sky.view.setEventCallback(handleEvent); |
| 365 sky.view.setBeginFrameCallback(beginFrame); |
| 366 |
| 367 var rootCircle = new RenderSectorRing(padding: 10.0); |
| 368 rootCircle.add(new RenderSolidColor(0xFF00FF00)); |
| 369 rootCircle.add(new RenderSolidColor(0xFF0000FF)); |
| 370 |
| 371 var root = new RenderBoxToRenderSectorAdapter(innerRadius: 50.0, child: rootCi
rcle); |
| 372 renderView = new RenderView(root: root); |
| 373 renderView.layout(newWidth: sky.view.width, newHeight: sky.view.height); |
| 374 |
| 375 sky.view.scheduleFrame(); |
| 376 print("window is ${sky.view.width}x${sky.view.height}"); |
| 377 } |
OLD | NEW |