Implement rich_text widget and markdown example
This commit is contained in:
parent
ffb520fb37
commit
910eb72a06
11 changed files with 787 additions and 99 deletions
|
|
@ -77,6 +77,11 @@ impl text::Paragraph for () {
|
|||
|
||||
fn with_text(_text: Text<&str>) -> Self {}
|
||||
|
||||
fn with_spans(
|
||||
_text: Text<&[text::Span<'_, Self::Font>], Self::Font>,
|
||||
) -> Self {
|
||||
}
|
||||
|
||||
fn resize(&mut self, _new_bounds: Size) {}
|
||||
|
||||
fn compare(&self, _text: Text<()>) -> text::Difference {
|
||||
|
|
|
|||
148
core/src/text.rs
148
core/src/text.rs
|
|
@ -10,6 +10,7 @@ pub use paragraph::Paragraph;
|
|||
use crate::alignment;
|
||||
use crate::{Color, Pixels, Point, Rectangle, Size};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
/// A paragraph.
|
||||
|
|
@ -220,3 +221,150 @@ pub trait Renderer: crate::Renderer {
|
|||
clip_bounds: Rectangle,
|
||||
);
|
||||
}
|
||||
|
||||
/// A span of text.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Span<'a, Font = crate::Font> {
|
||||
/// The [`Fragment`] of text.
|
||||
pub text: Fragment<'a>,
|
||||
/// The size of the [`Span`] in [`Pixels`].
|
||||
pub size: Option<Pixels>,
|
||||
/// The [`LineHeight`] of the [`Span`].
|
||||
pub line_height: Option<LineHeight>,
|
||||
/// The font of the [`Span`].
|
||||
pub font: Option<Font>,
|
||||
/// The [`Color`] of the [`Span`].
|
||||
pub color: Option<Color>,
|
||||
}
|
||||
|
||||
impl<'a, Font> Span<'a, Font> {
|
||||
/// Creates a new [`Span`] of text with the given text fragment.
|
||||
pub fn new(fragment: impl IntoFragment<'a>) -> Self {
|
||||
Self {
|
||||
text: fragment.into_fragment(),
|
||||
size: None,
|
||||
line_height: None,
|
||||
font: None,
|
||||
color: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the size of the [`Span`].
|
||||
pub fn size(mut self, size: impl Into<Pixels>) -> Self {
|
||||
self.size = Some(size.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the [`LineHeight`] of the [`Span`].
|
||||
pub fn line_height(mut self, line_height: impl Into<LineHeight>) -> Self {
|
||||
self.line_height = Some(line_height.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the font of the [`Span`].
|
||||
pub fn font(mut self, font: impl Into<Font>) -> Self {
|
||||
self.font = Some(font.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the [`Color`] of the [`Span`].
|
||||
pub fn color(mut self, color: impl Into<Color>) -> Self {
|
||||
self.color = Some(color.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Turns the [`Span`] into a static one.
|
||||
pub fn to_static(self) -> Span<'static, Font> {
|
||||
Span {
|
||||
text: Cow::Owned(self.text.into_owned()),
|
||||
size: self.size,
|
||||
line_height: self.line_height,
|
||||
font: self.font,
|
||||
color: self.color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Font> From<&'a str> for Span<'a, Font> {
|
||||
fn from(value: &'a str) -> Self {
|
||||
Span::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// A fragment of [`Text`].
|
||||
///
|
||||
/// This is just an alias to a string that may be either
|
||||
/// borrowed or owned.
|
||||
pub type Fragment<'a> = Cow<'a, str>;
|
||||
|
||||
/// A trait for converting a value to some text [`Fragment`].
|
||||
pub trait IntoFragment<'a> {
|
||||
/// Converts the value to some text [`Fragment`].
|
||||
fn into_fragment(self) -> Fragment<'a>;
|
||||
}
|
||||
|
||||
impl<'a> IntoFragment<'a> for Fragment<'a> {
|
||||
fn into_fragment(self) -> Fragment<'a> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> IntoFragment<'a> for &'a Fragment<'b> {
|
||||
fn into_fragment(self) -> Fragment<'a> {
|
||||
Fragment::Borrowed(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoFragment<'a> for &'a str {
|
||||
fn into_fragment(self) -> Fragment<'a> {
|
||||
Fragment::Borrowed(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoFragment<'a> for &'a String {
|
||||
fn into_fragment(self) -> Fragment<'a> {
|
||||
Fragment::Borrowed(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoFragment<'a> for String {
|
||||
fn into_fragment(self) -> Fragment<'a> {
|
||||
Fragment::Owned(self)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! into_fragment {
|
||||
($type:ty) => {
|
||||
impl<'a> IntoFragment<'a> for $type {
|
||||
fn into_fragment(self) -> Fragment<'a> {
|
||||
Fragment::Owned(self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoFragment<'a> for &$type {
|
||||
fn into_fragment(self) -> Fragment<'a> {
|
||||
Fragment::Owned(self.to_string())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
into_fragment!(char);
|
||||
into_fragment!(bool);
|
||||
|
||||
into_fragment!(u8);
|
||||
into_fragment!(u16);
|
||||
into_fragment!(u32);
|
||||
into_fragment!(u64);
|
||||
into_fragment!(u128);
|
||||
into_fragment!(usize);
|
||||
|
||||
into_fragment!(i8);
|
||||
into_fragment!(i16);
|
||||
into_fragment!(i32);
|
||||
into_fragment!(i64);
|
||||
into_fragment!(i128);
|
||||
into_fragment!(isize);
|
||||
|
||||
into_fragment!(f32);
|
||||
into_fragment!(f64);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! Draw paragraphs.
|
||||
use crate::alignment;
|
||||
use crate::text::{Difference, Hit, Text};
|
||||
use crate::text::{Difference, Hit, Span, Text};
|
||||
use crate::{Point, Size};
|
||||
|
||||
/// A text paragraph.
|
||||
|
|
@ -11,6 +11,9 @@ pub trait Paragraph: Sized + Default {
|
|||
/// Creates a new [`Paragraph`] laid out with the given [`Text`].
|
||||
fn with_text(text: Text<&str, Self::Font>) -> Self;
|
||||
|
||||
/// Creates a new [`Paragraph`] laid out with the given [`Text`].
|
||||
fn with_spans(text: Text<&[Span<'_, Self::Font>], Self::Font>) -> Self;
|
||||
|
||||
/// Lays out the [`Paragraph`] with some new boundaries.
|
||||
fn resize(&mut self, new_bounds: Size);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ use crate::{
|
|||
Widget,
|
||||
};
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub use text::{LineHeight, Shaping};
|
||||
|
||||
/// A paragraph of text.
|
||||
|
|
@ -22,7 +20,7 @@ where
|
|||
Theme: Catalog,
|
||||
Renderer: text::Renderer,
|
||||
{
|
||||
fragment: Fragment<'a>,
|
||||
fragment: text::Fragment<'a>,
|
||||
size: Option<Pixels>,
|
||||
line_height: LineHeight,
|
||||
width: Length,
|
||||
|
|
@ -40,7 +38,7 @@ where
|
|||
Renderer: text::Renderer,
|
||||
{
|
||||
/// Create a new fragment of [`Text`] with the given contents.
|
||||
pub fn new(fragment: impl IntoFragment<'a>) -> Self {
|
||||
pub fn new(fragment: impl text::IntoFragment<'a>) -> Self {
|
||||
Text {
|
||||
fragment: fragment.into_fragment(),
|
||||
size: None,
|
||||
|
|
@ -216,7 +214,7 @@ where
|
|||
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
|
||||
let style = theme.style(&self.class);
|
||||
|
||||
draw(renderer, defaults, layout, state, style, viewport);
|
||||
draw(renderer, defaults, layout, state.0.raw(), style, viewport);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -275,13 +273,12 @@ pub fn draw<Renderer>(
|
|||
renderer: &mut Renderer,
|
||||
style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
state: &State<Renderer::Paragraph>,
|
||||
paragraph: &Renderer::Paragraph,
|
||||
appearance: Style,
|
||||
viewport: &Rectangle,
|
||||
) where
|
||||
Renderer: text::Renderer,
|
||||
{
|
||||
let State(ref paragraph) = state;
|
||||
let bounds = layout.bounds();
|
||||
|
||||
let x = match paragraph.horizontal_alignment() {
|
||||
|
|
@ -297,7 +294,7 @@ pub fn draw<Renderer>(
|
|||
};
|
||||
|
||||
renderer.fill_paragraph(
|
||||
paragraph.raw(),
|
||||
paragraph,
|
||||
Point::new(x, y),
|
||||
appearance.color.unwrap_or(style.text_color),
|
||||
*viewport,
|
||||
|
|
@ -415,81 +412,3 @@ pub fn danger(theme: &Theme) -> Style {
|
|||
color: Some(theme.palette().danger),
|
||||
}
|
||||
}
|
||||
|
||||
/// A fragment of [`Text`].
|
||||
///
|
||||
/// This is just an alias to a string that may be either
|
||||
/// borrowed or owned.
|
||||
pub type Fragment<'a> = Cow<'a, str>;
|
||||
|
||||
/// A trait for converting a value to some text [`Fragment`].
|
||||
pub trait IntoFragment<'a> {
|
||||
/// Converts the value to some text [`Fragment`].
|
||||
fn into_fragment(self) -> Fragment<'a>;
|
||||
}
|
||||
|
||||
impl<'a> IntoFragment<'a> for Fragment<'a> {
|
||||
fn into_fragment(self) -> Fragment<'a> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> IntoFragment<'a> for &'a Fragment<'b> {
|
||||
fn into_fragment(self) -> Fragment<'a> {
|
||||
Fragment::Borrowed(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoFragment<'a> for &'a str {
|
||||
fn into_fragment(self) -> Fragment<'a> {
|
||||
Fragment::Borrowed(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoFragment<'a> for &'a String {
|
||||
fn into_fragment(self) -> Fragment<'a> {
|
||||
Fragment::Borrowed(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoFragment<'a> for String {
|
||||
fn into_fragment(self) -> Fragment<'a> {
|
||||
Fragment::Owned(self)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! into_fragment {
|
||||
($type:ty) => {
|
||||
impl<'a> IntoFragment<'a> for $type {
|
||||
fn into_fragment(self) -> Fragment<'a> {
|
||||
Fragment::Owned(self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoFragment<'a> for &$type {
|
||||
fn into_fragment(self) -> Fragment<'a> {
|
||||
Fragment::Owned(self.to_string())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
into_fragment!(char);
|
||||
into_fragment!(bool);
|
||||
|
||||
into_fragment!(u8);
|
||||
into_fragment!(u16);
|
||||
into_fragment!(u32);
|
||||
into_fragment!(u64);
|
||||
into_fragment!(u128);
|
||||
into_fragment!(usize);
|
||||
|
||||
into_fragment!(i8);
|
||||
into_fragment!(i16);
|
||||
into_fragment!(i32);
|
||||
into_fragment!(i64);
|
||||
into_fragment!(i128);
|
||||
into_fragment!(isize);
|
||||
|
||||
into_fragment!(f32);
|
||||
into_fragment!(f64);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue