From b21d1be1bbdb1b7d3e2d762fbf43518e7908c8f1 Mon Sep 17 00:00:00 2001 From: Axel Kappel <69117984+Kl4rry@users.noreply.github.com> Date: Sun, 12 May 2024 21:11:41 +0200 Subject: [PATCH] fix crop dragging --- README.md | 46 ++--- src/app.rs | 2 +- src/app/help.rs | 2 +- src/app/image_view/crop.rs | 337 +++++++++++++++++++++++-------------- src/image_io/load.rs | 2 +- 5 files changed, 238 insertions(+), 151 deletions(-) diff --git a/README.md b/README.md index 4dad37a..26fc3bd 100644 --- a/README.md +++ b/README.md @@ -60,29 +60,29 @@ Simp is a fast and simple GPU-accelerated image manipulation program. ## Keybinds -| Action | Input | -| -------------- | :------------------- | -| Open image | Ctrl + O | -| Save as | Ctrl + S | -| Reload image | F5 | -| New window | Ctrl + N | -| Undo | Ctrl + Z | -| Redo | Ctrl + Y | -| Copy | Ctrl + C | -| Paste | Ctrl + V | -| Resize | Ctrl + R | -| Rotate left | Q | -| Rotate right | E | -| Zoom in | - or Mousewheel up | -| Zoom out | + or Mousewheel down | -| Best fit | Ctrl + B | -| Largest fit | Ctrl + L | -| Crop | Ctrl + X | -| Fullscreen | F11 or F | -| Delete image | Delete | -| 1 - 9 | 100% - 900% Zoom | -| Previous image | A or Left arrow | -| Next image | D or Right arrow | +| Action | Input | +| ---------------- | :------------------- | +| Open image | Ctrl + O | +| Save as | Ctrl + S | +| Reload image | F5 | +| New window | Ctrl + N | +| Undo | Ctrl + Z | +| Redo | Ctrl + Y | +| Copy | Ctrl + C | +| Paste | Ctrl + V | +| Resize | Ctrl + R | +| Rotate left | Q | +| Rotate right | E | +| Zoom in | - or Mousewheel up | +| Zoom out | + or Mousewheel down | +| Best fit | Ctrl + B | +| Largest fit | Ctrl + L | +| Crop | Ctrl + X | +| Fullscreen | F11 or F | +| Delete image | Delete | +| 100% - 900% Zoom | Ctrl + 1 - 9 | +| Previous image | A or Left arrow | +| Next image | D or Right arrow | ## Runtime dependencies The dav1d library is required for AVIF support and libheif is required for heif/heic support. diff --git a/src/app.rs b/src/app.rs index 0b9970b..a150268 100644 --- a/src/app.rs +++ b/src/app.rs @@ -618,7 +618,7 @@ impl App { let nums = [Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9]; for (i, num) in nums.into_iter().enumerate() { if input.consume_shortcut(&KeyboardShortcut { - modifiers: Modifiers::NONE, + modifiers: Modifiers::CTRL, logical_key: num, }) { if let Some(ref mut view) = self.image_view { diff --git a/src/app/help.rs b/src/app/help.rs index f646d40..cb059d5 100644 --- a/src/app/help.rs +++ b/src/app/help.rs @@ -40,7 +40,7 @@ impl App { ("Crop", "Ctrl + X"), ("F11 or F", "Fullscreen"), ("Delete image", "Delete"), - ("1 - 9", "100% - 900% Zoom"), + ("100% - 900% Zoom", "Ctrl + 1 - 9"), ("Previous image", "A or L or Left Arrow"), ("Next image", "D or H or Right Arrow"), ]; diff --git a/src/app/image_view/crop.rs b/src/app/image_view/crop.rs index c392b6b..9700ac7 100644 --- a/src/app/image_view/crop.rs +++ b/src/app/image_view/crop.rs @@ -1,3 +1,4 @@ +use bytemuck::Zeroable; use cgmath::{EuclideanSpace, Point2, Vector2}; use egui::{CursorIcon, PointerButton}; use num_traits::Zero; @@ -14,11 +15,6 @@ pub struct Crop { pub y: String, pub width: String, pub height: String, - drag_m: bool, - drag_l: bool, - drag_r: bool, - drag_t: bool, - drag_b: bool, drag_rem: Vector2, dragging: bool, } @@ -37,11 +33,6 @@ impl Crop { y: String::new(), width: String::new(), height: String::new(), - drag_m: false, - drag_l: false, - drag_r: false, - drag_t: false, - drag_b: false, drag_rem: Vector2::zero(), dragging: false, } @@ -85,49 +76,162 @@ impl Crop { let mut l = false; let mut m = false; + let mut drag_m = false; + let mut drag_l = false; + let mut drag_r = false; + let mut drag_t = false; + let mut drag_b = false; + + const HITBOX_SIZE: f32 = 10.0; + const HITBOX_SIZE2: f32 = HITBOX_SIZE * 2.0; + + // TODO fix this hideous hitmax math code. like why does it mix adding vectors with just add floats!?!? + let top = { - let start = pos - Vector2::new(5.0, 5.0); - let size = Vector2::new(size.x + 10.0, 10.0); + let start = pos - Vector2::new(HITBOX_SIZE - HITBOX_SIZE2, HITBOX_SIZE); + let size = Vector2::new(size.x - HITBOX_SIZE2, HITBOX_SIZE2); egui::Rect::from_min_size(p2(Point2::from_vec(start)).into(), v2(size).into()) }; let bottom = { - let start = Vector2::new(pos.x - 5.0, pos.y + size.y - 5.0); - let size = Vector2::new(size.x + 10.0, 10.0); + let start = Vector2::new( + pos.x - HITBOX_SIZE + HITBOX_SIZE2, + pos.y + size.y - HITBOX_SIZE, + ); + let size = Vector2::new(size.x - HITBOX_SIZE2, HITBOX_SIZE2); egui::Rect::from_min_size(p2(Point2::from_vec(start)).into(), v2(size).into()) }; let left = { - let start = pos - Vector2::new(5.0, 5.0); - let size = Vector2::new(10.0, size.y + 10.0); + let start = pos - Vector2::new(HITBOX_SIZE, HITBOX_SIZE - HITBOX_SIZE2); + let size = Vector2::new(HITBOX_SIZE2, size.y - HITBOX_SIZE2); egui::Rect::from_min_size(p2(Point2::from_vec(start)).into(), v2(size).into()) }; let right = { - let start = Vector2::new(pos.x + size.x - 5.0, pos.y - 5.0); - let size = Vector2::new(10.0, size.y + 10.0); + let start = Vector2::new(pos.x + size.x - HITBOX_SIZE, pos.y + HITBOX_SIZE); + let size = Vector2::new(HITBOX_SIZE2, size.y - HITBOX_SIZE2); + egui::Rect::from_min_size(p2(Point2::from_vec(start)).into(), v2(size).into()) + }; + + let top_left = { + let start = pos - Vector2::new(HITBOX_SIZE, HITBOX_SIZE); + let size = Vector2::new(HITBOX_SIZE2, HITBOX_SIZE2); + egui::Rect::from_min_size(p2(Point2::from_vec(start)).into(), v2(size).into()) + }; + + let top_right = { + let start = pos - Vector2::new(HITBOX_SIZE - size.x, HITBOX_SIZE); + let size = Vector2::new(HITBOX_SIZE2, HITBOX_SIZE2); egui::Rect::from_min_size(p2(Point2::from_vec(start)).into(), v2(size).into()) }; + let bottom_left = { + let start = pos - Vector2::new(HITBOX_SIZE, HITBOX_SIZE - size.y); + let size = Vector2::new(HITBOX_SIZE2, HITBOX_SIZE2); + egui::Rect::from_min_size(p2(Point2::from_vec(start)).into(), v2(size).into()) + }; + + let bottom_right = { + let start = pos - Vector2::new(HITBOX_SIZE - size.x, HITBOX_SIZE - size.y); + let size = Vector2::new(HITBOX_SIZE2, HITBOX_SIZE2); + egui::Rect::from_min_size(p2(Point2::from_vec(start)).into(), v2(size).into()) + }; + + let sense = egui::Sense::hover().union(egui::Sense::drag()); + + let mut drag_delta = egui::Vec2::zeroed(); + { - let res = ui.interact(top, ui.id(), egui::Sense::hover()); + let res = ui.interact(top, "crop top".into(), sense); if res.hovered() { t = true; } + if res.dragged_by(PointerButton::Primary) { + drag_t = true; + drag_delta = res.drag_delta(); + } - let res = ui.interact(bottom, ui.id(), egui::Sense::hover()); + let res = ui.interact(bottom, "crop bottom".into(), sense); if res.hovered() { b = true; } + if res.dragged_by(PointerButton::Primary) { + drag_b = true; + drag_delta = res.drag_delta(); + } + + let res = ui.interact(left, "crop left".into(), sense); + if res.hovered() { + l = true; + } + if res.dragged_by(PointerButton::Primary) { + drag_l = true; + drag_delta = res.drag_delta(); + } + + let res = ui.interact(right, "crop right".into(), sense); + if res.hovered() { + r = true; + } + if res.dragged_by(PointerButton::Primary) { + drag_r = true; + drag_delta = res.drag_delta(); + } + + let res = ui.interact(top_left, "crop top_left".into(), sense); + if res.hovered() { + l = true; + t = true; + } + if res.dragged_by(PointerButton::Primary) { + drag_l = true; + drag_t = true; + drag_delta = res.drag_delta(); + } - let res = ui.interact(left, ui.id(), egui::Sense::hover()); + let res = ui.interact(top_right, "crop top_right".into(), sense); + if res.hovered() { + r = true; + t = true; + } + if res.dragged_by(PointerButton::Primary) { + drag_r = true; + drag_t = true; + drag_delta = res.drag_delta(); + } + + let res = ui.interact(bottom_left, "crop bottom_left".into(), sense); if res.hovered() { l = true; + b = true; + } + if res.dragged_by(PointerButton::Primary) { + drag_l = true; + drag_b = true; + drag_delta = res.drag_delta(); } - let res = ui.interact(right, ui.id(), egui::Sense::hover()); + let res = ui.interact(bottom_right, "crop bottom_right".into(), sense); if res.hovered() { r = true; + b = true; + } + if res.dragged_by(PointerButton::Primary) { + drag_r = true; + drag_b = true; + drag_delta = res.drag_delta(); + } + + let middle = + egui::Rect::from_min_size(p2(Point2::from_vec(pos)).into(), v2(size).into()); + let res = ui.interact(middle.shrink(HITBOX_SIZE), "crop middle".into(), sense); + if res.hovered() { + m = true; + } + if res.dragged_by(PointerButton::Primary) { + drag_m = true; + drag_delta = res.drag_delta(); } } @@ -149,13 +253,6 @@ impl Crop { } } - let middle = - egui::Rect::from_min_size(p2(Point2::from_vec(pos)).into(), v2(size).into()); - let res = ui.interact(middle, ui.id(), egui::Sense::hover()); - if res.hovered() { - m = true; - } - if t && l { ui.ctx().set_cursor_icon(CursorIcon::ResizeNorthWest); } else if t && r { @@ -170,110 +267,100 @@ impl Crop { ui.ctx().set_cursor_icon(CursorIcon::ResizeHorizontal); } else if m { ui.ctx().set_cursor_icon(CursorIcon::Grabbing); + } else { + ui.ctx().set_cursor_icon(CursorIcon::Default); } - let res = ui.interact(egui::Rect::EVERYTHING, ui.id(), egui::Sense::drag()); - if res.dragged_by(PointerButton::Primary) { - if !self.dragging { - self.dragging = true; - self.drag_m = m; - self.drag_l = l; - self.drag_r = r; - self.drag_t = t; - self.drag_b = b; - } else { - let mut delta = Vector2::from(v2(res.drag_delta())) / scale + self.drag_rem; - - if delta.x.abs() > 1.0 { - self.drag_rem.x = delta.x % 1.0; - delta.x = delta.x - delta.x % 1.0; - } else { - self.drag_rem.x = delta.x; - delta.x = 0.0; - } + if !self.dragging && (drag_m || drag_l || drag_r || drag_t || drag_b) { + self.dragging = true; + } else { + let mut delta = Vector2::from(v2(drag_delta)) / scale + self.drag_rem; - if delta.y.abs() > 1.0 { - self.drag_rem.y = delta.y % 1.0; - delta.y = delta.y - delta.y % 1.0; - } else { - self.drag_rem.y = delta.y; - delta.y = 0.0; - } + if delta.x.abs() > 1.0 { + self.drag_rem.x = delta.x % 1.0; + delta.x = delta.x - delta.x % 1.0; + } else { + self.drag_rem.x = delta.x; + delta.x = 0.0; + } - let (new_pos, new_size) = if self.drag_t && self.drag_l { - let new_pos = crop.position + delta; - let new_size = crop.size - delta; - ui.ctx().set_cursor_icon(CursorIcon::ResizeNorthWest); - (new_pos, new_size) - } else if self.drag_t && self.drag_r { - let new_pos = Vector2::new(crop.position.x, crop.position.y + delta.y); - let new_size = Vector2::new(crop.size.x + delta.x, crop.size.y - delta.y); - ui.ctx().set_cursor_icon(CursorIcon::ResizeNorthEast); - (new_pos, new_size) - } else if self.drag_b && self.drag_l { - let new_pos = Vector2::new(crop.position.x + delta.x, crop.position.y); - let new_size = Vector2::new(crop.size.x - delta.x, crop.size.y + delta.y); - ui.ctx().set_cursor_icon(CursorIcon::ResizeSouthWest); - (new_pos, new_size) - } else if self.drag_b && self.drag_r { - let new_pos = crop.position; - let new_size = crop.size + delta; - ui.ctx().set_cursor_icon(CursorIcon::ResizeSouthEast); - (new_pos, new_size) - } else if self.drag_t { - let new_pos = Vector2::new(crop.position.x, crop.position.y + delta.y); - let new_size = Vector2::new(crop.size.x, crop.size.y - delta.y); - ui.ctx().set_cursor_icon(CursorIcon::ResizeVertical); - (new_pos, new_size) - } else if self.drag_b { - let new_pos = Vector2::new(crop.position.x, crop.position.y); - let new_size = Vector2::new(crop.size.x, crop.size.y + delta.y); - ui.ctx().set_cursor_icon(CursorIcon::ResizeVertical); - (new_pos, new_size) - } else if self.drag_l { - let new_pos = Vector2::new(crop.position.x + delta.x, crop.position.y); - let new_size = Vector2::new(crop.size.x - delta.x, crop.size.y); - ui.ctx().set_cursor_icon(CursorIcon::ResizeHorizontal); - (new_pos, new_size) - } else if self.drag_r { - let new_pos = Vector2::new(crop.position.x, crop.position.y); - let new_size = Vector2::new(crop.size.x + delta.x, crop.size.y); - ui.ctx().set_cursor_icon(CursorIcon::ResizeHorizontal); - (new_pos, new_size) - } else if self.drag_m { - crop.position += delta; - let max = (image_size - crop.size).map(|v| v.max(0.0)); - crop.position.x = crop.position.x.clamp(0.0, max.x); - crop.position.y = crop.position.y.clamp(0.0, max.y); - ui.ctx().set_cursor_icon(CursorIcon::Grabbing); - (crop.position, crop.size) - } else { - (crop.position, crop.size) - }; + if delta.y.abs() > 1.0 { + self.drag_rem.y = delta.y % 1.0; + delta.y = delta.y - delta.y % 1.0; + } else { + self.drag_rem.y = delta.y; + delta.y = 0.0; + } - if new_size.x >= 1.0 && new_pos.x >= 0.0 { - crop.position.x = new_pos.x; - crop.size.x = new_size.x; - } - if new_size.y >= 1.0 && new_pos.y >= 0.0 { - crop.position.y = new_pos.y; - crop.size.y = new_size.y; - } + let (new_pos, new_size) = if drag_t && drag_l { + let new_pos = crop.position + delta; + let new_size = crop.size - delta; + ui.ctx().set_cursor_icon(CursorIcon::ResizeNorthWest); + (new_pos, new_size) + } else if drag_t && drag_r { + let new_pos = Vector2::new(crop.position.x, crop.position.y + delta.y); + let new_size = Vector2::new(crop.size.x + delta.x, crop.size.y - delta.y); + ui.ctx().set_cursor_icon(CursorIcon::ResizeNorthEast); + (new_pos, new_size) + } else if drag_b && drag_l { + let new_pos = Vector2::new(crop.position.x + delta.x, crop.position.y); + let new_size = Vector2::new(crop.size.x - delta.x, crop.size.y + delta.y); + ui.ctx().set_cursor_icon(CursorIcon::ResizeSouthWest); + (new_pos, new_size) + } else if drag_b && drag_r { + let new_pos = crop.position; + let new_size = crop.size + delta; + ui.ctx().set_cursor_icon(CursorIcon::ResizeSouthEast); + (new_pos, new_size) + } else if drag_t { + let new_pos = Vector2::new(crop.position.x, crop.position.y + delta.y); + let new_size = Vector2::new(crop.size.x, crop.size.y - delta.y); + ui.ctx().set_cursor_icon(CursorIcon::ResizeVertical); + (new_pos, new_size) + } else if drag_b { + let new_pos = Vector2::new(crop.position.x, crop.position.y); + let new_size = Vector2::new(crop.size.x, crop.size.y + delta.y); + ui.ctx().set_cursor_icon(CursorIcon::ResizeVertical); + (new_pos, new_size) + } else if drag_l { + let new_pos = Vector2::new(crop.position.x + delta.x, crop.position.y); + let new_size = Vector2::new(crop.size.x - delta.x, crop.size.y); + ui.ctx().set_cursor_icon(CursorIcon::ResizeHorizontal); + (new_pos, new_size) + } else if drag_r { + let new_pos = Vector2::new(crop.position.x, crop.position.y); + let new_size = Vector2::new(crop.size.x + delta.x, crop.size.y); + ui.ctx().set_cursor_icon(CursorIcon::ResizeHorizontal); + (new_pos, new_size) + } else if drag_m { + crop.position += delta; + let max = (image_size - crop.size).map(|v| v.max(0.0)); + crop.position.x = crop.position.x.clamp(0.0, max.x); + crop.position.y = crop.position.y.clamp(0.0, max.y); + ui.ctx().set_cursor_icon(CursorIcon::Grabbing); + (crop.position, crop.size) + } else { + (crop.position, crop.size) + }; - self.x = crop.x().to_string(); - self.y = crop.y().to_string(); - self.width = crop.width().to_string(); - self.height = crop.height().to_string(); + if new_size.x >= 1.0 && new_pos.x >= 0.0 { + crop.position.x = new_pos.x; + crop.size.x = new_size.x; } - } else { - self.drag_m = false; - self.drag_l = false; - self.drag_r = false; - self.drag_t = false; - self.drag_b = false; - self.dragging = false; + if new_size.y >= 1.0 && new_pos.y >= 0.0 { + crop.position.y = new_pos.y; + crop.size.y = new_size.y; + } + + self.x = crop.x().to_string(); + self.y = crop.y().to_string(); + self.width = crop.width().to_string(); + self.height = crop.height().to_string(); } + drag_m || drag_l || drag_r || drag_t || drag_b + } else { + self.dragging = false; + false } - self.drag_m || self.drag_l || self.drag_r || self.drag_t || self.drag_b } } diff --git a/src/image_io/load.rs b/src/image_io/load.rs index c4c998e..0198762 100644 --- a/src/image_io/load.rs +++ b/src/image_io/load.rs @@ -246,4 +246,4 @@ pub fn load_heif(bytes: &[u8]) -> Option> { let _ = bytes; None } -} \ No newline at end of file +}