@@ -174,6 +174,9 @@ | |||
<li><tt>Grid</tt> selection API has been removed from component level to <tt>GridSelectionModel</tt> which is available via <tt>Grid::getSelectionModel()</tt></li> | |||
<li><tt>Grid::setSelectionModel(GridSelectionModel)</tt> visibility has been changed from <tt>public</tt> to <tt>protected</tt> to reduce confusion with <tt>Grid::setSelectionMode</tt></li> | |||
</ul> | |||
<ul><h4>Client side widget specific API changes</h4> | |||
<li><tt>VTextualDate</tt></li> widget class is removed and replaced by abstract <tt>VAbstractTextualDate</tt> class which is supposed to be inherited by concrete date field implementation widgets | |||
</ul> | |||
<ul><h4>Component specific visual changes</h4> | |||
<li>The default width of <tt>Label</tt> is now undefined, matching other components</li> | |||
<li>The default width for <tt>ComboBox</tt> pop-up is now 100 % (previously undefined)</li> |
@@ -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; |
@@ -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 |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -62,9 +62,9 @@ import com.vaadin.v7.shared.ui.datefield.Resolution; | |||
* 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. | |||
* should extend <code>com.vaadin.v7.client.ui.VCalendarPanel</code> and then | |||
* pass set it by calling the | |||
* <code>setCalendarPanel(VCalendarPanel panel)</code> method. | |||
* | |||
*/ | |||
public class VPopupCalendar extends VTextualDate | |||
@@ -500,7 +500,7 @@ public class VPopupCalendar extends VTextualDate | |||
/** | |||
* For internal use only. May be removed or replaced in the future. | |||
* | |||
* @see com.vaadin.client.ui.VTextualDate#buildDate() | |||
* @see com.vaadin.v7.client.ui.VTextualDate#buildDate() | |||
*/ | |||
@Override | |||
public void buildDate() { |
@@ -17,7 +17,7 @@ package com.vaadin.v7.data.validator; | |||
import java.util.Date; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.v7.shared.ui.datefield.Resolution; | |||
/** | |||
* Validator for validating that a Date is inside a given range. |
@@ -11,8 +11,8 @@ import java.util.TimeZone; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.v7.data.validator.DateRangeValidator; | |||
import com.vaadin.v7.shared.ui.datefield.Resolution; | |||
public class DateRangeValidatorTest { | |||
Calendar startDate = new GregorianCalendar(TimeZone.getTimeZone("GMT"), |
@@ -1223,9 +1223,10 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
/** | |||
* Returns a collection of attributes that should not be handled by the | |||
* basic implementation of the {@link #readDesign(Element, DesignContext)} and {@link #writeDesign(Element, DesignContext)} | |||
* methods. Typically these are handled in a custom way in the overridden | |||
* versions of the above methods | |||
* basic implementation of the {@link #readDesign(Element, DesignContext)} | |||
* and {@link #writeDesign(Element, DesignContext)} methods. Typically these | |||
* are handled in a custom way in the overridden versions of the above | |||
* methods | |||
* | |||
* @since 7.4 | |||
* |
@@ -15,24 +15,31 @@ | |||
*/ | |||
package com.vaadin.ui; | |||
import java.io.Serializable; | |||
import java.lang.reflect.Type; | |||
import java.text.SimpleDateFormat; | |||
import java.time.Instant; | |||
import java.time.LocalDate; | |||
import java.time.ZoneOffset; | |||
import java.time.temporal.Temporal; | |||
import java.time.temporal.TemporalAdjuster; | |||
import java.util.Calendar; | |||
import java.util.Date; | |||
import java.util.EventObject; | |||
import java.util.HashMap; | |||
import java.util.Locale; | |||
import java.util.Map; | |||
import java.util.Optional; | |||
import java.util.Set; | |||
import java.util.logging.Logger; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.Stream; | |||
import org.jsoup.nodes.Element; | |||
import com.googlecode.gentyref.GenericTypeReflector; | |||
import com.vaadin.data.Result; | |||
import com.vaadin.data.ValidationResult; | |||
import com.vaadin.data.ValueContext; | |||
import com.vaadin.data.validator.DateRangeValidator; | |||
import com.vaadin.data.validator.RangeValidator; | |||
import com.vaadin.event.FieldEvents.BlurEvent; | |||
import com.vaadin.event.FieldEvents.BlurListener; | |||
import com.vaadin.event.FieldEvents.BlurNotifier; | |||
@@ -43,9 +50,9 @@ import com.vaadin.server.PaintException; | |||
import com.vaadin.server.PaintTarget; | |||
import com.vaadin.server.UserError; | |||
import com.vaadin.shared.Registration; | |||
import com.vaadin.shared.ui.datefield.AbstractDateFieldState; | |||
import com.vaadin.shared.ui.datefield.DateFieldConstants; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.TextualDateFieldState; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.ui.declarative.DesignAttributeHandler; | |||
import com.vaadin.ui.declarative.DesignContext; | |||
@@ -55,20 +62,26 @@ import com.vaadin.ui.declarative.DesignContext; | |||
* @author Vaadin Ltd | |||
* | |||
* @since 8.0 | |||
* | |||
* | |||
* @param <T> | |||
* type of date ({@code LocalDate} or {@code LocalDateTime}). | |||
* @param <R> | |||
* resolution enumeration type | |||
* | |||
*/ | |||
public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
public abstract class AbstractDateField<T extends Temporal & TemporalAdjuster & Serializable & Comparable<? super T>, R extends Enum<R>> | |||
extends AbstractField<T> | |||
implements LegacyComponent, FocusNotifier, BlurNotifier { | |||
/** | |||
* Value of the field. | |||
*/ | |||
private LocalDate value; | |||
private T value; | |||
/** | |||
* Specified smallest modifiable unit for the date field. | |||
*/ | |||
private Resolution resolution = Resolution.DAY; | |||
private R resolution; | |||
/** | |||
* Overridden format string | |||
@@ -94,8 +107,6 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
private String defaultParseErrorMessage = "Date format not recognized"; | |||
private static Map<Resolution, String> variableNameForResolution = new HashMap<>(); | |||
private String dateOutOfRangeMessage = "Date is out of allowed range"; | |||
/** | |||
@@ -105,42 +116,46 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
*/ | |||
private boolean preventValueChangeEvent; | |||
static { | |||
variableNameForResolution.put(Resolution.DAY, "day"); | |||
variableNameForResolution.put(Resolution.MONTH, "month"); | |||
variableNameForResolution.put(Resolution.YEAR, "year"); | |||
} | |||
/* Constructors */ | |||
/** | |||
* Constructs an empty <code>DateField</code> with no caption. | |||
* Constructs an empty <code>AbstractDateField</code> with no caption and | |||
* specified {@code resolution}. | |||
* | |||
* @param resolution | |||
* initial resolution for the field | |||
*/ | |||
public AbstractDateField() { | |||
public AbstractDateField(R resolution) { | |||
this.resolution = resolution; | |||
} | |||
/** | |||
* Constructs an empty <code>DateField</code> with caption. | |||
* Constructs an empty <code>AbstractDateField</code> with caption. | |||
* | |||
* @param caption | |||
* the caption of the datefield. | |||
* @param resolution | |||
* initial resolution for the field | |||
*/ | |||
public AbstractDateField(String caption) { | |||
public AbstractDateField(String caption, R resolution) { | |||
this(resolution); | |||
setCaption(caption); | |||
} | |||
/** | |||
* Constructs a new <code>DateField</code> with the given caption and | |||
* initial text contents. | |||
* Constructs a new <code>AbstractDateField</code> with the given caption | |||
* and initial text contents. | |||
* | |||
* @param caption | |||
* the caption <code>String</code> for the editor. | |||
* @param value | |||
* the LocalDate value. | |||
* the date/time value. | |||
* @param resolution | |||
* initial resolution for the field | |||
*/ | |||
public AbstractDateField(String caption, LocalDate value) { | |||
public AbstractDateField(String caption, T value, R resolution) { | |||
this(caption, resolution); | |||
setValue(value); | |||
setCaption(caption); | |||
} | |||
/* Component basic features */ | |||
@@ -174,17 +189,16 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
* app or refresh. | |||
*/ | |||
final LocalDate currentDate = getValue(); | |||
final T currentDate = getValue(); | |||
// Only paint variables for the resolution and up, e.g. Resolution DAY | |||
// paints DAY,MONTH,YEAR | |||
for (Resolution res : Resolution | |||
.getResolutionsHigherOrEqualTo(resolution)) { | |||
for (R res : getResolutionsHigherOrEqualTo(getResolution())) { | |||
int value = -1; | |||
if (currentDate != null) { | |||
value = getDateValue(currentDate, res); | |||
value = getDatePart(currentDate, res); | |||
} | |||
target.addVariable(this, variableNameForResolution.get(res), value); | |||
target.addVariable(this, getResolutionVariable(res), value); | |||
} | |||
} | |||
@@ -195,15 +209,15 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
*/ | |||
@Override | |||
public void changeVariables(Object source, Map<String, Object> variables) { | |||
if (!isReadOnly() && (variables.containsKey("year") | |||
|| variables.containsKey("month") | |||
|| variables.containsKey("day") | |||
Set<String> resolutionNames = getResolutions() | |||
.map(this::getResolutionVariable).collect(Collectors.toSet()); | |||
resolutionNames.retainAll(variables.keySet()); | |||
if (!isReadOnly() && (!resolutionNames.isEmpty() | |||
|| variables.containsKey("dateString"))) { | |||
// Old and new dates | |||
final LocalDate oldDate = getValue(); | |||
LocalDate newDate = null; | |||
final T oldDate = getValue(); | |||
T newDate = null; | |||
// this enables analyzing invalid input on the server | |||
final String newDateString = (String) variables.get("dateString"); | |||
@@ -211,15 +225,15 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
// Gets the new date in parts | |||
boolean hasChanges = false; | |||
Map<Resolution, Integer> calendarFields = new HashMap<>(); | |||
Map<R, Integer> calendarFields = new HashMap<>(); | |||
for (Resolution resolution : Resolution | |||
.getResolutionsHigherOrEqualTo(getResolution())) { | |||
for (R resolution : getResolutionsHigherOrEqualTo( | |||
getResolution())) { | |||
// Only handle what the client is allowed to send. The same | |||
// resolutions that are painted | |||
String variableName = variableNameForResolution.get(resolution); | |||
String variableName = getResolutionVariable(resolution); | |||
Integer value = getDateValue(oldDate, resolution); | |||
int value = getDatePart(oldDate, resolution); | |||
if (variables.containsKey(variableName)) { | |||
Integer newValue = (Integer) variables.get(variableName); | |||
if (newValue >= 0) { | |||
@@ -234,15 +248,12 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
if (!hasChanges) { | |||
newDate = null; | |||
} else { | |||
newDate = LocalDate.of(calendarFields.get(Resolution.YEAR), | |||
calendarFields.getOrDefault(Resolution.MONTH, 1), | |||
calendarFields.getOrDefault(Resolution.DAY, 1)); | |||
newDate = buildDate(calendarFields); | |||
} | |||
if (newDate == null && dateString != null | |||
&& !dateString.isEmpty()) { | |||
Result<LocalDate> parsedDate = handleUnparsableDateString( | |||
dateString); | |||
Result<T> parsedDate = handleUnparsableDateString(dateString); | |||
if (parsedDate.isError()) { | |||
/* | |||
@@ -332,8 +343,8 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
* @param startDate | |||
* - the allowed range's start date | |||
*/ | |||
public void setRangeStart(LocalDate startDate) { | |||
Date date = convertLocalDate(startDate); | |||
public void setRangeStart(T startDate) { | |||
Date date = convertToDate(startDate); | |||
if (date != null && getState().rangeEnd != null | |||
&& date.after(getState().rangeEnd)) { | |||
throw new IllegalStateException( | |||
@@ -367,21 +378,21 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
/** | |||
* Gets the resolution. | |||
* | |||
* @return int | |||
* @return the date/time field resolution | |||
*/ | |||
public Resolution getResolution() { | |||
public R getResolution() { | |||
return resolution; | |||
} | |||
/** | |||
* Sets the resolution of the DateField. | |||
* | |||
* The default resolution is {@link Resolution#DAY} since Vaadin 7.0. | |||
* The default resolution is {@link DateResolution#DAY} since Vaadin 7.0. | |||
* | |||
* @param resolution | |||
* the resolution to set. | |||
* the resolution to set, not {@code null} | |||
*/ | |||
public void setResolution(Resolution resolution) { | |||
public void setResolution(R resolution) { | |||
this.resolution = resolution; | |||
markAsDirty(); | |||
} | |||
@@ -396,8 +407,8 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
* - the allowed range's end date (inclusive, based on the | |||
* current resolution) | |||
*/ | |||
public void setRangeEnd(LocalDate endDate) { | |||
Date date = convertLocalDate(endDate); | |||
public void setRangeEnd(T endDate) { | |||
Date date = convertToDate(endDate); | |||
if (date != null && getState().rangeStart != null | |||
&& getState().rangeStart.after(date)) { | |||
throw new IllegalStateException( | |||
@@ -412,8 +423,8 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
* | |||
* @return the precise rangeStart used, may be null. | |||
*/ | |||
public LocalDate getRangeStart() { | |||
return convertDate(getState(false).rangeStart); | |||
public T getRangeStart() { | |||
return convertFromDate(getState(false).rangeStart); | |||
} | |||
/** | |||
@@ -421,8 +432,8 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
* | |||
* @return the precise rangeEnd used, may be null. | |||
*/ | |||
public LocalDate getRangeEnd() { | |||
return convertDate(getState(false).rangeEnd); | |||
public T getRangeEnd() { | |||
return convertFromDate(getState(false).rangeEnd); | |||
} | |||
/** | |||
@@ -482,7 +493,7 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
} | |||
@Override | |||
public LocalDate getValue() { | |||
public T getValue() { | |||
return value; | |||
} | |||
@@ -494,7 +505,7 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
* the new value, may be {@code null} | |||
*/ | |||
@Override | |||
public void setValue(LocalDate value) { | |||
public void setValue(T value) { | |||
/* | |||
* First handle special case when the client side component have a date | |||
* string but value is null (e.g. unparsable date string typed in by the | |||
@@ -580,17 +591,28 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
} | |||
@Override | |||
@SuppressWarnings("unchecked") | |||
public void readDesign(Element design, DesignContext designContext) { | |||
super.readDesign(design, designContext); | |||
if (design.hasAttr("value") && !design.attr("value").isEmpty()) { | |||
LocalDate date = DesignAttributeHandler.getFormatter() | |||
.parse(design.attr("value"), LocalDate.class); | |||
// formatting will return null if it cannot parse the string | |||
if (date == null) { | |||
Logger.getLogger(AbstractDateField.class.getName()).info( | |||
"cannot parse " + design.attr("value") + " as date"); | |||
Type dateType = GenericTypeReflector.getTypeParameter(getClass(), | |||
AbstractDateField.class.getTypeParameters()[0]); | |||
if (dateType instanceof Class<?>) { | |||
Class<?> clazz = (Class<?>) dateType; | |||
T date = (T) DesignAttributeHandler.getFormatter() | |||
.parse(design.attr("value"), clazz); | |||
// formatting will return null if it cannot parse the string | |||
if (date == null) { | |||
Logger.getLogger(AbstractDateField.class.getName()) | |||
.info("cannot parse " + design.attr("value") | |||
+ " as date"); | |||
} | |||
doSetValue(date); | |||
} else { | |||
throw new RuntimeException("Cannot detect resoluton type " | |||
+ Optional.ofNullable(dateType).map(Type::getTypeName) | |||
.orElse(null)); | |||
} | |||
doSetValue(date); | |||
} | |||
} | |||
@@ -629,22 +651,22 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
* date string to handle | |||
* @return result that contains parsed Date as a value or an error | |||
*/ | |||
protected Result<LocalDate> handleUnparsableDateString(String dateString) { | |||
protected Result<T> handleUnparsableDateString(String dateString) { | |||
return Result.error(getParseErrorMessage()); | |||
} | |||
@Override | |||
protected TextualDateFieldState getState() { | |||
return (TextualDateFieldState) super.getState(); | |||
protected AbstractDateFieldState getState() { | |||
return (AbstractDateFieldState) super.getState(); | |||
} | |||
@Override | |||
protected TextualDateFieldState getState(boolean markAsDirty) { | |||
return (TextualDateFieldState) super.getState(markAsDirty); | |||
protected AbstractDateFieldState getState(boolean markAsDirty) { | |||
return (AbstractDateFieldState) super.getState(markAsDirty); | |||
} | |||
@Override | |||
protected void doSetValue(LocalDate value) { | |||
protected void doSetValue(T value) { | |||
// Also set the internal dateString | |||
if (value != null) { | |||
dateString = value.toString(); | |||
@@ -659,10 +681,7 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
uiHasValidDateString = true; | |||
setComponentError(new UserError(currentParseErrorMessage)); | |||
} else { | |||
DateRangeValidator validator = new DateRangeValidator( | |||
getDateOutOfRangeMessage(), | |||
getDate(getRangeStart(), getResolution()), | |||
getDate(getRangeEnd(), getResolution())); | |||
RangeValidator<T> validator = getRangeValidator(); | |||
ValidationResult result = validator.apply(value, | |||
new ValueContext(this)); | |||
if (result.isError()) { | |||
@@ -671,50 +690,80 @@ public abstract class AbstractDateField extends AbstractField<LocalDate> | |||
} | |||
} | |||
private LocalDate getDate(LocalDate date, Resolution forResolution) { | |||
if (date == null) { | |||
return null; | |||
} | |||
if (forResolution == Resolution.YEAR) { | |||
return date.withDayOfYear(1); | |||
} else if (forResolution == Resolution.MONTH) { | |||
return date.withDayOfMonth(1); | |||
} else { | |||
return date; | |||
} | |||
} | |||
/** | |||
* Returns a date integer value part for the given {@code date} for the | |||
* given {@code resolution}. | |||
* | |||
* @param date | |||
* the given date | |||
* @param resolution | |||
* the resolution to extract a value from the date by | |||
* @return the integer value part of the date by the given resolution | |||
*/ | |||
protected abstract int getDatePart(T date, R resolution); | |||
private int getDateValue(LocalDate date, Resolution resolution) { | |||
LocalDate value = date; | |||
if (value == null) { | |||
value = LocalDate.of(1, 1, 1); | |||
} | |||
switch (resolution) { | |||
case DAY: | |||
return value.getDayOfMonth(); | |||
case MONTH: | |||
return value.getMonthValue(); | |||
case YEAR: | |||
return value.getYear(); | |||
default: | |||
assert false : "Unexpected resolution argument " + resolution; | |||
return -1; | |||
} | |||
/** | |||
* Builds date by the given {@code resolutionValues} which is a map whose | |||
* keys are resolution and integer values. | |||
* <p> | |||
* This is the opposite to {@link #getDatePart(Temporal, Enum)}. | |||
* | |||
* @param resolutionValues | |||
* date values to construct a date | |||
* @return date built from the given map of date values | |||
*/ | |||
protected abstract T buildDate(Map<R, Integer> resolutionValues); | |||
/** | |||
* Returns a custom date range validator which is applicable for the type | |||
* {@code T}. | |||
* | |||
* @return the date range validator | |||
*/ | |||
protected abstract RangeValidator<T> getRangeValidator(); | |||
/** | |||
* Converts {@link Date} to date type {@code T}. | |||
* | |||
* @param date | |||
* a date to convert | |||
* @return object of type {@code T} representing the {@code date} | |||
*/ | |||
protected abstract T convertFromDate(Date date); | |||
/** | |||
* Converts the object of type {@code T} to {@link Date}. | |||
* <p> | |||
* This is the opposite to {@link #convertFromDate(Date)}. | |||
* | |||
* @param date | |||
* the date of type {@code T} | |||
* @return converted date of type {@code Date} | |||
*/ | |||
protected abstract Date convertToDate(T date); | |||
private String getResolutionVariable(R resolution) { | |||
return resolution.name().toLowerCase(Locale.ENGLISH); | |||
} | |||
private Date convertLocalDate(LocalDate date) { | |||
if (date == null) { | |||
return null; | |||
@SuppressWarnings("unchecked") | |||
private Stream<R> getResolutions() { | |||
Type resolutionType = GenericTypeReflector.getTypeParameter(getClass(), | |||
AbstractDateField.class.getTypeParameters()[1]); | |||
if (resolutionType instanceof Class<?>) { | |||
Class<?> clazz = (Class<?>) resolutionType; | |||
return Stream.of(clazz.getEnumConstants()) | |||
.map(object -> (R) object); | |||
} else { | |||
throw new RuntimeException("Cannot detect resoluton type " | |||
+ Optional.ofNullable(resolutionType).map(Type::getTypeName) | |||
.orElse(null)); | |||
} | |||
return Date.from(date.atStartOfDay(ZoneOffset.UTC).toInstant()); | |||
} | |||
private LocalDate convertDate(Date date) { | |||
if (date == null) { | |||
return null; | |||
} | |||
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneOffset.UTC) | |||
.toLocalDate(); | |||
private Iterable<R> getResolutionsHigherOrEqualTo(R resoution) { | |||
return getResolutions().skip(resolution.ordinal()) | |||
.collect(Collectors.toList()); | |||
} | |||
} |
@@ -0,0 +1,139 @@ | |||
/* | |||
* 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.ui; | |||
import java.time.Instant; | |||
import java.time.LocalDate; | |||
import java.time.ZoneOffset; | |||
import java.util.Date; | |||
import java.util.Map; | |||
import com.vaadin.data.validator.DateRangeValidator; | |||
import com.vaadin.data.validator.RangeValidator; | |||
import com.vaadin.shared.ui.datefield.AbstractTextualDateFieldState; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
/** | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public abstract class AbstractLocalDateField | |||
extends AbstractDateField<LocalDate, DateResolution> { | |||
/** | |||
* Constructs an empty <code>AbstractLocalDateField</code> with no caption. | |||
*/ | |||
public AbstractLocalDateField() { | |||
super(DateResolution.DAY); | |||
} | |||
/** | |||
* Constructs an empty <code>AbstractLocalDateField</code> with caption. | |||
* | |||
* @param caption | |||
* the caption of the datefield. | |||
*/ | |||
public AbstractLocalDateField(String caption) { | |||
super(caption, DateResolution.DAY); | |||
} | |||
/** | |||
* Constructs a new <code>AbstractLocalDateField</code> with the given | |||
* caption and initial text contents. | |||
* | |||
* @param caption | |||
* the caption <code>String</code> for the editor. | |||
* @param value | |||
* the LocalDate value. | |||
*/ | |||
public AbstractLocalDateField(String caption, LocalDate value) { | |||
super(caption, value, DateResolution.DAY); | |||
} | |||
@Override | |||
protected int getDatePart(LocalDate date, DateResolution resolution) { | |||
LocalDate value = date; | |||
if (value == null) { | |||
value = LocalDate.of(1, 1, 1); | |||
} | |||
switch (resolution) { | |||
case DAY: | |||
return value.getDayOfMonth(); | |||
case MONTH: | |||
return value.getMonthValue(); | |||
case YEAR: | |||
return value.getYear(); | |||
default: | |||
assert false : "Unexpected resolution argument " + resolution; | |||
return -1; | |||
} | |||
} | |||
@Override | |||
protected LocalDate buildDate( | |||
Map<DateResolution, Integer> resolutionValues) { | |||
return LocalDate.of(resolutionValues.get(DateResolution.YEAR), | |||
resolutionValues.getOrDefault(DateResolution.MONTH, 1), | |||
resolutionValues.getOrDefault(DateResolution.DAY, 1)); | |||
} | |||
@Override | |||
protected RangeValidator<LocalDate> getRangeValidator() { | |||
return new DateRangeValidator(getDateOutOfRangeMessage(), | |||
getDate(getRangeStart(), getResolution()), | |||
getDate(getRangeEnd(), getResolution())); | |||
} | |||
@Override | |||
protected AbstractTextualDateFieldState getState() { | |||
return (AbstractTextualDateFieldState) super.getState(); | |||
} | |||
@Override | |||
protected AbstractTextualDateFieldState getState(boolean markAsDirty) { | |||
return (AbstractTextualDateFieldState) super.getState(markAsDirty); | |||
} | |||
@Override | |||
protected LocalDate convertFromDate(Date date) { | |||
if (date == null) { | |||
return null; | |||
} | |||
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneOffset.UTC) | |||
.toLocalDate(); | |||
} | |||
@Override | |||
protected Date convertToDate(LocalDate date) { | |||
if (date == null) { | |||
return null; | |||
} | |||
return Date.from(date.atStartOfDay(ZoneOffset.UTC).toInstant()); | |||
} | |||
private LocalDate getDate(LocalDate date, DateResolution forResolution) { | |||
if (date == null) { | |||
return null; | |||
} | |||
if (forResolution == DateResolution.YEAR) { | |||
return date.withDayOfYear(1); | |||
} else if (forResolution == DateResolution.MONTH) { | |||
return date.withDayOfMonth(1); | |||
} else { | |||
return date; | |||
} | |||
} | |||
} |
@@ -0,0 +1,58 @@ | |||
/* | |||
* 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.ui; | |||
import java.time.LocalDateTime; | |||
import com.vaadin.shared.ui.datefield.DateTimeResolution; | |||
import com.vaadin.shared.ui.datefield.LocalDateTimeFieldState; | |||
/** | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public abstract class AbstractLocalDateTimeField | |||
extends AbstractDateField<LocalDateTime, DateTimeResolution> { | |||
/** | |||
* Constructs an empty <code>AbstractLocalDateTimeField</code> with no | |||
* caption. | |||
*/ | |||
public AbstractLocalDateTimeField() { | |||
super(DateTimeResolution.MINUTE); | |||
} | |||
/** | |||
* Constructs an empty <code>AbstractLocalDateTimeField</code> with caption. | |||
* | |||
* @param caption | |||
* the caption of the datefield. | |||
*/ | |||
public AbstractLocalDateTimeField(String caption) { | |||
super(caption, DateTimeResolution.MINUTE); | |||
} | |||
@Override | |||
protected LocalDateTimeFieldState getState() { | |||
return (LocalDateTimeFieldState) super.getState(); | |||
} | |||
@Override | |||
protected LocalDateTimeFieldState getState(boolean markAsDirty) { | |||
return (LocalDateTimeFieldState) super.getState(markAsDirty); | |||
} | |||
} |
@@ -17,17 +17,17 @@ package com.vaadin.ui; | |||
import java.time.LocalDate; | |||
import com.vaadin.shared.ui.datefield.DateFieldState; | |||
import com.vaadin.shared.ui.datefield.LocalDateFieldState; | |||
/** | |||
* A date entry component, which displays the actual date selector as a popup. | |||
* | |||
* @see AbstractDateField | |||
* @see AbstractLocalDateField | |||
* @see InlineDateField | |||
* @author Vaadin Ltd. | |||
* @since 8.0 | |||
*/ | |||
public class DateField extends AbstractDateField { | |||
public class DateField extends AbstractLocalDateField { | |||
/** | |||
* Constructs an empty <code>DateField</code> with no caption. | |||
@@ -81,13 +81,13 @@ public class DateField extends AbstractDateField { | |||
} | |||
@Override | |||
protected DateFieldState getState() { | |||
return (DateFieldState) super.getState(); | |||
protected LocalDateFieldState getState() { | |||
return (LocalDateFieldState) super.getState(); | |||
} | |||
@Override | |||
protected DateFieldState getState(boolean markAsDirty) { | |||
return (DateFieldState) super.getState(markAsDirty); | |||
protected LocalDateFieldState getState(boolean markAsDirty) { | |||
return (LocalDateFieldState) super.getState(markAsDirty); | |||
} | |||
/** |
@@ -22,12 +22,12 @@ import com.vaadin.shared.ui.datefield.InlineDateFieldState; | |||
/** | |||
* A date entry component, which displays the actual date selector inline. | |||
* | |||
* @see AbstractDateField | |||
* @see AbstractLocalDateField | |||
* @see DateField | |||
* @author Vaadin Ltd. | |||
* @since 8.0 | |||
*/ | |||
public class InlineDateField extends AbstractDateField { | |||
public class InlineDateField extends AbstractLocalDateField { | |||
/** | |||
* Constructs an empty <code>InlineDateField</code> with no caption. |
@@ -21,6 +21,7 @@ import java.beans.Introspector; | |||
import java.beans.PropertyDescriptor; | |||
import java.io.Serializable; | |||
import java.lang.reflect.Method; | |||
import java.lang.reflect.Type; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.List; | |||
@@ -36,6 +37,7 @@ import org.jsoup.nodes.Attributes; | |||
import org.jsoup.nodes.Element; | |||
import org.jsoup.nodes.Node; | |||
import com.googlecode.gentyref.GenericTypeReflector; | |||
import com.vaadin.data.Converter; | |||
import com.vaadin.data.ValueContext; | |||
import com.vaadin.shared.ui.AlignmentInfo; | |||
@@ -115,8 +117,9 @@ public class DesignAttributeHandler implements Serializable { | |||
success = false; | |||
} else { | |||
// we have a value from design attributes, let's use that | |||
Object param = getFormatter().parse(value, | |||
setter.getParameterTypes()[0]); | |||
Type[] types = GenericTypeReflector | |||
.getExactParameterTypes(setter, target.getClass()); | |||
Object param = getFormatter().parse(value, (Class<?>) types[0]); | |||
setter.invoke(target, param); | |||
success = true; | |||
} | |||
@@ -208,7 +211,9 @@ public class DesignAttributeHandler implements Serializable { | |||
Object value = getter.invoke(component); | |||
Object defaultValue = getter.invoke(defaultInstance); | |||
writeAttribute(attribute, attr, value, defaultValue, | |||
(Class) getter.getReturnType(), context); | |||
(Class) GenericTypeReflector.getExactReturnType(getter, | |||
component.getClass()), | |||
context); | |||
} catch (Exception e) { | |||
getLogger().log(Level.SEVERE, | |||
"Failed to invoke getter for attribute " + attribute, |
@@ -20,9 +20,9 @@ import java.util.Locale; | |||
import org.junit.Test; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.tests.server.component.abstractfield.AbstractFieldDeclarativeTest; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
/** | |||
* Abstract test class which contains tests for declarative format for | |||
@@ -36,7 +36,7 @@ import com.vaadin.ui.AbstractDateField; | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public abstract class AbstractDateFieldDeclarativeTest<T extends AbstractDateField> | |||
public abstract class AbstractLocalDateFieldDeclarativeTest<T extends AbstractLocalDateField> | |||
extends AbstractFieldDeclarativeTest<T, LocalDate> { | |||
@Override | |||
@@ -60,7 +60,7 @@ public abstract class AbstractDateFieldDeclarativeTest<T extends AbstractDateFie | |||
LocalDate end = LocalDate.of(2019, 01, 15); | |||
LocalDate start = LocalDate.of(2001, 02, 11); | |||
String dateOutOfRange = "test date out of range"; | |||
Resolution resolution = Resolution.MONTH; | |||
DateResolution resolution = DateResolution.MONTH; | |||
String dateFormat = "test format"; | |||
boolean lenient = true; | |||
String parseErrorMsg = "test parse error"; |
@@ -17,7 +17,7 @@ package com.vaadin.tests.server.component.datefield; | |||
import org.junit.Test; | |||
import com.vaadin.tests.server.component.abstractdatefield.AbstractDateFieldDeclarativeTest; | |||
import com.vaadin.tests.server.component.abstractdatefield.AbstractLocalDateFieldDeclarativeTest; | |||
import com.vaadin.ui.DateField; | |||
/** | |||
@@ -27,7 +27,7 @@ import com.vaadin.ui.DateField; | |||
* @author Vaadin Ltd | |||
*/ | |||
public class DateFieldDeclarativeTest | |||
extends AbstractDateFieldDeclarativeTest<DateField> { | |||
extends AbstractLocalDateFieldDeclarativeTest<DateField> { | |||
@Test | |||
public void remainingAttributes() |
@@ -1,7 +1,14 @@ | |||
package com.vaadin.tests.server.component.datefield; | |||
import java.io.Serializable; | |||
import java.time.temporal.Temporal; | |||
import java.time.temporal.TemporalAdjuster; | |||
import java.util.Date; | |||
import java.util.Map; | |||
import org.junit.Test; | |||
import com.vaadin.data.validator.RangeValidator; | |||
import com.vaadin.event.FieldEvents.BlurEvent; | |||
import com.vaadin.event.FieldEvents.BlurListener; | |||
import com.vaadin.event.FieldEvents.FocusEvent; | |||
@@ -11,7 +18,37 @@ import com.vaadin.ui.AbstractDateField; | |||
public class DateFieldListenersTest extends AbstractListenerMethodsTestBase { | |||
public static class TestDateField extends AbstractDateField { | |||
public static class TestDateField<T extends Temporal & TemporalAdjuster & Serializable & Comparable<? super T>, R extends Enum<R>> | |||
extends AbstractDateField<T, R> { | |||
public TestDateField() { | |||
super(null); | |||
} | |||
@Override | |||
protected int getDatePart(T date, R resolution) { | |||
return 0; | |||
} | |||
@Override | |||
protected T buildDate(Map<R, Integer> resolutionValues) { | |||
return null; | |||
} | |||
@Override | |||
protected RangeValidator<T> getRangeValidator() { | |||
return null; | |||
} | |||
@Override | |||
protected T convertFromDate(Date date) { | |||
return null; | |||
} | |||
@Override | |||
protected Date convertToDate(T date) { | |||
return null; | |||
} | |||
} | |||
@@ -21,7 +21,7 @@ import java.time.LocalDate; | |||
import org.junit.Test; | |||
import com.vaadin.tests.server.component.abstractdatefield.AbstractDateFieldDeclarativeTest; | |||
import com.vaadin.tests.server.component.abstractdatefield.AbstractLocalDateFieldDeclarativeTest; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.InlineDateField; | |||
import com.vaadin.ui.declarative.Design; | |||
@@ -34,7 +34,7 @@ import com.vaadin.ui.declarative.Design; | |||
* @author Vaadin Ltd | |||
*/ | |||
public class InlineDateFieldDeclarativeTest | |||
extends AbstractDateFieldDeclarativeTest<InlineDateField> { | |||
extends AbstractLocalDateFieldDeclarativeTest<InlineDateField> { | |||
@Test | |||
public void testInlineDateFieldToFromDesign() throws Exception { |
@@ -4,39 +4,39 @@ import java.util.ArrayList; | |||
import org.junit.Test; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.tests.util.TestUtil; | |||
public class ResolutionTest { | |||
@Test | |||
public void testResolutionHigherOrEqualToYear() { | |||
Iterable<Resolution> higherOrEqual = Resolution | |||
.getResolutionsHigherOrEqualTo(Resolution.YEAR); | |||
ArrayList<Resolution> expected = new ArrayList<>(); | |||
expected.add(Resolution.YEAR); | |||
Iterable<DateResolution> higherOrEqual = DateResolution | |||
.getResolutionsHigherOrEqualTo(DateResolution.YEAR); | |||
ArrayList<DateResolution> expected = new ArrayList<>(); | |||
expected.add(DateResolution.YEAR); | |||
TestUtil.assertIterableEquals(expected, higherOrEqual); | |||
} | |||
@Test | |||
public void testResolutionHigherOrEqualToDay() { | |||
Iterable<Resolution> higherOrEqual = Resolution | |||
.getResolutionsHigherOrEqualTo(Resolution.DAY); | |||
ArrayList<Resolution> expected = new ArrayList<>(); | |||
expected.add(Resolution.DAY); | |||
expected.add(Resolution.MONTH); | |||
expected.add(Resolution.YEAR); | |||
Iterable<DateResolution> higherOrEqual = DateResolution | |||
.getResolutionsHigherOrEqualTo(DateResolution.DAY); | |||
ArrayList<DateResolution> expected = new ArrayList<>(); | |||
expected.add(DateResolution.DAY); | |||
expected.add(DateResolution.MONTH); | |||
expected.add(DateResolution.YEAR); | |||
TestUtil.assertIterableEquals(expected, higherOrEqual); | |||
} | |||
@Test | |||
public void testResolutionLowerThanYear() { | |||
Iterable<Resolution> higherOrEqual = Resolution | |||
.getResolutionsLowerThan(Resolution.YEAR); | |||
ArrayList<Resolution> expected = new ArrayList<>(); | |||
expected.add(Resolution.MONTH); | |||
expected.add(Resolution.DAY); | |||
Iterable<DateResolution> higherOrEqual = DateResolution | |||
.getResolutionsLowerThan(DateResolution.YEAR); | |||
ArrayList<DateResolution> expected = new ArrayList<>(); | |||
expected.add(DateResolution.MONTH); | |||
expected.add(DateResolution.DAY); | |||
TestUtil.assertIterableEquals(expected, higherOrEqual); | |||
} |
@@ -12,12 +12,12 @@ import org.junit.Test; | |||
public class DateFieldTestCase { | |||
private AbstractDateField dateField; | |||
private AbstractLocalDateField dateField; | |||
private LocalDate date; | |||
@Before | |||
public void setup() { | |||
dateField = new AbstractDateField() { | |||
dateField = new AbstractLocalDateField() { | |||
}; | |||
date = LocalDate.now(); | |||
} |
@@ -15,20 +15,34 @@ | |||
*/ | |||
package com.vaadin.shared.ui.datefield; | |||
import com.vaadin.shared.annotations.DelegateToWidget; | |||
import java.util.Date; | |||
import com.vaadin.shared.AbstractFieldState; | |||
import com.vaadin.shared.annotations.NoLayout; | |||
public class DateFieldState extends TextualDateFieldState { | |||
public static final String DESCRIPTION_FOR_ASSISTIVE_DEVICES = "Arrow down key opens calendar element for choosing the date"; | |||
/** | |||
* Shared state for the AbstractDateField component. | |||
* | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public class AbstractDateFieldState extends AbstractFieldState { | |||
{ | |||
primaryStyleName = "v-datefield"; | |||
} | |||
public boolean textFieldEnabled = true; | |||
/* | |||
* Start range that has been cleared, depending on the resolution of the | |||
* date field | |||
*/ | |||
@NoLayout | |||
public String descriptionForAssistiveDevices = DESCRIPTION_FOR_ASSISTIVE_DEVICES; | |||
public Date rangeStart = null; | |||
/* | |||
* End range that has been cleared, depending on the resolution of the date | |||
* field | |||
*/ | |||
@NoLayout | |||
@DelegateToWidget | |||
public String placeholder = null; | |||
public Date rangeEnd = null; | |||
} |
@@ -0,0 +1,26 @@ | |||
/* | |||
* 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.shared.ui.datefield; | |||
/** | |||
* Shared state for the AbstractLocalDateField component. | |||
* | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public class AbstractTextualDateFieldState extends AbstractDateFieldState { | |||
} |
@@ -24,7 +24,7 @@ import java.util.List; | |||
* @author Vaadin Ltd. | |||
* @since 7.0 | |||
*/ | |||
public enum Resolution { | |||
public enum DateResolution { | |||
DAY, MONTH, YEAR; | |||
/** | |||
@@ -36,10 +36,10 @@ public enum Resolution { | |||
* The resolution to start from | |||
* @return An iterable for the resolutions higher or equal to r | |||
*/ | |||
public static Iterable<Resolution> getResolutionsHigherOrEqualTo( | |||
Resolution r) { | |||
List<Resolution> resolutions = new ArrayList<>(); | |||
Resolution[] values = Resolution.values(); | |||
public static Iterable<DateResolution> getResolutionsHigherOrEqualTo( | |||
DateResolution r) { | |||
List<DateResolution> resolutions = new ArrayList<>(); | |||
DateResolution[] values = DateResolution.values(); | |||
for (int i = r.ordinal(); i < values.length; i++) { | |||
resolutions.add(values[i]); | |||
} | |||
@@ -55,9 +55,10 @@ public enum Resolution { | |||
* The resolution to start from | |||
* @return An iterable for the resolutions lower than r | |||
*/ | |||
public static List<Resolution> getResolutionsLowerThan(Resolution r) { | |||
List<Resolution> resolutions = new ArrayList<>(); | |||
Resolution[] values = Resolution.values(); | |||
public static List<DateResolution> getResolutionsLowerThan( | |||
DateResolution r) { | |||
List<DateResolution> resolutions = new ArrayList<>(); | |||
DateResolution[] values = DateResolution.values(); | |||
for (int i = r.ordinal() - 1; i >= 0; i--) { | |||
resolutions.add(values[i]); | |||
} |
@@ -0,0 +1,27 @@ | |||
/* | |||
* 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.shared.ui.datefield; | |||
/** | |||
* Resolutions for DateTimeFields | |||
* | |||
* @author Vaadin Ltd. | |||
* @since 8.0 | |||
*/ | |||
public enum DateTimeResolution { | |||
SECOND, MINUTE, HOUR, DAY, MONTH, YEAR; | |||
} |
@@ -15,7 +15,7 @@ | |||
*/ | |||
package com.vaadin.shared.ui.datefield; | |||
public class InlineDateFieldState extends TextualDateFieldState { | |||
public class InlineDateFieldState extends AbstractTextualDateFieldState { | |||
{ | |||
primaryStyleName = "v-inline-datefield"; | |||
} |
@@ -0,0 +1,26 @@ | |||
/* | |||
* 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.shared.ui.datefield; | |||
/** | |||
* Shared state for the DateField component. | |||
* | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public class LocalDateFieldState extends TextualDateFieldState { | |||
} |
@@ -0,0 +1,26 @@ | |||
/* | |||
* 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.shared.ui.datefield; | |||
/** | |||
* Shared state for the DateTimeField component. | |||
* | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public class LocalDateTimeFieldState extends AbstractTextualDateFieldState { | |||
} |
@@ -15,27 +15,20 @@ | |||
*/ | |||
package com.vaadin.shared.ui.datefield; | |||
import java.util.Date; | |||
import com.vaadin.shared.AbstractFieldState; | |||
import com.vaadin.shared.annotations.DelegateToWidget; | |||
import com.vaadin.shared.annotations.NoLayout; | |||
public class TextualDateFieldState extends AbstractFieldState { | |||
public class TextualDateFieldState extends AbstractTextualDateFieldState { | |||
public static final String DESCRIPTION_FOR_ASSISTIVE_DEVICES = "Arrow down key opens calendar element for choosing the date"; | |||
{ | |||
primaryStyleName = "v-datefield"; | |||
} | |||
/* | |||
* Start range that has been cleared, depending on the resolution of the | |||
* date field | |||
*/ | |||
public boolean textFieldEnabled = true; | |||
@NoLayout | |||
public Date rangeStart = null; | |||
/* | |||
* End range that has been cleared, depending on the resolution of the date | |||
* field | |||
*/ | |||
public String descriptionForAssistiveDevices = DESCRIPTION_FOR_ASSISTIVE_DEVICES; | |||
@NoLayout | |||
public Date rangeEnd = null; | |||
@DelegateToWidget | |||
public String placeholder = null; | |||
} |
@@ -19,7 +19,7 @@ package com.vaadin.tests; | |||
import java.util.Locale; | |||
import com.vaadin.server.Sizeable; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.Button; | |||
@@ -83,7 +83,7 @@ public class TestForBasicApplicationLayout extends CustomComponent { | |||
controls.addComponent(click2); | |||
reportLayout.addComponent(controls); | |||
final AbstractDateField cal = new TestDateField(); | |||
cal.setResolution(Resolution.DAY); | |||
cal.setResolution(DateResolution.DAY); | |||
cal.setLocale(new Locale("en", "US")); | |||
reportLayout.addComponent(cal); | |||
reportLayout.setExpandRatio(controls, 1); |
@@ -17,13 +17,13 @@ package com.vaadin.tests.components; | |||
import java.time.LocalDate; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
/** | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public class TestDateField extends AbstractDateField { | |||
public class TestDateField extends AbstractLocalDateField { | |||
/** | |||
* Constructs an empty <code>DateField</code> with no caption. |
@@ -6,12 +6,12 @@ import java.time.LocalDate; | |||
import java.util.LinkedHashMap; | |||
import java.util.Locale; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.tests.components.abstractfield.AbstractFieldTest; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
public class AbstractDateFieldTest<T extends AbstractDateField> | |||
public class AbstractDateFieldTest<T extends AbstractLocalDateField> | |||
extends AbstractFieldTest<T, LocalDate> { | |||
@SuppressWarnings("unchecked") | |||
@@ -90,19 +90,19 @@ public class AbstractDateFieldTest<T extends AbstractDateField> | |||
} | |||
private void createResolutionSelectAction(String category) { | |||
LinkedHashMap<String, Resolution> options = new LinkedHashMap<>(); | |||
options.put("Year", Resolution.YEAR); | |||
options.put("Month", Resolution.MONTH); | |||
options.put("Day", Resolution.DAY); | |||
LinkedHashMap<String, DateResolution> options = new LinkedHashMap<>(); | |||
options.put("Year", DateResolution.YEAR); | |||
options.put("Month", DateResolution.MONTH); | |||
options.put("Day", DateResolution.DAY); | |||
createSelectAction("Resolution", category, options, "Year", | |||
resolutionCommand); | |||
} | |||
private Command<T, Resolution> resolutionCommand = new Command<T, Resolution>() { | |||
private Command<T, DateResolution> resolutionCommand = new Command<T, DateResolution>() { | |||
@Override | |||
public void execute(T c, Resolution value, Object data) { | |||
public void execute(T c, DateResolution value, Object data) { | |||
c.setResolution(value); | |||
} |
@@ -18,7 +18,7 @@ package com.vaadin.tests.components.datefield; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
import com.vaadin.ui.VerticalLayout; | |||
public class AriaDisabled extends AbstractReindeerTestUI { | |||
@@ -29,7 +29,7 @@ public class AriaDisabled extends AbstractReindeerTestUI { | |||
content.setMargin(true); | |||
content.setSpacing(true); | |||
final AbstractDateField disabledDateField = new TestDateField( | |||
final AbstractLocalDateField disabledDateField = new TestDateField( | |||
"Disabled DateField"); | |||
disabledDateField.setEnabled(false); | |||
@@ -4,18 +4,18 @@ import java.time.LocalDate; | |||
import java.util.Locale; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.tests.components.AbstractTestUI; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
public class CustomDateFormat extends AbstractTestUI { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
Locale locale = new Locale("fi", "FI"); | |||
AbstractDateField df = new TestDateField(); | |||
df.setResolution(Resolution.DAY); | |||
AbstractLocalDateField df = new TestDateField(); | |||
df.setResolution(DateResolution.DAY); | |||
df.setLocale(locale); | |||
df.setWidth("300px"); | |||
@@ -19,19 +19,19 @@ import java.time.LocalDate; | |||
import java.util.Locale; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
import com.vaadin.ui.VerticalLayout; | |||
public class CustomDateFormatEEE extends AbstractReindeerTestUI { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
AbstractDateField df = new TestDateField( | |||
AbstractLocalDateField df = new TestDateField( | |||
"Should display 14/03/2014 Fri"); | |||
df.setResolution(Resolution.DAY); | |||
df.setResolution(DateResolution.DAY); | |||
df.setLocale(new Locale("en", "US")); | |||
String pattern = "dd/MM/yyyy EEE"; |
@@ -16,9 +16,9 @@ | |||
package com.vaadin.tests.components.datefield; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.DateField; | |||
import com.vaadin.ui.HorizontalLayout; | |||
@@ -32,8 +32,8 @@ public class DateFieldChangeResolution extends AbstractReindeerTestUI { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
final AbstractDateField dateField = new DateField("Enter date"); | |||
dateField.setResolution(Resolution.YEAR); | |||
final AbstractLocalDateField dateField = new DateField("Enter date"); | |||
dateField.setResolution(DateResolution.YEAR); | |||
dateField.setId(DATEFIELD_ID); | |||
addComponent(dateField); | |||
@@ -41,7 +41,7 @@ public class DateFieldChangeResolution extends AbstractReindeerTestUI { | |||
addComponent(l); | |||
HorizontalLayout hlayout = new HorizontalLayout(); | |||
addComponent(hlayout); | |||
for (final Resolution value : Resolution.values()) { | |||
for (final DateResolution value : DateResolution.values()) { | |||
String resolutionString = value.toString().toLowerCase(); | |||
Button button = new Button(resolutionString); | |||
button.addClickListener(event -> dateField.setResolution(value)); |
@@ -18,7 +18,7 @@ package com.vaadin.tests.components.datefield; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
public class DateFieldClose extends AbstractReindeerTestUI { | |||
@@ -26,7 +26,7 @@ public class DateFieldClose extends AbstractReindeerTestUI { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
final AbstractDateField df = new TestDateField(); | |||
final AbstractLocalDateField df = new TestDateField(); | |||
df.setId(DATEFIELD_ID); | |||
addComponent(df); | |||
} |
@@ -4,7 +4,7 @@ import java.time.LocalDate; | |||
import java.time.format.DateTimeFormatter; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.ui.AbstractDateField; | |||
@@ -20,7 +20,8 @@ public class DateFieldDayResolutionOffset extends AbstractReindeerTestUI { | |||
dateValue.setId("dateValue"); | |||
final DateTimeFormatter dateformat = getDateFormat(); | |||
final AbstractDateField dateField = getDateField(dateformat); | |||
final AbstractDateField<LocalDate, DateResolution> dateField = getDateField( | |||
dateformat); | |||
addComponent(dateValue); | |||
addComponent(dateField); | |||
@@ -29,11 +30,12 @@ public class DateFieldDayResolutionOffset extends AbstractReindeerTestUI { | |||
.setValue(dateformat.format(dateField.getValue()))); | |||
} | |||
private AbstractDateField getDateField(DateTimeFormatter dateformat) { | |||
final AbstractDateField dateField = new TestDateField(); | |||
private AbstractDateField<LocalDate, DateResolution> getDateField( | |||
DateTimeFormatter dateformat) { | |||
final AbstractDateField<LocalDate, DateResolution> dateField = new TestDateField(); | |||
LocalDate initialDate = dateformat.parse(initialDateString, | |||
LocalDate::from); | |||
dateField.setResolution(Resolution.DAY); | |||
dateField.setResolution(DateResolution.DAY); | |||
dateField.setValue(initialDate); | |||
return dateField; | |||
} |
@@ -6,7 +6,7 @@ import java.time.format.DateTimeFormatter; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractTestUIWithLog; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
public class DateFieldIsValid extends AbstractTestUIWithLog { | |||
@@ -26,7 +26,8 @@ public class DateFieldIsValid extends AbstractTestUIWithLog { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
final AbstractDateField dateField = new TestDateField("Insert Date: "); | |||
final AbstractLocalDateField dateField = new TestDateField( | |||
"Insert Date: "); | |||
dateField.setDateFormat(pattern); | |||
dateField.addValueChangeListener(event -> log("valueChange: value: " |
@@ -18,7 +18,7 @@ package com.vaadin.tests.components.datefield; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
public class DateFieldPopupClosing extends AbstractReindeerTestUI { | |||
@@ -26,7 +26,7 @@ public class DateFieldPopupClosing extends AbstractReindeerTestUI { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
final AbstractDateField df = new TestDateField(); | |||
final AbstractLocalDateField df = new TestDateField(); | |||
df.setId(DATEFIELD_ID); | |||
addComponent(df); | |||
} |
@@ -21,7 +21,7 @@ import java.util.TimerTask; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
public class DateFieldPopupClosingOnDetach extends AbstractReindeerTestUI { | |||
@@ -30,7 +30,7 @@ public class DateFieldPopupClosingOnDetach extends AbstractReindeerTestUI { | |||
// Use polling to notice the removal of DateField. | |||
getUI().setPollInterval(500); | |||
final AbstractDateField df = new TestDateField(); | |||
final AbstractLocalDateField df = new TestDateField(); | |||
getLayout().addLayoutClickListener(event -> { | |||
// Use a background Thread to remove the DateField 1 second | |||
// after being clicked. |
@@ -6,7 +6,7 @@ import java.util.Locale; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
import com.vaadin.ui.Button; | |||
public class DateFieldReadOnly extends AbstractReindeerTestUI { | |||
@@ -23,7 +23,7 @@ public class DateFieldReadOnly extends AbstractReindeerTestUI { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
final AbstractDateField timeField = new TestDateField( | |||
final AbstractLocalDateField timeField = new TestDateField( | |||
"A read-only datefield"); | |||
timeField.setCaption(null); | |||
timeField.setIcon(null); |
@@ -5,7 +5,7 @@ import java.time.LocalDate; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
import com.vaadin.ui.CheckBox; | |||
import com.vaadin.ui.DateField; | |||
import com.vaadin.ui.VerticalLayout; | |||
@@ -22,8 +22,8 @@ public class DateFieldWhenChangingValueAndEnablingParent | |||
main.setMargin(true); | |||
setContent(main); | |||
final AbstractDateField df1 = createDateField(true); | |||
final AbstractDateField df2 = createDateField(false); | |||
final AbstractLocalDateField df1 = createDateField(true); | |||
final AbstractLocalDateField df2 = createDateField(false); | |||
final DateField pdf1 = createPopupDateField(true, true); | |||
final DateField pdf2 = createPopupDateField(true, false); | |||
final DateField pdf3 = createPopupDateField(false, true); | |||
@@ -50,8 +50,8 @@ public class DateFieldWhenChangingValueAndEnablingParent | |||
}); | |||
} | |||
private AbstractDateField createDateField(boolean enabled) { | |||
AbstractDateField df = new TestDateField( | |||
private AbstractLocalDateField createDateField(boolean enabled) { | |||
AbstractLocalDateField df = new TestDateField( | |||
"DateField, " + (enabled ? "enabled" : "disabled")); | |||
df.setEnabled(enabled); | |||
df.setId("DATEFIELD_" + (enabled ? "ENABLED" : "DISABLED")); |
@@ -5,7 +5,7 @@ import java.util.LinkedHashMap; | |||
import java.util.List; | |||
import java.util.Locale; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.tests.components.ComponentTestCase; | |||
import com.vaadin.ui.Component; | |||
import com.vaadin.ui.DateField; | |||
@@ -46,7 +46,7 @@ public class DateFields extends ComponentTestCase<DateField> { | |||
pd.setWidth(width); | |||
pd.setValue(LocalDate.of(1970, 05, 23)); | |||
pd.setLocale(locale); | |||
pd.setResolution(Resolution.YEAR); | |||
pd.setResolution(DateResolution.YEAR); | |||
return pd; | |||
} | |||
@@ -65,10 +65,10 @@ public class DateFields extends ComponentTestCase<DateField> { | |||
} | |||
private Component createResolutionSelectAction() { | |||
LinkedHashMap<String, Resolution> options = new LinkedHashMap<>(); | |||
options.put("Year", Resolution.YEAR); | |||
options.put("Month", Resolution.MONTH); | |||
options.put("Day", Resolution.DAY); | |||
LinkedHashMap<String, DateResolution> options = new LinkedHashMap<>(); | |||
options.put("Year", DateResolution.YEAR); | |||
options.put("Month", DateResolution.MONTH); | |||
options.put("Day", DateResolution.DAY); | |||
return createSelectAction("Resolution", options, "Year", | |||
(field, value, data) -> field.setResolution(value)); | |||
} |
@@ -18,13 +18,13 @@ package com.vaadin.tests.components.datefield; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
public class DisabledDateFieldPopup extends AbstractReindeerTestUI { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
AbstractDateField field = new TestDateField(); | |||
AbstractLocalDateField field = new TestDateField(); | |||
field.setEnabled(false); | |||
addComponent(field); | |||
} |
@@ -19,14 +19,14 @@ import java.time.LocalDate; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
import com.vaadin.ui.InlineDateField; | |||
public class DisabledInlineDateField extends AbstractReindeerTestUI { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
AbstractDateField df = new InlineDateField("Disabled"); | |||
AbstractLocalDateField df = new InlineDateField("Disabled"); | |||
LocalDate date = LocalDate.of(2014, 6, 5); | |||
df.setValue(date); | |||
df.setEnabled(false); |
@@ -18,7 +18,7 @@ package com.vaadin.tests.components.datefield; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.VerticalLayout; | |||
@@ -36,18 +36,13 @@ public class DisabledParentLayout extends AbstractReindeerTestUI { | |||
content.setMargin(true); | |||
final VerticalLayout pane = new VerticalLayout(); | |||
AbstractDateField dateField = new TestDateField(); | |||
AbstractLocalDateField dateField = new TestDateField(); | |||
pane.addComponent(dateField); | |||
content.addComponent(pane); | |||
Button button = new Button("Test"); | |||
button.addClickListener(new Button.ClickListener() { | |||
@Override | |||
public void buttonClick(Button.ClickEvent event) { | |||
pane.setEnabled(!pane.isEnabled()); | |||
} | |||
}); | |||
button.addClickListener(event -> pane.setEnabled(!pane.isEnabled())); | |||
content.addComponent(button); | |||
addComponent(content); |
@@ -5,7 +5,7 @@ import java.util.LinkedHashMap; | |||
import java.util.List; | |||
import java.util.Locale; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.tests.components.ComponentTestCase; | |||
import com.vaadin.ui.Component; | |||
import com.vaadin.ui.InlineDateField; | |||
@@ -51,7 +51,7 @@ public class InlineDateFields extends ComponentTestCase<InlineDateField> { | |||
pd.setWidth(width); | |||
pd.setValue(LocalDate.of(1970, 05, 23)); | |||
pd.setLocale(locale); | |||
pd.setResolution(Resolution.YEAR); | |||
pd.setResolution(DateResolution.YEAR); | |||
return pd; | |||
} | |||
@@ -70,10 +70,10 @@ public class InlineDateFields extends ComponentTestCase<InlineDateField> { | |||
} | |||
private Component createResolutionSelectAction() { | |||
LinkedHashMap<String, Resolution> options = new LinkedHashMap<>(); | |||
options.put("Year", Resolution.YEAR); | |||
options.put("Month", Resolution.MONTH); | |||
options.put("Day", Resolution.DAY); | |||
LinkedHashMap<String, DateResolution> options = new LinkedHashMap<>(); | |||
options.put("Year", DateResolution.YEAR); | |||
options.put("Month", DateResolution.MONTH); | |||
options.put("Day", DateResolution.DAY); | |||
return createSelectAction("Resolution", options, "Year", | |||
(field, value, data) -> field.setResolution(value)); | |||
} |
@@ -16,27 +16,27 @@ | |||
package com.vaadin.tests.components.datefield; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.AbstractLocalDateField; | |||
import com.vaadin.ui.VerticalLayout; | |||
public class PopupClosingWithEsc extends AbstractReindeerTestUI { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
AbstractDateField df1 = new TestDateField("Day"); | |||
AbstractLocalDateField df1 = new TestDateField("Day"); | |||
df1.setId("day"); | |||
df1.setResolution(Resolution.DAY); | |||
df1.setResolution(DateResolution.DAY); | |||
AbstractDateField df2 = new TestDateField("Month"); | |||
AbstractLocalDateField df2 = new TestDateField("Month"); | |||
df2.setId("month"); | |||
df2.setResolution(Resolution.MONTH); | |||
df2.setResolution(DateResolution.MONTH); | |||
AbstractDateField df3 = new TestDateField("Year"); | |||
AbstractLocalDateField df3 = new TestDateField("Year"); | |||
df3.setId("year"); | |||
df3.setResolution(Resolution.YEAR); | |||
df3.setResolution(DateResolution.YEAR); | |||
VerticalLayout layout = new VerticalLayout(); | |||
layout.setMargin(true); |
@@ -5,7 +5,7 @@ import java.util.Locale; | |||
import java.util.stream.Stream; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.DateField; | |||
@@ -52,7 +52,7 @@ public class PopupDateFieldExtendedRange extends AbstractReindeerTestUI { | |||
private DateField makeDateField() { | |||
DateField pdf = new DateField(); | |||
pdf.setResolution(Resolution.DAY); | |||
pdf.setResolution(DateResolution.DAY); | |||
pdf.setValue(LocalDate.of(2011, 1, 1)); | |||
return pdf; | |||
} |
@@ -1,7 +1,7 @@ | |||
package com.vaadin.tests.components.gridlayout; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.GridLayout; | |||
@@ -32,7 +32,7 @@ public class GridLayoutCellSizesUI extends AbstractReindeerTestUI { | |||
grid.addComponent(new Button("3x1 button"), 1, 1, 3, 1); | |||
grid.addComponent(new Label("1x2 cell"), 1, 2, 1, 3); | |||
final InlineDateField date = new InlineDateField("A 2x2 date field"); | |||
date.setResolution(Resolution.DAY); | |||
date.setResolution(DateResolution.DAY); | |||
grid.addComponent(date, 2, 2, 3, 3); | |||
grid.setMargin(true); |
@@ -22,7 +22,7 @@ import java.util.Locale; | |||
import com.vaadin.navigator.View; | |||
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; | |||
import com.vaadin.server.UserError; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.tests.components.TestDateField; | |||
import com.vaadin.ui.AbstractDateField; | |||
import com.vaadin.ui.Button; | |||
@@ -90,58 +90,58 @@ public class DateFields extends VerticalLayout implements View { | |||
date = new TestDateField("Day resolution"); | |||
setDate(date); | |||
date.setResolution(Resolution.DAY); | |||
date.setResolution(DateResolution.DAY); | |||
row.addComponent(date); | |||
date = new TestDateField("Month resolution"); | |||
setDate(date); | |||
date.setResolution(Resolution.MONTH); | |||
date.setResolution(DateResolution.MONTH); | |||
row.addComponent(date); | |||
date = new TestDateField("Year resolution"); | |||
setDate(date); | |||
date.setResolution(Resolution.YEAR); | |||
date.setResolution(DateResolution.YEAR); | |||
row.addComponent(date); | |||
date = new TestDateField("Custom color"); | |||
setDate(date); | |||
date.setResolution(Resolution.DAY); | |||
date.setResolution(DateResolution.DAY); | |||
date.addStyleName("color1"); | |||
row.addComponent(date); | |||
date = new TestDateField("Custom color"); | |||
setDate(date); | |||
date.setResolution(Resolution.DAY); | |||
date.setResolution(DateResolution.DAY); | |||
date.addStyleName("color2"); | |||
row.addComponent(date); | |||
date = new TestDateField("Custom color"); | |||
setDate(date); | |||
date.setResolution(Resolution.DAY); | |||
date.setResolution(DateResolution.DAY); | |||
date.addStyleName("color3"); | |||
row.addComponent(date); | |||
date = new TestDateField("Small"); | |||
setDate(date); | |||
date.setResolution(Resolution.DAY); | |||
date.setResolution(DateResolution.DAY); | |||
date.addStyleName(ValoTheme.DATEFIELD_SMALL); | |||
row.addComponent(date); | |||
date = new TestDateField("Large"); | |||
setDate(date); | |||
date.setResolution(Resolution.DAY); | |||
date.setResolution(DateResolution.DAY); | |||
date.addStyleName(ValoTheme.DATEFIELD_LARGE); | |||
row.addComponent(date); | |||
date = new TestDateField("Borderless"); | |||
setDate(date); | |||
date.setResolution(Resolution.DAY); | |||
date.setResolution(DateResolution.DAY); | |||
date.addStyleName(ValoTheme.DATEFIELD_BORDERLESS); | |||
row.addComponent(date); | |||
date = new TestDateField("Week numbers"); | |||
setDate(date); | |||
date.setResolution(Resolution.DAY); | |||
date.setResolution(DateResolution.DAY); | |||
date.setLocale(new Locale("fi", "fi")); | |||
date.setShowISOWeekNumbers(true); | |||
row.addComponent(date); | |||
@@ -153,13 +153,13 @@ public class DateFields extends VerticalLayout implements View { | |||
date = new TestDateField("Tiny"); | |||
setDate(date); | |||
date.setResolution(Resolution.DAY); | |||
date.setResolution(DateResolution.DAY); | |||
date.addStyleName(ValoTheme.DATEFIELD_TINY); | |||
row.addComponent(date); | |||
date = new TestDateField("Huge"); | |||
setDate(date); | |||
date.setResolution(Resolution.DAY); | |||
date.setResolution(DateResolution.DAY); | |||
date.addStyleName(ValoTheme.DATEFIELD_HUGE); | |||
row.addComponent(date); | |||
@@ -24,7 +24,7 @@ import org.junit.Test; | |||
import org.openqa.selenium.Keys; | |||
import org.openqa.selenium.WebElement; | |||
import com.vaadin.shared.ui.datefield.Resolution; | |||
import com.vaadin.shared.ui.datefield.DateResolution; | |||
import com.vaadin.testbench.By; | |||
import com.vaadin.tests.tb3.MultiBrowserTest; | |||
@@ -37,19 +37,19 @@ public class DateFieldChangeResolutionTest extends MultiBrowserTest { | |||
public void changeResolutionBetweenYearAndMonth() throws Exception { | |||
initialize(); | |||
click(resolutionMonth); | |||
checkHeaderAndBody(Resolution.MONTH, true); | |||
checkHeaderAndBody(DateResolution.MONTH, true); | |||
click(resolutionYear); | |||
checkHeaderAndBody(Resolution.YEAR, true); | |||
checkHeaderAndBody(DateResolution.YEAR, true); | |||
} | |||
@Test | |||
public void changeResolutionToDayThenMonth() throws Exception { | |||
initialize(); | |||
checkHeaderAndBody(Resolution.YEAR, true); // check the initial state | |||
checkHeaderAndBody(DateResolution.YEAR, true); // check the initial state | |||
click(resolutionDay); | |||
checkHeaderAndBody(Resolution.DAY, true); | |||
checkHeaderAndBody(DateResolution.DAY, true); | |||
click(resolutionMonth); | |||
checkHeaderAndBody(Resolution.MONTH, true); | |||
checkHeaderAndBody(DateResolution.MONTH, true); | |||
} | |||
@Test | |||
@@ -66,7 +66,7 @@ public class DateFieldChangeResolutionTest extends MultiBrowserTest { | |||
// Change resolutions and check that the selected date is not lost and | |||
// that the calendar has the correct resolution. | |||
click(resolutionYear); | |||
checkHeaderAndBody(Resolution.YEAR, false); | |||
checkHeaderAndBody(DateResolution.YEAR, false); | |||
} | |||
private void initialize() { | |||
@@ -81,19 +81,19 @@ public class DateFieldChangeResolutionTest extends MultiBrowserTest { | |||
resolutionYear = driver.findElement(By.id(BUTTON_BASE_ID + "year")); | |||
} | |||
private void checkHeaderAndBody(Resolution resolution, | |||
private void checkHeaderAndBody(DateResolution resolution, | |||
boolean textFieldIsEmpty) throws Exception { | |||
// Popup date field has all kinds of strange timers on the | |||
// client side | |||
sleep(100); | |||
// Open the popup calendar, perform checks and close the popup. | |||
openPopupDateField(); | |||
if (resolution.compareTo(Resolution.MONTH) <= 0) { | |||
if (resolution.compareTo(DateResolution.MONTH) <= 0) { | |||
checkMonthHeader(); | |||
} else { | |||
checkYearHeader(); | |||
} | |||
if (resolution.compareTo(Resolution.DAY) <= 0) { | |||
if (resolution.compareTo(DateResolution.DAY) <= 0) { | |||
assertTrue( | |||
"A calendar with the chosen resolution should have a body", | |||
calendarHasBody()); |