summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorDenis <denis@vaadin.com>2017-01-10 12:22:34 +0200
committerGitHub <noreply@github.com>2017-01-10 12:22:34 +0200
commit3ef30789d6ac773eed7346dcaa670426fa6f662c (patch)
tree2e5aee20b96e3a66088aa6cde13796256ec2bcf1 /client
parentb4af93bebf1b7e51d33330c42e3c89d5e3e4fd45 (diff)
downloadvaadin-framework-3ef30789d6ac773eed7346dcaa670426fa6f662c.tar.gz
vaadin-framework-3ef30789d6ac773eed7346dcaa670426fa6f662c.zip
Refactor AbstractDateField. (#8146)
First round for #8132.
Diffstat (limited to 'client')
-rw-r--r--client/src/main/java/com/vaadin/client/DateTimeService.java10
-rw-r--r--client/src/main/java/com/vaadin/client/ui/AbstractComponentConnector.java2
-rw-r--r--client/src/main/java/com/vaadin/client/ui/VAbstractCalendarPanel.java (renamed from client/src/main/java/com/vaadin/client/ui/VCalendarPanel.java)248
-rw-r--r--client/src/main/java/com/vaadin/client/ui/VAbstractDateFieldCalendar.java69
-rw-r--r--client/src/main/java/com/vaadin/client/ui/VAbstractPopupCalendar.java730
-rw-r--r--client/src/main/java/com/vaadin/client/ui/VAbstractTextualDate.java (renamed from client/src/main/java/com/vaadin/client/ui/VTextualDate.java)142
-rw-r--r--client/src/main/java/com/vaadin/client/ui/VDateCalendarPanel.java46
-rw-r--r--client/src/main/java/com/vaadin/client/ui/VDateField.java133
-rw-r--r--client/src/main/java/com/vaadin/client/ui/VDateFieldCalendar.java93
-rw-r--r--client/src/main/java/com/vaadin/client/ui/VPopupCalendar.java714
-rw-r--r--client/src/main/java/com/vaadin/client/ui/datefield/AbstractDateFieldConnector.java76
-rw-r--r--client/src/main/java/com/vaadin/client/ui/datefield/AbstractInlineDateFieldConnector.java127
-rw-r--r--client/src/main/java/com/vaadin/client/ui/datefield/AbstractTextualDateConnector.java65
-rw-r--r--client/src/main/java/com/vaadin/client/ui/datefield/DateFieldConnector.java170
-rw-r--r--client/src/main/java/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java81
-rw-r--r--client/src/main/java/com/vaadin/client/ui/datefield/TextualDateConnector.java161
16 files changed, 1707 insertions, 1160 deletions
diff --git a/client/src/main/java/com/vaadin/client/DateTimeService.java b/client/src/main/java/com/vaadin/client/DateTimeService.java
index bfb8533f39..ec7c18e94d 100644
--- a/client/src/main/java/com/vaadin/client/DateTimeService.java
+++ b/client/src/main/java/com/vaadin/client/DateTimeService.java
@@ -22,7 +22,7 @@ import java.util.logging.Logger;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.i18n.shared.DateTimeFormat;
-import com.vaadin.shared.ui.datefield.Resolution;
+import com.vaadin.shared.ui.datefield.DateResolution;
/**
* This class provides date/time parsing services to all components on the
@@ -203,7 +203,7 @@ public class DateTimeService {
}
public static boolean isInRange(Date date, Date rangeStart, Date rangeEnd,
- Resolution resolution) {
+ DateResolution resolution) {
Date s;
Date e;
if (rangeStart.after(rangeEnd)) {
@@ -217,19 +217,19 @@ public class DateTimeService {
long end = e.getYear() * 10000000000l;
long target = date.getYear() * 10000000000l;
- if (resolution == Resolution.YEAR) {
+ if (resolution == DateResolution.YEAR) {
return (start <= target && end >= target);
}
start += s.getMonth() * 100000000l;
end += e.getMonth() * 100000000l;
target += date.getMonth() * 100000000l;
- if (resolution == Resolution.MONTH) {
+ if (resolution == DateResolution.MONTH) {
return (start <= target && end >= target);
}
start += s.getDate() * 1000000l;
end += e.getDate() * 1000000l;
target += date.getDate() * 1000000l;
- if (resolution == Resolution.DAY) {
+ if (resolution == DateResolution.DAY) {
return (start <= target && end >= target);
}
start += s.getHours() * 10000l;
diff --git a/client/src/main/java/com/vaadin/client/ui/AbstractComponentConnector.java b/client/src/main/java/com/vaadin/client/ui/AbstractComponentConnector.java
index 858b21d308..d7b6d52f98 100644
--- a/client/src/main/java/com/vaadin/client/ui/AbstractComponentConnector.java
+++ b/client/src/main/java/com/vaadin/client/ui/AbstractComponentConnector.java
@@ -706,7 +706,7 @@ public abstract class AbstractComponentConnector extends AbstractConnector
* updated in another widget in addition to the one returned by the
* <code>Connector</code>'s {@link #getWidget()}, or if the prefix should be
* different. For example see
- * {@link com.vaadin.client.ui.datefield.DateFieldConnector#setWidgetStyleNameWithPrefix(String, String, boolean)}
+ * {@link com.vaadin.client.ui.datefield.TextualDateConnector#setWidgetStyleNameWithPrefix(String, String, boolean)}
* </p>
*
* @param styleName
diff --git a/client/src/main/java/com/vaadin/client/ui/VCalendarPanel.java b/client/src/main/java/com/vaadin/client/ui/VAbstractCalendarPanel.java
index 2a03b91d2b..601348fea4 100644
--- a/client/src/main/java/com/vaadin/client/ui/VCalendarPanel.java
+++ b/client/src/main/java/com/vaadin/client/ui/VAbstractCalendarPanel.java
@@ -18,6 +18,10 @@ package com.vaadin.client.ui;
import java.util.Date;
import java.util.Iterator;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.aria.client.SelectedValue;
@@ -51,13 +55,22 @@ import com.vaadin.client.BrowserInfo;
import com.vaadin.client.DateTimeService;
import com.vaadin.client.VConsole;
import com.vaadin.client.WidgetUtil;
-import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.util.SharedUtil;
+/**
+ * Abstract calendar panel to show and select a date using a resolution. The
+ * class is parameterized by the date resolution enumeration type.
+ *
+ * @author Vaadin Ltd
+ *
+ * @param <R>
+ * the resolution type which this field is based on (day, month, ...)
+ */
@SuppressWarnings("deprecation")
-public class VCalendarPanel extends FocusableFlexTable implements
- KeyDownHandler, KeyPressHandler, MouseOutHandler, MouseDownHandler,
- MouseUpHandler, BlurHandler, FocusHandler, SubPartAware {
+public abstract class VAbstractCalendarPanel<R extends Enum<R>>
+ extends FocusableFlexTable implements KeyDownHandler, KeyPressHandler,
+ MouseOutHandler, MouseDownHandler, MouseUpHandler, BlurHandler,
+ FocusHandler, SubPartAware {
public interface SubmitListener {
@@ -96,9 +109,9 @@ public class VCalendarPanel extends FocusableFlexTable implements
*/
private class VEventButton extends Button {
public VEventButton() {
- addMouseDownHandler(VCalendarPanel.this);
- addMouseOutHandler(VCalendarPanel.this);
- addMouseUpHandler(VCalendarPanel.this);
+ addMouseDownHandler(VAbstractCalendarPanel.this);
+ addMouseOutHandler(VAbstractCalendarPanel.this);
+ addMouseUpHandler(VAbstractCalendarPanel.this);
}
}
@@ -131,7 +144,8 @@ public class VCalendarPanel extends FocusableFlexTable implements
}
Date newDate = ((Day) event.getSource()).getDate();
- if (!isDateInsideRange(newDate, Resolution.DAY)) {
+ if (!isDateInsideRange(newDate,
+ getResolution(VAbstractCalendarPanel.this::isDay))) {
return;
}
if (newDate.getMonth() != displayedMonth.getMonth()
@@ -158,7 +172,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
private FlexTable days = new FlexTable();
- private Resolution resolution = Resolution.YEAR;
+ private R resolution;
private Timer mouseTimer;
@@ -184,11 +198,11 @@ public class VCalendarPanel extends FocusableFlexTable implements
private boolean hasFocus = false;
- private VDateField parent;
+ private VDateField<R> parent;
private boolean initialRenderDone = false;
- public VCalendarPanel() {
+ public VAbstractCalendarPanel() {
getElement().setId(DOM.createUniqueId());
setStyleName(VDateField.CLASSNAME + "-calendarpanel");
Roles.getGridRole().set(getElement());
@@ -207,7 +221,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
addBlurHandler(this);
}
- public void setParentField(VDateField parent) {
+ public void setParentField(VDateField<R> parent) {
this.parent = parent;
}
@@ -221,7 +235,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
*/
private void focusDay(Date date) {
// Only used when calender body is present
- if (isDay(getResolution())) {
+ if (acceptDayFocus()) {
if (focusedDay != null) {
focusedDay.removeStyleDependentName(CN_FOCUSED);
}
@@ -233,7 +247,8 @@ public class VCalendarPanel extends FocusableFlexTable implements
int cellCount = days.getCellCount(i);
for (int j = 0; j < cellCount; j++) {
Widget widget = days.getWidget(i, j);
- if (widget != null && widget instanceof Day) {
+ if (widget != null
+ && widget instanceof VAbstractCalendarPanel.Day) {
Day curday = (Day) widget;
if (curday.getDate().equals(date)) {
curday.addStyleDependentName(CN_FOCUSED);
@@ -247,8 +262,80 @@ public class VCalendarPanel extends FocusableFlexTable implements
}
}
- private boolean isDay(Resolution resolution) {
- return Resolution.DAY.equals(resolution);
+ /**
+ * Returns {@code true} if current resolution assumes handling focus event
+ * for day UI component.
+ *
+ * @return {@code true} if day focus events should be handled, {@code false}
+ * otherwise
+ */
+ protected abstract boolean acceptDayFocus();
+
+ /**
+ * Returns {@code true} if the provided {@code resolution} represents a day.
+ *
+ * @param resolution
+ * the given resolution
+ * @return {@code true} if the {@code resolution} represents a day
+ */
+ protected abstract boolean isDay(R resolution);
+
+ /**
+ * Returns {@code true} if the provided {@code resolution} represents a
+ * month.
+ *
+ * @param resolution
+ * the given resolution
+ * @return {@code true} if the {@code resolution} represents a month
+ */
+ protected abstract boolean isMonth(R resolution);
+
+ /**
+ * Returns {@code true} if the provided {@code resolution} represents an
+ * year.
+ *
+ * @param resolution
+ * the given resolution
+ * @return {@code true} if the {@code resolution} represents a year
+ */
+ protected boolean isYear(R resolution) {
+ return parent.isYear(resolution);
+ }
+
+ /**
+ * Returns {@code true} if the {@code resolution} representation is strictly
+ * below month (day, hour, etc..).
+ *
+ * @param resolution
+ * the given resolution
+ * @return whether the {@code resolution} is below the month resolution
+ */
+ protected abstract boolean isBelowMonth(R resolution);
+
+ /**
+ * Returns all available resolutions for the widget.
+ *
+ * @return all available resolutions
+ */
+ protected Stream<R> getResolutions() {
+ return parent.getResolutions();
+ }
+
+ /**
+ * Finds the resolution by the {@code filter}.
+ *
+ * @param filter
+ * predicate to filter resolutions
+ * @return the resolution accepted by the {@code filter}
+ */
+ protected R getResolution(Predicate<R> filter) {
+ List<R> resolutions = getResolutions().filter(filter)
+ .collect(Collectors.toList());
+ assert resolutions
+ .size() == 1 : "The result of filtering by the predicate "
+ + "contains unexpected number of resolution items :"
+ + resolutions.size();
+ return resolutions.get(0);
}
/**
@@ -271,7 +358,8 @@ public class VCalendarPanel extends FocusableFlexTable implements
int cellCount = days.getCellCount(i);
for (int j = 0; j < cellCount; j++) {
Widget widget = days.getWidget(i, j);
- if (widget != null && widget instanceof Day) {
+ if (widget != null
+ && widget instanceof VAbstractCalendarPanel.Day) {
Day curday = (Day) widget;
if (curday.getDate().equals(date)) {
curday.addStyleDependentName(CN_SELECTED);
@@ -289,7 +377,8 @@ public class VCalendarPanel extends FocusableFlexTable implements
* Updates year, month, day from focusedDate to value
*/
private void selectFocused() {
- if (focusedDate != null && isDateInsideRange(focusedDate, resolution)) {
+ if (focusedDate != null
+ && isDateInsideRange(focusedDate, getResolution())) {
if (value == null) {
// No previously selected value (set to null on server side).
// Create a new date using current date and time
@@ -324,11 +413,11 @@ public class VCalendarPanel extends FocusableFlexTable implements
return false;
}
- public Resolution getResolution() {
+ public R getResolution() {
return resolution;
}
- public void setResolution(Resolution resolution) {
+ public void setResolution(R resolution) {
this.resolution = resolution;
}
@@ -460,14 +549,15 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date prevMonthDate = (Date) focusedDate.clone();
removeOneMonth(prevMonthDate);
- if (!isDateInsideRange(prevMonthDate, Resolution.MONTH)) {
+ R month = getResolution(VAbstractCalendarPanel.this::isMonth);
+ if (!isDateInsideRange(prevMonthDate, month)) {
prevMonth.addStyleName(CN_OUTSIDE_RANGE);
} else {
prevMonth.removeStyleName(CN_OUTSIDE_RANGE);
}
Date nextMonthDate = (Date) focusedDate.clone();
addOneMonth(nextMonthDate);
- if (!isDateInsideRange(nextMonthDate, Resolution.MONTH)) {
+ if (!isDateInsideRange(nextMonthDate, month)) {
nextMonth.addStyleName(CN_OUTSIDE_RANGE);
} else {
nextMonth.removeStyleName(CN_OUTSIDE_RANGE);
@@ -476,7 +566,8 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date prevYearDate = (Date) focusedDate.clone();
prevYearDate.setYear(prevYearDate.getYear() - 1);
- if (!isDateInsideRange(prevYearDate, Resolution.YEAR)) {
+ R year = getResolution(VAbstractCalendarPanel.this::isYear);
+ if (!isDateInsideRange(prevYearDate, year)) {
prevYear.addStyleName(CN_OUTSIDE_RANGE);
} else {
prevYear.removeStyleName(CN_OUTSIDE_RANGE);
@@ -484,7 +575,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date nextYearDate = (Date) focusedDate.clone();
nextYearDate.setYear(nextYearDate.getYear() + 1);
- if (!isDateInsideRange(nextYearDate, Resolution.YEAR)) {
+ if (!isDateInsideRange(nextYearDate, year)) {
nextYear.addStyleName(CN_OUTSIDE_RANGE);
} else {
nextYear.removeStyleName(CN_OUTSIDE_RANGE);
@@ -521,7 +612,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
* @param date
* @return
*/
- private boolean isDateInsideRange(Date date, Resolution minResolution) {
+ private boolean isDateInsideRange(Date date, R minResolution) {
assert (date != null);
return isAcceptedByRangeEnd(date, minResolution)
@@ -539,8 +630,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
* @param minResolution
* @return
*/
- private boolean isAcceptedByRangeStart(Date date,
- Resolution minResolution) {
+ private boolean isAcceptedByRangeStart(Date date, R minResolution) {
assert (date != null);
// rangeStart == null means that we accept all values below rangeEnd
@@ -551,10 +641,10 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date valueDuplicate = (Date) date.clone();
Date rangeStartDuplicate = (Date) rangeStart.clone();
- if (minResolution == Resolution.YEAR) {
+ if (isYear(minResolution)) {
return valueDuplicate.getYear() >= rangeStartDuplicate.getYear();
}
- if (minResolution == Resolution.MONTH) {
+ if (isMonth(minResolution)) {
valueDuplicate = clearDateBelowMonth(valueDuplicate);
rangeStartDuplicate = clearDateBelowMonth(rangeStartDuplicate);
} else {
@@ -576,7 +666,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
* @param minResolution
* @return
*/
- private boolean isAcceptedByRangeEnd(Date date, Resolution minResolution) {
+ private boolean isAcceptedByRangeEnd(Date date, R minResolution) {
assert (date != null);
// rangeEnd == null means that we accept all values above rangeStart
@@ -587,10 +677,10 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date valueDuplicate = (Date) date.clone();
Date rangeEndDuplicate = (Date) rangeEnd.clone();
- if (minResolution == Resolution.YEAR) {
+ if (isYear(minResolution)) {
return valueDuplicate.getYear() <= rangeEndDuplicate.getYear();
}
- if (minResolution == Resolution.MONTH) {
+ if (isMonth(minResolution)) {
valueDuplicate = clearDateBelowMonth(valueDuplicate);
rangeEndDuplicate = clearDateBelowMonth(rangeEndDuplicate);
} else {
@@ -667,7 +757,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
if (day > 6) {
day = 0;
}
- if (isDay(getResolution())) {
+ if (isBelowMonth(getResolution())) {
days.setHTML(headerRow, firstWeekdayColumn + i, "<strong>"
+ getDateTimeService().getShortDay(day) + "</strong>");
} else {
@@ -705,7 +795,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
day.setStyleName(
parent.getStylePrimaryName() + "-calendarpanel-day");
- if (!isDateInsideRange(dayDate, Resolution.DAY)) {
+ if (!isDateInsideRange(dayDate, getResolution(this::isDay))) {
day.addStyleDependentName(CN_OUTSIDE_RANGE);
}
@@ -771,7 +861,24 @@ public class VCalendarPanel extends FocusableFlexTable implements
* selected.
*/
public void renderCalendar(boolean updateDate) {
+ doRenderCalendar(updateDate);
+
+ initialRenderDone = true;
+ }
+ /**
+ * Performs the rendering required by the {@link #renderCalendar(boolean)}.
+ * Subclasses may override this method to provide a custom implementation
+ * avoiding {@link #renderCalendar(boolean)} override. The latter method
+ * contains a common logic which should not be overriden.
+ *
+ * @param updateDate
+ * The value false prevents setting the selected date of the
+ * calendar based on focusedDate. That can be used when only the
+ * resolution of the calendar is changed and no date has been
+ * selected.
+ */
+ protected void doRenderCalendar(boolean updateDate) {
super.setStylePrimaryName(
parent.getStylePrimaryName() + "-calendarpanel");
@@ -788,15 +895,13 @@ public class VCalendarPanel extends FocusableFlexTable implements
focusChangeListener.focusChanged(new Date(focusedDate.getTime()));
}
- final boolean needsMonth = !getResolution().equals(Resolution.YEAR);
+ final boolean needsMonth = !isYear(getResolution());
boolean needsBody = isDay(getResolution());
buildCalendarHeader(needsMonth);
clearCalendarBody(!needsBody);
if (needsBody) {
buildCalendarBody();
}
-
- initialRenderDone = true;
}
/**
@@ -809,7 +914,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date focusCopy = ((Date) focusedDate.clone());
focusCopy.setDate(focusedDate.getDate() + days);
- if (!isDateInsideRange(focusCopy, resolution)) {
+ if (!isDateInsideRange(focusCopy, getResolution())) {
// If not inside allowed range, then do not move anything
return;
}
@@ -850,14 +955,16 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date requestedNextMonthDate = (Date) focusedDate.clone();
addOneMonth(requestedNextMonthDate);
- if (!isDateInsideRange(requestedNextMonthDate, Resolution.MONTH)) {
+ if (!isDateInsideRange(requestedNextMonthDate,
+ getResolution(this::isMonth))) {
return;
}
// Now also checking whether the day is inside the range or not. If not
// inside,
// correct it
- if (!isDateInsideRange(requestedNextMonthDate, Resolution.DAY)) {
+ if (!isDateInsideRange(requestedNextMonthDate,
+ getResolution(this::isDay))) {
requestedNextMonthDate = adjustDateToFitInsideRange(
requestedNextMonthDate);
}
@@ -910,11 +1017,13 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date requestedPreviousMonthDate = (Date) focusedDate.clone();
removeOneMonth(requestedPreviousMonthDate);
- if (!isDateInsideRange(requestedPreviousMonthDate, Resolution.MONTH)) {
+ if (!isDateInsideRange(requestedPreviousMonthDate,
+ getResolution(this::isMonth))) {
return;
}
- if (!isDateInsideRange(requestedPreviousMonthDate, Resolution.DAY)) {
+ if (!isDateInsideRange(requestedPreviousMonthDate,
+ getResolution(this::isDay))) {
requestedPreviousMonthDate = adjustDateToFitInsideRange(
requestedPreviousMonthDate);
}
@@ -935,12 +1044,12 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date previousYearDate = (Date) focusedDate.clone();
previousYearDate.setYear(previousYearDate.getYear() - years);
// Do not focus if not inside range
- if (!isDateInsideRange(previousYearDate, Resolution.YEAR)) {
+ if (!isDateInsideRange(previousYearDate, getResolution(this::isYear))) {
return;
}
// If we remove one year, but have to roll back a bit, fit it
// into the calendar. Also the months have to be changed
- if (!isDateInsideRange(previousYearDate, Resolution.DAY)) {
+ if (!isDateInsideRange(previousYearDate, getResolution(this::isDay))) {
previousYearDate = adjustDateToFitInsideRange(previousYearDate);
focusedDate.setYear(previousYearDate.getYear());
@@ -977,12 +1086,12 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date nextYearDate = (Date) focusedDate.clone();
nextYearDate.setYear(nextYearDate.getYear() + years);
// Do not focus if not inside range
- if (!isDateInsideRange(nextYearDate, Resolution.YEAR)) {
+ if (!isDateInsideRange(nextYearDate, getResolution(this::isYear))) {
return;
}
// If we add one year, but have to roll back a bit, fit it
// into the calendar. Also the months have to be changed
- if (!isDateInsideRange(nextYearDate, Resolution.DAY)) {
+ if (!isDateInsideRange(nextYearDate, getResolution(this::isDay))) {
nextYearDate = adjustDateToFitInsideRange(nextYearDate);
focusedDate.setYear(nextYearDate.getYear());
@@ -1339,15 +1448,15 @@ public class VCalendarPanel extends FocusableFlexTable implements
return false;
}
- else if (resolution == Resolution.YEAR) {
+ else if (isYear(getResolution())) {
return handleNavigationYearMode(keycode, ctrl, shift);
}
- else if (resolution == Resolution.MONTH) {
+ else if (isMonth(getResolution())) {
return handleNavigationMonthMode(keycode, ctrl, shift);
}
- else if (resolution == Resolution.DAY) {
+ else if (isDay(getResolution())) {
return handleNavigationDayMode(keycode, ctrl, shift);
}
@@ -1461,8 +1570,8 @@ public class VCalendarPanel extends FocusableFlexTable implements
// Timer is first used for a 500ms delay after mousedown. After that has
// elapsed, another timer is triggered to go off every 150ms. Both
// timers are cancelled on mouseup or mouseout.
- if (event.getNativeButton() == NativeEvent.BUTTON_LEFT
- && event.getSource() instanceof VEventButton) {
+ if (event.getNativeButton() == NativeEvent.BUTTON_LEFT && event
+ .getSource() instanceof VAbstractCalendarPanel.VEventButton) {
final VEventButton sender = (VEventButton) event.getSource();
processClickEvent(sender);
mouseTimer = new Timer() {
@@ -1517,7 +1626,27 @@ public class VCalendarPanel extends FocusableFlexTable implements
* The date to set
*/
public void setDate(Date currentDate) {
+ doSetDate(currentDate, false, () -> {
+ });
+ }
+ /**
+ * The actual implementation of the logic which sets the data of the Panel.
+ * The method {@link #setDate(Date)} just delegate a call to this method
+ * providing additional config parameters.
+ *
+ * @param currentDate
+ * currentDate The date to set
+ * @param needRerender
+ * if {@code true} then calendar will be rerendered regardless of
+ * internal logic, otherwise the decision will be made on the
+ * internal state inside the method
+ * @param focusAction
+ * an additional action which will be executed in case
+ * rerendering is not required
+ */
+ protected void doSetDate(Date currentDate, boolean needRerender,
+ Runnable focusAction) {
// Check that we are not re-rendering an already active date
if (currentDate == value && currentDate != null) {
return;
@@ -1525,7 +1654,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
boolean currentDateWasAdjusted = false;
// Check that selected date is inside the allowed range
if (currentDate != null
- && !isDateInsideRange(currentDate, resolution)) {
+ && !isDateInsideRange(currentDate, getResolution())) {
currentDate = adjustDateToFitInsideRange(currentDate);
currentDateWasAdjusted = true;
}
@@ -1566,13 +1695,14 @@ public class VCalendarPanel extends FocusableFlexTable implements
}
// Re-render calendar if the displayed month is changed.
- if (oldDisplayedMonth == null || value == null
+ if (needRerender || oldDisplayedMonth == null || value == null
|| oldDisplayedMonth.getYear() != value.getYear()
|| oldDisplayedMonth.getMonth() != value.getMonth()) {
renderCalendar();
} else {
focusDay(focusedDate);
selectFocused();
+ focusAction.run();
}
if (!hasFocus) {
@@ -1666,7 +1796,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
*/
@Override
public void onBlur(final BlurEvent event) {
- if (event.getSource() instanceof VCalendarPanel) {
+ if (event.getSource() instanceof VAbstractCalendarPanel) {
hasFocus = false;
focusDay(null);
}
@@ -1681,7 +1811,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
*/
@Override
public void onFocus(FocusEvent event) {
- if (event.getSource() instanceof VCalendarPanel) {
+ if (event.getSource() instanceof VAbstractCalendarPanel) {
hasFocus = true;
// Focuses the current day if the calendar shows the days
@@ -1749,7 +1879,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
* @param subElement
* @return true if {@code w} is a parent of subElement, false otherwise.
*/
- private boolean contains(Widget w, Element subElement) {
+ protected boolean contains(Widget w, Element subElement) {
if (w == null || w.getElement() == null) {
return false;
}
@@ -1782,7 +1912,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
Iterator<Widget> iter = days.iterator();
while (iter.hasNext()) {
Widget w = iter.next();
- if (w instanceof Day) {
+ if (w instanceof VAbstractCalendarPanel.Day) {
Day day = (Day) w;
if (day.getDate().equals(date)) {
return day.getElement();
@@ -1846,8 +1976,8 @@ public class VCalendarPanel extends FocusableFlexTable implements
}
private void setLabel() {
- if (parent instanceof VPopupCalendar) {
- ((VPopupCalendar) parent).setFocusedDate(this);
+ if (parent instanceof VAbstractPopupCalendar) {
+ ((VAbstractPopupCalendar) parent).setFocusedDate(this);
}
}
}
diff --git a/client/src/main/java/com/vaadin/client/ui/VAbstractDateFieldCalendar.java b/client/src/main/java/com/vaadin/client/ui/VAbstractDateFieldCalendar.java
new file mode 100644
index 0000000000..4e6aa26020
--- /dev/null
+++ b/client/src/main/java/com/vaadin/client/ui/VAbstractDateFieldCalendar.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.vaadin.client.ui;
+
+import com.google.gwt.event.dom.client.DomEvent;
+import com.vaadin.client.ui.VAbstractCalendarPanel.FocusOutListener;
+import com.vaadin.client.ui.VAbstractCalendarPanel.SubmitListener;
+
+/**
+ * A client side implementation for inline date field.
+ */
+public abstract class VAbstractDateFieldCalendar<R extends Enum<R>>
+ extends VDateField<R> {
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public final VAbstractCalendarPanel<R> calendarPanel;
+
+ public VAbstractDateFieldCalendar(VAbstractCalendarPanel<R> panel,
+ R resolution) {
+ super(resolution);
+ calendarPanel = panel;
+ calendarPanel.setParentField(this);
+ add(calendarPanel);
+ calendarPanel.setSubmitListener(new SubmitListener() {
+ @Override
+ public void onSubmit() {
+ updateValueFromPanel();
+ }
+
+ @Override
+ public void onCancel() {
+ // TODO Auto-generated method stub
+
+ }
+ });
+ calendarPanel.setFocusOutListener(new FocusOutListener() {
+ @Override
+ public boolean onFocusOut(DomEvent<?> event) {
+ updateValueFromPanel();
+ return false;
+ }
+ });
+ }
+
+ @SuppressWarnings("deprecation")
+ public abstract void updateValueFromPanel();
+
+ public void setTabIndex(int tabIndex) {
+ calendarPanel.getElement().setTabIndex(tabIndex);
+ }
+
+ public int getTabIndex() {
+ return calendarPanel.getElement().getTabIndex();
+ }
+}
diff --git a/client/src/main/java/com/vaadin/client/ui/VAbstractPopupCalendar.java b/client/src/main/java/com/vaadin/client/ui/VAbstractPopupCalendar.java
new file mode 100644
index 0000000000..74530835f1
--- /dev/null
+++ b/client/src/main/java/com/vaadin/client/ui/VAbstractPopupCalendar.java
@@ -0,0 +1,730 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.vaadin.client.ui;
+
+import java.util.Date;
+import java.util.Locale;
+
+import com.google.gwt.aria.client.Id;
+import com.google.gwt.aria.client.LiveValue;
+import com.google.gwt.aria.client.Roles;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.DomEvent;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.MouseOutEvent;
+import com.google.gwt.event.dom.client.MouseOutHandler;
+import com.google.gwt.event.dom.client.MouseOverEvent;
+import com.google.gwt.event.dom.client.MouseOverHandler;
+import com.google.gwt.event.logical.shared.CloseEvent;
+import com.google.gwt.event.logical.shared.CloseHandler;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.Timer;
+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.Label;
+import com.google.gwt.user.client.ui.PopupPanel;
+import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.BrowserInfo;
+import com.vaadin.client.ComputedStyle;
+import com.vaadin.client.VConsole;
+import com.vaadin.client.ui.VAbstractCalendarPanel.FocusOutListener;
+import com.vaadin.client.ui.VAbstractCalendarPanel.SubmitListener;
+import com.vaadin.client.ui.aria.AriaHelper;
+import com.vaadin.shared.ui.datefield.TextualDateFieldState;
+
+/**
+ * Represents a date selection component with a text field and a popup date/time
+ * selector.
+ *
+ * <b>Note:</b> To change the keyboard assignments used in the popup dialog you
+ * should extend <code>com.vaadin.client.ui.VAbstractCalendarPanel</code> and
+ * then pass set it by calling the
+ * <code>setCalendarPanel(VAbstractCalendarPanel panel)</code> method.
+ *
+ */
+public abstract class VAbstractPopupCalendar<R extends Enum<R>>
+ extends VAbstractTextualDate<R>
+ implements Field, ClickHandler, CloseHandler<PopupPanel>, SubPartAware {
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public final Button calendarToggle = new Button();
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public VAbstractCalendarPanel<R> calendar;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public final VOverlay popup;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public boolean parsable = true;
+
+ private boolean open = false;
+
+ /*
+ * #14857: If calendarToggle button is clicked when calendar popup is
+ * already open we should prevent calling openCalendarPanel() in onClick,
+ * since we don't want to reopen it again right after it closes.
+ */
+ private boolean preventOpenPopupCalendar = false;
+ private boolean cursorOverCalendarToggleButton = false;
+ private boolean toggleButtonClosesWithGuarantee = false;
+
+ private boolean textFieldEnabled = true;
+
+ private String captionId;
+
+ private Label selectedDate;
+
+ private Element descriptionForAssisitveDevicesElement;
+
+ private final String CALENDAR_TOGGLE_ID = "popupButton";
+
+ public VAbstractPopupCalendar(VAbstractCalendarPanel<R> calendarPanel,
+ R resolution) {
+ super(resolution);
+
+ calendarToggle.setText("");
+ calendarToggle.addClickHandler(this);
+
+ calendarToggle.addDomHandler(new MouseOverHandler() {
+ @Override
+ public void onMouseOver(MouseOverEvent event) {
+ cursorOverCalendarToggleButton = true;
+ }
+ }, MouseOverEvent.getType());
+
+ calendarToggle.addDomHandler(new MouseOutHandler() {
+ @Override
+ public void onMouseOut(MouseOutEvent event) {
+ cursorOverCalendarToggleButton = false;
+ }
+ }, MouseOutEvent.getType());
+
+ // -2 instead of -1 to avoid FocusWidget.onAttach to reset it
+ calendarToggle.getElement().setTabIndex(-2);
+
+ Roles.getButtonRole().set(calendarToggle.getElement());
+ Roles.getButtonRole().setAriaHiddenState(calendarToggle.getElement(),
+ true);
+
+ add(calendarToggle);
+
+ // Description of the usage of the widget for assisitve device users
+ descriptionForAssisitveDevicesElement = DOM.createDiv();
+ descriptionForAssisitveDevicesElement.setInnerText(
+ TextualDateFieldState.DESCRIPTION_FOR_ASSISTIVE_DEVICES);
+ AriaHelper.ensureHasId(descriptionForAssisitveDevicesElement);
+ Roles.getTextboxRole().setAriaDescribedbyProperty(text.getElement(),
+ Id.of(descriptionForAssisitveDevicesElement));
+ AriaHelper.setVisibleForAssistiveDevicesOnly(
+ descriptionForAssisitveDevicesElement, true);
+
+ calendar = calendarPanel;
+ calendar.setParentField(this);
+ calendar.setFocusOutListener(new FocusOutListener() {
+ @Override
+ public boolean onFocusOut(DomEvent<?> event) {
+ event.preventDefault();
+ closeCalendarPanel();
+ return true;
+ }
+ });
+
+ // FIXME: Problem is, that the element with the provided id does not
+ // exist yet in html. This is the same problem as with the context menu.
+ // Apply here the same fix (#11795)
+ Roles.getTextboxRole().setAriaControlsProperty(text.getElement(),
+ Id.of(calendar.getElement()));
+ Roles.getButtonRole().setAriaControlsProperty(
+ calendarToggle.getElement(), Id.of(calendar.getElement()));
+
+ calendar.setSubmitListener(new SubmitListener() {
+ @Override
+ public void onSubmit() {
+ // Update internal value and send valuechange event if immediate
+ updateValue(calendar.getDate());
+
+ // Update text field (a must when not immediate).
+ buildDate(true);
+
+ closeCalendarPanel();
+ }
+
+ @Override
+ public void onCancel() {
+ closeCalendarPanel();
+ }
+ });
+
+ popup = new VOverlay(true, false);
+ popup.setOwner(this);
+
+ FlowPanel wrapper = new FlowPanel();
+ selectedDate = new Label();
+ selectedDate.setStyleName(getStylePrimaryName() + "-selecteddate");
+ AriaHelper.setVisibleForAssistiveDevicesOnly(selectedDate.getElement(),
+ true);
+
+ Roles.getTextboxRole().setAriaLiveProperty(selectedDate.getElement(),
+ LiveValue.ASSERTIVE);
+ Roles.getTextboxRole().setAriaAtomicProperty(selectedDate.getElement(),
+ true);
+ wrapper.add(selectedDate);
+ wrapper.add(calendar);
+
+ popup.setWidget(wrapper);
+ popup.addCloseHandler(this);
+
+ DOM.setElementProperty(calendar.getElement(), "id",
+ "PID_VAADIN_POPUPCAL");
+
+ sinkEvents(Event.ONKEYDOWN);
+
+ updateStyleNames();
+ }
+
+ @Override
+ protected void onAttach() {
+ super.onAttach();
+ DOM.appendChild(RootPanel.get().getElement(),
+ descriptionForAssisitveDevicesElement);
+ }
+
+ @Override
+ protected void onDetach() {
+ super.onDetach();
+ descriptionForAssisitveDevicesElement.removeFromParent();
+ closeCalendarPanel();
+ }
+
+ @SuppressWarnings("deprecation")
+ public void updateValue(Date newDate) {
+ Date currentDate = getCurrentDate();
+ if (currentDate == null || newDate.getTime() != currentDate.getTime()) {
+ setCurrentDate((Date) newDate.clone());
+ getClient().updateVariable(getId(),
+ getResolutionVariable(
+ calendar.getResolution(calendar::isYear)),
+ newDate.getYear() + 1900, false);
+ if (!calendar.isYear(getCurrentResolution())) {
+ getClient().updateVariable(getId(),
+ getResolutionVariable(
+ calendar.getResolution(calendar::isMonth))
+ .toLowerCase(Locale.ENGLISH),
+ newDate.getMonth() + 1, false);
+ if (!calendar.isMonth(getCurrentResolution())) {
+ getClient().updateVariable(getId(),
+ getResolutionVariable(
+ calendar.getResolution(calendar::isDay)),
+ newDate.getDate(), false);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks whether the text field is enabled.
+ *
+ * @see VAbstractPopupCalendar#setTextFieldEnabled(boolean)
+ * @return The current state of the text field.
+ */
+ public boolean isTextFieldEnabled() {
+ return textFieldEnabled;
+ }
+
+ /**
+ * Sets the state of the text field of this component. By default the text
+ * field is enabled. Disabling it causes only the button for date selection
+ * to be active, thus preventing the user from entering invalid dates. See
+ * {@link http://dev.vaadin.com/ticket/6790}.
+ *
+ * @param state
+ */
+ public void setTextFieldEnabled(boolean textFieldEnabled) {
+ this.textFieldEnabled = textFieldEnabled;
+ updateTextFieldEnabled();
+ }
+
+ protected void updateTextFieldEnabled() {
+ boolean reallyEnabled = isEnabled() && isTextFieldEnabled();
+ // IE has a non input disabled themeing that can not be overridden so we
+ // must fake the functionality using readonly and unselectable
+ if (BrowserInfo.get().isIE()) {
+ if (!reallyEnabled) {
+ text.getElement().setAttribute("unselectable", "on");
+ text.getElement().setAttribute("readonly", "");
+ text.setTabIndex(-2);
+ } else if (reallyEnabled
+ && text.getElement().hasAttribute("unselectable")) {
+ text.getElement().removeAttribute("unselectable");
+ text.getElement().removeAttribute("readonly");
+ text.setTabIndex(0);
+ }
+ } else {
+ text.setEnabled(reallyEnabled);
+ }
+
+ if (reallyEnabled) {
+ calendarToggle.setTabIndex(-1);
+ Roles.getButtonRole()
+ .setAriaHiddenState(calendarToggle.getElement(), true);
+ } else {
+ calendarToggle.setTabIndex(0);
+ Roles.getButtonRole()
+ .setAriaHiddenState(calendarToggle.getElement(), false);
+ }
+
+ handleAriaAttributes();
+ }
+
+ /**
+ * Set correct tab index for disabled text field in IE as the value set in
+ * setTextFieldEnabled(...) gets overridden in
+ * TextualDateConnection.updateFromUIDL(...)
+ *
+ * @since 7.3.1
+ */
+ public void setTextFieldTabIndex() {
+ if (BrowserInfo.get().isIE() && !textFieldEnabled) {
+ // index needs to be -2 because FocusWidget updates -1 to 0 onAttach
+ text.setTabIndex(-2);
+ }
+ }
+
+ @Override
+ public void bindAriaCaption(
+ com.google.gwt.user.client.Element captionElement) {
+ if (captionElement == null) {
+ captionId = null;
+ } else {
+ captionId = captionElement.getId();
+ }
+
+ if (isTextFieldEnabled()) {
+ super.bindAriaCaption(captionElement);
+ } else {
+ AriaHelper.bindCaption(calendarToggle, captionElement);
+ }
+
+ handleAriaAttributes();
+ }
+
+ private void handleAriaAttributes() {
+ Widget removeFromWidget;
+ Widget setForWidget;
+
+ if (isTextFieldEnabled()) {
+ setForWidget = text;
+ removeFromWidget = calendarToggle;
+ } else {
+ setForWidget = calendarToggle;
+ removeFromWidget = text;
+ }
+
+ Roles.getFormRole()
+ .removeAriaLabelledbyProperty(removeFromWidget.getElement());
+ if (captionId == null) {
+ Roles.getFormRole()
+ .removeAriaLabelledbyProperty(setForWidget.getElement());
+ } else {
+ Roles.getFormRole().setAriaLabelledbyProperty(
+ setForWidget.getElement(), Id.of(captionId));
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.user.client.ui.UIObject#setStyleName(java.lang.String)
+ */
+ @Override
+ public void setStyleName(String style) {
+ super.setStyleName(style);
+ updateStyleNames();
+ }
+
+ @Override
+ public void setStylePrimaryName(String style) {
+ removeStyleName(getStylePrimaryName() + "-popupcalendar");
+ super.setStylePrimaryName(style);
+ updateStyleNames();
+ }
+
+ @Override
+ protected void updateStyleNames() {
+ super.updateStyleNames();
+ if (getStylePrimaryName() != null && calendarToggle != null) {
+ addStyleName(getStylePrimaryName() + "-popupcalendar");
+ calendarToggle.setStyleName(getStylePrimaryName() + "-button");
+ popup.setStyleName(getStylePrimaryName() + "-popup");
+ calendar.setStyleName(getStylePrimaryName() + "-calendarpanel");
+ }
+ }
+
+ /**
+ * Opens the calendar panel popup
+ */
+ public void openCalendarPanel() {
+
+ if (!open && !readonly && isEnabled()) {
+ open = true;
+
+ if (getCurrentDate() != null) {
+ calendar.setDate((Date) getCurrentDate().clone());
+ } else {
+ calendar.setDate(new Date());
+ }
+
+ // clear previous values
+ popup.setWidth("");
+ popup.setHeight("");
+ popup.setPopupPositionAndShow(new PopupPositionCallback());
+ } else {
+ VConsole.error("Cannot reopen popup, it is already open!");
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event
+ * .dom.client.ClickEvent)
+ */
+ @Override
+ public void onClick(ClickEvent event) {
+ if (event.getSource() == calendarToggle && isEnabled()) {
+ if (!preventOpenPopupCalendar) {
+ openCalendarPanel();
+ }
+ preventOpenPopupCalendar = false;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.logical.shared.CloseHandler#onClose(com.google.gwt
+ * .event.logical.shared.CloseEvent)
+ */
+ @Override
+ public void onClose(CloseEvent<PopupPanel> event) {
+ if (event.getSource() == popup) {
+ buildDate();
+ if (!BrowserInfo.get().isTouchDevice() && textFieldEnabled) {
+ /*
+ * Move focus to textbox, unless on touch device (avoids opening
+ * virtual keyboard) or if textField is disabled.
+ */
+ focus();
+ }
+
+ open = false;
+
+ if (cursorOverCalendarToggleButton
+ && !toggleButtonClosesWithGuarantee) {
+ preventOpenPopupCalendar = true;
+ }
+
+ toggleButtonClosesWithGuarantee = false;
+ }
+ }
+
+ /**
+ * Sets focus to Calendar panel.
+ *
+ * @param focus
+ */
+ public void setFocus(boolean focus) {
+ calendar.setFocus(focus);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ updateTextFieldEnabled();
+ calendarToggle.setEnabled(enabled);
+ Roles.getButtonRole().setAriaDisabledState(calendarToggle.getElement(),
+ !enabled);
+ }
+
+ /**
+ * Sets the content of a special field for assistive devices, so that they
+ * can recognize the change and inform the user (reading out in case of
+ * screen reader)
+ *
+ * @param selectedDate
+ * Date that is currently selected
+ */
+ public void setFocusedDate(Date selectedDate) {
+ this.selectedDate.setText(DateTimeFormat.getFormat("dd, MMMM, yyyy")
+ .format(selectedDate));
+ }
+
+ /**
+ * For internal use only. May be removed or replaced in the future.
+ *
+ * @see com.vaadin.client.ui.VAbstractTextualDate#buildDate()
+ */
+ @Override
+ public void buildDate() {
+ // Save previous value
+ String previousValue = getText();
+ super.buildDate();
+
+ // Restore previous value if the input could not be parsed
+ if (!parsable) {
+ setText(previousValue);
+ }
+ updateTextFieldEnabled();
+ }
+
+ /**
+ * Update the text field contents from the date. See {@link #buildDate()}.
+ *
+ * @param forceValid
+ * true to force the text field to be updated, false to only
+ * update if the parsable flag is true.
+ */
+ protected void buildDate(boolean forceValid) {
+ if (forceValid) {
+ parsable = true;
+ }
+ buildDate();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.client.ui.VDateField#onBrowserEvent(com.google
+ * .gwt.user.client.Event)
+ */
+ @Override
+ public void onBrowserEvent(com.google.gwt.user.client.Event event) {
+ super.onBrowserEvent(event);
+ if (DOM.eventGetType(event) == Event.ONKEYDOWN
+ && event.getKeyCode() == getOpenCalenderPanelKey()) {
+ openCalendarPanel();
+ event.preventDefault();
+ }
+ }
+
+ /**
+ * Get the key code that opens the calendar panel. By default it is the down
+ * key but you can override this to be whatever you like
+ *
+ * @return
+ */
+ protected int getOpenCalenderPanelKey() {
+ return KeyCodes.KEY_DOWN;
+ }
+
+ /**
+ * Closes the open popup panel
+ */
+ public void closeCalendarPanel() {
+ if (open) {
+ toggleButtonClosesWithGuarantee = true;
+ popup.hide(true);
+ }
+ }
+
+ @Override
+ public com.google.gwt.user.client.Element getSubPartElement(
+ String subPart) {
+ if (subPart.equals(CALENDAR_TOGGLE_ID)) {
+ return calendarToggle.getElement();
+ }
+
+ return super.getSubPartElement(subPart);
+ }
+
+ @Override
+ public String getSubPartName(
+ com.google.gwt.user.client.Element subElement) {
+ if (calendarToggle.getElement().isOrHasChild(subElement)) {
+ return CALENDAR_TOGGLE_ID;
+ }
+
+ return super.getSubPartName(subElement);
+ }
+
+ /**
+ * Set a description that explains the usage of the Widget for users of
+ * assistive devices.
+ *
+ * @param descriptionForAssistiveDevices
+ * String with the description
+ */
+ public void setDescriptionForAssistiveDevices(
+ String descriptionForAssistiveDevices) {
+ descriptionForAssisitveDevicesElement
+ .setInnerText(descriptionForAssistiveDevices);
+ }
+
+ /**
+ * Get the description that explains the usage of the Widget for users of
+ * assistive devices.
+ *
+ * @return String with the description
+ */
+ public String getDescriptionForAssistiveDevices() {
+ return descriptionForAssisitveDevicesElement.getInnerText();
+ }
+
+ /**
+ * Sets the start range for this component. The start range is inclusive,
+ * and it depends on the current resolution, what is considered inside the
+ * range.
+ *
+ * @param startDate
+ * - the allowed range's start date
+ */
+ public void setRangeStart(Date rangeStart) {
+ calendar.setRangeStart(rangeStart);
+ }
+
+ /**
+ * Sets the end range for this component. The end range is inclusive, and it
+ * depends on the current resolution, what is considered inside the range.
+ *
+ * @param endDate
+ * - the allowed range's end date
+ */
+ public void setRangeEnd(Date rangeEnd) {
+ calendar.setRangeEnd(rangeEnd);
+ }
+
+ private class PopupPositionCallback implements PositionCallback {
+
+ @Override
+ public void setPosition(int offsetWidth, int offsetHeight) {
+ final int width = offsetWidth;
+ final int height = offsetHeight;
+ final int browserWindowWidth = Window.getClientWidth()
+ + Window.getScrollLeft();
+ final int windowHeight = Window.getClientHeight()
+ + Window.getScrollTop();
+ int left = calendarToggle.getAbsoluteLeft();
+
+ // Add a little extra space to the right to avoid
+ // problems with IE7 scrollbars and to make it look
+ // nicer.
+ int extraSpace = 30;
+
+ boolean overflow = left + width + extraSpace > browserWindowWidth;
+ if (overflow) {
+ // Part of the popup is outside the browser window
+ // (to the right)
+ left = browserWindowWidth - width - extraSpace;
+ }
+
+ int top = calendarToggle.getAbsoluteTop();
+ int extraHeight = 2;
+ boolean verticallyRepositioned = false;
+ ComputedStyle style = new ComputedStyle(popup.getElement());
+ int[] margins = style.getMargin();
+ int desiredPopupBottom = top + height
+ + calendarToggle.getOffsetHeight() + margins[0]
+ + margins[2];
+
+ if (desiredPopupBottom > windowHeight) {
+ int updatedLeft = left;
+ left = getLeftPosition(left, width, style, overflow);
+
+ // if position has not been changed then it means there is no
+ // space to make popup fully visible
+ if (updatedLeft == left) {
+ // let's try to show popup on the top of the field
+ int updatedTop = top - extraHeight - height - margins[0]
+ - margins[2];
+ verticallyRepositioned = updatedTop >= 0;
+ if (verticallyRepositioned) {
+ top = updatedTop;
+ }
+ }
+ // Part of the popup is outside the browser window
+ // (below)
+ if (!verticallyRepositioned) {
+ verticallyRepositioned = true;
+ top = windowHeight - height - extraSpace + extraHeight;
+ }
+ }
+ if (verticallyRepositioned) {
+ popup.setPopupPosition(left, top);
+ } else {
+ popup.setPopupPosition(left,
+ top + calendarToggle.getOffsetHeight() + extraHeight);
+ }
+ doSetFocus();
+ }
+
+ private int getLeftPosition(int left, int width, ComputedStyle style,
+ boolean overflow) {
+ if (positionRightSide()) {
+ // Show to the right of the popup button unless we
+ // are in the lower right corner of the screen
+ if (overflow) {
+ return left;
+ } else {
+ return left + calendarToggle.getOffsetWidth();
+ }
+ } else {
+ int[] margins = style.getMargin();
+ int desiredLeftPosition = calendarToggle.getAbsoluteLeft()
+ - width - margins[1] - margins[3];
+ if (desiredLeftPosition >= 0) {
+ return desiredLeftPosition;
+ } else {
+ return left;
+ }
+ }
+ }
+
+ private boolean positionRightSide() {
+ int buttonRightSide = calendarToggle.getAbsoluteLeft()
+ + calendarToggle.getOffsetWidth();
+ int textRightSide = text.getAbsoluteLeft() + text.getOffsetWidth();
+ return buttonRightSide >= textRightSide;
+ }
+
+ private void doSetFocus() {
+ /*
+ * We have to wait a while before focusing since the popup needs to
+ * be opened before we can focus
+ */
+ Timer focusTimer = new Timer() {
+ @Override
+ public void run() {
+ setFocus(true);
+ }
+ };
+
+ focusTimer.schedule(100);
+ }
+ }
+
+}
diff --git a/client/src/main/java/com/vaadin/client/ui/VTextualDate.java b/client/src/main/java/com/vaadin/client/ui/VAbstractTextualDate.java
index b7c89c6397..d99e1c0a6c 100644
--- a/client/src/main/java/com/vaadin/client/ui/VTextualDate.java
+++ b/client/src/main/java/com/vaadin/client/ui/VAbstractTextualDate.java
@@ -19,12 +19,9 @@ package com.vaadin.client.ui;
import java.util.Date;
import com.google.gwt.aria.client.Roles;
-import com.google.gwt.event.dom.client.BlurEvent;
-import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
-import com.google.gwt.event.dom.client.FocusEvent;
-import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
@@ -39,10 +36,20 @@ import com.vaadin.client.ui.aria.HandlesAriaCaption;
import com.vaadin.client.ui.aria.HandlesAriaInvalid;
import com.vaadin.client.ui.aria.HandlesAriaRequired;
import com.vaadin.shared.EventId;
-import com.vaadin.shared.ui.datefield.Resolution;
-public class VTextualDate extends VDateField implements Field, ChangeHandler,
- Focusable, SubPartAware, HandlesAriaCaption, HandlesAriaInvalid,
+/**
+ * Abstract textual date field base implementation. Provides a text box as an
+ * editor for a date. The class is parameterized by the date resolution
+ * enumeration type.
+ *
+ * @author Vaadin Ltd
+ *
+ * @param <R>
+ * the resolution type which this field is based on (day, month, ...)
+ */
+public abstract class VAbstractTextualDate<R extends Enum<R>>
+ extends VDateField<R> implements Field, ChangeHandler, Focusable,
+ SubPartAware, HandlesAriaCaption, HandlesAriaInvalid,
HandlesAriaRequired, KeyDownHandler {
private static final String PARSE_ERROR_CLASSNAME = "-parseerror";
@@ -51,51 +58,30 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler,
public final TextBox text;
/** For internal use only. May be removed or replaced in the future. */
- public String formatStr;
+ public boolean lenient;
+
+ private final String TEXTFIELD_ID = "field";
/** For internal use only. May be removed or replaced in the future. */
- public boolean lenient;
+ public String formatStr;
- public VTextualDate() {
- super();
+ public VAbstractTextualDate(R resoluton) {
+ super(resoluton);
text = new TextBox();
text.addChangeHandler(this);
- text.addFocusHandler(new FocusHandler() {
- @Override
- public void onFocus(FocusEvent event) {
- text.addStyleName(VTextField.CLASSNAME + "-"
- + VTextField.CLASSNAME_FOCUS);
- if (getClient() != null && getClient()
- .hasEventListeners(VTextualDate.this, EventId.FOCUS)) {
- getClient().updateVariable(getId(), EventId.FOCUS, "",
- true);
- }
-
- // Needed for tooltip event handling
- VTextualDate.this.fireEvent(event);
- }
- });
- text.addBlurHandler(new BlurHandler() {
- @Override
- public void onBlur(BlurEvent event) {
- text.removeStyleName(VTextField.CLASSNAME + "-"
- + VTextField.CLASSNAME_FOCUS);
- String value = getText();
- if (getClient() != null && getClient()
- .hasEventListeners(VTextualDate.this, EventId.BLUR)) {
- getClient().updateVariable(getId(), EventId.BLUR, "", true);
- }
-
- // Needed for tooltip event handling
- VTextualDate.this.fireEvent(event);
- }
- });
+ text.addFocusHandler(
+ event -> fireBlurFocusEvent(event, true, EventId.FOCUS));
+ text.addBlurHandler(
+ event -> fireBlurFocusEvent(event, false, EventId.BLUR));
if (BrowserInfo.get().isIE()) {
addDomHandler(this, KeyDownEvent.getType());
}
add(text);
}
+ /**
+ * Updates style names for the widget (and its children).
+ */
protected void updateStyleNames() {
if (text != null) {
text.setStyleName(VTextField.CLASSNAME);
@@ -103,9 +89,14 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler,
}
}
+ /**
+ * Gets the date format string for the current locale.
+ *
+ * @return the format string
+ */
protected String getFormatString() {
if (formatStr == null) {
- if (currentResolution == Resolution.YEAR) {
+ if (isYear(getCurrentResolution())) {
formatStr = "yyyy"; // force full year
} else {
@@ -224,34 +215,39 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler,
getClient().updateVariable(getId(), "dateString", text.getText(),
false);
+ updateDateVariables();
+ }
+
+ /**
+ * Updates variables to send a response to the server.
+ * <p>
+ * The method can be overridden by subclasses to provide a custom logic for
+ * date variables to avoid overriding the {@link #onChange(ChangeEvent)}
+ * method.
+ */
+ protected void updateDateVariables() {
// Update variables
// (only the smallest defining resolution needs to be
// immediate)
Date currentDate = getDate();
- getClient().updateVariable(getId(), "year",
+ getClient().updateVariable(getId(),
+ getResolutionVariable(getResolutions().filter(this::isYear)
+ .findFirst().get()),
currentDate != null ? currentDate.getYear() + 1900 : -1,
- currentResolution == Resolution.YEAR);
- if (currentResolution.compareTo(Resolution.MONTH) <= 0) {
- getClient().updateVariable(getId(), "month",
- currentDate != null ? currentDate.getMonth() + 1 : -1,
- currentResolution == Resolution.MONTH);
- }
- if (currentResolution.compareTo(Resolution.DAY) <= 0) {
- getClient().updateVariable(getId(), "day",
- currentDate != null ? currentDate.getDate() : -1,
- currentResolution == Resolution.DAY);
- }
+ isYear(getCurrentResolution()));
}
- private String cleanFormat(String format) {
- // Remove unnecessary d & M if resolution is too low
- if (currentResolution.compareTo(Resolution.DAY) > 0) {
- format = format.replaceAll("d", "");
- }
- if (currentResolution.compareTo(Resolution.MONTH) > 0) {
- format = format.replaceAll("M", "");
- }
-
+ /**
+ * Clean date format string to make it suitable for
+ * {@link #getFormatString()}.
+ *
+ * @see #getFormatString()
+ *
+ * @param format
+ * date format string
+ * @return cleaned up string
+ */
+ protected String cleanFormat(String format) {
// Remove unsupported patterns
// TODO support for 'G', era designator (used at least in Japan)
format = format.replaceAll("[GzZwWkK]", "");
@@ -311,8 +307,6 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler,
this.text.setText(text);
}
- private final String TEXTFIELD_ID = "field";
-
@Override
public com.google.gwt.user.client.Element getSubPartElement(
String subPart) {
@@ -342,4 +336,22 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler,
onChange(null);
}
}
+
+ private void fireBlurFocusEvent(DomEvent<?> event,
+ boolean addFocusStyleName, String eventId) {
+ String styleName = VTextField.CLASSNAME + "-"
+ + VTextField.CLASSNAME_FOCUS;
+ if (addFocusStyleName) {
+ text.addStyleName(styleName);
+ } else {
+ text.removeStyleName(styleName);
+ }
+ if (getClient() != null && getClient()
+ .hasEventListeners(VAbstractTextualDate.this, eventId)) {
+ getClient().updateVariable(getId(), eventId, "", true);
+ }
+
+ // Needed for tooltip event handling
+ fireEvent(event);
+ }
}
diff --git a/client/src/main/java/com/vaadin/client/ui/VDateCalendarPanel.java b/client/src/main/java/com/vaadin/client/ui/VDateCalendarPanel.java
new file mode 100644
index 0000000000..9685b05f10
--- /dev/null
+++ b/client/src/main/java/com/vaadin/client/ui/VDateCalendarPanel.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.vaadin.client.ui;
+
+import com.vaadin.shared.ui.datefield.DateResolution;
+
+/**
+ * @author Vaadin Ltd
+ *
+ */
+public class VDateCalendarPanel extends VAbstractCalendarPanel<DateResolution> {
+
+ @Override
+ protected boolean acceptDayFocus() {
+ return isDay(getResolution());
+ }
+
+ @Override
+ protected boolean isDay(DateResolution resolution) {
+ return DateResolution.DAY.equals(resolution);
+ }
+
+ @Override
+ protected boolean isMonth(DateResolution resolution) {
+ return DateResolution.MONTH.equals(resolution);
+ }
+
+ @Override
+ protected boolean isBelowMonth(DateResolution resolution) {
+ return isDay(resolution);
+ }
+
+}
diff --git a/client/src/main/java/com/vaadin/client/ui/VDateField.java b/client/src/main/java/com/vaadin/client/ui/VDateField.java
index 2f8c72081e..e7a49ef5be 100644
--- a/client/src/main/java/com/vaadin/client/ui/VDateField.java
+++ b/client/src/main/java/com/vaadin/client/ui/VDateField.java
@@ -17,14 +17,25 @@
package com.vaadin.client.ui;
import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
+import java.util.stream.Stream;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasEnabled;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.DateTimeService;
-import com.vaadin.shared.ui.datefield.Resolution;
-public class VDateField extends FlowPanel implements Field, HasEnabled {
+/**
+ * A very base widget class for a date field.
+ *
+ * @author Vaadin Ltd
+ *
+ * @param <R>
+ * the resolution type which this field is based on (day, month, ...)
+ */
+public abstract class VDateField<R extends Enum<R>> extends FlowPanel
+ implements Field, HasEnabled {
public static final String CLASSNAME = "v-datefield";
@@ -34,18 +45,7 @@ public class VDateField extends FlowPanel implements Field, HasEnabled {
/** For internal use only. May be removed or replaced in the future. */
public ApplicationConnection client;
- /** For internal use only. May be removed or replaced in the future. */
- public static String resolutionToString(Resolution res) {
- if (res == Resolution.DAY) {
- return "day";
- }
- if (res == Resolution.MONTH) {
- return "month";
- }
- return "year";
- }
-
- protected Resolution currentResolution = Resolution.YEAR;
+ private R currentResolution;
protected String currentLocale;
@@ -64,33 +64,17 @@ public class VDateField extends FlowPanel implements Field, HasEnabled {
protected boolean showISOWeekNumbers = false;
- public VDateField() {
+ public VDateField(R resolution) {
setStyleName(CLASSNAME);
dts = new DateTimeService();
+ currentResolution = resolution;
}
- /**
- * For internal use only. May be removed or replaced in the future.
- */
- public static Date getTime(int year, int month, int day) {
- Date date = new Date(2000 - 1900, 0, 1);
- if (year >= 0) {
- date.setYear(year - 1900);
- }
- if (month >= 0) {
- date.setMonth(month - 1);
- }
- if (day >= 0) {
- date.setDate(day);
- }
- return date;
- }
-
- public Resolution getCurrentResolution() {
+ public R getCurrentResolution() {
return currentResolution;
}
- public void setCurrentResolution(Resolution currentResolution) {
+ public void setCurrentResolution(R currentResolution) {
this.currentResolution = currentResolution;
}
@@ -110,6 +94,20 @@ public class VDateField extends FlowPanel implements Field, HasEnabled {
this.date = date;
}
+ /**
+ * Set the current date using a map with date values.
+ * <p>
+ * The map contains integer representation of values per resolution. The
+ * method should construct a date based on the map and set it via
+ * {@link #setCurrentDate(Date)}
+ *
+ * @param dateValues
+ * a map with date values to convert into a date
+ */
+ public void setCurrentDate(Map<R, Integer> dateValues) {
+ setCurrentDate(getDate(dateValues));
+ }
+
public boolean isReadonly() {
return readonly;
}
@@ -182,4 +180,71 @@ public class VDateField extends FlowPanel implements Field, HasEnabled {
protected void setDate(Date date) {
this.date = date;
}
+
+ /**
+ * Returns a resolution variable name for the given {@code resolution}.
+ *
+ * @param resolution
+ * the given resolution
+ * @return the resolution variable name
+ */
+ public String getResolutionVariable(R resolution) {
+ return resolution.name().toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Returns all available resolutions for the field in the ascending order
+ * (which is the same as order of enumeration ordinals).
+ * <p>
+ * The method uses {@link #doGetResolutions()} to make sure that the order
+ * is the correct one.
+ *
+ * @see #doGetResolutions()
+ *
+ * @return stream of all available resolutions in the ascending order.
+ */
+ public Stream<R> getResolutions() {
+ return Stream.of(doGetResolutions()).sorted();
+ }
+
+ /**
+ * Returns a current resolution as a string.
+ * <p>
+ * The method is used to generate a style name for the current resolution.
+ *
+ * @return the current resolution as a string
+ */
+ public abstract String resolutionAsString();
+
+ /**
+ * Checks whether the given {@code resolution} represents an year.
+ *
+ * @param resolution
+ * the given resolution
+ * @return {@code true} if the {@code resolution} represents an year
+ */
+ public abstract boolean isYear(R resolution);
+
+ /**
+ * Returns a date based on the provided date values map.
+ *
+ * @see #setCurrentDate(Map)
+ *
+ * @param dateVaules
+ * a map with date values to convert into a date
+ * @return the date based on the dateValues map
+ */
+ protected abstract Date getDate(Map<R, Integer> dateVaules);
+
+ /**
+ * Returns all available resolutions as an array.
+ * <p>
+ * No any order is required (in contrary to {@link #getResolutions()}.
+ *
+ * @see #getResolutions()
+ *
+ * @return all available resolutions
+ */
+ protected abstract R[] doGetResolutions();
+
}
diff --git a/client/src/main/java/com/vaadin/client/ui/VDateFieldCalendar.java b/client/src/main/java/com/vaadin/client/ui/VDateFieldCalendar.java
index 18d896e487..8c3c3a805f 100644
--- a/client/src/main/java/com/vaadin/client/ui/VDateFieldCalendar.java
+++ b/client/src/main/java/com/vaadin/client/ui/VDateFieldCalendar.java
@@ -13,48 +13,25 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-
package com.vaadin.client.ui;
import java.util.Date;
+import java.util.Map;
-import com.google.gwt.event.dom.client.DomEvent;
-import com.vaadin.client.ui.VCalendarPanel.FocusOutListener;
-import com.vaadin.client.ui.VCalendarPanel.SubmitListener;
-import com.vaadin.shared.ui.datefield.Resolution;
+import com.google.gwt.core.shared.GWT;
+import com.vaadin.shared.ui.datefield.DateResolution;
/**
- * A client side implementation for InlineDateField
+ * A client side implementation for InlineDateField.
+ *
+ * @author Vaadin Ltd
+ *
*/
-public class VDateFieldCalendar extends VDateField {
-
- /** For internal use only. May be removed or replaced in the future. */
- public final VCalendarPanel calendarPanel;
+public class VDateFieldCalendar
+ extends VAbstractDateFieldCalendar<DateResolution> {
public VDateFieldCalendar() {
- super();
- calendarPanel = new VCalendarPanel();
- calendarPanel.setParentField(this);
- add(calendarPanel);
- calendarPanel.setSubmitListener(new SubmitListener() {
- @Override
- public void onSubmit() {
- updateValueFromPanel();
- }
-
- @Override
- public void onCancel() {
- // TODO Auto-generated method stub
-
- }
- });
- calendarPanel.setFocusOutListener(new FocusOutListener() {
- @Override
- public boolean onFocusOut(DomEvent<?> event) {
- updateValueFromPanel();
- return false;
- }
- });
+ super(GWT.create(VDateCalendarPanel.class), DateResolution.YEAR);
}
/**
@@ -62,10 +39,9 @@ public class VDateFieldCalendar extends VDateField {
* <p>
* For internal use only. May be removed or replaced in the future.
*/
-
+ @Override
@SuppressWarnings("deprecation")
public void updateValueFromPanel() {
-
// If field is invisible at the beginning, client can still be null when
// this function is called.
if (getClient() == null) {
@@ -76,25 +52,50 @@ public class VDateFieldCalendar extends VDateField {
Date currentDate = getCurrentDate();
if (currentDate == null || date2.getTime() != currentDate.getTime()) {
setCurrentDate((Date) date2.clone());
- getClient().updateVariable(getId(), "year", date2.getYear() + 1900,
- false);
- if (getCurrentResolution().compareTo(Resolution.YEAR) < 0) {
- getClient().updateVariable(getId(), "month",
+ getClient().updateVariable(getId(),
+ getResolutionVariable(DateResolution.YEAR),
+ // Java Date uses the year aligned to 1900 (no to zero).
+ // So we should add 1900 to get a correct year aligned to 0.
+ date2.getYear() + 1900, false);
+ if (getCurrentResolution().compareTo(DateResolution.YEAR) < 0) {
+ getClient().updateVariable(getId(),
+ getResolutionVariable(DateResolution.MONTH),
date2.getMonth() + 1, false);
- if (getCurrentResolution().compareTo(Resolution.MONTH) < 0) {
- getClient().updateVariable(getId(), "day", date2.getDate(),
- false);
+ if (getCurrentResolution()
+ .compareTo(DateResolution.MONTH) < 0) {
+ getClient().updateVariable(getId(),
+ getResolutionVariable(DateResolution.DAY),
+ date2.getDate(), false);
}
}
getClient().sendPendingVariableChanges();
}
}
- public void setTabIndex(int tabIndex) {
- calendarPanel.getElement().setTabIndex(tabIndex);
+ @Override
+ public void setCurrentResolution(DateResolution resolution) {
+ super.setCurrentResolution(
+ resolution == null ? DateResolution.YEAR : resolution);
+ }
+
+ @Override
+ public String resolutionAsString() {
+ return getResolutionVariable(getCurrentResolution());
+ }
+
+ @Override
+ public boolean isYear(DateResolution resolution) {
+ return DateResolution.YEAR.equals(resolution);
+ }
+
+ @Override
+ protected DateResolution[] doGetResolutions() {
+ return DateResolution.values();
}
- public int getTabIndex() {
- return calendarPanel.getElement().getTabIndex();
+ @Override
+ protected Date getDate(Map<DateResolution, Integer> dateVaules) {
+ return VPopupCalendar.makeDate(dateVaules);
}
+
}
diff --git a/client/src/main/java/com/vaadin/client/ui/VPopupCalendar.java b/client/src/main/java/com/vaadin/client/ui/VPopupCalendar.java
index eece76b39a..20425faa8b 100644
--- a/client/src/main/java/com/vaadin/client/ui/VPopupCalendar.java
+++ b/client/src/main/java/com/vaadin/client/ui/VPopupCalendar.java
@@ -13,709 +13,91 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-
package com.vaadin.client.ui;
import java.util.Date;
+import java.util.Map;
-import com.google.gwt.aria.client.Id;
-import com.google.gwt.aria.client.LiveValue;
-import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.DomEvent;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.MouseOutEvent;
-import com.google.gwt.event.dom.client.MouseOutHandler;
-import com.google.gwt.event.dom.client.MouseOverEvent;
-import com.google.gwt.event.dom.client.MouseOverHandler;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.i18n.client.DateTimeFormat;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.Timer;
-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.Label;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
-import com.google.gwt.user.client.ui.RootPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.client.BrowserInfo;
-import com.vaadin.client.ComputedStyle;
-import com.vaadin.client.VConsole;
-import com.vaadin.client.ui.VCalendarPanel.FocusOutListener;
-import com.vaadin.client.ui.VCalendarPanel.SubmitListener;
-import com.vaadin.client.ui.aria.AriaHelper;
-import com.vaadin.shared.ui.datefield.DateFieldState;
-import com.vaadin.shared.ui.datefield.Resolution;
+import com.vaadin.shared.ui.datefield.DateResolution;
/**
* Represents a date selection component with a text field and a popup date
* selector.
- *
- * <b>Note:</b> To change the keyboard assignments used in the popup dialog you
- * should extend <code>com.vaadin.client.ui.VCalendarPanel</code> and then pass
- * set it by calling the <code>setCalendarPanel(VCalendarPanel panel)</code>
- * method.
+ *
+ * @author Vaadin Ltd
*
*/
-public class VPopupCalendar extends VTextualDate
- implements Field, ClickHandler, CloseHandler<PopupPanel>, SubPartAware {
-
- /** For internal use only. May be removed or replaced in the future. */
- public final Button calendarToggle = new Button();
-
- /** For internal use only. May be removed or replaced in the future. */
- public VCalendarPanel calendar;
-
- /** For internal use only. May be removed or replaced in the future. */
- public final VOverlay popup;
-
- /** For internal use only. May be removed or replaced in the future. */
- public boolean parsable = true;
-
- private boolean open = false;
-
- /*
- * #14857: If calendarToggle button is clicked when calendar popup is
- * already open we should prevent calling openCalendarPanel() in onClick,
- * since we don't want to reopen it again right after it closes.
- */
- private boolean preventOpenPopupCalendar = false;
- private boolean cursorOverCalendarToggleButton = false;
- private boolean toggleButtonClosesWithGuarantee = false;
-
- private boolean textFieldEnabled = true;
-
- private String captionId;
-
- private Label selectedDate;
-
- private Element descriptionForAssisitveDevicesElement;
+public class VPopupCalendar extends VAbstractPopupCalendar<DateResolution> {
public VPopupCalendar() {
- super();
-
- calendarToggle.setText("");
- calendarToggle.addClickHandler(this);
-
- calendarToggle.addDomHandler(new MouseOverHandler() {
- @Override
- public void onMouseOver(MouseOverEvent event) {
- cursorOverCalendarToggleButton = true;
- }
- }, MouseOverEvent.getType());
-
- calendarToggle.addDomHandler(new MouseOutHandler() {
- @Override
- public void onMouseOut(MouseOutEvent event) {
- cursorOverCalendarToggleButton = false;
- }
- }, MouseOutEvent.getType());
-
- // -2 instead of -1 to avoid FocusWidget.onAttach to reset it
- calendarToggle.getElement().setTabIndex(-2);
-
- Roles.getButtonRole().set(calendarToggle.getElement());
- Roles.getButtonRole().setAriaHiddenState(calendarToggle.getElement(),
- true);
-
- add(calendarToggle);
-
- // Description of the usage of the widget for assisitve device users
- descriptionForAssisitveDevicesElement = DOM.createDiv();
- descriptionForAssisitveDevicesElement
- .setInnerText(DateFieldState.DESCRIPTION_FOR_ASSISTIVE_DEVICES);
- AriaHelper.ensureHasId(descriptionForAssisitveDevicesElement);
- Roles.getTextboxRole().setAriaDescribedbyProperty(text.getElement(),
- Id.of(descriptionForAssisitveDevicesElement));
- AriaHelper.setVisibleForAssistiveDevicesOnly(
- descriptionForAssisitveDevicesElement, true);
-
- calendar = GWT.create(VCalendarPanel.class);
- calendar.setParentField(this);
- calendar.setFocusOutListener(new FocusOutListener() {
- @Override
- public boolean onFocusOut(DomEvent<?> event) {
- event.preventDefault();
- closeCalendarPanel();
- return true;
- }
- });
-
- // FIXME: Problem is, that the element with the provided id does not
- // exist yet in html. This is the same problem as with the context menu.
- // Apply here the same fix (#11795)
- Roles.getTextboxRole().setAriaControlsProperty(text.getElement(),
- Id.of(calendar.getElement()));
- Roles.getButtonRole().setAriaControlsProperty(
- calendarToggle.getElement(), Id.of(calendar.getElement()));
-
- calendar.setSubmitListener(new SubmitListener() {
- @Override
- public void onSubmit() {
- // Update internal value and send valuechange event if immediate
- updateValue(calendar.getDate());
-
- // Update text field (a must when not immediate).
- buildDate(true);
-
- closeCalendarPanel();
- }
-
- @Override
- public void onCancel() {
- closeCalendarPanel();
- }
- });
-
- popup = new VOverlay(true, false);
- popup.setOwner(this);
-
- FlowPanel wrapper = new FlowPanel();
- selectedDate = new Label();
- selectedDate.setStyleName(getStylePrimaryName() + "-selecteddate");
- AriaHelper.setVisibleForAssistiveDevicesOnly(selectedDate.getElement(),
- true);
-
- Roles.getTextboxRole().setAriaLiveProperty(selectedDate.getElement(),
- LiveValue.ASSERTIVE);
- Roles.getTextboxRole().setAriaAtomicProperty(selectedDate.getElement(),
- true);
- wrapper.add(selectedDate);
- wrapper.add(calendar);
-
- popup.setWidget(wrapper);
- popup.addCloseHandler(this);
-
- DOM.setElementProperty(calendar.getElement(), "id",
- "PID_VAADIN_POPUPCAL");
-
- sinkEvents(Event.ONKEYDOWN);
-
- updateStyleNames();
- }
-
- @Override
- protected void onAttach() {
- super.onAttach();
- DOM.appendChild(RootPanel.get().getElement(),
- descriptionForAssisitveDevicesElement);
- }
-
- @Override
- protected void onDetach() {
- super.onDetach();
- descriptionForAssisitveDevicesElement.removeFromParent();
- closeCalendarPanel();
- }
-
- @SuppressWarnings("deprecation")
- public void updateValue(Date newDate) {
- Date currentDate = getCurrentDate();
- if (currentDate == null || newDate.getTime() != currentDate.getTime()) {
- setCurrentDate((Date) newDate.clone());
- getClient().updateVariable(getId(), "year",
- newDate.getYear() + 1900, false);
- if (getCurrentResolution().compareTo(Resolution.YEAR) < 0) {
- getClient().updateVariable(getId(), "month",
- newDate.getMonth() + 1, false);
- if (getCurrentResolution().compareTo(Resolution.MONTH) < 0) {
- getClient().updateVariable(getId(), "day",
- newDate.getDate(), false);
- }
- }
- }
- }
-
- /**
- * Checks whether the text field is enabled.
- *
- * @see VPopupCalendar#setTextFieldEnabled(boolean)
- * @return The current state of the text field.
- */
- public boolean isTextFieldEnabled() {
- return textFieldEnabled;
- }
-
- /**
- * Sets the state of the text field of this component. By default the text
- * field is enabled. Disabling it causes only the button for date selection
- * to be active, thus preventing the user from entering invalid dates. See
- * {@link http://dev.vaadin.com/ticket/6790}.
- *
- * @param state
- */
- public void setTextFieldEnabled(boolean textFieldEnabled) {
- this.textFieldEnabled = textFieldEnabled;
- updateTextFieldEnabled();
- }
-
- protected void updateTextFieldEnabled() {
- boolean reallyEnabled = isEnabled() && isTextFieldEnabled();
- // IE has a non input disabled themeing that can not be overridden so we
- // must fake the functionality using readonly and unselectable
- if (BrowserInfo.get().isIE()) {
- if (!reallyEnabled) {
- text.getElement().setAttribute("unselectable", "on");
- text.getElement().setAttribute("readonly", "");
- text.setTabIndex(-2);
- } else if (reallyEnabled
- && text.getElement().hasAttribute("unselectable")) {
- text.getElement().removeAttribute("unselectable");
- text.getElement().removeAttribute("readonly");
- text.setTabIndex(0);
- }
- } else {
- text.setEnabled(reallyEnabled);
- }
-
- if (reallyEnabled) {
- calendarToggle.setTabIndex(-1);
- Roles.getButtonRole()
- .setAriaHiddenState(calendarToggle.getElement(), true);
- } else {
- calendarToggle.setTabIndex(0);
- Roles.getButtonRole()
- .setAriaHiddenState(calendarToggle.getElement(), false);
- }
-
- handleAriaAttributes();
- }
-
- /**
- * Set correct tab index for disabled text field in IE as the value set in
- * setTextFieldEnabled(...) gets overridden in
- * TextualDateConnection.updateFromUIDL(...)
- *
- * @since 7.3.1
- */
- public void setTextFieldTabIndex() {
- if (BrowserInfo.get().isIE() && !textFieldEnabled) {
- // index needs to be -2 because FocusWidget updates -1 to 0 onAttach
- text.setTabIndex(-2);
- }
- }
-
- @Override
- public void bindAriaCaption(
- com.google.gwt.user.client.Element captionElement) {
- if (captionElement == null) {
- captionId = null;
- } else {
- captionId = captionElement.getId();
- }
-
- if (isTextFieldEnabled()) {
- super.bindAriaCaption(captionElement);
- } else {
- AriaHelper.bindCaption(calendarToggle, captionElement);
- }
-
- handleAriaAttributes();
- }
-
- private void handleAriaAttributes() {
- Widget removeFromWidget;
- Widget setForWidget;
-
- if (isTextFieldEnabled()) {
- setForWidget = text;
- removeFromWidget = calendarToggle;
- } else {
- setForWidget = calendarToggle;
- removeFromWidget = text;
- }
-
- Roles.getFormRole()
- .removeAriaLabelledbyProperty(removeFromWidget.getElement());
- if (captionId == null) {
- Roles.getFormRole()
- .removeAriaLabelledbyProperty(setForWidget.getElement());
- } else {
- Roles.getFormRole().setAriaLabelledbyProperty(
- setForWidget.getElement(), Id.of(captionId));
- }
+ super(GWT.create(VDateCalendarPanel.class), DateResolution.YEAR);
}
- /*
- * (non-Javadoc)
- *
- * @see
- * com.google.gwt.user.client.ui.UIObject#setStyleName(java.lang.String)
- */
@Override
- public void setStyleName(String style) {
- super.setStyleName(style);
- updateStyleNames();
+ protected DateResolution[] doGetResolutions() {
+ return DateResolution.values();
}
@Override
- public void setStylePrimaryName(String style) {
- removeStyleName(getStylePrimaryName() + "-popupcalendar");
- super.setStylePrimaryName(style);
- updateStyleNames();
+ public String resolutionAsString() {
+ return getResolutionVariable(getCurrentResolution());
}
@Override
- protected void updateStyleNames() {
- super.updateStyleNames();
- if (getStylePrimaryName() != null && calendarToggle != null) {
- addStyleName(getStylePrimaryName() + "-popupcalendar");
- calendarToggle.setStyleName(getStylePrimaryName() + "-button");
- popup.setStyleName(getStylePrimaryName() + "-popup");
- calendar.setStyleName(getStylePrimaryName() + "-calendarpanel");
- }
- }
-
- /**
- * Opens the calendar panel popup
- */
- public void openCalendarPanel() {
-
- if (!open && !readonly && isEnabled()) {
- open = true;
-
- if (getCurrentDate() != null) {
- calendar.setDate((Date) getCurrentDate().clone());
- } else {
- calendar.setDate(new Date());
- }
-
- // clear previous values
- popup.setWidth("");
- popup.setHeight("");
- popup.setPopupPositionAndShow(new PopupPositionCallback());
- } else {
- VConsole.error("Cannot reopen popup, it is already open!");
- }
+ public void setCurrentResolution(DateResolution resolution) {
+ super.setCurrentResolution(
+ resolution == null ? DateResolution.YEAR : resolution);
}
- /*
- * (non-Javadoc)
- *
- * @see
- * com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event
- * .dom.client.ClickEvent)
- */
- @Override
- public void onClick(ClickEvent event) {
- if (event.getSource() == calendarToggle && isEnabled()) {
- if (!preventOpenPopupCalendar) {
- openCalendarPanel();
- }
- preventOpenPopupCalendar = false;
+ public static Date makeDate(Map<DateResolution, Integer> dateVaules) {
+ if (dateVaules.get(DateResolution.YEAR) == -1) {
+ return null;
}
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * com.google.gwt.event.logical.shared.CloseHandler#onClose(com.google.gwt
- * .event.logical.shared.CloseEvent)
- */
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- if (event.getSource() == popup) {
- buildDate();
- if (!BrowserInfo.get().isTouchDevice() && textFieldEnabled) {
- /*
- * Move focus to textbox, unless on touch device (avoids opening
- * virtual keyboard) or if textField is disabled.
- */
- focus();
- }
-
- open = false;
-
- if (cursorOverCalendarToggleButton
- && !toggleButtonClosesWithGuarantee) {
- preventOpenPopupCalendar = true;
- }
-
- toggleButtonClosesWithGuarantee = false;
+ Date date = new Date(2000 - 1900, 0, 1);
+ int year = dateVaules.get(DateResolution.YEAR);
+ if (year >= 0) {
+ date.setYear(year - 1900);
}
- }
-
- /**
- * Sets focus to Calendar panel.
- *
- * @param focus
- */
- public void setFocus(boolean focus) {
- calendar.setFocus(focus);
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- super.setEnabled(enabled);
- updateTextFieldEnabled();
- calendarToggle.setEnabled(enabled);
- Roles.getButtonRole().setAriaDisabledState(calendarToggle.getElement(),
- !enabled);
- }
-
- /**
- * Sets the content of a special field for assistive devices, so that they
- * can recognize the change and inform the user (reading out in case of
- * screen reader)
- *
- * @param selectedDate
- * Date that is currently selected
- */
- public void setFocusedDate(Date selectedDate) {
- this.selectedDate.setText(DateTimeFormat.getFormat("dd, MMMM, yyyy")
- .format(selectedDate));
- }
-
- /**
- * For internal use only. May be removed or replaced in the future.
- *
- * @see com.vaadin.client.ui.VTextualDate#buildDate()
- */
- @Override
- public void buildDate() {
- // Save previous value
- String previousValue = getText();
- super.buildDate();
-
- // Restore previous value if the input could not be parsed
- if (!parsable) {
- setText(previousValue);
+ int month = dateVaules.get(DateResolution.MONTH);
+ if (month >= 0) {
+ date.setMonth(month - 1);
}
- updateTextFieldEnabled();
- }
-
- /**
- * Update the text field contents from the date. See {@link #buildDate()}.
- *
- * @param forceValid
- * true to force the text field to be updated, false to only
- * update if the parsable flag is true.
- */
- protected void buildDate(boolean forceValid) {
- if (forceValid) {
- parsable = true;
+ int day = dateVaules.get(DateResolution.DAY);
+ if (day >= 0) {
+ date.setDate(day);
}
- buildDate();
+ return date;
}
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.client.ui.VDateField#onBrowserEvent(com.google
- * .gwt.user.client.Event)
- */
@Override
- public void onBrowserEvent(com.google.gwt.user.client.Event event) {
- super.onBrowserEvent(event);
- if (DOM.eventGetType(event) == Event.ONKEYDOWN
- && event.getKeyCode() == getOpenCalenderPanelKey()) {
- openCalendarPanel();
- event.preventDefault();
- }
- }
-
- /**
- * Get the key code that opens the calendar panel. By default it is the down
- * key but you can override this to be whatever you like
- *
- * @return
- */
- protected int getOpenCalenderPanelKey() {
- return KeyCodes.KEY_DOWN;
- }
-
- /**
- * Closes the open popup panel
- */
- public void closeCalendarPanel() {
- if (open) {
- toggleButtonClosesWithGuarantee = true;
- popup.hide(true);
- }
+ public boolean isYear(DateResolution resolution) {
+ return DateResolution.YEAR.equals(resolution);
}
- private final String CALENDAR_TOGGLE_ID = "popupButton";
-
@Override
- public com.google.gwt.user.client.Element getSubPartElement(
- String subPart) {
- if (subPart.equals(CALENDAR_TOGGLE_ID)) {
- return calendarToggle.getElement();
- }
-
- return super.getSubPartElement(subPart);
+ protected Date getDate(Map<DateResolution, Integer> dateValues) {
+ return makeDate(dateValues);
}
@Override
- public String getSubPartName(
- com.google.gwt.user.client.Element subElement) {
- if (calendarToggle.getElement().isOrHasChild(subElement)) {
- return CALENDAR_TOGGLE_ID;
- }
-
- return super.getSubPartName(subElement);
- }
-
- /**
- * Set a description that explains the usage of the Widget for users of
- * assistive devices.
- *
- * @param descriptionForAssistiveDevices
- * String with the description
- */
- public void setDescriptionForAssistiveDevices(
- String descriptionForAssistiveDevices) {
- descriptionForAssisitveDevicesElement
- .setInnerText(descriptionForAssistiveDevices);
- }
-
- /**
- * Get the description that explains the usage of the Widget for users of
- * assistive devices.
- *
- * @return String with the description
- */
- public String getDescriptionForAssistiveDevices() {
- return descriptionForAssisitveDevicesElement.getInnerText();
- }
-
- /**
- * Sets the start range for this component. The start range is inclusive,
- * and it depends on the current resolution, what is considered inside the
- * range.
- *
- * @param startDate
- * - the allowed range's start date
- */
- public void setRangeStart(Date rangeStart) {
- calendar.setRangeStart(rangeStart);
- }
-
- /**
- * Sets the end range for this component. The end range is inclusive, and it
- * depends on the current resolution, what is considered inside the range.
- *
- * @param endDate
- * - the allowed range's end date
- */
- public void setRangeEnd(Date rangeEnd) {
- calendar.setRangeEnd(rangeEnd);
- }
-
- private class PopupPositionCallback implements PositionCallback {
-
- @Override
- public void setPosition(int offsetWidth, int offsetHeight) {
- final int width = offsetWidth;
- final int height = offsetHeight;
- final int browserWindowWidth = Window.getClientWidth()
- + Window.getScrollLeft();
- final int windowHeight = Window.getClientHeight()
- + Window.getScrollTop();
- int left = calendarToggle.getAbsoluteLeft();
-
- // Add a little extra space to the right to avoid
- // problems with IE7 scrollbars and to make it look
- // nicer.
- int extraSpace = 30;
-
- boolean overflow = left + width + extraSpace > browserWindowWidth;
- if (overflow) {
- // Part of the popup is outside the browser window
- // (to the right)
- left = browserWindowWidth - width - extraSpace;
- }
-
- int top = calendarToggle.getAbsoluteTop();
- int extraHeight = 2;
- boolean verticallyRepositioned = false;
- ComputedStyle style = new ComputedStyle(popup.getElement());
- int[] margins = style.getMargin();
- int desiredPopupBottom = top + height
- + calendarToggle.getOffsetHeight() + margins[0]
- + margins[2];
-
- if (desiredPopupBottom > windowHeight) {
- int updatedLeft = left;
- left = getLeftPosition(left, width, style, overflow);
-
- // if position has not been changed then it means there is no
- // space to make popup fully visible
- if (updatedLeft == left) {
- // let's try to show popup on the top of the field
- int updatedTop = top - extraHeight - height - margins[0]
- - margins[2];
- verticallyRepositioned = updatedTop >= 0;
- if (verticallyRepositioned) {
- top = updatedTop;
- }
- }
- // Part of the popup is outside the browser window
- // (below)
- if (!verticallyRepositioned) {
- verticallyRepositioned = true;
- top = windowHeight - height - extraSpace + extraHeight;
- }
- }
- if (verticallyRepositioned) {
- popup.setPopupPosition(left, top);
- } else {
- popup.setPopupPosition(left,
- top + calendarToggle.getOffsetHeight() + extraHeight);
- }
- doSetFocus();
- }
-
- private int getLeftPosition(int left, int width, ComputedStyle style,
- boolean overflow) {
- if (positionRightSide()) {
- // Show to the right of the popup button unless we
- // are in the lower right corner of the screen
- if (overflow) {
- return left;
- } else {
- return left + calendarToggle.getOffsetWidth();
- }
- } else {
- int[] margins = style.getMargin();
- int desiredLeftPosition = calendarToggle.getAbsoluteLeft()
- - width - margins[1] - margins[3];
- if (desiredLeftPosition >= 0) {
- return desiredLeftPosition;
- } else {
- return left;
- }
- }
- }
-
- private boolean positionRightSide() {
- int buttonRightSide = calendarToggle.getAbsoluteLeft()
- + calendarToggle.getOffsetWidth();
- int textRightSide = text.getAbsoluteLeft() + text.getOffsetWidth();
- return buttonRightSide >= textRightSide;
- }
-
- private void doSetFocus() {
- /*
- * We have to wait a while before focusing since the popup needs to
- * be opened before we can focus
- */
- Timer focusTimer = new Timer() {
- @Override
- public void run() {
- setFocus(true);
- }
- };
-
- focusTimer.schedule(100);
+ protected void updateDateVariables() {
+ super.updateDateVariables();
+ // Update variables
+ // (only the smallest defining resolution needs to be
+ // immediate)
+ Date currentDate = getDate();
+ if (getCurrentResolution().compareTo(DateResolution.MONTH) <= 0) {
+ getClient().updateVariable(getId(),
+ getResolutionVariable(DateResolution.MONTH),
+ currentDate != null ? currentDate.getMonth() + 1 : -1,
+ getCurrentResolution() == DateResolution.MONTH);
+ }
+ if (getCurrentResolution().compareTo(DateResolution.DAY) <= 0) {
+ getClient().updateVariable(getId(),
+ getResolutionVariable(DateResolution.DAY),
+ currentDate != null ? currentDate.getDate() : -1,
+ getCurrentResolution() == DateResolution.DAY);
}
}
diff --git a/client/src/main/java/com/vaadin/client/ui/datefield/AbstractDateFieldConnector.java b/client/src/main/java/com/vaadin/client/ui/datefield/AbstractDateFieldConnector.java
index 5e0fe7cc2e..c851c13679 100644
--- a/client/src/main/java/com/vaadin/client/ui/datefield/AbstractDateFieldConnector.java
+++ b/client/src/main/java/com/vaadin/client/ui/datefield/AbstractDateFieldConnector.java
@@ -15,6 +15,12 @@
*/
package com.vaadin.client.ui.datefield;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.LocaleNotLoadedException;
import com.vaadin.client.Paintable;
@@ -22,12 +28,10 @@ import com.vaadin.client.UIDL;
import com.vaadin.client.VConsole;
import com.vaadin.client.ui.AbstractFieldConnector;
import com.vaadin.client.ui.VDateField;
-import com.vaadin.client.ui.VTextualDate;
import com.vaadin.shared.ui.datefield.DateFieldConstants;
-import com.vaadin.shared.ui.datefield.Resolution;
-public class AbstractDateFieldConnector extends AbstractFieldConnector
- implements Paintable {
+public abstract class AbstractDateFieldConnector<R extends Enum<R>>
+ extends AbstractFieldConnector implements Paintable {
@Override
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
@@ -62,46 +66,42 @@ public class AbstractDateFieldConnector extends AbstractFieldConnector
uidl.getBooleanAttribute(DateFieldConstants.ATTR_WEEK_NUMBERS)
&& getWidget().dts.getFirstDayOfWeek() == 1);
- Resolution newResolution;
- if (uidl.hasVariable("day")) {
- newResolution = Resolution.DAY;
- } else if (uidl.hasVariable("month")) {
- newResolution = Resolution.MONTH;
- } else {
- newResolution = Resolution.YEAR;
- }
-
// Remove old stylename that indicates current resolution
- setWidgetStyleName(
- getWidget().getStylePrimaryName() + "-" + VDateField
- .resolutionToString(getWidget().getCurrentResolution()),
- false);
+ setWidgetStyleName(getWidget().getStylePrimaryName() + "-"
+ + getWidget().resolutionAsString(), false);
- getWidget().setCurrentResolution(newResolution);
+ updateResolution(uidl);
// Add stylename that indicates current resolution
- setWidgetStyleName(
- getWidget().getStylePrimaryName() + "-" + VDateField
- .resolutionToString(getWidget().getCurrentResolution()),
- true);
-
- final Resolution resolution = getWidget().getCurrentResolution();
- final int year = uidl.getIntVariable("year");
- final int month = resolution.compareTo(Resolution.MONTH) <= 0
- ? uidl.getIntVariable("month") : -1;
- final int day = resolution.compareTo(Resolution.DAY) <= 0
- ? uidl.getIntVariable("day") : -1;
-
- // Construct new date for this datefield (only if not null)
- if (year > -1) {
- getWidget().setCurrentDate(VTextualDate.getTime(year, month, day));
- } else {
- getWidget().setCurrentDate(null);
- }
+ setWidgetStyleName(getWidget().getStylePrimaryName() + "-"
+ + getWidget().resolutionAsString(), true);
+
+ getWidget().setCurrentDate(getTimeValues(uidl));
}
+ private void updateResolution(UIDL uidl) {
+ Optional<R> newResolution = getWidget().getResolutions().filter(
+ res -> uidl.hasVariable(getWidget().getResolutionVariable(res)))
+ .findFirst();
+
+ getWidget().setCurrentResolution(newResolution.orElse(null));
+ }
+
+ protected Map<R, Integer> getTimeValues(UIDL uidl) {
+ Stream<R> resolutions = getWidget().getResolutions();
+ R resolution = getWidget().getCurrentResolution();
+ return resolutions
+ .collect(Collectors.toMap(Function.identity(),
+ res -> (resolution.compareTo(res) <= 0)
+ ? uidl.getIntVariable(
+ getWidget().getResolutionVariable(res))
+ : -1));
+ }
+
+ @SuppressWarnings("unchecked")
@Override
- public VDateField getWidget() {
- return (VDateField) super.getWidget();
+ public VDateField<R> getWidget() {
+ return (VDateField<R>) super.getWidget();
}
+
}
diff --git a/client/src/main/java/com/vaadin/client/ui/datefield/AbstractInlineDateFieldConnector.java b/client/src/main/java/com/vaadin/client/ui/datefield/AbstractInlineDateFieldConnector.java
new file mode 100644
index 0000000000..5252c40d77
--- /dev/null
+++ b/client/src/main/java/com/vaadin/client/ui/datefield/AbstractInlineDateFieldConnector.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.vaadin.client.ui.datefield;
+
+import java.util.Date;
+
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.UIDL;
+import com.vaadin.client.communication.StateChangeEvent;
+import com.vaadin.client.ui.VAbstractCalendarPanel.FocusChangeListener;
+import com.vaadin.client.ui.VAbstractDateFieldCalendar;
+import com.vaadin.shared.ui.datefield.InlineDateFieldState;
+
+/**
+ * Base class for inline data field connector.
+ *
+ * @author Vaadin Ltd
+ *
+ * @param <R>
+ * the resolution type which the field is based on (day, month, ...)
+ */
+public abstract class AbstractInlineDateFieldConnector<R extends Enum<R>>
+ extends AbstractDateFieldConnector<R> {
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ super.updateFromUIDL(uidl, client);
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ getWidget().calendarPanel
+ .setShowISOWeekNumbers(getWidget().isShowISOWeekNumbers());
+ getWidget().calendarPanel
+ .setDateTimeService(getWidget().getDateTimeService());
+ getWidget().calendarPanel
+ .setResolution(getWidget().getCurrentResolution());
+ Date currentDate = getWidget().getCurrentDate();
+ if (currentDate != null) {
+ getWidget().calendarPanel.setDate(new Date(currentDate.getTime()));
+ } else {
+ getWidget().calendarPanel.setDate(null);
+ }
+
+ updateListeners();
+
+ // Update possible changes
+ getWidget().calendarPanel.renderCalendar();
+ }
+
+ /**
+ * Updates listeners registered (or register them) for the widget based on
+ * the current resolution.
+ * <p>
+ * Subclasses may override this method to keep the common logic inside the
+ * {@link #updateFromUIDL(UIDL, ApplicationConnection)} method as is and
+ * customizing only listeners logic.
+ */
+ protected void updateListeners() {
+ if (isResolutionMonthOrHigher()) {
+ getWidget().calendarPanel
+ .setFocusChangeListener(new FocusChangeListener() {
+ @Override
+ public void focusChanged(Date date) {
+ Date date2 = new Date();
+ if (getWidget().calendarPanel.getDate() != null) {
+ date2.setTime(getWidget().calendarPanel
+ .getDate().getTime());
+ }
+ /*
+ * Update the value of calendarPanel
+ */
+ date2.setYear(date.getYear());
+ date2.setMonth(date.getMonth());
+ getWidget().calendarPanel.setDate(date2);
+ /*
+ * Then update the value from panel to server
+ */
+ getWidget().updateValueFromPanel();
+ }
+ });
+ } else {
+ getWidget().calendarPanel.setFocusChangeListener(null);
+ }
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+ getWidget().setTabIndex(getState().tabIndex);
+ getWidget().calendarPanel.setRangeStart(getState().rangeStart);
+ getWidget().calendarPanel.setRangeEnd(getState().rangeEnd);
+ }
+
+ @Override
+ public VAbstractDateFieldCalendar<R> getWidget() {
+ return (VAbstractDateFieldCalendar<R>) super.getWidget();
+ }
+
+ @Override
+ public InlineDateFieldState getState() {
+ return (InlineDateFieldState) super.getState();
+ }
+
+ /**
+ * Returns {@code true} is the current resolution of the widget is month or
+ * less specific (e.g. month, year, quarter, etc).
+ *
+ * @return {@code true} if the current resolution is above month
+ */
+ protected abstract boolean isResolutionMonthOrHigher();
+
+}
diff --git a/client/src/main/java/com/vaadin/client/ui/datefield/AbstractTextualDateConnector.java b/client/src/main/java/com/vaadin/client/ui/datefield/AbstractTextualDateConnector.java
new file mode 100644
index 0000000000..b9448862e7
--- /dev/null
+++ b/client/src/main/java/com/vaadin/client/ui/datefield/AbstractTextualDateConnector.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.vaadin.client.ui.datefield;
+
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.UIDL;
+import com.vaadin.client.ui.VAbstractTextualDate;
+import com.vaadin.shared.ui.datefield.AbstractTextualDateFieldState;
+
+public abstract class AbstractTextualDateConnector<R extends Enum<R>>
+ extends AbstractDateFieldConnector<R> {
+
+ @Override
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ R origRes = getWidget().getCurrentResolution();
+ String oldLocale = getWidget().getCurrentLocale();
+ super.updateFromUIDL(uidl, client);
+ if (origRes != getWidget().getCurrentResolution()
+ || oldLocale != getWidget().getCurrentLocale()) {
+ // force recreating format string
+ getWidget().formatStr = null;
+ }
+ if (uidl.hasAttribute("format")) {
+ getWidget().formatStr = uidl.getStringAttribute("format");
+ }
+
+ getWidget().lenient = !uidl.getBooleanAttribute("strict");
+
+ getWidget().buildDate();
+ // not a FocusWidget -> needs own tabindex handling
+ getWidget().text.setTabIndex(getState().tabIndex);
+
+ if (getWidget().isReadonly()) {
+ getWidget().text.addStyleDependentName("readonly");
+ } else {
+ getWidget().text.removeStyleDependentName("readonly");
+ }
+
+ }
+
+ @Override
+ public VAbstractTextualDate<R> getWidget() {
+ return (VAbstractTextualDate<R>) super.getWidget();
+ }
+
+ @Override
+ public AbstractTextualDateFieldState getState() {
+ return (AbstractTextualDateFieldState) super.getState();
+ }
+
+}
diff --git a/client/src/main/java/com/vaadin/client/ui/datefield/DateFieldConnector.java b/client/src/main/java/com/vaadin/client/ui/datefield/DateFieldConnector.java
index 7161283176..35a9420ada 100644
--- a/client/src/main/java/com/vaadin/client/ui/datefield/DateFieldConnector.java
+++ b/client/src/main/java/com/vaadin/client/ui/datefield/DateFieldConnector.java
@@ -13,124 +13,25 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-
package com.vaadin.client.ui.datefield;
-import java.util.Date;
-
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.vaadin.client.ApplicationConnection;
-import com.vaadin.client.UIDL;
-import com.vaadin.client.communication.StateChangeEvent;
-import com.vaadin.client.ui.VCalendarPanel.FocusChangeListener;
import com.vaadin.client.ui.VPopupCalendar;
import com.vaadin.shared.ui.Connect;
-import com.vaadin.shared.ui.datefield.DateFieldState;
-import com.vaadin.shared.ui.datefield.Resolution;
-import com.vaadin.ui.AbstractDateField;
-
-@Connect(AbstractDateField.class)
-public class DateFieldConnector extends TextualDateConnector {
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.client.ui.AbstractConnector#init()
- */
- @Override
- protected void init() {
- getWidget().popup.addCloseHandler(new CloseHandler<PopupPanel>() {
+import com.vaadin.shared.ui.datefield.DateResolution;
+import com.vaadin.shared.ui.datefield.LocalDateFieldState;
+import com.vaadin.ui.AbstractLocalDateField;
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- /*
- * FIXME This is a hack so we do not have to rewrite half of the
- * datefield so values are not sent while selecting a date
- * (#6252).
- *
- * The datefield will now only set the date UIDL variables while
- * the user is selecting year/month/date/time and not send them
- * directly. Only when the user closes the popup (by clicking on
- * a day/enter/clicking outside of popup) then the new value is
- * communicated to the server.
- */
- getConnection().getServerRpcQueue().flush();
- }
- });
- }
+/**
+ * @author Vaadin Ltd
+ *
+ */
+@Connect(AbstractLocalDateField.class)
+public class DateFieldConnector extends TextualDateConnector<DateResolution> {
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.client.ui.VTextualDate#updateFromUIDL(com.vaadin
- * .client.UIDL, com.vaadin.client.ApplicationConnection)
- */
@Override
- @SuppressWarnings("deprecation")
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
-
- String oldLocale = getWidget().getCurrentLocale();
-
- getWidget().parsable = uidl.getBooleanAttribute("parsable");
-
- super.updateFromUIDL(uidl, client);
-
- getWidget().calendar
- .setDateTimeService(getWidget().getDateTimeService());
- getWidget().calendar
- .setShowISOWeekNumbers(getWidget().isShowISOWeekNumbers());
- if (getWidget().calendar.getResolution() != getWidget()
- .getCurrentResolution()) {
- boolean hasSelectedDate = false;
- getWidget().calendar
- .setResolution(getWidget().getCurrentResolution());
- if (getWidget().calendar.getDate() != null
- && getWidget().getCurrentDate() != null) {
- hasSelectedDate = true;
- getWidget().calendar
- .setDate((Date) getWidget().getCurrentDate().clone());
- }
- // force re-render when changing resolution only
- getWidget().calendar.renderCalendar(hasSelectedDate);
- }
-
- // Force re-render of calendar if locale has changed (#12153)
- if (!getWidget().getCurrentLocale().equals(oldLocale)) {
- getWidget().calendar.renderCalendar();
- }
-
- if (getWidget().getCurrentResolution()
- .compareTo(Resolution.MONTH) >= 0) {
- getWidget().calendar
- .setFocusChangeListener(new FocusChangeListener() {
- @Override
- public void focusChanged(Date date) {
-
- getWidget().updateValue(date);
- getWidget().buildDate();
- Date date2 = getWidget().calendar.getDate();
- date2.setYear(date.getYear());
- date2.setMonth(date.getMonth());
- }
- });
- } else {
- getWidget().calendar.setFocusChangeListener(null);
- }
-
- if (getWidget().isReadonly()) {
- getWidget().calendarToggle.addStyleName(
- VPopupCalendar.CLASSNAME + "-button-readonly");
- } else {
- getWidget().calendarToggle.removeStyleName(
- VPopupCalendar.CLASSNAME + "-button-readonly");
- }
-
- getWidget().setDescriptionForAssistiveDevices(
- getState().descriptionForAssistiveDevices);
-
- getWidget().setTextFieldTabIndex();
+ protected boolean isResolutionAboveMonth() {
+ return getWidget().getCurrentResolution()
+ .compareTo(DateResolution.MONTH) >= 0;
}
@Override
@@ -139,50 +40,7 @@ public class DateFieldConnector extends TextualDateConnector {
}
@Override
- public DateFieldState getState() {
- return (DateFieldState) super.getState();
- }
-
- @Override
- public void onStateChanged(StateChangeEvent stateChangeEvent) {
- super.onStateChanged(stateChangeEvent);
- getWidget().setTextFieldEnabled(getState().textFieldEnabled);
- getWidget().setRangeStart(nullSafeDateClone(getState().rangeStart));
- getWidget().setRangeEnd(nullSafeDateClone(getState().rangeEnd));
- }
-
- private Date nullSafeDateClone(Date date) {
- if (date == null) {
- return null;
- } else {
- return (Date) date.clone();
- }
- }
-
- @Override
- protected void setWidgetStyleName(String styleName, boolean add) {
- super.setWidgetStyleName(styleName, add);
-
- // update the style change to popup calendar widget
- getWidget().popup.setStyleName(styleName, add);
- }
-
- @Override
- protected void setWidgetStyleNameWithPrefix(String prefix, String styleName,
- boolean add) {
- super.setWidgetStyleNameWithPrefix(prefix, styleName, add);
-
- // update the style change to popup calendar widget with the correct
- // prefix
- if (!styleName.startsWith("-")) {
- getWidget().popup.setStyleName(
- getWidget().getStylePrimaryName() + "-popup-" + styleName,
- add);
- } else {
- getWidget().popup.setStyleName(
- getWidget().getStylePrimaryName() + "-popup" + styleName,
- add);
- }
+ public LocalDateFieldState getState() {
+ return (LocalDateFieldState) super.getState();
}
-
}
diff --git a/client/src/main/java/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java b/client/src/main/java/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java
index add25ae99b..7339191ee9 100644
--- a/client/src/main/java/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java
+++ b/client/src/main/java/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java
@@ -15,88 +15,27 @@
*/
package com.vaadin.client.ui.datefield;
-import java.util.Date;
-
-import com.vaadin.client.ApplicationConnection;
-import com.vaadin.client.UIDL;
-import com.vaadin.client.communication.StateChangeEvent;
-import com.vaadin.client.ui.VCalendarPanel.FocusChangeListener;
import com.vaadin.client.ui.VDateFieldCalendar;
import com.vaadin.shared.ui.Connect;
-import com.vaadin.shared.ui.datefield.InlineDateFieldState;
-import com.vaadin.shared.ui.datefield.Resolution;
+import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.ui.InlineDateField;
+/**
+ * @author Vaadin Ltd
+ *
+ */
@Connect(InlineDateField.class)
-public class InlineDateFieldConnector extends AbstractDateFieldConnector {
-
- @Override
- @SuppressWarnings("deprecation")
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- super.updateFromUIDL(uidl, client);
- if (!isRealUpdate(uidl)) {
- return;
- }
-
- getWidget().calendarPanel
- .setShowISOWeekNumbers(getWidget().isShowISOWeekNumbers());
- getWidget().calendarPanel
- .setDateTimeService(getWidget().getDateTimeService());
- getWidget().calendarPanel
- .setResolution(getWidget().getCurrentResolution());
- Date currentDate = getWidget().getCurrentDate();
- if (currentDate != null) {
- getWidget().calendarPanel.setDate(new Date(currentDate.getTime()));
- } else {
- getWidget().calendarPanel.setDate(null);
- }
-
- if (getWidget().getCurrentResolution()
- .compareTo(Resolution.MONTH) >= 0) {
- getWidget().calendarPanel
- .setFocusChangeListener(new FocusChangeListener() {
- @Override
- public void focusChanged(Date date) {
- Date date2 = new Date();
- if (getWidget().calendarPanel.getDate() != null) {
- date2.setTime(getWidget().calendarPanel
- .getDate().getTime());
- }
- /*
- * Update the value of calendarPanel
- */
- date2.setYear(date.getYear());
- date2.setMonth(date.getMonth());
- getWidget().calendarPanel.setDate(date2);
- /*
- * Then update the value from panel to server
- */
- getWidget().updateValueFromPanel();
- }
- });
- } else {
- getWidget().calendarPanel.setFocusChangeListener(null);
- }
-
- // Update possible changes
- getWidget().calendarPanel.renderCalendar();
- }
+public class InlineDateFieldConnector
+ extends AbstractInlineDateFieldConnector<DateResolution> {
@Override
- public void onStateChanged(StateChangeEvent stateChangeEvent) {
- super.onStateChanged(stateChangeEvent);
- getWidget().setTabIndex(getState().tabIndex);
- getWidget().calendarPanel.setRangeStart(getState().rangeStart);
- getWidget().calendarPanel.setRangeEnd(getState().rangeEnd);
+ protected boolean isResolutionMonthOrHigher() {
+ return getWidget().getCurrentResolution()
+ .compareTo(DateResolution.MONTH) >= 0;
}
@Override
public VDateFieldCalendar getWidget() {
return (VDateFieldCalendar) super.getWidget();
}
-
- @Override
- public InlineDateFieldState getState() {
- return (InlineDateFieldState) super.getState();
- }
}
diff --git a/client/src/main/java/com/vaadin/client/ui/datefield/TextualDateConnector.java b/client/src/main/java/com/vaadin/client/ui/datefield/TextualDateConnector.java
index bf77d3b25f..2179c7a9ca 100644
--- a/client/src/main/java/com/vaadin/client/ui/datefield/TextualDateConnector.java
+++ b/client/src/main/java/com/vaadin/client/ui/datefield/TextualDateConnector.java
@@ -16,49 +16,172 @@
package com.vaadin.client.ui.datefield;
+import java.util.Date;
+
+import com.google.gwt.event.logical.shared.CloseEvent;
+import com.google.gwt.event.logical.shared.CloseHandler;
+import com.google.gwt.user.client.ui.PopupPanel;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.UIDL;
-import com.vaadin.client.ui.VTextualDate;
-import com.vaadin.shared.ui.datefield.Resolution;
+import com.vaadin.client.communication.StateChangeEvent;
+import com.vaadin.client.ui.VAbstractCalendarPanel.FocusChangeListener;
+import com.vaadin.client.ui.VAbstractPopupCalendar;
import com.vaadin.shared.ui.datefield.TextualDateFieldState;
-public class TextualDateConnector extends AbstractDateFieldConnector {
+public abstract class TextualDateConnector<R extends Enum<R>>
+ extends AbstractTextualDateConnector<R> {
+
+ @Override
+ protected void init() {
+ getWidget().popup.addCloseHandler(new CloseHandler<PopupPanel>() {
+
+ @Override
+ public void onClose(CloseEvent<PopupPanel> event) {
+ /*
+ * FIXME This is a hack so we do not have to rewrite half of the
+ * datefield so values are not sent while selecting a date
+ * (#6252).
+ *
+ * The datefield will now only set the date UIDL variables while
+ * the user is selecting year/month/date/time and not send them
+ * directly. Only when the user closes the popup (by clicking on
+ * a day/enter/clicking outside of popup) then the new value is
+ * communicated to the server.
+ */
+ getConnection().getServerRpcQueue().flush();
+ }
+ });
+ }
@Override
+ @SuppressWarnings("deprecation")
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- Resolution origRes = getWidget().getCurrentResolution();
+
String oldLocale = getWidget().getCurrentLocale();
+
+ getWidget().parsable = uidl.getBooleanAttribute("parsable");
+
super.updateFromUIDL(uidl, client);
- if (origRes != getWidget().getCurrentResolution()
- || oldLocale != getWidget().getCurrentLocale()) {
- // force recreating format string
- getWidget().formatStr = null;
- }
- if (uidl.hasAttribute("format")) {
- getWidget().formatStr = uidl.getStringAttribute("format");
+
+ getWidget().calendar
+ .setDateTimeService(getWidget().getDateTimeService());
+ getWidget().calendar
+ .setShowISOWeekNumbers(getWidget().isShowISOWeekNumbers());
+ if (getWidget().calendar.getResolution() != getWidget()
+ .getCurrentResolution()) {
+ boolean hasSelectedDate = false;
+ getWidget().calendar
+ .setResolution(getWidget().getCurrentResolution());
+ if (getWidget().calendar.getDate() != null
+ && getWidget().getCurrentDate() != null) {
+ hasSelectedDate = true;
+ getWidget().calendar
+ .setDate((Date) getWidget().getCurrentDate().clone());
+ }
+ // force re-render when changing resolution only
+ getWidget().calendar.renderCalendar(hasSelectedDate);
}
- getWidget().lenient = !uidl.getBooleanAttribute("strict");
+ // Force re-render of calendar if locale has changed (#12153)
+ if (!getWidget().getCurrentLocale().equals(oldLocale)) {
+ getWidget().calendar.renderCalendar();
+ }
- getWidget().buildDate();
- // not a FocusWidget -> needs own tabindex handling
- getWidget().text.setTabIndex(getState().tabIndex);
+ updateListeners();
if (getWidget().isReadonly()) {
- getWidget().text.addStyleDependentName("readonly");
+ getWidget().calendarToggle.addStyleName(
+ VAbstractPopupCalendar.CLASSNAME + "-button-readonly");
} else {
- getWidget().text.removeStyleDependentName("readonly");
+ getWidget().calendarToggle.removeStyleName(
+ VAbstractPopupCalendar.CLASSNAME + "-button-readonly");
}
+ getWidget().setDescriptionForAssistiveDevices(
+ getState().descriptionForAssistiveDevices);
+
+ getWidget().setTextFieldTabIndex();
}
+ /**
+ * Updates listeners registered (or register them) for the widget based on
+ * the current resolution.
+ * <p>
+ * Subclasses may override this method to keep the common logic inside the
+ * {@link #updateFromUIDL(UIDL, ApplicationConnection)} method as is and
+ * customizing only listeners logic.
+ */
+ protected void updateListeners() {
+ if (isResolutionAboveMonth()) {
+ getWidget().calendar
+ .setFocusChangeListener(new FocusChangeListener() {
+ @Override
+ public void focusChanged(Date date) {
+
+ getWidget().updateValue(date);
+ getWidget().buildDate();
+ Date date2 = getWidget().calendar.getDate();
+ date2.setYear(date.getYear());
+ date2.setMonth(date.getMonth());
+ }
+ });
+ } else {
+ getWidget().calendar.setFocusChangeListener(null);
+ }
+ }
+
+ protected abstract boolean isResolutionAboveMonth();
+
@Override
- public VTextualDate getWidget() {
- return (VTextualDate) super.getWidget();
+ public VAbstractPopupCalendar<R> getWidget() {
+ return (VAbstractPopupCalendar<R>) super.getWidget();
}
@Override
public TextualDateFieldState getState() {
return (TextualDateFieldState) super.getState();
}
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+ getWidget().setTextFieldEnabled(getState().textFieldEnabled);
+ getWidget().setRangeStart(nullSafeDateClone(getState().rangeStart));
+ getWidget().setRangeEnd(nullSafeDateClone(getState().rangeEnd));
+ }
+
+ private Date nullSafeDateClone(Date date) {
+ if (date == null) {
+ return null;
+ } else {
+ return (Date) date.clone();
+ }
+ }
+
+ @Override
+ protected void setWidgetStyleName(String styleName, boolean add) {
+ super.setWidgetStyleName(styleName, add);
+
+ // update the style change to popup calendar widget
+ getWidget().popup.setStyleName(styleName, add);
+ }
+
+ @Override
+ protected void setWidgetStyleNameWithPrefix(String prefix, String styleName,
+ boolean add) {
+ super.setWidgetStyleNameWithPrefix(prefix, styleName, add);
+
+ // update the style change to popup calendar widget with the correct
+ // prefix
+ if (!styleName.startsWith("-")) {
+ getWidget().popup.setStyleName(
+ getWidget().getStylePrimaryName() + "-popup-" + styleName,
+ add);
+ } else {
+ getWidget().popup.setStyleName(
+ getWidget().getStylePrimaryName() + "-popup" + styleName,
+ add);
+ }
+ }
+
}