chore: wrap long titles
45037f22
1 file(s) · +52 −18
| 85 | 85 | format!("{} {}{}, {}", dt.format("%B"), day, suffix, dt.format("%Y")) |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | + | fn wrap_text(text: &str, max_width: usize) -> Vec<String> { |
|
| 89 | + | if max_width == 0 { |
|
| 90 | + | return vec![text.to_string()]; |
|
| 91 | + | } |
|
| 92 | + | let mut lines: Vec<String> = Vec::new(); |
|
| 93 | + | let mut current = String::new(); |
|
| 94 | + | for word in text.split_whitespace() { |
|
| 95 | + | if current.is_empty() { |
|
| 96 | + | current.push_str(word); |
|
| 97 | + | } else if current.len() + 1 + word.len() <= max_width { |
|
| 98 | + | current.push(' '); |
|
| 99 | + | current.push_str(word); |
|
| 100 | + | } else { |
|
| 101 | + | lines.push(current); |
|
| 102 | + | current = word.to_string(); |
|
| 103 | + | } |
|
| 104 | + | } |
|
| 105 | + | if !current.is_empty() { |
|
| 106 | + | lines.push(current); |
|
| 107 | + | } |
|
| 108 | + | if lines.is_empty() { |
|
| 109 | + | lines.push(String::new()); |
|
| 110 | + | } |
|
| 111 | + | lines |
|
| 112 | + | } |
|
| 113 | + | ||
| 88 | 114 | fn render(frame: &mut Frame, entries: &[(&Entry, Option<&str>)], state: &mut ListState) { |
|
| 115 | + | let outer = frame.area(); |
|
| 116 | + | let [_, center, _] = Layout::default() |
|
| 117 | + | .direction(Direction::Horizontal) |
|
| 118 | + | .constraints([ |
|
| 119 | + | Constraint::Fill(1), |
|
| 120 | + | Constraint::Max(80), |
|
| 121 | + | Constraint::Fill(1), |
|
| 122 | + | ]) |
|
| 123 | + | .areas(outer); |
|
| 124 | + | ||
| 125 | + | let block = Block::new().padding(Padding::symmetric(2, 1)); |
|
| 126 | + | let inner = block.inner(center); |
|
| 127 | + | let title_width = inner.width.saturating_sub(2) as usize; |
|
| 128 | + | ||
| 89 | 129 | let dim = Style::new().fg(Color::DarkGray); |
|
| 90 | 130 | let author_style = Style::new() |
|
| 91 | 131 | .fg(Color::DarkGray) |
|
| 110 | 150 | .and_then(|a| a.name.as_deref()) |
|
| 111 | 151 | .or(*feed_title) |
|
| 112 | 152 | .unwrap_or("anon"); |
|
| 113 | - | ListItem::new(Text::from(vec![ |
|
| 114 | - | Line::from(vec![Span::raw(bar), Span::styled(date, dim)]), |
|
| 115 | - | Line::from(vec![Span::raw(bar), Span::raw(title.to_string())]), |
|
| 116 | - | Line::from(vec![Span::raw(bar), Span::styled(author.to_string(), author_style)]), |
|
| 117 | - | Line::from(""), |
|
| 118 | - | ])) |
|
| 153 | + | ||
| 154 | + | let mut lines = vec![Line::from(vec![Span::raw(bar), Span::styled(date, dim)])]; |
|
| 155 | + | for wrapped in wrap_text(title, title_width) { |
|
| 156 | + | lines.push(Line::from(vec![Span::raw(bar), Span::raw(wrapped)])); |
|
| 157 | + | } |
|
| 158 | + | lines.push(Line::from(vec![ |
|
| 159 | + | Span::raw(bar), |
|
| 160 | + | Span::styled(author.to_string(), author_style), |
|
| 161 | + | ])); |
|
| 162 | + | lines.push(Line::from("")); |
|
| 163 | + | ||
| 164 | + | ListItem::new(Text::from(lines)) |
|
| 119 | 165 | }) |
|
| 120 | 166 | .collect(); |
|
| 121 | 167 | ||
| 122 | - | let outer = frame.area(); |
|
| 123 | - | let [_, center, _] = Layout::default() |
|
| 124 | - | .direction(Direction::Horizontal) |
|
| 125 | - | .constraints([ |
|
| 126 | - | Constraint::Fill(1), |
|
| 127 | - | Constraint::Max(80), |
|
| 128 | - | Constraint::Fill(1), |
|
| 129 | - | ]) |
|
| 130 | - | .areas(outer); |
|
| 131 | - | ||
| 132 | - | let block = Block::new().padding(Padding::symmetric(2, 1)); |
|
| 133 | - | let inner = block.inner(center); |
|
| 134 | 168 | frame.render_widget(block, center); |
|
| 135 | 169 | frame.render_stateful_widget( |
|
| 136 | 170 | List::new(items) |
|