introduce autocomplete textfield with RSQL suggestion

Signed-off-by: Michael Hirsch <michael.hirsch@bosch-si.com>
This commit is contained in:
Michael Hirsch
2016-08-26 13:26:37 +02:00
parent 2f415c4839
commit 79b1ae8728
28 changed files with 1735 additions and 462 deletions

View File

@@ -1,44 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
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
-->
<!-- Copyright (c) 2015 Bosch Software Innovations GmbH and others. 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 -->
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.5.1//EN" "http://google-web-toolkit.googlecode.com/svn/tags/2.5.1/distro-source/core/src/gwt-module.dtd">
<module>
<!-- This file is automatically updated based on new dependencies by the
goal "vaadin:update-widgetset". -->
<!-- This file is automatically updated based on new dependencies by the goal "vaadin:update-widgetset". -->
<!-- Inherit DefaultWidgetSet -->
<inherits name="com.vaadin.DefaultWidgetSet" />
<!-- Inherit DefaultWidgetSet -->
<inherits name="com.vaadin.DefaultWidgetSet" />
<inherits
name="org.vaadin.hene.flexibleoptiongroup.widgetset.FlexibleOptionGroupWidgetset" />
<inherits name="org.vaadin.hene.flexibleoptiongroup.widgetset.FlexibleOptionGroupWidgetset" />
<inherits name="org.vaadin.tokenfield.TokenfieldWidgetset" />
<inherits name="org.vaadin.tokenfield.TokenfieldWidgetset" />
<inherits
name="org.vaadin.alump.distributionbar.gwt.DistributionBarWidgetset" />
<inherits name="org.vaadin.alump.distributionbar.gwt.DistributionBarWidgetset" />
<inherits name="org.vaadin.peter.contextmenu.ContextmenuWidgetset" />
<inherits name="org.vaadin.peter.contextmenu.ContextmenuWidgetset" />
<inherits
name="org.vaadin.hene.flexibleoptiongroup.widgetset.FlexibleOptionGroupWidgetset" />
<inherits name="org.vaadin.hene.flexibleoptiongroup.widgetset.FlexibleOptionGroupWidgetset" />
<inherits
name="org.vaadin.alump.distributionbar.gwt.DistributionBarWidgetset" />
<inherits name="org.vaadin.alump.distributionbar.gwt.DistributionBarWidgetset" />
<inherits name="org.eclipse.hawkbit.ui.customrenderers.CustomRendererWidgetSet" />
<inherits name="org.eclipse.hawkbit.ui.customrenderers.CustomRendererWidgetSet" />
<inherits name="org.eclipse.hawkbit.ui.filtermanagement.TextFieldSuggestionBox" />
<inherits name="org.vaadin.hene.flexibleoptiongroup.widgetset.FlexibleOptionGroupWidgetset" />
<inherits name="org.vaadin.hene.flexibleoptiongroup.widgetset.FlexibleOptionGroupWidgetset" />
<inherits name="org.vaadin.alump.distributionbar.gwt.DistributionBarWidgetset" />
<inherits name="org.vaadin.alump.distributionbar.gwt.DistributionBarWidgetset" />
</module>

View File

@@ -0,0 +1,255 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.ui.filtermanagement;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executor;
import javax.annotation.PostConstruct;
import org.eclipse.hawkbit.repository.rsql.RsqlValidationOracle;
import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder;
import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent;
import org.eclipse.hawkbit.ui.filtermanagement.state.FilterManagementUIState;
import org.eclipse.hawkbit.ui.utils.SPUIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.vaadin.spring.events.EventBus;
import com.vaadin.event.ShortcutListener;
import com.vaadin.server.FontAwesome;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;
import com.vaadin.ui.AbstractTextField.TextChangeEventMode;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
/**
* An textfield with the {@link TextFieldSuggestionBox} extension which shows
* suggestions in a suggestion-pop-up window while typing.
*/
@SpringComponent
@ViewScope
public class AutoCompleteTextFieldComponent extends HorizontalLayout {
private static final long serialVersionUID = 1L;
@Autowired
private FilterManagementUIState filterManagementUIState;
@Autowired
private transient EventBus.SessionEventBus eventBus;
@Autowired
private transient RsqlValidationOracle rsqlValidationOracle;
@Autowired
@Qualifier("uiExecutor")
private transient Executor executor;
private transient List<FilterQueryChangeListener> listeners = new LinkedList<>();
private final Label validationIcon;
private final TextField queryTextField;
/**
* Constructor.
*/
public AutoCompleteTextFieldComponent() {
queryTextField = createSearchField();
validationIcon = createStatusIcon();
setSizeUndefined();
setSpacing(true);
addStyleName("custom-search-layout");
addComponents(validationIcon, queryTextField);
setComponentAlignment(validationIcon, Alignment.TOP_CENTER);
}
/**
* Called by the spring-framework when this bean has be post-constructed.
*/
@PostConstruct
public void postConstruct() {
new TextFieldSuggestionBox(rsqlValidationOracle, this).extend(queryTextField);
}
/**
* Clears the textfield and resets the validation icon.
*/
public void clear() {
queryTextField.clear();
validationIcon.setValue(FontAwesome.CHECK_CIRCLE.getHtml());
validationIcon.setStyleName("hide-status-label");
}
@Override
public void focus() {
queryTextField.focus();
}
/**
* Adds the given listener
*
* @param textChangeListener
* the listener to be called in case of text changed
*/
public void addTextChangeListener(final FilterQueryChangeListener textChangeListener) {
listeners.add(textChangeListener);
}
public void setValue(final String textValue) {
queryTextField.setValue(textValue);
}
public String getValue() {
return queryTextField.getValue();
}
/**
* Called when the filter-query has been changed in the textfield, e.g. from
* client-side.
*
* @param currentText
* the current text of the textfield which has been changed
* @param valid
* {@code boolean} if the current text is RSQL syntax valid
* otherwise {@code false}
* @param validationMessage
* a message shown in case of syntax errors as tooltip
*/
public void onQueryFilterChange(final String currentText, final boolean valid, final String validationMessage) {
if (valid) {
showValidationSuccesIcon(currentText);
} else {
showValidationFailureIcon(validationMessage);
}
listeners.forEach(listener -> listener.queryChanged(valid, currentText));
}
/**
* Shows the validation success icon in the textfield
*
* @param text
* the text to store in the UI state object
*/
public void showValidationSuccesIcon(final String text) {
validationIcon.setValue(FontAwesome.CHECK_CIRCLE.getHtml());
validationIcon.setStyleName(SPUIStyleDefinitions.SUCCESS_ICON);
filterManagementUIState.setFilterQueryValue(text);
filterManagementUIState.setIsFilterByInvalidFilterQuery(Boolean.FALSE);
}
/**
* Shows the validation error icon in the textfield
*
* @param validationMessage
* the validation message which should be added to the error-icon
* tooltip
*/
public void showValidationFailureIcon(final String validationMessage) {
validationIcon.setValue(FontAwesome.TIMES_CIRCLE.getHtml());
validationIcon.setStyleName(SPUIStyleDefinitions.ERROR_ICON);
validationIcon.setDescription(validationMessage);
filterManagementUIState.setFilterQueryValue(null);
filterManagementUIState.setIsFilterByInvalidFilterQuery(Boolean.TRUE);
}
public boolean isValidationError() {
return validationIcon.getStyleName().equals(SPUIStyleDefinitions.ERROR_ICON);
}
private TextField createSearchField() {
final TextField textField = new TextFieldBuilder().immediate(true).id("custom.query.text.Id")
.maxLengthAllowed(SPUILabelDefinitions.TARGET_FILTER_QUERY_TEXT_FIELD_LENGTH).buildTextComponent();
textField.addStyleName("target-filter-textfield");
textField.setWidth(900.0F, Unit.PIXELS);
textField.setTextChangeEventMode(TextChangeEventMode.EAGER);
textField.setImmediate(true);
textField.setTextChangeTimeout(100);
textField.addShortcutListener(new EnterShortCutListener());
return textField;
}
private static Label createStatusIcon() {
final Label statusIcon = new Label();
statusIcon.setImmediate(true);
statusIcon.setContentMode(ContentMode.HTML);
statusIcon.setSizeFull();
setInitialStatusIconStyle(statusIcon);
statusIcon.setId(SPUIComponentIdProvider.VALIDATION_STATUS_ICON_ID);
return statusIcon;
}
private static void setInitialStatusIconStyle(final Label statusIcon) {
statusIcon.setValue(FontAwesome.CHECK_CIRCLE.getHtml());
statusIcon.setStyleName("hide-status-label");
}
class StatusCircledAsync implements Runnable {
private final UI current;
public StatusCircledAsync(final UI current) {
this.current = current;
}
@Override
public void run() {
UI.setCurrent(current);
eventBus.publish(this, CustomFilterUIEvent.FILTER_TARGET_BY_QUERY);
}
}
private final class EnterShortCutListener extends ShortcutListener {
private static final long serialVersionUID = 1L;
public EnterShortCutListener() {
super("Enter", KeyCode.ENTER, new int[0]);
}
@Override
public void handleAction(final Object sender, final Object target) {
if (!isValidationError()) {
showValidationInProgress();
executor.execute(new StatusCircledAsync(UI.getCurrent()));
}
}
private void showValidationInProgress() {
validationIcon.setValue(null);
validationIcon.addStyleName("show-status-label");
validationIcon.setStyleName(SPUIStyleDefinitions.TARGET_FILTER_SEARCH_PROGRESS_INDICATOR_STYLE);
}
}
/**
* Change listener on the textfield.
*/
@FunctionalInterface
public interface FilterQueryChangeListener {
/**
* Called when the text has been changed and validated.
*
* @param valid
* indicates if the entered query text is valid
* @param query
* the entered query text
*/
void queryChanged(final boolean valid, final String query);
}
}

View File

@@ -8,7 +8,7 @@
*/
package org.eclipse.hawkbit.ui.filtermanagement;
import java.util.concurrent.Executor;
import java.util.Optional;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@@ -23,6 +23,7 @@ import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder;
import org.eclipse.hawkbit.ui.components.SPUIButton;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder;
import org.eclipse.hawkbit.ui.filtermanagement.AutoCompleteTextFieldComponent.FilterQueryChangeListener;
import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent;
import org.eclipse.hawkbit.ui.filtermanagement.state.FilterManagementUIState;
import org.eclipse.hawkbit.ui.utils.I18N;
@@ -31,7 +32,6 @@ import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.eclipse.hawkbit.ui.utils.UINotification;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.vaadin.spring.events.EventBus;
import org.vaadin.spring.events.EventScope;
import org.vaadin.spring.events.annotation.EventBusListenerMethod;
@@ -40,16 +40,11 @@ import com.google.common.base.Strings;
import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.TextChangeEvent;
import com.vaadin.event.FieldEvents.TextChangeListener;
import com.vaadin.event.LayoutEvents.LayoutClickEvent;
import com.vaadin.event.LayoutEvents.LayoutClickListener;
import com.vaadin.event.ShortcutAction.KeyCode;
import com.vaadin.server.FontAwesome;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;
import com.vaadin.ui.AbstractField;
import com.vaadin.ui.AbstractTextField.TextChangeEventMode;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
@@ -57,7 +52,6 @@ import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Link;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.themes.ValoTheme;
@@ -94,11 +88,10 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button
private transient UiProperties uiProperties;
@Autowired
@Qualifier("uiExecutor")
private transient Executor executor;
private transient EntityFactory entityFactory;
@Autowired
private transient EntityFactory entityFactory;
private AutoCompleteTextFieldComponent queryTextField;
private HorizontalLayout breadcrumbLayout;
@@ -108,8 +101,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button
private Label headerCaption;
private TextField queryTextField;
private TextField nameTextField;
private Label nameLabel;
@@ -120,10 +111,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button
private Link helpLink;
private Label validationIcon;
private HorizontalLayout searchLayout;
private String oldFilterName;
private String oldFilterQuery;
@@ -136,8 +123,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button
private LayoutClickListener nameLayoutClickListner;
private boolean validationFailed = false;
/**
* Initialize the Campaign Status History Header.
*/
@@ -170,8 +155,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button
} else if (custFUIEvent == CustomFilterUIEvent.CREATE_NEW_FILTER_CLICK) {
setUpCaptionLayout(true);
resetComponents();
} else if (custFUIEvent == CustomFilterUIEvent.UPDATE_TARGET_FILTER_SEARCH_ICON) {
UI.getCurrent().access(() -> updateStatusIconAfterTablePopulated());
}
}
@@ -183,38 +166,22 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button
oldFilterQuery = filterManagementUIState.getTfQuery().get().getQuery();
}
breadcrumbName.setValue(nameLabel.getValue());
showValidationSuccesIcon();
queryTextField.showValidationSuccesIcon(filterManagementUIState.getFilterQueryValue());
titleFilterIconsLayout.addStyleName(SPUIStyleDefinitions.TARGET_FILTER_CAPTION_LAYOUT);
headerCaption.setVisible(false);
setUpCaptionLayout(false);
}
private void resetComponents() {
queryTextField.clear();
queryTextField.focus();
headerCaption.setVisible(true);
breadcrumbName.setValue(headerCaption.getValue());
nameLabel.setValue("");
queryTextField.setValue("");
setInitialStatusIconStyle(validationIcon);
validationFailed = false;
saveButton.setEnabled(false);
titleFilterIconsLayout.removeStyleName(SPUIStyleDefinitions.TARGET_FILTER_CAPTION_LAYOUT);
}
private Label createStatusIcon() {
final Label statusIcon = new Label();
statusIcon.setImmediate(true);
statusIcon.setContentMode(ContentMode.HTML);
statusIcon.setSizeFull();
setInitialStatusIconStyle(statusIcon);
statusIcon.setId(SPUIComponentIdProvider.VALIDATION_STATUS_ICON_ID);
return statusIcon;
}
private void setInitialStatusIconStyle(final Label statusIcon) {
statusIcon.setValue(FontAwesome.CHECK_CIRCLE.getHtml());
statusIcon.setStyleName("hide-status-label");
}
private void createComponents() {
breadcrumbButton = createBreadcrumbButton();
@@ -227,10 +194,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button
nameTextField = createNameTextField();
nameTextField.setWidth(380, Unit.PIXELS);
queryTextField = createSearchField();
addSearchLisenter();
validationIcon = createStatusIcon();
saveButton = createSaveButton();
helpLink = SPUIComponentProvider.getHelpLink(uiProperties.getLinks().getDocumentation().getTargetfilterView());
@@ -282,6 +245,14 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button
}
}
};
queryTextField.addTextChangeListener(new FilterQueryChangeListener() {
@Override
public void queryChanged(final boolean valid, final String query) {
enableDisableSaveButton(!valid, query);
}
});
}
private void onFilterNameChange(final TextChangeEvent event) {
@@ -318,15 +289,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button
titleFilterLayout.setComponentAlignment(titleFilterIconsLayout, Alignment.TOP_LEFT);
titleFilterLayout.setComponentAlignment(closeIcon, Alignment.TOP_RIGHT);
validationIcon = createStatusIcon();
searchLayout = new HorizontalLayout();
searchLayout.setSizeUndefined();
searchLayout.setSpacing(false);
searchLayout.addComponents(validationIcon, queryTextField);
searchLayout.addStyleName("custom-search-layout");
searchLayout.setComponentAlignment(validationIcon, Alignment.TOP_CENTER);
final HorizontalLayout iconLayout = new HorizontalLayout();
iconLayout.setSizeUndefined();
iconLayout.setSpacing(false);
@@ -335,7 +297,7 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button
final HorizontalLayout queryLayout = new HorizontalLayout();
queryLayout.setSizeUndefined();
queryLayout.setSpacing(true);
queryLayout.addComponents(searchLayout, iconLayout);
queryLayout.addComponents(queryTextField, iconLayout);
addComponent(breadcrumbLayout);
addComponent(titleFilterLayout);
@@ -358,58 +320,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button
}
}
private void addSearchLisenter() {
queryTextField.addTextChangeListener(new TextChangeListener() {
private static final long serialVersionUID = -6668604418942689391L;
@Override
public void textChange(final TextChangeEvent event) {
validationIcon.addStyleName("show-status-label");
showValidationInProgress();
onQueryChange(event.getText());
executor.execute(new StatusCircledAsync(UI.getCurrent()));
}
});
}
class StatusCircledAsync implements Runnable {
private final UI current;
public StatusCircledAsync(final UI current) {
this.current = current;
}
@Override
public void run() {
UI.setCurrent(current);
eventBus.publish(this, CustomFilterUIEvent.FILTER_TARGET_BY_QUERY);
}
}
private void onQueryChange(final String input) {
if (!Strings.isNullOrEmpty(input)) {
final ValidationResult validationResult = FilterQueryValidation.getExpectedTokens(input);
if (!validationResult.getIsValidationFailed()) {
filterManagementUIState.setFilterQueryValue(input);
filterManagementUIState.setIsFilterByInvalidFilterQuery(Boolean.FALSE);
validationFailed = false;
} else {
validationFailed = true;
filterManagementUIState.setFilterQueryValue(null);
filterManagementUIState.setIsFilterByInvalidFilterQuery(Boolean.TRUE);
validationIcon.setDescription(validationResult.getMessage());
showValidationFailureIcon();
}
enableDisableSaveButton(validationFailed, input);
} else {
setInitialStatusIconStyle(validationIcon);
filterManagementUIState.setFilterQueryValue(null);
filterManagementUIState.setIsFilterByInvalidFilterQuery(Boolean.TRUE);
}
queryTextField.setValue(input);
}
private void enableDisableSaveButton(final boolean validationFailed, final String query) {
if (validationFailed || (isNameAndQueryEmpty(nameTextField.getValue(), query)
|| (query.equals(oldFilterQuery) && nameTextField.getValue().equals(oldFilterName)))) {
@@ -428,21 +338,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button
return false;
}
private void showValidationSuccesIcon() {
validationIcon.setValue(FontAwesome.CHECK_CIRCLE.getHtml());
validationIcon.setStyleName(SPUIStyleDefinitions.SUCCESS_ICON);
}
private void showValidationFailureIcon() {
validationIcon.setValue(FontAwesome.TIMES_CIRCLE.getHtml());
validationIcon.setStyleName(SPUIStyleDefinitions.ERROR_ICON);
}
private void showValidationInProgress() {
validationIcon.setValue(null);
validationIcon.setStyleName(SPUIStyleDefinitions.TARGET_FILTER_SEARCH_PROGRESS_INDICATOR_STYLE);
}
private SPUIButton createSearchResetIcon() {
final SPUIButton button = (SPUIButton) SPUIComponentProvider.getButton("create.custom.filter.close.Id", "", "",
null, false, FontAwesome.TIMES, SPUIButtonStyleSmallNoBorder.class);
@@ -450,18 +345,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button
return button;
}
private static TextField createSearchField() {
final TextField textField = new TextFieldBuilder().immediate(true).id("custom.query.text.Id")
.maxLengthAllowed(SPUILabelDefinitions.TARGET_FILTER_QUERY_TEXT_FIELD_LENGTH).buildTextComponent();
textField.addStyleName("target-filter-textfield");
textField.setWidth(900.0F, Unit.PIXELS);
textField.setTextChangeEventMode(TextChangeEventMode.LAZY);
textField.setTextChangeTimeout(1000);
textField.addShortcutListener(new AbstractField.FocusShortcut(textField, KeyCode.ENTER));
return textField;
}
private void closeFilterLayout() {
filterManagementUIState.setFilterQueryValue(null);
filterManagementUIState.setCreateFilterBtnClicked(false);
@@ -508,7 +391,11 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button
}
private void updateCustomFilter() {
final TargetFilterQuery targetFilterQuery = filterManagementUIState.getTfQuery().get();
final Optional<TargetFilterQuery> tfQuery = filterManagementUIState.getTfQuery();
if (!tfQuery.isPresent()) {
return;
}
final TargetFilterQuery targetFilterQuery = tfQuery.get();
targetFilterQuery.setName(nameTextField.getValue());
targetFilterQuery.setQuery(queryTextField.getValue());
final TargetFilterQuery updatedTargetFilter = targetFilterQueryManagement
@@ -545,13 +432,6 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button
return true;
}
private void updateStatusIconAfterTablePopulated() {
queryTextField.focus();
if (!validationFailed && !Strings.isNullOrEmpty(queryTextField.getValue())) {
showValidationSuccesIcon();
}
}
private void showCustomFiltersView() {
eventBus.publish(this, CustomFilterUIEvent.SHOW_FILTER_MANAGEMENT);
}

View File

@@ -94,8 +94,7 @@ public class FilterManagementView extends VerticalLayout implements View {
void onEvent(final CustomFilterUIEvent custFilterUIEvent) {
if (custFilterUIEvent == CustomFilterUIEvent.TARGET_FILTER_DETAIL_VIEW) {
viewTargetFilterDetailLayout();
} else if (custFilterUIEvent == CustomFilterUIEvent.CREATE_NEW_FILTER_CLICK
|| custFilterUIEvent == CustomFilterUIEvent.FILTER_TARGET_BY_QUERY) {
} else if (custFilterUIEvent == CustomFilterUIEvent.CREATE_NEW_FILTER_CLICK) {
this.getUI().access(() -> viewCreateTargetFilterLayout());
} else if (custFilterUIEvent == CustomFilterUIEvent.EXIT_CREATE_OR_UPDATE_FILTRER_VIEW
|| custFilterUIEvent == CustomFilterUIEvent.SHOW_FILTER_MANAGEMENT) {

View File

@@ -1,182 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.ui.filtermanagement;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.ui.utils.SpringContextHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import cz.jirutka.rsql.parser.ParseException;
import cz.jirutka.rsql.parser.RSQLParserException;
/**
*
* Validates the target filter query.
*
*/
public final class FilterQueryValidation {
private static final Logger LOGGER = LoggerFactory.getLogger(FilterQueryValidation.class);
private FilterQueryValidation() {
}
/**
* method for get ExpectedTokens.
*
* @param input
* @param entityManager
* @return
*/
public static ValidationResult getExpectedTokens(final String input) {
final TokenDescription tokenDesc = new TokenDescription();
final ValidationResult result = new ValidationResult();
final List<String> expectedTokens = new ArrayList<>();
try {
final TargetManagement management = SpringContextHelper.getBean(TargetManagement.class);
management.findTargetsAll(input, new PageRequest(0, 100));
} catch (final RSQLParameterSyntaxException ex) {
setExceptionDetails(new Exception(ex.getCause().getCause()), expectedTokens, result, tokenDesc);
result.setMessage(getCustomMessage(ex.getCause().getMessage(), result.getExpectedTokens()));
result.setIsValidationFailed(Boolean.TRUE);
LOGGER.trace("Syntax exception on parsing :", ex);
} catch (final RSQLParserException ex) {
setExceptionDetails(ex, expectedTokens, result, tokenDesc);
result.setMessage(getCustomMessage(ex.getMessage(), result.getExpectedTokens()));
result.setIsValidationFailed(Boolean.TRUE);
LOGGER.trace("Exception on parsing :", ex);
} catch (final IllegalArgumentException ex) {
result.setMessage(getCustomMessage(ex.getMessage(), null));
result.setIsValidationFailed(Boolean.TRUE);
LOGGER.trace("Illegal argument on parsing :", ex);
} catch (final RSQLParameterUnsupportedFieldException ex) {
result.setMessage(getCustomMessage(ex.getMessage(), null));
result.setIsValidationFailed(Boolean.TRUE);
LOGGER.trace("Unsupported field on parsing :", ex);
}
return result;
}
private static void setExceptionDetails(final Exception ex, final List<String> expectedTokens,
final ValidationResult result, final TokenDescription tokenDesc) {
for (final Integer node : getNextTokens(ex)) {
if (node != 12) {
expectedTokens.add(tokenDesc.getTokenImage()[node]);
}
}
final List<String> customExpectTokenList = processExpectedTokens(getNextTokens(ex));
if (!customExpectTokenList.isEmpty()) {
result.setExpectedTokens(customExpectTokenList);
} else {
result.setExpectedTokens(expectedTokens);
}
}
/**
* method for process ExpectedTokens.
*
* @param expectedTokens
* @return
*/
// Exception squid:S2095 - see
// https://jira.sonarsource.com/browse/SONARJAVA-1478
@SuppressWarnings({ "squid:S2095" })
public static List<String> processExpectedTokens(final List<Integer> expectedTokens) {
final List<String> expectToken = new ArrayList<>();
if (expectedTokens.size() == 2 && expectedTokens.contains(9) && expectedTokens.contains(4)) {
final List<String> expectedFieldList = Arrays.stream(TargetFields.values()).map(v -> v.name().toLowerCase())
.collect(Collectors.toList());
expectToken.addAll(expectedFieldList);
expectToken.add("assignedds.name");
expectToken.add("assignedds.version");
}
return expectToken;
}
/**
* Method To Get Next Token.
*
* @param e
* .
* @return list.
*/
public static List<Integer> getNextTokens(final Exception e) {
Throwable parseException = e.getCause();
final List<Integer> listTokens = new ArrayList<>();
if (parseException != null) {
do {
if (parseException instanceof ParseException) {
try {
Field declaredField;
declaredField = parseException.getClass().getDeclaredField("expectedTokenSequences");
int[][] tokens;
tokens = (int[][]) declaredField.get(parseException);
for (final int[] is : tokens) {
for (final int i : is) {
listTokens.add(i);
}
}
return listTokens;
} catch (SecurityException | NoSuchFieldException | IllegalArgumentException
| IllegalAccessException ex) {
LOGGER.info("Exception on parsing :", ex);
}
} else {
return listTokens;
}
} while ((parseException = parseException.getCause()) != null);
}
return Collections.emptyList();
}
/**
* To Get Custom Message.
*
* @param message
* @param expectedTokens
* @return String.
*/
public static String getCustomMessage(final String message, final List<String> expectedTokens) {
String builder = message;
if (message.contains(":")) {
builder = message.substring(message.indexOf(':') + 1, message.length());
if (builder.indexOf("Was expecting") != -1) {
builder = builder.substring(0, builder.lastIndexOf("Was expecting"));
}
if (null != expectedTokens && !expectedTokens.isEmpty()) {
final StringBuilder tokens = new StringBuilder();
expectedTokens.stream().forEach(value -> tokens.append(value + ","));
builder = builder.concat("Was expecting :" + tokens.toString().substring(0, tokens.length() - 1));
}
builder = builder.replace('\r', ' ');
builder = builder.replace('\n', ' ');
builder = builder.replaceAll(">", " ");
builder = builder.replaceAll("<", " ");
}
return builder;
}
}

View File

@@ -114,6 +114,7 @@ public class TargetFilterHeader extends VerticalLayout {
}
private void addNewFilter() {
filterManagementUIState.setTfQuery(null);
filterManagementUIState.setFilterQueryValue(null);
filterManagementUIState.setCreateFilterBtnClicked(true);
eventBus.publish(this, CustomFilterUIEvent.CREATE_NEW_FILTER_CLICK);

View File

@@ -225,7 +225,6 @@ public class TargetFilterTable extends Table {
final String targetFilterName = (String) ((Button) event.getComponent()).getData();
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.findTargetFilterQueryByName(targetFilterName);
filterManagementUIState.setTfQuery(targetFilterQuery);
filterManagementUIState.setFilterQueryValue(targetFilterQuery.getQuery());
filterManagementUIState.setEditViewDisplayed(true);

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<module>
<inherits name="com.vaadin.DefaultWidgetSet" />
<inherits name="org.eclipse.hawkbit.ui.AppWidgetSet" />
<inherits name="org.vaadin.peter.contextmenu.ContextmenuWidgetset" />
<inherits name="org.vaadin.tokenfield.TokenfieldWidgetset" />
<inherits name="org.eclipse.hawkbit.ui.customrenderers.CustomRendererWidgetSet" />
<inherits name="org.vaadin.hene.flexibleoptiongroup.widgetset.FlexibleOptionGroupWidgetset" />
<inherits name="org.vaadin.alump.distributionbar.gwt.DistributionBarWidgetset" />
</module>

View File

@@ -0,0 +1,83 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.ui.filtermanagement;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.rsql.RsqlValidationOracle;
import org.eclipse.hawkbit.repository.rsql.SuggestionContext;
import org.eclipse.hawkbit.repository.rsql.ValidationOracleContext;
import org.eclipse.hawkbit.ui.filtermanagement.client.SuggestTokenDto;
import org.eclipse.hawkbit.ui.filtermanagement.client.SuggestionContextDto;
import org.eclipse.hawkbit.ui.filtermanagement.client.TextFieldSuggestionBoxClientRpc;
import org.eclipse.hawkbit.ui.filtermanagement.client.TextFieldSuggestionBoxServerRpc;
import com.vaadin.server.AbstractExtension;
import com.vaadin.ui.TextField;
/**
* Extension for the AutoCompleteTexfield.
*
*/
public class TextFieldSuggestionBox extends AbstractExtension implements TextFieldSuggestionBoxServerRpc {
private static final long serialVersionUID = 1L;
private final transient RsqlValidationOracle rsqlValidationOracle;
private final AutoCompleteTextFieldComponent autoCompleteTextFieldComponent;
/**
* Constructor.
*
* @param autoCompleteTextFieldComponent
* @param rsqlValidationOracle
* the suggestion oracle where to retrieve the suggestions from
*/
public TextFieldSuggestionBox(final RsqlValidationOracle rsqlValidationOracle,
final AutoCompleteTextFieldComponent autoCompleteTextFieldComponent) {
this.rsqlValidationOracle = rsqlValidationOracle;
this.autoCompleteTextFieldComponent = autoCompleteTextFieldComponent;
registerRpc(this, TextFieldSuggestionBoxServerRpc.class);
}
/**
* Add this extension to the target connector. This method is protected to
* allow subclasses to require a more specific type of target.
*
* @param target
* the connector to attach this extension to
*/
public void extend(final TextField target) {
super.extend(target);
}
@Override
public void suggest(final String text, final int cursor) {
final ValidationOracleContext suggest = rsqlValidationOracle.suggest(text, cursor);
updateValidationIcon(suggest, text);
getRpcProxy(TextFieldSuggestionBoxClientRpc.class).showSuggestions(mapToDto(suggest.getSuggestionContext()));
}
private static SuggestionContextDto mapToDto(final SuggestionContext suggestionContext) {
return new SuggestionContextDto(suggestionContext.getCursorPosition(),
suggestionContext.getSuggestions().stream()
.filter(suggestion -> suggestion.getTokenImageName() == null
|| suggestion.getSuggestion().contains(suggestion.getTokenImageName()))
.map(suggestion -> new SuggestTokenDto(suggestion.getStart(), suggestion.getEnd(),
suggestion.getSuggestion()))
.collect(Collectors.toList()));
}
private void updateValidationIcon(final ValidationOracleContext suggest, final String text) {
final String errorMessage = (suggest.getSyntaxErrorContext() != null)
? suggest.getSyntaxErrorContext().getErrorMessage() : null;
autoCompleteTextFieldComponent.onQueryFilterChange(text, !suggest.isSyntaxError(), errorMessage);
}
}

View File

@@ -1,30 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.ui.filtermanagement;
import java.util.Arrays;
/**
*
* Available token details.
*
*
*
*/
public class TokenDescription {
/** Literal token values. */
private static final String[] TOKEN_IMAGE = { "<EOF>", "\" \"", "\"\\t\"", "<ALPHA>", "<UNRESERVED_STR>",
"<SINGLE_QUOTED_STR>", "<DOUBLE_QUOTED_STR>", "<AND>", "<OR>", "\"(\"", "\")\"", "<==>|<!=>", ">=|<=", };
public String[] getTokenImage() {
return Arrays.copyOf(TOKEN_IMAGE, TOKEN_IMAGE.length);
}
}

View File

@@ -1,73 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.ui.filtermanagement;
import java.util.ArrayList;
import java.util.List;
/**
* Query validation result with expected token on error.
*
*
*
*/
public class ValidationResult {
private List<String> expectedTokens = new ArrayList<>();
private String message;
private Boolean isValidationFailed = Boolean.FALSE;
/**
* @return the isValidationFailed
*/
public Boolean getIsValidationFailed() {
return isValidationFailed;
}
/**
* @param isValidationFailed
* the isValidationFailed to set
*/
public void setIsValidationFailed(final Boolean isValidationFailed) {
this.isValidationFailed = isValidationFailed;
}
/**
* @return the expectedTokens
*/
public List<String> getExpectedTokens() {
return expectedTokens;
}
/**
* @param expectedTokens
* the expectedTokens to set
*/
public void setExpectedTokens(final List<String> expectedTokens) {
this.expectedTokens = expectedTokens;
}
/**
* @return the message
*/
public String getMessage() {
return message;
}
/**
* @param message
* the message to set
*/
public void setMessage(final String message) {
this.message = message;
}
}

View File

@@ -0,0 +1,137 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.ui.filtermanagement.client;
import java.util.List;
import org.eclipse.hawkbit.ui.filtermanagement.TextFieldSuggestionBox;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.ui.MenuItem;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.extensions.AbstractExtensionConnector;
import com.vaadin.client.ui.VOverlay;
import com.vaadin.client.ui.VTextField;
import com.vaadin.shared.ui.Connect;
/**
* Connector for the AutoCompleteTextField which automatically listens to
* key-events to show pop-up panel with entered suggestions based on the
* {@link TextFieldSuggestionBoxServerRpc} call.
*
*/
@SuppressWarnings({ "deprecation", "squid:CallToDeprecatedMethod" })
// need to use VOverlay because otherwise it's not in the correct theme
// widget @see com.vaadin.client.ui.VOverlay.getOverlayContainer()
@Connect(TextFieldSuggestionBox.class)
public class AutoCompleteTextFieldConnector extends AbstractExtensionConnector {
private static final long serialVersionUID = 1L;
private final transient SuggestionsSelectList select = new SuggestionsSelectList();
private transient VTextField textFieldWidget;
private final TextFieldSuggestionBoxServerRpc rpc = getRpcProxy(TextFieldSuggestionBoxServerRpc.class);
private final transient VOverlay panel = new VOverlay(true, false, true);
@Override
protected void init() {
super.init();
registerRpc(TextFieldSuggestionBoxClientRpc.class, new TextFieldSuggestionBoxClientRpc() {
private static final long serialVersionUID = 1L;
@Override
public void showSuggestions(final SuggestionContextDto suggestContext) {
select.clearItems();
if (suggestContext != null) {
final List<SuggestTokenDto> suggestions = suggestContext.getSuggestions();
if (suggestions != null && !suggestions.isEmpty()) {
select.addItems(suggestions, textFieldWidget, panel, rpc);
panel.showRelativeTo(textFieldWidget);
select.moveSelectionDown();
return;
}
}
panel.hide();
}
});
}
@Override
protected void extend(final ServerConnector target) {
textFieldWidget = (VTextField) ((ComponentConnector) target).getWidget();
textFieldWidget.setImmediate(true);
textFieldWidget.textChangeEventMode = "EAGER";
panel.setWidget(select);
panel.setStyleName("suggestion-popup");
panel.setOwner(textFieldWidget);
textFieldWidget.addKeyUpHandler(new KeyUpHandler() {
@Override
public void onKeyUp(final KeyUpEvent event) {
if (panel.isAttached()) {
handlePanelEventDelegation(event);
} else {
doAskForSuggestion();
}
}
});
}
private void handlePanelEventDelegation(final KeyUpEvent event) {
switch (event.getNativeKeyCode()) {
case KeyCodes.KEY_DOWN:
arrowKeyDown(event);
break;
case KeyCodes.KEY_UP:
arrorKeyUp(event);
break;
case KeyCodes.KEY_ESCAPE:
escapeKey();
break;
case KeyCodes.KEY_ENTER:
enterKey();
break;
default:
doAskForSuggestion();
}
}
private void escapeKey() {
panel.hide();
}
private void enterKey() {
final MenuItem item = select.getSelectedItem();
if (item != null) {
item.getScheduledCommand().execute();
}
}
private void arrorKeyUp(final KeyUpEvent event) {
select.moveSelectionUp();
event.preventDefault();
event.stopPropagation();
}
private void arrowKeyDown(final KeyUpEvent event) {
select.moveSelectionDown();
event.preventDefault();
event.stopPropagation();
}
private void doAskForSuggestion() {
rpc.suggest(textFieldWidget.getValue(), textFieldWidget.getCursorPos());
}
}

View File

@@ -0,0 +1,68 @@
package org.eclipse.hawkbit.ui.filtermanagement.client;
import java.io.Serializable;
/**
* A suggestion which contains the start and the end character position of the
* suggested token of the suggestion of the token and the actual suggestion.
*/
public class SuggestTokenDto implements Serializable {
private static final long serialVersionUID = 1L;
private int start;
private int end;
private String suggestion;
/**
* Default constructor.
*/
public SuggestTokenDto() {
// necessary for java serialization with GWT.
}
/**
* Constructor.
*
* @param start
* the character position of the start of the token
* @param end
* the character position of the end of the token
* @param suggestion
* the token suggestion
*/
public SuggestTokenDto(final int start, final int end, final String suggestion) {
this.start = start;
this.end = end;
this.suggestion = suggestion;
}
public int getStart() {
return start;
}
public int getEnd() {
return end;
}
public String getSuggestion() {
return suggestion;
}
public void setStart(final int start) {
this.start = start;
}
public void setEnd(final int end) {
this.end = end;
}
public void setSuggestion(final String suggestion) {
this.suggestion = suggestion;
}
@Override
public String toString() {
return "SuggestTokenDto [start=" + start + ", end=" + end + ", suggestion=" + suggestion + "]";
}
}

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.ui.filtermanagement.client;
import java.io.Serializable;
import java.util.List;
public class SuggestionContextDto implements Serializable {
private static final long serialVersionUID = 1L;
private int cursorPosition;
private List<SuggestTokenDto> suggestions;
/**
* Default constructor.
*/
public SuggestionContextDto() {
// necessary for java serialization with GWT.
}
/**
* Constructor.
*
* @param rsqlQuery
* the original RSQL based query the suggestions based on
* @param cursorPosition
* the current cursor position
* @param suggestions
* the suggestions for the current cursor position
*/
public SuggestionContextDto(final int cursorPosition, final List<SuggestTokenDto> suggestions) {
this.cursorPosition = cursorPosition;
this.suggestions = suggestions;
}
public List<SuggestTokenDto> getSuggestions() {
return suggestions;
}
public int getCursorPosition() {
return cursorPosition;
}
public void setCursorPosition(final int cursorPosition) {
this.cursorPosition = cursorPosition;
}
public void setSuggestions(final List<SuggestTokenDto> suggestions) {
this.suggestions = suggestions;
}
@Override
public String toString() {
return "SuggestionContextDto [cursorPosition=" + cursorPosition + ", suggestions=" + suggestions + "]";
}
}

View File

@@ -0,0 +1,116 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.ui.filtermanagement.client;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.user.client.ui.PopupPanel;
import com.vaadin.client.WidgetUtil;
import com.vaadin.client.ui.VTextField;
/**
* The suggestion list within the suggestion pop-up panel.
*/
public class SuggestionsSelectList extends MenuBar {
public static final String CLASSNAME = "autocomplete";
private final Map<String, TokenStartEnd> tokenMap = new HashMap<>();
/**
* Constructor.
*/
public SuggestionsSelectList() {
super(true);
setFocusOnHoverEnabled(false);
}
/**
* Adds suggestions to the suggestion menu bar.
*
* @param suggestions
* the suggestions to be added
* @param textFieldWidget
* the text field which the suggestion is attached to to bring
* back the focus after selection
* @param popupPanel
* pop-up panel where the menu bar is shown to hide it after
* selection
* @param suggestionServerRpc
* server RPC to ask for new suggestion after a selection
*/
public void addItems(final List<SuggestTokenDto> suggestions, final VTextField textFieldWidget,
final PopupPanel popupPanel, final TextFieldSuggestionBoxServerRpc suggestionServerRpc) {
for (int index = 0; index < suggestions.size(); index++) {
final SuggestTokenDto suggestToken = suggestions.get(index);
final MenuItem mi = new MenuItem(suggestToken.getSuggestion(), true, new ScheduledCommand() {
@Override
public void execute() {
final String tmpSuggestion = suggestToken.getSuggestion();
final TokenStartEnd tokenStartEnd = tokenMap.get(tmpSuggestion);
final String text = textFieldWidget.getValue();
final StringBuilder builder = new StringBuilder(text);
builder.replace(tokenStartEnd.getStart(), tokenStartEnd.getEnd() + 1, tmpSuggestion);
textFieldWidget.setValue(builder.toString(), true);
popupPanel.hide();
textFieldWidget.setFocus(true);
suggestionServerRpc.suggest(builder.toString(), textFieldWidget.getCursorPos());
}
});
tokenMap.put(suggestToken.getSuggestion(),
new TokenStartEnd(suggestToken.getStart(), suggestToken.getEnd()));
Roles.getListitemRole().set(mi.getElement());
WidgetUtil.sinkOnloadForImages(mi.getElement());
addItem(mi);
}
}
@Override
public void setStyleName(final String style) {
super.setStyleName(style + "-" + CLASSNAME);
}
@Override
public MenuItem getSelectedItem() {
return super.getSelectedItem();
}
/**
* Suggestion Token start and end index.
*
*/
public static final class TokenStartEnd {
final int start;
final int end;
/**
* Constructor.
*
* @param start
* @param end
*/
public TokenStartEnd(final int start, final int end) {
this.start = start;
this.end = end;
}
public int getStart() {
return start;
}
public int getEnd() {
return end;
}
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.ui.filtermanagement.client;
import com.vaadin.shared.communication.ClientRpc;
/**
* Client RPC for the AutocompleteTextField. The Client RPC interface is used to
* make server to client calls in Vaadin. Only void methods are allowed in
* ClientRpc calls.
*
*/
@FunctionalInterface
public interface TextFieldSuggestionBoxClientRpc extends ClientRpc {
/**
* Notifies the client about showing the given suggestions in the suggestion
* box.
*
* @param suggestionContext
* the suggestion context which contains all informations about
* showing suggestions
*/
void showSuggestions(final SuggestionContextDto suggestionContext);
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.ui.filtermanagement.client;
import com.vaadin.shared.communication.ServerRpc;
/**
* Server RPC for the AutoCompleteTextField. The Server RPC interface is used to
* make client to server calls in Vaadin. Only void methods are allowed in
* ServerRpc calls.
*/
@FunctionalInterface
public interface TextFieldSuggestionBoxServerRpc extends ServerRpc {
/**
* Parses the given RSQL based query and try finding suggestions at the
* current given cursor position. When suggestions are possible the
* {@link TextFieldSuggestionBoxClientRpc#showSuggestions(org.eclipse.hawkbit.rsql.SuggestionContext)}
* is called as a callback mechanism back to the client.
*
* @param text
* the current entered text e.g. in a text field to retrieve
* suggestion for
* @param cursor
* the current cursor position
*/
void suggest(final String text, final int cursor);
}

View File

@@ -8,6 +8,39 @@
*/
@mixin target-filter-query {
.gwt-MenuBar-autocomplete {
cursor: default;
}
.gwt-MenuBar-autocomplete .gwt-MenuItem{
border-radius: 3px !important;
cursor: pointer !important;
font-weight: 400 !important;
line-height: 27px !important;
padding: 0 20px 0 10px !important;
position: relative !important;
white-space: nowrap !important;
}
.gwt-MenuBar-autocomplete .gwt-MenuItem-selected {
background-color: #197de1 !important;
background-image: linear-gradient(to bottom, #1b87e3 2%, #166ed5 98%) !important;
color: #ecf2f8 !important;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.05) !important;
}
.suggestion-popup{
backface-visibility: hidden;
background-color: white;
border-radius: 4px;
box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.1), 0 3px 5px 0 rgba(0, 0, 0, 0.05), 0 0 0 1px rgba(0, 0, 0, 0.09);
box-sizing: border-box;
color: #474747;
padding: 4px;
position: relative;
z-index: 1;
}
.caption-header-layout{
padding-left:10px;
}
@@ -23,15 +56,17 @@
.target-filter-textfield, .target-filter-textfield:focus{
border:none !important;
box-shadow: none !important;
height:26px !important;
height: 26px !important;
}
.error-icon{
margin-left: 5px;
color:$success-icon-color !important;
padding-left:2px !important;
}
.success-icon{
margin-left: 5px;
color:$error-icon-color !important;
padding-left:2px !important;
}
@@ -42,10 +77,12 @@
}
.target-filter-spinner{
@include valo-spinner(
$size: $v-font-size--small,
$color: $bosch-color-light-blue
);
margin-top: 5px;
margin-left: 5px;
@include valo-spinner(
$size:16px,
$speed:500ms
);
}
.hide-status-label {