diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 22ae7af617f..0608c4e32e0 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -576,7 +576,8 @@ fn after_krate(&mut self, krate: &clean::Crate, cache: &Cache) -> Result<(), Err
settings(
self.shared.static_root_path.as_deref().unwrap_or("./"),
&self.shared.resource_suffix,
- ),
+ &self.shared.style_files,
+ )?,
&style_files,
);
self.shared.fs.write(&settings_file, v.as_bytes())?;
@@ -811,6 +812,7 @@ fn write_shared(
but.textContent = item;
but.onclick = function(el) {{
switchTheme(currentTheme, mainTheme, item, true);
+ useSystemTheme(false);
}};
but.onblur = handleThemeButtonsBlur;
themes.appendChild(but);
@@ -1344,22 +1346,35 @@ fn print(self, f: &mut Buffer) {
#[derive(Debug)]
enum Setting {
- Section { description: &'static str, sub_settings: Vec },
- Entry { js_data_name: &'static str, description: &'static str, default_value: bool },
+ Section {
+ description: &'static str,
+ sub_settings: Vec,
+ },
+ Toggle {
+ js_data_name: &'static str,
+ description: &'static str,
+ default_value: bool,
+ },
+ Select {
+ js_data_name: &'static str,
+ description: &'static str,
+ default_value: &'static str,
+ options: Vec<(String, String)>,
+ },
}
impl Setting {
- fn display(&self) -> String {
+ fn display(&self, root_path: &str, suffix: &str) -> String {
match *self {
- Setting::Section { ref description, ref sub_settings } => format!(
+ Setting::Section { description, ref sub_settings } => format!(
"",
description,
- sub_settings.iter().map(|s| s.display()).collect::()
+ sub_settings.iter().map(|s| s.display(root_path, suffix)).collect::()
),
- Setting::Entry { ref js_data_name, ref description, ref default_value } => format!(
+ Setting::Toggle { js_data_name, description, default_value } => format!(
"",
js_data_name,
- if *default_value { " checked" } else { "" },
+ if default_value { " checked" } else { "" },
description,
),
+ Setting::Select { js_data_name, description, default_value, ref options } => format!(
+ "\
+
{}
\
+
\
+
",
+ description,
+ js_data_name,
+ options
+ .iter()
+ .map(|opt| format!(
+ "",
+ opt.0,
+ if &opt.0 == default_value { "selected" } else { "" },
+ opt.1,
+ ))
+ .collect::(),
+ root_path,
+ suffix,
+ ),
}
}
}
impl From<(&'static str, &'static str, bool)> for Setting {
fn from(values: (&'static str, &'static str, bool)) -> Setting {
- Setting::Entry { js_data_name: values.0, description: values.1, default_value: values.2 }
+ Setting::Toggle { js_data_name: values.0, description: values.1, default_value: values.2 }
}
}
@@ -1390,9 +1427,39 @@ fn from(values: (&'static str, Vec)) -> Setting {
}
}
-fn settings(root_path: &str, suffix: &str) -> String {
+fn settings(root_path: &str, suffix: &str, themes: &[StylePath]) -> Result {
+ let theme_names: Vec<(String, String)> = themes
+ .iter()
+ .map(|entry| {
+ let theme =
+ try_none!(try_none!(entry.path.file_stem(), &entry.path).to_str(), &entry.path)
+ .to_string();
+
+ Ok((theme.clone(), theme))
+ })
+ .collect::>()?;
+
// (id, explanation, default value)
let settings: &[Setting] = &[
+ (
+ "Theme preferences",
+ vec![
+ Setting::from(("use-system-theme", "Use system theme", true)),
+ Setting::Select {
+ js_data_name: "preferred-dark-theme",
+ description: "Preferred dark theme",
+ default_value: "dark",
+ options: theme_names.clone(),
+ },
+ Setting::Select {
+ js_data_name: "preferred-light-theme",
+ description: "Preferred light theme",
+ default_value: "light",
+ options: theme_names,
+ },
+ ],
+ )
+ .into(),
(
"Auto-hide item declarations",
vec![
@@ -1414,16 +1481,17 @@ fn settings(root_path: &str, suffix: &str) -> String {
("line-numbers", "Show line numbers on code examples", false).into(),
("disable-shortcuts", "Disable keyboard shortcuts", false).into(),
];
- format!(
+
+ Ok(format!(
"\
- Rustdoc settings\
-
\
-{}
\
-",
- settings.iter().map(|s| s.display()).collect::(),
+ Rustdoc settings\
+ \
+ {}
\
+ ",
+ settings.iter().map(|s| s.display(root_path, suffix)).collect::(),
root_path,
suffix
- )
+ ))
}
impl Context {
diff --git a/src/librustdoc/html/static/settings.css b/src/librustdoc/html/static/settings.css
index d03cf7fcc45..4bacd7b245b 100644
--- a/src/librustdoc/html/static/settings.css
+++ b/src/librustdoc/html/static/settings.css
@@ -4,7 +4,6 @@
}
.setting-line > div {
- max-width: calc(100% - 74px);
display: inline-block;
vertical-align: top;
font-size: 17px;
@@ -30,6 +29,38 @@
display: none;
}
+.select-wrapper {
+ float: right;
+ position: relative;
+ height: 27px;
+ min-width: 25%;
+}
+
+.select-wrapper select {
+ appearance: none;
+ -moz-appearance: none;
+ -webkit-appearance: none;
+ background: none;
+ border: 2px solid #ccc;
+ padding-right: 28px;
+ width: 100%;
+}
+
+.select-wrapper img {
+ pointer-events: none;
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ background: #ccc;
+ height: 100%;
+ width: 28px;
+ padding: 0px 4px;
+}
+
+.select-wrapper select option {
+ color: initial;
+}
+
.slider {
position: absolute;
cursor: pointer;
diff --git a/src/librustdoc/html/static/settings.js b/src/librustdoc/html/static/settings.js
index 427a74c0c87..00a01ac30bc 100644
--- a/src/librustdoc/html/static/settings.js
+++ b/src/librustdoc/html/static/settings.js
@@ -1,30 +1,56 @@
// Local js definitions:
-/* global getCurrentValue, updateLocalStorage */
+/* global getCurrentValue, updateLocalStorage, updateSystemTheme */
(function () {
- function changeSetting(settingName, isEnabled) {
- updateLocalStorage('rustdoc-' + settingName, isEnabled);
+ function changeSetting(settingName, value) {
+ updateLocalStorage("rustdoc-" + settingName, value);
+
+ switch (settingName) {
+ case "preferred-dark-theme":
+ case "preferred-light-theme":
+ case "use-system-theme":
+ updateSystemTheme();
+ break;
+ }
}
function getSettingValue(settingName) {
- return getCurrentValue('rustdoc-' + settingName);
+ return getCurrentValue("rustdoc-" + settingName);
}
function setEvents() {
- var elems = document.getElementsByClassName("slider");
- if (!elems || elems.length === 0) {
- return;
- }
- for (var i = 0; i < elems.length; ++i) {
- var toggle = elems[i].previousElementSibling;
- var settingId = toggle.id;
- var settingValue = getSettingValue(settingId);
- if (settingValue !== null) {
- toggle.checked = settingValue === "true";
+ var elems = {
+ toggles: document.getElementsByClassName("slider"),
+ selects: document.getElementsByClassName("select-wrapper")
+ };
+ var i;
+
+ if (elems.toggles && elems.toggles.length > 0) {
+ for (i = 0; i < elems.toggles.length; ++i) {
+ var toggle = elems.toggles[i].previousElementSibling;
+ var settingId = toggle.id;
+ var settingValue = getSettingValue(settingId);
+ if (settingValue !== null) {
+ toggle.checked = settingValue === "true";
+ }
+ toggle.onchange = function() {
+ changeSetting(this.id, this.checked);
+ };
+ }
+ }
+
+ if (elems.selects && elems.selects.length > 0) {
+ for (i = 0; i < elems.selects.length; ++i) {
+ var select = elems.selects[i].getElementsByTagName("select")[0];
+ var settingId = select.id;
+ var settingValue = getSettingValue(settingId);
+ if (settingValue !== null) {
+ select.value = settingValue;
+ }
+ select.onchange = function() {
+ changeSetting(this.id, this.value);
+ };
}
- toggle.onchange = function() {
- changeSetting(this.id, this.checked);
- };
}
}
diff --git a/src/librustdoc/html/static/storage.js b/src/librustdoc/html/static/storage.js
index 0a2fae274fa..a027d6845ea 100644
--- a/src/librustdoc/html/static/storage.js
+++ b/src/librustdoc/html/static/storage.js
@@ -1,8 +1,10 @@
// From rust:
/* global resourcesSuffix */
+var darkThemes = ["dark", "ayu"];
var currentTheme = document.getElementById("themeStyle");
var mainTheme = document.getElementById("mainThemeStyle");
+var localStoredTheme = getCurrentValue("rustdoc-theme");
var savedHref = [];
@@ -110,19 +112,90 @@ function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) {
});
if (found === true) {
styleElem.href = newHref;
- // If this new value comes from a system setting or from the previously saved theme, no
- // need to save it.
+ // If this new value comes from a system setting or from the previously
+ // saved theme, no need to save it.
if (saveTheme === true) {
updateLocalStorage("rustdoc-theme", newTheme);
}
}
}
-function getSystemValue() {
- var property = getComputedStyle(document.documentElement).getPropertyValue('content');
- return property.replace(/[\"\']/g, "");
+function useSystemTheme(value) {
+ if (value === undefined) {
+ value = true;
+ }
+
+ updateLocalStorage("rustdoc-use-system-theme", value);
+
+ // update the toggle if we're on the settings page
+ var toggle = document.getElementById("use-system-theme");
+ if (toggle && toggle instanceof HTMLInputElement) {
+ toggle.checked = value;
+ }
}
-switchTheme(currentTheme, mainTheme,
- getCurrentValue("rustdoc-theme") || getSystemValue() || "light",
- false);
+var updateSystemTheme = (function() {
+ if (!window.matchMedia) {
+ // fallback to the CSS computed value
+ return function() {
+ let cssTheme = getComputedStyle(document.documentElement)
+ .getPropertyValue('content');
+
+ switchTheme(
+ currentTheme,
+ mainTheme,
+ JSON.parse(cssTheme) || light,
+ true
+ );
+ };
+ }
+
+ // only listen to (prefers-color-scheme: dark) because light is the default
+ var mql = window.matchMedia("(prefers-color-scheme: dark)");
+
+ function handlePreferenceChange(mql) {
+ // maybe the user has disabled the setting in the meantime!
+ if (getCurrentValue("rustdoc-use-system-theme") !== "false") {
+ var lightTheme = getCurrentValue("rustdoc-preferred-light-theme") || "light";
+ var darkTheme = getCurrentValue("rustdoc-preferred-dark-theme") || "dark";
+
+ if (mql.matches) {
+ // prefers a dark theme
+ switchTheme(currentTheme, mainTheme, darkTheme, true);
+ } else {
+ // prefers a light theme, or has no preference
+ switchTheme(currentTheme, mainTheme, lightTheme, true);
+ }
+
+ // note: we save the theme so that it doesn't suddenly change when
+ // the user disables "use-system-theme" and reloads the page or
+ // navigates to another page
+ }
+ }
+
+ mql.addListener(handlePreferenceChange);
+
+ return function() {
+ handlePreferenceChange(mql);
+ };
+})();
+
+if (getCurrentValue("rustdoc-use-system-theme") !== "false" && window.matchMedia) {
+ // update the preferred dark theme if the user is already using a dark theme
+ // See https://github.com/rust-lang/rust/pull/77809#issuecomment-707875732
+ if (getCurrentValue("rustdoc-use-system-theme") === null
+ && getCurrentValue("rustdoc-preferred-dark-theme") === null
+ && darkThemes.indexOf(localStoredTheme) >= 0) {
+ updateLocalStorage("rustdoc-preferred-dark-theme", localStoredTheme);
+ }
+
+ // call the function to initialize the theme at least once!
+ updateSystemTheme();
+} else {
+ switchTheme(
+ currentTheme,
+ mainTheme,
+ getCurrentValue("rustdoc-theme") || "light",
+ false
+ );
+}