render_table.rs

  1use crate::types::TableCell;
  2use gpui::{AnyElement, Entity};
  3use std::ops::Range;
  4use ui::{ColumnWidthConfig, ResizableColumnsState, Table, UncheckedTableRow, div, prelude::*};
  5
  6use crate::{
  7    CsvPreviewView,
  8    settings::RowRenderMechanism,
  9    types::{AnyColumn, DisplayCellId, DisplayRow},
 10};
 11
 12impl CsvPreviewView {
 13    /// Creates a new table.
 14    /// Column number is derived from the `ResizableColumnsState` entity.
 15    pub(crate) fn create_table(
 16        &self,
 17        current_widths: &Entity<ResizableColumnsState>,
 18        cx: &mut Context<Self>,
 19    ) -> AnyElement {
 20        self.create_table_inner(self.engine.contents.rows.len(), current_widths, cx)
 21    }
 22
 23    fn create_table_inner(
 24        &self,
 25        row_count: usize,
 26        current_widths: &Entity<ResizableColumnsState>,
 27        cx: &mut Context<Self>,
 28    ) -> AnyElement {
 29        let cols = current_widths.read(cx).cols();
 30        // Create headers array with interactive elements
 31        let mut headers = Vec::with_capacity(cols);
 32
 33        headers.push(self.create_row_identifier_header(cx));
 34
 35        // Add the actual CSV headers with sort buttons
 36        for i in 0..(cols - 1) {
 37            let header_text = self
 38                .engine
 39                .contents
 40                .headers
 41                .get(AnyColumn(i))
 42                .and_then(|h| h.display_value().cloned())
 43                .unwrap_or_else(|| format!("Col {}", i + 1).into());
 44
 45            headers.push(self.create_header_element_with_sort_button(
 46                header_text,
 47                cx,
 48                AnyColumn::from(i),
 49            ));
 50        }
 51
 52        Table::new(cols)
 53            .interactable(&self.table_interaction_state)
 54            .striped()
 55            .width_config(ColumnWidthConfig::Resizable(current_widths.clone()))
 56            .header(headers)
 57            .disable_base_style()
 58            .map(|table| {
 59                let row_identifier_text_color = cx.theme().colors().editor_line_number;
 60                match self.settings.rendering_with {
 61                    RowRenderMechanism::VariableList => {
 62                        table.variable_row_height_list(row_count, self.list_state.clone(), {
 63                            cx.processor(move |this, display_row: usize, _window, cx| {
 64                                this.performance_metrics.rendered_indices.push(display_row);
 65
 66                                let display_row = DisplayRow(display_row);
 67                                Self::render_single_table_row(
 68                                    this,
 69                                    cols,
 70                                    display_row,
 71                                    row_identifier_text_color,
 72                                    cx,
 73                                )
 74                                .unwrap_or_else(|| panic!("Expected to render a table row"))
 75                            })
 76                        })
 77                    }
 78                    RowRenderMechanism::UniformList => {
 79                        table.uniform_list("csv-table", row_count, {
 80                            cx.processor(move |this, range: Range<usize>, _window, cx| {
 81                                // Record all display indices in the range for performance metrics
 82                                this.performance_metrics
 83                                    .rendered_indices
 84                                    .extend(range.clone());
 85
 86                                range
 87                                    .filter_map(|display_index| {
 88                                        Self::render_single_table_row(
 89                                            this,
 90                                            cols,
 91                                            DisplayRow(display_index),
 92                                            row_identifier_text_color,
 93                                            cx,
 94                                        )
 95                                    })
 96                                    .collect()
 97                            })
 98                        })
 99                    }
100                }
101            })
102            .into_any_element()
103    }
104
105    /// Render a single table row
106    ///
107    /// Used both by UniformList and VariableRowHeightList
108    fn render_single_table_row(
109        this: &CsvPreviewView,
110        cols: usize,
111        display_row: DisplayRow,
112        row_identifier_text_color: gpui::Hsla,
113        cx: &Context<CsvPreviewView>,
114    ) -> Option<UncheckedTableRow<AnyElement>> {
115        // Get the actual row index from our sorted indices
116        let data_row = this.engine.d2d_mapping().get_data_row(display_row)?;
117        let row = this.engine.contents.get_row(data_row)?;
118
119        let mut elements = Vec::with_capacity(cols);
120        elements.push(this.create_row_identifier_cell(display_row, data_row, cx)?);
121
122        // Remaining columns: actual CSV data
123        for col in (0..this.engine.contents.number_of_cols).map(AnyColumn) {
124            let table_cell = row.expect_get(col);
125
126            // TODO: Introduce `<null>` cell type
127            let cell_content = table_cell.display_value().cloned().unwrap_or_default();
128
129            let display_cell_id = DisplayCellId::new(display_row, col);
130
131            let cell = div().size_full().whitespace_nowrap().text_ellipsis().child(
132                CsvPreviewView::create_selectable_cell(
133                    display_cell_id,
134                    cell_content,
135                    this.settings.vertical_alignment,
136                    this.settings.font_type,
137                    cx,
138                ),
139            );
140
141            elements.push(
142                div()
143                    .size_full()
144                    .when(this.settings.show_debug_info, |parent| {
145                        parent.child(div().text_color(row_identifier_text_color).child(
146                            match table_cell {
147                                TableCell::Real { position: pos, .. } => {
148                                    let slv = pos.start.timestamp().value;
149                                    let so = pos.start.offset;
150                                    let elv = pos.end.timestamp().value;
151                                    let eo = pos.end.offset;
152                                    format!("Pos {so}(L{slv})-{eo}(L{elv})")
153                                }
154                                TableCell::Virtual => "Virtual cell".into(),
155                            },
156                        ))
157                    })
158                    .text_ui(cx)
159                    .child(cell)
160                    .into_any_element(),
161            );
162        }
163
164        Some(elements)
165    }
166}