Skip to content

Commit cf1e854

Browse files
committed
rustdoc: use a button instead of a bar for search
This is a response to complaints that the header area takes up too much vertical space, forcing the user to scroll more than they ought to need. It also adds a reasonable way to pick the crate name before actually searching, which was also a feature that people ask for.
1 parent bfe809d commit cf1e854

File tree

5 files changed

+222
-120
lines changed

5 files changed

+222
-120
lines changed

src/librustdoc/html/static/css/rustdoc.css

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ h1, h2, h3, h4 {
175175
more aggressively when we want them to. */
176176
overflow-wrap: anywhere;
177177
}
178+
.search-results-main-heading nav.sub {
179+
grid-area: main-heading-h1;
180+
}
178181
.main-heading {
179182
position: relative;
180183
display: grid;
@@ -195,6 +198,16 @@ h1, h2, h3, h4 {
195198
align-items: end;
196199
padding-top: 5px;
197200
}
201+
.search-switcher {
202+
grid-area: main-heading-breadcrumbs;
203+
line-height: 1.25;
204+
display: flex;
205+
flex-wrap: wrap;
206+
color: var(--main-color);
207+
align-items: baseline;
208+
white-space: nowrap;
209+
margin-top: -1px;
210+
}
198211
.rustdoc-breadcrumbs a {
199212
padding: 4px 0;
200213
margin: -4px 0;
@@ -249,6 +262,7 @@ rustdoc-toolbar,
249262
summary.hideme,
250263
.scraped-example-list,
251264
.rustdoc-breadcrumbs,
265+
.search-switcher,
252266
/* This selector is for the items listed in the "all items" page. */
253267
ul.all-items {
254268
font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif;
@@ -996,16 +1010,15 @@ div.where {
9961010
nav.sub {
9971011
flex-grow: 1;
9981012
flex-flow: row nowrap;
999-
margin: 4px 0 0 0;
10001013
display: flex;
1001-
align-items: center;
1014+
align-items: start;
1015+
margin-top: 4px;
10021016
}
10031017
.search-form {
10041018
position: relative;
10051019
display: flex;
10061020
height: 34px;
10071021
flex-grow: 1;
1008-
margin-bottom: 4px;
10091022
}
10101023
.src nav.sub {
10111024
margin: 0 0 -10px 0;
@@ -1109,21 +1122,6 @@ table,
11091122
padding-right: 1.25rem;
11101123
}
11111124

1112-
.search-results-title {
1113-
margin-top: 0;
1114-
white-space: nowrap;
1115-
/* flex layout allows shrinking the <select> appropriately if it becomes too large */
1116-
display: flex;
1117-
/* make things look like in a line, despite the fact that we're using a layout
1118-
with boxes (i.e. from the flex layout) */
1119-
align-items: baseline;
1120-
}
1121-
.search-results-title + .sub-heading {
1122-
color: var(--main-color);
1123-
display: flex;
1124-
align-items: baseline;
1125-
white-space: nowrap;
1126-
}
11271125
#crate-search-div {
11281126
/* ensures that 100% in properties of #crate-search-div:after
11291127
are relative to the size of this div */
@@ -1848,7 +1846,7 @@ a.tooltip:hover::after {
18481846
border-bottom: 1px solid var(--border-color);
18491847
}
18501848

1851-
#settings-menu, #help-button, button#toggle-all-docs {
1849+
#search-button, #settings-menu, #help-button, button#toggle-all-docs {
18521850
margin-left: var(--button-left-margin);
18531851
display: flex;
18541852
line-height: 1.25;
@@ -1876,7 +1874,11 @@ a.tooltip:hover::after {
18761874
.hide-sidebar .src #sidebar-button {
18771875
position: static;
18781876
}
1879-
#settings-menu > a, #help-button > a, #sidebar-button > a, button#toggle-all-docs {
1877+
#search-button > a,
1878+
#settings-menu > a,
1879+
#help-button > a,
1880+
#sidebar-button > a,
1881+
button#toggle-all-docs {
18801882
display: flex;
18811883
align-items: center;
18821884
justify-content: center;
@@ -1885,11 +1887,11 @@ a.tooltip:hover::after {
18851887
border-radius: var(--button-border-radius);
18861888
color: var(--main-color);
18871889
}
1888-
#settings-menu > a, #help-button > a, button#toggle-all-docs {
1890+
#search-button > a, #settings-menu > a, #help-button > a, button#toggle-all-docs {
18891891
width: 80px;
18901892
border-radius: var(--toolbar-button-border-radius);
18911893
}
1892-
#settings-menu > a, #help-button > a {
1894+
#search-button > a, #settings-menu > a, #help-button > a {
18931895
min-width: 0;
18941896
}
18951897
#sidebar-button > a {
@@ -1898,6 +1900,7 @@ a.tooltip:hover::after {
18981900
width: 33px;
18991901
}
19001902

1903+
#search-button > a:hover, #search-button > a:focus-visible,
19011904
#settings-menu > a:hover, #settings-menu > a:focus-visible,
19021905
#help-button > a:hover, #help-button > a:focus-visible,
19031906
#sidebar-button > a:hover, #sidebar-button > a:focus-visible,
@@ -1906,6 +1909,19 @@ button#toggle-all-docs:hover, button#toggle-all-docs:focus-visible {
19061909
text-decoration: none;
19071910
}
19081911

1912+
#search-button > a:before {
1913+
/* Magnifying glass */
1914+
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \
1915+
width="18" height="18" viewBox="0 0 16 16">\
1916+
<circle r="5" cy="7" cx="6" style="fill:none;stroke:currentColor;stroke-width:3"></circle>\
1917+
<path d="M 16,15 10,10" style="fill:none;stroke:currentColor;stroke-width:4"></path>\
1918+
<desc>Search</desc>\
1919+
</svg>');
1920+
width: 18px;
1921+
height: 18px;
1922+
filter: var(--settings-menu-filter);
1923+
}
1924+
19091925
#settings-menu > a:before {
19101926
/* Wheel <https://www.svgrepo.com/svg/384069/settings-cog-gear> */
19111927
content: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 12 12" \
@@ -1948,6 +1964,7 @@ button#toggle-all-docs:before {
19481964
filter: var(--settings-menu-filter);
19491965
}
19501966

1967+
#search-button > a:before,
19511968
button#toggle-all-docs:before,
19521969
#help-button > a:before,
19531970
#settings-menu > a:before {
@@ -1956,6 +1973,7 @@ button#toggle-all-docs:before,
19561973
}
19571974

19581975
@media not (pointer: coarse) {
1976+
#search-button > a:hover:before,
19591977
button#toggle-all-docs:hover:before,
19601978
#help-button > a:hover:before,
19611979
#settings-menu > a:hover:before {
@@ -2247,6 +2265,20 @@ However, it's not needed with smaller screen width because the doc/code block is
22472265
.side-by-side > div {
22482266
width: auto;
22492267
}
2268+
2269+
/* Text label takes up too much space at this size. */
2270+
rustdoc-toolbar span.label {
2271+
display: none;
2272+
}
2273+
#search-button > a, #settings-menu > a, #help-button > a, button#toggle-all-docs {
2274+
width: 33px;
2275+
}
2276+
#settings.popover {
2277+
--popover-arrow-offset: 86px;
2278+
}
2279+
#help.popover {
2280+
--popover-arrow-offset: 48px;
2281+
}
22502282
}
22512283

22522284
/*
@@ -2273,20 +2305,6 @@ in src-script.js and main.js
22732305
visibility: hidden;
22742306
}
22752307

2276-
/* Text label takes up too much space at this size. */
2277-
rustdoc-toolbar span.label {
2278-
display: none;
2279-
}
2280-
#settings-menu > a, #help-button > a, button#toggle-all-docs {
2281-
width: 33px;
2282-
}
2283-
#settings.popover {
2284-
--popover-arrow-offset: 86px;
2285-
}
2286-
#help.popover {
2287-
--popover-arrow-offset: 48px;
2288-
}
2289-
22902308
.rustdoc {
22912309
/* Sidebar should overlay main content, rather than pushing main content to the right.
22922310
Turn off `display: flex` on the body element. */

src/librustdoc/html/static/js/main.js

Lines changed: 75 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,36 @@ function preLoadCss(cssUrl) {
241241
window.searchState = {
242242
rustdocToolbar: document.querySelector("rustdoc-toolbar"),
243243
loadingText: "Loading search results...",
244-
input: document.getElementsByClassName("search-input")[0],
245-
outputElement: () => {
244+
inputElement: () => {
245+
let el = document.getElementsByClassName("search-input")[0];
246+
if (!el) {
247+
const out = searchState.outputElement().parentElement;
248+
const hdr = document.createElement("div");
249+
hdr.className = "main-heading search-results-main-heading";
250+
const rootPath = getVar("root-path");
251+
const currentCrate = getVar("current-crate");
252+
hdr.innerHTML = `<nav class="sub">
253+
<form class="search-form">
254+
<span></span> <!-- This empty span is a hacky fix for Safari: see #93184 -->
255+
<div id="sidebar-button" tabindex="-1">
256+
<a href="${rootPath}${currentCrate}/all.html" title="show sidebar"></a>
257+
</div>
258+
<input
259+
class="search-input"
260+
name="search"
261+
aria-label="Run search in the documentation"
262+
autocomplete="off"
263+
spellcheck="false"
264+
placeholder="Type ‘S’ or ‘/’ to search, ‘?’ for more options…"
265+
type="search">
266+
</form>
267+
</nav><div class="search-switcher"></div>`;
268+
out.insertBefore(hdr, searchState.outputElement());
269+
el = document.getElementsByClassName("search-input")[0];
270+
}
271+
return el;
272+
},
273+
containerElement: () => {
246274
let el = document.getElementById("search");
247275
if (!el) {
248276
el = document.createElement("section");
@@ -251,6 +279,16 @@ function preLoadCss(cssUrl) {
251279
}
252280
return el;
253281
},
282+
outputElement: () => {
283+
const container = searchState.containerElement();
284+
let el = container.querySelector(".search-out");
285+
if (!el) {
286+
el = document.createElement("div");
287+
el.className = "search-out";
288+
container.appendChild(el);
289+
}
290+
return el;
291+
},
254292
title: document.title,
255293
titleBeforeSearch: document.title,
256294
timeout: null,
@@ -268,22 +306,38 @@ function preLoadCss(cssUrl) {
268306
searchState.timeout = null;
269307
}
270308
},
271-
isDisplayed: () => searchState.outputElement().parentElement.id === ALTERNATIVE_DISPLAY_ID,
309+
isDisplayed: () => searchState.containerElement().parentElement.id ===
310+
ALTERNATIVE_DISPLAY_ID,
272311
// Sets the focus on the search bar at the top of the page
273312
focus: () => {
274-
searchState.input.focus();
313+
searchState.showResults();
314+
searchState.inputElement().focus();
275315
},
276316
// Removes the focus from the search bar.
277317
defocus: () => {
278-
searchState.input.blur();
318+
searchState.inputElement().blur();
319+
},
320+
toggle: () => {
321+
if (searchState.isDisplayed()) {
322+
searchState.defocus();
323+
searchState.hideResults();
324+
} else {
325+
searchState.focus();
326+
}
279327
},
280-
showResults: search => {
281-
if (search === null || typeof search === "undefined") {
282-
search = searchState.outputElement();
328+
showResults: () => {
329+
document.title = searchState.title;
330+
if (searchState.isDisplayed()) {
331+
return;
283332
}
333+
const search = searchState.containerElement();
284334
switchDisplayedElement(search);
285335
searchState.mouseMovedAfterSearch = false;
286-
document.title = searchState.title;
336+
const btn = document.querySelector("#search-button a");
337+
if (browserSupportsHistoryApi() && btn &&
338+
searchState.getQueryStringParams().search === undefined) {
339+
history.pushState(null, "", btn.href);
340+
}
287341
},
288342
removeQueryParameters: () => {
289343
// We change the document title.
@@ -309,11 +363,8 @@ function preLoadCss(cssUrl) {
309363
return params;
310364
},
311365
setup: () => {
312-
const search_input = searchState.input;
313-
if (!searchState.input) {
314-
return;
315-
}
316366
let searchLoaded = false;
367+
const search_input = searchState.inputElement();
317368
// If you're browsing the nightly docs, the page might need to be refreshed for the
318369
// search to work because the hash of the JS scripts might have changed.
319370
function sendSearchForm() {
@@ -333,8 +384,16 @@ function preLoadCss(cssUrl) {
333384
loadSearch();
334385
});
335386

336-
if (search_input.value !== "") {
337-
loadSearch();
387+
const btn = document.getElementById("search-button");
388+
if (btn) {
389+
btn.onclick = event => {
390+
if (event.ctrlKey || event.altKey || event.metaKey) {
391+
return;
392+
}
393+
event.preventDefault();
394+
searchState.toggle();
395+
loadSearch();
396+
};
338397
}
339398

340399
const params = searchState.getQueryStringParams();
@@ -346,7 +405,7 @@ function preLoadCss(cssUrl) {
346405
setLoadingSearch: () => {
347406
const search = searchState.outputElement();
348407
search.innerHTML = "<h3 class=\"search-loading\">" + searchState.loadingText + "</h3>";
349-
searchState.showResults(search);
408+
searchState.showResults();
350409
},
351410
descShards: new Map(),
352411
loadDesc: async function({descShard, descIndex}) {

0 commit comments

Comments
 (0)