| OLD | NEW |
| 1 Sky Style Language | 1 Sky Style Language |
| 2 ================== | 2 ================== |
| 3 | 3 |
| 4 Planed changes | 4 Planed changes |
| 5 -------------- | 5 -------------- |
| 6 | 6 |
| 7 Add //-to-end-of-line comments to be consistent with the script | 7 Add //-to-end-of-line comments to be consistent with the script |
| 8 language. | 8 language. |
| 9 | 9 |
| 10 | 10 |
| (...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 184 readonly attribute String value; | 184 readonly attribute String value; |
| 185 readonly attribute String unit; // for 'dimension' type, this is the punctuati
on or identifier that follows the number, for 'literal' type, this is the punctu
ation that precedes it | 185 readonly attribute String unit; // for 'dimension' type, this is the punctuati
on or identifier that follows the number, for 'literal' type, this is the punctu
ation that precedes it |
| 186 } | 186 } |
| 187 | 187 |
| 188 class TokenSource { | 188 class TokenSource { |
| 189 constructor (Array<StyleToken> tokens); | 189 constructor (Array<StyleToken> tokens); |
| 190 IteratorResult next(); | 190 IteratorResult next(); |
| 191 TokenSourceBookmark getBookmark(); | 191 TokenSourceBookmark getBookmark(); |
| 192 void rewind(TokenSourceBookmark bookmark); | 192 void rewind(TokenSourceBookmark bookmark); |
| 193 } | 193 } |
| 194 |
| 194 class TokenSourceBookmark { | 195 class TokenSourceBookmark { |
| 195 constructor (); | 196 constructor (); |
| 196 // TokenSource stores unforgeable state on this object using symbols or a weak
map or some such | 197 // TokenSource stores unforgeable state on this object using symbols or a weak
map or some such |
| 197 } | 198 } |
| 198 | 199 |
| 199 // TODO(ianh): this is a non-starter, we need something better to handle units a
nd custom painting | 200 callback ParserCallback = AbstractStyleValue (TokenSource tokens); // return if
successful, throw if not |
| 200 dictionary ParsedValue { | |
| 201 any value = null; | |
| 202 ValueResolver? resolver = null; | |
| 203 Boolean relativeDimension = false; // if true, e.g. for % lengths, the callbac
k will be called again if an ancestor's dimensions change | |
| 204 Painter? painter = null; | |
| 205 } | |
| 206 | 201 |
| 207 // best practice convention: if you're creating a property with needsPaint, you
should | 202 class StyleGrammar { |
| 208 // create a new style value type for it so that it can set the paint callback ri
ght; | |
| 209 // you should never use such a style type when parsing another property | |
| 210 | |
| 211 callback any ParserCallback (TokenSource tokens); | |
| 212 | |
| 213 class StyleValueType { | |
| 214 constructor (); | 203 constructor (); |
| 215 void addParser(ParserCallback parser); | 204 void addParser(ParserCallback parser); |
| 216 any parse(TokenSource tokens, Boolean root = false); | 205 AbstractStyleValue parse(TokenSource tokens, Boolean root = false); |
| 217 // for each parser callback that was registered, in reverse | 206 // for each parser callback that was registered, in reverse |
| 218 // order (most recently registered first), run these steps: | 207 // order (most recently registered first), run these steps: |
| 219 // let bookmark = tokens.getBookmark(); | 208 // let bookmark = tokens.getBookmark(); |
| 220 // try { | 209 // try { |
| 221 // let result = parser(tokens); | 210 // let result = parser(tokens); |
| 222 // if (root) { | 211 // if (root) { |
| 223 // if (!tokens.next().done) | 212 // if (!tokens.next().done) |
| 224 // throw new Error(); | 213 // throw new Error(); |
| 225 // } | 214 // } |
| 226 // } except { | 215 // } except { |
| 227 // tokens.rewind(bookmark); | 216 // tokens.rewind(bookmark); |
| 228 // } | 217 // } |
| 229 // (root is set when you need to parse the entire token stream to be valid) | 218 // (root is set when you need to parse the entire token stream to be valid) |
| 230 } | 219 } |
| 231 | 220 |
| 232 // note: if you define a style value type that uses other style value types, e.g
. a "length pair" that accepts two lengths, then | 221 /* |
| 233 // if any of the subtypes have a resolver, you need to make sure you have a reso
lver that calls them to compute the final value | 222 StyleNode |
| 223 | |
| 224 +-- Property |
| 225 | |
| 226 +-- AbstractStyleValue |
| 227 | |
| 228 +-- NumericStyleValue |
| 229 | | |
| 230 | +-- AnimatableNumericStyleValue |
| 231 | |
| 232 +-- LengthStyleValue |
| 233 | | |
| 234 | +-- AnimatableLengthStyleValue |
| 235 | | |
| 236 | +-- PixelLengthStyleValue |
| 237 | | |
| 238 | +-- EmLengthStyleValue |
| 239 | | |
| 240 | +-- VHLengthStyleValue |
| 241 | | |
| 242 | +-- CalcLengthStyleValue |
| 243 | |
| 244 +-- ColorStyleValue |
| 245 | | |
| 246 | +-- RGBColorStyleValue |
| 247 | | |
| 248 | +-- AnimatableColorStyleValue |
| 249 | |
| 250 +-- AbstractStringStyleValue |
| 251 | | |
| 252 | +-- IdentifierStyleValue |
| 253 | | | |
| 254 | | +-- AnimatableIdentifierStyleValue |
| 255 | | |
| 256 | +-- URLStyleValue |
| 257 | | | |
| 258 | | +-- AnimatableURLStyleValue |
| 259 | | |
| 260 | +-- StringStyleValue |
| 261 | | |
| 262 | +-- AnimatableStringStyleValue |
| 263 | |
| 264 +-- PrimitiveValuesListStyleValue |
| 265 */ |
| 266 |
| 267 abstract class StyleNode { |
| 268 abstract void markDirty(); |
| 269 } |
| 270 |
| 271 class StyleValueResolverSettings { |
| 272 // this is used as an "out" parameter for 'resolve()' below |
| 273 constructor(); |
| 274 void reset(); // resets values to defaults so that object can be reused |
| 275 attribute Boolean layoutDependent; // default to false |
| 276 // set this if the value should be recomputed each time the ownerLayoutManag
er's dimensions change, rather than being precomputed |
| 277 |
| 278 // attribute "BitField" dependencies; // defaults to no bits set |
| 279 void dependsOn(PropertyHandle property); |
| 280 // if the given property doesn't have a dependency bit assigned: |
| 281 // - assign the next bit to the property |
| 282 // - if there's no bits left, throw |
| 283 // set the bit on this StyleValueResolverSettings's dependencies bitfield |
| 284 } |
| 285 |
| 286 class Property : StyleNode { |
| 287 constructor (StyleDeclaration parentNode, PropertyHandle property, AbstractSty
leValue? initialValue = null); |
| 288 readonly attribute StyleDeclaration parentNode; |
| 289 readonly attribute PropertyHandle property; |
| 290 readonly attribute AbstractStyleValue value; |
| 291 |
| 292 void setValue(AbstractStyleValue? newValue); |
| 293 // updates value and calls markDirty() |
| 294 |
| 295 void markDirty(); |
| 296 // call parentNode.markDirty(property); |
| 297 |
| 298 abstract any resolve(RenderNode node, StyleValueResolverSettings? settings = n
ull); |
| 299 // if value is null, returns null |
| 300 // otherwise, returns value.resolve(property, node, settings) |
| 301 } |
| 302 |
| 303 abstract class AbstractStyleValue : StyleNode { |
| 304 abstract constructor(StyleNode parentNode); |
| 305 readonly attribute StyleNode parentNode; |
| 306 |
| 307 void markDirty(); |
| 308 // call this.parentNode.markDirty() |
| 309 |
| 310 abstract any resolve(PropertyHandle property, RenderNode node, StyleValueResol
verSettings? settings = null); |
| 311 } |
| 312 |
| 313 abstract class LengthStyleValue : AbstractStyleValue { |
| 314 abstract Float resolve(PropertyHandle property, RenderNode node, StyleValueRes
olverSettings? settings = null); |
| 315 } |
| 316 |
| 317 class PixelLengthStyleValue : LengthStyleValue { |
| 318 Float resolve(PropertyHandle property, RenderNode node, StyleValueResolverSett
ings? settings = null); |
| 319 } |
| 320 |
| 321 // ... |
| 234 | 322 |
| 235 dictionary PropertySettings { | 323 dictionary PropertySettings { |
| 236 String name; | 324 String name; |
| 237 StyleValueType type; // the output from the parser is coerced to a ParsedValue | 325 StyleGrammar grammar; |
| 238 Boolean inherits = false; | 326 Boolean inherited = false; |
| 239 any initialValue = null; | 327 any initialValue = null; |
| 328 Boolean needsManager = false; |
| 240 Boolean needsLayout = false; | 329 Boolean needsLayout = false; |
| 241 Boolean needsPaint = false; | 330 Boolean needsPaint = false; |
| 331 // PropertyHandle propertyHandle; // assigned by registerProperty |
| 332 // Integer dependencyBit; // assigned by StyleValueResolverSettings.dependsOn(
) |
| 242 } | 333 } |
| 334 typedef PropertyHandle Integer; |
| 335 PropertyHandle registerProperty(PropertySettings propertySettings); |
| 243 | 336 |
| 244 void registerProperty(PropertySettings propertySettings); | 337 // sky:core exports a bunch of style grammars so that people can extend them |
| 245 // when you register a new property, document the format that is expected to b
e cascaded | 338 attribute StyleGrammar PositiveLengthOrInfinityStyleGrammar; // resolves to Floa
t |
| 246 // (the output from the propertySettings.type parser's ParsedValue.value field
after the resolver, if any, has been called) | 339 attribute StyleGrammar PositiveLengthOrAutoStyleGrammar; // resolves to Float or
null |
| 247 | 340 attribute StyleGrammar PositiveLengthStyleGrammar; // resolves to Float |
| 248 // sky:core exports a bunch of style value types so that people can | 341 attribute StyleGrammar NumberGrammar; // resolves to Float |
| 249 // extend them | 342 attribute StyleGrammar ColorGrammar; // resolves to object with 'red', 'green',
'blue', and 'alpha' properties each of which is a Float 0..1 |
| 250 attribute StyleValueType PositiveLengthOrInfinityStyleValueType; | 343 attribute StyleGrammar DisplayStyleGrammar; // resolves to null or LayoutManager
constructor |
| 251 attribute StyleValueType PositiveLengthOrAutoStyleValueType; | |
| 252 attribute StyleValueType PositiveLengthStyleValueType; | |
| 253 attribute StyleValueType DisplayStyleValueType; | |
| 254 ``` | 344 ``` |
| 255 | 345 |
| 256 Inline Styles | 346 Inline Styles |
| 257 ------------- | 347 ------------- |
| 258 | 348 |
| 259 ```javascript | 349 ```javascript |
| 260 partial class Element { | |
| 261 readonly attribute StyleDeclarationList style; | |
| 262 } | |
| 263 | |
| 264 class StyleDeclarationList { | 350 class StyleDeclarationList { |
| 265 constructor (); | 351 constructor (Element? element); |
| 266 | 352 |
| 267 // There are two batches of styles in a StyleDeclarationList. | 353 // There are two batches of styles in a StyleDeclarationList. |
| 268 | 354 |
| 269 // The first batch is the per-frame styles. These get cleared each | 355 // The first batch is the per-frame styles. These get cleared each |
| 270 // frame, after which all the matching rules in relevant <style> blocks | 356 // frame, after which all the matching rules in relevant <style> blocks |
| 271 // get added back in, followed by all the animation-derived rules. | 357 // get added back in, followed by all the animation-derived rules. |
| 272 // Scripts can add styles themselves. | 358 // Scripts can add styles themselves. |
| 273 void addFrameStyles(StyleDeclaration styles, String? pseudoElement = null); //
O(1) | 359 void addFrameStyles(StyleDeclaration styles, String? pseudoElement = null); //
O(1) |
| 274 void clearFrameStyles(); | 360 void clearFrameStyles(); |
| 275 | 361 |
| 276 // The second batch is the persistent styles. | 362 // The second batch is the persistent styles. |
| 277 // Once added, they remain forever until removed. | 363 // Once added, they remain forever until removed. |
| 278 void addPersistentStyles(StyleDeclaration styles, String? pseudoElement = null
); // O(1) | 364 void addPersistentStyles(StyleDeclaration styles, String? pseudoElement = null
); // O(1) |
| 279 void removePersistentStyles(StyleDeclaration styles, String? pseudoElement = n
ull); // O(N) in number of declarations | 365 void removePersistentStyles(StyleDeclaration styles, String? pseudoElement = n
ull); // O(N) in number of declarations |
| 280 | 366 |
| 367 // as StyleDeclaration are added and removed here, the StyleDeclarationList ca
lls register(element) and |
| 368 // unregister(element) respectively on those StyleDeclaration objects, where e
lement is the element that |
| 369 // was passed to the constructor, if not null |
| 370 // then, it calls element.renderNode.cascadedValueAdded/cascadedValueRemoved f
or each property on the object |
| 371 |
| 281 // This returns all the frame styles followed by all the persistent styles, in
insertion order. | 372 // This returns all the frame styles followed by all the persistent styles, in
insertion order. |
| 282 Array<StyleDeclaration> getDeclarations(String? pseudoElement = null); // O(N)
in number of declarations | 373 Array<StyleDeclaration> getDeclarations(String? pseudoElement = null); // O(N)
in number of declarations |
| 283 } | 374 } |
| 284 | 375 |
| 285 class StyleDeclaration { | 376 class StyleDeclaration { |
| 286 // TODO(ianh): define this | 377 void markDirty(PropertyHandle property); |
| 378 // this indicates that the cascaded value of the property thinks |
| 379 // it will now have a different result (as opposed to the cascaded |
| 380 // value itself having changed) |
| 381 // invoke element.renderNode.cascadedValueDirty(property, pseudoElement); fo
r each |
| 382 // currently registered consumer element/pseudoElement pair |
| 383 |
| 384 void register(Element element, String? pseudoElement = null); // O(1) |
| 385 void unregister(Element element, String? pseudoElement = null); // O(N) |
| 386 // registers an element/pseudoElement pair with this StyleDeclaration so tha
t when |
| 387 // a property/value on the style declaration is marked dirty, the element |
| 388 // is informed and can then clear its property cache |
| 389 |
| 390 getter AbstractStyleValue? (PropertyHandle property); |
| 391 // looks up the Property object for /property/, and returns its value |
| 392 // null if property is missing |
| 393 |
| 394 setter void (PropertyHandle property, AbstractStyleValue value); |
| 395 // if there is no Property object for /property/, creates one |
| 396 // else calls its update() method to change the value |
| 397 // if the value changed: |
| 398 // invoke consumer.renderNode.cascadedValueChanged(property); for each |
| 399 // currently registered consumer |
| 400 // if the value is new: |
| 401 // invoke consumer.renderNode.cascadedValueAdded(property); for each |
| 402 // currently registered consumer |
| 403 |
| 404 void remove(PropertyHandle property); |
| 405 // drops the Property object for /property/ from this StyleDeclaration objec
t |
| 406 // invoke consumer.renderNode.cascadedValueRemoved(property); for each |
| 407 // currently registered consumer |
| 287 } | 408 } |
| 288 ``` | 409 ``` |
| 289 | 410 |
| 290 Rule Matching | 411 Rule Matching |
| 291 ------------- | 412 ------------- |
| 292 | 413 |
| 293 ```javascript | 414 ```javascript |
| 294 partial class StyleElement { | |
| 295 Array<Rule> getRules(); // O(N) in rules | |
| 296 } | |
| 297 | |
| 298 class Rule { | 415 class Rule { |
| 299 constructor (); | 416 constructor (); |
| 300 attribute SelectorQuery selector; // O(1) | 417 attribute SelectorQuery selector; // O(1) |
| 301 attribute String? pseudoElement; // O(1) | 418 attribute String? pseudoElement; // O(1) |
| 302 attribute StyleDeclaration styles; // O(1) | 419 attribute StyleDeclaration styles; // O(1) |
| 303 } | 420 } |
| 304 ``` | 421 ``` |
| 305 | 422 |
| 306 Each frame, at some defined point relative to requestAnimationFrame(): | 423 Each frame, at some defined point relative to requestAnimationFrame(): |
| 307 - If a rule starts applying to an element, sky:core calls thatElement.style.add
(rule.styles, rule.pseudoElement); | 424 - If a rule starts applying to an element, sky:core calls thatElement.style.add
(rule.styles, rule.pseudoElement); |
| 308 - If a rule stops applying to an element, sky:core calls thatElement.style.remo
ve(rule.styles, rule.pseudoElement); | 425 - If a rule stops applying to an element, sky:core calls thatElement.style.remo
ve(rule.styles, rule.pseudoElement); |
| 309 | 426 |
| 310 TODO(ianh): fix the above so that rule order is maintained | 427 TODO(ianh): fix the above so that rule order is maintained |
| 311 | 428 |
| 312 | 429 |
| 313 Cascade | 430 Cascade |
| 314 ------- | 431 ------- |
| 315 | 432 |
| 316 For each Element, the StyleDeclarationList is conceptually flattened | 433 Simultaneously walk the tree rooted at the application Document, |
| 317 so that only the last declaration mentioning a property is left. | 434 taking into account shadow trees and child distribution, and the tree |
| 435 rooted at the document's RenderNode. |
| 318 | 436 |
| 319 Create the flattened render tree as a tree of StyleNode objects | 437 If you come across a node that doesn't have an assigned RenderNode, |
| 320 (described below). For each one, run the equivalent of the following | 438 then create one and mark it "isNew", and place it in the appropriate |
| 321 code: | 439 place in the RenderTree tree, after any nodes marked isGhost. |
| 440 |
| 441 For each element, if the node's needsManager is true, call |
| 442 getLayoutManager() on the element, and if that's not null, and if the |
| 443 returned class isn't the same class as the current layoutManager, if |
| 444 any, construct the given class and assign it to the RenderNode's |
| 445 layoutManager, then set all the child RenderNodes' ownerLayoutManager |
| 446 to that object; if it returns null, and that node already has a |
| 447 layoutManager, then set isGhost=true for that node and all its |
| 448 children (without changing the layoutManager). Otherwise, if it |
| 449 returned null and there's already no layoutManager, remove the node |
| 450 from the tree. Then, in any case, clear the needsManager bit. |
| 451 |
| 452 When an Element or Text node is to be removed from its parent, and it |
| 453 has a renderNode, and that renderNode has an ownerLayoutManager with |
| 454 autoreap=false, then before actually removing the node, the node's |
| 455 renderNode should be marked isGhost=true, and the relevant |
| 456 StyleDeclarationList should be flattened and the values stored on the |
| 457 RenderNode for use later. |
| 458 |
| 459 When an Element is to be removed from its parent, regardless of the |
| 460 above, the node's renderNode attribute should be nulled out. |
| 461 |
| 322 | 462 |
| 323 ```javascript | 463 ```javascript |
| 324 var display = node.getProperty('display'); | 464 callback any ValueResolver (any value, String propertyName, RenderNode node, Flo
at containerWidth, Float containerHeight); |
| 325 if (display) { | |
| 326 node.layoutManager = new display(node, ownerManager); | |
| 327 return true; | |
| 328 } | |
| 329 return false; | |
| 330 ``` | |
| 331 | 465 |
| 332 If that code returns false, then that node an all its descendants must | 466 class RenderNode { // implemented in C++ with no virtual tables |
| 333 be dropped from the render tree. | |
| 334 | |
| 335 If any node is removed in this pass relative to the previous pass, and | |
| 336 it has an ownerLayoutManager, then call | |
| 337 | |
| 338 ```javascript | |
| 339 node.ownerLayoutManager.release(node) | |
| 340 ``` | |
| 341 | |
| 342 ...to notify the layout manager that the node went away, then set the | |
| 343 node's ownerLayoutManager attribute to null. | |
| 344 | |
| 345 ```javascript | |
| 346 partial class Element { | |
| 347 readonly attribute StyleNode? layout; // TODO(ianh): come up with a better nam
e (sadly "style" is taken) | |
| 348 // this will be null until the first time it is rendered | |
| 349 } | |
| 350 | |
| 351 callback any ValueResolver (any value, String propertyName, StyleNode node, Floa
t containerWidth, Float containerHeight); | |
| 352 | |
| 353 class StyleNode { // implemented in C++ with no virtual tables | |
| 354 // this is generated before layout | 467 // this is generated before layout |
| 355 readonly attribute String text; | 468 readonly attribute String text; |
| 356 readonly attribute Node? parentNode; | 469 readonly attribute Node? parentNode; |
| 357 readonly attribute Node? firstChild; | 470 readonly attribute Node? firstChild; |
| 358 readonly attribute Node? nextSibling; | 471 readonly attribute Node? nextSibling; |
| 359 | 472 |
| 360 // access to the results of the cascade | 473 any getProperty(PropertyHandle property, String? pseudoElement = null); |
| 361 // only works during layout and painting | 474 // looking at the cached data for the given pseudoElement: |
| 362 any getProperty(String name, String? pseudoElement = null); | |
| 363 // throw if this isn't during layout or painting | |
| 364 // TODO(ianh): if the implementation of this does allow it to be queried
the rest of the time too, relax this constraint | |
| 365 // looking at the declarations for the given pseudoElement: | |
| 366 // if there's a cached value, return it | 475 // if there's a cached value, return it |
| 367 // otherwise, if there's an applicable ParsedValue, then | 476 // otherwise, figure out which StyleValue we're going to be using, in this
order: |
| 368 // if it has a resolver: | 477 // - if we're isGhost, look out our cached declarations |
| 369 // call it | 478 // - look at this element's StyleDeclarations for the given pseudo-elemen
t (if any) |
| 370 // cache the value | 479 // - look at this element's StyleDeclarations with no pseudo-element |
| 371 // if relativeDimension is true, then mark the value as provisional | 480 // - if it's an inherited property and there's a parent |
| 372 // return the value | 481 // - call getProperty() on the parent |
| 373 // otherwise use the ParsedValue's value; cache it; return it | 482 // otherwise use the default value |
| 374 // otherwise, if a pseudo-element was specified, try again without one | 483 // resolve the StyleValue giving it the property and node in question |
| 375 // otherwise, if the property is inherited and there's a parent: | 484 // cache the value, along with the StyleValueResolverSettings |
| 376 // get it from the parent (without pseudo); cache it; return it | 485 |
| 377 // otherwise, get the default value; cache it; return it | 486 private void cascadedValueAdded(PropertyHandle property, String? pseudoElement
= null); |
| 487 private void cascadedValueRemoved(PropertyHandle property, String? pseudoEleme
nt = null); |
| 488 private void cascadedValueChanged(PropertyHandle property, String? pseudoEleme
nt = null); |
| 489 private void cascadedValueDirty(PropertyHandle property, String? pseudoElement
= null); |
| 490 // - clear the cached data for this property/pseudoElement pair |
| 491 // - if the property is needsManager, set needsManager to true |
| 492 // - if the property is needsLayout, set needsLayout to true and walk |
| 493 // up the tree setting descendantNeedsLayout |
| 494 // - if the property is needsPaint, add the node to the list of nodes that n
eed painting |
| 495 // - if the property has a dependencyBit defined, then check the cache of al
l the |
| 496 // properties on this RenderNode, and the cache for the property in all th
e child |
| 497 // nodes and (if pseudoElement is null) or the pseudoElements |
| 498 // and if any of them have the relevant dependency bit set then call |
| 499 // thatRenderNode.cascadedValueDirty(thatProperty, thatPseudoElement) |
| 500 // - if the property is inherited: |
| 501 // - call this.cascadedValueDirty(property, eachPseudoElement) |
| 502 // - call eachChildRenderNode.cascadedValueDirty(property, null) |
| 503 |
| 504 readonly attribute Boolean needsManager; |
| 505 // means that a property with needsManager:true has changed on this node |
| 378 | 506 |
| 379 readonly attribute Boolean needsLayout; | 507 readonly attribute Boolean needsLayout; |
| 380 // means that either needsLayout is true or a property with needsLayout:true
has changed on this node | 508 // means that either needsManager is true or a property with needsLayout:tru
e has changed on this node |
| 381 // needsLayout is set to false by the ownerLayoutManager's default layout()
method | 509 // needsLayout is set to false by the ownerLayoutManager's default layout()
method |
| 382 | 510 |
| 383 readonly attribute Boolean descendantNeedsLayout; | 511 readonly attribute Boolean descendantNeedsLayout; |
| 384 // means that some child of this node has needsLayout set to true | 512 // means that some child of this node has needsLayout set to true |
| 385 // descendantNeedsLayout is set to false by the ownerLayoutManager's default
layout() method | 513 // descendantNeedsLayout is set to false by the ownerLayoutManager's default
layout() method |
| 386 | 514 |
| 387 readonly attribute LayoutManager layoutManager; | 515 readonly attribute LayoutManager layoutManager; |
| 388 readonly attribute LayoutManager ownerLayoutManager; // defaults to the parent
Node.layoutManager | 516 readonly attribute LayoutManager ownerLayoutManager; // defaults to the parent
Node.layoutManager |
| 389 // if you are not the ownerLayoutManager, then ignore this StyleNode in layo
ut() and paintChildren() | 517 // if you are not the ownerLayoutManager, then ignore this RenderNode in lay
out() and paintChildren() |
| 390 // using walkChildren() does this for you | 518 // using walkChildren() does this for you |
| 391 | 519 |
| 392 // only the ownerLayoutManager can change these | 520 // only the ownerLayoutManager can change these |
| 393 readonly attribute Float x; // relative to left edge of ownerLayoutManager | 521 readonly attribute Float x; // relative to left edge of ownerLayoutManager |
| 394 readonly attribute Float y; // relative to top edge of ownerLayoutManager | 522 readonly attribute Float y; // relative to top edge of ownerLayoutManager |
| 395 readonly attribute Float width; | 523 readonly attribute Float width; |
| 396 readonly attribute Float height; | 524 readonly attribute Float height; |
| 397 readonly attribute Boolean isNew; // node has just been added (and maybe you w
ant to animate it in) | 525 readonly attribute Boolean isNew; // node has just been added (and maybe you w
ant to animate it in) |
| 398 readonly attribute Boolean isGhost; // node has just been removed (and maybe y
ou want to animate it away) | 526 readonly attribute Boolean isGhost; // node has just been removed (and maybe y
ou want to animate it away) |
| 399 } | 527 } |
| 400 ``` | 528 ``` |
| 401 | 529 |
| 402 The flattened tree is represented as a hierarchy of Node objects. For | 530 The flattened tree is represented as a hierarchy of Node objects. For |
| 403 any element that only contains text node children, the "text" property | 531 any element that only contains text node children, the "text" property |
| 404 is set accordingly. For elements with mixed text node and non-text | 532 is set accordingly. For elements with mixed text node and non-text |
| 405 node children, each run of text nodes is represented as a separate | 533 node children, each run of text nodes is represented as a separate |
| 406 Node with the "text" property set accordingly and the styles set as if | 534 Node with the "text" property set accordingly and the styles set as if |
| 407 the Node inherited everything inheritable from its parent. | 535 the Node inherited everything inheritable from its parent. |
| 408 | 536 |
| 409 | 537 |
| 410 Layout | 538 Layout |
| 411 ------ | 539 ------ |
| 412 | 540 |
| 413 sky:core registers 'display' as follows: | 541 sky:core registers 'display' as follows: |
| 414 | 542 |
| 415 ```javascript | 543 ```javascript |
| 416 { | 544 { |
| 417 name: 'display', | 545 name: 'display', |
| 418 type: sky.DisplayStyleValueType, | 546 grammar: sky.DisplayStyleGrammar, |
| 419 inherits: false, | 547 inherited: false, |
| 420 initialValue: sky.BlockLayoutManager, | 548 initialValue: sky.BlockLayoutManager, |
| 421 needsLayout: true, | 549 needsManager: true, |
| 422 } | 550 } |
| 423 ``` | 551 ``` |
| 424 | 552 |
| 425 The following API is then used to add new layout manager types to 'display': | 553 The following API is then used to add new layout manager types to 'display': |
| 426 | 554 |
| 427 ```javascript | 555 ```javascript |
| 428 void registerLayoutManager(String displayValue, LayoutManagerConstructor? layout
Manager); | 556 void registerLayoutManager(String displayValue, LayoutManagerConstructor? layout
Manager); |
| 429 ``` | 557 ``` |
| 430 | 558 |
| 431 sky:core by default registers: | 559 sky:core by default registers: |
| 432 | 560 |
| 433 - 'block': sky.BlockLayoutManager | 561 - 'block': sky.BlockLayoutManager |
| 434 - 'paragraph': sky.ParagraphLayoutManager | 562 - 'paragraph': sky.ParagraphLayoutManager |
| 435 - 'inline': sky.InlineLayoutManager | 563 - 'inline': sky.InlineLayoutManager |
| 436 - 'none': null | 564 - 'none': null |
| 437 | 565 |
| 438 | 566 |
| 439 Layout managers inherit from the following API: | 567 Layout managers inherit from the following API: |
| 440 | 568 |
| 441 ```javascript | 569 ```javascript |
| 442 class LayoutManager : EventTarget { | 570 class LayoutManager : EventTarget { |
| 443 readonly attribute StyleNode node; | 571 readonly attribute RenderNode node; |
| 444 constructor LayoutManager(StyleNode node); | 572 constructor LayoutManager(RenderNode node); |
| 573 // sets needsManager to false on the node |
| 445 | 574 |
| 446 readonly attribute Boolean autoreap; | 575 readonly attribute Boolean autoreap; |
| 447 // defaults to true | 576 // defaults to true |
| 448 // when true, any children that are isNew or isGhost are welcomed/reaped imp
licitly by default layout() | 577 // when true, any children that are isNew are automatically welcomed by the
default layout() |
| 578 // when true, children that are removd don't get set to isGhost=true, they'r
e just removed |
| 449 | 579 |
| 450 virtual Array<EventTarget> getEventDispatchChain(); // O(N) in number of this.
node's ancestors // implements EventTarget.getEventDispatchChain() | 580 virtual Array<EventTarget> getEventDispatchChain(); // O(N) in number of this.
node's ancestors // implements EventTarget.getEventDispatchChain() |
| 451 // let result = []; | 581 // let result = []; |
| 452 // let node = this.node; | 582 // let node = this.node; |
| 453 // while (node && node.layoutManager) { | 583 // while (node && node.layoutManager) { |
| 454 // result.push(node.layoutManager); | 584 // result.push(node.layoutManager); |
| 455 // node = node.parentNode; | 585 // node = node.parentNode; |
| 456 // } | 586 // } |
| 457 // return result; | 587 // return result; |
| 458 | 588 |
| 459 void take(StyleNode victim); // sets victim.ownerLayoutManager = this; | 589 void setProperty(RenderNode node, PropertyHandle property, any value, String?
pseudoElement = null); // O(1) |
| 590 // if called from an adjustProperties() method during the property adjustmen
t phase, |
| 591 // replaces the value that getProperty() would return on that node with /val
ue/ |
| 592 |
| 593 virtual void adjustProperties(); |
| 594 // called before layout; can call setProperty to set new values |
| 595 // note that this happens after the cascade so inheritance isn't applied to
this new value |
| 596 // also note that the value you set is a post-computation value, not an Abst
ractStyleValue descendant |
| 597 // so e.g. you can have an AnimatableColorStyleValue, get its value, and pus
h it into setProperty() |
| 598 // but you can't push the AnimatableColorStyleValue directly in, it won't do
what you expect |
| 599 |
| 600 void take(RenderNode victim); // sets victim.ownerLayoutManager = this; |
| 460 // assert: victim hasn't been take()n yet during this layout | 601 // assert: victim hasn't been take()n yet during this layout |
| 461 // assert: victim.needsLayout == true | 602 // assert: victim.needsLayout == true |
| 462 // assert: an ancestor of victim has node.layoutManager == this (aka, victim
is a descendant of this.node) | 603 // assert: an ancestor of victim has node.layoutManager == this (aka, victim
is a descendant of this.node) |
| 463 | 604 |
| 464 virtual void release(StyleNode victim); | 605 virtual void release(RenderNode victim); |
| 465 // called when the StyleNode was removed from the tree | 606 // called when the RenderNode was removed from the tree |
| 466 | 607 |
| 467 void setChildPosition(child, x, y); // sets child.x, child.y | 608 void setChildPosition(child, x, y); // sets child.x, child.y |
| 468 void setChildX(child, y); // sets child.x | 609 void setChildX(child, y); // sets child.x |
| 469 void setChildY(child, y); // sets child.y | 610 void setChildY(child, y); // sets child.y |
| 470 void setChildSize(child, width, height); // sets child.width, child.height | 611 void setChildSize(child, width, height); // sets child.width, child.height |
| 471 void setChildWidth(child, width); // sets child.width | 612 void setChildWidth(child, width); // sets child.width |
| 472 void setChildHeight(child, height); // sets child.height | 613 void setChildHeight(child, height); // sets child.height |
| 473 // for setChildSize/Width/Height: if the new dimension is different than the
last assumed dimensions, and | 614 // for setChildSize/Width/Height: if the new dimension is different than the
last assumed dimensions, and |
| 474 // any StyleNodes with an ownerLayoutManager==this have cached values for ge
tProperty() that are marked | 615 // any RenderNodes with an ownerLayoutManager==this have cached values for g
etProperty() that are marked |
| 475 // as provisional, clear them | 616 // as layout-dependent, clear them |
| 476 void welcomeChild(child); // resets child.isNew | 617 void welcomeChild(child); // resets child.isNew |
| 477 void reapChild(child); // resets child.isGhost | 618 void reapChild(child); // resets child.isGhost |
| 478 | 619 |
| 479 Generator<StyleNode> walkChildren(); | 620 Generator<RenderNode> walkChildren(); |
| 480 // returns a generator that iterates over the children, skipping any whose o
wnerLayoutManager is not |this| | 621 // returns a generator that iterates over the children, skipping any whose o
wnerLayoutManager is not |this| |
| 481 | 622 |
| 482 Generator<StyleNode> walkChildrenBackwards(); | 623 Generator<RenderNode> walkChildrenBackwards(); |
| 483 // returns a generator that iterates over the children backwards, skipping a
ny whose ownerLayoutManager is not |this| | 624 // returns a generator that iterates over the children backwards, skipping a
ny whose ownerLayoutManager is not |this| |
| 484 | 625 |
| 485 void assumeDimensions(Float width, Float height); | 626 void assumeDimensions(Float width, Float height); |
| 486 // sets the assumed dimensions for calls to getProperty() on StyleNodes that
have this as an ownerLayoutManager | 627 // sets the assumed dimensions for calls to getProperty() on RenderNodes tha
t have this as an ownerLayoutManager |
| 487 // if the new dimension is different than the last assumed dimensions, and a
ny StyleNodes with an | 628 // if the new dimension is different than the last assumed dimensions, and a
ny RenderNodes with an |
| 488 // ownerLayoutManager==this have cached values for getProperty() that are ma
rked as provisional, clear them | 629 // ownerLayoutManager==this have cached values for getProperty() that are ma
rked as layout-dependent, clear them |
| 489 // TODO(ianh): should we force this to match the input to layout(), when cal
led from inside layout() and when | 630 // TODO(ianh): should we force this to match the input to layout(), when cal
led from inside layout() and when |
| 490 // layout() has a forced width and/or height? | 631 // layout() has a forced width and/or height? |
| 491 | 632 |
| 492 virtual LayoutValueRange getIntrinsicWidth(Float? defaultWidth = null); | 633 virtual LayoutValueRange getIntrinsicWidth(Float? defaultWidth = null); |
| 493 /* | 634 /* |
| 494 function getIntrinsicWidth(defaultWidth) { | 635 function getIntrinsicWidth(defaultWidth) { |
| 495 if (defaultWidth == null) { | 636 if (defaultWidth == null) { |
| 496 defaultWidth = this.node.getProperty('width'); | 637 defaultWidth = this.node.getProperty('width'); |
| 497 if (typeof defaultWidth != 'number') | 638 if (typeof defaultWidth != 'number') |
| 498 defaultWidth = 0; | 639 defaultWidth = 0; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 539 defaultHeight = minHeight; | 680 defaultHeight = minHeight; |
| 540 return { | 681 return { |
| 541 minimum: minHeight, | 682 minimum: minHeight, |
| 542 value: defaultHeight, | 683 value: defaultHeight, |
| 543 maximum: maxHeight, | 684 maximum: maxHeight, |
| 544 }; | 685 }; |
| 545 } | 686 } |
| 546 */ | 687 */ |
| 547 | 688 |
| 548 void markAsLaidOut(); // sets this.node.needsLayout and this.node.descendantNe
edsLayout to false | 689 void markAsLaidOut(); // sets this.node.needsLayout and this.node.descendantNe
edsLayout to false |
| 549 virtual Dimensions layout(Number? width, Number? height); | 690 virtual Dimensions layout(Float? width, Float? height); |
| 550 // call markAsLaidOut(); | 691 // call markAsLaidOut(); |
| 551 // if autoreap is true: use walkChildren() to call welcomeChild() and reapCh
ild() on each child | 692 // if autoreap is true: use walkChildren() to call welcomeChild() and reapCh
ild() on each child |
| 552 // if width is null, set width to getIntrinsicWidth().value | 693 // if width is null, set width to getIntrinsicWidth().value |
| 553 // if height is null, set width height getIntrinsicHeight().value | 694 // if height is null, set width height getIntrinsicHeight().value |
| 554 // call this.assumeDimensions(width, height); | 695 // call this.assumeDimensions(width, height); |
| 555 // call this.layoutChildren(width, height); | 696 // call this.layoutChildren(width, height); |
| 556 // return { width: width, height: height } | 697 // return { width: width, height: height } |
| 557 // - this should always call this.markAsLaidOut() to reset needsLayout | 698 // - this should always call this.markAsLaidOut() to reset needsLayout |
| 558 // - the return value should include the final value for whichever of the wi
dth and height arguments | 699 // - the return value should include the final value for whichever of the wi
dth and height arguments |
| 559 // that is null | 700 // that is null |
| 560 // - subclasses that want to make 'auto' values dependent on the children sh
ould override this | 701 // - subclasses that want to make 'auto' values dependent on the children sh
ould override this |
| 561 // entirely, rather than overriding layoutChildren | 702 // entirely, rather than overriding layoutChildren |
| 562 | 703 |
| 563 virtual void layoutChildren(Number width, Number height); | 704 virtual void layoutChildren(Float width, Float height); |
| 564 // default implementation does nothing | 705 // default implementation does nothing |
| 565 // - override this if you want to lay out children but not have the children
affect your dimensions | 706 // - override this if you want to lay out children but not have the children
affect your dimensions |
| 566 | 707 |
| 567 virtual void paint(RenderingSurface canvas); | 708 virtual void paint(RenderingSurface canvas); |
| 568 // set a clip rect on the canvas for rect(0,0,this.width,this.height) | 709 // set a clip rect on the canvas for rect(0,0,this.width,this.height) |
| 710 // (? we don't really have to do this; consider shadows...) |
| 569 // call the painter of each property, in order they were registered, which o
n this element has a painter | 711 // call the painter of each property, in order they were registered, which o
n this element has a painter |
| 570 // call this.paintChildren(canvas) | 712 // call this.paintChildren(canvas) |
| 571 // (the default implementation doesn't paint anything on top of the children
) | 713 // (the default implementation doesn't paint anything on top of the children
) |
| 572 // unset the clip | 714 // unset the clip |
| 573 // - this gets called by the system if: | 715 // - this gets called by the system if: |
| 574 // - you are in your parent's current display list and it's in its parent
's and so on up to the top, and | 716 // - you are in your parent's current display list and it's in its parent
's and so on up to the top, and |
| 575 // - you haven't had paint() called since the last time you were dirtied | 717 // - you haven't had paint() called since the last time you were dirtied |
| 576 // - the following things make you dirty: | 718 // - the following things make you dirty: |
| 577 // - dimensions of your style node changed | 719 // - dimensions of your RenderNode changed |
| 578 // - one of your properties with needsLayout or needsPaint changed | 720 // - one of your properties with needsLayout or needsPaint changed |
| 579 | 721 |
| 580 virtual void paintChildren(RenderingSurface canvas); | 722 virtual void paintChildren(RenderingSurface canvas); |
| 581 // for each child returned by walkChildren(): | 723 // for each child returned by walkChildren(): |
| 582 // if child bounds intersects our bounds: | 724 // if child bounds intersects our bounds: |
| 583 // call canvas.paintChild(child); | 725 // call canvas.paintChild(child); |
| 584 // - you should skip children that will be clipped out of yourself because t
hey're outside your bounds | 726 // - you should skip children that will be clipped out of yourself because t
hey're outside your bounds |
| 585 // - if you transform the canvas, you'll have to implement your own version
of paintChildren() so | 727 // - if you transform the canvas, you'll have to implement your own version
of paintChildren() so |
| 586 // that you don't skip the children that are visible in the new coordinate
space but wouldn't be | 728 // that you don't skip the children that are visible in the new coordinate
space but wouldn't be |
| 587 // without the transform | 729 // without the transform |
| 588 | 730 |
| 589 virtual StyleNode hitTest(Float x, Float y); | 731 virtual RenderNode hitTest(Float x, Float y); |
| 590 // default implementation uses the node's children nodes' x, y, | 732 // default implementation uses the node's children nodes' x, y, |
| 591 // width, and height, skipping any that have width=0 or height=0, or | 733 // width, and height, skipping any that have width=0 or height=0, or |
| 592 // whose ownerLayoutManager is not |this| | 734 // whose ownerLayoutManager is not |this| |
| 593 // default implementation walks the tree backwards from its built-in order | 735 // default implementation walks the tree backwards from its built-in order |
| 594 // if no child is hit, then return this.node | 736 // if no child is hit, then return this.node |
| 595 // override this if you changed your children's z-order, or if you used take
() to | 737 // override this if you changed your children's z-order, or if you used take
() to |
| 596 // hoist some descendants up to be your responsibility, or if your children
aren't | 738 // hoist some descendants up to be your responsibility, or if your children
aren't |
| 597 // rectangular (e.g. you lay them out in a hex grid) | 739 // rectangular (e.g. you lay them out in a hex grid) |
| 598 // make sure to offset the value you pass your children: child.layoutManager
.hitTest(x-child.x, y-child.y) | 740 // make sure to offset the value you pass your children: child.layoutManager
.hitTest(x-child.x, y-child.y) |
| 599 | |
| 600 } | 741 } |
| 601 | 742 |
| 602 dictionary LayoutValueRange { | 743 dictionary LayoutValueRange { |
| 603 // negative values here should be treated as zero | 744 // negative values here should be treated as zero |
| 604 Float minimum = 0; | 745 Float minimum = 0; |
| 605 Float value = 0; // ideal desired width; if it's not in the range minimum .. m
aximum then it overrides minimum and maximum | 746 Float value = 0; // ideal desired width; if it's not in the range minimum .. m
aximum then it overrides minimum and maximum |
| 606 (Float or Infinity) maximum = Infinity; | 747 (Float or Infinity) maximum = Infinity; |
| 607 } | 748 } |
| 608 | 749 |
| 609 dictionary Dimensions { | 750 dictionary Dimensions { |
| 610 Float width = 0; | 751 Float width = 0; |
| 611 Float height = 0; | 752 Float height = 0; |
| 612 } | 753 } |
| 613 ``` | 754 ``` |
| 614 | 755 |
| 615 | 756 |
| 616 Paint | 757 Paint |
| 617 ----- | 758 ----- |
| 618 | 759 |
| 760 Sky has a list of RenderNodes that need painting. |
| 761 When a RenderNode is created, it's added to this list. |
| 762 |
| 619 ```javascript | 763 ```javascript |
| 620 callback void Painter (StyleNode node, RenderingSurface canvas); | 764 callback void Painter (RenderNode node, RenderingSurface canvas); |
| 621 | 765 |
| 622 class RenderingSurface { | 766 class RenderingSurface { |
| 623 | 767 |
| 624 // ... (API similar to <canvas>'s 2D API) | 768 // ... (API similar to <canvas>'s 2D API) |
| 625 | 769 |
| 626 void paintChild(StyleNode node); | 770 void paintChild(RenderNode node); |
| 627 // inserts a "paint this child" instruction in this canvas's display list. | 771 // inserts a "paint this child" instruction in this canvas's display list. |
| 628 // the child's display list, transformed by the child's x and y coordinates,
will be inserted into this | 772 // the child's display list, transformed by the child's x and y coordinates,
will be inserted into this |
| 629 // display list during painting. | 773 // display list during painting. |
| 630 } | 774 } |
| 631 ``` | 775 ``` |
| 632 | 776 |
| 633 | 777 |
| 778 The default framework provides global hooks for extending the painting of: |
| 779 |
| 780 - borders |
| 781 - backgrounds |
| 782 |
| 783 These are called during the default framework's layout managers' |
| 784 paint() functions. They are also made available so that other people |
| 785 can call them from their paint() functions. |
| 786 |
| 787 |
| 788 |
| 634 Default Styles | 789 Default Styles |
| 635 -------------- | 790 -------------- |
| 636 | 791 |
| 637 In the constructors for the default elements, they add to themselves | 792 In the constructors for the default elements, they add to themselves |
| 638 StyleDeclaration objects as follows: | 793 StyleDeclaration objects as follows: |
| 639 | 794 |
| 640 * ``import`` | 795 * ``import`` |
| 641 * ``template`` | 796 * ``template`` |
| 642 * ``style`` | 797 * ``style`` |
| 643 * ``script`` | 798 * ``script`` |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 678 ```javascript | 833 ```javascript |
| 679 { display: { value: sky.ErrorLayoutManager } } | 834 { display: { value: sky.ErrorLayoutManager } } |
| 680 ``` | 835 ``` |
| 681 | 836 |
| 682 The ``div`` element doesn't have any default styles. | 837 The ``div`` element doesn't have any default styles. |
| 683 | 838 |
| 684 These declarations are all shared between all the elements (so e.g. if | 839 These declarations are all shared between all the elements (so e.g. if |
| 685 you reach in and change the declaration that was added to a ``title`` | 840 you reach in and change the declaration that was added to a ``title`` |
| 686 element, you're going to change the styles of all the other | 841 element, you're going to change the styles of all the other |
| 687 default-hidden elements). | 842 default-hidden elements). |
| OLD | NEW |