use std::collections::HashSet;
use std::sync::Arc;
use crate::clipboard::ImageContent;
use crate::utils::text::SentinelInfo;
use super::{
IMAGE_SENTINEL_BASE, SENTINEL_BASE, SentinelEvent, SentinelId, TextArea, TextAreaImageMode,
TextAreaSentinel,
};
#[derive(Clone, Debug)]
pub struct TextAreaSnapshot {
pub value: Arc<str>,
pub cursor: usize,
pub anchor: Option<usize>,
pub sentinels: Vec<TextAreaSentinel>,
pub images: Vec<ImageContent>,
pub image_mode: TextAreaImageMode,
}
pub struct TextAreaClipboardTransformEvent<'a> {
pub text: &'a str,
pub raw_text: &'a str,
}
pub type TextAreaClipboardTransform =
Arc<dyn for<'a> Fn(TextAreaClipboardTransformEvent<'a>) -> String>;
impl TextAreaSnapshot {
pub fn capture(ta: &TextArea) -> Self {
Self {
value: ta.value.clone(),
cursor: ta.cursor,
anchor: ta.anchor,
sentinels: ta.sentinels.clone(),
images: ta.images.clone(),
image_mode: ta.image_mode,
}
}
pub fn apply(self, ta: TextArea) -> TextArea {
ta.value(self.value)
.cursor(self.cursor)
.anchor(self.anchor)
.sentinels(self.sentinels)
.images(self.images)
.image_mode(self.image_mode)
}
pub fn diff(&self, other: &Self) -> Vec<SentinelEvent> {
let other_ids: HashSet<SentinelId> = other
.sentinels
.iter()
.filter_map(TextAreaSentinel::sentinel_id)
.collect();
let mut events = Vec::new();
for s in &self.sentinels {
if let Some(id) = s.sentinel_id()
&& !other_ids.contains(&id)
{
events.push(SentinelEvent::Deleted {
id,
sentinel: s.clone(),
});
}
}
events
}
}
pub(crate) fn sentinel_info_for(
image_mode: TextAreaImageMode,
images_count: usize,
image_placeholder: &str,
sentinels: &[TextAreaSentinel],
) -> Option<SentinelInfo> {
let image = if image_mode == TextAreaImageMode::Inline && images_count > 0 {
let base = IMAGE_SENTINEL_BASE as u32;
let end = base + images_count as u32;
let ph_width = if image_placeholder.contains('X') {
let label = image_placeholder.replace('X', images_count.to_string().as_str());
unicode_width::UnicodeWidthStr::width(label.as_str())
} else {
unicode_width::UnicodeWidthStr::width(image_placeholder)
};
Some((base, end, ph_width))
} else {
None
};
let custom = if !sentinels.is_empty() {
let base = SENTINEL_BASE as u32;
let end = base + sentinels.len() as u32;
let widths: Vec<usize> = sentinels
.iter()
.map(|s| unicode_width::UnicodeWidthStr::width(s.label.as_ref()))
.collect();
let labels: Vec<std::sync::Arc<str>> = sentinels.iter().map(|s| s.label.clone()).collect();
Some((base, end, widths, labels))
} else {
None
};
if image.is_none() && custom.is_none() {
None
} else {
Some(SentinelInfo { image, custom })
}
}