// Copyright (c) 2024-2025 Sophie Herold
// Copyright (c) 2025 Hubert Figuière
//
// This program 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.
//
// This program 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 this program. If not, see .
//
// SPDX-License-Identifier: GPL-3.0-or-later
use std::cell::{Cell, OnceCell};
use adw::gtk::graphene;
use adw::prelude::*;
use adw::subclass::prelude::*;
use adw::{glib, gtk};
use super::crop::{LpAspectRatio, LpEditCrop, LpOrientation};
use crate::deps::*;
const MIN_SELECTION_SIZE: f32 = 80.;
#[derive(Debug, Clone, Copy)]
struct InResize {
corner: Corner,
initial_selection: graphene::Rect,
}
#[derive(Debug, Clone, Copy)]
struct InMove {
initial_selection: graphene::Rect,
}
#[derive(Debug, Clone, Copy)]
enum Corner {
TopLeft,
Top,
TopRight,
Right,
BottomRight,
Bottom,
BottomLeft,
Left,
}
#[derive(Debug, Clone, Copy)]
enum HEdge {
Left,
Right,
}
#[derive(Debug, Clone, Copy)]
enum VEdge {
Top,
Bottom,
}
impl Corner {
fn cursor_name(&self) -> &str {
match self {
Self::TopLeft => "nw-resize",
Self::Top => "n-resize",
Self::TopRight => "ne-resize",
Self::Right => "e-resize",
Self::BottomRight => "se-resize",
Self::Bottom => "s-resize",
Self::BottomLeft => "sw-resize",
Self::Left => "w-resize",
}
}
fn h_edge(&self) -> Option {
match self {
Self::TopLeft => Some(HEdge::Left),
Self::Top => None,
Self::TopRight => Some(HEdge::Right),
Self::Right => Some(HEdge::Right),
Self::BottomRight => Some(HEdge::Right),
Self::Bottom => None,
Self::BottomLeft => Some(HEdge::Left),
Self::Left => Some(HEdge::Left),
}
}
fn v_edge(&self) -> Option {
match self {
Self::TopLeft => Some(VEdge::Top),
Self::Top => Some(VEdge::Top),
Self::TopRight => Some(VEdge::Top),
Self::Right => None,
Self::BottomRight => Some(VEdge::Bottom),
Self::Bottom => Some(VEdge::Bottom),
Self::BottomLeft => Some(VEdge::Bottom),
Self::Left => None,
}
}
}
impl LpAspectRatio {
fn frac(&self) -> Option<(u32, u32)> {
match self {
Self::Free => None,
Self::Original => None,
Self::Square => Some((1, 1)),
Self::R5to4 => Some((5, 4)),
Self::R4to3 => Some((4, 3)),
Self::R3to2 => Some((3, 2)),
Self::R16to9 => Some((16, 9)),
}
}
fn num(&self, crop: &LpEditCrop) -> Option {
let (x, y) = if matches!(self, Self::Original) {
let width = crop.selection().total_width() as u32;
let height = crop.selection().total_height() as u32;
if width > height {
(width, height)
} else {
(height, width)
}
} else {
self.frac()?
};
let result = match crop.orientation() {
LpOrientation::Landscape => x as f32 / y as f32,
LpOrientation::Portrait => y as f32 / x as f32,
};
Some(result)
}
}
trait RectExt: Into {
fn coordinates(self) -> (f32, f32, f32, f32) {
let rect: graphene::Rect = self.into();
(rect.x(), rect.y(), rect.width(), rect.height())
}
}
impl RectExt for graphene::Rect {}
mod imp {
use super::*;
#[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)]
#[properties(wrapper_type = super::LpEditCropSelection)]
#[template(file = "crop_selection.ui")]
pub struct LpEditCropSelection {
#[template_child]
pub(super) space_top: TemplateChild,
#[template_child]
pub(super) space_right: TemplateChild,
#[template_child]
pub(super) space_bottom: TemplateChild,
#[template_child]
pub(super) space_left: TemplateChild,
#[template_child]
pub(super) selection: TemplateChild,
#[template_child]
handle_top_left: TemplateChild,
#[template_child]
handle_top: TemplateChild,
#[template_child]
handle_top_right: TemplateChild,
#[template_child]
handle_right: TemplateChild,
#[template_child]
handle_bottom_right: TemplateChild,
#[template_child]
handle_bottom: TemplateChild,
#[template_child]
handle_bottom_left: TemplateChild,
#[template_child]
handle_left: TemplateChild,
#[template_child]
apply_button: TemplateChild,
#[template_child]
selection_move: TemplateChild,
#[template_child]
selection_overlay: TemplateChild,
/// Set while in drag gesture for changing crop area
pub(super) selection_in_resize: Cell