diff --git a/modules/gui/src/com/haulmont/cuba/gui/window.xsd b/modules/gui/src/com/haulmont/cuba/gui/window.xsd index 98f0bd14bd..56240ee03d 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/window.xsd +++ b/modules/gui/src/com/haulmont/cuba/gui/window.xsd @@ -1246,6 +1246,16 @@ + + + + + + + + + + @@ -2351,6 +2361,7 @@ + diff --git a/modules/gui/src/com/haulmont/cuba/gui/xml/layout/LayoutLoaderConfig.java b/modules/gui/src/com/haulmont/cuba/gui/xml/layout/LayoutLoaderConfig.java index 763b3ddf43..e987827b52 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/xml/layout/LayoutLoaderConfig.java +++ b/modules/gui/src/com/haulmont/cuba/gui/xml/layout/LayoutLoaderConfig.java @@ -76,6 +76,7 @@ public class LayoutLoaderConfig { config.register(TimeField.NAME, TimeFieldLoader.class); config.register(DatePicker.NAME, DatePickerLoader.class); config.register(LookupField.NAME, LookupFieldLoader.class); + config.register(SuggestionField.NAME, SuggestionFieldLoader.class); config.register(PickerField.NAME, PickerFieldLoader.class); config.register(ColorPicker.NAME, ColorPickerLoader.class); config.register(LookupPickerField.NAME, LookupPickerFieldLoader.class); diff --git a/modules/gui/src/com/haulmont/cuba/gui/xml/layout/loaders/SuggestionFieldLoader.java b/modules/gui/src/com/haulmont/cuba/gui/xml/layout/loaders/SuggestionFieldLoader.java new file mode 100644 index 0000000000..83aaa22007 --- /dev/null +++ b/modules/gui/src/com/haulmont/cuba/gui/xml/layout/loaders/SuggestionFieldLoader.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2008-2017 Haulmont. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.haulmont.cuba.gui.xml.layout.loaders; + +import com.haulmont.cuba.gui.components.SuggestionField; + +public class SuggestionFieldLoader extends AbstractFieldLoader { + + @Override + public void createComponent() { + resultComponent = (SuggestionField) factory.createComponent(SuggestionField.NAME); + loadId(resultComponent, element); + } + + @Override + public void loadComponent() { + super.loadComponent(); + + loadAsyncSearchDelayMs(); + loadMinSearchStringLength(); + loadSuggestionsLimit(); + } + + private void loadSuggestionsLimit() { + if (element.attribute("suggestionsLimit") != null) { + String suggestionsLimit = element.attributeValue("suggestionsLimit"); + resultComponent.setSuggestionsLimit(Integer.parseInt(suggestionsLimit)); + } + } + + private void loadMinSearchStringLength() { + if (element.attribute("minSearchStringLength") != null) { + String minSearchStringLength = element.attributeValue("minSearchStringLength"); + resultComponent.setMinSearchStringLength(Integer.parseInt(minSearchStringLength)); + } + } + + private void loadAsyncSearchDelayMs() { + if (element.attribute("asyncSearchDelayMs") != null) { + String asyncSearchDelayMs = element.attributeValue("asyncSearchDelayMs"); + resultComponent.setAsyncSearchDelayMs(Integer.parseInt(asyncSearchDelayMs)); + } + } +} diff --git a/modules/web-toolkit/src/com/haulmont/cuba/web/toolkit/ui/client/suggestionfield/CubaSuggestionFieldConnector.java b/modules/web-toolkit/src/com/haulmont/cuba/web/toolkit/ui/client/suggestionfield/CubaSuggestionFieldConnector.java index 884ce6f05e..3bf9ec4182 100644 --- a/modules/web-toolkit/src/com/haulmont/cuba/web/toolkit/ui/client/suggestionfield/CubaSuggestionFieldConnector.java +++ b/modules/web-toolkit/src/com/haulmont/cuba/web/toolkit/ui/client/suggestionfield/CubaSuggestionFieldConnector.java @@ -71,5 +71,7 @@ public class CubaSuggestionFieldConnector extends AbstractFieldConnector { if (stateChangeEvent.hasPropertyChanged("text")) { widget.setValue(getState().text, false); } + + widget.setReadonly(isReadOnly()); } } \ No newline at end of file diff --git a/modules/web-toolkit/src/com/haulmont/cuba/web/toolkit/ui/client/suggestionfield/CubaSuggestionFieldWidget.java b/modules/web-toolkit/src/com/haulmont/cuba/web/toolkit/ui/client/suggestionfield/CubaSuggestionFieldWidget.java index dc9c75caec..71a7ba0f0b 100644 --- a/modules/web-toolkit/src/com/haulmont/cuba/web/toolkit/ui/client/suggestionfield/CubaSuggestionFieldWidget.java +++ b/modules/web-toolkit/src/com/haulmont/cuba/web/toolkit/ui/client/suggestionfield/CubaSuggestionFieldWidget.java @@ -21,7 +21,6 @@ import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.EventTarget; -import com.google.gwt.dom.client.Style; import com.google.gwt.event.dom.client.*; import com.google.gwt.event.logical.shared.*; import com.google.gwt.event.shared.HandlerRegistration; @@ -69,13 +68,15 @@ public class CubaSuggestionFieldWidget extends Composite implements HasEnabled, // search query protected String prevQuery; + public boolean iePreventBlur = false; + protected List suggestions = new ArrayList<>(); public CubaSuggestionFieldWidget() { textField = GWT.create(VTextField.class); initTextField(); - suggestionsContainer = new SuggestionsContainer(); + suggestionsContainer = new SuggestionsContainer(this); suggestionsPopup = new CubaSuggestionFieldWidget.SuggestionPopup(suggestionsContainer); suggestionTimer = new CubaSuggestionFieldWidget.SuggestionTimer(); @@ -197,6 +198,14 @@ public class CubaSuggestionFieldWidget extends Composite implements HasEnabled, } } + public boolean isReadonly() { + return textField.isReadOnly(); + } + + public void setReadonly(boolean readonly) { + textField.setReadOnly(readonly); + } + protected void cancelSearch() { if (suggestionTimer != null) { suggestionTimer.cancel(); @@ -267,22 +276,31 @@ public class CubaSuggestionFieldWidget extends Composite implements HasEnabled, protected void handleOnBlur(BlurEvent event) { removeStyleName(MODIFIED_STYLENAME); - if (!suggestionsPopup.isShowing()) { - resetComponentState(); - return; - } - - EventTarget eventTarget = event.getNativeEvent().getRelatedEventTarget(); - if (eventTarget == null) { - resetComponentState(); - return; - } - - if (Element.is(eventTarget)) { - Widget widget = WidgetUtil.findWidget(Element.as(eventTarget), null); - if (widget != suggestionsContainer) { + if (BrowserInfo.get().isIE()) { + if (iePreventBlur) { + textField.setFocus(true); + iePreventBlur = false; + } else { resetComponentState(); } + } else { + if (!suggestionsPopup.isShowing()) { + resetComponentState(); + return; + } + + EventTarget eventTarget = event.getNativeEvent().getRelatedEventTarget(); + if (eventTarget == null) { + resetComponentState(); + return; + } + + if (Element.is(eventTarget)) { + Widget widget = WidgetUtil.findWidget(Element.as(eventTarget), null); + if (widget != suggestionsContainer) { + resetComponentState(); + } + } } } @@ -367,23 +385,6 @@ public class CubaSuggestionFieldWidget extends Composite implements HasEnabled, popupOuterPadding = WidgetUtil.measureHorizontalPaddingAndBorder(getElement(), 2); } - Widget popup = getWidget(); - - Element containerFirstChild = popup.getElement().getFirstChild().cast(); - int naturalMenuWidth = containerFirstChild.getOffsetWidth(); - final int textFieldWidth = textField.getOffsetWidth(); - - if (naturalMenuWidth < textFieldWidth) { - popup.setWidth((textFieldWidth - popupOuterPadding) + "px"); - containerFirstChild.getStyle().setWidth(100, Style.Unit.PCT); - naturalMenuWidth = textFieldWidth; - } - - if (BrowserInfo.get().isIE()) { - int rootWidth = naturalMenuWidth - popupOuterPadding; - getContainerElement().getStyle().setWidth(rootWidth, Style.Unit.PX); - } - int top; int left; @@ -398,6 +399,10 @@ public class CubaSuggestionFieldWidget extends Composite implements HasEnabled, top -= topMargin; } + Widget popup = getWidget(); + Element containerFirstChild = popup.getElement().getFirstChild().cast(); + final int textFieldWidth = textField.getOffsetWidth(); + offsetWidth = containerFirstChild.getOffsetWidth(); if (offsetWidth + getPopupLeft() > Window.getClientWidth() + Window.getScrollLeft()) { left = textField.getAbsoluteLeft() + textFieldWidth + Window.getScrollLeft() - offsetWidth; diff --git a/modules/web-toolkit/src/com/haulmont/cuba/web/toolkit/ui/client/suggestionfield/menu/SuggestionsContainer.java b/modules/web-toolkit/src/com/haulmont/cuba/web/toolkit/ui/client/suggestionfield/menu/SuggestionsContainer.java index 893938edd6..dadf72b17b 100644 --- a/modules/web-toolkit/src/com/haulmont/cuba/web/toolkit/ui/client/suggestionfield/menu/SuggestionsContainer.java +++ b/modules/web-toolkit/src/com/haulmont/cuba/web/toolkit/ui/client/suggestionfield/menu/SuggestionsContainer.java @@ -29,6 +29,8 @@ import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.UIObject; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.impl.FocusImpl; +import com.haulmont.cuba.web.toolkit.ui.client.suggestionfield.CubaSuggestionFieldWidget; +import com.vaadin.client.BrowserInfo; import java.util.ArrayList; import java.util.List; @@ -41,14 +43,17 @@ public class SuggestionsContainer extends Widget { protected final Element container; protected SuggestionItem selectedSuggestion; - public SuggestionsContainer() { + protected final CubaSuggestionFieldWidget suggestionFieldWidget; + + public SuggestionsContainer(CubaSuggestionFieldWidget suggestionFieldWidget) { + this.suggestionFieldWidget = suggestionFieldWidget; container = DOM.createDiv(); final Element outer = FocusImpl.getFocusImplForPanel().createFocusable(); DOM.appendChild(outer, container); setElement(outer); - sinkEvents(Event.ONCLICK | Event.ONMOUSEOVER | Event.ONMOUSEOUT | Event.ONFOCUS | Event.ONKEYDOWN); + sinkEvents(Event.ONCLICK | Event.ONMOUSEDOWN | Event.ONMOUSEOVER | Event.ONMOUSEOUT | Event.ONFOCUS | Event.ONKEYDOWN); addDomHandler(event -> selectItem(null), BlurEvent.getType()); @@ -139,6 +144,13 @@ public class SuggestionsContainer extends Widget { SuggestionItem item = findItem(DOM.eventGetTarget(event)); switch (DOM.eventGetType(event)) { + case Event.ONMOUSEDOWN: { + if (BrowserInfo.get().isIE()) { + suggestionFieldWidget.iePreventBlur = true; + } + break; + } + case Event.ONCLICK: { if (event.getButton() == NativeEvent.BUTTON_LEFT) { performItemCommand(item); diff --git a/modules/web/src/com/haulmont/cuba/web/toolkit/ui/CubaSuggestionField.java b/modules/web/src/com/haulmont/cuba/web/toolkit/ui/CubaSuggestionField.java index 37d3c4ef7d..597d5a4f94 100644 --- a/modules/web/src/com/haulmont/cuba/web/toolkit/ui/CubaSuggestionField.java +++ b/modules/web/src/com/haulmont/cuba/web/toolkit/ui/CubaSuggestionField.java @@ -21,6 +21,9 @@ import com.haulmont.cuba.web.toolkit.ui.client.suggestionfield.CubaSuggestionFie import com.haulmont.cuba.web.toolkit.ui.client.suggestionfield.CubaSuggestionFieldState; import com.vaadin.data.util.converter.Converter; import com.vaadin.event.FieldEvents; +import com.vaadin.server.AbstractErrorMessage; +import com.vaadin.server.CompositeErrorMessage; +import com.vaadin.server.ErrorMessage; import com.vaadin.server.KeyMapper; import com.vaadin.ui.AbstractField; import elemental.json.Json; @@ -48,6 +51,7 @@ public class CubaSuggestionField extends AbstractField { protected int suggestionsLimit = 10; public CubaSuggestionField() { + setValidationVisible(false); serverRpc = new CubaSuggestionFieldServerRpc() { @Override public void searchSuggestions(String query) { @@ -112,6 +116,19 @@ public class CubaSuggestionField extends AbstractField { } } + @Override + public ErrorMessage getErrorMessage() { + ErrorMessage superError = super.getErrorMessage(); + if (!isReadOnly() && isRequired() && isEmpty()) { + ErrorMessage error = AbstractErrorMessage.getErrorMessageForException( + new com.vaadin.data.Validator.EmptyValueException(getRequiredError())); + if (error != null) { + return new CompositeErrorMessage(superError, error); + } + } + return superError; + } + @SuppressWarnings("unchecked") public void setTextViewConverter(Converter converter) { this.textViewConverter = (Converter) converter; diff --git a/modules/web/themes/halo/components/suggestionfield/suggestionfield.scss b/modules/web/themes/halo/components/suggestionfield/suggestionfield.scss index 0a6924778c..960e5a6cea 100644 --- a/modules/web/themes/halo/components/suggestionfield/suggestionfield.scss +++ b/modules/web/themes/halo/components/suggestionfield/suggestionfield.scss @@ -11,14 +11,27 @@ $cuba-suggestionfield-edit-color: scale-color(lighten($v-focus-color, 35%), $sat } } + .#{$primary-stylename}-error { + @include valo-textfield-error-style; + } + + .#{$primary-stylename}.v-readonly, + .#{$primary-stylename}-error.v-readonly { + @include valo-textfield-readonly-style; + } + .#{$primary-stylename}-popup { @include valo-selection-overlay-style; + min-width: 200px; + max-width: 400px; overflow-y: auto; outline: 0px; } .#{$primary-stylename}-item { - overflow-x: hidden; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; border-radius: 3px; cursor: pointer; line-height: $v-selection-item-height;