From 43ac679829a56474d0a51bcac9d19418e9331a7b Mon Sep 17 00:00:00 2001 From: Artem Zatsarynnyi Date: Mon, 24 Apr 2017 10:09:34 +0300 Subject: [PATCH] CHE-4774: Improvements for command toolbar (#4875) --- .../explorer/CommandsExplorerPresenter.java | 2 +- .../command/toolbar/CommandCreationGuide.java | 2 +- .../toolbar/CommandToolbarPresenter.java | 5 +- .../command/toolbar/CommandToolbarView.java | 3 +- .../toolbar/CommandToolbarViewImpl.java | 4 +- .../toolbar/CommandToolbarViewImpl.ui.xml | 2 +- .../toolbar/OpenCommandsPaletteButton.java | 36 +--- .../ide/command/toolbar/ToolbarButton.java | 46 +++++ .../ide/command/toolbar/ToolbarMessages.java | 5 +- .../button/ExecuteCommandButtonFactory.java | 4 +- .../ExecuteCommandButtonItemsProvider.java | 9 +- .../toolbar/commands/button/GuideItem.java | 8 +- .../commands/button/MenuItemsFactory.java | 3 +- .../previews/PreviewUrlItemRenderer.java | 9 - .../toolbar/previews/PreviewsPresenter.java | 17 +- .../toolbar/previews/PreviewsViewImpl.java | 69 ++++++-- .../toolbar/processes/EmptyListWidget.java | 71 +------- .../toolbar/processes/ProcessWidget.java | 4 +- .../processes/ProcessesListPresenter.java | 11 +- .../toolbar/processes/ProcessesListView.java | 3 + .../processes/ProcessesListViewImpl.java | 68 +++++++- .../toolbar/ToolbarMessages.properties | 3 +- .../che/ide/command/toolbar/button.css | 37 ++++ .../ide/command/toolbar/processes/styles.css | 2 - ...ExecuteCommandButtonItemsProviderTest.java | 5 +- .../che/ide/ui/dropdown/DropdownList.java | 158 +++++++----------- .../che/ide/ui/dropdown/DropdownList.ui.xml | 6 +- .../ui/dropdown/DropdownListResources.java | 2 +- .../che/ide/ui/dropdown/DropdownMenu.java | 102 +++++++++++ .../che/ide/ui/menubutton/ActionHandler.java | 4 +- .../che/ide/ui/menubutton/MenuButton.java | 140 +++++++++------- .../eclipse/che/ide/ui/dropdown/styles.css | 5 +- .../{expansionIcon.svg => arrowIcon.svg} | 0 .../eclipse/che/ide/ui/menubutton/button.css | 18 +- .../che/ide/ui/menubutton/rightArrowIcon.svg | 18 -- .../java/org/eclipse/che/ide/ui/Tooltip.java | 1 - 36 files changed, 537 insertions(+), 345 deletions(-) create mode 100644 ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/ToolbarButton.java create mode 100644 ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/command/toolbar/button.css create mode 100644 ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownMenu.java rename ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/menubutton/{expansionIcon.svg => arrowIcon.svg} (100%) delete mode 100644 ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/menubutton/rightArrowIcon.svg diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/explorer/CommandsExplorerPresenter.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/explorer/CommandsExplorerPresenter.java index f714647fb52..2305f4fcd0f 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/explorer/CommandsExplorerPresenter.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/explorer/CommandsExplorerPresenter.java @@ -119,7 +119,7 @@ public void start(Callback callback) { refreshView(); - eventBus.addHandler(CommandAddedEvent.getType(), e -> refreshView()); + eventBus.addHandler(CommandAddedEvent.getType(), e -> refreshViewAndSelectCommand(e.getCommand())); eventBus.addHandler(CommandRemovedEvent.getType(), e -> refreshView()); eventBus.addHandler(CommandUpdatedEvent.getType(), e -> refreshView()); } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/CommandCreationGuide.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/CommandCreationGuide.java index bc25808a3ee..53e23fcd1d9 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/CommandCreationGuide.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/CommandCreationGuide.java @@ -52,7 +52,7 @@ public CommandCreationGuide(Provider workspaceAgentProvider, } - /** Shows the guide of creating a command. */ + /** Shows the guide of creating a command of the 'Run' goal. */ public void guide() { guide(runGoal); } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/CommandToolbarPresenter.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/CommandToolbarPresenter.java index 23ed2eb6b8e..45dc24e6d32 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/CommandToolbarPresenter.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/CommandToolbarPresenter.java @@ -18,12 +18,11 @@ import org.eclipse.che.ide.command.toolbar.commands.ExecuteCommandPresenter; import org.eclipse.che.ide.command.toolbar.previews.PreviewsPresenter; import org.eclipse.che.ide.command.toolbar.processes.ProcessesListPresenter; -import org.eclipse.che.ide.ui.menubutton.MenuButton; import javax.inject.Inject; import javax.inject.Singleton; -/** Presenter for command toolbar. */ +/** Presenter for the commands toolbar. */ @Singleton public class CommandToolbarPresenter implements Presenter, CommandToolbarView.ActionDelegate { @@ -32,7 +31,7 @@ public class CommandToolbarPresenter implements Presenter, CommandToolbarView.Ac private final ExecuteCommandPresenter executeCommandPresenter; private final ToolbarButtonsFactory toolbarButtonsFactory; private final CommandToolbarView view; - private MenuButton openCommandsPaletteButton; + private ToolbarButton openCommandsPaletteButton; @Inject public CommandToolbarPresenter(CommandToolbarView view, diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/CommandToolbarView.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/CommandToolbarView.java index 2dc892f6aab..de97a2eb29b 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/CommandToolbarView.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/CommandToolbarView.java @@ -13,7 +13,6 @@ import com.google.gwt.user.client.ui.AcceptsOneWidget; import org.eclipse.che.ide.api.mvp.View; -import org.eclipse.che.ide.ui.menubutton.MenuButton; /** View for command toolbar. */ public interface CommandToolbarView extends View { @@ -24,7 +23,7 @@ public interface CommandToolbarView extends View actionManagerProvider; private final Provider keyBindingAgentProvider; @@ -55,31 +47,7 @@ class OpenCommandsPaletteButton extends MenuButton { Provider keyBindingAgentProvider, Provider showCommandsPaletteActionProvider, @Assisted SafeHtml content) { - super(content, new ItemsProvider() { - @Override - public Optional getDefaultItem() { - return Optional.empty(); - } - - @Override - public List getItems() { - return emptyList(); - } - - @Override - public boolean isGroup(MenuItem item) { - return false; - } - - @Override - public Pair, String> getChildren(MenuItem parent) { - return null; - } - - @Override - public void setDataChangedHandler(DataChangedHandler handler) { - } - }); + super(content); this.actionManagerProvider = actionManagerProvider; this.keyBindingAgentProvider = keyBindingAgentProvider; diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/ToolbarButton.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/ToolbarButton.java new file mode 100644 index 00000000000..9f7b04ac522 --- /dev/null +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/ToolbarButton.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * 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.command.toolbar; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Document; +import com.google.gwt.resources.client.ClientBundle; +import com.google.gwt.resources.client.CssResource; +import com.google.gwt.safehtml.shared.SafeHtml; +import com.google.gwt.user.client.ui.FocusWidget; + +/** Button for the commands toolbar. */ +class ToolbarButton extends FocusWidget { + + private static final Resources RESOURCES; + + ToolbarButton(SafeHtml content) { + super(Document.get().createDivElement()); + + getElement().setInnerSafeHtml(content); + + addStyleName(RESOURCES.css().button()); + } + + public interface Resources extends ClientBundle { + @Source({"button.css", "org/eclipse/che/ide/api/ui/style.css"}) + Css css(); + } + + public interface Css extends CssResource { + String button(); + } + + static { + RESOURCES = GWT.create(Resources.class); + RESOURCES.css().ensureInjected(); + } +} diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/ToolbarMessages.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/ToolbarMessages.java index 182f60f5686..a8972df25ec 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/ToolbarMessages.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/ToolbarMessages.java @@ -20,7 +20,7 @@ public interface ToolbarMessages extends Messages { @Key("guide.label") - String guideItemLabel(); + String guideItemLabel(String goalName); @Key("goal_button.tooltip.no_command") String goalButtonTooltipNoCommand(String goalId); @@ -37,6 +37,9 @@ public interface ToolbarMessages extends Messages { @Key("previews.tooltip") String previewsTooltip(); + @Key("previews.no_previews") + String previewsNoPreviews(); + @Key("previews.error.not_available") String previewsNotAvailableError(); } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/ExecuteCommandButtonFactory.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/ExecuteCommandButtonFactory.java index f06a3c30f6b..ee4c36909f3 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/ExecuteCommandButtonFactory.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/ExecuteCommandButtonFactory.java @@ -73,7 +73,9 @@ public ExecuteCommandButtonFactory(CommandResources resources, * @return {@link ExecuteCommandButton} */ public ExecuteCommandButton newButton(CommandGoal goal, ActionDelegate delegate, @Nullable CharCodeWithModifiers keyBinding) { - final ExecuteCommandButtonItemsProvider itemsProvider = new ExecuteCommandButtonItemsProvider(appContext, menuItemsFactory); + final ExecuteCommandButtonItemsProvider itemsProvider = new ExecuteCommandButtonItemsProvider(appContext, + menuItemsFactory, + goal); final ExecuteCommandButton button = new ExecuteCommandButton(goal, getIconForGoal(goal), itemsProvider, diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/ExecuteCommandButtonItemsProvider.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/ExecuteCommandButtonItemsProvider.java index 23806390fc9..639ccedc5c6 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/ExecuteCommandButtonItemsProvider.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/ExecuteCommandButtonItemsProvider.java @@ -11,6 +11,7 @@ package org.eclipse.che.ide.command.toolbar.commands.button; import org.eclipse.che.ide.api.app.AppContext; +import org.eclipse.che.ide.api.command.CommandGoal; import org.eclipse.che.ide.api.command.CommandImpl; import org.eclipse.che.ide.api.machine.MachineEntity; import org.eclipse.che.ide.ui.menubutton.ItemsProvider; @@ -29,16 +30,18 @@ /** Provides items for {@link ExecuteCommandButton}. */ public class ExecuteCommandButtonItemsProvider implements ItemsProvider { - private final Set commands; private final AppContext appContext; private final MenuItemsFactory menuItemsFactory; + private final CommandGoal goal; + private final Set commands; private DataChangedHandler dataChangedHandler; private MenuItem defaultItem; - ExecuteCommandButtonItemsProvider(AppContext appContext, MenuItemsFactory menuItemsFactory) { + ExecuteCommandButtonItemsProvider(AppContext appContext, MenuItemsFactory menuItemsFactory, CommandGoal goal) { this.appContext = appContext; this.menuItemsFactory = menuItemsFactory; + this.goal = goal; this.commands = new HashSet<>(); } @@ -64,7 +67,7 @@ public List getItems() { } if (items.isEmpty()) { - items.add(menuItemsFactory.newGuideItem()); + items.add(menuItemsFactory.newGuideItem(goal)); } return items; diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/GuideItem.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/GuideItem.java index 6092e8ee9f6..b399141a2ff 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/GuideItem.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/GuideItem.java @@ -11,7 +11,9 @@ package org.eclipse.che.ide.command.toolbar.commands.button; import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; +import org.eclipse.che.ide.api.command.CommandGoal; import org.eclipse.che.ide.command.toolbar.ToolbarMessages; import org.eclipse.che.ide.ui.menubutton.MenuItem; @@ -19,14 +21,16 @@ public class GuideItem implements MenuItem { private final ToolbarMessages messages; + private final CommandGoal goal; @Inject - public GuideItem(ToolbarMessages messages) { + public GuideItem(ToolbarMessages messages, @Assisted CommandGoal goal) { this.messages = messages; + this.goal = goal; } @Override public String getName() { - return messages.guideItemLabel(); + return messages.guideItemLabel(goal.getId()); } } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/MenuItemsFactory.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/MenuItemsFactory.java index bacaa509120..962be7cfee1 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/MenuItemsFactory.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/commands/button/MenuItemsFactory.java @@ -11,13 +11,14 @@ package org.eclipse.che.ide.command.toolbar.commands.button; import org.eclipse.che.api.core.model.machine.Machine; +import org.eclipse.che.ide.api.command.CommandGoal; import org.eclipse.che.ide.api.command.CommandImpl; import org.eclipse.che.ide.ui.menubutton.MenuItem; /** Factory for {@link MenuItem}s for {@link ExecuteCommandButton}. */ public interface MenuItemsFactory { - GuideItem newGuideItem(); + GuideItem newGuideItem(CommandGoal goal); CommandItem newCommandItem(CommandImpl command); diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/previews/PreviewUrlItemRenderer.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/previews/PreviewUrlItemRenderer.java index dd9637734dc..a19e4978954 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/previews/PreviewUrlItemRenderer.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/previews/PreviewUrlItemRenderer.java @@ -10,8 +10,6 @@ *******************************************************************************/ package org.eclipse.che.ide.command.toolbar.previews; -import elemental.dom.Element; - import com.google.gwt.core.client.GWT; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.user.client.ui.Label; @@ -19,14 +17,10 @@ import org.eclipse.che.ide.FontAwesome; import org.eclipse.che.ide.command.CommandResources; -import org.eclipse.che.ide.command.toolbar.ToolbarMessages; -import org.eclipse.che.ide.ui.Tooltip; import org.eclipse.che.ide.ui.dropdown.BaseListItem; import org.eclipse.che.ide.ui.dropdown.DropdownListItemRenderer; import static com.google.gwt.dom.client.Style.Unit.PX; -import static org.eclipse.che.ide.ui.menu.PositionController.HorizontalAlign.MIDDLE; -import static org.eclipse.che.ide.ui.menu.PositionController.VerticalAlign.BOTTOM; /** * Renders widgets for the 'Previews' list. Always returns the same @@ -62,7 +56,6 @@ public Widget renderListWidget() { private static class HeaderWidget extends Label { private static final CommandResources RESOURCES = GWT.create(CommandResources.class); - private static final ToolbarMessages MESSAGES = GWT.create(ToolbarMessages.class); HeaderWidget() { super(); @@ -72,8 +65,6 @@ private static class HeaderWidget extends Label { final SafeHtmlBuilder safeHtmlBuilder = new SafeHtmlBuilder(); safeHtmlBuilder.appendHtmlConstant(FontAwesome.BULLSEYE); getElement().setInnerSafeHtml(safeHtmlBuilder.toSafeHtml()); - - Tooltip.create((Element)getElement(), BOTTOM, MIDDLE, MESSAGES.previewsTooltip()); } } } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/previews/PreviewsPresenter.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/previews/PreviewsPresenter.java index d6d7d60af6d..3a0901696ca 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/previews/PreviewsPresenter.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/previews/PreviewsPresenter.java @@ -18,6 +18,7 @@ import com.google.web.bindery.event.shared.EventBus; import org.eclipse.che.api.core.model.machine.Machine; +import org.eclipse.che.api.core.model.machine.MachineRuntimeInfo; import org.eclipse.che.api.core.model.machine.Server; import org.eclipse.che.api.core.model.workspace.WorkspaceRuntime; import org.eclipse.che.api.machine.shared.dto.execagent.GetProcessResponseDto; @@ -37,7 +38,7 @@ import org.eclipse.che.ide.api.mvp.Presenter; import org.eclipse.che.ide.command.toolbar.ToolbarMessages; -import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import static com.google.common.base.Strings.isNullOrEmpty; @@ -98,7 +99,7 @@ private void updateView() { if (runtime != null) { runtime.getMachines().forEach(machine -> execAgentClient.getProcesses(machine.getId(), false).then(processes -> { - processes.forEach(process -> getPreviewUrl(process.getPid(), machine).then(view::addUrl)); + processes.forEach(process -> getPreviewUrl(process.getPid(), machine).then(view::addUrl).catchError(ignore -> {})); })); } } @@ -132,12 +133,20 @@ private Promise getPreviewUrl(int pid, Machine machine) { private Optional getPreviewUrlDisplayName(String previewUrl) { final DevMachine devMachine = appContext.getDevMachine(); - final Map servers = devMachine.getRuntime().getServers(); + final MachineRuntimeInfo devMachineRuntime = devMachine.getRuntime(); - for (Map.Entry entry : servers.entrySet()) { + if (devMachineRuntime == null) { + return Optional.empty(); + } + + for (Entry entry : devMachineRuntime.getServers().entrySet()) { Server server = entry.getValue(); String serverUrl = server.getUrl(); + if (serverUrl == null) { + continue; + } + if (previewUrl.startsWith(serverUrl)) { String displayName = previewUrl.replace(serverUrl, devMachine.getDisplayName() + ':' + entry.getKey()); diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/previews/PreviewsViewImpl.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/previews/PreviewsViewImpl.java index ec521c08e34..d5339f0d08b 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/previews/PreviewsViewImpl.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/previews/PreviewsViewImpl.java @@ -10,18 +10,24 @@ *******************************************************************************/ package org.eclipse.che.ide.command.toolbar.previews; +import elemental.dom.Element; + import com.google.gwt.user.client.ui.Widget; import com.google.inject.Inject; import com.google.inject.Singleton; +import org.eclipse.che.ide.command.toolbar.ToolbarMessages; +import org.eclipse.che.ide.ui.Tooltip; import org.eclipse.che.ide.ui.dropdown.BaseListItem; import org.eclipse.che.ide.ui.dropdown.DropdownList; +import org.eclipse.che.ide.ui.dropdown.StringItemRenderer; import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; import static org.eclipse.che.ide.command.toolbar.previews.PreviewUrlItemRenderer.HEADER_WIDGET; +import static org.eclipse.che.ide.ui.menu.PositionController.HorizontalAlign.MIDDLE; +import static org.eclipse.che.ide.ui.menu.PositionController.VerticalAlign.BOTTOM; /** Implementation of {@link PreviewsView} that displays preview URLs in a dropdown list. */ @Singleton @@ -30,25 +36,41 @@ public class PreviewsViewImpl implements PreviewsView { /** Mapping of URL to list item. */ private final Map> listItems; - private final DropdownList dropdownList; + private final DropdownList dropdownList; + private final NoPreviewsItem noPreviewsItem; + private final NoPreviewsItemRenderer noPreviewsItemRenderer; + private final ToolbarMessages messages; private ActionDelegate delegate; @Inject - public PreviewsViewImpl() { + public PreviewsViewImpl(ToolbarMessages messages) { + this.messages = messages; listItems = new HashMap<>(); - dropdownList = new DropdownList(HEADER_WIDGET); + dropdownList = new DropdownList(HEADER_WIDGET, false); dropdownList.setWidth("43px"); dropdownList.ensureDebugId("dropdown-preview_url"); - dropdownList.setSelectionHandler(item -> { - for (Entry> entry : listItems.entrySet()) { - if (item.equals(entry.getValue())) { - delegate.onUrlChosen(entry.getKey()); - return; - } - } - }); + + dropdownList.setSelectionHandler(item -> listItems.entrySet() + .stream() + .filter(entry -> item.equals(entry.getValue())) + .findAny() + .ifPresent(entry -> delegate.onUrlChosen(entry.getKey()))); + + noPreviewsItem = new NoPreviewsItem(); + noPreviewsItemRenderer = new NoPreviewsItemRenderer(); + checkNoPreviewsItem(); + + Tooltip.create((Element)dropdownList.getElement(), BOTTOM, MIDDLE, messages.previewsTooltip()); + } + + private void checkNoPreviewsItem() { + if (listItems.isEmpty()) { + dropdownList.addItem(noPreviewsItem, noPreviewsItemRenderer); + } else { + dropdownList.removeItem(noPreviewsItem); + } } @Override @@ -72,6 +94,8 @@ public void addUrl(PreviewUrlItem previewUrlItem) { listItems.put(previewUrlItem, listItem); dropdownList.addItem(listItem, renderer); + + checkNoPreviewsItem(); } @Override @@ -80,6 +104,8 @@ public void removeUrl(PreviewUrlItem previewUrlItem) { if (listItem != null) { dropdownList.removeItem(listItem); + + checkNoPreviewsItem(); } } @@ -87,5 +113,24 @@ public void removeUrl(PreviewUrlItem previewUrlItem) { public void removeAllURLs() { listItems.clear(); dropdownList.clear(); + + checkNoPreviewsItem(); + } + + private class NoPreviewsItem extends BaseListItem { + NoPreviewsItem() { + super(messages.previewsNoPreviews()); + } + } + + private class NoPreviewsItemRenderer extends StringItemRenderer { + NoPreviewsItemRenderer() { + super(noPreviewsItem); + } + + @Override + public Widget renderHeaderWidget() { + return HEADER_WIDGET; + } } } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/EmptyListWidget.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/EmptyListWidget.java index bbc52292620..71b33fb1366 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/EmptyListWidget.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/EmptyListWidget.java @@ -16,87 +16,28 @@ import com.google.gwt.user.client.ui.Widget; import com.google.inject.Inject; import com.google.inject.Singleton; -import com.google.web.bindery.event.shared.EventBus; -import org.eclipse.che.ide.api.command.CommandAddedEvent; -import org.eclipse.che.ide.api.command.CommandManager; -import org.eclipse.che.ide.api.command.CommandRemovedEvent; -import org.eclipse.che.ide.api.command.CommandsLoadedEvent; import org.eclipse.che.ide.command.CommandResources; -import org.eclipse.che.ide.command.toolbar.CommandCreationGuide; -import org.eclipse.che.ide.command.toolbar.ToolbarMessages; /** - * Empty state widget for processes dropdown list. Has two states: - *
    - *
  • there is no command;
  • - *
  • there is no process.
  • - *
+ * Empty state widget for processes list. * * @see org.eclipse.che.ide.ui.dropdown.DropdownList#DropdownList(Widget) */ @Singleton -public class EmptyListWidget extends FlowPanel { - - private final CommandManager commandManager; - private final CommandCreationGuide commandCreationGuide; - - private FlowPanel noProcessWidget; - private FlowPanel noCommandWidget; +class EmptyListWidget extends FlowPanel { @Inject - public EmptyListWidget(CommandManager commandManager, CommandCreationGuide commandCreationGuide, EventBus eventBus) { - this.commandManager = commandManager; - this.commandCreationGuide = commandCreationGuide; - - eventBus.addHandler(CommandsLoadedEvent.getType(), e -> updateState()); - eventBus.addHandler(CommandAddedEvent.getType(), e -> updateState()); - eventBus.addHandler(CommandRemovedEvent.getType(), e -> updateState()); - } + EmptyListWidget(CommandResources resources) { + addStyleName(resources.commandToolbarCss().processWidgetText()); - @Inject - private void init(CommandResources resources, ToolbarMessages messages) { - // initialize widget for the state when there's no process final Label commandNameLabel = new InlineHTML("Ready"); - commandNameLabel.addStyleName(resources.commandToolbarCss().processWidgetText()); commandNameLabel.addStyleName(resources.commandToolbarCss().processWidgetCommandNameLabel()); final Label machineNameLabel = new InlineHTML("  - start command"); - machineNameLabel.addStyleName(resources.commandToolbarCss().processWidgetText()); machineNameLabel.addStyleName(resources.commandToolbarCss().processWidgetMachineNameLabel()); - noProcessWidget = new FlowPanel(); - noProcessWidget.addStyleName(resources.commandToolbarCss().processWidgetText()); - noProcessWidget.add(commandNameLabel); - noProcessWidget.add(machineNameLabel); - - - // initialize widget for the state when there's no command - final Label hintLabel = new InlineHTML(messages.guideItemLabel()); - hintLabel.addStyleName(resources.commandToolbarCss().processWidgetText()); - hintLabel.addStyleName(resources.commandToolbarCss().processWidgetCommandNameLabel()); - hintLabel.addClickHandler(event -> commandCreationGuide.guide()); - - noCommandWidget = new FlowPanel(); - noCommandWidget.addStyleName(resources.commandToolbarCss().processWidgetText()); - noCommandWidget.add(hintLabel); - } - - private void updateState() { - if (commandManager.getCommands().isEmpty()) { - showNoCommandWidget(); - } else { - showNoProcessWidget(); - } - } - - private void showNoCommandWidget() { - noProcessWidget.removeFromParent(); - add(noCommandWidget); - } - - private void showNoProcessWidget() { - noCommandWidget.removeFromParent(); - add(noProcessWidget); + add(commandNameLabel); + add(machineNameLabel); } } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessWidget.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessWidget.java index 87990407423..d6405a10089 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessWidget.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessWidget.java @@ -18,8 +18,8 @@ import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.ui.ButtonBase; import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.FocusWidget; import com.google.gwt.user.client.ui.InlineHTML; import com.google.gwt.user.client.ui.Label; @@ -160,7 +160,7 @@ private void checkStopped() { stopButton.setVisible(!stopped); } - private static class ActionButton extends ButtonBase { + private static class ActionButton extends FocusWidget { ActionButton(SafeHtml content) { super(Document.get().createDivElement()); diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessesListPresenter.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessesListPresenter.java index 36a0bd547c7..c2bfd1f9f90 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessesListPresenter.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessesListPresenter.java @@ -27,6 +27,7 @@ import org.eclipse.che.ide.api.machine.events.WsAgentStateEvent; import org.eclipse.che.ide.api.machine.events.WsAgentStateHandler; import org.eclipse.che.ide.api.mvp.Presenter; +import org.eclipse.che.ide.command.toolbar.CommandCreationGuide; import javax.inject.Inject; import javax.inject.Singleton; @@ -43,6 +44,7 @@ public class ProcessesListPresenter implements Presenter, ProcessesListView.Acti private final AppContext appContext; private final CommandManager commandManager; private final Provider commandExecutorProvider; + private final CommandCreationGuide commandCreationGuide; private final Map runningProcesses; @@ -52,13 +54,15 @@ public ProcessesListPresenter(final ProcessesListView view, final ExecAgentCommandManager execAgentClient, final AppContext appContext, CommandManager commandManager, - Provider commandExecutorProvider) { + Provider commandExecutorProvider, + CommandCreationGuide commandCreationGuide) { this.view = view; this.eventBus = eventBus; this.execAgentClient = execAgentClient; this.appContext = appContext; this.commandManager = commandManager; this.commandExecutorProvider = commandExecutorProvider; + this.commandCreationGuide = commandCreationGuide; view.setDelegate(this); @@ -163,4 +167,9 @@ public void onReRunProcess(Process process) { public void onStopProcess(Process process) { execAgentClient.killProcess(process.getMachine().getId(), process.getPid()); } + + @Override + public void onCreateCommand() { + commandCreationGuide.guide(); + } } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessesListView.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessesListView.java index d6c934b4712..8d790fcebf3 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessesListView.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessesListView.java @@ -37,5 +37,8 @@ interface ActionDelegate { /** Called when stopping process is requested. */ void onStopProcess(Process process); + + /** Called when new command creation is requested. */ + void onCreateCommand(); } } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessesListViewImpl.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessesListViewImpl.java index 37ccd80fd20..7467c23a3ca 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessesListViewImpl.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/toolbar/processes/ProcessesListViewImpl.java @@ -16,8 +16,10 @@ import com.google.inject.Singleton; import org.eclipse.che.ide.command.CommandResources; +import org.eclipse.che.ide.command.toolbar.ToolbarMessages; import org.eclipse.che.ide.ui.dropdown.BaseListItem; import org.eclipse.che.ide.ui.dropdown.DropdownList; +import org.eclipse.che.ide.ui.dropdown.StringItemRenderer; import javax.inject.Inject; import java.util.HashMap; @@ -27,34 +29,59 @@ @Singleton public class ProcessesListViewImpl implements ProcessesListView { - private final FlowPanel rootPanel; - private final DropdownList dropdownList; private final Map> listItems; private final Map renderers; + private final FlowPanel rootPanel; + private final DropdownList dropdownList; + private final EmptyListWidget emptyListWidget; + private final ToolbarMessages messages; + private final CreateCommandItem createCommandItem; + private final CreateCommandItemRenderer createCommandItemRenderer; private ActionDelegate delegate; @Inject - public ProcessesListViewImpl(CommandResources resources, EmptyListWidget emptyListWidget) { + public ProcessesListViewImpl(CommandResources resources, EmptyListWidget emptyListWidget, ToolbarMessages messages) { + this.emptyListWidget = emptyListWidget; + this.messages = messages; + listItems = new HashMap<>(); renderers = new HashMap<>(); final Label label = new Label("EXEC"); label.addStyleName(resources.commandToolbarCss().processesListLabel()); - dropdownList = new DropdownList(emptyListWidget); + dropdownList = new DropdownList(emptyListWidget, true); dropdownList.setWidth("100%"); dropdownList.ensureDebugId("dropdown-processes"); - dropdownList.syncWidths(); - dropdownList.setSelectionHandler(item -> listItems.entrySet() - .stream() - .filter(entry -> item.equals(entry.getValue())) - .forEach(entry -> delegate.onProcessChosen(entry.getKey()))); + dropdownList.setSelectionHandler(item -> { + if (item instanceof CreateCommandItem) { + delegate.onCreateCommand(); + } else { + listItems.entrySet() + .stream() + .filter(entry -> item.equals(entry.getValue())) + .forEach(entry -> delegate.onProcessChosen(entry.getKey())); + } + }); rootPanel = new FlowPanel(); rootPanel.add(label); rootPanel.add(dropdownList); + + createCommandItem = new CreateCommandItem(); + createCommandItemRenderer = new CreateCommandItemRenderer(); + checkCreateCommandItem(); + } + + /** Ensures that item for creating command added to the empty list or removed from non empty list. */ + private void checkCreateCommandItem() { + if (listItems.isEmpty()) { + dropdownList.addItem(createCommandItem, createCommandItemRenderer); + } else { + dropdownList.removeItem(createCommandItem); + } } @Override @@ -70,6 +97,8 @@ public Widget asWidget() { @Override public void clearList() { dropdownList.clear(); + + checkCreateCommandItem(); } @Override @@ -92,6 +121,8 @@ public void addProcess(Process process) { renderers.put(process, renderer); dropdownList.addItem(listItem, renderer); + + checkCreateCommandItem(); } @Override @@ -103,6 +134,25 @@ public void removeProcess(Process process) { renderers.remove(process); dropdownList.removeItem(listItem); + + checkCreateCommandItem(); + } + } + + private class CreateCommandItem extends BaseListItem { + CreateCommandItem() { + super(messages.guideItemLabel("new")); + } + } + + private class CreateCommandItemRenderer extends StringItemRenderer { + CreateCommandItemRenderer() { + super(createCommandItem); + } + + @Override + public Widget renderHeaderWidget() { + return emptyListWidget; } } } diff --git a/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/command/toolbar/ToolbarMessages.properties b/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/command/toolbar/ToolbarMessages.properties index 58389f3d25a..cd362495abe 100644 --- a/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/command/toolbar/ToolbarMessages.properties +++ b/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/command/toolbar/ToolbarMessages.properties @@ -9,7 +9,7 @@ # Codenvy, S.A. - initial API and implementation # -guide.label=Create and start command +guide.label=Create {0} command goal_button.tooltip.no_command=No command defined for {0}. Configure it in Commands panel goal_button.tooltip.choose_command=Choose command of {0} goal to execute @@ -17,4 +17,5 @@ goal_button.tooltip.execute=Execute ''{0}'' goal_button.tooltip.execute_on_machine=Execute ''{0}'' on ''{1}'' previews.tooltip=Previews +previews.no_previews=No commands running previews.error.not_available=Preview URL is not available diff --git a/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/command/toolbar/button.css b/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/command/toolbar/button.css new file mode 100644 index 00000000000..caf6bf7f408 --- /dev/null +++ b/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/command/toolbar/button.css @@ -0,0 +1,37 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +.button { + width: 32px; + height: 22px; + color: #4990e2; + background: menuButtonBackground; + border: menuButtonBorder; + font-size: 17px; + font-weight: normal; + font-style: normal; + font-stretch: normal; + text-align: center; + cursor: pointer; +} + +.button:focus { + outline: none; +} + +.button:hover { + color: #53a2ff; + background: hoveredMenuButtonBackground; +} + +.button:active { + color: #235c9e; + background: activeMenuButtonBackground; +} diff --git a/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/command/toolbar/processes/styles.css b/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/command/toolbar/processes/styles.css index 87c44689e26..50abd1e7243 100644 --- a/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/command/toolbar/processes/styles.css +++ b/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/command/toolbar/processes/styles.css @@ -37,8 +37,6 @@ .processWidgetText { font-family: mainFontFamily; font-size: fontSize; - color: selectCommandActionColor; - margin: 0 0 0 0; } .processWidgetMachineNameLabel { diff --git a/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/command/toolbar/commands/button/ExecuteCommandButtonItemsProviderTest.java b/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/command/toolbar/commands/button/ExecuteCommandButtonItemsProviderTest.java index 4d4dff2d2ca..3f497791016 100644 --- a/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/command/toolbar/commands/button/ExecuteCommandButtonItemsProviderTest.java +++ b/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/command/toolbar/commands/button/ExecuteCommandButtonItemsProviderTest.java @@ -12,6 +12,7 @@ import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.command.CommandImpl; +import org.eclipse.che.ide.command.goal.RunGoal; import org.eclipse.che.ide.ui.menubutton.ItemsProvider.DataChangedHandler; import org.eclipse.che.ide.ui.menubutton.MenuItem; import org.junit.Test; @@ -38,6 +39,8 @@ public class ExecuteCommandButtonItemsProviderTest { private AppContext appContext; @Mock private MenuItemsFactory menuItemsFactory; + @Mock + private RunGoal goal; @InjectMocks private ExecuteCommandButtonItemsProvider provider; @@ -99,7 +102,7 @@ public void testRemoveCommand() throws Exception { @Test public void shouldProvideGuideItemOnlyWhenNoCommands() throws Exception { GuideItem guideItem = mock(GuideItem.class); - when(menuItemsFactory.newGuideItem()).thenReturn(guideItem); + when(menuItemsFactory.newGuideItem(goal)).thenReturn(guideItem); assertThat(provider.getItems()).hasSize(1).containsOnly(guideItem); } diff --git a/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownList.java b/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownList.java index 0cd42f4bad9..4dcfc3afb90 100644 --- a/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownList.java +++ b/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownList.java @@ -14,12 +14,8 @@ 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.Window; import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.PopupPanel; -import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; @@ -27,8 +23,7 @@ import java.util.HashMap; import java.util.Map; - -import static com.google.gwt.user.client.ui.PopupPanel.AnimationType.ROLL_DOWN; +import java.util.Set; /** Dropdown list widget. */ public class DropdownList extends Composite { @@ -36,16 +31,11 @@ public class DropdownList extends Composite { private static final DropdownListUiBinder UI_BINDER = GWT.create(DropdownListUiBinder.class); private static final DropdownListResources RESOURCES = GWT.create(DropdownListResources.class); - /** Maximum amount of items that should visible in dropdown list without scrolling. */ - private static final int MAX_VISIBLE_ITEMS = 7; - /** Amount of pixels reserved for displaying one item in the dropdown list. */ - private static final int ITEM_WIDGET_HEIGHT = 22; - - private static final int DEFAULT_WIDGET_WIDTH_PX = 200; + /** Default width of this widget. */ + private static final int DEFAULT_WIDTH_PX = 200; - private final PopupPanel dropdownPopupPanel; - private final FlowPanel dropdownContentPanel; - private final Widget emptyStateWidget; + private final DropdownMenu menu; + private final Widget emptyStateWidget; private final Map itemListWidgets; private final Map itemHeaderWidgets; @@ -53,64 +43,64 @@ public class DropdownList extends Composite { @UiField SimplePanel selectedItemPanel; @UiField - SimplePanel dropButtonPanel; + SimplePanel dropdownMenuButton; private SelectionHandler selectionHandler; private DropdownListItem selectedItem; - /** Stores true if dropdown panels's width should be always synchronized with the list header's width. */ - private boolean widthsSynced; - - /** Creates new dropdown widget. */ - public DropdownList() { - this(new Label("---")); + /** + * 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 widget. - * Uses the given {@code emptyStateText} for displaying an empty list's state. + * 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) { - this(new Label(emptyStateText)); + public DropdownList(String emptyStateText, boolean syncWidths) { + this(new Label(emptyStateText), syncWidths); } /** - * Creates new dropdown widget. - * Uses the given {@code emptyStateWidget} for displaying an empty list's state. + * 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) { + public DropdownList(Widget emptyStateWidget, boolean syncWidths) { + this.emptyStateWidget = emptyStateWidget; itemListWidgets = new HashMap<>(); itemHeaderWidgets = new HashMap<>(); - this.emptyStateWidget = emptyStateWidget; - initWidget(UI_BINDER.createAndBindUi(this)); - dropButtonPanel.getElement().appendChild(RESOURCES.expansionImage().getSvg().getElement()); + setWidth(DEFAULT_WIDTH_PX + "px"); - dropdownContentPanel = new FlowPanel(); - dropdownContentPanel.ensureDebugId("dropdown-list-content-panel"); + menu = new DropdownMenu(this, syncWidths); + dropdownMenuButton.getElement().appendChild(RESOURCES.expansionImage().getSvg().getElement()); - dropdownPopupPanel = new PopupPanel(true); - dropdownPopupPanel.removeStyleName("gwt-PopupPanel"); - dropdownPopupPanel.addStyleName(RESOURCES.dropdownListCss().itemsPanel()); - dropdownPopupPanel.setAnimationEnabled(true); - dropdownPopupPanel.addAutoHidePartner(getElement()); - dropdownPopupPanel.setAnimationType(ROLL_DOWN); - dropdownPopupPanel.add(new ScrollPanel(dropdownContentPanel)); + addDomHandler(e -> menu.toggleMenuVisibility(), ClickEvent.getType()); - attachEventHandlers(); setSelectedItem(null); - - setWidth(DEFAULT_WIDGET_WIDTH_PX + "px"); } /** * {@inheritDoc} *

Note: this method sets the list header's width only. - * Use {@link #setDropdownPanelWidth(String)} to set the dropdown panels's width. + * Use {@link #setDropdownMenuWidth(String)} to set the dropdown menu's width. * - * @see #setDropdownPanelWidth(String) + * @see #setDropdownMenuWidth(String) */ @Override public void setWidth(String width) { @@ -118,47 +108,15 @@ public void setWidth(String width) { } /** - * Sets the dropdown panels's width. - * If it's not set explicitly then it will be calculated depending on the children 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 panels's new width, in CSS units (e.g. "10px", "1em") + * the dropdown menu's width, in CSS units (e.g. "10px", "1em") * @see #setWidth(String) */ - public void setDropdownPanelWidth(String width) { - dropdownPopupPanel.setWidth(width); - } - - /** Set the dropdown panels's width should be always synchronized with the list header's width. */ - public void syncWidths() { - widthsSynced = true; - Window.addResizeHandler(e -> setDropdownPanelWidth(getElement().getClientWidth() + "px")); - } - - /** Adapts dropdown panel's height depending on the amount of child items. */ - private void adaptDropDownPanelHeight() { - final int visibleRowsCount = Math.min(MAX_VISIBLE_ITEMS, itemListWidgets.size()); - final int dropdownPanelHeight = ITEM_WIDGET_HEIGHT * visibleRowsCount; - - dropdownPopupPanel.setHeight(dropdownPanelHeight + "px"); - } - - private void attachEventHandlers() { - selectedItemPanel.addDomHandler(e -> toggleListVisibility(), ClickEvent.getType()); - emptyStateWidget.addDomHandler(e -> toggleListVisibility(), ClickEvent.getType()); - dropButtonPanel.addDomHandler(e -> toggleListVisibility(), ClickEvent.getType()); - } - - private void toggleListVisibility() { - if (dropdownPopupPanel.isShowing()) { - dropdownPopupPanel.hide(); - } else { - dropdownPopupPanel.showRelativeTo(this); - - if (widthsSynced) { - setDropdownPanelWidth(getElement().getClientWidth() + "px"); - } - } + public void setDropdownMenuWidth(String width) { + menu.setWidth(width); } private void checkListEmptiness() { @@ -167,7 +125,7 @@ private void checkListEmptiness() { } } - /** Sets the given {@code handler} to notify it about changing selected item. */ + /** Sets the given {@link SelectionHandler}. */ public void setSelectionHandler(SelectionHandler handler) { selectionHandler = handler; } @@ -178,7 +136,7 @@ public DropdownListItem getSelectedItem() { return selectedItem; } - /** Set the given item as currently selected. Sets empty state widget if {@code null} were provided. */ + /** 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); @@ -199,20 +157,20 @@ public void addItem(DropdownListItem item, DropdownListItemRenderer renderer) { itemHeaderWidgets.put(item, headerWidget); itemListWidgets.put(item, listWidget); - headerWidget.addHandler(e -> toggleListVisibility(), ClickEvent.getType()); + headerWidget.addHandler(e -> menu.toggleMenuVisibility(), ClickEvent.getType()); listWidget.addStyleName(RESOURCES.dropdownListCss().listItem()); listWidget.addDomHandler(e -> { setSelectedItem(item); - dropdownPopupPanel.hide(); + menu.hide(); if (selectionHandler != null) { selectionHandler.onItemSelected(item); } }, ClickEvent.getType()); - dropdownContentPanel.insert(listWidget, 0); - adaptDropDownPanelHeight(); + menu.addWidget(listWidget); + setSelectedItem(item); } @@ -235,29 +193,33 @@ public BaseListItem addItem(String value) { public void removeItem(DropdownListItem item) { final Widget widget = itemListWidgets.remove(item); - if (widget != null) { - dropdownContentPanel.remove(widget); + if (widget == null) { + return; } + menu.removeWidget(widget); + itemHeaderWidgets.remove(item); - if (!itemListWidgets.isEmpty()) { - // set any available item as currently selected - setSelectedItem(itemListWidgets.entrySet().iterator().next().getKey()); - } else { + if (itemListWidgets.isEmpty()) { checkListEmptiness(); + } else if (item.equals(getSelectedItem())) { + setSelectedItem(itemListWidgets.keySet().iterator().next()); } + } - adaptDropDownPanelHeight(); + /** Returns all list's items. */ + public Set getItems() { + return itemListWidgets.keySet(); } /** Clear the list. */ public void clear() { itemListWidgets.clear(); itemHeaderWidgets.clear(); - dropdownContentPanel.clear(); - adaptDropDownPanelHeight(); + menu.removeAllWidgets(); + checkListEmptiness(); } diff --git a/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownList.ui.xml b/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownList.ui.xml index b26511621a4..a213151ec00 100644 --- a/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownList.ui.xml +++ b/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownList.ui.xml @@ -42,10 +42,6 @@ float: left; width: literal("calc(100% - 27px)"); padding-left: 5px; - color: inherit; - font-size: inherit; - overflow: hidden; - text-overflow: ellipsis; line-height: 21px; } @@ -65,6 +61,6 @@ - + diff --git a/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownListResources.java b/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownListResources.java index 894b4ea4870..53775f72646 100644 --- a/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownListResources.java +++ b/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownListResources.java @@ -26,7 +26,7 @@ public interface DropdownListResources extends ClientBundle { interface DropdownCss extends CssResource { - String itemsPanel(); + String menu(); String listItem(); } diff --git a/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownMenu.java b/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownMenu.java new file mode 100644 index 00000000000..7272f5fb016 --- /dev/null +++ b/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/dropdown/DropdownMenu.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * 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.user.client.Window; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.IsWidget; +import com.google.gwt.user.client.ui.PopupPanel; +import com.google.gwt.user.client.ui.ScrollPanel; + +import static com.google.gwt.user.client.ui.PopupPanel.AnimationType.ROLL_DOWN; + +/** Menu for {@link DropdownList}. */ +class DropdownMenu extends PopupPanel { + + private static final DropdownListResources RESOURCES = GWT.create(DropdownListResources.class); + + /** Maximum amount of widgets that should visible without scrolling. */ + private static final int MAX_VISIBLE_WIDGETS = 7; + /** Amount of pixels reserved for displaying one widget. */ + private static final int CHILD_WIDGET_HEIGHT = 22; + + private final DropdownList dropdownList; + private final FlowPanel contentPanel; + + /** Stores true if dropdown menu's width should be always synchronized with the dropdown header's width. */ + private boolean widthsSynced; + + /** + * Creates new dropdown menu. + * + * @param dropdownList + * a dropdown list to which the menu should be bound + * @param syncWidths + * specifies whether this dropdown menu's width always should be the same as the given {@code dropdownList}'s width + */ + DropdownMenu(DropdownList dropdownList, boolean syncWidths) { + super(true); + + this.dropdownList = dropdownList; + widthsSynced = syncWidths; + if (syncWidths) { + Window.addResizeHandler(e -> setWidth(dropdownList.getElement().getClientWidth() + "px")); + } + + removeStyleName("gwt-PopupPanel"); + addStyleName(RESOURCES.dropdownListCss().menu()); + + addAutoHidePartner(dropdownList.getElement()); + setAnimationEnabled(true); + setAnimationType(ROLL_DOWN); + + contentPanel = new FlowPanel(); + contentPanel.ensureDebugId("dropdown-list-content-panel"); + + add(new ScrollPanel(contentPanel)); + } + + void toggleMenuVisibility() { + if (isShowing()) { + hide(); + } else { + if (widthsSynced) { + setWidth(dropdownList.getElement().getClientWidth() + "px"); + } + + showRelativeTo(dropdownList); + } + } + + void addWidget(IsWidget widget) { + contentPanel.insert(widget, 0); + adaptMenuHeight(); + } + + void removeWidget(IsWidget widget) { + contentPanel.remove(widget); + adaptMenuHeight(); + } + + void removeAllWidgets() { + contentPanel.clear(); + adaptMenuHeight(); + } + + /** Adapts menu's height depending on the amount of the child widgets. */ + private void adaptMenuHeight() { + final int visibleRowsCount = Math.min(MAX_VISIBLE_WIDGETS, contentPanel.getWidgetCount()); + final int dropdownPanelHeight = CHILD_WIDGET_HEIGHT * visibleRowsCount; + + setHeight(dropdownPanelHeight + "px"); + } +} diff --git a/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/menubutton/ActionHandler.java b/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/menubutton/ActionHandler.java index 5e472c63773..6101e5d0736 100644 --- a/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/menubutton/ActionHandler.java +++ b/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/menubutton/ActionHandler.java @@ -14,10 +14,10 @@ public interface ActionHandler { /** - * Called when action (mouse click) on the {@code item} has been performed. + * Called when action on the {@code item} has been requested. * * @param item - * the item on which action has been performed + * the item on which action has been requested */ void onAction(MenuItem item); } diff --git a/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/menubutton/MenuButton.java b/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/menubutton/MenuButton.java index eb7cb7e4561..f6e4e80b9c5 100644 --- a/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/menubutton/MenuButton.java +++ b/ide/che-core-ide-ui/src/main/java/org/eclipse/che/ide/ui/menubutton/MenuButton.java @@ -12,41 +12,39 @@ import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Document; -import com.google.gwt.dom.client.NativeEvent; -import com.google.gwt.event.dom.client.MouseDownEvent; -import com.google.gwt.event.dom.client.MouseOutEvent; -import com.google.gwt.event.dom.client.MouseUpEvent; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.ui.ButtonBase; import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.FocusWidget; import org.vectomatic.dom.svg.ui.SVGResource; import java.util.List; import java.util.Optional; +import static com.google.gwt.dom.client.NativeEvent.BUTTON_LEFT; +import static java.util.Optional.ofNullable; + /** Button with popup menu. */ -public class MenuButton extends ButtonBase { +public class MenuButton extends FlowPanel { private static final Resources RESOURCES; protected final ItemsProvider itemsProvider; - private final Timer showMenuTimer; - private ActionHandler actionHandler; + private final Timer showMenuTimer; - private FlowPanel marker = new FlowPanel(); - private ItemsList menu; + private ActionHandler actionHandler; + private ItemsList menu; public MenuButton(SafeHtml content, ItemsProvider itemsProvider) { - super(Document.get().createDivElement()); + super(); this.itemsProvider = itemsProvider; - getElement().setInnerSafeHtml(content); + addStyleName(RESOURCES.css().menuButton()); showMenuTimer = new Timer() { @Override @@ -55,42 +53,30 @@ public void run() { } }; - addStyleName(RESOURCES.css().button()); - attachEventHandlers(); + final FocusWidget mainButton = new MainButton(content); + final FocusWidget dropButton = new DropButton(); - itemsProvider.setDataChangedHandler(this::updateButton); - } + add(mainButton); + add(dropButton); - private void attachEventHandlers() { - addDomHandler(event -> showMenuTimer.cancel(), MouseOutEvent.getType()); + attachMouseEventHandlers(mainButton); + attachMouseEventHandlers(dropButton); + } - addDomHandler(event -> { - if (event.getNativeButton() == NativeEvent.BUTTON_LEFT) { + private void attachMouseEventHandlers(FocusWidget widget) { + widget.addMouseOutHandler(event -> showMenuTimer.cancel()); + widget.addMouseUpHandler(event -> showMenuTimer.cancel()); + widget.addMouseDownHandler(event -> { + if (event.getNativeButton() == BUTTON_LEFT) { showMenuTimer.schedule(1000); } else { showMenuTimer.cancel(); } - }, MouseDownEvent.getType()); - - addDomHandler(event -> showMenuTimer.cancel(), MouseUpEvent.getType()); - - addClickHandler(event -> { - if (menu != null && menu.isShowing()) { - return; - } - - final Optional defaultItem = itemsProvider.getDefaultItem(); - - if (defaultItem.isPresent()) { - getActionHandler().ifPresent(actionHandler -> actionHandler.onAction(defaultItem.get())); - } else { - showMenu(); - } }); } public Optional getActionHandler() { - return Optional.ofNullable(actionHandler); + return ofNullable(actionHandler); } /** Set {@link ActionHandler} to handle {@link MenuItem}s selection. */ @@ -98,24 +84,12 @@ public void setActionHandler(ActionHandler actionHandler) { this.actionHandler = actionHandler; } - /** Shows or hides 'Open Menu' button. */ - private void updateButton() { - if (itemsProvider.getItems().isEmpty()) { - marker.removeFromParent(); - } else { - marker = new FlowPanel(); - marker.getElement().appendChild(RESOURCES.menuArrow().getSvg().getElement()); - marker.addStyleName(RESOURCES.css().expandedImage()); - getElement().appendChild(marker.getElement()); - } - } - private void showMenu() { - final List menuItems = itemsProvider.getItems(); + final List items = itemsProvider.getItems(); - if (!menuItems.isEmpty()) { - menu = new ItemsList(menuItems, itemsProvider, RESOURCES, null); - getActionHandler().ifPresent(actionHandler -> menu.setActionHandler(actionHandler)); + if (!items.isEmpty()) { + menu = new ItemsList(items, itemsProvider, RESOURCES, null); + getActionHandler().ifPresent(handler -> menu.setActionHandler(handler)); menu.setPopupPosition(getAbsoluteLeft(), getAbsoluteTop() + getOffsetHeight()); menu.show(); } @@ -123,17 +97,23 @@ private void showMenu() { public interface Resources extends ClientBundle { - @Source("rightArrowIcon.svg") - SVGResource menuArrow(); - @Source({"button.css", "org/eclipse/che/ide/api/ui/style.css"}) Css css(); + + @Source("arrowIcon.svg") + SVGResource arrowIcon(); } public interface Css extends CssResource { + String menuButton(); + String button(); + String mainButton(); + + String dropButton(); + String popupPanel(); String expandedImage(); @@ -147,6 +127,54 @@ public interface Css extends CssResource { String label(); } + private class MainButton extends FocusWidget { + + MainButton(SafeHtml content) { + super(Document.get().createDivElement()); + + getElement().setInnerSafeHtml(content); + + addStyleName(RESOURCES.css().button()); + addStyleName(RESOURCES.css().mainButton()); + + addClickHandler(event -> { + if (menu != null && menu.isShowing()) { + return; + } + + final Optional defaultItem = itemsProvider.getDefaultItem(); + + if (defaultItem.isPresent()) { + getActionHandler().ifPresent(actionHandler -> actionHandler.onAction(defaultItem.get())); + } else { + showMenu(); + } + }); + } + } + + private class DropButton extends FocusWidget { + + DropButton() { + super(Document.get().createDivElement()); + + addStyleName(RESOURCES.css().button()); + addStyleName(RESOURCES.css().dropButton()); + + final FlowPanel marker = new FlowPanel(); + marker.getElement().appendChild(RESOURCES.arrowIcon().getSvg().getElement()); + marker.addStyleName(RESOURCES.css().expandedImage()); + + getElement().appendChild(marker.getElement()); + + addClickHandler(event -> { + if (menu == null || !menu.isShowing()) { + showMenu(); + } + }); + } + } + static { RESOURCES = GWT.create(Resources.class); RESOURCES.css().ensureInjected(); diff --git a/ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/dropdown/styles.css b/ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/dropdown/styles.css index d7572f57bfc..4229164ed5b 100644 --- a/ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/dropdown/styles.css +++ b/ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/dropdown/styles.css @@ -8,7 +8,7 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -.itemsPanel { +.menu { background-color: openedFilesDropdownListBackgroundColor; border: 1px solid openedFilesDropdownListBorderColor; box-shadow: 0 2px 2px 0 openedFilesDropdownListShadowColor; @@ -16,11 +16,12 @@ } .listItem { + float: left; width: literal("calc(100% - 27px)"); - height: 22px; padding: 0 22px 0 5px; cursor: pointer; line-height: 22px; + white-space: nowrap; } .listItem:hover { diff --git a/ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/menubutton/expansionIcon.svg b/ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/menubutton/arrowIcon.svg similarity index 100% rename from ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/menubutton/expansionIcon.svg rename to ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/menubutton/arrowIcon.svg diff --git a/ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/menubutton/button.css b/ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/menubutton/button.css index 163c78acd98..cbb2a4f901e 100644 --- a/ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/menubutton/button.css +++ b/ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/menubutton/button.css @@ -8,12 +8,14 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ +.menuButton { + border: menuButtonBorder; +} + .button { - width: 32px; height: 22px; color: #4990e2; background: menuButtonBackground; - border: menuButtonBorder; font-size: 17px; font-weight: normal; font-style: normal; @@ -36,6 +38,17 @@ background: activeMenuButtonBackground; } +.mainButton { + float: left; + width: 22px; +} + +.dropButton { + float: right; + width: 13px; + margin-left: 1px; +} + .popupPanel { background-color: openedFilesDropdownListBackgroundColor; border: 1px solid openedFilesDropdownListBorderColor; @@ -52,7 +65,6 @@ .expandedImage { transform: scaleY(0.6) rotate(90deg); - margin-left: 1px; margin-top: 6px; float: right; } diff --git a/ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/menubutton/rightArrowIcon.svg b/ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/menubutton/rightArrowIcon.svg deleted file mode 100644 index 527260974f5..00000000000 --- a/ide/che-core-ide-ui/src/main/resources/org/eclipse/che/ide/ui/menubutton/rightArrowIcon.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - diff --git a/ide/commons-gwt/src/main/java/org/eclipse/che/ide/ui/Tooltip.java b/ide/commons-gwt/src/main/java/org/eclipse/che/ide/ui/Tooltip.java index 7385fc08def..d4d8af12de2 100644 --- a/ide/commons-gwt/src/main/java/org/eclipse/che/ide/ui/Tooltip.java +++ b/ide/commons-gwt/src/main/java/org/eclipse/che/ide/ui/Tooltip.java @@ -477,7 +477,6 @@ public Element renderDom() { content.appendChild(Elements.createTextNode(p)); if (i < tooltipText.length - 1) { content.appendChild(Elements.createBRElement()); - content.appendChild(Elements.createBRElement()); } i++; }