Skip to content

Commit

Permalink
Add a setting to use the system theme
Browse files Browse the repository at this point in the history
  • Loading branch information
nasso committed Oct 13, 2020
1 parent 790d19c commit 45f6762
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 36 deletions.
103 changes: 88 additions & 15 deletions src/librustdoc/html/render/mod.rs
Expand Up @@ -575,7 +575,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())?;
Expand Down Expand Up @@ -810,6 +811,7 @@ themePicker.onblur = handleThemeButtonsBlur;
but.textContent = item;
but.onclick = function(el) {{
switchTheme(currentTheme, mainTheme, item, true);
useSystemTheme(false);
}};
but.onblur = handleThemeButtonsBlur;
themes.appendChild(but);
Expand Down Expand Up @@ -1343,22 +1345,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!(
"<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 { ref js_data_name, ref description, ref default_value } => format!(
"<div class='setting-line'>\
<label class='toggle'>\
<input type='checkbox' id='{}' {}>\
Expand All @@ -1370,13 +1385,40 @@ impl Setting {
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 }
}
}

Expand All @@ -1389,9 +1431,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![
Expand All @@ -1413,16 +1485,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 {
Expand Down
40 changes: 39 additions & 1 deletion src/librustdoc/html/static/settings.css
Expand Up @@ -4,7 +4,6 @@
}

.setting-line > div {
max-width: calc(100% - 74px);
display: inline-block;
vertical-align: top;
font-size: 17px;
Expand All @@ -30,6 +29,45 @@
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;
Expand Down
53 changes: 39 additions & 14 deletions src/librustdoc/html/static/settings.js
Expand Up @@ -2,29 +2,54 @@
/* 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) {
return getCurrentValue('rustdoc-' + 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);
};
}
}

Expand Down
72 changes: 66 additions & 6 deletions src/librustdoc/html/static/storage.js
Expand Up @@ -118,11 +118,71 @@ function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) {
}
}

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);
}

0 comments on commit 45f6762

Please sign in to comment.