... at the end of the dialog truncation

This commit is contained in:
filipriec
2025-04-13 14:58:41 +02:00
parent 16d9fcdadc
commit c1c4394f94
3 changed files with 38 additions and 11 deletions

1
Cargo.lock generated
View File

@@ -436,6 +436,7 @@ dependencies = [
"toml",
"tonic",
"tracing",
"unicode-segmentation",
"unicode-width 0.2.0",
]

View File

@@ -19,4 +19,5 @@ tokio = { version = "1.43.0", features = ["full", "macros"] }
toml = "0.8.20"
tonic = "0.12.3"
tracing = "0.1.41"
unicode-segmentation = "1.12.0"
unicode-width = "0.2.0"

View File

@@ -1,13 +1,14 @@
// src/components/common/dialog.rs
use crate::config::colors::themes::Theme;
use ratatui::{
layout::{Constraint, Direction, Layout, Margin, Rect},
prelude::Alignment,
style::{Modifier, Style},
text::{Line, Span, Text},
widgets::{Block, BorderType, Borders, Paragraph, Clear}, // Added Clear
widgets::{Block, BorderType, Borders, Paragraph, Clear},
Frame,
};
use unicode_segmentation::UnicodeSegmentation; // For grapheme clusters
use unicode_width::UnicodeWidthStr; // For accurate width calculation
pub fn render_dialog(
f: &mut Frame,
@@ -18,15 +19,16 @@ pub fn render_dialog(
dialog_buttons: &[String],
dialog_active_button_index: usize,
) {
// Calculate required height based on the actual number of lines in the message
let message_lines: Vec<_> = dialog_message.lines().collect();
let message_height = message_lines.len() as u16;
let button_row_height = if dialog_buttons.is_empty() { 0 } else { 3 };
let vertical_padding = 2; // Block borders (top/bottom)
let inner_vertical_margin = 2; // Margin inside block (top/bottom)
// Calculate required height based on actual message lines
let required_inner_height =
message_height + button_row_height + inner_vertical_margin;
// Add block border height
let required_total_height = required_inner_height + vertical_padding;
// Use a fixed percentage width, clamped to min/max
@@ -61,10 +63,10 @@ pub fn render_dialog(
vertical: 1, // Top/Bottom padding inside border
});
// Layout for Message and Buttons
// Layout for Message and Buttons based on actual message height
let mut constraints = vec![
// Allocate space for message, ensuring at least 1 line height
Constraint::Min(message_height.max(1)),
Constraint::Length(message_height.max(1)), // Use actual calculated height
];
if button_row_height > 0 {
constraints.push(Constraint::Length(button_row_height));
@@ -76,15 +78,39 @@ pub fn render_dialog(
.split(inner_area);
// Render Message
let message_text = Text::from(
let available_width = inner_area.width as usize;
let ellipsis = "...";
let ellipsis_width = UnicodeWidthStr::width(ellipsis);
let processed_lines: Vec<Line> =
message_lines
.into_iter()
.map(|l| Line::from(Span::styled(l, Style::default().fg(theme.fg))))
.collect::<Vec<_>>(),
);
.map(|line| {
let line_width = UnicodeWidthStr::width(line);
if line_width > available_width {
// Truncate with ellipsis
let mut truncated_len = 0;
let mut current_width = 0;
// Iterate over graphemes to handle multi-byte characters correctly
for (idx, grapheme) in line.grapheme_indices(true) {
let grapheme_width = UnicodeWidthStr::width(grapheme);
if current_width + grapheme_width > available_width.saturating_sub(ellipsis_width) {
break; // Stop before exceeding width needed for text + ellipsis
}
current_width += grapheme_width;
truncated_len = idx + grapheme.len(); // Store the byte index of the end of the last fitting grapheme
}
let truncated_line = format!("{}{}", &line[..truncated_len], ellipsis);
Line::from(Span::styled(truncated_line, Style::default().fg(theme.fg)))
} else {
// Line fits, use it as is
Line::from(Span::styled(line, Style::default().fg(theme.fg)))
}
})
.collect();
let message_paragraph =
Paragraph::new(message_text).alignment(Alignment::Center);
Paragraph::new(Text::from(processed_lines)).alignment(Alignment::Center);
// Render message in the first chunk
f.render_widget(message_paragraph, chunks[0]);
@@ -143,4 +169,3 @@ pub fn render_dialog(
}
}
}