Compare commits
No commits in common. "master" and "Stable(0.2.2)" have entirely different histories.
master
...
Stable(0.2
File diff suppressed because it is too large
Load Diff
18
Cargo.toml
18
Cargo.toml
|
|
@ -1,8 +1,8 @@
|
|||
[package]
|
||||
name = "post_maker"
|
||||
version = "0.6.3"
|
||||
version = "0.2.2"
|
||||
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>"]
|
||||
license = "GPL-3.0-only"
|
||||
readme = "README.md"
|
||||
|
|
@ -10,28 +10,18 @@ repository = "https://github.com/PiyushXCoder/post_maker"
|
|||
keywords = ["posts", "instagram", "facebook", "twitter"]
|
||||
# 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]
|
||||
clap = { version = "3.0", features = ["derive"] }
|
||||
log = "0.4"
|
||||
simplelog = "0.11"
|
||||
fltk = "1.2"
|
||||
fltk-theme = "0.4"
|
||||
image = "0.24.1"
|
||||
imageproc = "0.23"
|
||||
webp = "0.2"
|
||||
image = "0.23"
|
||||
imageproc = "0.22"
|
||||
rusttype = "0.9"
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
lazy_static = "1.4"
|
||||
dirs = "4.0"
|
||||
infer = "0.7.0"
|
||||
textwrap = "0.14"
|
||||
webbrowser = "0.5"
|
||||
mozjpeg = "0.9.2"
|
||||
bichannel = "0.0.4"
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
[target.x86_64-pc-windows-gnu]
|
||||
image = "rustembedded/cross:x86_64-pc-windows-gnu"
|
||||
10
README.md
10
README.md
|
|
@ -4,10 +4,7 @@
|
|||
<img alt="actix-web-grants" src="./assets/icon_with_text.svg" width="170">
|
||||
</p>
|
||||
|
||||
[](https://crates.io/crates/post_maker)
|
||||
[](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.
|
||||
> Post Maker helps you to make post for instagram and other social media apps easily.
|
||||
|
||||
## Installing
|
||||
|
||||
|
|
@ -44,10 +41,7 @@ You can follow rust's official guide to install rust compiler and cargo [here](h
|
|||
|
||||
## General Overview of Controls
|
||||
|
||||

|
||||
|
||||
## Achievement
|
||||
The Post Maker is listed in [Official Project Showcase of fltk-rs](https://github.com/fltk-rs/fltk-rs/issues/418)
|
||||

|
||||
|
||||
## 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.
|
|
@ -13,10 +13,11 @@
|
|||
*/
|
||||
|
||||
//! About Window
|
||||
use crate::{config, globals, result_ext::ResultExt};
|
||||
use crate::{config, globals};
|
||||
use fltk::{
|
||||
app,
|
||||
button::Button,
|
||||
dialog,
|
||||
enums::{self, Align, Event},
|
||||
frame::Frame,
|
||||
group::Flex,
|
||||
|
|
@ -151,7 +152,10 @@ impl About {
|
|||
// Repository Link
|
||||
self.repo_link.handle(|_, ev| {
|
||||
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
|
||||
});
|
||||
|
|
@ -159,7 +163,10 @@ impl About {
|
|||
// Developer's Link
|
||||
self.dev_link.handle(|_, ev| {
|
||||
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
|
||||
});
|
||||
|
|
@ -167,8 +174,10 @@ impl About {
|
|||
// License Link
|
||||
self.license_link.handle(|_, ev| {
|
||||
if ev == Event::Push {
|
||||
webbrowser::open("https://www.gnu.org/licenses/gpl-3.0.html")
|
||||
.warn_log("Failed to open the link!");
|
||||
if let Err(e) = webbrowser::open("https://www.gnu.org/licenses/gpl-3.0.html") {
|
||||
dialog::alert_default("Failed to open the link!");
|
||||
warn!("Failed to open the link!\n{:?}", e);
|
||||
}
|
||||
}
|
||||
true
|
||||
});
|
||||
|
|
@ -13,14 +13,18 @@
|
|||
*/
|
||||
|
||||
//! load, save configuration and parse cli args
|
||||
use crate::{
|
||||
config_picker::ConfigPicker, dialog, globals, result_ext::ResultExt, utils::ImageType,
|
||||
};
|
||||
use crate::{config_picker::ConfigPicker, globals};
|
||||
use clap::{ArgEnum, Parser};
|
||||
use fltk::dialog;
|
||||
use fltk_theme::ThemeType;
|
||||
use lazy_static::lazy_static;
|
||||
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! {
|
||||
/// Directory where all Configurations are present
|
||||
|
|
@ -125,16 +129,10 @@ pub(crate) struct ConfigFile {
|
|||
pub(crate) quote_position_ratio: f64,
|
||||
pub(crate) subquote_position_ratio: f64,
|
||||
pub(crate) subquote2_position_ratio: f64,
|
||||
pub(crate) tag_x_position_ratio: f64,
|
||||
pub(crate) tag_y_position_ratio: f64,
|
||||
pub(crate) tag_position_ratio: f64,
|
||||
pub(crate) tag2_position_ratio: f64,
|
||||
pub(crate) image_ratio: (f64, f64),
|
||||
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 {
|
||||
|
|
@ -145,7 +143,7 @@ impl Default for ConfigFile {
|
|||
subquote2_font: String::new(),
|
||||
tag_font: String::new(),
|
||||
tag2_font: String::new(),
|
||||
quote_font_ratio: 250.0,
|
||||
quote_font_ratio: 230.0,
|
||||
subquote_font_ratio: 230.0,
|
||||
subquote2_font_ratio: 230.0,
|
||||
tag_font_ratio: 150.0,
|
||||
|
|
@ -153,16 +151,10 @@ impl Default for ConfigFile {
|
|||
quote_position_ratio: 0.7,
|
||||
subquote_position_ratio: 0.8,
|
||||
subquote2_position_ratio: 0.9,
|
||||
tag_x_position_ratio: 0.95,
|
||||
tag_y_position_ratio: 0.5,
|
||||
tag_position_ratio: 0.5,
|
||||
tag2_position_ratio: 0.95,
|
||||
image_ratio: (4.0, 5.0),
|
||||
color_layer: [20, 22, 25, 0],
|
||||
minimum_width_limit: 650.0,
|
||||
maximum_width_limit: 1080.0,
|
||||
draw_box_around_quote: true,
|
||||
line_spacing: true,
|
||||
image_format: ImageType::Jpeg,
|
||||
color_layer: [20, 22, 25, 197],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -177,7 +169,7 @@ impl ConfigFile {
|
|||
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))
|
||||
&& map.len() != 0
|
||||
{
|
||||
|
|
@ -192,7 +184,7 @@ impl ConfigFile {
|
|||
};
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -200,7 +192,7 @@ impl ConfigFile {
|
|||
let config = Self::default();
|
||||
let mut configs = HashMap::new();
|
||||
configs.insert(
|
||||
(&*rw_read!(globals::CONFIG_NAME)).to_owned(),
|
||||
(&*globals::CONFIG_NAME.read().unwrap()).to_owned(),
|
||||
config.clone(),
|
||||
);
|
||||
save_configs(configs);
|
||||
|
|
@ -218,16 +210,46 @@ pub(crate) fn get_configs() -> Option<HashMap<String, ConfigFile>> {
|
|||
|
||||
/// Save configs
|
||||
pub(crate) fn save_configs(configs: HashMap<String, ConfigFile>) {
|
||||
std::fs::write(&*CONFIG_FILE, serde_json::to_string(&configs).unwrap())
|
||||
.expect_log("Can't write config!");
|
||||
if let Err(e) = std::fs::write(&*CONFIG_FILE, serde_json::to_string(&configs).unwrap()) {
|
||||
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 {
|
||||
match File::create(&*LOG_FILE) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
dialog::alert_default("Can't open log file!");
|
||||
panic!("{:?}", e);
|
||||
match File::open(&*LOG_FILE) {
|
||||
Ok(mut file) => {
|
||||
if is_file_30_days_old(&file) {
|
||||
match File::create(&*LOG_FILE) {
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ impl ConfigPicker {
|
|||
let top_padding_btn = Frame::default();
|
||||
let mut panel_flex = Flex::default().row();
|
||||
Frame::default();
|
||||
let apply_btn = Button::default().with_label("Apply");
|
||||
let apply_btn = Button::default().with_label("apply");
|
||||
Frame::default();
|
||||
panel_flex.set_size(&apply_btn, 100);
|
||||
panel_flex.end();
|
||||
|
|
|
|||
|
|
@ -14,27 +14,27 @@
|
|||
|
||||
//! Window to edit configuration
|
||||
|
||||
use crate::{
|
||||
config::{self, ConfigFile},
|
||||
dialog, globals,
|
||||
result_ext::ResultExt,
|
||||
utils::{self, ImageType},
|
||||
};
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
|
||||
use fltk::{
|
||||
app,
|
||||
browser::{Browser, BrowserType},
|
||||
button::{Button, CheckButton, RadioRoundButton},
|
||||
dialog::{FileDialogOptions, NativeFileChooser},
|
||||
button::Button,
|
||||
dialog::{self, FileDialogOptions, NativeFileChooser},
|
||||
enums::{self, Align, Event, Font},
|
||||
frame::Frame,
|
||||
group::{Flex, Scroll},
|
||||
group::Flex,
|
||||
image::SvgImage,
|
||||
output::Output,
|
||||
prelude::*,
|
||||
valuator::ValueInput,
|
||||
window::Window,
|
||||
};
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
config::{self, ConfigFile},
|
||||
globals, utils,
|
||||
};
|
||||
|
||||
pub(crate) struct ConfigWindow {
|
||||
pub(crate) win: Window,
|
||||
|
|
@ -60,21 +60,14 @@ pub(crate) struct ConfigWindow {
|
|||
pub(crate) quote_position_ratio: ValueInput,
|
||||
pub(crate) subquote_position_ratio: ValueInput,
|
||||
pub(crate) subquote2_position_ratio: ValueInput,
|
||||
pub(crate) tag_x_position_ratio: ValueInput,
|
||||
pub(crate) tag_y_position_ratio: ValueInput,
|
||||
pub(crate) tag_position_ratio: ValueInput,
|
||||
pub(crate) tag2_position_ratio: ValueInput,
|
||||
pub(crate) image_ratio_width: 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
|
||||
pub(crate) translucent_layer_rgb: Button,
|
||||
/// opacity value of top translucent layer
|
||||
pub(crate) translucent_layer_alpha: ValueInput,
|
||||
pub(crate) png_format: RadioRoundButton,
|
||||
pub(crate) jpeg_format: RadioRoundButton,
|
||||
pub(crate) defaults_btn: Button,
|
||||
pub(crate) save_btn: Button,
|
||||
pub(crate) cancel_btn: Button,
|
||||
|
|
@ -85,16 +78,12 @@ pub(crate) struct ConfigWindow {
|
|||
impl ConfigWindow {
|
||||
pub(crate) fn new() -> Self {
|
||||
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(
|
||||
SvgImage::from_data(globals::ICON.to_str().unwrap()).unwrap(),
|
||||
));
|
||||
|
||||
// Config picking area
|
||||
let mut config_picker_flex = Flex::default()
|
||||
.with_size(200, win.height() - 50)
|
||||
.with_pos(5, 5)
|
||||
.column();
|
||||
let mut row = Flex::default().with_size(890, 670).with_pos(5, 5).row();
|
||||
let mut config_picker_flex = Flex::default().column();
|
||||
// Picker
|
||||
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(&bottom_padding_btn, 5);
|
||||
config_picker_flex.end();
|
||||
row.set_size(&config_picker_flex, 200);
|
||||
|
||||
// Bottom Panel
|
||||
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 col = Flex::default().column();
|
||||
|
||||
let mut label = Frame::default().with_label("Fonts:");
|
||||
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:");
|
||||
label.set_label_font(enums::Font::HelveticaBold);
|
||||
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_size(12);
|
||||
col.set_size(&hint, 20);
|
||||
|
|
@ -338,27 +306,16 @@ impl ConfigWindow {
|
|||
|
||||
// column 2
|
||||
let mut col_grp = Flex::default().column();
|
||||
let mut tag_x_position_ratio_grp = Flex::default().row();
|
||||
tag_x_position_ratio_grp.set_size(
|
||||
let mut tag_position_ratio_grp = Flex::default().row();
|
||||
tag_position_ratio_grp.set_size(
|
||||
&Frame::default()
|
||||
.with_label("Tag (x)")
|
||||
.with_label("Tag")
|
||||
.with_align(Align::Right | Align::Inside),
|
||||
130,
|
||||
);
|
||||
let tag_x_position_ratio = ValueInput::default();
|
||||
tag_x_position_ratio_grp.end();
|
||||
col_grp.set_size(&tag_x_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 tag_position_ratio = ValueInput::default();
|
||||
tag_position_ratio_grp.end();
|
||||
col_grp.set_size(&tag_position_ratio_grp, 30);
|
||||
|
||||
let mut tag2_position_ratio_grp = Flex::default().row();
|
||||
tag2_position_ratio_grp.set_size(
|
||||
|
|
@ -391,60 +348,6 @@ impl ConfigWindow {
|
|||
image_ratio_grp.end();
|
||||
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("e_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:");
|
||||
label.set_label_font(enums::Font::HelveticaBold);
|
||||
col.set_size(&label, 15);
|
||||
|
|
@ -455,7 +358,7 @@ impl ConfigWindow {
|
|||
col.set_size(&hint, 20);
|
||||
|
||||
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);
|
||||
let mut translucent_layer_rgb = Button::default();
|
||||
translucent_layer_rgb.set_frame(enums::FrameType::BorderBox);
|
||||
|
|
@ -465,32 +368,25 @@ impl ConfigWindow {
|
|||
translucent_layer_flex.end();
|
||||
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();
|
||||
|
||||
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();
|
||||
|
||||
scroll.end();
|
||||
scroll.make_resizable(true);
|
||||
scroll.scroll_to(-1 * col.x() - 5, -1 * col.y() - 5);
|
||||
|
||||
row.end();
|
||||
win.end();
|
||||
win.make_modal(true);
|
||||
win.make_resizable(true);
|
||||
|
||||
let mut config_window = Self {
|
||||
win,
|
||||
|
|
@ -516,19 +412,12 @@ impl ConfigWindow {
|
|||
quote_position_ratio,
|
||||
subquote_position_ratio,
|
||||
subquote2_position_ratio,
|
||||
tag_x_position_ratio,
|
||||
tag_y_position_ratio,
|
||||
tag_position_ratio,
|
||||
tag2_position_ratio,
|
||||
image_ratio_width,
|
||||
image_ratio_height,
|
||||
draw_box_around_quote,
|
||||
line_spacing,
|
||||
minimum_width_limit,
|
||||
maximum_width_limit,
|
||||
translucent_layer_rgb,
|
||||
translucent_layer_alpha,
|
||||
png_format,
|
||||
jpeg_format,
|
||||
defaults_btn,
|
||||
save_btn,
|
||||
cancel_btn,
|
||||
|
|
@ -542,7 +431,7 @@ impl ConfigWindow {
|
|||
|
||||
// Show to edit config
|
||||
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();
|
||||
for (idx, name) in self.configs.borrow().keys().enumerate() {
|
||||
self.browse.add(name);
|
||||
|
|
@ -551,7 +440,7 @@ impl ConfigWindow {
|
|||
}
|
||||
}
|
||||
*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.subquote_font.set_value(config.subquote_font.as_str());
|
||||
self.subquote2_font
|
||||
|
|
@ -571,37 +460,14 @@ impl ConfigWindow {
|
|||
.set_value(config.subquote_position_ratio);
|
||||
self.subquote2_position_ratio
|
||||
.set_value(config.subquote2_position_ratio);
|
||||
self.tag_x_position_ratio
|
||||
.set_value(config.tag_x_position_ratio);
|
||||
self.tag_y_position_ratio
|
||||
.set_value(config.tag_y_position_ratio);
|
||||
self.tag_position_ratio.set_value(config.tag_position_ratio);
|
||||
self.tag2_position_ratio
|
||||
.set_value(config.tag2_position_ratio);
|
||||
self.image_ratio_width.set_value(config.image_ratio.0);
|
||||
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);
|
||||
self.translucent_layer_alpha
|
||||
.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;
|
||||
drop(config);
|
||||
self.win.show();
|
||||
|
|
@ -627,15 +493,10 @@ impl ConfigWindow {
|
|||
let mut quote_position_ratio = self.quote_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 tag_x_position_ratio = self.tag_x_position_ratio.clone();
|
||||
let mut tag_y_position_ratio = self.tag_y_position_ratio.clone();
|
||||
let mut tag_position_ratio = self.tag_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_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_alpha = self.translucent_layer_alpha.clone();
|
||||
let mut browse = self.browse.clone();
|
||||
|
|
@ -675,15 +536,10 @@ impl ConfigWindow {
|
|||
quote_position_ratio.set_value(conf.quote_position_ratio);
|
||||
subquote_position_ratio.set_value(conf.subquote_position_ratio);
|
||||
subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
|
||||
tag_x_position_ratio.set_value(conf.tag_x_position_ratio);
|
||||
tag_y_position_ratio.set_value(conf.tag_y_position_ratio);
|
||||
tag_position_ratio.set_value(conf.tag_position_ratio);
|
||||
tag2_position_ratio.set_value(conf.tag2_position_ratio);
|
||||
image_ratio_width.set_value(conf.image_ratio.0);
|
||||
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);
|
||||
layer_alpha.set_value(conf.color_layer[3] as f64);
|
||||
browse.add(&name);
|
||||
|
|
@ -707,22 +563,17 @@ impl ConfigWindow {
|
|||
let mut quote_position_ratio = self.quote_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 tag_x_position_ratio = self.tag_x_position_ratio.clone();
|
||||
let mut tag_y_position_ratio = self.tag_y_position_ratio.clone();
|
||||
let mut tag_position_ratio = self.tag_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_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_alpha = self.translucent_layer_alpha.clone();
|
||||
let mut browse = self.browse.clone();
|
||||
let configs = Rc::clone(&self.configs);
|
||||
let selected_browse_line = Rc::clone(&self.selected_browse_line);
|
||||
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 {
|
||||
return;
|
||||
}
|
||||
|
|
@ -754,15 +605,10 @@ impl ConfigWindow {
|
|||
quote_position_ratio.set_value(conf.quote_position_ratio);
|
||||
subquote_position_ratio.set_value(conf.subquote_position_ratio);
|
||||
subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
|
||||
tag_x_position_ratio.set_value(conf.tag_x_position_ratio);
|
||||
tag_y_position_ratio.set_value(conf.tag_y_position_ratio);
|
||||
tag_position_ratio.set_value(conf.tag_position_ratio);
|
||||
tag2_position_ratio.set_value(conf.tag2_position_ratio);
|
||||
image_ratio_width.set_value(conf.image_ratio.0);
|
||||
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);
|
||||
layer_alpha.set_value(conf.color_layer[3] as f64);
|
||||
layer_rgb.redraw();
|
||||
|
|
@ -783,15 +629,10 @@ impl ConfigWindow {
|
|||
let mut quote_position_ratio = self.quote_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 tag_x_position_ratio = self.tag_x_position_ratio.clone();
|
||||
let mut tag_y_position_ratio = self.tag_y_position_ratio.clone();
|
||||
let mut tag_position_ratio = self.tag_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_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_alpha = self.translucent_layer_alpha.clone();
|
||||
let configs = Rc::clone(&self.configs);
|
||||
|
|
@ -820,15 +661,10 @@ impl ConfigWindow {
|
|||
quote_position_ratio.set_value(conf.quote_position_ratio);
|
||||
subquote_position_ratio.set_value(conf.subquote_position_ratio);
|
||||
subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
|
||||
tag_x_position_ratio.set_value(conf.tag_x_position_ratio);
|
||||
tag_y_position_ratio.set_value(conf.tag_y_position_ratio);
|
||||
tag_position_ratio.set_value(conf.tag_position_ratio);
|
||||
tag2_position_ratio.set_value(conf.tag2_position_ratio);
|
||||
image_ratio_width.set_value(conf.image_ratio.0);
|
||||
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);
|
||||
layer_alpha.set_value(conf.color_layer[3] as f64);
|
||||
layer_rgb.redraw();
|
||||
|
|
@ -1066,31 +902,16 @@ impl ConfigWindow {
|
|||
true
|
||||
});
|
||||
|
||||
// Tag x position ratio
|
||||
// Tag position ratio
|
||||
let browse = self.browse.clone();
|
||||
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 let Some(conf) = configs
|
||||
.borrow_mut()
|
||||
.get_mut(&browse.selected_text().unwrap())
|
||||
{
|
||||
conf.tag_x_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();
|
||||
conf.tag_position_ratio = f.value();
|
||||
}
|
||||
}
|
||||
true
|
||||
|
|
@ -1141,62 +962,6 @@ impl ConfigWindow {
|
|||
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
|
||||
let browse = self.browse.clone();
|
||||
let configs = Rc::clone(&self.configs);
|
||||
|
|
@ -1241,30 +1006,6 @@ impl ConfigWindow {
|
|||
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
|
||||
let mut quote_font = self.quote_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 subquote_position_ratio = self.subquote_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_y_position_ratio = self.tag_y_position_ratio.clone();
|
||||
let mut tag_position_ratio = self.tag_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_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_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 browse = self.browse.clone();
|
||||
self.defaults_btn.set_callback(move |_| {
|
||||
|
|
@ -1309,29 +1043,13 @@ impl ConfigWindow {
|
|||
quote_position_ratio.set_value(conf.quote_position_ratio);
|
||||
subquote_position_ratio.set_value(conf.subquote_position_ratio);
|
||||
subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
|
||||
tag_x_position_ratio.set_value(conf.tag_x_position_ratio);
|
||||
tag_y_position_ratio.set_value(conf.tag_y_position_ratio);
|
||||
tag_position_ratio.set_value(conf.tag_position_ratio);
|
||||
tag2_position_ratio.set_value(conf.tag2_position_ratio);
|
||||
image_ratio_width.set_value(conf.image_ratio.0);
|
||||
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);
|
||||
layer_rgb.redraw();
|
||||
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
|
||||
.borrow_mut()
|
||||
.insert(browse.selected_text().unwrap(), conf);
|
||||
|
|
@ -1344,8 +1062,8 @@ impl ConfigWindow {
|
|||
self.save_btn.set_callback(move |_| {
|
||||
config::save_configs((*configs.borrow()).clone());
|
||||
|
||||
if let Some(c) = configs.borrow().get(&*rw_read!(globals::CONFIG_NAME)) {
|
||||
*rw_write!(globals::CONFIG) = c.to_owned();
|
||||
if let Some(c) = configs.borrow().get(&*globals::CONFIG_NAME.read().unwrap()) {
|
||||
*globals::CONFIG.write().unwrap() = c.to_owned();
|
||||
}
|
||||
*did_save.borrow_mut() = true;
|
||||
win.hide();
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
//! Window to change Crop properties of image
|
||||
use crate::{
|
||||
globals,
|
||||
result_ext::ResultExt,
|
||||
utils::{self, Coord, ImageContainer, ImageInfo, ImageProperties},
|
||||
utils::{self, Coord, ImageContainer, ImageProperties},
|
||||
};
|
||||
use fltk::{
|
||||
app, button::Button, draw, enums::Event, frame::Frame, group::Flex, image::SvgImage,
|
||||
|
|
@ -25,6 +24,7 @@ use fltk::{
|
|||
use image::GenericImageView;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
path::PathBuf,
|
||||
rc::Rc,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
|
@ -108,13 +108,13 @@ impl CropWindow {
|
|||
/// Call it to show window to crop image
|
||||
pub(crate) fn load_to_crop(
|
||||
&mut self,
|
||||
path: &ImageInfo,
|
||||
path: &PathBuf,
|
||||
crop_pos: Option<(f64, f64)>,
|
||||
) -> Option<(f64, f64)> {
|
||||
let mut container =
|
||||
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.crop_position = match crop_pos {
|
||||
Some(a) => Some(a),
|
||||
|
|
@ -146,7 +146,7 @@ impl CropWindow {
|
|||
}
|
||||
|
||||
if let Some(cont) = &*self.container.borrow() {
|
||||
rw_read!(cont.properties).crop_position
|
||||
cont.properties.read().unwrap().crop_position
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -169,7 +169,7 @@ impl CropWindow {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let prop = rw_read!(cont.properties);
|
||||
let prop = cont.properties.read().unwrap();
|
||||
let (original_width, original_height) = prop.original_dimension;
|
||||
let (original_x, original_y) = prop.crop_position.unwrap();
|
||||
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() {
|
||||
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 {
|
||||
Some(v) => v,
|
||||
|
|
@ -272,7 +272,7 @@ impl CropWindow {
|
|||
let container = Rc::clone(&self.container);
|
||||
self.win.set_callback(move |f| {
|
||||
if let Some(cont) = &*container.borrow_mut() {
|
||||
rw_write!(cont.properties).crop_position = None;
|
||||
cont.properties.write().unwrap().crop_position = None;
|
||||
}
|
||||
f.hide();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -14,17 +14,16 @@
|
|||
|
||||
//! Thread to manage drawing in background
|
||||
|
||||
use crate::utils::{ImageContainer, ImageProperties};
|
||||
use crate::{
|
||||
globals,
|
||||
main_window::{MainWindow, Page},
|
||||
result_ext::ResultExt,
|
||||
utils::{self, ImageContainer, ImageInfo, ImageProperties, ImagePropertiesFile},
|
||||
utils::{self, ImagePropertiesFile},
|
||||
AppMessage,
|
||||
};
|
||||
use fltk::{
|
||||
app,
|
||||
button::Button,
|
||||
enums,
|
||||
dialog, enums,
|
||||
frame::Frame,
|
||||
input::{Input, MultilineInput},
|
||||
menu,
|
||||
|
|
@ -34,6 +33,7 @@ use fltk::{
|
|||
};
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
sync::{mpsc, Arc, RwLock},
|
||||
};
|
||||
|
||||
|
|
@ -53,10 +53,6 @@ pub(crate) enum DrawMessage {
|
|||
Clone,
|
||||
/// Delete file
|
||||
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
|
||||
|
|
@ -68,7 +64,6 @@ pub(crate) fn spawn_image_thread(
|
|||
) {
|
||||
let mut win = main_win.win.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 subquote = main_win.subquote.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 count = main_win.count.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;
|
||||
std::thread::spawn(move || loop {
|
||||
|
|
@ -100,9 +95,8 @@ pub(crate) fn spawn_image_thread(
|
|||
status.set_label("Loading...");
|
||||
load_image(
|
||||
&mut file_choice,
|
||||
Arc::clone(&images_list),
|
||||
Arc::clone(&images_path),
|
||||
None,
|
||||
&mut name_prefix,
|
||||
&mut quote,
|
||||
&mut subquote,
|
||||
&mut subquote2,
|
||||
|
|
@ -134,9 +128,8 @@ pub(crate) fn spawn_image_thread(
|
|||
status.set_label("Loading...");
|
||||
load_image(
|
||||
&mut file_choice,
|
||||
Arc::clone(&images_list),
|
||||
Arc::clone(&images_path),
|
||||
Some((x, y)),
|
||||
&mut name_prefix,
|
||||
&mut quote,
|
||||
&mut subquote,
|
||||
&mut subquote2,
|
||||
|
|
@ -176,23 +169,21 @@ pub(crate) fn spawn_image_thread(
|
|||
status.set_label("Saving...");
|
||||
win.deactivate();
|
||||
cont.save();
|
||||
status.set_label("");
|
||||
win.activate();
|
||||
win.redraw();
|
||||
app::awake();
|
||||
status.set_label("");
|
||||
}
|
||||
}
|
||||
DrawMessage::Clone => {
|
||||
if let Some(cont) = &mut _container {
|
||||
status.set_label("Cloning...");
|
||||
win.deactivate();
|
||||
if let Some(image_info) = cont.clone_img() {
|
||||
if let Some(path) = cont.clone_img() {
|
||||
let idx = file_choice.value();
|
||||
let mut imgs = rw_write!(images_list);
|
||||
imgs.insert(idx as usize, image_info.clone());
|
||||
let mut imgs = images_path.write().unwrap();
|
||||
imgs.insert(idx as usize, path.clone());
|
||||
file_choice.insert(
|
||||
idx,
|
||||
image_info.path.file_name().unwrap().to_str().unwrap(),
|
||||
path.file_name().unwrap().to_str().unwrap(),
|
||||
enums::Shortcut::None,
|
||||
menu::MenuFlag::Normal,
|
||||
|a| a.do_callback(),
|
||||
|
|
@ -201,8 +192,6 @@ pub(crate) fn spawn_image_thread(
|
|||
}
|
||||
status.set_label("");
|
||||
win.activate();
|
||||
win.redraw();
|
||||
app::awake();
|
||||
}
|
||||
}
|
||||
DrawMessage::Delete => {
|
||||
|
|
@ -210,7 +199,7 @@ pub(crate) fn spawn_image_thread(
|
|||
status.set_label("Deleting...");
|
||||
win.deactivate();
|
||||
cont.delete();
|
||||
let mut imgs = rw_write!(images_list);
|
||||
let mut imgs = images_path.write().unwrap();
|
||||
imgs.remove(file_choice.value() as usize);
|
||||
file_choice.remove(file_choice.value());
|
||||
if file_choice.value() != imgs.len() as i32 {
|
||||
|
|
@ -220,17 +209,6 @@ pub(crate) fn spawn_image_thread(
|
|||
}
|
||||
status.set_label("");
|
||||
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
|
||||
fn load_image(
|
||||
file_choice: &mut menu::Choice,
|
||||
images_list: Arc<RwLock<Vec<ImageInfo>>>,
|
||||
images_path: Arc<RwLock<Vec<PathBuf>>>,
|
||||
crop: Option<(f64, f64)>,
|
||||
name_prefix: &mut Input,
|
||||
quote: &mut MultilineInput,
|
||||
subquote: &mut MultilineInput,
|
||||
subquote2: &mut MultilineInput,
|
||||
|
|
@ -268,36 +245,43 @@ fn load_image(
|
|||
properties: Arc<RwLock<ImageProperties>>,
|
||||
container: &mut Option<ImageContainer>,
|
||||
) {
|
||||
let imgs = rw_read!(images_list);
|
||||
let imgs = images_path.read().unwrap();
|
||||
if imgs.len() == 0 {
|
||||
*container = None;
|
||||
flush_buffer(app_sender, container);
|
||||
return;
|
||||
}
|
||||
count.set_label(&format!("[{}/{}]", file_choice.value() + 1, imgs.len()));
|
||||
let 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 {
|
||||
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 = match serde_json::from_str::<ImagePropertiesFile>(&read) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Result::<(), _>::Err(e).warn_log("Config is corrupt");
|
||||
fs::remove_file(&properties_file)
|
||||
.warn_log("Failed to delete image properties file!");
|
||||
ImagePropertiesFile::default()
|
||||
warn!("Config is corrupt\n{:?}", e);
|
||||
match dialog::choice_default("Config is corrupt, fix??", "yes", "no", "") {
|
||||
1 => {
|
||||
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.is_saved = true;
|
||||
|
||||
name_prefix.set_value(&properties.name_prefix);
|
||||
quote.set_value(&properties.quote);
|
||||
subquote.set_value(&properties.subquote);
|
||||
subquote2.set_value(&properties.subquote2);
|
||||
|
|
@ -348,7 +332,7 @@ fn load_image(
|
|||
}
|
||||
|
||||
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.row_flex.set_size(&page.col_flex, width as i32);
|
||||
page.col_flex.recalc();
|
||||
|
|
@ -358,40 +342,6 @@ fn load_image(
|
|||
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
|
||||
// for drawing buffer for fltk (check in main.rs)
|
||||
fn flush_buffer(app_sender: &app::Sender<crate::AppMessage>, container: &Option<ImageContainer>) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
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 rusttype::Font;
|
||||
use std::{ffi::OsString, io::Read, sync::RwLock};
|
||||
|
|
@ -29,23 +29,86 @@ lazy_static! {
|
|||
pub(crate) static ref CONFIG: RwLock<config::ConfigFile> =
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
pub(crate) static ref ICON: OsString = include_str!("../assets/icon.svg").into();
|
||||
|
|
@ -62,15 +125,3 @@ lazy_static! {
|
|||
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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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!")
|
||||
};
|
||||
}
|
||||
59
src/main.rs
59
src/main.rs
|
|
@ -17,29 +17,23 @@
|
|||
extern crate log;
|
||||
extern crate simplelog;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod about_window;
|
||||
mod about;
|
||||
mod config;
|
||||
mod config_picker;
|
||||
mod config_window;
|
||||
mod crop_window;
|
||||
mod dialog;
|
||||
mod draw_thread;
|
||||
mod export_all_window;
|
||||
mod globals;
|
||||
mod main_window;
|
||||
mod result_ext;
|
||||
mod utils;
|
||||
|
||||
use fltk::{
|
||||
app::{channel, App},
|
||||
dialog,
|
||||
prelude::*,
|
||||
};
|
||||
use fltk_theme::WidgetTheme;
|
||||
use main_window::MainWindow;
|
||||
use result_ext::ResultExt;
|
||||
use simplelog::*;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
|
|
@ -47,27 +41,17 @@ use std::sync::{Arc, RwLock};
|
|||
pub(crate) enum AppMessage {
|
||||
/// Copy recived image buffer from draw_thread to Buffer for fltk frame
|
||||
RedrawMainWindowImage(Option<Vec<u8>>),
|
||||
Message(String),
|
||||
Alert(String),
|
||||
ProgramPanicMessage(String),
|
||||
|
||||
// Only for Main windows
|
||||
DeleteImage,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let app = App::default();
|
||||
WidgetTheme::new(globals::THEME.clone().into()).apply();
|
||||
|
||||
if let Err(e) = CombinedLogger::init(vec![
|
||||
WriteLogger::new(LevelFilter::Warn, Config::default(), config::log_file()),
|
||||
TermLogger::new(
|
||||
LevelFilter::Info,
|
||||
Config::default(),
|
||||
TerminalMode::Mixed,
|
||||
ColorChoice::Auto,
|
||||
),
|
||||
]) {
|
||||
if let Err(e) = CombinedLogger::init(vec![WriteLogger::new(
|
||||
LevelFilter::Info,
|
||||
Config::default(),
|
||||
config::log_file(),
|
||||
)]) {
|
||||
dialog::alert_default("Failed to start logger");
|
||||
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 (main_sender, main_receiver) = channel::<AppMessage>();
|
||||
*rw_write!(globals::MAIN_SENDER) = Some(main_sender);
|
||||
let mut main_window = MainWindow::new(Arc::clone(&draw_buff));
|
||||
let mut main_window = MainWindow::new(main_sender, Arc::clone(&draw_buff));
|
||||
|
||||
while app.wait() {
|
||||
if let Some(msg) = main_receiver.recv() {
|
||||
match msg {
|
||||
AppMessage::RedrawMainWindowImage(data) => {
|
||||
let mut buff = rw_write!(draw_buff);
|
||||
let mut buff = draw_buff.write().unwrap();
|
||||
*buff = data;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,19 +13,16 @@
|
|||
*/
|
||||
|
||||
//! Main window where you do all editing
|
||||
use crate::{
|
||||
about_window::About,
|
||||
config_window::ConfigWindow,
|
||||
crop_window::CropWindow,
|
||||
dialog,
|
||||
draw_thread::*,
|
||||
export_all_window::ExportAllWindow,
|
||||
globals,
|
||||
result_ext::ResultExt,
|
||||
utils::{self, ImageInfo, ImageProperties, ImageType},
|
||||
};
|
||||
use crate::about::About;
|
||||
use crate::crop_window::CropWindow;
|
||||
use crate::draw_thread::*;
|
||||
use crate::utils;
|
||||
use crate::utils::ImageProperties;
|
||||
use crate::{config_window::ConfigWindow, globals};
|
||||
use fltk::{
|
||||
app,
|
||||
button::Button,
|
||||
dialog,
|
||||
dialog::NativeFileChooser,
|
||||
draw as dr, enums,
|
||||
enums::Shortcut,
|
||||
|
|
@ -39,14 +36,9 @@ use fltk::{
|
|||
valuator::{Slider, SliderType},
|
||||
window::Window,
|
||||
};
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
fs,
|
||||
path::PathBuf,
|
||||
process::Command,
|
||||
sync::Arc,
|
||||
sync::{mpsc, RwLock},
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{mpsc, RwLock};
|
||||
use std::{ffi::OsStr, fs, sync::Arc};
|
||||
|
||||
pub(crate) struct MainWindow {
|
||||
pub(crate) win: Window,
|
||||
|
|
@ -56,7 +48,6 @@ pub(crate) struct MainWindow {
|
|||
pub(crate) save_btn: Button,
|
||||
/// To choose the file which is being edited in directory
|
||||
pub(crate) file_choice: menu::Choice,
|
||||
pub(crate) name_prefix: Input,
|
||||
pub(crate) quote: MultilineInput,
|
||||
pub(crate) subquote: MultilineInput,
|
||||
pub(crate) subquote2: MultilineInput,
|
||||
|
|
@ -90,7 +81,7 @@ pub(crate) struct MainWindow {
|
|||
pub(crate) count: Frame,
|
||||
pub(crate) dimension: Frame,
|
||||
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) properties: Arc<RwLock<ImageProperties>>,
|
||||
pub(crate) sender: mpsc::Sender<DrawMessage>,
|
||||
|
|
@ -105,7 +96,10 @@ pub(crate) struct Page {
|
|||
}
|
||||
|
||||
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();
|
||||
win.set_icon(Some(
|
||||
SvgImage::from_data(globals::ICON.to_str().unwrap()).unwrap(),
|
||||
|
|
@ -134,15 +128,6 @@ impl MainWindow {
|
|||
let mut workspace_flex = Flex::default().row();
|
||||
// Controls Left
|
||||
let mut left_controls_flex = Flex::default().column();
|
||||
left_controls_flex.set_size(
|
||||
&Frame::default()
|
||||
.with_label("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(
|
||||
&Frame::default()
|
||||
.with_label("Quote:")
|
||||
|
|
@ -365,7 +350,6 @@ impl MainWindow {
|
|||
next_btn,
|
||||
save_btn,
|
||||
file_choice,
|
||||
name_prefix,
|
||||
quote,
|
||||
subquote,
|
||||
subquote2,
|
||||
|
|
@ -396,7 +380,7 @@ impl MainWindow {
|
|||
status,
|
||||
count,
|
||||
dimension,
|
||||
images_list: Arc::new(RwLock::new(vec![])),
|
||||
images_path: Arc::new(RwLock::new(vec![])),
|
||||
draw_buff,
|
||||
properties: Arc::clone(&properties),
|
||||
page: Page {
|
||||
|
|
@ -406,10 +390,7 @@ impl MainWindow {
|
|||
},
|
||||
sender: rx,
|
||||
};
|
||||
|
||||
if let Some(a) = &*rw_read!(globals::MAIN_SENDER) {
|
||||
spawn_image_thread(tx, a.to_owned(), Arc::clone(&properties), &main_win);
|
||||
}
|
||||
spawn_image_thread(tx, sender, Arc::clone(&properties), &main_win);
|
||||
main_win.menu();
|
||||
main_win.draw();
|
||||
main_win.events();
|
||||
|
|
@ -420,8 +401,7 @@ impl MainWindow {
|
|||
fn menu(&mut self) {
|
||||
let mut file_choice = self.file_choice.clone();
|
||||
let sender = self.sender.clone();
|
||||
let imgs = Arc::clone(&self.images_list);
|
||||
let mut win = self.win.clone();
|
||||
let imgs = Arc::clone(&self.images_path);
|
||||
self.menubar.add(
|
||||
"&File/Open Folder...\t",
|
||||
Shortcut::Ctrl | 'o',
|
||||
|
|
@ -435,12 +415,14 @@ impl MainWindow {
|
|||
if !path.exists() {
|
||||
return;
|
||||
}
|
||||
win.set_label(&format!(
|
||||
"{} - Post Maker",
|
||||
path.file_name()
|
||||
.unwrap_or(OsStr::new("Unknown"))
|
||||
.to_string_lossy()
|
||||
));
|
||||
let expost_dir = path.join("export");
|
||||
if !expost_dir.exists() {
|
||||
if let Err(e) = fs::create_dir(expost_dir) {
|
||||
fltk::dialog::alert_default("Failed to create export folder!");
|
||||
warn!("Failed to create export folder!\n{:?}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
load_dir(&path, Arc::clone(&imgs), &mut file_choice, &sender);
|
||||
},
|
||||
);
|
||||
|
|
@ -448,93 +430,13 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
self.menubar.add(
|
||||
"&File/Save Image...\t",
|
||||
"&File/Save...\t",
|
||||
Shortcut::Ctrl | 's',
|
||||
menu::MenuFlag::Normal,
|
||||
move |_| {
|
||||
let mut prop = rw_write!(properties);
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.is_saved = true;
|
||||
sender.send_it(DrawMessage::Save);
|
||||
},
|
||||
);
|
||||
|
||||
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();
|
||||
sender.send(DrawMessage::Save).unwrap();
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -547,8 +449,8 @@ impl MainWindow {
|
|||
menu::MenuFlag::Normal,
|
||||
move |_| {
|
||||
if config_window.show() {
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
}
|
||||
},
|
||||
|
|
@ -570,8 +472,8 @@ impl MainWindow {
|
|||
let buff = Arc::clone(&self.draw_buff);
|
||||
let properties = Arc::clone(&self.properties);
|
||||
self.page.image.draw(move |f| {
|
||||
let (width, height) = rw_read!(properties).dimension;
|
||||
if let Some(image) = &*rw_read!(buff) {
|
||||
let (width, height) = properties.read().unwrap().dimension;
|
||||
if let Some(image) = &*buff.read().unwrap() {
|
||||
dr::draw_image(
|
||||
&image,
|
||||
f.x(),
|
||||
|
|
@ -590,10 +492,10 @@ impl MainWindow {
|
|||
// Resest Button for FileChoice
|
||||
let mut file_choice = self.file_choice.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 |_| {
|
||||
let path = match rw_read!(imgs).first() {
|
||||
Some(image_info) => image_info.path.parent().unwrap().to_path_buf(),
|
||||
let path = match imgs.read().unwrap().first() {
|
||||
Some(path) => path.parent().unwrap().to_path_buf(),
|
||||
None => return,
|
||||
};
|
||||
load_dir(&path, Arc::clone(&imgs), &mut file_choice, &sender);
|
||||
|
|
@ -606,14 +508,14 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
self.reset_translucent_layer_btn.set_callback(move |_| {
|
||||
let mut prop = rw_write!(properties);
|
||||
let color = rw_read!(globals::CONFIG).color_layer;
|
||||
let mut prop = properties.write().unwrap();
|
||||
let color = globals::CONFIG.read().unwrap().color_layer;
|
||||
prop.translucent_layer_color = color;
|
||||
prop.is_saved = false;
|
||||
utils::set_color_btn_rgba(color, &mut layer_rgb);
|
||||
layer_alpha.set_value(color[3] as f64);
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -624,16 +526,16 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
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 pos = height * rw_read!(globals::CONFIG).quote_position_ratio;
|
||||
let pos = height * globals::CONFIG.read().unwrap().quote_position_ratio;
|
||||
prop.quote_position = pos;
|
||||
prop.is_saved = false;
|
||||
quote_position.set_value(pos);
|
||||
quote_position_slider.set_value(pos);
|
||||
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -644,16 +546,16 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
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 pos = height * rw_read!(globals::CONFIG).subquote_position_ratio;
|
||||
let pos = height * globals::CONFIG.read().unwrap().subquote_position_ratio;
|
||||
prop.subquote_position = pos;
|
||||
prop.is_saved = false;
|
||||
subquote_position.set_value(pos);
|
||||
subquote_position_slider.set_value(pos);
|
||||
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -664,16 +566,16 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
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 pos = height * rw_read!(globals::CONFIG).subquote2_position_ratio;
|
||||
let pos = height * globals::CONFIG.read().unwrap().subquote2_position_ratio;
|
||||
prop.subquote2_position = pos;
|
||||
prop.is_saved = false;
|
||||
subquote2_position.set_value(pos);
|
||||
subquote2_position_slider.set_value(pos);
|
||||
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -684,16 +586,16 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
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 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.is_saved = false;
|
||||
tag_position.set_value(pos);
|
||||
tag_position_slider.set_value(pos);
|
||||
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -704,16 +606,16 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
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 pos = height * rw_read!(globals::CONFIG).tag2_position_ratio;
|
||||
let pos = height * globals::CONFIG.read().unwrap().tag2_position_ratio;
|
||||
prop.tag2_position = pos;
|
||||
prop.is_saved = false;
|
||||
tag2_position.set_value(pos);
|
||||
tag2_position_slider.set_value(pos);
|
||||
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -721,9 +623,9 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
self.save_btn.set_callback(move |_| {
|
||||
let mut prop = rw_write!(properties);
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.is_saved = true;
|
||||
sender.send_it(DrawMessage::Save);
|
||||
sender.send(DrawMessage::Save).unwrap()
|
||||
});
|
||||
|
||||
// Clone Button
|
||||
|
|
@ -731,11 +633,10 @@ impl MainWindow {
|
|||
let mut file_choice = self.file_choice.clone();
|
||||
let sender = self.sender.clone();
|
||||
self.clone_btn.set_callback(move |_| {
|
||||
let ch = dialog::choice_default("Do you want to clone??", "Yes", "No");
|
||||
let ch = dialog::choice_default("Do you want to clone??", "Yes", "No", "");
|
||||
if ch == 0 {
|
||||
sender.send_it(DrawMessage::Clone);
|
||||
sender.send_it(DrawMessage::Open);
|
||||
sender.send_it(DrawMessage::CheckImage);
|
||||
sender.send(DrawMessage::Clone).unwrap();
|
||||
sender.send(DrawMessage::Open).unwrap();
|
||||
image.redraw();
|
||||
file_choice.redraw();
|
||||
}
|
||||
|
|
@ -746,11 +647,10 @@ impl MainWindow {
|
|||
let mut file_choice = self.file_choice.clone();
|
||||
let sender = self.sender.clone();
|
||||
self.delete_btn.set_callback(move |_| {
|
||||
let ch = dialog::choice_default("Do you want to delete??", "Yes", "No");
|
||||
let ch = dialog::choice_default("Do you want to delete??", "Yes", "No", "");
|
||||
if ch == 0 {
|
||||
sender.send_it(DrawMessage::Delete);
|
||||
sender.send_it(DrawMessage::Open);
|
||||
sender.send_it(DrawMessage::CheckImage);
|
||||
sender.send(DrawMessage::Delete).unwrap();
|
||||
sender.send(DrawMessage::Open).unwrap();
|
||||
image.redraw();
|
||||
file_choice.redraw();
|
||||
}
|
||||
|
|
@ -761,10 +661,10 @@ impl MainWindow {
|
|||
let mut crop_win = CropWindow::new();
|
||||
let sender = self.sender.clone();
|
||||
self.crop_btn.set_callback(move |_| {
|
||||
let mut prop = rw_write!(properties);
|
||||
if let Some(image_info) = &prop.image_info {
|
||||
if let Some((x, y)) = crop_win.load_to_crop(&image_info, prop.crop_position) {
|
||||
sender.send_it(DrawMessage::ChangeCrop((x, y)));
|
||||
let mut prop = properties.write().unwrap();
|
||||
if let Some(path) = &prop.path {
|
||||
if let Some((x, y)) = crop_win.load_to_crop(path, prop.crop_position) {
|
||||
sender.send(DrawMessage::ChangeCrop((x, y))).unwrap();
|
||||
prop.is_saved = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -775,11 +675,11 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
self.next_btn.set_callback(move |_| {
|
||||
let prop = rw_read!(properties);
|
||||
let prop = properties.read().unwrap();
|
||||
if !prop.is_saved {
|
||||
let save = fltk::dialog::choice2_default("Save?", "yes", "no", "cancel");
|
||||
match save.unwrap_or(-1) {
|
||||
0 => sender.send_it(DrawMessage::Save),
|
||||
let save = fltk::dialog::choice_default("Save?", "yes", "no", "cancel");
|
||||
match save {
|
||||
0 => sender.send(DrawMessage::Save).unwrap(),
|
||||
1 => {}
|
||||
_ => return,
|
||||
}
|
||||
|
|
@ -790,8 +690,7 @@ impl MainWindow {
|
|||
} else {
|
||||
file_choice.set_value(file_choice.value() + 1);
|
||||
}
|
||||
sender.send_it(DrawMessage::Open);
|
||||
sender.send_it(DrawMessage::CheckImage);
|
||||
sender.send(DrawMessage::Open).unwrap();
|
||||
});
|
||||
|
||||
// Back Image Button
|
||||
|
|
@ -799,11 +698,11 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
self.back_btn.set_callback(move |_| {
|
||||
let prop = rw_read!(properties);
|
||||
let prop = properties.read().unwrap();
|
||||
if !prop.is_saved {
|
||||
let save = fltk::dialog::choice2_default("Save?", "yes", "no", "cancel");
|
||||
match save.unwrap_or(-1) {
|
||||
0 => sender.send_it(DrawMessage::Save),
|
||||
let save = fltk::dialog::choice_default("Save?", "yes", "no", "cancel");
|
||||
match save {
|
||||
0 => sender.send(DrawMessage::Save).unwrap(),
|
||||
1 => {}
|
||||
_ => return,
|
||||
}
|
||||
|
|
@ -814,36 +713,23 @@ impl MainWindow {
|
|||
} else {
|
||||
file_choice.set_value(file_choice.value() - 1);
|
||||
}
|
||||
sender.send_it(DrawMessage::Open);
|
||||
sender.send_it(DrawMessage::CheckImage);
|
||||
sender.send(DrawMessage::Open).unwrap();
|
||||
});
|
||||
|
||||
// File Choice
|
||||
let sender = self.sender.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
self.file_choice.set_callback(move |_| {
|
||||
let prop = rw_read!(properties);
|
||||
let prop = properties.read().unwrap();
|
||||
if !prop.is_saved {
|
||||
let save = fltk::dialog::choice2_default("Save?", "yes", "no", "cancel");
|
||||
match save.unwrap_or(-1) {
|
||||
0 => sender.send_it(DrawMessage::Save),
|
||||
let save = fltk::dialog::choice_default("Save?", "yes", "no", "cancel");
|
||||
match save {
|
||||
0 => sender.send(DrawMessage::Save).unwrap(),
|
||||
1 => {}
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
sender.send_it(DrawMessage::Open);
|
||||
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
|
||||
sender.send(DrawMessage::Open).unwrap();
|
||||
});
|
||||
|
||||
// Quote Input
|
||||
|
|
@ -852,11 +738,11 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
self.quote.handle(move |f, ev| {
|
||||
if ev == enums::Event::KeyUp {
|
||||
let mut prop = rw_write!(properties);
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.quote = f.value();
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
}
|
||||
true
|
||||
|
|
@ -868,11 +754,11 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
self.subquote.handle(move |f, ev| {
|
||||
if ev == enums::Event::KeyUp {
|
||||
let mut prop = rw_write!(properties);
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.subquote = f.value();
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
}
|
||||
true
|
||||
|
|
@ -884,11 +770,11 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
self.subquote2.handle(move |f, ev| {
|
||||
if ev == enums::Event::KeyUp {
|
||||
let mut prop = rw_write!(properties);
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.subquote2 = f.value();
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
}
|
||||
true
|
||||
|
|
@ -900,11 +786,11 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
self.tag.handle(move |f, ev| {
|
||||
if ev == enums::Event::KeyUp {
|
||||
let mut prop = rw_write!(properties);
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.tag = f.value();
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
}
|
||||
true
|
||||
|
|
@ -916,11 +802,11 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
self.tag2.handle(move |f, ev| {
|
||||
if ev == enums::Event::KeyUp {
|
||||
let mut prop = rw_write!(properties);
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.tag2 = f.value();
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
}
|
||||
true
|
||||
|
|
@ -932,12 +818,12 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let mut quote_position_slider = self.quote_position_slider.clone();
|
||||
self.quote_position.set_callback(move |f| {
|
||||
let mut prop = rw_write!(properties);
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.quote_position = f.value();
|
||||
quote_position_slider.set_value(f.value());
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -947,12 +833,12 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let mut quote_position = self.quote_position.clone();
|
||||
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();
|
||||
quote_position.set_value(f.value());
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -962,12 +848,12 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let mut subquote_position_slider = self.subquote_position_slider.clone();
|
||||
self.subquote_position.set_callback(move |f| {
|
||||
let mut prop = rw_write!(properties);
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.subquote_position = f.value();
|
||||
subquote_position_slider.set_value(f.value());
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -977,12 +863,12 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let mut subquote_position = self.subquote_position.clone();
|
||||
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();
|
||||
subquote_position.set_value(f.value());
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -992,12 +878,12 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let mut subquote2_position_slider = self.subquote2_position_slider.clone();
|
||||
self.subquote2_position.set_callback(move |f| {
|
||||
let mut prop = rw_write!(properties);
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.subquote2_position = f.value();
|
||||
subquote2_position_slider.set_value(f.value());
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -1007,12 +893,12 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let mut subquote2_position = self.subquote2_position.clone();
|
||||
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();
|
||||
subquote2_position.set_value(f.value());
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -1022,12 +908,12 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let mut tag_position_slider = self.tag_position_slider.clone();
|
||||
self.tag_position.set_callback(move |f| {
|
||||
let mut prop = rw_write!(properties);
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.tag_position = f.value();
|
||||
tag_position_slider.set_value(f.value());
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -1037,12 +923,12 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let mut tag_position = self.tag_position.clone();
|
||||
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();
|
||||
tag_position.set_value(f.value());
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -1052,12 +938,12 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let mut tag2_position_slider = self.tag2_position_slider.clone();
|
||||
self.tag2_position.set_callback(move |f| {
|
||||
let mut prop = rw_write!(properties);
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.tag2_position = f.value();
|
||||
tag2_position_slider.set_value(f.value());
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -1067,12 +953,12 @@ impl MainWindow {
|
|||
let sender = self.sender.clone();
|
||||
let mut tag2_position = self.tag2_position.clone();
|
||||
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();
|
||||
tag2_position.set_value(f.value());
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -1081,7 +967,7 @@ impl MainWindow {
|
|||
let properties = Arc::clone(&self.properties);
|
||||
let sender = self.sender.clone();
|
||||
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(
|
||||
"Pick a colour",
|
||||
dialog::ColorMode::Byte,
|
||||
|
|
@ -1095,8 +981,8 @@ impl MainWindow {
|
|||
utils::set_color_btn_rgba(prop.translucent_layer_color, &mut f);
|
||||
f.redraw();
|
||||
prop.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
|
|
@ -1105,11 +991,11 @@ impl MainWindow {
|
|||
let properties = Arc::clone(&self.properties);
|
||||
let sender = self.sender.clone();
|
||||
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.is_saved = false;
|
||||
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||
sender.send_it(DrawMessage::Flush);
|
||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
}
|
||||
|
|
@ -1118,7 +1004,7 @@ impl MainWindow {
|
|||
/// Load all iamges in a directory
|
||||
fn load_dir(
|
||||
path: &PathBuf,
|
||||
imgs: Arc<RwLock<Vec<ImageInfo>>>,
|
||||
imgs: Arc<RwLock<Vec<PathBuf>>>,
|
||||
file_choice: &mut menu::Choice,
|
||||
sender: &mpsc::Sender<DrawMessage>,
|
||||
) {
|
||||
|
|
@ -1128,22 +1014,15 @@ fn load_dir(
|
|||
.collect::<Vec<fs::DirEntry>>();
|
||||
files.sort_by_key(|i| i.file_name());
|
||||
let mut text = String::new();
|
||||
let mut imgs_b = rw_write!(imgs);
|
||||
let mut imgs_b = imgs.write().unwrap();
|
||||
*imgs_b = vec![];
|
||||
for file in files {
|
||||
let path = file.path();
|
||||
if let Ok(Some(ty)) = infer::get_from_path(&path) {
|
||||
let mime = ty.mime_type();
|
||||
match ImageType::from_mime(mime) {
|
||||
ImageType::None => (),
|
||||
_ => {
|
||||
text = format!("{}|{}", text, path.file_name().unwrap().to_str().unwrap());
|
||||
imgs_b.push(ImageInfo {
|
||||
path,
|
||||
image_type: ImageType::from_mime(mime),
|
||||
});
|
||||
}
|
||||
}
|
||||
if path.extension() == Some(OsStr::new("jpg"))
|
||||
|| path.extension() == Some(OsStr::new("png"))
|
||||
{
|
||||
text = format!("{}|{}", text, path.file_name().unwrap().to_str().unwrap());
|
||||
imgs_b.push(path);
|
||||
}
|
||||
}
|
||||
if text.len() == 0 {
|
||||
|
|
@ -1152,16 +1031,5 @@ fn load_dir(
|
|||
file_choice.clear();
|
||||
file_choice.add_choice(&text[1..]);
|
||||
file_choice.set_value(0);
|
||||
sender.send_it(DrawMessage::Open);
|
||||
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!");
|
||||
}
|
||||
sender.send(DrawMessage::Open).unwrap();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
609
src/utils.rs
609
src/utils.rs
|
|
@ -13,19 +13,16 @@
|
|||
*/
|
||||
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::Read,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use fltk::{button::Button, enums, prelude::*};
|
||||
use image::{DynamicImage, GenericImageView, ImageBuffer, ImageEncoder, Pixel};
|
||||
use imageproc::rect::Rect;
|
||||
use fltk::{button::Button, dialog, enums, prelude::*};
|
||||
use image::{DynamicImage, GenericImageView, ImageBuffer};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::globals;
|
||||
use crate::result_ext::ResultExt;
|
||||
|
||||
/// helps cast tupels to 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)
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ImageContainer {
|
||||
|
|
@ -116,18 +72,27 @@ pub(crate) struct ImageContainer {
|
|||
}
|
||||
|
||||
impl ImageContainer {
|
||||
pub(crate) fn new(image_info: &ImageInfo, properties: Arc<RwLock<ImageProperties>>) -> Self {
|
||||
let img = load_image(&image_info);
|
||||
pub(crate) fn new(path: &PathBuf, properties: Arc<RwLock<ImageProperties>>) -> Self {
|
||||
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 config = rw_read!(globals::CONFIG);
|
||||
let mut prop = rw_write!(properties);
|
||||
prop.image_info = Some(image_info.to_owned());
|
||||
let config = globals::CONFIG.read().unwrap();
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.path = Some(path.to_owned());
|
||||
prop.original_dimension = (width, height);
|
||||
prop.quote_position = height * config.quote_position_ratio;
|
||||
prop.subquote_position = height * config.subquote_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;
|
||||
|
||||
Self {
|
||||
|
|
@ -139,7 +104,7 @@ impl ImageContainer {
|
|||
|
||||
/// Resize image
|
||||
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 (s_width, s_height) = ((width * 500.0) / height, 500.0);
|
||||
|
||||
|
|
@ -151,14 +116,13 @@ impl ImageContainer {
|
|||
|
||||
/// Crop Image
|
||||
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 (origina_crop_width, origina_crop_height) =
|
||||
croped_ratio(original_width, original_height);
|
||||
|
||||
prop.crop_position = Some((
|
||||
(original_width - origina_crop_width) / 2.0,
|
||||
(original_height - origina_crop_height) / 2.0,
|
||||
original_width / 2.0 - origina_crop_width / 2.0,
|
||||
original_height / 2.0 - origina_crop_height / 2.0,
|
||||
));
|
||||
|
||||
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) {
|
||||
let mut prop = rw_write!(self.properties);
|
||||
let mut prop = self.properties.write().unwrap();
|
||||
let (original_width, original_height) = prop.original_dimension;
|
||||
prop.crop_position = Some((original_x, original_y));
|
||||
|
||||
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 (cx, cy) = (
|
||||
(original_x * s_width) / original_width,
|
||||
(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
|
||||
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();
|
||||
|
||||
draw_layer_and_text(
|
||||
|
|
@ -212,58 +175,51 @@ impl ImageContainer {
|
|||
&prop.tag2,
|
||||
prop.tag_position,
|
||||
prop.tag2_position,
|
||||
prop.original_dimension.0,
|
||||
prop.original_dimension.1,
|
||||
);
|
||||
|
||||
self.buffer = tmp;
|
||||
}
|
||||
|
||||
/// Save image and properities
|
||||
/// Save image anf properities
|
||||
pub(crate) fn save(&self) {
|
||||
let prop = rw_read!(self.properties);
|
||||
let image_info = &prop.image_info;
|
||||
let (export_path, path_properties, mut original_image) = match image_info {
|
||||
Some(p) => (
|
||||
get_export_image_path(p, &prop.name_prefix),
|
||||
get_properties_path(p),
|
||||
load_image(p),
|
||||
),
|
||||
let prop = self.properties.read().unwrap();
|
||||
|
||||
let path_original = match &prop.path {
|
||||
Some(p) => Path::new(p),
|
||||
None => return,
|
||||
};
|
||||
let config = rw_read!(globals::CONFIG);
|
||||
let export_format = &config.image_format;
|
||||
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(),
|
||||
);
|
||||
|
||||
let mut prop = prop.clone();
|
||||
prop.image_info = None;
|
||||
fs::write(
|
||||
prop.path = None;
|
||||
if let Err(e) = fs::write(
|
||||
&path_properties,
|
||||
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_width, crop_height) = croped_ratio(width, height);
|
||||
let mut img = original_image.crop(
|
||||
let mut img = img.crop(
|
||||
crop_x as u32,
|
||||
crop_y as u32,
|
||||
crop_width 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(
|
||||
&mut img,
|
||||
&prop.translucent_layer_color,
|
||||
|
|
@ -277,113 +233,89 @@ impl ImageContainer {
|
|||
&prop.tag2,
|
||||
prop.tag_position,
|
||||
prop.tag2_position,
|
||||
prop.original_dimension.0,
|
||||
prop.original_dimension.1,
|
||||
);
|
||||
|
||||
let mut output = match File::create(&export_path) {
|
||||
Ok(a) => a,
|
||||
Err(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!"),
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
if let Err(e) = img.save_with_format(&export, image::ImageFormat::Png) {
|
||||
dialog::alert_default("Failed to export png!");
|
||||
warn!("Failed to export png!\n{:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn clone_img(&self) -> Option<ImageInfo> {
|
||||
let prop = rw_read!(self.properties);
|
||||
pub(crate) fn clone_img(&self) -> Option<PathBuf> {
|
||||
let prop = self.properties.read().unwrap();
|
||||
|
||||
match &prop.image_info {
|
||||
Some(image_info) => {
|
||||
let stem = image_info.path.file_stem().unwrap().to_string_lossy();
|
||||
let extension = image_info.path.extension().unwrap().to_string_lossy();
|
||||
match &prop.path {
|
||||
Some(path) => {
|
||||
let name = path.file_stem().unwrap().to_string_lossy();
|
||||
let ext = path.extension().unwrap().to_string_lossy();
|
||||
let mut i = 1;
|
||||
let mut new_image_info = image_info.clone();
|
||||
while new_image_info.path.exists() {
|
||||
let new_file = format!("{}{}.{}", stem, "-copy".repeat(i), extension);
|
||||
new_image_info.path = image_info.path.with_file_name(&new_file);
|
||||
let mut new_path = path.clone();
|
||||
while new_path.exists() {
|
||||
let new_file = format!("{}{}.{}", name, "-copy".repeat(i), ext);
|
||||
new_path = path.with_file_name(&new_file);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let path_properties = get_properties_path(&image_info);
|
||||
let path_properties_new = get_properties_path(&new_image_info);
|
||||
let path_properties = path.with_extension("prop");
|
||||
let path_properties_new = new_path.with_extension("prop");
|
||||
|
||||
if image_info.path.exists() {
|
||||
fs::copy(&image_info.path, &new_image_info.path)
|
||||
.warn_log("Failed to clone image!");
|
||||
if path.exists() {
|
||||
if let Err(e) = fs::copy(path, &new_path) {
|
||||
dialog::alert_default("Failed to clone image!");
|
||||
warn!("Failed to clone image!\n{:?}", e);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if path_properties.exists() {
|
||||
fs::copy(path_properties, &path_properties_new)
|
||||
.warn_log("Failed to clone image properties!");
|
||||
if let Err(e) = fs::copy(path_properties, &path_properties_new) {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn delete(&self) {
|
||||
let prop = rw_read!(self.properties);
|
||||
let image_info = &prop.image_info;
|
||||
let (export_path, path_image, path_properties) = match image_info {
|
||||
Some(p) => (
|
||||
get_export_image_path(p, &prop.name_prefix),
|
||||
Path::new(&p.path),
|
||||
get_properties_path(p),
|
||||
),
|
||||
let prop = self.properties.read().unwrap();
|
||||
|
||||
let path_original = match &prop.path {
|
||||
Some(p) => Path::new(p),
|
||||
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() {
|
||||
fs::remove_file(path_image).warn_log("Failed to delete image!");
|
||||
if path_original.exists() {
|
||||
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() {
|
||||
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() {
|
||||
fs::remove_file(export_path).warn_log("Failed to delete exported image!");
|
||||
if export.exists() {
|
||||
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)]
|
||||
pub(crate) struct ImagePropertiesFile {
|
||||
pub(crate) crop_position: Option<(f64, f64)>,
|
||||
pub(crate) name_prefix: Option<String>,
|
||||
pub(crate) quote: Option<String>,
|
||||
pub(crate) subquote: Option<String>,
|
||||
pub(crate) subquote2: Option<String>,
|
||||
|
|
@ -410,7 +341,6 @@ impl Default for ImagePropertiesFile {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
crop_position: None,
|
||||
name_prefix: None,
|
||||
quote: None,
|
||||
subquote: None,
|
||||
subquote2: None,
|
||||
|
|
@ -430,7 +360,6 @@ impl From<&ImageProperties> for ImagePropertiesFile {
|
|||
fn from(props: &ImageProperties) -> Self {
|
||||
Self {
|
||||
crop_position: props.crop_position,
|
||||
name_prefix: Some(props.name_prefix.clone()),
|
||||
quote: Some(props.quote.clone()),
|
||||
subquote: Some(props.subquote.clone()),
|
||||
subquote2: Some(props.subquote2.clone()),
|
||||
|
|
@ -449,11 +378,10 @@ impl From<&ImageProperties> for ImagePropertiesFile {
|
|||
/// Properties of loaded image
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub(crate) struct ImageProperties {
|
||||
pub(crate) image_info: Option<ImageInfo>,
|
||||
pub(crate) path: Option<PathBuf>,
|
||||
pub(crate) dimension: (f64, f64),
|
||||
pub(crate) original_dimension: (f64, f64),
|
||||
pub(crate) crop_position: Option<(f64, f64)>,
|
||||
pub(crate) name_prefix: String,
|
||||
pub(crate) quote: String,
|
||||
pub(crate) subquote: String,
|
||||
pub(crate) subquote2: String,
|
||||
|
|
@ -471,11 +399,10 @@ pub(crate) struct ImageProperties {
|
|||
impl Default for ImageProperties {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
image_info: None,
|
||||
path: None,
|
||||
dimension: (0.0, 0.0),
|
||||
original_dimension: (0.0, 0.0),
|
||||
crop_position: None,
|
||||
name_prefix: "".to_owned(),
|
||||
quote: "".to_owned(),
|
||||
subquote: "".to_owned(),
|
||||
subquote2: "".to_owned(),
|
||||
|
|
@ -499,8 +426,10 @@ impl ImageProperties {
|
|||
tag_default: &str,
|
||||
tag2_default: &str,
|
||||
) {
|
||||
self.crop_position = props.crop_position;
|
||||
self.name_prefix = props.name_prefix.unwrap_or("".to_owned());
|
||||
if let Some(v) = props.crop_position {
|
||||
self.crop_position = Some(v);
|
||||
}
|
||||
|
||||
self.quote = props.quote.unwrap_or("".to_owned());
|
||||
self.subquote = props.subquote.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.translucent_layer_color = props
|
||||
.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
|
||||
fn draw_layer_and_text(
|
||||
tmp: &mut DynamicImage,
|
||||
|
|
@ -572,9 +458,8 @@ fn draw_layer_and_text(
|
|||
subquote2_position: f64,
|
||||
tag: &str,
|
||||
tag2: &str,
|
||||
tag_y_position: f64,
|
||||
tag_position: f64,
|
||||
tag2_position: f64,
|
||||
original_width: f64,
|
||||
original_height: f64,
|
||||
) {
|
||||
let (width, height): (f64, f64) = Coord::from(tmp.dimensions()).into();
|
||||
|
|
@ -590,21 +475,16 @@ fn draw_layer_and_text(
|
|||
&globals::FONT_QUOTE,
|
||||
size,
|
||||
quote_position,
|
||||
original_width,
|
||||
original_height,
|
||||
true,
|
||||
quote,
|
||||
);
|
||||
|
||||
let size = subquote_from_height(height);
|
||||
draw_multiline_mid_string(
|
||||
tmp,
|
||||
&globals::FONT_SUBQUOTE,
|
||||
size,
|
||||
subquote_position,
|
||||
original_width,
|
||||
original_height,
|
||||
true,
|
||||
subquote,
|
||||
);
|
||||
let size = subquote2_from_height(height);
|
||||
|
|
@ -613,9 +493,7 @@ fn draw_layer_and_text(
|
|||
&globals::FONT_SUBQUOTE2,
|
||||
size,
|
||||
subquote2_position,
|
||||
original_width,
|
||||
original_height,
|
||||
true,
|
||||
subquote2,
|
||||
);
|
||||
|
||||
|
|
@ -625,9 +503,7 @@ fn draw_layer_and_text(
|
|||
&globals::FONT_TAG2,
|
||||
size,
|
||||
tag2_position,
|
||||
original_width,
|
||||
original_height,
|
||||
false,
|
||||
tag2,
|
||||
);
|
||||
|
||||
|
|
@ -642,9 +518,8 @@ fn draw_layer_and_text(
|
|||
imageproc::drawing::draw_text_mut(
|
||||
tmp,
|
||||
image::Rgba([255, 255, 255, 255]),
|
||||
(width * rw_read!(globals::CONFIG).tag_x_position_ratio - text_width) as i32,
|
||||
((tag_y_position * height) / original_height + index as f64 * (text_height * 1.2))
|
||||
as i32,
|
||||
(width * 0.99 - text_width) as u32,
|
||||
((tag_position * height) / original_height + index as f64 * (text_height * 1.2)) as u32,
|
||||
rusttype::Scale::uniform(size as f32),
|
||||
&globals::FONT_TAG,
|
||||
line,
|
||||
|
|
@ -658,144 +533,26 @@ pub(crate) fn draw_multiline_mid_string(
|
|||
font: &rusttype::Font,
|
||||
size: f64,
|
||||
position: f64,
|
||||
original_width: f64,
|
||||
original_height: f64,
|
||||
boxed: bool,
|
||||
text: &str,
|
||||
) {
|
||||
let (mut box_width, mut box_height) = (0.0, 0.0);
|
||||
|
||||
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() {
|
||||
let (text_width, text_height) =
|
||||
measure_line(font, line, rusttype::Scale::uniform(size as f32));
|
||||
|
||||
if text_width > box_width {
|
||||
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(
|
||||
imageproc::drawing::draw_text_mut(
|
||||
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,
|
||||
size,
|
||||
position,
|
||||
original_width,
|
||||
original_height,
|
||||
false,
|
||||
text,
|
||||
line,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
pub(crate) fn measure_line(
|
||||
font: &rusttype::Font,
|
||||
|
|
@ -814,95 +571,6 @@ pub(crate) fn measure_line(
|
|||
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
|
||||
pub(crate) fn set_color_btn_rgba(rgba: [u8; 4], btn: &mut Button) {
|
||||
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));
|
||||
}
|
||||
|
||||
/// 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
|
||||
pub(crate) fn croped_ratio(width: f64, height: f64) -> (f64, f64) {
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
/// Get required height to crop image from width as per image ratio
|
||||
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
|
||||
}
|
||||
|
||||
/// Get required quote size for crop image from height as per image ratio
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
pub(crate) fn tag2_from_height(height: f64) -> f64 {
|
||||
(height * rw_read!(globals::CONFIG).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()));
|
||||
}
|
||||
(height * globals::CONFIG.read().unwrap().tag2_font_ratio) / 5000.0
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue