Przeglądaj źródła

Implement focus and blur events for CheckBoxGroup.

Fixes vaadin/framework8-issues#334


Change-Id: I4c7ca424cc4f4a1f0cdecd7671827465ab74ace7
tags/8.0.0.alpha6
Denis Anisimov 7 lat temu
rodzic
commit
9c88657db5

+ 31
- 1
client/src/main/java/com/vaadin/client/EventHelper.java Wyświetl plik

@@ -18,6 +18,8 @@ package com.vaadin.client;
import static com.vaadin.shared.EventId.BLUR;
import static com.vaadin.shared.EventId.FOCUS;

import java.util.function.Supplier;

import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.DomEvent.Type;
@@ -137,9 +139,36 @@ public class EventHelper {
ComponentConnector connector, H handler, String eventIdentifier,
HandlerRegistration handlerRegistration, Type<H> type,
Widget widget) {
return updateHandler(connector, eventIdentifier, handlerRegistration,
() -> widget.addDomHandler(handler, type));
}

/**
* Updates handler registered using {@code handlerProvider}: removes it if
* connector doesn't have anymore {@code eventIdentifier} using provided
* {@code handlerRegistration} and adds it via provided
* {@code handlerProvider} if connector has event listener with
* {@code eventIdentifier}.
*
* @param connector
* connector to check event listener presence
* @param eventIdentifier
* event identifier whose presence in the connector is checked
* @param handlerRegistration
* resulting handler registration to remove added handler in case
* of absence event listener
* @param handlerProvider
* the strategy to register handler
* @return handlerRegistration which should be used to remove registered
* handler via {@code handlerProvider}
*/
public static <H extends EventHandler, W extends Widget> HandlerRegistration updateHandler(
ComponentConnector connector, String eventIdentifier,
HandlerRegistration handlerRegistration,
Supplier<HandlerRegistration> handlerProvider) {
if (connector.hasEventListener(eventIdentifier)) {
if (handlerRegistration == null) {
handlerRegistration = widget.addDomHandler(handler, type);
handlerRegistration = handlerProvider.get();
}
} else if (handlerRegistration != null) {
handlerRegistration.removeHandler();
@@ -147,4 +176,5 @@ public class EventHelper {
}
return handlerRegistration;
}

}

+ 62
- 10
client/src/main/java/com/vaadin/client/ui/ConnectorFocusAndBlurHandler.java Wyświetl plik

@@ -19,6 +19,7 @@ import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.HasAllFocusHandlers;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.EventHelper;
@@ -42,15 +43,39 @@ public class ConnectorFocusAndBlurHandler
private final Widget widget;
private HandlerRegistration focusRegistration = null;
private HandlerRegistration blurRegistration = null;
private HandlerRegistration stateChangeRegistration = null;

public static void addHandlers(AbstractComponentConnector connector) {
addHandlers(connector, connector.getWidget());
/**
* Add focus/blur handlers to the widget of the {@code connector}.
*
* @param connector
* connector whose widget is a target to add focus/blur handlers
* @return ConnectorFocusAndBlurHandler instance to remove all registered
* handlers
*/
public static ConnectorFocusAndBlurHandler addHandlers(
AbstractComponentConnector connector) {
return addHandlers(connector, connector.getWidget());
}

public static void addHandlers(AbstractComponentConnector connector,
Widget widget) {
connector.addStateChangeHandler("registeredEventListeners",
new ConnectorFocusAndBlurHandler(connector, widget));
/**
* Add focus/blur handlers to the widget and a state change handler for the
* {@code connector}.
*
* @param connector
* connector to register state change handler
* @param widget
* widget to register focus/blur handler
* @return ConnectorFocusAndBlurHandler instance to remove all registered
* handlers
*/
public static ConnectorFocusAndBlurHandler addHandlers(
AbstractComponentConnector connector, Widget widget) {
ConnectorFocusAndBlurHandler handler = new ConnectorFocusAndBlurHandler(
connector, widget);
handler.stateChangeRegistration = connector
.addStateChangeHandler("registeredEventListeners", handler);
return handler;
}

private ConnectorFocusAndBlurHandler(AbstractComponentConnector connector,
@@ -61,10 +86,22 @@ public class ConnectorFocusAndBlurHandler

@Override
public void onStateChanged(StateChangeEvent stateChangeEvent) {
focusRegistration = EventHelper.updateHandler(connector, this,
EventId.FOCUS, focusRegistration, FocusEvent.getType(), widget);
blurRegistration = EventHelper.updateHandler(connector, this,
EventId.BLUR, blurRegistration, BlurEvent.getType(), widget);
if (widget instanceof HasAllFocusHandlers) {
HasAllFocusHandlers focusHandlers = (HasAllFocusHandlers) widget;
focusRegistration = EventHelper.updateHandler(connector,
EventId.FOCUS, focusRegistration,
() -> focusHandlers.addFocusHandler(this));
blurRegistration = EventHelper.updateHandler(connector,
EventId.BLUR, blurRegistration,
() -> focusHandlers.addBlurHandler(this));
} else {
focusRegistration = EventHelper.updateHandler(connector, this,
EventId.FOCUS, focusRegistration, FocusEvent.getType(),
widget);
blurRegistration = EventHelper.updateHandler(connector, this,
EventId.BLUR, blurRegistration, BlurEvent.getType(),
widget);
}
}

@Override
@@ -81,6 +118,21 @@ public class ConnectorFocusAndBlurHandler
getRpc().blur();
}

/**
* Remove all handlers from the widget and the connector.
*/
public void removeHandlers() {
if (focusRegistration != null) {
focusRegistration.removeHandler();
}
if (blurRegistration != null) {
blurRegistration.removeHandler();
}
if (stateChangeRegistration != null) {
stateChangeRegistration.removeHandler();
}
}

private FocusAndBlurServerRpc getRpc() {
return connector.getRpcProxy(FocusAndBlurServerRpc.class);
}

+ 66
- 40
client/src/main/java/com/vaadin/client/ui/VCheckBoxGroup.java Wyświetl plik

@@ -18,23 +18,24 @@ package com.vaadin.client.ui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;

import com.google.gwt.aria.client.Roles;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.HasAllFocusHandlers;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.Focusable;
import com.google.gwt.user.client.ui.HasEnabled;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.WidgetUtil;
import com.vaadin.client.widgets.ChildFocusAwareFlowPanel;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.ListingJsonConstants;

@@ -47,7 +48,7 @@ import elemental.json.JsonObject;
* @since 8.0
*/
public class VCheckBoxGroup extends Composite implements Field, ClickHandler,
com.vaadin.client.Focusable, HasEnabled {
com.vaadin.client.Focusable, HasEnabled, HasAllFocusHandlers {

public static final String CLASSNAME = "v-select-optiongroup";
public static final String CLASSNAME_OPTION = "v-select-option";
@@ -65,7 +66,7 @@ public class VCheckBoxGroup extends Composite implements Field, ClickHandler,
* <p>
* For internal use only. May be removed or replaced in the future.
*/
public Panel optionsContainer;
private ChildFocusAwareFlowPanel optionsContainer;

private boolean htmlContentAllowed = false;

@@ -74,7 +75,7 @@ public class VCheckBoxGroup extends Composite implements Field, ClickHandler,
private List<BiConsumer<JsonObject, Boolean>> selectionChangeListeners;

public VCheckBoxGroup() {
optionsContainer = new FlowPanel();
optionsContainer = new ChildFocusAwareFlowPanel();
initWidget(optionsContainer);
optionsContainer.setStyleName(CLASSNAME);
optionsToItems = new HashMap<>();
@@ -85,40 +86,58 @@ public class VCheckBoxGroup extends Composite implements Field, ClickHandler,
* Build all the options
*/
public void buildOptions(List<JsonObject> items) {
/*
* In order to retain focus, we need to update values rather than
* recreate panel from scratch (#10451). However, the panel will be
* rebuilt (losing focus) if number of elements or their order is
* changed.
*/

Roles.getRadiogroupRole().set(getElement());
optionsContainer.clear();
for (JsonObject item : items) {
String itemHtml = item
.getString(ListingJsonConstants.JSONKEY_ITEM_VALUE);
if (!isHtmlContentAllowed()) {
itemHtml = WidgetUtil.escapeHTML(itemHtml);
Roles.getGroupRole().set(getElement());
int i = 0;
int widgetsToRemove = optionsContainer.getWidgetCount() - items.size();
if (widgetsToRemove < 0) {
widgetsToRemove = 0;
}
List<Widget> remove = new ArrayList<>(widgetsToRemove);
for (Widget widget : optionsContainer) {
if (i < items.size()) {
updateItem((VCheckBox) widget, items.get(i), false);
i++;
} else {
remove.add(widget);
}
VCheckBox checkBox = new VCheckBox();
}
remove.stream().forEach(this::remove);
while (i < items.size()) {
updateItem(new VCheckBox(), items.get(i), true);
i++;
}
}

String iconUrl = item
.getString(ListingJsonConstants.JSONKEY_ITEM_ICON);
if (iconUrl != null && iconUrl.length() != 0) {
Icon icon = client.getIcon(iconUrl);
itemHtml = icon.getElement().getString() + itemHtml;
}
private void remove(Widget widget) {
optionsContainer.remove(widget);
optionsToItems.remove(widget);
}

checkBox.addStyleName(CLASSNAME_OPTION);
checkBox.addClickHandler(this);
checkBox.setHTML(itemHtml);
checkBox.setValue(item
.getBoolean(ListingJsonConstants.JSONKEY_ITEM_SELECTED));
setOptionEnabled(checkBox, item);
private void updateItem(VCheckBox widget, JsonObject item,
boolean requireInitializations) {
String itemHtml = item
.getString(ListingJsonConstants.JSONKEY_ITEM_VALUE);
if (!isHtmlContentAllowed()) {
itemHtml = WidgetUtil.escapeHTML(itemHtml);
}

optionsContainer.add(checkBox);
optionsToItems.put(checkBox, item);
String iconUrl = item.getString(ListingJsonConstants.JSONKEY_ITEM_ICON);
if (iconUrl != null && iconUrl.length() != 0) {
Icon icon = client.getIcon(iconUrl);
itemHtml = icon.getElement().getString() + itemHtml;
}

widget.setHTML(itemHtml);
widget.setValue(
item.getBoolean(ListingJsonConstants.JSONKEY_ITEM_SELECTED));
setOptionEnabled(widget, item);

if (requireInitializations) {
widget.addStyleName(CLASSNAME_OPTION);
widget.addClickHandler(this);
optionsContainer.add(widget);
}
optionsToItems.put(widget, item);
}

@Override
@@ -166,10 +185,7 @@ public class VCheckBoxGroup extends Composite implements Field, ClickHandler,

@Override
public void focus() {
Iterator<Widget> iterator = optionsContainer.iterator();
if (iterator.hasNext()) {
((Focusable) iterator.next()).setFocus(true);
}
optionsContainer.focus();
}

public boolean isHtmlContentAllowed() {
@@ -210,4 +226,14 @@ public class VCheckBoxGroup extends Composite implements Field, ClickHandler,
return (Registration) () -> selectionChangeListeners
.remove(selectionChanged);
}

@Override
public HandlerRegistration addFocusHandler(FocusHandler handler) {
return optionsContainer.addFocusHandler(handler);
}

@Override
public HandlerRegistration addBlurHandler(BlurHandler handler) {
return optionsContainer.addBlurHandler(handler);
}
}

+ 12
- 0
client/src/main/java/com/vaadin/client/ui/optiongroup/CheckBoxGroupConnector.java Wyświetl plik

@@ -24,6 +24,7 @@ import java.util.List;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.connectors.AbstractListingConnector;
import com.vaadin.client.data.DataSource;
import com.vaadin.client.ui.ConnectorFocusAndBlurHandler;
import com.vaadin.client.ui.VCheckBoxGroup;
import com.vaadin.shared.data.selection.MultiSelectServerRpc;
import com.vaadin.shared.data.selection.SelectionModel;
@@ -38,10 +39,20 @@ import elemental.json.JsonObject;
public class CheckBoxGroupConnector
extends AbstractListingConnector<SelectionModel<?>> {

private ConnectorFocusAndBlurHandler handler;

@Override
protected void init() {
super.init();
getWidget().addSelectionChangeHandler(this::selectionChanged);
handler = ConnectorFocusAndBlurHandler.addHandlers(this);
}

@Override
public void onUnregister() {
super.onUnregister();
handler.removeHandlers();
handler = null;
}

private void selectionChanged(JsonObject changedItem, Boolean selected) {
@@ -88,4 +99,5 @@ public class CheckBoxGroupConnector
public CheckBoxGroupState getState() {
return (CheckBoxGroupState) super.getState();
}

}

+ 183
- 0
client/src/main/java/com/vaadin/client/widgets/ChildFocusAwareFlowPanel.java Wyświetl plik

@@ -0,0 +1,183 @@
/*
* Copyright 2000-2016 Vaadin Ltd.
*
* 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.vaadin.client.widgets;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Style.OutlineStyle;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.HasAllFocusHandlers;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.Focusable;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ui.FocusableFlowPanel;

/**
* Focusable flow panel which fires focus/blur events if it or any of its child
* is focused/blured, but doesn't fire events if it happens between its content
* (child) elements.
*
* @author Vaadin Ltd
*
*/
public class ChildFocusAwareFlowPanel extends FocusableFlowPanel
implements HasAllFocusHandlers {

private class FocusBlurHandler implements BlurHandler, FocusHandler {

private boolean blurOccured;

@Override
public void onBlur(BlurEvent event) {
blurOccured = true;
Scheduler.get().scheduleDeferred(() -> fireBlurEvent(event));
}

@Override
public void onFocus(FocusEvent event) {
if (!blurOccured) {
// no blur occured before this focus event
eventBus.fireEvent(event);
} else {
// blur occured before this focus event
// another component inside the panel was
// blurred => do not fire the focus and set blurOccured to
// false, so
// blur will not be fired, too
blurOccured = false;
}
}

private void fireBlurEvent(BlurEvent event) {
if (blurOccured) {
eventBus.fireEvent(event);
blurOccured = false;
}
}
}

private final HandlerManager eventBus;

private final FocusBlurHandler handler = new FocusBlurHandler();

private final Map<Widget, HandlerRegistration> focusRegistrations = new HashMap<>();
private final Map<Widget, HandlerRegistration> blurRegistrations = new HashMap<>();

/**
* Creates a new panel instance.
*/
public ChildFocusAwareFlowPanel() {
eventBus = new HandlerManager(this);
getElement().getStyle().setOutlineStyle(OutlineStyle.NONE);
super.addFocusHandler(handler);
super.addBlurHandler(handler);
}

@Override
public void add(Widget widget) {
super.add(widget);
addHandlers(widget);
}

@Override
public void clear() {
super.clear();
focusRegistrations.clear();
blurRegistrations.clear();
}

@Override
public void insert(Widget widget, int beforeIndex) {
super.insert(widget, beforeIndex);
addHandlers(widget);
}

@Override
public boolean remove(int index) {
Widget widget = getWidget(index);
boolean isRemoved = super.remove(index);
if (isRemoved) {
removeHandlers(widget);
}
return isRemoved;
}

@Override
public boolean remove(Widget widget) {
boolean isRemoved = super.remove(widget);
if (isRemoved) {
removeHandlers(widget);
}
return isRemoved;
}

@Override
public HandlerRegistration addFocusHandler(FocusHandler handler) {
return eventBus.addHandler(FocusEvent.getType(), handler);
}

@Override
public HandlerRegistration addBlurHandler(BlurHandler handler) {
return eventBus.addHandler(BlurEvent.getType(), handler);
}

@Override
public void focus() {
Iterator<Widget> iterator = iterator();
if (iterator.hasNext()) {
Widget widget = iterator.next();
if (widget instanceof Focusable) {
((Focusable) widget).setFocus(true);
}
}
}

private void addHandlers(Widget widget) {
if (focusRegistrations.containsKey(widget)) {
assert blurRegistrations.containsKey(widget);
return;
}
if (widget instanceof FocusWidget) {
HandlerRegistration focusRegistration = ((FocusWidget) widget)
.addFocusHandler(handler);
HandlerRegistration blurRegistration = ((FocusWidget) widget)
.addBlurHandler(handler);
focusRegistrations.put(widget, focusRegistration);
blurRegistrations.put(widget, blurRegistration);
}
}

private void removeHandlers(Widget widget) {
HandlerRegistration focusRegistration = focusRegistrations
.remove(widget);
if (focusRegistration != null) {
focusRegistration.removeHandler();
}
HandlerRegistration blurRegistration = blurRegistrations.remove(widget);
if (blurRegistration != null) {
blurRegistration.removeHandler();
}
}

}

+ 5
- 5
compatibility-client/src/main/java/com/vaadin/v7/client/ui/VOptionGroup.java Wyświetl plik

@@ -95,8 +95,8 @@ public class VOptionGroup extends VOptionGroupBase
public VOptionGroup() {
super(CLASSNAME);
panel = (Panel) optionsContainer;
optionsToKeys = new HashMap<CheckBox, String>();
optionsEnabled = new HashMap<CheckBox, Boolean>();
optionsToKeys = new HashMap<>();
optionsEnabled = new HashMap<>();

wasMultiselect = isMultiselect();
}
@@ -113,12 +113,12 @@ public class VOptionGroup extends VOptionGroupBase
* rebuilt (losing focus) if number of elements or their order is
* changed.
*/
HashMap<String, CheckBox> keysToOptions = new HashMap<String, CheckBox>();
HashMap<String, CheckBox> keysToOptions = new HashMap<>();
for (Map.Entry<CheckBox, String> entry : optionsToKeys.entrySet()) {
keysToOptions.put(entry.getValue(), entry.getKey());
}
ArrayList<Widget> existingwidgets = new ArrayList<Widget>();
ArrayList<Widget> newwidgets = new ArrayList<Widget>();
ArrayList<Widget> existingwidgets = new ArrayList<>();
ArrayList<Widget> newwidgets = new ArrayList<>();

// Get current order of elements
for (Widget wid : panel) {

+ 2
- 10
compatibility-server/src/main/java/com/vaadin/v7/ui/CheckBox.java Wyświetl plik

@@ -23,7 +23,7 @@ import org.jsoup.nodes.Element;

import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl;
import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator;
import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.shared.MouseEventDetails;
@@ -67,20 +67,12 @@ public class CheckBox extends AbstractField<Boolean> {
}
};

FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl(
this) {
@Override
protected void fireEvent(Event event) {
CheckBox.this.fireEvent(event);
}
};

/**
* Creates a new checkbox.
*/
public CheckBox() {
registerRpc(rpc);
registerRpc(focusBlurRpc);
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
setValue(Boolean.FALSE);
}


+ 5
- 14
compatibility-server/src/main/java/com/vaadin/v7/ui/NativeSelect.java Wyświetl plik

@@ -20,7 +20,7 @@ import java.util.Collection;

import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl;
import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator;
import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.v7.data.Container;
@@ -37,33 +37,24 @@ import com.vaadin.v7.event.FieldEvents;
public class NativeSelect extends AbstractSelect
implements FieldEvents.BlurNotifier, FieldEvents.FocusNotifier {

FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl(
this) {

@Override
protected void fireEvent(Event event) {
NativeSelect.this.fireEvent(event);
}
};

public NativeSelect() {
super();
registerRpc(focusBlurRpc);
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
}

public NativeSelect(String caption, Collection<?> options) {
super(caption, options);
registerRpc(focusBlurRpc);
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
}

public NativeSelect(String caption, Container dataSource) {
super(caption, dataSource);
registerRpc(focusBlurRpc);
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
}

public NativeSelect(String caption) {
super(caption);
registerRpc(focusBlurRpc);
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
}

@Override

+ 33
- 0
server/src/main/java/com/vaadin/event/FieldEvents.java Wyświetl plik

@@ -19,6 +19,7 @@ package com.vaadin.event;
import java.io.Serializable;
import java.lang.reflect.Method;

import com.vaadin.server.SerializableConsumer;
import com.vaadin.shared.EventId;
import com.vaadin.shared.Registration;
import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc;
@@ -212,4 +213,36 @@ public interface FieldEvents {
}
}

/**
* Focus and blur server RPC implementation which fires focus or blur event
* using a provided event handler.
*
* @author Vaadin Ltd
*
*/
public static class FocusAndBlurServerRpcDecorator
extends FocusAndBlurServerRpcImpl {

private final SerializableConsumer<Event> eventHandler;

/**
* Create a new decorator instance.
*
* @param component
* the source events component
* @param eventHandler
* the event handler to delegate event firing
*/
public FocusAndBlurServerRpcDecorator(Component component,
SerializableConsumer<Event> eventHandler) {
super(component);
this.eventHandler = eventHandler;
}

@Override
protected void fireEvent(Event event) {
eventHandler.accept(event);
}
}

}

+ 35
- 0
server/src/main/java/com/vaadin/server/SerializableConsumer.java Wyświetl plik

@@ -0,0 +1,35 @@
/*
* Copyright 2000-2016 Vaadin Ltd.
*
* 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.vaadin.server;

import java.io.Serializable;
import java.util.function.Consumer;

/**
* A {@link Consumer} that is also {@link Serializable}.
*
* @see {@link Consumer}
* @param <T>
* the type of the first argument to the operation
*
* @since 8.0
* @author Vaadin Ltd
*
*/
@FunctionalInterface
public interface SerializableConsumer<T> extends Consumer<T>, Serializable {
// Only method inherited from Consumer
}

+ 2
- 9
server/src/main/java/com/vaadin/ui/AbstractFocusable.java Wyświetl plik

@@ -15,12 +15,10 @@
*/
package com.vaadin.ui;

import java.util.Objects;

import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.BlurNotifier;
import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl;
import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator;
import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.event.FieldEvents.FocusNotifier;
@@ -39,12 +37,7 @@ public abstract class AbstractFocusable extends AbstractComponent
implements Focusable, FocusNotifier, BlurNotifier {

protected AbstractFocusable() {
registerRpc(new FocusAndBlurServerRpcImpl(this) {
@Override
protected void fireEvent(Event event) {
AbstractFocusable.this.fireEvent(event);
}
});
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
}

@Override

+ 2
- 10
server/src/main/java/com/vaadin/ui/CheckBox.java Wyświetl plik

@@ -25,7 +25,7 @@ import org.jsoup.nodes.Element;
import com.vaadin.event.FieldEvents;
import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl;
import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator;
import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.shared.MouseEventDetails;
@@ -69,20 +69,12 @@ public class CheckBox extends AbstractField<Boolean>
}
};

FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl(
this) {
@Override
protected void fireEvent(Event event) {
CheckBox.this.fireEvent(event);
}
};

/**
* Creates a new checkbox.
*/
public CheckBox() {
registerRpc(rpc);
registerRpc(focusBlurRpc);
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
setValue(Boolean.FALSE);
}


+ 39
- 1
server/src/main/java/com/vaadin/ui/CheckBoxGroup.java Wyświetl plik

@@ -19,8 +19,16 @@ package com.vaadin.ui;
import java.util.Collection;

import com.vaadin.data.Listing;
import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.BlurNotifier;
import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator;
import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.event.FieldEvents.FocusNotifier;
import com.vaadin.server.SerializablePredicate;
import com.vaadin.server.data.DataSource;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.optiongroup.CheckBoxGroupState;

/**
@@ -32,7 +40,8 @@ import com.vaadin.shared.ui.optiongroup.CheckBoxGroupState;
* @author Vaadin Ltd
* @since 8.0
*/
public class CheckBoxGroup<T> extends AbstractMultiSelect<T> {
public class CheckBoxGroup<T> extends AbstractMultiSelect<T>
implements FocusNotifier, BlurNotifier {

/**
* Constructs a new CheckBoxGroup with caption.
@@ -80,6 +89,7 @@ public class CheckBoxGroup<T> extends AbstractMultiSelect<T> {
* @see Listing#setDataSource(DataSource)
*/
public CheckBoxGroup() {
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
}

/**
@@ -137,4 +147,32 @@ public class CheckBoxGroup<T> extends AbstractMultiSelect<T> {
SerializablePredicate<T> itemEnabledProvider) {
super.setItemEnabledProvider(itemEnabledProvider);
}

@Override
public Registration addFocusListener(FocusListener listener) {
addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
FocusListener.focusMethod);
return () -> removeListener(FocusEvent.EVENT_ID, FocusEvent.class,
listener);
}

@Override
@Deprecated
public void removeFocusListener(FocusListener listener) {
removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener);
}

@Override
public Registration addBlurListener(BlurListener listener) {
addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener,
BlurListener.blurMethod);
return () -> removeListener(BlurEvent.EVENT_ID, BlurEvent.class,
listener);
}

@Override
@Deprecated
public void removeBlurListener(BlurListener listener) {
removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener);
}
}

+ 2
- 10
server/src/main/java/com/vaadin/ui/ComboBox.java Wyświetl plik

@@ -26,7 +26,7 @@ import com.vaadin.data.HasValue;
import com.vaadin.event.FieldEvents;
import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl;
import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator;
import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.server.KeyMapper;
@@ -118,14 +118,6 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>,
}
};

private FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl(
this) {
@Override
protected void fireEvent(Component.Event event) {
ComboBox.this.fireEvent(event);
}
};

private String filterstring;

/**
@@ -218,7 +210,7 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>,
*/
private void init() {
registerRpc(rpc);
registerRpc(focusBlurRpc);
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));

addDataGenerator((T data, JsonObject jsonObject) -> {
jsonObject.put(DataCommunicatorConstants.NAME,

+ 2
- 11
server/src/main/java/com/vaadin/ui/TabSheet.java Wyświetl plik

@@ -31,7 +31,7 @@ import org.jsoup.nodes.Element;
import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.BlurNotifier;
import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl;
import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator;
import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.event.FieldEvents.FocusNotifier;
@@ -134,7 +134,7 @@ public class TabSheet extends AbstractComponentContainer
super();

registerRpc(rpc);
registerRpc(focusBlurRpc);
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));

// expand horizontally by default
setWidth(100, UNITS_PERCENTAGE);
@@ -675,15 +675,6 @@ public class TabSheet extends AbstractComponentContainer

private TabsheetServerRpcImpl rpc = new TabsheetServerRpcImpl();

private FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl(
this) {

@Override
protected void fireEvent(Event event) {
TabSheet.this.fireEvent(event);
}
};

/**
* Replaces a component (tab content) with another. This can be used to
* change tab contents or to rearrange tabs. The tab position and some

+ 41
- 0
uitest/src/main/java/com/vaadin/tests/components/checkboxgroup/CheckBoxGroupFocusBlur.java Wyświetl plik

@@ -0,0 +1,41 @@
/*
* Copyright 2000-2016 Vaadin Ltd.
*
* 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.vaadin.tests.components.checkboxgroup;

import java.util.stream.IntStream;

import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUIWithLog;
import com.vaadin.ui.CheckBoxGroup;

/**
* @author Vaadin Ltd
*
*/
public class CheckBoxGroupFocusBlur extends AbstractTestUIWithLog {

@Override
protected void setup(VaadinRequest request) {
CheckBoxGroup<Integer> group = new CheckBoxGroup<>();
group.setItems(IntStream.range(1, 10).mapToObj(Integer::valueOf)
.toArray(Integer[]::new));
addComponent(group);

group.addFocusListener(event -> log("Focus Event"));
group.addBlurListener(event -> log("Blur Event"));
}

}

+ 86
- 0
uitest/src/test/java/com/vaadin/tests/components/checkboxgroup/CheckBoxGroupFocusBlurTest.java Wyświetl plik

@@ -0,0 +1,86 @@
/*
* Copyright 2000-2016 Vaadin Ltd.
*
* 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.vaadin.tests.components.checkboxgroup;

import java.util.List;

import org.junit.Assert;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;

import com.vaadin.testbench.customelements.CheckBoxGroupElement;
import com.vaadin.testbench.elements.LabelElement;
import com.vaadin.tests.tb3.MultiBrowserTest;

/**
* @author Vaadin Ltd
*
*/
public class CheckBoxGroupFocusBlurTest extends MultiBrowserTest {

@Test
public void focusBlurEvents() {
openTestURL();

List<WebElement> checkBoxes = $(CheckBoxGroupElement.class).first()
.findElements(By.tagName("input"));
checkBoxes.get(0).click();

// Focus event is fired
Assert.assertTrue(logContainsText("1. Focus Event"));

checkBoxes.get(1).click();
// click on the second checkbox doesn't fire anything
Assert.assertFalse(logContainsText("2."));

// click in the middle between the first and the second (inside group).
WebElement first = checkBoxes.get(0);
int middle = (first.getLocation().y + first.getSize().height
+ checkBoxes.get(1).getLocation().y) / 2;
new Actions(getDriver()).moveByOffset(first.getLocation().x, middle)
.click().build().perform();
// no new events
Assert.assertFalse(logContainsText("2."));

// click to label of a checkbox
$(CheckBoxGroupElement.class).first().findElements(By.tagName("label"))
.get(2).click();
// no new events
Assert.assertFalse(logContainsText("2."));

// click on log label => blur
$(LabelElement.class).first().click();
// blur event is fired
Assert.assertTrue(logContainsText("2. Blur Event"));

checkBoxes.get(3).click();
// Focus event is fired
Assert.assertTrue(logContainsText("3. Focus Event"));

// move keyboard focus to the next checkbox
checkBoxes.get(3).sendKeys(Keys.TAB);
// no new events
Assert.assertFalse(logContainsText("4."));

// select the next checkbox
checkBoxes.get(4).sendKeys(Keys.SPACE);
// no new events
Assert.assertFalse(logContainsText("4."));
}
}

Ładowanie…
Anuluj
Zapisz