diff --git a/cursive-core/src/backend.rs b/cursive-core/src/backend.rs index 4a03211..508739a 100644 --- a/cursive-core/src/backend.rs +++ b/cursive-core/src/backend.rs @@ -31,6 +31,10 @@ use unicode_width::UnicodeWidthStr; /// [1]: ../struct.Cursive.html#method.new /// [`Event`]: ../event/enum.Event.html pub trait Backend { + /// . + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + panic!() + } /// Polls the backend for any input. /// /// Should return immediately: diff --git a/cursive-core/src/cursive_run.rs b/cursive-core/src/cursive_run.rs index b22b6dc..9117dd3 100644 --- a/cursive-core/src/cursive_run.rs +++ b/cursive-core/src/cursive_run.rs @@ -13,7 +13,8 @@ const INPUT_POLL_DELAY_MS: u64 = 30; /// The `C` type is usually either `Cursive` or `&mut Cursive`. pub struct CursiveRunner { siv: C, - backend: Box, + /// . + pub backend: Box, boring_frame_count: u32, // Last layer sizes of the stack view. // If it changed, clear the screen. diff --git a/cursive-core/src/lib.rs b/cursive-core/src/lib.rs index 234f6ce..faa35d6 100644 --- a/cursive-core/src/lib.rs +++ b/cursive-core/src/lib.rs @@ -68,6 +68,7 @@ pub use self::cursive_run::CursiveRunner; pub use self::dump::Dump; pub use self::printer::Printer; pub use self::rect::Rect; +pub use self::utils::lines::spans::WrapMethod; pub use self::vec::Vec2; pub use self::view::View; pub use self::with::With; diff --git a/cursive-core/src/utils/lines/spans/chunk_iterator.rs b/cursive-core/src/utils/lines/spans/chunk_iterator.rs index 13fc350..cd7623c 100644 --- a/cursive-core/src/utils/lines/spans/chunk_iterator.rs +++ b/cursive-core/src/utils/lines/spans/chunk_iterator.rs @@ -1,3 +1,4 @@ +use super::WrapMethod; use super::chunk::Chunk; use super::segment::Segment; use crate::utils::span::SpannedText; @@ -18,15 +19,18 @@ pub struct ChunkIterator { /// How much of the current span has been processed already. offset: usize, + + wrap_method: WrapMethod, } impl ChunkIterator { /// Creates a new ChunkIterator on the given styled string. - pub fn new(source: Rc) -> Self { + pub fn new(source: Rc, wrap_method: WrapMethod) -> Self { ChunkIterator { source, current_span: 0, offset: 0, + wrap_method, } } } @@ -63,7 +67,9 @@ where let mut segments: Vec = Vec::new(); // We'll use an iterator from xi-unicode to detect possible breaks. - let mut iter = LineBreakLeafIter::new(span_text, self.offset); + let mut iter = if self.wrap_method == WrapMethod::XiUnicode { + Some(LineBreakLeafIter::new(span_text, self.offset)) + } else { None }; // When we reach the end of a span, xi-unicode returns a break, but it // actually depends on the next span. Such breaks are "fake" breaks. @@ -77,7 +83,14 @@ where // Look at next possible break // `hard_stop = true` means that the break is non-optional, // like after a `\n`. - let (pos, hard_stop) = iter.next(span_text); + let (pos, mut hard_stop) = if let Some(iter) = iter.as_mut() { + iter.next(span_text) + } else { + span_text[self.offset..].find('\n').map(|x| (x + self.offset + 1, true)).unwrap_or((span_text.len(), false)) + }; + if pos == span_text.len() && iter.is_none() { + hard_stop = false; + } // When xi-unicode reaches the end of a span, it returns a "fake" // break. To know if it's actually a true break, we need to give diff --git a/cursive-core/src/utils/lines/spans/lines_iterator.rs b/cursive-core/src/utils/lines/spans/lines_iterator.rs index c2b5833..70bc699 100644 --- a/cursive-core/src/utils/lines/spans/lines_iterator.rs +++ b/cursive-core/src/utils/lines/spans/lines_iterator.rs @@ -10,6 +10,19 @@ use std::rc::Rc; use unicode_segmentation::UnicodeSegmentation; use unicode_width::UnicodeWidthStr; +// TODO docs + +#[derive(Clone, Copy, PartialEq, Eq)] +/// Wrapping method +pub enum WrapMethod { + /// Default wrapping method based on xi-unicode. + XiUnicode, + /// Wrap when row width is exceeded + Newlines, + /// Do not wrap + Dont +} + /// Generates rows of text in constrainted width. /// /// Works on spans of text. @@ -41,7 +54,7 @@ where let source = Rc::new(source); let chunk_source = source.clone(); LinesIterator { - iter: ChunkIterator::new(chunk_source).peekable(), + iter: ChunkIterator::new(chunk_source, WrapMethod::XiUnicode).peekable(), source, width, chunk_offset: ChunkPart::default(), @@ -49,6 +62,12 @@ where } } + /// Set the wrapping method. Resets the iterator position. + pub fn with_method(mut self, wrap_method: WrapMethod) -> Self { + self.iter = ChunkIterator::new(self.source.clone(), wrap_method).peekable(); + self + } + /// Leave a blank cell at the end of lines. /// /// Unless a word had to be truncated, in which case diff --git a/cursive-core/src/utils/lines/spans/mod.rs b/cursive-core/src/utils/lines/spans/mod.rs index ab4c275..aeb61b8 100644 --- a/cursive-core/src/utils/lines/spans/mod.rs +++ b/cursive-core/src/utils/lines/spans/mod.rs @@ -15,6 +15,6 @@ mod segment_merge_iterator; #[cfg(test)] mod tests; -pub use self::lines_iterator::LinesIterator; +pub use self::lines_iterator::{LinesIterator, WrapMethod}; pub use self::row::Row; pub use self::segment::Segment; diff --git a/cursive-core/src/view/any.rs b/cursive-core/src/view/any.rs index 705197d..93ee09d 100644 --- a/cursive-core/src/view/any.rs +++ b/cursive-core/src/view/any.rs @@ -1,4 +1,4 @@ -use crate::view::View; +use crate::{cursive, view::View}; use std::any::Any; /// A view that can be downcasted to its concrete type. diff --git a/cursive-core/src/view/scroll/core.rs b/cursive-core/src/view/scroll/core.rs index 0a29281..ea0a421 100644 --- a/cursive-core/src/view/scroll/core.rs +++ b/cursive-core/src/view/scroll/core.rs @@ -227,7 +227,7 @@ impl Core { self.last_available = last_size.saturating_sub( scrolling .swap() - .select_or(self.scrollbar_padding + (1, 1), Vec2::zero()), + .select_or(if self.show_scrollbars { self.scrollbar_padding + (1, 1) } else { Vec2::zero() }, Vec2::zero()), ); } @@ -523,7 +523,7 @@ impl Core { } /// Clears the cache. - fn invalidate_cache(&mut self) { + pub(crate) fn invalidate_cache(&mut self) { self.size_cache = None; } @@ -544,6 +544,9 @@ impl Core { /// /// The scrollbar_size().x will be the horizontal space taken by the vertical scrollbar. pub fn scrollbar_size(&self) -> Vec2 { + if !self.show_scrollbars { + return Vec2::zero(); + } self.is_scrolling() .swap() .select_or(self.scrollbar_padding + (1, 1), Vec2::zero()) diff --git a/cursive-core/src/views/scroll_view.rs b/cursive-core/src/views/scroll_view.rs index 5a56a8f..4ff0cec 100644 --- a/cursive-core/src/views/scroll_view.rs +++ b/cursive-core/src/views/scroll_view.rs @@ -99,6 +99,14 @@ impl ScrollView { /// /// Defaults to `true`. pub fn set_show_scrollbars(&mut self, show_scrollbars: bool) { + /* + let mut xy = self.core.inner_size(); + if show_scrollbars { + xy.x -= 2; + } else { + xy.x += 2; + } + self.core.set_inner_size(xy);*/ self.core.set_show_scrollbars(show_scrollbars); } diff --git a/cursive-core/src/views/text_view.rs b/cursive-core/src/views/text_view.rs index 0e72b53..be179c6 100644 --- a/cursive-core/src/views/text_view.rs +++ b/cursive-core/src/views/text_view.rs @@ -7,7 +7,7 @@ use unicode_width::UnicodeWidthStr; use crate::align::*; use crate::theme::{Effect, Style}; -use crate::utils::lines::spans::{LinesIterator, Row}; +use crate::utils::lines::spans::{LinesIterator, Row, WrapMethod}; use crate::utils::markup::StyledString; use crate::view::{SizeCache, View}; use crate::{Printer, Vec2, With, XY}; @@ -197,8 +197,7 @@ pub struct TextView { style: Style, - // True if we can wrap long lines. - wrap: bool, + wrap: WrapMethod, // ScrollBase make many scrolling-related things easier last_size: Vec2, @@ -235,7 +234,7 @@ impl TextView { content, style: Style::default(), rows: Vec::new(), - wrap: true, + wrap: WrapMethod::XiUnicode, align: Align::top_left(), last_size: Vec2::zero(), width: None, @@ -284,7 +283,16 @@ impl TextView { /// /// If `true` (the default), text will wrap long lines when needed. pub fn set_content_wrap(&mut self, wrap: bool) { - self.wrap = wrap; + if wrap { + self.wrap = WrapMethod::XiUnicode; + } else { + self.wrap = WrapMethod::Dont; + } + } + + /// TODO docs + pub fn set_wrap_method(&mut self, method: WrapMethod) { + self.wrap = method; } /// Sets the horizontal alignment for this view. @@ -357,7 +365,8 @@ impl TextView { // This must be non-destructive, as it may be called // multiple times during layout. fn compute_rows(&mut self, size: Vec2) { - let size = if self.wrap { size } else { Vec2::max_value() }; + eprintln!("computing rows with {:?}!", size); + let size = if self.wrap != WrapMethod::Dont { size } else { Vec2::max_value() }; let mut content = self.content.content.lock().unwrap(); if content.is_cache_valid(size) { @@ -375,7 +384,7 @@ impl TextView { } self.rows = - LinesIterator::new(content.get_cache().as_ref(), size.x).collect(); + LinesIterator::new(content.get_cache().as_ref(), size.x).with_method(self.wrap).collect(); // Desired width self.width = if self.rows.iter().any(|row| row.is_wrapped) { @@ -422,12 +431,14 @@ impl View for TextView { } fn required_size(&mut self, size: Vec2) -> Vec2 { + eprintln!("required_size {:?}", size); self.compute_rows(size); Vec2::new(self.width.unwrap_or(0), self.rows.len()) } fn layout(&mut self, size: Vec2) { + eprintln!("layout {:?}", size); // Compute the text rows. self.last_size = size; self.compute_rows(size); diff --git a/cursive/src/backends/termion.rs b/cursive/src/backends/termion.rs index b86220f..e1f338f 100644 --- a/cursive/src/backends/termion.rs +++ b/cursive/src/backends/termion.rs @@ -29,8 +29,8 @@ use std::thread; /// Backend using termion pub struct Backend { - // Do we want to make this generic on the writer? - terminal: + /// Do we want to make this generic on the writer? + pub terminal: RefCell>>>>, current_style: Cell, @@ -39,6 +39,8 @@ pub struct Backend { input_receiver: Receiver, resize_receiver: Receiver<()>, + /// . + pub locked: RefCell, } impl Backend { @@ -112,11 +114,24 @@ impl Backend { last_button: None, input_receiver, resize_receiver, + locked: false.into() }; Ok(Box::new(c)) } + fn lock(&self) { + use std::time::*; + let mut locked = self.locked.borrow_mut(); + if !*locked { + eprintln!("{} lock!", SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap().as_millis()); + self.terminal.borrow_mut().write_all(&[b'\x1b', b'\x50', b't', b'm', b'u', b'x', b';', b'\x1b', b'\x1b', b'\x50', b'=', b'1', b's', b'\x1b', b'\x1b', b'\x5c', b'\x1b', b'\\']).unwrap(); + *locked = true; + } + } + fn apply_colors(&self, colors: theme::ColorPair) { with_color(colors.front, |c| self.write(tcolor::Fg(c))); with_color(colors.back, |c| self.write(tcolor::Bg(c))); @@ -225,6 +240,9 @@ impl Drop for Backend { } impl backend::Backend for Backend { + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } fn name(&self) -> &str { "termion" } @@ -241,6 +259,7 @@ impl backend::Backend for Backend { } fn set_effect(&self, effect: theme::Effect) { + self.lock(); match effect { theme::Effect::Simple => (), theme::Effect::Reverse => self.write(tstyle::Invert), @@ -253,6 +272,7 @@ impl backend::Backend for Backend { } fn unset_effect(&self, effect: theme::Effect) { + self.lock(); match effect { theme::Effect::Simple => (), theme::Effect::Reverse => self.write(tstyle::NoInvert), @@ -290,6 +310,7 @@ impl backend::Backend for Backend { } fn print_at(&self, pos: Vec2, text: &str) { + self.lock(); write!( self.terminal.borrow_mut(), "{}{}", @@ -300,6 +321,7 @@ impl backend::Backend for Backend { } fn print_at_rep(&self, pos: Vec2, repetitions: usize, text: &str) { + self.lock(); if repetitions > 0 { let mut out = self.terminal.borrow_mut(); write!(