Skip to main content

par_term_render/renderer/
resize.rs

1// Surface resize and DPI scale-factor changes. Both recompute the grid and fan
2// the new geometry out to the graphics and shader renderers, so they are kept
3// together: `handle_scale_factor_change` finishes by delegating to `resize`.
4
5use winit::dpi::PhysicalSize;
6
7use super::Renderer;
8
9impl Renderer {
10    /// Resize the renderer and recalculate grid dimensions based on padding/font metrics
11    pub fn resize(&mut self, new_size: PhysicalSize<u32>) -> (usize, usize) {
12        if new_size.width > 0 && new_size.height > 0 {
13            self.size = new_size;
14            self.dirty = true; // Mark dirty on resize
15            let result = self.cell_renderer.resize(new_size.width, new_size.height);
16
17            // Update graphics renderer cell dimensions
18            self.graphics_renderer.update_cell_dimensions(
19                self.cell_renderer.cell_width(),
20                self.cell_renderer.cell_height(),
21                self.cell_renderer.window_padding(),
22            );
23
24            // Update custom shader renderer dimensions
25            if let Some(ref mut custom_shader) = self.custom_shader_renderer {
26                custom_shader.resize(self.cell_renderer.device(), new_size.width, new_size.height);
27                // Sync cell dimensions for cursor position calculation
28                custom_shader.update_cell_dimensions(
29                    self.cell_renderer.cell_width(),
30                    self.cell_renderer.cell_height(),
31                    self.cell_renderer.window_padding(),
32                );
33            }
34
35            // Update cursor shader renderer dimensions
36            if let Some(ref mut cursor_shader) = self.cursor_shader_renderer {
37                cursor_shader.resize(self.cell_renderer.device(), new_size.width, new_size.height);
38                // Sync cell dimensions for cursor position calculation
39                cursor_shader.update_cell_dimensions(
40                    self.cell_renderer.cell_width(),
41                    self.cell_renderer.cell_height(),
42                    self.cell_renderer.window_padding(),
43                );
44            }
45
46            return result;
47        }
48
49        self.cell_renderer.grid_size()
50    }
51
52    /// Update scale factor and resize so the PTY grid matches the new DPI.
53    pub fn handle_scale_factor_change(
54        &mut self,
55        scale_factor: f64,
56        new_size: PhysicalSize<u32>,
57    ) -> (usize, usize) {
58        let old_scale = self.cell_renderer.scale_factor;
59        self.cell_renderer.update_scale_factor(scale_factor);
60        let new_scale = self.cell_renderer.scale_factor;
61
62        // Rescale physical pixel values when DPI changes
63        if old_scale > 0.0 && (old_scale - new_scale).abs() > f32::EPSILON {
64            // Rescale content_offset_y
65            let logical_offset_y = self.cell_renderer.content_offset_y() / old_scale;
66            let new_physical_offset_y = logical_offset_y * new_scale;
67            self.cell_renderer
68                .set_content_offset_y(new_physical_offset_y);
69            self.graphics_renderer
70                .set_content_offset_y(new_physical_offset_y);
71            if let Some(ref mut cs) = self.custom_shader_renderer {
72                cs.set_content_offset_y(new_physical_offset_y);
73            }
74            if let Some(ref mut cs) = self.cursor_shader_renderer {
75                cs.set_content_offset_y(new_physical_offset_y);
76            }
77
78            // Rescale content_offset_x
79            let logical_offset_x = self.cell_renderer.content_offset_x() / old_scale;
80            let new_physical_offset_x = logical_offset_x * new_scale;
81            self.cell_renderer
82                .set_content_offset_x(new_physical_offset_x);
83            self.graphics_renderer
84                .set_content_offset_x(new_physical_offset_x);
85            if let Some(ref mut cs) = self.custom_shader_renderer {
86                cs.set_content_offset_x(new_physical_offset_x);
87            }
88            if let Some(ref mut cs) = self.cursor_shader_renderer {
89                cs.set_content_offset_x(new_physical_offset_x);
90            }
91
92            // Rescale content_inset_bottom
93            let logical_inset_bottom = self.cell_renderer.content_inset_bottom() / old_scale;
94            let new_physical_inset_bottom = logical_inset_bottom * new_scale;
95            self.cell_renderer
96                .set_content_inset_bottom(new_physical_inset_bottom);
97
98            // Rescale egui_bottom_inset (status bar)
99            if self.cell_renderer.grid.egui_bottom_inset > 0.0 {
100                let logical_egui_bottom = self.cell_renderer.grid.egui_bottom_inset / old_scale;
101                self.cell_renderer.grid.egui_bottom_inset = logical_egui_bottom * new_scale;
102            }
103
104            // Rescale content_inset_right (AI Inspector panel).
105            // The shader renderers keep their own copy so they can exclude the
106            // panel area from effects; `set_content_inset_right` fans the value
107            // out to them and this path has to do the same, or the shaders keep
108            // masking the pre-DPI-change panel width.
109            if self.cell_renderer.grid.content_inset_right > 0.0 {
110                let logical_inset_right = self.cell_renderer.grid.content_inset_right / old_scale;
111                let new_physical_inset_right = logical_inset_right * new_scale;
112                self.cell_renderer.grid.content_inset_right = new_physical_inset_right;
113                if let Some(ref mut cs) = self.custom_shader_renderer {
114                    cs.set_content_inset_right(new_physical_inset_right);
115                }
116                if let Some(ref mut cs) = self.cursor_shader_renderer {
117                    cs.set_content_inset_right(new_physical_inset_right);
118                }
119            }
120
121            // Rescale egui_right_inset
122            if self.cell_renderer.grid.egui_right_inset > 0.0 {
123                let logical_egui_right = self.cell_renderer.grid.egui_right_inset / old_scale;
124                self.cell_renderer.grid.egui_right_inset = logical_egui_right * new_scale;
125            }
126
127            // Rescale window_padding
128            let logical_padding = self.cell_renderer.window_padding() / old_scale;
129            let new_physical_padding = logical_padding * new_scale;
130            self.cell_renderer
131                .update_window_padding(new_physical_padding);
132
133            // Rescale scrollbar width
134            let logical_scrollbar = self.cell_renderer.scrollbar.width() / old_scale;
135            let new_physical_scrollbar = logical_scrollbar * new_scale;
136            self.cell_renderer.scrollbar.update_appearance(
137                new_physical_scrollbar,
138                self.cell_renderer.scrollbar.thumb_color(),
139                self.cell_renderer.scrollbar.track_color(),
140            );
141
142            // Sync new scale factor to shader renderers for cursor sizing
143            if let Some(ref mut cs) = self.custom_shader_renderer {
144                cs.set_scale_factor(new_scale);
145            }
146            if let Some(ref mut cs) = self.cursor_shader_renderer {
147                cs.set_scale_factor(new_scale);
148            }
149
150            // Every inset rescaled above feeds scrollbar geometry, and so does the
151            // new scrollbar width. `update_scrollbar` skips the GPU upload when its
152            // cached tuple is unchanged, and none of those values are in the tuple,
153            // so without this the scrollbar stays at its pre-DPI-change size and
154            // position — the same failure the inset setters call out.
155            self.last_scrollbar_state = (usize::MAX, 0, 0, 0, 0, 0, 0, 0, 0, 0);
156        }
157
158        self.resize(new_size)
159    }
160}