Bug 406095 - add emptyText property to textbox. patch from Dao Gottwald <dao@mozilla.com>, r=gavin.

This commit is contained in:
mozilla.mano@sent.com 2008-02-06 11:00:09 -08:00
parent f1d078e293
commit 02541d8498
11 changed files with 162 additions and 141 deletions

View File

@ -122,7 +122,6 @@
// Refresh the display (updating icon, etc)
this.updateDisplay();
this._textbox._displayCurrentEngine();
var os =
Components.classes["@mozilla.org/observer-service;1"]
@ -191,31 +190,8 @@
]]></getter>
</property>
<property name="value"
onget="return this._textbox.value;">
<setter><![CDATA[
// Make sure to remove the "empty" attribute if someone is setting
// the search bar value to a non-empty string. Similarly, we need to
// add the "empty" attribute if someone is clearing the search box,
// but only if the search box currently doesn't have focus.
if (val) {
this.removeAttribute("empty");
this._textbox.value = val;
}
else {
if (this._textbox.hasAttribute("focused")) {
// Just clear the textbox
this._textbox.value = "";
}
else {
// Display the current engine
this._textbox._displayCurrentEngine();
}
}
return val;
]]></setter>
</property>
<property name="value" onget="return this._textbox.value;"
onset="return this._textbox.value = val;"/>
<method name="focus">
<body><![CDATA[
@ -339,18 +315,13 @@
<method name="updateDisplay">
<body><![CDATA[
var uri = this.currentEngine.iconURI;
if (uri)
this.setAttribute("src", uri.spec);
else
this.setAttribute("src", "");
// Update current engine display
if (this.hasAttribute("empty"))
this._textbox._displayCurrentEngine();
this.setAttribute("src", uri ? uri.spec : "");
var name = this.currentEngine.name;
var text = this._stringBundle.getFormattedString("searchtip", [name]);
this.setAttribute("tooltiptext", text);
this._textbox.emptyText = name;
this._textbox.label = text;
this._textbox.tooltipText = text;
]]></body>
</method>
@ -495,9 +466,6 @@
<body><![CDATA[
var textBox = this._textbox;
var textValue = textBox.value;
// Ignore greyed-out hint text in "empty" searchboxes.
if (this.getAttribute("empty") == "true")
textValue = "";
// Save the current value in the form history
if (textValue) {
@ -564,7 +532,7 @@
<binding id="searchbar-textbox"
extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
<implementation implements="nsIObserver nsIDOMXULLabeledControlElement">
<implementation implements="nsIObserver">
<constructor><![CDATA[
if (document.getBindingParent(this).parentNode.parentNode.localName ==
"toolbarpaletteitem")
@ -584,9 +552,6 @@
} catch (ex) { }
]]></destructor>
<property name="label" readonly="true"
onget="return '&searchItem.title; ' +
document.getBindingParent(this).currentEngine.name;"/>
<field name="_stringBundle"/>
<field name="_formHistSvc"/>
<field name="_prefBranch"/>
@ -710,28 +675,6 @@
</body>
</method>
<!-- Displays a grayed-out hint string containing the name of the
current search engine in the search text box. (It makes it gray
by setting an empty="true" attribute on the searchbox element.)
-->
<method name="_displayCurrentEngine">
<body><![CDATA[
var searchbar = document.getBindingParent(this);
// This section is a wee bit hacky; without the timeout, the CSS
// style corresponding to the "empty" attribute doesn't kick in
// until the text has changed, leading to an unpleasant moment
// where the engine name flashes black before turning gray.
searchbar.setAttribute("empty", "true");
var searchTextbox = this;
setTimeout(function() {
if (searchbar.getAttribute("empty") == "true")
searchTextbox.value = searchbar.currentEngine.name;
}, 0);
]]></body>
</method>
<!-- overload |onTextEntered| in autocomplete.xml -->
<method name="onTextEntered">
<parameter name="aEvent"/>
@ -785,12 +728,7 @@
var data = transferUtils.retrieveURLFromData(aXferData.data,
aXferData.flavour.contentType);
if (data) {
// Remove the search bar's empty attribute, since we're setting
// a value without focusing the textbox. If it's not empty, this
// won't do anything. This can be removed if bug 280635 is fixed.
document.getBindingParent(this.mOuter).removeAttribute("empty");
this.mOuter.value = data;
this.mOuter.onTextEntered(aEvent);
}
},
@ -833,20 +771,6 @@
nsDragAndDrop.drop(event, this.searchbarDNDObserver);
</handler>
<handler event="focus" phase="capturing"><![CDATA[
var searchbar = document.getBindingParent(this);
if (searchbar.getAttribute("empty") == "true") {
searchbar.removeAttribute("empty");
this.value = "";
}
]]></handler>
<handler event="blur" phase="capturing"><![CDATA[
var searchbar = document.getBindingParent(this);
if (this.value == "")
this._displayCurrentEngine();
]]></handler>
</handlers>
</binding>
</bindings>

View File

@ -60,21 +60,6 @@
min-height: 26px;
}
#searchbar[empty="true"] > .searchbar-textbox {
color: GrayText;
direction: ltr !important;
}
#searchbar[empty="true"] > .searchbar-textbox > hbox > hbox > html|input {
direction: ltr !important;
text-align: left !important;
}
#searchbar[chromedir="rtl"][empty="true"] > .searchbar-textbox > hbox > hbox > html|input {
direction: rtl !important;
text-align: right !important;
}
#wrapper-search-container #searchbar html|*.textbox-input {
visibility: hidden;
}

View File

@ -51,15 +51,6 @@
/* ----- SEARCH FIELD ----- */
#searchbar[empty="true"] > .searchbar-textbox {
color: GrayText;
}
#searchbar[empty="true"] html|input {
direction: ltr !important;
text-align: left !important;
}
#wrapper-search-container #searchbar html|*.textbox-input {
visibility: hidden;
}

View File

@ -58,21 +58,6 @@
min-height: 26px;
}
#searchbar[empty="true"] > .searchbar-textbox {
color: GrayText;
direction: ltr !important;
}
#searchbar[empty="true"] > .searchbar-textbox > hbox > hbox > html|input {
direction: ltr !important;
text-align: left !important;
}
#searchbar[chromedir="rtl"][empty="true"] > .searchbar-textbox > hbox > hbox > html|input {
direction: rtl !important;
text-align: right !important;
}
#wrapper-search-container #searchbar html|*.textbox-input {
visibility: hidden;
}

View File

@ -82,6 +82,7 @@ _TEST_FILES = test_bug360220.xul \
test_tree_hier_cell.xul \
test_tree_column_reorder.xul \
tree_shared.js \
test_textbox_emptytext.xul \
test_textbox_number.xul \
xul_selectcontrol.js \
test_popupincontent.xul \

View File

@ -0,0 +1,47 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
<!--
XUL Widget Test for textbox with emptyText
-->
<window title="Textbox with emptyText test" width="500" height="600"
onfocus="if (!gDone) { gDone = true; doTests(); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<hbox>
<textbox id="t1"/>
</hbox>
<!-- test resuls are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
var gDone = false;
SimpleTest.waitForExplicitFinish();
function doTests() {
var t1 = $("t1");
t1.emptyText = 1;
ok("1" === t1.label, "emptyText exposed as label");
ok("" === t1.value, "emptyText not exposed as value");
t1.label = 2;
ok("2" === t1.label, "label can be set explicitly");
ok("1" === t1.emptyText, "emptyText persists after setting label");
t1.value = 3;
ok("3" === t1.value, "value setter/getter works while emptyText is present");
ok("1" === t1.emptyText, "emptyText persists after setting value");
SimpleTest.finish();
}
]]></script>
</window>

View File

@ -82,10 +82,6 @@
<field name="mEnterEvent">null</field>
<field name="mConsumeRollupEvent">false</field>
<field name="mInputElt">
document.getAnonymousElementByAttribute(this, "anonid", "input");
</field>
<constructor><![CDATA[
mController = Components.classes["@mozilla.org/autocomplete/controller;1"].
getService(Components.interfaces.nsIAutoCompleteController);
@ -200,7 +196,7 @@
<parameter name="aStartIndex"/>
<parameter name="aEndIndex"/>
<body><![CDATA[
this.mInputElt.setSelectionRange(aStartIndex, aEndIndex);
this.inputField.setSelectionRange(aStartIndex, aEndIndex);
]]></body>
</method>
@ -238,8 +234,6 @@
onset="this.setAttribute('crop',val); return val;"
onget="return this.getAttribute('crop');"/>
<property name="label" readonly="true" onget="return this.mInputElt.value;"/>
<property name="open"
onget="return this.getAttribute('open') == 'true';">
<setter><![CDATA[
@ -257,14 +251,22 @@
<!-- =================== PUBLIC MEMBERS =================== -->
<property name="value"
onget="return this.mInputElt.value;">
onget="return this.hasAttribute('empty') ? '' : this.inputField.value;">
<setter><![CDATA[
this.mIgnoreInput = true;
this.mInputElt.value = val;
if (val) {
// clear the emptyText _before_ setting a new non-empty value
this._clearEmptyText();
this.inputField.value = val;
} else {
// display the emptyText _after_ setting a value that's an empty string
this.inputField.value = val;
this._updateVisibleText();
}
this.mIgnoreInput = false;
var event = document.createEvent('Events');
event.initEvent('ValueChange', true, true);
this.mInputElt.dispatchEvent(event);
this.inputField.dispatchEvent(event);
return val;
]]></setter>
</property>
@ -516,7 +518,7 @@
<handler event="focus" phase="capturing">
<![CDATA[
this.attachController();
this.mInputElt.parentNode.parentNode.setAttribute('focused', 'true');
this.inputField.parentNode.parentNode.setAttribute('focused', 'true');
]]>
</handler>
@ -524,7 +526,7 @@
<![CDATA[
if (!this._dontBlur) {
this.detachController();
this.mInputElt.parentNode.parentNode.removeAttribute('focused');
this.inputField.parentNode.parentNode.removeAttribute('focused');
}
]]>
</handler>

View File

@ -24,7 +24,7 @@
</xul:hbox>
</content>
<implementation implements="nsIAccessibleProvider, nsIDOMXULTextBoxElement">
<implementation implements="nsIAccessibleProvider, nsIDOMXULTextBoxElement, nsIDOMXULLabeledControlElement">
<property name="accessibleType" readonly="true">
<getter>
<![CDATA[
@ -33,6 +33,12 @@
</getter>
</property>
<!-- nsIDOMXULLabeledControlElement -->
<field name="crop">""</field>
<field name="image">""</field>
<field name="command">""</field>
<field name="accessKey">""</field>
<field name="mInputField">null</field>
<field name="mIgnoreClick">false</field>
<field name="mIgnoreFocus">false</field>
@ -45,10 +51,30 @@
]]></getter>
</property>
<property name="value" onset="this.inputField.value = val; return val;"
onget="return this.inputField.value;"/>
<property name="value"
onget="return this.hasAttribute('empty') ? '' : this.inputField.value;">
<setter><![CDATA[
if (val) {
// clear the emptyText _before_ setting a new non-empty value
this._clearEmptyText();
this.inputField.value = val;
} else {
// display the emptyText _after_ setting a value that's an empty string
this.inputField.value = val;
this._updateVisibleText();
}
return val;
]]></setter>
</property>
<property name="defaultValue" onset="this.inputField.defaultValue = val; return val;"
onget="return this.inputField.defaultValue;"/>
<property name="label" onset="this.setAttribute('label', val); return val;"
onget="return this.getAttribute('label') ||
(this.labelElement ? this.labelElement.value :
this.emptyText);"/>
<property name="emptyText" onget="return this.getAttribute('emptytext') || '';"
onset="this.setAttribute('emptytext', val);
this._updateVisibleText(); return val;" />
<property name="type" onset="if (val) this.setAttribute('type', val);
else this.removeAttribute('type'); return val;"
onget="return this.getAttribute('type');"/>
@ -131,16 +157,53 @@
]]></body>
</method>
<method name="_updateVisibleText">
<body><![CDATA[
if (!this.value && this.emptyText) {
// This section is a wee bit hacky; without the timeout, the CSS
// style corresponding to the "empty" attribute doesn't kick in
// until the text has changed, leading to an unpleasant moment
// where the emptyText flashes black before turning gray.
this.setAttribute("empty", true);
setTimeout(function (textbox) {
if (textbox.hasAttribute("empty")) {
try {
textbox.editor.transactionManager.beginBatch();
} catch (e) {}
textbox.inputField.value = textbox.emptyText;
}
}, 0, this);
}
]]></body>
</method>
<method name="_clearEmptyText">
<body><![CDATA[
if (this.hasAttribute("empty")) {
this.inputField.value = "";
try {
this.editor.transactionManager.endBatch();
} catch (e) {}
this.removeAttribute("empty");
}
]]></body>
</method>
<constructor><![CDATA[
var str = this.boxObject.getProperty("value");
if (str) {
this.inputField.value = str;
this.boxObject.removeProperty("value");
}
// this.editor may not be initialized yet in
// bindings that inherit from xul:textbox, so
// do this after construction
setTimeout(function (a) { a._setNewlineHandling(); }, 0, this);
setTimeout(function (a) {
a._updateVisibleText();
a._setNewlineHandling();
}, 0, this);
]]></constructor>
<destructor>
@ -156,6 +219,8 @@
<handlers>
<handler event="focus" phase="capturing">
<![CDATA[
this._clearEmptyText();
if (!this.hasAttribute("focused")) {
if (event.originalTarget == this)
this.inputField.focus(); // Forward focus to actual HTML input
@ -174,9 +239,18 @@
<handler event="blur" phase="capturing">
<![CDATA[
this.removeAttribute('focused');
this._updateVisibleText();
]]>
</handler>
<handler event="dragover" phase="capturing">
this._clearEmptyText();
</handler>
<handler event="dragexit" phase="capturing">
this._updateVisibleText();
</handler>
<handler event="mousedown">
<![CDATA[
this.mIgnoreClick = this.hasAttribute("focused");

View File

@ -60,6 +60,10 @@ textbox
color: -moz-FieldText;
}
textbox[empty="true"] {
color: GrayText;
}
html|*.textbox-input,
html|*.textbox-textarea {
margin: 0px !important;

View File

@ -64,6 +64,10 @@ textbox {
color: -moz-FieldText;
}
textbox[empty="true"] {
color: GrayText;
}
html|*.textbox-input,
html|*.textbox-textarea {
margin: 0px !important;

View File

@ -60,6 +60,10 @@ textbox
color: -moz-FieldText;
}
textbox[empty="true"] {
color: GrayText;
}
html|*.textbox-input,
html|*.textbox-textarea {
margin: 0px !important;