Implement DatePickerCell and use it the MailRecipe demo
Review at http://gwt-code-reviews.appspot.com/320803
Review by: jgw@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7923 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/src/com/google/gwt/bikeshed/cells/client/Cell.java b/bikeshed/src/com/google/gwt/bikeshed/cells/client/Cell.java
index d26b299..32271d7 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/cells/client/Cell.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/cells/client/Cell.java
@@ -45,7 +45,6 @@
* @param valueUpdater a {@link ValueUpdater}, or null
* @return a view data object which may be the one passed in or a new object
*/
- @SuppressWarnings("unused")
public V onBrowserEvent(Element parent, C value, V viewData,
NativeEvent event, ValueUpdater<C, V> valueUpdater) {
return null;
diff --git a/bikeshed/src/com/google/gwt/bikeshed/cells/client/DateCell.java b/bikeshed/src/com/google/gwt/bikeshed/cells/client/DateCell.java
index 31dfe98..d9cef80 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/cells/client/DateCell.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/cells/client/DateCell.java
@@ -40,5 +40,4 @@
sb.append(format.format(value));
}
}
-
}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/cells/client/DatePickerCell.java b/bikeshed/src/com/google/gwt/bikeshed/cells/client/DatePickerCell.java
new file mode 100644
index 0000000..d1c302b
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/bikeshed/cells/client/DatePickerCell.java
@@ -0,0 +1,118 @@
+/*
+ * 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.bikeshed.cells.client;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.user.client.ui.PopupPanel;
+import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
+import com.google.gwt.user.datepicker.client.DatePicker;
+
+import java.util.Date;
+
+/**
+ * A {@link Cell} used to render and edit {@link Date}s. When a cell is selected
+ * by clicking on it, a {@link DatePicker} is popped up. When a date is selected
+ * using the DatePicker, the new date is passed to the
+ * {@link ValueUpdater#update update} method of the {@link ValueUpdater} that
+ * was passed to {@link #onBrowserEvent} for the click event. Note that this
+ * means that the call to ValueUpdater.update will occur after onBrowserEvent
+ * has returned. Pressing the 'escape' key dismisses the DatePicker popup
+ * without calling ValueUpdater.update.
+ *
+ * <p>
+ * Each DatePickerCell has a unique DatePicker popup associated with it; thus,
+ * if a single DatePickerCell is used as the cell for a column in a table, only
+ * one entry in that column will be editable at a given time.
+ *
+ * @param <V> the view data type
+ */
+public class DatePickerCell<V> extends Cell<Date, V> {
+
+ private static final int ESCAPE = 27;
+
+ private final DatePicker datePicker;
+ private final DateTimeFormat format;
+ private int offsetX = 10;
+ private int offsetY = 10;
+ private PopupPanel panel;
+ private ValueUpdater<Date, V> valueUpdater;
+ private V viewData;
+
+ /**
+ * Constructs a new DatePickerCell that uses the date/time format
+ * given by {@link DateTimeFormat#getFullDateFormat}.
+ */
+ public DatePickerCell() {
+ this(DateTimeFormat.getFullDateFormat());
+ }
+
+ /**
+ * Constructs a new DatePickerCell that uses the given date/time format.
+ */
+ public DatePickerCell(DateTimeFormat format) {
+ this.format = format;
+
+ this.datePicker = new DatePicker();
+ this.panel = new PopupPanel(true, true) {
+ // Dismiss when escape is pressed
+ @Override
+ public boolean onKeyUpPreview(char key, int modifiers) {
+ if (key == ESCAPE) {
+ panel.hide();
+ }
+ return true;
+ }
+ };
+ panel.add(datePicker);
+
+ // Hide the panel and call valueUpdater.update when a date is selected
+ datePicker.addValueChangeHandler(new ValueChangeHandler<Date>() {
+ public void onValueChange(ValueChangeEvent<Date> event) {
+ panel.hide();
+ valueUpdater.update(event.getValue(), viewData);
+ }
+ });
+ }
+
+ @Override
+ public V onBrowserEvent(final Element parent, Date value, V viewData,
+ NativeEvent event, ValueUpdater<Date, V> valueUpdater) {
+ if (event.getType().equals("click")) {
+ this.viewData = viewData;
+ this.valueUpdater = valueUpdater;
+
+ datePicker.setValue(value);
+ panel.setPopupPositionAndShow(new PositionCallback() {
+ public void setPosition(int offsetWidth, int offsetHeight) {
+ panel.setPopupPosition(parent.getAbsoluteLeft() + offsetX,
+ parent.getAbsoluteTop() + offsetY);
+ }
+ });
+ }
+ return viewData;
+ }
+
+ @Override
+ public void render(Date value, V viewData, StringBuilder sb) {
+ if (value != null) {
+ sb.append(format.format(value));
+ }
+ }
+}
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/MailRecipe.java b/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/MailRecipe.java
index 4456372..a099a0a 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/MailRecipe.java
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/MailRecipe.java
@@ -17,7 +17,9 @@
import com.google.gwt.bikeshed.cells.client.ButtonCell;
import com.google.gwt.bikeshed.cells.client.CheckboxCell;
+import com.google.gwt.bikeshed.cells.client.DatePickerCell;
import com.google.gwt.bikeshed.cells.client.FieldUpdater;
+import com.google.gwt.bikeshed.list.client.Column;
import com.google.gwt.bikeshed.list.client.PagingTableListView;
import com.google.gwt.bikeshed.list.client.SimpleColumn;
import com.google.gwt.bikeshed.list.client.TextColumn;
@@ -28,6 +30,7 @@
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
+import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
@@ -36,6 +39,7 @@
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -165,12 +169,14 @@
boolean isRead;
String sender;
String subject;
+ Date date;
- public Message(int id, String sender, String subject) {
+ public Message(int id, String sender, String subject, Date date) {
super();
this.id = id;
this.sender = sender;
this.subject = subject;
+ this.date = date;
}
@Override
@@ -181,6 +187,10 @@
return id == ((Message) obj).id;
}
+ public Date getDate() {
+ return date;
+ }
+
public int getId() {
return id;
}
@@ -205,7 +215,7 @@
@Override
public String toString() {
return "Message [id=" + id + ", sender=" + sender + ", subject="
- + subject + ", read=" + isRead + "]";
+ + subject + ", read=" + isRead + ", date=" + date + "]";
}
}
@@ -255,11 +265,15 @@
protected Widget createWidget() {
ListViewAdapter<Message> adapter = new ListViewAdapter<Message>();
List<Message> messages = adapter.getList();
+ Date now = new Date();
Random rand = new Random();
for (int i = 0; i < 1000; i++) {
+ // Go back up to 90 days from the current date
+ long dateOffset = rand.nextInt(60 * 60 * 24 * 90) * 1000L;
Message message = new Message(10000 + i,
senders[rand.nextInt(senders.length)],
- subjects[rand.nextInt(subjects.length)]);
+ subjects[rand.nextInt(subjects.length)],
+ new Date(now.getTime() - dateOffset));
message.isRead = rand.nextBoolean();
messages.add(message);
}
@@ -305,6 +319,22 @@
};
table.addColumn(isReadColumn, "Read");
+ Column<Message, Date, Void> dateColumn =
+ new Column<Message, Date, Void>(new DatePickerCell<Void>()) {
+ @Override
+ public Date getValue(Message object) {
+ return object.getDate();
+ }
+ };
+ dateColumn.setFieldUpdater(new FieldUpdater<Message, Date, Void>() {
+ public void update(int index, Message object, Date value, Void viewData) {
+ Window.alert("Changed date from " + object.date + " to " + value);
+ object.date = value;
+ table.refresh();
+ }
+ });
+ table.addColumn(dateColumn, "Date");
+
TextColumn<Message> senderColumn = new TextColumn<Message>() {
@Override
public String getValue(Message object) {
diff --git a/bikeshed/war/Cookbook.css b/bikeshed/war/Cookbook.css
index 7856cdc..e63c422 100644
--- a/bikeshed/war/Cookbook.css
+++ b/bikeshed/war/Cookbook.css
@@ -107,3 +107,81 @@
.gwt-SplitLayoutPanel-VDragger {
background: transparent url(vsplitter-grip.png) center center no-repeat;
}
+
+/* Date picker */
+
+.gwt-DatePicker {
+ border: 1px solid #A2BBDD;
+ cursor: default;
+}
+.gwt-DatePicker td,
+.datePickerMonthSelector td:focus {
+ outline: none
+}
+.datePickerDays {
+ width: 100%;
+ background: white;
+}
+.datePickerDay,
+.datePickerWeekdayLabel,
+.datePickerWeekendLabel {
+ font-size: 75%;
+ text-align: center;
+ padding: 4px;
+ outline: none;
+}
+.datePickerWeekdayLabel,
+.datePickerWeekendLabel {
+ background: #C3D9FF;
+ padding: 0px 4px 2px;
+ cursor: default;
+}
+.datePickerDay {
+ padding: 4px;
+ cursor: hand;
+ cursor: pointer;
+}
+.datePickerDayIsToday {
+ border: 1px solid black;
+ padding: 3px;
+}
+.datePickerDayIsWeekend {
+ background: #EEEEEE;
+}
+.datePickerDayIsFiller {
+ color: #888888;
+}
+.datePickerDayIsValue {
+ background: #aaccee;
+}
+.datePickerDayIsDisabled {
+ color: #AAAAAA;
+ font-style: italic;
+}
+.datePickerDayIsHighlighted {
+ background: #F0E68C;
+}
+.datePickerDayIsValueAndHighlighted {
+ background: #bbddd9;
+}
+.datePickerMonthSelector {
+ background: #C3D9FF;
+ width: 100%;
+}
+td.datePickerMonth {
+ text-align: center;
+ vertical-align: center;
+ white-space: nowrap;
+ font-size: 70%;
+ font-weight: bold;
+ color: blue;
+}
+.datePickerPreviousButton,
+.datePickerNextButton {
+ font-size: 120%;
+ line-height: 1em;
+ color: blue;
+ cursor: hand;
+ cursor: pointer;
+ padding: 0px 4px;
+}