/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.ui.dropdown; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; import org.eclipse.che.commons.annotation.Nullable; import java.util.HashMap; import java.util.Map; import java.util.Set; /** Dropdown list widget. */ public class DropdownList extends Composite { private static final DropdownListUiBinder UI_BINDER = GWT.create(DropdownListUiBinder.class); private static final DropdownListResources RESOURCES = GWT.create(DropdownListResources.class); /** Default width of this widget. */ private static final int DEFAULT_WIDTH_PX = 200; private final DropdownMenu menu; private final Widget emptyStateWidget; private final Map<DropdownListItem, Widget> itemListWidgets; private final Map<DropdownListItem, Widget> itemHeaderWidgets; @UiField SimplePanel selectedItemPanel; @UiField SimplePanel dropdownMenuButton; private SelectionHandler selectionHandler; private DropdownListItem selectedItem; /** * Creates new dropdown list widget. * * @param syncWidths * specifies whether the dropdown menu's width always should be the same as this dropdown list's width */ public DropdownList(boolean syncWidths) { this(new Label("---"), syncWidths); } /** * Creates new dropdown list widget. * * @param emptyStateText * text that should be used for displaying an empty list's state * @param syncWidths * specifies whether the dropdown menu's width always should be the same as this dropdown list's width */ public DropdownList(String emptyStateText, boolean syncWidths) { this(new Label(emptyStateText), syncWidths); } /** * Creates new dropdown list widget. * * @param emptyStateWidget * widget that should be used for displaying an empty list's state * @param syncWidths * specifies whether the dropdown menu's width always should be the same as this dropdown list's width */ public DropdownList(Widget emptyStateWidget, boolean syncWidths) { this.emptyStateWidget = emptyStateWidget; itemListWidgets = new HashMap<>(); itemHeaderWidgets = new HashMap<>(); initWidget(UI_BINDER.createAndBindUi(this)); setWidth(DEFAULT_WIDTH_PX + "px"); menu = new DropdownMenu(this, syncWidths); dropdownMenuButton.getElement().appendChild(RESOURCES.expansionImage().getSvg().getElement()); addDomHandler(e -> menu.toggleMenuVisibility(), ClickEvent.getType()); setSelectedItem(null); } /** * {@inheritDoc} * <p><b>Note:</b> this method sets the list header's width only. * Use {@link #setDropdownMenuWidth(String)} to set the dropdown menu's width. * * @see #setDropdownMenuWidth(String) */ @Override public void setWidth(String width) { super.setWidth(width); } /** * Sets the dropdown menu's width. * If it's not set explicitly then it will be calculated depending on the content's width. * * @param width * the dropdown menu's width, in CSS units (e.g. "10px", "1em") * @see #setWidth(String) */ public void setDropdownMenuWidth(String width) { menu.setWidth(width); } private void checkListEmptiness() { if (itemListWidgets.isEmpty()) { setSelectedItem(null); } } /** Sets the given {@link SelectionHandler}. */ public void setSelectionHandler(SelectionHandler handler) { selectionHandler = handler; } /** Returns the currently selected item or {@code null} if none. */ @Nullable public DropdownListItem getSelectedItem() { return selectedItem; } /** Set the given item as currently selected. Sets empty state widget if {@code null} provided. */ private void setSelectedItem(@Nullable DropdownListItem item) { selectedItem = item; selectedItemPanel.setWidget(item != null ? itemHeaderWidgets.get(item) : emptyStateWidget); } /** * Add the given {@code item} to the top of the list. * * @param item * item to add to the list * @param renderer * renderer provides widgets for representing the given {@code item} in the list */ public void addItem(DropdownListItem item, DropdownListItemRenderer renderer) { final Widget headerWidget = renderer.renderHeaderWidget(); final Widget listWidget = new SimplePanel(renderer.renderListWidget()); itemHeaderWidgets.put(item, headerWidget); itemListWidgets.put(item, listWidget); headerWidget.addHandler(e -> menu.toggleMenuVisibility(), ClickEvent.getType()); listWidget.addStyleName(RESOURCES.dropdownListCss().listItem()); listWidget.addDomHandler(e -> { setSelectedItem(item); menu.hide(); if (selectionHandler != null) { selectionHandler.onItemSelected(item); } }, ClickEvent.getType()); menu.addWidget(listWidget); setSelectedItem(item); } /** * Short for quick adding text value to the list. * * @param value * text value to add to the list * @return added item which wraps the given {@code value} */ public BaseListItem<String> addItem(String value) { BaseListItem<String> item = new BaseListItem<>(value); addItem(item, new StringItemRenderer(item)); return item; } /** Remove item from the list. */ public void removeItem(DropdownListItem item) { final Widget widget = itemListWidgets.remove(item); if (widget == null) { return; } menu.removeWidget(widget); itemHeaderWidgets.remove(item); if (itemListWidgets.isEmpty()) { checkListEmptiness(); } else if (item.equals(getSelectedItem())) { setSelectedItem(itemListWidgets.keySet().iterator().next()); } } /** Returns all list's items. */ public Set<DropdownListItem> getItems() { return itemListWidgets.keySet(); } /** Clear the list. */ public void clear() { itemListWidgets.clear(); itemHeaderWidgets.clear(); menu.removeAllWidgets(); checkListEmptiness(); } interface DropdownListUiBinder extends UiBinder<Widget, DropdownList> { } public interface SelectionHandler { /** * Called when currently selected item has been changed. * * @param item * currently selected item */ void onItemSelected(DropdownListItem item); } static { RESOURCES.dropdownListCss().ensureInjected(); } }