001: /*
002: * GWT-Ext Widget Library
003: * Copyright(c) 2007-2008, GWT-Ext.
004: * licensing@gwt-ext.com
005: *
006: * http://www.gwt-ext.com/license
007: */
008: package com.gwtext.client.widgets.layout;
009:
010: import com.google.gwt.core.client.JavaScriptObject;
011:
012: /**
013: * <p>Layout that distributes heights of elements so they take 100% of the
014: * container height.</p>
015: * <p>Height of the child element can be given in pixels (as an integer) or
016: * in percent. All elements with absolute height (i.e. in pixels) always will
017: * have the given height. All "free" space (that is not filled with elements
018: * with 'absolute' height) will be distributed among other elements in
019: * proportion of their height percentage. Elements without 'height' in the
020: * config will take equal portions of the "unallocated" height.</p>
021: * <p>Supports panel collapsing, hiding, removal/addition. </p>
022: * <p>Example usage:</p>
023: * <pre><code>
024: *
025: * Panel panel = new Panel();
026: * panel.setLayout(new RowLayout());
027: *
028: * Panel first = new Panel();
029: * first.setTitle("Height in pixels");
030: * first.setHtml("panel height = 100px");
031: * panel.add(first, new RowLayoutData(100);
032: *
033: * Panel second = new Panel();
034: * second.setTitle("1/2");
035: * second.setHtml("Will take half of remaining height");
036: * panel.add(second, new RowLayoutData("50%"));
037: *
038: * Panel third = new Panel();
039: * third.setTitle("No height 1");
040: * third.setHtml("Panel without given height");
041: * panel.add(third);
042: *
043: * Panel fourth = new Panel();
044: * fourth.setTitle("No height 2");
045: * fourth.setHtml("Another Panel");
046: * panel.add(fourth);
047: *
048: * Viewport viewport = new Viewport(panel);
049: *
050: * </code></pre>
051: *
052: */
053: public class RowLayout extends ContainerLayout {
054:
055: static {
056: init();
057: }
058:
059: protected native JavaScriptObject create(JavaScriptObject config) /*-{
060: return new $wnd.Ext.ux.layout.RowFitLayout(config);
061: }-*/;
062:
063: //author Kirill Hryapin "kx" -- http://extjs.com/forum/showthread.php?t=17116
064: private static native void init()/*-{
065: $wnd.Ext.namespace('Ext.ux.layout');
066:
067:
068:
069: $wnd.Ext.ux.layout.RowFitLayout = $wnd.Ext.extend($wnd.Ext.layout.ContainerLayout, {
070: // private
071: monitorResize: true,
072:
073: // private
074: trackChildEvents: ['collapse', 'expand', 'hide', 'show'],
075:
076: // private
077: renderAll: function(ct, target) {
078: $wnd.Ext.ux.layout.RowFitLayout.superclass.renderAll.apply(this, arguments);
079: // add event listeners on addition/removal of children
080: ct.on('add', this.containerListener);
081: ct.on('remove', this.containerListener);
082: },
083:
084: // private
085: renderItem: function(c, position, target) {
086: $wnd.Ext.ux.layout.RowFitLayout.superclass.renderItem.apply(this, arguments);
087:
088: // add event listeners
089: for (var i=0, n = this.trackChildEvents.length; i < n; i++) {
090: c.on(this.trackChildEvents[i], this.itemListener);
091: }
092: c.animCollapse = false; // looks ugly together with row-fit layout
093:
094: // store some layout-specific calculations
095: c.rowFit = {
096: hasAbsHeight: false, // whether the component has absolute height (in pixels)
097: relHeight: 0, // relative height, in pixels (if applicable)
098: calcRelHeight: 0, // calculated relative height (used when element is resized)
099: calcAbsHeight: 0 // calculated absolute height
100: };
101:
102: // process height config option
103: if (c.height) {
104: // store relative (given in percent) height
105: if ((typeof c.height == "string") && ( c.height.indexOf("%") >= 0)) {
106: c.rowFit.relHeight = parseInt(c.height);
107: }
108: else { // set absolute height
109: var cheight;
110: if (typeof c.height == "string") {
111: cheight = parseInt(c.height);
112: }
113: else {
114: cheight = c.height;
115: }
116: c.setHeight(cheight);
117: c.rowFit.hasAbsHeight = true;
118: }
119: }
120: },
121:
122: // private
123: onLayout: function(ct, target) {
124: $wnd.Ext.ux.layout.RowFitLayout.superclass.onLayout.call(this, ct, target);
125:
126: if (this.container.collapsed || !ct.items || !ct.items.length) { return; }
127:
128: // first loop: determine how many elements with relative height are there,
129: // sums of absolute and relative heights etc.
130: var absHeightSum = 0, // sum of elements' absolute heights
131: relHeightSum = 0, // sum of all percent heights given in children configs
132: relHeightRatio = 1, // "scale" ratio used in case sum <> 100%
133: relHeightElements = [], // array of elements with 'relative' height for the second loop
134: noHeightCount = 0; // number of elements with no height given
135:
136: // Set width for all items.
137: var w = target.getViewSize().width - target.getPadding('lr');
138: for (var i=0, n = ct.items.length; i < n; i++) {
139: var c = ct.items.itemAt(i);
140:
141: if (!c.isVisible()) { continue; }
142:
143: //patch from jay prasad
144: c.setWidth(w);
145:
146: // collapsed panel is treated as an element with absolute height
147: if (c.collapsed) { absHeightSum += c.getFrameHeight(); }
148: // element that has an absolute height
149: else if (c.rowFit.hasAbsHeight) {
150: absHeightSum += c.height;
151: }
152: // 'relative-heighted'
153: else {
154: if (!c.rowFit.relHeight) { noHeightCount++; } // element with no height given
155: else { relHeightSum += c.rowFit.relHeight; }
156: relHeightElements.push(c);
157: }
158: }
159:
160: // if sum of relative heights <> 100% (e.g. error in config or consequence
161: // of collapsing/removing panels), scale 'em so it becomes 100%
162: if (noHeightCount == 0 && relHeightSum != 100) {
163: relHeightRatio = 100 / relHeightSum;
164: }
165:
166: var freeHeight = target.getStyleSize().height - absHeightSum, // "unallocated" height we have
167: absHeightLeft = freeHeight; // track how much free space we have
168:
169: while (relHeightElements.length) {
170: var c = relHeightElements.shift(), // element we're working with
171: relH = c.rowFit.relHeight * relHeightRatio, // height of this element in percent
172: absH = 0; // height in pixels
173:
174: // no height in config
175: if (!relH) {
176: relH = (100 - relHeightSum) / noHeightCount;
177: }
178:
179: // last element takes all remaining space
180: if (!relHeightElements.length) { absH = absHeightLeft; }
181: else { absH = Math.round(freeHeight * relH / 100); }
182:
183: // anyway, height can't be negative
184: if (absH < 0) { absH = 0; }
185:
186: c.rowFit.calcAbsHeight = absH;
187: c.rowFit.calcRelHeight = relH;
188:
189: c.setHeight(absH);
190: absHeightLeft -= absH;
191: }
192: },
193:
194: itemListener: function(item) {
195: item.ownerCt.doLayout();
196: },
197:
198:
199: containerListener: function(ct) {
200: ct.doLayout();
201: }
202:
203: });
204:
205: // Split adapter
206: if ($wnd.Ext.SplitBar.BasicLayoutAdapter) {
207: $wnd.Ext.ux.layout.RowFitLayout.SplitAdapter = function(splitbar) {
208: if (splitbar && splitbar.el.dom.nextSibling) {
209: var next = $wnd.Ext.getCmp( splitbar.el.dom.nextSibling.id ),
210: resized = $wnd.Ext.getCmp(splitbar.resizingEl.id);
211:
212: if (next) {
213: splitbar.maxSize = (resized.height || resized.rowFit.calcAbsHeight) +
214: next.getInnerHeight() - 1; // seems can't set height=0 in IE, "1" works fine
215: }
216: splitbar.minSize = resized.getFrameHeight() + 1;
217: }
218: }
219:
220: $wnd.Ext.extend($wnd.Ext.ux.layout.RowFitLayout.SplitAdapter, $wnd.Ext.SplitBar.BasicLayoutAdapter, {
221:
222: setElementSize: function(splitbar, newSize, onComplete) {
223: var resized = $wnd.Ext.getCmp(splitbar.resizingEl.id);
224:
225: // can't resize absent, collapsed or hidden panel
226: if (!resized || resized.collapsed || !resized.isVisible()) return;
227:
228: // resizingEl has absolute height: just change it
229: if (resized.rowFit.hasAbsHeight) {
230: resized.setHeight(newSize);
231: }
232: // resizingEl has relative height: affects next sibling
233: else {
234: if (splitbar.el.dom.nextSibling) {
235: var nextSibling = $wnd.Ext.getCmp( splitbar.el.dom.nextSibling.id ),
236: deltaAbsHeight = newSize - resized.rowFit.calcAbsHeight, // pixels
237: nsRf = nextSibling.rowFit, // shortcut
238: rzRf = resized.rowFit,
239: // pixels in a percent
240: pctPxRatio = rzRf.calcRelHeight / rzRf.calcAbsHeight,
241: deltaRelHeight = pctPxRatio * deltaAbsHeight; // change in height in percent
242:
243: rzRf.relHeight = rzRf.calcRelHeight + deltaRelHeight;
244:
245: if (nsRf.hasAbsHeight) {
246: var newHeight = nextSibling.height - deltaAbsHeight;
247: nextSibling.height = newHeight;
248: nextSibling.setHeight(newHeight);
249: }
250: else {
251: nsRf.relHeight = nsRf.calcRelHeight - deltaRelHeight;
252: }
253: }
254: }
255: // recalculate heights
256: resized.ownerCt.doLayout();
257: } // of setElementSize
258:
259: }); // of SplitAdapter
260: }
261:
262: $wnd.Ext.Container.LAYOUTS['row-fit'] = $wnd.Ext.ux.layout.RowFitLayout;
263: }-*/;
264: }
|