Skip to content

Instantly share code, notes, and snippets.

@branflake2267
Last active September 19, 2018 02:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save branflake2267/44025882d6100f20932cace1f28ab1a8 to your computer and use it in GitHub Desktop.
Save branflake2267/44025882d6100f20932cace1f28ab1a8 to your computer and use it in GitHub Desktop.
GXT 4.0.3 workqround for HBoxLayoutContainer UiBinder issue. 'child' works again with this workaround. This adds a "sibling" tag to fix it. This will be fixed in 4.0.4
package com.sencha.gxt.widget.core.client.container;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
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.Style.Unit;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safehtml.shared.SimpleHtmlSanitizer;
import com.google.gwt.uibinder.client.UiChild;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.core.client.GXTLogConfiguration;
import com.sencha.gxt.core.client.Style;
import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.core.client.resources.CommonStyles;
import com.sencha.gxt.core.client.util.Margins;
import com.sencha.gxt.core.client.util.Size;
import com.sencha.gxt.core.client.util.Util;
import com.sencha.gxt.core.shared.event.GroupingHandlerRegistration;
import com.sencha.gxt.widget.core.client.Component;
import com.sencha.gxt.widget.core.client.button.ButtonGroup;
import com.sencha.gxt.widget.core.client.button.SplitButton;
import com.sencha.gxt.widget.core.client.button.TextButton;
import com.sencha.gxt.widget.core.client.button.ToggleButton;
import com.sencha.gxt.widget.core.client.event.BeforeShowEvent;
import com.sencha.gxt.widget.core.client.event.BeforeShowEvent.BeforeShowHandler;
import com.sencha.gxt.widget.core.client.event.CheckChangeEvent;
import com.sencha.gxt.widget.core.client.event.CheckChangeEvent.CheckChangeHandler;
import com.sencha.gxt.widget.core.client.event.OverflowEvent;
import com.sencha.gxt.widget.core.client.event.OverflowEvent.HasOverflowHandlers;
import com.sencha.gxt.widget.core.client.event.OverflowEvent.OverflowHandler;
import com.sencha.gxt.widget.core.client.event.SelectEvent;
import com.sencha.gxt.widget.core.client.menu.CheckMenuItem;
import com.sencha.gxt.widget.core.client.menu.ColorMenu;
import com.sencha.gxt.widget.core.client.menu.HeaderMenuItem;
import com.sencha.gxt.widget.core.client.menu.Item;
import com.sencha.gxt.widget.core.client.menu.Menu;
import com.sencha.gxt.widget.core.client.menu.MenuItem;
import com.sencha.gxt.widget.core.client.menu.SeparatorMenuItem;
import com.sencha.gxt.widget.core.client.toolbar.FillToolItem;
import com.sencha.gxt.widget.core.client.toolbar.LabelToolItem;
import com.sencha.gxt.widget.core.client.toolbar.SeparatorToolItem;
import com.sencha.gxt.widget.core.client.toolbar.ToolBar;
/**
* <p>
* A layout container for horizontal rows of widgets. Provides support for automatic overflow (i.e. when there are too
* many widgets to display in the available width -- see {@link #setEnableOverflow(boolean)}).
* </p>
*
* <p>
* Code Snippet:
* </p>
*
* <pre>
* <code>
* HBoxLayoutContainer c = new HBoxLayoutContainer();
* c.setHBoxLayoutAlign(HBoxLayoutAlign.TOP);
* BoxLayoutData layoutData = new BoxLayoutData(new Margins(5, 0, 0, 5));
* c.add(new TextButton("Button 1"), layoutData);
* c.add(new TextButton("Button 2"), layoutData);
* c.add(new TextButton("Button 3"), layoutData);
* Viewport v = new Viewport();
* v.add(c);
* RootPanel.get().add(v);
* </code>
* </pre>
*
* @see ToolBar
*/
public class HBoxLayoutContainer extends BoxLayoutContainer implements HasOverflowHandlers {
/**
* The vertical alignment of the horizontal widgets.
*/
public enum HBoxLayoutAlign {
/**
* Children are aligned horizontally at the <b>bottom</b> side of the container.
*/
BOTTOM,
/**
* Children are aligned horizontally at the <b>mid-height</b> of the container.
*/
MIDDLE,
/**
* Children are stretched vertically to fill the height of the container.
*/
STRETCH,
/**
* Children heights are set the size of the largest child's height.
*/
STRETCHMAX,
/**
* Children are aligned horizontally at the <b>top</b> side of the container.
*/
TOP
}
public interface HBoxLayoutContainerAppearance extends BoxLayoutContainerAppearance {
ImageResource moreIcon();
String moreButtonStyle();
}
/**
* Use this Component data attribute name to provide custom menu item text.
*/
public static String PROVIDE_MENU_TEXT_DATA_KEY = "gxt-menutext";
protected List<Widget> hiddens = new ArrayList<Widget>();
protected boolean hasOverflow;
protected TextButton more;
protected Menu moreMenu;
protected HashMap<Integer, MenuItem> moreMenuItems;
protected GroupingHandlerRegistration handlerRegistration = new GroupingHandlerRegistration();
protected boolean enableOverflow = true;
protected HBoxLayoutAlign hBoxLayoutAlign;
protected Map<Widget, Integer> loopWidthMap = new HashMap<Widget, Integer>();
protected Map<Widget, Integer> loopHeightMap = new HashMap<Widget, Integer>();
protected int triggerWidth = 35;
protected Map<Widget, Integer> widthMap = new HashMap<Widget, Integer>();
protected boolean moreMenuEnableScrolling;
protected static Logger logger = Logger.getLogger(HBoxLayoutContainer.class.getName());
/**
* Creates a new HBoxlayout.
*/
public HBoxLayoutContainer() {
this(HBoxLayoutAlign.TOP);
}
/**
* Creates a new HBoxlayout.
*
* @param appearance the hbox appearance
*/
public HBoxLayoutContainer(HBoxLayoutContainerAppearance appearance) {
this(HBoxLayoutAlign.TOP, appearance);
}
/**
* Creates a new HBoxlayout.
*
* @param align the horizontal alignment
*/
public HBoxLayoutContainer(HBoxLayoutAlign align) {
this(align, GWT.<HBoxLayoutContainerAppearance>create(HBoxLayoutContainerAppearance.class));
}
protected HBoxLayoutContainer(HBoxLayoutAlign align, HBoxLayoutContainerAppearance appearance) {
super(appearance);
setHBoxLayoutAlign(align);
}
@Override
public void add(IsWidget child) {
super.add(child);
}
/**
* Add a widget and a custom menu item to show when overlfow menu is displayed.
* <p>
*
* This caused a regression in 4.0.3, so a parameter was added. This has been deprecated. Change to
* {@link #addWithMenu(IsWidget, MenuItem)}.
*
* @param child the widget
* @param menuItem the menu item for the widget when it's not visible
* @param ignoreParam this has been added to fix a compiler regression in 4.0.3.
* @since 4.0.4
*/
@Deprecated
public void add(IsWidget child, MenuItem menuItem, Boolean ignoreParam) {
super.add(child);
addMenuItem(menuItem);
}
/**
* Add a widget and a custom menu item to show when overlfow menu is displayed.
* <p>
*
* This caused a regression in 4.0.3, so a parameter was added. This has been deprecated. Change to
* {@link #addWithMenu(Widget, MenuItem)}.
*
* @param child the widget
* @param menuItem the menu item for the widget when it's not visible
* @param ignoreParam this has been added to fix a compiler regression in 4.0.3.
* @since 4.0.4
*/
@Deprecated
public void add(Widget child, MenuItem menuItem, Boolean ignoreParam) {
super.add(child);
addMenuItem(menuItem);
}
/**
* Add a widget.
*
* @param child the child to add.
*/
@Override
public void add(Widget child) {
super.add(child);
}
/**
* Add a widget.
*
* In UiBinder use &#x3C;container:child/&#x3E;
*
* @param child the widget
* @param layoutData the layout data.
*/
@UiChild(tagname = "child")
@Override
public void add(IsWidget child, BoxLayoutData layoutData) {
super.add(child, layoutData);
}
/**
* This was added to deal with the 4.0.3 regression. This covers those who changed to use sibling.
*
* Please use the &#x3C;container:child/&#x3E; for the children in uibinder.
*
* @param child the child to add
* @param layoutData the box layout data
* @since 4.0.4
*/
@UiChild(tagname = "sibling")
@Deprecated
public void addWithUiBinder(IsWidget child, BoxLayoutData layoutData) {
if (child != null) {
child.asWidget().setLayoutData(layoutData);
}
super.add(child);
}
/**
* Add a widget and a custom menu item to show when overlfow menu is displayed.
* <p>
*
* @param child the widget
* @param menuItem the menu item for the widget when it's not visible
* @since 4.0.4
*/
public void addWithMenu(IsWidget child, MenuItem menuItem) {
super.add(child);
addMenuItem(menuItem);
}
/**
* Add a widget and a custom menu item to show when overlfow menu is displayed.
* <p>
*
* @param child the widget
* @param menuItem the menu item for the widget when it's not visible
* @since 4.0.4
*/
public void addWithMenu(Widget child, MenuItem menuItem) {
super.add(child);
addMenuItem(menuItem);
}
/**
* Add a widget with layout data and a custom menu item to show when overlfow menu is displayed.
* <p>
*
* @param child the widget
* @param layoutData the sizing for the child
* @param menuItem the menu item when it's not visible
* @since 4.0.3
*/
public void add(IsWidget child, BoxLayoutData layoutData, MenuItem menuItem) {
super.add(child, layoutData);
addMenuItem(menuItem);
}
@Override
public void insert(Widget w, int beforeIndex) {
super.insert(w, beforeIndex);
// when adding in an hbox child, measure it, then a parent can calculate it's size.
if (isAttached()) {
forceLayout();
}
}
@Override
public boolean remove(Widget child) {
boolean removed = super.remove(child);
// when adding in an hbox child, measure it, then a parent can calculate it's size.
if (isAttached()) {
forceLayout();
}
return removed;
}
/**
* When the overflow menu is shown.
*/
@Override
public HandlerRegistration addOverflowHandler(OverflowHandler handler) {
return addHandler(handler, OverflowEvent.getType());
}
protected void addMenuItem(MenuItem menuItem) {
if (moreMenuItems == null) {
moreMenuItems = new HashMap<Integer, MenuItem>();
}
int index = getWidgetCount() - 1;
moreMenuItems.put(index, menuItem);
}
/**
* Returns the horizontal layout appearance.
*
* @return the appearance
*/
@Override
public HBoxLayoutContainerAppearance getAppearance() {
return (HBoxLayoutContainerAppearance) super.getAppearance();
}
/**
* Returns the horizontal alignment.
*
* @return the horizontal alignment
*/
public HBoxLayoutAlign getHBoxLayoutAlign() {
return hBoxLayoutAlign;
}
/**
* Returns true if overflow is enabled.
*
* @return the overflow state
*/
public boolean isEnableOverflow() {
return enableOverflow;
}
/**
* True to show a drop down icon when the available width is less than the required width (defaults to true).
*
* @param enableOverflow true to enable overflow support
*/
public void setEnableOverflow(boolean enableOverflow) {
this.enableOverflow = enableOverflow;
}
/**
* Sets the vertical alignment for child items (defaults to TOP).
*
* @param hBoxLayoutAlign the vertical alignment
*/
public void setHBoxLayoutAlign(HBoxLayoutAlign hBoxLayoutAlign) {
this.hBoxLayoutAlign = hBoxLayoutAlign;
}
/**
* Add widget to overflow menu.
*
* @param menu the overflow menu
* @param w the widget that will get converted into the menu item
*/
protected void addWidgetToMenu(Menu menu, Widget w) {
if (w instanceof SeparatorToolItem) {
addMenuItemToMoreMenuSeparatorItem(w, menu);
} else if (w instanceof SplitButton) {
addWidgetToMenuSplitButton(menu, w);
} else if (w instanceof TextButton) {
addWidgetToMenuTextButton(menu, w);
} else if (w instanceof ButtonGroup) {
addWidgetToMenuButtonGroup(menu, w);
} else if (w instanceof ToggleButton) {
addWidgetToMenuToggleButton(menu, w);
} else if (w instanceof LabelToolItem) {
addWidgetToMenuLabelToolItem(menu, w);
}
afterAddWidgetToMenu(menu);
}
/**
* @since 4.0.3
*/
protected void addMenuItemToMoreMenuSeparatorItem(Widget w, Menu menu) {
SeparatorMenuItem item = new SeparatorMenuItem();
addMenuItemToMoreMenu(w, menu, item);
}
/**
* @since 4.0.3
*/
protected void addWidgetToMenuLabelToolItem(Menu menu, Widget w) {
final LabelToolItem lti = (LabelToolItem) w;
MenuItem item = new MenuItem();
item.setHTML(lti.getLabel());
addMenuItemToMoreMenu(w, menu, item);
}
/**
* @since 4.0.3
*/
protected void addMenuItemToMoreMenu(Widget w, Menu menu, Item item) {
menu.add(item);
}
/**
* @since 4.0.3
*/
protected void addWidgetToMenuToggleButton(Menu menu, Widget w) {
final ToggleButton b = (ToggleButton) w;
final CheckMenuItem item = new CheckMenuItem(b.getText());
item.setItemId(b.getItemId());
item.setChecked(b.getValue());
item.setEnabled(b.isEnabled());
if (b.getData(PROVIDE_MENU_TEXT_DATA_KEY) != null) {
item.setHTML(SimpleHtmlSanitizer.sanitizeHtml(b.getData(PROVIDE_MENU_TEXT_DATA_KEY).toString()));
}
handlerRegistration.add(item.addCheckChangeHandler(new CheckChangeHandler<CheckMenuItem>() {
@Override
public void onCheckChange(CheckChangeEvent<CheckMenuItem> event) {
// must pass true to cause value change event to fire
b.setValue(event.getItem().isChecked(), true);
b.fireEvent(new SelectEvent());
}
}));
addMenuItemToMoreMenu(w, menu, item);
}
/**
* @since 4.0.3
*/
protected void addWidgetToMenuButtonGroup(Menu menu, Widget w) {
ButtonGroup buttonGroup = (ButtonGroup) w;
buttonGroup.setItemId(buttonGroup.getItemId());
addMenuItemToMoreMenu(w, menu, new SeparatorMenuItem());
addMenuItemToMoreMenu(w, menu, new HeaderMenuItem(buttonGroup.getHeading()));
Widget con = buttonGroup.getWidget();
if (con != null && con instanceof HasWidgets) {
HasWidgets widgets = (HasWidgets) con;
Iterator<Widget> it = widgets.iterator();
while (it.hasNext()) {
addWidgetToMenu(menu, it.next());
}
}
addMenuItemToMoreMenu(w, menu, new SeparatorMenuItem());
}
/**
* @since 4.0.3
*/
protected void addWidgetToMenuTextButton(Menu menu, Widget w) {
final TextButton textButton = (TextButton) w;
MenuItem item = new MenuItem(textButton.getText(), textButton.getIcon());
item.setItemId(textButton.getItemId());
if (textButton.getData(PROVIDE_MENU_TEXT_DATA_KEY) != null) {
item.setHTML(SimpleHtmlSanitizer.sanitizeHtml(textButton.getData(PROVIDE_MENU_TEXT_DATA_KEY).toString()));
}
if (textButton.getMenu() != null) {
item.setHideOnClick(false);
item.setSubMenu(textButton.getMenu());
if (item.getSubMenu() instanceof ColorMenu) {
ColorMenu colorMenu = (ColorMenu) item.getSubMenu();
handlerRegistration.add(colorMenu.getPalette().addValueChangeHandler(new ValueChangeHandler<String>() {
@Override
public void onValueChange(ValueChangeEvent<String> valueChangeEvent) {
moreMenu.hide();
}
}));
}
}
item.setEnabled(textButton.isEnabled());
handlerRegistration.add(item.addSelectionHandler(new SelectionHandler<Item>() {
@Override
public void onSelection(SelectionEvent<Item> event) {
textButton.fireEvent(new SelectEvent());
}
}));
addMenuItemToMoreMenu(w, menu, item);
}
/**
* @since 4.0.3
*/
protected void addWidgetToMenuSplitButton(Menu menu, Widget w) {
final SplitButton sb = (SplitButton) w;
MenuItem item = new MenuItem(sb.getText(), sb.getIcon());
item.setEnabled(sb.isEnabled());
item.setItemId(sb.getItemId());
if (sb.getData(PROVIDE_MENU_TEXT_DATA_KEY) != null) {
item.setHTML(SimpleHtmlSanitizer.sanitizeHtml(sb.getData(PROVIDE_MENU_TEXT_DATA_KEY).toString()));
}
if (sb.getMenu() != null) {
item.setSubMenu(sb.getMenu());
}
handlerRegistration.add(item.addSelectionHandler(new SelectionHandler<Item>() {
@Override
public void onSelection(SelectionEvent<Item> event) {
sb.fireEvent(new SelectEvent());
}
}));
addMenuItemToMoreMenu(w, menu, item);
}
/**
* @since 4.0.3
*/
protected void afterAddWidgetToMenu(Menu menu) {
if (menu.getWidgetCount() > 0) {
if (menu.getWidget(0) instanceof SeparatorMenuItem) {
menu.remove(menu.getWidget(0));
}
if (menu.getWidgetCount() > 0) {
if (menu.getWidget(menu.getWidgetCount() - 1) instanceof SeparatorMenuItem) {
menu.remove(menu.getWidget(menu.getWidgetCount() - 1));
}
}
}
}
protected void clearMenu() {
moreMenu.clear();
}
@Override
protected void doLayout() {
Size size = getElement().getStyleSize();
if (GXTLogConfiguration.loggingIsEnabled()) {
logger.finest(getId() + " doLayout size: " + size);
}
if ((size.getHeight() == 0 && size.getWidth() == 0) || size.getWidth() == 0) {
return;
}
int w = size.getWidth() - getScrollOffset();
int h = size.getHeight();
int styleHeight = Util.parseInt(getElement().getStyle().getProperty("height"), Style.DEFAULT);
int styleWidth = Util.parseInt(getElement().getStyle().getProperty("width"), Style.DEFAULT);
boolean findWidth = styleWidth == -1;
boolean findHeight = styleHeight == -1;
if (GXTLogConfiguration.loggingIsEnabled()) {
logger.finest(getId() + " findWidth: " + findWidth + " findHeight: " + findHeight);
}
int calculateWidth = 0;
int maxWidgetHeight = 0;
int maxMarginTop = 0;
int maxMarginBottom = 0;
for (int i = 0, len = getWidgetCount(); i < len; i++) {
Widget widget = getWidget(i);
BoxLayoutData layoutData = null;
Object d = widget.getLayoutData();
if (d instanceof BoxLayoutData) {
layoutData = (BoxLayoutData) d;
} else {
layoutData = new BoxLayoutData();
widget.setLayoutData(layoutData);
}
Margins cm = layoutData.getMargins();
if (cm == null) {
cm = new Margins(0);
layoutData.setMargins(cm);
}
}
if (findWidth || findHeight) {
for (int i = 0, len = getWidgetCount(); i < len; i++) {
Widget widget = getWidget(i);
if (!widget.isVisible()) {
continue;
}
BoxLayoutData layoutData = (BoxLayoutData) widget.getLayoutData();
Margins cm = layoutData.getMargins();
calculateWidth += widget.getOffsetWidth();
maxWidgetHeight = Math.max(maxWidgetHeight, widget.getOffsetHeight());
calculateWidth += (cm.getLeft() + cm.getRight());
maxMarginTop = Math.max(maxMarginTop, cm.getTop());
maxMarginBottom = Math.max(maxMarginBottom, cm.getBottom());
} // end for
maxWidgetHeight += (maxMarginTop + maxMarginBottom);
if (findWidth) {
w = calculateWidth;
}
if (findHeight) {
h = maxWidgetHeight;
}
} // end findWidth/Height
int padlLeft = 0;
int padTop = 0;
int padBottom = 0;
int padRight = 0;
if (getPadding() != null) {
padlLeft = getPadding().getLeft();
padTop = getPadding().getTop();
padBottom = getPadding().getBottom();
padRight = getPadding().getRight();
}
if (findHeight) {
h += padTop + padBottom;
}
if (findWidth) {
w += padlLeft + padRight;
}
int stretchHeight = h - padTop - padBottom;
int totalFlex = 0;
int totalWidth = 0;
int maxHeight = 0;
for (int i = 0, len = getWidgetCount(); i < len; i++) {
Widget widget = getWidget(i);
widget.addStyleName(CommonStyles.get().positionable());
widget.getElement().getStyle().setMargin(0, Unit.PX);
if (!widget.isVisible()) {
continue;
}
if (widget == more) {
triggerWidth = widget.getOffsetWidth() + 10;
}
BoxLayoutData layoutData = (BoxLayoutData) widget.getLayoutData();
Margins cm = layoutData.getMargins();
// There is an issue where getOffsetWidth call in 2nd loop is returning smaller number than actual offset
// when packing CENTER or END so we cache the offsetWidth for use in 2nd loop
// with buttons, the button is word wrapping causing the button to be narrower and taller
int ww = widget.getOffsetWidth();
loopWidthMap.put(widget, ww);
loopHeightMap.put(widget, widget.getOffsetHeight());
totalFlex += layoutData.getFlex();
totalWidth += (ww + cm.getLeft() + cm.getRight());
maxHeight = Math.max(maxHeight, widget.getOffsetHeight() + cm.getTop() + cm.getBottom());
} // end for
int innerCtHeight = maxHeight + padTop + padBottom;
if (hBoxLayoutAlign.equals(HBoxLayoutAlign.STRETCH)) {
getContainerTarget().setSize(w, h, true);
} else if (hBoxLayoutAlign.equals(HBoxLayoutAlign.MIDDLE) || hBoxLayoutAlign.equals(HBoxLayoutAlign.BOTTOM)) {
getContainerTarget().setSize(w, h = Math.max(h, innerCtHeight), true);
} else {
getContainerTarget().setSize(w, innerCtHeight, true);
}
int extraWidth = w - totalWidth - padlLeft - padRight;
int allocated = 0;
int componentWidth, componentHeight, componentTop;
int availableHeight = h - padTop - padBottom;
if (getPack().equals(BoxLayoutPack.CENTER)) {
padlLeft += extraWidth / 2;
} else if (getPack().equals(BoxLayoutPack.END)) {
padlLeft += extraWidth;
}
for (int i = 0, len = getWidgetCount(); i < len; i++) {
Widget widget = getWidget(i);
if (!widget.isVisible()) {
continue;
}
BoxLayoutData layoutData = (BoxLayoutData) widget.getLayoutData();
Margins cm = layoutData.getMargins();
componentWidth = loopWidthMap.remove(widget);
componentHeight = loopHeightMap.remove(widget);
padlLeft += cm.getLeft();
padlLeft = Math.max(0, padlLeft);
if (hBoxLayoutAlign.equals(HBoxLayoutAlign.MIDDLE)) {
int diff = availableHeight - (componentHeight + cm.getTop() + cm.getBottom());
if (diff == 0) {
componentTop = padTop + cm.getTop();
} else {
componentTop = padTop + cm.getTop() + (diff / 2);
}
} else {
if (hBoxLayoutAlign.equals(HBoxLayoutAlign.BOTTOM)) {
componentTop = h - (padBottom + cm.getBottom() + componentHeight);
} else {
componentTop = padTop + cm.getTop();
}
}
boolean component = widget instanceof Component;
Component c = null;
if (component) {
c = (Component) widget;
}
int width = -1;
if (component) {
c.setPosition(padlLeft, componentTop);
} else {
XElement.as(widget.getElement()).setLeftTop(padlLeft, componentTop);
}
if (getPack().equals(BoxLayoutPack.START) && layoutData.getFlex() > 0) {
int add = (int) Math.floor(extraWidth * (layoutData.getFlex() / totalFlex));
allocated += add;
if (isAdjustForFlexRemainder() && i == getWidgetCount() - 1) {
add += (extraWidth - allocated);
}
componentWidth += add;
width = componentWidth;
}
if (hBoxLayoutAlign.equals(HBoxLayoutAlign.STRETCH)) {
applyLayout(widget, width, Util.constrain(stretchHeight - cm.getTop() - cm.getBottom(), layoutData.getMinSize(),
layoutData.getMaxSize()));
} else if (hBoxLayoutAlign.equals(HBoxLayoutAlign.STRETCHMAX)) {
applyLayout(widget, width,
Util.constrain(maxHeight - cm.getTop() - cm.getBottom(), layoutData.getMinSize(), layoutData.getMaxSize()));
} else if (width > 0) {
applyLayout(widget, width, -1);
}
padlLeft += componentWidth + cm.getRight();
} // end for
// do we need overflow
if (enableOverflow) {
int runningWidth = 0;
for (int i = 0, len = getWidgetCount(); i < len; i++) {
Widget widget = getWidget(i);
if (widget == more) {
continue;
}
BoxLayoutData layoutData = null;
Object d = widget.getLayoutData();
if (d instanceof BoxLayoutData) {
layoutData = (BoxLayoutData) d;
} else {
layoutData = new BoxLayoutData();
}
Margins cm = layoutData.getMargins();
if (cm == null) {
cm = new Margins(0);
}
runningWidth += getWidgetWidth(widget);
runningWidth += cm.getLeft();
runningWidth += cm.getRight();
}
if (runningWidth > w) {
hasOverflow = true;
onOverflow();
} else {
hasOverflow = false;
onUnoverflow();
}
}
}
protected int getWidgetWidth(Widget widget) {
Integer w = widthMap.get(widget);
if (w != null) {
return w;
} else {
return widget.getOffsetWidth();
}
}
protected void hideComponent(Widget w) {
widthMap.put(w, w.getOffsetWidth());
hiddens.add(w);
w.setVisible(false);
}
protected void initMore() {
if (more == null) {
moreMenu = new Menu();
moreMenu.setEnableScrolling(isMoreMenuEnableScrolling());
moreMenu.addBeforeShowHandler(new BeforeShowHandler() {
@Override
public void onBeforeShow(BeforeShowEvent event) {
HBoxLayoutContainer.this.onBeforeShow(event);
}
});
more = new TextButton();
more.addStyleName("x-toolbar-more");
more.addStyleName(getAppearance().moreButtonStyle());
more.setData("x-ignore-width", true);
more.setData("gxt-more", "true");
more.setIcon(getAppearance().moreIcon());
more.setMenu(moreMenu);
}
if (more.getParent() != this) {
add(more);
}
}
/**
* Called before showing more menu.
* <p>
*
* @param event the before show event
* @since 4.0.3
*/
protected void onBeforeShow(BeforeShowEvent event) {
handlerRegistration.removeHandler();
clearMenu();
int widgetCount = getWidgetCount();
for (int i = 0; i < widgetCount; i++) {
Widget w = getWidget(i);
if (isHidden(w) && w != more) {
if (moreMenuItems != null && moreMenuItems.get(i) != null) {
addWidgetToMenuFromMoreMenuItemsMap(moreMenu, w, i);
} else {
addWidgetToMenu(moreMenu, w);
}
}
}
fireEvent(new OverflowEvent(moreMenu));
}
/**
* Add a menu from the more menu items map. Menu item will not hide on click by default.
* <p>
*
* @param moreMenu the more items menu
* @param widget the widget that doesn't fit
* @param index the widget index
* @since 4.0.3
*/
protected void addWidgetToMenuFromMoreMenuItemsMap(Menu moreMenu, Widget widget, int index) {
MenuItem menuItem = moreMenuItems.get(index);
menuItem.setHideOnClick(false);
moreMenu.add(menuItem);
}
protected boolean isHidden(Widget w) {
return hiddens != null && hiddens.contains(w);
}
@Override
protected void onInsert(int index, Widget child) {
super.onInsert(index, child);
child.addStyleName(CommonStyles.get().floatLeft());
}
@Override
protected void onRemove(Widget child) {
super.onRemove(child);
child.removeStyleName(CommonStyles.get().floatLeft());
}
protected void onOverflow() {
Size size = getElement().getStyleSize();
final int w = size.getWidth() - getScrollOffset() - triggerWidth;
boolean change = false;
int loopWidth = 0;
for (int i = 0; i < getWidgetCount(); i++) {
Widget widget = getWidget(i);
if (widget == more)
continue;
if (!(widget instanceof FillToolItem)) {
loopWidth += getWidgetWidth(widget);
BoxLayoutData data = (BoxLayoutData) widget.getLayoutData();
if (data != null && data.getMargins() != null) {
loopWidth += (data.getMargins().getLeft() + data.getMargins().getRight());
}
if (loopWidth >= w) {
if (!isHidden(widget)) {
change = true;
hideComponent(widget);
}
} else {
if (isHidden(widget)) {
change = true;
unhideComponent(widget);
}
}
}
}
if (hiddens != null && hiddens.size() > 0) {
initMore();
}
if (change) {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
forceLayout();
}
});
}
}
protected void onUnoverflow() {
boolean change = false;
for (int i = 0; i < getWidgetCount(); i++) {
Widget widget = getWidget(i);
if (widget == more)
continue;
if (isHidden(widget)) {
change = true;
unhideComponent(widget);
}
}
if (more != null && more.getParent() == HBoxLayoutContainer.this) {
change = true;
remove(more);
}
if (change) {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
forceLayout();
}
});
}
}
protected void unhideComponent(Widget w) {
if (hiddens.remove(w)) {
widthMap.remove(w);
w.setVisible(true);
}
}
/**
* Returns if the more menu has enabled scrolling.
*
* @return true or false
* @since 4.0.3
*/
public boolean isMoreMenuEnableScrolling() {
return moreMenuEnableScrolling;
}
/**
* Enable more menu scrolling.
*
* @param moreMenuEnableScrolling turn on enabled scrolling for more menu
* @since 4.0.3
*/
public void setMoreMenuEnableScrolling(boolean moreMenuEnableScrolling) {
this.moreMenuEnableScrolling = moreMenuEnableScrolling;
}
}
package com.sencha.gxt.test.client.layout_hboxlayoutcontainer;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.test.client.TestSample;
@TestSample(
name = "HBoxLayout with UiBinder",
description = { "Expectation, no rendering exceptions.", "Verify you can see two buttons.",
"No need to test more than browser." },
jiraIds = { 4735 })
public class HBoxWithUiBinderTestCase implements EntryPoint {
interface MyUiBinder extends UiBinder<Widget, HBoxWithUiBinderTestCase> {
}
private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);
@Override
public void onModuleLoad() {
Widget widget = uiBinder.createAndBindUi(this);
widget.setPixelSize(400, 400);
RootPanel.get().add(widget);
}
}
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:container="urn:import:com.sencha.gxt.widget.core.client.container"
xmlns:button="urn:import:com.sencha.gxt.widget.core.client.button"
xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client"
xmlns:tb="urn:import:com.sencha.gxt.widget.core.client.button">
<gxt:ContentPanel heading="Horizontal Box Layout" pixelSize="600, 500">
<container:HBoxLayoutContainer>
<container:child>
<button:TextButton text="Button Item 1"></button:TextButton>
</container:child>
<container:sibling>
<button:TextButton text="Button Item 2"></button:TextButton>
</container:sibling>
</container:HBoxLayoutContainer>
</gxt:ContentPanel>
</ui:UiBinder>
@branflake2267
Copy link
Author

To use this workaround.

  1. Copy the HBoxLayoutContainer to your source folder with the same package name
  2. Change the uibinder tag name to a sibling.

We'll aim to keep the name sibling in 4.0.4.

@branflake2267
Copy link
Author

This will be fixed in the 4.0.4 nightly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment