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