use adw::{prelude::*, subclass::prelude::*};
use glib::subclass::InitializingObject;
use gtk::{
Button, CompositeTemplate, Entry, Label, MenuButton, SpinButton, Stack, ToggleButton, gio, glib,
};
use png::ColorType;
use std::sync::{Arc, Mutex};
use crate::gui::window::{Base, Mode::Normal, Mode::Rewind};
#[derive(CompositeTemplate)]
#[template(resource = "/org/hellbyte/pixelmosh/window.ui")]
pub struct Window {
#[template_child]
pub btn_ansi: TemplateChild<ToggleButton>,
#[template_child]
pub btn_min_rate: TemplateChild<SpinButton>,
#[template_child]
pub btn_max_rate: TemplateChild<SpinButton>,
#[template_child]
pub btn_pixelation: TemplateChild<SpinButton>,
#[template_child]
pub btn_line_shift: TemplateChild<SpinButton>,
#[template_child]
pub btn_reverse: TemplateChild<SpinButton>,
#[template_child]
pub btn_flip: TemplateChild<SpinButton>,
#[template_child]
pub btn_channel_swap: TemplateChild<SpinButton>,
#[template_child]
pub btn_channel_shift: TemplateChild<SpinButton>,
#[template_child]
pub btn_open: TemplateChild<Button>,
#[template_child]
pub btn_save: TemplateChild<Button>,
#[template_child]
pub btn_menu: TemplateChild<MenuButton>,
#[template_child]
pub btn_rewind: TemplateChild<Button>,
#[template_child]
pub btn_mosh: TemplateChild<Button>,
#[template_child]
pub spinner: TemplateChild<adw::Spinner>,
#[template_child]
pub toast_overlay: TemplateChild<adw::ToastOverlay>,
pub dialog_open: gtk::FileDialog,
pub dialog_save: gtk::FileDialog,
pub base: Arc<Mutex<Base>>,
#[template_child]
pub picture: TemplateChild<gtk::Picture>,
#[template_child]
pub stack: TemplateChild<Stack>,
#[template_child]
pub seed: TemplateChild<Entry>,
#[template_child]
pub color_type: TemplateChild<Label>,
pub style_manager: adw::StyleManager,
}
#[glib::object_subclass]
impl ObjectSubclass for Window {
const NAME: &'static str = "PixelmoshWindow";
type Type = super::Window;
type ParentType = adw::ApplicationWindow;
fn new() -> Self {
let filters = gio::ListStore::new::<gtk::FileFilter>();
let png_filter = gtk::FileFilter::new();
png_filter.add_mime_type("image/png");
png_filter.set_name(Some("PNG"));
if !cfg!(target_os = "macos") {
filters.append(&png_filter);
}
let dialog_open = gtk::FileDialog::builder()
.title("Open file")
.accept_label("Open")
.filters(&filters)
.modal(true)
.build();
let dialog_save = gtk::FileDialog::builder()
.title("Save file")
.accept_label("Save")
.initial_name("moshed.png")
.filters(&filters)
.modal(true)
.build();
let style_manager = adw::StyleManager::default();
style_manager.set_color_scheme(adw::ColorScheme::ForceDark);
Self {
btn_ansi: TemplateChild::default(),
btn_min_rate: TemplateChild::default(),
btn_max_rate: TemplateChild::default(),
btn_pixelation: TemplateChild::default(),
btn_line_shift: TemplateChild::default(),
btn_reverse: TemplateChild::default(),
btn_flip: TemplateChild::default(),
btn_channel_swap: TemplateChild::default(),
btn_channel_shift: TemplateChild::default(),
btn_open: TemplateChild::default(),
btn_save: TemplateChild::default(),
btn_menu: TemplateChild::default(),
btn_rewind: TemplateChild::default(),
btn_mosh: TemplateChild::default(),
spinner: TemplateChild::default(),
toast_overlay: TemplateChild::default(),
dialog_open,
dialog_save,
base: Arc::new(Mutex::new(Base::default())),
picture: TemplateChild::default(),
stack: TemplateChild::default(),
seed: TemplateChild::default(),
color_type: TemplateChild::default(),
style_manager,
}
}
fn class_init(klass: &mut Self::Class) {
klass.bind_template();
klass.bind_template_callbacks();
klass.install_action(
"win.mosh-file",
None,
|win, _action_name, _action_target| win.mosh(Normal),
);
klass.install_action(
"win.mosh-rewind",
None,
|win, _action_name, _action_target| win.mosh(Rewind),
);
klass.install_action_async(
"win.open-file",
None,
|win, _action_name, _action_target| async move {
let dialog = &win.imp().dialog_open;
if let Ok(file) = dialog.open_future(Some(&win)).await {
win.load_file(&file);
let color_type = match win.imp().base.lock().unwrap().core.data.color_type {
ColorType::Grayscale => "Grayscale",
ColorType::Indexed => "Indexed",
ColorType::GrayscaleAlpha => "Grayscale/A",
ColorType::Rgb => "RGB",
ColorType::Rgba => "RGB/A",
};
if let Some(file_path) = file.path() {
win.set_title(file_path.file_name().and_then(|name| name.to_str()));
}
win.set_color_type(color_type);
}
},
);
klass.install_action_async(
"win.save-file",
None,
|win, _action_name, _action_target| async move {
let dialog = &win.imp().dialog_save;
if let Ok(file) = dialog.save_future(Some(&win)).await {
match win.save_file(&file) {
Err(error) => {
win.show_message(&format!("Error saving the image: {error}"), 10);
}
_ => {
win.show_message("DONE", 3);
}
}
}
},
);
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
impl ObjectImpl for Window {
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
obj.setup_callbacks();
obj.setup_actions();
#[cfg(debug_assertions)]
obj.setup_debug();
}
}
#[gtk::template_callbacks]
impl Window {
#[template_callback]
fn handle_min_rate(&self, button: >k::SpinButton) {
self.base
.lock()
.unwrap()
.set_min_rate(button.value() as u16);
}
#[template_callback]
fn handle_max_rate(&self, button: >k::SpinButton) {
self.base
.lock()
.unwrap()
.set_max_rate(button.value() as u16);
}
#[template_callback]
fn handle_pixelation(&self, button: >k::SpinButton) {
self.base
.lock()
.unwrap()
.set_pixelation(button.value() as u8);
}
#[template_callback]
fn handle_line_shift(&self, button: >k::SpinButton) {
self.base.lock().unwrap().set_line_shift(button.value());
}
#[template_callback]
fn handle_reverse(&self, button: >k::SpinButton) {
self.base.lock().unwrap().set_reverse(button.value());
}
#[template_callback]
fn handle_flip(&self, button: >k::SpinButton) {
self.base.lock().unwrap().set_flip(button.value());
}
#[template_callback]
fn handle_channel_swap(&self, button: >k::SpinButton) {
self.base.lock().unwrap().set_channel_swap(button.value());
}
#[template_callback]
fn handle_channel_shift(&self, button: >k::SpinButton) {
self.base.lock().unwrap().set_channel_shift(button.value());
}
}
impl WidgetImpl for Window {}
impl WindowImpl for Window {}
impl ApplicationWindowImpl for Window {}
impl AdwApplicationWindowImpl for Window {}