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        // Derive insets purely from the pane viewport bounds.  Do NOT add
47        // global egui/content insets here — the viewport already accounts for
48        // them (pane bounds are computed from content that subtracts those
49        // insets).  Adding them again would double-count, shifting the
50        // scrollbar and shrinking the track on HiDPI displays.
51        let pane_content_offset_y = viewport.y;
52        let pane_bottom_inset = (win_h - (viewport.y + viewport.height)).max(0.0);
53        let pane_right_inset = (win_w - (viewport.x + viewport.width)).max(0.0);
54
55        self.scrollbar.update(
56            &self.queue,
57            crate::scrollbar::ScrollbarUpdateParams {
58                scroll_offset,
59                visible_lines,
60                total_lines,
61                window_width: self.config.width,
62                window_height: self.config.height,
63                content_offset_y: pane_content_offset_y,
64                content_inset_bottom: pane_bottom_inset,
65                content_inset_right: pane_right_inset,
66                marks,
67            },
68        );
69    }
70
71    pub fn set_visual_bell_intensity(&mut self, intensity: f32) {
72        self.visual_bell_intensity = intensity;
73    }
74
75    /// Set the visual bell flash color (RGB, 0.0-1.0 per channel).
76    /// The intensity is handled separately via `set_visual_bell_intensity`.
77    pub fn set_visual_bell_color(&mut self, color: [f32; 3]) {
78        self.visual_bell_color = color;
79    }
80
81    pub fn update_opacity(&mut self, opacity: f32) {
82        self.window_opacity = opacity;
83        // update_bg_image_uniforms() multiplies bg_image_opacity by window_opacity,
84        // so both images and solid colors respect window transparency
85        self.update_bg_image_uniforms(None);
86    }
87
88    /// Set whether transparency affects only default background cells.
89    /// When true, non-default (colored) backgrounds remain opaque for readability.
90    pub fn set_transparency_affects_only_default_background(&mut self, value: bool) {
91        if self.transparency_affects_only_default_background != value {
92            log::info!(
93                "transparency_affects_only_default_background: {} -> {} (window_opacity={})",
94                self.transparency_affects_only_default_background,
95                value,
96                self.window_opacity
97            );
98            self.transparency_affects_only_default_background = value;
99            // Mark all rows dirty to re-render with new transparency behavior
100            self.dirty_rows.fill(true);
101        }
102    }
103
104    /// Set whether text should always be rendered at full opacity.
105    /// When true, text remains opaque regardless of window transparency settings.
106    pub fn set_keep_text_opaque(&mut self, value: bool) {
107        if self.keep_text_opaque != value {
108            log::info!(
109                "keep_text_opaque: {} -> {} (window_opacity={}, transparency_affects_only_default_bg={})",
110                self.keep_text_opaque,
111                value,
112                self.window_opacity,
113                self.transparency_affects_only_default_background
114            );
115            self.keep_text_opaque = value;
116            // Mark all rows dirty to re-render with new text opacity behavior
117            self.dirty_rows.fill(true);
118        }
119    }
120
121    pub fn set_link_underline_style(&mut self, style: par_term_config::LinkUnderlineStyle) {
122        if self.link_underline_style != style {
123            self.link_underline_style = style;
124            self.dirty_rows.fill(true);
125        }
126    }
127
128    /// Update command separator settings from config
129    pub fn update_command_separator(
130        &mut self,
131        enabled: bool,
132        thickness: f32,
133        opacity: f32,
134        exit_color: bool,
135        color: [u8; 3],
136    ) {
137        self.separator.enabled = enabled;
138        self.separator.thickness = thickness;
139        self.separator.opacity = opacity;
140        self.separator.exit_color = exit_color;
141        self.separator.color = color_u8_to_f32(color);
142    }
143
144    /// Set the visible separator marks for the current frame.
145    /// Returns `true` if the marks changed.
146    pub fn set_separator_marks(&mut self, marks: Vec<SeparatorMark>) -> bool {
147        if self.separator.visible_marks != marks {
148            self.separator.visible_marks = marks;
149            return true;
150        }
151        false
152    }
153
154    /// Set the gutter indicator data for the current frame.
155    ///
156    /// Each entry is `(screen_row, [r, g, b, a])` for the gutter background.
157    pub fn set_gutter_indicators(&mut self, indicators: Vec<(usize, [f32; 4])>) {
158        self.gutter_indicators = indicators;
159    }
160
161    /// Compute separator color based on exit code and settings
162    pub(crate) fn separator_color(
163        &self,
164        exit_code: Option<i32>,
165        custom_color: Option<(u8, u8, u8)>,
166        opacity_mult: f32,
167    ) -> [f32; 4] {
168        let alpha = self.separator.opacity * opacity_mult;
169        // Custom color from trigger marks takes priority
170        if let Some((r, g, b)) = custom_color {
171            return color_tuple_to_f32_a(r, g, b, alpha);
172        }
173        if self.separator.exit_color {
174            match exit_code {
175                Some(0) => [0.3, 0.75, 0.3, alpha],   // Green for success
176                Some(_) => [0.85, 0.25, 0.25, alpha], // Red for failure
177                None => [0.5, 0.5, 0.5, alpha],       // Gray for unknown
178            }
179        } else {
180            [
181                self.separator.color[0],
182                self.separator.color[1],
183                self.separator.color[2],
184                alpha,
185            ]
186        }
187    }
188
189    pub fn update_scrollbar_appearance(
190        &mut self,
191        width: f32,
192        thumb_color: [f32; 4],
193        track_color: [f32; 4],
194    ) {
195        self.scrollbar
196            .update_appearance(width, thumb_color, track_color);
197    }
198
199    pub fn update_scrollbar_position(&mut self, position: &str) {
200        self.scrollbar.update_position(position);
201    }
202
203    pub fn scrollbar_contains_point(&self, x: f32, y: f32) -> bool {
204        self.scrollbar.contains_point(x, y)
205    }
206
207    pub fn scrollbar_thumb_bounds(&self) -> Option<(f32, f32)> {
208        self.scrollbar.thumb_bounds()
209    }
210
211    pub fn scrollbar_track_contains_x(&self, x: f32) -> bool {
212        self.scrollbar.track_contains_x(x)
213    }
214
215    pub fn scrollbar_mouse_y_to_scroll_offset(&self, mouse_y: f32) -> Option<usize> {
216        self.scrollbar.mouse_y_to_scroll_offset(mouse_y)
217    }
218
219    /// Find a scrollbar mark at the given mouse position for tooltip display.
220    /// Returns the mark if mouse is within `tolerance` pixels of a mark.
221    pub fn scrollbar_mark_at_position(
222        &self,
223        mouse_x: f32,
224        mouse_y: f32,
225        tolerance: f32,
226    ) -> Option<&par_term_config::ScrollbackMark> {
227        self.scrollbar.mark_at_position(mouse_x, mouse_y, tolerance)
228    }
229}