blob: 4f72bb7aeeb5287d60418c0bee3e71fe2715c4a2 [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.user.client.ui;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.DOM;
/**
* A panel that arranges two widgets in a single vertical column and allows the
* user to interactively change the proportion of the height dedicated to each
* of the two widgets. Widgets contained within a
* <code>VerticalSplitterPanel</code> will be automatically decorated with
* scrollbars when necessary.
*
* <p>
* This widget will <em>only</em> work in quirks mode. If your application is in
* Standards Mode, use {@link SplitLayoutPanel} instead.
* </p>
*
* <p>
* <img class='gallery' src='doc-files/VerticalSplitPanel.png'/>
* </p>
*
* <h3>CSS Style Rules</h3>
* <ul>
* <li>.gwt-VerticalSplitPanel { the panel itself }</li>
* <li>.gwt-VerticalSplitPanel vsplitter { the splitter }</li>
* </ul>
*
* @deprecated Use {@link SplitLayoutPanel} instead, but understand that it is
* not a drop in replacement for this class. It requires standards
* mode, and is most easily used under a {@link RootLayoutPanel} (as
* opposed to a {@link RootPanel}
*
* @see SplitLayoutPanel
*/
@Deprecated
public final class VerticalSplitPanel extends SplitPanel {
/**
* The default resources used by this widget.
*/
public interface Resources extends ClientBundle {
/**
* An image representing the drag thumb.
*/
@Source("splitPanelThumb.png")
ImageResource verticalSplitPanelThumb();
}
/**
* Provides a base implementation for splitter layout that relies on CSS
* positioned layout.
*/
private static class Impl {
private static void expandToFitParentHorizontally(Element elem) {
addAbsolutePositoning(elem);
elem.getStyle().setProperty("left", "0");
elem.getStyle().setProperty("right", "0");
}
protected VerticalSplitPanel panel;
public void init(VerticalSplitPanel panel) {
this.panel = panel;
panel.getElement().getStyle().setProperty("position", "relative");
final Element topElem = panel.getElement(TOP);
final Element bottomElem = panel.getElement(BOTTOM);
expandToFitParentHorizontally(topElem);
expandToFitParentHorizontally(bottomElem);
expandToFitParentHorizontally(panel.getSplitElement());
expandToFitParentUsingCssOffsets(panel.container);
// Snap the bottom wrapper to the bottom side.
bottomElem.getStyle().setProperty("bottom", "0");
}
public void onAttach() {
}
public void onDetach() {
}
public void onSplitterResize(int px) {
setSplitPosition(px);
}
public void setSplitPosition(int px) {
final Element splitElem = panel.getSplitElement();
final int rootElemHeight = getOffsetHeight(panel.container);
final int splitElemHeight = getOffsetHeight(splitElem);
if (rootElemHeight < splitElemHeight) {
return;
}
int newBottomHeight = rootElemHeight - px - splitElemHeight;
if (px < 0) {
px = 0;
newBottomHeight = rootElemHeight - splitElemHeight;
} else if (newBottomHeight < 0) {
px = rootElemHeight - splitElemHeight;
newBottomHeight = 0;
}
updateElements(panel.getElement(TOP), splitElem,
panel.getElement(BOTTOM), px, px + splitElemHeight, newBottomHeight);
}
/**
* @param topElem
* @param splitElem
* @param bottomElem
* @param topHeight
* @param bottomTop
* @param bottomHeight
*/
protected void updateElements(Element topElem, Element splitElem,
Element bottomElem, int topHeight, int bottomTop, int bottomHeight) {
setHeight(topElem, topHeight + "px");
setTop(splitElem, topHeight + "px");
setTop(bottomElem, bottomTop + "px");
// bottom's height is handled by CSS.
}
}
/**
* Constant makes for readable calls to {@link #getElement(int)} and
* {@link #getWidget(int)}.
*/
private static final int TOP = 0;
/**
* Constant makes for readable calls to {@link #getElement(int)} and
* {@link #getWidget(int)}.
*/
private static final int BOTTOM = 1;
// Captures the height of the top container when drag resizing starts.
private int initialTopHeight = 0;
// Captures the offset of a user's mouse pointer during drag resizing.
private int initialThumbPos = 0;
// A style-free element to serve as the root container.
private final Element container;
private final Impl impl = GWT.create(Impl.class);
private String lastSplitPosition;
public VerticalSplitPanel() {
this(GWT.<Resources> create(Resources.class));
}
/**
* Creates an empty vertical split panel.
* @deprecated replaced by {@link #VerticalSplitPanel(Resources)}
*/
@Deprecated
public VerticalSplitPanel(VerticalSplitPanelImages images) {
this(images.verticalSplitPanelThumb());
}
public VerticalSplitPanel(Resources resources) {
this(AbstractImagePrototype.create(resources.verticalSplitPanelThumb()));
}
private VerticalSplitPanel(AbstractImagePrototype thumbImage) {
super(DOM.createDiv(), DOM.createDiv(), preventBoxStyles(DOM.createDiv()),
preventBoxStyles(DOM.createDiv()));
container = preventBoxStyles(DOM.createDiv());
buildDOM(thumbImage);
setStyleName("gwt-VerticalSplitPanel");
impl.init(this);
setSplitPosition("50%");
}
/**
* Gets the widget in the bottom of the panel.
*
* @return the widget, <code>null</code> if there is not one
*/
public Widget getBottomWidget() {
return getWidget(BOTTOM);
}
/**
* Gets the widget in the top of the panel.
*
* @return the widget, <code>null</code> if there is not one
*/
public Widget getTopWidget() {
return getWidget(TOP);
}
/**
* Sets the widget in the bottom of the panel.
*
* @param w the widget
*/
public void setBottomWidget(Widget w) {
setWidget(BOTTOM, w);
}
@Override
public void setHeight(String height) {
super.setHeight(height);
}
@Override
public void setSplitPosition(String pos) {
lastSplitPosition = pos;
final Element topElem = getElement(TOP);
setHeight(topElem, pos);
impl.setSplitPosition(getOffsetHeight(topElem));
}
/**
* Sets the widget in the top of the panel.
*
* @param w the widget
*/
public void setTopWidget(Widget w) {
setWidget(TOP, w);
}
/**
* <b>Affected Elements:</b>
* <ul>
* <li>-splitter = the container containing the splitter element.</li>
* <li>-top = the container above the splitter.</li>
* <li>-bottom = the container below the splitter.</li>
* </ul>
*
* @see UIObject#onEnsureDebugId(String)
*/
@Override
protected void onEnsureDebugId(String baseID) {
super.onEnsureDebugId(baseID);
ensureDebugId(getElement(TOP), baseID, "top");
ensureDebugId(getElement(BOTTOM), baseID, "bottom");
}
@Override
protected void onLoad() {
impl.onAttach();
/*
* Set the position realizing it might not work until after layout runs.
* This first call is simply to try to avoid a jitter effect if possible.
*/
setSplitPosition(lastSplitPosition);
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
public void execute() {
setSplitPosition(lastSplitPosition);
}
});
}
@Override
protected void onUnload() {
impl.onDetach();
}
@Override
void onSplitterResize(int x, int y) {
impl.onSplitterResize(initialTopHeight + y - initialThumbPos);
}
@Override
void onSplitterResizeStarted(int x, int y) {
initialThumbPos = y;
initialTopHeight = getOffsetHeight(getElement(TOP));
}
private void buildDOM(AbstractImagePrototype thumb) {
final Element topDiv = getElement(TOP);
final Element bottomDiv = getElement(BOTTOM);
final Element splitDiv = getSplitElement();
DOM.appendChild(getElement(), container);
DOM.appendChild(container, topDiv);
DOM.appendChild(container, splitDiv);
DOM.appendChild(container, bottomDiv);
/*
* The style name is placed on the table rather than splitElem to allow the
* splitter to be styled without interfering with layout.
*/
SafeHtmlBuilder sb = new SafeHtmlBuilder();
sb.appendHtmlConstant("<div class='vsplitter' style='text-align:center;'>");
sb.append(thumb.getSafeHtml());
sb.appendHtmlConstant("</div>");
splitDiv.setInnerSafeHtml(sb.toSafeHtml());
addScrolling(topDiv);
addScrolling(bottomDiv);
}
}