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}