mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-03-25 15:38:01 +02:00
Merge pull request #107 from google/speaker-notes
Add support for speaker notes
This commit is contained in:
commit
15f88b3700
@ -13,8 +13,8 @@ class = "bob"
|
|||||||
|
|
||||||
[output.html]
|
[output.html]
|
||||||
curly-quotes = true
|
curly-quotes = true
|
||||||
additional-js = ["ga4.js"]
|
additional-js = ["ga4.js", "speaker-notes.js"]
|
||||||
additional-css = ["svgbob.css"]
|
additional-css = ["svgbob.css", "speaker-notes.css"]
|
||||||
git-repository-url = "https://github.com/google/comprehensive-rust"
|
git-repository-url = "https://github.com/google/comprehensive-rust"
|
||||||
edit-url-template = "https://github.com/google/comprehensive-rust/edit/main/{path}"
|
edit-url-template = "https://github.com/google/comprehensive-rust/edit/main/{path}"
|
||||||
|
|
||||||
|
24
speaker-notes.css
Normal file
24
speaker-notes.css
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
.content details {
|
||||||
|
background: var(--sidebar-bg);
|
||||||
|
color: var(--sidebar-fg) !important;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
padding: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content details summary h4 {
|
||||||
|
display: inline-block;
|
||||||
|
list-style: none;
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: italic;
|
||||||
|
margin: 0.5em 0.25em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content details summary h4:target::before {
|
||||||
|
margin-left: -40px;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content details summary a {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
226
speaker-notes.js
Normal file
226
speaker-notes.js
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
let notes = document.querySelector("details");
|
||||||
|
// Create an unattached DOM node for the code below.
|
||||||
|
if (!notes) {
|
||||||
|
notes = document.createElement("details");
|
||||||
|
}
|
||||||
|
let popIn = document.createElement("button");
|
||||||
|
|
||||||
|
// Mark the speaker note window defunct. This means that it will no longer
|
||||||
|
// show the notes.
|
||||||
|
function markDefunct() {
|
||||||
|
const main = document.querySelector("main");
|
||||||
|
const h4 = document.createElement("h4");
|
||||||
|
h4.append("(You can close this window now.)");
|
||||||
|
main.replaceChildren(h4);
|
||||||
|
window.location.hash = "#speaker-notes-defunct";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the window. This shows/hides controls as necessary for regular and
|
||||||
|
// speaker note pages.
|
||||||
|
function applyState() {
|
||||||
|
if (window.location.hash == "#speaker-notes-open") {
|
||||||
|
if (getState() != "popup") {
|
||||||
|
markDefunct();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (getState()) {
|
||||||
|
case "popup":
|
||||||
|
popIn.classList.remove("hidden");
|
||||||
|
notes.classList.add("hidden");
|
||||||
|
break;
|
||||||
|
case "inline-open":
|
||||||
|
popIn.classList.add("hidden");
|
||||||
|
notes.open = true;
|
||||||
|
notes.classList.remove("hidden");
|
||||||
|
break;
|
||||||
|
case "inline-closed":
|
||||||
|
popIn.classList.add("hidden");
|
||||||
|
notes.open = false;
|
||||||
|
notes.classList.remove("hidden");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the state of the speaker note window: "inline-open", "inline-closed",
|
||||||
|
// or "popup".
|
||||||
|
function getState() {
|
||||||
|
return window.localStorage["speakerNotes"] || "inline-open";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the state of the speaker note window. Call applyState as needed
|
||||||
|
// afterwards.
|
||||||
|
function setState(state) {
|
||||||
|
window.localStorage["speakerNotes"] = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create controls for a regular page.
|
||||||
|
function setupRegularPage() {
|
||||||
|
// Create pop-in button.
|
||||||
|
popIn.setAttribute("id", "speaker-notes-toggle");
|
||||||
|
popIn.setAttribute("type", "button");
|
||||||
|
popIn.setAttribute("title", "Close speaker notes");
|
||||||
|
popIn.setAttribute("aria-label", "Close speaker notes");
|
||||||
|
popIn.classList.add("icon-button");
|
||||||
|
let i = document.createElement("i");
|
||||||
|
i.classList.add("fa", "fa-window-close-o");
|
||||||
|
popIn.append(i);
|
||||||
|
popIn.addEventListener("click", (event) => {
|
||||||
|
setState("inline-open");
|
||||||
|
applyState();
|
||||||
|
});
|
||||||
|
document.querySelector(".left-buttons").append(popIn);
|
||||||
|
|
||||||
|
// Create speaker notes.
|
||||||
|
notes.addEventListener("toggle", (event) => {
|
||||||
|
setState(notes.open ? "inline-open" : "inline-closed");
|
||||||
|
});
|
||||||
|
|
||||||
|
let summary = document.createElement("summary");
|
||||||
|
notes.insertBefore(summary, notes.firstChild);
|
||||||
|
|
||||||
|
let h4 = document.createElement("h4");
|
||||||
|
h4.setAttribute("id", "speaker-notes");
|
||||||
|
h4.append("Speaker Notes");
|
||||||
|
h4.addEventListener("click", (event) => {
|
||||||
|
// Update fragment as if we had clicked a link. A regular a element would
|
||||||
|
// result in double-fire of the event.
|
||||||
|
window.location.hash = "#speaker-notes";
|
||||||
|
});
|
||||||
|
summary.append(h4);
|
||||||
|
|
||||||
|
// Create pop-out button.
|
||||||
|
let popOutLocation = new URL(window.location.href);
|
||||||
|
popOutLocation.hash = "#speaker-notes-open";
|
||||||
|
let popOut = document.createElement("a");
|
||||||
|
popOut.setAttribute("href", popOutLocation.href);
|
||||||
|
popOut.setAttribute("target", "speakerNotes");
|
||||||
|
popOut.classList.add("fa", "fa-external-link");
|
||||||
|
summary.append(popOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create controls for a speaker note window.
|
||||||
|
function setupSpeakerNotes() {
|
||||||
|
// Show the notes inline again when the window is closed.
|
||||||
|
window.addEventListener("pagehide", (event) => {
|
||||||
|
setState("inline-open");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hide sidebar and buttons.
|
||||||
|
document.querySelector("html").classList.remove("sidebar-visible");
|
||||||
|
document.querySelector("html").classList.add("sidebar-hidden");
|
||||||
|
document.querySelector(".left-buttons").classList.add("hidden");
|
||||||
|
document.querySelector(".right-buttons").classList.add("hidden");
|
||||||
|
|
||||||
|
// Hide content except for the speaker notes and h1 elements.
|
||||||
|
const main = document.querySelector("main");
|
||||||
|
let children = main.childNodes;
|
||||||
|
let i = 0;
|
||||||
|
while (i < children.length) {
|
||||||
|
const node = children[i];
|
||||||
|
switch (node.tagName) {
|
||||||
|
case "DETAILS":
|
||||||
|
// We found the speaker notes: extract their content.
|
||||||
|
let div = document.createElement("div");
|
||||||
|
div.replaceChildren(...node.childNodes);
|
||||||
|
node.replaceWith(div);
|
||||||
|
i += 1;
|
||||||
|
break;
|
||||||
|
case "H1":
|
||||||
|
// We found a header: turn it into a smaller header for the speaker
|
||||||
|
// note window.
|
||||||
|
let h4 = document.createElement("h4");
|
||||||
|
let pageLocation = new URL(window.location.href);
|
||||||
|
pageLocation.hash = "";
|
||||||
|
let a = document.createElement("a");
|
||||||
|
a.setAttribute("href", pageLocation.href);
|
||||||
|
a.append(node.innerText);
|
||||||
|
h4.append("Speaker Notes for ", a);
|
||||||
|
node.replaceWith(h4);
|
||||||
|
i += 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// We found something else: remove it.
|
||||||
|
main.removeChild(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update prev/next buttons to keep speaker note state.
|
||||||
|
document.querySelectorAll('a[rel="prev"], a[rel="next"]').forEach((elem) => {
|
||||||
|
elem.href += "#speaker-notes-open";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeout = null;
|
||||||
|
// This will fire on _other_ open windows when we change window.localStorage.
|
||||||
|
window.addEventListener("storage", (event) => {
|
||||||
|
switch (event.key) {
|
||||||
|
case "currentPage":
|
||||||
|
if (getState() == "popup") {
|
||||||
|
// We link all windows when we are showing speaker notes.
|
||||||
|
window.location.pathname = event.newValue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "speakerNotes":
|
||||||
|
// When nagigating to another page, we see two state changes in rapid
|
||||||
|
// succession:
|
||||||
|
//
|
||||||
|
// - "popup" -> "inline-open"
|
||||||
|
// - "inline-open" -> "popup"
|
||||||
|
//
|
||||||
|
// When the page is closed, we only see:
|
||||||
|
//
|
||||||
|
// - "popup" -> "inline-open"
|
||||||
|
//
|
||||||
|
// We can use a timeout to detect the difference. The effect is that
|
||||||
|
// showing the speaker notes is delayed by 500 ms when closing the
|
||||||
|
// speaker notes window.
|
||||||
|
if (timeout) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
timeout = setTimeout(applyState, 500);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.localStorage["currentPage"] = window.location.pathname;
|
||||||
|
|
||||||
|
// We encode the kind of page in the location hash:
|
||||||
|
switch (window.location.hash) {
|
||||||
|
case "#speaker-notes-open":
|
||||||
|
// We are on a page in the speaker notes. We need to re-establish the
|
||||||
|
// popup state so that the main window will hide the notes.
|
||||||
|
setState("popup");
|
||||||
|
setupSpeakerNotes();
|
||||||
|
break;
|
||||||
|
case "#speaker-notes-defunct":
|
||||||
|
// We are on a page in a defunct speaker note window. We keep the state
|
||||||
|
// unchanged and mark the window defunct.
|
||||||
|
setupSpeakerNotes();
|
||||||
|
markDefunct();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// We are on a regular page. We force the state to "inline-open" if this
|
||||||
|
// looks like a direct link to the speaker notes.
|
||||||
|
if (window.location.hash == "#speaker-notes") {
|
||||||
|
setState("inline-open");
|
||||||
|
}
|
||||||
|
applyState();
|
||||||
|
setupRegularPage();
|
||||||
|
}
|
||||||
|
})();
|
@ -16,3 +16,21 @@ What you see:
|
|||||||
* The `main` function is the entry point of the program.
|
* The `main` function is the entry point of the program.
|
||||||
* Rust has hygienic macros, `println!` is an example of this.
|
* Rust has hygienic macros, `println!` is an example of this.
|
||||||
* Rust strings are UTF-8 encoded and can contain any Unicode character.
|
* Rust strings are UTF-8 encoded and can contain any Unicode character.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
This slide tries to make the students comfortable with Rust code. They will see
|
||||||
|
a ton of it over the next four days so we start small with something familiar.
|
||||||
|
|
||||||
|
Key points:
|
||||||
|
|
||||||
|
* Rust is very much like other languages in the C/C++/Java tradition. It is
|
||||||
|
imperative (not functional) and it doesn't try to reinvent things unless
|
||||||
|
absolutely necessary.
|
||||||
|
|
||||||
|
* Rust is modern with full support for things like Unicode.
|
||||||
|
|
||||||
|
* Rust uses macros for situations where you want to have a variable number of
|
||||||
|
arguments (no function [overloading](basic-syntax/functions-interlude.md)).
|
||||||
|
|
||||||
|
</details>
|
||||||
|
@ -18,3 +18,27 @@ fn main() { // Program entry point
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
The code implements the Collatz conjecture: it is believed that the loop will
|
||||||
|
always end, but this is not yet proved. Edit the code and play with different
|
||||||
|
inputs.
|
||||||
|
|
||||||
|
Key points:
|
||||||
|
|
||||||
|
* Explain that all variables are statically typed. Try removing `i32` to trigger
|
||||||
|
type inference. Try with `i8` instead and trigger a runtime integer overflow.
|
||||||
|
|
||||||
|
* Change `let mut x` to `let x`, discuss the compiler error.
|
||||||
|
|
||||||
|
* Show how `print!` gives a compilation error if the arguments don't match the
|
||||||
|
format string.
|
||||||
|
|
||||||
|
* Show how you need to use `{}` as a placeholder if you want to print an
|
||||||
|
expression which is more complex than just a single variable.
|
||||||
|
|
||||||
|
* Show the students the standard library, show them how to search for `std::fmt`
|
||||||
|
which has the rules of the formatting mini-language. It's important that the
|
||||||
|
students become familiar with searching in the standard library.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
@ -10,3 +10,20 @@ today:
|
|||||||
management, and garbage collection.
|
management, and garbage collection.
|
||||||
|
|
||||||
* Ownership: move semantics, copying and cloning, borrowing, and lifetimes.
|
* Ownership: move semantics, copying and cloning, borrowing, and lifetimes.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
The idea for the first day is to show _just enough_ of Rust to be able to speak
|
||||||
|
about the famous borrow checker. The way Rust handles memory is a major feature
|
||||||
|
and we should show students this right away.
|
||||||
|
|
||||||
|
If you're teaching this in a classroom, this is a good place to go over the
|
||||||
|
schedule. We suggest splitting the day into two parts (following the slides):
|
||||||
|
|
||||||
|
* Morning: 9:00 to 12:00,
|
||||||
|
* Afternoon: 13:00 to 16:00.
|
||||||
|
|
||||||
|
You can of course adjust this as necessary. Please make sure to include breaks,
|
||||||
|
we recommend a break every hour!
|
||||||
|
|
||||||
|
</details>
|
||||||
|
@ -14,3 +14,14 @@ Rust is a new programming language which had its 1.0 release in 2015:
|
|||||||
* mobile phones,
|
* mobile phones,
|
||||||
* desktops,
|
* desktops,
|
||||||
* servers.
|
* servers.
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
Rust fits in the same area as C++:
|
||||||
|
|
||||||
|
* High flexibility.
|
||||||
|
* High level of control.
|
||||||
|
* Can be scaled down to very constrained devices like mobile phones.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user