use crate::types::TableCell; use gpui::{AnyElement, Entity}; use std::ops::Range; use ui::Table; use ui::TableColumnWidths; use ui::TableResizeBehavior; use ui::UncheckedTableRow; use ui::{DefiniteLength, div, prelude::*}; use crate::{ CsvPreviewView, settings::RowRenderMechanism, types::{AnyColumn, DisplayCellId, DisplayRow}, }; impl CsvPreviewView { /// Creates a new table. /// Column number is derived from the `TableColumnWidths` entity. pub(crate) fn create_table( &self, current_widths: &Entity, cx: &mut Context, ) -> AnyElement { let cols = current_widths.read(cx).cols(); let remaining_col_number = cols - 1; let fraction = if remaining_col_number > 0 { 1. / remaining_col_number as f32 } else { 1. // only column with line numbers is present. Put 100%, but it will be overwritten anyways :D }; let mut widths = vec![DefiniteLength::Fraction(fraction); cols]; let line_number_width = self.calculate_row_identifier_column_width(); widths[0] = DefiniteLength::Absolute(AbsoluteLength::Pixels(line_number_width.into())); let mut resize_behaviors = vec![TableResizeBehavior::Resizable; cols]; resize_behaviors[0] = TableResizeBehavior::None; self.create_table_inner( self.engine.contents.rows.len(), widths, resize_behaviors, current_widths, cx, ) } fn create_table_inner( &self, row_count: usize, widths: UncheckedTableRow, resize_behaviors: UncheckedTableRow, current_widths: &Entity, cx: &mut Context, ) -> AnyElement { let cols = widths.len(); // Create headers array with interactive elements let mut headers = Vec::with_capacity(cols); headers.push(self.create_row_identifier_header(cx)); // Add the actual CSV headers with sort buttons for i in 0..(cols - 1) { let header_text = self .engine .contents .headers .get(AnyColumn(i)) .and_then(|h| h.display_value().cloned()) .unwrap_or_else(|| format!("Col {}", i + 1).into()); headers.push(self.create_header_element_with_sort_button( header_text, cx, AnyColumn::from(i), )); } Table::new(cols) .interactable(&self.table_interaction_state) .striped() .column_widths(widths) .resizable_columns(resize_behaviors, current_widths, cx) .header(headers) .disable_base_style() .map(|table| { let row_identifier_text_color = cx.theme().colors().editor_line_number; match self.settings.rendering_with { RowRenderMechanism::VariableList => { table.variable_row_height_list(row_count, self.list_state.clone(), { cx.processor(move |this, display_row: usize, _window, cx| { this.performance_metrics.rendered_indices.push(display_row); let display_row = DisplayRow(display_row); Self::render_single_table_row( this, cols, display_row, row_identifier_text_color, cx, ) .unwrap_or_else(|| panic!("Expected to render a table row")) }) }) } RowRenderMechanism::UniformList => { table.uniform_list("csv-table", row_count, { cx.processor(move |this, range: Range, _window, cx| { // Record all display indices in the range for performance metrics this.performance_metrics .rendered_indices .extend(range.clone()); range .filter_map(|display_index| { Self::render_single_table_row( this, cols, DisplayRow(display_index), row_identifier_text_color, cx, ) }) .collect() }) }) } } }) .into_any_element() } /// Render a single table row /// /// Used both by UniformList and VariableRowHeightList fn render_single_table_row( this: &CsvPreviewView, cols: usize, display_row: DisplayRow, row_identifier_text_color: gpui::Hsla, cx: &Context, ) -> Option> { // Get the actual row index from our sorted indices let data_row = this.engine.d2d_mapping().get_data_row(display_row)?; let row = this.engine.contents.get_row(data_row)?; let mut elements = Vec::with_capacity(cols); elements.push(this.create_row_identifier_cell(display_row, data_row, cx)?); // Remaining columns: actual CSV data for col in (0..this.engine.contents.number_of_cols).map(AnyColumn) { let table_cell = row.expect_get(col); // TODO: Introduce `` cell type let cell_content = table_cell.display_value().cloned().unwrap_or_default(); let display_cell_id = DisplayCellId::new(display_row, col); let cell = div().size_full().whitespace_nowrap().text_ellipsis().child( CsvPreviewView::create_selectable_cell( display_cell_id, cell_content, this.settings.vertical_alignment, this.settings.font_type, cx, ), ); elements.push( div() .size_full() .when(this.settings.show_debug_info, |parent| { parent.child(div().text_color(row_identifier_text_color).child( match table_cell { TableCell::Real { position: pos, .. } => { let slv = pos.start.timestamp().value; let so = pos.start.offset; let elv = pos.end.timestamp().value; let eo = pos.end.offset; format!("Pos {so}(L{slv})-{eo}(L{elv})") } TableCell::Virtual => "Virtual cell".into(), }, )) }) .text_ui(cx) .child(cell) .into_any_element(), ); } Some(elements) } }