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