Misc. hacks

This commit is contained in:
FliegendeWurst 2022-06-20 15:42:27 +02:00
parent 8ec46107e1
commit 924684215a
11 changed files with 100 additions and 18 deletions

View File

@ -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:

View File

@ -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.

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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.

View File

@ -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())

View File

@ -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);
} }

View File

@ -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);

View File

@ -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!(