Skip to main content

par_term_render/cell_renderer/
settings.rs

1use par_term_config::{SeparatorMark, color_tuple_to_f32_a, color_u8_to_f32};
2
3use super::{CellRenderer, PaneViewport};
4
5impl CellRenderer {
6    pub fn update_scrollbar(
7        &mut self,
8        scroll_offset: usize,
9        visible_lines: usize,
10        total_lines: usize,
11        marks: &[par_term_config::ScrollbackMark],
12    ) {
13        let right_inset = self.grid.content_inset_right + self.grid.egui_right_inset;
14        self.scrollbar.update(
15            &self.queue,
16            crate::scrollbar::ScrollbarUpdateParams {
17                scroll_offset,
18                visible_lines,
19                total_lines,
20                window_width: self.config.width,
21                window_height: self.config.height,
22                content_offset_y: self.grid.content_offset_y,
23                content_inset_bottom: self.grid.content_inset_bottom + self.grid.egui_bottom_inset,
24                content_inset_right: right_inset,
25                marks,
26            },
27        );
28    }
29
30    /// Update scrollbar state constrained to a specific pane's bounds.
31    ///
32    /// Converts the pane viewport (pixel bounds) into the inset parameters
33    /// that `Scrollbar::update` expects, so the track and thumb are confined
34    /// to the pane instead of spanning the full window.
35    pub fn update_scrollbar_for_pane(
36        &mut self,
37        scroll_offset: usize,
38        visible_lines: usize,
39        total_lines: usize,
40        marks: &[par_term_config::ScrollbackMark],
41        viewport: &PaneViewport,
42    ) {
43        let win_w = self.config.width as f32;
44        let win_h = self.config.height as f32;
45
46        // Top inset: space above the pane
47        let pane_content_offset_y = viewport.y;
48
49        // Bottom inset: space below the pane + existing egui bottom inset
50        let pane_bottom_inset =
51            (win_h - (viewport.y + viewport.height)).max(0.0) + self.grid.egui_bottom_inset;
52
53        // Right inset: space to the right of the pane + existing egui/panel right inset
54        let pane_right_inset = (win_w - (viewport.x + viewport.width)).max(0.0)
55            + self.grid.content_inset_right
56            + self.grid.egui_right_inset;
57
58        self.scrollbar.update(
59            &self.queue,
60            crate::scrollbar::ScrollbarUpdateParams {
61                scroll_offset,
62                visible_lines,
63                total_lines,
64                window_width: self.config.width,
65                window_height: self.config.height,
66                content_offset_y: pane_content_offset_y,
67                content_inset_bottom: pane_bottom_inset,
68                content_inset_right: pane_right_inset,
69                marks,
70            },
71        );
72    }
73
74    pub fn set_visual_bell_intensity(&mut self, intensity: f32) {
75        self.visual_bell_intensity = intensity;
76    }
77
78    /// Set the visual bell flash color (RGB, 0.0-1.0 per channel).
79    /// The intensity is handled separately via `set_visual_bell_intensity`.
80    pub fn set_visual_bell_color(&mut self, color: [f32; 3]) {
81        self.visual_bell_color = color;
82    }
83
84    pub fn update_opacity(&mut self, opacity: f32) {
85        self.window_opacity = opacity;
86        // update_bg_image_uniforms() multiplies bg_image_opacity by window_opacity,
87        // so both images and solid colors respect window transparency
88        self.update_bg_image_uniforms(None);
89    }
90
91    /// Set whether transparency affects only default background cells.
92    /// When true, non-default (colored) backgrounds remain opaque for readability.
93    pub fn set_transparency_affects_only_default_background(&mut self, value: bool) {
94        if self.transparency_affects_only_default_background != value {
95            log::info!(
96                "transparency_affects_only_default_background: {} -> {} (window_opacity={})",
97                self.transparency_affects_only_default_background,
98                value,
99                self.window_opacity
100            );
101            self.transparency_affects_only_default_background = value;
102            // Mark all rows dirty to re-render with new transparency behavior
103            self.dirty_rows.fill(true);
104        }
105    }
106
107    /// Set whether text should always be rendered at full opacity.
108    /// When true, text remains opaque regardless of window transparency settings.
109    pub fn set_keep_text_opaque(&mut self, value: bool) {
110        if self.keep_text_opaque != value {
111            log::info!(
112                "keep_text_opaque: {} -> {} (window_opacity={}, transparency_affects_only_default_bg={})",
113                self.keep_text_opaque,
114                value,
115                self.window_opacity,
116                self.transparency_affects_only_default_background
117            );
118            self.keep_text_opaque = value;
119            // Mark all rows dirty to re-render with new text opacity behavior
120            self.dirty_rows.fill(true);
121        }
122    }
123
124    pub fn set_link_underline_style(&mut self, style: par_term_config::LinkUnderlineStyle) {
125        if self.link_underline_style != style {
126            self.link_underline_style = style;
127            self.dirty_rows.fill(true);
128        }
129    }
130
131    /// Update command separator settings from config
132    pub fn update_command_separator(
133        &mut self,
134        enabled: bool,
135        thickness: f32,
136        opacity: f32,
137        exit_color: bool,
138        color: [u8; 3],
139    ) {
140        self.separator.enabled = enabled;
141        self.separator.thickness = thickness;
142        self.separator.opacity = opacity;
143        self.separator.exit_color = exit_color;
144        self.separator.color = color_u8_to_f32(color);
145    }
146
147    /// Set the visible separator marks for the current frame.
148    /// Returns `true` if the marks changed.
149    pub fn set_separator_marks(&mut self, marks: Vec<SeparatorMark>) -> bool {
150        if self.separator.visible_marks != marks {
151            self.separator.visible_marks = marks;
152            return true;
153        }
154        false
155    }
156
157    /// Set the gutter indicator data for the current frame.
158    ///
159    /// Each entry is `(screen_row, [r, g, b, a])` for the gutter background.
160    pub fn set_gutter_indicators(&mut self, indicators: Vec<(usize, [f32; 4])>) {
161        self.gutter_indicators = indicators;
162    }
163
164    /// Compute separator color based on exit code and settings
165    pub(crate) fn separator_color(
166        &self,
167        exit_code: Option<i32>,
168        custom_color: Option<(u8, u8, u8)>,
169        opacity_mult: f32,
170    ) -> [f32; 4] {
171        let alpha = self.separator.opacity * opacity_mult;
172        // Custom color from trigger marks takes priority
173        if let Some((r, g, b)) = custom_color {
174            return color_tuple_to_f32_a(r, g, b, alpha);
175        }
176        if self.separator.exit_color {
177            match exit_code {
178                Some(0) => [0.3, 0.75, 0.3, alpha],   // Green for success
179                Some(_) => [0.85, 0.25, 0.25, alpha], // Red for failure
180                None => [0.5, 0.5, 0.5, alpha],       // Gray for unknown
181            }
182        } else {
183            [
184                self.separator.color[0],
185                self.separator.color[1],
186                self.separator.color[2],
187                alpha,
188            ]
189        }
190    }
191
192    pub fn update_scrollbar_appearance(
193        &mut self,
194        width: f32,
195        thumb_color: [f32; 4],
196        track_color: [f32; 4],
197    ) {
198        self.scrollbar
199            .update_appearance(width, thumb_color, track_color);
200    }
201
202    pub fn update_scrollbar_position(&mut self, position: &str) {
203        self.scrollbar.update_position(position);
204    }
205
206    pub fn scrollbar_contains_point(&self, x: f32, y: f32) -> bool {
207        self.scrollbar.contains_point(x, y)
208    }
209
210    pub fn scrollbar_thumb_bounds(&self) -> Option<(f32, f32)> {
211        self.scrollbar.thumb_bounds()
212    }
213
214    pub fn scrollbar_track_contains_x(&self, x: f32) -> bool {
215        self.scrollbar.track_contains_x(x)
216    }
217
218    pub fn scrollbar_mouse_y_to_scroll_offset(&self, mouse_y: f32) -> Option<usize> {
219        self.scrollbar.mouse_y_to_scroll_offset(mouse_y)
220    }
221
222    /// Find a scrollbar mark at the given mouse position for tooltip display.
223    /// Returns the mark if mouse is within `tolerance` pixels of a mark.
224    pub fn scrollbar_mark_at_position(
225        &self,
226        mouse_x: f32,
227        mouse_y: f32,
228        tolerance: f32,
229    ) -> Option<&par_term_config::ScrollbackMark> {
230        self.scrollbar.mark_at_position(mouse_x, mouse_y, tolerance)
231    }
232}