mod composer;
mod note;
mod reply;
mod sequence;
use super::*;
pub use composer::*;
pub use note::*;
pub use reply::*;
pub use sequence::*;
#[cfg(feature = "gui")]
use crate::link::*;
#[allow(unused_imports)]
use crossbeam_channel::Sender;
#[cfg(feature = "gui")]
use mkutil::tempfile::NamedTempFile;
#[cfg(all(feature = "gui", not(feature = "image_processing")))]
const IMAGE_LABEL: &str = "img .";
#[derive(Debug, Clone, PartialEq)]
pub enum ImageUploaded {
Success(u8),
NoTask,
}
#[derive(Debug)]
struct UserImage<T>
where
T: AsRef<Path>,
{
selecting: Option<T>,
uploaded: Option<AnyResult<PathBuf>>,
}
impl<T> UserImage<T>
where
T: AsRef<Path>,
{
fn selecting(img: T) -> Self {
Self {
selecting: Some(img),
uploaded: None,
}
}
fn is_selected(&self) -> bool {
self.selecting.is_some()
}
#[cfg(feature = "gui")]
fn unselect(&mut self) {
self.selecting.take();
}
fn selecting_as_ref_unwrap(&self) -> &T {
self.selecting.as_ref().unwrap()
}
fn uploaded_mut(&mut self, result: AnyResult<PathBuf>, count: &mut u8) {
if let Ok(_) = &result {
*count = *count + 1;
};
self.uploaded = Some(result);
}
#[cfg(debug_assertions)]
fn log_uploaded_unwrap(&self) {
match &self.uploaded {
Some(Ok(path)) => {
debug!("Uploaded image into {}", path.display());
}
Some(Err(e)) => {
error!(
"Failed to upload image {}: {}",
self.selecting_as_ref_unwrap().as_ref().display(),
e
);
}
None => {
unreachable!()
}
}
}
#[cfg(feature = "gui")]
fn hint_selected_ui(&mut self, ui: &mut egui::Ui) {
if let Some(img) = &self.selecting {
ui.label(format!(
"{:?}",
img.as_ref()
.file_name()
.expect("Failed to extract file name")
));
if ui.button("⊗").clicked() {
self.unselect();
};
};
}
}
#[cfg(feature = "gui")]
impl UserImage<NamedTempFile> {
fn hint_saved_ui(&mut self, ui: &mut egui::Ui, num: usize) {
if let Some(_) = self.selecting {
ui.label(format!("clipboard img #{}", num + 1));
if ui.button("🗑").clicked() {
self.unselect_and_close();
};
};
}
fn unselect_and_close(&mut self) {
if let Some(temp_file) = self.selecting.take() {
match temp_file.close() {
Ok(_) => {}
Err(e) => {
warn!("{}", e);
}
}
};
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, strum::AsRefStr)]
pub enum SequenceType {
#[default]
#[strum(serialize = "snapshot ")]
Snapshot,
#[strum(serialize = "frame ")]
Image,
}
#[derive(Debug, Clone, Default)]
pub struct ImageArray {
img_paths: Option<Vec<PathBuf>>,
#[cfg(all(feature = "image_processing", feature = "gui"))]
img_specs: Option<Vec<Option<ImageSpec>>>,
}
impl PartialEq for ImageArray {
fn eq(&self, other: &Self) -> bool {
self.img_paths == other.img_paths
}
}
impl ImageArray {
fn with_img_paths(mut self, img_paths: Option<Vec<PathBuf>>) -> Self {
self.img_paths = img_paths;
self
}
#[cfg(all(feature = "image_processing", feature = "gui"))]
fn img_specs_mut(&mut self) {
self.img_specs = self.img_paths.as_ref().map(|paths| {
paths
.iter()
.map(|i| ImageSpec::from_retained_image(i))
.collect()
});
}
#[cfg(feature = "gui")]
fn ui(&mut self, ui: &mut egui::Ui, _thumb_width: f32) {
#[cfg(feature = "image_processing")]
if let Some(specs) = self.img_specs.as_mut() {
for (path, spec) in self
.img_paths
.as_ref()
.unwrap()
.iter()
.zip(specs.iter())
.filter(|(_, spec)| spec.is_some())
{
ui.add(ImageLink::with_retained_image_actual_height(
ui.ctx(),
spec.as_ref().unwrap().inner(),
Some(path),
_thumb_width,
));
}
};
#[cfg(not(feature = "image_processing"))]
if let Some(paths) = self.img_paths.as_ref() {
for (i, path) in paths.iter().enumerate() {
ui.add(FinderLink::with_url(
Some(&format!("{}{}", IMAGE_LABEL, i + 1)),
&path,
));
}
};
}
}
#[derive(Debug, Clone, Default, PartialEq, strum::AsRefStr, strum::EnumIter)]
#[cfg_attr(feature = "persistence", derive(Serialize, Deserialize))]
pub enum EmbedLayoutPref {
#[strum(serialize = "Array")]
RowOrColumn,
#[default]
Carousel,
}
#[derive(Debug, Clone, PartialEq, strum::AsRefStr)]
pub enum ImageEmbedLayout {
RowOrColumn(ImageArray),
Carousel(InPlaceSeqDisplay),
}
impl Default for ImageEmbedLayout {
fn default() -> Self {
Self::RowOrColumn(Default::default())
}
}
impl ImageEmbedLayout {
pub(super) fn with_img_paths(self, img_paths: Option<Vec<PathBuf>>) -> Self {
match self {
Self::RowOrColumn(seq) => Self::RowOrColumn(seq.with_img_paths(img_paths)),
Self::Carousel(seq) => Self::Carousel(seq.with_img_paths(img_paths)),
}
}
pub(super) fn img_paths_as_ref(&self) -> Option<&Vec<PathBuf>> {
match self {
Self::RowOrColumn(seq) => seq.img_paths.as_ref(),
Self::Carousel(seq) => seq.inner().img_paths.as_ref(),
}
}
#[cfg(debug_assertions)]
pub(super) fn img_paths_as_mut(&mut self) -> Option<&mut Vec<PathBuf>> {
match self {
Self::RowOrColumn(seq) => seq.img_paths.as_mut(),
Self::Carousel(seq) => seq.inner_mut().img_paths.as_mut(),
}
}
}
#[cfg(all(feature = "image_processing", feature = "gui"))]
impl ImageEmbedLayout {
pub(super) fn img_specs(&self) -> Option<&Vec<Option<ImageSpec>>> {
match self {
Self::RowOrColumn(seq) => seq.img_specs.as_ref(),
Self::Carousel(seq) => seq.inner().scrub.img_specs.as_ref(),
}
}
pub(super) fn img_specs_mut(&mut self) {
match self {
Self::RowOrColumn(seq) => {
seq.img_specs_mut();
}
Self::Carousel(seq) => {
seq.inner_mut().img_specs_mut();
}
}
}
}