Auto merge of #77809 - nasso:master, r=jyn514,guillaumegomez
Add settings to rustdoc to use the system theme
This PR adds new settings to `rustdoc` to use the operating system color scheme.

`rustdoc` actually had [basic support for this](b1af43bc63/src/librustdoc/html/static/storage.js (L121)
), but the setting wasn't visible and couldn't be set back once the theme was explicitly set by the user. It also didn't update if the operating system theme preference changed while viewing a page.
I'm using [this method](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Testing_media_queries#Receiving_query_notifications) to query and listen to changes to the `(prefers-color-scheme: dark)` media query. I kept the old method (based on `getComputedStyle`) as a fallback in case the user-agent doesn't support `window.matchMedia` (so like... [pretty much nobody](https://caniuse.com/?search=matchMedia)).
Since there's now more than one official ""dark"" theme in `rustdoc` (and also to support custom/third-party themes), the preferred dark and light themes can be configured in the settings page (the defaults are just "dark" and "light").
This is also my very first "proper" PR to Rust! Please let me know if I did anything wrong :).
This commit is contained in:
commit
6999ff33c9
4 changed files with 241 additions and 43 deletions
|
@ -576,7 +576,8 @@ impl FormatRenderer for Context {
|
|||
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 @@ themePicker.onblur = handleThemeButtonsBlur;
|
|||
but.textContent = item;
|
||||
but.onclick = function(el) {{
|
||||
switchTheme(currentTheme, mainTheme, item, true);
|
||||
useSystemTheme(false);
|
||||
}};
|
||||
but.onblur = handleThemeButtonsBlur;
|
||||
themes.appendChild(but);
|
||||
|
@ -1344,22 +1346,35 @@ impl AllTypes {
|
|||
|
||||
#[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!(
|
||||
Setting::Section { description, ref sub_settings } => format!(
|
||||
"<div class='setting-line'>\
|
||||
<div class='title'>{}</div>\
|
||||
<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 { js_data_name, description, default_value } => format!(
|
||||
"<div class='setting-line'>\
|
||||
<label class='toggle'>\
|
||||
<input type='checkbox' id='{}' {}>\
|
||||
|
@ -1368,16 +1383,38 @@ impl Setting {
|
|||
<div>{}</div>\
|
||||
</div>",
|
||||
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!(
|
||||
"<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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1390,9 +1427,39 @@ impl<T: Into<Setting>> From<(&'static str, Vec<T>)> for Setting {
|
|||
}
|
||||
}
|
||||
|
||||
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![
|
||||
|
@ -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!(
|
||||
"<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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue