| OLD | NEW |
| (Empty) | |
| 1 Sky Style Language |
| 2 ================== |
| 3 |
| 4 Note: This is a work in progress that will eventually replace |
| 5 (style.md)[style.md]. |
| 6 |
| 7 The Sky style API looks like the following: |
| 8 |
| 9 ```dart |
| 10 |
| 11 // all properties can be set as strings: |
| 12 element.style['color'] = 'blue'; |
| 13 |
| 14 // some properties have dedicated APIs |
| 15 // color |
| 16 element.style.color.red += 1; // 0..255 |
| 17 element.style.color.blue += 10; // 0..255 |
| 18 element.style.color.green = 255; // 0..255 |
| 19 element.style.color.alpha = 128; // 0..255 |
| 20 // transform |
| 21 element.style.transform..reset() |
| 22 ..translate(100, 100) |
| 23 ..rotate(PI/8) |
| 24 ..translate(-100, -100); |
| 25 element.style.transform.translate(10, 0); |
| 26 // height, width |
| 27 element.style.height.auto = true; |
| 28 if (element.style.height.auto) |
| 29 element.style.height.pixels = 10; |
| 30 element.style.height.pixels += 1; |
| 31 element.style.height.em = 1; |
| 32 |
| 33 // each property with a dedicated API defines a shorthand setter |
| 34 // style.transform takes a matrix: |
| 35 element.style.transform = new Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); |
| 36 // style.color takes a 32bit int: |
| 37 element.style.color = 0xFF009900; |
| 38 // style.height and style.width takes pixels or the constant 'auto': |
| 39 element.style.height = auto; |
| 40 element.style.width = 100; |
| 41 // all properties with a dedicated API can also be set to null, inherit, or in
itial: |
| 42 element.style.transform = null; // unset the property |
| 43 element.style.color = initial; // set it to its initial value |
| 44 element.style.color = inherit; // make it get its parent's value |
| 45 |
| 46 // you can create a blank StyleDeclaration object: |
| 47 var style = new StyleDeclaration(); |
| 48 // you can replace an element's StyleDeclaration object wholesale: |
| 49 element.style = style; |
| 50 // you can clone a StyleDeclaration object: |
| 51 var style2 = new StyleDeclaration.clone(style); |
| 52 ``` |
| 53 |
| 54 The dart:sky library contains the following to define this API: |
| 55 |
| 56 ```dart |
| 57 import 'dart:mirrors'; |
| 58 import 'dart:math'; |
| 59 |
| 60 class WeakMap<Key, Value> { |
| 61 Expando<Value> _map = new Expando<Value>(); |
| 62 operator[](Key key) => _map[key]; |
| 63 operator[]=(Key key, Value value) => _map[key] = value; |
| 64 bool containsKey(Key key) => _map[key] != null; |
| 65 } |
| 66 |
| 67 typedef void StringSetter(Symbol propertySymbol, StyleDeclaration declaration, S
tring value); |
| 68 typedef String StringGetter(Symbol propertySymbol, StyleDeclaration declaration)
; |
| 69 typedef Property ObjectConstructor(Symbol propertySymbol, StyleDeclaration decla
ration); |
| 70 |
| 71 class PropertyTable { |
| 72 const PropertyTable({this.symbol, this.inherited, this.stringGetter, this.stri
ngSetter, this.objectConstructor}); |
| 73 final Symbol symbol; |
| 74 final bool inherited; |
| 75 final StringSetter stringSetter; |
| 76 final StringGetter stringGetter; |
| 77 final ObjectConstructor objectConstructor; |
| 78 } |
| 79 |
| 80 Map<Symbol, PropertyTable> _registeredProperties = new Map<Symbol, PropertyTable
>(); |
| 81 void registerProperty(PropertyTable data) { |
| 82 assert(data.symbol is Symbol); |
| 83 assert(data.inherited is bool); |
| 84 assert(data.stringSetter is StringSetter); |
| 85 assert(data.stringGetter is StringGetter); |
| 86 assert(data.objectConstructor == null || data.objectConstructor is ObjectConst
ructor); |
| 87 assert(!_registeredProperties.containsKey(data.symbol)); |
| 88 _registeredProperties[data.symbol] = data; |
| 89 } |
| 90 |
| 91 @proxy |
| 92 class StyleDeclaration { |
| 93 StyleDeclaration() { this._init(); } |
| 94 StyleDeclaration.clone(StyleDeclaration template) { this.init(template); } |
| 95 external void _init([StyleDeclaration template]); // O(1) |
| 96 // This class has C++-backed internal state representing the |
| 97 // properties known to the system. It's assumed that Property |
| 98 // subclasses are also C++-backed and can directly manipulate this |
| 99 // internal state. |
| 100 // If the argument 'template' is provided, then this should be a clone |
| 101 // of the styles of the template StyleDeclaration |
| 102 |
| 103 operator [](String propertyName) { |
| 104 var propertySymbol = new Symbol(propertyName); |
| 105 if (_registeredProperties.containsKey(propertySymbol)) |
| 106 return _registeredProperties[propertySymbol].stringGetter(propertySymbol,
this); |
| 107 throw new ArgumentError(propertyName); |
| 108 } |
| 109 |
| 110 operator []=(String propertyName, String newValue) { |
| 111 var propertySymbol = new Symbol(propertyName); |
| 112 if (_registeredProperties.containsKey(propertySymbol)) |
| 113 return _registeredProperties[propertySymbol].stringSetter(propertySymbol,
this, newValue); |
| 114 throw new ArgumentError(propertyName); |
| 115 } |
| 116 |
| 117 // some properties expose dedicated APIs so you don't have to use string manip
ulation |
| 118 WeakMap<Symbol, Property> _properties = new WeakMap<Symbol, Property>(); |
| 119 noSuchMethod(Invocation invocation) { |
| 120 Symbol propertySymbol; |
| 121 if (invocation.isSetter) { |
| 122 // when it's a setter, the name will be "foo=" rather than "foo" |
| 123 String propertyName = MirrorSystem.getName(invocation.memberName); |
| 124 assert(propertyName[propertyName.length-1] == '='); |
| 125 propertySymbol = new Symbol(propertyName.substring(0, propertyName.length-
1)); |
| 126 } else { |
| 127 propertySymbol = invocation.memberName; |
| 128 } |
| 129 Property property; |
| 130 if (!_properties.containsKey(propertySymbol)) { |
| 131 if (_registeredProperties.containsKey(propertySymbol)) { |
| 132 var constructor = _registeredProperties[propertySymbol].objectConstructo
r; |
| 133 if (constructor == null) |
| 134 return super.noSuchMethod(invocation); |
| 135 property = constructor(propertySymbol, this); |
| 136 } else { |
| 137 return super.noSuchMethod(invocation); |
| 138 } |
| 139 } else { |
| 140 property = _properties[propertySymbol]; |
| 141 } |
| 142 if (invocation.isMethod) { |
| 143 if (property is Function) |
| 144 return Function.apply(property as Function, invocation.positionalArgumen
ts, invocation.namedArguments); |
| 145 return super.noSuchMethod(invocation); |
| 146 } |
| 147 if (invocation.isSetter) |
| 148 return Function.apply(property.setter, invocation.positionalArguments, inv
ocation.namedArguments); |
| 149 return property; |
| 150 } |
| 151 } |
| 152 |
| 153 const initial = const Object(); |
| 154 const inherit = const Object(); |
| 155 |
| 156 abstract class Property { |
| 157 Property(this.propertySymbol, this.declaration); |
| 158 final StyleDeclaration declaration; |
| 159 final Symbol propertySymbol; |
| 160 |
| 161 bool get inherited => _registeredProperties[propertySymbol].inherited; |
| 162 |
| 163 bool get initial => _isInitial(); |
| 164 void set initial (value) { |
| 165 if (value == true) |
| 166 return _setInitial(); |
| 167 throw new ArgumentError(value); |
| 168 } |
| 169 |
| 170 bool get inherit => _isInherit(); |
| 171 void set inherit (value) { |
| 172 if (value == true) |
| 173 return _setInherit(); |
| 174 throw new ArgumentError(value); |
| 175 } |
| 176 |
| 177 void setter(dynamic value) { |
| 178 if (value == initial) |
| 179 return _setInitial(); |
| 180 if (value == inherit) |
| 181 return _setInitial(); |
| 182 if (value == null) |
| 183 return _unset(); |
| 184 throw new ArgumentError(value); |
| 185 } |
| 186 |
| 187 external bool _isInitial(); |
| 188 external void _setInitial(); |
| 189 external bool _isInherit(); |
| 190 external void _setInherit(); |
| 191 external void _unset(); |
| 192 } |
| 193 ``` |
| 194 |
| 195 Sky defines the following properties, currently as part of the core, |
| 196 but eventually this will be moved to the framework: |
| 197 |
| 198 ```dart |
| 199 class LengthProperty extends Property { |
| 200 LengthProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(pr
opertySymbol, declaration); |
| 201 |
| 202 double get pixels => _getPixels(); |
| 203 void set pixels (value) => _setPixels(value); |
| 204 |
| 205 double get inches => _getPixels() / 96.0; |
| 206 void set inches (value) => _setPixels(value * 96.0); |
| 207 |
| 208 double get em => _getEm(); |
| 209 void set em (value) => _setEm(value); |
| 210 |
| 211 void setter(dynamic value) { |
| 212 if (value is num) |
| 213 return _setPixels(value.toDouble()); |
| 214 return super.setter(value); |
| 215 } |
| 216 |
| 217 external double _getPixels(); |
| 218 // throws StateError if the value isn't in pixels |
| 219 external void _setPixels(double value); |
| 220 |
| 221 external double _getEm(); |
| 222 // throws StateError if the value isn't in pixels |
| 223 external void _setEm(double value); |
| 224 } |
| 225 |
| 226 const auto = const Object(); |
| 227 |
| 228 class AutoLengthProperty extends LengthProperty { |
| 229 AutoLengthProperty(Symbol propertySymbol, StyleDeclaration declaration) : supe
r(propertySymbol, declaration); |
| 230 |
| 231 bool get auto => _isAuto(); |
| 232 void set auto (value) { |
| 233 if (value == true) |
| 234 _setAuto(); |
| 235 throw new ArgumentError(value); |
| 236 } |
| 237 |
| 238 void setter(dynamic value) { |
| 239 if (value == auto) |
| 240 return _setAuto(); |
| 241 return super.setter(value); |
| 242 } |
| 243 |
| 244 external bool _isAuto(); |
| 245 external void _setAuto(); |
| 246 } |
| 247 |
| 248 class ColorProperty extends Property { |
| 249 ColorProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(pro
pertySymbol, declaration); |
| 250 |
| 251 int get alpha => _getRGBA() & 0xFF000000 >> 24; |
| 252 void set alpha (int value) => _setRGBA(_getRGBA() & 0x00FFFFFF + value << 24); |
| 253 int get red => _getRGBA() & 0x00FF0000 >> 16; |
| 254 void set red (int value) => _setRGBA(_getRGBA() & 0xFF00FFFF + value << 16); |
| 255 int get green => _getRGBA() & 0x0000FF00 >> 8; |
| 256 void set green (int value) => _setRGBA(_getRGBA() & 0xFFFF00FF + value << 8); |
| 257 int get blue => _getRGBA() & 0x000000FF >> 0; |
| 258 void set blue (int value) => _setRGBA(_getRGBA() & 0xFFFFFF00 + value << 0); |
| 259 |
| 260 int get rgba => _getRGBA(); |
| 261 void set rgba (int value) => _setRGBA(value); |
| 262 |
| 263 void setter(dynamic value) { |
| 264 if (value is int) |
| 265 return _setRGBA(value); |
| 266 return super.setter(value); |
| 267 } |
| 268 |
| 269 external int _getRGBA(); |
| 270 // throws StateError if the value isn't a color |
| 271 external void _setRGBA(int value); |
| 272 } |
| 273 |
| 274 class Matrix { |
| 275 const Matrix(this.a, this.b, this.c, this.d, this.e, this.f); |
| 276 |
| 277 // +- -+ |
| 278 // | a c e | |
| 279 // | b d f | |
| 280 // | 0 0 1 | |
| 281 // +- -+ |
| 282 |
| 283 final double a; |
| 284 final double b; |
| 285 final double c; |
| 286 final double d; |
| 287 final double e; |
| 288 final double f; |
| 289 } |
| 290 |
| 291 class TransformProperty extends Property { |
| 292 TransformProperty(Symbol propertySymbol, StyleDeclaration declaration) : super
(propertySymbol, declaration); |
| 293 |
| 294 void reset() => setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); |
| 295 |
| 296 void translate(double dx, double dy) => transform(1.0, 0.0, 0.0, 1.0, dx, dy); |
| 297 void scale(double dw, double dh) => transform(dw, 0.0, 0.0, dh, 0.0, 0.0); |
| 298 void rotate(double theta) => transform(cos(theta), -sin(theta), sin(theta), co
s(theta), 0.0, 0.0); |
| 299 |
| 300 // there's no "transform" getter since it would always return a new Matrix |
| 301 // such that foo.transform == foo.transform would never be true |
| 302 // and foo.transform = bar; bar == foo.transform would also never be true |
| 303 // which is bad API |
| 304 |
| 305 external Matrix getTransform(); |
| 306 // throws StateError if the value isn't a matrix |
| 307 // returns a new matrix each time |
| 308 external void setTransform(a, b, c, d, e, f); |
| 309 external void transform(a, b, c, d, e, f); |
| 310 // throws StateError if the value isn't a matrix |
| 311 } |
| 312 |
| 313 external void autoLengthPropertyStringSetter(Symbol propertySymbol, StyleDeclara
tion declaration, String value); |
| 314 external String autoLengthPropertyStringGetter(Symbol propertySymbol, StyleDecla
ration declaration); |
| 315 external void colorPropertyStringSetter(Symbol propertySymbol, StyleDeclaration
declaration, String value); |
| 316 external String colorPropertyStringGetter(Symbol propertySymbol, StyleDeclaratio
n declaration); |
| 317 external void transformPropertyStringSetter(Symbol propertySymbol, StyleDeclarat
ion declaration, String value); |
| 318 external String transformPropertyStringGetter(Symbol propertySymbol, StyleDeclar
ation declaration); |
| 319 |
| 320 void _init() { |
| 321 registerProperty(new PropertyTable( |
| 322 symbol: #height, |
| 323 inherited: false, |
| 324 stringSetter: autoLengthPropertyStringSetter, |
| 325 stringGetter: autoLengthPropertyStringGetter, |
| 326 objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) => |
| 327 new AutoLengthProperty(propertySymbol, declaration))); |
| 328 registerProperty(new PropertyTable( |
| 329 symbol: #width, |
| 330 inherited: false, |
| 331 stringSetter: autoLengthPropertyStringSetter, |
| 332 stringGetter: autoLengthPropertyStringGetter, |
| 333 objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) => |
| 334 new AutoLengthProperty(propertySymbol, declaration))); |
| 335 registerProperty(new PropertyTable( |
| 336 symbol: #color, |
| 337 inherited: false, |
| 338 stringSetter: colorPropertyStringSetter, |
| 339 stringGetter: colorPropertyStringGetter, |
| 340 objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) => |
| 341 new ColorProperty(propertySymbol, declaration))); |
| 342 registerProperty(new PropertyTable( |
| 343 symbol: #transform, |
| 344 inherited: false, |
| 345 stringSetter: transformPropertyStringSetter, |
| 346 stringGetter: transformPropertyStringGetter, |
| 347 objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) => |
| 348 new TransformProperty(propertySymbol, declaration))); |
| 349 } |
| 350 ``` |
| 351 |
| OLD | NEW |