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())?;
but.textContent = item;
but.onclick = function(el) {{
switchTheme(currentTheme, mainTheme, item, true);
+ useSystemTheme(false);
}};
but.onblur = handleThemeButtonsBlur;
themes.appendChild(but);
#[derive(Debug)]
enum Setting {
- Section { description: &'static str, sub_settings: Vec<Setting> },
- Entry { js_data_name: &'static str, description: &'static str, default_value: bool },
+ Section {
+ description: &'static str,
+ sub_settings: Vec<Setting>,
+ },
+ 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!(
"<div class='setting-line'>\
<div class='sub-settings'>{}</div>
</div>",
description,
- sub_settings.iter().map(|s| s.display()).collect::<String>()
+ sub_settings.iter().map(|s| s.display(root_path, suffix)).collect::<String>()
),
- Setting::Entry { ref js_data_name, ref description, ref default_value } => format!(
+ Setting::Toggle { ref js_data_name, ref description, ref default_value } => format!(
"<div class='setting-line'>\
<label class='toggle'>\
<input type='checkbox' id='{}' {}>\
if *default_value { " checked" } else { "" },
description,
),
+ Setting::Select {
+ ref js_data_name,
+ ref description,
+ ref default_value,
+ ref options,
+ } => format!(
+ "<div class='setting-line'>\
+ <div>{}</div>\
+ <label class='select-wrapper'>\
+ <select id='{}' autocomplete='off'>{}</select>\
+ <img src='{}down-arrow{}.svg' alt='Select item'>\
+ </label>\
+ </div>",
+ description,
+ js_data_name,
+ options
+ .iter()
+ .map(|opt| format!(
+ "<option value=\"{}\" {}>{}</option>",
+ opt.0,
+ if &opt.0 == *default_value { "selected" } else { "" },
+ opt.1,
+ ))
+ .collect::<String>(),
+ 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 }
}
}
}
}
-fn settings(root_path: &str, suffix: &str) -> String {
+fn settings(root_path: &str, suffix: &str, themes: &[StylePath]) -> Result<String, Error> {
+ 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::<Result<_, Error>>()?;
+
// (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![
("line-numbers", "Show line numbers on code examples", false).into(),
("disable-shortcuts", "Disable keyboard shortcuts", false).into(),
];
- format!(
+
+ Ok(format!(
"<h1 class='fqn'>\
- <span class='in-band'>Rustdoc settings</span>\
-</h1>\
-<div class='settings'>{}</div>\
-<script src='{}settings{}.js'></script>",
- settings.iter().map(|s| s.display()).collect::<String>(),
+ <span class='in-band'>Rustdoc settings</span>\
+ </h1>\
+ <div class='settings'>{}</div>\
+ <script src='{}settings{}.js'></script>",
+ settings.iter().map(|s| s.display(root_path, suffix)).collect::<String>(),
root_path,
suffix
- )
+ ))
}
impl Context {
/* global getCurrentValue, updateLocalStorage */
(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) {
}
function setEvents() {
- var elems = document.getElementsByClassName("slider");
- if (!elems || elems.length === 0) {
- return;
+ var elems = {
+ toggles: document.getElementsByClassName("slider"),
+ selects: document.getElementsByClassName("select-wrapper")
+ };
+
+ if (elems.toggles && elems.toggles.length > 0) {
+ for (var 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);
+ };
+ }
}
- 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";
+
+ if (elems.selects && elems.selects.length > 0) {
+ for (var 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);
- };
}
}
}
}
-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) {
+ // call the function to initialize the theme at least once!
+ updateSystemTheme();
+} else {
+ switchTheme(currentTheme, mainTheme,
+ getCurrentValue("rustdoc-theme") || "light",
+ false);
+}