From bf32bb98ae09c4f866f22c0fd424e0366fa13ec1 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Tue, 23 Dec 2025 09:34:39 +0100 Subject: [PATCH] Refactor crop border dragging logic to improve user interaction Enhance the crop border dragging functionality in the VideoEditor class by introducing logic to determine whether the drag starts inside or outside the crop area. Adjustments to crop dimensions are now based on the direction of movement and the position relative to the crop area, ensuring a more intuitive user experience. This update also includes the removal of the previous edge detection mechanism, streamlining the dragging process. --- croppa/main.py | 127 ++++++++++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 55 deletions(-) diff --git a/croppa/main.py b/croppa/main.py index 7c5f601..5130fb7 100644 --- a/croppa/main.py +++ b/croppa/main.py @@ -154,9 +154,9 @@ class VideoEditor: self.crop_preview_rect = None self.crop_history = [] # For undo self.crop_border_dragging = False - self.crop_border_drag_edge = None # 'left', 'right', 'top', 'bottom' self.crop_border_drag_start_pos = None # (screen_x, screen_y) when drag started self.crop_border_drag_start_rect = None # (x, y, w, h) in rotated coords when drag started + self.crop_border_drag_inside = None # True if drag started inside crop area, False if outside # Zoom settings self.zoom_factor = 1.0 @@ -3118,46 +3118,22 @@ class VideoEditor: # Handle crop border dragging (only when Shift and Ctrl are NOT pressed) if not (flags & cv2.EVENT_FLAG_SHIFTKEY) and not (flags & cv2.EVENT_FLAG_CTRLKEY) and self.crop_rect: - border_threshold = self.CROP_BORDER_DETECTION_MAX_DISTANCE - # Get effective crop in rotated coords and map to screen eff_x, eff_y, eff_w, eff_h = self._get_effective_crop_rect_for_frame(getattr(self, 'current_frame', 0)) sx1, sy1 = self._map_rotated_to_screen(eff_x, eff_y) sx2, sy2 = self._map_rotated_to_screen(eff_x + eff_w, eff_y + eff_h) - # Detect which border is closest to cursor - def detect_border(): - # Calculate distances to each border - dist_left = abs(x - sx1) if sy1 <= y <= sy2 else float('inf') - dist_right = abs(x - sx2) if sy1 <= y <= sy2 else float('inf') - dist_top = abs(y - sy1) if sx1 <= x <= sx2 else float('inf') - dist_bottom = abs(y - sy2) if sx1 <= x <= sx2 else float('inf') - - # Find closest border - distances = { - 'left': dist_left, - 'right': dist_right, - 'top': dist_top, - 'bottom': dist_bottom - } - - closest_edge = min(distances, key=distances.get) - closest_dist = distances[closest_edge] - - # Only return if within threshold - if closest_dist < border_threshold: - return closest_edge - return None + # Check if cursor is inside crop area + inside_crop = sx1 <= x <= sx2 and sy1 <= y <= sy2 if event == cv2.EVENT_LBUTTONDOWN: - edge = detect_border() - if edge: - self.crop_border_dragging = True - self.crop_border_drag_edge = edge - self.crop_border_drag_start_pos = (x, y) - self.crop_border_drag_start_rect = (eff_x, eff_y, eff_w, eff_h) + # Start dragging - record position and whether we're inside/outside + self.crop_border_dragging = True + self.crop_border_drag_start_pos = (x, y) + self.crop_border_drag_start_rect = (eff_x, eff_y, eff_w, eff_h) + self.crop_border_drag_inside = inside_crop elif event == cv2.EVENT_MOUSEMOVE and self.crop_border_dragging: - if self.crop_border_drag_edge and self.crop_border_drag_start_pos and self.crop_border_drag_start_rect: + if self.crop_border_drag_start_pos and self.crop_border_drag_start_rect and self.crop_border_drag_inside is not None: # Convert mouse movement from screen to rotated coords start_sx, start_sy = self.crop_border_drag_start_pos start_rx, start_ry = self._map_screen_to_rotated(start_sx, start_sy) @@ -3166,7 +3142,11 @@ class VideoEditor: dx_r = curr_rx - start_rx dy_r = curr_ry - start_ry - # Adjust the appropriate border + # Determine primary direction (horizontal vs vertical) + abs_dx = abs(dx_r) + abs_dy = abs(dy_r) + + # Adjust the appropriate border based on direction and inside/outside new_x, new_y, new_w, new_h = self.crop_border_drag_start_rect # Get rotated frame dimensions @@ -3175,26 +3155,63 @@ class VideoEditor: else: rot_w, rot_h = self.frame_width, self.frame_height - if self.crop_border_drag_edge == 'left': - new_x = max(0, new_x + dx_r) - new_w = new_w - dx_r - if new_w < 10: - new_w = 10 - new_x = self.crop_border_drag_start_rect[0] + self.crop_border_drag_start_rect[2] - 10 - elif self.crop_border_drag_edge == 'right': - new_w = max(10, new_w + dx_r) - if new_x + new_w > rot_w: - new_w = rot_w - new_x - elif self.crop_border_drag_edge == 'top': - new_y = max(0, new_y + dy_r) - new_h = new_h - dy_r - if new_h < 10: - new_h = 10 - new_y = self.crop_border_drag_start_rect[1] + self.crop_border_drag_start_rect[3] - 10 - elif self.crop_border_drag_edge == 'bottom': - new_h = max(10, new_h + dy_r) - if new_y + new_h > rot_h: - new_h = rot_h - new_y + # Determine which border to adjust based on movement direction + if abs_dx > abs_dy: + # Horizontal movement + if self.crop_border_drag_inside: + # Inside crop: drag left -> adjust left border, drag right -> adjust right border + if dx_r < 0: + # Dragging left -> move left border left (expand left) + new_x = max(0, new_x + dx_r) + new_w = new_w - dx_r + if new_w < self.CROP_MIN_SIZE: + new_w = self.CROP_MIN_SIZE + new_x = self.crop_border_drag_start_rect[0] + self.crop_border_drag_start_rect[2] - self.CROP_MIN_SIZE + else: + # Dragging right -> move right border right (expand right) + new_w = max(self.CROP_MIN_SIZE, new_w + dx_r) + if new_x + new_w > rot_w: + new_w = rot_w - new_x + else: + # Outside crop: drag left -> adjust right border left (contract), drag right -> adjust right border right (expand) + if dx_r < 0: + # Dragging left -> move right border left (contract right) + new_w = max(self.CROP_MIN_SIZE, new_w + dx_r) + if new_w < self.CROP_MIN_SIZE: + new_w = self.CROP_MIN_SIZE + else: + # Dragging right -> move right border right (expand right) + new_w = max(self.CROP_MIN_SIZE, new_w + dx_r) + if new_x + new_w > rot_w: + new_w = rot_w - new_x + else: + # Vertical movement + if self.crop_border_drag_inside: + # Inside crop: drag up -> adjust top border, drag down -> adjust bottom border + if dy_r < 0: + # Dragging up -> move top border up (expand up) + new_y = max(0, new_y + dy_r) + new_h = new_h - dy_r + if new_h < self.CROP_MIN_SIZE: + new_h = self.CROP_MIN_SIZE + new_y = self.crop_border_drag_start_rect[1] + self.crop_border_drag_start_rect[3] - self.CROP_MIN_SIZE + else: + # Dragging down -> move bottom border down (expand down) + new_h = max(self.CROP_MIN_SIZE, new_h + dy_r) + if new_y + new_h > rot_h: + new_h = rot_h - new_y + else: + # Outside crop: drag up -> adjust bottom border up (contract), drag down -> adjust bottom border down (expand) + if dy_r < 0: + # Dragging up -> move bottom border up (contract bottom) + new_h = max(self.CROP_MIN_SIZE, new_h + dy_r) + if new_h < self.CROP_MIN_SIZE: + new_h = self.CROP_MIN_SIZE + else: + # Dragging down -> move bottom border down (expand down) + new_h = max(self.CROP_MIN_SIZE, new_h + dy_r) + if new_y + new_h > rot_h: + new_h = rot_h - new_y # Convert back from rotated to original frame coords self._set_crop_from_rotated_rect((new_x, new_y, new_w, new_h)) @@ -3202,9 +3219,9 @@ class VideoEditor: self.display_current_frame() elif event == cv2.EVENT_LBUTTONUP and self.crop_border_dragging: self.crop_border_dragging = False - self.crop_border_drag_edge = None self.crop_border_drag_start_pos = None self.crop_border_drag_start_rect = None + self.crop_border_drag_inside = None self.save_state() # Handle crop selection (Shift + click and drag)