/* This file is part of Post Maker. Post Maker is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Post Maker is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Post Maker. If not, see */ //! Main window where you do all editing use crate::about_window::About; use crate::crop_window::CropWindow; use crate::draw_thread::*; use crate::result_ext::ResultExt; use crate::utils; use crate::utils::ImageInfo; use crate::utils::ImageType; use crate::utils::ImageProperties; use crate::{config_window::ConfigWindow, globals}; use fltk::{ app, button::Button, dialog, dialog::NativeFileChooser, draw as dr, enums, enums::Shortcut, frame::Frame, group::Flex, image::SvgImage, input::{Input, MultilineInput}, menu, misc::Spinner, prelude::*, valuator::{Slider, SliderType}, window::Window, }; use std::ffi::OsStr; use std::path::PathBuf; use std::sync::{mpsc, RwLock}; use std::{ fs, sync::Arc}; pub(crate) struct MainWindow { pub(crate) win: Window, pub(crate) menubar: menu::SysMenuBar, pub(crate) back_btn: Button, pub(crate) next_btn: Button, pub(crate) save_btn: Button, /// To choose the file which is being edited in directory pub(crate) file_choice: menu::Choice, pub(crate) quote: MultilineInput, pub(crate) subquote: MultilineInput, pub(crate) subquote2: MultilineInput, pub(crate) tag: Input, pub(crate) tag2: Input, /// RGB value of top translucent layer pub(crate) translucent_layer_rgb: Button, /// opacity value of top translucent layer pub(crate) translucent_layer_alpha: Spinner, pub(crate) quote_position: Spinner, pub(crate) subquote_position: Spinner, pub(crate) subquote2_position: Spinner, pub(crate) tag_position: Spinner, pub(crate) tag2_position: Spinner, pub(crate) quote_position_slider: Slider, pub(crate) subquote_position_slider: Slider, pub(crate) subquote2_position_slider: Slider, pub(crate) tag_position_slider: Slider, pub(crate) tag2_position_slider: Slider, pub(crate) reset_translucent_layer_btn: Button, pub(crate) reset_quote_position_btn: Button, pub(crate) reset_subquote_position_btn: Button, pub(crate) reset_subquote2_position_btn: Button, pub(crate) reset_tag_position_btn: Button, pub(crate) reset_tag2_position_btn: Button, pub(crate) reset_file_choice: Button, pub(crate) crop_btn: Button, pub(crate) clone_btn: Button, pub(crate) delete_btn: Button, pub(crate) status: Frame, pub(crate) count: Frame, pub(crate) dimension: Frame, pub(crate) page: Page, pub(crate) images_list: Arc>>, pub(crate) draw_buff: Arc>>>, pub(crate) properties: Arc>, pub(crate) sender: mpsc::Sender, } /// Contains the elements to draw page in mid of workspace #[derive(Clone)] pub(crate) struct Page { pub(crate) image: Frame, pub(crate) row_flex: Flex, pub(crate) col_flex: Flex, } impl MainWindow { pub(crate) fn new( sender: app::Sender, draw_buff: Arc>>>, ) -> Self { let mut win = Window::new(0, 0, 1100, 700, "Post Maker").center_screen(); win.set_icon(Some( SvgImage::from_data(globals::ICON.to_str().unwrap()).unwrap(), )); let reload_image = SvgImage::from_data(globals::RELOAD_ICON.to_str().unwrap()).unwrap(); let mut main_flex = Flex::default().size_of_parent().column(); let menubar = menu::SysMenuBar::default(); main_flex.set_size(&menubar, 30); let mut toolbar_flex = Flex::default().row(); let back_btn = Button::default().with_label("Back"); toolbar_flex.set_size(&back_btn, 50); let next_btn = Button::default().with_label("Next"); toolbar_flex.set_size(&next_btn, 50); let save_btn = Button::default().with_label("Save"); toolbar_flex.set_size(&save_btn, 50); let file_choice = menu::Choice::default(); let mut reset_file_choice = Button::default(); reset_file_choice.set_image(Some(reload_image.clone())); toolbar_flex.set_size(&reset_file_choice, 30); toolbar_flex.end(); main_flex.set_size(&toolbar_flex, 30); let mut workspace_flex = Flex::default().row(); // Controls Left let mut left_controls_flex = Flex::default().column(); left_controls_flex.set_size( &Frame::default() .with_label("Quote:") .with_align(enums::Align::Left | enums::Align::Inside), 25, ); let quote = MultilineInput::default(); left_controls_flex.set_size("e, 70); left_controls_flex.set_size( &Frame::default() .with_label("Subquote:") .with_align(enums::Align::Left | enums::Align::Inside), 25, ); let subquote = MultilineInput::default(); left_controls_flex.set_size(&subquote, 70); left_controls_flex.set_size( &Frame::default() .with_label("Subquote 2:") .with_align(enums::Align::Left | enums::Align::Inside), 25, ); let subquote2 = MultilineInput::default(); left_controls_flex.set_size(&subquote2, 70); left_controls_flex.set_size( &Frame::default() .with_label("Tag:") .with_align(enums::Align::Left | enums::Align::Inside), 25, ); let tag = Input::default(); left_controls_flex.set_size(&tag, 30); left_controls_flex.set_size( &Frame::default() .with_label("Tag 2:") .with_align(enums::Align::Left | enums::Align::Inside), 25, ); let tag2 = Input::default(); left_controls_flex.set_size(&tag2, 30); let mut actions_flex = Flex::default().row(); Frame::default(); let delete_btn = Button::default().with_label("Delete"); actions_flex.set_size(&delete_btn, 100); let clone_btn = Button::default().with_label("Clone"); actions_flex.set_size(&clone_btn, 100); let crop_btn = Button::default().with_label("Crop"); actions_flex.set_size(&crop_btn, 100); Frame::default(); actions_flex.end(); left_controls_flex.set_size(&actions_flex, 30); Frame::default(); let info_flex = Flex::default().row(); let count = Frame::default().with_align(enums::Align::Left | enums::Align::Inside); let status = Frame::default(); let dimension = Frame::default().with_align(enums::Align::Right | enums::Align::Inside); info_flex.end(); left_controls_flex.set_size(&info_flex, 30); left_controls_flex.end(); workspace_flex.set_size(&left_controls_flex, 310); // Page let mut center_row_flex = Flex::default().row(); Frame::default(); let mut center_col_flex = Flex::default().column(); Frame::default(); let img_view = Frame::default(); center_col_flex.set_size(&img_view, 500); Frame::default(); center_col_flex.end(); Frame::default(); center_row_flex.set_size(¢er_col_flex, 400); center_row_flex.end(); // Controls right let mut right_controls_flex = Flex::default().column(); let mut translucent_layer_head_flex = Flex::default().row(); Frame::default() .with_label("Translucent Layer:") .with_align(enums::Align::Left | enums::Align::Inside); let mut reset_darklayer_btn = Button::default(); reset_darklayer_btn.set_image(Some(reload_image.clone())); translucent_layer_head_flex.set_size(&reset_darklayer_btn, 30); translucent_layer_head_flex.end(); right_controls_flex.set_size(&translucent_layer_head_flex, 30); let mut translucent_layer_flex = Flex::default().row(); translucent_layer_flex.set_pad(2); translucent_layer_flex.set_size(&Frame::default().with_label("Colour"), 50); let mut translucent_layer_rgb = Button::default(); translucent_layer_rgb.set_frame(enums::FrameType::BorderBox); translucent_layer_flex.set_size(&Frame::default().with_label("Alpha"), 50); let mut translucent_layer_alpha = Spinner::default(); translucent_layer_alpha.set_range(0.0, 255.0); translucent_layer_flex.end(); right_controls_flex.set_size(&translucent_layer_flex, 30); let mut quote_position_flex = Flex::default().row(); quote_position_flex.set_size( &Frame::default() .with_label("Quote Position") .with_align(enums::Align::Left | enums::Align::Inside), 140, ); let quote_position = fltk::misc::Spinner::default(); let mut reset_quote_position_btn = Button::default(); reset_quote_position_btn.set_image(Some(reload_image.clone())); quote_position_flex.set_size(&reset_quote_position_btn, 30); quote_position_flex.end(); right_controls_flex.set_size("e_position_flex, 30); let mut quote_position_slider = Slider::default().with_type(SliderType::HorizontalNice); quote_position_slider.set_step(1.0, 1); quote_position_slider.set_frame(enums::FrameType::NoBox); right_controls_flex.set_size("e_position_slider, 30); let mut subquote_position_flex = Flex::default().row(); subquote_position_flex.set_size( &Frame::default() .with_label("Subquote Position") .with_align(enums::Align::Left | enums::Align::Inside), 140, ); let subquote_position = fltk::misc::Spinner::default(); let mut reset_subquote_position_btn = Button::default(); reset_subquote_position_btn.set_image(Some(reload_image.clone())); subquote_position_flex.set_size(&reset_subquote_position_btn, 30); subquote_position_flex.end(); right_controls_flex.set_size(&subquote_position_flex, 30); let mut subquote_position_slider = Slider::default().with_type(SliderType::HorizontalNice); subquote_position_slider.set_step(1.0, 1); subquote_position_slider.set_frame(enums::FrameType::NoBox); right_controls_flex.set_size(&subquote_position_slider, 30); let mut subquote2_position_flex = Flex::default().row(); subquote2_position_flex.set_size( &Frame::default() .with_label("Subquote 2 Position") .with_align(enums::Align::Left | enums::Align::Inside), 140, ); let subquote2_position = fltk::misc::Spinner::default(); let mut reset_subquote2_position_btn = Button::default(); reset_subquote2_position_btn.set_image(Some(reload_image.clone())); subquote2_position_flex.set_size(&reset_subquote2_position_btn, 30); subquote2_position_flex.end(); right_controls_flex.set_size(&subquote2_position_flex, 30); let mut subquote2_position_slider = Slider::default().with_type(SliderType::HorizontalNice); subquote2_position_slider.set_step(1.0, 1); subquote2_position_slider.set_frame(enums::FrameType::NoBox); right_controls_flex.set_size(&subquote2_position_slider, 30); let mut tag_position_flex = Flex::default().row(); tag_position_flex.set_size( &Frame::default() .with_label("Tag Position") .with_align(enums::Align::Left | enums::Align::Inside), 140, ); let tag_position = fltk::misc::Spinner::default(); let mut reset_tag_position_btn = Button::default(); reset_tag_position_btn.set_image(Some(reload_image.clone())); tag_position_flex.set_size(&reset_tag_position_btn, 30); tag_position_flex.end(); right_controls_flex.set_size(&tag_position_flex, 30); let mut tag_position_slider = Slider::default().with_type(SliderType::HorizontalNice); tag_position_slider.set_step(1.0, 1); tag_position_slider.set_frame(enums::FrameType::NoBox); right_controls_flex.set_size(&tag_position_slider, 30); let mut tag2_position_flex = Flex::default().row(); tag2_position_flex.set_size( &Frame::default() .with_label("Tag 2 Position") .with_align(enums::Align::Left | enums::Align::Inside), 140, ); let tag2_position = fltk::misc::Spinner::default(); let mut reset_tag2_position_btn = Button::default(); reset_tag2_position_btn.set_image(Some(reload_image.clone())); tag2_position_flex.set_size(&reset_tag2_position_btn, 30); tag2_position_flex.end(); right_controls_flex.set_size(&tag2_position_flex, 30); let mut tag2_position_slider = Slider::default().with_type(SliderType::HorizontalNice); tag2_position_slider.set_step(1.0, 1); tag2_position_slider.set_frame(enums::FrameType::NoBox); right_controls_flex.set_size(&tag2_position_slider, 30); Frame::default(); right_controls_flex.end(); workspace_flex.set_size(&right_controls_flex, 270); workspace_flex.end(); main_flex.end(); win.end(); win.make_resizable(true); win.show(); let properties = Arc::new(RwLock::new(ImageProperties::default())); let (rx, tx) = std::sync::mpsc::channel(); let mut main_win = Self { win, menubar, back_btn, next_btn, save_btn, file_choice, quote, subquote, subquote2, tag, tag2, translucent_layer_rgb, translucent_layer_alpha, quote_position, subquote_position, subquote2_position, tag_position, tag2_position, quote_position_slider, subquote_position_slider, subquote2_position_slider, tag_position_slider, tag2_position_slider, reset_translucent_layer_btn: reset_darklayer_btn, reset_quote_position_btn, reset_subquote_position_btn, reset_subquote2_position_btn, reset_tag_position_btn, reset_tag2_position_btn, reset_file_choice, crop_btn, clone_btn, delete_btn, status, count, dimension, images_list: Arc::new(RwLock::new(vec![])), draw_buff, properties: Arc::clone(&properties), page: Page { image: img_view, row_flex: center_row_flex, col_flex: center_col_flex, }, sender: rx, }; spawn_image_thread(tx, sender, Arc::clone(&properties), &main_win); main_win.menu(); main_win.draw(); main_win.events(); main_win } /// Set menubar in window fn menu(&mut self) { let mut file_choice = self.file_choice.clone(); let sender = self.sender.clone(); let imgs = Arc::clone(&self.images_list); let mut win = self.win.clone(); self.menubar.add( "&File/Open Folder...\t", Shortcut::Ctrl | 'o', menu::MenuFlag::Normal, move |_| { let mut chooser = NativeFileChooser::new(fltk::dialog::FileDialogType::BrowseDir); chooser.set_option(fltk::dialog::FileDialogOptions::NewFolder); chooser.show(); let path = chooser.filename(); let path = fs::canonicalize(&path).unwrap_or(path); if !path.exists() { return; } let expost_dir = path.join("export"); if !expost_dir.exists() { if let Err(e) = fs::create_dir(expost_dir) { Result::<(), _>::Err(e).warn_log("Failed to create export folder!"); return; } } win.set_label(&format!("{} - Post Maker", path.file_name().unwrap_or(OsStr::new("Unknown")).to_string_lossy())); load_dir(&path, Arc::clone(&imgs), &mut file_choice, &sender); }, ); let sender = self.sender.clone(); let properties = Arc::clone(&self.properties); self.menubar.add( "&File/Save Image...\t", Shortcut::Ctrl | 's', menu::MenuFlag::Normal, move |_| { let mut prop = properties.write().unwrap(); prop.is_saved = true; sender.send(DrawMessage::Save).unwrap(); }, ); let sender = self.sender.clone(); self.menubar.add( "&Actions/Show Details...\t", Shortcut::None, menu::MenuFlag::Normal, move |_| { sender.send(DrawMessage::ShowImagesDetails).unwrap(); }, ); let mut config_window = ConfigWindow::new(); let sender = self.sender.clone(); let mut image = self.page.image.clone(); self.menubar.add( "&Edit/Configure...\t", Shortcut::None, menu::MenuFlag::Normal, move |_| { if config_window.show() { sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); } }, ); let mut about_win = About::new(); self.menubar.add( "&Help/About...\t", Shortcut::None, menu::MenuFlag::Normal, move |_| { about_win.show(); }, ); } /// Set drawing in window fn draw(&mut self) { let buff = Arc::clone(&self.draw_buff); let properties = Arc::clone(&self.properties); self.page.image.draw(move |f| { let (width, height) = properties.read().unwrap().dimension; if let Some(image) = &*buff.read().unwrap() { dr::draw_image( &image, f.x(), f.y(), width as i32, height as i32, enums::ColorDepth::Rgb8, ) .unwrap(); } }) } /// Set callbacks of elements fn events(&mut self) { // Resest Button for FileChoice let mut file_choice = self.file_choice.clone(); let sender = self.sender.clone(); let imgs = Arc::clone(&self.images_list); self.reset_file_choice.set_callback(move |_| { let path = match imgs.read().unwrap().first() { Some(image_info) => image_info.path.parent().unwrap().to_path_buf(), None => return, }; load_dir(&path, Arc::clone(&imgs), &mut file_choice, &sender); }); // Reset Button for Translucent Layer let mut layer_rgb = self.translucent_layer_rgb.clone(); let mut layer_alpha = self.translucent_layer_alpha.clone(); let mut image = self.page.image.clone(); let sender = self.sender.clone(); let properties = Arc::clone(&self.properties); self.reset_translucent_layer_btn.set_callback(move |_| { let mut prop = properties.write().unwrap(); let color = globals::CONFIG.read().unwrap().color_layer; prop.translucent_layer_color = color; prop.is_saved = false; utils::set_color_btn_rgba(color, &mut layer_rgb); layer_alpha.set_value(color[3] as f64); sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Reset Button for Quote Input let mut quote_position = self.quote_position.clone(); let mut quote_position_slider = self.quote_position_slider.clone(); let mut image = self.page.image.clone(); let sender = self.sender.clone(); let properties = Arc::clone(&self.properties); self.reset_quote_position_btn.set_callback(move |_| { let mut prop = properties.write().unwrap(); let height = prop.original_dimension.1; let pos = height * globals::CONFIG.read().unwrap().quote_position_ratio; prop.quote_position = pos; prop.is_saved = false; quote_position.set_value(pos); quote_position_slider.set_value(pos); sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Reset Button for Subquote Input let mut subquote_position = self.subquote_position.clone(); let mut subquote_position_slider = self.subquote_position_slider.clone(); let mut image = self.page.image.clone(); let sender = self.sender.clone(); let properties = Arc::clone(&self.properties); self.reset_subquote_position_btn.set_callback(move |_| { let mut prop = properties.write().unwrap(); let height = prop.original_dimension.1; let pos = height * globals::CONFIG.read().unwrap().subquote_position_ratio; prop.subquote_position = pos; prop.is_saved = false; subquote_position.set_value(pos); subquote_position_slider.set_value(pos); sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Reset Button for Subquotes2 Input let mut subquote2_position = self.subquote2_position.clone(); let mut subquote2_position_slider = self.subquote2_position_slider.clone(); let mut image = self.page.image.clone(); let sender = self.sender.clone(); let properties = Arc::clone(&self.properties); self.reset_subquote2_position_btn.set_callback(move |_| { let mut prop = properties.write().unwrap(); let height = prop.original_dimension.1; let pos = height * globals::CONFIG.read().unwrap().subquote2_position_ratio; prop.subquote2_position = pos; prop.is_saved = false; subquote2_position.set_value(pos); subquote2_position_slider.set_value(pos); sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Reset Button for Tag Input let mut tag_position = self.tag_position.clone(); let mut tag_position_slider = self.tag_position_slider.clone(); let mut image = self.page.image.clone(); let sender = self.sender.clone(); let properties = Arc::clone(&self.properties); self.reset_tag_position_btn.set_callback(move |_| { let mut prop = properties.write().unwrap(); let height = prop.original_dimension.1; let pos = height * globals::CONFIG.read().unwrap().tag_position_ratio; prop.tag_position = pos; prop.is_saved = false; tag_position.set_value(pos); tag_position_slider.set_value(pos); sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Reset Button for Tag2 Input let mut tag2_position = self.tag2_position.clone(); let mut tag2_position_slider = self.tag2_position_slider.clone(); let mut image = self.page.image.clone(); let sender = self.sender.clone(); let properties = Arc::clone(&self.properties); self.reset_tag2_position_btn.set_callback(move |_| { let mut prop = properties.write().unwrap(); let height = prop.original_dimension.1; let pos = height * globals::CONFIG.read().unwrap().tag2_position_ratio; prop.tag2_position = pos; prop.is_saved = false; tag2_position.set_value(pos); tag2_position_slider.set_value(pos); sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Save Button let sender = self.sender.clone(); let properties = Arc::clone(&self.properties); self.save_btn.set_callback(move |_| { let mut prop = properties.write().unwrap(); prop.is_saved = true; sender.send(DrawMessage::Save).unwrap() }); // Clone Button let mut image = self.page.image.clone(); let mut file_choice = self.file_choice.clone(); let sender = self.sender.clone(); self.clone_btn.set_callback(move |_| { let ch = dialog::choice_default("Do you want to clone??", "Yes", "No", ""); if ch == 0 { sender.send(DrawMessage::Clone).unwrap(); sender.send(DrawMessage::Open).unwrap(); image.redraw(); file_choice.redraw(); } }); // Delete Button let mut image = self.page.image.clone(); let mut file_choice = self.file_choice.clone(); let sender = self.sender.clone(); self.delete_btn.set_callback(move |_| { let ch = dialog::choice_default("Do you want to delete??", "Yes", "No", ""); if ch == 0 { sender.send(DrawMessage::Delete).unwrap(); sender.send(DrawMessage::Open).unwrap(); image.redraw(); file_choice.redraw(); } }); // Crop Button let properties = Arc::clone(&self.properties); let mut crop_win = CropWindow::new(); let sender = self.sender.clone(); self.crop_btn.set_callback(move |_| { let mut prop = properties.write().unwrap(); if let Some(image_info) = &prop.image_info { if let Some((x, y)) = crop_win.load_to_crop(&image_info, prop.crop_position) { sender.send(DrawMessage::ChangeCrop((x, y))).unwrap(); prop.is_saved = false; } } }); // Next Image Button let mut file_choice = self.file_choice.clone(); let sender = self.sender.clone(); let properties = Arc::clone(&self.properties); self.next_btn.set_callback(move |_| { let prop = properties.read().unwrap(); if !prop.is_saved { let save = fltk::dialog::choice_default("Save?", "yes", "no", "cancel"); match save { 0 => sender.send(DrawMessage::Save).unwrap(), 1 => {} _ => return, } } if file_choice.value() == file_choice.size() - 2 { file_choice.set_value(0); } else { file_choice.set_value(file_choice.value() + 1); } sender.send(DrawMessage::Open).unwrap(); }); // Back Image Button let mut file_choice = self.file_choice.clone(); let sender = self.sender.clone(); let properties = Arc::clone(&self.properties); self.back_btn.set_callback(move |_| { let prop = properties.read().unwrap(); if !prop.is_saved { let save = fltk::dialog::choice_default("Save?", "yes", "no", "cancel"); match save { 0 => sender.send(DrawMessage::Save).unwrap(), 1 => {} _ => return, } } if file_choice.value() == 0 { file_choice.set_value(file_choice.size() - 2); } else { file_choice.set_value(file_choice.value() - 1); } sender.send(DrawMessage::Open).unwrap(); }); // File Choice let sender = self.sender.clone(); let properties = Arc::clone(&self.properties); self.file_choice.set_callback(move |_| { let prop = properties.read().unwrap(); if !prop.is_saved { let save = fltk::dialog::choice_default("Save?", "yes", "no", "cancel"); match save { 0 => sender.send(DrawMessage::Save).unwrap(), 1 => {} _ => return, } } sender.send(DrawMessage::Open).unwrap(); }); // Quote Input let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); self.quote.handle(move |f, ev| { if ev == enums::Event::KeyUp { let mut prop = properties.write().unwrap(); prop.quote = f.value(); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); } true }); // Subquote Input let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); self.subquote.handle(move |f, ev| { if ev == enums::Event::KeyUp { let mut prop = properties.write().unwrap(); prop.subquote = f.value(); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); } true }); // Subquote2 Input let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); self.subquote2.handle(move |f, ev| { if ev == enums::Event::KeyUp { let mut prop = properties.write().unwrap(); prop.subquote2 = f.value(); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); } true }); // Tag Input let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); self.tag.handle(move |f, ev| { if ev == enums::Event::KeyUp { let mut prop = properties.write().unwrap(); prop.tag = f.value(); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); } true }); // Tag2 Input let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); self.tag2.handle(move |f, ev| { if ev == enums::Event::KeyUp { let mut prop = properties.write().unwrap(); prop.tag2 = f.value(); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); } true }); // Quote Position Input let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); let mut quote_position_slider = self.quote_position_slider.clone(); self.quote_position.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.quote_position = f.value(); quote_position_slider.set_value(f.value()); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Quote Position Slider let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); let mut quote_position = self.quote_position.clone(); self.quote_position_slider.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.quote_position = f.value(); quote_position.set_value(f.value()); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Subquote Position Input let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); let mut subquote_position_slider = self.subquote_position_slider.clone(); self.subquote_position.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.subquote_position = f.value(); subquote_position_slider.set_value(f.value()); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Subquote Position Slider let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); let mut subquote_position = self.subquote_position.clone(); self.subquote_position_slider.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.subquote_position = f.value(); subquote_position.set_value(f.value()); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Subquote2 Position Input let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); let mut subquote2_position_slider = self.subquote2_position_slider.clone(); self.subquote2_position.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.subquote2_position = f.value(); subquote2_position_slider.set_value(f.value()); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Subquote2 Position Slider let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); let mut subquote2_position = self.subquote2_position.clone(); self.subquote2_position_slider.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.subquote2_position = f.value(); subquote2_position.set_value(f.value()); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Tag Position Input let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); let mut tag_position_slider = self.tag_position_slider.clone(); self.tag_position.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.tag_position = f.value(); tag_position_slider.set_value(f.value()); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Tag Position Slider let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); let mut tag_position = self.tag_position.clone(); self.tag_position_slider.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.tag_position = f.value(); tag_position.set_value(f.value()); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Tag2 Position Input let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); let mut tag2_position_slider = self.tag2_position_slider.clone(); self.tag2_position.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.tag2_position = f.value(); tag2_position_slider.set_value(f.value()); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Tag2 Position Slider let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); let mut tag2_position = self.tag2_position.clone(); self.tag2_position_slider.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.tag2_position = f.value(); tag2_position.set_value(f.value()); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Translucent Layer RGB let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); self.translucent_layer_rgb.set_callback(move |mut f| { let mut prop = properties.write().unwrap(); let (r, g, b) = dialog::color_chooser_with_default( "Pick a colour", dialog::ColorMode::Byte, ( prop.translucent_layer_color[0], prop.translucent_layer_color[1], prop.translucent_layer_color[2], ), ); prop.translucent_layer_color = [r, g, b, prop.translucent_layer_color[3]]; utils::set_color_btn_rgba(prop.translucent_layer_color, &mut f); f.redraw(); prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); // Translucent Layer Opacity let mut image = self.page.image.clone(); let properties = Arc::clone(&self.properties); let sender = self.sender.clone(); self.translucent_layer_alpha.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.translucent_layer_color[3] = f.value() as u8; prop.is_saved = false; sender.send(DrawMessage::RedrawToBuffer).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); }); } } /// Load all iamges in a directory fn load_dir( path: &PathBuf, imgs: Arc>>, file_choice: &mut menu::Choice, sender: &mpsc::Sender, ) { let mut files = fs::read_dir(path) .unwrap() .map(|r| r.unwrap()) .collect::>(); files.sort_by_key(|i| i.file_name()); let mut text = String::new(); let mut imgs_b = imgs.write().unwrap(); *imgs_b = vec![]; for file in files { let path = file.path(); if let Ok(Some(ty)) = infer::get_from_path(&path) { let mime = ty.mime_type(); match ImageType::from_mime(mime) { ImageType::None => (), _ => { text = format!("{}|{}", text, path.file_name().unwrap().to_str().unwrap()); imgs_b.push(ImageInfo { path, image_type: ImageType::from_mime(mime) }); } } } } if text.len() == 0 { return; } file_choice.clear(); file_choice.add_choice(&text[1..]); file_choice.set_value(0); sender.send(DrawMessage::Open).unwrap(); }