Browse Source

Implement focus and blur events for CheckBoxGroup.

Fixes vaadin/framework8-issues#334


Change-Id: I4c7ca424cc4f4a1f0cdecd7671827465ab74ace7
tags/8.0.0.alpha6
Denis Anisimov 7 years ago
parent
commit
9c88657db5

+ 31
- 1
client/src/main/java/com/vaadin/client/EventHelper.java View File

import static com.vaadin.shared.EventId.BLUR; import static com.vaadin.shared.EventId.BLUR;
import static com.vaadin.shared.EventId.FOCUS; 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.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler; import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.DomEvent.Type; import com.google.gwt.event.dom.client.DomEvent.Type;
ComponentConnector connector, H handler, String eventIdentifier, ComponentConnector connector, H handler, String eventIdentifier,
HandlerRegistration handlerRegistration, Type<H> type, HandlerRegistration handlerRegistration, Type<H> type,
Widget widget) { 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 (connector.hasEventListener(eventIdentifier)) {
if (handlerRegistration == null) { if (handlerRegistration == null) {
handlerRegistration = widget.addDomHandler(handler, type);
handlerRegistration = handlerProvider.get();
} }
} else if (handlerRegistration != null) { } else if (handlerRegistration != null) {
handlerRegistration.removeHandler(); handlerRegistration.removeHandler();
} }
return handlerRegistration; return handlerRegistration;
} }

} }

+ 62
- 10
client/src/main/java/com/vaadin/client/ui/ConnectorFocusAndBlurHandler.java View File

import com.google.gwt.event.dom.client.BlurHandler; import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.FocusEvent; import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler; 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.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.EventHelper; import com.vaadin.client.EventHelper;
private final Widget widget; private final Widget widget;
private HandlerRegistration focusRegistration = null; private HandlerRegistration focusRegistration = null;
private HandlerRegistration blurRegistration = 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, private ConnectorFocusAndBlurHandler(AbstractComponentConnector connector,


@Override @Override
public void onStateChanged(StateChangeEvent stateChangeEvent) { 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 @Override
getRpc().blur(); 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() { private FocusAndBlurServerRpc getRpc() {
return connector.getRpcProxy(FocusAndBlurServerRpc.class); return connector.getRpcProxy(FocusAndBlurServerRpc.class);
} }

+ 66
- 40
client/src/main/java/com/vaadin/client/ui/VCheckBoxGroup.java View File



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


import com.google.gwt.aria.client.Roles; 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.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler; 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.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FocusWidget; 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.HasEnabled;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection; import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.WidgetUtil; import com.vaadin.client.WidgetUtil;
import com.vaadin.client.widgets.ChildFocusAwareFlowPanel;
import com.vaadin.shared.Registration; import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.ListingJsonConstants; import com.vaadin.shared.ui.ListingJsonConstants;


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


private boolean htmlContentAllowed = false; private boolean htmlContentAllowed = false;


private List<BiConsumer<JsonObject, Boolean>> selectionChangeListeners; private List<BiConsumer<JsonObject, Boolean>> selectionChangeListeners;


public VCheckBoxGroup() { public VCheckBoxGroup() {
optionsContainer = new FlowPanel();
optionsContainer = new ChildFocusAwareFlowPanel();
initWidget(optionsContainer); initWidget(optionsContainer);
optionsContainer.setStyleName(CLASSNAME); optionsContainer.setStyleName(CLASSNAME);
optionsToItems = new HashMap<>(); optionsToItems = new HashMap<>();
* Build all the options * Build all the options
*/ */
public void buildOptions(List<JsonObject> items) { 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 @Override


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


public boolean isHtmlContentAllowed() { public boolean isHtmlContentAllowed() {
return (Registration) () -> selectionChangeListeners return (Registration) () -> selectionChangeListeners
.remove(selectionChanged); .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 View File

import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.connectors.AbstractListingConnector; import com.vaadin.client.connectors.AbstractListingConnector;
import com.vaadin.client.data.DataSource; import com.vaadin.client.data.DataSource;
import com.vaadin.client.ui.ConnectorFocusAndBlurHandler;
import com.vaadin.client.ui.VCheckBoxGroup; import com.vaadin.client.ui.VCheckBoxGroup;
import com.vaadin.shared.data.selection.MultiSelectServerRpc; import com.vaadin.shared.data.selection.MultiSelectServerRpc;
import com.vaadin.shared.data.selection.SelectionModel; import com.vaadin.shared.data.selection.SelectionModel;
public class CheckBoxGroupConnector public class CheckBoxGroupConnector
extends AbstractListingConnector<SelectionModel<?>> { extends AbstractListingConnector<SelectionModel<?>> {


private ConnectorFocusAndBlurHandler handler;

@Override @Override
protected void init() { protected void init() {
super.init(); super.init();
getWidget().addSelectionChangeHandler(this::selectionChanged); 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) { private void selectionChanged(JsonObject changedItem, Boolean selected) {
public CheckBoxGroupState getState() { public CheckBoxGroupState getState() {
return (CheckBoxGroupState) super.getState(); return (CheckBoxGroupState) super.getState();
} }

} }

+ 183
- 0
client/src/main/java/com/vaadin/client/widgets/ChildFocusAwareFlowPanel.java View File

/*
* 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 View File

public VOptionGroup() { public VOptionGroup() {
super(CLASSNAME); super(CLASSNAME);
panel = (Panel) optionsContainer; panel = (Panel) optionsContainer;
optionsToKeys = new HashMap<CheckBox, String>();
optionsEnabled = new HashMap<CheckBox, Boolean>();
optionsToKeys = new HashMap<>();
optionsEnabled = new HashMap<>();


wasMultiselect = isMultiselect(); wasMultiselect = isMultiselect();
} }
* rebuilt (losing focus) if number of elements or their order is * rebuilt (losing focus) if number of elements or their order is
* changed. * changed.
*/ */
HashMap<String, CheckBox> keysToOptions = new HashMap<String, CheckBox>();
HashMap<String, CheckBox> keysToOptions = new HashMap<>();
for (Map.Entry<CheckBox, String> entry : optionsToKeys.entrySet()) { for (Map.Entry<CheckBox, String> entry : optionsToKeys.entrySet()) {
keysToOptions.put(entry.getValue(), entry.getKey()); 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 // Get current order of elements
for (Widget wid : panel) { for (Widget wid : panel) {

+ 2
- 10
compatibility-server/src/main/java/com/vaadin/v7/ui/CheckBox.java View File



import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener; 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.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.MouseEventDetails;
} }
}; };


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

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



+ 5
- 14
compatibility-server/src/main/java/com/vaadin/v7/ui/NativeSelect.java View File



import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener; 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.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.v7.data.Container; import com.vaadin.v7.data.Container;
public class NativeSelect extends AbstractSelect public class NativeSelect extends AbstractSelect
implements FieldEvents.BlurNotifier, FieldEvents.FocusNotifier { implements FieldEvents.BlurNotifier, FieldEvents.FocusNotifier {


FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl(
this) {

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

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


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


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


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


@Override @Override

+ 33
- 0
server/src/main/java/com/vaadin/event/FieldEvents.java View File

import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Method; import java.lang.reflect.Method;


import com.vaadin.server.SerializableConsumer;
import com.vaadin.shared.EventId; import com.vaadin.shared.EventId;
import com.vaadin.shared.Registration; import com.vaadin.shared.Registration;
import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc; import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc;
} }
} }


/**
* 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 View File

/*
* 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 View File

*/ */
package com.vaadin.ui; package com.vaadin.ui;


import java.util.Objects;

import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener; import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.BlurNotifier; 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.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.event.FieldEvents.FocusNotifier; import com.vaadin.event.FieldEvents.FocusNotifier;
implements Focusable, FocusNotifier, BlurNotifier { implements Focusable, FocusNotifier, BlurNotifier {


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


@Override @Override

+ 2
- 10
server/src/main/java/com/vaadin/ui/CheckBox.java View File

import com.vaadin.event.FieldEvents; import com.vaadin.event.FieldEvents;
import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener; 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.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.MouseEventDetails;
} }
}; };


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

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



+ 39
- 1
server/src/main/java/com/vaadin/ui/CheckBoxGroup.java View File

import java.util.Collection; import java.util.Collection;


import com.vaadin.data.Listing; 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.SerializablePredicate;
import com.vaadin.server.data.DataSource; import com.vaadin.server.data.DataSource;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.optiongroup.CheckBoxGroupState; import com.vaadin.shared.ui.optiongroup.CheckBoxGroupState;


/** /**
* @author Vaadin Ltd * @author Vaadin Ltd
* @since 8.0 * @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. * Constructs a new CheckBoxGroup with caption.
* @see Listing#setDataSource(DataSource) * @see Listing#setDataSource(DataSource)
*/ */
public CheckBoxGroup() { public CheckBoxGroup() {
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
} }


/** /**
SerializablePredicate<T> itemEnabledProvider) { SerializablePredicate<T> itemEnabledProvider) {
super.setItemEnabledProvider(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 View File

import com.vaadin.event.FieldEvents; import com.vaadin.event.FieldEvents;
import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener; 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.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.server.KeyMapper; import com.vaadin.server.KeyMapper;
} }
}; };


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

private String filterstring; private String filterstring;


/** /**
*/ */
private void init() { private void init() {
registerRpc(rpc); registerRpc(rpc);
registerRpc(focusBlurRpc);
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));


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

+ 2
- 11
server/src/main/java/com/vaadin/ui/TabSheet.java View File

import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener; import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.BlurNotifier; 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.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.event.FieldEvents.FocusNotifier; import com.vaadin.event.FieldEvents.FocusNotifier;
super(); super();


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


// expand horizontally by default // expand horizontally by default
setWidth(100, UNITS_PERCENTAGE); setWidth(100, UNITS_PERCENTAGE);


private TabsheetServerRpcImpl rpc = new TabsheetServerRpcImpl(); 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 * Replaces a component (tab content) with another. This can be used to
* change tab contents or to rearrange tabs. The tab position and some * 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 View File

/*
* 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 View File

/*
* 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."));
}
}

Loading…
Cancel
Save