kent yeh - 2009-07-17

I think DateFieldWidget is a good user experience for input date value,but it will more better if it can show a DatePicker like DateBox, So I stole some DateBox's code to make up a DateFieldPickerWidget, here is the class.

import java.util.Date;
import org.gwtiger.client.widget.field.DateFieldWidget;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
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.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
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.event.shared.HasHandlers;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.datepicker.client.CalendarUtil;
import com.google.gwt.user.datepicker.client.DatePicker;
public class DateFieldPickerWidget extends DateFieldWidget implements HasValue<Date> {
    private final PopupPanel popup = new PopupPanel();
    private final DatePicker picker = new DatePicker();
    private boolean allowDPShow = true;
    public DateFieldPickerWidget(String labelText) {
        super(labelText);
        popup.setAutoHideEnabled(true);
        popup.addAutoHidePartner(textBox.getElement());
        popup.setWidget(picker);
        popup.setStyleName("dateBoxPopup");
        DateBoxHandler handler = new DateBoxHandler();
        picker.addValueChangeHandler(handler);
        textBox.addFocusHandler(handler);
        textBox.addBlurHandler(handler);
        textBox.addClickHandler(handler);
        textBox.addKeyDownHandler(handler);
        popup.addCloseHandler(handler);
    }
    public DatePicker getDatePicker() {
        return picker;
    }
    public void hideDatePicker() {
        popup.hide();
    }
    public boolean isDatePickerShowing() {
        return popup.isShowing();
    }
    public void showDatePicker() {
        Date current = validate()?getDate():null;
        if (current == null) {
            current = new Date();
        }
        picker.setCurrentMonth(current);
        popup.showRelativeTo(this);
    }
    private void preventDatePickerPopup() {
        allowDPShow = false;
        DeferredCommand.addCommand(new Command() {
            public void execute() {
                allowDPShow = true;
            }
        });
    }
    private void setValue(Date oldDate, Date date, boolean fireEvents) {
        if (date != null) {
            picker.setCurrentMonth(date);
        }
        picker.setValue(date, false);
        super.setDate(date);
        if (fireEvents) {
            DateChangeEvent.fireIfNotEqualDates(this, oldDate, date);
        }
    }
    private void updateDateFromTextBox() {
        validate();
    }
    @Override
    public HandlerRegistration addValueChangeHandler(
            ValueChangeHandler<Date> handler) {
        return addHandler(handler, ValueChangeEvent.getType());
    }
    @Override
    public Date getValue() {
        return super.getDate();
    }
    @Override
    public void setValue(Date date, boolean fireEvents) {
        setValue(picker.getValue(), date, fireEvents);
    }
    @Override
    public void setValue(Date date) {
        setValue(date, false);
    }
    public static class DateChangeEvent extends ValueChangeEvent<Date> {
        protected DateChangeEvent(Date value) {
            super(CalendarUtil.copyDate(value));
        }
        public static <S extends HasValueChangeHandlers<Date> & HasHandlers> void fireIfNotEqualDates(
                S source, Date oldValue, Date newValue) {
            if (ValueChangeEvent.shouldFire(source, oldValue, newValue)) {
                source.fireEvent(new DateChangeEvent(newValue));
            }
        }
        @Override
        public Date getValue() {
            return CalendarUtil.copyDate(super.getValue());
        }
    }
    private class DateBoxHandler implements ValueChangeHandler<Date>,FocusHandler, BlurHandler,
    ClickHandler, KeyDownHandler,CloseHandler<PopupPanel> {

        @Override
        public void onValueChange(ValueChangeEvent<Date> event) {
            setValue(validate()?getDate():null, event.getValue(),true);
            hideDatePicker();
            preventDatePickerPopup();
            textBox.setFocus(true);
        }
        @Override
        public void onClick(ClickEvent event) {
            showDatePicker();
        }
        @Override
        public void onKeyDown(KeyDownEvent event) {
            switch (event.getNativeKeyCode()) {
            case KeyCodes.KEY_ENTER:
            case KeyCodes.KEY_TAB:
                updateDateFromTextBox();
                // Deliberate fall through
            case KeyCodes.KEY_ESCAPE:
            case KeyCodes.KEY_UP:
                hideDatePicker();
                break;
            case KeyCodes.KEY_DOWN:
                showDatePicker();
                break;
            }
        }
        @Override
        public void onBlur(BlurEvent event) {
            if (isDatePickerShowing() == false) {
                updateDateFromTextBox();
            }
        }
        @Override
        public void onClose(CloseEvent<PopupPanel> event) {
            if (allowDPShow) {
                updateDateFromTextBox();
            }
        }
        @Override
        public void onFocus(FocusEvent event) {
            if (allowDPShow && isDatePickerShowing() == false) {
                showDatePicker();
            }
        }
    }
}