mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-08 18:30:40 +00:00
Misc. hacks
This commit is contained in:
parent
8ec46107e1
commit
924684215a
@ -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:
|
||||
|
@ -13,7 +13,8 @@ const INPUT_POLL_DELAY_MS: u64 = 30;
|
||||
/// The `C` type is usually either `Cursive` or `&mut Cursive`.
|
||||
pub struct CursiveRunner<C> {
|
||||
siv: C,
|
||||
backend: Box<dyn backend::Backend>,
|
||||
/// .
|
||||
pub backend: Box<dyn backend::Backend>,
|
||||
boring_frame_count: u32,
|
||||
// Last layer sizes of the stack view.
|
||||
// If it changed, clear the screen.
|
||||
|
@ -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;
|
||||
|
@ -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<S> {
|
||||
|
||||
/// How much of the current span has been processed already.
|
||||
offset: usize,
|
||||
|
||||
wrap_method: WrapMethod,
|
||||
}
|
||||
|
||||
impl<S> ChunkIterator<S> {
|
||||
/// Creates a new ChunkIterator on the given styled string.
|
||||
pub fn new(source: Rc<S>) -> Self {
|
||||
pub fn new(source: Rc<S>, wrap_method: WrapMethod) -> Self {
|
||||
ChunkIterator {
|
||||
source,
|
||||
current_span: 0,
|
||||
offset: 0,
|
||||
wrap_method,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -63,7 +67,9 @@ where
|
||||
let mut segments: Vec<Segment> = 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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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())
|
||||
|
@ -99,6 +99,14 @@ impl<V> ScrollView<V> {
|
||||
///
|
||||
/// 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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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<AlternateScreen<MouseTerminal<RawTerminal<BufWriter<File>>>>>,
|
||||
current_style: Cell<theme::ColorPair>,
|
||||
|
||||
@ -39,6 +39,8 @@ pub struct Backend {
|
||||
|
||||
input_receiver: Receiver<TEvent>,
|
||||
resize_receiver: Receiver<()>,
|
||||
/// .
|
||||
pub locked: RefCell<bool>,
|
||||
}
|
||||
|
||||
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!(
|
||||
|
Loading…
Reference in New Issue
Block a user