edit/crop: Track selection correctly on widget resize
This commit is contained in:
parent
5c31272605
commit
7611b11b55
4 changed files with 85 additions and 45 deletions
|
|
@ -84,6 +84,11 @@ mod imp {
|
||||||
|
|
||||||
#[property(get, set)]
|
#[property(get, set)]
|
||||||
child: OnceCell<gtk::Widget>,
|
child: OnceCell<gtk::Widget>,
|
||||||
|
|
||||||
|
/// Last selected crop area
|
||||||
|
crop_area_image_coord: Cell<Option<(u32, u32, u32, u32)>>,
|
||||||
|
|
||||||
|
last_allocation: Cell<(i32, i32, i32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
|
|
@ -183,15 +188,23 @@ mod imp {
|
||||||
|
|
||||||
obj.child().allocate(width, height, baseline, None);
|
obj.child().allocate(width, height, baseline, None);
|
||||||
|
|
||||||
let (x, y, width, height) = (
|
// Adjust position and size for crop selection after widget resize
|
||||||
image.image_rendering_x(),
|
if self.last_allocation.get() != (width, height, baseline) {
|
||||||
image.image_rendering_y(),
|
self.last_allocation.replace((width, height, baseline));
|
||||||
image.image_rendering_width(),
|
|
||||||
image.image_rendering_height(),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.selection.ensure_initialized(x, y, width, height);
|
let (x, y, width, height) = (
|
||||||
self.selection.set_size(x, y, width, height);
|
image.image_rendering_x(),
|
||||||
|
image.image_rendering_y(),
|
||||||
|
image.image_rendering_width(),
|
||||||
|
image.image_rendering_height(),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.selection.ensure_initialized(x, y, width, height);
|
||||||
|
self.selection.set_image_area(x, y, width, height);
|
||||||
|
|
||||||
|
let (x, y, width, height) = self.crop_area_widget_coord();
|
||||||
|
self.selection.set_crop_size(x, y, width, height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn measure(&self, orientation: gtk::Orientation, for_size: i32) -> (i32, i32, i32, i32) {
|
fn measure(&self, orientation: gtk::Orientation, for_size: i32) -> (i32, i32, i32, i32) {
|
||||||
|
|
@ -202,12 +215,17 @@ mod imp {
|
||||||
|
|
||||||
impl LpEditCrop {
|
impl LpEditCrop {
|
||||||
fn selection_changed(&self) {
|
fn selection_changed(&self) {
|
||||||
let apply_sensitive = self.selection.is_copped();
|
let crop_area_image_coord = self.crop_area_image_coord();
|
||||||
|
if self.selection.is_in_user_change() {
|
||||||
|
self.crop_area_image_coord.replace(crop_area_image_coord);
|
||||||
|
}
|
||||||
|
|
||||||
|
let apply_sensitive = self.selection.is_cropped();
|
||||||
self.apply_crop.set_visible(apply_sensitive);
|
self.apply_crop.set_visible(apply_sensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn crop_area_image_coord(&self) -> Option<(u32, u32, u32, u32)> {
|
fn crop_area_image_coord(&self) -> Option<(u32, u32, u32, u32)> {
|
||||||
if !self.selection.is_copped() {
|
if !self.selection.is_cropped() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,6 +250,18 @@ mod imp {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn crop_area_widget_coord(&self) -> (f64, f64, f64, f64) {
|
||||||
|
let size = self.image.image_size();
|
||||||
|
let (x, y, w, h) =
|
||||||
|
self.crop_area_image_coord
|
||||||
|
.get()
|
||||||
|
.unwrap_or((0, 0, size.0 as u32, size.1 as u32));
|
||||||
|
let (x, y) = self.image.img_to_draw_coord((x as f64, y as f64));
|
||||||
|
let (w, h) = self.image.img_to_draw_coord((w as f64, h as f64));
|
||||||
|
|
||||||
|
(x.round(), y.round(), w.round(), h.round())
|
||||||
|
}
|
||||||
|
|
||||||
fn reset_selection(&self) {
|
fn reset_selection(&self) {
|
||||||
let image = &self.image;
|
let image = &self.image;
|
||||||
|
|
||||||
|
|
@ -245,7 +275,7 @@ mod imp {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_crop(&self) {
|
fn apply_crop(&self) {
|
||||||
if let Some(crop) = self.crop_area_image_coord() {
|
if let Some(crop) = self.crop_area_image_coord.get() {
|
||||||
self.add_operation(Operation::Clip(crop));
|
self.add_operation(Operation::Clip(crop));
|
||||||
|
|
||||||
self.reset_selection();
|
self.reset_selection();
|
||||||
|
|
|
||||||
|
|
@ -155,12 +155,12 @@ mod imp {
|
||||||
handle_bottom_left: TemplateChild<adw::Bin>,
|
handle_bottom_left: TemplateChild<adw::Bin>,
|
||||||
|
|
||||||
#[template_child]
|
#[template_child]
|
||||||
selection_drag: TemplateChild<gtk::GestureDrag>,
|
selection_move: TemplateChild<gtk::GestureDrag>,
|
||||||
|
|
||||||
/// Set while in drag gesture for changing crop area
|
/// Set while in drag gesture for changing crop area
|
||||||
selection_in_resize: Cell<Option<InResize>>,
|
pub(super) selection_in_resize: Cell<Option<InResize>>,
|
||||||
/// Set while in drag gesture for moving crop area
|
/// Set while in drag gesture for moving crop area
|
||||||
selection_in_move: Cell<Option<InMove>>,
|
pub(super) selection_in_move: Cell<Option<InMove>>,
|
||||||
|
|
||||||
// Animates changes between different fixed aspect ratios
|
// Animates changes between different fixed aspect ratios
|
||||||
aspect_ratio_animation: OnceCell<adw::TimedAnimation>,
|
aspect_ratio_animation: OnceCell<adw::TimedAnimation>,
|
||||||
|
|
@ -175,13 +175,13 @@ mod imp {
|
||||||
|
|
||||||
pub(super) initialized: Cell<bool>,
|
pub(super) initialized: Cell<bool>,
|
||||||
|
|
||||||
#[property(get, set=Self::set_crop_x)]
|
#[property(get, set=Self::set_crop_x, explicit_notify)]
|
||||||
crop_x: Cell<f32>,
|
crop_x: Cell<f32>,
|
||||||
#[property(get, set=Self::set_crop_y)]
|
#[property(get, set=Self::set_crop_y, explicit_notify)]
|
||||||
crop_y: Cell<f32>,
|
crop_y: Cell<f32>,
|
||||||
#[property(get, set=Self::set_crop_width)]
|
#[property(get, set=Self::set_crop_width, explicit_notify)]
|
||||||
crop_width: Cell<f32>,
|
crop_width: Cell<f32>,
|
||||||
#[property(get, set=Self::set_crop_height)]
|
#[property(get, set=Self::set_crop_height, explicit_notify)]
|
||||||
crop_height: Cell<f32>,
|
crop_height: Cell<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,7 +231,7 @@ mod imp {
|
||||||
.connect_orientation_notify(|x| x.selection().imp().on_aspect_ratio_changed());
|
.connect_orientation_notify(|x| x.selection().imp().on_aspect_ratio_changed());
|
||||||
|
|
||||||
// Drag begin
|
// Drag begin
|
||||||
self.selection_drag.connect_drag_begin(glib::clone!(
|
self.selection_move.connect_drag_begin(glib::clone!(
|
||||||
#[weak]
|
#[weak]
|
||||||
obj,
|
obj,
|
||||||
move |gesture, _, _| {
|
move |gesture, _, _| {
|
||||||
|
|
@ -272,7 +272,7 @@ mod imp {
|
||||||
));
|
));
|
||||||
|
|
||||||
// Drag moved
|
// Drag moved
|
||||||
self.selection_drag.connect_drag_update(glib::clone!(
|
self.selection_move.connect_drag_update(glib::clone!(
|
||||||
#[weak]
|
#[weak]
|
||||||
obj,
|
obj,
|
||||||
move |_, x, y| {
|
move |_, x, y| {
|
||||||
|
|
@ -308,7 +308,7 @@ mod imp {
|
||||||
));
|
));
|
||||||
|
|
||||||
// Drag finished
|
// Drag finished
|
||||||
self.selection_drag.connect_drag_end(glib::clone!(
|
self.selection_move.connect_drag_end(glib::clone!(
|
||||||
#[weak]
|
#[weak]
|
||||||
obj,
|
obj,
|
||||||
move |_, _, _| {
|
move |_, _, _| {
|
||||||
|
|
@ -673,23 +673,25 @@ mod imp {
|
||||||
/// Set x coordinate of crop selection rectangle origin
|
/// Set x coordinate of crop selection rectangle origin
|
||||||
pub(super) fn set_crop_x(&self, x: f32) {
|
pub(super) fn set_crop_x(&self, x: f32) {
|
||||||
if x < 0. {
|
if x < 0. {
|
||||||
eprintln!("Tried to set x coordinate to {x}");
|
log::error!("Tried to set x coordinate to {x}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.crop_x.set(x);
|
self.crop_x.set(x);
|
||||||
self.space_top_left.set_width_request(x.round() as i32);
|
self.space_top_left.set_width_request(x.round() as i32);
|
||||||
|
self.obj().notify_crop_x();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set y coordinate of crop selection rectangle origin
|
/// Set y coordinate of crop selection rectangle origin
|
||||||
pub(super) fn set_crop_y(&self, y: f32) {
|
pub(super) fn set_crop_y(&self, y: f32) {
|
||||||
if y < 0. {
|
if y < 0. {
|
||||||
eprintln!("Tried to set y coordinate to {y}");
|
log::error!("Tried to set y coordinate to {y}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.crop_y.set(y);
|
self.crop_y.set(y);
|
||||||
self.space_top_left.set_height_request(y.round() as i32);
|
self.space_top_left.set_height_request(y.round() as i32);
|
||||||
|
self.obj().notify_crop_y();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set width of crop selection rectangle
|
/// Set width of crop selection rectangle
|
||||||
|
|
@ -701,6 +703,7 @@ mod imp {
|
||||||
|
|
||||||
self.crop_width.set(width);
|
self.crop_width.set(width);
|
||||||
self.selection.set_width_request(width.round() as i32);
|
self.selection.set_width_request(width.round() as i32);
|
||||||
|
self.obj().notify_crop_width();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set height of crop selection rectangle
|
/// Set height of crop selection rectangle
|
||||||
|
|
@ -712,6 +715,7 @@ mod imp {
|
||||||
|
|
||||||
self.crop_height.set(height);
|
self.crop_height.set(height);
|
||||||
self.selection.set_height_request(height.round() as i32);
|
self.selection.set_height_request(height.round() as i32);
|
||||||
|
self.obj().notify_crop_height();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Width of area in which the crop selection can exist
|
/// Width of area in which the crop selection can exist
|
||||||
|
|
@ -770,38 +774,34 @@ impl LpEditCropSelection {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&self, x: f64, y: f64, width: f64, height: f64) {
|
pub fn reset(&self, x: f64, y: f64, width: f64, height: f64) {
|
||||||
let imp = self.imp();
|
self.set_image_area(x, y, width, height);
|
||||||
|
self.set_crop_size(0., 0., width, height);
|
||||||
self.set_margin_start(x as i32);
|
|
||||||
self.set_margin_top(y as i32);
|
|
||||||
self.set_total_width(width as i32);
|
|
||||||
self.set_total_height(height as i32);
|
|
||||||
|
|
||||||
imp.set_crop_x(0.);
|
|
||||||
imp.set_crop_y(0.);
|
|
||||||
imp.set_crop_width(width as f32);
|
|
||||||
imp.set_crop_height(height as f32);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_size(&self, x: f64, y: f64, width: f64, height: f64) {
|
pub fn set_image_area(&self, x: f64, y: f64, width: f64, height: f64) {
|
||||||
let ratio = width as f32 / self.total_width() as f32;
|
|
||||||
|
|
||||||
self.set_crop_x(self.crop_x() * ratio);
|
|
||||||
self.set_crop_y(self.crop_y() * ratio);
|
|
||||||
self.set_crop_width(self.crop_width() * ratio);
|
|
||||||
self.set_crop_height(self.crop_height() * ratio);
|
|
||||||
|
|
||||||
self.set_margin_start(x as i32);
|
self.set_margin_start(x as i32);
|
||||||
self.set_margin_top(y as i32);
|
self.set_margin_top(y as i32);
|
||||||
self.set_total_width(width as i32);
|
self.set_total_width(width as i32);
|
||||||
self.set_total_height(height as i32);
|
self.set_total_height(height as i32);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_copped(&self) -> bool {
|
pub fn set_crop_size(&self, x: f64, y: f64, width: f64, height: f64) {
|
||||||
|
self.set_crop_x(x as f32);
|
||||||
|
self.set_crop_y(y as f32);
|
||||||
|
self.set_crop_width(width as f32);
|
||||||
|
self.set_crop_height(height as f32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_cropped(&self) -> bool {
|
||||||
let untouched = self.crop_x() == 0.
|
let untouched = self.crop_x() == 0.
|
||||||
&& self.crop_y() == 0.
|
&& self.crop_y() == 0.
|
||||||
&& self.crop_width() as i32 == self.total_width()
|
&& self.crop_width() as i32 == self.total_width()
|
||||||
&& self.crop_height() as i32 == self.total_height();
|
&& self.crop_height() as i32 == self.total_height();
|
||||||
!untouched
|
!untouched
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_in_user_change(&self) -> bool {
|
||||||
|
self.imp().selection_in_resize.get().is_some()
|
||||||
|
|| self.imp().selection_in_move.get().is_some()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@
|
||||||
<property name="halign">start</property>
|
<property name="halign">start</property>
|
||||||
<property name="valign">start</property>
|
<property name="valign">start</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkGestureDrag" id="selection_drag">
|
<object class="GtkGestureDrag" id="selection_move" />
|
||||||
</object>
|
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwBin" id="space_top_left">
|
<object class="AdwBin" id="space_top_left">
|
||||||
|
|
|
||||||
|
|
@ -387,4 +387,15 @@ impl LpImage {
|
||||||
|
|
||||||
(x, y)
|
(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert image coordinates to drawn image coordinates
|
||||||
|
pub fn img_to_draw_coord(&self, (cur_x, cur_y): (f64, f64)) -> (f64, f64) {
|
||||||
|
let imp = self.imp();
|
||||||
|
let zoom = imp.applicable_zoom();
|
||||||
|
|
||||||
|
let x = cur_x * zoom;
|
||||||
|
let y = cur_y * zoom;
|
||||||
|
|
||||||
|
(x, y)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue