use super::{BackgroundInstance, Cell, CellRenderer, RowCacheEntry, TextInstance, pipeline};
pub(crate) struct GridLayout {
pub(crate) cols: usize,
pub(crate) rows: usize,
pub(crate) cell_width: f32,
pub(crate) cell_height: f32,
pub(crate) window_padding: f32,
pub(crate) content_offset_y: f32,
pub(crate) content_offset_x: f32,
pub(crate) content_inset_bottom: f32,
pub(crate) content_inset_right: f32,
pub(crate) egui_bottom_inset: f32,
pub(crate) egui_right_inset: f32,
}
impl CellRenderer {
pub fn cell_width(&self) -> f32 {
self.grid.cell_width
}
pub fn cell_height(&self) -> f32 {
self.grid.cell_height
}
pub fn window_padding(&self) -> f32 {
self.grid.window_padding
}
pub fn content_offset_y(&self) -> f32 {
self.grid.content_offset_y
}
pub fn set_content_offset_y(&mut self, offset: f32) -> Option<(usize, usize)> {
if (self.grid.content_offset_y - offset).abs() > f32::EPSILON {
self.grid.content_offset_y = offset;
let size = (self.config.width, self.config.height);
return Some(self.resize(size.0, size.1));
}
None
}
pub fn content_offset_x(&self) -> f32 {
self.grid.content_offset_x
}
pub fn set_content_offset_x(&mut self, offset: f32) -> Option<(usize, usize)> {
if (self.grid.content_offset_x - offset).abs() > f32::EPSILON {
self.grid.content_offset_x = offset;
let size = (self.config.width, self.config.height);
return Some(self.resize(size.0, size.1));
}
None
}
pub fn content_inset_bottom(&self) -> f32 {
self.grid.content_inset_bottom
}
pub fn set_content_inset_bottom(&mut self, inset: f32) -> Option<(usize, usize)> {
if (self.grid.content_inset_bottom - inset).abs() > f32::EPSILON {
self.grid.content_inset_bottom = inset;
let size = (self.config.width, self.config.height);
return Some(self.resize(size.0, size.1));
}
None
}
pub fn content_inset_right(&self) -> f32 {
self.grid.content_inset_right
}
pub fn set_content_inset_right(&mut self, inset: f32) -> Option<(usize, usize)> {
if (self.grid.content_inset_right - inset).abs() > f32::EPSILON {
log::info!(
"[SCROLLBAR] set_content_inset_right: {:.1} -> {:.1} (physical px)",
self.grid.content_inset_right,
inset
);
self.grid.content_inset_right = inset;
let size = (self.config.width, self.config.height);
return Some(self.resize(size.0, size.1));
}
None
}
pub fn grid_size(&self) -> (usize, usize) {
(self.grid.cols, self.grid.rows)
}
pub fn resize(&mut self, width: u32, height: u32) -> (usize, usize) {
if width == 0 || height == 0 {
return (self.grid.cols, self.grid.rows);
}
self.config.width = width;
self.config.height = height;
self.surface.configure(&self.device, &self.config);
let available_width = (width as f32
- self.grid.window_padding * 2.0
- self.grid.content_offset_x
- self.grid.content_inset_right)
.max(0.0);
let available_height = (height as f32
- self.grid.window_padding
- self.grid.content_offset_y
- self.grid.content_inset_bottom
- self.grid.egui_bottom_inset)
.max(0.0);
let new_cols = (available_width / self.grid.cell_width).max(1.0) as usize;
let new_rows = (available_height / self.grid.cell_height).max(1.0) as usize;
if new_cols != self.grid.cols || new_rows != self.grid.rows {
self.grid.cols = new_cols;
self.grid.rows = new_rows;
self.cells = vec![Cell::default(); self.grid.cols * self.grid.rows];
self.dirty_rows = vec![true; self.grid.rows];
self.row_cache = vec![None::<RowCacheEntry>; self.grid.rows];
self.recreate_instance_buffers();
}
self.update_bg_image_uniforms(None);
(self.grid.cols, self.grid.rows)
}
pub fn chrome_overhead(&self) -> (f32, f32) {
let chrome_x = self.grid.window_padding * 2.0
+ self.grid.content_offset_x
+ self.grid.content_inset_right;
let chrome_y = self.grid.window_padding
+ self.grid.content_offset_y
+ self.grid.content_inset_bottom
+ self.grid.egui_bottom_inset;
(chrome_x, chrome_y)
}
pub(crate) fn recreate_instance_buffers(&mut self) {
self.buffers.max_bg_instances =
self.grid.cols * self.grid.rows + 10 + self.grid.rows + self.grid.rows; self.buffers.max_text_instances = self.grid.cols * self.grid.rows * 2;
let (bg_buf, text_buf) = pipeline::create_instance_buffers(
&self.device,
self.buffers.max_bg_instances,
self.buffers.max_text_instances,
);
self.buffers.bg_instance_buffer = bg_buf;
self.buffers.text_instance_buffer = text_buf;
self.buffers.actual_bg_instances = 0;
self.buffers.actual_text_instances = 0;
self.bg_instances = vec![
BackgroundInstance {
position: [0.0, 0.0],
size: [0.0, 0.0],
color: [0.0, 0.0, 0.0, 0.0],
};
self.buffers.max_bg_instances
];
self.text_instances = vec![
TextInstance {
position: [0.0, 0.0],
size: [0.0, 0.0],
tex_offset: [0.0, 0.0],
tex_size: [0.0, 0.0],
color: [0.0, 0.0, 0.0, 0.0],
is_colored: 0,
};
self.buffers.max_text_instances
];
self.scratch_row_bg.reserve(
self.grid
.cols
.saturating_sub(self.scratch_row_bg.capacity()),
);
self.scratch_row_text
.reserve((self.grid.cols * 2).saturating_sub(self.scratch_row_text.capacity()));
}
pub fn update_scale_factor(&mut self, scale_factor: f64) {
let new_scale = scale_factor as f32;
if (self.scale_factor - new_scale).abs() < f32::EPSILON {
return;
}
log::info!(
"Recalculating font metrics for scale factor change: {} -> {}",
self.scale_factor,
new_scale
);
self.scale_factor = new_scale;
let platform_dpi = if cfg!(target_os = "macos") {
crate::cell_renderer::MACOS_PLATFORM_DPI
} else {
crate::cell_renderer::DEFAULT_PLATFORM_DPI
};
let base_font_pixels =
self.font.base_font_size * platform_dpi / crate::cell_renderer::FONT_REFERENCE_DPI;
self.font.font_size_pixels = (base_font_pixels * new_scale).max(1.0);
let (font_ascent, font_descent, font_leading, char_advance) = {
let primary_font = self.font_manager.get_font(0).expect(
"Primary font at index 0 must exist in FontManager when updating scale factor",
);
let metrics = primary_font.metrics(&[]);
let scale = self.font.font_size_pixels / metrics.units_per_em as f32;
let glyph_id = primary_font.charmap().map('m');
let advance = primary_font.glyph_metrics(&[]).advance_width(glyph_id) * scale;
(
metrics.ascent * scale,
metrics.descent * scale,
metrics.leading * scale,
advance,
)
};
self.font.font_ascent = font_ascent;
self.font.font_descent = font_descent;
self.font.font_leading = font_leading;
self.font.char_advance = char_advance;
let natural_line_height = font_ascent + font_descent + font_leading;
self.grid.cell_height = (natural_line_height * self.font.line_spacing)
.max(1.0)
.round();
self.grid.cell_width = (char_advance * self.font.char_spacing).max(1.0).round();
log::info!(
"New cell dimensions: {}x{} (font_size_pixels: {})",
self.grid.cell_width,
self.grid.cell_height,
self.font.font_size_pixels
);
self.clear_glyph_cache();
self.dirty_rows.fill(true);
}
pub fn update_window_padding(&mut self, padding: f32) -> Option<(usize, usize)> {
if (self.grid.window_padding - padding).abs() > f32::EPSILON {
self.grid.window_padding = padding;
let size = (self.config.width, self.config.height);
return Some(self.resize(size.0, size.1));
}
None
}
}