package com.baselet.gwt.client.view.widgets.propertiespanel;
import java.util.Collection;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay;
import com.google.gwt.user.client.ui.SuggestBox.SuggestionCallback;
import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
import com.google.gwt.user.client.ui.Widget;
/**
* The purpose of this class is to avoid mouse interaction with the palette as long as the suggestbox is visible
* a clean approach would be avoiding event-propagation (eg: with event.stopPropagation()) but unfortunately
* GWT DefaultSuggestionDisplay is very inflexible and it would be necessary to change the behavior of MenuBar.onBrowserEvent(Event event)
* This is not possible without copying many classes because of package-private visibility
*
* This alternative approach uses a timer to avoid palette interaction for some time after the popup closes to make sure mouseevents can be avoided
*
* The display also supports a maximum height and uses the width of the suggestionBox, if these
* limits are exceeded scrollbars will appear and it will auto scroll to the selected element.
*/
public class MySuggestionDisplay extends DefaultSuggestionDisplay {
/**
* padding from the browser frame to the top of the display box.
*/
private static final int DISPLAY_BOX_TOP_PADDING = 40;
private static final int DISPLAY_BOX_MIN_HEIGHT = 60;
private boolean paletteShouldIgnoreMouseClicks = false;
private final Timer popupHideTimer = new Timer() {
@Override
public void run() {
paletteShouldIgnoreMouseClicks = false;
}
};
@Override
protected PopupPanel createPopup() {
PopupPanel p = super.createPopup();
p.addCloseHandler(new CloseHandler<PopupPanel>() {
@Override
public void onClose(CloseEvent<PopupPanel> event) {
popupHideTimer.schedule(400);
}
});
return p;
}
@Override
protected void showSuggestions(SuggestBox suggestBox, Collection<? extends Suggestion> suggestions, boolean isDisplayStringHTML, boolean isAutoSelectEnabled, SuggestionCallback callback) {
getPopupPanel().getWidget().setWidth(suggestBox.getElement().getScrollWidth() + Unit.PX.getType());
getPopupPanel().getWidget().getElement().getStyle().setProperty("maxHeight",
Math.max(DISPLAY_BOX_MIN_HEIGHT, suggestBox.getElement().getAbsoluteTop() - DISPLAY_BOX_TOP_PADDING) + Unit.PX.getType());
super.showSuggestions(suggestBox, suggestions, isDisplayStringHTML, isAutoSelectEnabled, callback);
if (isSuggestionListShowing()) {
popupHideTimer.cancel();
paletteShouldIgnoreMouseClicks = true;
}
}
@Override
protected void moveSelectionDown() {
super.moveSelectionDown();
scrollToSelected();
}
@Override
protected void moveSelectionUp() {
super.moveSelectionUp();
scrollToSelected();
}
private void scrollToSelected() {
// since the DefaultSuggestionDisplay does not provide a way to access the selected Element
// we need to search for the "item-selected" class in the td tags
NodeList<Element> tdChilds = getPopupPanel().getWidget().getElement().getElementsByTagName("td");
for (int i = 0; i < tdChilds.getLength(); i++) {
Element e = tdChilds.getItem(i);
if (e.getClassName().contains("item-selected")) {
((ScrollPanel) getPopupPanel().getWidget()).setVerticalScrollPosition(e.getOffsetTop());
break;
}
}
}
@Override
protected Widget decorateSuggestionList(Widget suggestionList) {
// if the decoration is changed check the other methods, because some assume that there is only a scroll panel
suggestionList = new ScrollPanel(suggestionList);
return super.decorateSuggestionList(suggestionList);
}
public boolean getPaletteShouldIgnoreMouseClicks() {
return paletteShouldIgnoreMouseClicks;
}
};