use crate::settings::EnforceMinimumContrast; use crate::terminal::color; use crate::terminal::grid_renderer::{render_cursor, render_grid, CellGlyphCache}; use crate::terminal::model::blockgrid::{BlockGrid, CursorDisplayPoint}; use crate::terminal::model::grid::grid_handler::Link; use crate::terminal::model::index::Point; use crate::terminal::model::ObfuscateSecrets; use crate::terminal::SizeInfo; use crate::themes::theme::GalaxyTheme; use galaxyui::fonts::{FamilyId, Properties, Weight}; use galaxyui::geometry::rect::RectF; use galaxyui::geometry::vector::{vec2f, Vector2F}; use galaxyui::{AppContext, Element, EntityId, PaintContext}; use pathfinder_color::ColorU; use std::collections::HashMap; use std::ops::Neg; use std::ops::RangeInclusive; use super::model::ansi::{CursorShape, CursorStyle}; use super::model::grid::RespectDisplayedOutput; use super::model::image_map::StoredImageMetadata; use super::model::SecretHandle; pub struct GridRenderParams { pub warp_theme: GalaxyTheme, pub font_family: FamilyId, pub font_size: f32, pub font_weight: Weight, pub line_height_ratio: f32, pub enforce_minimum_contrast: EnforceMinimumContrast, pub obfuscate_secrets: ObfuscateSecrets, pub size_info: SizeInfo, pub cell_size: Vector2F, pub use_ligature_rendering: bool, /// When true, suppresses cursor rendering for CLI agents when rich input is open. For agents that draw their own cursor (SHOW_CURSOR off), /// the cursor cell is skipped. For agents that let Warp draw the cursor /// (SHOW_CURSOR on), the `draw_cursor` call and cursor contrast colouring /// are suppressed instead. pub hide_cursor_cell: bool, } pub struct BlockGridParams { pub grid_render_params: GridRenderParams, pub colors: color::List, pub override_colors: color::OverrideList, pub bounds: RectF, } impl BlockGrid { #[allow(clippy::too_many_arguments)] pub fn draw<'a>( &self, grid_origin: Vector2F, origin: Vector2F, glyphs: &mut CellGlyphCache, alpha: u8, highlighted_url: Option<&Link>, link_tool_tip: Option<&Link>, hovered_secret: Option, ordered_matches: Option>>, focused_match_range: Option<&RangeInclusive>, properties: Properties, block_grid_params: &BlockGridParams, visible_cursor_shape: Option, image_metadata: &HashMap, ctx: &mut PaintContext, app: &AppContext, ) { let start_row = hidden_rows_above( grid_origin, origin, block_grid_params.grid_render_params.cell_size, ); let hidden_rows_below = hidden_rows_below( grid_origin, origin, block_grid_params.grid_render_params.cell_size, block_grid_params.bounds, self.len_displayed(), ); let end_row = self.len_displayed().saturating_sub(hidden_rows_below); let grid = self.grid_handler(); let obfuscate_secrets = block_grid_params .grid_render_params .obfuscate_secrets .and(&grid.get_secret_obfuscation()); render_grid( grid, start_row, end_row, &block_grid_params.colors, &block_grid_params.override_colors, &block_grid_params.grid_render_params.warp_theme, properties, block_grid_params.grid_render_params.font_family, block_grid_params.grid_render_params.font_size, block_grid_params.grid_render_params.line_height_ratio, block_grid_params.grid_render_params.cell_size, block_grid_params .grid_render_params .size_info .padding_x_px(), grid_origin, glyphs, alpha, highlighted_url, link_tool_tip, ordered_matches, focused_match_range, block_grid_params .grid_render_params .enforce_minimum_contrast, obfuscate_secrets, hovered_secret, block_grid_params.grid_render_params.use_ligature_rendering, visible_cursor_shape, RespectDisplayedOutput::Yes, image_metadata, None, block_grid_params.grid_render_params.hide_cursor_cell, ctx, app, ); } pub fn draw_with_default_params( &self, grid_origin: Vector2F, origin: Vector2F, block_grid_params: &BlockGridParams, ctx: &mut PaintContext, app: &AppContext, ) { let mut glyphs = CellGlyphCache::default(); self.draw( grid_origin, origin, &mut glyphs, 255, None, None, None, None::>>, None, Properties::default(), block_grid_params, None, &HashMap::new(), ctx, app, ) } #[allow(clippy::too_many_arguments)] pub fn draw_cursor( &self, grid_origin: Vector2F, grid_render_params: &GridRenderParams, ctx: &mut PaintContext, terminal_view_id: EntityId, cursor_hint_text: Option<&mut Box>, color: ColorU, app: &AppContext, ) { let cursor_style = self.cursor_style(); let (cursor_display_point, is_cursor_on_wide_char, cursor_style, cursor_hint_text) = match self.cursor_display_point() { Some(CursorDisplayPoint::Visible(cursor_display_point)) => ( cursor_display_point, self.grid_handler().is_cursor_on_wide_char(), cursor_style, cursor_hint_text, ), Some(CursorDisplayPoint::HiddenCache(cursor_display_point)) => ( cursor_display_point, false, CursorStyle { shape: CursorShape::Hidden, ..cursor_style }, None, ), None => return, }; render_cursor( grid_render_params, cursor_display_point, is_cursor_on_wide_char, cursor_style, grid_render_params.size_info.padding_x_px(), grid_origin, color, ctx, terminal_view_id, cursor_hint_text, app, ) } } fn lines_to_pixels(line: usize, cell_size: Vector2F) -> Vector2F { vec2f(0., line as f32 * cell_size.y()) } fn pixels_to_lines(coord: Vector2F, cell_size: Vector2F) -> usize { (coord.y() / cell_size.y()) as usize } /// Computes the number of rows that are not visible in the current grid above the viewport. fn hidden_rows_above(grid_origin: Vector2F, origin: Vector2F, cell_size: Vector2F) -> usize { ((grid_origin.y() - origin.y()) / cell_size.y()) .neg() .max(0.) .floor() as usize } /// Computes the number of rows that are not visible in the grid below the viewport. fn hidden_rows_below( grid_origin: Vector2F, origin: Vector2F, cell_size: Vector2F, bounds: RectF, grid_len: usize, ) -> usize { let grid_end = grid_origin + lines_to_pixels(grid_len, cell_size); let lower_left = origin.y() + bounds.height(); pixels_to_lines(grid_end - lower_left, cell_size) }