//! Thread to manage drawing in background use crate::{ globals, main_window::{MainWindow, Page}, utils, AppMessage, }; use fltk::{ app, button::Button, enums, frame::Frame, input::{Input, MultilineInput}, menu, misc::Spinner, prelude::*, valuator::Slider, }; use std::{ fs, path::{Path, PathBuf}, sync::{mpsc, Arc, RwLock}, }; use crate::utils::{ImageContainer, ImageProperties}; #[derive(Debug, Clone)] pub(crate) enum DrawMessage { /// Open file or cropped file Open, /// Load file with specific cropped size ChangeCrop((f64, f64)), /// Recalculate and draw on buffer image in Container Recalc, /// Flush buffer to u8 vector present in main, to draw on screen Flush, /// Save to file Save, /// Clone file Clone, /// Delete file Delete, } pub(crate) fn spawn_image_thread( reciver: mpsc::Receiver, app_sender: app::Sender, properties: Arc>, main_win: &MainWindow, ) { let mut win = main_win.win.clone(); let mut file_choice = main_win.file_choice.clone(); let mut quote = main_win.quote.clone(); let mut subquote = main_win.subquote.clone(); let mut subquote2 = main_win.subquote2.clone(); let mut tag = main_win.tag.clone(); let mut tag2 = main_win.tag2.clone(); let mut layer_rgb = main_win.layer_rgb.clone(); let mut layer_alpha = main_win.layer_alpha.clone(); let mut quote_position = main_win.quote_position.clone(); let mut subquote_position = main_win.subquote_position.clone(); let mut subquote2_position = main_win.subquote2_position.clone(); let mut tag_position = main_win.tag_position.clone(); let mut tag2_position = main_win.tag2_position.clone(); let mut quote_position_slider = main_win.quote_position_slider.clone(); let mut subquote_position_slider = main_win.subquote_position_slider.clone(); let mut subquote2_position_slider = main_win.subquote2_position_slider.clone(); let mut tag_position_slider = main_win.tag_position_slider.clone(); let mut tag2_position_slider = main_win.tag2_position_slider.clone(); let mut page = main_win.page.clone(); let mut status = main_win.status.clone(); let mut count = main_win.count.clone(); let mut dimension = main_win.dimension.clone(); let images_path = Arc::clone(&main_win.images_path); let mut _container: Option = None; std::thread::spawn(move || loop { if let Ok(val) = reciver.recv() { match val { DrawMessage::Open => { status.set_label("Loading..."); load_image( &mut file_choice, Arc::clone(&images_path), None, &mut quote, &mut subquote, &mut subquote2, &mut tag, &mut tag2, &mut layer_rgb, &mut layer_alpha, &mut quote_position, &mut subquote_position, &mut subquote2_position, &mut tag_position, &mut tag2_position, &mut quote_position_slider, &mut subquote_position_slider, &mut subquote2_position_slider, &mut tag_position_slider, &mut tag2_position_slider, &mut page, &mut count, &mut dimension, &app_sender, &properties, &mut _container, ); status.set_label(""); status.set_label(""); } DrawMessage::ChangeCrop((x, y)) => { status.set_label("Loading..."); load_image( &mut file_choice, Arc::clone(&images_path), Some((x, y)), &mut quote, &mut subquote, &mut subquote2, &mut tag, &mut tag2, &mut layer_rgb, &mut layer_alpha, &mut quote_position, &mut subquote_position, &mut subquote2_position, &mut tag_position, &mut tag2_position, &mut quote_position_slider, &mut subquote_position_slider, &mut subquote2_position_slider, &mut tag_position_slider, &mut tag2_position_slider, &mut page, &mut count, &mut dimension, &app_sender, &properties, &mut _container, ); status.set_label(""); } DrawMessage::Recalc => { if let Some(cont) = &mut _container { cont.recalc(); } } DrawMessage::Flush => { flush_buffer(&app_sender, &mut _container); } DrawMessage::Save => { if let Some(cont) = &mut _container { status.set_label("Saving..."); win.deactivate(); cont.save(); win.activate(); status.set_label(""); } } DrawMessage::Clone => { if let Some(cont) = &mut _container { status.set_label("Cloning..."); win.deactivate(); if let Some(path) = cont.clone_img() { let idx = file_choice.value(); let mut imgs = images_path.write().unwrap(); imgs.insert(idx as usize, path.clone()); file_choice.insert( idx, path.file_name().unwrap().to_str().unwrap(), enums::Shortcut::None, menu::MenuFlag::Normal, |a| a.do_callback(), ); file_choice.set_value(idx); } status.set_label(""); win.activate(); } } DrawMessage::Delete => { if let Some(cont) = &mut _container { status.set_label("Deleting..."); win.deactivate(); cont.delete(); let mut imgs = images_path.write().unwrap(); imgs.remove(file_choice.value() as usize); file_choice.remove(file_choice.value()); if file_choice.value() != imgs.len() as i32 { file_choice.set_value(file_choice.value()); } else { file_choice.set_value(file_choice.value() - 1); } status.set_label(""); win.activate(); } } } } }); } fn load_image( file_choice: &mut menu::Choice, images_path: Arc>>, crop: Option<(f64, f64)>, quote: &mut MultilineInput, subquote: &mut MultilineInput, subquote2: &mut MultilineInput, tag: &mut Input, tag2: &mut Input, layer_rgb: &mut Button, layer_alpha: &mut Spinner, quote_position: &mut Spinner, subquote_position: &mut Spinner, subquote2_position: &mut Spinner, tag_position: &mut Spinner, tag2_position: &mut Spinner, quote_position_slider: &mut Slider, subquote_position_slider: &mut Slider, subquote2_position_slider: &mut Slider, tag_position_slider: &mut Slider, tag2_position_slider: &mut Slider, page: &mut Page, count: &mut Frame, dimension: &mut Frame, app_sender: &app::Sender, properties: &Arc>, container: &mut Option, ) { let imgs = images_path.read().unwrap(); if imgs.len() == 0 { *container = None; flush_buffer(app_sender, container); return; } count.set_label(&format!("[{}/{}]", file_choice.value() + 1, imgs.len())); let file = imgs.get(file_choice.value() as usize).unwrap(); *container = Some(ImageContainer::new(&file, Arc::clone(properties))); if let Some(cont) = container { let file = Path::new(&file); let conf = file.with_extension("conf"); let properties = Arc::clone(&cont.properties); let mut use_defaults = true; if conf.exists() { let mut prop = properties.write().unwrap(); let read = fs::read_to_string(&conf).unwrap(); if let Ok(saved_prop) = serde_json::from_str::(&read) { utils::set_color_btn_rgba(saved_prop.rgba, layer_rgb); layer_alpha.set_value(saved_prop.rgba[3] as f64); quote.set_value(&saved_prop.quote); subquote.set_value(&saved_prop.subquote); subquote2.set_value(&saved_prop.subquote2); tag.set_value(&saved_prop.tag); tag2.set_value(&saved_prop.tag2); quote_position.set_range(0.0, prop.original_dimension.1); quote_position.set_value(saved_prop.quote_position); subquote_position.set_range(0.0, prop.original_dimension.1); subquote_position.set_value(saved_prop.subquote_position); subquote2_position.set_range(0.0, prop.original_dimension.1); subquote2_position.set_value(saved_prop.subquote2_position); tag_position.set_range(0.0, prop.original_dimension.1); tag_position.set_value(saved_prop.tag_position); tag2_position.set_range(0.0, prop.original_dimension.1); tag2_position.set_value(saved_prop.tag2_position); quote_position_slider.set_range(0.0, prop.original_dimension.1); subquote_position_slider.set_range(0.0, prop.original_dimension.1); subquote2_position_slider.set_range(0.0, prop.original_dimension.1); quote_position_slider.set_value(saved_prop.quote_position); subquote_position_slider.set_value(saved_prop.subquote_position); subquote2_position_slider.set_value(saved_prop.subquote2_position); tag_position_slider.set_range(0.0, prop.original_dimension.1); tag_position_slider.set_value(saved_prop.tag_position); tag2_position_slider.set_range(0.0, prop.original_dimension.1); tag2_position_slider.set_value(saved_prop.tag2_position); dimension.set_label(&format!( "[{}x{}]", prop.original_dimension.0, prop.original_dimension.1 )); prop.quote = saved_prop.quote; prop.subquote = saved_prop.subquote; prop.subquote2 = saved_prop.subquote2; prop.tag = saved_prop.tag; prop.tag2 = saved_prop.tag2; prop.quote_position = saved_prop.quote_position; prop.subquote_position = saved_prop.subquote_position; prop.subquote2_position = saved_prop.subquote2_position; prop.tag_position = saved_prop.tag_position; prop.tag2_position = saved_prop.tag2_position; prop.rgba = saved_prop.rgba; prop.is_saved = true; use_defaults = false; drop(prop); match crop { Some((x, y)) => cont.apply_crop_pos(x, y), None => match saved_prop.crop_position { Some((x, y)) => cont.apply_crop_pos(x, y), None => cont.apply_crop(), }, } } } if use_defaults { let mut prop = properties.write().unwrap(); if crop.is_none() { quote.set_value(""); prop.quote = "".to_owned(); subquote.set_value(""); prop.subquote = "".to_owned(); subquote2.set_value(""); prop.subquote2 = "".to_owned(); } quote_position.set_range(0.0, prop.original_dimension.1); quote_position.set_value(prop.quote_position); subquote_position.set_range(0.0, prop.original_dimension.1); subquote_position.set_value(prop.subquote_position); subquote2_position.set_range(0.0, prop.original_dimension.1); subquote2_position.set_value(prop.subquote2_position); tag_position.set_range(0.0, prop.original_dimension.1); tag_position.set_value(prop.tag_position); tag2_position.set_range(0.0, prop.original_dimension.1); tag2_position.set_value(prop.tag2_position); quote_position_slider.set_range(0.0, prop.original_dimension.1); quote_position_slider.set_value(prop.quote_position); subquote_position_slider.set_range(0.0, prop.original_dimension.1); subquote_position_slider.set_value(prop.subquote_position); subquote2_position_slider.set_range(0.0, prop.original_dimension.1); subquote2_position_slider.set_value(prop.subquote2_position); tag_position_slider.set_range(0.0, prop.original_dimension.1); tag_position_slider.set_value(prop.tag_position); tag2_position_slider.set_range(0.0, prop.original_dimension.1); tag2_position_slider.set_value(prop.tag2_position); let glob = &globals::CONFIG.read().unwrap(); utils::set_color_btn_rgba(glob.color_layer, layer_rgb); layer_alpha.set_value(glob.color_layer[3] as f64); prop.rgba = glob.color_layer; drop(glob); match crop { Some((x, y)) => { prop.is_saved = false; drop(prop); cont.apply_crop_pos(x, y); } None => { prop.is_saved = true; drop(prop); cont.apply_crop(); } } } cont.apply_scale(); let prop = properties.read().unwrap(); let (width, height) = prop.dimension; page.col_flex.set_size(&page.image, height as i32); page.row_flex.set_size(&page.col_flex, width as i32); page.col_flex.recalc(); page.row_flex.recalc(); cont.recalc(); } flush_buffer(&app_sender, &container); } fn flush_buffer(app_sender: &app::Sender, container: &Option) { match container { Some(cont) => { app_sender.send(AppMessage::RedrawMainWindowImage(Some( cont.buffer.as_rgb8().unwrap().as_raw().to_owned(), ))); } None => { app_sender.send(AppMessage::RedrawMainWindowImage(None)); } } }