PageRenderTime 48ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/mad-components/MadComponentsStage3D/src/com/danielfreeman/stage3Dacceleration/ListScrolling.as

http://mad-components.googlecode.com/
ActionScript | 980 lines | 715 code | 117 blank | 148 comment | 147 complexity | c3f35b3d1ebc8900b8212bebbd2509b2 MD5 | raw file
  1. /**
  2. * <p>Original Author: Daniel Freeman</p>
  3. *
  4. * <p>Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:</p>
  10. *
  11. * <p>The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.</p>
  13. *
  14. * <p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. * THE SOFTWARE.</p>
  21. *
  22. * <p>Licensed under The MIT License</p>
  23. * <p>Redistributions of files must retain the above copyright notice.</p>
  24. */
  25. package com.danielfreeman.stage3Dacceleration {
  26. import com.adobe.utils.AGALMiniAssembler;
  27. import com.danielfreeman.madcomponents.IContainerUI;
  28. import com.danielfreeman.madcomponents.UI;
  29. import com.danielfreeman.madcomponents.UIForm;
  30. import com.danielfreeman.madcomponents.UIList;
  31. import com.danielfreeman.madcomponents.UIPages;
  32. import com.danielfreeman.madcomponents.UIPicker;
  33. import com.danielfreeman.madcomponents.UIScrollVertical;
  34. import flash.display.Bitmap;
  35. import flash.display.BitmapData;
  36. import flash.display.DisplayObject;
  37. import flash.display.IBitmapDrawable;
  38. import flash.display.Sprite;
  39. import flash.display3D.Context3DProgramType;
  40. import flash.display3D.Context3DTextureFormat;
  41. import flash.display3D.Context3DVertexBufferFormat;
  42. import flash.display3D.IndexBuffer3D;
  43. import flash.display3D.Program3D;
  44. import flash.display3D.VertexBuffer3D;
  45. import flash.display3D.textures.Texture;
  46. import flash.events.Event;
  47. import flash.events.MouseEvent;
  48. import flash.geom.Matrix;
  49. import flash.geom.Point;
  50. import flash.geom.Rectangle;
  51. import flash.utils.Dictionary;
  52. /**
  53. * Accelerated list scrolling.
  54. * This class may only be used where each list is guaranteed to fit within a single 2048x2048 texture.
  55. * (And this must be guaranteed for all resolutions and screen desities of devices that the app will run on).
  56. * - note that there is a subclass, LongListScrolling that removes his restriction.
  57. */
  58. public class ListScrolling extends Stage3DAcceleration {
  59. public static const COMPONENT_CHANGED:String = "componentChanged";
  60. protected static const TEXTURE_WIDTH:int = 2048;
  61. protected static const TEXTURE_HEIGHT:int = 2048;
  62. protected static const SUPPORTED_LISTS:Vector.<String> = Vector.<String>(["scrollVertical", "list", "dividedList", "groupedList", "picker"]);
  63. protected static const INCREMENT:Number = 0.1;
  64. protected static const DECAY:Number = 0.95;
  65. protected static const FASTER_DECAY:Number = 0.10;
  66. protected static const DELTA_THRESHOLD:Number = 1.0;
  67. protected static const MOVE_FACTOR:Number = 2.0;
  68. protected static const BOUNCE_FACTOR:Number = 0.3;
  69. protected static const MOVE_THRESHOLD:Number = 1.0;
  70. protected static const CLICK_FRAMES:int = 3;
  71. protected static const CLICK_THRESHOLD:Number = 4.0;
  72. protected static const HIGHLIGHT_FRAMES:int = 10;
  73. protected static const LONG_PRESS:int = 20;
  74. protected static const FAST_THRESHOLD:Number = 10.0;
  75. protected static const ACCELERATE:Number = 3.0;
  76. protected static const DELTA_LIMIT:Number = 800.0;
  77. protected static const ALTERNATIVE_SCHEME:Boolean = true;
  78. protected static const DEBUG:Boolean = false;
  79. protected var _textureBitMapData:Vector.<BitmapData> = new Vector.<BitmapData>();
  80. protected var _listRecords:Vector.<ListRecord> = new Vector.<ListRecord>();
  81. protected var _vertexes:Vector.<Number> = null;
  82. protected var _uv:Vector.<Number> = null;
  83. protected var _indices:Vector.<uint>;
  84. protected var _indexBuffer:IndexBuffer3D;
  85. protected var _xyzVertexBuffer:VertexBuffer3D;
  86. protected var _uvVertexBuffer:VertexBuffer3D;
  87. protected var _listScrollingVertexShader:AGALMiniAssembler = new AGALMiniAssembler();
  88. protected var _listScrollingFragmentShader:AGALMiniAssembler = new AGALMiniAssembler();
  89. protected var _listScrollingShaderProgram:Program3D;
  90. protected var _listScrollingTexture:Vector.<Texture> = new Vector.<Texture>();
  91. protected var _supportedLists:Vector.<String> = SUPPORTED_LISTS;
  92. protected var _holes:Vector.<IContainerUI>;
  93. protected var _lists:Vector.<IContainerUI> = new Vector.<IContainerUI>();
  94. protected var _count:Number = 0.0;
  95. protected var _textureRegister:int = 0;
  96. protected var _enabled:Boolean = false;
  97. protected var _container:UIPages = null;
  98. protected var _slideXyzVertexBuffer:VertexBuffer3D;
  99. protected var _slideUvVertexBuffer:VertexBuffer3D;
  100. protected var _listRecordCurrent:ListRecord;
  101. protected var _listRecordNext:ListRecord;
  102. protected var _back:Boolean;
  103. protected var _centre:Rectangle;
  104. protected var _slideIndexBuffer:IndexBuffer3D;
  105. protected var _activeList:ListRecord = null;
  106. protected var _startMouseY:Number;
  107. protected var _startSliderY:Number;
  108. protected var _somethingMoving:int = 0;
  109. protected var _frameCount:int = 0;
  110. protected var _lastPositionY:Number;
  111. protected var _searchHit:DisplayObject;
  112. protected var _clicked:Boolean = false;
  113. protected var _notTooFastForClick:Boolean = true;
  114. protected var _oldActiveList:ListRecord = null;
  115. protected var _oldDelta:Number = Number.NaN;
  116. protected var _autoUpdateComponents:Boolean = true;
  117. protected var _componentChanged:DisplayObject;
  118. protected var _stopping:Boolean;
  119. public function ListScrolling() {
  120. initialise();
  121. }
  122. /**
  123. * A standard and simple vertes shader simply outputs vertices (without transformation), and interpolates UV.
  124. *
  125. * The texture shader is more sophisticated. List scrolling is accomplished by fast UV animation.
  126. */
  127. public function initialise():void {
  128. _listScrollingVertexShader.assemble( Context3DProgramType.VERTEX,
  129. "mov op, va0 \n" + // vertex
  130. "mov v0, va1 \n" // interpolate UV
  131. );
  132. _listScrollingFragmentShader.assemble( Context3DProgramType.FRAGMENT,
  133. "sge ft0.z, v0.y, fc0.y \n" + // Deal with negative scroll position
  134. "mul ft0.z, ft0.z, v0.y \n" +
  135. "frc ft0.y, ft0.z \n" +
  136. "sub ft0.z, ft0.z, ft0.y \n" + // integer part of V is portion
  137. "mul ft0.z, ft0.z, fc0.x \n" + // multiply portion by list width
  138. "add ft0.x, v0.x, ft0.z \n" + // add to U.
  139. "tex oc, ft0.xy, fs0 <2d,linear,nomip> \n" // output texture
  140. );
  141. _listScrollingShaderProgram = _context3D.createProgram();
  142. _listScrollingShaderProgram.upload( _listScrollingVertexShader.agalcode, _listScrollingFragmentShader.agalcode);
  143. }
  144. /**
  145. * For custom renderers, if a components is clicked - update textures.
  146. */
  147. public function set autoUpdateComponents(value:Boolean):void {
  148. _autoUpdateComponents = value;
  149. }
  150. /**
  151. * Extend the set of MadComponents list classes supported.
  152. * (You might want to do this if you've built your own subclass of a MadComponents UIScrollVertical)
  153. */
  154. public function extend(value:Vector.<String>):void {
  155. _supportedLists = _supportedLists.concat(value);
  156. }
  157. /**
  158. * Clear all texture and bitmapdata memory.
  159. */
  160. public function clear():void {
  161. for each(var bitmapData:BitmapData in _textureBitMapData) {
  162. bitmapData.dispose();
  163. }
  164. _textureBitMapData = new Vector.<BitmapData>();
  165. _listRecords = new Vector.<ListRecord>();
  166. _listScrollingTexture = new Vector.<Texture>();
  167. _lists = new Vector.<IContainerUI>();
  168. }
  169. /**
  170. * Generate UV, according to list scroll value. (UV animation).
  171. */
  172. protected function createUV(listRecord:ListRecord):void {
  173. if (listRecord.container is UIForm) {
  174. _uv.push(
  175. 0, listRecord.uvHeight,
  176. listRecord.uvWidth, listRecord.uvHeight,
  177. listRecord.uvWidth, 0,
  178. 0, 0
  179. );
  180. }
  181. else {
  182. var textureTop:Number = UI.scale*(UIScrollVertical(listRecord.container).scrollPositionY + 1)/listRecord.textureHeight;
  183. var textureBottom:Number = textureTop + listRecord.uvHeight;
  184. _uv.push(
  185. 0, textureBottom,
  186. listRecord.uvWidth, textureBottom,
  187. listRecord.uvWidth, textureTop,
  188. 0, textureTop
  189. );
  190. }
  191. }
  192. /**
  193. * Search the entire MadComponents layout for ALL supported lists, and accelerate them.
  194. */
  195. public function allListTextures():Boolean {
  196. _lists = new Vector.<IContainerUI>();
  197. allListTextures0(_screen);
  198. return listTextures(_lists);
  199. }
  200. /**
  201. * Perform search for ALL supported lists in the layout.
  202. */
  203. protected function allListTextures0(item:Sprite):Boolean {
  204. for (var i:int = 0; i<item.numChildren; i++) {
  205. var child:DisplayObject = item.getChildAt(i);
  206. if (child is UIScrollVertical && _supportedLists.indexOf(UIScrollVertical(child).xml.localName())>=0) {
  207. _lists.push(child);
  208. }
  209. else if (child is Sprite) {
  210. allListTextures0(Sprite(child));
  211. }
  212. }
  213. return true;
  214. }
  215. /**
  216. * Provide a list of the specific lists in your layout that you want accelerated
  217. */
  218. public function listTextures(lists:Vector.<IContainerUI>):Boolean {
  219. var error:Boolean = false;
  220. _listRecords = new Vector.<ListRecord>();
  221. _vertexes = new Vector.<Number>();
  222. _uv = new Vector.<Number>();
  223. _indices = new Vector.<uint>();
  224. for each (var list:IContainerUI in lists) {
  225. var point:Point = Sprite(list).localToGlobal(new Point(0,0));
  226. var listWidth:Number = UI.scale * theWidth(list);
  227. var listHeight:Number = UI.scale * theHeight(list);
  228. var left:Number = 2 * point.x / _screen.stage.stageWidth - 1.0;
  229. var top:Number = - 2 * point.y / _screen.stage.stageHeight + 1.0;
  230. var right:Number = left + 2 * listWidth / _screen.stage.stageWidth;
  231. var bottom:Number = top - 2 * listHeight / _screen.stage.stageHeight;
  232. _vertexes.push(
  233. left, bottom, 0,
  234. right, bottom, 0,
  235. right, top, 0,
  236. left, top, 0
  237. );
  238. var count:uint = 4*_listRecords.length;
  239. _indices.push(
  240. count, count+1, count+2, count, count+2, count+3
  241. );
  242. var scroller:Sprite = (list is UIScrollVertical) ? Sprite(list.pages[0]) : null;
  243. var scrollerHeight:Number = (scroller ? UI.scale * scroller.height : listHeight);
  244. var listPortions:Number = Math.ceil(scrollerHeight / TEXTURE_HEIGHT);
  245. var textureWidth:Number = power2(listWidth * listPortions);
  246. if (textureWidth>TEXTURE_WIDTH) {
  247. textureWidth = TEXTURE_WIDTH;
  248. listPortions = Math.floor(TEXTURE_WIDTH / listWidth);
  249. error = true;
  250. }
  251. var textureHeight:Number = (scrollerHeight < TEXTURE_HEIGHT/2) ? power2(scrollerHeight) : TEXTURE_HEIGHT;
  252. var backgroundColour:uint = list.attributes.backgroundColours.length>0 ? list.attributes.backgroundColours[0] : 0xFFFFFF;
  253. var bitmapData:BitmapData = new BitmapData(textureWidth, textureHeight, false, DEBUG ? 0xffff00 : backgroundColour);
  254. var listRecord:ListRecord = new ListRecord(list, _listRecords.length, list is UIList && UIList(list).showPressed, listWidth/textureWidth, listHeight/textureHeight, textureHeight);
  255. _listRecords.push(listRecord);
  256. createUV(listRecord);
  257. var matrix:Matrix = new Matrix();
  258. matrix.identity();
  259. matrix.scale(scale, scale);
  260. matrix.translate(0, 1);
  261. for (var i:int = 0; i < listPortions; i++)
  262. {
  263. try {
  264. var iBitmapDrawable:IBitmapDrawable = IBitmapDrawable((scroller!=null) ? scroller : list);
  265. bitmapData.draw(iBitmapDrawable, matrix, null, null, new Rectangle(i*listWidth, 0, listWidth, textureHeight));
  266. }
  267. catch(err:Error) {
  268. trace(i+"error in bitmapData.draw :"+err.message+" scroller="+scroller+" list="+list+" scrollerHeight="+scrollerHeight);
  269. error = true;
  270. }
  271. matrix.translate(listWidth, -textureHeight);
  272. }
  273. bitmapData.fillRect(
  274. new Rectangle(0, 0, listWidth, 1),
  275. 0xFF000000 | backgroundColour
  276. );
  277. _textureBitMapData.push(bitmapData);
  278. var listScrollingTexture:Texture = _context3D.createTexture(bitmapData.width, bitmapData.height, Context3DTextureFormat.BGRA, false);
  279. listScrollingTexture.uploadFromBitmapData(bitmapData);
  280. _listScrollingTexture.push(listScrollingTexture);
  281. }
  282. contextResumed(false);
  283. isDefault(this);
  284. activate(this);
  285. onEnterFrame(this, updateLists);
  286. return error;
  287. }
  288. /**
  289. * Restore shaders, streams and textures after context loss.
  290. */
  291. override public function contextResumed(running:Boolean):void {
  292. _listScrollingShaderProgram = _context3D.createProgram();
  293. _listScrollingShaderProgram.upload( _listScrollingVertexShader.agalcode, _listScrollingFragmentShader.agalcode);
  294. _xyzVertexBuffer = _context3D.createVertexBuffer(_vertexes.length / 3, 3);
  295. _xyzVertexBuffer.uploadFromVector(_vertexes, 0, _vertexes.length / 3);
  296. _uvVertexBuffer = _context3D.createVertexBuffer(_uv.length / 2, 2);
  297. _indexBuffer = _context3D.createIndexBuffer( _indices.length );
  298. _indexBuffer.uploadFromVector(_indices, 0, _indices.length );
  299. _listScrollingTexture = new Vector.<Texture>();
  300. for each (var bitmapData:BitmapData in _textureBitMapData) {
  301. var texture:Texture = _context3D.createTexture(bitmapData.width, bitmapData.height, Context3DTextureFormat.BGRA, false);
  302. texture.uploadFromBitmapData(bitmapData);
  303. _listScrollingTexture.push(texture);
  304. }
  305. if (running) {
  306. enable();
  307. }
  308. }
  309. /**
  310. * For accelerated slide transitions (For navigation based applications), you must specify the container component that holds the pages.
  311. */
  312. public function containerPageTextures(container:UIPages):void {
  313. _container = container;
  314. _lists = new Vector.<IContainerUI>();
  315. var page0:UIForm = UIForm(container.pages[container.pageNumber]);
  316. var point:Point = page0.localToGlobal(new Point(0,0));
  317. _centre = new Rectangle(
  318. point.x,
  319. point.y,
  320. UI.scale*page0.attributes.width,
  321. UI.scale*page0.attributes.height
  322. );
  323. for each (var page:Sprite in container.pages ) {
  324. if (page.getChildAt(0) is UIScrollVertical && _supportedLists.indexOf(UIScrollVertical(page.getChildAt(0)).xml.localName())>=0) {
  325. _lists.push(page.getChildAt(0));
  326. }
  327. else {
  328. _lists.push(page);
  329. }
  330. }
  331. for each (var form:IContainerUI in _lists) {
  332. if (form is UIForm) {
  333. allListTextures0(Sprite(form));
  334. }
  335. }
  336. listTextures(_lists);
  337. _slideXyzVertexBuffer = _context3D.createVertexBuffer(8, 3);
  338. _slideUvVertexBuffer = _context3D.createVertexBuffer(8, 2);
  339. _slideIndexBuffer = _context3D.createIndexBuffer(12);
  340. _slideIndexBuffer.uploadFromVector(Vector.<uint>([0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7]), 0, 12 );
  341. }
  342. /**
  343. * Perform an accelerated slide transitions.
  344. */
  345. public function slidePage(currentPage:uint, nextPage:uint, back:Boolean = false):void {
  346. _listRecordCurrent = _listRecords[currentPage];
  347. Sprite(_listRecordCurrent.container).visible = false;
  348. _listRecordNext = _listRecords[nextPage];
  349. _back = back;
  350. _count = 0;
  351. _stopping = false;
  352. _enabled = false;
  353. _context3D.setVertexBufferAt( 0, _slideXyzVertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3 ); //va0
  354. _context3D.setVertexBufferAt( 1, _slideUvVertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2 ); //va1
  355. onEnterFrame(this, onEnterFrameSlide);
  356. _context3D.setProgram(_listScrollingShaderProgram);
  357. }
  358. /**
  359. * Calculate quad vertices at a particular slide position
  360. */
  361. protected function offsetPage(position:Number):Vector.<Number> {
  362. return Vector.<Number> ([
  363. toGraphX(position+_centre.x), -toGraphY(_centre.y+_centre.height-1), 0.0,
  364. toGraphX(position+_centre.x+_centre.width), -toGraphY(_centre.y+_centre.height-1), 0.0,
  365. toGraphX(position+_centre.x+_centre.width), -toGraphY(_centre.y-1), 0.0,
  366. toGraphX(position+_centre.x), -toGraphY(_centre.y-1), 0.0
  367. ]);
  368. }
  369. protected function prepareToStop():void {
  370. _stopping = true;
  371. if (_container) {
  372. Sprite(_listRecordCurrent.container).visible = true;
  373. _container.goToPage(_listRecordNext.textureIndex);
  374. }
  375. }
  376. /**
  377. * Do slide
  378. */
  379. protected function onEnterFrameSlide(event:Event):void {
  380. _count += INCREMENT;
  381. drawSlideFrame();
  382. if (_stopping) {
  383. slideComplete();
  384. }
  385. else if (_count >= 1.0-INCREMENT - 0.0001) {
  386. prepareToStop();
  387. }
  388. }
  389. /**
  390. * Render sliding transition
  391. */
  392. protected function drawSlideFrame(event:Event = null):void {
  393. var shift:Number = (_back ? 1.0 : -1.0) * easing(_count) * _centre.width;
  394. var positionCurrent:Number = shift;
  395. var positionNext:Number = (_back ? -1.0 : 1.0) * _centre.width + shift;
  396. var slidingVertices:Vector.<Number> = offsetPage(positionCurrent).concat(offsetPage(positionNext));
  397. _slideXyzVertexBuffer.uploadFromVector(slidingVertices, 0, 8);
  398. _uv = new Vector.<Number>();
  399. createUV(_listRecordCurrent);
  400. createUV(_listRecordNext);
  401. _slideUvVertexBuffer.uploadFromVector(_uv, 0, 8);
  402. _context3D.setVertexBufferAt( 1, _slideUvVertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2 ); //va1
  403. _context3D.clear(0.9, 0.9, 0.9);
  404. _context3D.setTextureAt(0, _listScrollingTexture[_listRecordCurrent.textureIndex]);
  405. _context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([_listRecordCurrent.uvWidth, 0.0, 0.0, 1.0 ]) ); // fc0
  406. _context3D.drawTriangles(_slideIndexBuffer, 0, 2);
  407. _context3D.setTextureAt(0, _listScrollingTexture[_listRecordNext.textureIndex]);
  408. _context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([_listRecordNext.uvWidth, 0.0, 0.0, 1.0 ]) ); // fc0
  409. _context3D.drawTriangles(_slideIndexBuffer, 6, 2);
  410. _context3D.present();
  411. }
  412. /**
  413. * Sliding transition is complete
  414. */
  415. protected function slideComplete():void {
  416. setRegisters();
  417. _listRecordCurrent = null;
  418. _listRecordNext = null;
  419. _stopping = false;
  420. _enabled = true;
  421. _count = 1.0;
  422. onEnterFrame(this, updateLists);
  423. }
  424. protected function setRegisters():void {
  425. _context3D.setVertexBufferAt( 0, _xyzVertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3 ); //va0
  426. _context3D.setVertexBufferAt( 1, _uvVertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2 ); //va1
  427. _context3D.setProgram(_listScrollingShaderProgram);
  428. }
  429. protected function unSetRegisters():void {
  430. _context3D.setVertexBufferAt( 0, null ); //va0
  431. _context3D.setVertexBufferAt( 1, null ); //va1
  432. }
  433. /**
  434. * Render all lists on a page
  435. */
  436. protected function drawLists():void {
  437. _context3D.clear(0.9, 1.0, 0.9);
  438. for each (var listRecord:ListRecord in _listRecords) {
  439. if (listRecord.container is UIScrollVertical && listRecord.onScreen) { // && (!event || !UIScrollVertical(listRecord.container).sliderVisible)) {
  440. _context3D.setTextureAt(0, _listScrollingTexture[listRecord.textureIndex]);
  441. _context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([ listRecord.uvWidth, 0.0, 0.0, 1.0 ]) ); // fc0
  442. _context3D.drawTriangles(_indexBuffer, 6 * listRecord.textureIndex, 2);
  443. }
  444. }
  445. _context3D.present();
  446. }
  447. /**
  448. * Cut a hole in a particular container background so that the accelerated list is visible underneath.
  449. */
  450. public function cutRectangle(container:Sprite, rectangle:Rectangle):void {
  451. if (container is UIList) {
  452. return;
  453. }
  454. var topLeft:Point = container.globalToLocal(new Point(rectangle.x, rectangle.y));
  455. container.graphics.drawRect(topLeft.x, topLeft.y, rectangle.width, rectangle.height);
  456. if (container is IContainerUI && _holes.indexOf(IContainerUI(container)) < 0) {
  457. _holes.push(container);
  458. }
  459. }
  460. /**
  461. * Put a hole in the MadComponents display-list UI, so you can see the accelerated Stage3D list, exactly where the display-list list is.
  462. * (We can seemlessly switch between the two lists - and the user is unaware of holes in th UI, or this mechanism)
  463. */
  464. public function pokeHole(component:UIScrollVertical, rectangle:Rectangle = null, container:Sprite = null):void {
  465. if (!rectangle) {
  466. var point:Point = component.localToGlobal(new Point(0,0));
  467. rectangle = new Rectangle(point.x, point.y, component.attributes.width, component.height);
  468. container = component;
  469. }
  470. else if (container != UI.uiLayer && container is IContainerUI && IContainerUI(container).attributes.backgroundColours.length> 0 && !(container is UIForm && UIForm(container).hasPickerBackground)) {
  471. cutRectangle(container, rectangle);
  472. }
  473. if (container == UI.uiLayer) {
  474. cutRectangle(UI.uiLayer, rectangle);
  475. }
  476. else {
  477. pokeHole(component, rectangle, Sprite(container.parent));
  478. }
  479. }
  480. /**
  481. * Is the list on a page which is visible?
  482. */
  483. protected function isVisible(container:Sprite):Boolean {
  484. if (!container.visible) {
  485. return false;
  486. }
  487. else if (container != UI.uiLayer) {
  488. return isVisible(Sprite(container.parent));
  489. }
  490. return true;
  491. }
  492. /**
  493. * You must call this if you change page.
  494. * (But if you utilise this class and accelerated page transition slides - then no need.)
  495. */
  496. public function pageChanged():void {
  497. disable();
  498. enable();
  499. }
  500. override public function enable():void {
  501. if (!_enabled) {
  502. _enabled = true;
  503. _holes = new Vector.<IContainerUI>();
  504. for each (var listRecord:ListRecord in _listRecords) {
  505. var onScreen:Boolean = isVisible(Sprite(listRecord.container));
  506. listRecord.onScreen = onScreen;
  507. if (onScreen && listRecord.container is UIScrollVertical) {
  508. pokeHole(UIScrollVertical(listRecord.container));
  509. if (ALTERNATIVE_SCHEME) {
  510. takeOverFromListScroller(listRecord);
  511. }
  512. }
  513. }
  514. _screen.stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
  515. _screen.stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
  516. }
  517. setRegisters();
  518. makeUV();
  519. onEnterFrame(this, updateLists);
  520. }
  521. override public function disable():void {
  522. if (_enabled) {
  523. _enabled = false;
  524. for each (var container:IContainerUI in _holes) {
  525. container.drawComponent();
  526. }
  527. _holes = new Vector.<IContainerUI>();
  528. UI.drawStageBackground();
  529. unSetRegisters();
  530. _screen.stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
  531. _screen.stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUp);
  532. }
  533. }
  534. /**
  535. * Mouse down handler
  536. */
  537. protected function mouseDown(event:MouseEvent):void {
  538. for each (var listRecord:ListRecord in _listRecords) {
  539. if (listRecord.onScreen && listRecord.container is UIScrollVertical) {
  540. var point:Point = Sprite(listRecord.container).localToGlobal(new Point(0,0));
  541. if (isNaN(listRecord.destination) &&
  542. _screen.stage.mouseX > point.x &&
  543. _screen.stage.mouseX < point.x + listRecord.container.attributes.width * UI.scale &&
  544. _screen.stage.mouseY > point.y &&
  545. _screen.stage.mouseY < point.y + Sprite(listRecord.container).height * UI.scale) {
  546. _activeList = listRecord;
  547. }
  548. }
  549. }
  550. if (_activeList) {
  551. _oldDelta = (_activeList == _oldActiveList) ? _activeList.delta : Number.NaN;
  552. startMovement(_activeList);
  553. _screen.stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
  554. _screen.stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
  555. event.stopPropagation();
  556. }
  557. }
  558. /**
  559. * The last custom-row component clicked
  560. */
  561. public function get componentChanged():DisplayObject {
  562. return _componentChanged;
  563. }
  564. /**
  565. * Not implemented in ListScrolling - override in extended class
  566. */
  567. public function updateComponent(pageNumber:int, component:DisplayObject = null):void {
  568. //Not implemented
  569. }
  570. /**
  571. * Mouse up handler
  572. */
  573. protected function mouseUp(event:MouseEvent):void {
  574. _screen.stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUp);
  575. _screen.stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
  576. if (_activeList) {
  577. var noMove:Boolean = Math.abs(_screen.stage.mouseY - _startMouseY) < UI.scale;
  578. if (_searchHit) {
  579. _componentChanged = _searchHit;
  580. _screen.stage.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_UP, false));
  581. _searchHit.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_UP));
  582. _screen.stage.dispatchEvent(new Event(COMPONENT_CHANGED));
  583. if (_autoUpdateComponents) {
  584. updateComponent(_activeList.textureIndex, _searchHit);
  585. }
  586. if (ALTERNATIVE_SCHEME) {
  587. takeOverFromListScroller(_activeList);
  588. }
  589. }
  590. else if (_clicked && _activeList.container is UIList && !_activeList.showPressed) {
  591. UIList(_activeList.container).clearPressed(_frameCount < HIGHLIGHT_FRAMES);
  592. }
  593. else if (_frameCount < CLICK_FRAMES && _notTooFastForClick && noMove && _activeList.container is UIList) {
  594. // Very brief list click heurustic
  595. listClick(_activeList);
  596. UIList(_activeList.container).clearPressed(true);
  597. }
  598. if (noMove && _activeList.container is UIPicker) {
  599. _oldActiveList = _activeList;
  600. _activeList = null;
  601. return;
  602. }
  603. else if (!_clicked && !_searchHit) {
  604. _somethingMoving++;
  605. _activeList.inertia = true;
  606. _activeList.delta = (_screen.stage.mouseY - _lastPositionY) * MOVE_FACTOR / UI.scale;
  607. if (!isNaN(_oldDelta) && _activeList.delta * _oldDelta > 0 && Math.abs(_activeList.delta) > FAST_THRESHOLD && Math.abs(_oldDelta) > FAST_THRESHOLD) { // swipe twice to make it accelerate
  608. _activeList.delta = ACCELERATE * (_activeList.delta + _oldDelta)/2;
  609. trace("accelerate scrolling",_activeList.delta);
  610. if (_activeList.delta < - DELTA_LIMIT) {
  611. _activeList.delta = - DELTA_LIMIT;
  612. }
  613. else if (_activeList.delta > DELTA_LIMIT) {
  614. _activeList.delta = DELTA_LIMIT;
  615. }
  616. }
  617. }
  618. _searchHit = null;
  619. _clicked = false;
  620. _oldActiveList = _activeList;
  621. _activeList = null;
  622. }
  623. else {
  624. _oldActiveList = null;
  625. }
  626. }
  627. protected function isClickOnComponent(listRecord:ListRecord):void {
  628. var list:UIScrollVertical = UIScrollVertical(listRecord.container);
  629. if (_notTooFastForClick) {
  630. if (ALTERNATIVE_SCHEME && list is UIList) {
  631. UIList(list).sliderVisible = false;
  632. }
  633. _searchHit = UIScrollVertical.searchHit(list.pages[0]);
  634. if (_searchHit) {
  635. _searchHit = UIScrollVertical.searchHitChild(_searchHit);
  636. if (ALTERNATIVE_SCHEME) {
  637. showRealListScroller(listRecord);
  638. }
  639. }
  640. }
  641. else {
  642. _searchHit = null;
  643. }
  644. }
  645. /**
  646. * Start scroll movement
  647. */
  648. protected function startMovement(listRecord:ListRecord):void {
  649. var list:UIScrollVertical = UIScrollVertical(listRecord.container);
  650. if (!ALTERNATIVE_SCHEME || list.sliderVisible) { //|| list.sliderVisible should restore lost sync.
  651. doTakeOverFromListScroller(list);
  652. }
  653. _notTooFastForClick = !listRecord.inertia || Math.abs(listRecord.delta) < FAST_THRESHOLD;
  654. if (listRecord.inertia || !isNaN(listRecord.destination)) {
  655. _somethingMoving--;
  656. listRecord.destination = Number.NaN;
  657. listRecord.inertia = false;
  658. }
  659. isClickOnComponent(listRecord);
  660. _frameCount = 0;
  661. _lastPositionY = _startMouseY = _screen.stage.mouseY;
  662. _startSliderY = list.sliderY;
  663. }
  664. /**
  665. * The user has swiped the screen, and removed their finger. The scrolling motion has momentum,
  666. */
  667. protected function inertiaMovement(listRecord:ListRecord):void {
  668. var sliderY:Number = UIScrollVertical(listRecord.container).sliderY;
  669. var maximumSlide:Number = -UIScrollVertical(listRecord.container).maximumSlide;
  670. // if (listRecord.container is UIPicker) {
  671. // maximumSlide = -(listRecord.container.pages[0].height - Sprite(listRecord.container).height);
  672. // }
  673. listRecord.delta *= (sliderY > 0 || sliderY < maximumSlide) ? FASTER_DECAY : DECAY;
  674. UIScrollVertical(listRecord.container).sliderY = sliderY + listRecord.delta;
  675. if (Math.abs(listRecord.delta) < DELTA_THRESHOLD) {
  676. listRecord.inertia = false;
  677. if (listRecord.container is UIPicker) {
  678. listRecord.destination = - UIPicker(listRecord.container).snapToCellPosition;
  679. }
  680. else if (sliderY > 0) {
  681. listRecord.destination = 0;
  682. }
  683. else if (sliderY < maximumSlide) {
  684. listRecord.destination = maximumSlide;
  685. }
  686. else {
  687. stopMovement(listRecord);
  688. }
  689. }
  690. }
  691. /**
  692. * Scroll to a position
  693. */
  694. protected function destinationMovement(listRecord:ListRecord):void {
  695. var sliderY:Number = UIScrollVertical(listRecord.container).sliderY;
  696. var distance:Number = listRecord.destination - sliderY;
  697. if (Math.abs(distance) < MOVE_THRESHOLD) {
  698. stopMovement(listRecord, true);
  699. }
  700. else {
  701. UIScrollVertical(listRecord.container).sliderY = sliderY + distance * BOUNCE_FACTOR ;
  702. }
  703. }
  704. /**
  705. * Stop list scrolling
  706. */
  707. protected function stopMovement(listRecord:ListRecord, destination:Boolean = false):void {
  708. var list:UIScrollVertical = UIScrollVertical(listRecord.container);
  709. if (destination) {
  710. list.sliderY = listRecord.destination;
  711. }
  712. if (!isNaN(listRecord.destination) || listRecord.inertia) {
  713. listRecord.inertia = false;
  714. listRecord.destination = Number.NaN;
  715. _somethingMoving--;
  716. }
  717. if (!ALTERNATIVE_SCHEME) {
  718. showRealListScroller(listRecord);
  719. }
  720. }
  721. /**
  722. * Generate UV for each list
  723. */
  724. protected function makeUV():void {
  725. _uv = new Vector.<Number>();
  726. for each (var listRecord:ListRecord in _listRecords) {
  727. createUV(listRecord);
  728. if (_uvVertexBuffer) {
  729. _uvVertexBuffer.uploadFromVector(_uv, 0, _uv.length / 2);
  730. }
  731. }
  732. }
  733. /**
  734. * Render lists
  735. */
  736. protected function updateLists(event:Event):void {
  737. if (_activeList) {
  738. _lastPositionY = _screen.stage.mouseY;
  739. if (!_clicked) {
  740. UIScrollVertical(_activeList.container).sliderY = _startSliderY + (_screen.stage.mouseY - _startMouseY);
  741. }
  742. else if (_searchHit) {
  743. _screen.stage.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_MOVE, false));
  744. }
  745. _frameCount++;
  746. if (_frameCount == CLICK_FRAMES && Math.abs(_startMouseY - _screen.stage.mouseY ) < CLICK_THRESHOLD) {
  747. if (_notTooFastForClick) {
  748. listClick(_activeList);
  749. }
  750. else {
  751. trace("too fast for click");
  752. }
  753. }
  754. else if (_clicked && _frameCount == LONG_PRESS && Math.abs(_startMouseY - _screen.stage.mouseY ) < CLICK_THRESHOLD *UI.scale) {
  755. (_searchHit ? _searchHit : UIScrollVertical(_activeList.container)).dispatchEvent(new Event(UIList.LONG_CLICK));
  756. }
  757. }
  758. if (_activeList || _somethingMoving) {
  759. for each (var listRecord:ListRecord in _listRecords) {
  760. if (listRecord.inertia) {
  761. inertiaMovement(listRecord);
  762. }
  763. if (!isNaN(listRecord.destination)) {
  764. destinationMovement(listRecord);
  765. }
  766. }
  767. makeUV();
  768. }
  769. drawLists();
  770. }
  771. /**
  772. * Handle click on a list row
  773. */
  774. protected function listClick(listRecord:ListRecord):void {
  775. var list:UIScrollVertical = UIScrollVertical(listRecord.container);
  776. if (_searchHit) {
  777. _searchHit.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_DOWN));
  778. _clicked = true;
  779. }
  780. else {
  781. if (list is UIList && !(list is UIPicker)) {
  782. if (ALTERNATIVE_SCHEME) {
  783. showRealListScroller(listRecord);
  784. list.removeEventListener(UIList.CLICKED_END, clickedEndHandler);
  785. list.addEventListener(UIList.CLICKED_END, clickedEndHandler);
  786. }
  787. _clicked = UIList(list).doClickRow();
  788. }
  789. }
  790. if (_clicked) {
  791. stopMovement(listRecord);
  792. }
  793. }
  794. /**
  795. * End of click sequence
  796. */
  797. protected function clickedEndHandler(event:Event):void {
  798. var list:UIScrollVertical = UIScrollVertical(event.currentTarget);
  799. doTakeOverFromListScroller(list);
  800. list.removeEventListener(UIList.CLICKED_END, clickedEndHandler);
  801. }
  802. /**
  803. * Show the accelerated Stage3D list
  804. */
  805. protected function takeOverFromListScroller(listRecord:ListRecord):void {
  806. doTakeOverFromListScroller(UIScrollVertical(listRecord.container));
  807. }
  808. /**
  809. * Show the accelerated Stage3D list
  810. */
  811. protected function doTakeOverFromListScroller(list:UIScrollVertical):void {
  812. if (!(list is UIPicker)) {
  813. list.visible = false;
  814. }
  815. else if (list.sliderVisible) {
  816. list.sliderVisible = false;
  817. list.graphics.clear();
  818. list.graphics.beginFill(0,0);
  819. list.graphics.drawRect(0, 0, list.attributes.width, list.attributes.height);
  820. }
  821. }
  822. /**
  823. * Show the display-list list (you are interacting with it - not scrolling it)
  824. */
  825. protected function showRealListScroller(listRecord:ListRecord):void {
  826. var list:UIScrollVertical = UIScrollVertical(listRecord.container);
  827. if (!(list is UIPicker)) {
  828. list.visible = true;
  829. }
  830. else if (!list.sliderVisible) {
  831. list.drawComponent();
  832. }
  833. list.sliderVisible = true;
  834. }
  835. /**
  836. * Clear all textures and bitmaps
  837. */
  838. protected function disposeOfTextures():void {
  839. for each (var bitmapData:BitmapData in _textureBitMapData) {
  840. bitmapData.dispose();
  841. }
  842. for each (var texture:Texture in _listScrollingTexture) {
  843. texture.dispose();
  844. }
  845. }
  846. public function freezeLists():void {
  847. for each (var listRecord:ListRecord in _listRecords) {
  848. var list:IContainerUI = listRecord.container;
  849. if (list is UIScrollVertical) {
  850. if (!(list is UIPicker)) {
  851. stopMovement(listRecord);
  852. }
  853. showRealListScroller(listRecord);
  854. }
  855. }
  856. }
  857. override public function destructor():void {
  858. super.destructor();
  859. _screen.stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUp);
  860. _screen.stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
  861. }
  862. }
  863. }