Skip to main content

gpui_component/text/
style.rs

1use std::sync::Arc;
2
3use gpui::{HighlightStyle, Pixels, Rems, StyleRefinement, px, rems};
4
5use crate::highlighter::HighlightTheme;
6
7/// TextViewStyle used to customize the style for [`super::TextView`].
8///
9/// This is the component-level style. It is folded onto the style the active
10/// [`crate::Theme`] already derived, so a field left at its default keeps the
11/// themed value rather than overriding it with a neutral one.
12#[derive(Clone)]
13pub struct TextViewStyle {
14    /// Gap of each paragraphs, default is 0.75 rem.
15    pub paragraph_gap: Rems,
16    /// Base font size for headings, default is 14px.
17    ///
18    /// Left at the default together with [`Self::heading_font_size`], headings
19    /// follow the text view's own scale, proportional to the body text.
20    pub heading_base_font_size: Pixels,
21    /// Function to calculate heading font size based on heading level (1-6).
22    ///
23    /// The first parameter is the heading level (1-6), the second parameter is
24    /// the base font size.
25    pub heading_font_size: Option<Arc<dyn Fn(u8, Pixels) -> Pixels + Send + Sync + 'static>>,
26    /// Highlight theme for code blocks. Default: [`HighlightTheme::default_light()`]
27    pub highlight_theme: Arc<HighlightTheme>,
28    /// The style refinement for code blocks.
29    ///
30    /// Set `overflow.y` to `Overflow::Scroll` with a max height here to scroll
31    /// long code inside the block, e.g.
32    /// `TextViewStyle::default().code_block({ let mut s = StyleRefinement::default().max_h(px(320.)); s.overflow.y = Some(Overflow::Scroll); s })`.
33    pub code_block: StyleRefinement,
34    /// Style refinement applied to the table container (the bordered wrapper
35    /// in wrap mode, the scroll viewport in horizontal-scroll mode).
36    ///
37    /// Set `overflow_x: scroll` here for adaptive table layout: columns fit
38    /// their content when space allows, shrink (wrapping cell text) down to a
39    /// per-column floor when the frame is narrower, and below that the table
40    /// scrolls horizontally instead of squeezing further, e.g.
41    /// `TextViewStyle::default().table({ let mut s = StyleRefinement::default(); s.overflow.x = Some(Overflow::Scroll); s })`.
42    pub table: StyleRefinement,
43    /// Style refinement applied to the header row (the first row) of a table,
44    /// on top of the `table_head` background and foreground from the theme.
45    pub table_head: StyleRefinement,
46    /// Style refinement applied to each table cell.
47    ///
48    /// With the scroll layout, set `white_space: nowrap` here to keep cells
49    /// on a single line — columns then never shrink and the table scrolls as
50    /// soon as the content is wider than the frame.
51    pub table_cell: StyleRefinement,
52    /// The highlight style for inline code.
53    ///
54    /// Default is [`HighlightStyle::default()`], the `background_color` will
55    /// fallback to `cx.theme().accent`, if it is `None`.
56    pub inline_code: HighlightStyle,
57    /// Whether content-specific rendering should use dark-mode assets.
58    pub is_dark: bool,
59}
60
61impl Default for TextViewStyle {
62    fn default() -> Self {
63        Self {
64            paragraph_gap: rems(0.75),
65            heading_base_font_size: px(14.),
66            heading_font_size: None,
67            highlight_theme: HighlightTheme::default_light().clone(),
68            code_block: StyleRefinement::default(),
69            table: StyleRefinement::default(),
70            table_head: StyleRefinement::default(),
71            table_cell: StyleRefinement::default(),
72            inline_code: HighlightStyle::default(),
73            is_dark: false,
74        }
75    }
76}
77
78impl PartialEq for TextViewStyle {
79    fn eq(&self, other: &Self) -> bool {
80        self.paragraph_gap == other.paragraph_gap
81            && self.heading_base_font_size == other.heading_base_font_size
82            && match (&self.heading_font_size, &other.heading_font_size) {
83                (Some(left), Some(right)) => (1..=6).all(|level| {
84                    left(level, self.heading_base_font_size)
85                        == right(level, other.heading_base_font_size)
86                }),
87                (None, None) => true,
88                _ => false,
89            }
90            && self.highlight_theme == other.highlight_theme
91            && self.code_block == other.code_block
92            && self.table == other.table
93            && self.table_head == other.table_head
94            && self.table_cell == other.table_cell
95            && self.inline_code == other.inline_code
96            && self.is_dark == other.is_dark
97    }
98}
99
100impl TextViewStyle {
101    /// Set paragraph gap, default is 0.75 rem.
102    pub fn paragraph_gap(mut self, gap: Rems) -> Self {
103        self.paragraph_gap = gap;
104        self
105    }
106    /// Set the function that resolves a heading's font size from its level
107    /// (1-6) and [`Self::heading_base_font_size`].
108    pub fn heading_font_size<F>(mut self, f: F) -> Self
109    where
110        F: Fn(u8, Pixels) -> Pixels + Send + Sync + 'static,
111    {
112        self.heading_font_size = Some(Arc::new(f));
113        self
114    }
115    /// Set style for code blocks.
116    ///
117    /// Set `overflow.y` to `Overflow::Scroll` with a max height on the
118    /// refinement to scroll long code inside the block instead of growing it.
119    pub fn code_block(mut self, style: StyleRefinement) -> Self {
120        self.code_block = style;
121        self
122    }
123    /// Set style for inline code spans.
124    pub fn inline_code(mut self, style: HighlightStyle) -> Self {
125        self.inline_code = style;
126        self
127    }
128    /// Set extra style for the table container.
129    ///
130    /// Set `overflow_x: scroll` on the refinement for adaptive layout: cells
131    /// wrap as the frame narrows, and once columns reach their minimum width
132    /// the table scrolls horizontally instead of shrinking further.
133    pub fn table(mut self, style: StyleRefinement) -> Self {
134        self.table = style;
135        self
136    }
137    /// Set extra style for the table header row.
138    pub fn table_head(mut self, style: StyleRefinement) -> Self {
139        self.table_head = style;
140        self
141    }
142    /// Set extra style for each table cell.
143    ///
144    /// With the scroll table layout, `white_space: nowrap` here keeps cells
145    /// on a single line and the table scrolls whenever the content is wider
146    /// than the frame.
147    pub fn table_cell(mut self, style: StyleRefinement) -> Self {
148        self.table_cell = style;
149        self
150    }
151}