Bug 1361950 - [WIP] Give month/year selectors visual focus when opened in date picker
MozReview-Commit-ID: 4eNnIns4PIm
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -2716,16 +2716,29 @@ HTMLInputElement::SetDateTimePickerState
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
if (frame) {
frame->SetPickerState(aOpen);
}
}
void
+HTMLInputElement::SetDateTimePickerFocus(bool aFocus)
+{
+ if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
+ return;
+ }
+
+ nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
+ if (frame) {
+ frame->SetPickerFocus(aFocus);
+ }
+}
+
+void
HTMLInputElement::OpenDateTimePicker(const DateTimeValue& aInitialValue)
{
if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
return;
}
mDateTimeInputBoxValue = new DateTimeValue(aInitialValue);
nsContentUtils::DispatchChromeEvent(OwnerDoc(),
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -821,16 +821,17 @@ public:
/*
* The following functions are called from datetime picker to let input box
* know the current state of the picker or to update the input box on changes.
*/
void GetDateTimeInputBoxValue(DateTimeValue& aValue);
void UpdateDateTimeInputBox(const DateTimeValue& aValue);
void SetDateTimePickerState(bool aOpen);
+ void SetDateTimePickerFocus(bool aFocus);
/*
* The following functions are called from datetime input box XBL to control
* and update the picker.
*/
void OpenDateTimePicker(const DateTimeValue& aInitialValue);
void UpdateDateTimePicker(const DateTimeValue& aValue);
void CloseDateTimePicker();
--- a/dom/html/nsIDateTimeInputArea.idl
+++ b/dom/html/nsIDateTimeInputArea.idl
@@ -30,16 +30,21 @@ interface nsIDateTimeInputArea : nsISupp
void blurInnerTextBox();
/**
* Set the current state of the picker, true if it's opened, false otherwise.
*/
void setPickerState(in boolean isOpen);
/**
+ * Set the focus state of the picker, true if it's focused, false otherwise.
+ */
+ void setPickerFocus(in boolean isFocus);
+
+ /**
* Set the attribute of the inner text boxes. Only "tabindex", "readonly",
* and "disabled" are allowed.
*/
void setEditAttribute(in DOMString name, in DOMString value);
/**
* Remove the attribute of the inner text boxes. Only "tabindex", "readonly",
* and "disabled" are allowed.
--- a/dom/webidl/HTMLInputElement.webidl
+++ b/dom/webidl/HTMLInputElement.webidl
@@ -248,16 +248,19 @@ partial interface HTMLInputElement {
DateTimeValue getDateTimeInputBoxValue();
[Pref="dom.forms.datetime", ChromeOnly]
void updateDateTimeInputBox(optional DateTimeValue value);
[Pref="dom.forms.datetime", ChromeOnly]
void setDateTimePickerState(boolean open);
+ [Pref="dom.forms.datetime", ChromeOnly]
+ void setDateTimePickerFocus(boolean focus);
+
[Pref="dom.forms.datetime", Func="IsChromeOrXBL"]
void openDateTimePicker(optional DateTimeValue initialValue);
[Pref="dom.forms.datetime", Func="IsChromeOrXBL"]
void updateDateTimePicker(optional DateTimeValue value);
[Pref="dom.forms.datetime", Func="IsChromeOrXBL"]
void closeDateTimePicker();
--- a/layout/forms/nsDateTimeControlFrame.cpp
+++ b/layout/forms/nsDateTimeControlFrame.cpp
@@ -101,16 +101,26 @@ nsDateTimeControlFrame::SetPickerState(b
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
do_QueryInterface(mInputAreaContent);
if (inputAreaContent) {
inputAreaContent->SetPickerState(aOpen);
}
}
void
+nsDateTimeControlFrame::SetPickerFocus(bool aFocus)
+{
+ nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
+ do_QueryInterface(mInputAreaContent);
+ if (inputAreaContent) {
+ inputAreaContent->SetPickerFocus(aFocus);
+ }
+}
+
+void
nsDateTimeControlFrame::HandleFocusEvent()
{
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
do_QueryInterface(mInputAreaContent);
if (inputAreaContent) {
inputAreaContent->FocusInnerTextBox();
}
}
--- a/layout/forms/nsDateTimeControlFrame.h
+++ b/layout/forms/nsDateTimeControlFrame.h
@@ -74,16 +74,17 @@ public:
nsresult AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute,
int32_t aModType) override;
void UpdateInputBoxValue();
void SetValueFromPicker(const DateTimeValue& aValue);
void HandleFocusEvent();
void HandleBlurEvent();
void SetPickerState(bool aOpen);
+ void SetPickerFocus(bool aFocus);
private:
class SyncDisabledStateEvent;
friend class SyncDisabledStateEvent;
class SyncDisabledStateEvent : public mozilla::Runnable
{
public:
explicit SyncDisabledStateEvent(nsDateTimeControlFrame* aFrame)
--- a/toolkit/content/browser-content.js
+++ b/toolkit/content/browser-content.js
@@ -1641,41 +1641,44 @@ let DateTimePickerListener = {
},
/**
* Cleanup function called when picker is closed.
*/
close() {
this.removeListeners();
this._inputElement.setDateTimePickerState(false);
+ this._inputElement.setDateTimePickerFocus(false);
this._inputElement = null;
},
/**
* Called after picker is opened to start listening for input box update
* events.
*/
addListeners() {
addEventListener("MozUpdateDateTimePicker", this);
addEventListener("MozCloseDateTimePicker", this);
addEventListener("pagehide", this);
addMessageListener("FormDateTime:PickerValueChanged", this);
+ addMessageListener("FormDateTime:PickerFocusChanged", this);
addMessageListener("FormDateTime:PickerClosed", this);
},
/**
* Stop listeneing for events when picker is closed.
*/
removeListeners() {
removeEventListener("MozUpdateDateTimePicker", this);
removeEventListener("MozCloseDateTimePicker", this);
removeEventListener("pagehide", this);
removeMessageListener("FormDateTime:PickerValueChanged", this);
+ removeMessageListener("FormDateTime:PickerFocusChanged", this);
removeMessageListener("FormDateTime:PickerClosed", this);
},
/**
* Helper function that returns the CSS direction property of the element.
*/
getComputedDirection(aElement) {
return aElement.ownerGlobal.getComputedStyle(aElement)
@@ -1702,16 +1705,20 @@ let DateTimePickerListener = {
case "FormDateTime:PickerClosed": {
this.close();
break;
}
case "FormDateTime:PickerValueChanged": {
this._inputElement.updateDateTimeInputBox(aMessage.data);
break;
}
+ case "FormDateTime:PickerFocusChanged": {
+ this._inputElement.setDateTimePickerFocus(aMessage.data);
+ break;
+ }
default:
break;
}
},
/**
* nsIDOMEventListener, for chrome events sent by the input element and other
* DOM events.
--- a/toolkit/content/widgets/datetimebox.xml
+++ b/toolkit/content/widgets/datetimebox.xml
@@ -1299,16 +1299,26 @@
<parameter name="aValue"/>
<body>
<![CDATA[
this.setFieldsFromPicker(aValue);
]]>
</body>
</method>
+ <method name="setPickerFocus">
+ <parameter name="aFocus"/>
+ <body>
+ <![CDATA[
+ this.log("pickerFocus: " + aFocus);
+ this.pickerFocus = aFocus;
+ ]]>
+ </body>
+ </method>
+
<method name="advanceToNextField">
<parameter name="aReverse"/>
<body>
<![CDATA[
this.log("advanceToNextField");
let focusedInput = this.mLastFocusedField;
let next = aReverse ? focusedInput.previousElementSibling
@@ -1608,57 +1618,67 @@
if (aEvent.target == this.mInputElement && this.mIsPickerOpen) {
this.mInputElement.closeDateTimePicker();
}
let target = aEvent.originalTarget;
target.setAttribute("typeBuffer", "");
this.setInputValueFromFields();
this.mInputElement.setFocusState(false);
+ this.pickerFocus = false;
]]>
</body>
</method>
<method name="onKeyPress">
<parameter name="aEvent"/>
<body>
<![CDATA[
this.log("onKeyPress key: " + aEvent.key);
switch (aEvent.key) {
// Close picker on Enter, Escape or Space key.
case "Enter":
case "Escape":
case " ": {
if (this.mIsPickerOpen) {
+ this.pickerFocus = false;
this.mInputElement.closeDateTimePicker();
aEvent.preventDefault();
}
break;
}
case "Backspace": {
let targetField = aEvent.originalTarget;
this.clearFieldValue(targetField);
this.setInputValueFromFields();
aEvent.preventDefault();
break;
}
case "ArrowRight":
case "ArrowLeft": {
- this.advanceToNextField(aEvent.key == "ArrowRight" ? false : true);
+ if (this.pickerFocus) {
+ // Send key to picker
+ } else {
+ this.advanceToNextField(aEvent.key == "ArrowRight" ? false : true);
+ }
aEvent.preventDefault();
break;
}
case "ArrowUp":
case "ArrowDown":
case "PageUp":
case "PageDown":
case "Home":
case "End": {
- this.handleKeyboardNav(aEvent);
+ if (this.pickerFocus) {
+ // Send key to picker
+ } else {
+ this.handleKeyboardNav(aEvent);
+ }
aEvent.preventDefault();
break;
}
default: {
// printable characters
if (aEvent.keyCode == 0 &&
!(aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey)) {
this.handleKeypress(aEvent);
--- a/toolkit/content/widgets/datetimepopup.xml
+++ b/toolkit/content/widgets/datetimepopup.xml
@@ -224,16 +224,24 @@
day: value.day
}
}));
break;
}
}
]]></body>
</method>
+ <method name="sendPickerFocusChanged">
+ <parameter name="isFocus"/>
+ <body><![CDATA[
+ this.dispatchEvent(new CustomEvent("DateTimePickerFocusChanged", {
+ detail: isFocus,
+ }));
+ ]]></body>
+ </method>
<method name="getCalendarInfo">
<parameter name="locale"/>
<body><![CDATA[
const calendarInfo = this.mozIntl.getCalendarInfo(locale);
// Day of week from calendarInfo starts from 1 as Sunday to 7 as Saturday,
// so they need to be mapped to JavaScript convention with 0 as Sunday
// and 6 as Saturday
--- a/toolkit/modules/DateTimePickerHelper.jsm
+++ b/toolkit/modules/DateTimePickerHelper.jsm
@@ -78,16 +78,20 @@ this.DateTimePickerHelper = {
// nsIDOMEventListener
handleEvent(aEvent) {
debug("handleEvent: " + aEvent.type);
switch (aEvent.type) {
case "DateTimePickerValueChanged": {
this.updateInputBoxValue(aEvent);
break;
}
+ case "DateTimePickerFocusChanged": {
+ this.setPickerFocus(aEvent);
+ break;
+ }
case "popuphidden": {
let browser = this.weakBrowser ? this.weakBrowser.get() : null;
if (browser) {
browser.messageManager.sendAsyncMessage("FormDateTime:PickerClosed");
}
this.close();
break;
}
@@ -100,16 +104,24 @@ this.DateTimePickerHelper = {
updateInputBoxValue(aEvent) {
let browser = this.weakBrowser ? this.weakBrowser.get() : null;
if (browser) {
browser.messageManager.sendAsyncMessage(
"FormDateTime:PickerValueChanged", aEvent.detail);
}
},
+ setPickerFocus(aEvent) {
+ let browser = this.weakBrowser ? this.weakBrowser.get() : null;
+ if (browser) {
+ browser.messageManager.sendAsyncMessage(
+ "FormDateTime:PickerFocusChanged", aEvent.detail);
+ }
+ },
+
// Get picker from browser and show it anchored to the input box.
showPicker: Task.async(function* (aBrowser, aData) {
let rect = aData.rect;
let type = aData.type;
let detail = aData.detail;
this._anchor = aBrowser.ownerGlobal.gBrowser.popupAnchor;
this._anchor.left = rect.left;
@@ -163,19 +175,21 @@ this.DateTimePickerHelper = {
// Listen to picker's event.
addPickerListeners() {
if (!this.picker) {
return;
}
this.picker.addEventListener("popuphidden", this);
this.picker.addEventListener("DateTimePickerValueChanged", this);
+ this.picker.addEventListener("DateTimePickerFocusChanged", this);
},
// Stop listening to picker's event.
removePickerListeners() {
if (!this.picker) {
return;
}
this.picker.removeEventListener("popuphidden", this);
this.picker.removeEventListener("DateTimePickerValueChanged", this);
+ this.picker.removeEventListener("DateTimePickerFocusChanged", this);
},
};