render_table.rs

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