diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index e936e1ca07ecd..1b9dfdf3fdd67 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -1474,7 +1474,20 @@ a.test-arrow:hover {
.example-wrap:hover > .test-arrow {
padding: 2px 7px;
}
-.example-wrap:hover > .test-arrow, .example-wrap:hover > .button-holder {
+/*
+On iPad, the ":hover" state sticks around, making things work not greatly. Do work around
+it, we move it into this media query. More information can be found at:
+https://css-tricks.com/solving-sticky-hover-states-with-media-hover-hover/
+
+However, using `@media (hover: hover)` makes this rule never to be applied in GUI tests, so
+instead, we check that it's not a "finger" cursor.
+*/
+@media not (pointer: coarse) {
+ .example-wrap:hover > .test-arrow, .example-wrap:hover > .button-holder {
+ visibility: visible;
+ }
+}
+.example-wrap .button-holder.keep-visible {
visibility: visible;
}
.example-wrap .button-holder .copy-button {
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 40d65ae7910e2..e0ea234f9e710 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -1829,14 +1829,22 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm
copyContentToClipboard(codeElem.textContent);
}
- function addCopyButton(event) {
+ function getExampleWrap(event) {
let elem = event.target;
while (!hasClass(elem, "example-wrap")) {
elem = elem.parentElement;
if (elem.tagName === "body" || hasClass(elem, "docblock")) {
- return;
+ return null;
}
}
+ return elem;
+ }
+
+ function addCopyButton(event) {
+ const elem = getExampleWrap(event);
+ if (elem === null) {
+ return;
+ }
// Since the button will be added, no need to keep this listener around.
elem.removeEventListener("mouseover", addCopyButton);
@@ -1858,7 +1866,20 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm
parent.appendChild(copyButton);
}
+ function showHideCodeExampleButtons(event) {
+ const elem = getExampleWrap(event);
+ if (elem === null) {
+ return;
+ }
+ const buttons = elem.querySelector(".button-holder");
+ if (buttons === null) {
+ return;
+ }
+ buttons.classList.toggle("keep-visible");
+ }
+
onEachLazy(document.querySelectorAll(".docblock .example-wrap"), elem => {
elem.addEventListener("mouseover", addCopyButton);
+ elem.addEventListener("click", showHideCodeExampleButtons);
});
}());
diff --git a/tests/rustdoc-gui/code-example-buttons.goml b/tests/rustdoc-gui/code-example-buttons.goml
new file mode 100644
index 0000000000000..57ea2970072e6
--- /dev/null
+++ b/tests/rustdoc-gui/code-example-buttons.goml
@@ -0,0 +1,21 @@
+// This test ensures that code blocks buttons are displayed on hover and when you click on them.
+go-to: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html"
+
+// First we check we "hover".
+move-cursor-to: ".example-wrap"
+assert-css: (".example-wrap .copy-button", { "visibility": "visible" })
+move-cursor-to: ".search-input"
+assert-css: (".example-wrap .copy-button", { "visibility": "hidden" })
+
+// Now we check the click.
+assert-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 0)
+click: ".example-wrap"
+move-cursor-to: ".search-input"
+// It should have a new class and be visible.
+wait-for-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 1)
+wait-for-css: (".example-wrap:not(:hover) .button-holder.keep-visible", { "visibility": "visible" })
+// Clicking again will remove the class.
+click: ".example-wrap"
+move-cursor-to: ".search-input"
+assert-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 0)
+assert-css: (".example-wrap .copy-button", { "visibility": "hidden" })