summaryrefslogtreecommitdiffstats
path: root/src/ui/stateful_paragraph.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/stateful_paragraph.rs')
-rw-r--r--src/ui/stateful_paragraph.rs346
1 files changed, 173 insertions, 173 deletions
diff --git a/src/ui/stateful_paragraph.rs b/src/ui/stateful_paragraph.rs
index f785c60b..49b7a22f 100644
--- a/src/ui/stateful_paragraph.rs
+++ b/src/ui/stateful_paragraph.rs
@@ -3,211 +3,211 @@
use easy_cast::Cast;
use std::iter;
use tui::{
- buffer::Buffer,
- layout::{Alignment, Rect},
- style::Style,
- text::{StyledGrapheme, Text},
- widgets::{Block, StatefulWidget, Widget, Wrap},
+ buffer::Buffer,
+ layout::{Alignment, Rect},
+ style::Style,
+ text::{StyledGrapheme, Text},
+ widgets::{Block, StatefulWidget, Widget, Wrap},
};
use unicode_width::UnicodeWidthStr;
use super::reflow::{LineComposer, LineTruncator, WordWrapper};
const fn get_line_offset(
- line_width: u16,
- text_area_width: u16,
- alignment: Alignment,
+ line_width: u16,
+ text_area_width: u16,
+ alignment: Alignment,
) -> u16 {
- match alignment {
- Alignment::Center => {
- (text_area_width / 2).saturating_sub(line_width / 2)
- }
- Alignment::Right => {
- text_area_width.saturating_sub(line_width)
- }
- Alignment::Left => 0,
- }
+ match alignment {
+ Alignment::Center => {
+ (text_area_width / 2).saturating_sub(line_width / 2)
+ }
+ Alignment::Right => {
+ text_area_width.saturating_sub(line_width)
+ }
+ Alignment::Left => 0,
+ }
}
#[derive(Debug, Clone)]
pub struct StatefulParagraph<'a> {
- /// A block to wrap the widget in
- block: Option<Block<'a>>,
- /// Widget style
- style: Style,
- /// How to wrap the text
- wrap: Option<Wrap>,
- /// The text to display
- text: Text<'a>,
- /// Alignment of the text
- alignment: Alignment,
+ /// A block to wrap the widget in
+ block: Option<Block<'a>>,
+ /// Widget style
+ style: Style,
+ /// How to wrap the text
+ wrap: Option<Wrap>,
+ /// The text to display
+ text: Text<'a>,
+ /// Alignment of the text
+ alignment: Alignment,
}
#[derive(Debug, Default, Clone, Copy)]
pub struct ScrollPos {
- pub x: u16,
- pub y: u16,
+ pub x: u16,
+ pub y: u16,
}
impl ScrollPos {
- pub const fn new(x: u16, y: u16) -> Self {
- Self { x, y }
- }
+ pub const fn new(x: u16, y: u16) -> Self {
+ Self { x, y }
+ }
}
#[derive(Debug, Copy, Clone, Default)]
pub struct ParagraphState {
- /// Scroll
- scroll: ScrollPos,
- /// after all wrapping this is the amount of lines
- lines: u16,
- /// last visible height
- height: u16,
+ /// Scroll
+ scroll: ScrollPos,
+ /// after all wrapping this is the amount of lines
+ lines: u16,
+ /// last visible height
+ height: u16,
}
impl ParagraphState {
- pub const fn lines(self) -> u16 {
- self.lines
- }
+ pub const fn lines(self) -> u16 {
+ self.lines
+ }
- pub const fn height(self) -> u16 {
- self.height
- }
+ pub const fn height(self) -> u16 {
+ self.height
+ }
- pub const fn scroll(self) -> ScrollPos {
- self.scroll
- }
+ pub const fn scroll(self) -> ScrollPos {
+ self.scroll
+ }
- pub fn set_scroll(&mut self, scroll: ScrollPos) {
- self.scroll = scroll;
- }
+ pub fn set_scroll(&mut self, scroll: ScrollPos) {
+ self.scroll = scroll;
+ }
}
impl<'a> StatefulParagraph<'a> {
- pub fn new<T>(text: T) -> Self
- where
- T: Into<Text<'a>>,
- {
- Self {
- block: None,
- style: Style::default(),
- wrap: None,
- text: text.into(),
- alignment: Alignment::Left,
- }
- }
-
- #[allow(clippy::missing_const_for_fn)]
- pub fn block(mut self, block: Block<'a>) -> Self {
- self.block = Some(block);
- self
- }
-
- pub const fn style(mut self, style: Style) -> Self {
- self.style = style;
- self
- }
-
- pub const fn wrap(mut self, wrap: Wrap) -> Self {
- self.wrap = Some(wrap);
- self
- }
-
- pub const fn alignment(mut self, alignment: Alignment) -> Self {
- self.alignment = alignment;
- self
- }
+ pub fn new<T>(text: T) -> Self
+ where
+ T: Into<Text<'a>>,
+ {
+ Self {
+ block: None,
+ style: Style::default(),
+ wrap: None,
+ text: text.into(),
+ alignment: Alignment::Left,
+ }
+ }
+
+ #[allow(clippy::missing_const_for_fn)]
+ pub fn block(mut self, block: Block<'a>) -> Self {
+ self.block = Some(block);
+ self
+ }
+
+ pub const fn style(mut self, style: Style) -> Self {
+ self.style = style;
+ self
+ }
+
+ pub const fn wrap(mut self, wrap: Wrap) -> Self {
+ self.wrap = Some(wrap);
+ self
+ }
+
+ pub const fn alignment(mut self, alignment: Alignment) -> Self {
+ self.alignment = alignment;
+ self
+ }
}
impl<'a> StatefulWidget for StatefulParagraph<'a> {
- type State = ParagraphState;
-
- fn render(
- mut self,
- area: Rect,
- buf: &mut Buffer,
- state: &mut Self::State,
- ) {
- buf.set_style(area, self.style);
- let text_area = match self.block.take() {
- Some(b) => {
- let inner_area = b.inner(area);
- b.render(area, buf);
- inner_area
- }
- None => area,
- };
-
- if text_area.height < 1 {
- return;
- }
-
- let style = self.style;
- let mut styled = self.text.lines.iter().flat_map(|spans| {
- spans
- .0
- .iter()
- .flat_map(|span| span.styled_graphemes(style))
- // Required given the way composers work but might be refactored out if we change
- // composers to operate on lines instead of a stream of graphemes.
- .chain(iter::once(StyledGrapheme {
- symbol: "\n",
- style: self.style,
- }))
- });
-
- let mut line_composer: Box<dyn LineComposer> =
- if let Some(Wrap { trim }) = self.wrap {
- Box::new(WordWrapper::new(
- &mut styled,
- text_area.width,
- trim,
- ))
- } else {
- let mut line_composer = Box::new(LineTruncator::new(
- &mut styled,
- text_area.width,
- ));
- if let Alignment::Left = self.alignment {
- line_composer
- .set_horizontal_offset(state.scroll.x);
- }
- line_composer
- };
- let mut y = 0;
- let mut end_reached = false;
- while let Some((current_line, current_line_width)) =
- line_composer.next_line()
- {
- if !end_reached && y >= state.scroll.y {
- let mut x = get_line_offset(
- current_line_width,
- text_area.width,
- self.alignment,
- );
- for StyledGrapheme { symbol, style } in current_line {
- buf.get_mut(
- text_area.left() + x,
- text_area.top() + y - state.scroll.y,
- )
- .set_symbol(if symbol.is_empty() {
- // If the symbol is empty, the last char which rendered last time will
- // leave on the line. It's a quick fix.
- " "
- } else {
- symbol
- })
- .set_style(*style);
- x += Cast::<u16>::cast(symbol.width());
- }
- }
- y += 1;
- if y >= text_area.height + state.scroll.y {
- end_reached = true;
- }
- }
-
- state.lines = y;
- state.height = area.height;
- }
+ type State = ParagraphState;
+
+ fn render(
+ mut self,
+ area: Rect,
+ buf: &mut Buffer,
+ state: &mut Self::State,
+ ) {
+ buf.set_style(area, self.style);
+ let text_area = match self.block.take() {
+ Some(b) => {
+ let inner_area = b.inner(area);
+ b.render(area, buf);
+ inner_area
+ }
+ None => area,
+ };
+
+ if text_area.height < 1 {
+ return;
+ }
+
+ let style = self.style;
+ let mut styled = self.text.lines.iter().flat_map(|spans| {
+ spans
+ .0
+ .iter()
+ .flat_map(|span| span.styled_graphemes(style))
+ // Required given the way composers work but might be refactored out if we change
+ // composers to operate on lines instead of a stream of graphemes.
+ .chain(iter::once(StyledGrapheme {
+ symbol: "\n",
+ style: self.style,
+ }))
+ });
+
+ let mut line_composer: Box<dyn LineComposer> =
+ if let Some(Wrap { trim }) = self.wrap {
+ Box::new(WordWrapper::new(
+ &mut styled,
+ text_area.width,
+ trim,
+ ))
+ } else {
+ let mut line_composer = Box::new(LineTruncator::new(
+ &mut styled,
+ text_area.width,
+ ));
+ if let Alignment::Left = self.alignment {
+ line_composer
+ .set_horizontal_offset(state.scroll.x);
+ }
+ line_composer
+ };
+ let mut y = 0;
+ let mut end_reached = false;
+ while let Some((current_line, current_line_width)) =
+ line_composer.next_line()
+ {
+ if !end_reached && y >= state.scroll.y {
+ let mut x = get_line_offset(
+ current_line_width,
+ text_area.width,
+ self.alignment,
+ );
+ for StyledGrapheme { symbol, style } in current_line {
+ buf.get_mut(
+ text_area.left() + x,
+ text_area.top() + y - state.scroll.y,
+ )
+ .set_symbol(if symbol.is_empty() {
+ // If the symbol is empty, the last char which rendered last time will
+ // leave on the line. It's a quick fix.
+ " "
+ } else {
+ symbol
+ })
+ .set_style(*style);
+ x += Cast::<u16>::cast(symbol.width());
+ }
+ }
+ y += 1;
+ if y >= text_area.height + state.scroll.y {
+ end_reached = true;
+ }
+ }
+
+ state.lines = y;
+ state.height = area.height;
+ }
}