### Changes Adds a checkbox to toggle between multiline cells (current behavior) and single-line rows that clip overflow, so wide CSVs stay compact when full cell content isn't needed at a glance. #### Demo | Before | After | | --- | --- | | No single line mode | <img width="662" height="316" alt="image" src="https://github.com/user-attachments/assets/3f2a9673-557c-4539-b755-9ff2633de2d7" /> | | <img width="666" height="287" alt="image" src="https://github.com/user-attachments/assets/703f7214-a722-4fa5-9125-f043439e21b2" /> | <img width="660" height="317" alt="image" src="https://github.com/user-attachments/assets/be6ca912-e686-44da-90eb-58f09fab51f1" /> | ### Testing - Toggle "Display multiline rows" off: cells clip to one line with ellipsis instead of wrapping - Toggle it back on: cells wrap and grow to show full content - Resize the window / change font size: single-line clip height tracks the actual text line height instead of a stale hardcoded value Release Notes: - N/A
180 lines
5.8 KiB
Rust
180 lines
5.8 KiB
Rust
use ui::{
|
|
ActiveTheme as _, AnyElement, Button, ButtonCommon as _, ButtonSize, ButtonStyle,
|
|
Clickable as _, Context, ElementId, IntoElement as _, ParentElement as _, SharedString,
|
|
Styled as _, StyledTypography as _, Tooltip, div,
|
|
};
|
|
|
|
use crate::{
|
|
CsvPreviewView,
|
|
settings::RowIdentifiers,
|
|
types::{DataRow, DisplayRow, LineNumber},
|
|
};
|
|
|
|
pub enum RowIdentDisplayMode {
|
|
/// E.g
|
|
/// ```text
|
|
/// 1
|
|
/// ...
|
|
/// 5
|
|
/// ```
|
|
Vertical,
|
|
/// E.g.
|
|
/// ```text
|
|
/// 1-5
|
|
/// ```
|
|
Horizontal,
|
|
}
|
|
|
|
impl LineNumber {
|
|
pub fn display_string(&self, mode: RowIdentDisplayMode) -> String {
|
|
match *self {
|
|
LineNumber::Line(line) => line.to_string(),
|
|
LineNumber::LineRange(start, end) => match mode {
|
|
RowIdentDisplayMode::Vertical => {
|
|
if start + 1 == end {
|
|
format!("{start}\n{end}")
|
|
} else {
|
|
format!("{start}\n-\n{end}")
|
|
}
|
|
}
|
|
RowIdentDisplayMode::Horizontal => {
|
|
format!("{start}-{end}")
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CsvPreviewView {
|
|
/// Calculate the optimal width for the row identifier column (line numbers or row numbers).
|
|
///
|
|
/// This ensures the column is wide enough to display the largest identifier comfortably,
|
|
/// but not wastefully wide for small files.
|
|
pub(crate) fn calculate_row_identifier_column_width(&self) -> f32 {
|
|
match self.settings.numbering_type {
|
|
RowIdentifiers::SrcLines => self.calculate_line_number_width(),
|
|
RowIdentifiers::RowNum => self.calculate_row_number_width(),
|
|
}
|
|
}
|
|
|
|
/// Calculate width needed for line numbers (can be multi-line)
|
|
fn calculate_line_number_width(&self) -> f32 {
|
|
// Find the maximum line number that could be displayed
|
|
let max_line_number = self
|
|
.engine
|
|
.contents
|
|
.line_numbers
|
|
.iter()
|
|
.map(|ln| match ln {
|
|
LineNumber::Line(n) => *n,
|
|
LineNumber::LineRange(_, end) => *end,
|
|
})
|
|
.max()
|
|
.unwrap_or_default();
|
|
|
|
let digit_count = if max_line_number == 0 {
|
|
1
|
|
} else {
|
|
(max_line_number as f32).log10().floor() as usize + 1
|
|
};
|
|
|
|
let char_width_px = 9.0; // TODO: get real width of the characters
|
|
let base_width = (digit_count as f32) * char_width_px;
|
|
let padding = 20.0;
|
|
let min_width = 60.0;
|
|
(base_width + padding).max(min_width)
|
|
}
|
|
|
|
/// Calculate width needed for sequential row numbers
|
|
fn calculate_row_number_width(&self) -> f32 {
|
|
let max_row_number = self.engine.contents.rows.len();
|
|
|
|
let digit_count = if max_row_number == 0 {
|
|
1
|
|
} else {
|
|
(max_row_number as f32).log10().floor() as usize + 1
|
|
};
|
|
|
|
let char_width_px = 9.0; // TODO: get real width of the characters
|
|
let base_width = (digit_count as f32) * char_width_px;
|
|
let padding = 20.0;
|
|
let min_width = 60.0;
|
|
(base_width + padding).max(min_width)
|
|
}
|
|
|
|
pub(crate) fn create_row_identifier_header(
|
|
&self,
|
|
cx: &mut Context<'_, CsvPreviewView>,
|
|
) -> AnyElement {
|
|
// First column: row identifier (clickable to toggle between Lines and Rows)
|
|
let row_identifier_text = match self.settings.numbering_type {
|
|
RowIdentifiers::SrcLines => "Lines",
|
|
RowIdentifiers::RowNum => "Rows",
|
|
};
|
|
|
|
let view = cx.entity();
|
|
let value = div()
|
|
.font_buffer(cx)
|
|
.child(
|
|
Button::new(
|
|
ElementId::Name("row-identifier-toggle".into()),
|
|
row_identifier_text,
|
|
)
|
|
.style(ButtonStyle::Subtle)
|
|
.size(ButtonSize::Compact)
|
|
.tooltip(Tooltip::text(
|
|
"Toggle between: file line numbers or sequential row numbers",
|
|
))
|
|
.on_click(move |_event, _window, cx| {
|
|
view.update(cx, |this, cx| {
|
|
this.settings.numbering_type = match this.settings.numbering_type {
|
|
RowIdentifiers::SrcLines => RowIdentifiers::RowNum,
|
|
RowIdentifiers::RowNum => RowIdentifiers::SrcLines,
|
|
};
|
|
this.sync_column_widths(cx);
|
|
cx.notify();
|
|
});
|
|
}),
|
|
)
|
|
.into_any_element();
|
|
value
|
|
}
|
|
|
|
pub(crate) fn create_row_identifier_cell(
|
|
&self,
|
|
display_row: DisplayRow,
|
|
data_row: DataRow,
|
|
cx: &Context<'_, CsvPreviewView>,
|
|
) -> Option<AnyElement> {
|
|
let row_identifier: SharedString = match self.settings.numbering_type {
|
|
RowIdentifiers::SrcLines => self
|
|
.engine
|
|
.contents
|
|
.line_numbers
|
|
.get(*data_row)?
|
|
.display_string(if self.settings.multiline_cells_effectively_enabled() {
|
|
RowIdentDisplayMode::Vertical
|
|
} else {
|
|
RowIdentDisplayMode::Horizontal
|
|
})
|
|
.into(),
|
|
RowIdentifiers::RowNum => (*display_row + 1).to_string().into(),
|
|
};
|
|
|
|
let value = div()
|
|
.flex()
|
|
.px_1()
|
|
.border_color(cx.theme().colors().border_variant)
|
|
.bg(cx.theme().colors().panel_background)
|
|
.h_full()
|
|
.text_color(cx.theme().colors().text_muted)
|
|
.justify_center()
|
|
.items_center()
|
|
.font_buffer(cx)
|
|
.text_ui(cx)
|
|
.child(row_identifier)
|
|
.into_any_element();
|
|
Some(value)
|
|
}
|
|
}
|