Fix task list alignment: use char count not byte count, truncate title
- Use .chars().count() instead of .len() for accurate visual width - Truncate title with … when title + due_text exceeds available width - Compute padding from truncated title width for consistent alignment
This commit is contained in:
+27
-4
@@ -94,9 +94,34 @@ pub fn render_task_list(
|
|||||||
.map(relative_due_str)
|
.map(relative_due_str)
|
||||||
.unwrap_or((String::new(), Color::DarkGray));
|
.unwrap_or((String::new(), Color::DarkGray));
|
||||||
|
|
||||||
|
let checkbox_str = format!("{} ", checkbox);
|
||||||
|
let checkbox_width = checkbox_str.chars().count();
|
||||||
|
let title_width = task.title.chars().count();
|
||||||
|
let due_width = due_text.chars().count();
|
||||||
|
|
||||||
|
let max_title = content_width.saturating_sub(checkbox_width + due_width);
|
||||||
|
|
||||||
|
let display_title: String = if title_width <= max_title {
|
||||||
|
task.title.to_string()
|
||||||
|
} else if max_title >= 2 {
|
||||||
|
let take = max_title - 1;
|
||||||
|
let mut s: String = task.title.chars().take(take).collect();
|
||||||
|
s.push('…');
|
||||||
|
s
|
||||||
|
} else {
|
||||||
|
task.title.chars().take(max_title).collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
let pad = if due_text.is_empty() {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
let used = checkbox_width + display_title.chars().count() + due_width;
|
||||||
|
content_width.saturating_sub(used)
|
||||||
|
};
|
||||||
|
|
||||||
let mut spans = vec![
|
let mut spans = vec![
|
||||||
Span::styled(
|
Span::styled(
|
||||||
format!("{} ", checkbox),
|
checkbox_str,
|
||||||
Style::default().fg(if task.status == TaskStatus::Completed {
|
Style::default().fg(if task.status == TaskStatus::Completed {
|
||||||
Color::Green
|
Color::Green
|
||||||
} else {
|
} else {
|
||||||
@@ -104,7 +129,7 @@ pub fn render_task_list(
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
Span::styled(
|
Span::styled(
|
||||||
&task.title,
|
display_title,
|
||||||
Style::default().fg(DETAIL_COLOR).add_modifier(
|
Style::default().fg(DETAIL_COLOR).add_modifier(
|
||||||
if task.status == TaskStatus::Completed {
|
if task.status == TaskStatus::Completed {
|
||||||
Modifier::CROSSED_OUT
|
Modifier::CROSSED_OUT
|
||||||
@@ -116,8 +141,6 @@ pub fn render_task_list(
|
|||||||
];
|
];
|
||||||
|
|
||||||
if !due_text.is_empty() {
|
if !due_text.is_empty() {
|
||||||
let left_len: usize = spans.iter().map(|s| s.content.len()).sum();
|
|
||||||
let pad = content_width.saturating_sub(left_len + due_text.len());
|
|
||||||
spans.push(Span::raw(" ".repeat(pad)));
|
spans.push(Span::raw(" ".repeat(pad)));
|
||||||
spans.push(Span::styled(due_text, Style::default().fg(due_color)));
|
spans.push(Span::styled(due_text, Style::default().fg(due_color)));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user