mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 09:25:01 +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
|
/// [1]: ../struct.Cursive.html#method.new
|
||||||
/// [`Event`]: ../event/enum.Event.html
|
/// [`Event`]: ../event/enum.Event.html
|
||||||
pub trait Backend {
|
pub trait Backend {
|
||||||
|
/// .
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
/// Polls the backend for any input.
|
/// Polls the backend for any input.
|
||||||
///
|
///
|
||||||
/// Should return immediately:
|
/// Should return immediately:
|
||||||
|
@ -13,7 +13,8 @@ const INPUT_POLL_DELAY_MS: u64 = 30;
|
|||||||
/// The `C` type is usually either `Cursive` or `&mut Cursive`.
|
/// The `C` type is usually either `Cursive` or `&mut Cursive`.
|
||||||
pub struct CursiveRunner<C> {
|
pub struct CursiveRunner<C> {
|
||||||
siv: C,
|
siv: C,
|
||||||
backend: Box<dyn backend::Backend>,
|
/// .
|
||||||
|
pub backend: Box<dyn backend::Backend>,
|
||||||
boring_frame_count: u32,
|
boring_frame_count: u32,
|
||||||
// Last layer sizes of the stack view.
|
// Last layer sizes of the stack view.
|
||||||
// If it changed, clear the screen.
|
// If it changed, clear the screen.
|
||||||
|
@ -68,6 +68,7 @@ pub use self::cursive_run::CursiveRunner;
|
|||||||
pub use self::dump::Dump;
|
pub use self::dump::Dump;
|
||||||
pub use self::printer::Printer;
|
pub use self::printer::Printer;
|
||||||
pub use self::rect::Rect;
|
pub use self::rect::Rect;
|
||||||
|
pub use self::utils::lines::spans::WrapMethod;
|
||||||
pub use self::vec::Vec2;
|
pub use self::vec::Vec2;
|
||||||
pub use self::view::View;
|
pub use self::view::View;
|
||||||
pub use self::with::With;
|
pub use self::with::With;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use super::WrapMethod;
|
||||||
use super::chunk::Chunk;
|
use super::chunk::Chunk;
|
||||||
use super::segment::Segment;
|
use super::segment::Segment;
|
||||||
use crate::utils::span::SpannedText;
|
use crate::utils::span::SpannedText;
|
||||||
@ -18,15 +19,18 @@ pub struct ChunkIterator<S> {
|
|||||||
|
|
||||||
/// How much of the current span has been processed already.
|
/// How much of the current span has been processed already.
|
||||||
offset: usize,
|
offset: usize,
|
||||||
|
|
||||||
|
wrap_method: WrapMethod,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> ChunkIterator<S> {
|
impl<S> ChunkIterator<S> {
|
||||||
/// Creates a new ChunkIterator on the given styled string.
|
/// 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 {
|
ChunkIterator {
|
||||||
source,
|
source,
|
||||||
current_span: 0,
|
current_span: 0,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
|
wrap_method,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,7 +67,9 @@ where
|
|||||||
let mut segments: Vec<Segment> = Vec::new();
|
let mut segments: Vec<Segment> = Vec::new();
|
||||||
|
|
||||||
// We'll use an iterator from xi-unicode to detect possible breaks.
|
// 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
|
// 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.
|
// actually depends on the next span. Such breaks are "fake" breaks.
|
||||||
@ -77,7 +83,14 @@ where
|
|||||||
// Look at next possible break
|
// Look at next possible break
|
||||||
// `hard_stop = true` means that the break is non-optional,
|
// `hard_stop = true` means that the break is non-optional,
|
||||||
// like after a `\n`.
|
// 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"
|
// 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
|
// 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_segmentation::UnicodeSegmentation;
|
||||||
use unicode_width::UnicodeWidthStr;
|
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.
|
/// Generates rows of text in constrainted width.
|
||||||
///
|
///
|
||||||
/// Works on spans of text.
|
/// Works on spans of text.
|
||||||
@ -41,7 +54,7 @@ where
|
|||||||
let source = Rc::new(source);
|
let source = Rc::new(source);
|
||||||
let chunk_source = source.clone();
|
let chunk_source = source.clone();
|
||||||
LinesIterator {
|
LinesIterator {
|
||||||
iter: ChunkIterator::new(chunk_source).peekable(),
|
iter: ChunkIterator::new(chunk_source, WrapMethod::XiUnicode).peekable(),
|
||||||
source,
|
source,
|
||||||
width,
|
width,
|
||||||
chunk_offset: ChunkPart::default(),
|
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.
|
/// Leave a blank cell at the end of lines.
|
||||||
///
|
///
|
||||||
/// Unless a word had to be truncated, in which case
|
/// Unless a word had to be truncated, in which case
|
||||||
|
@ -15,6 +15,6 @@ mod segment_merge_iterator;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
pub use self::lines_iterator::LinesIterator;
|
pub use self::lines_iterator::{LinesIterator, WrapMethod};
|
||||||
pub use self::row::Row;
|
pub use self::row::Row;
|
||||||
pub use self::segment::Segment;
|
pub use self::segment::Segment;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::view::View;
|
use crate::{cursive, view::View};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
/// A view that can be downcasted to its concrete type.
|
/// A view that can be downcasted to its concrete type.
|
||||||
|
@ -227,7 +227,7 @@ impl Core {
|
|||||||
self.last_available = last_size.saturating_sub(
|
self.last_available = last_size.saturating_sub(
|
||||||
scrolling
|
scrolling
|
||||||
.swap()
|
.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.
|
/// Clears the cache.
|
||||||
fn invalidate_cache(&mut self) {
|
pub(crate) fn invalidate_cache(&mut self) {
|
||||||
self.size_cache = None;
|
self.size_cache = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,6 +544,9 @@ impl Core {
|
|||||||
///
|
///
|
||||||
/// The scrollbar_size().x will be the horizontal space taken by the vertical scrollbar.
|
/// The scrollbar_size().x will be the horizontal space taken by the vertical scrollbar.
|
||||||
pub fn scrollbar_size(&self) -> Vec2 {
|
pub fn scrollbar_size(&self) -> Vec2 {
|
||||||
|
if !self.show_scrollbars {
|
||||||
|
return Vec2::zero();
|
||||||
|
}
|
||||||
self.is_scrolling()
|
self.is_scrolling()
|
||||||
.swap()
|
.swap()
|
||||||
.select_or(self.scrollbar_padding + (1, 1), Vec2::zero())
|
.select_or(self.scrollbar_padding + (1, 1), Vec2::zero())
|
||||||
|
@ -99,6 +99,14 @@ impl<V> ScrollView<V> {
|
|||||||
///
|
///
|
||||||
/// Defaults to `true`.
|
/// Defaults to `true`.
|
||||||
pub fn set_show_scrollbars(&mut self, show_scrollbars: bool) {
|
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);
|
self.core.set_show_scrollbars(show_scrollbars);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ use unicode_width::UnicodeWidthStr;
|
|||||||
|
|
||||||
use crate::align::*;
|
use crate::align::*;
|
||||||
use crate::theme::{Effect, Style};
|
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::utils::markup::StyledString;
|
||||||
use crate::view::{SizeCache, View};
|
use crate::view::{SizeCache, View};
|
||||||
use crate::{Printer, Vec2, With, XY};
|
use crate::{Printer, Vec2, With, XY};
|
||||||
@ -197,8 +197,7 @@ pub struct TextView {
|
|||||||
|
|
||||||
style: Style,
|
style: Style,
|
||||||
|
|
||||||
// True if we can wrap long lines.
|
wrap: WrapMethod,
|
||||||
wrap: bool,
|
|
||||||
|
|
||||||
// ScrollBase make many scrolling-related things easier
|
// ScrollBase make many scrolling-related things easier
|
||||||
last_size: Vec2,
|
last_size: Vec2,
|
||||||
@ -235,7 +234,7 @@ impl TextView {
|
|||||||
content,
|
content,
|
||||||
style: Style::default(),
|
style: Style::default(),
|
||||||
rows: Vec::new(),
|
rows: Vec::new(),
|
||||||
wrap: true,
|
wrap: WrapMethod::XiUnicode,
|
||||||
align: Align::top_left(),
|
align: Align::top_left(),
|
||||||
last_size: Vec2::zero(),
|
last_size: Vec2::zero(),
|
||||||
width: None,
|
width: None,
|
||||||
@ -284,7 +283,16 @@ impl TextView {
|
|||||||
///
|
///
|
||||||
/// If `true` (the default), text will wrap long lines when needed.
|
/// If `true` (the default), text will wrap long lines when needed.
|
||||||
pub fn set_content_wrap(&mut self, wrap: bool) {
|
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.
|
/// Sets the horizontal alignment for this view.
|
||||||
@ -357,7 +365,8 @@ impl TextView {
|
|||||||
// This must be non-destructive, as it may be called
|
// This must be non-destructive, as it may be called
|
||||||
// multiple times during layout.
|
// multiple times during layout.
|
||||||
fn compute_rows(&mut self, size: Vec2) {
|
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();
|
let mut content = self.content.content.lock().unwrap();
|
||||||
if content.is_cache_valid(size) {
|
if content.is_cache_valid(size) {
|
||||||
@ -375,7 +384,7 @@ impl TextView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.rows =
|
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
|
// Desired width
|
||||||
self.width = if self.rows.iter().any(|row| row.is_wrapped) {
|
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 {
|
fn required_size(&mut self, size: Vec2) -> Vec2 {
|
||||||
|
eprintln!("required_size {:?}", size);
|
||||||
self.compute_rows(size);
|
self.compute_rows(size);
|
||||||
|
|
||||||
Vec2::new(self.width.unwrap_or(0), self.rows.len())
|
Vec2::new(self.width.unwrap_or(0), self.rows.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&mut self, size: Vec2) {
|
fn layout(&mut self, size: Vec2) {
|
||||||
|
eprintln!("layout {:?}", size);
|
||||||
// Compute the text rows.
|
// Compute the text rows.
|
||||||
self.last_size = size;
|
self.last_size = size;
|
||||||
self.compute_rows(size);
|
self.compute_rows(size);
|
||||||
|
@ -29,8 +29,8 @@ use std::thread;
|
|||||||
|
|
||||||
/// Backend using termion
|
/// Backend using termion
|
||||||
pub struct Backend {
|
pub struct Backend {
|
||||||
// Do we want to make this generic on the writer?
|
/// Do we want to make this generic on the writer?
|
||||||
terminal:
|
pub terminal:
|
||||||
RefCell<AlternateScreen<MouseTerminal<RawTerminal<BufWriter<File>>>>>,
|
RefCell<AlternateScreen<MouseTerminal<RawTerminal<BufWriter<File>>>>>,
|
||||||
current_style: Cell<theme::ColorPair>,
|
current_style: Cell<theme::ColorPair>,
|
||||||
|
|
||||||
@ -39,6 +39,8 @@ pub struct Backend {
|
|||||||
|
|
||||||
input_receiver: Receiver<TEvent>,
|
input_receiver: Receiver<TEvent>,
|
||||||
resize_receiver: Receiver<()>,
|
resize_receiver: Receiver<()>,
|
||||||
|
/// .
|
||||||
|
pub locked: RefCell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Backend {
|
impl Backend {
|
||||||
@ -112,11 +114,24 @@ impl Backend {
|
|||||||
last_button: None,
|
last_button: None,
|
||||||
input_receiver,
|
input_receiver,
|
||||||
resize_receiver,
|
resize_receiver,
|
||||||
|
locked: false.into()
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Box::new(c))
|
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) {
|
fn apply_colors(&self, colors: theme::ColorPair) {
|
||||||
with_color(colors.front, |c| self.write(tcolor::Fg(c)));
|
with_color(colors.front, |c| self.write(tcolor::Fg(c)));
|
||||||
with_color(colors.back, |c| self.write(tcolor::Bg(c)));
|
with_color(colors.back, |c| self.write(tcolor::Bg(c)));
|
||||||
@ -225,6 +240,9 @@ impl Drop for Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl backend::Backend for Backend {
|
impl backend::Backend for Backend {
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"termion"
|
"termion"
|
||||||
}
|
}
|
||||||
@ -241,6 +259,7 @@ impl backend::Backend for Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_effect(&self, effect: theme::Effect) {
|
fn set_effect(&self, effect: theme::Effect) {
|
||||||
|
self.lock();
|
||||||
match effect {
|
match effect {
|
||||||
theme::Effect::Simple => (),
|
theme::Effect::Simple => (),
|
||||||
theme::Effect::Reverse => self.write(tstyle::Invert),
|
theme::Effect::Reverse => self.write(tstyle::Invert),
|
||||||
@ -253,6 +272,7 @@ impl backend::Backend for Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn unset_effect(&self, effect: theme::Effect) {
|
fn unset_effect(&self, effect: theme::Effect) {
|
||||||
|
self.lock();
|
||||||
match effect {
|
match effect {
|
||||||
theme::Effect::Simple => (),
|
theme::Effect::Simple => (),
|
||||||
theme::Effect::Reverse => self.write(tstyle::NoInvert),
|
theme::Effect::Reverse => self.write(tstyle::NoInvert),
|
||||||
@ -290,6 +310,7 @@ impl backend::Backend for Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn print_at(&self, pos: Vec2, text: &str) {
|
fn print_at(&self, pos: Vec2, text: &str) {
|
||||||
|
self.lock();
|
||||||
write!(
|
write!(
|
||||||
self.terminal.borrow_mut(),
|
self.terminal.borrow_mut(),
|
||||||
"{}{}",
|
"{}{}",
|
||||||
@ -300,6 +321,7 @@ impl backend::Backend for Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn print_at_rep(&self, pos: Vec2, repetitions: usize, text: &str) {
|
fn print_at_rep(&self, pos: Vec2, repetitions: usize, text: &str) {
|
||||||
|
self.lock();
|
||||||
if repetitions > 0 {
|
if repetitions > 0 {
|
||||||
let mut out = self.terminal.borrow_mut();
|
let mut out = self.terminal.borrow_mut();
|
||||||
write!(
|
write!(
|
||||||
|
Loading…
Reference in New Issue
Block a user