use std::rc::Rc;
use crate::base::Rect;
use crate::render::RichText;
use crate::ui::StyledCanvas;
pub enum FeedBlock {
Text(String),
Markdown(String),
Code {
lang: String,
source: String,
},
Custom(CustomBlock),
}
pub(super) type SharedDrawFn = Rc<dyn Fn(&mut dyn StyledCanvas, Rect)>;
pub struct CustomBlock {
pub(super) height: Box<dyn Fn(i32) -> i32>,
pub(super) draw: SharedDrawFn,
}
impl CustomBlock {
pub fn new(
height: impl Fn(i32) -> i32 + 'static,
draw: impl Fn(&mut dyn StyledCanvas, Rect) + 'static,
) -> CustomBlock {
CustomBlock {
height: Box::new(height),
draw: Rc::new(draw),
}
}
}
pub(super) struct RowCap {
pub(super) max_rows: Option<usize>,
pub(super) marker: Option<Rc<dyn Fn(usize) -> String>>,
}
impl RowCap {
fn empty() -> RowCap {
RowCap {
max_rows: None,
marker: None,
}
}
}
pub(super) enum ItemBlock {
Text {
text: String,
cap: Option<RowCap>,
},
Markdown(String),
Code {
lang: String,
source: String,
},
Custom(CustomBlock),
Rich {
text: RichText,
cap: Option<RowCap>,
},
}
impl From<FeedBlock> for ItemBlock {
fn from(b: FeedBlock) -> ItemBlock {
match b {
FeedBlock::Text(s) => ItemBlock::Text { text: s, cap: None },
FeedBlock::Markdown(s) => ItemBlock::Markdown(s),
FeedBlock::Code { lang, source } => ItemBlock::Code { lang, source },
FeedBlock::Custom(c) => ItemBlock::Custom(c),
}
}
}
pub struct FeedItem {
pub(super) blocks: Vec<ItemBlock>,
}
impl FeedItem {
pub fn new() -> FeedItem {
FeedItem { blocks: Vec::new() }
}
pub fn markdown(src: impl Into<String>) -> FeedItem {
FeedItem::new().block(FeedBlock::Markdown(src.into()))
}
pub fn text(s: impl Into<String>) -> FeedItem {
FeedItem::new().block(FeedBlock::Text(s.into()))
}
pub fn code(lang: impl Into<String>, source: impl Into<String>) -> FeedItem {
FeedItem::new().block(FeedBlock::Code {
lang: lang.into(),
source: source.into(),
})
}
pub fn rich(text: RichText) -> FeedItem {
FeedItem::new().rich_block(text)
}
pub fn rich_lines(lines: Vec<crate::render::RichLine>) -> FeedItem {
FeedItem::rich(RichText::from_lines(lines))
}
pub fn rich_block(mut self, text: RichText) -> FeedItem {
self.blocks.push(ItemBlock::Rich { text, cap: None });
self
}
pub fn block(mut self, b: FeedBlock) -> FeedItem {
self.blocks.push(b.into());
self
}
pub fn max_rows(mut self, rows: usize) -> FeedItem {
let slot = self.last_cap();
debug_assert!(
slot.is_some(),
"FeedItem::max_rows targets the last appended Text/Rich block"
);
if let Some(cap) = slot {
cap.get_or_insert_with(RowCap::empty).max_rows = Some(rows.max(1));
}
self
}
pub fn overflow_marker(mut self, marker: impl Fn(usize) -> String + 'static) -> FeedItem {
let slot = self.last_cap();
debug_assert!(
slot.is_some(),
"FeedItem::overflow_marker targets the last appended Text/Rich block"
);
if let Some(cap) = slot {
cap.get_or_insert_with(RowCap::empty).marker = Some(Rc::new(marker));
}
self
}
fn last_cap(&mut self) -> Option<&mut Option<RowCap>> {
match self.blocks.last_mut() {
Some(ItemBlock::Text { cap, .. }) | Some(ItemBlock::Rich { cap, .. }) => Some(cap),
_ => None,
}
}
}
impl Default for FeedItem {
fn default() -> Self {
FeedItem::new()
}
}