Compare commits

..

No commits in common. "master" and "Stable(0.2.2)" have entirely different histories.

24 changed files with 708 additions and 2294 deletions

773
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
[package] [package]
name = "post_maker" name = "post_maker"
version = "0.6.3" version = "0.2.2"
edition = "2021" edition = "2021"
description = "Post Maker helps you to make post for instagram and other social media apps easily and in massive amount. " description = "Post Maker helps you to make post for Instagram and other Social Media apps easily."
authors = ["PiyushXCoder <https://piyushxcoder.in>"] authors = ["PiyushXCoder <https://piyushxcoder.in>"]
license = "GPL-3.0-only" license = "GPL-3.0-only"
readme = "README.md" readme = "README.md"
@ -10,28 +10,18 @@ repository = "https://github.com/PiyushXCoder/post_maker"
keywords = ["posts", "instagram", "facebook", "twitter"] keywords = ["posts", "instagram", "facebook", "twitter"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.dev]
opt-level = 1
[profile.release]
opt-level = 3
[dependencies] [dependencies]
clap = { version = "3.0", features = ["derive"] } clap = { version = "3.0", features = ["derive"] }
log = "0.4" log = "0.4"
simplelog = "0.11" simplelog = "0.11"
fltk = "1.2" fltk = "1.2"
fltk-theme = "0.4" fltk-theme = "0.4"
image = "0.24.1" image = "0.23"
imageproc = "0.23" imageproc = "0.22"
webp = "0.2"
rusttype = "0.9" rusttype = "0.9"
serde_json = "1.0" serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
lazy_static = "1.4" lazy_static = "1.4"
dirs = "4.0" dirs = "4.0"
infer = "0.7.0"
textwrap = "0.14" textwrap = "0.14"
webbrowser = "0.5" webbrowser = "0.5"
mozjpeg = "0.9.2"
bichannel = "0.0.4"

View File

@ -1,2 +0,0 @@
[target.x86_64-pc-windows-gnu]
image = "rustembedded/cross:x86_64-pc-windows-gnu"

View File

@ -4,10 +4,7 @@
<img alt="actix-web-grants" src="./assets/icon_with_text.svg" width="170"> <img alt="actix-web-grants" src="./assets/icon_with_text.svg" width="170">
</p> </p>
[![Crates.io](https://img.shields.io/crates/v/post_maker?color=%23069060&style=for-the-badge)](https://crates.io/crates/post_maker) > Post Maker helps you to make post for instagram and other social media apps easily.
[![Crates.io](https://img.shields.io/crates/l/post_maker?style=for-the-badge)](https://spdx.org/licenses/GPL-3.0-only.html)
> Post Maker helps you to make post for instagram and other social media apps easily and in massive amount.
## Installing ## Installing
@ -44,10 +41,7 @@ You can follow rust's official guide to install rust compiler and cargo [here](h
## General Overview of Controls ## General Overview of Controls
![](assets/Screenshot_2022-08-19_13-48-49.png) ![](assets/20220124_152902_screenshot.png)
## Achievement
The Post Maker is listed in [Official Project Showcase of fltk-rs](https://github.com/fltk-rs/fltk-rs/issues/418)
## License ## License

Binary file not shown.

After

Width:  |  Height:  |  Size: 887 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -13,10 +13,11 @@
*/ */
//! About Window //! About Window
use crate::{config, globals, result_ext::ResultExt}; use crate::{config, globals};
use fltk::{ use fltk::{
app, app,
button::Button, button::Button,
dialog,
enums::{self, Align, Event}, enums::{self, Align, Event},
frame::Frame, frame::Frame,
group::Flex, group::Flex,
@ -151,7 +152,10 @@ impl About {
// Repository Link // Repository Link
self.repo_link.handle(|_, ev| { self.repo_link.handle(|_, ev| {
if ev == Event::Push { if ev == Event::Push {
webbrowser::open(env!("CARGO_PKG_REPOSITORY")).warn_log("Failed to open the link!"); if let Err(e) = webbrowser::open(env!("CARGO_PKG_REPOSITORY")) {
dialog::alert_default("Failed to open the link!");
warn!("Failed to open the link!\n{:?}", e);
}
} }
true true
}); });
@ -159,7 +163,10 @@ impl About {
// Developer's Link // Developer's Link
self.dev_link.handle(|_, ev| { self.dev_link.handle(|_, ev| {
if ev == Event::Push { if ev == Event::Push {
webbrowser::open("https://piyushxcoder.in").warn_log("Failed to open the link!"); if let Err(e) = webbrowser::open("https://piyushxcoder.in") {
dialog::alert_default("Failed to open the link!");
warn!("Failed to open the link!\n{:?}", e);
}
} }
true true
}); });
@ -167,8 +174,10 @@ impl About {
// License Link // License Link
self.license_link.handle(|_, ev| { self.license_link.handle(|_, ev| {
if ev == Event::Push { if ev == Event::Push {
webbrowser::open("https://www.gnu.org/licenses/gpl-3.0.html") if let Err(e) = webbrowser::open("https://www.gnu.org/licenses/gpl-3.0.html") {
.warn_log("Failed to open the link!"); dialog::alert_default("Failed to open the link!");
warn!("Failed to open the link!\n{:?}", e);
}
} }
true true
}); });

View File

@ -13,14 +13,18 @@
*/ */
//! load, save configuration and parse cli args //! load, save configuration and parse cli args
use crate::{ use crate::{config_picker::ConfigPicker, globals};
config_picker::ConfigPicker, dialog, globals, result_ext::ResultExt, utils::ImageType,
};
use clap::{ArgEnum, Parser}; use clap::{ArgEnum, Parser};
use fltk::dialog;
use fltk_theme::ThemeType; use fltk_theme::ThemeType;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fs::File, path::PathBuf}; use std::{
collections::HashMap,
fs::File,
path::PathBuf,
time::{Duration, SystemTime},
};
lazy_static! { lazy_static! {
/// Directory where all Configurations are present /// Directory where all Configurations are present
@ -125,16 +129,10 @@ pub(crate) struct ConfigFile {
pub(crate) quote_position_ratio: f64, pub(crate) quote_position_ratio: f64,
pub(crate) subquote_position_ratio: f64, pub(crate) subquote_position_ratio: f64,
pub(crate) subquote2_position_ratio: f64, pub(crate) subquote2_position_ratio: f64,
pub(crate) tag_x_position_ratio: f64, pub(crate) tag_position_ratio: f64,
pub(crate) tag_y_position_ratio: f64,
pub(crate) tag2_position_ratio: f64, pub(crate) tag2_position_ratio: f64,
pub(crate) image_ratio: (f64, f64), pub(crate) image_ratio: (f64, f64),
pub(crate) color_layer: [u8; 4], pub(crate) color_layer: [u8; 4],
pub(crate) minimum_width_limit: f64, // for export of image
pub(crate) maximum_width_limit: f64, // for export of image
pub(crate) draw_box_around_quote: bool,
pub(crate) line_spacing: bool,
pub(crate) image_format: ImageType,
} }
impl Default for ConfigFile { impl Default for ConfigFile {
@ -145,7 +143,7 @@ impl Default for ConfigFile {
subquote2_font: String::new(), subquote2_font: String::new(),
tag_font: String::new(), tag_font: String::new(),
tag2_font: String::new(), tag2_font: String::new(),
quote_font_ratio: 250.0, quote_font_ratio: 230.0,
subquote_font_ratio: 230.0, subquote_font_ratio: 230.0,
subquote2_font_ratio: 230.0, subquote2_font_ratio: 230.0,
tag_font_ratio: 150.0, tag_font_ratio: 150.0,
@ -153,16 +151,10 @@ impl Default for ConfigFile {
quote_position_ratio: 0.7, quote_position_ratio: 0.7,
subquote_position_ratio: 0.8, subquote_position_ratio: 0.8,
subquote2_position_ratio: 0.9, subquote2_position_ratio: 0.9,
tag_x_position_ratio: 0.95, tag_position_ratio: 0.5,
tag_y_position_ratio: 0.5,
tag2_position_ratio: 0.95, tag2_position_ratio: 0.95,
image_ratio: (4.0, 5.0), image_ratio: (4.0, 5.0),
color_layer: [20, 22, 25, 0], color_layer: [20, 22, 25, 197],
minimum_width_limit: 650.0,
maximum_width_limit: 1080.0,
draw_box_around_quote: true,
line_spacing: true,
image_format: ImageType::Jpeg,
} }
} }
} }
@ -177,7 +169,7 @@ impl ConfigFile {
None => HashMap::new(), None => HashMap::new(),
}; };
let default_config = (&*rw_read!(globals::CONFIG_NAME)).to_string(); let default_config = (&*globals::CONFIG_NAME.read().unwrap()).to_string();
let config_name = if (map.len() > 1 || !map.contains_key(&default_config)) let config_name = if (map.len() > 1 || !map.contains_key(&default_config))
&& map.len() != 0 && map.len() != 0
{ {
@ -192,7 +184,7 @@ impl ConfigFile {
}; };
if let Some(config) = map.get(&config_name) { if let Some(config) = map.get(&config_name) {
*rw_write!(globals::CONFIG_NAME) = config_name; *globals::CONFIG_NAME.write().unwrap() = config_name;
return config.to_owned(); return config.to_owned();
} }
} }
@ -200,7 +192,7 @@ impl ConfigFile {
let config = Self::default(); let config = Self::default();
let mut configs = HashMap::new(); let mut configs = HashMap::new();
configs.insert( configs.insert(
(&*rw_read!(globals::CONFIG_NAME)).to_owned(), (&*globals::CONFIG_NAME.read().unwrap()).to_owned(),
config.clone(), config.clone(),
); );
save_configs(configs); save_configs(configs);
@ -218,16 +210,46 @@ pub(crate) fn get_configs() -> Option<HashMap<String, ConfigFile>> {
/// Save configs /// Save configs
pub(crate) fn save_configs(configs: HashMap<String, ConfigFile>) { pub(crate) fn save_configs(configs: HashMap<String, ConfigFile>) {
std::fs::write(&*CONFIG_FILE, serde_json::to_string(&configs).unwrap()) if let Err(e) = std::fs::write(&*CONFIG_FILE, serde_json::to_string(&configs).unwrap()) {
.expect_log("Can't write config!"); dialog::alert_default("Can't write config!");
error!("Can't write config!\n{:?}", e);
panic!("Can't write config!\n{:?}", e);
}
} }
pub(crate) fn log_file() -> File { pub(crate) fn log_file() -> File {
match File::create(&*LOG_FILE) { match File::open(&*LOG_FILE) {
Ok(f) => f, Ok(mut file) => {
Err(e) => { if is_file_30_days_old(&file) {
dialog::alert_default("Can't open log file!"); match File::create(&*LOG_FILE) {
panic!("{:?}", e); Ok(f) => file = f,
Err(e) => {
dialog::alert_default("Can't open log file!");
panic!("{:?}", e);
}
}
}
file
} }
Err(_) => match File::create(&*LOG_FILE) {
Ok(f) => f,
Err(e) => {
dialog::alert_default("Can't open log file!");
panic!("{:?}", e);
}
},
} }
} }
pub(crate) fn is_file_30_days_old(file: &File) -> bool {
if let Ok(meta) = file.metadata() {
if let Ok(time) = meta.created() {
if let Ok(dur) = SystemTime::now().duration_since(time) {
if dur > Duration::from_secs(60 * 60 * 24 * 30) {
return true;
}
}
}
}
false
}

View File

@ -58,7 +58,7 @@ impl ConfigPicker {
let top_padding_btn = Frame::default(); let top_padding_btn = Frame::default();
let mut panel_flex = Flex::default().row(); let mut panel_flex = Flex::default().row();
Frame::default(); Frame::default();
let apply_btn = Button::default().with_label("Apply"); let apply_btn = Button::default().with_label("apply");
Frame::default(); Frame::default();
panel_flex.set_size(&apply_btn, 100); panel_flex.set_size(&apply_btn, 100);
panel_flex.end(); panel_flex.end();

View File

@ -14,27 +14,27 @@
//! Window to edit configuration //! Window to edit configuration
use crate::{ use std::{cell::RefCell, collections::HashMap, rc::Rc};
config::{self, ConfigFile},
dialog, globals,
result_ext::ResultExt,
utils::{self, ImageType},
};
use fltk::{ use fltk::{
app, app,
browser::{Browser, BrowserType}, browser::{Browser, BrowserType},
button::{Button, CheckButton, RadioRoundButton}, button::Button,
dialog::{FileDialogOptions, NativeFileChooser}, dialog::{self, FileDialogOptions, NativeFileChooser},
enums::{self, Align, Event, Font}, enums::{self, Align, Event, Font},
frame::Frame, frame::Frame,
group::{Flex, Scroll}, group::Flex,
image::SvgImage, image::SvgImage,
output::Output, output::Output,
prelude::*, prelude::*,
valuator::ValueInput, valuator::ValueInput,
window::Window, window::Window,
}; };
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use crate::{
config::{self, ConfigFile},
globals, utils,
};
pub(crate) struct ConfigWindow { pub(crate) struct ConfigWindow {
pub(crate) win: Window, pub(crate) win: Window,
@ -60,21 +60,14 @@ pub(crate) struct ConfigWindow {
pub(crate) quote_position_ratio: ValueInput, pub(crate) quote_position_ratio: ValueInput,
pub(crate) subquote_position_ratio: ValueInput, pub(crate) subquote_position_ratio: ValueInput,
pub(crate) subquote2_position_ratio: ValueInput, pub(crate) subquote2_position_ratio: ValueInput,
pub(crate) tag_x_position_ratio: ValueInput, pub(crate) tag_position_ratio: ValueInput,
pub(crate) tag_y_position_ratio: ValueInput,
pub(crate) tag2_position_ratio: ValueInput, pub(crate) tag2_position_ratio: ValueInput,
pub(crate) image_ratio_width: ValueInput, pub(crate) image_ratio_width: ValueInput,
pub(crate) image_ratio_height: ValueInput, pub(crate) image_ratio_height: ValueInput,
pub(crate) draw_box_around_quote: CheckButton,
pub(crate) line_spacing: CheckButton,
pub(crate) minimum_width_limit: ValueInput,
pub(crate) maximum_width_limit: ValueInput,
/// RGB value of top translucent layer /// RGB value of top translucent layer
pub(crate) translucent_layer_rgb: Button, pub(crate) translucent_layer_rgb: Button,
/// opacity value of top translucent layer /// opacity value of top translucent layer
pub(crate) translucent_layer_alpha: ValueInput, pub(crate) translucent_layer_alpha: ValueInput,
pub(crate) png_format: RadioRoundButton,
pub(crate) jpeg_format: RadioRoundButton,
pub(crate) defaults_btn: Button, pub(crate) defaults_btn: Button,
pub(crate) save_btn: Button, pub(crate) save_btn: Button,
pub(crate) cancel_btn: Button, pub(crate) cancel_btn: Button,
@ -85,16 +78,12 @@ pub(crate) struct ConfigWindow {
impl ConfigWindow { impl ConfigWindow {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
let configs = config::get_configs().unwrap_or(HashMap::new()); let configs = config::get_configs().unwrap_or(HashMap::new());
let mut win = Window::new(0, 0, 900, 600, "Config").center_screen(); let mut win = Window::new(0, 0, 900, 680, "Config").center_screen();
win.set_icon(Some( win.set_icon(Some(
SvgImage::from_data(globals::ICON.to_str().unwrap()).unwrap(), SvgImage::from_data(globals::ICON.to_str().unwrap()).unwrap(),
)); ));
let mut row = Flex::default().with_size(890, 670).with_pos(5, 5).row();
// Config picking area let mut config_picker_flex = Flex::default().column();
let mut config_picker_flex = Flex::default()
.with_size(200, win.height() - 50)
.with_pos(5, 5)
.column();
// Picker // Picker
let browse = Browser::default().with_type(BrowserType::Hold); let browse = Browser::default().with_type(BrowserType::Hold);
@ -114,30 +103,9 @@ impl ConfigWindow {
config_picker_flex.set_size(&panel_flex, 30); config_picker_flex.set_size(&panel_flex, 30);
config_picker_flex.set_size(&bottom_padding_btn, 5); config_picker_flex.set_size(&bottom_padding_btn, 5);
config_picker_flex.end(); config_picker_flex.end();
row.set_size(&config_picker_flex, 200);
// Bottom Panel let mut col = Flex::default().column();
let mut panel_grp = Flex::default()
.with_size(win.width() - 10, 30)
.with_pos(5, win.height() - 40)
.row();
Frame::default();
let defaults_btn = Button::default().with_label("Defaults");
let save_btn = Button::default().with_label("Save");
let cancel_btn = Button::default().with_label("Cancel");
panel_grp.set_size(&defaults_btn, 100);
panel_grp.set_size(&save_btn, 100);
panel_grp.set_size(&cancel_btn, 100);
panel_grp.end();
// Rest everything
let mut scroll = Scroll::default()
.with_size(win.width() - 210, win.height() - 50)
.with_pos(205, 5);
let mut col = Flex::default()
.with_size(scroll.width() - 35, 850)
.column()
.with_pos(100, 0);
let mut label = Frame::default().with_label("Fonts:"); let mut label = Frame::default().with_label("Fonts:");
label.set_label_font(enums::Font::HelveticaBold); label.set_label_font(enums::Font::HelveticaBold);
@ -220,7 +188,7 @@ impl ConfigWindow {
let mut label = Frame::default().with_label("Ratio of size of text:"); let mut label = Frame::default().with_label("Ratio of size of text:");
label.set_label_font(enums::Font::HelveticaBold); label.set_label_font(enums::Font::HelveticaBold);
col.set_size(&label, 15); col.set_size(&label, 15);
let mut hint = Frame::default().with_label("Font size in image of height 4000 pixels"); let mut hint = Frame::default().with_label("Font size in image of resolution 4000x5000");
hint.set_label_font(Font::CourierItalic); hint.set_label_font(Font::CourierItalic);
hint.set_label_size(12); hint.set_label_size(12);
col.set_size(&hint, 20); col.set_size(&hint, 20);
@ -338,27 +306,16 @@ impl ConfigWindow {
// column 2 // column 2
let mut col_grp = Flex::default().column(); let mut col_grp = Flex::default().column();
let mut tag_x_position_ratio_grp = Flex::default().row(); let mut tag_position_ratio_grp = Flex::default().row();
tag_x_position_ratio_grp.set_size( tag_position_ratio_grp.set_size(
&Frame::default() &Frame::default()
.with_label("Tag (x)") .with_label("Tag")
.with_align(Align::Right | Align::Inside), .with_align(Align::Right | Align::Inside),
130, 130,
); );
let tag_x_position_ratio = ValueInput::default(); let tag_position_ratio = ValueInput::default();
tag_x_position_ratio_grp.end(); tag_position_ratio_grp.end();
col_grp.set_size(&tag_x_position_ratio_grp, 30); col_grp.set_size(&tag_position_ratio_grp, 30);
let mut tag_y_position_ratio_grp = Flex::default().row();
tag_y_position_ratio_grp.set_size(
&Frame::default()
.with_label("Tag (y)")
.with_align(Align::Right | Align::Inside),
130,
);
let tag_y_position_ratio = ValueInput::default();
tag_y_position_ratio_grp.end();
col_grp.set_size(&tag_y_position_ratio_grp, 30);
let mut tag2_position_ratio_grp = Flex::default().row(); let mut tag2_position_ratio_grp = Flex::default().row();
tag2_position_ratio_grp.set_size( tag2_position_ratio_grp.set_size(
@ -391,60 +348,6 @@ impl ConfigWindow {
image_ratio_grp.end(); image_ratio_grp.end();
col.set_size(&image_ratio_grp, 30); col.set_size(&image_ratio_grp, 30);
// Draw box around Quote
let mut label = Frame::default().with_label("Quote Special:");
label.set_label_font(enums::Font::HelveticaBold);
col.set_size(&label, 15);
let mut quote_special_flex = Flex::default().row();
quote_special_flex.set_size(&Frame::default(), 20);
let mut draw_box_around_quote = CheckButton::default().with_label("Draw box around text");
draw_box_around_quote.set_value(true);
let mut line_spacing = CheckButton::default().with_label("Line spacing in quotes");
line_spacing.set_value(true);
quote_special_flex.end();
col.set_size(&quote_special_flex, 30);
let mut label = Frame::default().with_label("Image with limits:");
label.set_label_font(enums::Font::HelveticaBold);
col.set_size(&label, 15);
let mut hint = Frame::default().with_label("Limiting width of image in pixels");
hint.set_label_font(Font::CourierItalic);
hint.set_label_size(12);
col.set_size(&hint, 20);
// Size Ratio Group
let row_grp = Flex::default().row();
// column 1
let mut col_grp = Flex::default().column();
let mut minimum_width_limit_grp = Flex::default().row();
minimum_width_limit_grp.set_size(
&Frame::default()
.with_label("Minimum")
.with_align(Align::Right | Align::Inside),
130,
);
let minimum_width_limit = ValueInput::default();
minimum_width_limit_grp.end();
col_grp.set_size(&minimum_width_limit_grp, 30);
col_grp.end();
// column 2
let mut col_grp = Flex::default().column();
let mut maximum_width_limit_grp = Flex::default().row();
maximum_width_limit_grp.set_size(
&Frame::default()
.with_label("Maximim")
.with_align(Align::Right | Align::Inside),
130,
);
let maximum_width_limit = ValueInput::default();
maximum_width_limit_grp.end();
col_grp.set_size(&maximum_width_limit_grp, 30);
col_grp.end();
row_grp.end();
col.set_size(&row_grp, 40);
let mut label = Frame::default().with_label("Colour for dark layer:"); let mut label = Frame::default().with_label("Colour for dark layer:");
label.set_label_font(enums::Font::HelveticaBold); label.set_label_font(enums::Font::HelveticaBold);
col.set_size(&label, 15); col.set_size(&label, 15);
@ -455,7 +358,7 @@ impl ConfigWindow {
col.set_size(&hint, 20); col.set_size(&hint, 20);
let mut translucent_layer_flex = Flex::default().row(); let mut translucent_layer_flex = Flex::default().row();
translucent_layer_flex.set_size(&Frame::default(), 20); translucent_layer_flex.set_pad(2);
translucent_layer_flex.set_size(&Frame::default().with_label("Colour"), 50); translucent_layer_flex.set_size(&Frame::default().with_label("Colour"), 50);
let mut translucent_layer_rgb = Button::default(); let mut translucent_layer_rgb = Button::default();
translucent_layer_rgb.set_frame(enums::FrameType::BorderBox); translucent_layer_rgb.set_frame(enums::FrameType::BorderBox);
@ -465,32 +368,25 @@ impl ConfigWindow {
translucent_layer_flex.end(); translucent_layer_flex.end();
col.set_size(&translucent_layer_flex, 30); col.set_size(&translucent_layer_flex, 30);
let mut label = Frame::default().with_label("Export Format:");
label.set_label_font(enums::Font::HelveticaBold);
col.set_size(&label, 15);
let mut hint = Frame::default().with_label("Image format to export image");
hint.set_label_font(Font::CourierItalic);
hint.set_label_size(12);
col.set_size(&hint, 20);
let mut image_format_flex = Flex::default().row();
image_format_flex.set_size(&Frame::default(), 20);
let mut png_format = RadioRoundButton::default().with_label("Png");
png_format.set_value(true);
let jpeg_format = RadioRoundButton::default().with_label("Jpeg");
image_format_flex.end();
col.set_size(&image_format_flex, 30);
Frame::default(); Frame::default();
let mut panel_grp = Flex::default().row();
Frame::default();
let defaults_btn = Button::default().with_label("Defaults");
let save_btn = Button::default().with_label("Save");
let cancel_btn = Button::default().with_label("Cancel");
panel_grp.set_size(&defaults_btn, 100);
panel_grp.set_size(&save_btn, 100);
panel_grp.set_size(&cancel_btn, 100);
panel_grp.end();
col.set_size(&panel_grp, 30);
col.end(); col.end();
row.end();
scroll.end();
scroll.make_resizable(true);
scroll.scroll_to(-1 * col.x() - 5, -1 * col.y() - 5);
win.end(); win.end();
win.make_modal(true); win.make_modal(true);
win.make_resizable(true);
let mut config_window = Self { let mut config_window = Self {
win, win,
@ -516,19 +412,12 @@ impl ConfigWindow {
quote_position_ratio, quote_position_ratio,
subquote_position_ratio, subquote_position_ratio,
subquote2_position_ratio, subquote2_position_ratio,
tag_x_position_ratio, tag_position_ratio,
tag_y_position_ratio,
tag2_position_ratio, tag2_position_ratio,
image_ratio_width, image_ratio_width,
image_ratio_height, image_ratio_height,
draw_box_around_quote,
line_spacing,
minimum_width_limit,
maximum_width_limit,
translucent_layer_rgb, translucent_layer_rgb,
translucent_layer_alpha, translucent_layer_alpha,
png_format,
jpeg_format,
defaults_btn, defaults_btn,
save_btn, save_btn,
cancel_btn, cancel_btn,
@ -542,7 +431,7 @@ impl ConfigWindow {
// Show to edit config // Show to edit config
pub(crate) fn show(&mut self) -> bool { pub(crate) fn show(&mut self) -> bool {
let config_name = &*rw_read!(globals::CONFIG_NAME); let config_name = &*globals::CONFIG_NAME.read().unwrap();
self.browse.clear(); self.browse.clear();
for (idx, name) in self.configs.borrow().keys().enumerate() { for (idx, name) in self.configs.borrow().keys().enumerate() {
self.browse.add(name); self.browse.add(name);
@ -551,7 +440,7 @@ impl ConfigWindow {
} }
} }
*self.selected_browse_line.borrow_mut() = self.browse.value(); *self.selected_browse_line.borrow_mut() = self.browse.value();
let config = rw_read!(globals::CONFIG); let config = globals::CONFIG.read().unwrap();
self.quote_font.set_value(config.quote_font.as_str()); self.quote_font.set_value(config.quote_font.as_str());
self.subquote_font.set_value(config.subquote_font.as_str()); self.subquote_font.set_value(config.subquote_font.as_str());
self.subquote2_font self.subquote2_font
@ -571,37 +460,14 @@ impl ConfigWindow {
.set_value(config.subquote_position_ratio); .set_value(config.subquote_position_ratio);
self.subquote2_position_ratio self.subquote2_position_ratio
.set_value(config.subquote2_position_ratio); .set_value(config.subquote2_position_ratio);
self.tag_x_position_ratio self.tag_position_ratio.set_value(config.tag_position_ratio);
.set_value(config.tag_x_position_ratio);
self.tag_y_position_ratio
.set_value(config.tag_y_position_ratio);
self.tag2_position_ratio self.tag2_position_ratio
.set_value(config.tag2_position_ratio); .set_value(config.tag2_position_ratio);
self.image_ratio_width.set_value(config.image_ratio.0); self.image_ratio_width.set_value(config.image_ratio.0);
self.image_ratio_height.set_value(config.image_ratio.1); self.image_ratio_height.set_value(config.image_ratio.1);
self.draw_box_around_quote
.set_checked(config.draw_box_around_quote);
self.line_spacing.set_checked(config.line_spacing);
self.minimum_width_limit
.set_value(config.minimum_width_limit);
self.maximum_width_limit
.set_value(config.maximum_width_limit);
utils::set_color_btn_rgba(config.color_layer, &mut self.translucent_layer_rgb); utils::set_color_btn_rgba(config.color_layer, &mut self.translucent_layer_rgb);
self.translucent_layer_alpha self.translucent_layer_alpha
.set_value(config.color_layer[3] as f64); .set_value(config.color_layer[3] as f64);
match config.image_format {
utils::ImageType::Png => {
self.png_format.set_value(true);
self.jpeg_format.set_value(false)
}
utils::ImageType::Jpeg => {
self.png_format.set_value(false);
self.jpeg_format.set_value(true)
}
_ => (),
}
*self.did_save.borrow_mut() = false; *self.did_save.borrow_mut() = false;
drop(config); drop(config);
self.win.show(); self.win.show();
@ -627,15 +493,10 @@ impl ConfigWindow {
let mut quote_position_ratio = self.quote_position_ratio.clone(); let mut quote_position_ratio = self.quote_position_ratio.clone();
let mut subquote_position_ratio = self.subquote_position_ratio.clone(); let mut subquote_position_ratio = self.subquote_position_ratio.clone();
let mut subquote2_position_ratio = self.subquote2_position_ratio.clone(); let mut subquote2_position_ratio = self.subquote2_position_ratio.clone();
let mut tag_x_position_ratio = self.tag_x_position_ratio.clone(); let mut tag_position_ratio = self.tag_position_ratio.clone();
let mut tag_y_position_ratio = self.tag_y_position_ratio.clone();
let mut tag2_position_ratio = self.tag2_position_ratio.clone(); let mut tag2_position_ratio = self.tag2_position_ratio.clone();
let mut image_ratio_width = self.image_ratio_width.clone(); let mut image_ratio_width = self.image_ratio_width.clone();
let mut image_ratio_height = self.image_ratio_height.clone(); let mut image_ratio_height = self.image_ratio_height.clone();
let draw_box_around_quote = self.draw_box_around_quote.clone();
let line_spacing = self.line_spacing.clone();
let mut minimum_width_limit = self.minimum_width_limit.clone();
let mut maximum_width_limit = self.maximum_width_limit.clone();
let mut layer_rgb = self.translucent_layer_rgb.clone(); let mut layer_rgb = self.translucent_layer_rgb.clone();
let mut layer_alpha = self.translucent_layer_alpha.clone(); let mut layer_alpha = self.translucent_layer_alpha.clone();
let mut browse = self.browse.clone(); let mut browse = self.browse.clone();
@ -675,15 +536,10 @@ impl ConfigWindow {
quote_position_ratio.set_value(conf.quote_position_ratio); quote_position_ratio.set_value(conf.quote_position_ratio);
subquote_position_ratio.set_value(conf.subquote_position_ratio); subquote_position_ratio.set_value(conf.subquote_position_ratio);
subquote2_position_ratio.set_value(conf.subquote2_position_ratio); subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
tag_x_position_ratio.set_value(conf.tag_x_position_ratio); tag_position_ratio.set_value(conf.tag_position_ratio);
tag_y_position_ratio.set_value(conf.tag_y_position_ratio);
tag2_position_ratio.set_value(conf.tag2_position_ratio); tag2_position_ratio.set_value(conf.tag2_position_ratio);
image_ratio_width.set_value(conf.image_ratio.0); image_ratio_width.set_value(conf.image_ratio.0);
image_ratio_height.set_value(conf.image_ratio.1); image_ratio_height.set_value(conf.image_ratio.1);
draw_box_around_quote.set_checked(conf.draw_box_around_quote);
line_spacing.set_checked(conf.line_spacing);
minimum_width_limit.set_value(conf.minimum_width_limit);
maximum_width_limit.set_value(conf.maximum_width_limit);
utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb); utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb);
layer_alpha.set_value(conf.color_layer[3] as f64); layer_alpha.set_value(conf.color_layer[3] as f64);
browse.add(&name); browse.add(&name);
@ -707,22 +563,17 @@ impl ConfigWindow {
let mut quote_position_ratio = self.quote_position_ratio.clone(); let mut quote_position_ratio = self.quote_position_ratio.clone();
let mut subquote_position_ratio = self.subquote_position_ratio.clone(); let mut subquote_position_ratio = self.subquote_position_ratio.clone();
let mut subquote2_position_ratio = self.subquote2_position_ratio.clone(); let mut subquote2_position_ratio = self.subquote2_position_ratio.clone();
let mut tag_x_position_ratio = self.tag_x_position_ratio.clone(); let mut tag_position_ratio = self.tag_position_ratio.clone();
let mut tag_y_position_ratio = self.tag_y_position_ratio.clone();
let mut tag2_position_ratio = self.tag2_position_ratio.clone(); let mut tag2_position_ratio = self.tag2_position_ratio.clone();
let mut image_ratio_width = self.image_ratio_width.clone(); let mut image_ratio_width = self.image_ratio_width.clone();
let mut image_ratio_height = self.image_ratio_height.clone(); let mut image_ratio_height = self.image_ratio_height.clone();
let draw_box_around_quote = self.draw_box_around_quote.clone();
let line_spacing = self.line_spacing.clone();
let mut minimum_width_limit = self.minimum_width_limit.clone();
let mut maximum_width_limit = self.maximum_width_limit.clone();
let mut layer_rgb = self.translucent_layer_rgb.clone(); let mut layer_rgb = self.translucent_layer_rgb.clone();
let mut layer_alpha = self.translucent_layer_alpha.clone(); let mut layer_alpha = self.translucent_layer_alpha.clone();
let mut browse = self.browse.clone(); let mut browse = self.browse.clone();
let configs = Rc::clone(&self.configs); let configs = Rc::clone(&self.configs);
let selected_browse_line = Rc::clone(&self.selected_browse_line); let selected_browse_line = Rc::clone(&self.selected_browse_line);
self.del_config_btn.set_callback(move |_| { self.del_config_btn.set_callback(move |_| {
let ch = dialog::choice_default("Do you want to delete??", "Yes", "No"); let ch = dialog::choice_default("Do you want to delete??", "Yes", "No", "");
if ch == 1 { if ch == 1 {
return; return;
} }
@ -754,15 +605,10 @@ impl ConfigWindow {
quote_position_ratio.set_value(conf.quote_position_ratio); quote_position_ratio.set_value(conf.quote_position_ratio);
subquote_position_ratio.set_value(conf.subquote_position_ratio); subquote_position_ratio.set_value(conf.subquote_position_ratio);
subquote2_position_ratio.set_value(conf.subquote2_position_ratio); subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
tag_x_position_ratio.set_value(conf.tag_x_position_ratio); tag_position_ratio.set_value(conf.tag_position_ratio);
tag_y_position_ratio.set_value(conf.tag_y_position_ratio);
tag2_position_ratio.set_value(conf.tag2_position_ratio); tag2_position_ratio.set_value(conf.tag2_position_ratio);
image_ratio_width.set_value(conf.image_ratio.0); image_ratio_width.set_value(conf.image_ratio.0);
image_ratio_height.set_value(conf.image_ratio.1); image_ratio_height.set_value(conf.image_ratio.1);
draw_box_around_quote.set_checked(conf.draw_box_around_quote);
line_spacing.set_checked(conf.line_spacing);
minimum_width_limit.set_value(conf.minimum_width_limit);
maximum_width_limit.set_value(conf.maximum_width_limit);
utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb); utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb);
layer_alpha.set_value(conf.color_layer[3] as f64); layer_alpha.set_value(conf.color_layer[3] as f64);
layer_rgb.redraw(); layer_rgb.redraw();
@ -783,15 +629,10 @@ impl ConfigWindow {
let mut quote_position_ratio = self.quote_position_ratio.clone(); let mut quote_position_ratio = self.quote_position_ratio.clone();
let mut subquote_position_ratio = self.subquote_position_ratio.clone(); let mut subquote_position_ratio = self.subquote_position_ratio.clone();
let mut subquote2_position_ratio = self.subquote2_position_ratio.clone(); let mut subquote2_position_ratio = self.subquote2_position_ratio.clone();
let mut tag_x_position_ratio = self.tag_x_position_ratio.clone(); let mut tag_position_ratio = self.tag_position_ratio.clone();
let mut tag_y_position_ratio = self.tag_y_position_ratio.clone();
let mut tag2_position_ratio = self.tag2_position_ratio.clone(); let mut tag2_position_ratio = self.tag2_position_ratio.clone();
let mut image_ratio_width = self.image_ratio_width.clone(); let mut image_ratio_width = self.image_ratio_width.clone();
let mut image_ratio_height = self.image_ratio_height.clone(); let mut image_ratio_height = self.image_ratio_height.clone();
let draw_box_around_quote = self.draw_box_around_quote.clone();
let line_spacing = self.line_spacing.clone();
let mut minimum_width_limit = self.minimum_width_limit.clone();
let mut maximum_width_limit = self.maximum_width_limit.clone();
let mut layer_rgb = self.translucent_layer_rgb.clone(); let mut layer_rgb = self.translucent_layer_rgb.clone();
let mut layer_alpha = self.translucent_layer_alpha.clone(); let mut layer_alpha = self.translucent_layer_alpha.clone();
let configs = Rc::clone(&self.configs); let configs = Rc::clone(&self.configs);
@ -820,15 +661,10 @@ impl ConfigWindow {
quote_position_ratio.set_value(conf.quote_position_ratio); quote_position_ratio.set_value(conf.quote_position_ratio);
subquote_position_ratio.set_value(conf.subquote_position_ratio); subquote_position_ratio.set_value(conf.subquote_position_ratio);
subquote2_position_ratio.set_value(conf.subquote2_position_ratio); subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
tag_x_position_ratio.set_value(conf.tag_x_position_ratio); tag_position_ratio.set_value(conf.tag_position_ratio);
tag_y_position_ratio.set_value(conf.tag_y_position_ratio);
tag2_position_ratio.set_value(conf.tag2_position_ratio); tag2_position_ratio.set_value(conf.tag2_position_ratio);
image_ratio_width.set_value(conf.image_ratio.0); image_ratio_width.set_value(conf.image_ratio.0);
image_ratio_height.set_value(conf.image_ratio.1); image_ratio_height.set_value(conf.image_ratio.1);
draw_box_around_quote.set_checked(conf.draw_box_around_quote);
line_spacing.set_checked(conf.line_spacing);
minimum_width_limit.set_value(conf.minimum_width_limit);
maximum_width_limit.set_value(conf.maximum_width_limit);
utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb); utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb);
layer_alpha.set_value(conf.color_layer[3] as f64); layer_alpha.set_value(conf.color_layer[3] as f64);
layer_rgb.redraw(); layer_rgb.redraw();
@ -1066,31 +902,16 @@ impl ConfigWindow {
true true
}); });
// Tag x position ratio // Tag position ratio
let browse = self.browse.clone(); let browse = self.browse.clone();
let configs = Rc::clone(&self.configs); let configs = Rc::clone(&self.configs);
self.tag_x_position_ratio.handle(move |f, ev| { self.tag_position_ratio.handle(move |f, ev| {
if ev == Event::KeyUp { if ev == Event::KeyUp {
if let Some(conf) = configs if let Some(conf) = configs
.borrow_mut() .borrow_mut()
.get_mut(&browse.selected_text().unwrap()) .get_mut(&browse.selected_text().unwrap())
{ {
conf.tag_x_position_ratio = f.value(); conf.tag_position_ratio = f.value();
}
}
true
});
// Tag y position ratio
let browse = self.browse.clone();
let configs = Rc::clone(&self.configs);
self.tag_y_position_ratio.handle(move |f, ev| {
if ev == Event::KeyUp {
if let Some(conf) = configs
.borrow_mut()
.get_mut(&browse.selected_text().unwrap())
{
conf.tag_y_position_ratio = f.value();
} }
} }
true true
@ -1141,62 +962,6 @@ impl ConfigWindow {
true true
}); });
// Draw box around quote
let browse = self.browse.clone();
let configs = Rc::clone(&self.configs);
self.draw_box_around_quote.handle(move |f, _| {
if let Some(conf) = configs
.borrow_mut()
.get_mut(&browse.selected_text().unwrap())
{
conf.draw_box_around_quote = f.value();
}
true
});
// line spacing
let browse = self.browse.clone();
let configs = Rc::clone(&self.configs);
self.line_spacing.handle(move |f, _| {
if let Some(conf) = configs
.borrow_mut()
.get_mut(&browse.selected_text().unwrap())
{
conf.line_spacing = f.value();
}
true
});
// Minimum Width Limit
let browse = self.browse.clone();
let configs = Rc::clone(&self.configs);
self.minimum_width_limit.handle(move |f, ev| {
if ev == Event::KeyUp {
if let Some(conf) = configs
.borrow_mut()
.get_mut(&browse.selected_text().unwrap())
{
conf.minimum_width_limit = f.value();
}
}
true
});
// Maximim Width Limit
let browse = self.browse.clone();
let configs = Rc::clone(&self.configs);
self.maximum_width_limit.handle(move |f, ev| {
if ev == Event::KeyUp {
if let Some(conf) = configs
.borrow_mut()
.get_mut(&browse.selected_text().unwrap())
{
conf.maximum_width_limit = f.value();
}
}
true
});
// Translucent Layer RGB // Translucent Layer RGB
let browse = self.browse.clone(); let browse = self.browse.clone();
let configs = Rc::clone(&self.configs); let configs = Rc::clone(&self.configs);
@ -1241,30 +1006,6 @@ impl ConfigWindow {
true true
}); });
// Png Image format
let browse = self.browse.clone();
let configs = Rc::clone(&self.configs);
self.png_format.set_callback(move |_| {
if let Some(conf) = configs
.borrow_mut()
.get_mut(&browse.selected_text().unwrap())
{
conf.image_format = ImageType::Png;
}
});
// Jpeg Image format
let browse = self.browse.clone();
let configs = Rc::clone(&self.configs);
self.jpeg_format.set_callback(move |_| {
if let Some(conf) = configs
.borrow_mut()
.get_mut(&browse.selected_text().unwrap())
{
conf.image_format = ImageType::Jpeg;
}
});
// Reset to default configuation button // Reset to default configuation button
let mut quote_font = self.quote_font.clone(); let mut quote_font = self.quote_font.clone();
let mut subquote_font = self.subquote_font.clone(); let mut subquote_font = self.subquote_font.clone();
@ -1279,19 +1020,12 @@ impl ConfigWindow {
let mut quote_position_ratio = self.quote_position_ratio.clone(); let mut quote_position_ratio = self.quote_position_ratio.clone();
let mut subquote_position_ratio = self.subquote_position_ratio.clone(); let mut subquote_position_ratio = self.subquote_position_ratio.clone();
let mut subquote2_position_ratio = self.subquote2_position_ratio.clone(); let mut subquote2_position_ratio = self.subquote2_position_ratio.clone();
let mut tag_x_position_ratio = self.tag_x_position_ratio.clone(); let mut tag_position_ratio = self.tag_position_ratio.clone();
let mut tag_y_position_ratio = self.tag_y_position_ratio.clone();
let mut tag2_position_ratio = self.tag2_position_ratio.clone(); let mut tag2_position_ratio = self.tag2_position_ratio.clone();
let mut image_ratio_width = self.image_ratio_width.clone(); let mut image_ratio_width = self.image_ratio_width.clone();
let mut image_ratio_height = self.image_ratio_height.clone(); let mut image_ratio_height = self.image_ratio_height.clone();
let draw_box_around_quote = self.draw_box_around_quote.clone();
let line_spacing = self.line_spacing.clone();
let mut minimum_width_limit = self.minimum_width_limit.clone();
let mut maximum_width_limit = self.maximum_width_limit.clone();
let mut layer_rgb = self.translucent_layer_rgb.clone(); let mut layer_rgb = self.translucent_layer_rgb.clone();
let mut layer_alpha = self.translucent_layer_alpha.clone(); let mut layer_alpha = self.translucent_layer_alpha.clone();
let mut png_format = self.png_format.clone();
let mut jpeg_format = self.jpeg_format.clone();
let configs = Rc::clone(&self.configs); let configs = Rc::clone(&self.configs);
let browse = self.browse.clone(); let browse = self.browse.clone();
self.defaults_btn.set_callback(move |_| { self.defaults_btn.set_callback(move |_| {
@ -1309,29 +1043,13 @@ impl ConfigWindow {
quote_position_ratio.set_value(conf.quote_position_ratio); quote_position_ratio.set_value(conf.quote_position_ratio);
subquote_position_ratio.set_value(conf.subquote_position_ratio); subquote_position_ratio.set_value(conf.subquote_position_ratio);
subquote2_position_ratio.set_value(conf.subquote2_position_ratio); subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
tag_x_position_ratio.set_value(conf.tag_x_position_ratio); tag_position_ratio.set_value(conf.tag_position_ratio);
tag_y_position_ratio.set_value(conf.tag_y_position_ratio);
tag2_position_ratio.set_value(conf.tag2_position_ratio); tag2_position_ratio.set_value(conf.tag2_position_ratio);
image_ratio_width.set_value(conf.image_ratio.0); image_ratio_width.set_value(conf.image_ratio.0);
image_ratio_height.set_value(conf.image_ratio.1); image_ratio_height.set_value(conf.image_ratio.1);
draw_box_around_quote.set_checked(conf.draw_box_around_quote);
line_spacing.set_checked(conf.line_spacing);
minimum_width_limit.set_value(conf.minimum_width_limit);
maximum_width_limit.set_value(conf.maximum_width_limit);
utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb); utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb);
layer_rgb.redraw(); layer_rgb.redraw();
layer_alpha.set_value(conf.color_layer[3] as f64); layer_alpha.set_value(conf.color_layer[3] as f64);
match conf.image_format {
ImageType::Png => {
png_format.set_value(true);
jpeg_format.set_value(false);
}
ImageType::Jpeg => {
png_format.set_value(false);
jpeg_format.set_value(true);
}
_ => {}
}
configs configs
.borrow_mut() .borrow_mut()
.insert(browse.selected_text().unwrap(), conf); .insert(browse.selected_text().unwrap(), conf);
@ -1344,8 +1062,8 @@ impl ConfigWindow {
self.save_btn.set_callback(move |_| { self.save_btn.set_callback(move |_| {
config::save_configs((*configs.borrow()).clone()); config::save_configs((*configs.borrow()).clone());
if let Some(c) = configs.borrow().get(&*rw_read!(globals::CONFIG_NAME)) { if let Some(c) = configs.borrow().get(&*globals::CONFIG_NAME.read().unwrap()) {
*rw_write!(globals::CONFIG) = c.to_owned(); *globals::CONFIG.write().unwrap() = c.to_owned();
} }
*did_save.borrow_mut() = true; *did_save.borrow_mut() = true;
win.hide(); win.hide();

View File

@ -15,8 +15,7 @@
//! Window to change Crop properties of image //! Window to change Crop properties of image
use crate::{ use crate::{
globals, globals,
result_ext::ResultExt, utils::{self, Coord, ImageContainer, ImageProperties},
utils::{self, Coord, ImageContainer, ImageInfo, ImageProperties},
}; };
use fltk::{ use fltk::{
app, button::Button, draw, enums::Event, frame::Frame, group::Flex, image::SvgImage, app, button::Button, draw, enums::Event, frame::Frame, group::Flex, image::SvgImage,
@ -25,6 +24,7 @@ use fltk::{
use image::GenericImageView; use image::GenericImageView;
use std::{ use std::{
cell::RefCell, cell::RefCell,
path::PathBuf,
rc::Rc, rc::Rc,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
}; };
@ -108,13 +108,13 @@ impl CropWindow {
/// Call it to show window to crop image /// Call it to show window to crop image
pub(crate) fn load_to_crop( pub(crate) fn load_to_crop(
&mut self, &mut self,
path: &ImageInfo, path: &PathBuf,
crop_pos: Option<(f64, f64)>, crop_pos: Option<(f64, f64)>,
) -> Option<(f64, f64)> { ) -> Option<(f64, f64)> {
let mut container = let mut container =
ImageContainer::new(path, Arc::new(RwLock::new(ImageProperties::default()))); ImageContainer::new(path, Arc::new(RwLock::new(ImageProperties::default())));
{ {
let prop = &mut rw_write!(container.properties); let prop = &mut container.properties.write().unwrap();
prop.dimension = prop.original_dimension; prop.dimension = prop.original_dimension;
prop.crop_position = match crop_pos { prop.crop_position = match crop_pos {
Some(a) => Some(a), Some(a) => Some(a),
@ -146,7 +146,7 @@ impl CropWindow {
} }
if let Some(cont) = &*self.container.borrow() { if let Some(cont) = &*self.container.borrow() {
rw_read!(cont.properties).crop_position cont.properties.read().unwrap().crop_position
} else { } else {
None None
} }
@ -169,7 +169,7 @@ impl CropWindow {
) )
.unwrap(); .unwrap();
let prop = rw_read!(cont.properties); let prop = cont.properties.read().unwrap();
let (original_width, original_height) = prop.original_dimension; let (original_width, original_height) = prop.original_dimension;
let (original_x, original_y) = prop.crop_position.unwrap(); let (original_x, original_y) = prop.crop_position.unwrap();
let (resized_width, resized_height) = (image.width() as f64, image.height() as f64); let (resized_width, resized_height) = (image.width() as f64, image.height() as f64);
@ -201,7 +201,7 @@ impl CropWindow {
if let Some(cont) = &*container.borrow_mut() { if let Some(cont) = &*container.borrow_mut() {
let image = &cont.buffer; let image = &cont.buffer;
let mut prop = rw_write!(cont.properties); let mut prop = cont.properties.write().unwrap();
let (original_x, original_y) = match prop.crop_position { let (original_x, original_y) = match prop.crop_position {
Some(v) => v, Some(v) => v,
@ -272,7 +272,7 @@ impl CropWindow {
let container = Rc::clone(&self.container); let container = Rc::clone(&self.container);
self.win.set_callback(move |f| { self.win.set_callback(move |f| {
if let Some(cont) = &*container.borrow_mut() { if let Some(cont) = &*container.borrow_mut() {
rw_write!(cont.properties).crop_position = None; cont.properties.write().unwrap().crop_position = None;
} }
f.hide(); f.hide();
}); });

View File

@ -1,36 +0,0 @@
/*
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 <https://www.gnu.org/licenses/>
*/
pub(crate) use fltk::dialog::{color_chooser_with_default, ColorMode};
use fltk::{app::get_mouse, dialog};
pub(crate) fn input_default(txt: &str, deflt: &str) -> Option<String> {
let (x, y) = get_mouse();
dialog::input(x, y, txt, deflt)
}
pub(crate) fn alert_default(txt: &str) {
let (x, y) = get_mouse();
dialog::alert(x, y, txt)
}
pub(crate) fn message_default(txt: &str) {
let (x, y) = get_mouse();
dialog::message(x, y, txt)
}
pub(crate) fn choice_default(txt: &str, b0: &str, b1: &str) -> i32 {
let (x, y) = get_mouse();
dialog::choice2(x, y, txt, b0, b1, "").unwrap_or(-1)
}

View File

@ -14,17 +14,16 @@
//! Thread to manage drawing in background //! Thread to manage drawing in background
use crate::utils::{ImageContainer, ImageProperties};
use crate::{ use crate::{
globals,
main_window::{MainWindow, Page}, main_window::{MainWindow, Page},
result_ext::ResultExt, utils::{self, ImagePropertiesFile},
utils::{self, ImageContainer, ImageInfo, ImageProperties, ImagePropertiesFile},
AppMessage, AppMessage,
}; };
use fltk::{ use fltk::{
app, app,
button::Button, button::Button,
enums, dialog, enums,
frame::Frame, frame::Frame,
input::{Input, MultilineInput}, input::{Input, MultilineInput},
menu, menu,
@ -34,6 +33,7 @@ use fltk::{
}; };
use std::{ use std::{
fs, fs,
path::{Path, PathBuf},
sync::{mpsc, Arc, RwLock}, sync::{mpsc, Arc, RwLock},
}; };
@ -53,10 +53,6 @@ pub(crate) enum DrawMessage {
Clone, Clone,
/// Delete file /// Delete file
Delete, Delete,
/// Show details about images linke count of quotes
ShowImagesDetails,
/// Check If image is proper
CheckImage,
} }
/// Spawn thread to manage all actions related to image, like: edit, save, delete /// Spawn thread to manage all actions related to image, like: edit, save, delete
@ -68,7 +64,6 @@ pub(crate) fn spawn_image_thread(
) { ) {
let mut win = main_win.win.clone(); let mut win = main_win.win.clone();
let mut file_choice = main_win.file_choice.clone(); let mut file_choice = main_win.file_choice.clone();
let mut name_prefix = main_win.name_prefix.clone();
let mut quote = main_win.quote.clone(); let mut quote = main_win.quote.clone();
let mut subquote = main_win.subquote.clone(); let mut subquote = main_win.subquote.clone();
let mut subquote2 = main_win.subquote2.clone(); let mut subquote2 = main_win.subquote2.clone();
@ -90,7 +85,7 @@ pub(crate) fn spawn_image_thread(
let mut status = main_win.status.clone(); let mut status = main_win.status.clone();
let mut count = main_win.count.clone(); let mut count = main_win.count.clone();
let mut dimension = main_win.dimension.clone(); let mut dimension = main_win.dimension.clone();
let images_list = Arc::clone(&main_win.images_list); let images_path = Arc::clone(&main_win.images_path);
let mut _container: Option<ImageContainer> = None; let mut _container: Option<ImageContainer> = None;
std::thread::spawn(move || loop { std::thread::spawn(move || loop {
@ -100,9 +95,8 @@ pub(crate) fn spawn_image_thread(
status.set_label("Loading..."); status.set_label("Loading...");
load_image( load_image(
&mut file_choice, &mut file_choice,
Arc::clone(&images_list), Arc::clone(&images_path),
None, None,
&mut name_prefix,
&mut quote, &mut quote,
&mut subquote, &mut subquote,
&mut subquote2, &mut subquote2,
@ -134,9 +128,8 @@ pub(crate) fn spawn_image_thread(
status.set_label("Loading..."); status.set_label("Loading...");
load_image( load_image(
&mut file_choice, &mut file_choice,
Arc::clone(&images_list), Arc::clone(&images_path),
Some((x, y)), Some((x, y)),
&mut name_prefix,
&mut quote, &mut quote,
&mut subquote, &mut subquote,
&mut subquote2, &mut subquote2,
@ -176,23 +169,21 @@ pub(crate) fn spawn_image_thread(
status.set_label("Saving..."); status.set_label("Saving...");
win.deactivate(); win.deactivate();
cont.save(); cont.save();
status.set_label("");
win.activate(); win.activate();
win.redraw(); status.set_label("");
app::awake();
} }
} }
DrawMessage::Clone => { DrawMessage::Clone => {
if let Some(cont) = &mut _container { if let Some(cont) = &mut _container {
status.set_label("Cloning..."); status.set_label("Cloning...");
win.deactivate(); win.deactivate();
if let Some(image_info) = cont.clone_img() { if let Some(path) = cont.clone_img() {
let idx = file_choice.value(); let idx = file_choice.value();
let mut imgs = rw_write!(images_list); let mut imgs = images_path.write().unwrap();
imgs.insert(idx as usize, image_info.clone()); imgs.insert(idx as usize, path.clone());
file_choice.insert( file_choice.insert(
idx, idx,
image_info.path.file_name().unwrap().to_str().unwrap(), path.file_name().unwrap().to_str().unwrap(),
enums::Shortcut::None, enums::Shortcut::None,
menu::MenuFlag::Normal, menu::MenuFlag::Normal,
|a| a.do_callback(), |a| a.do_callback(),
@ -201,8 +192,6 @@ pub(crate) fn spawn_image_thread(
} }
status.set_label(""); status.set_label("");
win.activate(); win.activate();
win.redraw();
app::awake();
} }
} }
DrawMessage::Delete => { DrawMessage::Delete => {
@ -210,7 +199,7 @@ pub(crate) fn spawn_image_thread(
status.set_label("Deleting..."); status.set_label("Deleting...");
win.deactivate(); win.deactivate();
cont.delete(); cont.delete();
let mut imgs = rw_write!(images_list); let mut imgs = images_path.write().unwrap();
imgs.remove(file_choice.value() as usize); imgs.remove(file_choice.value() as usize);
file_choice.remove(file_choice.value()); file_choice.remove(file_choice.value());
if file_choice.value() != imgs.len() as i32 { if file_choice.value() != imgs.len() as i32 {
@ -220,17 +209,6 @@ pub(crate) fn spawn_image_thread(
} }
status.set_label(""); status.set_label("");
win.activate(); win.activate();
win.redraw();
app::awake();
}
}
DrawMessage::ShowImagesDetails => show_images_details(Arc::clone(&images_list)),
DrawMessage::CheckImage => {
let (width, height) = rw_read!(properties).original_dimension;
if utils::is_too_small(width, height) {
if let Some(a) = &*rw_read!(globals::MAIN_SENDER) {
a.send(crate::AppMessage::DeleteImage);
}
} }
} }
} }
@ -241,9 +219,8 @@ pub(crate) fn spawn_image_thread(
/// Loads the selected image in file_choice to ImageContainer to edit /// Loads the selected image in file_choice to ImageContainer to edit
fn load_image( fn load_image(
file_choice: &mut menu::Choice, file_choice: &mut menu::Choice,
images_list: Arc<RwLock<Vec<ImageInfo>>>, images_path: Arc<RwLock<Vec<PathBuf>>>,
crop: Option<(f64, f64)>, crop: Option<(f64, f64)>,
name_prefix: &mut Input,
quote: &mut MultilineInput, quote: &mut MultilineInput,
subquote: &mut MultilineInput, subquote: &mut MultilineInput,
subquote2: &mut MultilineInput, subquote2: &mut MultilineInput,
@ -268,36 +245,43 @@ fn load_image(
properties: Arc<RwLock<ImageProperties>>, properties: Arc<RwLock<ImageProperties>>,
container: &mut Option<ImageContainer>, container: &mut Option<ImageContainer>,
) { ) {
let imgs = rw_read!(images_list); let imgs = images_path.read().unwrap();
if imgs.len() == 0 { if imgs.len() == 0 {
*container = None; *container = None;
flush_buffer(app_sender, container); flush_buffer(app_sender, container);
return; return;
} }
count.set_label(&format!("[{}/{}]", file_choice.value() + 1, imgs.len())); count.set_label(&format!("[{}/{}]", file_choice.value() + 1, imgs.len()));
let image_info = imgs.get(file_choice.value() as usize).unwrap(); let file = imgs.get(file_choice.value() as usize).unwrap();
*container = Some(ImageContainer::new(&image_info, Arc::clone(&properties))); *container = Some(ImageContainer::new(&file, Arc::clone(&properties)));
if let Some(cont) = container { if let Some(cont) = container {
let properties_file = utils::get_properties_path(&image_info); let file = Path::new(&file);
let properties_file = file.with_extension("prop");
let read = fs::read_to_string(&properties_file).unwrap_or("{}".to_owned()); let read = fs::read_to_string(&properties_file).unwrap_or("{}".to_owned());
let read = match serde_json::from_str::<ImagePropertiesFile>(&read) { let read = match serde_json::from_str::<ImagePropertiesFile>(&read) {
Ok(r) => r, Ok(r) => r,
Err(e) => { Err(e) => {
Result::<(), _>::Err(e).warn_log("Config is corrupt"); warn!("Config is corrupt\n{:?}", e);
fs::remove_file(&properties_file) match dialog::choice_default("Config is corrupt, fix??", "yes", "no", "") {
.warn_log("Failed to delete image properties file!"); 1 => {
ImagePropertiesFile::default() if let Err(e) = fs::remove_file(&properties_file) {
dialog::alert_default("Failed to delete image properties file!");
warn!("Failed to delete image properties file!\n{:?}", e);
}
ImagePropertiesFile::default()
}
_ => return,
}
} }
}; };
let mut properties = rw_write!(cont.properties); let mut properties = cont.properties.write().unwrap();
properties.merge(read, &tag.value(), &tag2.value()); properties.merge(read, &tag.value(), &tag2.value());
properties.is_saved = true; properties.is_saved = true;
name_prefix.set_value(&properties.name_prefix);
quote.set_value(&properties.quote); quote.set_value(&properties.quote);
subquote.set_value(&properties.subquote); subquote.set_value(&properties.subquote);
subquote2.set_value(&properties.subquote2); subquote2.set_value(&properties.subquote2);
@ -348,7 +332,7 @@ fn load_image(
} }
cont.apply_resize(); cont.apply_resize();
let (width, height) = rw_read!(cont.properties).dimension; let (width, height) = cont.properties.read().unwrap().dimension;
page.col_flex.set_size(&page.image, height as i32); page.col_flex.set_size(&page.image, height as i32);
page.row_flex.set_size(&page.col_flex, width as i32); page.row_flex.set_size(&page.col_flex, width as i32);
page.col_flex.recalc(); page.col_flex.recalc();
@ -358,40 +342,6 @@ fn load_image(
flush_buffer(&app_sender, &container); flush_buffer(&app_sender, &container);
} }
fn show_images_details(images_list: Arc<RwLock<Vec<ImageInfo>>>) {
let mut image_with_quote: usize = 0;
let mut image_without_quote: usize = 0;
let list = rw_read!(images_list);
for image_info in list.iter() {
let properties_file = utils::get_properties_path(&image_info);
let read = fs::read_to_string(&properties_file).unwrap_or("{}".to_owned());
let read = match serde_json::from_str::<ImagePropertiesFile>(&read) {
Ok(r) => r,
Err(_) => {
image_without_quote += 1;
continue;
}
};
if let Some(t) = read.quote {
if t.trim().len() == 0 {
image_without_quote += 1;
} else {
image_with_quote += 1;
}
} else {
image_without_quote += 1;
}
}
utils::show_message(&format!(
"With Quote: {}\nWithout Quote: {}",
image_with_quote, image_without_quote
));
}
/// Flush the Buffer from image container to drawing buffer for fltk /// Flush the Buffer from image container to drawing buffer for fltk
// for drawing buffer for fltk (check in main.rs) // for drawing buffer for fltk (check in main.rs)
fn flush_buffer(app_sender: &app::Sender<crate::AppMessage>, container: &Option<ImageContainer>) { fn flush_buffer(app_sender: &app::Sender<crate::AppMessage>, container: &Option<ImageContainer>) {

View File

@ -1,268 +0,0 @@
/*
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 <https://www.gnu.org/licenses/>
*/
//! Picker to pick config if multiple configs are present or defalut config is not present
use crate::{
config, dialog, globals,
result_ext::ResultExt,
utils::{self, ImageContainer, ImageInfo, ImageProperties, ImagePropertiesFile},
};
use bichannel::Channel;
use fltk::{
app::{self},
button::Button,
enums,
frame::Frame,
group::Flex,
image::SvgImage,
misc::Progress,
prelude::*,
window::Window,
};
use std::{
fs::File,
sync::{Arc, RwLock},
thread,
};
pub(crate) struct ExportAllWindow {
pub(crate) win: Window,
pub(crate) progress: Progress,
pub(crate) image_name: Frame,
pub(crate) close_btn: Button,
pub(crate) images_list: Arc<RwLock<Vec<ImageInfo>>>,
pub(crate) channel: Arc<RwLock<Option<Channel<ThreadMessage, ThreadMessage>>>>,
pub(crate) finished: Arc<RwLock<bool>>,
}
impl ExportAllWindow {
pub(crate) fn new(images_list: Arc<RwLock<Vec<ImageInfo>>>) -> Self {
let mut win = Window::new(0, 0, 500, 130, "Export All").center_screen();
win.set_icon(Some(
SvgImage::from_data(globals::ICON.to_str().unwrap()).unwrap(),
));
let progress_color = if *globals::THEME == config::Themes::Dark
|| *globals::THEME == config::Themes::HighContrast
{
enums::Color::rgb_color(0, 116, 80)
} else {
enums::Color::rgb_color(34, 203, 135)
};
let mut main_flex = Flex::default().size_of_parent().column();
//label
let mut panel_flex = Flex::default().row();
panel_flex.set_size(&Frame::default(), 1);
Frame::default()
.with_label("Exporting all with quotes")
.with_align(enums::Align::Left | enums::Align::Inside);
panel_flex.end();
main_flex.set_size(&panel_flex, 25);
//image name
let mut panel_flex = Flex::default().row();
panel_flex.set_size(&Frame::default(), 1);
let image_name = Frame::default()
.with_label("")
.with_align(enums::Align::Left | enums::Align::Inside);
panel_flex.end();
main_flex.set_size(&panel_flex, 25);
// progress bar
let mut panel_flex = Flex::default().row();
Frame::default();
let mut progress = Progress::default().with_label("Exporting...");
progress.set_maximum(1.0);
progress.set_value(0.0);
progress.set_frame(enums::FrameType::ThinDownBox);
progress.set_selection_color(progress_color);
Frame::default();
panel_flex.set_size(&progress, 490);
panel_flex.end();
main_flex.set_size(&panel_flex, 30);
//close button
let mut panel_flex = Flex::default().row();
Frame::default();
let close_btn = Button::default().with_label("Cancel");
panel_flex.set_size(&Frame::default(), 1);
panel_flex.set_size(&close_btn, 100);
panel_flex.end();
main_flex.set_size(&panel_flex, 30);
main_flex.end();
win.end();
win.make_modal(true);
let mut config_picker = Self {
win,
progress,
image_name,
close_btn,
images_list,
channel: Arc::new(RwLock::new(None)),
finished: Arc::new(RwLock::new(false)),
};
config_picker.event();
config_picker
}
pub(crate) fn export(&mut self) {
self.image_name.set_label("");
self.progress.set_label("Exporting...");
self.close_btn.set_label("Cancel");
self.progress.set_maximum(1.0);
self.progress.set_value(0.0);
*rw_write!(self.finished) = false;
self.win.show();
let (left, right) = bichannel::channel();
*rw_write!(self.channel) = Some(left);
spawn_export_thread(self, right);
while self.win.shown() {
if let Some(channel) = &*rw_read!(self.channel) {
if let Ok(msg) = channel.try_recv() {
match msg {
ThreadMessage::HideWindow => {
self.win.hide();
}
_ => (),
}
}
}
app::wait();
}
}
// Set callbacks of elements
fn event(&mut self) {
// Close Button
let channel = Arc::clone(&self.channel);
let finished = Arc::clone(&self.finished);
let mut win = self.win.clone();
self.close_btn.set_callback(move |_| {
if *rw_read!(finished) == true {
win.hide();
} else if dialog::choice_default("Are you sure?", "Yes", "No") == 0 {
if let Some(c) = &*rw_read!(channel) {
c.send(ThreadMessage::Stop).error_log("Failed to stop task");
}
}
});
// Window Close
let channel = Arc::clone(&self.channel);
let finished = Arc::clone(&self.finished);
self.win.set_callback(move |f| {
if *rw_read!(finished) == true {
f.hide();
} else if dialog::choice_default("Are you sure?", "Yes", "No") == 0 {
if let Some(c) = &*rw_read!(channel) {
c.send(ThreadMessage::Stop).error_log("Failed to stop task");
}
}
});
}
}
pub(crate) enum ThreadMessage {
Stop,
HideWindow,
}
fn spawn_export_thread(
export_all: &mut ExportAllWindow,
channel: Channel<ThreadMessage, ThreadMessage>,
) {
let mut win = export_all.win.clone();
let mut progress = export_all.progress.clone();
let mut image_name = export_all.image_name.clone();
let mut close_btn = export_all.close_btn.clone();
let finished = Arc::clone(&export_all.finished);
let images_list = Arc::clone(&export_all.images_list);
thread::spawn(move || {
let total = rw_read!(images_list).len();
progress.set_maximum(total as f64);
progress.set_value(0.0);
let mut update_progress = |idx: usize| {
progress.set_value(idx as f64 + 1.0);
progress.set_label(&format!("[{}/{}]", idx + 1, total));
};
for (idx, image) in (*rw_read!(images_list)).iter().enumerate() {
image_name.set_label(
image
.path
.file_name()
.unwrap_or_default()
.to_str()
.unwrap_or_default(),
);
let properties = Arc::new(RwLock::new(ImageProperties::default()));
let container = ImageContainer::new(image, properties);
let properties_file = utils::get_properties_path(image);
let read = match File::open(&properties_file) {
Ok(r) => r,
Err(_) => {
update_progress(idx);
continue;
}
};
let read = match serde_json::from_reader::<File, ImagePropertiesFile>(read) {
Ok(r) => r,
Err(_) => {
update_progress(idx);
continue;
}
};
rw_write!(container.properties).merge(read, "", "");
if rw_read!(container.properties).quote.trim().len() == 0 {
update_progress(idx);
continue;
}
container.save();
update_progress(idx);
win.redraw();
app::awake();
if let Ok(msg) = channel.try_recv() {
match msg {
ThreadMessage::Stop => {
channel
.send(ThreadMessage::HideWindow)
.error_log("Failed to close window");
return;
}
_ => (),
}
}
}
image_name.set_label("Finished");
progress.set_value(total as f64);
close_btn.set_label("Close");
*rw_write!(finished) = true;
win.redraw();
app::awake();
});
}

View File

@ -12,7 +12,7 @@
along with Post Maker. If not, see <https://www.gnu.org/licenses/> along with Post Maker. If not, see <https://www.gnu.org/licenses/>
*/ */
use crate::{config, result_ext::ResultExt}; use crate::config;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use rusttype::Font; use rusttype::Font;
use std::{ffi::OsString, io::Read, sync::RwLock}; use std::{ffi::OsString, io::Read, sync::RwLock};
@ -29,23 +29,86 @@ lazy_static! {
pub(crate) static ref CONFIG: RwLock<config::ConfigFile> = pub(crate) static ref CONFIG: RwLock<config::ConfigFile> =
RwLock::new(config::ConfigFile::load()); RwLock::new(config::ConfigFile::load());
/// Main Sender
pub(crate) static ref MAIN_SENDER: RwLock<Option<fltk::app::Sender<crate::AppMessage>>> = RwLock::new(None);
/// TTF Font for Quote /// TTF Font for Quote
pub(crate) static ref FONT_QUOTE: Font<'static> = load_font(rw_read!(CONFIG).quote_font.as_str()); pub(crate) static ref FONT_QUOTE: Font<'static> = {
let mut buffer = Vec::new();
if let Ok(mut file) = std::fs::File::open(CONFIG.read().unwrap().quote_font.as_str()) {
if let Ok(_) = file.read_to_end(&mut buffer) {
if let Some(out) = rusttype::Font::try_from_vec(buffer) {
return out;
}
}
}
rusttype::Font::try_from_vec(
include_bytes!("../assets/fonts/ReenieBeanie-Regular.ttf").to_vec(),
)
.unwrap()
};
/// TTF Font for Subquote /// TTF Font for Subquote
pub(crate) static ref FONT_SUBQUOTE: Font<'static> = load_font(rw_read!(CONFIG).subquote_font.as_str()); pub(crate) static ref FONT_SUBQUOTE: Font<'static> = {
let mut buffer = Vec::new();
if let Ok(mut file) = std::fs::File::open(CONFIG.read().unwrap().subquote_font.as_str())
{
if let Ok(_) = file.read_to_end(&mut buffer) {
if let Some(out) = rusttype::Font::try_from_vec(buffer) {
return out;
}
}
}
rusttype::Font::try_from_vec(
include_bytes!("../assets/fonts/ReenieBeanie-Regular.ttf").to_vec(),
)
.unwrap()
};
/// TTF Font for Subquote 2 /// TTF Font for Subquote 2
pub(crate) static ref FONT_SUBQUOTE2: Font<'static> = load_font(rw_read!(CONFIG).subquote2_font.as_str()); pub(crate) static ref FONT_SUBQUOTE2: Font<'static> = {
let mut buffer = Vec::new();
if let Ok(mut file) =
std::fs::File::open(CONFIG.read().unwrap().subquote2_font.as_str())
{
if let Ok(_) = file.read_to_end(&mut buffer) {
if let Some(out) = rusttype::Font::try_from_vec(buffer) {
return out;
}
}
}
rusttype::Font::try_from_vec(
include_bytes!("../assets/fonts/Rajdhani-Regular.ttf").to_vec(),
)
.unwrap()
};
/// TTF Font for Tag /// TTF Font for Tag
pub(crate) static ref FONT_TAG: Font<'static> = load_font(rw_read!(CONFIG).tag_font.as_str()); pub(crate) static ref FONT_TAG: Font<'static> = {
let mut buffer = Vec::new();
if let Ok(mut file) = std::fs::File::open(&CONFIG.read().unwrap().tag_font.as_str()) {
if let Ok(_) = file.read_to_end(&mut buffer) {
if let Some(out) = rusttype::Font::try_from_vec(buffer) {
return out;
}
}
}
rusttype::Font::try_from_vec(include_bytes!("../assets/fonts/Kalam-Regular.ttf").to_vec())
.unwrap()
};
/// TTF Font for Tag 2 /// TTF Font for Tag 2
pub(crate) static ref FONT_TAG2: Font<'static> = load_font(rw_read!(CONFIG).tag2_font.as_str()); pub(crate) static ref FONT_TAG2: Font<'static> = {
let mut buffer = Vec::new();
if let Ok(mut file) = std::fs::File::open(&CONFIG.read().unwrap().tag2_font.as_str()) {
if let Ok(_) = file.read_to_end(&mut buffer) {
if let Some(out) = rusttype::Font::try_from_vec(buffer) {
return out;
}
}
}
rusttype::Font::try_from_vec(
include_bytes!("../assets/fonts/Rajdhani-Regular.ttf").to_vec(),
)
.unwrap()
};
/// Image to use for Window /// Image to use for Window
pub(crate) static ref ICON: OsString = include_str!("../assets/icon.svg").into(); pub(crate) static ref ICON: OsString = include_str!("../assets/icon.svg").into();
@ -62,15 +125,3 @@ lazy_static! {
img.into() img.into()
}; };
} }
fn load_font(path: &str) -> Font<'static> {
let mut buffer = Vec::new();
if let Ok(mut file) = std::fs::File::open(path) {
if let Ok(_) = file.read_to_end(&mut buffer) {
if let Some(out) = rusttype::Font::try_from_vec(buffer) {
return out;
}
}
}
rusttype::Font::try_from_vec(include_bytes!("../assets/OpenSans-Regular.ttf").to_vec()).unwrap()
}

View File

@ -1,27 +0,0 @@
/*
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 <https://www.gnu.org/licenses/>
*/
#[macro_export]
macro_rules! rw_read {
($var:expr) => {
$var.read().expect_log("Program got panic!")
};
}
#[macro_export]
macro_rules! rw_write {
($var:expr) => {
$var.write().expect_log("Program got panic!")
};
}

View File

@ -17,29 +17,23 @@
extern crate log; extern crate log;
extern crate simplelog; extern crate simplelog;
#[macro_use] mod about;
mod macros;
mod about_window;
mod config; mod config;
mod config_picker; mod config_picker;
mod config_window; mod config_window;
mod crop_window; mod crop_window;
mod dialog;
mod draw_thread; mod draw_thread;
mod export_all_window;
mod globals; mod globals;
mod main_window; mod main_window;
mod result_ext;
mod utils; mod utils;
use fltk::{ use fltk::{
app::{channel, App}, app::{channel, App},
dialog,
prelude::*, prelude::*,
}; };
use fltk_theme::WidgetTheme; use fltk_theme::WidgetTheme;
use main_window::MainWindow; use main_window::MainWindow;
use result_ext::ResultExt;
use simplelog::*; use simplelog::*;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
@ -47,27 +41,17 @@ use std::sync::{Arc, RwLock};
pub(crate) enum AppMessage { pub(crate) enum AppMessage {
/// Copy recived image buffer from draw_thread to Buffer for fltk frame /// Copy recived image buffer from draw_thread to Buffer for fltk frame
RedrawMainWindowImage(Option<Vec<u8>>), RedrawMainWindowImage(Option<Vec<u8>>),
Message(String),
Alert(String),
ProgramPanicMessage(String),
// Only for Main windows
DeleteImage,
} }
fn main() { fn main() {
let app = App::default(); let app = App::default();
WidgetTheme::new(globals::THEME.clone().into()).apply(); WidgetTheme::new(globals::THEME.clone().into()).apply();
if let Err(e) = CombinedLogger::init(vec![ if let Err(e) = CombinedLogger::init(vec![WriteLogger::new(
WriteLogger::new(LevelFilter::Warn, Config::default(), config::log_file()), LevelFilter::Info,
TermLogger::new( Config::default(),
LevelFilter::Info, config::log_file(),
Config::default(), )]) {
TerminalMode::Mixed,
ColorChoice::Auto,
),
]) {
dialog::alert_default("Failed to start logger"); dialog::alert_default("Failed to start logger");
panic!("Failed to start logger\n{:?}", e); panic!("Failed to start logger\n{:?}", e);
} }
@ -78,41 +62,16 @@ fn main() {
let draw_buff: Arc<RwLock<Option<Vec<u8>>>> = Arc::new(RwLock::new(None)); let draw_buff: Arc<RwLock<Option<Vec<u8>>>> = Arc::new(RwLock::new(None));
let (main_sender, main_receiver) = channel::<AppMessage>(); let (main_sender, main_receiver) = channel::<AppMessage>();
*rw_write!(globals::MAIN_SENDER) = Some(main_sender); let mut main_window = MainWindow::new(main_sender, Arc::clone(&draw_buff));
let mut main_window = MainWindow::new(Arc::clone(&draw_buff));
while app.wait() { while app.wait() {
if let Some(msg) = main_receiver.recv() { if let Some(msg) = main_receiver.recv() {
match msg { match msg {
AppMessage::RedrawMainWindowImage(data) => { AppMessage::RedrawMainWindowImage(data) => {
let mut buff = rw_write!(draw_buff); let mut buff = draw_buff.write().unwrap();
*buff = data; *buff = data;
main_window.win.redraw(); main_window.win.redraw();
} }
AppMessage::Message(msg) => {
dialog::message_default(&msg);
}
AppMessage::Alert(msg) => dialog::alert_default(&msg),
AppMessage::ProgramPanicMessage(msg) => {
dialog::message_default(&msg);
std::process::exit(1);
}
// Fltk does not show dialogs from other thread in windows, So this is hack to get things done
AppMessage::DeleteImage => {
let ch = dialog::choice_default("Image is too small", "Delete", "Keep");
if ch == 0 {
main_window
.sender
.send(draw_thread::DrawMessage::Delete)
.unwrap();
main_window
.sender
.send(draw_thread::DrawMessage::Open)
.unwrap();
main_window.page.image.redraw();
main_window.file_choice.redraw();
}
}
} }
} }
} }

View File

@ -13,19 +13,16 @@
*/ */
//! Main window where you do all editing //! Main window where you do all editing
use crate::{ use crate::about::About;
about_window::About, use crate::crop_window::CropWindow;
config_window::ConfigWindow, use crate::draw_thread::*;
crop_window::CropWindow, use crate::utils;
dialog, use crate::utils::ImageProperties;
draw_thread::*, use crate::{config_window::ConfigWindow, globals};
export_all_window::ExportAllWindow,
globals,
result_ext::ResultExt,
utils::{self, ImageInfo, ImageProperties, ImageType},
};
use fltk::{ use fltk::{
app,
button::Button, button::Button,
dialog,
dialog::NativeFileChooser, dialog::NativeFileChooser,
draw as dr, enums, draw as dr, enums,
enums::Shortcut, enums::Shortcut,
@ -39,14 +36,9 @@ use fltk::{
valuator::{Slider, SliderType}, valuator::{Slider, SliderType},
window::Window, window::Window,
}; };
use std::{ use std::path::PathBuf;
ffi::OsStr, use std::sync::{mpsc, RwLock};
fs, use std::{ffi::OsStr, fs, sync::Arc};
path::PathBuf,
process::Command,
sync::Arc,
sync::{mpsc, RwLock},
};
pub(crate) struct MainWindow { pub(crate) struct MainWindow {
pub(crate) win: Window, pub(crate) win: Window,
@ -56,7 +48,6 @@ pub(crate) struct MainWindow {
pub(crate) save_btn: Button, pub(crate) save_btn: Button,
/// To choose the file which is being edited in directory /// To choose the file which is being edited in directory
pub(crate) file_choice: menu::Choice, pub(crate) file_choice: menu::Choice,
pub(crate) name_prefix: Input,
pub(crate) quote: MultilineInput, pub(crate) quote: MultilineInput,
pub(crate) subquote: MultilineInput, pub(crate) subquote: MultilineInput,
pub(crate) subquote2: MultilineInput, pub(crate) subquote2: MultilineInput,
@ -90,7 +81,7 @@ pub(crate) struct MainWindow {
pub(crate) count: Frame, pub(crate) count: Frame,
pub(crate) dimension: Frame, pub(crate) dimension: Frame,
pub(crate) page: Page, pub(crate) page: Page,
pub(crate) images_list: Arc<RwLock<Vec<ImageInfo>>>, pub(crate) images_path: Arc<RwLock<Vec<PathBuf>>>,
pub(crate) draw_buff: Arc<RwLock<Option<Vec<u8>>>>, pub(crate) draw_buff: Arc<RwLock<Option<Vec<u8>>>>,
pub(crate) properties: Arc<RwLock<ImageProperties>>, pub(crate) properties: Arc<RwLock<ImageProperties>>,
pub(crate) sender: mpsc::Sender<DrawMessage>, pub(crate) sender: mpsc::Sender<DrawMessage>,
@ -105,7 +96,10 @@ pub(crate) struct Page {
} }
impl MainWindow { impl MainWindow {
pub(crate) fn new(draw_buff: Arc<RwLock<Option<Vec<u8>>>>) -> Self { pub(crate) fn new(
sender: app::Sender<crate::AppMessage>,
draw_buff: Arc<RwLock<Option<Vec<u8>>>>,
) -> Self {
let mut win = Window::new(0, 0, 1100, 700, "Post Maker").center_screen(); let mut win = Window::new(0, 0, 1100, 700, "Post Maker").center_screen();
win.set_icon(Some( win.set_icon(Some(
SvgImage::from_data(globals::ICON.to_str().unwrap()).unwrap(), SvgImage::from_data(globals::ICON.to_str().unwrap()).unwrap(),
@ -134,15 +128,6 @@ impl MainWindow {
let mut workspace_flex = Flex::default().row(); let mut workspace_flex = Flex::default().row();
// Controls Left // Controls Left
let mut left_controls_flex = Flex::default().column(); let mut left_controls_flex = Flex::default().column();
left_controls_flex.set_size(
&Frame::default()
.with_label("Name Prefix:")
.with_align(enums::Align::Left | enums::Align::Inside),
25,
);
let name_prefix = Input::default();
left_controls_flex.set_size(&name_prefix, 30);
left_controls_flex.set_size( left_controls_flex.set_size(
&Frame::default() &Frame::default()
.with_label("Quote:") .with_label("Quote:")
@ -365,7 +350,6 @@ impl MainWindow {
next_btn, next_btn,
save_btn, save_btn,
file_choice, file_choice,
name_prefix,
quote, quote,
subquote, subquote,
subquote2, subquote2,
@ -396,7 +380,7 @@ impl MainWindow {
status, status,
count, count,
dimension, dimension,
images_list: Arc::new(RwLock::new(vec![])), images_path: Arc::new(RwLock::new(vec![])),
draw_buff, draw_buff,
properties: Arc::clone(&properties), properties: Arc::clone(&properties),
page: Page { page: Page {
@ -406,10 +390,7 @@ impl MainWindow {
}, },
sender: rx, sender: rx,
}; };
spawn_image_thread(tx, sender, Arc::clone(&properties), &main_win);
if let Some(a) = &*rw_read!(globals::MAIN_SENDER) {
spawn_image_thread(tx, a.to_owned(), Arc::clone(&properties), &main_win);
}
main_win.menu(); main_win.menu();
main_win.draw(); main_win.draw();
main_win.events(); main_win.events();
@ -420,8 +401,7 @@ impl MainWindow {
fn menu(&mut self) { fn menu(&mut self) {
let mut file_choice = self.file_choice.clone(); let mut file_choice = self.file_choice.clone();
let sender = self.sender.clone(); let sender = self.sender.clone();
let imgs = Arc::clone(&self.images_list); let imgs = Arc::clone(&self.images_path);
let mut win = self.win.clone();
self.menubar.add( self.menubar.add(
"&File/Open Folder...\t", "&File/Open Folder...\t",
Shortcut::Ctrl | 'o', Shortcut::Ctrl | 'o',
@ -435,12 +415,14 @@ impl MainWindow {
if !path.exists() { if !path.exists() {
return; return;
} }
win.set_label(&format!( let expost_dir = path.join("export");
"{} - Post Maker", if !expost_dir.exists() {
path.file_name() if let Err(e) = fs::create_dir(expost_dir) {
.unwrap_or(OsStr::new("Unknown")) fltk::dialog::alert_default("Failed to create export folder!");
.to_string_lossy() warn!("Failed to create export folder!\n{:?}", e);
)); return;
}
}
load_dir(&path, Arc::clone(&imgs), &mut file_choice, &sender); load_dir(&path, Arc::clone(&imgs), &mut file_choice, &sender);
}, },
); );
@ -448,93 +430,13 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let properties = Arc::clone(&self.properties); let properties = Arc::clone(&self.properties);
self.menubar.add( self.menubar.add(
"&File/Save Image...\t", "&File/Save...\t",
Shortcut::Ctrl | 's', Shortcut::Ctrl | 's',
menu::MenuFlag::Normal, menu::MenuFlag::Normal,
move |_| { move |_| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.is_saved = true; prop.is_saved = true;
sender.send_it(DrawMessage::Save); 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_it(DrawMessage::ShowImagesDetails);
},
);
let properties = Arc::clone(&self.properties);
self.menubar.add(
"&Actions/Open Exports Folder...\t",
Shortcut::None,
menu::MenuFlag::Normal,
move |_| {
let props = rw_read!(properties);
if let Some(prop) = &props.image_info {
let export = prop.path.parent().unwrap().join("export");
if export.exists() {
if cfg!(windows) {
Command::new("explorer")
.arg(export.to_str().unwrap_or_default())
.spawn()
.warn_log("Failed top spawn command");
} else if cfg!(unix) {
Command::new("xdg-open")
.arg(export.to_str().unwrap_or_default())
.spawn()
.warn_log("Failed top spawn command");
} else if cfg!(macos) {
Command::new("open")
.arg(export.to_str().unwrap_or_default())
.spawn()
.warn_log("Failed top spawn command");
} else {
dialog::alert_default("Unknown Operating System")
}
}
}
},
);
let mut win = self.win.clone();
let mut export_all = ExportAllWindow::new(Arc::clone(&self.images_list));
self.menubar.add(
"&Actions/Export All with Quotes...\t",
Shortcut::None,
menu::MenuFlag::Normal,
move |_| {
export_all.export();
win.redraw();
fltk::app::awake();
},
);
let properties = Arc::clone(&self.properties);
let mut win = self.win.clone();
self.menubar.add(
"&Actions/Delete Exports...\t",
Shortcut::None,
menu::MenuFlag::Normal,
move |_| {
win.deactivate();
if dialog::choice_default("Do you want to remove exports?", "Yes", "No") == 0 {
let props = rw_read!(properties);
if let Some(prop) = &props.image_info {
let export = prop.path.parent().unwrap().join("export");
if export.exists() {
fs::remove_dir_all(&export)
.warn_log("Failed to remove export directory");
}
}
}
win.activate();
win.redraw();
fltk::app::awake();
}, },
); );
@ -547,8 +449,8 @@ impl MainWindow {
menu::MenuFlag::Normal, menu::MenuFlag::Normal,
move |_| { move |_| {
if config_window.show() { if config_window.show() {
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
} }
}, },
@ -570,8 +472,8 @@ impl MainWindow {
let buff = Arc::clone(&self.draw_buff); let buff = Arc::clone(&self.draw_buff);
let properties = Arc::clone(&self.properties); let properties = Arc::clone(&self.properties);
self.page.image.draw(move |f| { self.page.image.draw(move |f| {
let (width, height) = rw_read!(properties).dimension; let (width, height) = properties.read().unwrap().dimension;
if let Some(image) = &*rw_read!(buff) { if let Some(image) = &*buff.read().unwrap() {
dr::draw_image( dr::draw_image(
&image, &image,
f.x(), f.x(),
@ -590,10 +492,10 @@ impl MainWindow {
// Resest Button for FileChoice // Resest Button for FileChoice
let mut file_choice = self.file_choice.clone(); let mut file_choice = self.file_choice.clone();
let sender = self.sender.clone(); let sender = self.sender.clone();
let imgs = Arc::clone(&self.images_list); let imgs = Arc::clone(&self.images_path);
self.reset_file_choice.set_callback(move |_| { self.reset_file_choice.set_callback(move |_| {
let path = match rw_read!(imgs).first() { let path = match imgs.read().unwrap().first() {
Some(image_info) => image_info.path.parent().unwrap().to_path_buf(), Some(path) => path.parent().unwrap().to_path_buf(),
None => return, None => return,
}; };
load_dir(&path, Arc::clone(&imgs), &mut file_choice, &sender); load_dir(&path, Arc::clone(&imgs), &mut file_choice, &sender);
@ -606,14 +508,14 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let properties = Arc::clone(&self.properties); let properties = Arc::clone(&self.properties);
self.reset_translucent_layer_btn.set_callback(move |_| { self.reset_translucent_layer_btn.set_callback(move |_| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
let color = rw_read!(globals::CONFIG).color_layer; let color = globals::CONFIG.read().unwrap().color_layer;
prop.translucent_layer_color = color; prop.translucent_layer_color = color;
prop.is_saved = false; prop.is_saved = false;
utils::set_color_btn_rgba(color, &mut layer_rgb); utils::set_color_btn_rgba(color, &mut layer_rgb);
layer_alpha.set_value(color[3] as f64); layer_alpha.set_value(color[3] as f64);
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -624,16 +526,16 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let properties = Arc::clone(&self.properties); let properties = Arc::clone(&self.properties);
self.reset_quote_position_btn.set_callback(move |_| { self.reset_quote_position_btn.set_callback(move |_| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
let height = prop.original_dimension.1; let height = prop.original_dimension.1;
let pos = height * rw_read!(globals::CONFIG).quote_position_ratio; let pos = height * globals::CONFIG.read().unwrap().quote_position_ratio;
prop.quote_position = pos; prop.quote_position = pos;
prop.is_saved = false; prop.is_saved = false;
quote_position.set_value(pos); quote_position.set_value(pos);
quote_position_slider.set_value(pos); quote_position_slider.set_value(pos);
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -644,16 +546,16 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let properties = Arc::clone(&self.properties); let properties = Arc::clone(&self.properties);
self.reset_subquote_position_btn.set_callback(move |_| { self.reset_subquote_position_btn.set_callback(move |_| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
let height = prop.original_dimension.1; let height = prop.original_dimension.1;
let pos = height * rw_read!(globals::CONFIG).subquote_position_ratio; let pos = height * globals::CONFIG.read().unwrap().subquote_position_ratio;
prop.subquote_position = pos; prop.subquote_position = pos;
prop.is_saved = false; prop.is_saved = false;
subquote_position.set_value(pos); subquote_position.set_value(pos);
subquote_position_slider.set_value(pos); subquote_position_slider.set_value(pos);
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -664,16 +566,16 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let properties = Arc::clone(&self.properties); let properties = Arc::clone(&self.properties);
self.reset_subquote2_position_btn.set_callback(move |_| { self.reset_subquote2_position_btn.set_callback(move |_| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
let height = prop.original_dimension.1; let height = prop.original_dimension.1;
let pos = height * rw_read!(globals::CONFIG).subquote2_position_ratio; let pos = height * globals::CONFIG.read().unwrap().subquote2_position_ratio;
prop.subquote2_position = pos; prop.subquote2_position = pos;
prop.is_saved = false; prop.is_saved = false;
subquote2_position.set_value(pos); subquote2_position.set_value(pos);
subquote2_position_slider.set_value(pos); subquote2_position_slider.set_value(pos);
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -684,16 +586,16 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let properties = Arc::clone(&self.properties); let properties = Arc::clone(&self.properties);
self.reset_tag_position_btn.set_callback(move |_| { self.reset_tag_position_btn.set_callback(move |_| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
let height = prop.original_dimension.1; let height = prop.original_dimension.1;
let pos = height * rw_read!(globals::CONFIG).tag_y_position_ratio; let pos = height * globals::CONFIG.read().unwrap().tag_position_ratio;
prop.tag_position = pos; prop.tag_position = pos;
prop.is_saved = false; prop.is_saved = false;
tag_position.set_value(pos); tag_position.set_value(pos);
tag_position_slider.set_value(pos); tag_position_slider.set_value(pos);
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -704,16 +606,16 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let properties = Arc::clone(&self.properties); let properties = Arc::clone(&self.properties);
self.reset_tag2_position_btn.set_callback(move |_| { self.reset_tag2_position_btn.set_callback(move |_| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
let height = prop.original_dimension.1; let height = prop.original_dimension.1;
let pos = height * rw_read!(globals::CONFIG).tag2_position_ratio; let pos = height * globals::CONFIG.read().unwrap().tag2_position_ratio;
prop.tag2_position = pos; prop.tag2_position = pos;
prop.is_saved = false; prop.is_saved = false;
tag2_position.set_value(pos); tag2_position.set_value(pos);
tag2_position_slider.set_value(pos); tag2_position_slider.set_value(pos);
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -721,9 +623,9 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let properties = Arc::clone(&self.properties); let properties = Arc::clone(&self.properties);
self.save_btn.set_callback(move |_| { self.save_btn.set_callback(move |_| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.is_saved = true; prop.is_saved = true;
sender.send_it(DrawMessage::Save); sender.send(DrawMessage::Save).unwrap()
}); });
// Clone Button // Clone Button
@ -731,11 +633,10 @@ impl MainWindow {
let mut file_choice = self.file_choice.clone(); let mut file_choice = self.file_choice.clone();
let sender = self.sender.clone(); let sender = self.sender.clone();
self.clone_btn.set_callback(move |_| { self.clone_btn.set_callback(move |_| {
let ch = dialog::choice_default("Do you want to clone??", "Yes", "No"); let ch = dialog::choice_default("Do you want to clone??", "Yes", "No", "");
if ch == 0 { if ch == 0 {
sender.send_it(DrawMessage::Clone); sender.send(DrawMessage::Clone).unwrap();
sender.send_it(DrawMessage::Open); sender.send(DrawMessage::Open).unwrap();
sender.send_it(DrawMessage::CheckImage);
image.redraw(); image.redraw();
file_choice.redraw(); file_choice.redraw();
} }
@ -746,11 +647,10 @@ impl MainWindow {
let mut file_choice = self.file_choice.clone(); let mut file_choice = self.file_choice.clone();
let sender = self.sender.clone(); let sender = self.sender.clone();
self.delete_btn.set_callback(move |_| { self.delete_btn.set_callback(move |_| {
let ch = dialog::choice_default("Do you want to delete??", "Yes", "No"); let ch = dialog::choice_default("Do you want to delete??", "Yes", "No", "");
if ch == 0 { if ch == 0 {
sender.send_it(DrawMessage::Delete); sender.send(DrawMessage::Delete).unwrap();
sender.send_it(DrawMessage::Open); sender.send(DrawMessage::Open).unwrap();
sender.send_it(DrawMessage::CheckImage);
image.redraw(); image.redraw();
file_choice.redraw(); file_choice.redraw();
} }
@ -761,10 +661,10 @@ impl MainWindow {
let mut crop_win = CropWindow::new(); let mut crop_win = CropWindow::new();
let sender = self.sender.clone(); let sender = self.sender.clone();
self.crop_btn.set_callback(move |_| { self.crop_btn.set_callback(move |_| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
if let Some(image_info) = &prop.image_info { if let Some(path) = &prop.path {
if let Some((x, y)) = crop_win.load_to_crop(&image_info, prop.crop_position) { if let Some((x, y)) = crop_win.load_to_crop(path, prop.crop_position) {
sender.send_it(DrawMessage::ChangeCrop((x, y))); sender.send(DrawMessage::ChangeCrop((x, y))).unwrap();
prop.is_saved = false; prop.is_saved = false;
} }
} }
@ -775,11 +675,11 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let properties = Arc::clone(&self.properties); let properties = Arc::clone(&self.properties);
self.next_btn.set_callback(move |_| { self.next_btn.set_callback(move |_| {
let prop = rw_read!(properties); let prop = properties.read().unwrap();
if !prop.is_saved { if !prop.is_saved {
let save = fltk::dialog::choice2_default("Save?", "yes", "no", "cancel"); let save = fltk::dialog::choice_default("Save?", "yes", "no", "cancel");
match save.unwrap_or(-1) { match save {
0 => sender.send_it(DrawMessage::Save), 0 => sender.send(DrawMessage::Save).unwrap(),
1 => {} 1 => {}
_ => return, _ => return,
} }
@ -790,8 +690,7 @@ impl MainWindow {
} else { } else {
file_choice.set_value(file_choice.value() + 1); file_choice.set_value(file_choice.value() + 1);
} }
sender.send_it(DrawMessage::Open); sender.send(DrawMessage::Open).unwrap();
sender.send_it(DrawMessage::CheckImage);
}); });
// Back Image Button // Back Image Button
@ -799,11 +698,11 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let properties = Arc::clone(&self.properties); let properties = Arc::clone(&self.properties);
self.back_btn.set_callback(move |_| { self.back_btn.set_callback(move |_| {
let prop = rw_read!(properties); let prop = properties.read().unwrap();
if !prop.is_saved { if !prop.is_saved {
let save = fltk::dialog::choice2_default("Save?", "yes", "no", "cancel"); let save = fltk::dialog::choice_default("Save?", "yes", "no", "cancel");
match save.unwrap_or(-1) { match save {
0 => sender.send_it(DrawMessage::Save), 0 => sender.send(DrawMessage::Save).unwrap(),
1 => {} 1 => {}
_ => return, _ => return,
} }
@ -814,36 +713,23 @@ impl MainWindow {
} else { } else {
file_choice.set_value(file_choice.value() - 1); file_choice.set_value(file_choice.value() - 1);
} }
sender.send_it(DrawMessage::Open); sender.send(DrawMessage::Open).unwrap();
sender.send_it(DrawMessage::CheckImage);
}); });
// File Choice // File Choice
let sender = self.sender.clone(); let sender = self.sender.clone();
let properties = Arc::clone(&self.properties); let properties = Arc::clone(&self.properties);
self.file_choice.set_callback(move |_| { self.file_choice.set_callback(move |_| {
let prop = rw_read!(properties); let prop = properties.read().unwrap();
if !prop.is_saved { if !prop.is_saved {
let save = fltk::dialog::choice2_default("Save?", "yes", "no", "cancel"); let save = fltk::dialog::choice_default("Save?", "yes", "no", "cancel");
match save.unwrap_or(-1) { match save {
0 => sender.send_it(DrawMessage::Save), 0 => sender.send(DrawMessage::Save).unwrap(),
1 => {} 1 => {}
_ => return, _ => return,
} }
} }
sender.send_it(DrawMessage::Open); sender.send(DrawMessage::Open).unwrap();
sender.send_it(DrawMessage::CheckImage);
});
// Name Prefix Input
let properties = Arc::clone(&self.properties);
self.name_prefix.handle(move |f, ev| {
if ev == enums::Event::KeyUp {
let mut prop = rw_write!(properties);
prop.name_prefix = f.value();
prop.is_saved = false;
}
true
}); });
// Quote Input // Quote Input
@ -852,11 +738,11 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
self.quote.handle(move |f, ev| { self.quote.handle(move |f, ev| {
if ev == enums::Event::KeyUp { if ev == enums::Event::KeyUp {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.quote = f.value(); prop.quote = f.value();
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
} }
true true
@ -868,11 +754,11 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
self.subquote.handle(move |f, ev| { self.subquote.handle(move |f, ev| {
if ev == enums::Event::KeyUp { if ev == enums::Event::KeyUp {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.subquote = f.value(); prop.subquote = f.value();
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
} }
true true
@ -884,11 +770,11 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
self.subquote2.handle(move |f, ev| { self.subquote2.handle(move |f, ev| {
if ev == enums::Event::KeyUp { if ev == enums::Event::KeyUp {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.subquote2 = f.value(); prop.subquote2 = f.value();
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
} }
true true
@ -900,11 +786,11 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
self.tag.handle(move |f, ev| { self.tag.handle(move |f, ev| {
if ev == enums::Event::KeyUp { if ev == enums::Event::KeyUp {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.tag = f.value(); prop.tag = f.value();
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
} }
true true
@ -916,11 +802,11 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
self.tag2.handle(move |f, ev| { self.tag2.handle(move |f, ev| {
if ev == enums::Event::KeyUp { if ev == enums::Event::KeyUp {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.tag2 = f.value(); prop.tag2 = f.value();
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
} }
true true
@ -932,12 +818,12 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let mut quote_position_slider = self.quote_position_slider.clone(); let mut quote_position_slider = self.quote_position_slider.clone();
self.quote_position.set_callback(move |f| { self.quote_position.set_callback(move |f| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.quote_position = f.value(); prop.quote_position = f.value();
quote_position_slider.set_value(f.value()); quote_position_slider.set_value(f.value());
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -947,12 +833,12 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let mut quote_position = self.quote_position.clone(); let mut quote_position = self.quote_position.clone();
self.quote_position_slider.set_callback(move |f| { self.quote_position_slider.set_callback(move |f| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.quote_position = f.value(); prop.quote_position = f.value();
quote_position.set_value(f.value()); quote_position.set_value(f.value());
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -962,12 +848,12 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let mut subquote_position_slider = self.subquote_position_slider.clone(); let mut subquote_position_slider = self.subquote_position_slider.clone();
self.subquote_position.set_callback(move |f| { self.subquote_position.set_callback(move |f| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.subquote_position = f.value(); prop.subquote_position = f.value();
subquote_position_slider.set_value(f.value()); subquote_position_slider.set_value(f.value());
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -977,12 +863,12 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let mut subquote_position = self.subquote_position.clone(); let mut subquote_position = self.subquote_position.clone();
self.subquote_position_slider.set_callback(move |f| { self.subquote_position_slider.set_callback(move |f| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.subquote_position = f.value(); prop.subquote_position = f.value();
subquote_position.set_value(f.value()); subquote_position.set_value(f.value());
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -992,12 +878,12 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let mut subquote2_position_slider = self.subquote2_position_slider.clone(); let mut subquote2_position_slider = self.subquote2_position_slider.clone();
self.subquote2_position.set_callback(move |f| { self.subquote2_position.set_callback(move |f| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.subquote2_position = f.value(); prop.subquote2_position = f.value();
subquote2_position_slider.set_value(f.value()); subquote2_position_slider.set_value(f.value());
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -1007,12 +893,12 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let mut subquote2_position = self.subquote2_position.clone(); let mut subquote2_position = self.subquote2_position.clone();
self.subquote2_position_slider.set_callback(move |f| { self.subquote2_position_slider.set_callback(move |f| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.subquote2_position = f.value(); prop.subquote2_position = f.value();
subquote2_position.set_value(f.value()); subquote2_position.set_value(f.value());
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -1022,12 +908,12 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let mut tag_position_slider = self.tag_position_slider.clone(); let mut tag_position_slider = self.tag_position_slider.clone();
self.tag_position.set_callback(move |f| { self.tag_position.set_callback(move |f| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.tag_position = f.value(); prop.tag_position = f.value();
tag_position_slider.set_value(f.value()); tag_position_slider.set_value(f.value());
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -1037,12 +923,12 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let mut tag_position = self.tag_position.clone(); let mut tag_position = self.tag_position.clone();
self.tag_position_slider.set_callback(move |f| { self.tag_position_slider.set_callback(move |f| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.tag_position = f.value(); prop.tag_position = f.value();
tag_position.set_value(f.value()); tag_position.set_value(f.value());
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -1052,12 +938,12 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let mut tag2_position_slider = self.tag2_position_slider.clone(); let mut tag2_position_slider = self.tag2_position_slider.clone();
self.tag2_position.set_callback(move |f| { self.tag2_position.set_callback(move |f| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.tag2_position = f.value(); prop.tag2_position = f.value();
tag2_position_slider.set_value(f.value()); tag2_position_slider.set_value(f.value());
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -1067,12 +953,12 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
let mut tag2_position = self.tag2_position.clone(); let mut tag2_position = self.tag2_position.clone();
self.tag2_position_slider.set_callback(move |f| { self.tag2_position_slider.set_callback(move |f| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.tag2_position = f.value(); prop.tag2_position = f.value();
tag2_position.set_value(f.value()); tag2_position.set_value(f.value());
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -1081,7 +967,7 @@ impl MainWindow {
let properties = Arc::clone(&self.properties); let properties = Arc::clone(&self.properties);
let sender = self.sender.clone(); let sender = self.sender.clone();
self.translucent_layer_rgb.set_callback(move |mut f| { self.translucent_layer_rgb.set_callback(move |mut f| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
let (r, g, b) = dialog::color_chooser_with_default( let (r, g, b) = dialog::color_chooser_with_default(
"Pick a colour", "Pick a colour",
dialog::ColorMode::Byte, dialog::ColorMode::Byte,
@ -1095,8 +981,8 @@ impl MainWindow {
utils::set_color_btn_rgba(prop.translucent_layer_color, &mut f); utils::set_color_btn_rgba(prop.translucent_layer_color, &mut f);
f.redraw(); f.redraw();
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
@ -1105,11 +991,11 @@ impl MainWindow {
let properties = Arc::clone(&self.properties); let properties = Arc::clone(&self.properties);
let sender = self.sender.clone(); let sender = self.sender.clone();
self.translucent_layer_alpha.set_callback(move |f| { self.translucent_layer_alpha.set_callback(move |f| {
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.translucent_layer_color[3] = f.value() as u8; prop.translucent_layer_color[3] = f.value() as u8;
prop.is_saved = false; prop.is_saved = false;
sender.send_it(DrawMessage::RedrawToBuffer); sender.send(DrawMessage::RedrawToBuffer).unwrap();
sender.send_it(DrawMessage::Flush); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
}); });
} }
@ -1118,7 +1004,7 @@ impl MainWindow {
/// Load all iamges in a directory /// Load all iamges in a directory
fn load_dir( fn load_dir(
path: &PathBuf, path: &PathBuf,
imgs: Arc<RwLock<Vec<ImageInfo>>>, imgs: Arc<RwLock<Vec<PathBuf>>>,
file_choice: &mut menu::Choice, file_choice: &mut menu::Choice,
sender: &mpsc::Sender<DrawMessage>, sender: &mpsc::Sender<DrawMessage>,
) { ) {
@ -1128,22 +1014,15 @@ fn load_dir(
.collect::<Vec<fs::DirEntry>>(); .collect::<Vec<fs::DirEntry>>();
files.sort_by_key(|i| i.file_name()); files.sort_by_key(|i| i.file_name());
let mut text = String::new(); let mut text = String::new();
let mut imgs_b = rw_write!(imgs); let mut imgs_b = imgs.write().unwrap();
*imgs_b = vec![]; *imgs_b = vec![];
for file in files { for file in files {
let path = file.path(); let path = file.path();
if let Ok(Some(ty)) = infer::get_from_path(&path) { if path.extension() == Some(OsStr::new("jpg"))
let mime = ty.mime_type(); || path.extension() == Some(OsStr::new("png"))
match ImageType::from_mime(mime) { {
ImageType::None => (), text = format!("{}|{}", text, path.file_name().unwrap().to_str().unwrap());
_ => { imgs_b.push(path);
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 { if text.len() == 0 {
@ -1152,16 +1031,5 @@ fn load_dir(
file_choice.clear(); file_choice.clear();
file_choice.add_choice(&text[1..]); file_choice.add_choice(&text[1..]);
file_choice.set_value(0); file_choice.set_value(0);
sender.send_it(DrawMessage::Open); sender.send(DrawMessage::Open).unwrap();
sender.send_it(DrawMessage::CheckImage);
}
trait SenderExt {
fn send_it(&self, a: DrawMessage);
}
impl SenderExt for mpsc::Sender<DrawMessage> {
fn send_it(&self, a: DrawMessage) {
self.send(a).expect_log("Program panic!");
}
} }

View File

@ -1,52 +0,0 @@
/*
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 <https://www.gnu.org/licenses/>
*/
use crate::utils;
use std::{fmt::Debug, panic::Location};
pub trait ResultExt<T, E> {
fn expect_log(self, msg: &str) -> T;
fn error_log(&self, msg: &str);
fn warn_log(&self, msg: &str);
}
impl<T, E: Debug> ResultExt<T, E> for Result<T, E> {
#[track_caller]
fn expect_log(self, msg: &str) -> T {
match self {
Ok(v) => v,
Err(e) => {
error!("{}\n{:?}\n{}", msg, e, Location::caller());
utils::show_program_panic(msg);
panic!("[panic]");
}
}
}
#[track_caller]
fn error_log(&self, msg: &str) {
if let Err(e) = self {
error!("{}\n{:?}\n{}", msg, e, Location::caller());
utils::show_alert(msg);
}
}
#[track_caller]
fn warn_log(&self, msg: &str) {
if let Err(e) = self {
warn!("{}\n{:?}", msg, e);
utils::show_alert(msg);
}
}
}

View File

@ -13,19 +13,16 @@
*/ */
use std::{ use std::{
fs::{self, File}, fs,
io::Read,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{Arc, RwLock}, sync::{Arc, RwLock},
}; };
use fltk::{button::Button, enums, prelude::*}; use fltk::{button::Button, dialog, enums, prelude::*};
use image::{DynamicImage, GenericImageView, ImageBuffer, ImageEncoder, Pixel}; use image::{DynamicImage, GenericImageView, ImageBuffer};
use imageproc::rect::Rect;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::globals; use crate::globals;
use crate::result_ext::ResultExt;
/// helps cast tupels to f64 /// helps cast tupels to f64
pub(crate) struct Coord(pub(crate) f64, pub(crate) f64); pub(crate) struct Coord(pub(crate) f64, pub(crate) f64);
@ -66,47 +63,6 @@ impl Into<(i32, i32)> for Coord {
} }
} }
impl Into<(usize, usize)> for Coord {
fn into(self) -> (usize, usize) {
(self.0 as usize, self.1 as usize)
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub(crate) struct ImageInfo {
pub(crate) path: PathBuf,
pub(crate) image_type: ImageType,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub(crate) enum ImageType {
Jpeg,
Png,
Webp,
None,
}
impl ImageType {
pub(crate) fn from_mime(v: &str) -> Self {
match v {
"image/jpeg" | "image/jpg" => Self::Jpeg,
"image/png" => Self::Png,
"image/webp" => Self::Webp,
_ => Self::None,
}
}
pub(crate) fn as_extension(&self) -> String {
match self {
Self::Jpeg => "jpg",
Self::Png => "png",
Self::Webp => "webp",
Self::None => "none",
}
.to_owned()
}
}
/// Contains Image and its buffer(edited image) /// Contains Image and its buffer(edited image)
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ImageContainer { pub(crate) struct ImageContainer {
@ -116,18 +72,27 @@ pub(crate) struct ImageContainer {
} }
impl ImageContainer { impl ImageContainer {
pub(crate) fn new(image_info: &ImageInfo, properties: Arc<RwLock<ImageProperties>>) -> Self { pub(crate) fn new(path: &PathBuf, properties: Arc<RwLock<ImageProperties>>) -> Self {
let img = load_image(&image_info); let img = match image::open(path) {
Ok(i) => i,
Err(e) => {
dialog::alert_default("Failed to open image!");
error!("Failed to open image\n{:?}", e);
panic!("Failed to open image\n{:?}", e);
}
};
let img = DynamicImage::ImageRgb8(img.into_rgb8());
let (width, height): (f64, f64) = Coord::from(img.dimensions()).into(); let (width, height): (f64, f64) = Coord::from(img.dimensions()).into();
let config = rw_read!(globals::CONFIG); let config = globals::CONFIG.read().unwrap();
let mut prop = rw_write!(properties); let mut prop = properties.write().unwrap();
prop.image_info = Some(image_info.to_owned()); prop.path = Some(path.to_owned());
prop.original_dimension = (width, height); prop.original_dimension = (width, height);
prop.quote_position = height * config.quote_position_ratio; prop.quote_position = height * config.quote_position_ratio;
prop.subquote_position = height * config.subquote_position_ratio; prop.subquote_position = height * config.subquote_position_ratio;
prop.subquote2_position = height * config.subquote2_position_ratio; prop.subquote2_position = height * config.subquote2_position_ratio;
prop.tag_position = height * config.tag_y_position_ratio; prop.tag_position = height * config.tag_position_ratio;
prop.tag2_position = height * config.tag2_position_ratio; prop.tag2_position = height * config.tag2_position_ratio;
Self { Self {
@ -139,7 +104,7 @@ impl ImageContainer {
/// Resize image /// Resize image
pub(crate) fn apply_resize(&mut self) { pub(crate) fn apply_resize(&mut self) {
let mut prop = rw_write!(self.properties); let mut prop = self.properties.write().unwrap();
let (width, height) = prop.dimension; let (width, height) = prop.dimension;
let (s_width, s_height) = ((width * 500.0) / height, 500.0); let (s_width, s_height) = ((width * 500.0) / height, 500.0);
@ -151,14 +116,13 @@ impl ImageContainer {
/// Crop Image /// Crop Image
pub(crate) fn apply_crop(&mut self) { pub(crate) fn apply_crop(&mut self) {
let mut prop = rw_write!(self.properties); let mut prop = self.properties.write().unwrap();
let (original_width, original_height) = prop.original_dimension; let (original_width, original_height) = prop.original_dimension;
let (origina_crop_width, origina_crop_height) = let (origina_crop_width, origina_crop_height) =
croped_ratio(original_width, original_height); croped_ratio(original_width, original_height);
prop.crop_position = Some(( prop.crop_position = Some((
(original_width - origina_crop_width) / 2.0, original_width / 2.0 - origina_crop_width / 2.0,
(original_height - origina_crop_height) / 2.0, original_height / 2.0 - origina_crop_height / 2.0,
)); ));
let (s_width, s_height): (f64, f64) = Coord::from(self.image.dimensions()).into(); let (s_width, s_height): (f64, f64) = Coord::from(self.image.dimensions()).into();
@ -174,13 +138,12 @@ impl ImageContainer {
} }
pub(crate) fn apply_crop_position(&mut self, original_x: f64, original_y: f64) { pub(crate) fn apply_crop_position(&mut self, original_x: f64, original_y: f64) {
let mut prop = rw_write!(self.properties); let mut prop = self.properties.write().unwrap();
let (original_width, original_height) = prop.original_dimension; let (original_width, original_height) = prop.original_dimension;
prop.crop_position = Some((original_x, original_y)); prop.crop_position = Some((original_x, original_y));
let (s_width, s_height): (f64, f64) = Coord::from(self.image.dimensions()).into(); let (s_width, s_height): (f64, f64) = Coord::from(self.image.dimensions()).into();
let (c_width, c_height) = croped_ratio(s_width, s_height); let (c_width, c_height) = croped_ratio(s_width, s_height);
let (cx, cy) = ( let (cx, cy) = (
(original_x * s_width) / original_width, (original_x * s_width) / original_width,
(original_y * s_height) / original_height, (original_y * s_height) / original_height,
@ -196,7 +159,7 @@ impl ImageContainer {
/// Redraw: Copy image from main image to buffer and draw text and all on it /// Redraw: Copy image from main image to buffer and draw text and all on it
pub(crate) fn redraw_to_buffer(&mut self) { pub(crate) fn redraw_to_buffer(&mut self) {
let prop = rw_read!(self.properties); let prop = self.properties.read().unwrap();
let mut tmp = self.image.clone(); let mut tmp = self.image.clone();
draw_layer_and_text( draw_layer_and_text(
@ -212,58 +175,51 @@ impl ImageContainer {
&prop.tag2, &prop.tag2,
prop.tag_position, prop.tag_position,
prop.tag2_position, prop.tag2_position,
prop.original_dimension.0,
prop.original_dimension.1, prop.original_dimension.1,
); );
self.buffer = tmp; self.buffer = tmp;
} }
/// Save image and properities /// Save image anf properities
pub(crate) fn save(&self) { pub(crate) fn save(&self) {
let prop = rw_read!(self.properties); let prop = self.properties.read().unwrap();
let image_info = &prop.image_info;
let (export_path, path_properties, mut original_image) = match image_info { let path_original = match &prop.path {
Some(p) => ( Some(p) => Path::new(p),
get_export_image_path(p, &prop.name_prefix),
get_properties_path(p),
load_image(p),
),
None => return, None => return,
}; };
let config = rw_read!(globals::CONFIG); let path_properties = path_original.with_extension("prop");
let export_format = &config.image_format; let export = path_original.parent().unwrap().join("export").join(
path_original
.with_extension("png")
.file_name()
.unwrap()
.to_str()
.unwrap(),
);
let mut prop = prop.clone(); let mut prop = prop.clone();
prop.image_info = None; prop.path = None;
fs::write( if let Err(e) = fs::write(
&path_properties, &path_properties,
serde_json::to_string(&ImagePropertiesFile::from(&prop)).unwrap(), serde_json::to_string(&ImagePropertiesFile::from(&prop)).unwrap(),
) ) {
.warn_log("Failed to save properties!"); dialog::alert_default("Failed to save properties!");
warn!("Failed to save properties!\n{:?}", e);
}
let (width, height): (f64, f64) = Coord::from(original_image.dimensions()).into(); let mut img = image::open(&path_original).unwrap();
let (width, height): (f64, f64) = Coord::from(img.dimensions()).into();
let (crop_x, crop_y) = prop.crop_position.unwrap(); let (crop_x, crop_y) = prop.crop_position.unwrap();
let (crop_width, crop_height) = croped_ratio(width, height); let (crop_width, crop_height) = croped_ratio(width, height);
let mut img = original_image.crop( let mut img = img.crop(
crop_x as u32, crop_x as u32,
crop_y as u32, crop_y as u32,
crop_width as u32, crop_width as u32,
crop_height as u32, crop_height as u32,
); );
if crop_width > config.maximum_width_limit {
let (resize_width, resize_height) = (
config.maximum_width_limit,
height_from_width(config.maximum_width_limit),
);
img = img.resize_exact(
resize_width as u32,
resize_height as u32,
image::imageops::FilterType::Lanczos3,
);
}
draw_layer_and_text( draw_layer_and_text(
&mut img, &mut img,
&prop.translucent_layer_color, &prop.translucent_layer_color,
@ -277,113 +233,89 @@ impl ImageContainer {
&prop.tag2, &prop.tag2,
prop.tag_position, prop.tag_position,
prop.tag2_position, prop.tag2_position,
prop.original_dimension.0,
prop.original_dimension.1, prop.original_dimension.1,
); );
let mut output = match File::create(&export_path) { if let Err(e) = img.save_with_format(&export, image::ImageFormat::Png) {
Ok(a) => a, dialog::alert_default("Failed to export png!");
Err(e) => { warn!("Failed to export png!\n{:?}", e);
Result::<(), _>::Err(e).warn_log("Failed to write to disk!");
return;
}
};
match export_format {
ImageType::Png => {
let encoder = image::codecs::png::PngEncoder::new_with_quality(
&mut output,
image::codecs::png::CompressionType::Best,
image::codecs::png::FilterType::Sub,
);
let (w, h) = img.dimensions();
encoder
.write_image(&img.into_rgba8(), w, h, image::ColorType::Rgba8)
.warn_log("Failed to export Image!");
}
ImageType::Jpeg => {
let (width, height) = Coord::from(img.dimensions()).into();
let buf = img.into_rgb8();
let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_RGB);
comp.set_size(width, height);
comp.set_quality(100.0);
comp.set_smoothing_factor(1);
comp.set_mem_dest();
comp.start_compress();
comp.write_scanlines(&buf);
comp.finish_compress();
match comp.data_to_vec() {
Ok(data) => {
std::fs::write(&export_path, data).warn_log("Failed to export Image!")
}
Err(e) => Result::<(), _>::Err(e).warn_log("Failed to encode image!"),
}
}
_ => (),
} }
} }
pub(crate) fn clone_img(&self) -> Option<ImageInfo> { pub(crate) fn clone_img(&self) -> Option<PathBuf> {
let prop = rw_read!(self.properties); let prop = self.properties.read().unwrap();
match &prop.image_info { match &prop.path {
Some(image_info) => { Some(path) => {
let stem = image_info.path.file_stem().unwrap().to_string_lossy(); let name = path.file_stem().unwrap().to_string_lossy();
let extension = image_info.path.extension().unwrap().to_string_lossy(); let ext = path.extension().unwrap().to_string_lossy();
let mut i = 1; let mut i = 1;
let mut new_image_info = image_info.clone(); let mut new_path = path.clone();
while new_image_info.path.exists() { while new_path.exists() {
let new_file = format!("{}{}.{}", stem, "-copy".repeat(i), extension); let new_file = format!("{}{}.{}", name, "-copy".repeat(i), ext);
new_image_info.path = image_info.path.with_file_name(&new_file); new_path = path.with_file_name(&new_file);
i += 1; i += 1;
} }
let path_properties = get_properties_path(&image_info); let path_properties = path.with_extension("prop");
let path_properties_new = get_properties_path(&new_image_info); let path_properties_new = new_path.with_extension("prop");
if image_info.path.exists() { if path.exists() {
fs::copy(&image_info.path, &new_image_info.path) if let Err(e) = fs::copy(path, &new_path) {
.warn_log("Failed to clone image!"); dialog::alert_default("Failed to clone image!");
warn!("Failed to clone image!\n{:?}", e);
return None;
}
} }
if path_properties.exists() { if path_properties.exists() {
fs::copy(path_properties, &path_properties_new) if let Err(e) = fs::copy(path_properties, &path_properties_new) {
.warn_log("Failed to clone image properties!"); dialog::alert_default("Failed to clone image properties!");
warn!("Failed to clone image properties!\n{:?}", e);
}
} }
Some(new_image_info) Some(new_path)
} }
None => None, None => None,
} }
} }
pub(crate) fn delete(&self) { pub(crate) fn delete(&self) {
let prop = rw_read!(self.properties); let prop = self.properties.read().unwrap();
let image_info = &prop.image_info;
let (export_path, path_image, path_properties) = match image_info { let path_original = match &prop.path {
Some(p) => ( Some(p) => Path::new(p),
get_export_image_path(p, &prop.name_prefix),
Path::new(&p.path),
get_properties_path(p),
),
None => return, None => return,
}; };
let path_properties = path_original.with_extension("prop");
let export = path_original.parent().unwrap().join("export").join(
path_original
.with_extension("png")
.file_name()
.unwrap()
.to_str()
.unwrap(),
);
if path_image.exists() { if path_original.exists() {
fs::remove_file(path_image).warn_log("Failed to delete image!"); if let Err(e) = fs::remove_file(path_original) {
dialog::alert_default("Failed to delete image!");
warn!("Failed to delete image!\n{:?}", e);
}
} }
if path_properties.exists() { if path_properties.exists() {
fs::remove_file(path_properties).warn_log("Failed to delete image properties!"); if let Err(e) = fs::remove_file(path_properties) {
dialog::alert_default("Failed to delete image properties!");
warn!("Failed to delete image properties!\n{:?}", e);
}
} }
if export_path.exists() { if export.exists() {
fs::remove_file(export_path).warn_log("Failed to delete exported image!"); if let Err(e) = fs::remove_file(export) {
dialog::alert_default("Failed to delete exported image!");
warn!("Failed to delete exported image!\n{:?}", e);
}
} }
} }
} }
@ -392,7 +324,6 @@ impl ImageContainer {
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub(crate) struct ImagePropertiesFile { pub(crate) struct ImagePropertiesFile {
pub(crate) crop_position: Option<(f64, f64)>, pub(crate) crop_position: Option<(f64, f64)>,
pub(crate) name_prefix: Option<String>,
pub(crate) quote: Option<String>, pub(crate) quote: Option<String>,
pub(crate) subquote: Option<String>, pub(crate) subquote: Option<String>,
pub(crate) subquote2: Option<String>, pub(crate) subquote2: Option<String>,
@ -410,7 +341,6 @@ impl Default for ImagePropertiesFile {
fn default() -> Self { fn default() -> Self {
Self { Self {
crop_position: None, crop_position: None,
name_prefix: None,
quote: None, quote: None,
subquote: None, subquote: None,
subquote2: None, subquote2: None,
@ -430,7 +360,6 @@ impl From<&ImageProperties> for ImagePropertiesFile {
fn from(props: &ImageProperties) -> Self { fn from(props: &ImageProperties) -> Self {
Self { Self {
crop_position: props.crop_position, crop_position: props.crop_position,
name_prefix: Some(props.name_prefix.clone()),
quote: Some(props.quote.clone()), quote: Some(props.quote.clone()),
subquote: Some(props.subquote.clone()), subquote: Some(props.subquote.clone()),
subquote2: Some(props.subquote2.clone()), subquote2: Some(props.subquote2.clone()),
@ -449,11 +378,10 @@ impl From<&ImageProperties> for ImagePropertiesFile {
/// Properties of loaded image /// Properties of loaded image
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub(crate) struct ImageProperties { pub(crate) struct ImageProperties {
pub(crate) image_info: Option<ImageInfo>, pub(crate) path: Option<PathBuf>,
pub(crate) dimension: (f64, f64), pub(crate) dimension: (f64, f64),
pub(crate) original_dimension: (f64, f64), pub(crate) original_dimension: (f64, f64),
pub(crate) crop_position: Option<(f64, f64)>, pub(crate) crop_position: Option<(f64, f64)>,
pub(crate) name_prefix: String,
pub(crate) quote: String, pub(crate) quote: String,
pub(crate) subquote: String, pub(crate) subquote: String,
pub(crate) subquote2: String, pub(crate) subquote2: String,
@ -471,11 +399,10 @@ pub(crate) struct ImageProperties {
impl Default for ImageProperties { impl Default for ImageProperties {
fn default() -> Self { fn default() -> Self {
Self { Self {
image_info: None, path: None,
dimension: (0.0, 0.0), dimension: (0.0, 0.0),
original_dimension: (0.0, 0.0), original_dimension: (0.0, 0.0),
crop_position: None, crop_position: None,
name_prefix: "".to_owned(),
quote: "".to_owned(), quote: "".to_owned(),
subquote: "".to_owned(), subquote: "".to_owned(),
subquote2: "".to_owned(), subquote2: "".to_owned(),
@ -499,8 +426,10 @@ impl ImageProperties {
tag_default: &str, tag_default: &str,
tag2_default: &str, tag2_default: &str,
) { ) {
self.crop_position = props.crop_position; if let Some(v) = props.crop_position {
self.name_prefix = props.name_prefix.unwrap_or("".to_owned()); self.crop_position = Some(v);
}
self.quote = props.quote.unwrap_or("".to_owned()); self.quote = props.quote.unwrap_or("".to_owned());
self.subquote = props.subquote.unwrap_or("".to_owned()); self.subquote = props.subquote.unwrap_or("".to_owned());
self.subquote2 = props.subquote2.unwrap_or("".to_owned()); self.subquote2 = props.subquote2.unwrap_or("".to_owned());
@ -513,53 +442,10 @@ impl ImageProperties {
self.tag2_position = props.tag2_position.unwrap_or(self.tag2_position); self.tag2_position = props.tag2_position.unwrap_or(self.tag2_position);
self.translucent_layer_color = props self.translucent_layer_color = props
.translucent_layer_color .translucent_layer_color
.unwrap_or(rw_read!(globals::CONFIG).color_layer); .unwrap_or(globals::CONFIG.read().unwrap().color_layer);
} }
} }
/// Load image as Dynamic Image
fn load_image(image_info: &ImageInfo) -> DynamicImage {
let img = match image_info.image_type {
ImageType::Webp => {
let mut f = File::open(&image_info.path).expect_log("Failed to load image!");
let mut buf = vec![];
f.read_to_end(&mut buf).expect_log("Failed to read image!");
let a = webp::Decoder::new(&buf)
.decode()
.ok_or("")
.expect_log("Failed to decode image!");
a.to_image()
}
ImageType::Jpeg => {
let mut f = File::open(&image_info.path).expect_log("Failed to load image!");
let mut buf = vec![];
f.read_to_end(&mut buf).expect_log("Failed to read image!");
let d = mozjpeg::Decompress::with_markers(mozjpeg::ALL_MARKERS)
.from_mem(&buf)
.expect_log("Failed to decompress image!");
let mut image = d.rgb().expect_log("Failed to convert to rgb image!");
let pixels = image.read_scanlines_flat().unwrap();
let image =
ImageBuffer::from_raw(image.width() as u32, image.height() as u32, pixels).unwrap();
DynamicImage::ImageRgb8(image)
}
ImageType::Png => {
let dec = image::codecs::png::PngDecoder::new(
File::open(&image_info.path).expect_log("Failed to load image!"),
)
.expect_log("Failed to decode image!");
DynamicImage::from_decoder(dec).expect_log("Failed to decode image!")
}
ImageType::None => {
Result::<(), _>::Err("Failed to load image!").expect_log("Unknown format!");
std::process::exit(1);
}
};
DynamicImage::ImageRgb8(img.into_rgb8())
}
/// Draw text and stuffs on image /// Draw text and stuffs on image
fn draw_layer_and_text( fn draw_layer_and_text(
tmp: &mut DynamicImage, tmp: &mut DynamicImage,
@ -572,9 +458,8 @@ fn draw_layer_and_text(
subquote2_position: f64, subquote2_position: f64,
tag: &str, tag: &str,
tag2: &str, tag2: &str,
tag_y_position: f64, tag_position: f64,
tag2_position: f64, tag2_position: f64,
original_width: f64,
original_height: f64, original_height: f64,
) { ) {
let (width, height): (f64, f64) = Coord::from(tmp.dimensions()).into(); let (width, height): (f64, f64) = Coord::from(tmp.dimensions()).into();
@ -590,21 +475,16 @@ fn draw_layer_and_text(
&globals::FONT_QUOTE, &globals::FONT_QUOTE,
size, size,
quote_position, quote_position,
original_width,
original_height, original_height,
true,
quote, quote,
); );
let size = subquote_from_height(height); let size = subquote_from_height(height);
draw_multiline_mid_string( draw_multiline_mid_string(
tmp, tmp,
&globals::FONT_SUBQUOTE, &globals::FONT_SUBQUOTE,
size, size,
subquote_position, subquote_position,
original_width,
original_height, original_height,
true,
subquote, subquote,
); );
let size = subquote2_from_height(height); let size = subquote2_from_height(height);
@ -613,9 +493,7 @@ fn draw_layer_and_text(
&globals::FONT_SUBQUOTE2, &globals::FONT_SUBQUOTE2,
size, size,
subquote2_position, subquote2_position,
original_width,
original_height, original_height,
true,
subquote2, subquote2,
); );
@ -625,9 +503,7 @@ fn draw_layer_and_text(
&globals::FONT_TAG2, &globals::FONT_TAG2,
size, size,
tag2_position, tag2_position,
original_width,
original_height, original_height,
false,
tag2, tag2,
); );
@ -642,9 +518,8 @@ fn draw_layer_and_text(
imageproc::drawing::draw_text_mut( imageproc::drawing::draw_text_mut(
tmp, tmp,
image::Rgba([255, 255, 255, 255]), image::Rgba([255, 255, 255, 255]),
(width * rw_read!(globals::CONFIG).tag_x_position_ratio - text_width) as i32, (width * 0.99 - text_width) as u32,
((tag_y_position * height) / original_height + index as f64 * (text_height * 1.2)) ((tag_position * height) / original_height + index as f64 * (text_height * 1.2)) as u32,
as i32,
rusttype::Scale::uniform(size as f32), rusttype::Scale::uniform(size as f32),
&globals::FONT_TAG, &globals::FONT_TAG,
line, line,
@ -658,144 +533,26 @@ pub(crate) fn draw_multiline_mid_string(
font: &rusttype::Font, font: &rusttype::Font,
size: f64, size: f64,
position: f64, position: f64,
original_width: f64,
original_height: f64, original_height: f64,
boxed: bool,
text: &str, text: &str,
) { ) {
let (mut box_width, mut box_height) = (0.0, 0.0);
let (width, height): (f64, f64) = Coord::from(tmp.dimensions()).into(); let (width, height): (f64, f64) = Coord::from(tmp.dimensions()).into();
let has_line_spacing = rw_read!(globals::CONFIG).line_spacing;
for (index, line) in text.lines().enumerate() { for (index, line) in text.lines().enumerate() {
let (text_width, text_height) = let (text_width, text_height) =
measure_line(font, line, rusttype::Scale::uniform(size as f32)); measure_line(font, line, rusttype::Scale::uniform(size as f32));
if text_width > box_width { imageproc::drawing::draw_text_mut(
box_width = text_width;
}
box_height += text_height
* if index == 0 || !has_line_spacing {
1.0
} else {
1.12
};
let (x, y) = (
(width - text_width) / 2.0,
(position * height) / original_height + index as f64 * (text_height * 1.12),
);
if !boxed || !rw_read!(globals::CONFIG).draw_box_around_quote {
imageproc::drawing::draw_text_mut(
tmp,
image::Rgba([255, 255, 255, 100]),
x as i32,
y as i32,
rusttype::Scale::uniform(size as f32),
font,
line,
);
}
}
if boxed && rw_read!(globals::CONFIG).draw_box_around_quote {
draw_box(
tmp,
box_width,
box_height,
position,
original_width,
original_height,
);
draw_multiline_mid_string(
tmp, tmp,
image::Rgba([255, 255, 255, 255]),
((width - text_width) / 2.0) as u32,
((position * height) / original_height + index as f64 * (text_height * 1.15)) as u32,
rusttype::Scale::uniform(size as f32),
font, font,
size, line,
position,
original_width,
original_height,
false,
text,
); );
} }
} }
/// Draws box around text.
fn draw_box(
tmp: &mut DynamicImage,
box_width: f64,
box_height: f64,
position: f64,
original_width: f64,
original_height: f64,
) {
if box_width <= 0.0 {
return;
}
let (width, height): (f64, f64) = Coord::from(tmp.dimensions()).into();
let (delta_x, delta_y) = (width / original_width, height / original_height);
let (x_gap, y_gap) = (30.0 * delta_x, 10.0 * delta_y);
let (x, y) = (
((width - box_width) / 2.0 - x_gap) as u32,
((position * height) / original_height - y_gap) as u32,
);
let (w, h) = (
(box_width + x_gap * 2.0) as u32,
(box_height + y_gap * 2.0) as u32,
);
if x >= width as u32 || y >= height as u32 {
return;
}
let mut buff = tmp.crop(x, y, w, h);
let layer = DynamicImage::ImageRgba8(ImageBuffer::from_fn(w, h, |_, _| {
image::Rgba([20, 22, 25, 80])
}));
image::imageops::overlay(&mut buff, &layer, 0, 0);
buff = buff.blur(15.0);
let (dx, dy) = (20.0 * delta_x, 20.0 * delta_y);
let mut shadow = DynamicImage::new_rgba8(w + (dx * 2.0) as u32, h + (dy * 2.0) as u32);
imageproc::drawing::draw_hollow_rect_mut(
&mut shadow,
Rect::at(dx as i32, dy as i32).of_size(w, h),
image::Rgba([30, 30, 30, 255]),
);
shadow = shadow.blur(5.0 * delta_x as f32);
image::imageops::overlay(tmp, &shadow, x as i64 - dx as i64, y as i64 - dy as i64);
image::imageops::overlay(tmp, &buff, x as i64, y as i64);
let mut color = buff.get_pixel(0, 0).to_rgba();
color.blend(&buff.get_pixel(0, buff.height() - 1).to_rgba());
color.blend(
&buff
.get_pixel(buff.width() - 1, buff.height() - 1)
.to_rgba(),
);
color.blend(&buff.get_pixel(buff.width() - 1, 0).to_rgba());
imageproc::drawing::draw_hollow_rect_mut(
tmp,
Rect::at(
x as i32 - (delta_x * 1.0) as i32,
y as i32 - (delta_x * 1.0) as i32,
)
.of_size(w + (delta_x * 2.0) as u32, h + (delta_x * 2.0) as u32),
color.clone(),
);
color.blend(&image::Rgba([0, 0, 0, 2]));
imageproc::drawing::draw_hollow_rect_mut(
tmp,
Rect::at(x as i32, y as i32).of_size(w, h),
color.clone(),
);
}
/// Get size of text to draw on image /// Get size of text to draw on image
pub(crate) fn measure_line( pub(crate) fn measure_line(
font: &rusttype::Font, font: &rusttype::Font,
@ -814,95 +571,6 @@ pub(crate) fn measure_line(
Coord::from((width, height)).into() Coord::from((width, height)).into()
} }
/// path of properties files
pub(crate) fn get_properties_path(image_info: &ImageInfo) -> PathBuf {
let img = &image_info.path;
let image_name: String = image_info
.path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.into_owned();
let mut occurance = 0;
let image_name = image_name
.chars()
.into_iter()
.rev()
.map(|c| {
if occurance == 0 && c == '.' {
occurance = 1;
'-'
} else {
c
}
})
.collect::<Vec<char>>()
.into_iter()
.rev();
let image_name = format!("{}.prop", String::from_iter(image_name));
let default_path = img.with_file_name(&image_name);
if default_path.exists() {
return default_path;
}
let path = img.with_extension("prop");
if path.exists() {
match std::fs::copy(&path, &default_path) {
Ok(_) => std::fs::remove_file(&path).warn_log("Failed to delete depricated prop file"),
Err(e) => Result::<(), _>::Err(e).warn_log("Failed to copy depricated prop file"),
}
}
default_path
}
/// path of properties files
pub(crate) fn get_export_image_path(image_info: &ImageInfo, name_prefix: &str) -> PathBuf {
let config = rw_read!(globals::CONFIG);
let export_format = &config.image_format;
let image_name = image_info
.path
.file_name()
.unwrap_or_default()
.to_string_lossy();
let mut occurance = 0;
let image_name = image_name
.chars()
.into_iter()
.rev()
.map(|c| {
if occurance == 0 && c == '.' {
occurance = 1;
'-'
} else {
c
}
})
.collect::<Vec<char>>()
.into_iter()
.rev();
let image_name = format!(
"{}{}.{}",
name_prefix,
String::from_iter(image_name),
export_format.as_extension()
);
let expost_dir = image_info.path.parent().unwrap().join("export");
if !expost_dir.exists() {
fs::create_dir(&expost_dir).expect_log("Failed to create export folder!");
}
let export = expost_dir.join(&image_name);
export
}
/// small hack because 0,0,0 rgb, because can't be set on fltk theme /// small hack because 0,0,0 rgb, because can't be set on fltk theme
pub(crate) fn set_color_btn_rgba(rgba: [u8; 4], btn: &mut Button) { pub(crate) fn set_color_btn_rgba(rgba: [u8; 4], btn: &mut Button) {
let [mut r, g, b, _] = rgba; let [mut r, g, b, _] = rgba;
@ -912,16 +580,6 @@ pub(crate) fn set_color_btn_rgba(rgba: [u8; 4], btn: &mut Button) {
btn.set_color(enums::Color::from_rgb(r, g, b)); btn.set_color(enums::Color::from_rgb(r, g, b));
} }
/// Check if image is too small
pub(crate) fn is_too_small(width: f64, height: f64) -> bool {
let (crop_width, _) = croped_ratio(width, height);
if crop_width < rw_read!(globals::CONFIG).minimum_width_limit {
true
} else {
false
}
}
/// Get required size to crop image as per image ratio /// Get required size to crop image as per image ratio
pub(crate) fn croped_ratio(width: f64, height: f64) -> (f64, f64) { pub(crate) fn croped_ratio(width: f64, height: f64) -> (f64, f64) {
if width > width_from_height(height) { if width > width_from_height(height) {
@ -933,58 +591,37 @@ pub(crate) fn croped_ratio(width: f64, height: f64) -> (f64, f64) {
/// Get required witdh to crop image from height as per image ratio /// Get required witdh to crop image from height as per image ratio
pub(crate) fn width_from_height(height: f64) -> f64 { pub(crate) fn width_from_height(height: f64) -> f64 {
let (w, h) = rw_read!(globals::CONFIG).image_ratio; let (w, h) = globals::CONFIG.read().unwrap().image_ratio;
(w * height) / h (w * height) / h
} }
/// Get required height to crop image from width as per image ratio /// Get required height to crop image from width as per image ratio
pub(crate) fn height_from_width(width: f64) -> f64 { pub(crate) fn height_from_width(width: f64) -> f64 {
let (w, h) = rw_read!(globals::CONFIG).image_ratio; let (w, h) = globals::CONFIG.read().unwrap().image_ratio;
(h * width) / w (h * width) / w
} }
/// Get required quote size for crop image from height as per image ratio /// Get required quote size for crop image from height as per image ratio
pub(crate) fn quote_from_height(height: f64) -> f64 { pub(crate) fn quote_from_height(height: f64) -> f64 {
(height * rw_read!(globals::CONFIG).quote_font_ratio) / 5000.0 (height * globals::CONFIG.read().unwrap().quote_font_ratio) / 5000.0
} }
/// Get required subquote size for crop image from height as per image ratio /// Get required subquote size for crop image from height as per image ratio
pub(crate) fn subquote_from_height(height: f64) -> f64 { pub(crate) fn subquote_from_height(height: f64) -> f64 {
(height * rw_read!(globals::CONFIG).subquote_font_ratio) / 5000.0 (height * globals::CONFIG.read().unwrap().subquote_font_ratio) / 5000.0
} }
/// Get required subquote2 size for crop image from height as per image ratio /// Get required subquote2 size for crop image from height as per image ratio
pub(crate) fn subquote2_from_height(height: f64) -> f64 { pub(crate) fn subquote2_from_height(height: f64) -> f64 {
(height * rw_read!(globals::CONFIG).subquote2_font_ratio) / 5000.0 (height * globals::CONFIG.read().unwrap().subquote2_font_ratio) / 5000.0
} }
/// Get required tag size for crop image from height as per image ratio /// Get required tag size for crop image from height as per image ratio
pub(crate) fn tag_from_height(height: f64) -> f64 { pub(crate) fn tag_from_height(height: f64) -> f64 {
(height * rw_read!(globals::CONFIG).tag_font_ratio) / 5000.0 (height * globals::CONFIG.read().unwrap().tag_font_ratio) / 5000.0
} }
/// Get required tag2 size for crop image from height as per image ratio /// Get required tag2 size for crop image from height as per image ratio
pub(crate) fn tag2_from_height(height: f64) -> f64 { pub(crate) fn tag2_from_height(height: f64) -> f64 {
(height * rw_read!(globals::CONFIG).tag2_font_ratio) / 5000.0 (height * globals::CONFIG.read().unwrap().tag2_font_ratio) / 5000.0
}
pub(crate) fn show_message(msg: &str) {
let a = rw_read!(globals::MAIN_SENDER);
if let Some(a) = &*a {
a.send(crate::AppMessage::Message(msg.to_owned()));
}
}
pub(crate) fn show_alert(msg: &str) {
let a = rw_read!(globals::MAIN_SENDER);
if let Some(a) = &*a {
a.send(crate::AppMessage::Alert(msg.to_owned()));
}
}
pub(crate) fn show_program_panic(msg: &str) {
let a = rw_read!(globals::MAIN_SENDER);
if let Some(a) = &*a {
a.send(crate::AppMessage::ProgramPanicMessage(msg.to_owned()));
}
} }