diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index be30871ea4c..307ce9ac375 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -170,7 +170,8 @@ function hideThemeButtonState() { // 1 for "In Parameters" // 2 for "In Return Types" currentTab: 0, - mouseMovedAfterSearch: true, + // tab and back preserves the element that was focused. + focusedByTab: [null, null, null], clearInputTimeout: function() { if (searchState.timeout !== null) { clearTimeout(searchState.timeout); @@ -262,10 +263,6 @@ function hideThemeButtonState() { search_input.placeholder = searchState.input.origPlaceholder; }); - document.addEventListener("mousemove", function() { - searchState.mouseMovedAfterSearch = true; - }); - search_input.removeAttribute('disabled'); // `crates{version}.js` should always be loaded before this script, so we can use it @@ -1064,7 +1061,7 @@ function hideThemeButtonState() { ["T", "Focus the theme picker menu"], ["↑", "Move up in search results"], ["↓", "Move down in search results"], - ["ctrl + ↑ / ↓", "Switch result tab"], + ["← / →", "Switch result tab (when results focused)"], ["⏎", "Go to active search result"], ["+", "Expand all sections"], ["-", "Collapse all sections"], diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index e2e1aefa4a8..c7bf01d148a 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -144,7 +144,7 @@ h4.type.trait-impl, h4.associatedconstant.trait-impl, h4.associatedtype.trait-im } h1, h2, h3, h4, -.sidebar, a.source, .search-input, .content table td:first-child > a, +.sidebar, a.source, .search-input, .search-results .result-name, div.item-list .out-of-band, #source-sidebar, #sidebar-toggle, details.rustdoc-toggle > summary::before, @@ -748,6 +748,15 @@ a { outline: 0; } +.search-results { + display: none; + padding-bottom: 2em; +} + +.search-results.active { + display: block; +} + .search-results .desc { white-space: nowrap; text-overflow: ellipsis; @@ -756,22 +765,14 @@ a { } .search-results a { + /* A little margin ensures the browser's outlining of focused links has room to display. */ + margin-left: 2px; + margin-right: 2px; display: block; } -.content .search-results td:first-child { - padding-right: 0; +.result-name { width: 50%; -} -.content .search-results td:first-child a { - padding-right: 10px; -} -.content .search-results td:first-child a:after { - clear: both; - content: ""; - display: block; -} -.content .search-results td:first-child a span { float: left; } @@ -1134,6 +1135,11 @@ pre.rust { .search-failed { text-align: center; margin-top: 20px; + display: none; +} + +.search-failed.active { + display: block; } .search-failed > ul { diff --git a/src/librustdoc/html/static/search.js b/src/librustdoc/html/static/search.js index c53b3d5f14b..01cfffc5429 100644 --- a/src/librustdoc/html/static/search.js +++ b/src/librustdoc/html/static/search.js @@ -51,9 +51,9 @@ function printTab(nb) { }); onEachLazy(document.getElementById("results").childNodes, function(elem) { if (nb === 0) { - elem.style.display = ""; + addClass(elem, "active"); } else { - elem.style.display = "none"; + removeClass(elem, "active"); } nb -= 1; }); @@ -878,106 +878,22 @@ window.initSearch = function(rawSearchIndex) { }; } - function initSearchNav() { - var hoverTimeout; + function nextTab(direction) { + var next = (searchState.currentTab + direction + 3) % searchState.focusedByTab.length; + searchState.focusedByTab[searchState.currentTab] = document.activeElement; + printTab(next); + focusSearchResult(); + } - var click_func = function(e) { - var el = e.target; - // to retrieve the real "owner" of the event. - while (el.tagName !== "TR") { - el = el.parentNode; - } - var dst = e.target.getElementsByTagName("a"); - if (dst.length < 1) { - return; - } - dst = dst[0]; - if (window.location.pathname === dst.pathname) { - searchState.hideResults(); - document.location.href = dst.href; - } - }; - var mouseover_func = function(e) { - if (searchState.mouseMovedAfterSearch) { - var el = e.target; - // to retrieve the real "owner" of the event. - while (el.tagName !== "TR") { - el = el.parentNode; - } - clearTimeout(hoverTimeout); - hoverTimeout = setTimeout(function() { - onEachLazy(document.getElementsByClassName("search-results"), function(e) { - onEachLazy(e.getElementsByClassName("result"), function(i_e) { - removeClass(i_e, "highlighted"); - }); - }); - addClass(el, "highlighted"); - }, 20); - } - }; - onEachLazy(document.getElementsByClassName("search-results"), function(e) { - onEachLazy(e.getElementsByClassName("result"), function(i_e) { - i_e.onclick = click_func; - i_e.onmouseover = mouseover_func; - }); - }); - - searchState.input.onkeydown = function(e) { - // "actives" references the currently highlighted item in each search tab. - // Each array in "actives" represents a tab. - var actives = [[], [], []]; - // "current" is used to know which tab we're looking into. - var current = 0; - onEachLazy(document.getElementById("results").childNodes, function(e) { - onEachLazy(e.getElementsByClassName("highlighted"), function(h_e) { - actives[current].push(h_e); - }); - current += 1; - }); - var SHIFT = 16; - var CTRL = 17; - var ALT = 18; - - var currentTab = searchState.currentTab; - if (e.which === 38) { // up - if (e.ctrlKey) { // Going through result tabs. - printTab(currentTab > 0 ? currentTab - 1 : 2); - } else { - if (!actives[currentTab].length || - !actives[currentTab][0].previousElementSibling) { - return; - } - addClass(actives[currentTab][0].previousElementSibling, "highlighted"); - removeClass(actives[currentTab][0], "highlighted"); - } - e.preventDefault(); - } else if (e.which === 40) { // down - if (e.ctrlKey) { // Going through result tabs. - printTab(currentTab > 1 ? 0 : currentTab + 1); - } else if (!actives[currentTab].length) { - var results = document.getElementById("results").childNodes; - if (results.length > 0) { - var res = results[currentTab].getElementsByClassName("result"); - if (res.length > 0) { - addClass(res[0], "highlighted"); - } - } - } else if (actives[currentTab][0].nextElementSibling) { - addClass(actives[currentTab][0].nextElementSibling, "highlighted"); - removeClass(actives[currentTab][0], "highlighted"); - } - e.preventDefault(); - } else if (e.which === 13) { // return - if (actives[currentTab].length) { - var elem = actives[currentTab][0].getElementsByTagName("a")[0]; - document.location.href = elem.href; - } - } else if ([SHIFT, CTRL, ALT].indexOf(e.which) !== -1) { - // Does nothing, it's just to avoid losing "focus" on the highlighted element. - } else if (actives[currentTab].length > 0) { - removeClass(actives[currentTab][0], "highlighted"); - } - }; + // focus the first search result on the active tab, or the result that + // was focused last time this tab was active. + function focusSearchResult() { + var target = searchState.focusedByTab[searchState.currentTab] || + document.querySelectorAll(".search-results.active a").item(0) || + document.querySelectorAll("#titles > button").item(searchState.currentTab); + if (target) { + target.focus(); + } } function buildHrefAndPath(item) { @@ -1047,16 +963,16 @@ window.initSearch = function(rawSearchIndex) { } function addTab(array, query, display) { - var extraStyle = ""; - if (display === false) { - extraStyle = " style=\"display: none;\""; + var extraClass = ""; + if (display === true) { + extraClass = " active"; } var output = ""; var duplicates = {}; var length = 0; if (array.length > 0) { - output = "
" +
- "" +
+ output += "" +
+ " " +
(item.is_alias === true ?
("" + item.alias + " - see ") : "") +
item.displayPath + "" +
- name + " | " +
- "" +
+ name + " " +
"" + item.desc +
- " |