mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
TextView: print styled spans of text
This commit is contained in:
parent
ef7cfb2dd6
commit
890b3f13e1
22
examples/markup.rs
Normal file
22
examples/markup.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
extern crate cursive;
|
||||||
|
|
||||||
|
use cursive::Cursive;
|
||||||
|
use cursive::utils::markup::MarkdownText;
|
||||||
|
use cursive::views::{Dialog, TextView};
|
||||||
|
|
||||||
|
// Make sure you compile with the `markdown` feature!
|
||||||
|
//
|
||||||
|
// cargo run --example markup --features markdown
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut siv = Cursive::new();
|
||||||
|
|
||||||
|
let text = MarkdownText("Isn't *that* **cool**?");
|
||||||
|
|
||||||
|
siv.add_layer(
|
||||||
|
Dialog::around(TextView::styled(text).unwrap())
|
||||||
|
.button("Hell yeah!", |s| s.quit()),
|
||||||
|
);
|
||||||
|
|
||||||
|
siv.run();
|
||||||
|
}
|
@ -143,6 +143,8 @@ impl<'a> Printer<'a> {
|
|||||||
let color = style.color;
|
let color = style.color;
|
||||||
let effects = style.effects;
|
let effects = style.effects;
|
||||||
|
|
||||||
|
// eprintln!("{:?}", effects);
|
||||||
|
|
||||||
if let Some(color) = color {
|
if let Some(color) = color {
|
||||||
self.with_color(color, |printer| {
|
self.with_color(color, |printer| {
|
||||||
printer.with_effects(effects, f);
|
printer.with_effects(effects, f);
|
||||||
@ -172,7 +174,8 @@ impl<'a> Printer<'a> {
|
|||||||
Some(effect) => {
|
Some(effect) => {
|
||||||
let mut effects = effects;
|
let mut effects = effects;
|
||||||
effects.remove(effect);
|
effects.remove(effect);
|
||||||
self.with_effects(effects, f);
|
|
||||||
|
self.with_effect(effect, |s| s.with_effects(effects, f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,47 @@ use std::borrow::Cow;
|
|||||||
use theme::{Effect, Style};
|
use theme::{Effect, Style};
|
||||||
use utils::lines::spans::Span;
|
use utils::lines::spans::Span;
|
||||||
|
|
||||||
|
/// `Markup` trait implementation for markdown text.
|
||||||
|
///
|
||||||
|
/// Requires the `markdown` feature.
|
||||||
|
pub struct Markdown;
|
||||||
|
|
||||||
|
impl super::Markup for Markdown {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn parse<'a>(input: &'a str) -> Result<Vec<Span<'a>>, Self::Error> {
|
||||||
|
Ok(parse(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Thin wrapper around text that should be parsed as Markdown.
|
||||||
|
///
|
||||||
|
/// This does not parse the text here, but indicates how it should be parsed.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// // Can use `&str`
|
||||||
|
/// let text = MarkdownText("*Markdown* text!");
|
||||||
|
///
|
||||||
|
/// // Or `String`
|
||||||
|
/// let text = MarkdownText(String::from("*Right __here__!"));
|
||||||
|
/// ```
|
||||||
|
pub struct MarkdownText<S>(pub S)
|
||||||
|
where
|
||||||
|
S: Into<String>;
|
||||||
|
|
||||||
|
impl<S> super::MarkupText for MarkdownText<S>
|
||||||
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
type M = Markdown;
|
||||||
|
|
||||||
|
fn to_string(self) -> String {
|
||||||
|
self.0.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Iterator that parse a markdown text and outputs styled spans.
|
/// Iterator that parse a markdown text and outputs styled spans.
|
||||||
pub struct Parser<'a> {
|
pub struct Parser<'a> {
|
||||||
first: bool,
|
first: bool,
|
||||||
@ -116,19 +157,7 @@ impl<'a> Iterator for Parser<'a> {
|
|||||||
/// This is a shortcut for `Parser::new(input).collect()`.
|
/// This is a shortcut for `Parser::new(input).collect()`.
|
||||||
pub fn parse<'a>(input: &'a str) -> Vec<Span<'a>> {
|
pub fn parse<'a>(input: &'a str) -> Vec<Span<'a>> {
|
||||||
Parser::new(input).collect()
|
Parser::new(input).collect()
|
||||||
}
|
// Parser::new(input).inspect(|span| eprintln!("{:?}", span)).collect()
|
||||||
|
|
||||||
/// `Markup` trait implementation for markdown text.
|
|
||||||
///
|
|
||||||
/// Requires the `markdown` feature.
|
|
||||||
pub struct Markdown;
|
|
||||||
|
|
||||||
impl super::Markup for Markdown {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn parse<'a>(input: &'a str) -> Result<Vec<Span<'a>>, Self::Error> {
|
|
||||||
Ok(parse(input))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -6,12 +6,14 @@
|
|||||||
pub mod markdown;
|
pub mod markdown;
|
||||||
|
|
||||||
#[cfg(feature = "markdown")]
|
#[cfg(feature = "markdown")]
|
||||||
pub use self::markdown::Markdown;
|
pub use self::markdown::MarkdownText;
|
||||||
|
|
||||||
use owning_ref::OwningHandle;
|
use owning_ref::OwningHandle;
|
||||||
use owning_ref::StringRef;
|
use owning_ref::StringRef;
|
||||||
use utils::lines::spans::Span;
|
|
||||||
use theme::Style;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use theme::Style;
|
||||||
|
use utils::lines::spans::Span;
|
||||||
|
|
||||||
/// Trait for parsing text into styled spans.
|
/// Trait for parsing text into styled spans.
|
||||||
pub trait Markup {
|
pub trait Markup {
|
||||||
@ -36,6 +38,28 @@ pub trait Markup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Thin wrapper around a string, with a markup format.
|
||||||
|
///
|
||||||
|
/// This only wraps the text and indicates how it should be parsed;
|
||||||
|
/// it does not parse the text itself.
|
||||||
|
pub trait MarkupText {
|
||||||
|
|
||||||
|
/// Markup format to use to parse the string.
|
||||||
|
type M: Markup;
|
||||||
|
|
||||||
|
/// Access the inner string.
|
||||||
|
fn to_string(self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unwrapped text gets the "Plain" markup for free.
|
||||||
|
impl<S: Into<String>> MarkupText for S {
|
||||||
|
type M = Plain;
|
||||||
|
|
||||||
|
fn to_string(self) -> String {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Dummy `Markup` implementation that returns the text as-is.
|
/// Dummy `Markup` implementation that returns the text as-is.
|
||||||
pub struct Plain;
|
pub struct Plain;
|
||||||
|
|
||||||
@ -43,10 +67,12 @@ impl Markup for Plain {
|
|||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn parse<'a>(input: &'a str) -> Result<Vec<Span<'a>>, Self::Error> {
|
fn parse<'a>(input: &'a str) -> Result<Vec<Span<'a>>, Self::Error> {
|
||||||
Ok(vec![Span {
|
Ok(vec![
|
||||||
|
Span {
|
||||||
text: Cow::Borrowed(input),
|
text: Cow::Borrowed(input),
|
||||||
style: Style::none(),
|
style: Style::none(),
|
||||||
}])
|
},
|
||||||
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,37 +83,105 @@ pub type StyledHandle = OwningHandle<StringRef, Vec<Span<'static>>>;
|
|||||||
|
|
||||||
/// A String that parses a markup language.
|
/// A String that parses a markup language.
|
||||||
pub struct StyledString {
|
pub struct StyledString {
|
||||||
content: StyledHandle,
|
content: Option<StyledHandle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledString {
|
impl StyledString {
|
||||||
|
|
||||||
/// Creates a new styled string, parsing the given content.
|
/// Creates a new styled string, parsing the given content.
|
||||||
pub fn new<S, M>(content: S) -> Result<Self, M::Error>
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use cursive::utils::markup::StyledString;
|
||||||
|
/// let styled_string = StyledString::new("*plain* text");
|
||||||
|
/// ```
|
||||||
|
pub fn new<T>(content: T) -> Result<Self, <T::M as Markup>::Error>
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
T: MarkupText,
|
||||||
M: Markup,
|
|
||||||
{
|
{
|
||||||
let content = M::make_handle(content)?;
|
let content = content.to_string();
|
||||||
|
|
||||||
|
let content = Some(T::M::make_handle(content)?);
|
||||||
|
|
||||||
Ok(StyledString { content })
|
Ok(StyledString { content })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a plain StyledString without any style.
|
||||||
|
///
|
||||||
|
/// > You got no style, Dutch. You know that.
|
||||||
|
pub fn plain<S>(content: S) -> Self
|
||||||
|
where S: Into<String>
|
||||||
|
{
|
||||||
|
Self::new(content).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the content of this string.
|
/// Sets the content of this string.
|
||||||
///
|
///
|
||||||
/// The content will be parsed; if an error is found,
|
/// # Examples
|
||||||
/// it will be returned here (and the content will be unchanged).
|
///
|
||||||
pub fn set_content<S, M>(&mut self, content: S) -> Result<(), M::Error>
|
/// ```rust
|
||||||
|
/// # use cursive::utils::markup::StyledString;
|
||||||
|
/// # let mut styled_string = StyledString::new("").unwrap();
|
||||||
|
/// styled_string.set_content("*plain* text").unwrap();
|
||||||
|
/// ```
|
||||||
|
pub fn set_content<T>(
|
||||||
|
&mut self, content: T
|
||||||
|
) -> Result<(), <<T as MarkupText>::M as Markup>::Error>
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
T: MarkupText,
|
||||||
M: Markup,
|
|
||||||
{
|
{
|
||||||
self.content = M::make_handle(content)?;
|
let content = content.to_string();
|
||||||
|
|
||||||
|
self.content = Some(T::M::make_handle(content)?);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the content of this string to plain text.
|
||||||
|
pub fn set_plain<S>(&mut self, content: S) where S: Into<String> {
|
||||||
|
self.set_content(content).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append `content` to the end.
|
||||||
|
///
|
||||||
|
/// Re-parse everything after.
|
||||||
|
pub fn append_content<T>(&mut self, content: T) -> Result<(), <T::M as Markup>::Error>
|
||||||
|
where
|
||||||
|
T: MarkupText
|
||||||
|
{
|
||||||
|
self.with_content::<T::M, _, _>(|c| c.push_str(&content.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run a closure on the text content.
|
||||||
|
///
|
||||||
|
/// And re-parse everything after.
|
||||||
|
pub fn with_content<M, F, O>(&mut self, f: F) -> Result<O, M::Error>
|
||||||
|
where
|
||||||
|
M: Markup,
|
||||||
|
F: FnOnce(&mut String) -> O,
|
||||||
|
{
|
||||||
|
// Get hold of the StyledHandle
|
||||||
|
let content = self.content.take().unwrap();
|
||||||
|
// Get the inner String
|
||||||
|
let mut content = content.into_inner().into_inner();
|
||||||
|
// Do what we have to do
|
||||||
|
let out = f(&mut content);
|
||||||
|
// And re-parse everything
|
||||||
|
self.content = Some(M::make_handle(content)?);
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
/// Gives access to the parsed styled spans.
|
/// Gives access to the parsed styled spans.
|
||||||
pub fn spans<'a>(&'a self) -> &'a [Span<'a>] {
|
pub fn spans<'a>(&'a self) -> &'a [Span<'a>] {
|
||||||
&self.content
|
&self.content.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for StyledString {
|
||||||
|
type Target = str;
|
||||||
|
|
||||||
|
fn deref(&self) -> &str {
|
||||||
|
&self.content.as_ref().unwrap().owner()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,8 @@ use std::sync::{Mutex, MutexGuard};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use theme::Effect;
|
use theme::Effect;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
use utils::lines::simple::{LinesIterator, Row};
|
use utils::lines::spans::{Row, SpanLinesIterator};
|
||||||
|
use utils::markup::{Markup, MarkupText, StyledString};
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use view::{ScrollBase, ScrollStrategy, SizeCache, View};
|
use view::{ScrollBase, ScrollStrategy, SizeCache, View};
|
||||||
|
|
||||||
@ -38,13 +39,28 @@ pub struct TextContent {
|
|||||||
|
|
||||||
impl TextContent {
|
impl TextContent {
|
||||||
/// Creates a new text content around the given value.
|
/// Creates a new text content around the given value.
|
||||||
pub fn new<S: Into<String>>(content: S) -> Self {
|
pub fn new<S>(content: S) -> Self
|
||||||
TextContent {
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
Self::styled(content).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new text content around the given value.
|
||||||
|
///
|
||||||
|
/// Parses the given value.
|
||||||
|
pub fn styled<T>(content: T) -> Result<Self, <T::M as Markup>::Error>
|
||||||
|
where
|
||||||
|
T: MarkupText,
|
||||||
|
{
|
||||||
|
let content = StyledString::new(content)?;
|
||||||
|
|
||||||
|
Ok(TextContent {
|
||||||
content: Arc::new(Mutex::new(TextContentInner {
|
content: Arc::new(Mutex::new(TextContentInner {
|
||||||
content: content.into(),
|
content,
|
||||||
size_cache: None,
|
size_cache: None,
|
||||||
})),
|
})),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,13 +86,33 @@ impl Deref for TextContentRef {
|
|||||||
|
|
||||||
impl TextContent {
|
impl TextContent {
|
||||||
/// Replaces the content with the given value.
|
/// Replaces the content with the given value.
|
||||||
pub fn set_content<S: Into<String>>(&mut self, content: S) {
|
pub fn set_content<S>(&mut self, content: S)
|
||||||
self.with_content(|c| *c = content.into());
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
self.with_content(|c| c.set_plain(content));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces the content with the given value.
|
||||||
|
///
|
||||||
|
/// The given markup text will be parsed.
|
||||||
|
pub fn set_markup<T>(
|
||||||
|
&mut self, content: T
|
||||||
|
) -> Result<(), <T::M as Markup>::Error>
|
||||||
|
where
|
||||||
|
T: MarkupText,
|
||||||
|
{
|
||||||
|
self.with_content(|c| c.set_content(content))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Append `content` to the end of a `TextView`.
|
/// Append `content` to the end of a `TextView`.
|
||||||
pub fn append_content(&mut self, content: &str) {
|
pub fn append_content<T>(
|
||||||
self.with_content(|c| c.push_str(content));
|
&mut self, content: T
|
||||||
|
) -> Result<(), <T::M as Markup>::Error>
|
||||||
|
where
|
||||||
|
T: MarkupText,
|
||||||
|
{
|
||||||
|
self.with_content(|c| c.append_content(content))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the content.
|
/// Returns a reference to the content.
|
||||||
@ -87,20 +123,25 @@ impl TextContent {
|
|||||||
TextContentInner::get_content(&self.content)
|
TextContentInner::get_content(&self.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_content<F>(&mut self, f: F)
|
fn with_content<F, O>(&mut self, f: F) -> O
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut String),
|
F: FnOnce(&mut StyledString) -> O,
|
||||||
{
|
{
|
||||||
let mut lock = self.content.lock().unwrap();
|
let mut lock = self.content.lock().unwrap();
|
||||||
|
|
||||||
f(&mut lock.content);
|
let out = f(&mut lock.content);
|
||||||
|
|
||||||
lock.size_cache = None;
|
lock.size_cache = None;
|
||||||
|
|
||||||
|
out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TextContentInner {
|
struct TextContentInner {
|
||||||
content: String,
|
// content: String,
|
||||||
|
content: StyledString,
|
||||||
|
|
||||||
|
// We keep the cache here so it can be busted when we change the content.
|
||||||
size_cache: Option<XY<SizeCache>>,
|
size_cache: Option<XY<SizeCache>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,8 +205,28 @@ fn strip_last_newline(content: &str) -> &str {
|
|||||||
|
|
||||||
impl TextView {
|
impl TextView {
|
||||||
/// Creates a new TextView with the given content.
|
/// Creates a new TextView with the given content.
|
||||||
pub fn new<S: Into<String>>(content: S) -> Self {
|
pub fn new<S>(content: S) -> Self
|
||||||
TextView::new_with_content(TextContent::new(content))
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
Self::styled(content).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new TextView by parsing the given content.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// # use cursive::views::TextView;
|
||||||
|
/// use cursive::utils::markup::MarkdownText;
|
||||||
|
/// // This will require the `markdown` feature!
|
||||||
|
/// let view = TextView::styled(MarkdownText("**Bold** text"));
|
||||||
|
/// ```
|
||||||
|
pub fn styled<T>(content: T) -> Result<Self, <T::M as Markup>::Error>
|
||||||
|
where
|
||||||
|
T: MarkupText,
|
||||||
|
{
|
||||||
|
TextContent::styled(content).map(TextView::new_with_content)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new TextView using the given `Arc<Mutex<String>>`.
|
/// Creates a new TextView using the given `Arc<Mutex<String>>`.
|
||||||
@ -263,21 +324,62 @@ impl TextView {
|
|||||||
/// Replace the text in this view.
|
/// Replace the text in this view.
|
||||||
///
|
///
|
||||||
/// Chainable variant.
|
/// Chainable variant.
|
||||||
pub fn content<S: Into<String>>(self, content: S) -> Self {
|
pub fn content<S>(self, content: S) -> Self
|
||||||
self.with(|s| s.set_content(content))
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
self.markup(content).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace the text in this view.
|
/// Replace the text in this view.
|
||||||
pub fn set_content<S: Into<String>>(&mut self, content: S) {
|
///
|
||||||
let content = content.into();
|
/// Parse the given markup text.
|
||||||
self.content.lock().unwrap().content = content;
|
///
|
||||||
|
/// Chainable variant.
|
||||||
|
pub fn markup<T>(self, content: T) -> Result<Self, <T::M as Markup>::Error>
|
||||||
|
where
|
||||||
|
T: MarkupText,
|
||||||
|
{
|
||||||
|
self.try_with(|s| s.set_markup(content))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replace the text in this view.
|
||||||
|
pub fn set_content<S>(&mut self, content: S)
|
||||||
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
self.set_markup(content).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replace the text in this view.
|
||||||
|
///
|
||||||
|
/// Parses the given markup text.
|
||||||
|
pub fn set_markup<T>(
|
||||||
|
&mut self, content: T
|
||||||
|
) -> Result<(), <T::M as Markup>::Error>
|
||||||
|
where
|
||||||
|
T: MarkupText,
|
||||||
|
{
|
||||||
|
self.content.lock().unwrap().content.set_content(content)?;
|
||||||
self.invalidate();
|
self.invalidate();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Append `content` to the end of a `TextView`.
|
/// Append `content` to the end of a `TextView`.
|
||||||
pub fn append_content(&mut self, content: &str) {
|
pub fn append_content<T>(
|
||||||
self.content.lock().unwrap().content.push_str(content);
|
&mut self, content: T
|
||||||
|
) -> Result<(), <T::M as Markup>::Error>
|
||||||
|
where
|
||||||
|
T: MarkupText,
|
||||||
|
{
|
||||||
|
self.content
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.content
|
||||||
|
.append_content(content)?;
|
||||||
self.invalidate();
|
self.invalidate();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current text in this view.
|
/// Returns the current text in this view.
|
||||||
@ -365,8 +467,7 @@ impl TextView {
|
|||||||
// First attempt: naively hope that we won't need a scrollbar_width
|
// First attempt: naively hope that we won't need a scrollbar_width
|
||||||
// (This means we try to use the entire available width for text).
|
// (This means we try to use the entire available width for text).
|
||||||
self.rows =
|
self.rows =
|
||||||
LinesIterator::new(strip_last_newline(&content.content), size.x)
|
SpanLinesIterator::new(content.content.spans(), size.x).collect();
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Width taken by the scrollbar. Without a scrollbar, it's 0.
|
// Width taken by the scrollbar. Without a scrollbar, it's 0.
|
||||||
let mut scrollbar_width = 0;
|
let mut scrollbar_width = 0;
|
||||||
@ -382,7 +483,8 @@ impl TextView {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.rows =
|
self.rows =
|
||||||
LinesIterator::new(&content.content, available).collect();
|
SpanLinesIterator::new(content.content.spans(), available)
|
||||||
|
.collect();
|
||||||
|
|
||||||
if self.rows.is_empty() && !content.content.is_empty() {
|
if self.rows.is_empty() && !content.content.is_empty() {
|
||||||
// We have some content, we we didn't find any row for it?
|
// We have some content, we we didn't find any row for it?
|
||||||
@ -431,10 +533,17 @@ impl View for TextView {
|
|||||||
printer.with_effect(self.effect, |printer| {
|
printer.with_effect(self.effect, |printer| {
|
||||||
self.scrollbase.draw(printer, |printer, i| {
|
self.scrollbase.draw(printer, |printer, i| {
|
||||||
let row = &self.rows[i];
|
let row = &self.rows[i];
|
||||||
let text = &content.content[row.start..row.end];
|
let l = row.width;
|
||||||
let l = text.width();
|
let mut x = self.align.h.get_offset(l, printer.size.x);
|
||||||
let x = self.align.h.get_offset(l, printer.size.x);
|
|
||||||
printer.print((x, 0), text);
|
for span in row.resolve(content.content.spans()) {
|
||||||
|
printer.with_style(span.style, |printer| {
|
||||||
|
printer.print((x, 0), &span.text);
|
||||||
|
x += span.text.len();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// let text = &content.content[row.start..row.end];
|
||||||
|
// printer.print((x, 0), text);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,15 @@ pub trait With: Sized {
|
|||||||
f(&mut self);
|
f(&mut self);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calls the given closure on `self`.
|
||||||
|
fn try_with<E, F>(mut self, f: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> Result<(), E>,
|
||||||
|
{
|
||||||
|
f(&mut self)?;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Sized> With for T {}
|
impl<T: Sized> With for T {}
|
||||||
|
Loading…
Reference in New Issue
Block a user