/******************************************************************************* * 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.plugin.languageserver.ide.quickopen; import elemental.dom.Element; import elemental.html.SpanElement; import com.google.gwt.core.client.Scheduler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.logical.shared.CloseEvent; import com.google.gwt.event.logical.shared.CloseHandler; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.TextBox; import com.google.inject.Inject; import org.eclipse.che.ide.Resources; import org.eclipse.che.ide.api.autocomplete.AutoCompleteResources; import org.eclipse.che.ide.filters.Match; import org.eclipse.che.ide.ui.list.SimpleList; import org.eclipse.che.ide.util.dom.Elements; import org.eclipse.che.plugin.languageserver.ide.LanguageServerResources; import org.eclipse.che.plugin.languageserver.ide.quickopen.QuickOpenEntry.Mode; import org.vectomatic.dom.svg.ui.SVGImage; import java.util.ArrayList; import java.util.List; /** * @author Evgen Vidolob * @author Dmitry Shnurenko */ public class QuickOpenViewImpl extends PopupPanel implements QuickOpenView { private final AutoCompleteResources.Css css; private final LanguageServerResources languageServerResources; private final SimpleList.ListItemRenderer<QuickOpenEntry> listItemRenderer = new SimpleList.ListItemRenderer<QuickOpenEntry>() { @Override public void render(Element itemElement, QuickOpenEntry itemData) { Element label = Elements.createSpanElement(css.proposalLabel()); Element icon = Elements.createSpanElement(css.proposalIcon()); Element group = Elements.createSpanElement(css.proposalGroup()); SafeHtmlBuilder builder = new SafeHtmlBuilder(); List<Match> highlights = itemData.getHighlights(); String text = itemData.getLabel(); int pos = 0; SpanElement spanElement = Elements.createSpanElement(); for (Match highlight : highlights) { if (highlight.getStart() == highlight.getEnd()) { continue; } if (pos < highlight.getStart()) { builder.appendHtmlConstant("<span>"); builder.appendEscaped(text.substring(pos, highlight.getStart())); builder.appendHtmlConstant("</span>"); } builder.appendHtmlConstant("<span class=\"" + languageServerResources.quickOpenListCss().searchMatch() + "\">"); builder.appendEscaped(text.substring(highlight.getStart(), highlight.getEnd())); builder.appendHtmlConstant("</span>"); pos = highlight.getEnd(); } if (pos < text.length()) { builder.appendHtmlConstant("<span>"); builder.appendEscaped(text.substring(pos)); builder.appendHtmlConstant("</span>"); } spanElement.setInnerHTML(builder.toSafeHtml().asString()); label.getStyle().setPaddingLeft("5px"); label.getStyle().setPaddingRight("5px"); if (itemData.getIcon() != null) { SVGImage svgImage = new SVGImage(itemData.getIcon()); icon.appendChild((elemental.dom.Node)svgImage.getElement()); itemElement.appendChild(icon); } if (itemData instanceof QuickOpenEntryGroup) { QuickOpenEntryGroup entryGroup = (QuickOpenEntryGroup)itemData; if (entryGroup.isWithBorder()) { Elements.addClassName(languageServerResources.quickOpenListCss().groupSeparator(), itemElement); } if (entryGroup.getGroupLabel() != null) { group.setInnerText(entryGroup.getGroupLabel()); } } else { if (itemData.getDescription() != null) { group.setInnerText(itemData.getDescription()); } } label.appendChild(spanElement); itemElement.appendChild(label); itemElement.appendChild(group); } @Override public Element createElement() { return Elements.createDivElement(); } }; @UiField TextBox nameField; @UiField DockLayoutPanel layoutPanel; @UiField FlowPanel actionsPanel; @UiField HTML actionsContainer; private ActionDelegate delegate; private Resources resources; private SimpleList<QuickOpenEntry> list; private QuickOpenModel model; private final SimpleList.ListEventDelegate<QuickOpenEntry> eventDelegate = new SimpleList.ListEventDelegate<QuickOpenEntry>() { @Override public void onListItemClicked(Element listItemBase, QuickOpenEntry itemData) { run(itemData, true); } @Override public void onListItemDoubleClicked(Element listItemBase, QuickOpenEntry itemData) { } }; @Inject public QuickOpenViewImpl(Resources resources, AutoCompleteResources autoCompleteResources, QuickOpenViewImplUiBinder uiBinder, LanguageServerResources languageServerResources) { this.resources = resources; this.languageServerResources = languageServerResources; css = autoCompleteResources.autocompleteComponentCss(); css.ensureInjected(); DockLayoutPanel rootElement = uiBinder.createAndBindUi(this); setWidget(rootElement); setAutoHideEnabled(true); setAnimationEnabled(true); layoutPanel.setWidgetHidden(actionsPanel, true); addCloseHandler(new CloseHandler<PopupPanel>() { @Override public void onClose(CloseEvent<PopupPanel> event) { delegate.onClose(event.isAutoClosed()); } }); } @Override public void setDelegate(ActionDelegate delegate) { this.delegate = delegate; } @Override public void focusOnInput() { nameField.setFocus(true); } @Override public void show(final String value) { super.show(); nameField.setValue(value); setPopupPositionAndShow(new PositionCallback() { @Override public void setPosition(int offsetWidth, int offsetHeight) { setPopupPosition((Window.getClientWidth() / 2) - (offsetWidth / 2), 60); } }); Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { @Override public void execute() { delegate.valueChanged(value); nameField.setFocus(true); } }); } @Override public void setModel(QuickOpenModel model) { this.model = model; actionsContainer.getElement().setInnerHTML(""); Element itemHolder = Elements.createDivElement(); itemHolder.setClassName(css.items()); actionsContainer.getElement().appendChild(((com.google.gwt.dom.client.Element)itemHolder)); list = SimpleList.create((SimpleList.View)actionsContainer.getElement().cast(), (Element)actionsContainer.getElement(), itemHolder, languageServerResources.quickOpenListCss(), listItemRenderer, eventDelegate); list.render(new ArrayList<>(model.getEntries())); layoutPanel.setWidgetHidden(actionsPanel, false); layoutPanel.setHeight("200px"); if (!nameField.getValue().isEmpty()) { list.getSelectionModel().setSelectedItem(0); run(list.getSelectionModel().getSelectedItem(), false); } } protected void run(QuickOpenEntry entry, boolean isOpen) { if (entry == null) { return; } if (model.run(entry, isOpen ? Mode.OPEN : Mode.PREVIEW)) { hide(false); } } @UiHandler("nameField") void handleKeyDown(KeyDownEvent event) { switch (event.getNativeKeyCode()) { case KeyCodes.KEY_UP: event.stopPropagation(); event.preventDefault(); list.getSelectionModel().selectPrevious(); run(list.getSelectionModel().getSelectedItem(), false); return; case KeyCodes.KEY_DOWN: event.stopPropagation(); event.preventDefault(); list.getSelectionModel().selectNext(); run(list.getSelectionModel().getSelectedItem(), false); return; case KeyCodes.KEY_PAGEUP: event.stopPropagation(); event.preventDefault(); list.getSelectionModel().selectPreviousPage(); run(list.getSelectionModel().getSelectedItem(), false); return; case KeyCodes.KEY_PAGEDOWN: event.stopPropagation(); event.preventDefault(); list.getSelectionModel().selectNextPage(); run(list.getSelectionModel().getSelectedItem(), false); return; case KeyCodes.KEY_ENTER: event.stopPropagation(); event.preventDefault(); run(list.getSelectionModel().getSelectedItem(), true); return; case KeyCodes.KEY_ESCAPE: event.stopPropagation(); event.preventDefault(); hide(true); return; } Scheduler.get().scheduleDeferred(new Command() { @Override public void execute() { delegate.valueChanged(nameField.getText()); } }); } interface QuickOpenViewImplUiBinder extends UiBinder<DockLayoutPanel, QuickOpenViewImpl> { } }