render_table.rs

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