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