/*
* Copyright 2010 Google Inc.
*
* 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.google.gwt.sample.showcase.client.content.cell;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.dom.client.HasKeyDownHandlers;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.CssResource.Import;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.Settings;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseRaw;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.content.cell.ContactDatabase.ContactInfo;
import com.google.gwt.sample.showcase.client.content.cell.CustomKeyboardHandler.SelectableWidget;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.CellList;
import com.google.gwt.user.cellview.client.CellWidget;
import com.google.gwt.user.cellview.client.HasKeyboardPagingPolicy.KeyboardPagingPolicy;
import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;
/**
* Example file.
*/
@ShowcaseRaw({
"ContactDatabase.java", "CwCellList.ui.xml", "ContactInfoForm.java",
"ShowMorePagerPanel.java", "RangeLabelPager.java", "CwCellList.css"})
public class CwCellList extends ContentWidget {
/**
* The UiBinder interface used by this example.
*/
@ShowcaseSource
interface Binder extends UiBinder<Widget, CwCellList> {
}
/**
* The constants used in this Content Widget.
*/
@ShowcaseSource
public static interface CwConstants extends Constants {
String cwCellListDescription();
String cwCellListName();
}
/**
* The images used for this example.
*/
@ShowcaseSource
static interface Images extends ClientBundle {
ImageResource contact();
ImageResource star();
ImageResource starOutline();
}
/**
* The resources used by this example.
*/
@ShowcaseSource
interface Resources extends ClientBundle {
/**
* Get the styles used but this example.
*/
@Source("CwCellList.css")
@Import(CellList.Style.class)
Styles styles();
}
/**
* The CSS Resources used by this example.
*/
@ShowcaseSource
interface Styles extends CssResource {
String contactFormCell();
String range();
String composite();
String selfAndOthersContainer();
String selfContact();
String scrollContainer();
String scrollable();
String settings();
}
/**
* The Cell used to render a {@link ContactInfo}.
*/
@ShowcaseSource
static class ContactCell extends AbstractCell<ContactInfo> {
/**
* The html of the image used for contacts.
*/
private final SafeHtml imageHtml;
public ContactCell(ImageResource image) {
this.imageHtml = AbstractImagePrototype.create(image).getSafeHtml();
}
interface Templates extends SafeHtmlTemplates {
@Template(
"<table>"
+ "<tr>"
+ " <td rowspan='2'>{0}</td>"
+ " <td style='font-size:95%;'>{1}</td>"
+ "</tr>"
+ "<tr>"
+ " <td>{2}</td>"
+ "</tr>"
+ "</table>")
SafeHtml cell(SafeHtml imageHtml, String fullName, String address);
}
@Override
public void render(Context context, ContactInfo value, SafeHtmlBuilder sb) {
// Value can be null, so do a null check..
if (value == null) {
return;
}
sb.append(
GWT.<Templates>create(Templates.class).cell(
imageHtml, value.getFullName(), value.getAddress()));
}
}
/**
* The contact form used to update contacts.
*/
@ShowcaseData
@UiField
ContactInfoForm contactForm;
/**
* The button used to generate more contacts.
*/
@ShowcaseData
@UiField
Button generateButton;
/**
* The pager used to change the range of data.
*/
@ShowcaseData
@UiField
ShowMorePagerPanel pagerPanel;
/**
* The pager used to display the current range.
*/
@ShowcaseData
@UiField
RangeLabelPager rangeLabelPager;
@UiField
CheckBox predictiveScrollingCheckbox;
@UiField
CheckBox prefetchingCheckbox;
@UiField
CheckBox conservativeStartCheckbox;
@UiField
CheckBox windowFillingCheckbox;
@UiField
CheckBox keyHandlingCheckbox;
@UiField
CheckBox focusDriftingCheckbox;
@UiField
CheckBox compositeCellCheckbox;
@UiField
FocusPanel selfContactContainer;
private CellWidget<ContactInfo> selfWidget;
/**
* The CellList.
*/
@ShowcaseData
private CellList<ContactInfo> cellList;
/**
* Constructor.
*
* @param constants the constants
*/
public CwCellList(CwConstants constants) {
super(constants.cwCellListName(), constants.cwCellListDescription(), false,
"ContactDatabase.java", "CwCellList.ui.xml", "ContactInfoForm.java",
"ShowMorePagerPanel.java", "RangeLabelPager.java");
}
/**
* Initialize this example.
*/
@ShowcaseSource
@Override
public Widget onInitialize() {
Resources resources = GWT.<Resources>create(Resources.class);
resources.styles().ensureInjected();
Images images = GWT.create(Images.class);
// Create the UiBinder.
// Bind this before creating the CellList so we can rely on the UiFields
// existing when the custom keyboard handler is set up.
Binder uiBinder = GWT.create(Binder.class);
Widget widget = uiBinder.createAndBindUi(this);
// Create a CellList.
Cell<ContactInfo> contactCell = Settings.get().getCompositeCell()
? new CompositeContactCell(images) : new ContactCell(images.contact());
// Set a key provider that provides a unique key for each contact. If key is
// used to identify contacts when fields (such as the name and address)
// change.
cellList = new CellList<>(
Settings.get().getFocusDrifting()
? Cells.makeFocusableWithoutScrolling(contactCell, pagerPanel.getScrollable())
: contactCell,
ContactDatabase.ContactInfo.KEY_PROVIDER);
cellList.setPageSize(getInitialPageSize());
setupKeyboardHandling();
// Add a selection model so we can select cells.
final SingleSelectionModel<ContactInfo> selectionModel =
new SingleSelectionModel<ContactInfo>(
ContactDatabase.ContactInfo.KEY_PROVIDER);
cellList.setSelectionModel(selectionModel);
selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
public void onSelectionChange(SelectionChangeEvent event) {
contactForm.setContact(selectionModel.getSelectedObject());
}
});
// Re-use the cell to render in a widget and show a contact for the user
selfWidget = new CellWidget<>(
contactCell, ContactDatabase.get().createContactForMe());
selfContactContainer.setWidget(selfWidget);
if (Settings.get().getCompositeCell()) {
selfWidget.addStyleName(resources.styles().composite());
}
addSelectHandlers(selfContactContainer, new Runnable() {
@Override
public void run() {
showSelfContactInfo();
}
});
// Add the CellList to the data provider in the database.
ContactDatabase.get().addDataDisplay(cellList);
// Set the cellList as the display of the pagers. This example has two
// pagers. pagerPanel is a scrollable pager that extends the range when the
// user scrolls to the bottom. rangeLabelPager is a pager that displays the
// current range, but does not have any controls to change the range.
pagerPanel.setDisplay(cellList);
rangeLabelPager.setDisplay(cellList);
// Handle events from the generate button.
// Buttons fire their click handler when Enter is pressed
generateButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
ContactDatabase.get().generateContacts(50);
}
});
Prefetcher.install(cellList);
WindowFiller.install(cellList);
Settings.get().addPredictiveScrollingValueChangeHandler(
new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
predictiveScrollingCheckbox.setValue(event.getValue());
}
});
predictiveScrollingCheckbox.setValue(
Settings.get().getPredictiveScrolling());
Settings.get().addPrefetchingValueChangeHandler(
new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
prefetchingCheckbox.setValue(event.getValue());
}
});
prefetchingCheckbox.setValue(Settings.get().getPrefetching());
Settings.get().addConservativeStartChangeHandler(
new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
conservativeStartCheckbox.setValue(event.getValue());
}
});
conservativeStartCheckbox.setValue(Settings.get().getConservativeStart());
Settings.get().addWindowFillingChangeHandler(
new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
windowFillingCheckbox.setValue(event.getValue());
}
});
windowFillingCheckbox.setValue(Settings.get().getWindowFilling());
Settings.get().addKeyHandlingChangeHandler(
new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
keyHandlingCheckbox.setValue(event.getValue());
setKeyboardPagingPolicy();
}
});
keyHandlingCheckbox.setValue(Settings.get().getKeyHandling());
Settings.get().addFocusDriftingChangeHandler(
new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
focusDriftingCheckbox.setValue(event.getValue());
}
});
focusDriftingCheckbox.setValue(Settings.get().getFocusDrifting());
Settings.get().addCompositeCellChangeHandler(
new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
compositeCellCheckbox.setValue(event.getValue());
setKeyboardPagingPolicy();
}
});
compositeCellCheckbox.setValue(Settings.get().getCompositeCell());
return widget;
}
@Override
protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
GWT.runAsync(CwCellList.class, new RunAsyncCallback() {
public void onFailure(Throwable caught) {
callback.onFailure(caught);
}
public void onSuccess() {
callback.onSuccess(onInitialize());
}
});
}
@Override
public boolean hasScrollableContent() {
return false;
}
private <W extends HasClickHandlers & HasKeyDownHandlers>
void addSelectHandlers(W widget, final Runnable handler) {
widget.addKeyDownHandler(new KeyDownHandler() {
@Override
public void onKeyDown(KeyDownEvent event) {
if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
handler.run();
}
}
});
widget.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
handler.run();
}
});
}
void showSelfContactInfo() {
// We always set the self contact, so we can count on it not being null
contactForm.setContact(selfWidget.getValue());
}
void setupKeyboardHandling() {
SelectableWidget topWidgetForKeyHandler = new SelectableWidget() {
@Override
public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
return selfContactContainer.addKeyDownHandler(handler);
}
@Override
public void fireEvent(GwtEvent<?> event) {
selfContactContainer.fireEvent(event);
}
@Override
public void selectWidget() {
selfContactContainer.setFocus(true);
showSelfContactInfo();
}
};
cellList.setKeyboardSelectionHandler(
new CustomKeyboardHandler(cellList, topWidgetForKeyHandler));
setKeyboardPagingPolicy();
cellList.setKeyboardSelectionPolicy(
KeyboardSelectionPolicy.BOUND_TO_SELECTION);
}
void setKeyboardPagingPolicy() {
if (Settings.get().getKeyHandling()) {
cellList.setKeyboardPagingPolicy(KeyboardPagingPolicy.CURRENT_PAGE);
} else {
cellList.setKeyboardPagingPolicy(KeyboardPagingPolicy.INCREASE_RANGE);
}
}
@UiHandler("predictiveScrollingCheckbox")
protected void onPredictiveScrollingCheckboxChange(
ValueChangeEvent<Boolean> event) {
Settings.get().setPredictiveScrolling(event.getValue());
}
@UiHandler("prefetchingCheckbox")
protected void onPrefetchingCheckboxChange(
ValueChangeEvent<Boolean> event) {
Settings.get().setPrefetching(event.getValue());
}
@UiHandler("conservativeStartCheckbox")
protected void onConservativeStartCheckboxChange(
ValueChangeEvent<Boolean> event) {
Settings.get().setConservativeStart(event.getValue());
}
@UiHandler("windowFillingCheckbox")
protected void onWindowFillingCheckboxChange(
ValueChangeEvent<Boolean> event) {
Settings.get().setWindowFilling(event.getValue());
}
@UiHandler("keyHandlingCheckbox")
protected void onKeyHandlingCheckboxChange(
ValueChangeEvent<Boolean> event) {
Settings.get().setKeyHandling(event.getValue());
setKeyboardPagingPolicy();
}
@UiHandler("focusDriftingCheckbox")
protected void onFocusDriftingCheckboxChange(
ValueChangeEvent<Boolean> event) {
Settings.get().setFocusDrifting(event.getValue());
}
@UiHandler("compositeCellCheckbox")
protected void onCompositeCellCheckboxChange(
ValueChangeEvent<Boolean> event) {
Settings.get().setCompositeCell(event.getValue());
}
private static int getInitialPageSize() {
return Settings.get().getConservativeStart() ? 5 : 15;
}
}