KickJava   Java API By Example, From Geeks To Geeks.

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


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 vertical column and allows the
26  * user to interactively change the proportion of the height dedicated to each
27  * of the two widgets. Widgets contained within a
28  * <code>VerticalSplitterPanel</code> will be automatically decorated with
29  * scrollbars when neccessary.
30  *
31  * <p>
32  * <img class='gallery' SRC='VerticalSplitPanel.png'/>
33  * </p>
34  *
35  * <h3>CSS Style Rules</h3>
36  * <ul class='css'>
37  * <li>.gwt-VerticalSplitPanel { the panel itself }</li>
38  * <li>.gwt-VerticalSplitPanel top { the top container }</li>
39  * <li>.gwt-VerticalSplitPanel bottom { the bottom container }</li>
40  * <li>.gwt-VerticalSplitPanel splitter { the splitter }</li>
41  * </ul>
42  */

43 public final class VerticalSplitPanel extends SplitPanel {
44
45   private static final int TOP = 0;
46   private static final int BOTTOM = 1;
47   private static final Impl impl = (Impl) GWT.create(Impl.class);
48
49   /**
50    * Provides different implementations for retrieving an element's height. The
51    * default binding is based on DOM1 clientHeight.
52    */

53   private static class Impl {
54     /**
55      * Gets an element's height.
56      *
57      * @param elem an element
58      * @return the height of the element
59      */

60     protected int getElementHeight(Element elem) {
61       return DOM.getElementPropertyInt(elem, "clientHeight");
62     }
63   }
64
65   /**
66    * Provides an implementation for IE6 based on Element.getBoundingClientRect.
67    */

68   private static class ImplIE6 extends Impl {
69     protected native int getElementHeight(Element elem) /*-{
70       var box = elem.getBoundingClientRect();
71       return box.bottom - box.top;
72     }-*/
;
73   }
74
75   private static int getOffsetTop(Element elem) {
76     return DOM.getElementPropertyInt(elem, "offsetTop");
77   }
78
79   private static Element lockStyles(final Element elem) {
80     DOM.setIntStyleAttribute(elem, "height", 0);
81     return preventElementBoxStyles(elem);
82   }
83
84   private static void setHeight(Element elem, int px) {
85     DOM.setStyleAttribute(elem, "height", Math.max(0, px) + "px");
86   }
87
88   // Element is added below bottom container element to make it possible to
89
// infer the bottom element's height.
90
private final Element probeElem;
91
92   // Captures the height of the top container when drag resizing starts.
93
private int initialTopHeight = 0;
94
95   // Captures the offset of a user's mouse pointer during drag resizing.
96
private int initialThumbPos = 0;
97
98   /**
99    * Creates an empty vertical split panel.
100    */

101   public VerticalSplitPanel() {
102     super(DOM.createDiv(), DOM.createDiv(), DOM.createDiv(), DOM.createDiv());
103
104     final Element thisElem = getElement();
105     final Element splitElem = getSplitElement();
106     final Element topElem = getElement(TOP);
107     final Element bottomElem = getElement(BOTTOM);
108     probeElem = lockStyles(DOM.createDiv());
109
110     DOM.appendChild(thisElem, topElem);
111     DOM.appendChild(thisElem, splitElem);
112     DOM.appendChild(thisElem, bottomElem);
113     DOM.appendChild(thisElem, probeElem);
114
115     addElementClipping(thisElem);
116     addElementScrolling(topElem);
117     addElementScrolling(bottomElem);
118
119     // Prevent padding on container elements.
120
preventElementPadding(thisElem);
121     preventElementPadding(topElem);
122     preventElementPadding(bottomElem);
123
124     setElementClassname(topElem, "top");
125     setElementClassname(splitElem, "splitter");
126     setElementClassname(bottomElem, "bottom");
127
128     setStyleName("gwt-VerticalSplitPanel");
129
130     // Must wait on layout to do the initial layout.
131
DeferredCommand.addCommand(new Command() {
132       public void execute() {
133         updateBottomHeight();
134       }
135     });
136   }
137
138   /**
139    * Gets the widget in the bottom of the panel.
140    *
141    * @return the widget, <code>null</code> if there is not one
142    */

143   public final Widget getBottomWidget() {
144     return getWidget(BOTTOM);
145   }
146
147   /**
148    * Gets the widget in the top of the panel.
149    *
150    * @return the widget, <code>null</code> if there is not one
151    */

152   public final Widget getTopWidget() {
153     return getWidget(TOP);
154   }
155
156   /**
157    * Sets the widget in the bottom of the panel.
158    *
159    * @param w the widget
160    */

161   public final void setBottomWidget(Widget w) {
162     setWidget(BOTTOM, w);
163   }
164
165   public final void setSplitPosition(String JavaDoc size) {
166     DOM.setStyleAttribute(getElement(TOP), "height", size);
167     updateBottomHeight();
168   }
169
170   /**
171    * Sets the widget in the top of the panel.
172    *
173    * @param w the widget
174    */

175   public final void setTopWidget(Widget w) {
176     setWidget(TOP, w);
177   }
178
179   final void onSplitterResize(int x, int y) {
180     /*
181      * When dragging starts we record the thumb position and the current height
182      * of the top div. On each subsequent resize event, we compute how far the
183      * thumb has moved and adjust the top and bottom div by that offset.
184      */

185     final Element topElem = getElement(TOP);
186     final Element botElem = getElement(BOTTOM);
187
188     // Compute what the new top height should be.
189
final int newTopHeight = initialTopHeight + (y - initialThumbPos);
190     final int newBotHeight = impl.getElementHeight(botElem)
191         + impl.getElementHeight(topElem) - newTopHeight;
192
193     /*
194      * NOTE: The bottom must be adjusted before the top due to FF bug which
195      * leaves scrollbar artifacts in the overflow region.
196      * https://bugzilla.mozilla.org/show_bug.cgi?id=368190
197      */

198     if (newBotHeight < 0) {
199       setHeight(botElem, 0);
200       setHeight(topElem, newTopHeight + newBotHeight);
201     } else {
202       setHeight(botElem, newBotHeight);
203       setHeight(topElem, newTopHeight);
204     }
205
206     updateBottomHeight();
207   }
208
209   final void onSplitterResizeStarted(int x, int y) {
210     initialThumbPos = y;
211     initialTopHeight = impl.getElementHeight(getElement(TOP));
212   }
213
214   /**
215    * Updates to the height on the bottom div so that it remains within the outer
216    * container.
217    */

218   private void updateBottomHeight() {
219     final Element thisElem = getElement();
220     final Element bottomElem = getElement(BOTTOM);
221
222     /*
223      * This is the definitive check that tells us how far (in pixels) the height
224      * of the bottom div must change. We do this by comparing the clientHeight
225      * of the root div with the offsetTop of a probe div under the bottom div.
226      */

227     final int adjust = impl.getElementHeight(thisElem)
228         - (getOffsetTop(probeElem) - getOffsetTop(thisElem));
229
230     /*
231      * In the case where the user is dragging the splitter, resizeTopBy should
232      * generally guess the right adjustment based on how far the top div was
233      * adjusted. So for the most common case, we find we do not need adjustment
234      * and exit here.
235      */

236     if (adjust == 0) {
237       return;
238     }
239
240     /*
241      * We don't know what margins and borders are in play on the bottom div, so
242      * we naively guess they are all zero, which would mean that the CSS height
243      * property will be equal to the clientHeight attribute. After we set the
244      * height in css, we take the difference between what we set and the
245      * reported clientHeight. If that is non-zero, it tells us how much to
246      * accomodate for margin, border and what not.
247      */

248     final int curHeight = impl.getElementHeight(bottomElem);
249     final int newHeight = curHeight + adjust;
250     setHeight(bottomElem, newHeight);
251     final int error = impl.getElementHeight(bottomElem) - newHeight;
252
253     if (error == 0) {
254       return;
255     }
256
257     setHeight(bottomElem, newHeight - error);
258   }
259 }
260
Popular Tags