use eframe::egui::{self, vec2, Color32, RichText, Sense};
use crate::i18n::{self, Key, Lang};
use crate::model::{Density, Theme, PALETTE};
use crate::ui::{style, Action};
use crate::app::App;
pub fn draw(app: &mut App, ctx: &egui::Context, actions: &mut Vec<Action>) {
if !app.settings_open {
return;
}
let lang = app.ws.settings.lang;
let settings = app.ws.settings.clone();
let pal = style::palette(style::is_dark(ctx, settings.theme), settings.accent);
let mut close = false;
egui::Modal::new(egui::Id::new("fd-settings")).show(ctx, |ui| {
ui.set_width(640.0);
ui.horizontal(|ui| {
ui.heading(i18n::t(lang, Key::Settings));
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if ui
.button(i18n::t(lang, Key::Close))
.clicked()
{
close = true;
}
});
});
ui.separator();
egui::ScrollArea::vertical()
.id_salt("fd-settings-scroll")
.max_height(540.0)
.show(ui, |ui| {
section(ui, i18n::t(lang, Key::LanguageLabel), &pal);
ui.horizontal(|ui| {
for candidate in Lang::all() {
let mut current = settings.lang;
if ui
.selectable_value(&mut current, candidate, candidate.label())
.clicked()
{
actions.push(Action::SetLang(candidate));
}
}
});
section(ui, i18n::t(lang, Key::ThemeLabel), &pal);
ui.horizontal(|ui| {
for (theme, key) in [
(Theme::Dark, Key::ThemeDark),
(Theme::Light, Key::ThemeLight),
(Theme::System, Key::ThemeSystem),
] {
let mut current = settings.theme;
if ui
.selectable_value(&mut current, theme, i18n::t(lang, key))
.clicked()
{
actions.push(Action::SetTheme(theme));
}
}
});
section(ui, i18n::t(lang, Key::AccentLabel), &pal);
ui.horizontal(|ui| {
for color in PALETTE {
let (dot, hit) = ui.allocate_exact_size(vec2(20.0, 20.0), Sense::click());
let is_current = color == settings.accent;
ui.painter().circle_filled(
dot.center(),
if is_current { 8.0 } else { 6.0 },
Color32::from_rgb(color[0], color[1], color[2]),
);
if is_current {
ui.painter().circle_stroke(
dot.center(),
9.0,
egui::Stroke::new(1.5, pal.text),
);
}
if hit.clicked() {
actions.push(Action::SetAccent(color));
}
if hit.hovered() {
ui.ctx().set_cursor_icon(egui::CursorIcon::PointingHand);
}
}
});
section(ui, i18n::t(lang, Key::FontLabel), &pal);
let mut current = settings.font_family.clone();
if ui
.selectable_value(&mut current, String::new(), i18n::t(lang, Key::FontBuiltin))
.clicked()
{
actions.push(Action::SetFont(String::new()));
}
ui.add(
egui::TextEdit::singleline(&mut app.font_filter)
.hint_text(i18n::t(lang, Key::FontSearchHint))
.desired_width(280.0),
);
let filter = app.font_filter.trim().to_lowercase();
let fonts = crate::shell::fonts::installed_fonts();
let mut shown = 0usize;
egui::ScrollArea::vertical()
.id_salt("fd-font-list")
.max_height(190.0)
.auto_shrink([false, false])
.show(ui, |ui| {
for entry in fonts {
if !filter.is_empty() && !entry.family.to_lowercase().contains(&filter) {
continue;
}
shown += 1;
let mut selected = current.clone();
if ui
.selectable_value(&mut selected, entry.family.clone(), &entry.family)
.clicked()
{
actions.push(Action::SetFont(entry.family.clone()));
}
}
});
ui.label(
RichText::new(i18n::tf(lang, Key::FontsFound, &[&shown.to_string()]))
.color(pal.muted)
.size(11.5),
);
section(ui, i18n::t(lang, Key::FontSizeLabel), &pal);
ui.horizontal(|ui| {
for (index, key) in [
(0usize, Key::ScaleSmall),
(1, Key::ScaleNormal),
(2, Key::ScaleLarge),
(3, Key::ScaleHuge),
] {
let mut current = settings.scale_index();
if ui
.selectable_value(&mut current, index, i18n::t(lang, key))
.clicked()
{
actions.push(Action::SetScaleIndex(index));
}
}
});
section(ui, i18n::t(lang, Key::DensityLabel), &pal);
ui.horizontal(|ui| {
for (density, key) in [
(Density::Cozy, Key::DensityCozy),
(Density::Compact, Key::DensityCompact),
] {
let mut current = settings.density;
if ui
.selectable_value(&mut current, density, i18n::t(lang, key))
.clicked()
{
actions.push(Action::SetDensity(density));
}
}
});
section(ui, i18n::t(lang, Key::Search), &pal);
let mut show_hidden = app.ws.settings.show_hidden;
if ui
.checkbox(&mut show_hidden, i18n::t(lang, Key::HiddenFiles))
.clicked()
{
actions.push(Action::ToggleHidden);
}
let mut respect = app.ws.settings.search_respect_ignore;
if ui
.checkbox(&mut respect, i18n::t(lang, Key::RespectGitignore))
.clicked()
{
actions.push(Action::ToggleRespectIgnore);
}
ui.add_space(4.0);
let scope_label = if app.ws.settings.scope_whole_project {
i18n::t(lang, Key::ScopeAll)
} else {
i18n::t(lang, Key::ScopeCurrent)
};
if ui
.button(scope_label)
.on_hover_text(i18n::t(lang, Key::ScopeTip))
.clicked()
{
actions.push(Action::ToggleSearchScope);
}
ui.add_space(6.0);
section(ui, i18n::t(lang, Key::DataDirLabel), &pal);
let current = crate::install::data_dir();
ui.label(
RichText::new(crate::fsops::elide_path(¤t, 64))
.color(pal.muted)
.size(11.5),
);
ui.add_space(2.0);
if ui
.button(i18n::t(lang, Key::DataDirChange))
.on_hover_text(i18n::t(lang, Key::DataDirChangeTip))
.clicked()
{
actions.push(Action::ChangeDataDir);
}
ui.add_space(2.0);
ui.label(
RichText::new(i18n::t(lang, Key::DataDirNote))
.color(pal.muted)
.size(11.0),
);
ui.add_space(6.0);
});
});
if close || ctx.input(|i| i.key_pressed(egui::Key::Escape)) {
actions.push(Action::CloseSettings);
}
}
fn section(ui: &mut egui::Ui, title: &str, pal: &style::Palette) {
ui.add_space(10.0);
ui.label(RichText::new(title).strong().color(pal.text));
ui.add_space(2.0);
}