All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.user.client.ui.HorizontalSplitPanel Maven / Gradle / Ivy

There is a newer version: 2.12.1
Show newest version
/*
 * 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.i18n.client.LocaleInfo;
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 horizontal row and allows the
 * user to interactively change the proportion of the width dedicated to each of
 * the two widgets. Widgets contained within a HorizontalSplitPanel
 * will be automatically decorated with scrollbars when necessary.
 * 
 * 

* This widget will only work in quirks mode. If your application is in * Standards Mode, use {@link SplitLayoutPanel} instead. *

* *

* *

* *

CSS Style Rules

*
    *
  • .gwt-HorizontalSplitPanel { the panel itself }
  • *
  • .gwt-HorizontalSplitPanel hsplitter { the splitter }
  • *
* * @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 HorizontalSplitPanel extends SplitPanel { /** * The default resources used by this widget. */ public interface Resources extends ClientBundle { /** * An image representing the drag thumb. */ @Source("splitPanelThumb.png") ImageResource horizontalSplitPanelThumb(); } /** * The standard implementation for horizontal split panels. */ private static class Impl { private static void expandToFitParentHorizontally(Element elem) { addAbsolutePositoning(elem); final String zeroSize = "0px"; setTop(elem, zeroSize); setBottom(elem, zeroSize); } protected HorizontalSplitPanel panel; public void init(HorizontalSplitPanel panel) { this.panel = panel; panel.getElement().getStyle().setProperty("position", "relative"); expandToFitParentHorizontally(panel.getElement(LEFT)); expandToFitParentHorizontally(panel.getElement(RIGHT)); expandToFitParentHorizontally(panel.getSplitElement()); expandToFitParentUsingCssOffsets(panel.container); // Right now, both panes are stacked on top of each other // on either the left side or the right side of the containing // panel. This happens because both panes have position:absolute // and no left/top values. The panes will be on the left side // if the directionality is LTR, and on the right side if the // directionality is RTL. In the LTR case, we need to snap the // right pane to the right of the container, and in the RTL case, // we need to snap the left pane to the left of the container. if (LocaleInfo.getCurrentLocale().isRTL()) { setLeft(panel.getElement(LEFT), "0px"); } else { setRight(panel.getElement(RIGHT), "0px"); } } public void onAttach() { } public void onDetach() { } public void onSplitResize(int px) { setSplitPositionUsingPixels(px); } public void setSplitPosition(String pos) { final Element leftElem = panel.getElement(LEFT); setWidth(leftElem, pos); setSplitPositionUsingPixels(getOffsetWidth(leftElem)); } /** * Set the splitter's position in units of pixels. * * px represents the splitter's position as a distance of px pixels from the * left edge of the container. This is true even in a bidi environment. * Callers of this method must be aware of this constraint. */ public void setSplitPositionUsingPixels(int px) { final Element splitElem = panel.getSplitElement(); final int rootElemWidth = getOffsetWidth(panel.container); final int splitElemWidth = getOffsetWidth(splitElem); // This represents an invalid state where layout is incomplete. This // typically happens before DOM attachment, but I leave it here as a // precaution because negative width/height style attributes produce // errors on IE. if (rootElemWidth < splitElemWidth) { return; } // Compute the new right side width. int newRightWidth = rootElemWidth - px - splitElemWidth; // Constrain the dragging to the physical size of the panel. if (px < 0) { px = 0; newRightWidth = rootElemWidth - splitElemWidth; } else if (newRightWidth < 0) { px = rootElemWidth - splitElemWidth; newRightWidth = 0; } final Element rightElem = panel.getElement(RIGHT); // Set the width of the left side. setWidth(panel.getElement(LEFT), px + "px"); // Move the splitter to the right edge of the left element. setLeft(splitElem, px + "px"); // Move the right element to the right of the splitter. setLeft(rightElem, (px + splitElemWidth) + "px"); updateRightWidth(rightElem, newRightWidth); } /** * Implemented by subclasses. * * @param rightElem * @param newRightWidth */ public void updateRightWidth(Element rightElem, int newRightWidth) { // No need to update the width of the right side; this will be // recomputed automatically by CSS. This is helpful, as we do not // have to worry about watching for resize events and adjusting the // right-side width. } } /** * The Safari implementation which owes its existence entirely to a single * WebKit bug: http://bugs.webkit.org/show_bug.cgi?id=9137. */ @SuppressWarnings("unused") // will be used by Safari permutation private static class ImplSafari extends Impl { @Override public void init(HorizontalSplitPanel panel) { this.panel = panel; final String fullSize = "100%"; super.init(panel); setHeight(panel.container, fullSize); setHeight(panel.getElement(LEFT), fullSize); setHeight(panel.getElement(RIGHT), fullSize); setHeight(panel.getSplitElement(), fullSize); } } /** * Constant makes for readable calls to {@link #getElement(int)} and * {@link #getWidget(int)}. */ private static final int LEFT = 0; /** * Constant makes for readable calls to {@link #getElement(int)} and * {@link #getWidget(int)}. */ private static final int RIGHT = 1; // A style-free element to serve as the root container. private final Element container; private final Impl impl = GWT.create(Impl.class); /** * If the split position is set while the split panel is not attached, save it * here to be applied when the panel is attached to the document. */ private String lastSplitPosition = "50%"; private int initialThumbPos; private int initialLeftWidth; public HorizontalSplitPanel() { this(GWT. create(Resources.class)); } /** * Creates an empty horizontal split panel. * * @param images ImageBundle containing an image for the splitter's drag thumb * @deprecated replaced by {@link #HorizontalSplitPanel(Resources)} */ @Deprecated public HorizontalSplitPanel(HorizontalSplitPanelImages images) { this(images.horizontalSplitPanelThumb()); } /** * Creates an empty horizontal split panel. * * @param resources ClientBundle containing an image for the splitter's drag * thumb */ public HorizontalSplitPanel(Resources resources) { this(AbstractImagePrototype.create(resources.horizontalSplitPanelThumb())); } private HorizontalSplitPanel(AbstractImagePrototype thumbImage) { super(DOM.createDiv(), DOM.createDiv(), preventBoxStyles(DOM.createDiv()), preventBoxStyles(DOM.createDiv())); container = preventBoxStyles(DOM.createDiv()); buildDOM(thumbImage); setStyleName("gwt-HorizontalSplitPanel"); impl.init(this); // By default, the panel will fill its parent vertically and horizontally. // The horizontal case is covered by the fact that the top level div is // block display. setHeight("100%"); } /** * Adds a widget to a pane in the HorizontalSplitPanel. The method will first * attempt to add the widget to the left pane. If a widget is already in that * position, it will attempt to add the widget to the right pane. If a widget * is already in that position, an exception will be thrown, as a * HorizontalSplitPanel can contain at most two widgets. * * Note that this method is bidi-sensitive. In an RTL environment, this method * will first attempt to add the widget to the right pane, and if a widget is * already in that position, it will attempt to add the widget to the left * pane. * * @param w the widget to be added * @throws IllegalStateException */ @Override public void add(Widget w) { if (getStartOfLineWidget() == null) { setStartOfLineWidget(w); } else if (getEndOfLineWidget() == null) { setEndOfLineWidget(w); } else { throw new IllegalStateException( "A Splitter can only contain two Widgets."); } } /** * Gets the widget in the pane that is at the end of the line direction for * the layout. That is, in an RTL layout, gets the widget in the left pane, * and in an LTR layout, gets the widget in the right pane. * * @return the widget, null if there is not one. */ public Widget getEndOfLineWidget() { return getWidget(getEndOfLinePos()); } /** * Gets the widget in the left side of the panel. * * @return the widget, null if there is not one. */ public Widget getLeftWidget() { return getWidget(LEFT); } /** * Gets the widget in the right side of the panel. * * @return the widget, null if there is not one. */ public Widget getRightWidget() { return getWidget(RIGHT); } /** * Gets the widget in the pane that is at the start of the line direction for * the layout. That is, in an RTL environment, gets the widget in the right * pane, and in an LTR environment, gets the widget in the left pane. * * @return the widget, null if there is not one. */ public Widget getStartOfLineWidget() { return getWidget(getStartOfLinePos()); } /** * Sets the widget in the pane that is at the end of the line direction for * the layout. That is, in an RTL layout, sets the widget in the left pane, * and in and RTL layout, sets the widget in the right pane. * * @param w the widget */ public void setEndOfLineWidget(Widget w) { setWidget(getEndOfLinePos(), w); } /** * Sets the widget in the left side of the panel. * * @param w the widget */ public void setLeftWidget(Widget w) { setWidget(LEFT, w); } /** * Sets the widget in the right side of the panel. * * @param w the widget */ public void setRightWidget(Widget w) { setWidget(RIGHT, w); } /** * Moves the position of the splitter. * * This method is not bidi-sensitive. The size specified is always the size of * the left region, regardless of directionality. * * @param pos the new size of the left region in CSS units (e.g. "10px", * "1em") */ @Override public void setSplitPosition(String pos) { lastSplitPosition = pos; impl.setSplitPosition(pos); } /** * Sets the widget in the pane that is at the start of the line direction for * the layout. That is, in an RTL layout, sets the widget in the right pane, * and in and RTL layout, sets the widget in the left pane. * * @param w the widget */ public void setStartOfLineWidget(Widget w) { setWidget(getStartOfLinePos(), w); } /** * Affected Elements: *
    *
  • -splitter = the container containing the splitter element.
  • *
  • -right = the container on the right side of the splitter.
  • *
  • -left = the container on the left side of the splitter.
  • *
* * @see UIObject#onEnsureDebugId(String) */ @Override protected void onEnsureDebugId(String baseID) { super.onEnsureDebugId(baseID); ensureDebugId(getElement(LEFT), baseID, "left"); ensureDebugId(getElement(RIGHT), baseID, "right"); } @Override protected void onLoad() { impl.onAttach(); /* * If the split position has been changed while detached, apply the change. * Set the position realizing that 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.onSplitResize(initialLeftWidth + x - initialThumbPos); } @Override void onSplitterResizeStarted(int x, int y) { initialThumbPos = x; initialLeftWidth = getOffsetWidth(getElement(LEFT)); } private void buildDOM(AbstractImagePrototype thumbImage) { final Element leftDiv = getElement(LEFT); final Element rightDiv = getElement(RIGHT); final Element splitDiv = getSplitElement(); DOM.appendChild(getElement(), container); DOM.appendChild(container, leftDiv); DOM.appendChild(container, splitDiv); DOM.appendChild(container, rightDiv); /* * Sadly, this is the only way I've found to get vertical centering in this * case. The usually CSS hacks (display: table-cell, vertical-align: middle) * don't work in an absolute positioned DIV. */ SafeHtmlBuilder sb = new SafeHtmlBuilder(); sb.appendHtmlConstant("
"); sb.append(thumbImage.getSafeHtml()); splitDiv.setInnerSafeHtml(sb.toSafeHtml()); addScrolling(leftDiv); addScrolling(rightDiv); } private int getEndOfLinePos() { return (LocaleInfo.getCurrentLocale().isRTL() ? LEFT : RIGHT); } private int getStartOfLinePos() { return (LocaleInfo.getCurrentLocale().isRTL() ? RIGHT : LEFT); } }