1#![allow(missing_docs)]
2
3use crate::schema::{status_colors_refinement, syntax_overrides, theme_colors_refinement};
4use crate::{merge_accent_colors, merge_player_colors};
5use collections::HashMap;
6use gpui::{
7 App, Context, Font, FontFallbacks, FontStyle, Global, Pixels, Subscription, Window, px,
8};
9use refineable::Refineable;
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12pub use settings::{FontFamilyName, IconThemeName, ThemeAppearanceMode, ThemeName};
13use settings::{IntoGpui, RegisterSetting, Settings, SettingsContent};
14use std::sync::Arc;
15use theme::{Appearance, DEFAULT_ICON_THEME_NAME, SyntaxTheme, Theme};
16
17const MIN_FONT_SIZE: Pixels = px(6.0);
18const MAX_FONT_SIZE: Pixels = px(100.0);
19const MIN_LINE_HEIGHT: f32 = 1.0;
20
21#[derive(
22 Debug,
23 Default,
24 PartialEq,
25 Eq,
26 PartialOrd,
27 Ord,
28 Hash,
29 Clone,
30 Copy,
31 Serialize,
32 Deserialize,
33 JsonSchema,
34)]
35
36/// Specifies the density of the UI.
37/// Note: This setting is still experimental. See [this tracking issue](https://github.com/zed-industries/zed/issues/18078)
38#[serde(rename_all = "snake_case")]
39pub enum UiDensity {
40 /// A denser UI with tighter spacing and smaller elements.
41 #[serde(alias = "compact")]
42 Compact,
43 #[default]
44 #[serde(alias = "default")]
45 /// The default UI density.
46 Default,
47 #[serde(alias = "comfortable")]
48 /// A looser UI with more spacing and larger elements.
49 Comfortable,
50}
51
52impl UiDensity {
53 /// The spacing ratio of a given density.
54 /// TODO: Standardize usage throughout the app or remove
55 pub fn spacing_ratio(self) -> f32 {
56 match self {
57 UiDensity::Compact => 0.75,
58 UiDensity::Default => 1.0,
59 UiDensity::Comfortable => 1.25,
60 }
61 }
62}
63
64impl From<String> for UiDensity {
65 fn from(s: String) -> Self {
66 match s.as_str() {
67 "compact" => Self::Compact,
68 "default" => Self::Default,
69 "comfortable" => Self::Comfortable,
70 _ => Self::default(),
71 }
72 }
73}
74
75impl From<UiDensity> for String {
76 fn from(val: UiDensity) -> Self {
77 match val {
78 UiDensity::Compact => "compact".to_string(),
79 UiDensity::Default => "default".to_string(),
80 UiDensity::Comfortable => "comfortable".to_string(),
81 }
82 }
83}
84
85impl From<settings::UiDensity> for UiDensity {
86 fn from(val: settings::UiDensity) -> Self {
87 match val {
88 settings::UiDensity::Compact => Self::Compact,
89 settings::UiDensity::Default => Self::Default,
90 settings::UiDensity::Comfortable => Self::Comfortable,
91 }
92 }
93}
94
95pub fn appearance_to_mode(appearance: Appearance) -> ThemeAppearanceMode {
96 match appearance {
97 Appearance::Light => ThemeAppearanceMode::Light,
98 Appearance::Dark => ThemeAppearanceMode::Dark,
99 }
100}
101
102/// Customizable settings for the UI and theme system.
103#[derive(Clone, PartialEq, RegisterSetting)]
104pub struct ThemeSettings {
105 /// The UI font size. Determines the size of text in the UI,
106 /// as well as the size of a [gpui::Rems] unit.
107 ///
108 /// Changing this will impact the size of all UI elements.
109 ui_font_size: Pixels,
110 /// The font used for UI elements.
111 pub ui_font: Font,
112 /// The font size used for buffers, and the terminal.
113 ///
114 /// The terminal font size can be overridden using it's own setting.
115 buffer_font_size: Pixels,
116 /// The font used for buffers, and the terminal.
117 ///
118 /// The terminal font family can be overridden using it's own setting.
119 pub buffer_font: Font,
120 /// The agent font size. Determines the size of text in the agent panel. Falls back to the UI font size if unset.
121 agent_ui_font_size: Option<Pixels>,
122 /// The agent buffer font size. Determines the size of user messages in the agent panel.
123 agent_buffer_font_size: Option<Pixels>,
124 /// The line height for buffers, and the terminal.
125 ///
126 /// Changing this may affect the spacing of some UI elements.
127 ///
128 /// The terminal font family can be overridden using it's own setting.
129 pub buffer_line_height: BufferLineHeight,
130 /// The current theme selection.
131 pub theme: ThemeSelection,
132 /// Manual overrides for the active theme.
133 ///
134 /// Note: This setting is still experimental. See [this tracking issue](https://github.com/zed-industries/zed/issues/18078)
135 pub experimental_theme_overrides: Option<settings::ThemeStyleContent>,
136 /// Manual overrides per theme
137 pub theme_overrides: HashMap<String, settings::ThemeStyleContent>,
138 /// The current icon theme selection.
139 pub icon_theme: IconThemeSelection,
140 /// The density of the UI.
141 /// Note: This setting is still experimental. See [this tracking issue](
142 pub ui_density: UiDensity,
143 /// The amount of fading applied to unnecessary code.
144 pub unnecessary_code_fade: f32,
145}
146
147/// Returns the name of the default theme for the given [`Appearance`].
148pub fn default_theme(appearance: Appearance) -> &'static str {
149 match appearance {
150 Appearance::Light => settings::DEFAULT_LIGHT_THEME,
151 Appearance::Dark => settings::DEFAULT_DARK_THEME,
152 }
153}
154
155#[derive(Default)]
156struct BufferFontSize(Pixels);
157
158impl Global for BufferFontSize {}
159
160#[derive(Default)]
161pub(crate) struct UiFontSize(Pixels);
162
163impl Global for UiFontSize {}
164
165/// In-memory override for the font size in the agent panel.
166#[derive(Default)]
167pub struct AgentFontSize(Pixels);
168
169impl Global for AgentFontSize {}
170
171/// Represents the selection of a theme, which can be either static or dynamic.
172#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
173#[serde(untagged)]
174pub enum ThemeSelection {
175 /// A static theme selection, represented by a single theme name.
176 Static(ThemeName),
177 /// A dynamic theme selection, which can change based the [ThemeMode].
178 Dynamic {
179 /// The mode used to determine which theme to use.
180 #[serde(default)]
181 mode: ThemeAppearanceMode,
182 /// The theme to use for light mode.
183 light: ThemeName,
184 /// The theme to use for dark mode.
185 dark: ThemeName,
186 },
187}
188
189impl From<settings::ThemeSelection> for ThemeSelection {
190 fn from(selection: settings::ThemeSelection) -> Self {
191 match selection {
192 settings::ThemeSelection::Static(theme) => ThemeSelection::Static(theme),
193 settings::ThemeSelection::Dynamic { mode, light, dark } => {
194 ThemeSelection::Dynamic { mode, light, dark }
195 }
196 }
197 }
198}
199
200impl ThemeSelection {
201 /// Returns the theme name for the selected [ThemeMode].
202 pub fn name(&self, system_appearance: Appearance) -> ThemeName {
203 match self {
204 Self::Static(theme) => theme.clone(),
205 Self::Dynamic { mode, light, dark } => match mode {
206 ThemeAppearanceMode::Light => light.clone(),
207 ThemeAppearanceMode::Dark => dark.clone(),
208 ThemeAppearanceMode::System => match system_appearance {
209 Appearance::Light => light.clone(),
210 Appearance::Dark => dark.clone(),
211 },
212 },
213 }
214 }
215
216 /// Returns the [ThemeMode] for the [ThemeSelection].
217 pub fn mode(&self) -> Option<ThemeAppearanceMode> {
218 match self {
219 ThemeSelection::Static(_) => None,
220 ThemeSelection::Dynamic { mode, .. } => Some(*mode),
221 }
222 }
223}
224
225/// Represents the selection of an icon theme, which can be either static or dynamic.
226#[derive(Clone, Debug, PartialEq, Eq)]
227pub enum IconThemeSelection {
228 /// A static icon theme selection, represented by a single icon theme name.
229 Static(IconThemeName),
230 /// A dynamic icon theme selection, which can change based on the [`ThemeMode`].
231 Dynamic {
232 /// The mode used to determine which theme to use.
233 mode: ThemeAppearanceMode,
234 /// The icon theme to use for light mode.
235 light: IconThemeName,
236 /// The icon theme to use for dark mode.
237 dark: IconThemeName,
238 },
239}
240
241impl From<settings::IconThemeSelection> for IconThemeSelection {
242 fn from(selection: settings::IconThemeSelection) -> Self {
243 match selection {
244 settings::IconThemeSelection::Static(theme) => IconThemeSelection::Static(theme),
245 settings::IconThemeSelection::Dynamic { mode, light, dark } => {
246 IconThemeSelection::Dynamic { mode, light, dark }
247 }
248 }
249 }
250}
251
252impl IconThemeSelection {
253 /// Returns the icon theme name based on the given [`Appearance`].
254 pub fn name(&self, system_appearance: Appearance) -> IconThemeName {
255 match self {
256 Self::Static(theme) => theme.clone(),
257 Self::Dynamic { mode, light, dark } => match mode {
258 ThemeAppearanceMode::Light => light.clone(),
259 ThemeAppearanceMode::Dark => dark.clone(),
260 ThemeAppearanceMode::System => match system_appearance {
261 Appearance::Light => light.clone(),
262 Appearance::Dark => dark.clone(),
263 },
264 },
265 }
266 }
267
268 /// Returns the [`ThemeMode`] for the [`IconThemeSelection`].
269 pub fn mode(&self) -> Option<ThemeAppearanceMode> {
270 match self {
271 IconThemeSelection::Static(_) => None,
272 IconThemeSelection::Dynamic { mode, .. } => Some(*mode),
273 }
274 }
275}
276
277/// Sets the theme for the given appearance to the theme with the specified name.
278///
279/// The caller should make sure that the [`Appearance`] matches the theme associated with the name.
280///
281/// If the current [`ThemeAppearanceMode`] is set to [`System`] and the user's system [`Appearance`]
282/// is different than the new theme's [`Appearance`], this function will update the
283/// [`ThemeAppearanceMode`] to the new theme's appearance in order to display the new theme.
284///
285/// [`System`]: ThemeAppearanceMode::System
286pub fn set_theme(
287 current: &mut SettingsContent,
288 theme_name: impl Into<Arc<str>>,
289 theme_appearance: Appearance,
290 system_appearance: Appearance,
291) {
292 let theme_name = ThemeName(theme_name.into());
293
294 let Some(selection) = current.theme.theme.as_mut() else {
295 current.theme.theme = Some(settings::ThemeSelection::Static(theme_name));
296 return;
297 };
298
299 match selection {
300 settings::ThemeSelection::Static(theme) => {
301 *theme = theme_name;
302 }
303 settings::ThemeSelection::Dynamic { mode, light, dark } => {
304 match theme_appearance {
305 Appearance::Light => *light = theme_name,
306 Appearance::Dark => *dark = theme_name,
307 }
308
309 let should_update_mode =
310 !(mode == &ThemeAppearanceMode::System && theme_appearance == system_appearance);
311
312 if should_update_mode {
313 *mode = appearance_to_mode(theme_appearance);
314 }
315 }
316 }
317}
318
319/// Sets the icon theme for the given appearance to the icon theme with the specified name.
320pub fn set_icon_theme(
321 current: &mut SettingsContent,
322 icon_theme_name: IconThemeName,
323 appearance: Appearance,
324) {
325 if let Some(selection) = current.theme.icon_theme.as_mut() {
326 let icon_theme_to_update = match selection {
327 settings::IconThemeSelection::Static(theme) => theme,
328 settings::IconThemeSelection::Dynamic { mode, light, dark } => match mode {
329 ThemeAppearanceMode::Light => light,
330 ThemeAppearanceMode::Dark => dark,
331 ThemeAppearanceMode::System => match appearance {
332 Appearance::Light => light,
333 Appearance::Dark => dark,
334 },
335 },
336 };
337
338 *icon_theme_to_update = icon_theme_name;
339 } else {
340 current.theme.icon_theme = Some(settings::IconThemeSelection::Static(icon_theme_name));
341 }
342}
343
344/// Sets the mode for the theme.
345pub fn set_mode(content: &mut SettingsContent, mode: ThemeAppearanceMode) {
346 let theme = content.theme.as_mut();
347
348 if let Some(selection) = theme.theme.as_mut() {
349 match selection {
350 settings::ThemeSelection::Static(_) => {
351 *selection = settings::ThemeSelection::Dynamic {
352 mode: ThemeAppearanceMode::System,
353 light: ThemeName(settings::DEFAULT_LIGHT_THEME.into()),
354 dark: ThemeName(settings::DEFAULT_DARK_THEME.into()),
355 };
356 }
357 settings::ThemeSelection::Dynamic {
358 mode: mode_to_update,
359 ..
360 } => *mode_to_update = mode,
361 }
362 } else {
363 theme.theme = Some(settings::ThemeSelection::Dynamic {
364 mode,
365 light: ThemeName(settings::DEFAULT_LIGHT_THEME.into()),
366 dark: ThemeName(settings::DEFAULT_DARK_THEME.into()),
367 });
368 }
369
370 if let Some(selection) = theme.icon_theme.as_mut() {
371 match selection {
372 settings::IconThemeSelection::Static(icon_theme) => {
373 *selection = settings::IconThemeSelection::Dynamic {
374 mode,
375 light: icon_theme.clone(),
376 dark: icon_theme.clone(),
377 };
378 }
379 settings::IconThemeSelection::Dynamic {
380 mode: mode_to_update,
381 ..
382 } => *mode_to_update = mode,
383 }
384 } else {
385 theme.icon_theme = Some(settings::IconThemeSelection::Static(IconThemeName(
386 DEFAULT_ICON_THEME_NAME.into(),
387 )));
388 }
389}
390
391/// The buffer's line height.
392#[derive(Clone, Copy, Debug, PartialEq, Default)]
393pub enum BufferLineHeight {
394 /// A less dense line height.
395 #[default]
396 Comfortable,
397 /// The default line height.
398 Standard,
399 /// A custom line height, where 1.0 is the font's height. Must be at least 1.0.
400 Custom(f32),
401}
402
403impl From<settings::BufferLineHeight> for BufferLineHeight {
404 fn from(value: settings::BufferLineHeight) -> Self {
405 match value {
406 settings::BufferLineHeight::Comfortable => BufferLineHeight::Comfortable,
407 settings::BufferLineHeight::Standard => BufferLineHeight::Standard,
408 settings::BufferLineHeight::Custom(line_height) => {
409 BufferLineHeight::Custom(line_height)
410 }
411 }
412 }
413}
414
415impl BufferLineHeight {
416 /// Returns the value of the line height.
417 pub fn value(&self) -> f32 {
418 match self {
419 BufferLineHeight::Comfortable => 1.618,
420 BufferLineHeight::Standard => 1.3,
421 BufferLineHeight::Custom(line_height) => *line_height,
422 }
423 }
424}
425
426impl ThemeSettings {
427 /// Returns the buffer font size.
428 pub fn buffer_font_size(&self, cx: &App) -> Pixels {
429 let font_size = cx
430 .try_global::<BufferFontSize>()
431 .map(|size| size.0)
432 .unwrap_or(self.buffer_font_size);
433 clamp_font_size(font_size)
434 }
435
436 /// Returns the UI font size.
437 pub fn ui_font_size(&self, cx: &App) -> Pixels {
438 let font_size = cx
439 .try_global::<UiFontSize>()
440 .map(|size| size.0)
441 .unwrap_or(self.ui_font_size);
442 clamp_font_size(font_size)
443 }
444
445 /// Returns the agent panel font size. Falls back to the UI font size if unset.
446 pub fn agent_ui_font_size(&self, cx: &App) -> Pixels {
447 cx.try_global::<AgentFontSize>()
448 .map(|size| size.0)
449 .or(self.agent_ui_font_size)
450 .map(clamp_font_size)
451 .unwrap_or_else(|| self.ui_font_size(cx))
452 }
453
454 /// Returns the agent panel buffer font size.
455 pub fn agent_buffer_font_size(&self, cx: &App) -> Pixels {
456 cx.try_global::<AgentFontSize>()
457 .map(|size| size.0)
458 .or(self.agent_buffer_font_size)
459 .map(clamp_font_size)
460 .unwrap_or_else(|| self.buffer_font_size(cx))
461 }
462
463 /// Returns the buffer font size, read from the settings.
464 ///
465 /// The real buffer font size is stored in-memory, to support temporary font size changes.
466 /// Use [`Self::buffer_font_size`] to get the real font size.
467 pub fn buffer_font_size_settings(&self) -> Pixels {
468 self.buffer_font_size
469 }
470
471 /// Returns the UI font size, read from the settings.
472 ///
473 /// The real UI font size is stored in-memory, to support temporary font size changes.
474 /// Use [`Self::ui_font_size`] to get the real font size.
475 pub fn ui_font_size_settings(&self) -> Pixels {
476 self.ui_font_size
477 }
478
479 /// Returns the agent font size, read from the settings.
480 ///
481 /// The real agent font size is stored in-memory, to support temporary font size changes.
482 /// Use [`Self::agent_ui_font_size`] to get the real font size.
483 pub fn agent_ui_font_size_settings(&self) -> Option<Pixels> {
484 self.agent_ui_font_size
485 }
486
487 /// Returns the agent buffer font size, read from the settings.
488 ///
489 /// The real agent buffer font size is stored in-memory, to support temporary font size changes.
490 /// Use [`Self::agent_buffer_font_size`] to get the real font size.
491 pub fn agent_buffer_font_size_settings(&self) -> Option<Pixels> {
492 self.agent_buffer_font_size
493 }
494
495 /// Returns the buffer's line height.
496 pub fn line_height(&self) -> f32 {
497 f32::max(self.buffer_line_height.value(), MIN_LINE_HEIGHT)
498 }
499
500 /// Applies the theme overrides, if there are any, to the current theme.
501 pub fn apply_theme_overrides(&self, mut arc_theme: Arc<Theme>) -> Arc<Theme> {
502 if let Some(experimental_theme_overrides) = &self.experimental_theme_overrides {
503 let mut theme = (*arc_theme).clone();
504 ThemeSettings::modify_theme(&mut theme, experimental_theme_overrides);
505 arc_theme = Arc::new(theme);
506 }
507
508 if let Some(theme_overrides) = self.theme_overrides.get(arc_theme.name.as_ref()) {
509 let mut theme = (*arc_theme).clone();
510 ThemeSettings::modify_theme(&mut theme, theme_overrides);
511 arc_theme = Arc::new(theme);
512 }
513
514 arc_theme
515 }
516
517 fn modify_theme(base_theme: &mut Theme, theme_overrides: &settings::ThemeStyleContent) {
518 if let Some(window_background_appearance) = theme_overrides.window_background_appearance {
519 base_theme.styles.window_background_appearance =
520 window_background_appearance.into_gpui();
521 }
522 let status_color_refinement = status_colors_refinement(&theme_overrides.status);
523
524 base_theme.styles.colors.refine(&theme_colors_refinement(
525 &theme_overrides.colors,
526 &status_color_refinement,
527 ));
528 base_theme.styles.status.refine(&status_color_refinement);
529 merge_player_colors(&mut base_theme.styles.player, &theme_overrides.players);
530 merge_accent_colors(&mut base_theme.styles.accents, &theme_overrides.accents);
531 base_theme.styles.syntax = SyntaxTheme::merge(
532 base_theme.styles.syntax.clone(),
533 syntax_overrides(theme_overrides),
534 );
535 }
536}
537
538/// Observe changes to the adjusted buffer font size.
539pub fn observe_buffer_font_size_adjustment<V: 'static>(
540 cx: &mut Context<V>,
541 f: impl 'static + Fn(&mut V, &mut Context<V>),
542) -> Subscription {
543 cx.observe_global::<BufferFontSize>(f)
544}
545
546/// Gets the font size, adjusted by the difference between the current buffer font size and the one set in the settings.
547pub fn adjusted_font_size(size: Pixels, cx: &App) -> Pixels {
548 let adjusted_font_size =
549 if let Some(BufferFontSize(adjusted_size)) = cx.try_global::<BufferFontSize>() {
550 let buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
551 let delta = *adjusted_size - buffer_font_size;
552 size + delta
553 } else {
554 size
555 };
556 clamp_font_size(adjusted_font_size)
557}
558
559/// Adjusts the buffer font size.
560pub fn adjust_buffer_font_size(cx: &mut App, f: impl FnOnce(Pixels) -> Pixels) {
561 let buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
562 let adjusted_size = cx
563 .try_global::<BufferFontSize>()
564 .map_or(buffer_font_size, |adjusted_size| adjusted_size.0);
565 cx.set_global(BufferFontSize(clamp_font_size(f(adjusted_size))));
566 cx.refresh_windows();
567}
568
569/// Resets the buffer font size to the default value.
570pub fn reset_buffer_font_size(cx: &mut App) {
571 if cx.has_global::<BufferFontSize>() {
572 cx.remove_global::<BufferFontSize>();
573 cx.refresh_windows();
574 }
575}
576
577#[allow(missing_docs)]
578pub fn setup_ui_font(window: &mut Window, cx: &mut App) -> gpui::Font {
579 let (ui_font, ui_font_size) = {
580 let theme_settings = ThemeSettings::get_global(cx);
581 let font = theme_settings.ui_font.clone();
582 (font, theme_settings.ui_font_size(cx))
583 };
584
585 window.set_rem_size(ui_font_size);
586 ui_font
587}
588
589/// Sets the adjusted UI font size.
590pub fn adjust_ui_font_size(cx: &mut App, f: impl FnOnce(Pixels) -> Pixels) {
591 let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
592 let adjusted_size = cx
593 .try_global::<UiFontSize>()
594 .map_or(ui_font_size, |adjusted_size| adjusted_size.0);
595 cx.set_global(UiFontSize(clamp_font_size(f(adjusted_size))));
596 cx.refresh_windows();
597}
598
599/// Resets the UI font size to the default value.
600pub fn reset_ui_font_size(cx: &mut App) {
601 if cx.has_global::<UiFontSize>() {
602 cx.remove_global::<UiFontSize>();
603 cx.refresh_windows();
604 }
605}
606
607/// Sets the adjusted font size of agent responses in the agent panel.
608pub fn adjust_agent_ui_font_size(cx: &mut App, f: impl FnOnce(Pixels) -> Pixels) {
609 let agent_ui_font_size = ThemeSettings::get_global(cx).agent_ui_font_size(cx);
610 let adjusted_size = cx
611 .try_global::<AgentFontSize>()
612 .map_or(agent_ui_font_size, |adjusted_size| adjusted_size.0);
613 cx.set_global(AgentFontSize(clamp_font_size(f(adjusted_size))));
614 cx.refresh_windows();
615}
616
617/// Resets the agent response font size in the agent panel to the default value.
618pub fn reset_agent_ui_font_size(cx: &mut App) {
619 if cx.has_global::<AgentFontSize>() {
620 cx.remove_global::<AgentFontSize>();
621 cx.refresh_windows();
622 }
623}
624
625/// Sets the adjusted font size of user messages in the agent panel.
626pub fn adjust_agent_buffer_font_size(cx: &mut App, f: impl FnOnce(Pixels) -> Pixels) {
627 let agent_buffer_font_size = ThemeSettings::get_global(cx).agent_buffer_font_size(cx);
628 let adjusted_size = cx
629 .try_global::<AgentFontSize>()
630 .map_or(agent_buffer_font_size, |adjusted_size| adjusted_size.0);
631 cx.set_global(AgentFontSize(clamp_font_size(f(adjusted_size))));
632 cx.refresh_windows();
633}
634
635/// Resets the user message font size in the agent panel to the default value.
636pub fn reset_agent_buffer_font_size(cx: &mut App) {
637 if cx.has_global::<AgentFontSize>() {
638 cx.remove_global::<AgentFontSize>();
639 cx.refresh_windows();
640 }
641}
642
643/// Ensures font size is within the valid range.
644pub fn clamp_font_size(size: Pixels) -> Pixels {
645 size.clamp(MIN_FONT_SIZE, MAX_FONT_SIZE)
646}
647
648fn font_fallbacks_from_settings(
649 fallbacks: Option<Vec<settings::FontFamilyName>>,
650) -> Option<FontFallbacks> {
651 fallbacks.map(|fallbacks| {
652 FontFallbacks::from_fonts(
653 fallbacks
654 .into_iter()
655 .map(|font_family| font_family.0.to_string())
656 .collect(),
657 )
658 })
659}
660
661impl settings::Settings for ThemeSettings {
662 fn from_settings(content: &settings::SettingsContent) -> Self {
663 let content = &content.theme;
664 let theme_selection: ThemeSelection = content.theme.clone().unwrap().into();
665 let icon_theme_selection: IconThemeSelection = content.icon_theme.clone().unwrap().into();
666 Self {
667 ui_font_size: clamp_font_size(content.ui_font_size.unwrap().into_gpui()),
668 ui_font: Font {
669 family: content.ui_font_family.as_ref().unwrap().0.clone().into(),
670 features: content.ui_font_features.clone().unwrap().into_gpui(),
671 fallbacks: font_fallbacks_from_settings(content.ui_font_fallbacks.clone()),
672 weight: content.ui_font_weight.unwrap().into_gpui(),
673 style: Default::default(),
674 },
675 buffer_font: Font {
676 family: content
677 .buffer_font_family
678 .as_ref()
679 .unwrap()
680 .0
681 .clone()
682 .into(),
683 features: content.buffer_font_features.clone().unwrap().into_gpui(),
684 fallbacks: font_fallbacks_from_settings(content.buffer_font_fallbacks.clone()),
685 weight: content.buffer_font_weight.unwrap().into_gpui(),
686 style: FontStyle::default(),
687 },
688 buffer_font_size: clamp_font_size(content.buffer_font_size.unwrap().into_gpui()),
689 buffer_line_height: content.buffer_line_height.unwrap().into(),
690 agent_ui_font_size: content.agent_ui_font_size.map(|s| s.into_gpui()),
691 agent_buffer_font_size: content.agent_buffer_font_size.map(|s| s.into_gpui()),
692 theme: theme_selection,
693 experimental_theme_overrides: content.experimental_theme_overrides.clone(),
694 theme_overrides: content.theme_overrides.clone(),
695 icon_theme: icon_theme_selection,
696 ui_density: content.ui_density.unwrap_or_default().into(),
697 unnecessary_code_fade: content.unnecessary_code_fade.unwrap().0.clamp(0.0, 0.9),
698 }
699 }
700}