Compare commits
48 Commits
Stable(0.2
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
b66770620d | |
|
|
54466c1216 | |
|
|
e0b686d66e | |
|
|
80824ed3d8 | |
|
|
e1ed10805c | |
|
|
8447939489 | |
|
|
aca3a0ec2d | |
|
|
9b6412b823 | |
|
|
286af71348 | |
|
|
ccfa458f35 | |
|
|
f0a68406f4 | |
|
|
658eba6f96 | |
|
|
c6763fca4b | |
|
|
90d72f9d4e | |
|
|
55d394380d | |
|
|
4ac7e1938c | |
|
|
ae1e61141f | |
|
|
c1633c7197 | |
|
|
f351dea81a | |
|
|
558f558dce | |
|
|
43d3a63c7f | |
|
|
9c3d4a4c0f | |
|
|
e241ac7ff5 | |
|
|
8a194a254f | |
|
|
44a3956a31 | |
|
|
231ab35f7b | |
|
|
627912f89e | |
|
|
6f39a8209a | |
|
|
efcf7eae1d | |
|
|
4b0466eb7a | |
|
|
c2dd76aef1 | |
|
|
dc5f7206dc | |
|
|
969c867cd4 | |
|
|
ca60e0e316 | |
|
|
2db68a3dc1 | |
|
|
fadf6e68e1 | |
|
|
59aec4ab3b | |
|
|
c0b31a9b31 | |
|
|
a21f638432 | |
|
|
3d9b20bbc8 | |
|
|
7f67d5ad29 | |
|
|
ad1aaae2a0 | |
|
|
57f79fe556 | |
|
|
b139ce4c98 | |
|
|
4df87ca333 | |
|
|
7aacea2310 | |
|
|
50924e9354 | |
|
|
cb3c12f708 |
File diff suppressed because it is too large
Load Diff
18
Cargo.toml
18
Cargo.toml
|
|
@ -1,8 +1,8 @@
|
||||||
[package]
|
[package]
|
||||||
name = "post_maker"
|
name = "post_maker"
|
||||||
version = "0.2.2"
|
version = "0.6.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Post Maker helps you to make post for Instagram and other Social Media apps easily."
|
description = "Post Maker helps you to make post for instagram and other social media apps easily and in massive amount. "
|
||||||
authors = ["PiyushXCoder <https://piyushxcoder.in>"]
|
authors = ["PiyushXCoder <https://piyushxcoder.in>"]
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
@ -10,18 +10,28 @@ repository = "https://github.com/PiyushXCoder/post_maker"
|
||||||
keywords = ["posts", "instagram", "facebook", "twitter"]
|
keywords = ["posts", "instagram", "facebook", "twitter"]
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 1
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 3
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "3.0", features = ["derive"] }
|
clap = { version = "3.0", features = ["derive"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
simplelog = "0.11"
|
simplelog = "0.11"
|
||||||
fltk = "1.2"
|
fltk = "1.2"
|
||||||
fltk-theme = "0.4"
|
fltk-theme = "0.4"
|
||||||
image = "0.23"
|
image = "0.24.1"
|
||||||
imageproc = "0.22"
|
imageproc = "0.23"
|
||||||
|
webp = "0.2"
|
||||||
rusttype = "0.9"
|
rusttype = "0.9"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
dirs = "4.0"
|
dirs = "4.0"
|
||||||
|
infer = "0.7.0"
|
||||||
textwrap = "0.14"
|
textwrap = "0.14"
|
||||||
webbrowser = "0.5"
|
webbrowser = "0.5"
|
||||||
|
mozjpeg = "0.9.2"
|
||||||
|
bichannel = "0.0.4"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
[target.x86_64-pc-windows-gnu]
|
||||||
|
image = "rustembedded/cross:x86_64-pc-windows-gnu"
|
||||||
10
README.md
10
README.md
|
|
@ -4,7 +4,10 @@
|
||||||
<img alt="actix-web-grants" src="./assets/icon_with_text.svg" width="170">
|
<img alt="actix-web-grants" src="./assets/icon_with_text.svg" width="170">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
> Post Maker helps you to make post for instagram and other social media apps easily.
|
[](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.
|
||||||
|
|
||||||
## Installing
|
## Installing
|
||||||
|
|
||||||
|
|
@ -41,7 +44,10 @@ You can follow rust's official guide to install rust compiler and cargo [here](h
|
||||||
|
|
||||||
## General Overview of Controls
|
## 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
|
## License
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 887 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 386 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -13,11 +13,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//! About Window
|
//! About Window
|
||||||
use crate::{config, globals};
|
use crate::{config, globals, result_ext::ResultExt};
|
||||||
use fltk::{
|
use fltk::{
|
||||||
app,
|
app,
|
||||||
button::Button,
|
button::Button,
|
||||||
dialog,
|
|
||||||
enums::{self, Align, Event},
|
enums::{self, Align, Event},
|
||||||
frame::Frame,
|
frame::Frame,
|
||||||
group::Flex,
|
group::Flex,
|
||||||
|
|
@ -152,10 +151,7 @@ impl About {
|
||||||
// Repository Link
|
// Repository Link
|
||||||
self.repo_link.handle(|_, ev| {
|
self.repo_link.handle(|_, ev| {
|
||||||
if ev == Event::Push {
|
if ev == Event::Push {
|
||||||
if let Err(e) = webbrowser::open(env!("CARGO_PKG_REPOSITORY")) {
|
webbrowser::open(env!("CARGO_PKG_REPOSITORY")).warn_log("Failed to open the link!");
|
||||||
dialog::alert_default("Failed to open the link!");
|
|
||||||
warn!("Failed to open the link!\n{:?}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
@ -163,10 +159,7 @@ impl About {
|
||||||
// Developer's Link
|
// Developer's Link
|
||||||
self.dev_link.handle(|_, ev| {
|
self.dev_link.handle(|_, ev| {
|
||||||
if ev == Event::Push {
|
if ev == Event::Push {
|
||||||
if let Err(e) = webbrowser::open("https://piyushxcoder.in") {
|
webbrowser::open("https://piyushxcoder.in").warn_log("Failed to open the link!");
|
||||||
dialog::alert_default("Failed to open the link!");
|
|
||||||
warn!("Failed to open the link!\n{:?}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
@ -174,10 +167,8 @@ impl About {
|
||||||
// License Link
|
// License Link
|
||||||
self.license_link.handle(|_, ev| {
|
self.license_link.handle(|_, ev| {
|
||||||
if ev == Event::Push {
|
if ev == Event::Push {
|
||||||
if let Err(e) = webbrowser::open("https://www.gnu.org/licenses/gpl-3.0.html") {
|
webbrowser::open("https://www.gnu.org/licenses/gpl-3.0.html")
|
||||||
dialog::alert_default("Failed to open the link!");
|
.warn_log("Failed to open the link!");
|
||||||
warn!("Failed to open the link!\n{:?}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
@ -13,18 +13,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//! load, save configuration and parse cli args
|
//! load, save configuration and parse cli args
|
||||||
use crate::{config_picker::ConfigPicker, globals};
|
use crate::{
|
||||||
|
config_picker::ConfigPicker, dialog, globals, result_ext::ResultExt, utils::ImageType,
|
||||||
|
};
|
||||||
use clap::{ArgEnum, Parser};
|
use clap::{ArgEnum, Parser};
|
||||||
use fltk::dialog;
|
|
||||||
use fltk_theme::ThemeType;
|
use fltk_theme::ThemeType;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{collections::HashMap, fs::File, path::PathBuf};
|
||||||
collections::HashMap,
|
|
||||||
fs::File,
|
|
||||||
path::PathBuf,
|
|
||||||
time::{Duration, SystemTime},
|
|
||||||
};
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Directory where all Configurations are present
|
/// Directory where all Configurations are present
|
||||||
|
|
@ -129,10 +125,16 @@ pub(crate) struct ConfigFile {
|
||||||
pub(crate) quote_position_ratio: f64,
|
pub(crate) quote_position_ratio: f64,
|
||||||
pub(crate) subquote_position_ratio: f64,
|
pub(crate) subquote_position_ratio: f64,
|
||||||
pub(crate) subquote2_position_ratio: f64,
|
pub(crate) subquote2_position_ratio: f64,
|
||||||
pub(crate) tag_position_ratio: f64,
|
pub(crate) tag_x_position_ratio: f64,
|
||||||
|
pub(crate) tag_y_position_ratio: f64,
|
||||||
pub(crate) tag2_position_ratio: f64,
|
pub(crate) tag2_position_ratio: f64,
|
||||||
pub(crate) image_ratio: (f64, f64),
|
pub(crate) image_ratio: (f64, f64),
|
||||||
pub(crate) color_layer: [u8; 4],
|
pub(crate) color_layer: [u8; 4],
|
||||||
|
pub(crate) minimum_width_limit: f64, // for export of image
|
||||||
|
pub(crate) maximum_width_limit: f64, // for export of image
|
||||||
|
pub(crate) draw_box_around_quote: bool,
|
||||||
|
pub(crate) line_spacing: bool,
|
||||||
|
pub(crate) image_format: ImageType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ConfigFile {
|
impl Default for ConfigFile {
|
||||||
|
|
@ -143,7 +145,7 @@ impl Default for ConfigFile {
|
||||||
subquote2_font: String::new(),
|
subquote2_font: String::new(),
|
||||||
tag_font: String::new(),
|
tag_font: String::new(),
|
||||||
tag2_font: String::new(),
|
tag2_font: String::new(),
|
||||||
quote_font_ratio: 230.0,
|
quote_font_ratio: 250.0,
|
||||||
subquote_font_ratio: 230.0,
|
subquote_font_ratio: 230.0,
|
||||||
subquote2_font_ratio: 230.0,
|
subquote2_font_ratio: 230.0,
|
||||||
tag_font_ratio: 150.0,
|
tag_font_ratio: 150.0,
|
||||||
|
|
@ -151,10 +153,16 @@ impl Default for ConfigFile {
|
||||||
quote_position_ratio: 0.7,
|
quote_position_ratio: 0.7,
|
||||||
subquote_position_ratio: 0.8,
|
subquote_position_ratio: 0.8,
|
||||||
subquote2_position_ratio: 0.9,
|
subquote2_position_ratio: 0.9,
|
||||||
tag_position_ratio: 0.5,
|
tag_x_position_ratio: 0.95,
|
||||||
|
tag_y_position_ratio: 0.5,
|
||||||
tag2_position_ratio: 0.95,
|
tag2_position_ratio: 0.95,
|
||||||
image_ratio: (4.0, 5.0),
|
image_ratio: (4.0, 5.0),
|
||||||
color_layer: [20, 22, 25, 197],
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -169,7 +177,7 @@ impl ConfigFile {
|
||||||
None => HashMap::new(),
|
None => HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let default_config = (&*globals::CONFIG_NAME.read().unwrap()).to_string();
|
let default_config = (&*rw_read!(globals::CONFIG_NAME)).to_string();
|
||||||
let config_name = if (map.len() > 1 || !map.contains_key(&default_config))
|
let config_name = if (map.len() > 1 || !map.contains_key(&default_config))
|
||||||
&& map.len() != 0
|
&& map.len() != 0
|
||||||
{
|
{
|
||||||
|
|
@ -184,7 +192,7 @@ impl ConfigFile {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(config) = map.get(&config_name) {
|
if let Some(config) = map.get(&config_name) {
|
||||||
*globals::CONFIG_NAME.write().unwrap() = config_name;
|
*rw_write!(globals::CONFIG_NAME) = config_name;
|
||||||
return config.to_owned();
|
return config.to_owned();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +200,7 @@ impl ConfigFile {
|
||||||
let config = Self::default();
|
let config = Self::default();
|
||||||
let mut configs = HashMap::new();
|
let mut configs = HashMap::new();
|
||||||
configs.insert(
|
configs.insert(
|
||||||
(&*globals::CONFIG_NAME.read().unwrap()).to_owned(),
|
(&*rw_read!(globals::CONFIG_NAME)).to_owned(),
|
||||||
config.clone(),
|
config.clone(),
|
||||||
);
|
);
|
||||||
save_configs(configs);
|
save_configs(configs);
|
||||||
|
|
@ -210,46 +218,16 @@ pub(crate) fn get_configs() -> Option<HashMap<String, ConfigFile>> {
|
||||||
|
|
||||||
/// Save configs
|
/// Save configs
|
||||||
pub(crate) fn save_configs(configs: HashMap<String, ConfigFile>) {
|
pub(crate) fn save_configs(configs: HashMap<String, ConfigFile>) {
|
||||||
if let Err(e) = std::fs::write(&*CONFIG_FILE, serde_json::to_string(&configs).unwrap()) {
|
std::fs::write(&*CONFIG_FILE, serde_json::to_string(&configs).unwrap())
|
||||||
dialog::alert_default("Can't write config!");
|
.expect_log("Can't write config!");
|
||||||
error!("Can't write config!\n{:?}", e);
|
|
||||||
panic!("Can't write config!\n{:?}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn log_file() -> File {
|
pub(crate) fn log_file() -> File {
|
||||||
match File::open(&*LOG_FILE) {
|
|
||||||
Ok(mut file) => {
|
|
||||||
if is_file_30_days_old(&file) {
|
|
||||||
match File::create(&*LOG_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,
|
Ok(f) => f,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
dialog::alert_default("Can't open log file!");
|
dialog::alert_default("Can't open log file!");
|
||||||
panic!("{:?}", e);
|
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 top_padding_btn = Frame::default();
|
||||||
let mut panel_flex = Flex::default().row();
|
let mut panel_flex = Flex::default().row();
|
||||||
Frame::default();
|
Frame::default();
|
||||||
let apply_btn = Button::default().with_label("apply");
|
let apply_btn = Button::default().with_label("Apply");
|
||||||
Frame::default();
|
Frame::default();
|
||||||
panel_flex.set_size(&apply_btn, 100);
|
panel_flex.set_size(&apply_btn, 100);
|
||||||
panel_flex.end();
|
panel_flex.end();
|
||||||
|
|
|
||||||
|
|
@ -14,27 +14,27 @@
|
||||||
|
|
||||||
//! Window to edit configuration
|
//! Window to edit configuration
|
||||||
|
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
use crate::{
|
||||||
|
config::{self, ConfigFile},
|
||||||
|
dialog, globals,
|
||||||
|
result_ext::ResultExt,
|
||||||
|
utils::{self, ImageType},
|
||||||
|
};
|
||||||
use fltk::{
|
use fltk::{
|
||||||
app,
|
app,
|
||||||
browser::{Browser, BrowserType},
|
browser::{Browser, BrowserType},
|
||||||
button::Button,
|
button::{Button, CheckButton, RadioRoundButton},
|
||||||
dialog::{self, FileDialogOptions, NativeFileChooser},
|
dialog::{FileDialogOptions, NativeFileChooser},
|
||||||
enums::{self, Align, Event, Font},
|
enums::{self, Align, Event, Font},
|
||||||
frame::Frame,
|
frame::Frame,
|
||||||
group::Flex,
|
group::{Flex, Scroll},
|
||||||
image::SvgImage,
|
image::SvgImage,
|
||||||
output::Output,
|
output::Output,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
valuator::ValueInput,
|
valuator::ValueInput,
|
||||||
window::Window,
|
window::Window,
|
||||||
};
|
};
|
||||||
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
use crate::{
|
|
||||||
config::{self, ConfigFile},
|
|
||||||
globals, utils,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) struct ConfigWindow {
|
pub(crate) struct ConfigWindow {
|
||||||
pub(crate) win: Window,
|
pub(crate) win: Window,
|
||||||
|
|
@ -60,14 +60,21 @@ pub(crate) struct ConfigWindow {
|
||||||
pub(crate) quote_position_ratio: ValueInput,
|
pub(crate) quote_position_ratio: ValueInput,
|
||||||
pub(crate) subquote_position_ratio: ValueInput,
|
pub(crate) subquote_position_ratio: ValueInput,
|
||||||
pub(crate) subquote2_position_ratio: ValueInput,
|
pub(crate) subquote2_position_ratio: ValueInput,
|
||||||
pub(crate) tag_position_ratio: ValueInput,
|
pub(crate) tag_x_position_ratio: ValueInput,
|
||||||
|
pub(crate) tag_y_position_ratio: ValueInput,
|
||||||
pub(crate) tag2_position_ratio: ValueInput,
|
pub(crate) tag2_position_ratio: ValueInput,
|
||||||
pub(crate) image_ratio_width: ValueInput,
|
pub(crate) image_ratio_width: ValueInput,
|
||||||
pub(crate) image_ratio_height: ValueInput,
|
pub(crate) image_ratio_height: ValueInput,
|
||||||
|
pub(crate) draw_box_around_quote: CheckButton,
|
||||||
|
pub(crate) line_spacing: CheckButton,
|
||||||
|
pub(crate) minimum_width_limit: ValueInput,
|
||||||
|
pub(crate) maximum_width_limit: ValueInput,
|
||||||
/// RGB value of top translucent layer
|
/// RGB value of top translucent layer
|
||||||
pub(crate) translucent_layer_rgb: Button,
|
pub(crate) translucent_layer_rgb: Button,
|
||||||
/// opacity value of top translucent layer
|
/// opacity value of top translucent layer
|
||||||
pub(crate) translucent_layer_alpha: ValueInput,
|
pub(crate) translucent_layer_alpha: ValueInput,
|
||||||
|
pub(crate) png_format: RadioRoundButton,
|
||||||
|
pub(crate) jpeg_format: RadioRoundButton,
|
||||||
pub(crate) defaults_btn: Button,
|
pub(crate) defaults_btn: Button,
|
||||||
pub(crate) save_btn: Button,
|
pub(crate) save_btn: Button,
|
||||||
pub(crate) cancel_btn: Button,
|
pub(crate) cancel_btn: Button,
|
||||||
|
|
@ -78,12 +85,16 @@ pub(crate) struct ConfigWindow {
|
||||||
impl ConfigWindow {
|
impl ConfigWindow {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
let configs = config::get_configs().unwrap_or(HashMap::new());
|
let configs = config::get_configs().unwrap_or(HashMap::new());
|
||||||
let mut win = Window::new(0, 0, 900, 680, "Config").center_screen();
|
let mut win = Window::new(0, 0, 900, 600, "Config").center_screen();
|
||||||
win.set_icon(Some(
|
win.set_icon(Some(
|
||||||
SvgImage::from_data(globals::ICON.to_str().unwrap()).unwrap(),
|
SvgImage::from_data(globals::ICON.to_str().unwrap()).unwrap(),
|
||||||
));
|
));
|
||||||
let mut row = Flex::default().with_size(890, 670).with_pos(5, 5).row();
|
|
||||||
let mut config_picker_flex = Flex::default().column();
|
// Config picking area
|
||||||
|
let mut config_picker_flex = Flex::default()
|
||||||
|
.with_size(200, win.height() - 50)
|
||||||
|
.with_pos(5, 5)
|
||||||
|
.column();
|
||||||
// Picker
|
// Picker
|
||||||
let browse = Browser::default().with_type(BrowserType::Hold);
|
let browse = Browser::default().with_type(BrowserType::Hold);
|
||||||
|
|
||||||
|
|
@ -103,9 +114,30 @@ impl ConfigWindow {
|
||||||
config_picker_flex.set_size(&panel_flex, 30);
|
config_picker_flex.set_size(&panel_flex, 30);
|
||||||
config_picker_flex.set_size(&bottom_padding_btn, 5);
|
config_picker_flex.set_size(&bottom_padding_btn, 5);
|
||||||
config_picker_flex.end();
|
config_picker_flex.end();
|
||||||
row.set_size(&config_picker_flex, 200);
|
|
||||||
|
|
||||||
let mut col = Flex::default().column();
|
// 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 label = Frame::default().with_label("Fonts:");
|
let mut label = Frame::default().with_label("Fonts:");
|
||||||
label.set_label_font(enums::Font::HelveticaBold);
|
label.set_label_font(enums::Font::HelveticaBold);
|
||||||
|
|
@ -188,7 +220,7 @@ impl ConfigWindow {
|
||||||
let mut label = Frame::default().with_label("Ratio of size of text:");
|
let mut label = Frame::default().with_label("Ratio of size of text:");
|
||||||
label.set_label_font(enums::Font::HelveticaBold);
|
label.set_label_font(enums::Font::HelveticaBold);
|
||||||
col.set_size(&label, 15);
|
col.set_size(&label, 15);
|
||||||
let mut hint = Frame::default().with_label("Font size in image of resolution 4000x5000");
|
let mut hint = Frame::default().with_label("Font size in image of height 4000 pixels");
|
||||||
hint.set_label_font(Font::CourierItalic);
|
hint.set_label_font(Font::CourierItalic);
|
||||||
hint.set_label_size(12);
|
hint.set_label_size(12);
|
||||||
col.set_size(&hint, 20);
|
col.set_size(&hint, 20);
|
||||||
|
|
@ -306,16 +338,27 @@ impl ConfigWindow {
|
||||||
|
|
||||||
// column 2
|
// column 2
|
||||||
let mut col_grp = Flex::default().column();
|
let mut col_grp = Flex::default().column();
|
||||||
let mut tag_position_ratio_grp = Flex::default().row();
|
let mut tag_x_position_ratio_grp = Flex::default().row();
|
||||||
tag_position_ratio_grp.set_size(
|
tag_x_position_ratio_grp.set_size(
|
||||||
&Frame::default()
|
&Frame::default()
|
||||||
.with_label("Tag")
|
.with_label("Tag (x)")
|
||||||
.with_align(Align::Right | Align::Inside),
|
.with_align(Align::Right | Align::Inside),
|
||||||
130,
|
130,
|
||||||
);
|
);
|
||||||
let tag_position_ratio = ValueInput::default();
|
let tag_x_position_ratio = ValueInput::default();
|
||||||
tag_position_ratio_grp.end();
|
tag_x_position_ratio_grp.end();
|
||||||
col_grp.set_size(&tag_position_ratio_grp, 30);
|
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 mut tag2_position_ratio_grp = Flex::default().row();
|
let mut tag2_position_ratio_grp = Flex::default().row();
|
||||||
tag2_position_ratio_grp.set_size(
|
tag2_position_ratio_grp.set_size(
|
||||||
|
|
@ -348,6 +391,60 @@ impl ConfigWindow {
|
||||||
image_ratio_grp.end();
|
image_ratio_grp.end();
|
||||||
col.set_size(&image_ratio_grp, 30);
|
col.set_size(&image_ratio_grp, 30);
|
||||||
|
|
||||||
|
// Draw box around Quote
|
||||||
|
let mut label = Frame::default().with_label("Quote Special:");
|
||||||
|
label.set_label_font(enums::Font::HelveticaBold);
|
||||||
|
col.set_size(&label, 15);
|
||||||
|
|
||||||
|
let mut quote_special_flex = Flex::default().row();
|
||||||
|
quote_special_flex.set_size(&Frame::default(), 20);
|
||||||
|
let mut draw_box_around_quote = CheckButton::default().with_label("Draw box around text");
|
||||||
|
draw_box_around_quote.set_value(true);
|
||||||
|
let mut line_spacing = CheckButton::default().with_label("Line spacing in quotes");
|
||||||
|
line_spacing.set_value(true);
|
||||||
|
quote_special_flex.end();
|
||||||
|
col.set_size("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:");
|
let mut label = Frame::default().with_label("Colour for dark layer:");
|
||||||
label.set_label_font(enums::Font::HelveticaBold);
|
label.set_label_font(enums::Font::HelveticaBold);
|
||||||
col.set_size(&label, 15);
|
col.set_size(&label, 15);
|
||||||
|
|
@ -358,7 +455,7 @@ impl ConfigWindow {
|
||||||
col.set_size(&hint, 20);
|
col.set_size(&hint, 20);
|
||||||
|
|
||||||
let mut translucent_layer_flex = Flex::default().row();
|
let mut translucent_layer_flex = Flex::default().row();
|
||||||
translucent_layer_flex.set_pad(2);
|
translucent_layer_flex.set_size(&Frame::default(), 20);
|
||||||
translucent_layer_flex.set_size(&Frame::default().with_label("Colour"), 50);
|
translucent_layer_flex.set_size(&Frame::default().with_label("Colour"), 50);
|
||||||
let mut translucent_layer_rgb = Button::default();
|
let mut translucent_layer_rgb = Button::default();
|
||||||
translucent_layer_rgb.set_frame(enums::FrameType::BorderBox);
|
translucent_layer_rgb.set_frame(enums::FrameType::BorderBox);
|
||||||
|
|
@ -368,25 +465,32 @@ impl ConfigWindow {
|
||||||
translucent_layer_flex.end();
|
translucent_layer_flex.end();
|
||||||
col.set_size(&translucent_layer_flex, 30);
|
col.set_size(&translucent_layer_flex, 30);
|
||||||
|
|
||||||
|
let mut label = Frame::default().with_label("Export Format:");
|
||||||
|
label.set_label_font(enums::Font::HelveticaBold);
|
||||||
|
col.set_size(&label, 15);
|
||||||
|
|
||||||
|
let mut hint = Frame::default().with_label("Image format to export image");
|
||||||
|
hint.set_label_font(Font::CourierItalic);
|
||||||
|
hint.set_label_size(12);
|
||||||
|
col.set_size(&hint, 20);
|
||||||
|
|
||||||
|
let mut image_format_flex = Flex::default().row();
|
||||||
|
image_format_flex.set_size(&Frame::default(), 20);
|
||||||
|
let mut png_format = RadioRoundButton::default().with_label("Png");
|
||||||
|
png_format.set_value(true);
|
||||||
|
let jpeg_format = RadioRoundButton::default().with_label("Jpeg");
|
||||||
|
image_format_flex.end();
|
||||||
|
col.set_size(&image_format_flex, 30);
|
||||||
|
|
||||||
Frame::default();
|
Frame::default();
|
||||||
|
|
||||||
let mut panel_grp = Flex::default().row();
|
|
||||||
Frame::default();
|
|
||||||
let defaults_btn = Button::default().with_label("Defaults");
|
|
||||||
let save_btn = Button::default().with_label("Save");
|
|
||||||
let cancel_btn = Button::default().with_label("Cancel");
|
|
||||||
panel_grp.set_size(&defaults_btn, 100);
|
|
||||||
panel_grp.set_size(&save_btn, 100);
|
|
||||||
panel_grp.set_size(&cancel_btn, 100);
|
|
||||||
panel_grp.end();
|
|
||||||
|
|
||||||
col.set_size(&panel_grp, 30);
|
|
||||||
|
|
||||||
col.end();
|
col.end();
|
||||||
row.end();
|
|
||||||
|
scroll.end();
|
||||||
|
scroll.make_resizable(true);
|
||||||
|
scroll.scroll_to(-1 * col.x() - 5, -1 * col.y() - 5);
|
||||||
|
|
||||||
win.end();
|
win.end();
|
||||||
win.make_modal(true);
|
win.make_modal(true);
|
||||||
win.make_resizable(true);
|
|
||||||
|
|
||||||
let mut config_window = Self {
|
let mut config_window = Self {
|
||||||
win,
|
win,
|
||||||
|
|
@ -412,12 +516,19 @@ impl ConfigWindow {
|
||||||
quote_position_ratio,
|
quote_position_ratio,
|
||||||
subquote_position_ratio,
|
subquote_position_ratio,
|
||||||
subquote2_position_ratio,
|
subquote2_position_ratio,
|
||||||
tag_position_ratio,
|
tag_x_position_ratio,
|
||||||
|
tag_y_position_ratio,
|
||||||
tag2_position_ratio,
|
tag2_position_ratio,
|
||||||
image_ratio_width,
|
image_ratio_width,
|
||||||
image_ratio_height,
|
image_ratio_height,
|
||||||
|
draw_box_around_quote,
|
||||||
|
line_spacing,
|
||||||
|
minimum_width_limit,
|
||||||
|
maximum_width_limit,
|
||||||
translucent_layer_rgb,
|
translucent_layer_rgb,
|
||||||
translucent_layer_alpha,
|
translucent_layer_alpha,
|
||||||
|
png_format,
|
||||||
|
jpeg_format,
|
||||||
defaults_btn,
|
defaults_btn,
|
||||||
save_btn,
|
save_btn,
|
||||||
cancel_btn,
|
cancel_btn,
|
||||||
|
|
@ -431,7 +542,7 @@ impl ConfigWindow {
|
||||||
|
|
||||||
// Show to edit config
|
// Show to edit config
|
||||||
pub(crate) fn show(&mut self) -> bool {
|
pub(crate) fn show(&mut self) -> bool {
|
||||||
let config_name = &*globals::CONFIG_NAME.read().unwrap();
|
let config_name = &*rw_read!(globals::CONFIG_NAME);
|
||||||
self.browse.clear();
|
self.browse.clear();
|
||||||
for (idx, name) in self.configs.borrow().keys().enumerate() {
|
for (idx, name) in self.configs.borrow().keys().enumerate() {
|
||||||
self.browse.add(name);
|
self.browse.add(name);
|
||||||
|
|
@ -440,7 +551,7 @@ impl ConfigWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*self.selected_browse_line.borrow_mut() = self.browse.value();
|
*self.selected_browse_line.borrow_mut() = self.browse.value();
|
||||||
let config = globals::CONFIG.read().unwrap();
|
let config = rw_read!(globals::CONFIG);
|
||||||
self.quote_font.set_value(config.quote_font.as_str());
|
self.quote_font.set_value(config.quote_font.as_str());
|
||||||
self.subquote_font.set_value(config.subquote_font.as_str());
|
self.subquote_font.set_value(config.subquote_font.as_str());
|
||||||
self.subquote2_font
|
self.subquote2_font
|
||||||
|
|
@ -460,14 +571,37 @@ impl ConfigWindow {
|
||||||
.set_value(config.subquote_position_ratio);
|
.set_value(config.subquote_position_ratio);
|
||||||
self.subquote2_position_ratio
|
self.subquote2_position_ratio
|
||||||
.set_value(config.subquote2_position_ratio);
|
.set_value(config.subquote2_position_ratio);
|
||||||
self.tag_position_ratio.set_value(config.tag_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.tag2_position_ratio
|
self.tag2_position_ratio
|
||||||
.set_value(config.tag2_position_ratio);
|
.set_value(config.tag2_position_ratio);
|
||||||
self.image_ratio_width.set_value(config.image_ratio.0);
|
self.image_ratio_width.set_value(config.image_ratio.0);
|
||||||
self.image_ratio_height.set_value(config.image_ratio.1);
|
self.image_ratio_height.set_value(config.image_ratio.1);
|
||||||
|
self.draw_box_around_quote
|
||||||
|
.set_checked(config.draw_box_around_quote);
|
||||||
|
self.line_spacing.set_checked(config.line_spacing);
|
||||||
|
self.minimum_width_limit
|
||||||
|
.set_value(config.minimum_width_limit);
|
||||||
|
self.maximum_width_limit
|
||||||
|
.set_value(config.maximum_width_limit);
|
||||||
utils::set_color_btn_rgba(config.color_layer, &mut self.translucent_layer_rgb);
|
utils::set_color_btn_rgba(config.color_layer, &mut self.translucent_layer_rgb);
|
||||||
self.translucent_layer_alpha
|
self.translucent_layer_alpha
|
||||||
.set_value(config.color_layer[3] as f64);
|
.set_value(config.color_layer[3] as f64);
|
||||||
|
|
||||||
|
match config.image_format {
|
||||||
|
utils::ImageType::Png => {
|
||||||
|
self.png_format.set_value(true);
|
||||||
|
self.jpeg_format.set_value(false)
|
||||||
|
}
|
||||||
|
utils::ImageType::Jpeg => {
|
||||||
|
self.png_format.set_value(false);
|
||||||
|
self.jpeg_format.set_value(true)
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
*self.did_save.borrow_mut() = false;
|
*self.did_save.borrow_mut() = false;
|
||||||
drop(config);
|
drop(config);
|
||||||
self.win.show();
|
self.win.show();
|
||||||
|
|
@ -493,10 +627,15 @@ impl ConfigWindow {
|
||||||
let mut quote_position_ratio = self.quote_position_ratio.clone();
|
let mut quote_position_ratio = self.quote_position_ratio.clone();
|
||||||
let mut subquote_position_ratio = self.subquote_position_ratio.clone();
|
let mut subquote_position_ratio = self.subquote_position_ratio.clone();
|
||||||
let mut subquote2_position_ratio = self.subquote2_position_ratio.clone();
|
let mut subquote2_position_ratio = self.subquote2_position_ratio.clone();
|
||||||
let mut tag_position_ratio = self.tag_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 tag2_position_ratio = self.tag2_position_ratio.clone();
|
let mut tag2_position_ratio = self.tag2_position_ratio.clone();
|
||||||
let mut image_ratio_width = self.image_ratio_width.clone();
|
let mut image_ratio_width = self.image_ratio_width.clone();
|
||||||
let mut image_ratio_height = self.image_ratio_height.clone();
|
let mut image_ratio_height = self.image_ratio_height.clone();
|
||||||
|
let draw_box_around_quote = self.draw_box_around_quote.clone();
|
||||||
|
let line_spacing = self.line_spacing.clone();
|
||||||
|
let mut minimum_width_limit = self.minimum_width_limit.clone();
|
||||||
|
let mut maximum_width_limit = self.maximum_width_limit.clone();
|
||||||
let mut layer_rgb = self.translucent_layer_rgb.clone();
|
let mut layer_rgb = self.translucent_layer_rgb.clone();
|
||||||
let mut layer_alpha = self.translucent_layer_alpha.clone();
|
let mut layer_alpha = self.translucent_layer_alpha.clone();
|
||||||
let mut browse = self.browse.clone();
|
let mut browse = self.browse.clone();
|
||||||
|
|
@ -536,10 +675,15 @@ impl ConfigWindow {
|
||||||
quote_position_ratio.set_value(conf.quote_position_ratio);
|
quote_position_ratio.set_value(conf.quote_position_ratio);
|
||||||
subquote_position_ratio.set_value(conf.subquote_position_ratio);
|
subquote_position_ratio.set_value(conf.subquote_position_ratio);
|
||||||
subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
|
subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
|
||||||
tag_position_ratio.set_value(conf.tag_position_ratio);
|
tag_x_position_ratio.set_value(conf.tag_x_position_ratio);
|
||||||
|
tag_y_position_ratio.set_value(conf.tag_y_position_ratio);
|
||||||
tag2_position_ratio.set_value(conf.tag2_position_ratio);
|
tag2_position_ratio.set_value(conf.tag2_position_ratio);
|
||||||
image_ratio_width.set_value(conf.image_ratio.0);
|
image_ratio_width.set_value(conf.image_ratio.0);
|
||||||
image_ratio_height.set_value(conf.image_ratio.1);
|
image_ratio_height.set_value(conf.image_ratio.1);
|
||||||
|
draw_box_around_quote.set_checked(conf.draw_box_around_quote);
|
||||||
|
line_spacing.set_checked(conf.line_spacing);
|
||||||
|
minimum_width_limit.set_value(conf.minimum_width_limit);
|
||||||
|
maximum_width_limit.set_value(conf.maximum_width_limit);
|
||||||
utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb);
|
utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb);
|
||||||
layer_alpha.set_value(conf.color_layer[3] as f64);
|
layer_alpha.set_value(conf.color_layer[3] as f64);
|
||||||
browse.add(&name);
|
browse.add(&name);
|
||||||
|
|
@ -563,17 +707,22 @@ impl ConfigWindow {
|
||||||
let mut quote_position_ratio = self.quote_position_ratio.clone();
|
let mut quote_position_ratio = self.quote_position_ratio.clone();
|
||||||
let mut subquote_position_ratio = self.subquote_position_ratio.clone();
|
let mut subquote_position_ratio = self.subquote_position_ratio.clone();
|
||||||
let mut subquote2_position_ratio = self.subquote2_position_ratio.clone();
|
let mut subquote2_position_ratio = self.subquote2_position_ratio.clone();
|
||||||
let mut tag_position_ratio = self.tag_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 tag2_position_ratio = self.tag2_position_ratio.clone();
|
let mut tag2_position_ratio = self.tag2_position_ratio.clone();
|
||||||
let mut image_ratio_width = self.image_ratio_width.clone();
|
let mut image_ratio_width = self.image_ratio_width.clone();
|
||||||
let mut image_ratio_height = self.image_ratio_height.clone();
|
let mut image_ratio_height = self.image_ratio_height.clone();
|
||||||
|
let draw_box_around_quote = self.draw_box_around_quote.clone();
|
||||||
|
let line_spacing = self.line_spacing.clone();
|
||||||
|
let mut minimum_width_limit = self.minimum_width_limit.clone();
|
||||||
|
let mut maximum_width_limit = self.maximum_width_limit.clone();
|
||||||
let mut layer_rgb = self.translucent_layer_rgb.clone();
|
let mut layer_rgb = self.translucent_layer_rgb.clone();
|
||||||
let mut layer_alpha = self.translucent_layer_alpha.clone();
|
let mut layer_alpha = self.translucent_layer_alpha.clone();
|
||||||
let mut browse = self.browse.clone();
|
let mut browse = self.browse.clone();
|
||||||
let configs = Rc::clone(&self.configs);
|
let configs = Rc::clone(&self.configs);
|
||||||
let selected_browse_line = Rc::clone(&self.selected_browse_line);
|
let selected_browse_line = Rc::clone(&self.selected_browse_line);
|
||||||
self.del_config_btn.set_callback(move |_| {
|
self.del_config_btn.set_callback(move |_| {
|
||||||
let ch = dialog::choice_default("Do you want to delete??", "Yes", "No", "");
|
let ch = dialog::choice_default("Do you want to delete??", "Yes", "No");
|
||||||
if ch == 1 {
|
if ch == 1 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -605,10 +754,15 @@ impl ConfigWindow {
|
||||||
quote_position_ratio.set_value(conf.quote_position_ratio);
|
quote_position_ratio.set_value(conf.quote_position_ratio);
|
||||||
subquote_position_ratio.set_value(conf.subquote_position_ratio);
|
subquote_position_ratio.set_value(conf.subquote_position_ratio);
|
||||||
subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
|
subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
|
||||||
tag_position_ratio.set_value(conf.tag_position_ratio);
|
tag_x_position_ratio.set_value(conf.tag_x_position_ratio);
|
||||||
|
tag_y_position_ratio.set_value(conf.tag_y_position_ratio);
|
||||||
tag2_position_ratio.set_value(conf.tag2_position_ratio);
|
tag2_position_ratio.set_value(conf.tag2_position_ratio);
|
||||||
image_ratio_width.set_value(conf.image_ratio.0);
|
image_ratio_width.set_value(conf.image_ratio.0);
|
||||||
image_ratio_height.set_value(conf.image_ratio.1);
|
image_ratio_height.set_value(conf.image_ratio.1);
|
||||||
|
draw_box_around_quote.set_checked(conf.draw_box_around_quote);
|
||||||
|
line_spacing.set_checked(conf.line_spacing);
|
||||||
|
minimum_width_limit.set_value(conf.minimum_width_limit);
|
||||||
|
maximum_width_limit.set_value(conf.maximum_width_limit);
|
||||||
utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb);
|
utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb);
|
||||||
layer_alpha.set_value(conf.color_layer[3] as f64);
|
layer_alpha.set_value(conf.color_layer[3] as f64);
|
||||||
layer_rgb.redraw();
|
layer_rgb.redraw();
|
||||||
|
|
@ -629,10 +783,15 @@ impl ConfigWindow {
|
||||||
let mut quote_position_ratio = self.quote_position_ratio.clone();
|
let mut quote_position_ratio = self.quote_position_ratio.clone();
|
||||||
let mut subquote_position_ratio = self.subquote_position_ratio.clone();
|
let mut subquote_position_ratio = self.subquote_position_ratio.clone();
|
||||||
let mut subquote2_position_ratio = self.subquote2_position_ratio.clone();
|
let mut subquote2_position_ratio = self.subquote2_position_ratio.clone();
|
||||||
let mut tag_position_ratio = self.tag_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 tag2_position_ratio = self.tag2_position_ratio.clone();
|
let mut tag2_position_ratio = self.tag2_position_ratio.clone();
|
||||||
let mut image_ratio_width = self.image_ratio_width.clone();
|
let mut image_ratio_width = self.image_ratio_width.clone();
|
||||||
let mut image_ratio_height = self.image_ratio_height.clone();
|
let mut image_ratio_height = self.image_ratio_height.clone();
|
||||||
|
let draw_box_around_quote = self.draw_box_around_quote.clone();
|
||||||
|
let line_spacing = self.line_spacing.clone();
|
||||||
|
let mut minimum_width_limit = self.minimum_width_limit.clone();
|
||||||
|
let mut maximum_width_limit = self.maximum_width_limit.clone();
|
||||||
let mut layer_rgb = self.translucent_layer_rgb.clone();
|
let mut layer_rgb = self.translucent_layer_rgb.clone();
|
||||||
let mut layer_alpha = self.translucent_layer_alpha.clone();
|
let mut layer_alpha = self.translucent_layer_alpha.clone();
|
||||||
let configs = Rc::clone(&self.configs);
|
let configs = Rc::clone(&self.configs);
|
||||||
|
|
@ -661,10 +820,15 @@ impl ConfigWindow {
|
||||||
quote_position_ratio.set_value(conf.quote_position_ratio);
|
quote_position_ratio.set_value(conf.quote_position_ratio);
|
||||||
subquote_position_ratio.set_value(conf.subquote_position_ratio);
|
subquote_position_ratio.set_value(conf.subquote_position_ratio);
|
||||||
subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
|
subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
|
||||||
tag_position_ratio.set_value(conf.tag_position_ratio);
|
tag_x_position_ratio.set_value(conf.tag_x_position_ratio);
|
||||||
|
tag_y_position_ratio.set_value(conf.tag_y_position_ratio);
|
||||||
tag2_position_ratio.set_value(conf.tag2_position_ratio);
|
tag2_position_ratio.set_value(conf.tag2_position_ratio);
|
||||||
image_ratio_width.set_value(conf.image_ratio.0);
|
image_ratio_width.set_value(conf.image_ratio.0);
|
||||||
image_ratio_height.set_value(conf.image_ratio.1);
|
image_ratio_height.set_value(conf.image_ratio.1);
|
||||||
|
draw_box_around_quote.set_checked(conf.draw_box_around_quote);
|
||||||
|
line_spacing.set_checked(conf.line_spacing);
|
||||||
|
minimum_width_limit.set_value(conf.minimum_width_limit);
|
||||||
|
maximum_width_limit.set_value(conf.maximum_width_limit);
|
||||||
utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb);
|
utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb);
|
||||||
layer_alpha.set_value(conf.color_layer[3] as f64);
|
layer_alpha.set_value(conf.color_layer[3] as f64);
|
||||||
layer_rgb.redraw();
|
layer_rgb.redraw();
|
||||||
|
|
@ -902,16 +1066,31 @@ impl ConfigWindow {
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Tag position ratio
|
// Tag x position ratio
|
||||||
let browse = self.browse.clone();
|
let browse = self.browse.clone();
|
||||||
let configs = Rc::clone(&self.configs);
|
let configs = Rc::clone(&self.configs);
|
||||||
self.tag_position_ratio.handle(move |f, ev| {
|
self.tag_x_position_ratio.handle(move |f, ev| {
|
||||||
if ev == Event::KeyUp {
|
if ev == Event::KeyUp {
|
||||||
if let Some(conf) = configs
|
if let Some(conf) = configs
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.get_mut(&browse.selected_text().unwrap())
|
.get_mut(&browse.selected_text().unwrap())
|
||||||
{
|
{
|
||||||
conf.tag_position_ratio = f.value();
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|
@ -962,6 +1141,62 @@ impl ConfigWindow {
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Draw box around quote
|
||||||
|
let browse = self.browse.clone();
|
||||||
|
let configs = Rc::clone(&self.configs);
|
||||||
|
self.draw_box_around_quote.handle(move |f, _| {
|
||||||
|
if let Some(conf) = configs
|
||||||
|
.borrow_mut()
|
||||||
|
.get_mut(&browse.selected_text().unwrap())
|
||||||
|
{
|
||||||
|
conf.draw_box_around_quote = f.value();
|
||||||
|
}
|
||||||
|
true
|
||||||
|
});
|
||||||
|
|
||||||
|
// line spacing
|
||||||
|
let browse = self.browse.clone();
|
||||||
|
let configs = Rc::clone(&self.configs);
|
||||||
|
self.line_spacing.handle(move |f, _| {
|
||||||
|
if let Some(conf) = configs
|
||||||
|
.borrow_mut()
|
||||||
|
.get_mut(&browse.selected_text().unwrap())
|
||||||
|
{
|
||||||
|
conf.line_spacing = f.value();
|
||||||
|
}
|
||||||
|
true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Minimum Width Limit
|
||||||
|
let browse = self.browse.clone();
|
||||||
|
let configs = Rc::clone(&self.configs);
|
||||||
|
self.minimum_width_limit.handle(move |f, ev| {
|
||||||
|
if ev == Event::KeyUp {
|
||||||
|
if let Some(conf) = configs
|
||||||
|
.borrow_mut()
|
||||||
|
.get_mut(&browse.selected_text().unwrap())
|
||||||
|
{
|
||||||
|
conf.minimum_width_limit = f.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Maximim Width Limit
|
||||||
|
let browse = self.browse.clone();
|
||||||
|
let configs = Rc::clone(&self.configs);
|
||||||
|
self.maximum_width_limit.handle(move |f, ev| {
|
||||||
|
if ev == Event::KeyUp {
|
||||||
|
if let Some(conf) = configs
|
||||||
|
.borrow_mut()
|
||||||
|
.get_mut(&browse.selected_text().unwrap())
|
||||||
|
{
|
||||||
|
conf.maximum_width_limit = f.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
});
|
||||||
|
|
||||||
// Translucent Layer RGB
|
// Translucent Layer RGB
|
||||||
let browse = self.browse.clone();
|
let browse = self.browse.clone();
|
||||||
let configs = Rc::clone(&self.configs);
|
let configs = Rc::clone(&self.configs);
|
||||||
|
|
@ -1006,6 +1241,30 @@ impl ConfigWindow {
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Png Image format
|
||||||
|
let browse = self.browse.clone();
|
||||||
|
let configs = Rc::clone(&self.configs);
|
||||||
|
self.png_format.set_callback(move |_| {
|
||||||
|
if let Some(conf) = configs
|
||||||
|
.borrow_mut()
|
||||||
|
.get_mut(&browse.selected_text().unwrap())
|
||||||
|
{
|
||||||
|
conf.image_format = ImageType::Png;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Jpeg Image format
|
||||||
|
let browse = self.browse.clone();
|
||||||
|
let configs = Rc::clone(&self.configs);
|
||||||
|
self.jpeg_format.set_callback(move |_| {
|
||||||
|
if let Some(conf) = configs
|
||||||
|
.borrow_mut()
|
||||||
|
.get_mut(&browse.selected_text().unwrap())
|
||||||
|
{
|
||||||
|
conf.image_format = ImageType::Jpeg;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Reset to default configuation button
|
// Reset to default configuation button
|
||||||
let mut quote_font = self.quote_font.clone();
|
let mut quote_font = self.quote_font.clone();
|
||||||
let mut subquote_font = self.subquote_font.clone();
|
let mut subquote_font = self.subquote_font.clone();
|
||||||
|
|
@ -1020,12 +1279,19 @@ impl ConfigWindow {
|
||||||
let mut quote_position_ratio = self.quote_position_ratio.clone();
|
let mut quote_position_ratio = self.quote_position_ratio.clone();
|
||||||
let mut subquote_position_ratio = self.subquote_position_ratio.clone();
|
let mut subquote_position_ratio = self.subquote_position_ratio.clone();
|
||||||
let mut subquote2_position_ratio = self.subquote2_position_ratio.clone();
|
let mut subquote2_position_ratio = self.subquote2_position_ratio.clone();
|
||||||
let mut tag_position_ratio = self.tag_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 tag2_position_ratio = self.tag2_position_ratio.clone();
|
let mut tag2_position_ratio = self.tag2_position_ratio.clone();
|
||||||
let mut image_ratio_width = self.image_ratio_width.clone();
|
let mut image_ratio_width = self.image_ratio_width.clone();
|
||||||
let mut image_ratio_height = self.image_ratio_height.clone();
|
let mut image_ratio_height = self.image_ratio_height.clone();
|
||||||
|
let draw_box_around_quote = self.draw_box_around_quote.clone();
|
||||||
|
let line_spacing = self.line_spacing.clone();
|
||||||
|
let mut minimum_width_limit = self.minimum_width_limit.clone();
|
||||||
|
let mut maximum_width_limit = self.maximum_width_limit.clone();
|
||||||
let mut layer_rgb = self.translucent_layer_rgb.clone();
|
let mut layer_rgb = self.translucent_layer_rgb.clone();
|
||||||
let mut layer_alpha = self.translucent_layer_alpha.clone();
|
let mut layer_alpha = self.translucent_layer_alpha.clone();
|
||||||
|
let mut png_format = self.png_format.clone();
|
||||||
|
let mut jpeg_format = self.jpeg_format.clone();
|
||||||
let configs = Rc::clone(&self.configs);
|
let configs = Rc::clone(&self.configs);
|
||||||
let browse = self.browse.clone();
|
let browse = self.browse.clone();
|
||||||
self.defaults_btn.set_callback(move |_| {
|
self.defaults_btn.set_callback(move |_| {
|
||||||
|
|
@ -1043,13 +1309,29 @@ impl ConfigWindow {
|
||||||
quote_position_ratio.set_value(conf.quote_position_ratio);
|
quote_position_ratio.set_value(conf.quote_position_ratio);
|
||||||
subquote_position_ratio.set_value(conf.subquote_position_ratio);
|
subquote_position_ratio.set_value(conf.subquote_position_ratio);
|
||||||
subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
|
subquote2_position_ratio.set_value(conf.subquote2_position_ratio);
|
||||||
tag_position_ratio.set_value(conf.tag_position_ratio);
|
tag_x_position_ratio.set_value(conf.tag_x_position_ratio);
|
||||||
|
tag_y_position_ratio.set_value(conf.tag_y_position_ratio);
|
||||||
tag2_position_ratio.set_value(conf.tag2_position_ratio);
|
tag2_position_ratio.set_value(conf.tag2_position_ratio);
|
||||||
image_ratio_width.set_value(conf.image_ratio.0);
|
image_ratio_width.set_value(conf.image_ratio.0);
|
||||||
image_ratio_height.set_value(conf.image_ratio.1);
|
image_ratio_height.set_value(conf.image_ratio.1);
|
||||||
|
draw_box_around_quote.set_checked(conf.draw_box_around_quote);
|
||||||
|
line_spacing.set_checked(conf.line_spacing);
|
||||||
|
minimum_width_limit.set_value(conf.minimum_width_limit);
|
||||||
|
maximum_width_limit.set_value(conf.maximum_width_limit);
|
||||||
utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb);
|
utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb);
|
||||||
layer_rgb.redraw();
|
layer_rgb.redraw();
|
||||||
layer_alpha.set_value(conf.color_layer[3] as f64);
|
layer_alpha.set_value(conf.color_layer[3] as f64);
|
||||||
|
match conf.image_format {
|
||||||
|
ImageType::Png => {
|
||||||
|
png_format.set_value(true);
|
||||||
|
jpeg_format.set_value(false);
|
||||||
|
}
|
||||||
|
ImageType::Jpeg => {
|
||||||
|
png_format.set_value(false);
|
||||||
|
jpeg_format.set_value(true);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
configs
|
configs
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(browse.selected_text().unwrap(), conf);
|
.insert(browse.selected_text().unwrap(), conf);
|
||||||
|
|
@ -1062,8 +1344,8 @@ impl ConfigWindow {
|
||||||
self.save_btn.set_callback(move |_| {
|
self.save_btn.set_callback(move |_| {
|
||||||
config::save_configs((*configs.borrow()).clone());
|
config::save_configs((*configs.borrow()).clone());
|
||||||
|
|
||||||
if let Some(c) = configs.borrow().get(&*globals::CONFIG_NAME.read().unwrap()) {
|
if let Some(c) = configs.borrow().get(&*rw_read!(globals::CONFIG_NAME)) {
|
||||||
*globals::CONFIG.write().unwrap() = c.to_owned();
|
*rw_write!(globals::CONFIG) = c.to_owned();
|
||||||
}
|
}
|
||||||
*did_save.borrow_mut() = true;
|
*did_save.borrow_mut() = true;
|
||||||
win.hide();
|
win.hide();
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
//! Window to change Crop properties of image
|
//! Window to change Crop properties of image
|
||||||
use crate::{
|
use crate::{
|
||||||
globals,
|
globals,
|
||||||
utils::{self, Coord, ImageContainer, ImageProperties},
|
result_ext::ResultExt,
|
||||||
|
utils::{self, Coord, ImageContainer, ImageInfo, ImageProperties},
|
||||||
};
|
};
|
||||||
use fltk::{
|
use fltk::{
|
||||||
app, button::Button, draw, enums::Event, frame::Frame, group::Flex, image::SvgImage,
|
app, button::Button, draw, enums::Event, frame::Frame, group::Flex, image::SvgImage,
|
||||||
|
|
@ -24,7 +25,6 @@ use fltk::{
|
||||||
use image::GenericImageView;
|
use image::GenericImageView;
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
path::PathBuf,
|
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
};
|
};
|
||||||
|
|
@ -108,13 +108,13 @@ impl CropWindow {
|
||||||
/// Call it to show window to crop image
|
/// Call it to show window to crop image
|
||||||
pub(crate) fn load_to_crop(
|
pub(crate) fn load_to_crop(
|
||||||
&mut self,
|
&mut self,
|
||||||
path: &PathBuf,
|
path: &ImageInfo,
|
||||||
crop_pos: Option<(f64, f64)>,
|
crop_pos: Option<(f64, f64)>,
|
||||||
) -> Option<(f64, f64)> {
|
) -> Option<(f64, f64)> {
|
||||||
let mut container =
|
let mut container =
|
||||||
ImageContainer::new(path, Arc::new(RwLock::new(ImageProperties::default())));
|
ImageContainer::new(path, Arc::new(RwLock::new(ImageProperties::default())));
|
||||||
{
|
{
|
||||||
let prop = &mut container.properties.write().unwrap();
|
let prop = &mut rw_write!(container.properties);
|
||||||
prop.dimension = prop.original_dimension;
|
prop.dimension = prop.original_dimension;
|
||||||
prop.crop_position = match crop_pos {
|
prop.crop_position = match crop_pos {
|
||||||
Some(a) => Some(a),
|
Some(a) => Some(a),
|
||||||
|
|
@ -146,7 +146,7 @@ impl CropWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(cont) = &*self.container.borrow() {
|
if let Some(cont) = &*self.container.borrow() {
|
||||||
cont.properties.read().unwrap().crop_position
|
rw_read!(cont.properties).crop_position
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -169,7 +169,7 @@ impl CropWindow {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let prop = cont.properties.read().unwrap();
|
let prop = rw_read!(cont.properties);
|
||||||
let (original_width, original_height) = prop.original_dimension;
|
let (original_width, original_height) = prop.original_dimension;
|
||||||
let (original_x, original_y) = prop.crop_position.unwrap();
|
let (original_x, original_y) = prop.crop_position.unwrap();
|
||||||
let (resized_width, resized_height) = (image.width() as f64, image.height() as f64);
|
let (resized_width, resized_height) = (image.width() as f64, image.height() as f64);
|
||||||
|
|
@ -201,7 +201,7 @@ impl CropWindow {
|
||||||
if let Some(cont) = &*container.borrow_mut() {
|
if let Some(cont) = &*container.borrow_mut() {
|
||||||
let image = &cont.buffer;
|
let image = &cont.buffer;
|
||||||
|
|
||||||
let mut prop = cont.properties.write().unwrap();
|
let mut prop = rw_write!(cont.properties);
|
||||||
|
|
||||||
let (original_x, original_y) = match prop.crop_position {
|
let (original_x, original_y) = match prop.crop_position {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
|
|
@ -272,7 +272,7 @@ impl CropWindow {
|
||||||
let container = Rc::clone(&self.container);
|
let container = Rc::clone(&self.container);
|
||||||
self.win.set_callback(move |f| {
|
self.win.set_callback(move |f| {
|
||||||
if let Some(cont) = &*container.borrow_mut() {
|
if let Some(cont) = &*container.borrow_mut() {
|
||||||
cont.properties.write().unwrap().crop_position = None;
|
rw_write!(cont.properties).crop_position = None;
|
||||||
}
|
}
|
||||||
f.hide();
|
f.hide();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
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,16 +14,17 @@
|
||||||
|
|
||||||
//! Thread to manage drawing in background
|
//! Thread to manage drawing in background
|
||||||
|
|
||||||
use crate::utils::{ImageContainer, ImageProperties};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
globals,
|
||||||
main_window::{MainWindow, Page},
|
main_window::{MainWindow, Page},
|
||||||
utils::{self, ImagePropertiesFile},
|
result_ext::ResultExt,
|
||||||
|
utils::{self, ImageContainer, ImageInfo, ImageProperties, ImagePropertiesFile},
|
||||||
AppMessage,
|
AppMessage,
|
||||||
};
|
};
|
||||||
use fltk::{
|
use fltk::{
|
||||||
app,
|
app,
|
||||||
button::Button,
|
button::Button,
|
||||||
dialog, enums,
|
enums,
|
||||||
frame::Frame,
|
frame::Frame,
|
||||||
input::{Input, MultilineInput},
|
input::{Input, MultilineInput},
|
||||||
menu,
|
menu,
|
||||||
|
|
@ -33,7 +34,6 @@ use fltk::{
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
path::{Path, PathBuf},
|
|
||||||
sync::{mpsc, Arc, RwLock},
|
sync::{mpsc, Arc, RwLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -53,6 +53,10 @@ pub(crate) enum DrawMessage {
|
||||||
Clone,
|
Clone,
|
||||||
/// Delete file
|
/// Delete file
|
||||||
Delete,
|
Delete,
|
||||||
|
/// Show details about images linke count of quotes
|
||||||
|
ShowImagesDetails,
|
||||||
|
/// Check If image is proper
|
||||||
|
CheckImage,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawn thread to manage all actions related to image, like: edit, save, delete
|
/// Spawn thread to manage all actions related to image, like: edit, save, delete
|
||||||
|
|
@ -64,6 +68,7 @@ pub(crate) fn spawn_image_thread(
|
||||||
) {
|
) {
|
||||||
let mut win = main_win.win.clone();
|
let mut win = main_win.win.clone();
|
||||||
let mut file_choice = main_win.file_choice.clone();
|
let mut file_choice = main_win.file_choice.clone();
|
||||||
|
let mut name_prefix = main_win.name_prefix.clone();
|
||||||
let mut quote = main_win.quote.clone();
|
let mut quote = main_win.quote.clone();
|
||||||
let mut subquote = main_win.subquote.clone();
|
let mut subquote = main_win.subquote.clone();
|
||||||
let mut subquote2 = main_win.subquote2.clone();
|
let mut subquote2 = main_win.subquote2.clone();
|
||||||
|
|
@ -85,7 +90,7 @@ pub(crate) fn spawn_image_thread(
|
||||||
let mut status = main_win.status.clone();
|
let mut status = main_win.status.clone();
|
||||||
let mut count = main_win.count.clone();
|
let mut count = main_win.count.clone();
|
||||||
let mut dimension = main_win.dimension.clone();
|
let mut dimension = main_win.dimension.clone();
|
||||||
let images_path = Arc::clone(&main_win.images_path);
|
let images_list = Arc::clone(&main_win.images_list);
|
||||||
|
|
||||||
let mut _container: Option<ImageContainer> = None;
|
let mut _container: Option<ImageContainer> = None;
|
||||||
std::thread::spawn(move || loop {
|
std::thread::spawn(move || loop {
|
||||||
|
|
@ -95,8 +100,9 @@ pub(crate) fn spawn_image_thread(
|
||||||
status.set_label("Loading...");
|
status.set_label("Loading...");
|
||||||
load_image(
|
load_image(
|
||||||
&mut file_choice,
|
&mut file_choice,
|
||||||
Arc::clone(&images_path),
|
Arc::clone(&images_list),
|
||||||
None,
|
None,
|
||||||
|
&mut name_prefix,
|
||||||
&mut quote,
|
&mut quote,
|
||||||
&mut subquote,
|
&mut subquote,
|
||||||
&mut subquote2,
|
&mut subquote2,
|
||||||
|
|
@ -128,8 +134,9 @@ pub(crate) fn spawn_image_thread(
|
||||||
status.set_label("Loading...");
|
status.set_label("Loading...");
|
||||||
load_image(
|
load_image(
|
||||||
&mut file_choice,
|
&mut file_choice,
|
||||||
Arc::clone(&images_path),
|
Arc::clone(&images_list),
|
||||||
Some((x, y)),
|
Some((x, y)),
|
||||||
|
&mut name_prefix,
|
||||||
&mut quote,
|
&mut quote,
|
||||||
&mut subquote,
|
&mut subquote,
|
||||||
&mut subquote2,
|
&mut subquote2,
|
||||||
|
|
@ -169,21 +176,23 @@ pub(crate) fn spawn_image_thread(
|
||||||
status.set_label("Saving...");
|
status.set_label("Saving...");
|
||||||
win.deactivate();
|
win.deactivate();
|
||||||
cont.save();
|
cont.save();
|
||||||
win.activate();
|
|
||||||
status.set_label("");
|
status.set_label("");
|
||||||
|
win.activate();
|
||||||
|
win.redraw();
|
||||||
|
app::awake();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DrawMessage::Clone => {
|
DrawMessage::Clone => {
|
||||||
if let Some(cont) = &mut _container {
|
if let Some(cont) = &mut _container {
|
||||||
status.set_label("Cloning...");
|
status.set_label("Cloning...");
|
||||||
win.deactivate();
|
win.deactivate();
|
||||||
if let Some(path) = cont.clone_img() {
|
if let Some(image_info) = cont.clone_img() {
|
||||||
let idx = file_choice.value();
|
let idx = file_choice.value();
|
||||||
let mut imgs = images_path.write().unwrap();
|
let mut imgs = rw_write!(images_list);
|
||||||
imgs.insert(idx as usize, path.clone());
|
imgs.insert(idx as usize, image_info.clone());
|
||||||
file_choice.insert(
|
file_choice.insert(
|
||||||
idx,
|
idx,
|
||||||
path.file_name().unwrap().to_str().unwrap(),
|
image_info.path.file_name().unwrap().to_str().unwrap(),
|
||||||
enums::Shortcut::None,
|
enums::Shortcut::None,
|
||||||
menu::MenuFlag::Normal,
|
menu::MenuFlag::Normal,
|
||||||
|a| a.do_callback(),
|
|a| a.do_callback(),
|
||||||
|
|
@ -192,6 +201,8 @@ pub(crate) fn spawn_image_thread(
|
||||||
}
|
}
|
||||||
status.set_label("");
|
status.set_label("");
|
||||||
win.activate();
|
win.activate();
|
||||||
|
win.redraw();
|
||||||
|
app::awake();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DrawMessage::Delete => {
|
DrawMessage::Delete => {
|
||||||
|
|
@ -199,7 +210,7 @@ pub(crate) fn spawn_image_thread(
|
||||||
status.set_label("Deleting...");
|
status.set_label("Deleting...");
|
||||||
win.deactivate();
|
win.deactivate();
|
||||||
cont.delete();
|
cont.delete();
|
||||||
let mut imgs = images_path.write().unwrap();
|
let mut imgs = rw_write!(images_list);
|
||||||
imgs.remove(file_choice.value() as usize);
|
imgs.remove(file_choice.value() as usize);
|
||||||
file_choice.remove(file_choice.value());
|
file_choice.remove(file_choice.value());
|
||||||
if file_choice.value() != imgs.len() as i32 {
|
if file_choice.value() != imgs.len() as i32 {
|
||||||
|
|
@ -209,6 +220,17 @@ pub(crate) fn spawn_image_thread(
|
||||||
}
|
}
|
||||||
status.set_label("");
|
status.set_label("");
|
||||||
win.activate();
|
win.activate();
|
||||||
|
win.redraw();
|
||||||
|
app::awake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DrawMessage::ShowImagesDetails => show_images_details(Arc::clone(&images_list)),
|
||||||
|
DrawMessage::CheckImage => {
|
||||||
|
let (width, height) = rw_read!(properties).original_dimension;
|
||||||
|
if utils::is_too_small(width, height) {
|
||||||
|
if let Some(a) = &*rw_read!(globals::MAIN_SENDER) {
|
||||||
|
a.send(crate::AppMessage::DeleteImage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -219,8 +241,9 @@ pub(crate) fn spawn_image_thread(
|
||||||
/// Loads the selected image in file_choice to ImageContainer to edit
|
/// Loads the selected image in file_choice to ImageContainer to edit
|
||||||
fn load_image(
|
fn load_image(
|
||||||
file_choice: &mut menu::Choice,
|
file_choice: &mut menu::Choice,
|
||||||
images_path: Arc<RwLock<Vec<PathBuf>>>,
|
images_list: Arc<RwLock<Vec<ImageInfo>>>,
|
||||||
crop: Option<(f64, f64)>,
|
crop: Option<(f64, f64)>,
|
||||||
|
name_prefix: &mut Input,
|
||||||
quote: &mut MultilineInput,
|
quote: &mut MultilineInput,
|
||||||
subquote: &mut MultilineInput,
|
subquote: &mut MultilineInput,
|
||||||
subquote2: &mut MultilineInput,
|
subquote2: &mut MultilineInput,
|
||||||
|
|
@ -245,43 +268,36 @@ fn load_image(
|
||||||
properties: Arc<RwLock<ImageProperties>>,
|
properties: Arc<RwLock<ImageProperties>>,
|
||||||
container: &mut Option<ImageContainer>,
|
container: &mut Option<ImageContainer>,
|
||||||
) {
|
) {
|
||||||
let imgs = images_path.read().unwrap();
|
let imgs = rw_read!(images_list);
|
||||||
if imgs.len() == 0 {
|
if imgs.len() == 0 {
|
||||||
*container = None;
|
*container = None;
|
||||||
flush_buffer(app_sender, container);
|
flush_buffer(app_sender, container);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
count.set_label(&format!("[{}/{}]", file_choice.value() + 1, imgs.len()));
|
count.set_label(&format!("[{}/{}]", file_choice.value() + 1, imgs.len()));
|
||||||
let file = imgs.get(file_choice.value() as usize).unwrap();
|
let image_info = imgs.get(file_choice.value() as usize).unwrap();
|
||||||
|
|
||||||
*container = Some(ImageContainer::new(&file, Arc::clone(&properties)));
|
*container = Some(ImageContainer::new(&image_info, Arc::clone(&properties)));
|
||||||
|
|
||||||
if let Some(cont) = container {
|
if let Some(cont) = container {
|
||||||
let file = Path::new(&file);
|
let properties_file = utils::get_properties_path(&image_info);
|
||||||
let properties_file = file.with_extension("prop");
|
|
||||||
|
|
||||||
let read = fs::read_to_string(&properties_file).unwrap_or("{}".to_owned());
|
let read = fs::read_to_string(&properties_file).unwrap_or("{}".to_owned());
|
||||||
let read = match serde_json::from_str::<ImagePropertiesFile>(&read) {
|
let read = match serde_json::from_str::<ImagePropertiesFile>(&read) {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Config is corrupt\n{:?}", e);
|
Result::<(), _>::Err(e).warn_log("Config is corrupt");
|
||||||
match dialog::choice_default("Config is corrupt, fix??", "yes", "no", "") {
|
fs::remove_file(&properties_file)
|
||||||
1 => {
|
.warn_log("Failed to delete image properties file!");
|
||||||
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()
|
ImagePropertiesFile::default()
|
||||||
}
|
}
|
||||||
_ => return,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut properties = cont.properties.write().unwrap();
|
let mut properties = rw_write!(cont.properties);
|
||||||
properties.merge(read, &tag.value(), &tag2.value());
|
properties.merge(read, &tag.value(), &tag2.value());
|
||||||
properties.is_saved = true;
|
properties.is_saved = true;
|
||||||
|
|
||||||
|
name_prefix.set_value(&properties.name_prefix);
|
||||||
quote.set_value(&properties.quote);
|
quote.set_value(&properties.quote);
|
||||||
subquote.set_value(&properties.subquote);
|
subquote.set_value(&properties.subquote);
|
||||||
subquote2.set_value(&properties.subquote2);
|
subquote2.set_value(&properties.subquote2);
|
||||||
|
|
@ -332,7 +348,7 @@ fn load_image(
|
||||||
}
|
}
|
||||||
|
|
||||||
cont.apply_resize();
|
cont.apply_resize();
|
||||||
let (width, height) = cont.properties.read().unwrap().dimension;
|
let (width, height) = rw_read!(cont.properties).dimension;
|
||||||
page.col_flex.set_size(&page.image, height as i32);
|
page.col_flex.set_size(&page.image, height as i32);
|
||||||
page.row_flex.set_size(&page.col_flex, width as i32);
|
page.row_flex.set_size(&page.col_flex, width as i32);
|
||||||
page.col_flex.recalc();
|
page.col_flex.recalc();
|
||||||
|
|
@ -342,6 +358,40 @@ fn load_image(
|
||||||
flush_buffer(&app_sender, &container);
|
flush_buffer(&app_sender, &container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_images_details(images_list: Arc<RwLock<Vec<ImageInfo>>>) {
|
||||||
|
let mut image_with_quote: usize = 0;
|
||||||
|
let mut image_without_quote: usize = 0;
|
||||||
|
|
||||||
|
let list = rw_read!(images_list);
|
||||||
|
for image_info in list.iter() {
|
||||||
|
let properties_file = utils::get_properties_path(&image_info);
|
||||||
|
|
||||||
|
let read = fs::read_to_string(&properties_file).unwrap_or("{}".to_owned());
|
||||||
|
let read = match serde_json::from_str::<ImagePropertiesFile>(&read) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(_) => {
|
||||||
|
image_without_quote += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(t) = read.quote {
|
||||||
|
if t.trim().len() == 0 {
|
||||||
|
image_without_quote += 1;
|
||||||
|
} else {
|
||||||
|
image_with_quote += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
image_without_quote += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::show_message(&format!(
|
||||||
|
"With Quote: {}\nWithout Quote: {}",
|
||||||
|
image_with_quote, image_without_quote
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
/// Flush the Buffer from image container to drawing buffer for fltk
|
/// Flush the Buffer from image container to drawing buffer for fltk
|
||||||
// for drawing buffer for fltk (check in main.rs)
|
// for drawing buffer for fltk (check in main.rs)
|
||||||
fn flush_buffer(app_sender: &app::Sender<crate::AppMessage>, container: &Option<ImageContainer>) {
|
fn flush_buffer(app_sender: &app::Sender<crate::AppMessage>, container: &Option<ImageContainer>) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,268 @@
|
||||||
|
/*
|
||||||
|
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/>
|
along with Post Maker. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::config;
|
use crate::{config, result_ext::ResultExt};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use rusttype::Font;
|
use rusttype::Font;
|
||||||
use std::{ffi::OsString, io::Read, sync::RwLock};
|
use std::{ffi::OsString, io::Read, sync::RwLock};
|
||||||
|
|
@ -29,86 +29,23 @@ lazy_static! {
|
||||||
pub(crate) static ref CONFIG: RwLock<config::ConfigFile> =
|
pub(crate) static ref CONFIG: RwLock<config::ConfigFile> =
|
||||||
RwLock::new(config::ConfigFile::load());
|
RwLock::new(config::ConfigFile::load());
|
||||||
|
|
||||||
|
/// Main Sender
|
||||||
|
pub(crate) static ref MAIN_SENDER: RwLock<Option<fltk::app::Sender<crate::AppMessage>>> = RwLock::new(None);
|
||||||
|
|
||||||
/// TTF Font for Quote
|
/// TTF Font for Quote
|
||||||
pub(crate) static ref FONT_QUOTE: Font<'static> = {
|
pub(crate) static ref FONT_QUOTE: Font<'static> = load_font(rw_read!(CONFIG).quote_font.as_str());
|
||||||
let mut buffer = Vec::new();
|
|
||||||
if let Ok(mut file) = std::fs::File::open(CONFIG.read().unwrap().quote_font.as_str()) {
|
|
||||||
if let Ok(_) = file.read_to_end(&mut buffer) {
|
|
||||||
if let Some(out) = rusttype::Font::try_from_vec(buffer) {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rusttype::Font::try_from_vec(
|
|
||||||
include_bytes!("../assets/fonts/ReenieBeanie-Regular.ttf").to_vec(),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
/// TTF Font for Subquote
|
/// TTF Font for Subquote
|
||||||
pub(crate) static ref FONT_SUBQUOTE: Font<'static> = {
|
pub(crate) static ref FONT_SUBQUOTE: Font<'static> = load_font(rw_read!(CONFIG).subquote_font.as_str());
|
||||||
let mut buffer = Vec::new();
|
|
||||||
if let Ok(mut file) = std::fs::File::open(CONFIG.read().unwrap().subquote_font.as_str())
|
|
||||||
{
|
|
||||||
if let Ok(_) = file.read_to_end(&mut buffer) {
|
|
||||||
if let Some(out) = rusttype::Font::try_from_vec(buffer) {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rusttype::Font::try_from_vec(
|
|
||||||
include_bytes!("../assets/fonts/ReenieBeanie-Regular.ttf").to_vec(),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
/// TTF Font for Subquote 2
|
/// TTF Font for Subquote 2
|
||||||
pub(crate) static ref FONT_SUBQUOTE2: Font<'static> = {
|
pub(crate) static ref FONT_SUBQUOTE2: Font<'static> = load_font(rw_read!(CONFIG).subquote2_font.as_str());
|
||||||
let mut buffer = Vec::new();
|
|
||||||
if let Ok(mut file) =
|
|
||||||
std::fs::File::open(CONFIG.read().unwrap().subquote2_font.as_str())
|
|
||||||
{
|
|
||||||
if let Ok(_) = file.read_to_end(&mut buffer) {
|
|
||||||
if let Some(out) = rusttype::Font::try_from_vec(buffer) {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rusttype::Font::try_from_vec(
|
|
||||||
include_bytes!("../assets/fonts/Rajdhani-Regular.ttf").to_vec(),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
/// TTF Font for Tag
|
/// TTF Font for Tag
|
||||||
pub(crate) static ref FONT_TAG: Font<'static> = {
|
pub(crate) static ref FONT_TAG: Font<'static> = load_font(rw_read!(CONFIG).tag_font.as_str());
|
||||||
let mut buffer = Vec::new();
|
|
||||||
if let Ok(mut file) = std::fs::File::open(&CONFIG.read().unwrap().tag_font.as_str()) {
|
|
||||||
if let Ok(_) = file.read_to_end(&mut buffer) {
|
|
||||||
if let Some(out) = rusttype::Font::try_from_vec(buffer) {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rusttype::Font::try_from_vec(include_bytes!("../assets/fonts/Kalam-Regular.ttf").to_vec())
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
/// TTF Font for Tag 2
|
/// TTF Font for Tag 2
|
||||||
pub(crate) static ref FONT_TAG2: Font<'static> = {
|
pub(crate) static ref FONT_TAG2: Font<'static> = load_font(rw_read!(CONFIG).tag2_font.as_str());
|
||||||
let mut buffer = Vec::new();
|
|
||||||
if let Ok(mut file) = std::fs::File::open(&CONFIG.read().unwrap().tag2_font.as_str()) {
|
|
||||||
if let Ok(_) = file.read_to_end(&mut buffer) {
|
|
||||||
if let Some(out) = rusttype::Font::try_from_vec(buffer) {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rusttype::Font::try_from_vec(
|
|
||||||
include_bytes!("../assets/fonts/Rajdhani-Regular.ttf").to_vec(),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Image to use for Window
|
/// Image to use for Window
|
||||||
pub(crate) static ref ICON: OsString = include_str!("../assets/icon.svg").into();
|
pub(crate) static ref ICON: OsString = include_str!("../assets/icon.svg").into();
|
||||||
|
|
@ -125,3 +62,15 @@ lazy_static! {
|
||||||
img.into()
|
img.into()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_font(path: &str) -> Font<'static> {
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
if let Ok(mut file) = std::fs::File::open(path) {
|
||||||
|
if let Ok(_) = file.read_to_end(&mut buffer) {
|
||||||
|
if let Some(out) = rusttype::Font::try_from_vec(buffer) {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rusttype::Font::try_from_vec(include_bytes!("../assets/OpenSans-Regular.ttf").to_vec()).unwrap()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
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!")
|
||||||
|
};
|
||||||
|
}
|
||||||
55
src/main.rs
55
src/main.rs
|
|
@ -17,23 +17,29 @@
|
||||||
extern crate log;
|
extern crate log;
|
||||||
extern crate simplelog;
|
extern crate simplelog;
|
||||||
|
|
||||||
mod about;
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
|
mod about_window;
|
||||||
mod config;
|
mod config;
|
||||||
mod config_picker;
|
mod config_picker;
|
||||||
mod config_window;
|
mod config_window;
|
||||||
mod crop_window;
|
mod crop_window;
|
||||||
|
mod dialog;
|
||||||
mod draw_thread;
|
mod draw_thread;
|
||||||
|
mod export_all_window;
|
||||||
mod globals;
|
mod globals;
|
||||||
mod main_window;
|
mod main_window;
|
||||||
|
mod result_ext;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use fltk::{
|
use fltk::{
|
||||||
app::{channel, App},
|
app::{channel, App},
|
||||||
dialog,
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use fltk_theme::WidgetTheme;
|
use fltk_theme::WidgetTheme;
|
||||||
use main_window::MainWindow;
|
use main_window::MainWindow;
|
||||||
|
use result_ext::ResultExt;
|
||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
|
@ -41,17 +47,27 @@ use std::sync::{Arc, RwLock};
|
||||||
pub(crate) enum AppMessage {
|
pub(crate) enum AppMessage {
|
||||||
/// Copy recived image buffer from draw_thread to Buffer for fltk frame
|
/// Copy recived image buffer from draw_thread to Buffer for fltk frame
|
||||||
RedrawMainWindowImage(Option<Vec<u8>>),
|
RedrawMainWindowImage(Option<Vec<u8>>),
|
||||||
|
Message(String),
|
||||||
|
Alert(String),
|
||||||
|
ProgramPanicMessage(String),
|
||||||
|
|
||||||
|
// Only for Main windows
|
||||||
|
DeleteImage,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let app = App::default();
|
let app = App::default();
|
||||||
WidgetTheme::new(globals::THEME.clone().into()).apply();
|
WidgetTheme::new(globals::THEME.clone().into()).apply();
|
||||||
|
|
||||||
if let Err(e) = CombinedLogger::init(vec![WriteLogger::new(
|
if let Err(e) = CombinedLogger::init(vec![
|
||||||
|
WriteLogger::new(LevelFilter::Warn, Config::default(), config::log_file()),
|
||||||
|
TermLogger::new(
|
||||||
LevelFilter::Info,
|
LevelFilter::Info,
|
||||||
Config::default(),
|
Config::default(),
|
||||||
config::log_file(),
|
TerminalMode::Mixed,
|
||||||
)]) {
|
ColorChoice::Auto,
|
||||||
|
),
|
||||||
|
]) {
|
||||||
dialog::alert_default("Failed to start logger");
|
dialog::alert_default("Failed to start logger");
|
||||||
panic!("Failed to start logger\n{:?}", e);
|
panic!("Failed to start logger\n{:?}", e);
|
||||||
}
|
}
|
||||||
|
|
@ -62,16 +78,41 @@ fn main() {
|
||||||
let draw_buff: Arc<RwLock<Option<Vec<u8>>>> = Arc::new(RwLock::new(None));
|
let draw_buff: Arc<RwLock<Option<Vec<u8>>>> = Arc::new(RwLock::new(None));
|
||||||
|
|
||||||
let (main_sender, main_receiver) = channel::<AppMessage>();
|
let (main_sender, main_receiver) = channel::<AppMessage>();
|
||||||
let mut main_window = MainWindow::new(main_sender, Arc::clone(&draw_buff));
|
*rw_write!(globals::MAIN_SENDER) = Some(main_sender);
|
||||||
|
let mut main_window = MainWindow::new(Arc::clone(&draw_buff));
|
||||||
|
|
||||||
while app.wait() {
|
while app.wait() {
|
||||||
if let Some(msg) = main_receiver.recv() {
|
if let Some(msg) = main_receiver.recv() {
|
||||||
match msg {
|
match msg {
|
||||||
AppMessage::RedrawMainWindowImage(data) => {
|
AppMessage::RedrawMainWindowImage(data) => {
|
||||||
let mut buff = draw_buff.write().unwrap();
|
let mut buff = rw_write!(draw_buff);
|
||||||
*buff = data;
|
*buff = data;
|
||||||
main_window.win.redraw();
|
main_window.win.redraw();
|
||||||
}
|
}
|
||||||
|
AppMessage::Message(msg) => {
|
||||||
|
dialog::message_default(&msg);
|
||||||
|
}
|
||||||
|
AppMessage::Alert(msg) => dialog::alert_default(&msg),
|
||||||
|
AppMessage::ProgramPanicMessage(msg) => {
|
||||||
|
dialog::message_default(&msg);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
// Fltk does not show dialogs from other thread in windows, So this is hack to get things done
|
||||||
|
AppMessage::DeleteImage => {
|
||||||
|
let ch = dialog::choice_default("Image is too small", "Delete", "Keep");
|
||||||
|
if ch == 0 {
|
||||||
|
main_window
|
||||||
|
.sender
|
||||||
|
.send(draw_thread::DrawMessage::Delete)
|
||||||
|
.unwrap();
|
||||||
|
main_window
|
||||||
|
.sender
|
||||||
|
.send(draw_thread::DrawMessage::Open)
|
||||||
|
.unwrap();
|
||||||
|
main_window.page.image.redraw();
|
||||||
|
main_window.file_choice.redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//! Main window where you do all editing
|
//! Main window where you do all editing
|
||||||
use crate::about::About;
|
use crate::{
|
||||||
use crate::crop_window::CropWindow;
|
about_window::About,
|
||||||
use crate::draw_thread::*;
|
config_window::ConfigWindow,
|
||||||
use crate::utils;
|
crop_window::CropWindow,
|
||||||
use crate::utils::ImageProperties;
|
|
||||||
use crate::{config_window::ConfigWindow, globals};
|
|
||||||
use fltk::{
|
|
||||||
app,
|
|
||||||
button::Button,
|
|
||||||
dialog,
|
dialog,
|
||||||
|
draw_thread::*,
|
||||||
|
export_all_window::ExportAllWindow,
|
||||||
|
globals,
|
||||||
|
result_ext::ResultExt,
|
||||||
|
utils::{self, ImageInfo, ImageProperties, ImageType},
|
||||||
|
};
|
||||||
|
use fltk::{
|
||||||
|
button::Button,
|
||||||
dialog::NativeFileChooser,
|
dialog::NativeFileChooser,
|
||||||
draw as dr, enums,
|
draw as dr, enums,
|
||||||
enums::Shortcut,
|
enums::Shortcut,
|
||||||
|
|
@ -36,9 +39,14 @@ use fltk::{
|
||||||
valuator::{Slider, SliderType},
|
valuator::{Slider, SliderType},
|
||||||
window::Window,
|
window::Window,
|
||||||
};
|
};
|
||||||
use std::path::PathBuf;
|
use std::{
|
||||||
use std::sync::{mpsc, RwLock};
|
ffi::OsStr,
|
||||||
use std::{ffi::OsStr, fs, sync::Arc};
|
fs,
|
||||||
|
path::PathBuf,
|
||||||
|
process::Command,
|
||||||
|
sync::Arc,
|
||||||
|
sync::{mpsc, RwLock},
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) struct MainWindow {
|
pub(crate) struct MainWindow {
|
||||||
pub(crate) win: Window,
|
pub(crate) win: Window,
|
||||||
|
|
@ -48,6 +56,7 @@ pub(crate) struct MainWindow {
|
||||||
pub(crate) save_btn: Button,
|
pub(crate) save_btn: Button,
|
||||||
/// To choose the file which is being edited in directory
|
/// To choose the file which is being edited in directory
|
||||||
pub(crate) file_choice: menu::Choice,
|
pub(crate) file_choice: menu::Choice,
|
||||||
|
pub(crate) name_prefix: Input,
|
||||||
pub(crate) quote: MultilineInput,
|
pub(crate) quote: MultilineInput,
|
||||||
pub(crate) subquote: MultilineInput,
|
pub(crate) subquote: MultilineInput,
|
||||||
pub(crate) subquote2: MultilineInput,
|
pub(crate) subquote2: MultilineInput,
|
||||||
|
|
@ -81,7 +90,7 @@ pub(crate) struct MainWindow {
|
||||||
pub(crate) count: Frame,
|
pub(crate) count: Frame,
|
||||||
pub(crate) dimension: Frame,
|
pub(crate) dimension: Frame,
|
||||||
pub(crate) page: Page,
|
pub(crate) page: Page,
|
||||||
pub(crate) images_path: Arc<RwLock<Vec<PathBuf>>>,
|
pub(crate) images_list: Arc<RwLock<Vec<ImageInfo>>>,
|
||||||
pub(crate) draw_buff: Arc<RwLock<Option<Vec<u8>>>>,
|
pub(crate) draw_buff: Arc<RwLock<Option<Vec<u8>>>>,
|
||||||
pub(crate) properties: Arc<RwLock<ImageProperties>>,
|
pub(crate) properties: Arc<RwLock<ImageProperties>>,
|
||||||
pub(crate) sender: mpsc::Sender<DrawMessage>,
|
pub(crate) sender: mpsc::Sender<DrawMessage>,
|
||||||
|
|
@ -96,10 +105,7 @@ pub(crate) struct Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MainWindow {
|
impl MainWindow {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(draw_buff: Arc<RwLock<Option<Vec<u8>>>>) -> Self {
|
||||||
sender: app::Sender<crate::AppMessage>,
|
|
||||||
draw_buff: Arc<RwLock<Option<Vec<u8>>>>,
|
|
||||||
) -> Self {
|
|
||||||
let mut win = Window::new(0, 0, 1100, 700, "Post Maker").center_screen();
|
let mut win = Window::new(0, 0, 1100, 700, "Post Maker").center_screen();
|
||||||
win.set_icon(Some(
|
win.set_icon(Some(
|
||||||
SvgImage::from_data(globals::ICON.to_str().unwrap()).unwrap(),
|
SvgImage::from_data(globals::ICON.to_str().unwrap()).unwrap(),
|
||||||
|
|
@ -128,6 +134,15 @@ impl MainWindow {
|
||||||
let mut workspace_flex = Flex::default().row();
|
let mut workspace_flex = Flex::default().row();
|
||||||
// Controls Left
|
// Controls Left
|
||||||
let mut left_controls_flex = Flex::default().column();
|
let mut left_controls_flex = Flex::default().column();
|
||||||
|
left_controls_flex.set_size(
|
||||||
|
&Frame::default()
|
||||||
|
.with_label("Name Prefix:")
|
||||||
|
.with_align(enums::Align::Left | enums::Align::Inside),
|
||||||
|
25,
|
||||||
|
);
|
||||||
|
let name_prefix = Input::default();
|
||||||
|
left_controls_flex.set_size(&name_prefix, 30);
|
||||||
|
|
||||||
left_controls_flex.set_size(
|
left_controls_flex.set_size(
|
||||||
&Frame::default()
|
&Frame::default()
|
||||||
.with_label("Quote:")
|
.with_label("Quote:")
|
||||||
|
|
@ -350,6 +365,7 @@ impl MainWindow {
|
||||||
next_btn,
|
next_btn,
|
||||||
save_btn,
|
save_btn,
|
||||||
file_choice,
|
file_choice,
|
||||||
|
name_prefix,
|
||||||
quote,
|
quote,
|
||||||
subquote,
|
subquote,
|
||||||
subquote2,
|
subquote2,
|
||||||
|
|
@ -380,7 +396,7 @@ impl MainWindow {
|
||||||
status,
|
status,
|
||||||
count,
|
count,
|
||||||
dimension,
|
dimension,
|
||||||
images_path: Arc::new(RwLock::new(vec![])),
|
images_list: Arc::new(RwLock::new(vec![])),
|
||||||
draw_buff,
|
draw_buff,
|
||||||
properties: Arc::clone(&properties),
|
properties: Arc::clone(&properties),
|
||||||
page: Page {
|
page: Page {
|
||||||
|
|
@ -390,7 +406,10 @@ impl MainWindow {
|
||||||
},
|
},
|
||||||
sender: rx,
|
sender: rx,
|
||||||
};
|
};
|
||||||
spawn_image_thread(tx, sender, Arc::clone(&properties), &main_win);
|
|
||||||
|
if let Some(a) = &*rw_read!(globals::MAIN_SENDER) {
|
||||||
|
spawn_image_thread(tx, a.to_owned(), Arc::clone(&properties), &main_win);
|
||||||
|
}
|
||||||
main_win.menu();
|
main_win.menu();
|
||||||
main_win.draw();
|
main_win.draw();
|
||||||
main_win.events();
|
main_win.events();
|
||||||
|
|
@ -401,7 +420,8 @@ impl MainWindow {
|
||||||
fn menu(&mut self) {
|
fn menu(&mut self) {
|
||||||
let mut file_choice = self.file_choice.clone();
|
let mut file_choice = self.file_choice.clone();
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let imgs = Arc::clone(&self.images_path);
|
let imgs = Arc::clone(&self.images_list);
|
||||||
|
let mut win = self.win.clone();
|
||||||
self.menubar.add(
|
self.menubar.add(
|
||||||
"&File/Open Folder...\t",
|
"&File/Open Folder...\t",
|
||||||
Shortcut::Ctrl | 'o',
|
Shortcut::Ctrl | 'o',
|
||||||
|
|
@ -415,14 +435,12 @@ impl MainWindow {
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let expost_dir = path.join("export");
|
win.set_label(&format!(
|
||||||
if !expost_dir.exists() {
|
"{} - Post Maker",
|
||||||
if let Err(e) = fs::create_dir(expost_dir) {
|
path.file_name()
|
||||||
fltk::dialog::alert_default("Failed to create export folder!");
|
.unwrap_or(OsStr::new("Unknown"))
|
||||||
warn!("Failed to create export folder!\n{:?}", e);
|
.to_string_lossy()
|
||||||
return;
|
));
|
||||||
}
|
|
||||||
}
|
|
||||||
load_dir(&path, Arc::clone(&imgs), &mut file_choice, &sender);
|
load_dir(&path, Arc::clone(&imgs), &mut file_choice, &sender);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -430,13 +448,93 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let properties = Arc::clone(&self.properties);
|
let properties = Arc::clone(&self.properties);
|
||||||
self.menubar.add(
|
self.menubar.add(
|
||||||
"&File/Save...\t",
|
"&File/Save Image...\t",
|
||||||
Shortcut::Ctrl | 's',
|
Shortcut::Ctrl | 's',
|
||||||
menu::MenuFlag::Normal,
|
menu::MenuFlag::Normal,
|
||||||
move |_| {
|
move |_| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.is_saved = true;
|
prop.is_saved = true;
|
||||||
sender.send(DrawMessage::Save).unwrap();
|
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();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -449,8 +547,8 @@ impl MainWindow {
|
||||||
menu::MenuFlag::Normal,
|
menu::MenuFlag::Normal,
|
||||||
move |_| {
|
move |_| {
|
||||||
if config_window.show() {
|
if config_window.show() {
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -472,8 +570,8 @@ impl MainWindow {
|
||||||
let buff = Arc::clone(&self.draw_buff);
|
let buff = Arc::clone(&self.draw_buff);
|
||||||
let properties = Arc::clone(&self.properties);
|
let properties = Arc::clone(&self.properties);
|
||||||
self.page.image.draw(move |f| {
|
self.page.image.draw(move |f| {
|
||||||
let (width, height) = properties.read().unwrap().dimension;
|
let (width, height) = rw_read!(properties).dimension;
|
||||||
if let Some(image) = &*buff.read().unwrap() {
|
if let Some(image) = &*rw_read!(buff) {
|
||||||
dr::draw_image(
|
dr::draw_image(
|
||||||
&image,
|
&image,
|
||||||
f.x(),
|
f.x(),
|
||||||
|
|
@ -492,10 +590,10 @@ impl MainWindow {
|
||||||
// Resest Button for FileChoice
|
// Resest Button for FileChoice
|
||||||
let mut file_choice = self.file_choice.clone();
|
let mut file_choice = self.file_choice.clone();
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let imgs = Arc::clone(&self.images_path);
|
let imgs = Arc::clone(&self.images_list);
|
||||||
self.reset_file_choice.set_callback(move |_| {
|
self.reset_file_choice.set_callback(move |_| {
|
||||||
let path = match imgs.read().unwrap().first() {
|
let path = match rw_read!(imgs).first() {
|
||||||
Some(path) => path.parent().unwrap().to_path_buf(),
|
Some(image_info) => image_info.path.parent().unwrap().to_path_buf(),
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
load_dir(&path, Arc::clone(&imgs), &mut file_choice, &sender);
|
load_dir(&path, Arc::clone(&imgs), &mut file_choice, &sender);
|
||||||
|
|
@ -508,14 +606,14 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let properties = Arc::clone(&self.properties);
|
let properties = Arc::clone(&self.properties);
|
||||||
self.reset_translucent_layer_btn.set_callback(move |_| {
|
self.reset_translucent_layer_btn.set_callback(move |_| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
let color = globals::CONFIG.read().unwrap().color_layer;
|
let color = rw_read!(globals::CONFIG).color_layer;
|
||||||
prop.translucent_layer_color = color;
|
prop.translucent_layer_color = color;
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
utils::set_color_btn_rgba(color, &mut layer_rgb);
|
utils::set_color_btn_rgba(color, &mut layer_rgb);
|
||||||
layer_alpha.set_value(color[3] as f64);
|
layer_alpha.set_value(color[3] as f64);
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -526,16 +624,16 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let properties = Arc::clone(&self.properties);
|
let properties = Arc::clone(&self.properties);
|
||||||
self.reset_quote_position_btn.set_callback(move |_| {
|
self.reset_quote_position_btn.set_callback(move |_| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
let height = prop.original_dimension.1;
|
let height = prop.original_dimension.1;
|
||||||
let pos = height * globals::CONFIG.read().unwrap().quote_position_ratio;
|
let pos = height * rw_read!(globals::CONFIG).quote_position_ratio;
|
||||||
prop.quote_position = pos;
|
prop.quote_position = pos;
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
quote_position.set_value(pos);
|
quote_position.set_value(pos);
|
||||||
quote_position_slider.set_value(pos);
|
quote_position_slider.set_value(pos);
|
||||||
|
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -546,16 +644,16 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let properties = Arc::clone(&self.properties);
|
let properties = Arc::clone(&self.properties);
|
||||||
self.reset_subquote_position_btn.set_callback(move |_| {
|
self.reset_subquote_position_btn.set_callback(move |_| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
let height = prop.original_dimension.1;
|
let height = prop.original_dimension.1;
|
||||||
let pos = height * globals::CONFIG.read().unwrap().subquote_position_ratio;
|
let pos = height * rw_read!(globals::CONFIG).subquote_position_ratio;
|
||||||
prop.subquote_position = pos;
|
prop.subquote_position = pos;
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
subquote_position.set_value(pos);
|
subquote_position.set_value(pos);
|
||||||
subquote_position_slider.set_value(pos);
|
subquote_position_slider.set_value(pos);
|
||||||
|
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -566,16 +664,16 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let properties = Arc::clone(&self.properties);
|
let properties = Arc::clone(&self.properties);
|
||||||
self.reset_subquote2_position_btn.set_callback(move |_| {
|
self.reset_subquote2_position_btn.set_callback(move |_| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
let height = prop.original_dimension.1;
|
let height = prop.original_dimension.1;
|
||||||
let pos = height * globals::CONFIG.read().unwrap().subquote2_position_ratio;
|
let pos = height * rw_read!(globals::CONFIG).subquote2_position_ratio;
|
||||||
prop.subquote2_position = pos;
|
prop.subquote2_position = pos;
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
subquote2_position.set_value(pos);
|
subquote2_position.set_value(pos);
|
||||||
subquote2_position_slider.set_value(pos);
|
subquote2_position_slider.set_value(pos);
|
||||||
|
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -586,16 +684,16 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let properties = Arc::clone(&self.properties);
|
let properties = Arc::clone(&self.properties);
|
||||||
self.reset_tag_position_btn.set_callback(move |_| {
|
self.reset_tag_position_btn.set_callback(move |_| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
let height = prop.original_dimension.1;
|
let height = prop.original_dimension.1;
|
||||||
let pos = height * globals::CONFIG.read().unwrap().tag_position_ratio;
|
let pos = height * rw_read!(globals::CONFIG).tag_y_position_ratio;
|
||||||
prop.tag_position = pos;
|
prop.tag_position = pos;
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
tag_position.set_value(pos);
|
tag_position.set_value(pos);
|
||||||
tag_position_slider.set_value(pos);
|
tag_position_slider.set_value(pos);
|
||||||
|
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -606,16 +704,16 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let properties = Arc::clone(&self.properties);
|
let properties = Arc::clone(&self.properties);
|
||||||
self.reset_tag2_position_btn.set_callback(move |_| {
|
self.reset_tag2_position_btn.set_callback(move |_| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
let height = prop.original_dimension.1;
|
let height = prop.original_dimension.1;
|
||||||
let pos = height * globals::CONFIG.read().unwrap().tag2_position_ratio;
|
let pos = height * rw_read!(globals::CONFIG).tag2_position_ratio;
|
||||||
prop.tag2_position = pos;
|
prop.tag2_position = pos;
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
tag2_position.set_value(pos);
|
tag2_position.set_value(pos);
|
||||||
tag2_position_slider.set_value(pos);
|
tag2_position_slider.set_value(pos);
|
||||||
|
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -623,9 +721,9 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let properties = Arc::clone(&self.properties);
|
let properties = Arc::clone(&self.properties);
|
||||||
self.save_btn.set_callback(move |_| {
|
self.save_btn.set_callback(move |_| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.is_saved = true;
|
prop.is_saved = true;
|
||||||
sender.send(DrawMessage::Save).unwrap()
|
sender.send_it(DrawMessage::Save);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Clone Button
|
// Clone Button
|
||||||
|
|
@ -633,10 +731,11 @@ impl MainWindow {
|
||||||
let mut file_choice = self.file_choice.clone();
|
let mut file_choice = self.file_choice.clone();
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
self.clone_btn.set_callback(move |_| {
|
self.clone_btn.set_callback(move |_| {
|
||||||
let ch = dialog::choice_default("Do you want to clone??", "Yes", "No", "");
|
let ch = dialog::choice_default("Do you want to clone??", "Yes", "No");
|
||||||
if ch == 0 {
|
if ch == 0 {
|
||||||
sender.send(DrawMessage::Clone).unwrap();
|
sender.send_it(DrawMessage::Clone);
|
||||||
sender.send(DrawMessage::Open).unwrap();
|
sender.send_it(DrawMessage::Open);
|
||||||
|
sender.send_it(DrawMessage::CheckImage);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
file_choice.redraw();
|
file_choice.redraw();
|
||||||
}
|
}
|
||||||
|
|
@ -647,10 +746,11 @@ impl MainWindow {
|
||||||
let mut file_choice = self.file_choice.clone();
|
let mut file_choice = self.file_choice.clone();
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
self.delete_btn.set_callback(move |_| {
|
self.delete_btn.set_callback(move |_| {
|
||||||
let ch = dialog::choice_default("Do you want to delete??", "Yes", "No", "");
|
let ch = dialog::choice_default("Do you want to delete??", "Yes", "No");
|
||||||
if ch == 0 {
|
if ch == 0 {
|
||||||
sender.send(DrawMessage::Delete).unwrap();
|
sender.send_it(DrawMessage::Delete);
|
||||||
sender.send(DrawMessage::Open).unwrap();
|
sender.send_it(DrawMessage::Open);
|
||||||
|
sender.send_it(DrawMessage::CheckImage);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
file_choice.redraw();
|
file_choice.redraw();
|
||||||
}
|
}
|
||||||
|
|
@ -661,10 +761,10 @@ impl MainWindow {
|
||||||
let mut crop_win = CropWindow::new();
|
let mut crop_win = CropWindow::new();
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
self.crop_btn.set_callback(move |_| {
|
self.crop_btn.set_callback(move |_| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
if let Some(path) = &prop.path {
|
if let Some(image_info) = &prop.image_info {
|
||||||
if let Some((x, y)) = crop_win.load_to_crop(path, prop.crop_position) {
|
if let Some((x, y)) = crop_win.load_to_crop(&image_info, prop.crop_position) {
|
||||||
sender.send(DrawMessage::ChangeCrop((x, y))).unwrap();
|
sender.send_it(DrawMessage::ChangeCrop((x, y)));
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -675,11 +775,11 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let properties = Arc::clone(&self.properties);
|
let properties = Arc::clone(&self.properties);
|
||||||
self.next_btn.set_callback(move |_| {
|
self.next_btn.set_callback(move |_| {
|
||||||
let prop = properties.read().unwrap();
|
let prop = rw_read!(properties);
|
||||||
if !prop.is_saved {
|
if !prop.is_saved {
|
||||||
let save = fltk::dialog::choice_default("Save?", "yes", "no", "cancel");
|
let save = fltk::dialog::choice2_default("Save?", "yes", "no", "cancel");
|
||||||
match save {
|
match save.unwrap_or(-1) {
|
||||||
0 => sender.send(DrawMessage::Save).unwrap(),
|
0 => sender.send_it(DrawMessage::Save),
|
||||||
1 => {}
|
1 => {}
|
||||||
_ => return,
|
_ => return,
|
||||||
}
|
}
|
||||||
|
|
@ -690,7 +790,8 @@ impl MainWindow {
|
||||||
} else {
|
} else {
|
||||||
file_choice.set_value(file_choice.value() + 1);
|
file_choice.set_value(file_choice.value() + 1);
|
||||||
}
|
}
|
||||||
sender.send(DrawMessage::Open).unwrap();
|
sender.send_it(DrawMessage::Open);
|
||||||
|
sender.send_it(DrawMessage::CheckImage);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Back Image Button
|
// Back Image Button
|
||||||
|
|
@ -698,11 +799,11 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let properties = Arc::clone(&self.properties);
|
let properties = Arc::clone(&self.properties);
|
||||||
self.back_btn.set_callback(move |_| {
|
self.back_btn.set_callback(move |_| {
|
||||||
let prop = properties.read().unwrap();
|
let prop = rw_read!(properties);
|
||||||
if !prop.is_saved {
|
if !prop.is_saved {
|
||||||
let save = fltk::dialog::choice_default("Save?", "yes", "no", "cancel");
|
let save = fltk::dialog::choice2_default("Save?", "yes", "no", "cancel");
|
||||||
match save {
|
match save.unwrap_or(-1) {
|
||||||
0 => sender.send(DrawMessage::Save).unwrap(),
|
0 => sender.send_it(DrawMessage::Save),
|
||||||
1 => {}
|
1 => {}
|
||||||
_ => return,
|
_ => return,
|
||||||
}
|
}
|
||||||
|
|
@ -713,23 +814,36 @@ impl MainWindow {
|
||||||
} else {
|
} else {
|
||||||
file_choice.set_value(file_choice.value() - 1);
|
file_choice.set_value(file_choice.value() - 1);
|
||||||
}
|
}
|
||||||
sender.send(DrawMessage::Open).unwrap();
|
sender.send_it(DrawMessage::Open);
|
||||||
|
sender.send_it(DrawMessage::CheckImage);
|
||||||
});
|
});
|
||||||
|
|
||||||
// File Choice
|
// File Choice
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let properties = Arc::clone(&self.properties);
|
let properties = Arc::clone(&self.properties);
|
||||||
self.file_choice.set_callback(move |_| {
|
self.file_choice.set_callback(move |_| {
|
||||||
let prop = properties.read().unwrap();
|
let prop = rw_read!(properties);
|
||||||
if !prop.is_saved {
|
if !prop.is_saved {
|
||||||
let save = fltk::dialog::choice_default("Save?", "yes", "no", "cancel");
|
let save = fltk::dialog::choice2_default("Save?", "yes", "no", "cancel");
|
||||||
match save {
|
match save.unwrap_or(-1) {
|
||||||
0 => sender.send(DrawMessage::Save).unwrap(),
|
0 => sender.send_it(DrawMessage::Save),
|
||||||
1 => {}
|
1 => {}
|
||||||
_ => return,
|
_ => return,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sender.send(DrawMessage::Open).unwrap();
|
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
|
||||||
});
|
});
|
||||||
|
|
||||||
// Quote Input
|
// Quote Input
|
||||||
|
|
@ -738,11 +852,11 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
self.quote.handle(move |f, ev| {
|
self.quote.handle(move |f, ev| {
|
||||||
if ev == enums::Event::KeyUp {
|
if ev == enums::Event::KeyUp {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.quote = f.value();
|
prop.quote = f.value();
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|
@ -754,11 +868,11 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
self.subquote.handle(move |f, ev| {
|
self.subquote.handle(move |f, ev| {
|
||||||
if ev == enums::Event::KeyUp {
|
if ev == enums::Event::KeyUp {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.subquote = f.value();
|
prop.subquote = f.value();
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|
@ -770,11 +884,11 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
self.subquote2.handle(move |f, ev| {
|
self.subquote2.handle(move |f, ev| {
|
||||||
if ev == enums::Event::KeyUp {
|
if ev == enums::Event::KeyUp {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.subquote2 = f.value();
|
prop.subquote2 = f.value();
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|
@ -786,11 +900,11 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
self.tag.handle(move |f, ev| {
|
self.tag.handle(move |f, ev| {
|
||||||
if ev == enums::Event::KeyUp {
|
if ev == enums::Event::KeyUp {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.tag = f.value();
|
prop.tag = f.value();
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|
@ -802,11 +916,11 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
self.tag2.handle(move |f, ev| {
|
self.tag2.handle(move |f, ev| {
|
||||||
if ev == enums::Event::KeyUp {
|
if ev == enums::Event::KeyUp {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.tag2 = f.value();
|
prop.tag2 = f.value();
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|
@ -818,12 +932,12 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let mut quote_position_slider = self.quote_position_slider.clone();
|
let mut quote_position_slider = self.quote_position_slider.clone();
|
||||||
self.quote_position.set_callback(move |f| {
|
self.quote_position.set_callback(move |f| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.quote_position = f.value();
|
prop.quote_position = f.value();
|
||||||
quote_position_slider.set_value(f.value());
|
quote_position_slider.set_value(f.value());
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -833,12 +947,12 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let mut quote_position = self.quote_position.clone();
|
let mut quote_position = self.quote_position.clone();
|
||||||
self.quote_position_slider.set_callback(move |f| {
|
self.quote_position_slider.set_callback(move |f| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.quote_position = f.value();
|
prop.quote_position = f.value();
|
||||||
quote_position.set_value(f.value());
|
quote_position.set_value(f.value());
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -848,12 +962,12 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let mut subquote_position_slider = self.subquote_position_slider.clone();
|
let mut subquote_position_slider = self.subquote_position_slider.clone();
|
||||||
self.subquote_position.set_callback(move |f| {
|
self.subquote_position.set_callback(move |f| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.subquote_position = f.value();
|
prop.subquote_position = f.value();
|
||||||
subquote_position_slider.set_value(f.value());
|
subquote_position_slider.set_value(f.value());
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -863,12 +977,12 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let mut subquote_position = self.subquote_position.clone();
|
let mut subquote_position = self.subquote_position.clone();
|
||||||
self.subquote_position_slider.set_callback(move |f| {
|
self.subquote_position_slider.set_callback(move |f| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.subquote_position = f.value();
|
prop.subquote_position = f.value();
|
||||||
subquote_position.set_value(f.value());
|
subquote_position.set_value(f.value());
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -878,12 +992,12 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let mut subquote2_position_slider = self.subquote2_position_slider.clone();
|
let mut subquote2_position_slider = self.subquote2_position_slider.clone();
|
||||||
self.subquote2_position.set_callback(move |f| {
|
self.subquote2_position.set_callback(move |f| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.subquote2_position = f.value();
|
prop.subquote2_position = f.value();
|
||||||
subquote2_position_slider.set_value(f.value());
|
subquote2_position_slider.set_value(f.value());
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -893,12 +1007,12 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let mut subquote2_position = self.subquote2_position.clone();
|
let mut subquote2_position = self.subquote2_position.clone();
|
||||||
self.subquote2_position_slider.set_callback(move |f| {
|
self.subquote2_position_slider.set_callback(move |f| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.subquote2_position = f.value();
|
prop.subquote2_position = f.value();
|
||||||
subquote2_position.set_value(f.value());
|
subquote2_position.set_value(f.value());
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -908,12 +1022,12 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let mut tag_position_slider = self.tag_position_slider.clone();
|
let mut tag_position_slider = self.tag_position_slider.clone();
|
||||||
self.tag_position.set_callback(move |f| {
|
self.tag_position.set_callback(move |f| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.tag_position = f.value();
|
prop.tag_position = f.value();
|
||||||
tag_position_slider.set_value(f.value());
|
tag_position_slider.set_value(f.value());
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -923,12 +1037,12 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let mut tag_position = self.tag_position.clone();
|
let mut tag_position = self.tag_position.clone();
|
||||||
self.tag_position_slider.set_callback(move |f| {
|
self.tag_position_slider.set_callback(move |f| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.tag_position = f.value();
|
prop.tag_position = f.value();
|
||||||
tag_position.set_value(f.value());
|
tag_position.set_value(f.value());
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -938,12 +1052,12 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let mut tag2_position_slider = self.tag2_position_slider.clone();
|
let mut tag2_position_slider = self.tag2_position_slider.clone();
|
||||||
self.tag2_position.set_callback(move |f| {
|
self.tag2_position.set_callback(move |f| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.tag2_position = f.value();
|
prop.tag2_position = f.value();
|
||||||
tag2_position_slider.set_value(f.value());
|
tag2_position_slider.set_value(f.value());
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -953,12 +1067,12 @@ impl MainWindow {
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
let mut tag2_position = self.tag2_position.clone();
|
let mut tag2_position = self.tag2_position.clone();
|
||||||
self.tag2_position_slider.set_callback(move |f| {
|
self.tag2_position_slider.set_callback(move |f| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.tag2_position = f.value();
|
prop.tag2_position = f.value();
|
||||||
tag2_position.set_value(f.value());
|
tag2_position.set_value(f.value());
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -967,7 +1081,7 @@ impl MainWindow {
|
||||||
let properties = Arc::clone(&self.properties);
|
let properties = Arc::clone(&self.properties);
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
self.translucent_layer_rgb.set_callback(move |mut f| {
|
self.translucent_layer_rgb.set_callback(move |mut f| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
let (r, g, b) = dialog::color_chooser_with_default(
|
let (r, g, b) = dialog::color_chooser_with_default(
|
||||||
"Pick a colour",
|
"Pick a colour",
|
||||||
dialog::ColorMode::Byte,
|
dialog::ColorMode::Byte,
|
||||||
|
|
@ -981,8 +1095,8 @@ impl MainWindow {
|
||||||
utils::set_color_btn_rgba(prop.translucent_layer_color, &mut f);
|
utils::set_color_btn_rgba(prop.translucent_layer_color, &mut f);
|
||||||
f.redraw();
|
f.redraw();
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -991,11 +1105,11 @@ impl MainWindow {
|
||||||
let properties = Arc::clone(&self.properties);
|
let properties = Arc::clone(&self.properties);
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
self.translucent_layer_alpha.set_callback(move |f| {
|
self.translucent_layer_alpha.set_callback(move |f| {
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.translucent_layer_color[3] = f.value() as u8;
|
prop.translucent_layer_color[3] = f.value() as u8;
|
||||||
prop.is_saved = false;
|
prop.is_saved = false;
|
||||||
sender.send(DrawMessage::RedrawToBuffer).unwrap();
|
sender.send_it(DrawMessage::RedrawToBuffer);
|
||||||
sender.send(DrawMessage::Flush).unwrap();
|
sender.send_it(DrawMessage::Flush);
|
||||||
image.redraw();
|
image.redraw();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1004,7 +1118,7 @@ impl MainWindow {
|
||||||
/// Load all iamges in a directory
|
/// Load all iamges in a directory
|
||||||
fn load_dir(
|
fn load_dir(
|
||||||
path: &PathBuf,
|
path: &PathBuf,
|
||||||
imgs: Arc<RwLock<Vec<PathBuf>>>,
|
imgs: Arc<RwLock<Vec<ImageInfo>>>,
|
||||||
file_choice: &mut menu::Choice,
|
file_choice: &mut menu::Choice,
|
||||||
sender: &mpsc::Sender<DrawMessage>,
|
sender: &mpsc::Sender<DrawMessage>,
|
||||||
) {
|
) {
|
||||||
|
|
@ -1014,15 +1128,22 @@ fn load_dir(
|
||||||
.collect::<Vec<fs::DirEntry>>();
|
.collect::<Vec<fs::DirEntry>>();
|
||||||
files.sort_by_key(|i| i.file_name());
|
files.sort_by_key(|i| i.file_name());
|
||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
let mut imgs_b = imgs.write().unwrap();
|
let mut imgs_b = rw_write!(imgs);
|
||||||
*imgs_b = vec![];
|
*imgs_b = vec![];
|
||||||
for file in files {
|
for file in files {
|
||||||
let path = file.path();
|
let path = file.path();
|
||||||
if path.extension() == Some(OsStr::new("jpg"))
|
if let Ok(Some(ty)) = infer::get_from_path(&path) {
|
||||||
|| path.extension() == Some(OsStr::new("png"))
|
let mime = ty.mime_type();
|
||||||
{
|
match ImageType::from_mime(mime) {
|
||||||
|
ImageType::None => (),
|
||||||
|
_ => {
|
||||||
text = format!("{}|{}", text, path.file_name().unwrap().to_str().unwrap());
|
text = format!("{}|{}", text, path.file_name().unwrap().to_str().unwrap());
|
||||||
imgs_b.push(path);
|
imgs_b.push(ImageInfo {
|
||||||
|
path,
|
||||||
|
image_type: ImageType::from_mime(mime),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if text.len() == 0 {
|
if text.len() == 0 {
|
||||||
|
|
@ -1031,5 +1152,16 @@ fn load_dir(
|
||||||
file_choice.clear();
|
file_choice.clear();
|
||||||
file_choice.add_choice(&text[1..]);
|
file_choice.add_choice(&text[1..]);
|
||||||
file_choice.set_value(0);
|
file_choice.set_value(0);
|
||||||
sender.send(DrawMessage::Open).unwrap();
|
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!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
603
src/utils.rs
603
src/utils.rs
|
|
@ -13,16 +13,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs::{self, File},
|
||||||
|
io::Read,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
use fltk::{button::Button, dialog, enums, prelude::*};
|
use fltk::{button::Button, enums, prelude::*};
|
||||||
use image::{DynamicImage, GenericImageView, ImageBuffer};
|
use image::{DynamicImage, GenericImageView, ImageBuffer, ImageEncoder, Pixel};
|
||||||
|
use imageproc::rect::Rect;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::globals;
|
use crate::globals;
|
||||||
|
use crate::result_ext::ResultExt;
|
||||||
|
|
||||||
/// helps cast tupels to f64
|
/// helps cast tupels to f64
|
||||||
pub(crate) struct Coord(pub(crate) f64, pub(crate) f64);
|
pub(crate) struct Coord(pub(crate) f64, pub(crate) f64);
|
||||||
|
|
@ -63,6 +66,47 @@ impl Into<(i32, i32)> for Coord {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Into<(usize, usize)> for Coord {
|
||||||
|
fn into(self) -> (usize, usize) {
|
||||||
|
(self.0 as usize, self.1 as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub(crate) struct ImageInfo {
|
||||||
|
pub(crate) path: PathBuf,
|
||||||
|
pub(crate) image_type: ImageType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub(crate) enum ImageType {
|
||||||
|
Jpeg,
|
||||||
|
Png,
|
||||||
|
Webp,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageType {
|
||||||
|
pub(crate) fn from_mime(v: &str) -> Self {
|
||||||
|
match v {
|
||||||
|
"image/jpeg" | "image/jpg" => Self::Jpeg,
|
||||||
|
"image/png" => Self::Png,
|
||||||
|
"image/webp" => Self::Webp,
|
||||||
|
_ => Self::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_extension(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::Jpeg => "jpg",
|
||||||
|
Self::Png => "png",
|
||||||
|
Self::Webp => "webp",
|
||||||
|
Self::None => "none",
|
||||||
|
}
|
||||||
|
.to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Contains Image and its buffer(edited image)
|
/// Contains Image and its buffer(edited image)
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct ImageContainer {
|
pub(crate) struct ImageContainer {
|
||||||
|
|
@ -72,27 +116,18 @@ pub(crate) struct ImageContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageContainer {
|
impl ImageContainer {
|
||||||
pub(crate) fn new(path: &PathBuf, properties: Arc<RwLock<ImageProperties>>) -> Self {
|
pub(crate) fn new(image_info: &ImageInfo, properties: Arc<RwLock<ImageProperties>>) -> Self {
|
||||||
let img = match image::open(path) {
|
let img = load_image(&image_info);
|
||||||
Ok(i) => i,
|
|
||||||
Err(e) => {
|
|
||||||
dialog::alert_default("Failed to open image!");
|
|
||||||
error!("Failed to open image\n{:?}", e);
|
|
||||||
panic!("Failed to open image\n{:?}", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let img = DynamicImage::ImageRgb8(img.into_rgb8());
|
|
||||||
let (width, height): (f64, f64) = Coord::from(img.dimensions()).into();
|
let (width, height): (f64, f64) = Coord::from(img.dimensions()).into();
|
||||||
|
|
||||||
let config = globals::CONFIG.read().unwrap();
|
let config = rw_read!(globals::CONFIG);
|
||||||
let mut prop = properties.write().unwrap();
|
let mut prop = rw_write!(properties);
|
||||||
prop.path = Some(path.to_owned());
|
prop.image_info = Some(image_info.to_owned());
|
||||||
prop.original_dimension = (width, height);
|
prop.original_dimension = (width, height);
|
||||||
prop.quote_position = height * config.quote_position_ratio;
|
prop.quote_position = height * config.quote_position_ratio;
|
||||||
prop.subquote_position = height * config.subquote_position_ratio;
|
prop.subquote_position = height * config.subquote_position_ratio;
|
||||||
prop.subquote2_position = height * config.subquote2_position_ratio;
|
prop.subquote2_position = height * config.subquote2_position_ratio;
|
||||||
prop.tag_position = height * config.tag_position_ratio;
|
prop.tag_position = height * config.tag_y_position_ratio;
|
||||||
prop.tag2_position = height * config.tag2_position_ratio;
|
prop.tag2_position = height * config.tag2_position_ratio;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -104,7 +139,7 @@ impl ImageContainer {
|
||||||
|
|
||||||
/// Resize image
|
/// Resize image
|
||||||
pub(crate) fn apply_resize(&mut self) {
|
pub(crate) fn apply_resize(&mut self) {
|
||||||
let mut prop = self.properties.write().unwrap();
|
let mut prop = rw_write!(self.properties);
|
||||||
let (width, height) = prop.dimension;
|
let (width, height) = prop.dimension;
|
||||||
let (s_width, s_height) = ((width * 500.0) / height, 500.0);
|
let (s_width, s_height) = ((width * 500.0) / height, 500.0);
|
||||||
|
|
||||||
|
|
@ -116,13 +151,14 @@ impl ImageContainer {
|
||||||
|
|
||||||
/// Crop Image
|
/// Crop Image
|
||||||
pub(crate) fn apply_crop(&mut self) {
|
pub(crate) fn apply_crop(&mut self) {
|
||||||
let mut prop = self.properties.write().unwrap();
|
let mut prop = rw_write!(self.properties);
|
||||||
let (original_width, original_height) = prop.original_dimension;
|
let (original_width, original_height) = prop.original_dimension;
|
||||||
let (origina_crop_width, origina_crop_height) =
|
let (origina_crop_width, origina_crop_height) =
|
||||||
croped_ratio(original_width, original_height);
|
croped_ratio(original_width, original_height);
|
||||||
|
|
||||||
prop.crop_position = Some((
|
prop.crop_position = Some((
|
||||||
original_width / 2.0 - origina_crop_width / 2.0,
|
(original_width - origina_crop_width) / 2.0,
|
||||||
original_height / 2.0 - origina_crop_height / 2.0,
|
(original_height - origina_crop_height) / 2.0,
|
||||||
));
|
));
|
||||||
|
|
||||||
let (s_width, s_height): (f64, f64) = Coord::from(self.image.dimensions()).into();
|
let (s_width, s_height): (f64, f64) = Coord::from(self.image.dimensions()).into();
|
||||||
|
|
@ -138,12 +174,13 @@ impl ImageContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn apply_crop_position(&mut self, original_x: f64, original_y: f64) {
|
pub(crate) fn apply_crop_position(&mut self, original_x: f64, original_y: f64) {
|
||||||
let mut prop = self.properties.write().unwrap();
|
let mut prop = rw_write!(self.properties);
|
||||||
let (original_width, original_height) = prop.original_dimension;
|
let (original_width, original_height) = prop.original_dimension;
|
||||||
prop.crop_position = Some((original_x, original_y));
|
prop.crop_position = Some((original_x, original_y));
|
||||||
|
|
||||||
let (s_width, s_height): (f64, f64) = Coord::from(self.image.dimensions()).into();
|
let (s_width, s_height): (f64, f64) = Coord::from(self.image.dimensions()).into();
|
||||||
let (c_width, c_height) = croped_ratio(s_width, s_height);
|
let (c_width, c_height) = croped_ratio(s_width, s_height);
|
||||||
|
|
||||||
let (cx, cy) = (
|
let (cx, cy) = (
|
||||||
(original_x * s_width) / original_width,
|
(original_x * s_width) / original_width,
|
||||||
(original_y * s_height) / original_height,
|
(original_y * s_height) / original_height,
|
||||||
|
|
@ -159,7 +196,7 @@ impl ImageContainer {
|
||||||
|
|
||||||
/// Redraw: Copy image from main image to buffer and draw text and all on it
|
/// Redraw: Copy image from main image to buffer and draw text and all on it
|
||||||
pub(crate) fn redraw_to_buffer(&mut self) {
|
pub(crate) fn redraw_to_buffer(&mut self) {
|
||||||
let prop = self.properties.read().unwrap();
|
let prop = rw_read!(self.properties);
|
||||||
let mut tmp = self.image.clone();
|
let mut tmp = self.image.clone();
|
||||||
|
|
||||||
draw_layer_and_text(
|
draw_layer_and_text(
|
||||||
|
|
@ -175,51 +212,58 @@ impl ImageContainer {
|
||||||
&prop.tag2,
|
&prop.tag2,
|
||||||
prop.tag_position,
|
prop.tag_position,
|
||||||
prop.tag2_position,
|
prop.tag2_position,
|
||||||
|
prop.original_dimension.0,
|
||||||
prop.original_dimension.1,
|
prop.original_dimension.1,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.buffer = tmp;
|
self.buffer = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save image anf properities
|
/// Save image and properities
|
||||||
pub(crate) fn save(&self) {
|
pub(crate) fn save(&self) {
|
||||||
let prop = self.properties.read().unwrap();
|
let prop = rw_read!(self.properties);
|
||||||
|
let image_info = &prop.image_info;
|
||||||
let path_original = match &prop.path {
|
let (export_path, path_properties, mut original_image) = match image_info {
|
||||||
Some(p) => Path::new(p),
|
Some(p) => (
|
||||||
|
get_export_image_path(p, &prop.name_prefix),
|
||||||
|
get_properties_path(p),
|
||||||
|
load_image(p),
|
||||||
|
),
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
let path_properties = path_original.with_extension("prop");
|
let config = rw_read!(globals::CONFIG);
|
||||||
let export = path_original.parent().unwrap().join("export").join(
|
let export_format = &config.image_format;
|
||||||
path_original
|
|
||||||
.with_extension("png")
|
|
||||||
.file_name()
|
|
||||||
.unwrap()
|
|
||||||
.to_str()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut prop = prop.clone();
|
let mut prop = prop.clone();
|
||||||
prop.path = None;
|
prop.image_info = None;
|
||||||
if let Err(e) = fs::write(
|
fs::write(
|
||||||
&path_properties,
|
&path_properties,
|
||||||
serde_json::to_string(&ImagePropertiesFile::from(&prop)).unwrap(),
|
serde_json::to_string(&ImagePropertiesFile::from(&prop)).unwrap(),
|
||||||
) {
|
)
|
||||||
dialog::alert_default("Failed to save properties!");
|
.warn_log("Failed to save properties!");
|
||||||
warn!("Failed to save properties!\n{:?}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut img = image::open(&path_original).unwrap();
|
let (width, height): (f64, f64) = Coord::from(original_image.dimensions()).into();
|
||||||
let (width, height): (f64, f64) = Coord::from(img.dimensions()).into();
|
|
||||||
let (crop_x, crop_y) = prop.crop_position.unwrap();
|
let (crop_x, crop_y) = prop.crop_position.unwrap();
|
||||||
let (crop_width, crop_height) = croped_ratio(width, height);
|
let (crop_width, crop_height) = croped_ratio(width, height);
|
||||||
let mut img = img.crop(
|
let mut img = original_image.crop(
|
||||||
crop_x as u32,
|
crop_x as u32,
|
||||||
crop_y as u32,
|
crop_y as u32,
|
||||||
crop_width as u32,
|
crop_width as u32,
|
||||||
crop_height as u32,
|
crop_height as u32,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if crop_width > config.maximum_width_limit {
|
||||||
|
let (resize_width, resize_height) = (
|
||||||
|
config.maximum_width_limit,
|
||||||
|
height_from_width(config.maximum_width_limit),
|
||||||
|
);
|
||||||
|
img = img.resize_exact(
|
||||||
|
resize_width as u32,
|
||||||
|
resize_height as u32,
|
||||||
|
image::imageops::FilterType::Lanczos3,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
draw_layer_and_text(
|
draw_layer_and_text(
|
||||||
&mut img,
|
&mut img,
|
||||||
&prop.translucent_layer_color,
|
&prop.translucent_layer_color,
|
||||||
|
|
@ -233,89 +277,113 @@ impl ImageContainer {
|
||||||
&prop.tag2,
|
&prop.tag2,
|
||||||
prop.tag_position,
|
prop.tag_position,
|
||||||
prop.tag2_position,
|
prop.tag2_position,
|
||||||
|
prop.original_dimension.0,
|
||||||
prop.original_dimension.1,
|
prop.original_dimension.1,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Err(e) = img.save_with_format(&export, image::ImageFormat::Png) {
|
let mut output = match File::create(&export_path) {
|
||||||
dialog::alert_default("Failed to export png!");
|
Ok(a) => a,
|
||||||
warn!("Failed to export png!\n{:?}", e);
|
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!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clone_img(&self) -> Option<PathBuf> {
|
pub(crate) fn clone_img(&self) -> Option<ImageInfo> {
|
||||||
let prop = self.properties.read().unwrap();
|
let prop = rw_read!(self.properties);
|
||||||
|
|
||||||
match &prop.path {
|
match &prop.image_info {
|
||||||
Some(path) => {
|
Some(image_info) => {
|
||||||
let name = path.file_stem().unwrap().to_string_lossy();
|
let stem = image_info.path.file_stem().unwrap().to_string_lossy();
|
||||||
let ext = path.extension().unwrap().to_string_lossy();
|
let extension = image_info.path.extension().unwrap().to_string_lossy();
|
||||||
let mut i = 1;
|
let mut i = 1;
|
||||||
let mut new_path = path.clone();
|
let mut new_image_info = image_info.clone();
|
||||||
while new_path.exists() {
|
while new_image_info.path.exists() {
|
||||||
let new_file = format!("{}{}.{}", name, "-copy".repeat(i), ext);
|
let new_file = format!("{}{}.{}", stem, "-copy".repeat(i), extension);
|
||||||
new_path = path.with_file_name(&new_file);
|
new_image_info.path = image_info.path.with_file_name(&new_file);
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path_properties = path.with_extension("prop");
|
let path_properties = get_properties_path(&image_info);
|
||||||
let path_properties_new = new_path.with_extension("prop");
|
let path_properties_new = get_properties_path(&new_image_info);
|
||||||
|
|
||||||
if path.exists() {
|
if image_info.path.exists() {
|
||||||
if let Err(e) = fs::copy(path, &new_path) {
|
fs::copy(&image_info.path, &new_image_info.path)
|
||||||
dialog::alert_default("Failed to clone image!");
|
.warn_log("Failed to clone image!");
|
||||||
warn!("Failed to clone image!\n{:?}", e);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if path_properties.exists() {
|
if path_properties.exists() {
|
||||||
if let Err(e) = fs::copy(path_properties, &path_properties_new) {
|
fs::copy(path_properties, &path_properties_new)
|
||||||
dialog::alert_default("Failed to clone image properties!");
|
.warn_log("Failed to clone image properties!");
|
||||||
warn!("Failed to clone image properties!\n{:?}", e);
|
|
||||||
}
|
}
|
||||||
}
|
Some(new_image_info)
|
||||||
Some(new_path)
|
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn delete(&self) {
|
pub(crate) fn delete(&self) {
|
||||||
let prop = self.properties.read().unwrap();
|
let prop = rw_read!(self.properties);
|
||||||
|
let image_info = &prop.image_info;
|
||||||
let path_original = match &prop.path {
|
let (export_path, path_image, path_properties) = match image_info {
|
||||||
Some(p) => Path::new(p),
|
Some(p) => (
|
||||||
|
get_export_image_path(p, &prop.name_prefix),
|
||||||
|
Path::new(&p.path),
|
||||||
|
get_properties_path(p),
|
||||||
|
),
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
let path_properties = path_original.with_extension("prop");
|
|
||||||
let export = path_original.parent().unwrap().join("export").join(
|
|
||||||
path_original
|
|
||||||
.with_extension("png")
|
|
||||||
.file_name()
|
|
||||||
.unwrap()
|
|
||||||
.to_str()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if path_original.exists() {
|
if path_image.exists() {
|
||||||
if let Err(e) = fs::remove_file(path_original) {
|
fs::remove_file(path_image).warn_log("Failed to delete image!");
|
||||||
dialog::alert_default("Failed to delete image!");
|
|
||||||
warn!("Failed to delete image!\n{:?}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if path_properties.exists() {
|
if path_properties.exists() {
|
||||||
if let Err(e) = fs::remove_file(path_properties) {
|
fs::remove_file(path_properties).warn_log("Failed to delete image properties!");
|
||||||
dialog::alert_default("Failed to delete image properties!");
|
|
||||||
warn!("Failed to delete image properties!\n{:?}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if export.exists() {
|
if export_path.exists() {
|
||||||
if let Err(e) = fs::remove_file(export) {
|
fs::remove_file(export_path).warn_log("Failed to delete exported image!");
|
||||||
dialog::alert_default("Failed to delete exported image!");
|
|
||||||
warn!("Failed to delete exported image!\n{:?}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -324,6 +392,7 @@ impl ImageContainer {
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub(crate) struct ImagePropertiesFile {
|
pub(crate) struct ImagePropertiesFile {
|
||||||
pub(crate) crop_position: Option<(f64, f64)>,
|
pub(crate) crop_position: Option<(f64, f64)>,
|
||||||
|
pub(crate) name_prefix: Option<String>,
|
||||||
pub(crate) quote: Option<String>,
|
pub(crate) quote: Option<String>,
|
||||||
pub(crate) subquote: Option<String>,
|
pub(crate) subquote: Option<String>,
|
||||||
pub(crate) subquote2: Option<String>,
|
pub(crate) subquote2: Option<String>,
|
||||||
|
|
@ -341,6 +410,7 @@ impl Default for ImagePropertiesFile {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
crop_position: None,
|
crop_position: None,
|
||||||
|
name_prefix: None,
|
||||||
quote: None,
|
quote: None,
|
||||||
subquote: None,
|
subquote: None,
|
||||||
subquote2: None,
|
subquote2: None,
|
||||||
|
|
@ -360,6 +430,7 @@ impl From<&ImageProperties> for ImagePropertiesFile {
|
||||||
fn from(props: &ImageProperties) -> Self {
|
fn from(props: &ImageProperties) -> Self {
|
||||||
Self {
|
Self {
|
||||||
crop_position: props.crop_position,
|
crop_position: props.crop_position,
|
||||||
|
name_prefix: Some(props.name_prefix.clone()),
|
||||||
quote: Some(props.quote.clone()),
|
quote: Some(props.quote.clone()),
|
||||||
subquote: Some(props.subquote.clone()),
|
subquote: Some(props.subquote.clone()),
|
||||||
subquote2: Some(props.subquote2.clone()),
|
subquote2: Some(props.subquote2.clone()),
|
||||||
|
|
@ -378,10 +449,11 @@ impl From<&ImageProperties> for ImagePropertiesFile {
|
||||||
/// Properties of loaded image
|
/// Properties of loaded image
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub(crate) struct ImageProperties {
|
pub(crate) struct ImageProperties {
|
||||||
pub(crate) path: Option<PathBuf>,
|
pub(crate) image_info: Option<ImageInfo>,
|
||||||
pub(crate) dimension: (f64, f64),
|
pub(crate) dimension: (f64, f64),
|
||||||
pub(crate) original_dimension: (f64, f64),
|
pub(crate) original_dimension: (f64, f64),
|
||||||
pub(crate) crop_position: Option<(f64, f64)>,
|
pub(crate) crop_position: Option<(f64, f64)>,
|
||||||
|
pub(crate) name_prefix: String,
|
||||||
pub(crate) quote: String,
|
pub(crate) quote: String,
|
||||||
pub(crate) subquote: String,
|
pub(crate) subquote: String,
|
||||||
pub(crate) subquote2: String,
|
pub(crate) subquote2: String,
|
||||||
|
|
@ -399,10 +471,11 @@ pub(crate) struct ImageProperties {
|
||||||
impl Default for ImageProperties {
|
impl Default for ImageProperties {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
path: None,
|
image_info: None,
|
||||||
dimension: (0.0, 0.0),
|
dimension: (0.0, 0.0),
|
||||||
original_dimension: (0.0, 0.0),
|
original_dimension: (0.0, 0.0),
|
||||||
crop_position: None,
|
crop_position: None,
|
||||||
|
name_prefix: "".to_owned(),
|
||||||
quote: "".to_owned(),
|
quote: "".to_owned(),
|
||||||
subquote: "".to_owned(),
|
subquote: "".to_owned(),
|
||||||
subquote2: "".to_owned(),
|
subquote2: "".to_owned(),
|
||||||
|
|
@ -426,10 +499,8 @@ impl ImageProperties {
|
||||||
tag_default: &str,
|
tag_default: &str,
|
||||||
tag2_default: &str,
|
tag2_default: &str,
|
||||||
) {
|
) {
|
||||||
if let Some(v) = props.crop_position {
|
self.crop_position = props.crop_position;
|
||||||
self.crop_position = Some(v);
|
self.name_prefix = props.name_prefix.unwrap_or("".to_owned());
|
||||||
}
|
|
||||||
|
|
||||||
self.quote = props.quote.unwrap_or("".to_owned());
|
self.quote = props.quote.unwrap_or("".to_owned());
|
||||||
self.subquote = props.subquote.unwrap_or("".to_owned());
|
self.subquote = props.subquote.unwrap_or("".to_owned());
|
||||||
self.subquote2 = props.subquote2.unwrap_or("".to_owned());
|
self.subquote2 = props.subquote2.unwrap_or("".to_owned());
|
||||||
|
|
@ -442,10 +513,53 @@ impl ImageProperties {
|
||||||
self.tag2_position = props.tag2_position.unwrap_or(self.tag2_position);
|
self.tag2_position = props.tag2_position.unwrap_or(self.tag2_position);
|
||||||
self.translucent_layer_color = props
|
self.translucent_layer_color = props
|
||||||
.translucent_layer_color
|
.translucent_layer_color
|
||||||
.unwrap_or(globals::CONFIG.read().unwrap().color_layer);
|
.unwrap_or(rw_read!(globals::CONFIG).color_layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load image as Dynamic Image
|
||||||
|
fn load_image(image_info: &ImageInfo) -> DynamicImage {
|
||||||
|
let img = match image_info.image_type {
|
||||||
|
ImageType::Webp => {
|
||||||
|
let mut f = File::open(&image_info.path).expect_log("Failed to load image!");
|
||||||
|
let mut buf = vec![];
|
||||||
|
f.read_to_end(&mut buf).expect_log("Failed to read image!");
|
||||||
|
let a = webp::Decoder::new(&buf)
|
||||||
|
.decode()
|
||||||
|
.ok_or("")
|
||||||
|
.expect_log("Failed to decode image!");
|
||||||
|
a.to_image()
|
||||||
|
}
|
||||||
|
ImageType::Jpeg => {
|
||||||
|
let mut f = File::open(&image_info.path).expect_log("Failed to load image!");
|
||||||
|
let mut buf = vec![];
|
||||||
|
f.read_to_end(&mut buf).expect_log("Failed to read image!");
|
||||||
|
|
||||||
|
let d = mozjpeg::Decompress::with_markers(mozjpeg::ALL_MARKERS)
|
||||||
|
.from_mem(&buf)
|
||||||
|
.expect_log("Failed to decompress image!");
|
||||||
|
let mut image = d.rgb().expect_log("Failed to convert to rgb image!");
|
||||||
|
let pixels = image.read_scanlines_flat().unwrap();
|
||||||
|
let image =
|
||||||
|
ImageBuffer::from_raw(image.width() as u32, image.height() as u32, pixels).unwrap();
|
||||||
|
DynamicImage::ImageRgb8(image)
|
||||||
|
}
|
||||||
|
ImageType::Png => {
|
||||||
|
let dec = image::codecs::png::PngDecoder::new(
|
||||||
|
File::open(&image_info.path).expect_log("Failed to load image!"),
|
||||||
|
)
|
||||||
|
.expect_log("Failed to decode image!");
|
||||||
|
DynamicImage::from_decoder(dec).expect_log("Failed to decode image!")
|
||||||
|
}
|
||||||
|
ImageType::None => {
|
||||||
|
Result::<(), _>::Err("Failed to load image!").expect_log("Unknown format!");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DynamicImage::ImageRgb8(img.into_rgb8())
|
||||||
|
}
|
||||||
|
|
||||||
/// Draw text and stuffs on image
|
/// Draw text and stuffs on image
|
||||||
fn draw_layer_and_text(
|
fn draw_layer_and_text(
|
||||||
tmp: &mut DynamicImage,
|
tmp: &mut DynamicImage,
|
||||||
|
|
@ -458,8 +572,9 @@ fn draw_layer_and_text(
|
||||||
subquote2_position: f64,
|
subquote2_position: f64,
|
||||||
tag: &str,
|
tag: &str,
|
||||||
tag2: &str,
|
tag2: &str,
|
||||||
tag_position: f64,
|
tag_y_position: f64,
|
||||||
tag2_position: f64,
|
tag2_position: f64,
|
||||||
|
original_width: f64,
|
||||||
original_height: f64,
|
original_height: f64,
|
||||||
) {
|
) {
|
||||||
let (width, height): (f64, f64) = Coord::from(tmp.dimensions()).into();
|
let (width, height): (f64, f64) = Coord::from(tmp.dimensions()).into();
|
||||||
|
|
@ -475,16 +590,21 @@ fn draw_layer_and_text(
|
||||||
&globals::FONT_QUOTE,
|
&globals::FONT_QUOTE,
|
||||||
size,
|
size,
|
||||||
quote_position,
|
quote_position,
|
||||||
|
original_width,
|
||||||
original_height,
|
original_height,
|
||||||
|
true,
|
||||||
quote,
|
quote,
|
||||||
);
|
);
|
||||||
|
|
||||||
let size = subquote_from_height(height);
|
let size = subquote_from_height(height);
|
||||||
draw_multiline_mid_string(
|
draw_multiline_mid_string(
|
||||||
tmp,
|
tmp,
|
||||||
&globals::FONT_SUBQUOTE,
|
&globals::FONT_SUBQUOTE,
|
||||||
size,
|
size,
|
||||||
subquote_position,
|
subquote_position,
|
||||||
|
original_width,
|
||||||
original_height,
|
original_height,
|
||||||
|
true,
|
||||||
subquote,
|
subquote,
|
||||||
);
|
);
|
||||||
let size = subquote2_from_height(height);
|
let size = subquote2_from_height(height);
|
||||||
|
|
@ -493,7 +613,9 @@ fn draw_layer_and_text(
|
||||||
&globals::FONT_SUBQUOTE2,
|
&globals::FONT_SUBQUOTE2,
|
||||||
size,
|
size,
|
||||||
subquote2_position,
|
subquote2_position,
|
||||||
|
original_width,
|
||||||
original_height,
|
original_height,
|
||||||
|
true,
|
||||||
subquote2,
|
subquote2,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -503,7 +625,9 @@ fn draw_layer_and_text(
|
||||||
&globals::FONT_TAG2,
|
&globals::FONT_TAG2,
|
||||||
size,
|
size,
|
||||||
tag2_position,
|
tag2_position,
|
||||||
|
original_width,
|
||||||
original_height,
|
original_height,
|
||||||
|
false,
|
||||||
tag2,
|
tag2,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -518,8 +642,9 @@ fn draw_layer_and_text(
|
||||||
imageproc::drawing::draw_text_mut(
|
imageproc::drawing::draw_text_mut(
|
||||||
tmp,
|
tmp,
|
||||||
image::Rgba([255, 255, 255, 255]),
|
image::Rgba([255, 255, 255, 255]),
|
||||||
(width * 0.99 - text_width) as u32,
|
(width * rw_read!(globals::CONFIG).tag_x_position_ratio - text_width) as i32,
|
||||||
((tag_position * height) / original_height + index as f64 * (text_height * 1.2)) as u32,
|
((tag_y_position * height) / original_height + index as f64 * (text_height * 1.2))
|
||||||
|
as i32,
|
||||||
rusttype::Scale::uniform(size as f32),
|
rusttype::Scale::uniform(size as f32),
|
||||||
&globals::FONT_TAG,
|
&globals::FONT_TAG,
|
||||||
line,
|
line,
|
||||||
|
|
@ -533,19 +658,40 @@ pub(crate) fn draw_multiline_mid_string(
|
||||||
font: &rusttype::Font,
|
font: &rusttype::Font,
|
||||||
size: f64,
|
size: f64,
|
||||||
position: f64,
|
position: f64,
|
||||||
|
original_width: f64,
|
||||||
original_height: f64,
|
original_height: f64,
|
||||||
|
boxed: bool,
|
||||||
text: &str,
|
text: &str,
|
||||||
) {
|
) {
|
||||||
|
let (mut box_width, mut box_height) = (0.0, 0.0);
|
||||||
|
|
||||||
let (width, height): (f64, f64) = Coord::from(tmp.dimensions()).into();
|
let (width, height): (f64, f64) = Coord::from(tmp.dimensions()).into();
|
||||||
|
let has_line_spacing = rw_read!(globals::CONFIG).line_spacing;
|
||||||
for (index, line) in text.lines().enumerate() {
|
for (index, line) in text.lines().enumerate() {
|
||||||
let (text_width, text_height) =
|
let (text_width, text_height) =
|
||||||
measure_line(font, line, rusttype::Scale::uniform(size as f32));
|
measure_line(font, line, rusttype::Scale::uniform(size as f32));
|
||||||
|
|
||||||
|
if text_width > box_width {
|
||||||
|
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(
|
imageproc::drawing::draw_text_mut(
|
||||||
tmp,
|
tmp,
|
||||||
image::Rgba([255, 255, 255, 255]),
|
image::Rgba([255, 255, 255, 100]),
|
||||||
((width - text_width) / 2.0) as u32,
|
x as i32,
|
||||||
((position * height) / original_height + index as f64 * (text_height * 1.15)) as u32,
|
y as i32,
|
||||||
rusttype::Scale::uniform(size as f32),
|
rusttype::Scale::uniform(size as f32),
|
||||||
font,
|
font,
|
||||||
line,
|
line,
|
||||||
|
|
@ -553,6 +699,103 @@ pub(crate) fn draw_multiline_mid_string(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if boxed && rw_read!(globals::CONFIG).draw_box_around_quote {
|
||||||
|
draw_box(
|
||||||
|
tmp,
|
||||||
|
box_width,
|
||||||
|
box_height,
|
||||||
|
position,
|
||||||
|
original_width,
|
||||||
|
original_height,
|
||||||
|
);
|
||||||
|
|
||||||
|
draw_multiline_mid_string(
|
||||||
|
tmp,
|
||||||
|
font,
|
||||||
|
size,
|
||||||
|
position,
|
||||||
|
original_width,
|
||||||
|
original_height,
|
||||||
|
false,
|
||||||
|
text,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws box around text.
|
||||||
|
fn draw_box(
|
||||||
|
tmp: &mut DynamicImage,
|
||||||
|
box_width: f64,
|
||||||
|
box_height: f64,
|
||||||
|
position: f64,
|
||||||
|
original_width: f64,
|
||||||
|
original_height: f64,
|
||||||
|
) {
|
||||||
|
if box_width <= 0.0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (width, height): (f64, f64) = Coord::from(tmp.dimensions()).into();
|
||||||
|
let (delta_x, delta_y) = (width / original_width, height / original_height);
|
||||||
|
|
||||||
|
let (x_gap, y_gap) = (30.0 * delta_x, 10.0 * delta_y);
|
||||||
|
let (x, y) = (
|
||||||
|
((width - box_width) / 2.0 - x_gap) as u32,
|
||||||
|
((position * height) / original_height - y_gap) as u32,
|
||||||
|
);
|
||||||
|
let (w, h) = (
|
||||||
|
(box_width + x_gap * 2.0) as u32,
|
||||||
|
(box_height + y_gap * 2.0) as u32,
|
||||||
|
);
|
||||||
|
|
||||||
|
if x >= width as u32 || y >= height as u32 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buff = tmp.crop(x, y, w, h);
|
||||||
|
let layer = DynamicImage::ImageRgba8(ImageBuffer::from_fn(w, h, |_, _| {
|
||||||
|
image::Rgba([20, 22, 25, 80])
|
||||||
|
}));
|
||||||
|
image::imageops::overlay(&mut buff, &layer, 0, 0);
|
||||||
|
buff = buff.blur(15.0);
|
||||||
|
|
||||||
|
let (dx, dy) = (20.0 * delta_x, 20.0 * delta_y);
|
||||||
|
let mut shadow = DynamicImage::new_rgba8(w + (dx * 2.0) as u32, h + (dy * 2.0) as u32);
|
||||||
|
imageproc::drawing::draw_hollow_rect_mut(
|
||||||
|
&mut shadow,
|
||||||
|
Rect::at(dx as i32, dy as i32).of_size(w, h),
|
||||||
|
image::Rgba([30, 30, 30, 255]),
|
||||||
|
);
|
||||||
|
shadow = shadow.blur(5.0 * delta_x as f32);
|
||||||
|
|
||||||
|
image::imageops::overlay(tmp, &shadow, x as i64 - dx as i64, y as i64 - dy as i64);
|
||||||
|
image::imageops::overlay(tmp, &buff, x as i64, y as i64);
|
||||||
|
|
||||||
|
let mut color = buff.get_pixel(0, 0).to_rgba();
|
||||||
|
color.blend(&buff.get_pixel(0, buff.height() - 1).to_rgba());
|
||||||
|
color.blend(
|
||||||
|
&buff
|
||||||
|
.get_pixel(buff.width() - 1, buff.height() - 1)
|
||||||
|
.to_rgba(),
|
||||||
|
);
|
||||||
|
color.blend(&buff.get_pixel(buff.width() - 1, 0).to_rgba());
|
||||||
|
imageproc::drawing::draw_hollow_rect_mut(
|
||||||
|
tmp,
|
||||||
|
Rect::at(
|
||||||
|
x as i32 - (delta_x * 1.0) as i32,
|
||||||
|
y as i32 - (delta_x * 1.0) as i32,
|
||||||
|
)
|
||||||
|
.of_size(w + (delta_x * 2.0) as u32, h + (delta_x * 2.0) as u32),
|
||||||
|
color.clone(),
|
||||||
|
);
|
||||||
|
color.blend(&image::Rgba([0, 0, 0, 2]));
|
||||||
|
imageproc::drawing::draw_hollow_rect_mut(
|
||||||
|
tmp,
|
||||||
|
Rect::at(x as i32, y as i32).of_size(w, h),
|
||||||
|
color.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Get size of text to draw on image
|
/// Get size of text to draw on image
|
||||||
pub(crate) fn measure_line(
|
pub(crate) fn measure_line(
|
||||||
font: &rusttype::Font,
|
font: &rusttype::Font,
|
||||||
|
|
@ -571,6 +814,95 @@ pub(crate) fn measure_line(
|
||||||
Coord::from((width, height)).into()
|
Coord::from((width, height)).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// path of properties files
|
||||||
|
pub(crate) fn get_properties_path(image_info: &ImageInfo) -> PathBuf {
|
||||||
|
let img = &image_info.path;
|
||||||
|
|
||||||
|
let image_name: String = image_info
|
||||||
|
.path
|
||||||
|
.file_name()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
|
||||||
|
let mut occurance = 0;
|
||||||
|
let image_name = image_name
|
||||||
|
.chars()
|
||||||
|
.into_iter()
|
||||||
|
.rev()
|
||||||
|
.map(|c| {
|
||||||
|
if occurance == 0 && c == '.' {
|
||||||
|
occurance = 1;
|
||||||
|
'-'
|
||||||
|
} else {
|
||||||
|
c
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<char>>()
|
||||||
|
.into_iter()
|
||||||
|
.rev();
|
||||||
|
let image_name = format!("{}.prop", String::from_iter(image_name));
|
||||||
|
|
||||||
|
let default_path = img.with_file_name(&image_name);
|
||||||
|
|
||||||
|
if default_path.exists() {
|
||||||
|
return default_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = img.with_extension("prop");
|
||||||
|
|
||||||
|
if path.exists() {
|
||||||
|
match std::fs::copy(&path, &default_path) {
|
||||||
|
Ok(_) => std::fs::remove_file(&path).warn_log("Failed to delete depricated prop file"),
|
||||||
|
Err(e) => Result::<(), _>::Err(e).warn_log("Failed to copy depricated prop file"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default_path
|
||||||
|
}
|
||||||
|
|
||||||
|
/// path of properties files
|
||||||
|
pub(crate) fn get_export_image_path(image_info: &ImageInfo, name_prefix: &str) -> PathBuf {
|
||||||
|
let config = rw_read!(globals::CONFIG);
|
||||||
|
let export_format = &config.image_format;
|
||||||
|
let image_name = image_info
|
||||||
|
.path
|
||||||
|
.file_name()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string_lossy();
|
||||||
|
|
||||||
|
let mut occurance = 0;
|
||||||
|
let image_name = image_name
|
||||||
|
.chars()
|
||||||
|
.into_iter()
|
||||||
|
.rev()
|
||||||
|
.map(|c| {
|
||||||
|
if occurance == 0 && c == '.' {
|
||||||
|
occurance = 1;
|
||||||
|
'-'
|
||||||
|
} else {
|
||||||
|
c
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<char>>()
|
||||||
|
.into_iter()
|
||||||
|
.rev();
|
||||||
|
let image_name = format!(
|
||||||
|
"{}{}.{}",
|
||||||
|
name_prefix,
|
||||||
|
String::from_iter(image_name),
|
||||||
|
export_format.as_extension()
|
||||||
|
);
|
||||||
|
|
||||||
|
let expost_dir = image_info.path.parent().unwrap().join("export");
|
||||||
|
if !expost_dir.exists() {
|
||||||
|
fs::create_dir(&expost_dir).expect_log("Failed to create export folder!");
|
||||||
|
}
|
||||||
|
|
||||||
|
let export = expost_dir.join(&image_name);
|
||||||
|
export
|
||||||
|
}
|
||||||
|
|
||||||
/// small hack because 0,0,0 rgb, because can't be set on fltk theme
|
/// small hack because 0,0,0 rgb, because can't be set on fltk theme
|
||||||
pub(crate) fn set_color_btn_rgba(rgba: [u8; 4], btn: &mut Button) {
|
pub(crate) fn set_color_btn_rgba(rgba: [u8; 4], btn: &mut Button) {
|
||||||
let [mut r, g, b, _] = rgba;
|
let [mut r, g, b, _] = rgba;
|
||||||
|
|
@ -580,6 +912,16 @@ pub(crate) fn set_color_btn_rgba(rgba: [u8; 4], btn: &mut Button) {
|
||||||
btn.set_color(enums::Color::from_rgb(r, g, b));
|
btn.set_color(enums::Color::from_rgb(r, g, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if image is too small
|
||||||
|
pub(crate) fn is_too_small(width: f64, height: f64) -> bool {
|
||||||
|
let (crop_width, _) = croped_ratio(width, height);
|
||||||
|
if crop_width < rw_read!(globals::CONFIG).minimum_width_limit {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get required size to crop image as per image ratio
|
/// Get required size to crop image as per image ratio
|
||||||
pub(crate) fn croped_ratio(width: f64, height: f64) -> (f64, f64) {
|
pub(crate) fn croped_ratio(width: f64, height: f64) -> (f64, f64) {
|
||||||
if width > width_from_height(height) {
|
if width > width_from_height(height) {
|
||||||
|
|
@ -591,37 +933,58 @@ pub(crate) fn croped_ratio(width: f64, height: f64) -> (f64, f64) {
|
||||||
|
|
||||||
/// Get required witdh to crop image from height as per image ratio
|
/// Get required witdh to crop image from height as per image ratio
|
||||||
pub(crate) fn width_from_height(height: f64) -> f64 {
|
pub(crate) fn width_from_height(height: f64) -> f64 {
|
||||||
let (w, h) = globals::CONFIG.read().unwrap().image_ratio;
|
let (w, h) = rw_read!(globals::CONFIG).image_ratio;
|
||||||
(w * height) / h
|
(w * height) / h
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get required height to crop image from width as per image ratio
|
/// Get required height to crop image from width as per image ratio
|
||||||
pub(crate) fn height_from_width(width: f64) -> f64 {
|
pub(crate) fn height_from_width(width: f64) -> f64 {
|
||||||
let (w, h) = globals::CONFIG.read().unwrap().image_ratio;
|
let (w, h) = rw_read!(globals::CONFIG).image_ratio;
|
||||||
(h * width) / w
|
(h * width) / w
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get required quote size for crop image from height as per image ratio
|
/// Get required quote size for crop image from height as per image ratio
|
||||||
pub(crate) fn quote_from_height(height: f64) -> f64 {
|
pub(crate) fn quote_from_height(height: f64) -> f64 {
|
||||||
(height * globals::CONFIG.read().unwrap().quote_font_ratio) / 5000.0
|
(height * rw_read!(globals::CONFIG).quote_font_ratio) / 5000.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get required subquote size for crop image from height as per image ratio
|
/// Get required subquote size for crop image from height as per image ratio
|
||||||
pub(crate) fn subquote_from_height(height: f64) -> f64 {
|
pub(crate) fn subquote_from_height(height: f64) -> f64 {
|
||||||
(height * globals::CONFIG.read().unwrap().subquote_font_ratio) / 5000.0
|
(height * rw_read!(globals::CONFIG).subquote_font_ratio) / 5000.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get required subquote2 size for crop image from height as per image ratio
|
/// Get required subquote2 size for crop image from height as per image ratio
|
||||||
pub(crate) fn subquote2_from_height(height: f64) -> f64 {
|
pub(crate) fn subquote2_from_height(height: f64) -> f64 {
|
||||||
(height * globals::CONFIG.read().unwrap().subquote2_font_ratio) / 5000.0
|
(height * rw_read!(globals::CONFIG).subquote2_font_ratio) / 5000.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get required tag size for crop image from height as per image ratio
|
/// Get required tag size for crop image from height as per image ratio
|
||||||
pub(crate) fn tag_from_height(height: f64) -> f64 {
|
pub(crate) fn tag_from_height(height: f64) -> f64 {
|
||||||
(height * globals::CONFIG.read().unwrap().tag_font_ratio) / 5000.0
|
(height * rw_read!(globals::CONFIG).tag_font_ratio) / 5000.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get required tag2 size for crop image from height as per image ratio
|
/// Get required tag2 size for crop image from height as per image ratio
|
||||||
pub(crate) fn tag2_from_height(height: f64) -> f64 {
|
pub(crate) fn tag2_from_height(height: f64) -> f64 {
|
||||||
(height * globals::CONFIG.read().unwrap().tag2_font_ratio) / 5000.0
|
(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()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue