123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710 |
- /*
- * Copyright 2000-2014 Vaadin Ltd.
- *
- * 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.vaadin.client.ui.orderedlayout;
-
- import java.util.List;
-
- 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.dom.client.Style.Unit;
- import com.google.gwt.user.client.ui.Widget;
- import com.vaadin.client.ApplicationConnection;
- import com.vaadin.client.ComponentConnector;
- import com.vaadin.client.ConnectorHierarchyChangeEvent;
- import com.vaadin.client.LayoutManager;
- import com.vaadin.client.Profiler;
- import com.vaadin.client.ServerConnector;
- import com.vaadin.client.TooltipInfo;
- import com.vaadin.client.Util;
- import com.vaadin.client.WidgetUtil;
- import com.vaadin.client.communication.StateChangeEvent;
- import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
- import com.vaadin.client.ui.AbstractFieldConnector;
- import com.vaadin.client.ui.AbstractLayoutConnector;
- import com.vaadin.client.ui.Icon;
- import com.vaadin.client.ui.LayoutClickEventHandler;
- import com.vaadin.client.ui.aria.AriaHelper;
- import com.vaadin.client.ui.layout.ElementResizeEvent;
- import com.vaadin.client.ui.layout.ElementResizeListener;
- import com.vaadin.shared.AbstractFieldState;
- import com.vaadin.shared.ComponentConstants;
- import com.vaadin.shared.communication.URLReference;
- import com.vaadin.shared.ui.AlignmentInfo;
- import com.vaadin.shared.ui.LayoutClickRpc;
- import com.vaadin.shared.ui.MarginInfo;
- import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutServerRpc;
- import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutState;
-
- /**
- * Base class for vertical and horizontal ordered layouts
- */
- public abstract class AbstractOrderedLayoutConnector extends
- AbstractLayoutConnector {
-
- /*
- * Handlers & Listeners
- */
-
- private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
- this) {
-
- @Override
- protected ComponentConnector getChildComponent(
- com.google.gwt.user.client.Element element) {
- return Util.getConnectorForElement(getConnection(), getWidget(),
- element);
- }
-
- @Override
- protected LayoutClickRpc getLayoutClickRPC() {
- return getRpcProxy(AbstractOrderedLayoutServerRpc.class);
- }
- };
-
- private StateChangeHandler childStateChangeHandler = new StateChangeHandler() {
- @Override
- public void onStateChanged(StateChangeEvent stateChangeEvent) {
- // Child state has changed, update stuff it hasn't already been done
- updateInternalState();
-
- /*
- * Some changes must always be done after each child's own state
- * change handler has been run because it might have changed some
- * styles that are overridden here.
- */
- ServerConnector child = stateChangeEvent.getConnector();
- if (child instanceof ComponentConnector) {
- ComponentConnector component = (ComponentConnector) child;
- Slot slot = getWidget().getSlot(component.getWidget());
-
- slot.setRelativeWidth(component.isRelativeWidth());
- slot.setRelativeHeight(component.isRelativeHeight());
- }
- }
- };
-
- private ElementResizeListener slotCaptionResizeListener = new ElementResizeListener() {
- @Override
- public void onElementResize(ElementResizeEvent e) {
-
- // Get all needed element references
- Element captionElement = e.getElement();
-
- // Caption position determines if the widget element is the first or
- // last child inside the caption wrap
- CaptionPosition pos = getWidget().getCaptionPositionFromElement(
- captionElement.getParentElement());
-
- // The default is the last child
- Element widgetElement = captionElement.getParentElement()
- .getLastChild().cast();
-
- // ...but if caption position is bottom or right, the widget is the
- // first child
- if (pos == CaptionPosition.BOTTOM || pos == CaptionPosition.RIGHT) {
- widgetElement = captionElement.getParentElement()
- .getFirstChildElement().cast();
- }
-
- if (captionElement == widgetElement) {
- // Caption element already detached
- Slot slot = getWidget().getSlot(widgetElement);
- if (slot != null) {
- slot.setCaptionResizeListener(null);
- }
- return;
- }
-
- String widgetWidth = widgetElement.getStyle().getWidth();
- String widgetHeight = widgetElement.getStyle().getHeight();
-
- if (widgetHeight.endsWith("%")
- && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
- getWidget().updateCaptionOffset(captionElement);
- } else if (widgetWidth.endsWith("%")
- && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) {
- getWidget().updateCaptionOffset(captionElement);
- }
-
- updateLayoutHeight();
-
- if (needsExpand()) {
- getWidget().updateExpandCompensation();
- }
- }
- };
-
- private ElementResizeListener childComponentResizeListener = new ElementResizeListener() {
- @Override
- public void onElementResize(ElementResizeEvent e) {
- updateLayoutHeight();
- if (needsExpand()) {
- getWidget().updateExpandCompensation();
- }
- }
- };
-
- private ElementResizeListener spacingResizeListener = new ElementResizeListener() {
- @Override
- public void onElementResize(ElementResizeEvent e) {
- if (needsExpand()) {
- getWidget().updateExpandCompensation();
- }
- }
- };
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.client.ui.AbstractComponentConnector#init()
- */
- @Override
- public void init() {
- super.init();
- getWidget().setLayoutManager(getLayoutManager());
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.client.ui.AbstractLayoutConnector#getState()
- */
- @Override
- public AbstractOrderedLayoutState getState() {
- return (AbstractOrderedLayoutState) super.getState();
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.client.ui.AbstractComponentConnector#getWidget()
- */
- @Override
- public VAbstractOrderedLayout getWidget() {
- return (VAbstractOrderedLayout) super.getWidget();
- }
-
- /**
- * Keep track of whether any child has relative height. Used to determine
- * whether measurements are needed to make relative child heights work
- * together with undefined container height.
- */
- private boolean hasChildrenWithRelativeHeight = false;
-
- /**
- * Keep track of whether any child has relative width. Used to determine
- * whether measurements are needed to make relative child widths work
- * together with undefined container width.
- */
- private boolean hasChildrenWithRelativeWidth = false;
-
- /**
- * Keep track of whether any child is middle aligned. Used to determine if
- * measurements are needed to make middle aligned children work.
- */
- private boolean hasChildrenWithMiddleAlignment = false;
-
- /**
- * Keeps track of whether slots should be expanded based on available space.
- */
- private boolean needsExpand = false;
-
- /**
- * The id of the previous response for which state changes have been
- * processed. If this is the same as the
- * {@link ApplicationConnection#getLastSeenServerSyncId()}, it means that we can
- * skip some quite expensive calculations because we know that the state
- * hasn't changed since the last time the values were calculated.
- */
- private int processedResponseId = -1;
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.client.HasComponentsConnector#updateCaption(com.vaadin
- * .client.ComponentConnector)
- */
- @Override
- public void updateCaption(ComponentConnector connector) {
- /*
- * Don't directly update captions here to avoid calling e.g.
- * updateLayoutHeight() before everything is initialized.
- * updateInternalState() will ensure all captions are updated when
- * appropriate.
- */
- updateInternalState();
- }
-
- private void updateCaptionInternal(ComponentConnector child) {
- Slot slot = getWidget().getSlot(child.getWidget());
-
- String caption = child.getState().caption;
- URLReference iconUrl = child.getState().resources
- .get(ComponentConstants.ICON_RESOURCE);
- String iconUrlString = iconUrl != null ? iconUrl.getURL() : null;
- Icon icon = child.getConnection().getIcon(iconUrlString);
-
- List<String> styles = child.getState().styles;
- String error = child.getState().errorMessage;
- boolean showError = error != null;
- if (child.getState() instanceof AbstractFieldState) {
- AbstractFieldState abstractFieldState = (AbstractFieldState) child
- .getState();
- showError = showError && !abstractFieldState.hideErrors;
- }
- boolean required = false;
- if (child instanceof AbstractFieldConnector) {
- required = ((AbstractFieldConnector) child).isRequired();
- }
- boolean enabled = child.isEnabled();
-
- if (slot.hasCaption() && null == caption) {
- slot.setCaptionResizeListener(null);
- }
-
- slot.setCaption(caption, icon, styles, error, showError, required,
- enabled, child.getState().captionAsHtml);
-
- AriaHelper.handleInputRequired(child.getWidget(), required);
- AriaHelper.handleInputInvalid(child.getWidget(), showError);
- AriaHelper.bindCaption(child.getWidget(), slot.getCaptionElement());
-
- if (slot.hasCaption()) {
- CaptionPosition pos = slot.getCaptionPosition();
- slot.setCaptionResizeListener(slotCaptionResizeListener);
- if (child.isRelativeHeight()
- && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
- getWidget().updateCaptionOffset(slot.getCaptionElement());
- } else if (child.isRelativeWidth()
- && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) {
- getWidget().updateCaptionOffset(slot.getCaptionElement());
- }
- }
-
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.client.ui.AbstractComponentContainerConnector#
- * onConnectorHierarchyChange
- * (com.vaadin.client.ConnectorHierarchyChangeEvent)
- */
- @Override
- public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
- Profiler.enter("AOLC.onConnectorHierarchyChange");
-
- List<ComponentConnector> previousChildren = event.getOldChildren();
- int currentIndex = 0;
- VAbstractOrderedLayout layout = getWidget();
-
- // remove spacing as it is exists as separate elements that cannot be
- // removed easily after reordering the contents
- Profiler.enter("AOLC.onConnectorHierarchyChange temporarily remove spacing");
- layout.setSpacing(false);
- Profiler.leave("AOLC.onConnectorHierarchyChange temporarily remove spacing");
-
- for (ComponentConnector child : getChildComponents()) {
- Profiler.enter("AOLC.onConnectorHierarchyChange add children");
- Slot slot = layout.getSlot(child.getWidget());
- if (slot.getParent() != layout) {
- Profiler.enter("AOLC.onConnectorHierarchyChange add state change handler");
- child.addStateChangeHandler(childStateChangeHandler);
- Profiler.leave("AOLC.onConnectorHierarchyChange add state change handler");
- }
- Profiler.enter("AOLC.onConnectorHierarchyChange addOrMoveSlot");
- layout.addOrMoveSlot(slot, currentIndex++, false);
- Profiler.leave("AOLC.onConnectorHierarchyChange addOrMoveSlot");
-
- Profiler.leave("AOLC.onConnectorHierarchyChange add children");
- }
-
- // re-add spacing for the elements that should have it
- Profiler.enter("AOLC.onConnectorHierarchyChange setSpacing");
- // spacings were removed above
- if (getState().spacing) {
- layout.setSpacing(true);
- }
- Profiler.leave("AOLC.onConnectorHierarchyChange setSpacing");
-
- for (ComponentConnector child : previousChildren) {
- Profiler.enter("AOLC.onConnectorHierarchyChange remove children");
- if (child.getParent() != this) {
- Slot slot = layout.getSlot(child.getWidget());
- slot.setWidgetResizeListener(null);
- if (slot.hasCaption()) {
- slot.setCaptionResizeListener(null);
- }
- slot.setSpacingResizeListener(null);
- child.removeStateChangeHandler(childStateChangeHandler);
- layout.removeWidget(child.getWidget());
- }
- Profiler.leave("AOLC.onConnectorHierarchyChange remove children");
- }
- Profiler.leave("AOLC.onConnectorHierarchyChange");
-
- updateInternalState();
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * com.vaadin.client.ui.AbstractComponentConnector#onStateChanged(com.vaadin
- * .client.communication.StateChangeEvent)
- */
- @Override
- public void onStateChanged(StateChangeEvent stateChangeEvent) {
- super.onStateChanged(stateChangeEvent);
-
- clickEventHandler.handleEventHandlerRegistration();
- getWidget().setMargin(new MarginInfo(getState().marginsBitmask));
- getWidget().setSpacing(getState().spacing);
-
- updateInternalState();
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * com.vaadin.client.ui.AbstractComponentConnector#getTooltipInfo(com.google
- * .gwt.dom.client.Element)
- */
- @Override
- public TooltipInfo getTooltipInfo(com.google.gwt.dom.client.Element element) {
- if (element != getWidget().getElement()) {
- Slot slot = WidgetUtil.findWidget(element, Slot.class);
- if (slot != null && slot.getCaptionElement() != null
- && slot.getParent() == getWidget()
- && slot.getCaptionElement().isOrHasChild(element)) {
- ComponentConnector connector = Util.findConnectorFor(slot
- .getWidget());
- if (connector != null) {
- return connector.getTooltipInfo(element);
- }
- }
- }
- return super.getTooltipInfo(element);
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.client.ui.AbstractComponentConnector#hasTooltip()
- */
- @Override
- public boolean hasTooltip() {
- /*
- * Tooltips are fetched from child connectors -> there's no quick way of
- * checking whether there might a tooltip hiding somewhere
- */
- return true;
- }
-
- /**
- * Updates DOM properties and listeners based on the current state of this
- * layout and its children.
- */
- private void updateInternalState() {
- // Avoid updating again for the same data
- int lastResponseId = getConnection().getLastSeenServerSyncId();
- if (processedResponseId == lastResponseId) {
- return;
- }
- Profiler.enter("AOLC.updateInternalState");
- // Remember that everything is updated for this response
- processedResponseId = lastResponseId;
-
- hasChildrenWithRelativeHeight = false;
- hasChildrenWithRelativeWidth = false;
-
- hasChildrenWithMiddleAlignment = false;
-
- needsExpand = getWidget().vertical ? !isUndefinedHeight()
- : !isUndefinedWidth();
-
- boolean onlyZeroExpands = true;
- if (needsExpand) {
- for (ComponentConnector child : getChildComponents()) {
- double expandRatio = getState().childData.get(child).expandRatio;
- if (expandRatio != 0) {
- onlyZeroExpands = false;
- break;
- }
- }
- }
-
- // First update bookkeeping for all children
- for (ComponentConnector child : getChildComponents()) {
- Slot slot = getWidget().getSlot(child.getWidget());
-
- slot.setRelativeWidth(child.isRelativeWidth());
- slot.setRelativeHeight(child.isRelativeHeight());
-
- if (child.delegateCaptionHandling()) {
- updateCaptionInternal(child);
- }
-
- // Update slot style names
- List<String> childStyles = child.getState().styles;
- if (childStyles == null) {
- getWidget().setSlotStyleNames(child.getWidget(),
- (String[]) null);
- } else {
- getWidget().setSlotStyleNames(child.getWidget(),
- childStyles.toArray(new String[childStyles.size()]));
- }
-
- AlignmentInfo alignment = new AlignmentInfo(
- getState().childData.get(child).alignmentBitmask);
- slot.setAlignment(alignment);
-
- if (alignment.isVerticalCenter()) {
- hasChildrenWithMiddleAlignment = true;
- }
-
- double expandRatio = onlyZeroExpands ? 1 : getState().childData
- .get(child).expandRatio;
-
- slot.setExpandRatio(expandRatio);
-
- if (child.isRelativeHeight()) {
- hasChildrenWithRelativeHeight = true;
- }
- if (child.isRelativeWidth()) {
- hasChildrenWithRelativeWidth = true;
- }
- }
-
- if (needsFixedHeight()) {
- // Add resize listener to ensure the widget itself is measured
- getLayoutManager().addElementResizeListener(
- getWidget().getElement(), childComponentResizeListener);
- } else {
- getLayoutManager().removeElementResizeListener(
- getWidget().getElement(), childComponentResizeListener);
- }
-
- // Then update listeners based on bookkeeping
- updateAllSlotListeners();
-
- // Update the layout at this point to ensure it's OK even if we get no
- // element resize events
- updateLayoutHeight();
- if (needsExpand()) {
- getWidget().updateExpandedSizes();
- // updateExpandedSizes causes fixed size components to temporarily
- // lose their size. updateExpandCompensation must be delayed until
- // the browser has a chance to measure them.
- Scheduler.get().scheduleFinally(new ScheduledCommand() {
- @Override
- public void execute() {
- getWidget().updateExpandCompensation();
- }
- });
- } else {
- getWidget().clearExpand();
- }
-
- Profiler.leave("AOLC.updateInternalState");
- }
-
- /**
- * Does the layout need a fixed height?
- */
- private boolean needsFixedHeight() {
- boolean isVertical = getWidget().vertical;
-
- if (isVertical) {
- // Doesn't need height fix for vertical layouts
- return false;
- }
-
- else if (!isUndefinedHeight()) {
- // Fix not needed unless the height is undefined
- return false;
- }
-
- else if (!hasChildrenWithRelativeHeight
- && !hasChildrenWithMiddleAlignment) {
- // Already works if there are no relative heights or middle aligned
- // children
- return false;
- }
-
- return true;
- }
-
- /**
- * Does the layout need to expand?
- */
- private boolean needsExpand() {
- return needsExpand;
- }
-
- /**
- * Add slot listeners
- */
- private void updateAllSlotListeners() {
- for (ComponentConnector child : getChildComponents()) {
- updateSlotListeners(child);
- }
- }
-
- /**
- * Add/remove necessary ElementResizeListeners for one slot. This should be
- * called after each update to the slot's or it's widget.
- */
- private void updateSlotListeners(ComponentConnector child) {
- Slot slot = getWidget().getSlot(child.getWidget());
-
- // Clear all possible listeners first
- slot.setWidgetResizeListener(null);
- if (slot.hasCaption()) {
- slot.setCaptionResizeListener(null);
- }
- if (slot.hasSpacing()) {
- slot.setSpacingResizeListener(null);
- }
-
- // Add all necessary listeners
- if (needsFixedHeight()) {
- slot.setWidgetResizeListener(childComponentResizeListener);
- if (slot.hasCaption()) {
- slot.setCaptionResizeListener(slotCaptionResizeListener);
- }
- } else if ((hasChildrenWithRelativeHeight || hasChildrenWithRelativeWidth)
- && slot.hasCaption()) {
- /*
- * If the slot has caption, we need to listen for its size changes
- * in order to update the padding/margin offset for relative sized
- * components.
- *
- * TODO might only be needed if the caption is in the same direction
- * as the relative size?
- */
- slot.setCaptionResizeListener(slotCaptionResizeListener);
- }
-
- if (needsExpand()) {
- // TODO widget resize only be needed for children without expand?
- slot.setWidgetResizeListener(childComponentResizeListener);
- if (slot.hasSpacing()) {
- slot.setSpacingResizeListener(spacingResizeListener);
- }
- }
- }
-
- /**
- * Re-calculate the layout height
- */
- private void updateLayoutHeight() {
- if (needsFixedHeight()) {
- int h = getMaxHeight();
- if (h < 0) {
- // Postpone change if there are elements that have not yet been
- // measured
- return;
- }
- h += getLayoutManager().getBorderHeight(getWidget().getElement())
- + getLayoutManager().getPaddingHeight(
- getWidget().getElement());
- getWidget().getElement().getStyle().setHeight(h, Unit.PX);
- getLayoutManager().setNeedsMeasure(this);
- }
- }
-
- /**
- * Measures the maximum height of the layout in pixels
- */
- private int getMaxHeight() {
- int highestNonRelative = -1;
- int highestRelative = -1;
-
- LayoutManager layoutManager = getLayoutManager();
-
- for (ComponentConnector child : getChildComponents()) {
- Widget childWidget = child.getWidget();
- Slot slot = getWidget().getSlot(childWidget);
- Element captionElement = slot.getCaptionElement();
- CaptionPosition captionPosition = slot.getCaptionPosition();
-
- int pixelHeight = layoutManager.getOuterHeight(childWidget
- .getElement());
- if (pixelHeight == -1) {
- // Height has not yet been measured -> postpone actions that
- // depend on the max height
- return -1;
- }
-
- boolean hasRelativeHeight = slot.hasRelativeHeight();
-
- boolean captionSizeShouldBeAddedtoComponentHeight = captionPosition == CaptionPosition.TOP
- || captionPosition == CaptionPosition.BOTTOM;
- boolean includeCaptionHeight = captionElement != null
- && captionSizeShouldBeAddedtoComponentHeight;
-
- if (includeCaptionHeight) {
- int captionHeight = layoutManager
- .getOuterHeight(captionElement)
- - getLayoutManager().getMarginHeight(captionElement);
- if (captionHeight == -1) {
- // Height has not yet been measured -> postpone actions that
- // depend on the max height
- return -1;
- }
- pixelHeight += captionHeight;
- }
-
- if (!hasRelativeHeight) {
- if (pixelHeight > highestNonRelative) {
- highestNonRelative = pixelHeight;
- }
- } else {
- if (pixelHeight > highestRelative) {
- highestRelative = pixelHeight;
- }
- }
- }
- return highestNonRelative > -1 ? highestNonRelative : highestRelative;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.client.ui.AbstractComponentConnector#onUnregister()
- */
- @Override
- public void onUnregister() {
- // Cleanup all ElementResizeListeners
- for (ComponentConnector child : getChildComponents()) {
- Slot slot = getWidget().getSlot(child.getWidget());
- if (slot.hasCaption()) {
- slot.setCaptionResizeListener(null);
- }
-
- if (slot.getSpacingElement() != null) {
- slot.setSpacingResizeListener(null);
- }
-
- slot.setWidgetResizeListener(null);
- }
-
- super.onUnregister();
- }
- }
|