_LayoutWidget.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dijit.layout._LayoutWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dijit.layout._LayoutWidget"] = true;
  8. dojo.provide("dijit.layout._LayoutWidget");
  9. dojo.require("dijit._Widget");
  10. dojo.require("dijit._Container");
  11. dojo.require("dijit._Contained");
  12. dojo.declare("dijit.layout._LayoutWidget",
  13. [dijit._Widget, dijit._Container, dijit._Contained],
  14. {
  15. // summary:
  16. // Base class for a _Container widget which is responsible for laying out its children.
  17. // Widgets which mixin this code must define layout() to manage placement and sizing of the children.
  18. // baseClass: [protected extension] String
  19. // This class name is applied to the widget's domNode
  20. // and also may be used to generate names for sub nodes,
  21. // for example dijitTabContainer-content.
  22. baseClass: "dijitLayoutContainer",
  23. // isLayoutContainer: [protected] Boolean
  24. // Indicates that this widget is going to call resize() on its
  25. // children widgets, setting their size, when they become visible.
  26. isLayoutContainer: true,
  27. buildRendering: function(){
  28. this.inherited(arguments);
  29. dojo.addClass(this.domNode, "dijitContainer");
  30. },
  31. startup: function(){
  32. // summary:
  33. // Called after all the widgets have been instantiated and their
  34. // dom nodes have been inserted somewhere under dojo.doc.body.
  35. //
  36. // Widgets should override this method to do any initialization
  37. // dependent on other widgets existing, and then call
  38. // this superclass method to finish things off.
  39. //
  40. // startup() in subclasses shouldn't do anything
  41. // size related because the size of the widget hasn't been set yet.
  42. if(this._started){ return; }
  43. // Need to call inherited first - so that child widgets get started
  44. // up correctly
  45. this.inherited(arguments);
  46. // If I am a not being controlled by a parent layout widget...
  47. var parent = this.getParent && this.getParent()
  48. if(!(parent && parent.isLayoutContainer)){
  49. // Do recursive sizing and layout of all my descendants
  50. // (passing in no argument to resize means that it has to glean the size itself)
  51. this.resize();
  52. // Since my parent isn't a layout container, and my style *may be* width=height=100%
  53. // or something similar (either set directly or via a CSS class),
  54. // monitor when my size changes so that I can re-layout.
  55. // For browsers where I can't directly monitor when my size changes,
  56. // monitor when the viewport changes size, which *may* indicate a size change for me.
  57. this.connect(dojo.isIE ? this.domNode : dojo.global, 'onresize', function(){
  58. // Using function(){} closure to ensure no arguments to resize.
  59. this.resize();
  60. });
  61. }
  62. },
  63. resize: function(changeSize, resultSize){
  64. // summary:
  65. // Call this to resize a widget, or after its size has changed.
  66. // description:
  67. // Change size mode:
  68. // When changeSize is specified, changes the marginBox of this widget
  69. // and forces it to relayout its contents accordingly.
  70. // changeSize may specify height, width, or both.
  71. //
  72. // If resultSize is specified it indicates the size the widget will
  73. // become after changeSize has been applied.
  74. //
  75. // Notification mode:
  76. // When changeSize is null, indicates that the caller has already changed
  77. // the size of the widget, or perhaps it changed because the browser
  78. // window was resized. Tells widget to relayout its contents accordingly.
  79. //
  80. // If resultSize is also specified it indicates the size the widget has
  81. // become.
  82. //
  83. // In either mode, this method also:
  84. // 1. Sets this._borderBox and this._contentBox to the new size of
  85. // the widget. Queries the current domNode size if necessary.
  86. // 2. Calls layout() to resize contents (and maybe adjust child widgets).
  87. //
  88. // changeSize: Object?
  89. // Sets the widget to this margin-box size and position.
  90. // May include any/all of the following properties:
  91. // | {w: int, h: int, l: int, t: int}
  92. //
  93. // resultSize: Object?
  94. // The margin-box size of this widget after applying changeSize (if
  95. // changeSize is specified). If caller knows this size and
  96. // passes it in, we don't need to query the browser to get the size.
  97. // | {w: int, h: int}
  98. var node = this.domNode;
  99. // set margin box size, unless it wasn't specified, in which case use current size
  100. if(changeSize){
  101. dojo.marginBox(node, changeSize);
  102. // set offset of the node
  103. if(changeSize.t){ node.style.top = changeSize.t + "px"; }
  104. if(changeSize.l){ node.style.left = changeSize.l + "px"; }
  105. }
  106. // If either height or width wasn't specified by the user, then query node for it.
  107. // But note that setting the margin box and then immediately querying dimensions may return
  108. // inaccurate results, so try not to depend on it.
  109. var mb = resultSize || {};
  110. dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize
  111. if( !("h" in mb) || !("w" in mb) ){
  112. mb = dojo.mixin(dojo.marginBox(node), mb); // just use dojo.marginBox() to fill in missing values
  113. }
  114. // Compute and save the size of my border box and content box
  115. // (w/out calling dojo.contentBox() since that may fail if size was recently set)
  116. var cs = dojo.getComputedStyle(node);
  117. var me = dojo._getMarginExtents(node, cs);
  118. var be = dojo._getBorderExtents(node, cs);
  119. var bb = (this._borderBox = {
  120. w: mb.w - (me.w + be.w),
  121. h: mb.h - (me.h + be.h)
  122. });
  123. var pe = dojo._getPadExtents(node, cs);
  124. this._contentBox = {
  125. l: dojo._toPixelValue(node, cs.paddingLeft),
  126. t: dojo._toPixelValue(node, cs.paddingTop),
  127. w: bb.w - pe.w,
  128. h: bb.h - pe.h
  129. };
  130. // Callback for widget to adjust size of its children
  131. this.layout();
  132. },
  133. layout: function(){
  134. // summary:
  135. // Widgets override this method to size and position their contents/children.
  136. // When this is called this._contentBox is guaranteed to be set (see resize()).
  137. //
  138. // This is called after startup(), and also when the widget's size has been
  139. // changed.
  140. // tags:
  141. // protected extension
  142. },
  143. _setupChild: function(/*dijit._Widget*/child){
  144. // summary:
  145. // Common setup for initial children and children which are added after startup
  146. // tags:
  147. // protected extension
  148. var cls = this.baseClass + "-child "
  149. + (child.baseClass ? this.baseClass + "-" + child.baseClass : "");
  150. dojo.addClass(child.domNode, cls);
  151. },
  152. addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
  153. // Overrides _Container.addChild() to call _setupChild()
  154. this.inherited(arguments);
  155. if(this._started){
  156. this._setupChild(child);
  157. }
  158. },
  159. removeChild: function(/*dijit._Widget*/ child){
  160. // Overrides _Container.removeChild() to remove class added by _setupChild()
  161. var cls = this.baseClass + "-child"
  162. + (child.baseClass ?
  163. " " + this.baseClass + "-" + child.baseClass : "");
  164. dojo.removeClass(child.domNode, cls);
  165. this.inherited(arguments);
  166. }
  167. }
  168. );
  169. dijit.layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){
  170. // summary:
  171. // Given the margin-box size of a node, return its content box size.
  172. // Functions like dojo.contentBox() but is more reliable since it doesn't have
  173. // to wait for the browser to compute sizes.
  174. var cs = dojo.getComputedStyle(node);
  175. var me = dojo._getMarginExtents(node, cs);
  176. var pb = dojo._getPadBorderExtents(node, cs);
  177. return {
  178. l: dojo._toPixelValue(node, cs.paddingLeft),
  179. t: dojo._toPixelValue(node, cs.paddingTop),
  180. w: mb.w - (me.w + pb.w),
  181. h: mb.h - (me.h + pb.h)
  182. };
  183. };
  184. (function(){
  185. var capitalize = function(word){
  186. return word.substring(0,1).toUpperCase() + word.substring(1);
  187. };
  188. var size = function(widget, dim){
  189. // size the child
  190. var newSize = widget.resize ? widget.resize(dim) : dojo.marginBox(widget.domNode, dim);
  191. // record child's size
  192. if(newSize){
  193. // if the child returned it's new size then use that
  194. dojo.mixin(widget, newSize);
  195. }else{
  196. // otherwise, call marginBox(), but favor our own numbers when we have them.
  197. // the browser lies sometimes
  198. dojo.mixin(widget, dojo.marginBox(widget.domNode));
  199. dojo.mixin(widget, dim);
  200. }
  201. };
  202. dijit.layout.layoutChildren = function(/*DomNode*/ container, /*Object*/ dim, /*Widget[]*/ children,
  203. /*String?*/ changedRegionId, /*Number?*/ changedRegionSize){
  204. // summary
  205. // Layout a bunch of child dom nodes within a parent dom node
  206. // container:
  207. // parent node
  208. // dim:
  209. // {l, t, w, h} object specifying dimensions of container into which to place children
  210. // children:
  211. // an array of Widgets or at least objects containing:
  212. // * domNode: pointer to DOM node to position
  213. // * region or layoutAlign: position to place DOM node
  214. // * resize(): (optional) method to set size of node
  215. // * id: (optional) Id of widgets, referenced from resize object, below.
  216. // changedRegionId:
  217. // If specified, the slider for the region with the specified id has been dragged, and thus
  218. // the region's height or width should be adjusted according to changedRegionSize
  219. // changedRegionSize:
  220. // See changedRegionId.
  221. // copy dim because we are going to modify it
  222. dim = dojo.mixin({}, dim);
  223. dojo.addClass(container, "dijitLayoutContainer");
  224. // Move "client" elements to the end of the array for layout. a11y dictates that the author
  225. // needs to be able to put them in the document in tab-order, but this algorithm requires that
  226. // client be last. TODO: move these lines to LayoutContainer? Unneeded other places I think.
  227. children = dojo.filter(children, function(item){ return item.region != "center" && item.layoutAlign != "client"; })
  228. .concat(dojo.filter(children, function(item){ return item.region == "center" || item.layoutAlign == "client"; }));
  229. // set positions/sizes
  230. dojo.forEach(children, function(child){
  231. var elm = child.domNode,
  232. pos = (child.region || child.layoutAlign);
  233. // set elem to upper left corner of unused space; may move it later
  234. var elmStyle = elm.style;
  235. elmStyle.left = dim.l+"px";
  236. elmStyle.top = dim.t+"px";
  237. elmStyle.position = "absolute";
  238. dojo.addClass(elm, "dijitAlign" + capitalize(pos));
  239. // Size adjustments to make to this child widget
  240. var sizeSetting = {};
  241. // Check for optional size adjustment due to splitter drag (height adjustment for top/bottom align
  242. // panes and width adjustment for left/right align panes.
  243. if(changedRegionId && changedRegionId == child.id){
  244. sizeSetting[child.region == "top" || child.region == "bottom" ? "h" : "w"] = changedRegionSize;
  245. }
  246. // set size && adjust record of remaining space.
  247. // note that setting the width of a <div> may affect its height.
  248. if(pos == "top" || pos == "bottom"){
  249. sizeSetting.w = dim.w;
  250. size(child, sizeSetting);
  251. dim.h -= child.h;
  252. if(pos == "top"){
  253. dim.t += child.h;
  254. }else{
  255. elmStyle.top = dim.t + dim.h + "px";
  256. }
  257. }else if(pos == "left" || pos == "right"){
  258. sizeSetting.h = dim.h;
  259. size(child, sizeSetting);
  260. dim.w -= child.w;
  261. if(pos == "left"){
  262. dim.l += child.w;
  263. }else{
  264. elmStyle.left = dim.l + dim.w + "px";
  265. }
  266. }else if(pos == "client" || pos == "center"){
  267. size(child, dim);
  268. }
  269. });
  270. };
  271. })();
  272. }