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)