Skip to content

Commit 4813468

Browse files
authored
Merge pull request #2608 from szabgab/navigation-help
Add pop-up window showing the keyboard shortcuts
2 parents 63ae0d5 + 33da0c2 commit 4813468

File tree

6 files changed

+129
-20
lines changed

6 files changed

+129
-20
lines changed

src/front-end/css/chrome.css

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,3 +656,46 @@ html:not(.sidebar-resizing) .sidebar {
656656
margin-inline-start: -14px;
657657
width: 14px;
658658
}
659+
660+
/* The container for the help popup that covers the whole window. */
661+
#mdbook-help-container {
662+
/* Position and size for the whole window. */
663+
position: fixed;
664+
top: 0;
665+
left: 0;
666+
right: 0;
667+
bottom: 0;
668+
/* This uses flex layout (which is set in book.js), and centers the popup
669+
in the window.*/
670+
display: none;
671+
align-items: center;
672+
justify-content: center;
673+
z-index: 1000;
674+
/* Dim out the book while the popup is visible. */
675+
background: var(--overlay-bg);
676+
}
677+
678+
/* The popup help box. */
679+
#mdbook-help-popup {
680+
box-shadow: 0 4px 24px rgba(0,0,0,0.15);
681+
min-width: 300px;
682+
max-width: 500px;
683+
width: 100%;
684+
box-sizing: border-box;
685+
display: flex;
686+
flex-direction: column;
687+
align-items: center;
688+
background-color: var(--bg);
689+
color: var(--fg);
690+
border-width: 1px;
691+
border-color: var(--theme-popup-border);
692+
border-style: solid;
693+
border-radius: 8px;
694+
padding: 10px;
695+
}
696+
697+
.mdbook-help-title {
698+
text-align: center;
699+
/* mdbook's margin for h2 is way too large. */
700+
margin: 10px;
701+
}

src/front-end/css/variables.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@
6464
--copy-button-filter-hover: invert(68%) sepia(55%) saturate(531%) hue-rotate(341deg) brightness(104%) contrast(101%);
6565

6666
--footnote-highlight: #2668a6;
67+
68+
--overlay-bg: rgba(33, 40, 48, 0.4);
6769
}
6870

6971
.coal {
@@ -115,6 +117,8 @@
115117
--copy-button-filter-hover: invert(36%) sepia(70%) saturate(503%) hue-rotate(167deg) brightness(98%) contrast(89%);
116118

117119
--footnote-highlight: #4079ae;
120+
121+
--overlay-bg: rgba(33, 40, 48, 0.4);
118122
}
119123

120124
.light, html:not(.js) {
@@ -166,6 +170,8 @@
166170
--copy-button-filter-hover: invert(14%) sepia(93%) saturate(4250%) hue-rotate(243deg) brightness(99%) contrast(130%);
167171

168172
--footnote-highlight: #7e7eff;
173+
174+
--overlay-bg: rgba(200, 200, 205, 0.4);
169175
}
170176

171177
.navy {
@@ -217,6 +223,8 @@
217223
--copy-button-filter-hover: invert(46%) sepia(20%) saturate(1537%) hue-rotate(156deg) brightness(85%) contrast(90%);
218224

219225
--footnote-highlight: #4079ae;
226+
227+
--overlay-bg: rgba(33, 40, 48, 0.4);
220228
}
221229

222230
.rust {
@@ -266,6 +274,8 @@
266274
--copy-button-filter-hover: invert(77%) sepia(16%) saturate(1798%) hue-rotate(328deg) brightness(98%) contrast(83%);
267275

268276
--footnote-highlight: #d3a17a;
277+
278+
--overlay-bg: rgba(150, 150, 150, 0.25);
269279
}
270280

271281
@media (prefers-color-scheme: dark) {

src/front-end/js/book.js

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,7 @@ aria-label="Show hidden lines"></button>';
623623

624624
(function chapterNavigation() {
625625
document.addEventListener('keydown', function(e) {
626-
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) {
626+
if (e.altKey || e.ctrlKey || e.metaKey) {
627627
return;
628628
}
629629
if (window.search && window.search.hasFocus()) {
@@ -643,6 +643,55 @@ aria-label="Show hidden lines"></button>';
643643
window.location.href = previousButton.href;
644644
}
645645
}
646+
function showHelp() {
647+
const container = document.getElementById('mdbook-help-container');
648+
const overlay = document.getElementById('mdbook-help-popup');
649+
container.style.display = 'flex';
650+
651+
// Clicking outside the popup will dismiss it.
652+
const mouseHandler = event => {
653+
if (overlay.contains(event.target)) {
654+
return;
655+
}
656+
if (event.button !== 0) {
657+
return;
658+
}
659+
event.preventDefault();
660+
event.stopPropagation();
661+
document.removeEventListener('mousedown', mouseHandler);
662+
hideHelp();
663+
};
664+
665+
// Pressing esc will dismiss the popup.
666+
const escapeKeyHandler = event => {
667+
if (event.key === 'Escape') {
668+
event.preventDefault();
669+
event.stopPropagation();
670+
document.removeEventListener('keydown', escapeKeyHandler, true);
671+
hideHelp();
672+
}
673+
};
674+
document.addEventListener('keydown', escapeKeyHandler, true);
675+
document.getElementById('mdbook-help-container')
676+
.addEventListener('mousedown', mouseHandler);
677+
}
678+
function hideHelp() {
679+
document.getElementById('mdbook-help-container').style.display = 'none';
680+
}
681+
682+
// Usually needs the Shift key to be pressed
683+
switch (e.key) {
684+
case '?':
685+
e.preventDefault();
686+
showHelp();
687+
break;
688+
}
689+
690+
// Rest of the keys are only active when the Shift key is not pressed
691+
if (e.shiftKey) {
692+
return;
693+
}
694+
646695
switch (e.key) {
647696
case 'ArrowRight':
648697
e.preventDefault();

src/front-end/searcher/searcher.js

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,7 @@ window.search = window.search || {};
3333
mark_exclude = ['text'],
3434
marker = new Mark(content),
3535
URL_SEARCH_PARAM = 'search',
36-
URL_MARK_PARAM = 'highlight',
37-
38-
SEARCH_HOTKEY_KEYCODES = [83, 191], // `s` or `/`.
39-
ESCAPE_KEYCODE = 27,
40-
DOWN_KEYCODE = 40,
41-
UP_KEYCODE = 38,
42-
SELECT_KEYCODE = 13;
36+
URL_MARK_PARAM = 'highlight';
4337

4438
let current_searchterm = '',
4539
doc_urls = [],
@@ -352,7 +346,7 @@ window.search = window.search || {};
352346
return;
353347
}
354348

355-
if (e.keyCode === ESCAPE_KEYCODE) {
349+
if (e.key === 'Escape') {
356350
e.preventDefault();
357351
searchbar.classList.remove('active');
358352
setSearchUrlParameters('',
@@ -362,46 +356,46 @@ window.search = window.search || {};
362356
}
363357
showSearch(false);
364358
marker.unmark();
365-
} else if (!hasFocus() && SEARCH_HOTKEY_KEYCODES.includes(e.keyCode)) {
359+
} else if (!hasFocus() && (e.key === 'S' || e.key === '/')) {
366360
e.preventDefault();
367361
showSearch(true);
368362
window.scrollTo(0, 0);
369363
searchbar.select();
370-
} else if (hasFocus() && (e.keyCode === DOWN_KEYCODE
371-
|| e.keyCode === SELECT_KEYCODE)) {
364+
} else if (hasFocus() && (e.key === 'ArrowDown'
365+
|| e.key === 'Enter')) {
372366
e.preventDefault();
373367
const first = searchresults.firstElementChild;
374368
if (first !== null) {
375369
unfocusSearchbar();
376370
first.classList.add('focus');
377-
if (e.keyCode === SELECT_KEYCODE) {
371+
if (e.key === 'Enter') {
378372
window.location.assign(first.querySelector('a'));
379373
}
380374
}
381-
} else if (!hasFocus() && (e.keyCode === DOWN_KEYCODE
382-
|| e.keyCode === UP_KEYCODE
383-
|| e.keyCode === SELECT_KEYCODE)) {
375+
} else if (!hasFocus() && (e.key === 'ArrowDown'
376+
|| e.key === 'ArrowUp'
377+
|| e.key === 'Enter')) {
384378
// not `:focus` because browser does annoying scrolling
385379
const focused = searchresults.querySelector('li.focus');
386380
if (!focused) {
387381
return;
388382
}
389383
e.preventDefault();
390-
if (e.keyCode === DOWN_KEYCODE) {
384+
if (e.key === 'ArrowDown') {
391385
const next = focused.nextElementSibling;
392386
if (next) {
393387
focused.classList.remove('focus');
394388
next.classList.add('focus');
395389
}
396-
} else if (e.keyCode === UP_KEYCODE) {
390+
} else if (e.key === 'ArrowUp') {
397391
focused.classList.remove('focus');
398392
const prev = focused.previousElementSibling;
399393
if (prev) {
400394
prev.classList.add('focus');
401395
} else {
402396
searchbar.select();
403397
}
404-
} else { // SELECT_KEYCODE
398+
} else { // Enter
405399
window.location.assign(focused.querySelector('a'));
406400
}
407401
}

src/front-end/templates/index.hbs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@
6363
<script src="{{ resource "toc.js" }}"></script>
6464
</head>
6565
<body>
66+
<div id="mdbook-help-container">
67+
<div id="mdbook-help-popup">
68+
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
69+
<div>
70+
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
71+
{{#if search_enabled}}
72+
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
73+
{{/if}}
74+
<p>Press <kbd>?</kbd> to show this help</p>
75+
<p>Press <kbd>Esc</kbd> to hide this help</p>
76+
</div>
77+
</div>
78+
</div>
6679
<div id="body-container">
6780
<!-- Work around some values being stored in localStorage wrapped in quotes -->
6881
<script>

src/renderer/html_handlebars/hbs_renderer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,7 @@ fn build_header_links(html: &str) -> String {
667667
static BUILD_HEADER_LINKS: LazyLock<Regex> = LazyLock::new(|| {
668668
Regex::new(r#"<h(\d)(?: id="([^"]+)")?(?: class="([^"]+)")?>(.*?)</h\d>"#).unwrap()
669669
});
670-
static IGNORE_CLASS: &[&str] = &["menu-title"];
670+
static IGNORE_CLASS: &[&str] = &["menu-title", "mdbook-help-title"];
671671

672672
let mut id_counter = HashMap::new();
673673

0 commit comments

Comments
 (0)