diff --git a/croppa/main.py b/croppa/main.py index 5130fb7..334a4fe 100644 --- a/croppa/main.py +++ b/croppa/main.py @@ -157,6 +157,7 @@ class VideoEditor: 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 + self.crop_border_drag_outside_side = None # 'left', 'right', 'top', 'bottom' if outside # Zoom settings self.zoom_factor = 1.0 @@ -3126,12 +3127,25 @@ class VideoEditor: # Check if cursor is inside crop area inside_crop = sx1 <= x <= sx2 and sy1 <= y <= sy2 + # Determine which side we're on if outside (for better contraction logic) + outside_side = None + if not inside_crop: + if x < sx1: + outside_side = 'left' + elif x > sx2: + outside_side = 'right' + elif y < sy1: + outside_side = 'top' + elif y > sy2: + outside_side = 'bottom' + if event == cv2.EVENT_LBUTTONDOWN: # 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 + self.crop_border_drag_outside_side = outside_side elif event == cv2.EVENT_MOUSEMOVE and self.crop_border_dragging: 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 @@ -3173,17 +3187,21 @@ class VideoEditor: 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) + # Outside crop: always contract based on drag direction + # Drag left -> contract right (move right border left) + # Drag right -> contract left (move left border right) 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 + # Dragging right -> move left border right (contract 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: # Vertical movement if self.crop_border_drag_inside: @@ -3201,17 +3219,21 @@ class VideoEditor: 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) + # Outside crop: always contract based on drag direction + # Drag up -> contract bottom (move bottom border up) + # Drag down -> contract top (move top border down) 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 + # Dragging down -> move top border down (contract top) + 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 # Convert back from rotated to original frame coords self._set_crop_from_rotated_rect((new_x, new_y, new_w, new_h)) @@ -3222,6 +3244,7 @@ class VideoEditor: self.crop_border_drag_start_pos = None self.crop_border_drag_start_rect = None self.crop_border_drag_inside = None + self.crop_border_drag_outside_side = None self.save_state() # Handle crop selection (Shift + click and drag)