Adding ItemPicker, ColorPicker, SuggestPicker, AbstractItemPicker Reviewed by: knorton git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@740 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/AbstractItemPicker.java b/user/src/com/google/gwt/user/client/ui/AbstractItemPicker.java new file mode 100644 index 0000000..f5fa331 --- /dev/null +++ b/user/src/com/google/gwt/user/client/ui/AbstractItemPicker.java
@@ -0,0 +1,264 @@ +/* + * Copyright 2006 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.user.client.ui; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Helpful base implementation of the {@link ItemPicker} interface. + */ +abstract class AbstractItemPicker extends Composite implements ItemPicker { + /* + * Implementation note:AbstractItemPicker is package protected because we are + * hoping we might eventually be able to slip in a more efficient + * implementation of this class, so do not want to be bound by this + * implementation. + */ + + /** + * Selectable item. + */ + class Item extends Widget { + private int index; + + /** + * + * Constructor for <code>Item</code>. + * + * @param index index associated with item + */ + Item(int index) { + setElement(DOM.createDiv()); + sinkEvents(Event.ONMOUSEDOWN | Event.ONMOUSEOVER); + this.index = index; + this.setStyleName(itemStyleName); + items.add(index, this); + } + + public void onBrowserEvent(Event event) { + switch (DOM.eventGetType(event)) { + case Event.ONMOUSEOVER: + setSelection(this); + break; + case Event.ONMOUSEDOWN: + commitSelection(); + break; + } + } + + public String toString() { + return "value: " + getValue(getIndex()) + " index: " + this.getIndex(); + } + + /** + * Gets the index of the item. + * + * @return the item's index + */ + int getIndex() { + return index; + } + + AbstractItemPicker getOwner() { + return AbstractItemPicker.this; + } + } + + private static final String STYLENAME_SELECTED_ITEM = "selected"; + private static final String STYLENAME_ITEM = "item"; + + private ChangeListenerCollection changeListeners = new ChangeListenerCollection(); + private Item selectedItem; + private final String selectedStyleName; + private final String itemStyleName; + private final ArrayList items; + + /** + * Constructor for <code>ItemPicker</code>. Provides "item" as the default + * item style name, and "selected" as the default selected item style name. + */ + public AbstractItemPicker() { + this(STYLENAME_ITEM, STYLENAME_SELECTED_ITEM); + } + + /** + * + * Constructor for <code>ItemPicker</code>. + * + * @param itemStyleName CSS style name for default items + * @param selectedItemStyleName CSS style name for the currently selected item + */ + public AbstractItemPicker(String itemStyleName, String selectedItemStyleName) { + initWidget(new FlexTable()); + this.selectedStyleName = selectedItemStyleName; + this.itemStyleName = itemStyleName; + + // CSS does not effect padding and spacing correctly. So setting to 0 + // here. + getLayout().setCellPadding(0); + getLayout().setCellSpacing(0); + items = new ArrayList(); + } + + public final void addChangeListener(ChangeListener listener) { + if (changeListeners == null) { + changeListeners = new ChangeListenerCollection(); + } + changeListeners.add(listener); + } + + public void commitSelection() { + if (selectedItem == null) { + throw new IllegalStateException("No element is selected"); + } + changeListeners.fireChange(this); + } + + public abstract boolean delegateKeyDown(char keyCode); + + public int getItemCount() { + return items.size(); + } + + public final int getSelectedIndex() { + Item item = getSelectedItem(); + if (item == null) { + return -1; + } + return item.getIndex(); + } + + public final Object getSelectedValue() { + int index = getSelectedIndex(); + if (index == -1) { + return null; + } + return getValue(index); + } + + public Object getValue(int index) { + return getValue(getItem(index).getElement()); + } + + public final void removeChangeListener(ChangeListener listener) { + this.changeListeners.remove(listener); + } + + public abstract void setItems(Collection items); + + public final void setSelectedIndex(int index) { + Item item = getItem(index); + setSelection(item); + } + + /** + * Formats the displayed element using the information given by the user + * supplied item. + * + * @param displayedElement the element used to the display the item + * @param item the user supplied item information + */ + protected void format(Element displayedElement, Object item) { + DOM.setInnerHTML(displayedElement, item.toString()); + } + + /** + * Gets the value from a given element. By default this method is used by + * {@link AbstractItemPicker#getValue(int)} to compute the value that should + * be returned to the user. + * + * @param displayedElement displayed element + * @return the value associated with the given displayed element + */ + protected Object getValue(Element displayedElement) { + return DOM.getInnerText(displayedElement); + } + + void clearItems() { + items.clear(); + getLayout().clear(); + } + + /** + * Gets the ith item. + * + * @param index index of item + * @return the ith item + */ + Item getItem(int index) { + return (Item) items.get(index); + } + + FlexTable getLayout() { + return (FlexTable) getWidget(); + } + + /** + * Gets the currently selected item. + * + * @return selected item + */ + Item getSelectedItem() { + return selectedItem; + } + + /** + * Sets the current selection. + * + * @param item item to set + */ + void setSelection(Item item) { + if (selectedItem == item) { + return; + } + + // Remove old selected item. + if (selectedItem != null) { + selectedItem.removeStyleName(selectedStyleName); + selectedItem.addStyleName(itemStyleName); + } + + // Add new selected item. + selectedItem = item; + if (selectedItem != null) { + selectedItem.removeStyleName(itemStyleName); + selectedItem.addStyleName(selectedStyleName); + } + } + + /** + * Shifts the current selection by the given amount, unless that would make + * the selection invalid. + * + * @param shift the amount to shift the current selection by + */ + void shiftSelection(int shift) { + int newIndex = getSelectedIndex() + shift; + if (newIndex < 0 || newIndex >= getItemCount()) { + return; + } else { + Item item = getItem(newIndex); + setSelection(item); + } + } + +}
diff --git a/user/src/com/google/gwt/user/client/ui/ColorPicker.java b/user/src/com/google/gwt/user/client/ui/ColorPicker.java new file mode 100644 index 0000000..ee0052e --- /dev/null +++ b/user/src/com/google/gwt/user/client/ui/ColorPicker.java
@@ -0,0 +1,202 @@ +/* + * Copyright 2007 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.user.client.ui; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + * Color picker. Each "item" represents a CSS color. + */ +public class ColorPicker extends AbstractItemPicker { + + class ColorItem extends Item { + private Object color; + + ColorItem(int index, Object color) { + super(index); + this.color = color; + } + + Object getColor() { + return color; + } + } + + /** + * Default colors supplied to the color picker popup. The default colors are a + * selection of 60 web-safe CSS color styles. + */ + public static final List DEFAULT_COLORS; + + private static final String STYLENAME_DEFAULT = "gwt-ColorPicker "; + + static { + String[] baseColors = { + "#ffffcc", "#ffff66", "#ffcc66", "#F2984C", "#E1771E", "#B47B10", + "#A9501B", "#6F3C1B", "#804000", "#CC0000", "#940F04", "#660000", + "#C3D9FF", "#99C9FF", "#66B5FF", "#3D81EE", "#0066CC", "#6C82B5", + "#32527A", "#2D6E89", "#006699", "#215670", "#003366", "#000033", + "#CAF99B", "#80FF00", "#00FF80", "#78B749", "#2BA94F", "#38B63C", + "#0D8F63", "#2D8930", "#1B703A", "#11593C", "#063E3F", "#002E3F", + "#FFBBE8", "#E895CC", "#FF6FCF", "#C94093", "#9D1961", "#800040", + "#800080", "#72179D", "#6728B2", "#6131BD", "#341473", "#400058", + "#ffffff", "#e6e6e6", "#cccccc", "#b3b3b3", "#999999", "#808080", + "#7f7f7f", "#666666", "#4c4c4c", "#333333", "#191919", "#000000"}; + DEFAULT_COLORS = Arrays.asList(baseColors); + } + + private int columnsPerRow = -1; + + /** + * Constructor for {@link ColorPicker}. + */ + public ColorPicker() { + this(12); + } + + /** + * Constructor for {@link ColorPicker}. + * + * @param numColumns number of columns to be displayed + */ + public ColorPicker(int numColumns) { + this(DEFAULT_COLORS, numColumns); + } + + /** + * + * Constructor for {@link ColorPicker}. The passed in {@link Collection} + * should contain objects such that the {@link String#toString()} method + * returns a representation of a CSS color. + * + * @param colors color collection + * @param numColumns number of columns to be displayed + */ + public ColorPicker(Collection colors, int numColumns) { + this.setColumnsPerRow(numColumns); + setStyleName(STYLENAME_DEFAULT); + setItems(colors); + } + + public boolean delegateKeyDown(char keyCode) { + if (isAttached()) { + switch (keyCode) { + case KeyboardListener.KEY_DOWN: + shiftSelection(getColumnsPerRow()); + break; + case KeyboardListener.KEY_UP: + shiftSelection(-getColumnsPerRow()); + break; + case KeyboardListener.KEY_LEFT: + shiftSelection(-1); + break; + case KeyboardListener.KEY_RIGHT: + shiftSelection(1); + break; + case KeyboardListener.KEY_ENTER: + commitSelection(); + break; + default: + // Avoid shared post processing. + return false; + } + return true; + } else { + return false; + } + } + + /** + * Gets the number of columns to display per row of colors. + * + * @return numColumns number of columns + */ + public int getColumnsPerRow() { + return columnsPerRow; + } + + public Object getValue(int i) { + ColorItem item = (ColorItem) getItem(i); + if (item == null) { + return null; + } else { + return item.getColor(); + } + } + + /** + * Sets the number of columns to display per row of colors. + * + * @param numColumns number of columns + */ + public void setColumnsPerRow(int numColumns) { + if (numColumns <= 0) { + throw new IllegalStateException("Cannot use " + numColumns + + " as the number of columns per row"); + } + this.columnsPerRow = numColumns; + } + + /** + * Sets the items in the {@link ColorPicker}. Each color's {@link String#toString()} + * method should return a representation of a CSS color. + * + * @param colors collection of colors. + */ + public final void setItems(Collection colors) { + setItems(colors.iterator()); + } + + protected void format(Element e, Object item) { + DOM.setStyleAttribute(e, "background", item.toString()); + } + + /** + * Sets the items in the {@link ColorPicker}. + */ + private final void setItems(Iterator colors) { + clearItems(); + int row = 0; + int i = 0; + while (true) { + + for (int column = 0; column < getColumnsPerRow(); column++) { + if (!colors.hasNext()) { + if (i == 0) { + throw new IllegalStateException( + "Cannot populate a color picker with 0 colors!"); + } + // All items have been placed. + return; + } + + ColorItem item = new ColorItem(i, colors.next()); + format(item.getElement(), item.getColor()); + getLayout().setWidget(row, column, item); + ++i; + } + ++row; + } + } + +}
diff --git a/user/src/com/google/gwt/user/client/ui/ItemPicker.java b/user/src/com/google/gwt/user/client/ui/ItemPicker.java new file mode 100644 index 0000000..1734a17 --- /dev/null +++ b/user/src/com/google/gwt/user/client/ui/ItemPicker.java
@@ -0,0 +1,99 @@ +/* + * Copyright 2006 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.user.client.ui; + +import java.util.Collection; + +/** + * Represents a pickable list of items. Each {@link ItemPicker} should be able + * to respond to mouse and keyboard events. + * + */ +public interface ItemPicker extends SourcesChangeEvents { + + /** + * Commits the current selection. Any relevant change listeners are fired. + */ + public void commitSelection(); + + /** + * Allows the {@link ItemPicker} to be controlled via keyboard input. This + * method should be hooked up to an appropriate + * {@link KeyboardListener#onKeyDown(Widget, char, int)} method. + * + * @param keyCode key code + * @return <code>true</code> if the key code was consumed by the picker, + * <code>false</code> otherwise + */ + public boolean delegateKeyDown(char keyCode); + + /** + * Gets the number of items. + * + * @return number of items + */ + public int getItemCount(); + + /** + * Gets the currently selected index. + * + * @return selected index, or -1 if no index is selected + */ + public int getSelectedIndex(); + + /** + * Gets the value associated with the currently selected index. + * + * <p> + * The value should be convertible into a human readable {@link String} by + * calling the {@link String#toString()} method. + * </p> + * + * @return current selected value, or null if no value is selected + */ + public Object getSelectedValue(); + + /** + * Gets the value associated with the given index. + * <p> + * The value should be convertible into a human readable {@link String} by + * calling the {@link String#toString()}. + * </p> + * + * @param index index + * @return the value associated with <code>index</code>. + */ + // Design note: Object is returned rather than String in order to allow the + // user to return type-safe enumerations. + public Object getValue(int index); + + /** + * Sets the items to be displayed. The expected type of each item should be + * clearly documented in each class which implements this interface. + * + * @param items items to be displayed + */ + public void setItems(Collection items); + + /** + * Sets the currently selected index. + * + * @param index new selected index + */ + public void setSelectedIndex(int index); + +} \ No newline at end of file
diff --git a/user/src/com/google/gwt/user/client/ui/SuggestPicker.java b/user/src/com/google/gwt/user/client/ui/SuggestPicker.java new file mode 100644 index 0000000..9c30670 --- /dev/null +++ b/user/src/com/google/gwt/user/client/ui/SuggestPicker.java
@@ -0,0 +1,143 @@ +/* + * Copyright 2007 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.user.client.ui; + +import java.util.Collection; +import java.util.Iterator; + +/** + * Suggestion picker. Each "item" represents a suggestion. + */ +public class SuggestPicker extends AbstractItemPicker { + + /** + * Default style for the picker. + */ + private static final String STYLENAME_DEFAULT = "gwt-SuggestPicker"; + + private int startInvisible = Integer.MAX_VALUE; + + /** + * Constructor for <code>SuggestPicker</code>. + */ + public SuggestPicker() { + setStyleName(STYLENAME_DEFAULT); + } + + public boolean delegateKeyDown(char keyCode) { + if (isAttached()) { + switch (keyCode) { + case KeyboardListener.KEY_DOWN: + shiftSelection(1); + return true; + case KeyboardListener.KEY_UP: + shiftSelection(-1); + return true; + case KeyboardListener.KEY_ENTER: + commitSelection(); + return true; + } + } + return false; + } + + public int getItemCount() { + if (startInvisible == Integer.MAX_VALUE) { + return 0; + } else { + return startInvisible; + } + } + + /** + * Sets the suggestions associated with this picker. + * + * @param suggestions suggestions for this picker; the suggestions must have + * valid {@link String#toString()} methods + */ + public final void setItems(Collection suggestions) { + setItems(suggestions.iterator()); + } + + void shiftSelection(int shift) { + int newSelect = getSelectedIndex() + shift; + if (newSelect >= super.getItemCount() || newSelect < 0 + || newSelect >= startInvisible) { + return; + } + setSelection(getItem(newSelect)); + } + + /** + * Ensures the existence of the given item and returns it. + * + * @param itemIndex item index to ensure + * @return associated item + */ + private Item ensureItem(int itemIndex) { + for (int i = super.getItemCount(); i <= itemIndex; i++) { + Item item = new Item(i); + getLayout().setWidget(i, 0, item); + } + return getItem(itemIndex); + } + + /** + * Sets the suggestions associated with this picker. + * + */ + private final void setItems(Iterator suggestions) { + int itemCount = 0; + + // Ensure all needed items exist and set each item's html to the given + // suggestion. + while (suggestions.hasNext()) { + Item item = ensureItem(itemCount); + format(item.getElement(), suggestions.next()); + ++itemCount; + } + + if (itemCount == 0) { + throw new IllegalStateException( + "Must set at least one item in a SuggestPicker"); + } + + // Render visible all needed cells. + int min = Math.min(itemCount, super.getItemCount()); + for (int i = startInvisible; i < min; i++) { + setVisible(i, true); + } + + // Render invisible all useless cells. + startInvisible = itemCount; + for (int i = itemCount; i < super.getItemCount(); i++) { + setVisible(i, false); + } + } + + /** + * Sets whether the given item is visible. + * + * @param itemIndex item index + * @param visible visible boolean + */ + private void setVisible(int itemIndex, boolean visible) { + UIObject.setVisible(getLayout().getRowFormatter().getElement(itemIndex), + visible); + } + +} \ No newline at end of file