Java tutorial
/* * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for * additional information regarding copyright ownership. * * The Apereo Foundation licenses this file to you 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 org.unitime.timetable.gwt.client.aria; import java.util.List; import org.unitime.timetable.gwt.client.ToolBox; import org.unitime.timetable.gwt.resources.GwtAriaMessages; import com.google.gwt.aria.client.AutocompleteValue; import com.google.gwt.aria.client.Id; import com.google.gwt.aria.client.Roles; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.core.shared.GWT; 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.HandlesAllKeyEvents; import com.google.gwt.event.dom.client.HasBlurHandlers; import com.google.gwt.event.dom.client.HasFocusHandlers; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.logical.shared.HasSelectionHandlers; import com.google.gwt.event.logical.shared.SelectionEvent; import com.google.gwt.event.logical.shared.SelectionHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.Focusable; import com.google.gwt.user.client.ui.HasEnabled; import com.google.gwt.user.client.ui.HasText; import com.google.gwt.user.client.ui.HasValue; import com.google.gwt.user.client.ui.MenuBar; import com.google.gwt.user.client.ui.MenuItem; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.SuggestOracle; import com.google.gwt.user.client.ui.ValueBoxBase; import com.google.gwt.user.client.ui.SuggestOracle.Request; import com.google.gwt.user.client.ui.SuggestOracle.Response; import com.google.gwt.user.client.ui.SuggestOracle.Suggestion; /** * @author Tomas Muller */ public class AriaSuggestBox extends Composite implements HasText, HasValue<String>, HasSelectionHandlers<Suggestion>, Focusable, HasEnabled, HasAriaLabel { private static GwtAriaMessages ARIA = GWT.create(GwtAriaMessages.class); private AriaTextBox iText; private SuggestOracle iOracle; private PopupPanel iSuggestionPopup; private SuggestionMenu iSuggestionMenu; private ScrollPanel iPopupScroll; private SuggestionCallback iSuggestionCallback; private SuggestOracle.Callback iOracleCallback; private String iCurrentText = null; public AriaSuggestBox(SuggestOracle oracle) { this(new AriaTextBox(), oracle); } public AriaSuggestBox(AriaTextBox box, SuggestOracle oracle) { iOracle = oracle; iText = box; iText.setStyleName("gwt-SuggestBox"); initWidget(iText); addEventsToTextBox(); iSuggestionMenu = new SuggestionMenu(); iPopupScroll = new ScrollPanel(iSuggestionMenu); iPopupScroll.addStyleName("scroll"); iSuggestionPopup = new PopupPanel(true, false); iSuggestionPopup.setPreviewingAllNativeEvents(true); iSuggestionPopup.setStyleName("unitime-SuggestBoxPopup"); iSuggestionPopup.setWidget(iPopupScroll); iSuggestionPopup.addAutoHidePartner(getElement()); iSuggestionCallback = new SuggestionCallback() { @Override public void onSuggestionSelected(Suggestion suggestion) { if (!suggestion.getReplacementString().isEmpty()) { setStatus(ARIA.suggestionSelected(status(suggestion))); } iCurrentText = suggestion.getReplacementString(); setText(suggestion.getReplacementString()); hideSuggestionList(); fireSuggestionEvent(suggestion); } }; iOracleCallback = new SuggestOracle.Callback() { @Override public void onSuggestionsReady(Request request, Response response) { if (response.getSuggestions() == null || response.getSuggestions().isEmpty()) { if (iSuggestionPopup.isShowing()) iSuggestionPopup.hide(); } else { iSuggestionMenu.clearItems(); SuggestOracle.Suggestion first = null; for (SuggestOracle.Suggestion suggestion : response.getSuggestions()) { iSuggestionMenu.addItem(new SuggestionMenuItem(suggestion)); if (first == null) first = suggestion; } iSuggestionMenu.selectItem(0); ToolBox.setMinWidth(iSuggestionMenu.getElement().getStyle(), (iText.getElement().getClientWidth() - 4) + "px"); iSuggestionPopup.showRelativeTo(iText); iSuggestionMenu.scrollToView(); if (response.getSuggestions().size() == 1) { if (first.getReplacementString().isEmpty()) setStatus(status(first)); else setStatus(ARIA.showingOneSuggestion(status(first))); } else { setStatus(ARIA.showingMultipleSuggestions(response.getSuggestions().size(), request.getQuery(), status(first))); } } } }; Roles.getTextboxRole().setAriaAutocompleteProperty(iText.getElement(), AutocompleteValue.NONE); iSuggestionPopup.getElement().setAttribute("id", DOM.createUniqueId()); Roles.getTextboxRole().setAriaOwnsProperty(iText.getElement(), Id.of(iSuggestionPopup.getElement())); } private String status(Suggestion suggestion) { return suggestion instanceof HasStatus ? ((HasStatus) suggestion).getStatusString() : suggestion.getDisplayString(); } public void setStatus(String text) { AriaStatus.getInstance().setText(text); } private void addEventsToTextBox() { class TextBoxEvents extends HandlesAllKeyEvents implements ValueChangeHandler<String> { public void onKeyDown(KeyDownEvent event) { switch (event.getNativeKeyCode()) { case KeyCodes.KEY_DOWN: if (moveSelectionDown()) { event.preventDefault(); return; } if (!isSuggestionListShowing() && (event.getNativeEvent().getAltKey() || iText.getCursorPos() == iText.getText().length())) showSuggestionList(); break; case KeyCodes.KEY_UP: if (moveSelectionUp()) { event.preventDefault(); return; } break; case KeyCodes.KEY_ENTER: if (isSuggestionListShowing()) iSuggestionMenu.executeSelected(); break; case KeyCodes.KEY_TAB: if (isSuggestionListShowing()) { if (getText().isEmpty()) hideSuggestionList(); else iSuggestionMenu.executeSelected(); } break; case KeyCodes.KEY_ESCAPE: if (isSuggestionListShowing()) hideSuggestionList(); } delegateEvent(AriaSuggestBox.this, event); } public void onKeyPress(KeyPressEvent event) { delegateEvent(AriaSuggestBox.this, event); } public void onKeyUp(KeyUpEvent event) { refreshSuggestions(); delegateEvent(AriaSuggestBox.this, event); } public void onValueChange(ValueChangeEvent<String> event) { delegateEvent(AriaSuggestBox.this, event); } } TextBoxEvents events = new TextBoxEvents(); events.addKeyHandlersTo(iText); iText.addValueChangeHandler(events); } private boolean moveSelectionDown() { if (!isSuggestionListShowing()) return false; if (iSuggestionMenu.selectItem(iSuggestionMenu.getSelectedItemIndex() + 1)) { if (iSuggestionMenu.getNumItems() > 1) setStatus(ARIA.onSuggestion(iSuggestionMenu.getSelectedItemIndex() + 1, iSuggestionMenu.getNumItems(), status(iSuggestionMenu.getSelectedSuggestion()))); return true; } else { return false; } } private boolean moveSelectionUp() { if (!isSuggestionListShowing()) return false; boolean selected = false; if (iSuggestionMenu.getSelectedItemIndex() == -1) { selected = iSuggestionMenu.selectItem(iSuggestionMenu.getNumItems() - 1); } else { selected = iSuggestionMenu.selectItem(iSuggestionMenu.getSelectedItemIndex() - 1); } if (selected) { if (iSuggestionMenu.getNumItems() > 1) setStatus(ARIA.onSuggestion(iSuggestionMenu.getSelectedItemIndex() + 1, iSuggestionMenu.getNumItems(), status(iSuggestionMenu.getSelectedSuggestion()))); return true; } else { return false; } } public void showSuggestionList() { iCurrentText = null; refreshSuggestions(); } private void refreshSuggestions() { String text = getText(); if (text.equals(iCurrentText)) { return; } else { iCurrentText = text; } showSuggestions(text); } public void hideSuggestionList() { if (iSuggestionPopup.isShowing()) iSuggestionPopup.hide(); } public void showSuggestions(String text) { if (text.isEmpty()) iOracle.requestDefaultSuggestions(new Request(null), iOracleCallback); else iOracle.requestSuggestions(new Request(text), iOracleCallback); } public boolean isSuggestionListShowing() { return iSuggestionPopup.isShowing(); } public SuggestionMenu getSuggestionMenu() { return iSuggestionMenu; } private class SuggestionMenu extends MenuBar implements HasFocusHandlers, HasBlurHandlers { SuggestionMenu() { super(true); setStyleName(""); setFocusOnHoverEnabled(false); sinkEvents(Event.ONBLUR); sinkEvents(Event.ONFOCUS); } @Override public void onBrowserEvent(Event event) { switch (DOM.eventGetType(event)) { case Event.ONBLUR: BlurEvent.fireNativeEvent(event, this); break; case Event.ONFOCUS: FocusEvent.fireNativeEvent(event, this); break; } super.onBrowserEvent(event); } public int getNumItems() { return getItems().size(); } public int getSelectedItemIndex() { MenuItem selectedItem = getSelectedItem(); if (selectedItem != null) return getItems().indexOf(selectedItem); return -1; } public boolean selectItem(int index) { List<MenuItem> items = getItems(); if (index > -1 && index < items.size()) { selectItem(items.get(index)); iPopupScroll.ensureVisible(items.get(index)); return true; } return false; } public void scrollToView() { List<MenuItem> items = getItems(); int index = getSelectedItemIndex(); if (index > -1 && index < items.size()) { iPopupScroll.ensureVisible(items.get(index)); } } public boolean executeSelected() { MenuItem selected = getSelectedItem(); if (selected == null) return false; selected.getScheduledCommand().execute(); return true; } public Suggestion getSelectedSuggestion() { MenuItem selectedItem = getSelectedItem(); return selectedItem == null ? null : ((SuggestionMenuItem) selectedItem).getSuggestion(); } @Override public HandlerRegistration addBlurHandler(BlurHandler handler) { return addHandler(handler, BlurEvent.getType()); } @Override public HandlerRegistration addFocusHandler(FocusHandler handler) { return addHandler(handler, FocusEvent.getType()); } } private class SuggestionMenuItem extends MenuItem { private Suggestion iSuggestion = null; private SuggestionMenuItem(final Suggestion suggestion) { super(suggestion.getDisplayString(), iOracle.isDisplayStringHTML(), new ScheduledCommand() { @Override public void execute() { iSuggestionCallback.onSuggestionSelected(suggestion); } }); setStyleName("item"); getElement().setAttribute("whiteSpace", "nowrap"); iSuggestion = suggestion; } public Suggestion getSuggestion() { return iSuggestion; } } @Override public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) { return addHandler(handler, ValueChangeEvent.getType()); } @Override public String getValue() { return iText.getValue(); } @Override public void setValue(String value) { setValue(value, false); } @Override public void setValue(String value, boolean fireEvents) { iText.setValue(value); if (fireEvents) ValueChangeEvent.fire(this, getValue()); } @Override public String getText() { return iText.getText(); } @Override public void setText(String text) { iText.setText(text); } @Override public HandlerRegistration addSelectionHandler(SelectionHandler<Suggestion> handler) { return addHandler(handler, SelectionEvent.getType()); } private void fireSuggestionEvent(Suggestion selectedSuggestion) { SelectionEvent.fire(this, selectedSuggestion); } @Override public int getTabIndex() { return iText.getTabIndex(); } @Override public void setAccessKey(char key) { iText.setAccessKey(key); } @Override public void setFocus(boolean focused) { iText.setFocus(focused); } @Override public void setTabIndex(int index) { iText.setTabIndex(index); } @Override public boolean isEnabled() { return iText.isEnabled(); } @Override public void setEnabled(boolean enabled) { iText.setEnabled(enabled); } public ValueBoxBase<String> getValueBox() { return iText; } public static interface SuggestionCallback { void onSuggestionSelected(Suggestion suggestion); } public static interface HasStatus { public String getStatusString(); } @Override public String getAriaLabel() { return iText.getAriaLabel(); } @Override public void setAriaLabel(String text) { iText.setAriaLabel(text); } }