Fixes vaadin/framework8-issues#334 Change-Id: I4c7ca424cc4f4a1f0cdecd7671827465ab74ace7tags/8.0.0.alpha6
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; | ||||
} | } | ||||
} | } |
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); | ||||
} | } |
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); | |||||
} | |||||
} | } |
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(); | ||||
} | } | ||||
} | } |
/* | |||||
* 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(); | |||||
} | |||||
} | |||||
} |
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) { |
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); | ||||
} | } | ||||
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 |
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); | |||||
} | |||||
} | |||||
} | } |
/* | |||||
* 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 | |||||
} |
*/ | */ | ||||
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 |
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); | ||||
} | } | ||||
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); | |||||
} | |||||
} | } |
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, |
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 |
/* | |||||
* 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")); | |||||
} | |||||
} |
/* | |||||
* 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.")); | |||||
} | |||||
} |