| Index: sky/specs/style2.md
|
| diff --git a/sky/specs/style2.md b/sky/specs/style2.md
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f53498a25b0046ff35501a5bc13b869ae6f00e7c
|
| --- /dev/null
|
| +++ b/sky/specs/style2.md
|
| @@ -0,0 +1,351 @@
|
| +Sky Style Language
|
| +==================
|
| +
|
| +Note: This is a work in progress that will eventually replace
|
| +(style.md)[style.md].
|
| +
|
| +The Sky style API looks like the following:
|
| +
|
| +```dart
|
| +
|
| + // all properties can be set as strings:
|
| + element.style['color'] = 'blue';
|
| +
|
| + // some properties have dedicated APIs
|
| + // color
|
| + element.style.color.red += 1; // 0..255
|
| + element.style.color.blue += 10; // 0..255
|
| + element.style.color.green = 255; // 0..255
|
| + element.style.color.alpha = 128; // 0..255
|
| + // transform
|
| + element.style.transform..reset()
|
| + ..translate(100, 100)
|
| + ..rotate(PI/8)
|
| + ..translate(-100, -100);
|
| + element.style.transform.translate(10, 0);
|
| + // height, width
|
| + element.style.height.auto = true;
|
| + if (element.style.height.auto)
|
| + element.style.height.pixels = 10;
|
| + element.style.height.pixels += 1;
|
| + element.style.height.em = 1;
|
| +
|
| + // each property with a dedicated API defines a shorthand setter
|
| + // style.transform takes a matrix:
|
| + element.style.transform = new Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
|
| + // style.color takes a 32bit int:
|
| + element.style.color = 0xFF009900;
|
| + // style.height and style.width takes pixels or the constant 'auto':
|
| + element.style.height = auto;
|
| + element.style.width = 100;
|
| + // all properties with a dedicated API can also be set to null, inherit, or initial:
|
| + element.style.transform = null; // unset the property
|
| + element.style.color = initial; // set it to its initial value
|
| + element.style.color = inherit; // make it get its parent's value
|
| +
|
| + // you can create a blank StyleDeclaration object:
|
| + var style = new StyleDeclaration();
|
| + // you can replace an element's StyleDeclaration object wholesale:
|
| + element.style = style;
|
| + // you can clone a StyleDeclaration object:
|
| + var style2 = new StyleDeclaration.clone(style);
|
| +```
|
| +
|
| +The dart:sky library contains the following to define this API:
|
| +
|
| +```dart
|
| +import 'dart:mirrors';
|
| +import 'dart:math';
|
| +
|
| +class WeakMap<Key, Value> {
|
| + Expando<Value> _map = new Expando<Value>();
|
| + operator[](Key key) => _map[key];
|
| + operator[]=(Key key, Value value) => _map[key] = value;
|
| + bool containsKey(Key key) => _map[key] != null;
|
| +}
|
| +
|
| +typedef void StringSetter(Symbol propertySymbol, StyleDeclaration declaration, String value);
|
| +typedef String StringGetter(Symbol propertySymbol, StyleDeclaration declaration);
|
| +typedef Property ObjectConstructor(Symbol propertySymbol, StyleDeclaration declaration);
|
| +
|
| +class PropertyTable {
|
| + const PropertyTable({this.symbol, this.inherited, this.stringGetter, this.stringSetter, this.objectConstructor});
|
| + final Symbol symbol;
|
| + final bool inherited;
|
| + final StringSetter stringSetter;
|
| + final StringGetter stringGetter;
|
| + final ObjectConstructor objectConstructor;
|
| +}
|
| +
|
| +Map<Symbol, PropertyTable> _registeredProperties = new Map<Symbol, PropertyTable>();
|
| +void registerProperty(PropertyTable data) {
|
| + assert(data.symbol is Symbol);
|
| + assert(data.inherited is bool);
|
| + assert(data.stringSetter is StringSetter);
|
| + assert(data.stringGetter is StringGetter);
|
| + assert(data.objectConstructor == null || data.objectConstructor is ObjectConstructor);
|
| + assert(!_registeredProperties.containsKey(data.symbol));
|
| + _registeredProperties[data.symbol] = data;
|
| +}
|
| +
|
| +@proxy
|
| +class StyleDeclaration {
|
| + StyleDeclaration() { this._init(); }
|
| + StyleDeclaration.clone(StyleDeclaration template) { this.init(template); }
|
| + external void _init([StyleDeclaration template]); // O(1)
|
| + // This class has C++-backed internal state representing the
|
| + // properties known to the system. It's assumed that Property
|
| + // subclasses are also C++-backed and can directly manipulate this
|
| + // internal state.
|
| + // If the argument 'template' is provided, then this should be a clone
|
| + // of the styles of the template StyleDeclaration
|
| +
|
| + operator [](String propertyName) {
|
| + var propertySymbol = new Symbol(propertyName);
|
| + if (_registeredProperties.containsKey(propertySymbol))
|
| + return _registeredProperties[propertySymbol].stringGetter(propertySymbol, this);
|
| + throw new ArgumentError(propertyName);
|
| + }
|
| +
|
| + operator []=(String propertyName, String newValue) {
|
| + var propertySymbol = new Symbol(propertyName);
|
| + if (_registeredProperties.containsKey(propertySymbol))
|
| + return _registeredProperties[propertySymbol].stringSetter(propertySymbol, this, newValue);
|
| + throw new ArgumentError(propertyName);
|
| + }
|
| +
|
| + // some properties expose dedicated APIs so you don't have to use string manipulation
|
| + WeakMap<Symbol, Property> _properties = new WeakMap<Symbol, Property>();
|
| + noSuchMethod(Invocation invocation) {
|
| + Symbol propertySymbol;
|
| + if (invocation.isSetter) {
|
| + // when it's a setter, the name will be "foo=" rather than "foo"
|
| + String propertyName = MirrorSystem.getName(invocation.memberName);
|
| + assert(propertyName[propertyName.length-1] == '=');
|
| + propertySymbol = new Symbol(propertyName.substring(0, propertyName.length-1));
|
| + } else {
|
| + propertySymbol = invocation.memberName;
|
| + }
|
| + Property property;
|
| + if (!_properties.containsKey(propertySymbol)) {
|
| + if (_registeredProperties.containsKey(propertySymbol)) {
|
| + var constructor = _registeredProperties[propertySymbol].objectConstructor;
|
| + if (constructor == null)
|
| + return super.noSuchMethod(invocation);
|
| + property = constructor(propertySymbol, this);
|
| + } else {
|
| + return super.noSuchMethod(invocation);
|
| + }
|
| + } else {
|
| + property = _properties[propertySymbol];
|
| + }
|
| + if (invocation.isMethod) {
|
| + if (property is Function)
|
| + return Function.apply(property as Function, invocation.positionalArguments, invocation.namedArguments);
|
| + return super.noSuchMethod(invocation);
|
| + }
|
| + if (invocation.isSetter)
|
| + return Function.apply(property.setter, invocation.positionalArguments, invocation.namedArguments);
|
| + return property;
|
| + }
|
| +}
|
| +
|
| +const initial = const Object();
|
| +const inherit = const Object();
|
| +
|
| +abstract class Property {
|
| + Property(this.propertySymbol, this.declaration);
|
| + final StyleDeclaration declaration;
|
| + final Symbol propertySymbol;
|
| +
|
| + bool get inherited => _registeredProperties[propertySymbol].inherited;
|
| +
|
| + bool get initial => _isInitial();
|
| + void set initial (value) {
|
| + if (value == true)
|
| + return _setInitial();
|
| + throw new ArgumentError(value);
|
| + }
|
| +
|
| + bool get inherit => _isInherit();
|
| + void set inherit (value) {
|
| + if (value == true)
|
| + return _setInherit();
|
| + throw new ArgumentError(value);
|
| + }
|
| +
|
| + void setter(dynamic value) {
|
| + if (value == initial)
|
| + return _setInitial();
|
| + if (value == inherit)
|
| + return _setInitial();
|
| + if (value == null)
|
| + return _unset();
|
| + throw new ArgumentError(value);
|
| + }
|
| +
|
| + external bool _isInitial();
|
| + external void _setInitial();
|
| + external bool _isInherit();
|
| + external void _setInherit();
|
| + external void _unset();
|
| +}
|
| +```
|
| +
|
| +Sky defines the following properties, currently as part of the core,
|
| +but eventually this will be moved to the framework:
|
| +
|
| +```dart
|
| +class LengthProperty extends Property {
|
| + LengthProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(propertySymbol, declaration);
|
| +
|
| + double get pixels => _getPixels();
|
| + void set pixels (value) => _setPixels(value);
|
| +
|
| + double get inches => _getPixels() / 96.0;
|
| + void set inches (value) => _setPixels(value * 96.0);
|
| +
|
| + double get em => _getEm();
|
| + void set em (value) => _setEm(value);
|
| +
|
| + void setter(dynamic value) {
|
| + if (value is num)
|
| + return _setPixels(value.toDouble());
|
| + return super.setter(value);
|
| + }
|
| +
|
| + external double _getPixels();
|
| + // throws StateError if the value isn't in pixels
|
| + external void _setPixels(double value);
|
| +
|
| + external double _getEm();
|
| + // throws StateError if the value isn't in pixels
|
| + external void _setEm(double value);
|
| +}
|
| +
|
| +const auto = const Object();
|
| +
|
| +class AutoLengthProperty extends LengthProperty {
|
| + AutoLengthProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(propertySymbol, declaration);
|
| +
|
| + bool get auto => _isAuto();
|
| + void set auto (value) {
|
| + if (value == true)
|
| + _setAuto();
|
| + throw new ArgumentError(value);
|
| + }
|
| +
|
| + void setter(dynamic value) {
|
| + if (value == auto)
|
| + return _setAuto();
|
| + return super.setter(value);
|
| + }
|
| +
|
| + external bool _isAuto();
|
| + external void _setAuto();
|
| +}
|
| +
|
| +class ColorProperty extends Property {
|
| + ColorProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(propertySymbol, declaration);
|
| +
|
| + int get alpha => _getRGBA() & 0xFF000000 >> 24;
|
| + void set alpha (int value) => _setRGBA(_getRGBA() & 0x00FFFFFF + value << 24);
|
| + int get red => _getRGBA() & 0x00FF0000 >> 16;
|
| + void set red (int value) => _setRGBA(_getRGBA() & 0xFF00FFFF + value << 16);
|
| + int get green => _getRGBA() & 0x0000FF00 >> 8;
|
| + void set green (int value) => _setRGBA(_getRGBA() & 0xFFFF00FF + value << 8);
|
| + int get blue => _getRGBA() & 0x000000FF >> 0;
|
| + void set blue (int value) => _setRGBA(_getRGBA() & 0xFFFFFF00 + value << 0);
|
| +
|
| + int get rgba => _getRGBA();
|
| + void set rgba (int value) => _setRGBA(value);
|
| +
|
| + void setter(dynamic value) {
|
| + if (value is int)
|
| + return _setRGBA(value);
|
| + return super.setter(value);
|
| + }
|
| +
|
| + external int _getRGBA();
|
| + // throws StateError if the value isn't a color
|
| + external void _setRGBA(int value);
|
| +}
|
| +
|
| +class Matrix {
|
| + const Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
|
| +
|
| + // +- -+
|
| + // | a c e |
|
| + // | b d f |
|
| + // | 0 0 1 |
|
| + // +- -+
|
| +
|
| + final double a;
|
| + final double b;
|
| + final double c;
|
| + final double d;
|
| + final double e;
|
| + final double f;
|
| +}
|
| +
|
| +class TransformProperty extends Property {
|
| + TransformProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(propertySymbol, declaration);
|
| +
|
| + void reset() => setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
|
| +
|
| + void translate(double dx, double dy) => transform(1.0, 0.0, 0.0, 1.0, dx, dy);
|
| + void scale(double dw, double dh) => transform(dw, 0.0, 0.0, dh, 0.0, 0.0);
|
| + void rotate(double theta) => transform(cos(theta), -sin(theta), sin(theta), cos(theta), 0.0, 0.0);
|
| +
|
| + // there's no "transform" getter since it would always return a new Matrix
|
| + // such that foo.transform == foo.transform would never be true
|
| + // and foo.transform = bar; bar == foo.transform would also never be true
|
| + // which is bad API
|
| +
|
| + external Matrix getTransform();
|
| + // throws StateError if the value isn't a matrix
|
| + // returns a new matrix each time
|
| + external void setTransform(a, b, c, d, e, f);
|
| + external void transform(a, b, c, d, e, f);
|
| + // throws StateError if the value isn't a matrix
|
| +}
|
| +
|
| +external void autoLengthPropertyStringSetter(Symbol propertySymbol, StyleDeclaration declaration, String value);
|
| +external String autoLengthPropertyStringGetter(Symbol propertySymbol, StyleDeclaration declaration);
|
| +external void colorPropertyStringSetter(Symbol propertySymbol, StyleDeclaration declaration, String value);
|
| +external String colorPropertyStringGetter(Symbol propertySymbol, StyleDeclaration declaration);
|
| +external void transformPropertyStringSetter(Symbol propertySymbol, StyleDeclaration declaration, String value);
|
| +external String transformPropertyStringGetter(Symbol propertySymbol, StyleDeclaration declaration);
|
| +
|
| +void _init() {
|
| + registerProperty(new PropertyTable(
|
| + symbol: #height,
|
| + inherited: false,
|
| + stringSetter: autoLengthPropertyStringSetter,
|
| + stringGetter: autoLengthPropertyStringGetter,
|
| + objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) =>
|
| + new AutoLengthProperty(propertySymbol, declaration)));
|
| + registerProperty(new PropertyTable(
|
| + symbol: #width,
|
| + inherited: false,
|
| + stringSetter: autoLengthPropertyStringSetter,
|
| + stringGetter: autoLengthPropertyStringGetter,
|
| + objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) =>
|
| + new AutoLengthProperty(propertySymbol, declaration)));
|
| + registerProperty(new PropertyTable(
|
| + symbol: #color,
|
| + inherited: false,
|
| + stringSetter: colorPropertyStringSetter,
|
| + stringGetter: colorPropertyStringGetter,
|
| + objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) =>
|
| + new ColorProperty(propertySymbol, declaration)));
|
| + registerProperty(new PropertyTable(
|
| + symbol: #transform,
|
| + inherited: false,
|
| + stringSetter: transformPropertyStringSetter,
|
| + stringGetter: transformPropertyStringGetter,
|
| + objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) =>
|
| + new TransformProperty(propertySymbol, declaration)));
|
| +}
|
| +```
|
| +
|
|
|