Theme · Importing: VS Code themes read into Ely's palette, code palettes to choose
settings::read_vscode_theme reads a VS Code color theme, comments and trailing commas and all: its type picks the mode, its colors and token scopes name what they give, and the rest keep Ely's palette of that mode. settings::ThemeImporter takes one dropped or browsed for, shows its name, mode and count, and hands it over on Apply; a file that does not read says why. theme::syntax_themes offers Ely's code colors, Quiet and Paper, chosen through settings::SyntaxThemePicker, radio cards with a line of code in each palette. A card's words now shrink as a plain block, so a preview wraps inside the card at 280px. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
38ee858b27
commit
d5cfaeb19c
13 files changed
+895
-9
No files matched your search
@@ -0,0 +1,115 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
App, ElementId, FontStyle, HighlightStyle, Hsla, InteractiveElement, IntoElement,
|
||||
ParentElement, RenderOnce, SharedString, Styled, StyledText, Window, div,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
forms::RadioCard,
|
||||
theme::{ActiveTheme, Mode, Radius, Syntax, SyntaxTheme, TextSize},
|
||||
typography::literal,
|
||||
};
|
||||
|
||||
/// A line of code in `syntax`'s colors.
|
||||
fn sample(syntax: &Syntax, cx: &App) -> impl IntoElement + use<> {
|
||||
let theme = cx.theme();
|
||||
let text = "fn answer() -> u32 { 42 } // truth";
|
||||
let ink = |color: Hsla| HighlightStyle {
|
||||
color: Some(color),
|
||||
..Default::default()
|
||||
};
|
||||
let spans = [
|
||||
(0..2, ink(syntax.keyword)),
|
||||
(3..9, ink(syntax.function)),
|
||||
(9..11, ink(syntax.punctuation)),
|
||||
(12..14, ink(syntax.operator)),
|
||||
(15..18, ink(syntax.type_name)),
|
||||
(19..20, ink(syntax.punctuation)),
|
||||
(21..23, ink(syntax.number)),
|
||||
(24..25, ink(syntax.punctuation)),
|
||||
(
|
||||
26..34,
|
||||
HighlightStyle {
|
||||
color: Some(syntax.comment),
|
||||
font_style: Some(FontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
];
|
||||
literal(div())
|
||||
.debug_selector(|| "syntax-sample".into())
|
||||
.px_3()
|
||||
.py_2()
|
||||
.rounded(theme.radius(Radius::Md))
|
||||
.bg(theme.colors.sunken)
|
||||
.font_family(theme.mono_family.clone())
|
||||
.text_size(theme.text_size(TextSize::Sm))
|
||||
.text_color(theme.colors.fg)
|
||||
.child(StyledText::new(text).with_highlights(spans))
|
||||
}
|
||||
|
||||
type OnName = Rc<dyn Fn(&'static str, &mut Window, &mut App)>;
|
||||
|
||||
/// Code palettes to choose from, each a card with a line of code in its colors for the mode shown.
|
||||
#[derive(IntoElement)]
|
||||
pub struct SyntaxThemePicker {
|
||||
id: ElementId,
|
||||
themes: Vec<SyntaxTheme>,
|
||||
selected: SharedString,
|
||||
on_change: Option<OnName>,
|
||||
}
|
||||
|
||||
impl SyntaxThemePicker {
|
||||
/// `selected` names one of `themes`.
|
||||
pub fn new(
|
||||
id: impl Into<ElementId>,
|
||||
themes: impl IntoIterator<Item = SyntaxTheme>,
|
||||
selected: impl Into<SharedString>,
|
||||
) -> Self {
|
||||
let themes: Vec<SyntaxTheme> = themes.into_iter().collect();
|
||||
let selected = selected.into();
|
||||
assert!(
|
||||
themes.iter().any(|theme| theme.name == selected.as_ref()),
|
||||
"syntax theme picker: no theme {selected}"
|
||||
);
|
||||
Self {
|
||||
id: id.into(),
|
||||
themes,
|
||||
selected,
|
||||
on_change: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_change(
|
||||
mut self,
|
||||
handler: impl Fn(&'static str, &mut Window, &mut App) + 'static,
|
||||
) -> Self {
|
||||
self.on_change = Some(Rc::new(handler));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for SyntaxThemePicker {
|
||||
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let id = self.id;
|
||||
let on_change = self
|
||||
.on_change
|
||||
.unwrap_or_else(|| panic!("syntax theme picker {id:?} has no on_change"));
|
||||
let mode: Mode = cx.theme().mode();
|
||||
let cards = self.themes.iter().map(|theme| {
|
||||
let (pick, name) = (on_change.clone(), theme.name);
|
||||
RadioCard::new(
|
||||
(id.clone(), format!("theme-{name}")),
|
||||
self.selected.as_ref() == name,
|
||||
name,
|
||||
)
|
||||
.preview(sample(&theme.of(mode), cx))
|
||||
.on_select(move |window, cx| {
|
||||
log::info!("syntax theme picker: {name}");
|
||||
pick(name, window, cx)
|
||||
})
|
||||
});
|
||||
div().flex().flex_col().gap_2().children(cards)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user