KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > user > client > ui > HorizontalSplitPanel


1 /*
2  * Copyright 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */

16 package com.google.gwt.user.client.ui;
17
18 import com.google.gwt.core.client.GWT;
19 import com.google.gwt.user.client.Command;
20 import com.google.gwt.user.client.DOM;
21 import com.google.gwt.user.client.DeferredCommand;
22 import com.google.gwt.user.client.Element;
23
24 /**
25  * A panel that arranges two widgets in a single horizontal row and allows the
26  * user to interactively change the proportion of the width dedicated to each of
27  * the two widgets. Widgets contained within a <code>HorizontalSplitPanel</code>
28  * will be automatically decorated with scrollbars when necessary.
29  *
30  * <p>
31  * <img class='gallery' SRC='HorizontalSplitPanel.png'/>
32  * </p>
33  *
34  * <h3>CSS Style Rules</h3>
35  * <ul class='css'>
36  * <li>.gwt-HorizontalSplitPanel { the panel itself }</li>
37  * <li>.gwt-HorizontalSplitPanel left { the left container }</li>
38  * <li>.gwt-HorizontalSplitPanel right { the right container }</li>
39  * <li>.gwt-HorizontalSplitPanel splitter { the splitter }</li>
40  * </ul>
41  */

42 public final class HorizontalSplitPanel extends SplitPanel {
43
44   /**
45    * The resizing implementation for standard browsers (anything other than
46    * Safari and IE6/7).
47    */

48   private static class Impl {
49     // The x position of the mouse when drag resizing begins.
50
protected int initialThumbPos = 0;
51
52     // Widths of elements which are needed to do relative drag resizing.
53
protected int initialLeftWidth = 0;
54     protected int initialLeftContentWidth = 0;
55     protected int initialRightContentWidth = 0;
56
57     /**
58      * Initializes css properties on the panels DOM structure.
59      *
60      * @param panel the panel
61      */

62     protected void init(HorizontalSplitPanel panel) {
63       // Ensure that the right side will be aggressive in expanding leftwards
64
setWidth(panel.rightTD, "100%");
65       setWidth(panel.rightDiv, "100%");
66     }
67
68     /**
69      * Called on each mouse move event during drag resizing.
70      *
71      * @param panel the panel
72      * @param pos the current horizontal mouse position relative to the panel
73      */

74     protected void onSplitResize(final HorizontalSplitPanel panel, int pos) {
75       // Compute the distance the splitter must be moved.
76
int offset = pos - initialThumbPos;
77
78       /*
79        * Compute the projected size of the content areas. This is to prevent
80        * out-of-bounds scrolling.
81        */

82       int newLeftContentWidth = initialLeftContentWidth + offset;
83       int newRightContentWidth = initialRightContentWidth - offset;
84
85       if (newLeftContentWidth < 0) {
86         offset -= newLeftContentWidth;
87       }
88
89       if (newRightContentWidth < 0) {
90         offset += newRightContentWidth;
91       }
92
93       // Move the split position by the offset.
94
setSplitPosition(panel, (initialLeftWidth + offset) + "px");
95     }
96
97     /**
98      * Called whenever drag resizing begins.
99      *
100      * @param panel the panel
101      * @param pos the current horizontal mouse position relative to the panel
102      */

103     protected void onSplitResizeStarted(final HorizontalSplitPanel panel,
104         int pos) {
105       initialThumbPos = pos;
106       initialLeftWidth = getOffsetWidth(panel.leftDiv);
107       initialLeftContentWidth = getClientWidth(panel.getElement(LEFT));
108       initialRightContentWidth = getClientWidth(panel.getElement(RIGHT));
109     }
110
111     /**
112      * Sets the horizontal position of the splitter.
113      *
114      * @param panel the panel
115      * @param pos the position as a css length
116      */

117     protected void setSplitPosition(final HorizontalSplitPanel panel,
118         final String JavaDoc pos) {
119       // Save the location of the splitter for future use
120
panel.lastSplitPosition = pos;
121
122       /*
123        * This default impl adjusts the width of the first level div and depends
124        * on the outer table to adjust its cell widths appropriately.
125        */

126       setWidth(panel.leftDiv, pos);
127     }
128   }
129
130   /**
131    * The resizing implementation for IE6/7.
132    */

133   private static class ImplIE6 extends Impl {
134     private static native void addWidthExpression(Element elem) /*-{
135       elem.style.setExpression('width', '"100%"');
136     }-*/
;
137
138     protected void init(final HorizontalSplitPanel panel) {
139       /*
140        * Without fixed table layout, IE will not respected the table width
141        * constraints.
142        */

143       DOM.setStyleAttribute(panel.table, "tableLayout", "fixed");
144
145       addWidthExpression(panel.leftDiv);
146       addWidthExpression(panel.rightDiv);
147       addWidthExpression(panel.getElement(LEFT));
148       addWidthExpression(panel.getElement(RIGHT));
149     }
150
151     protected void onSplitResizeStarted(final HorizontalSplitPanel panel,
152         final int x) {
153       initialThumbPos = x;
154       initialLeftWidth = getOffsetWidth(panel.leftTD);
155       initialLeftContentWidth = getClientWidth(panel.getElement(LEFT));
156       initialRightContentWidth = getClientWidth(panel.getElement(RIGHT));
157     }
158
159     protected void setSplitPosition(final HorizontalSplitPanel panel,
160         final String JavaDoc pos) {
161       // Save the location of the splitter for future use
162
panel.lastSplitPosition = pos;
163       final Element leftTD = panel.leftTD;
164       // adjust the width of the table cell instead of the inner div.
165
setWidth(leftTD, pos);
166     }
167   }
168
169   /**
170    * The resizing implemenation for Safari/WebKit.
171    */

172   private static class ImplSafari extends Impl {
173
174     protected void init(final HorizontalSplitPanel panel) {
175       /*
176        * Without fixed table layout, Safari will not respect the css width on
177        * the table.
178        */

179       DOM.setStyleAttribute(panel.table, "tableLayout", "fixed");
180
181       final String JavaDoc autoProp = "auto";
182       setWidth(panel.leftDiv, autoProp);
183       setWidth(panel.rightDiv, autoProp);
184       setWidth(panel.getElement(LEFT), autoProp);
185       setWidth(panel.getElement(RIGHT), autoProp);
186
187       /*
188        * Safari bug: a width must be set on the table when it is added to the
189        * DOM or else it cannot be set later.
190        */

191       panel.setWidth("100%");
192     }
193
194     protected void setSplitPosition(final HorizontalSplitPanel panel, String JavaDoc pos) {
195       // Save the location of the splitter for future use
196
panel.lastSplitPosition = pos;
197       // Adjust the width of the table cell instead of the inner div.
198
setWidth(panel.leftTD, pos);
199     }
200   }
201
202   /**
203    * Constants to provide more readable calls to {@link #getElement()} and
204    * {@link #getWidget(int)}.
205    */

206   private static final int LEFT = 0;
207   private static final int RIGHT = 1;
208
209   private static final int DEFAULT_SPLITTER_WIDTH = 10;
210
211   private static final int getClientWidth(final Element elem) {
212     return DOM.getElementPropertyInt(elem, "clientWidth");
213   }
214
215   private static final int getOffsetWidth(final Element elem) {
216     return DOM.getElementPropertyInt(elem, "offsetWidth");
217   }
218
219   private static final native int parseInt(String JavaDoc number) /*-{
220    return parseInt(number);
221    }-*/
;
222
223   private static final void setWidth(Element elem, String JavaDoc size) {
224     DOM.setStyleAttribute(elem, "width", size);
225   }
226
227   /**
228    * DOM elements needed to support splitter dragging. The underlying DOM
229    * structure is:
230    *
231    * <pre>
232    * table
233    * td (leftTD)
234    * div (leftDiv)
235    * div (getElement(LEFT))
236    * td (splitter)
237    * td (rightTD)
238    * div (rightDiv)
239    * div (getElement(RIGHT))
240    * </pre>
241    */

242   private final Element table;
243   private final Element leftTD, rightTD;
244   private final Element leftDiv, rightDiv;
245
246   private final Impl impl = (Impl) GWT.create(Impl.class);
247
248   /**
249    * If the split position is set while the split panel is not attached, save it
250    * here to be applied when the panel is attached to the document.
251    */

252   private String JavaDoc lastSplitPosition = "50%";
253
254   /**
255    * Creates an empty horizontal split panel.
256    */

257   public HorizontalSplitPanel() {
258     super(DOM.createTable(), DOM.createTD(), DOM.createDiv(), DOM.createDiv());
259
260     table = getElement();
261     leftDiv = preventElementBoxStyles(DOM.createDiv());
262     rightDiv = preventElementBoxStyles(DOM.createDiv());
263     leftTD = preventElementBoxStyles(DOM.createTD());
264     rightTD = preventElementBoxStyles(DOM.createTD());
265
266     buildDOM();
267
268     setStyleName("gwt-HorizontalSplitPanel");
269
270     impl.init(this);
271   }
272
273   /**
274    * Gets the widget in the left side of the panel.
275    *
276    * @return the widget, <code>null</code> if there is not one.
277    */

278   public final Widget getLeftWidget() {
279     return getWidget(LEFT);
280   }
281
282   /**
283    * Gets the widget in the right side of the panel.
284    *
285    * @return the widget, <code>null</code> if there is not one.
286    */

287   public final Widget getRightWidget() {
288     return getWidget(RIGHT);
289   }
290
291   public final void setHeight(String JavaDoc height) {
292     DOM.setStyleAttribute(getElement(LEFT), "height", height);
293     DOM.setStyleAttribute(getElement(RIGHT), "height", height);
294   }
295
296   /**
297    * Sets the widget in the left side of the panel.
298    *
299    * @param w the widget
300    */

301   public final void setLeftWidget(Widget w) {
302     setWidget(LEFT, w);
303   }
304
305   /**
306    * Sets the widget in the right side of the panel.
307    *
308    * @param w the widget
309    */

310   public final void setRightWidget(Widget w) {
311     setWidget(RIGHT, w);
312   }
313
314   public final void setSplitPosition(String JavaDoc pos) {
315     lastSplitPosition = pos.trim();
316     if (!lastSplitPosition.endsWith("%")) {
317       impl.setSplitPosition(this, pos);
318     } else if (isAttached()) {
319       // We have to convert percentage-based lengths to absolute lengths, but
320
// this can only be done while attached and after a rendering cycle if
321
// the attachment was triggered by a UI event.
322
DeferredCommand.addCommand(new Command() {
323         public void execute() {
324           int percentage = parseInt(lastSplitPosition);
325           impl.setSplitPosition(HorizontalSplitPanel.this,
326               (getOffsetWidth() * (percentage / 100.0)) + "px");
327         }
328       });
329     }
330   }
331
332   protected void onLoad() {
333     // If the split position has been changed while detached, apply the change
334
setSplitPosition(lastSplitPosition);
335   }
336
337   final void onSplitterResize(int x, int y) {
338     impl.onSplitResize(this, x);
339   }
340
341   final void onSplitterResizeStarted(int x, int y) {
342     impl.onSplitResizeStarted(this, x);
343   }
344
345   private void buildDOM() {
346     final Element leftContentDiv = getElement(LEFT);
347     final Element rightContentDiv = getElement(RIGHT);
348
349     final Element tbody = DOM.createTBody();
350     final Element tr = DOM.createTR();
351     final Element splitTD = getSplitElement();
352
353     DOM.appendChild(table, tbody);
354     DOM.appendChild(tbody, tr);
355     DOM.appendChild(tr, leftTD);
356     DOM.appendChild(tr, splitTD);
357     DOM.appendChild(tr, rightTD);
358     DOM.appendChild(leftTD, leftDiv);
359     DOM.appendChild(rightTD, rightDiv);
360     DOM.appendChild(leftDiv, leftContentDiv);
361     DOM.appendChild(rightDiv, rightContentDiv);
362
363     DOM.setInnerHTML(splitTD, "&nbsp;");
364
365     DOM.setElementProperty(table, "cellSpacing", "0");
366     DOM.setElementProperty(table, "cellPadding", "0");
367
368     addElementScrolling(leftContentDiv);
369     addElementScrolling(rightContentDiv);
370
371     setElementClassname(leftContentDiv, "left");
372     setElementClassname(splitTD, "splitter");
373     setElementClassname(rightContentDiv, "right");
374
375     DOM.setStyleAttribute(leftTD, "verticalAlign", "top");
376     DOM.setStyleAttribute(rightTD, "verticalAlign", "top");
377
378     /*
379      * Ensures that the splitter is of reasonable width when no CSS is active on
380      * it, but this value is immediately overridden by CSS values.
381      */

382     DOM.setElementPropertyInt(splitTD, "width", DEFAULT_SPLITTER_WIDTH);
383   }
384 }
385
Popular Tags