зеркало из https://github.com/mozilla/gecko-dev.git
Bug 406095 - add emptyText property to textbox. patch from Dao Gottwald <dao@mozilla.com>, r=gavin.
This commit is contained in:
Родитель
f1d078e293
Коммит
02541d8498
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -59,7 +59,11 @@ textbox
|
|||
background-color: -moz-Field;
|
||||
color: -moz-FieldText;
|
||||
}
|
||||
|
||||
|
||||
textbox[empty="true"] {
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
html|*.textbox-input,
|
||||
html|*.textbox-textarea {
|
||||
margin: 0px !important;
|
||||
|
|
|
@ -63,7 +63,11 @@ textbox {
|
|||
background-color: -moz-Field;
|
||||
color: -moz-FieldText;
|
||||
}
|
||||
|
||||
|
||||
textbox[empty="true"] {
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
html|*.textbox-input,
|
||||
html|*.textbox-textarea {
|
||||
margin: 0px !important;
|
||||
|
|
|
@ -59,7 +59,11 @@ textbox
|
|||
background-color: -moz-Field;
|
||||
color: -moz-FieldText;
|
||||
}
|
||||
|
||||
|
||||
textbox[empty="true"] {
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
html|*.textbox-input,
|
||||
html|*.textbox-textarea {
|
||||
margin: 0px !important;
|
||||
|
|
Загрузка…
Ссылка в новой задаче