Bug 1351867 - Show search tooltip on top of button r?mconley
MozReview-Commit-ID: HKbcs0kXLGq
--- a/browser/components/preferences/in-content/findInPage.js
+++ b/browser/components/preferences/in-content/findInPage.js
@@ -1,16 +1,17 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/* import-globals-from preferences.js */
var gSearchResultsPane = {
findSelection: null,
+ listSearchTooltips: [],
searchResultsCategory: null,
searchInput: null,
init() {
let controller = this.getSelectionController();
this.findSelection = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND);
this.findSelection.setColors("currentColor", "#ffe900", "currentColor", "#540ead");
this.searchResultsCategory = document.getElementById("category-search-results");
@@ -160,17 +161,17 @@ var gSearchResultsPane = {
range.setEnd(endNode, endValue);
this.findSelection.addRange(range);
}
return indices.length > 0;
},
getSelectionController() {
- // Yuck. See bug 138068.
+ // Yuck. See bug 138068.
let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
let controller = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsISelectionDisplay)
.QueryInterface(Ci.nsISelectionController);
@@ -187,16 +188,24 @@ var gSearchResultsPane = {
*
* @param String event
* to search for filted query in
*/
searchFunction(event) {
let query = event.target.value.trim().toLowerCase();
this.findSelection.removeAllRanges();
+ // Remove all search tooltips that were created
+ let searchTooltips = Array.from(document.querySelectorAll(".search-tooltip"));
+ for (let searchTooltip of searchTooltips) {
+ searchTooltip.parentElement.classList.remove("search-tooltip-parent");
+ searchTooltip.remove();
+ }
+ this.listSearchTooltips = [];
+
let srHeader = document.getElementById("header-searchResults");
if (query) {
// Showing the Search Results Tag
gotoPref("paneSearchResults");
this.searchResultsCategory.hidden = false;
@@ -230,16 +239,21 @@ var gSearchResultsPane = {
noResultsEl.hidden = false;
let strings = this.strings;
document.getElementById("sorry-message").textContent =
strings.getFormattedString("searchResults.sorryMessage2", [query]);
let brandName = document.getElementById("bundleBrand").getString("brandShortName");
document.getElementById("need-help").innerHTML =
strings.getFormattedString("searchResults.needHelp", [brandName]);
+ } else {
+ // Creating tooltips for all the instances found
+ for (let node of this.listSearchTooltips) {
+ this.createSearchTooltip(node, query);
+ }
}
} else {
this.searchResultsCategory.hidden = true;
document.getElementById("sorry-message").textContent = "";
// Going back to General when cleared
gotoPref("paneGeneral");
}
},
@@ -259,52 +273,97 @@ var gSearchResultsPane = {
if (nodeObject.childElementCount == 0) {
let simpleTextNodes = this.textNodeDescendants(nodeObject);
for (let node of simpleTextNodes) {
let result = this.highlightMatches([node], [node.length], node.textContent.toLowerCase(), searchPhrase);
matchesFound = matchesFound || result;
}
- // Collecting data from boxObject
+ // Collecting data from boxObject
let nodeSizes = [];
let allNodeText = "";
let runningSize = 0;
let labelResult = false;
let valueResult = false;
let accessKeyTextNodes = this.textNodeDescendants(nodeObject.boxObject);
for (let node of accessKeyTextNodes) {
runningSize += node.textContent.length;
allNodeText += node.textContent;
nodeSizes.push(runningSize);
}
// Access key are presented
let complexTextNodesResult = this.highlightMatches(accessKeyTextNodes, nodeSizes, allNodeText.toLowerCase(), searchPhrase);
- // Searching some elements, such as xul:button, have a 'label' attribute that contains the user-visible text.
+ // Searching some elements, such as xul:button, have a 'label' attribute that contains the user-visible text.
if (nodeObject.getAttribute("label")) {
labelResult = this.stringMatchesFilters(nodeObject.getAttribute("label"), searchPhrase);
}
- // Searching some elements, such as xul:label, store their user-visible text in a "value" attribute.
+ // Creating tooltips for buttons
+ if (labelResult && nodeObject.tagName === "button") {
+ this.listSearchTooltips.push(nodeObject);
+ }
+
+ // Searching some elements, such as xul:label, store their user-visible text in a "value" attribute.
if (nodeObject.getAttribute("value")) {
valueResult = this.stringMatchesFilters(nodeObject.getAttribute("value"), searchPhrase);
}
if (nodeObject.tagName == "button" && (labelResult || valueResult)) {
nodeObject.setAttribute("highlightable", "true");
}
+ // Creating tooltips for buttons
+ if (valueResult && nodeObject.tagName === "button") {
+ this.listSearchTooltips.push(nodeObject);
+ }
+
matchesFound = matchesFound || complexTextNodesResult || labelResult || valueResult;
}
for (let i = 0; i < nodeObject.childNodes.length; i++) {
// Search only if child node is not hidden
if (!nodeObject.childNodes[i].hidden && nodeObject.getAttribute("data-hidden-from-search") !== "true") {
let result = this.searchWithinNode(nodeObject.childNodes[i], searchPhrase);
+ // Creating tooltips for menulist element
+ if (result && nodeObject.tagName === "menulist") {
+ this.listSearchTooltips.push(nodeObject);
+ }
matchesFound = matchesFound || result;
}
}
return matchesFound;
+ },
+
+ /**
+ * Inserting a div structure infront of the DOM element matched textContent.
+ * Then calculation the offsets to position the tooltip in the correct place.
+ *
+ * @param Node currentNode
+ * DOM Element
+ * @param String query
+ * Word or words that are being searched for
+ */
+ createSearchTooltip(currentNode, query) {
+ let searchTooltip = document.createElement("span");
+ searchTooltip.setAttribute("class", "search-tooltip");
+ searchTooltip.textContent = query;
+
+ currentNode.parentElement.classList.add("search-tooltip-parent");
+ currentNode.parentElement.appendChild(searchTooltip);
+
+ // In order to get the up-to-date position of each of the nodes that we're
+ // putting tooltips on, we have to flush layout intentionally, and that
+ // this is the result of a XUL limitation (bug 1363730).
+ let anchorRect = currentNode.getBoundingClientRect();
+ let tooltipRect = searchTooltip.getBoundingClientRect();
+ let parentRect = currentNode.parentElement.getBoundingClientRect();
+
+ let offSet = (anchorRect.width / 2) - (tooltipRect.width / 2);
+ let relativeOffset = anchorRect.left - parentRect.left;
+ offSet += relativeOffset > 0 ? relativeOffset : 0;
+
+ searchTooltip.style.setProperty("left", `${offSet}px`);
}
}
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -483,73 +483,68 @@
preference="layout.spellcheckDefault"/>
</groupbox>
<!-- Fonts and Colors -->
<groupbox id="fontsGroup" data-category="paneGeneral" hidden="true">
<caption><label>&fontsAndColors.label;</label></caption>
- <grid id="fontsGrid">
- <columns>
- <column flex="1"/>
- <column/>
- </columns>
- <rows id="fontsRows">
- <row id="fontRow">
- <hbox align="center">
- <label control="defaultFont" accesskey="&defaultFont2.accesskey;">&defaultFont2.label;</label>
- <menulist id="defaultFont" delayprefsave="true" onsyncfrompreference="return FontBuilder.readFontSelection(this);"/>
- <label id="defaultFontSizeLabel" control="defaultFontSize" accesskey="&defaultSize2.accesskey;">&defaultSize2.label;</label>
- <menulist id="defaultFontSize" delayprefsave="true">
- <menupopup>
- <menuitem value="9" label="9"/>
- <menuitem value="10" label="10"/>
- <menuitem value="11" label="11"/>
- <menuitem value="12" label="12"/>
- <menuitem value="13" label="13"/>
- <menuitem value="14" label="14"/>
- <menuitem value="15" label="15"/>
- <menuitem value="16" label="16"/>
- <menuitem value="17" label="17"/>
- <menuitem value="18" label="18"/>
- <menuitem value="20" label="20"/>
- <menuitem value="22" label="22"/>
- <menuitem value="24" label="24"/>
- <menuitem value="26" label="26"/>
- <menuitem value="28" label="28"/>
- <menuitem value="30" label="30"/>
- <menuitem value="32" label="32"/>
- <menuitem value="34" label="34"/>
- <menuitem value="36" label="36"/>
- <menuitem value="40" label="40"/>
- <menuitem value="44" label="44"/>
- <menuitem value="48" label="48"/>
- <menuitem value="56" label="56"/>
- <menuitem value="64" label="64"/>
- <menuitem value="72" label="72"/>
- </menupopup>
- </menulist>
- </hbox>
- <button id="advancedFonts"
- class="accessory-button"
- icon="select-font"
- label="&advancedFonts.label;"
- accesskey="&advancedFonts.accesskey;"/>
- </row>
- <row id="colorsRow">
- <hbox/>
- <button id="colors"
- class="accessory-button"
- icon="select-color"
- label="&colors.label;"
- accesskey="&colors.accesskey;"/>
- </row>
- </rows>
- </grid>
+ <vbox>
+ <hbox id="fontSettings">
+ <hbox align="center">
+ <label control="defaultFont" accesskey="&defaultFont2.accesskey;">&defaultFont2.label;</label>
+ <menulist id="defaultFont" delayprefsave="true" onsyncfrompreference="return FontBuilder.readFontSelection(this);"/>
+ <label id="defaultFontSizeLabel" control="defaultFontSize" accesskey="&defaultSize2.accesskey;">&defaultSize2.label;</label>
+ <menulist id="defaultFontSize" delayprefsave="true">
+ <menupopup>
+ <menuitem value="9" label="9"/>
+ <menuitem value="10" label="10"/>
+ <menuitem value="11" label="11"/>
+ <menuitem value="12" label="12"/>
+ <menuitem value="13" label="13"/>
+ <menuitem value="14" label="14"/>
+ <menuitem value="15" label="15"/>
+ <menuitem value="16" label="16"/>
+ <menuitem value="17" label="17"/>
+ <menuitem value="18" label="18"/>
+ <menuitem value="20" label="20"/>
+ <menuitem value="22" label="22"/>
+ <menuitem value="24" label="24"/>
+ <menuitem value="26" label="26"/>
+ <menuitem value="28" label="28"/>
+ <menuitem value="30" label="30"/>
+ <menuitem value="32" label="32"/>
+ <menuitem value="34" label="34"/>
+ <menuitem value="36" label="36"/>
+ <menuitem value="40" label="40"/>
+ <menuitem value="44" label="44"/>
+ <menuitem value="48" label="48"/>
+ <menuitem value="56" label="56"/>
+ <menuitem value="64" label="64"/>
+ <menuitem value="72" label="72"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ <spacer flex="1" />
+ <button id="advancedFonts"
+ class="accessory-button"
+ icon="select-font"
+ label="&advancedFonts.label;"
+ accesskey="&advancedFonts.accesskey;"/>
+ </hbox>
+ <hbox id="colorsSettings">
+ <spacer flex="1" />
+ <button id="colors"
+ class="accessory-button"
+ icon="select-color"
+ label="&colors.label;"
+ accesskey="&colors.accesskey;"/>
+ </hbox>
+ </vbox>
</groupbox>
<!-- Browsing -->
<groupbox id="browsingGroup" data-category="paneGeneral">
<caption><label>&browsing.label;</label></caption>
<checkbox id="useAutoScroll"
label="&useAutoScroll.label;"
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -301,43 +301,37 @@
</vbox>
</deck>
</groupbox>
<!-- Passwords -->
<groupbox id="passwordsGroup" orient="vertical" data-category="panePrivacy" hidden="true">
<caption><label>&formsAndPasswords.label;</label></caption>
- <grid id="passwordGrid">
- <columns>
- <column flex="1"/>
- <column/>
- </columns>
- <rows id="passwordRows">
- <row id="savePasswordsBox">
- <checkbox id="savePasswords"
- label="&rememberLogins1.label;" accesskey="&rememberLogins1.accesskey;"
- preference="signon.rememberSignons"
- onsyncfrompreference="return gPrivacyPane.readSavePasswords();"
- flex="1" />
- <button id="passwordExceptions"
- class="accessory-button"
- label="&passwordExceptions.label;"
- accesskey="&passwordExceptions.accesskey;"
- preference="pref.privacy.disable_button.view_passwords_exceptions"/>
- </row>
- <row id="showPasswordRow">
- <hbox id="showPasswordsBox"/>
- <button id="showPasswords"
- class="accessory-button"
- label="&savedLogins.label;" accesskey="&savedLogins.accesskey;"
- preference="pref.privacy.disable_button.view_passwords"/>
- </row>
- </rows>
- </grid>
+ <vbox id="passwordSettings">
+ <hbox id="savePasswordsBox">
+ <checkbox id="savePasswords"
+ label="&rememberLogins1.label;" accesskey="&rememberLogins1.accesskey;"
+ preference="signon.rememberSignons"
+ onsyncfrompreference="return gPrivacyPane.readSavePasswords();"
+ flex="1" />
+ <button id="passwordExceptions"
+ class="accessory-button"
+ label="&passwordExceptions.label;"
+ accesskey="&passwordExceptions.accesskey;"
+ preference="pref.privacy.disable_button.view_passwords_exceptions"/>
+ </hbox>
+ <hbox id="showPasswordBox">
+ <hbox id="showPasswordsBox"/>
+ <button id="showPasswords"
+ class="accessory-button"
+ label="&savedLogins.label;" accesskey="&savedLogins.accesskey;"
+ preference="pref.privacy.disable_button.view_passwords"/>
+ </hbox>
+ </vbox>
<hbox id="masterPasswordRow">
<checkbox id="useMasterPassword"
label="&useMasterPassword.label;"
accesskey="&useMasterPassword.accesskey;"
flex="1" />
<button id="changeMasterPassword"
class="accessory-button"
label="&changeMasterPassword.label;"
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -604,8 +604,29 @@ description > html|a {
background-position: right 15px top 0;
}
.help-button:link,
.help-button:visited {
color: var(--in-content-category-text);
text-decoration: none;
}
+
+.search-tooltip {
+ position: absolute;
+ pointer-events: none;
+ padding: 0 10px;
+ bottom: 100%;
+ background-color: #ffe352;
+}
+
+.search-tooltip::before {
+ position: absolute;
+ content: "";
+ border: 6px solid transparent;
+ border-top-color: #ffe352;
+ top: 100%;
+ offset-inline-start: calc(50% - 6px);
+}
+
+.search-tooltip-parent {
+ position: relative;
+}