diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 85a4ff7a621..0bc687da6bf 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -9,6 +9,11 @@
:root {
--nav-sub-mobile-padding: 8px;
--search-typename-width: 6.75rem;
+ /* DEFAULT_SIDEBAR_WIDTH
+ see main.js for information on these values
+ and on the RUSTDOC_MOBILE_BREAKPOINT */
+ --desktop-sidebar-width: 200px;
+ --src-sidebar-width: 300px;
}
/* See FiraSans-LICENSE.txt for the Fira Sans license. */
@@ -383,7 +388,7 @@ img {
.sidebar {
font-size: 0.875rem;
- flex: 0 0 var(--desktop-sidebar-width, 200px);
+ flex: 0 0 var(--desktop-sidebar-width);
overflow-y: scroll;
overscroll-behavior: contain;
position: sticky;
@@ -414,7 +419,7 @@ img {
position: absolute;
height: 100%;
/* make sure there's a 1px gap between the scrollbar and resize handle */
- left: calc(var(--desktop-sidebar-width, 200px) + 1px);
+ left: calc(var(--desktop-sidebar-width) + 1px);
}
.rustdoc.src .sidebar-resizer {
@@ -426,7 +431,7 @@ img {
.src-sidebar-expanded .rustdoc.src .sidebar-resizer {
/* for src sidebar, gap is already provided by 1px border on sidebar itself, so place resizer
to right of it */
- left: var(--src-sidebar-width, 300px);
+ left: var(--src-sidebar-width);
}
.sidebar-resizing {
@@ -448,7 +453,7 @@ img {
margin: 0;
/* when active or hovered, place resizer glow on top of the sidebar (right next to, or even
on top of, the scrollbar) */
- left: var(--desktop-sidebar-width, 200px);
+ left: var(--desktop-sidebar-width);
border-left: solid 1px var(--sidebar-resizer-hover);
}
@@ -457,7 +462,7 @@ img {
.src-sidebar-expanded .rustdoc.src .sidebar-resizer:focus,
.src-sidebar-expanded .rustdoc.src .sidebar-resizer.active {
/* when active or hovered, place resizer glow on top of the normal src sidebar border */
- left: calc(var(--src-sidebar-width, 300px) - 1px);
+ left: calc(var(--src-sidebar-width) - 1px);
}
@media (pointer: coarse) {
@@ -497,7 +502,7 @@ img {
.src-sidebar-expanded .src .sidebar {
overflow-y: auto;
- flex-basis: var(--src-sidebar-width, 300px);
+ flex-basis: var(--src-sidebar-width);
}
.src-sidebar-expanded .src .sidebar > *:not(#src-sidebar-toggle) {
@@ -1806,7 +1811,7 @@ However, it's not needed with smaller screen width because the doc/code block is
/*
WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
If you update this line, then you also need to update the line with the same warning
-in src-script.js
+in src-script.js and main.js
*/
@media (max-width: 700px) {
/* When linking to an item with an `id` (for instance, by clicking a link in the sidebar,
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 851ee795d4d..e1d674e7d04 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -1273,8 +1273,49 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
searchState.setup();
}());
-// This section handles sidebar resizing
+// Hide, show, and resize the sidebar
+//
+// The body class and CSS variable are initially set up in storage.js,
+// but in this file, we implement:
+//
+// * the show sidebar button, which appears if the sidebar is hidden
+// and, by clicking on it, will bring it back
+// * the sidebar resize handle, which appears only on large viewports
+// with a [fine precision pointer] to allow the user to change
+// the size of the sidebar
+//
+// [fine precision pointer]: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer
(function() {
+ // 100 is the size of the logo
+ // don't let the sidebar get smaller than that, or it'll get squished
+ const SIDEBAR_MIN = 100;
+ // Don't let the sidebar get bigger than this
+ const SIDEBAR_MAX = 500;
+ // Don't let the body (including the gutter) get smaller than this
+ //
+ // WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
+ // Acceptable values for BODY_MIN are constrained by the mobile breakpoint
+ // (which is the minimum size of the whole page where the sidebar exists)
+ // and the default sidebar width:
+ //
+ // BODY_MIN <= RUSTDOC_MOBILE_BREAKPOINT - DEFAULT_SIDEBAR_WIDTH
+ //
+ // At the time of this writing, the DEFAULT_SIDEBAR_WIDTH on src pages is
+ // 300px, and the RUSTDOC_MOBILE_BREAKPOINT is 700px, so BODY_MIN must be
+ // at most 400px. Otherwise, it would start out at the default size, then
+ // grabbing the resize handle would suddenly cause it to jank to
+ // its contraint-generated maximum.
+ const BODY_MIN = 400;
+ // At half-way past the minimum size, vanish the sidebar entirely
+ const SIDEBAR_VANISH_THRESHOLD = SIDEBAR_MIN / 2;
+
+ // Toolbar button to show the sidebar.
+ //
+ // On small, "mobile-sized" viewports, it's not persistent and it
+ // can only be activated by going into Settings and hiding the nav bar.
+ // On larger, "desktop-sized" viewports (though that includes many
+ // tablets), it's fixed-position, appears in the left side margin,
+ // and it can be activated by resizing the sidebar into nothing.
const sidebarButton = document.getElementById("sidebar-button");
if (sidebarButton) {
sidebarButton.addEventListener("click", e => {
@@ -1283,13 +1324,38 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
e.preventDefault();
});
}
+
+ // Pointer capture.
+ //
+ // Resizing is a single-pointer gesture. Any secondary pointer is ignored
let currentPointerId = null;
+
+ // "Desired" sidebar size.
+ //
+ // This is stashed here for window resizing. If the sidebar gets
+ // shrunk to maintain BODY_MIN, and then the user grows the window again,
+ // it gets the sidebar to restore its size.
+ let desiredSidebarSize = null;
+
+ // If this page has no sidebar at all, bail out.
const resizer = document.querySelector(".sidebar-resizer");
const sidebar = document.querySelector(".sidebar");
if (!resizer || !sidebar) {
return;
}
+
+ // src page and docs page use different variables, because the contents of
+ // the sidebar are so different that it's reasonable to thing the user
+ // would want them to have different sizes
const isSrcPage = hasClass(document.body, "src");
+
+ // Call this function to hide the sidebar when using the resize handle
+ //
+ // This function also nulls out the sidebar width CSS variable and setting,
+ // causing it to return to its default. This does not happen if you do it
+ // from settings.js, which uses a separate function. It's done here because
+ // the minimum sidebar size is rather uncomfortable, and it must pass
+ // through that size when using the shrink-to-nothing gesture.
function hideSidebar() {
if (isSrcPage) {
window.rustdocCloseSourceSidebar();
@@ -1302,6 +1368,13 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
document.documentElement.style.removeProperty("--desktop-sidebar-width");
}
}
+
+ // Call this function to show the sidebar from the resize handle.
+ // On docs pages, this can only happen if the user has grabbed the resize
+ // handle, shrunk the sidebar down to nothing, and then pulls back into
+ // the visible range without releasing it. You can, however, grab the
+ // resize handle on a source page with the sidebar closed, because it
+ // remains visible all the time on there.
function showSidebar() {
if (isSrcPage) {
window.rustdocShowSourceSidebar();
@@ -1310,6 +1383,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
updateLocalStorage("hide-sidebar", "false");
}
}
+
+ // Call this to set the correct CSS variable and setting.
+ // This function doesn't enforce size constraints. Do that before calling it!
function changeSidebarSize(size) {
if (isSrcPage) {
updateLocalStorage("src-sidebar-width", size);
@@ -1319,34 +1395,54 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
document.documentElement.style.setProperty("--desktop-sidebar-width", size + "px");
}
}
+
+ // Check if the sidebar is hidden. Since src pages and doc pages have
+ // different settings, this function has to check that.
function isSidebarHidden() {
return isSrcPage ?
!hasClass(document.documentElement, "src-sidebar-expanded") :
hasClass(document.documentElement, "hide-sidebar");
}
+
+ // Respond to the resize handle event.
+ // This function enforces size constraints, and implements the
+ // shrink-to-nothing gesture based on thresholds defined above.
function resize(e) {
if (currentPointerId === null || currentPointerId !== e.pointerId) {
return;
}
e.preventDefault();
const pos = e.clientX - sidebar.offsetLeft - 3;
- if (pos < 50) {
+ if (pos < SIDEBAR_VANISH_THRESHOLD) {
hideSidebar();
- } else if (pos >= 100) {
- // 100 is the size of the logo
- // don't let the sidebar get smaller than that, or it'll get squished
+ } else if (pos >= SIDEBAR_MIN) {
if (isSidebarHidden()) {
showSidebar();
}
- // don't let the sidebar get wider than 500
- changeSidebarSize(Math.min(pos, window.innerWidth - 100, 500));
+ // don't let the sidebar get wider than SIDEBAR_MAX, or the body narrower
+ // than BODY_MIN
+ const constrainedPos = Math.min(pos, window.innerWidth - BODY_MIN, SIDEBAR_MAX);
+ changeSidebarSize(constrainedPos);
+ desiredSidebarSize = constrainedPos;
}
}
+ // Respond to the window resize event.
+ window.addEventListener("resize", () => {
+ stopResize();
+ if (desiredSidebarSize >= (window.innerWidth - BODY_MIN)) {
+ changeSidebarSize(window.innerWidth - BODY_MIN);
+ } else if (desiredSidebarSize !== null && desiredSidebarSize > SIDEBAR_MIN) {
+ changeSidebarSize(desiredSidebarSize);
+ }
+ });
function stopResize(e) {
if (currentPointerId === null) {
return;
}
- e.preventDefault();
+ if (e) {
+ e.preventDefault();
+ }
+ desiredSidebarSize = sidebar.getBoundingClientRect().width;
removeClass(resizer, "active");
window.removeEventListener("pointermove", resize, false);
window.removeEventListener("pointerup", stopResize, false);
@@ -1376,6 +1472,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
window.addEventListener("pointerup", stopResize, false);
addClass(resizer, "active");
addClass(document.documentElement, "sidebar-resizing");
+ desiredSidebarSize = null;
}
resizer.addEventListener("pointerdown", initResize, false);
}());
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index 20220f2e69a..18a127fbbd1 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -196,18 +196,21 @@ if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) {
updateTheme();
+// Hide, show, and resize the sidebar at page load time
+//
+// This needs to be done here because this JS is render-blocking,
+// so that the sidebar doesn't "jump" after appearing on screen.
+// The user interaction to change this is set up in main.js.
if (getSettingValue("source-sidebar-show") === "true") {
// At this point in page load, `document.body` is not available yet.
// Set a class on the `` element instead.
addClass(document.documentElement, "src-sidebar-expanded");
}
-
if (getSettingValue("hide-sidebar") === "true") {
// At this point in page load, `document.body` is not available yet.
// Set a class on the `` element instead.
addClass(document.documentElement, "hide-sidebar");
}
-
function updateSidebarWidth() {
const desktopSidebarWidth = getSettingValue("desktop-sidebar-width");
if (desktopSidebarWidth && desktopSidebarWidth !== "null") {
diff --git a/tests/rustdoc-gui/sidebar-resize-window.goml b/tests/rustdoc-gui/sidebar-resize-window.goml
new file mode 100644
index 00000000000..04321acc105
--- /dev/null
+++ b/tests/rustdoc-gui/sidebar-resize-window.goml
@@ -0,0 +1,33 @@
+// Checks sidebar resizing
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+set-window-size: (1280, 600)
+wait-for-property: (".sidebar", {"clientWidth": 200}, [NEAR])
+
+// resize past maximum (don't grow past 500)
+drag-and-drop: ((205, 100), (600, 100))
+wait-for-property: (".sidebar", {"clientWidth": 500}, [NEAR])
+
+// make the window small enough that the sidebar has to shrink
+set-window-size: (750, 600)
+wait-for-property: (".sidebar", {"clientWidth": 350}, [NEAR])
+
+// grow the window again to make the sidebar bigger
+set-window-size: (1280, 600)
+wait-for-property: (".sidebar", {"clientWidth": 500}, [NEAR])
+
+// make the window small enough that the sidebar has to shrink
+set-window-size: (750, 600)
+wait-for-property: (".sidebar", {"clientWidth": 350}, [NEAR])
+
+// grow the window again to make the sidebar bigger
+set-window-size: (1280, 600)
+wait-for-property: (".sidebar", {"clientWidth": 500}, [NEAR])
+
+// shrink back down again, then reload the page
+// the "desired size" is a bit of remembered implicit state,
+// and rustdoc tries to minimize things like this
+set-window-size: (800, 600)
+wait-for-property: (".sidebar", {"clientWidth": 400}, [NEAR])
+reload:
+set-window-size: (1280, 600)
+wait-for-property: (".sidebar", {"clientWidth": 400}, [NEAR])