Add crop border dragging functionality to VideoEditor class
Implement mouse event handling for dragging crop borders, allowing users to adjust crop dimensions interactively. Introduce methods to detect border proximity and update crop rectangle based on mouse movements, ensuring proper clamping to frame bounds. This enhances the user experience by providing more precise control over cropping in video editing.
This commit is contained in:
109
croppa/main.py
109
croppa/main.py
@@ -806,6 +806,10 @@ class VideoEditor:
|
||||
self.crop_start_point = None
|
||||
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
|
||||
|
||||
# Zoom settings
|
||||
self.zoom_factor = 1.0
|
||||
@@ -3767,6 +3771,85 @@ class VideoEditor:
|
||||
self.mouse_dragging = False
|
||||
return
|
||||
|
||||
# Handle crop border dragging (only when Shift is NOT pressed)
|
||||
if not (flags & cv2.EVENT_FLAG_SHIFTKEY) and self.crop_rect:
|
||||
border_threshold = 10 # pixels
|
||||
|
||||
# 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 near
|
||||
def detect_border():
|
||||
if abs(x - sx1) < border_threshold and sy1 <= y <= sy2:
|
||||
return 'left'
|
||||
elif abs(x - sx2) < border_threshold and sy1 <= y <= sy2:
|
||||
return 'right'
|
||||
elif abs(y - sy1) < border_threshold and sx1 <= x <= sx2:
|
||||
return 'top'
|
||||
elif abs(y - sy2) < border_threshold and sx1 <= x <= sx2:
|
||||
return 'bottom'
|
||||
return None
|
||||
|
||||
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)
|
||||
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:
|
||||
# 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)
|
||||
curr_rx, curr_ry = self._map_screen_to_rotated(x, y)
|
||||
|
||||
dx_r = curr_rx - start_rx
|
||||
dy_r = curr_ry - start_ry
|
||||
|
||||
# Adjust the appropriate border
|
||||
new_x, new_y, new_w, new_h = self.crop_border_drag_start_rect
|
||||
|
||||
# Get rotated frame dimensions
|
||||
if self.rotation_angle in (90, 270):
|
||||
rot_w, rot_h = self.frame_height, self.frame_width
|
||||
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
|
||||
|
||||
# Convert back from rotated to original frame coords
|
||||
self._set_crop_from_rotated_rect((new_x, new_y, new_w, new_h))
|
||||
self.clear_transformation_cache()
|
||||
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.save_state()
|
||||
|
||||
# Handle crop selection (Shift + click and drag)
|
||||
if flags & cv2.EVENT_FLAG_SHIFTKEY:
|
||||
|
||||
@@ -4044,6 +4127,32 @@ class VideoEditor:
|
||||
direction = 1 if flags > 0 else -1
|
||||
self.seek_video_exact_frame(direction)
|
||||
|
||||
def _set_crop_from_rotated_rect(self, rotated_rect):
|
||||
"""Set crop_rect from a rectangle in rotated frame coordinates"""
|
||||
rx, ry, rw, rh = rotated_rect
|
||||
|
||||
# Convert from rotated coords to original frame coords
|
||||
# Rotation is applied clockwise: 90° means ROTATE_90_CLOCKWISE
|
||||
if self.rotation_angle == 0:
|
||||
self.crop_rect = (rx, ry, rw, rh)
|
||||
elif self.rotation_angle == 90:
|
||||
# 90° clockwise: (rx, ry, rw, rh) rotated -> (ry, frame_height - rx - rw, rh, rw) original
|
||||
self.crop_rect = (ry, self.frame_height - rx - rw, rh, rw)
|
||||
elif self.rotation_angle == 180:
|
||||
# 180°: (rx, ry, rw, rh) rotated -> (frame_width - rx - rw, frame_height - ry - rh, rw, rh) original
|
||||
self.crop_rect = (self.frame_width - rx - rw, self.frame_height - ry - rh, rw, rh)
|
||||
elif self.rotation_angle == 270:
|
||||
# 270° (90° counterclockwise): (rx, ry, rw, rh) rotated -> (frame_width - ry - rh, rx, rh, rw) original
|
||||
self.crop_rect = (self.frame_width - ry - rh, rx, rh, rw)
|
||||
|
||||
# Clamp to frame bounds
|
||||
x, y, w, h = self.crop_rect
|
||||
x = max(0, min(x, self.frame_width - 1))
|
||||
y = max(0, min(y, self.frame_height - 1))
|
||||
w = min(w, self.frame_width - x)
|
||||
h = min(h, self.frame_height - y)
|
||||
self.crop_rect = (x, y, w, h)
|
||||
|
||||
def set_crop_from_screen_coords(self, screen_rect):
|
||||
"""Convert screen coordinates to video frame coordinates and set crop"""
|
||||
x, y, w, h = screen_rect
|
||||
|
||||
Reference in New Issue
Block a user