aboutsummaryrefslogtreecommitdiff
path: root/src/widgets.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets.rs')
-rw-r--r--src/widgets.rs121
1 files changed, 79 insertions, 42 deletions
diff --git a/src/widgets.rs b/src/widgets.rs
index 52eff3c..c1e057f 100644
--- a/src/widgets.rs
+++ b/src/widgets.rs
@@ -1,54 +1,77 @@
//! bevy_terminal_display widgets for dialog boxes
+use std::time::{Duration, Instant};
+
+use arbitrary_chunks::ArbitraryChunks as _;
use bevy::prelude::*;
-use bevy_terminal_display::{crossterm, ratatui::{layout::{Alignment, Constraint, Flex, Layout}, style::Style, text::{Line, Span}, widgets::{Block, Borders, Clear, HighlightSpacing, List, ListItem, ListState, Padding, Paragraph, Wrap}}, widgets::TerminalWidget};
-use yarnspinner::runtime::DialogueOption;
+use bevy_terminal_display::{
+ crossterm,
+ ratatui::{
+ layout::{Constraint, Flex, Layout},
+ style::{Style, Stylize},
+ text::{Line, Span},
+ widgets::{
+ Block, Borders, Clear, HighlightSpacing, List, ListItem, ListState, Padding, Paragraph,
+ Wrap,
+ },
+ },
+ widgets::TerminalWidget,
+};
use unicode_segmentation::UnicodeSegmentation as _;
-use arbitrary_chunks::ArbitraryChunks as _;
-
-/// Interaction tooltip widget marker
-// TODO: Move tooltip out of this crate?
-#[derive(Component)]
-pub struct InteractTooltip;
-
-/// Interaction tooltip widget
-pub struct InteractTooltipWidget;
-
-impl TerminalWidget for InteractTooltipWidget {
- fn render(
- &mut self,
- frame: &mut bevy_terminal_display::ratatui::Frame,
- rect: bevy_terminal_display::ratatui::prelude::Rect,
- ) {
- let text = Paragraph::new("E")
- .block(
- Block::new()
- .borders(Borders::ALL)
- .padding(Padding::horizontal(1)),
- )
- .alignment(Alignment::Center);
- let [area] = Layout::horizontal([Constraint::Length(5)])
- .flex(Flex::Center)
- .areas(rect);
- let [area] = Layout::vertical([Constraint::Length(3)])
- .flex(Flex::Center)
- .areas(area);
- frame.render_widget(Clear, area);
- frame.render_widget(text, area);
- }
-}
+use yarnspinner::runtime::DialogueOption;
/// Dialog box widget marker
#[derive(Component)]
pub struct DialogBox;
/// Dialog box widget
-#[derive(Default)]
pub struct DialogBoxWidget {
/// Name of speaking character
- pub character: Option<String>,
+ character: Option<String>,
/// Chunks of text and corresponding styles representing the currently spoken line
- pub text: Vec<(String, Style)>,
+ text: Vec<(String, Style)>,
+ /// Index of last character to draw (for typewriter effect).
+ typewriter_index: usize,
+ /// Time last character was drawn.
+ last_character: Instant,
+ /// Speed of typewriter effect (time between characters).
+ speed: Duration,
+ /// Number of characters in the text
+ character_count: usize,
+}
+
+impl DialogBoxWidget {
+ pub fn new(character: Option<String>, text: Vec<(String, Style)>, speed: Duration) -> Self {
+ Self {
+ character,
+ character_count: text
+ .iter()
+ .fold(0, |count, (string, _)| count + string.graphemes(true).count()),
+ text,
+ speed,
+ typewriter_index: 0,
+ last_character: Instant::now(),
+ }
+ }
+
+ pub fn set_character(&mut self, character: Option<String>) {
+ self.character = character
+ }
+
+ pub fn set_text(&mut self, text: Vec<(String, Style)>) {
+ self.character_count = text.iter().fold(0, |count, (string, _)| count + string.graphemes(true).count());
+ self.text = text;
+ self.typewriter_index = 0;
+ self.last_character = Instant::now();
+ }
+
+ pub fn typewriter_complete(&self) -> bool {
+ self.character_count == 0 || self.typewriter_index >= self.character_count
+ }
+
+ pub fn skip_typewriter(&mut self) {
+ self.typewriter_index = self.character_count;
+ }
}
impl TerminalWidget for DialogBoxWidget {
@@ -57,11 +80,16 @@ impl TerminalWidget for DialogBoxWidget {
frame: &mut bevy_terminal_display::ratatui::Frame,
rect: bevy_terminal_display::ratatui::prelude::Rect,
) {
- let text = Paragraph::new(bevy_terminal_display::ratatui::text::Line::from(
+ let line = Line::from_iter(
self.text
.iter()
- .map(|(text, style)| Span::styled(text, *style))
- .collect::<Vec<_>>(),
+ .map(|(text, style)| Span::styled(text, *style)),
+ );
+ let graphemes = line.styled_graphemes(Style::default());
+ let text = Paragraph::new(Line::from_iter(
+ graphemes
+ .take(self.typewriter_index)
+ .map(|g| Span::styled(g.symbol, g.style)),
))
.wrap(Wrap { trim: true })
.block({
@@ -69,7 +97,7 @@ impl TerminalWidget for DialogBoxWidget {
.borders(Borders::ALL)
.padding(Padding::horizontal(1));
if let Some(character) = &self.character {
- block = block.title(character.clone());
+ block = block.title(Line::from(character.clone()).bold().underlined());
}
block
});
@@ -82,6 +110,15 @@ impl TerminalWidget for DialogBoxWidget {
frame.render_widget(Clear, area);
frame.render_widget(text, area);
}
+
+ fn update(&mut self, _time: &Time, _commands: &mut Commands) {
+ if self.character_count > 0 && self.typewriter_index < self.character_count {
+ if self.last_character.elapsed() >= self.speed {
+ self.typewriter_index += 1;
+ self.last_character = Instant::now();
+ }
+ }
+ }
}
/// Option selection box widget marker