/******************************************************************************* * 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.menu; import elemental.html.DivElement; import com.google.gwt.dom.client.Element; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Widget; import com.google.inject.Inject; import com.google.inject.Provider; import org.eclipse.che.ide.api.action.Action; import org.eclipse.che.ide.api.action.ActionEvent; import org.eclipse.che.ide.api.action.ActionGroup; import org.eclipse.che.ide.api.action.ActionManager; import org.eclipse.che.ide.api.action.ActionSelectedHandler; import org.eclipse.che.ide.api.action.CustomComponentAction; import org.eclipse.che.ide.api.action.IdeActions; import org.eclipse.che.ide.api.action.Presentation; import org.eclipse.che.ide.api.action.Separator; import org.eclipse.che.ide.api.keybinding.KeyBindingAgent; import org.eclipse.che.ide.api.parts.PerspectiveManager; import org.eclipse.che.ide.command.toolbar.CommandToolbarPresenter; import org.eclipse.che.ide.ui.toolbar.CloseMenuHandler; import org.eclipse.che.ide.ui.toolbar.MenuLockLayer; import org.eclipse.che.ide.ui.toolbar.PresentationFactory; import org.eclipse.che.ide.util.dom.Elements; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static org.eclipse.che.ide.util.dom.Elements.disableTextSelection; /** * Implements {@link MainMenuView} using standard GWT Menu Widgets * * @author Nikolay Zamosenchuk * @author Oleksii Orel */ public class MainMenuViewImpl extends Composite implements MainMenuView, CloseMenuHandler, ActionSelectedHandler { private final MenuResources resources; private final Provider<PerspectiveManager> managerProvider; private final PresentationFactory presentationFactory = new PresentationFactory(); /** Working table, cells of which are contains element of Menu. */ private final MenuBarTable table = new MenuBarTable(); private final FlowPanel rightPanel = new FlowPanel(); private final FlowPanel leftPanel = new FlowPanel(); /** Panel, which contains top menu. */ private final FlowPanel rootPanel = new FlowPanel(); /** Lock layer for displaying popup menus. */ private MenuLockLayer lockLayer; /** List Menu Bar items. */ private Map<Element, MenuBarItem> menuBarItems = new LinkedHashMap<>(); private Map<Action, MenuBarItem> action2barItem = new HashMap<>(); /** Store selected Menu Bar item. */ private MenuBarItem selectedMenuBarItem; private List<Action> leftVisibleActions = new ArrayList<>(); private List<Action> menuVisibleActions = new ArrayList<>(); private ActionManager actionManager; private KeyBindingAgent keyBindingAgent; /** Create new {@link MainMenuViewImpl} */ @Inject public MainMenuViewImpl(MenuResources resources, ActionManager actionManager, KeyBindingAgent keyBindingAgent, Provider<PerspectiveManager> managerProvider, CommandToolbarPresenter toolbarPresenter) { this.resources = resources; this.actionManager = actionManager; this.keyBindingAgent = keyBindingAgent; this.managerProvider = managerProvider; initWidget(rootPanel); disableTextSelection(rootPanel.getElement(), true); rootPanel.setStyleName(resources.menuCss().menuBar()); leftPanel.addStyleName(resources.menuCss().leftPanel()); table.setStyleName(resources.menuCss().menuBarTable()); table.setCellPadding(0); table.setCellSpacing(0); final DivElement triangleSeparator = Elements.createDivElement(resources.menuCss().triangleSeparator()); rightPanel.addStyleName(resources.menuCss().rightPanel()); rightPanel.addStyleName(resources.menuCss().commandToolbar()); rootPanel.add(leftPanel); rootPanel.add(table); rootPanel.getElement().appendChild((Element)triangleSeparator); rootPanel.add(rightPanel); toolbarPresenter.go(rightPanel::add); } @Override public void setDelegate(ActionDelegate delegate) { new Timer() { @Override public void run() { updateMenuActions(); } }.scheduleRepeating(1000); } /** Handle closing of all popup windows. */ public void onCloseMenu() { selectedMenuBarItem.setNormalState(); selectedMenuBarItem = null; lockLayer = null; } void updateMenuActions() { if (selectedMenuBarItem != null) { return; } List<Action> newMenuVisibleActions = new ArrayList<>(); expandActionGroup(IdeActions.GROUP_MAIN_MENU, newMenuVisibleActions, actionManager); if (!newMenuVisibleActions.equals(menuVisibleActions)) { removeAll(); for (final Action action : newMenuVisibleActions) { add(action, presentationFactory); } menuVisibleActions = newMenuVisibleActions; } List<Action> newLeftVisibleActions = new ArrayList<>(); expandActionGroup(IdeActions.GROUP_LEFT_MAIN_MENU, newLeftVisibleActions, actionManager); if (!newLeftVisibleActions.equals(leftVisibleActions)) { leftPanel.clear(); for (Action action : newLeftVisibleActions) { addToPanel(leftPanel, action, presentationFactory); } leftVisibleActions = newLeftVisibleActions; } } /** * Create a new widget and add it to panel menu. */ private void addToPanel(FlowPanel panel, Action action, PresentationFactory presentationFactory) { Presentation presentation = presentationFactory.getPresentation(action); if (action instanceof Separator) { panel.add(new SeparatorItem(resources.menuCss().panelSeparator())); // todo find way to render non custom actions } else if (action instanceof CustomComponentAction) { CustomComponentAction customComponentAction = (CustomComponentAction)action; Widget component = customComponentAction.createCustomComponent(presentation); component.addStyleName(resources.menuCss().customComponent()); panel.add(component); } } private void removeAll() { table.clear(); menuBarItems.clear(); action2barItem.clear(); } private void expandActionGroup(String actionGroupId, final List<Action> newVisibleActions, ActionManager actionManager) { final ActionGroup mainActionGroup = (ActionGroup)actionManager.getAction(actionGroupId); if (mainActionGroup == null) return; expandActionGroup(newVisibleActions, actionManager, mainActionGroup); } private void expandActionGroup(List<Action> newVisibleActions, ActionManager actionManager, ActionGroup mainActionGroup) { final Action[] children = mainActionGroup.getChildren(null); for (final Action action : children) { final Presentation presentation = presentationFactory.getPresentation(action); final ActionEvent e = new ActionEvent(presentation, actionManager, managerProvider.get()); action.update(e); if (presentation.isVisible()) { // add only visible items newVisibleActions.add(action); } if (action2barItem.containsKey(action)) { action2barItem.get(action).update(); } } } /** * Create and add new item in menu. */ private void add(Action action, PresentationFactory presentationFactory) { Presentation presentation = presentationFactory.getPresentation(action); if (action instanceof ActionGroup) { ActionGroup group = (ActionGroup)action; table.setText(0, menuBarItems.size(), presentation.getText()); Element element = table.getCellFormatter().getElement(0, menuBarItems.size()); MenuBarItem item = new MenuBarItem(group, actionManager, managerProvider, presentationFactory, element, this, keyBindingAgent, resources.menuCss()); item.onMouseOut(); menuBarItems.put(element, item); action2barItem.put(group, item); } else if (action instanceof CustomComponentAction) { Widget widget = ((CustomComponentAction)action).createCustomComponent(presentation); table.setWidget(0, menuBarItems.size(), widget); Element element = table.getCellFormatter().getElement(0, menuBarItems.size()); menuBarItems.put(element, null); } } /** * Open Popup Menu. * * @param item * popup menu item. */ public void openPopupMenu(MenuBarItem item) { if (lockLayer == null) { int top = getAbsoluteTop() + getOffsetHeight(); lockLayer = new MenuLockLayer(this, top); } item.openPopupMenu(lockLayer); } @Override public void onActionSelected(Action action) { if (action2barItem.containsKey(action)) { MenuBarItem item = action2barItem.get(action); if (selectedMenuBarItem != null && selectedMenuBarItem != item) { selectedMenuBarItem.setNormalState(); selectedMenuBarItem.closePopupMenu(); } selectedMenuBarItem = item; } else { lockLayer.close(); lockLayer = null; } } private static class SeparatorItem extends Composite { public SeparatorItem(String styleName) { final FlowPanel widget = new FlowPanel(); widget.addStyleName(styleName); Element separator = widget.getElement(); for (int i = 0; i < 6; i++) { separator.appendChild(DOM.createDiv()); } initWidget(widget); } } /** * This is visual component. * Uses for handling mouse events on MenuBar. */ private class MenuBarTable extends FlexTable { public MenuBarTable() { sinkEvents(Event.ONMOUSEOVER | Event.ONMOUSEOUT | Event.ONMOUSEDOWN); } @Override public void onBrowserEvent(Event event) { Element td = getEventTargetCell(event); MenuBarItem item = menuBarItems.get(td); if (item == null) { return; } switch (DOM.eventGetType(event)) { case Event.ONMOUSEOVER: if (selectedMenuBarItem != null && item != selectedMenuBarItem) { if (item.onMouseDown()) { openPopupMenu(item); } } item.onMouseOver(); break; case Event.ONMOUSEOUT: item.onMouseOut(); break; case Event.ONMOUSEDOWN: if (item == selectedMenuBarItem) { if (lockLayer != null) { lockLayer.close(); } return; } if (item.onMouseDown()) { openPopupMenu(item); } break; default: break; } } } }