com.sencha.gxt.widget.core.client.ContentPanel.java Source code

Java tutorial

Introduction

Here is the source code for com.sencha.gxt.widget.core.client.ContentPanel.java

Source

/**
 * Sencha GXT 4.0.0 - Sencha for GWT
 * Copyright (c) 2006-2015, Sencha Inc.
 *
 * licensing@sencha.com
 * http://www.sencha.com/products/gxt/license/
 *
 * ================================================================================
 * Open Source License
 * ================================================================================
 * This version of Sencha GXT is licensed under the terms of the Open Source GPL v3
 * license. You may use this license only if you are prepared to distribute and
 * share the source code of your application under the GPL v3 license:
 * http://www.gnu.org/licenses/gpl.html
 *
 * If you are NOT prepared to distribute and share the source code of your
 * application under the GPL v3 license, other commercial and oem licenses
 * are available for an alternate download of Sencha GXT.
 *
 * Please see the Sencha GXT Licensing page at:
 * http://www.sencha.com/products/gxt/license/
 *
 * For clarification or additional options, please contact:
 * licensing@sencha.com
 * ================================================================================
 *
 *
 * ================================================================================
 * Disclaimer
 * ================================================================================
 * THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND
 * REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
 * IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY,
 * FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND
 * THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING.
 * ================================================================================
 */
package com.sencha.gxt.widget.core.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Cursor;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.uibinder.client.UiChild;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.core.client.Style.Direction;
import com.sencha.gxt.core.client.Style.Side;
import com.sencha.gxt.core.client.dom.XDOM;
import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.core.client.util.Size;
import com.sencha.gxt.fx.client.animation.AfterAnimateEvent;
import com.sencha.gxt.fx.client.animation.AfterAnimateEvent.AfterAnimateHandler;
import com.sencha.gxt.fx.client.animation.Fx;
import com.sencha.gxt.fx.client.animation.SlideIn;
import com.sencha.gxt.fx.client.animation.SlideOut;
import com.sencha.gxt.messages.client.DefaultMessages;
import com.sencha.gxt.widget.core.client.Header.HeaderAppearance;
import com.sencha.gxt.widget.core.client.button.ButtonBar;
import com.sencha.gxt.widget.core.client.button.IconButton.IconConfig;
import com.sencha.gxt.widget.core.client.button.ToolButton;
import com.sencha.gxt.widget.core.client.container.BoxLayoutContainer.BoxLayoutPack;
import com.sencha.gxt.widget.core.client.container.SimpleContainer;
import com.sencha.gxt.widget.core.client.event.BeforeCollapseEvent;
import com.sencha.gxt.widget.core.client.event.BeforeCollapseEvent.BeforeCollapseHandler;
import com.sencha.gxt.widget.core.client.event.BeforeCollapseEvent.HasBeforeCollapseHandlers;
import com.sencha.gxt.widget.core.client.event.BeforeExpandEvent;
import com.sencha.gxt.widget.core.client.event.BeforeExpandEvent.BeforeExpandHandler;
import com.sencha.gxt.widget.core.client.event.BeforeExpandEvent.HasBeforeExpandHandlers;
import com.sencha.gxt.widget.core.client.event.CollapseEvent;
import com.sencha.gxt.widget.core.client.event.CollapseEvent.CollapseHandler;
import com.sencha.gxt.widget.core.client.event.CollapseEvent.HasCollapseHandlers;
import com.sencha.gxt.widget.core.client.event.ExpandEvent;
import com.sencha.gxt.widget.core.client.event.ExpandEvent.ExpandHandler;
import com.sencha.gxt.widget.core.client.event.ExpandEvent.HasExpandHandlers;
import com.sencha.gxt.widget.core.client.event.SelectEvent;
import com.sencha.gxt.widget.core.client.event.SelectEvent.SelectHandler;

/**
 * ContentPanel is a component container that has specific functionality and structural components that make it the
 * perfect building block for application-oriented user interfaces. A content panel contains separate header, footer and
 * body sections. The header may contain an icon, text and a tool area that can be wired up to provide customized
 * behavior. The footer contains buttons added using {@link #addButton(Widget)}. The body contains a single widget,
 * added using {@link #add}. The widget is resized to match the size of the container. A content panel provides built-in
 * expandable and collapsible behavior.
 *
 * Code snippet:
 *
 * <pre>
 * public void onModuleLoad() {
 *   ContentPanel cp = new ContentPanel();
 *   cp.setHeading("Content Panel");
 *   cp.setPixelSize(250, 140);
 *   cp.setPosition(10, 10);
 *   cp.setCollapsible(true);
 *   cp.addTool(new ToolButton(ToolButton.GEAR));
 *   cp.addTool(new ToolButton(ToolButton.CLOSE));
 *   cp.setWidget(new HTML("This is an HTML Widget in a ContentPanel."));
 *   cp.addButton(new TextButton("Ok"));
 *   RootPanel.get().add(cp);
 * }
 * </pre>
 */
public class ContentPanel extends SimpleContainer implements HasBeforeExpandHandlers, HasExpandHandlers,
        HasBeforeCollapseHandlers, HasCollapseHandlers, Collapsible {

    private final ContentPanelAppearance appearance;
    protected Header header;
    protected ButtonBar buttonBar;
    protected boolean secondPassRequired;
    private ContentPanelMessages messages;
    private boolean animating;
    private boolean animCollapse = true;
    private int animationDuration = 500;
    private ToolButton collapseBtn;
    private boolean collapsed, hideCollapseTool;
    private boolean collapsible;
    private boolean titleCollapse;
    private boolean headerVisible = true;
    private boolean layoutOnExpand;

    /**
     * Creates a content panel with default appearance.
     */
    public ContentPanel() {
        this((ContentPanelAppearance) GWT.create(ContentPanelAppearance.class));
    }

    /**
     * Creates a content panel with the specified appearance.
     *
     * @param appearance the appearance of the content panel.
     */
    public ContentPanel(ContentPanelAppearance appearance) {
        super(true);
        this.appearance = appearance;

        setDeferHeight(true);

        SafeHtmlBuilder sb = new SafeHtmlBuilder();
        appearance.render(sb);

        setElement((Element) XDOM.create(sb.toSafeHtml()));

        header = new Header(appearance.getHeaderAppearance());
        ComponentHelper.setParent(this, header);

        XElement headerElem = appearance.getHeaderElem(getElement());
        headerElem.appendChild(header.getElement());

        buttonBar = new ButtonBar();
        buttonBar.setMinButtonWidth(75);
        buttonBar.setPack(BoxLayoutPack.END);
        buttonBar.setVisible(false);
        buttonBar.getElement().getStyle().setProperty("minHeight", "5px");
        appearance.getFooterElem(getElement()).appendChild(buttonBar.getElement());
    }

    @Override
    public HandlerRegistration addBeforeCollapseHandler(BeforeCollapseHandler handler) {
        return addHandler(handler, BeforeCollapseEvent.getType());
    }

    @Override
    public HandlerRegistration addBeforeExpandHandler(BeforeExpandHandler handler) {
        return addHandler(handler, BeforeExpandEvent.getType());
    }

    /**
     * Adds a widget the the button bar.
     *
     * @param widget the widget to add
     */
    @UiChild
    public void addButton(Widget widget) {
        buttonBar.add(widget);
        if (isOrWasAttached() && !buttonBar.isVisible()) {
            buttonBar.setVisible(true);
            if (isAttached()) {
                forceLayout();
            }
        }
    }

    @Override
    public HandlerRegistration addCollapseHandler(CollapseHandler handler) {
        return addHandler(handler, CollapseEvent.getType());
    }

    @Override
    public HandlerRegistration addExpandHandler(ExpandHandler handler) {
        return addHandler(handler, ExpandEvent.getType());
    }

    /**
     * Adds a Tool to Header
     *
     * @param tool the tool to add
     */
    @UiChild
    public void addTool(Widget tool) {
        header.addTool(tool);
    }

    @Override
    public void collapse() {
        if (!collapsed && !animating && fireCancellableEvent(new BeforeCollapseEvent())) {
            hideShadow();
            onCollapse();
        }
    }

    @Override
    public void expand() {
        if (collapsed && !animating && fireCancellableEvent(new BeforeExpandEvent())) {
            hideShadow();
            onExpand();
        }
    }

    @Override
    public void forceLayout() {
        if (collapsed) {
            doLayout();
        } else {
            super.forceLayout();
        }
    }

    /**
     * Gets the duration for the expand/collapse animations
     *
     * @return the duration for the expand/collapse animations in milliseconds.
     */
    public int getAnimationDuration() {
        return animationDuration;
    }

    /**
     * Sets the duration for the expand/collapse animations.
     *
     * @param animationDuration the duration of the expand/collapse animations in milliseconds
     */
    public void setAnimationDuration(int animationDuration) {
        this.animationDuration = animationDuration;
    }

    /**
     * Gets a reference to the appearance this object was instantiated with
     *
     * @return the appearance impl used by this component
     */
    public ContentPanelAppearance getAppearance() {
        return appearance;
    }

    /**
     * Returns the panel's body element.
     *
     * @return the body
     */
    public XElement getBody() {
        return getAppearance().getContentElem(getElement());
    }

    /**
     * Returns the panel's button alignment.
     *
     * @return the button alignment
     */
    public BoxLayoutPack getButtonAlign() {
        return buttonBar.getPack();
    }

    /**
     * Sets the button alignment of any buttons added to this panel (defaults to RIGHT, pre-render).
     *
     * @param buttonAlign the button alignment
     */
    public void setButtonAlign(BoxLayoutPack buttonAlign) {
        assertPreRender();
        buttonBar.setPack(buttonAlign);
    }

    /**
     * Returns the content panel button bar. In the default implementation, the button bar is displayed in the content
     * panel's footer.
     *
     * @return the button bar
     */
    public ButtonBar getButtonBar() {
        return buttonBar;
    }

    /**
     * Returns the content panel messages.
     *
     * @return the content panel messages
     */
    public ContentPanelMessages getMessages() {
        if (messages == null) {
            messages = new DefaultContentPanelMessages();
        }
        return messages;
    }

    /**
     * Sets the content panel messages.
     *
     * @param messages the messages
     */
    public void setMessages(ContentPanelMessages messages) {
        this.messages = messages;
    }

    /**
     * Returns the minimum button width.
     *
     * @return the minimum button width
     */
    public int getMinButtonWidth() {
        return buttonBar.getMinButtonWidth();
    }

    /**
     * Sets the minimum button width.
     *
     * @param width the button width
     */
    public void setMinButtonWidth(int width) {
        buttonBar.setMinButtonWidth(width);
    }

    /**
     * Returns true if animated collapsing is enabled.
     *
     * @return true if animating
     */
    public boolean isAnimCollapse() {
        return animCollapse;
    }

    /**
     * Sets whether expand and collapse is animating (defaults to true).
     *
     * @param animCollapse true to enable animations
     */
    public void setAnimCollapse(boolean animCollapse) {
        this.animCollapse = animCollapse;
    }

    /**
     * Returns true if the panel is collapsed.
     *
     * @return the collapsed state
     */
    public boolean isCollapsed() {
        return collapsed;
    }

    /**
     * Returns true if the panel is collapsible.
     *
     * @return the collapsible state
     */
    public boolean isCollapsible() {
        return collapsible;
    }

    /**
     * True to make the panel collapsible and have the expand/collapse toggle button automatically rendered into the
     * header tool button area (defaults to false, pre-render).
     *
     * @param collapsible the collapsible state
     */
    public void setCollapsible(boolean collapsible) {
        assertPreRender();
        this.collapsible = collapsible;
    }

    @Override
    public boolean isExpanded() {
        return !isCollapsed();
    }

    /**
     * Sets the panel's expand state.
     *
     * @param expanded <code>true<code> true to expand
     */
    public void setExpanded(boolean expanded) {
        if (expanded) {
            expand();
        } else {
            collapse();
        }
    }

    /**
     * Returns true if the collapse tool is hidden.
     *
     * @return the hide collapse tool state
     */
    public boolean isHideCollapseTool() {
        return hideCollapseTool;
    }

    /**
     * Sets whether the collapse tool should be displayed (when {@link #setCollapsible(boolean)} = true) (defaults to false, pre-render).
     *
     * @param hideCollapseTool true if the tool is hidden
     */
    public void setHideCollapseTool(boolean hideCollapseTool) {
        assertPreRender();
        this.hideCollapseTool = hideCollapseTool;
    }

    /**
     * Returns true if title collapsing has been enabled.
     *
     * @return true for title collapse
     */
    public boolean isTitleCollapse() {
        return titleCollapse;
    }

    /**
     * True to allow expanding and collapsing the panel (when {@link #setCollapsible(boolean)} = true) by clicking
     * anywhere in the header bar, false to allow it only by clicking to tool button (defaults to false).
     *
     * @param titleCollapse the titleCollapse to set
     */
    public void setTitleCollapse(boolean titleCollapse) {
        this.titleCollapse = titleCollapse;
        if (titleCollapse) {
            header.getElement().getStyle().setCursor(Cursor.POINTER);
            sinkEvents(Event.ONCLICK);
        } else {
            header.getElement().getStyle().setCursor(Cursor.DEFAULT);
        }
    }

    @Override
    public void onBrowserEvent(Event event) {
        super.onBrowserEvent(event);
        if (event.getTypeInt() == Event.ONCLICK && event.getEventTarget() != null) {
            onClick(event);
        }
    }

    /**
     * Displays or hides the body border.
     *
     * @param border true to display the border
     */
    public void setBodyBorder(boolean border) {
        getAppearance().onBodyBorder(getElement(), border);
    }

    /**
     * Sets multiple style properties on the body element. Style attribute names must be in lower camel case, e.g.
     * "backgroundColor:white; color:red;"
     *
     * @param style the style(s) to set
     */
    public void setBodyStyle(String style) {
        getAppearance().getContentElem(getElement()).applyStyles(style);
    }

    /**
     * Adds a style class name to the body element.
     *
     * @param style the style class name
     */
    public void setBodyStyleName(String style) {
        getAppearance().getContentElem(getElement()).addClassName(style);
    }

    /**
     * Returns the content panel header.
     *
     * @return the header
     */
    public Header getHeader() {
        return header;
    }

    /**
     * Shows or hides the content panel header.
     *
     * @param visible true to show the header.
     */
    public void setHeaderVisible(boolean visible) {
        this.headerVisible = visible;
        getAppearance().onHideHeader(getElement(), !visible);
    }

    /**
     * Returns the heading html.
     *
     * @return the heading html
     */
    public SafeHtml getHeading() {
        return header.getSafeHtml();
    }

    /**
     * Sets the heading html.
     *
     * @param html the heading html
     */
    public void setHeading(SafeHtml html) {
        header.setHTML(html);
    }

    /**
     * Sets the heading text.
     *
     * Text that contains reserved html characters will be escaped.
     *
     * @param text the text
     */
    public void setHeading(String text) {
        header.setHTML(SafeHtmlUtils.fromString(text));
    }

    protected Size adjustBodySize() {
        return new Size(0, 0);
    }

    protected void afterCollapse() {
        collapsed = true;
        animating = false;

        getAppearance().getBodyWrap(getElement()).hide();
        for (int i = 0; i < getWidgetCount(); i++) {
            Widget w = getWidget(i);
            if (w.isVisible() && w instanceof Component) {
                ((Component) w).notifyHide();
            }
        }

        if (buttonBar != null && buttonBar.isAttached()) {
            buttonBar.notifyHide();
        }

        sync(true);

        // Re-enable the toggle tool after an animated collapse
        if (animCollapse && collapseBtn != null) {
            collapseBtn.enable();
        }

        if (collapseBtn != null) {
            collapseBtn.changeStyle(getAppearance().expandIcon());
        }

        fireEvent(new CollapseEvent());
    }

    protected void afterExpand() {
        collapsed = false;
        animating = false;

        for (int i = 0; i < getWidgetCount(); i++) {
            Widget w = getWidget(i);
            if (w.isVisible() && w instanceof Component) {
                ((Component) w).notifyShow();
            }
        }

        if (buttonBar != null && buttonBar.isAttached()) {
            buttonBar.notifyShow();
        }

        sync(true);

        // Re-enable the toggle tool after an animated collapse
        if (animCollapse && collapseBtn != null) {
            collapseBtn.enable();
        }

        if (collapseBtn != null && !hideCollapseTool) {
            collapseBtn.changeStyle(getAppearance().collapseIcon());
        }

        if (layoutOnExpand) {
            layoutOnExpand = false;
            super.forceLayout();
        }

        fireEvent(new ExpandEvent());
    }

    @Override
    protected void doAttachChildren() {
        super.doAttachChildren();
        ComponentHelper.doAttach(header);
        ComponentHelper.doAttach(buttonBar);
    }

    @Override
    protected void doDetachChildren() {
        super.doDetachChildren();
        ComponentHelper.doDetach(header);
        ComponentHelper.doDetach(buttonBar);
    }

    @Override
    protected void doLayout() {
        if (collapsed && widget != null && resize) {
            layoutOnExpand = true;
            return;
        }

        // EXTGWT-3414 - When the button is autosized, a width is not given to it, this will give it a width.
        doLayoutButtonBar();

        super.doLayout();
    }

    protected void doLayoutButtonBar() {
        if (buttonBar != null) {
            int offsetWidth = getContainerTarget().getOffsetWidth();
            buttonBar.setWidth(offsetWidth);
        }
    }

    @Override
    protected XElement getContainerTarget() {
        return getAppearance().getContentElem(getElement());
    }

    protected Size getFrameSize() {
        return new Size(getAppearance().getFrameWidth(getElement()), getAppearance().getFrameHeight(getElement()));
    }

    protected void initTools() {
        if (collapsible && !hideCollapseTool) {
            collapseBtn = new ToolButton(collapsed ? getAppearance().expandIcon() : getAppearance().collapseIcon());
            collapseBtn.addSelectHandler(new SelectHandler() {
                @Override
                public void onSelect(SelectEvent event) {
                    setExpanded(!isExpanded());
                }
            });
            header.addTool(collapseBtn);
        }
    }

    protected boolean layoutBars() {
        if (buttonBar != null && buttonBar.getWidgetCount() > 0) {

            boolean hlr = hadLayoutRunning;
            if (!hadLayoutRunning) {

                hadLayoutRunning = true;
            }

            // first call to layoutBars will happen before the panel has been sized
            // button bar width = 0
            boolean overflow = buttonBar.isEnableOverflow();
            buttonBar.setEnableOverflow(false);
            buttonBar.forceLayout();
            buttonBar.setEnableOverflow(overflow);
            hadLayoutRunning = hlr;
            sync(true);
            return true;
        }
        return false;
    }

    @Override
    protected void onAfterFirstAttach() {
        super.onAfterFirstAttach();
        if (buttonBar.getWidgetCount() > 0) {
            buttonBar.setVisible(true);
        }

        initTools();
        layoutBars();

        if (!headerVisible) {
            header.setVisible(false);
        }
    }

    protected void onClick(Event ce) {
        if (collapsible && titleCollapse && header.getElement().isOrHasChild(ce.getEventTarget().<Element>cast())) {
            setExpanded(!isExpanded());
        }
    }

    protected void onCollapse() {
        if (isAttached() && animCollapse) {
            animating = true;
            // Disable layout adjustment during animation

            // Disable toggle tool during animated collapse
            if (collapseBtn != null) {
                collapseBtn.disable();
            }

            Fx fx = new Fx(getAnimationDuration());
            fx.addAfterAnimateHandler(new AfterAnimateHandler() {
                @Override
                public void onAfterAnimate(AfterAnimateEvent event) {
                    afterCollapse();
                }
            });
            fx.run(new SlideOut(getAppearance().getBodyWrap(getElement()), Direction.UP));

            addStyleDependentName("animated");
        } else {
            getAppearance().getBodyWrap(getElement()).hide();
            afterCollapse();
        }
    }

    @Override
    protected void onDisable() {
        mask();
        super.onDisable();
    }

    @Override
    protected void onEnable() {
        unmask();
        super.onEnable();
    }

    protected void onExpand() {
        if (isAttached() && animCollapse) {
            animating = true;
            // Show the body before animating
            getAppearance().getBodyWrap(getElement()).show();

            addStyleDependentName("animated");

            Fx fx = new Fx(getAnimationDuration());
            fx.addAfterAnimateHandler(new AfterAnimateHandler() {
                @Override
                public void onAfterAnimate(AfterAnimateEvent event) {
                    afterExpand();
                }
            });
            fx.run(new SlideIn(getAppearance().getBodyWrap(getElement()), Direction.DOWN));

            // Disable toggle tool during animated expand
            if (collapseBtn != null) {
                collapseBtn.disable();
            }
        } else {
            getAppearance().getBodyWrap(getElement()).show();
            afterExpand();
        }
    }

    @Override
    protected void onResize(int width, int height) {
        Size frameSize = getFrameSize();
        Size adjustBodySize = adjustBodySize();

        if (isAutoWidth()) {
            getContainerTarget().getStyle().clearWidth();
        } else {
            width -= frameSize.getWidth();

            if (header != null) {
                int aw = width - appearance.getHeaderElem(getElement()).getFrameWidth(Side.LEFT, Side.RIGHT);
                header.setWidth(aw);
            }
            getContainerTarget().setWidth(width - adjustBodySize.getWidth(), true);
        }

        layoutBars();

        // EXTGWT-2773 - Button bar inner frame height
        if (buttonBar != null) {
            appearance.getFooterElem(getElement()).setHeight(buttonBar.getOffsetHeight());
        }

        if (isAutoHeight()) {
            getContainerTarget().getStyle().clearHeight();
        } else {
            height -= frameSize.getHeight();
            height -= headerVisible ? getAppearance().getHeaderSize(getElement()).getHeight() : 0;
            height -= getAppearance().getFooterElem(getElement()).getHeight(false);

            getContainerTarget().setHeight(height - adjustBodySize.getHeight(), true);
        }

        super.onResize(width, height);
    }

    /**
     * The appearance of a content panel. A content panel has a header, body and footer. The header includes a button that
     * can be used to collapse or expand the body. The button has an icon that changes to indicate whether a collapse or
     * expand is possible. The body contains a single widget, added using {@link ContentPanel#add}. The widget is resized
     * to match the size of the container. The footer contains a button bar with optional buttons.
     */
    public interface ContentPanelAppearance {

        /**
         * Returns the button icon that indicates a collapse is possible.
         *
         * @return the collapse icon
         */
        IconConfig collapseIcon();

        /**
         * Returns the button icon that indicates an expand is possible.
         *
         * @return the expand icon
         */
        IconConfig expandIcon();

        /**
         * Returns the element that wraps the content panel body. In the default implementation, this wraps the body widget
         * and footer.
         *
         * @param parent the content panel root element
         * @return the element that wraps the body
         */
        XElement getBodyWrap(XElement parent);

        /**
         * Returns the content panel body element.
         *
         * @param parent the content panel root element
         * @return the body element
         */
        XElement getContentElem(XElement parent);

        /**
         * Returns the content panel footer element.
         *
         * @param parent the content panel root element
         * @return the body element
         */
        XElement getFooterElem(XElement parent);

        /**
         * Returns the total height of the content panel frame elements.
         *
         * @param parent the content panel root element
         * @return the total height of the frame elements
         */
        int getFrameHeight(XElement parent);

        /**
         * Returns the total width of the content panel frame elements.
         *
         * @param parent the content panel root element
         * @return the total width of the frame elements
         */
        int getFrameWidth(XElement parent);

        /**
         * Returns the content panel header's appearance
         *
         * @return the header appearance
         */
        HeaderAppearance getHeaderAppearance();

        /**
         * Returns the content panel header element.
         *
         * @param parent the content panel root element
         * @return the content panel header element
         */
        XElement getHeaderElem(XElement parent);

        /**
         * Returns the header size excluding any framing.
         *
         * @return the header size
         */
        Size getHeaderSize(XElement parent);

        /**
         * Handles a change in the visibility of the body border.
         *
         * @param parent content panel root element
         * @param border true to display the border
         */
        void onBodyBorder(XElement parent, boolean border);

        /**
         * Hides or shows the header.
         *
         * @param parent content panel root element
         * @param hide true to hide the header
         */
        void onHideHeader(XElement parent, boolean hide);

        /**
         * Renders the appearance of a content panel as HTML into a {@link SafeHtmlBuilder}, suitable for passing to
         * {@link Element#setInnerSafeHtml(SafeHtml)} on a container element.
         *
         * @param sb receives the rendered appearance
         */
        void render(SafeHtmlBuilder sb);

    }

    /**
     * Provides access to content panel messages.
     */
    public interface ContentPanelMessages {

        /**
         * Returns the content panel collapse message.
         *
         * @return the content panel collapse message
         */
        String panelCollapse();

        /**
         * Returns the content panel expand message.
         *
         * @return the content panel expand message
         */
        String panelExpand();

    }

    /**
     * Provides support for deferred binding for the panel header appearance.
     */
    public interface PanelHeaderAppearance extends HeaderAppearance {

    }

    protected class DefaultContentPanelMessages implements ContentPanelMessages {

        public String panelCollapse() {
            return DefaultMessages.getMessages().panel_collapsePanel();
        }

        public String panelExpand() {
            return DefaultMessages.getMessages().panel_expandPanel();
        }

    }
}