PageRenderTime 60ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/src/framework/display/Position.as

https://github.com/talltyler/ASTRID
ActionScript | 472 lines | 313 code | 44 blank | 115 comment | 121 complexity | 73b0a6e8c9c59821f061066a11918bd4 MD5 | raw file
  1. /* ,----,
  2. * ,/ .`|
  3. * ,---, .--.--. ,` .' :,-.----. ,---, ,---,
  4. * ' .' \ / / '. ; ; /\ / \ ,`--.' | .' .' `\
  5. * / ; '. | : /`. /.'___,/ ,' ; : \ | : :,---.' \
  6. * : : \ ; | |--` | : | | | .\ : : | '| | .`\ |
  7. * : | /\ \| : ;_ ; |.'; ; . : |: | | : |: : | ' |
  8. * | : ' ;. :\ \ `.`----' | | | | \ : ' ' ;| ' ' ; :
  9. * | | ;/ \ \`----. \ ' : ; | : . / | | |' | ; . |
  10. * ' : | \ \ ,'__ \ \ | | | ' ; | | \ ' : ;| | : | '
  11. * | | ' '--' / /`--' / ' : | | | ;\ \| | '' : | / ;
  12. * | : : '--'. / ; |.' : ' | \.'' : || | '` ,/
  13. * | | ,' `--'---' '---' : : :-' ; |.' ; : .'
  14. * `--'' | |.' '---' | ,.' Tyler
  15. * ActionScript tested rapid iterative dev `---' Copyright2010'---' Larson
  16. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  17. * This program is free software: you can redistribute it and/or modify
  18. * it under the terms of the GNU General Public License as published by
  19. * the Free Software Foundation, either version 3 of the License, or
  20. * (at your option) any later version.
  21. *
  22. * This program is distributed in the hope that it will be useful,
  23. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  24. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  25. * GNU Lesser General Public License for more details.
  26. * http://www.gnu.org/licenses
  27. */
  28. package framework.display
  29. {
  30. import flash.display.Sprite;
  31. import flash.geom.Matrix;
  32. import flash.geom.Rectangle;
  33. import framework.view.html.Element;
  34. import framework.utils.TypeUtils;
  35. import framework.debug.Log;
  36. public final class Position
  37. {
  38. private static const COMPUTED_STYLES:String = "computedStyles";
  39. private static const MARGIN:String = "margin";
  40. private static const PADDING:String = "padding";
  41. // private var current:Object = {top:0, bottom:0};
  42. // private var floatLeft:Object = {left:0, right:0, top:0, bottom:0};
  43. // private var floatRight:Object = {left:0, right:0, top:0, bottom:0};
  44. public function Position()
  45. {
  46. super();
  47. }
  48. /**
  49. * This is a very simple implimentation of absolute positioning, floats, margins, padding and so on need to be added back into this. I might want to make these bit of functionality modular so that all of the positioning methods can use them.
  50. * You can instanciate this from css by saying positioning:absolute;
  51. */
  52. public static function absolute( parentElement:Sprite, element:ElementBase ) : void
  53. {
  54. // TODO: pass in reference to frame, need scope to the highest thing
  55. //var appContainer:ApplicationContainer = ApplicationContainer.instance;
  56. //var local:Point = element.localToGlobal( new Point(appContainer.main.x, appContainer.main.y) );
  57. //element.x = -(local.x - element.style.x);
  58. //element.y = -(local.y - element.style.y);
  59. }
  60. /**
  61. * Auto is the default mode of positioning anything, it mirrors the standard way that HTML positions elements.
  62. * Currently this method impliments, margins(numbers, percents, 'auto'), padding(numbers, percents), float(left, right), clear(left, right, both), inline, text-align
  63. * There are still many things that need to be refined but it is pretty close to the way that a browser would handle positioning
  64. * Still TODO: z-index,
  65. */
  66. public static function auto( parentElement:Sprite, element:ElementBase ) : void
  67. {
  68. // SETTINGS
  69. const paddingAddsToSize:Boolean = true;
  70. const marginSubtractsFromSize:Boolean = true;
  71. // end settings
  72. var parent:*
  73. var parentStyle:Object;
  74. var parentWidth:Number;
  75. var parentHeight:Number;
  76. var parentPadding:Object
  77. if( parentElement is ElementBase ) {
  78. parent = parentElement as ElementBase;
  79. parentStyle = parent.computedStyles;
  80. parentWidth = parentStyle.width;
  81. parentHeight = parentStyle.height;
  82. parentPadding = parentStyle.padding||{top:0, right:0, bottom:0, left:0};
  83. }else{
  84. /* // Dont need to position something that is not an elementBase
  85. parent = parentElement;
  86. parentWidth = parentElement.width;
  87. parentHeight = parentElement.height;
  88. parentPadding = {top:0, right:0, bottom:0, left:0};
  89. */
  90. return
  91. }
  92. var style:Object = element.computedStyles;
  93. var x:Number = style.left;
  94. var y:Number = style.top;
  95. var width:Number = style.width;
  96. var height:Number = style.height;
  97. var center:Number = ( parentWidth - parentPadding.left - parentPadding.right ) / 2;
  98. var middle:Number = ( parentHeight - parentPadding.top - parentPadding.bottom ) / 2;
  99. var standardItems:Array = [];
  100. var floatedItems:Array = [];
  101. var floatedLeft:Array = [];
  102. var floatRight:Array = [];
  103. var tableCells:Array = [];
  104. var tableRows:Array = [];
  105. var lastChildRect:Rectangle
  106. var lastChildMargin:Object
  107. // table related variables
  108. var definedWidth:Number = 0;
  109. var widthPadding:Number = 0;
  110. var widthMargin:Number = 0;
  111. var numDefinedWidth:int = 0;
  112. var definedHeight:Number = 0;
  113. var heightPadding:Number = 0;
  114. var heightMargin:Number = 0;
  115. var numDefinedHeight:int = 0;
  116. // display:none;
  117. if( style.display == "none" ){
  118. style.width = 0;
  119. style.height = 0;
  120. element.visible = false;
  121. }
  122. // Changes depths of items based on there properties, floats are above. I'm not sure if this is correct.
  123. for( var h:int = 0; h < element.parent.numChildren; h++ ){
  124. var currentItem:* = element.parent.getChildAt( h );
  125. if( currentItem.hasOwnProperty(COMPUTED_STYLES) ){
  126. if( currentItem.computedStyles.display == "table-cell" ){
  127. tableCells.push( currentItem );
  128. }else if( currentItem.computedStyles.display == "table-row" ){
  129. tableRows.push( currentItem );
  130. }else if( currentItem.computedStyles.hasOwnProperty("float") ) {
  131. if( currentItem.computedStyles.float == "left"){
  132. floatedLeft.push( currentItem );
  133. floatedItems.push( currentItem );
  134. }
  135. else if( currentItem.computedStyles.float == "right") {
  136. floatRight.push( currentItem );
  137. floatedItems.push( currentItem );
  138. }
  139. }else{
  140. standardItems.push( currentItem );
  141. }
  142. }else{
  143. standardItems.push( currentItem );
  144. }
  145. }
  146. var depthCount:int = 0;
  147. for each( var depthItem:* in standardItems ){
  148. element.parent.addChildAt(depthItem, depthCount);
  149. depthCount++;
  150. }
  151. for each( depthItem in floatedItems ){
  152. element.parent.addChildAt(depthItem, depthCount)
  153. depthCount++
  154. }
  155. // get the last child, we need to find out if we are the first element or need to be position relative to others
  156. if( element.parent.getChildIndex( element ) != 0 ) {
  157. var lastChildIndex:int = element.parent.getChildIndex( element ) - 1;
  158. var lastChild:* = element.parent.getChildAt( lastChildIndex );
  159. //// Log.debug(element.parent.numChildren, lastChildIndex, lastChild.name)
  160. for each( var child:ElementBase in element.parent ){
  161. if( child.index == ( element.index - 1 ) ) {
  162. lastChildIndex = child.index;
  163. lastChild = child;
  164. }
  165. }
  166. /*
  167. for( var i:int = element.parent.numChildren-1; i>= 0; i-- ){
  168. if( lastChild.hasOwnProperty(COMPUTED_STYLES) && !lastChild.computedStyles.hasOwnProperty("float") ){
  169. lastChild = element.parent.getChildAt( lastChildIndex );
  170. lastChildIndex--;
  171. if()
  172. break;
  173. }
  174. }
  175. */
  176. if( lastChild.hasOwnProperty(COMPUTED_STYLES) && !lastChild.computedStyles.hasOwnProperty("float") ){
  177. lastChildRect = new Rectangle(lastChild.x, lastChild.y, lastChild.computedStyles.width||lastChild.width, lastChild.computedStyles.height||lastChild.height)
  178. if( lastChild.computedStyles.hasOwnProperty(MARGIN) )
  179. lastChildMargin = {left:lastChild.computedStyles.margin.left, right:lastChild.computedStyles.margin.right, bottom:lastChild.computedStyles.margin.bottom, top:lastChild.computedStyles.margin.top}
  180. else
  181. lastChildMargin = {left:0, right:0, bottom:0, top:0};
  182. }else{
  183. lastChildRect = new Rectangle(0, 0, 0, 0);
  184. lastChildMargin = {left:0, right:0, bottom:0, top:0};
  185. }
  186. }
  187. // implement clear
  188. var clearLeft:Boolean = false;
  189. var clearRight:Boolean = false;
  190. if( style.clear == "left" ) {
  191. clearLeft = true;
  192. }else if( style.clear == "both" || style.clear == "right" ) {
  193. clearRight = true;
  194. }
  195. if( element.parent.getChildIndex( element ) != 0 ) {
  196. var lastClearChild:* = element.parent.getChildAt( element.parent.getChildIndex( element ) - 1 );
  197. if( lastClearChild.hasOwnProperty(COMPUTED_STYLES) && ( lastClearChild.computedStyles.clear == "right" || lastClearChild.computedStyles.clear == "both" ) && int(lastClearChild.computedStyles.left) >= int(style.left) ){
  198. clearRight = true;
  199. }
  200. }
  201. if( style.hasOwnProperty(MARGIN) == false ) {
  202. style.margin = {left:0, right:0, bottom:0, top:0};
  203. }
  204. if( style.hasOwnProperty(PADDING) == false ) {
  205. style.padding = {left:0, right:0, bottom:0, top:0};
  206. }
  207. if( parent.computedStyles.hasOwnProperty(PADDING) == false ){
  208. parent.computedStyles.padding = {left:0, right:0, bottom:0, top:0};
  209. }
  210. if( style.display == "inline" || style.display == "inline-block" ){
  211. if( style.textAlign == "center" ){
  212. style.left = center - style.width/2;
  213. if( element.parent.getChildIndex( element ) == 0 ){
  214. style.left = parent.computedStyles.padding.top + style.margin.top + style.top;
  215. }else{
  216. style.top = lastChildRect.y - lastChildMargin.top + lastChildMargin.bottom + lastChildRect.height + style.margin.top + style.top;
  217. }
  218. }else if(style.textAlign == "right"){
  219. if( element.parent.getChildIndex( element ) == 0 ){
  220. style.left = parent.computedStyles.width - parent.computedStyles.padding.right - style.width - style.margin.right;
  221. style.top = parent.computedStyles.padding.top + style.top + style.margin.top;
  222. }else{
  223. if( lastChildRect.x - lastChildMargin.left - style.width - style.margin.right > 0 ){
  224. style.left = lastChildRect.x - lastChildMargin.left - style.width - style.margin.right;
  225. style.top = lastChildRect.y;
  226. }else{ // else wrap to next line
  227. style.left = parent.computedStyles.width - parent.computedStyles.padding.right - style.width - style.margin.right;
  228. style.top = style.top + style.margin.top + lastChildRect.y + lastChildRect.height + lastChildMargin.bottom;
  229. }
  230. }
  231. }else{ // left
  232. if( element.parent.getChildIndex( element ) == 0 ){
  233. style.left = parent.computedStyles.padding.left + style.margin.left + style.left;
  234. style.top = parent.computedStyles.padding.top + style.top + style.margin.top;
  235. }else{
  236. if( lastChildRect.x + lastChildRect.width + lastChildMargin.right + style.width + style.margin.left < parent.computedStyles.width ){
  237. style.left = lastChildRect.x + lastChildRect.width + lastChildMargin.right + style.margin.left;
  238. style.top = lastChildRect.y;
  239. }else{ // else wrap to next line
  240. style.left = parent.computedStyles.padding.left + style.margin.left + style.left;
  241. style.top = style.top + style.margin.top + lastChildRect.y + lastChildRect.height + lastChildMargin.bottom;
  242. }
  243. }
  244. }
  245. }else if( style.display == "table-cell" ){ // style.display == "table-row" ||
  246. for each( var item:Element in tableCells ){
  247. if( item.style.width ) {
  248. definedWidth += item.computedStyles.width;
  249. widthPadding += item.computedStyles.padding.left + item.computedStyles.padding.right;
  250. widthMargin += item.computedStyles.margin.left + item.computedStyles.margin.right;
  251. numDefinedHeight++;
  252. }
  253. if( item.style.height ){
  254. definedHeight += item.computedStyles.height;
  255. heightPadding += item.computedStyles.padding.top + item.computedStyles.padding.bottom;
  256. heightMargin += item.computedStyles.margin.top + item.computedStyles.margin.bottom;
  257. numDefinedHeight++;
  258. }
  259. }
  260. var tableWidth:Number = parent.computedStyles.width - parent.computedStyles.padding.left - parent.computedStyles.padding.right;
  261. var tableHeight:Number = parent.totalHeight;
  262. /*
  263. if( tableHeight < style.height )
  264. tableHeight = style.height;
  265. if( tableHeight == 0 )
  266. tableHeight = 100;
  267. */
  268. /*
  269. var isColumn:Boolean
  270. if( parent is ElementBase && parent.computedStyles.display == "table-cell" ) {
  271. if( parent.parent && parent.parent.parent is ElementBase && parent.parent.parent.computedStyles.display == "table-cell" ) {
  272. isColumn = false;
  273. }else{
  274. isColumn = true;
  275. }
  276. }else{
  277. isColumn = false;
  278. }
  279. */
  280. style.width = (tableWidth - widthPadding - widthMargin)/( tableCells.length - numDefinedWidth );
  281. //style.height = (tableHeight)/( tableCells.length - numDefinedHeight );
  282. // - heightPadding - heightMargin
  283. if( element.parent.getChildIndex(element) == 0 ) {
  284. style.left = style.margin.left;
  285. style.top = style.margin.top;
  286. }else{
  287. var lastCell:Element = element.parent.getChildAt(element.parent.getChildIndex(element) - 1) as Element;
  288. style.left = lastCell.x + lastCell.computedStyles.width + lastCell.computedStyles.margin.right + style.margin.left;
  289. style.top = lastCell.y;
  290. }
  291. /*
  292. if( isColumn ) {
  293. }else{
  294. if( element.parent.getChildIndex( element ) == 0 ){
  295. style.left = parent.computedStyles.padding.left + style.margin.left + style.left;
  296. style.top = parent.computedStyles.padding.top + style.top + style.margin.top;
  297. }else{
  298. style.left = parent.computedStyles.padding.left + style.margin.left + style.left;
  299. style.top = style.margin.top + lastChildRect.y + lastChildRect.height + lastChildMargin.bottom;
  300. }
  301. }
  302. */
  303. //// Log.debug(style.left, lastCell.x, lastCell.computedStyles.width, lastCell.computedStyles.margin.right, style.margin.left)
  304. }else{ // if( style.display == "block" || style.display == "table" ) // I dont know what layout options a table has over a block, for now I'm going to say they are the same
  305. if( element.parent.getChildIndex( element ) == 0 ){
  306. style.left = parent.computedStyles.padding.left + style.margin.left + TypeUtils.cleanNumber( element.style.left, parentWidth ); // style.left;
  307. style.top = parent.computedStyles.padding.top + style.margin.top + TypeUtils.cleanNumber( element.style.top, parentHeight ); // style.top;
  308. }else{
  309. style.left = parent.computedStyles.padding.left + style.margin.left + TypeUtils.cleanNumber( element.style.left||0, parentWidth ); // style.left;
  310. style.top = style.margin.top + lastChildRect.y + lastChildRect.height + lastChildMargin.bottom;
  311. }
  312. // // Log.debug("lastChildRect", lastChildRect.height, lastChildRect.y)
  313. // margin auto
  314. if(style.margin.left == "auto" || style.margin.right == "auto"){
  315. style.left = center - style.width/2;
  316. }
  317. if(style.margin.top == "auto" || style.margin.bottom == "auto"){
  318. style.top = middle - style.height/2;
  319. }
  320. // vertical-align
  321. if( style.verticalAlign == "middle" ){
  322. style.top = middle - style.margin.top;
  323. }else if( style.verticalAlign == "bottom" ){
  324. style.top = parent.computedStyles.height - parent.computedStyles.padding.bottom - style.margin.bottom;
  325. }else{
  326. if( element.parent.getChildIndex( element ) == 0 )
  327. style.top = style.margin.top + style.padding.top + style.top;
  328. else{
  329. /*
  330. if( lastChildRect.x + lastChildRect.width + par.parent.clean.padding.left + n.margin.left + lastChild.clean.padding.right + par.parent.clean.padding.right < par.parent.clean.width ){
  331. }else{
  332. }
  333. */
  334. }
  335. }
  336. var lastFloatIndex:Number;
  337. var currentLoopIndex:int;
  338. var lastFloatRect:Rectangle;
  339. var lastFloatMargin:Object;
  340. var lastFloatPadding:Object;
  341. var largestRightFloatY:Number;
  342. var lastFloat:*;
  343. var lastFloat2:*;
  344. var floatCount:int = 0;
  345. var floatIndex:int = 0;
  346. var i:int;
  347. if( style.float == "right" ){
  348. if( element.parent.getChildIndex( element ) == 0 ){
  349. style.left = parent.computedStyles.width - parent.computedStyles.padding.right - style.width - style.margin.right;
  350. style.top = parent.computedStyles.padding.top + style.margin.top;
  351. }else{
  352. // figure out what the last floated element is.
  353. lastFloatIndex = element.parent.getChildIndex( element );
  354. floatIndex = floatRight.indexOf(element);
  355. lastFloat = floatRight[ floatIndex - 1 ];
  356. if( lastFloat.computedStyles.clear == "right" || lastFloat.computedStyles.clear == "both" ){
  357. clearRight = true;
  358. }
  359. if(floatIndex != 0){
  360. lastFloatRect = new Rectangle(lastFloat.x, lastFloat.y , lastFloat.computedStyles.width, lastFloat.computedStyles.height)
  361. lastFloatMargin = {left:lastFloat.computedStyles.margin.left, right:lastFloat.computedStyles.margin.right, top:lastFloat.computedStyles.margin.top, bottom:lastFloat.computedStyles.margin.bottom}
  362. lastFloatPadding = {left:lastFloat.computedStyles.padding.left, right:lastFloat.computedStyles.padding.right, top:lastFloat.computedStyles.padding.top, bottom:lastFloat.computedStyles.padding.bottom}
  363. }else{
  364. lastFloatRect = new Rectangle(parent.computedStyles.width - parent.computedStyles.padding.right, parent.computedStyles.padding.top, lastFloat.computedStyles.width, lastFloat.computedStyles.height)
  365. lastFloatMargin = {left:lastFloat.computedStyles.margin.left, right:lastFloat.computedStyles.margin.right, top:lastFloat.computedStyles.margin.top, bottom:lastFloat.computedStyles.margin.bottom}
  366. lastFloatPadding = {left:lastFloat.computedStyles.padding.left, right:lastFloat.computedStyles.padding.right, top:lastFloat.computedStyles.padding.top, bottom:lastFloat.computedStyles.padding.bottom}
  367. }
  368. if( !clearLeft && !clearRight && lastFloatRect.x - lastFloatMargin.left - style.width - style.margin.left - style.margin.right >= 0 ){
  369. style.left = lastFloatRect.x - style.width - style.margin.right - lastFloatMargin.right;
  370. style.top = (lastFloatRect.y + style.margin.top);
  371. if(floatIndex != 0) style.top = style.top - lastFloatMargin.top;
  372. }else{ // wrapp to the next line
  373. largestRightFloatY = lastFloat.computedStyles.base.y + lastFloat.computedStyles.height + lastFloat.computedStyles.margin.bottom
  374. for( var j:int = element.parent.numChildren-1; j >= 0; j-- ){
  375. var q:* = element.parent.getChildAt( j );
  376. if( q.hasOwnProperty(COMPUTED_STYLES) && q.computedStyles.float == "right" && largestRightFloatY < q.computedStyles.top + q.computedStyles.height + q.computedStyles.margin.bottom ){
  377. largestRightFloatY = q.computedStyles.top + q.computedStyles.height + q.computedStyles.margin.bottom;
  378. }
  379. }
  380. style.left = parent.computedStyles.width - parent.computedStyles.padding.right - style.width - style.margin.right;
  381. style.top = largestRightFloatY; // - n.base.height
  382. }
  383. }
  384. }else if( style.float == "left" ){
  385. if( element.parent.getChildIndex( element ) == 0 ){ // if this is the first element
  386. style.left = parent.computedStyles.padding.left + style.margin.left;
  387. style.top = parent.computedStyles.padding.top + style.margin.top;
  388. }else{ // else we have to find the other elements that have relation to this one
  389. // figure out what the last floated element is.
  390. lastFloatIndex = element.parent.getChildIndex( element );
  391. floatIndex = floatedLeft.indexOf(element);
  392. lastFloat = floatedLeft[ floatIndex - 1 ];
  393. if( lastFloat == null ) return
  394. if( lastFloat.computedStyles.clear == "right" || lastFloat.computedStyles.clear == "both" ){
  395. clearRight = true;
  396. }
  397. // if this is the first floated item just place it, else position relative to last floated item
  398. if(floatIndex != 0){
  399. lastFloatRect = new Rectangle(lastFloat.x, lastFloat.y, lastFloat.computedStyles.width, lastFloat.computedStyles.height )
  400. lastFloatMargin = {left:lastFloat.computedStyles.margin.left, right:lastFloat.computedStyles.margin.right, top:lastFloat.computedStyles.margin.top, bottom:lastFloat.computedStyles.margin.bottom}
  401. lastFloatPadding = {left:lastFloat.computedStyles.padding.left, right:lastFloat.computedStyles.padding.right, top:lastFloat.computedStyles.padding.top, bottom:lastFloat.computedStyles.padding.bottom}
  402. }else{
  403. lastFloatRect = new Rectangle(0,0,0,0)
  404. lastFloatMargin = {left:0, right:0, top:0, bottom:0}
  405. lastFloatPadding = {left:0, right:0, top:0, bottom:0}
  406. }
  407. if( !clearLeft && !clearRight && lastFloatRect.x + lastFloatMargin.left + style.width + style.margin.left + style.margin.right + lastFloatRect.width <= parent.computedStyles.width ){
  408. style.left = lastFloatRect.x + lastFloatRect.width + style.margin.left + lastFloatMargin.right// + n.base.x
  409. style.top = (lastFloatRect.y + style.margin.top);
  410. if(floatIndex != 0) style.top = style.top - lastFloatMargin.top;
  411. }else{ // wrapp to the next line
  412. largestRightFloatY = lastFloat.computedStyles.top + lastFloat.computedStyles.height + lastFloat.computedStyles.margin.bottom // - par.parent.clean.padding.top
  413. style.left = parent.computedStyles.padding.left + style.margin.left;
  414. style.top = largestRightFloatY;
  415. }
  416. }
  417. }
  418. }
  419. }
  420. public static function relative( parentElement:Sprite, element:ElementBase ) : void
  421. {
  422. // Flash uses relative positioning by default, this method doesn't need to do anything
  423. element.x = element.style.x + element.style.margin.left + element.style.padding.left;
  424. element.y = element.style.y + element.style.margin.top + element.style.padding.top;
  425. }
  426. }
  427. }