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        self.sync_alpha_mode_to_opacity();
91        // update_bg_image_uniforms() multiplies bg_image_opacity by window_opacity,
92        // so both images and solid colors respect window transparency
93        self.update_bg_image_uniforms(None);
94    }
95
96    /// Set whether transparency affects only default background cells.
97    /// When true, non-default (colored) backgrounds remain opaque for readability.
98    pub fn set_transparency_affects_only_default_background(&mut self, value: bool) {
99        if self.transparency_affects_only_default_background != value {
100            log::info!(
101                "transparency_affects_only_default_background: {} -> {} (window_opacity={})",
102                self.transparency_affects_only_default_background,
103                value,
104                self.window_opacity
105            );
106            self.transparency_affects_only_default_background = value;
107            // Mark all rows dirty to re-render with new transparency behavior
108            self.dirty_rows.fill(true);
109        }
110    }
111
112    /// Set whether text should always be rendered at full opacity.
113    /// When true, text remains opaque regardless of window transparency settings.
114    pub fn set_keep_text_opaque(&mut self, value: bool) {
115        if self.keep_text_opaque != value {
116            log::info!(
117                "keep_text_opaque: {} -> {} (window_opacity={}, transparency_affects_only_default_bg={})",
118                self.keep_text_opaque,
119                value,
120                self.window_opacity,
121                self.transparency_affects_only_default_background
122            );
123            self.keep_text_opaque = value;
124            // Mark all rows dirty to re-render with new text opacity behavior
125            self.dirty_rows.fill(true);
126        }
127    }
128
129    pub fn set_link_underline_style(&mut self, style: par_term_config::LinkUnderlineStyle) {
130        if self.link_underline_style != style {
131            self.link_underline_style = style;
132            self.dirty_rows.fill(true);
133        }
134    }
135
136    /// Update command separator settings from config
137    pub fn update_command_separator(
138        &mut self,
139        enabled: bool,
140        thickness: f32,
141        opacity: f32,
142        exit_color: bool,
143        color: [u8; 3],
144    ) {
145        self.separator.enabled = enabled;
146        self.separator.thickness = thickness;
147        self.separator.opacity = opacity;
148        self.separator.exit_color = exit_color;
149        self.separator.color = color_u8_to_f32(color);
150    }
151
152    /// Set the visible separator marks for the current frame.
153    /// Returns `true` if the marks changed.
154    pub fn set_separator_marks(&mut self, marks: Vec<SeparatorMark>) -> bool {
155        if self.separator.visible_marks != marks {
156            self.separator.visible_marks = marks;
157            return true;
158        }
159        false
160    }
161
162    /// Set the gutter indicator data for the current frame.
163    ///
164    /// Each entry is `(screen_row, [r, g, b, a])` for the gutter background.
165    pub fn set_gutter_indicators(&mut self, indicators: Vec<(usize, [f32; 4])>) {
166        self.gutter_indicators = indicators;
167    }
168
169    /// Compute separator color based on exit code and settings
170    pub(crate) fn separator_color(
171        &self,
172        exit_code: Option<i32>,
173        custom_color: Option<(u8, u8, u8)>,
174        opacity_mult: f32,
175    ) -> [f32; 4] {
176        let alpha = self.separator.opacity * opacity_mult;
177        // Custom color from trigger marks takes priority
178        if let Some((r, g, b)) = custom_color {
179            return color_tuple_to_f32_a(r, g, b, alpha);
180        }
181        if self.separator.exit_color {
182            match exit_code {
183                Some(0) => [0.3, 0.75, 0.3, alpha],   // Green for success
184                Some(_) => [0.85, 0.25, 0.25, alpha], // Red for failure
185                None => [0.5, 0.5, 0.5, alpha],       // Gray for unknown
186            }
187        } else {
188            [
189                self.separator.color[0],
190                self.separator.color[1],
191                self.separator.color[2],
192                alpha,
193            ]
194        }
195    }
196
197    pub fn update_scrollbar_appearance(
198        &mut self,
199        width: f32,
200        thumb_color: [f32; 4],
201        track_color: [f32; 4],
202    ) {
203        self.scrollbar
204            .update_appearance(width, thumb_color, track_color);
205    }
206
207    pub fn update_scrollbar_position(&mut self, position: &str) {
208        self.scrollbar.update_position(position);
209    }
210
211    pub fn scrollbar_contains_point(&self, x: f32, y: f32) -> bool {
212        self.scrollbar.contains_point(x, y)
213    }
214
215    pub fn scrollbar_thumb_bounds(&self) -> Option<(f32, f32)> {
216        self.scrollbar.thumb_bounds()
217    }
218
219    pub fn scrollbar_track_contains_x(&self, x: f32) -> bool {
220        self.scrollbar.track_contains_x(x)
221    }
222
223    pub fn scrollbar_mouse_y_to_scroll_offset(&self, mouse_y: f32) -> Option<usize> {
224        self.scrollbar.mouse_y_to_scroll_offset(mouse_y)
225    }
226
227    /// Find a scrollbar mark at the given mouse position for tooltip display.
228    /// Returns the mark if mouse is within `tolerance` pixels of a mark.
229    pub fn scrollbar_mark_at_position(
230        &self,
231        mouse_x: f32,
232        mouse_y: f32,
233        tolerance: f32,
234    ) -> Option<&par_term_config::ScrollbackMark> {
235        self.scrollbar.mark_at_position(mouse_x, mouse_y, tolerance)
236    }
237}