PageRenderTime 72ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/src/reflex/skins/Skin.as

http://github.com/xtyler/reflex-framework
ActionScript | 430 lines | 262 code | 47 blank | 121 comment | 55 complexity | af10eb65211774d6fd4286af86f390a9 MD5 | raw file
Possible License(s): Unlicense
  1. package reflex.skins
  2. {
  3. import flash.display.DisplayObject;
  4. import flash.display.InteractiveObject;
  5. import flash.display.Sprite;
  6. import flash.events.Event;
  7. import flash.events.EventDispatcher;
  8. import flash.geom.Point;
  9. import flash.geom.Rectangle;
  10. import mx.collections.IList;
  11. import mx.events.CollectionEvent;
  12. import mx.events.CollectionEventKind;
  13. import reflex.binding.Bind;
  14. import reflex.collections.SimpleCollection;
  15. import reflex.components.IStateful;
  16. import reflex.display.IContainer;
  17. import reflex.display.addItemsAt;
  18. import reflex.events.PropertyEvent;
  19. import reflex.events.RenderPhase;
  20. import reflex.layouts.ILayout;
  21. import reflex.measurement.IMeasurable;
  22. import reflex.measurement.IMeasurements;
  23. import reflex.measurement.Measurements;
  24. /**
  25. * Skin is a convenient base class for many skins, a swappable graphical
  26. * definition. Skins decorate a target Sprite by drawing on its surface,
  27. * adding children to the Sprite, or both.
  28. * @alpha
  29. */
  30. [DefaultProperty("content")]
  31. public class Skin extends EventDispatcher implements ISkin, IContainer, IStateful, IMeasurable
  32. {
  33. static public const MEASURE:String = "measure";
  34. static public const LAYOUT:String = "layout";
  35. RenderPhase.registerPhase(MEASURE, 0, true);
  36. RenderPhase.registerPhase(LAYOUT, 0, true);
  37. private var renderers:Array = [];
  38. private var _layout:ILayout;
  39. private var _states:Array;
  40. private var _currentState:String;
  41. //private var _transitions:Array;
  42. private var _template:Object; // = new ReflexDataTemplate();
  43. private var unscaledWidth:Number = 160;
  44. private var unscaledHeight:Number = 22;
  45. private var _explicite:IMeasurements;
  46. private var _measured:IMeasurements;
  47. //
  48. /**
  49. * @inheritDoc
  50. */
  51. [Bindable(event="widthChange")]
  52. public function get width():Number { return unscaledWidth; }
  53. public function set width(value:Number):void {
  54. if(unscaledWidth == value) {
  55. return;
  56. }
  57. _explicite.width = value;
  58. PropertyEvent.dispatchChange(this, "width", unscaledWidth, unscaledWidth = value);
  59. RenderPhase.invalidate(target, LAYOUT);
  60. }
  61. /**
  62. * @inheritDoc
  63. */
  64. [Bindable(event="heightChange")]
  65. public function get height():Number { return unscaledHeight; }
  66. public function set height(value:Number):void {
  67. if(unscaledHeight == value) {
  68. return;
  69. }
  70. _explicite.height = value;
  71. PropertyEvent.dispatchChange(this, "height", unscaledHeight, unscaledHeight = value);
  72. RenderPhase.invalidate(target, LAYOUT);
  73. }
  74. /**
  75. * @inheritDoc
  76. */
  77. [Bindable(event="expliciteChange")]
  78. public function get explicite():IMeasurements { return _explicite; }
  79. /*public function set explicite(value:IMeasurements):void {
  80. if(value == _explicite) {
  81. return;
  82. }
  83. if(value != null) { // must not be null
  84. PropertyEvent.dispatchChange(this, "explicite", _explicite, _explicite = value);
  85. InvalidationEvent.invalidate(target, LAYOUT);
  86. }
  87. }*/
  88. /**
  89. * @inheritDoc
  90. */
  91. [Bindable(event="measuredChange")]
  92. public function get measured():IMeasurements { return _measured; }
  93. /*public function set measured(value:IMeasurements):void {
  94. if(value == _measured) {
  95. return;
  96. }
  97. if(value != null) { // must not be null
  98. PropertyEvent.dispatchChange(this, "measured", _measured, _measured = value);
  99. InvalidationEvent.invalidate(target, LAYOUT);
  100. }
  101. }*/
  102. /**
  103. * @inheritDoc
  104. */
  105. public function setSize(width:Number, height:Number):void {
  106. if(unscaledWidth != width) { PropertyEvent.dispatchChange(this, "width", unscaledWidth, unscaledWidth = width); }
  107. if(unscaledHeight != height) { PropertyEvent.dispatchChange(this, "height", unscaledHeight, unscaledHeight = height); }
  108. RenderPhase.invalidate(target, LAYOUT);
  109. }
  110. /**
  111. * @inheritDoc
  112. */
  113. [Bindable(event="layoutChange")]
  114. public function get layout():ILayout { return _layout; }
  115. public function set layout(value:ILayout):void {
  116. if(_layout == value) {
  117. return;
  118. }
  119. var oldLayout:ILayout = _layout;
  120. if(_layout) { _layout.target = null; }
  121. _layout = value;
  122. _layout.target = target;
  123. if(target) {
  124. RenderPhase.invalidate(target, MEASURE);
  125. RenderPhase.invalidate(target, LAYOUT);
  126. }
  127. PropertyEvent.dispatchChange(this, "layout", oldLayout, _layout);
  128. }
  129. [Bindable(event="templateChange")]
  130. public function get template():Object { return _template; }
  131. public function set template(value:Object):void {
  132. if(_template == value) {
  133. return;
  134. }
  135. PropertyEvent.dispatchChange(this, "template", _template, _template = value);
  136. }
  137. [Bindable(event="currentStateChange")]
  138. public function get currentState():String { return _currentState; }
  139. public function set currentState(value:String):void {
  140. if(_currentState == value) {
  141. return;
  142. }
  143. PropertyEvent.dispatchChange(this, "currentState", _currentState, _currentState = value);
  144. }
  145. [Bindable(event="statesChange")]
  146. public function get states():Array { return _states; }
  147. public function set states(value:Array):void {
  148. if(_states == value) {
  149. return;
  150. }
  151. PropertyEvent.dispatchChange(this, "states", _states, _states = value);
  152. }
  153. //protected var containerPart:DisplayObjectContainer;
  154. //protected var defaultContainer:Boolean = true;
  155. private var _target:Sprite;
  156. private var _content:IList;
  157. public function Skin()
  158. {
  159. super();
  160. _content = new SimpleCollection();
  161. _explicite = new Measurements(this);
  162. _measured = new Measurements(this, 160, 22);
  163. if(_layout == null) {
  164. //_layout = new XYLayout();
  165. }
  166. _content.addEventListener(CollectionEvent.COLLECTION_CHANGE, onChildrenChange);
  167. Bind.addListener(this, onLayoutChange, this, "target.layout");
  168. Bind.addListener(this, onLayoutChange, this, "layout");
  169. //Bind.addBinding(this, "data", this, "target.data");
  170. //Bind.addBinding(this, "state", this, "target.state");
  171. //addEventListener(MEASURE, onMeasure, false, 0, true);
  172. addEventListener(LAYOUT, onLayout, false, 0, true);
  173. }
  174. [Bindable(event="targetChange")]
  175. public function get target():Sprite { return _target; }
  176. public function set target(value:Sprite):void
  177. {
  178. if (_target == value) {
  179. return;
  180. }
  181. /*
  182. var skinnable:IContainer;
  183. if (_target != null && _target is IContainer) {
  184. skinnable = _target as IContainer;
  185. //skinnable.children.removeEventListener(ListEvent.LIST_CHANGE, onContentChange);
  186. for (var i:int = 0; i < _children.length; i++) {
  187. _target.removeChild(_children.getItemAt(i) as DisplayObject);
  188. }
  189. }
  190. */
  191. var oldValue:Object = _target;
  192. _target = value;
  193. if(layout) {
  194. layout.target = _target;
  195. }
  196. if(this.hasOwnProperty('hostComponent')) {
  197. this['hostComponent'] = _target;
  198. }
  199. if (_target != null) {
  200. /*
  201. //var i:int;
  202. //for (i = 0; i < _children.length; i++) {
  203. //_target.addChildAt(_children.getItemAt(i) as DisplayObject, i);
  204. //}
  205. var items:Array = [];
  206. for (i = 0; i < _children.length; i++) {
  207. items.push(_children.getItemAt(i));
  208. }
  209. reflex.display.addItemsAt(_target, items, 0);
  210. /*
  211. containerPart = getSkinPart("container") as DisplayObjectContainer;
  212. if (_target is IContainer && containerPart != null) {
  213. skinnable = _target as IContainer;
  214. skinnable.children.addEventListener(ListEvent.LIST_CHANGE, onContentChange, false, 0xF);
  215. if (skinnable.children.length > 0) {
  216. defaultContainer = false;
  217. Bind.addBinding(containerPart, "padding", this, "target.padding");
  218. while (containerPart.numChildren) {
  219. removeContainerChildAt(containerPart.numChildren-1);
  220. }
  221. for (i = 0; i < skinnable.children.length; i++) {
  222. addContainerChildAt(skinnable.children.getItemAt(i) as DisplayObject, i);
  223. }
  224. }
  225. }
  226. */
  227. target.addEventListener(MEASURE, onMeasure, false, 0, true);
  228. target.addEventListener(LAYOUT, onLayout, false, 0, true);
  229. RenderPhase.invalidate(target, MEASURE);
  230. RenderPhase.invalidate(target, LAYOUT);
  231. }
  232. PropertyEvent.dispatchChange(this, "target", oldValue, _target);
  233. var items:Array = [];
  234. for (var i:int = 0; i < _content.length; i++) {
  235. items.push(_content.getItemAt(i));
  236. }
  237. reset(items);
  238. }
  239. protected function init():void
  240. {
  241. }
  242. /**
  243. * @inheritDoc
  244. */
  245. [ArrayElementType("Object")]
  246. [Bindable(event="contentChange")]
  247. public function get content():IList
  248. {
  249. return _content;
  250. }
  251. public function set content(value:*):void
  252. {
  253. if(_content == value) {
  254. return;
  255. }
  256. var oldContent:IList = _content;
  257. if(_content) {
  258. _content.removeEventListener(CollectionEvent.COLLECTION_CHANGE, onChildrenChange);
  259. }
  260. if(value == null) {
  261. _content = null;
  262. } else if(value is IList) {
  263. _content = value as IList;
  264. } else if(value is Array || value is Vector) {
  265. _content = new SimpleCollection(value);
  266. } else {
  267. _content = new SimpleCollection([value]);
  268. }
  269. if(_content) {
  270. _content.addEventListener(CollectionEvent.COLLECTION_CHANGE, onChildrenChange);
  271. var items:Array = [];
  272. for (var i:int = 0; i < _content.length; i++) {
  273. items.push(_content.getItemAt(i));
  274. }
  275. reset(items);
  276. }
  277. PropertyEvent.dispatchChange(this, "content", oldContent, _content);
  278. }
  279. public function getSkinPart(part:String):InteractiveObject
  280. {
  281. return (part in this) ? this[part] : null;
  282. }
  283. private function onChildrenChange(event:CollectionEvent):void
  284. {
  285. if (_target == null) {
  286. return;
  287. }
  288. var child:DisplayObject;
  289. var loc:int = event.location;
  290. switch (event.kind) {
  291. case CollectionEventKind.ADD :
  292. add(event.items, loc++);
  293. break;
  294. case CollectionEventKind.REMOVE :
  295. for each (child in event.items) {
  296. _target.removeChild(child);
  297. }
  298. break;
  299. case CollectionEventKind.REPLACE :
  300. _target.removeChild(event.items[1]);
  301. _target.addChildAt(event.items[0], loc);
  302. break;
  303. case CollectionEventKind.RESET :
  304. default:
  305. reset(event.items);
  306. break;
  307. }
  308. }
  309. private function add(items:Array, index:int):void {
  310. var children:Array = reflex.display.addItemsAt(_target, items, index, template);
  311. renderers.concat(children); // todo: correct ordering
  312. }
  313. private function reset(items:Array):void {
  314. if(_target) {
  315. while (_target.numChildren) {
  316. _target.removeChildAt(_target.numChildren-1);
  317. }
  318. renderers = reflex.display.addItemsAt(_target, items, 0, template); // todo: correct ordering
  319. RenderPhase.invalidate(_target, MEASURE);
  320. RenderPhase.invalidate(_target, LAYOUT);
  321. }
  322. }
  323. private function onLayoutChange(value:ILayout):void
  324. {
  325. if (_target == null) {
  326. return;
  327. }
  328. /*
  329. var targetLayout:LayoutWrapper = LayoutWrapper.getLayout(_target);
  330. if (containerPart != null && _target is IContainer) {
  331. var skinnable:IContainer = _target as IContainer;
  332. var containerLayout:LayoutWrapper = LayoutWrapper.getLayout(containerPart);
  333. if (containerLayout != null) {
  334. //containerLayout.algorithm = skinnable.layout;
  335. } else if (targetLayout != null) {
  336. containerLayout = new targetLayout["constructor"]();
  337. containerLayout.target = containerPart;
  338. //containerLayout.algorithm = skinnable.layout;
  339. }
  340. }
  341. if (targetLayout != null) {
  342. //targetLayout.algorithm = layout;
  343. }*/
  344. }
  345. private function onMeasure(event:Event):void {
  346. var target:IMeasurable= this.target as IMeasurable;
  347. if(layout && target && (isNaN(target.explicite.width) || isNaN(target.explicite.height))) {
  348. var items:Array = [];
  349. var length:int = _content.length;
  350. for(var i:int = 0; i < length; i++) {
  351. items.push(_content.getItemAt(i));
  352. }
  353. var point:Point = layout.measure(items);
  354. // this if statement blocks an infinite loop
  355. // the lifecycle should be handled better here in some way
  356. // update: target should update it's own sizing?
  357. /*
  358. if(point.x != target.measured.width || point.y != target.measured.height) {
  359. target.measured.width = point.x;
  360. target.measured.height = point.y;
  361. }
  362. */
  363. }
  364. //InvalidationEvent.invalidate(this.target, LAYOUT);
  365. }
  366. private function onLayout(event:Event):void {
  367. if(layout) {
  368. var items:Array = [];
  369. var length:int = _content.length;
  370. for(var i:int = 0; i < length; i++) {
  371. items.push(_content.getItemAt(i));
  372. }
  373. //var width:Number = reflex.measurement.resolveWidth(this);
  374. //var height:Number = reflex.measurement.resolveHeight(this);
  375. var rectangle:Rectangle = new Rectangle(0, 0, unscaledWidth, unscaledHeight);
  376. layout.update(items, rectangle);
  377. }
  378. }
  379. }
  380. }