main: reimplement text wrapping across lists
This commit is contained in:
+70
-21
@@ -115,7 +115,30 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_line<'a>(text: &str, prefix: &'a str, prefix_style: Style, max_width: usize) -> Text<'a> {
|
fn format_line<'a>(entry: &ConversationEntry, max_width: usize) -> Text<'a> {
|
||||||
|
let prefix = match entry {
|
||||||
|
ConversationEntry::Eva(_) => "Eva: ",
|
||||||
|
ConversationEntry::User(_) => "Argee: ",
|
||||||
|
ConversationEntry::ShipComputer(_) => "Ship Computer: ",
|
||||||
|
_ => "",
|
||||||
|
};
|
||||||
|
|
||||||
|
let style = match entry {
|
||||||
|
ConversationEntry::Eva(_) => Style::new().fg(style::Color::Cyan),
|
||||||
|
ConversationEntry::User(_) => Style::new().fg(style::Color::Magenta),
|
||||||
|
ConversationEntry::ShipComputer(_) => Style::new().fg(style::Color::Green),
|
||||||
|
ConversationEntry::StageDirection(_) => Style::new().fg(style::Color::Yellow),
|
||||||
|
ConversationEntry::SystemMessage(_) => Style::new().fg(style::Color::DarkGray),
|
||||||
|
};
|
||||||
|
|
||||||
|
let text = match entry {
|
||||||
|
ConversationEntry::Eva(text) => text,
|
||||||
|
ConversationEntry::ShipComputer(text) => text,
|
||||||
|
ConversationEntry::StageDirection(text) => text,
|
||||||
|
ConversationEntry::SystemMessage(text) => text,
|
||||||
|
ConversationEntry::User(text) => text
|
||||||
|
};
|
||||||
|
|
||||||
let avail_width = max_width - prefix.len();
|
let avail_width = max_width - prefix.len();
|
||||||
let indent = " ".repeat(prefix.len());
|
let indent = " ".repeat(prefix.len());
|
||||||
let wrap_options = textwrap::Options::new(avail_width).initial_indent(prefix).subsequent_indent(&indent);
|
let wrap_options = textwrap::Options::new(avail_width).initial_indent(prefix).subsequent_indent(&indent);
|
||||||
@@ -124,7 +147,7 @@ impl App {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(idx, s)| {
|
.map(|(idx, s)| {
|
||||||
if idx == 0 {
|
if idx == 0 {
|
||||||
Line::from_iter([Span::from(prefix).style(prefix_style), Span::from(s[prefix.len()..].to_string())])
|
Line::from_iter([Span::from(prefix).style(style), Span::from(s[prefix.len()..].to_string())])
|
||||||
} else {
|
} else {
|
||||||
Line::from(s.to_string())
|
Line::from(s.to_string())
|
||||||
}
|
}
|
||||||
@@ -136,13 +159,7 @@ impl App {
|
|||||||
fn draw_conversation(&mut self, frame: &mut Frame, area: Rect) {
|
fn draw_conversation(&mut self, frame: &mut Frame, area: Rect) {
|
||||||
let width = area.width.into();
|
let width = area.width.into();
|
||||||
let items: Vec<Text> = self.scene.conversation().iter().rev().map(|entry| {
|
let items: Vec<Text> = self.scene.conversation().iter().rev().map(|entry| {
|
||||||
match entry {
|
Self::format_line(entry, width)
|
||||||
ConversationEntry::User(text) => Self::format_line(text, "Argee: ", Style::new().fg(style::Color::Magenta), width),
|
|
||||||
ConversationEntry::Eva(text) => Self::format_line(text, "Eva: ", Style::new().fg(style::Color::Cyan), width),
|
|
||||||
ConversationEntry::ShipComputer(text) => Self::format_line(text, "Ship Computer: ", Style::new().fg(style::Color::Green), width),
|
|
||||||
ConversationEntry::StageDirection(text) => Self::format_line(text, "", Style::new(), width).style(ratatui::style::Color::Yellow),
|
|
||||||
ConversationEntry::SystemMessage(text) => Self::format_line(text, "", Style::new(), width).style(ratatui::style::Color::DarkGray)
|
|
||||||
}
|
|
||||||
}).collect();
|
}).collect();
|
||||||
// TODO: Would be nice to be able to scroll a longer conversation with the scroll wheel, or with page up/down
|
// TODO: Would be nice to be able to scroll a longer conversation with the scroll wheel, or with page up/down
|
||||||
frame.render_stateful_widget(
|
frame.render_stateful_widget(
|
||||||
@@ -166,12 +183,31 @@ impl App {
|
|||||||
.block(borders);
|
.block(borders);
|
||||||
frame.render_widget(list, area);
|
frame.render_widget(list, area);
|
||||||
} else {
|
} else {
|
||||||
|
let wrap_options = textwrap::Options::new(area.width as usize).subsequent_indent("...");
|
||||||
|
let options: Vec<Text> = self.scene.reply_options().iter().map(|option| {
|
||||||
|
let mut contents: Vec<Line> = vec![];
|
||||||
|
|
||||||
|
if let Some(direction) = &option.stage_direction {
|
||||||
|
let padded = format!("({})", direction);
|
||||||
|
let mut wrapped_direction: Vec<Line> = textwrap::wrap(&padded, wrap_options.clone())
|
||||||
|
.iter()
|
||||||
|
.map(|x| { Line::from(x.to_string()).fg(style::Color::Yellow)}).collect();
|
||||||
|
contents.append(&mut wrapped_direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut text: Vec<Line> = textwrap::wrap(&option.text, wrap_options.clone())
|
||||||
|
.iter()
|
||||||
|
.map(|x| { Line::from(x.to_string())}).collect();
|
||||||
|
contents.append(&mut text);
|
||||||
|
Text::from_iter(contents)
|
||||||
|
}).collect();
|
||||||
frame.render_stateful_widget(
|
frame.render_stateful_widget(
|
||||||
List::new(self.scene.reply_options().clone())
|
List::new(options)
|
||||||
.block(borders)
|
.block(borders)
|
||||||
.style(ratatui::style::Color::White)
|
.style(ratatui::style::Color::White)
|
||||||
.highlight_symbol("> ")
|
.highlight_symbol("> ".fg(Color::Cyan))
|
||||||
.highlight_style(style::Style::new().bold().fg(style::Color::Cyan)),
|
.highlight_style(style::Style::new().bold().bg(style::Color::DarkGray))
|
||||||
|
.repeat_highlight_symbol(true),
|
||||||
area,
|
area,
|
||||||
&mut self.reply_state
|
&mut self.reply_state
|
||||||
);
|
);
|
||||||
@@ -199,24 +235,37 @@ impl App {
|
|||||||
|
|
||||||
fn draw_status(&self, frame: &mut Frame, area: Rect) {
|
fn draw_status(&self, frame: &mut Frame, area: Rect) {
|
||||||
let minutes_remaining = self.direction.time_remaining.num_seconds() / 60;
|
let minutes_remaining = self.direction.time_remaining.num_seconds() / 60;
|
||||||
let time_style = if minutes_remaining == 0 {
|
|
||||||
Style::new().fg(ratatui::style::Color::Red).bold().rapid_blink()
|
let negative = self.direction.time_remaining.abs() != self.direction.time_remaining;
|
||||||
} else if minutes_remaining <= 5 {
|
|
||||||
Style::new().fg(ratatui::style::Color::Red).bold().slow_blink()
|
|
||||||
} else if minutes_remaining <= 10 {
|
let time_style = if minutes_remaining <= 0 || negative {
|
||||||
|
Style::new().fg(ratatui::style::Color::LightRed).bold()
|
||||||
|
} else if minutes_remaining < 5 {
|
||||||
|
Style::new().fg(ratatui::style::Color::LightRed).bold()
|
||||||
|
} else if minutes_remaining < 10 {
|
||||||
ratatui::style::Color::Red.into()
|
ratatui::style::Color::Red.into()
|
||||||
} else if minutes_remaining <= 25 {
|
} else if minutes_remaining < 25 {
|
||||||
ratatui::style::Color::Yellow.into()
|
ratatui::style::Color::Yellow.into()
|
||||||
} else if minutes_remaining <= 60 {
|
} else if minutes_remaining < 60 {
|
||||||
ratatui::style::Color::Green.into()
|
ratatui::style::Color::Green.into()
|
||||||
} else {
|
} else {
|
||||||
ratatui::style::Color::Blue.into()
|
ratatui::style::Color::Blue.into()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let formatted_time = if negative {
|
||||||
|
format!("-{:0>2}:{:0>2}:{:0>2}", self.direction.time_remaining.num_hours().abs(), self.direction.time_remaining.num_minutes().abs()% 60, self.direction.time_remaining.num_seconds().abs() % 60)
|
||||||
|
} else {
|
||||||
|
format!("{:0>2}:{:0>2}:{:0>2}", self.direction.time_remaining.num_hours(), self.direction.time_remaining.num_minutes() % 60, self.direction.time_remaining.num_seconds() % 60)
|
||||||
|
};
|
||||||
|
|
||||||
let status_line = Line::from_iter([
|
let status_line = Line::from_iter([
|
||||||
Span::from(format!("Episode {}", self.direction.episode_number)).style(ratatui::style::Color::LightBlue),
|
Span::from(format!("Episode {}", self.direction.episode_number)).style(ratatui::style::Color::LightBlue),
|
||||||
Span::from(" | ").style(ratatui::style::Color::DarkGray),
|
Span::from(" | ").style(ratatui::style::Color::DarkGray),
|
||||||
// FIXME: Looks weird with negative numbers, and it doesn't actually blink in the vscode terminal.
|
Span::from(format!("{} tracks", self.direction.current_playlist.len())).style(ratatui::style::Color::LightBlue),
|
||||||
Span::from(format!("Time Remaining: {:0>2}:{:0>2}:{:0>2}", self.direction.time_remaining.num_hours(), self.direction.time_remaining.num_minutes() % 60, self.direction.time_remaining.num_seconds() % 60)).style(time_style)
|
Span::from(" | ").style(ratatui::style::Color::DarkGray),
|
||||||
|
Span::from(format!("Time Remaining: {}", formatted_time)).style(time_style),
|
||||||
|
Span::from(" | ").style(ratatui::style::Color::DarkGray),
|
||||||
]);
|
]);
|
||||||
frame.render_widget(status_line, area);
|
frame.render_widget(status_line, area);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user