From 91ce544569b03226756ad5270f970eb414757488 Mon Sep 17 00:00:00 2001 From: Isaiah Odhner Date: Tue, 25 Apr 2023 16:51:36 -0400 Subject: [PATCH] Move selection box with arrow keys Note: This doesn't cut out the selection if you haven't dragged it yet, with the mouse. It should probably be equivalent to mouse dragging, but this current behavior could be useful too. --- paint.py | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/paint.py b/paint.py index 06bbbbf..da01348 100755 --- a/paint.py +++ b/paint.py @@ -2350,18 +2350,8 @@ class PaintApp(App[None]): self.selection_drag_offset.x + event.mouse_move_event.x, self.selection_drag_offset.y + event.mouse_move_event.y, ) - # Constrain to have at least one row/column within the bounds of the document. - # This ensures you can always drag the selection back into the document, - # but doesn't limit you from positioning it partially outside. - # (It is useless to position it _completely_ outside, since you could just delete it.) - offset = ( - max(1-sel.region.width, min(self.image.width - 1, offset[0])), - max(1-sel.region.height, min(self.image.height - 1, offset[1])), - ) - old_region = sel.region - sel.region = Region.from_offset(offset, sel.region.size) - combined_region = old_region.union(sel.region) - self.canvas.refresh_scaled_region(combined_region) + # Handles constraints and canvas refresh. + self.move_selection_absolute(*offset) elif self.selected_tool == Tool.free_form_select: self.tool_points.append(Offset(event.mouse_move_event.x, event.mouse_move_event.y)) self.make_preview(self.draw_current_free_form_select_polyline, show_dimensions_in_status_bar=True) @@ -2556,10 +2546,42 @@ class PaintApp(App[None]): # Not reliably unset, so might as well not rely on it. (See early returns above.) # self.mouse_at_start = None + def move_selection_absolute(self, x: int, y: int) -> None: + """Positions the selection relative to the document.""" + # Constrain to have at least one row/column within the bounds of the document. + # This ensures you can always drag the selection back into the document, + # but doesn't limit you from positioning it partially outside. + # (It is useless to position it _completely_ outside, since you could just delete it.) + sel = self.image.selection + assert sel is not None, "move_selection_absolute called without a selection" + offset = Offset( + max(1-sel.region.width, min(self.image.width - 1, x)), + max(1-sel.region.height, min(self.image.height - 1, y)), + ) + old_region = sel.region + sel.region = Region.from_offset(offset, sel.region.size) + combined_region = old_region.union(sel.region) + self.canvas.refresh_scaled_region(combined_region) + + def move_selection_relative(self, delta_x: int, delta_y: int) -> None: + """Moves the selection relative to its current position.""" + sel = self.image.selection + assert sel is not None, "move_selection_relative called without a selection" + self.move_selection_absolute(sel.region.offset.x + delta_x, sel.region.offset.y + delta_y) + def on_key(self, event: events.Key) -> None: """Called when the user presses a key.""" + key = event.key + if self.image.selection and not self.image.selection.textbox_mode: + if key == "left": + self.move_selection_relative(-1, 0) + elif key == "right": + self.move_selection_relative(1, 0) + elif key == "up": + self.move_selection_relative(0, -1) + elif key == "down": + self.move_selection_relative(0, 1) if self.image.selection and self.image.selection.textbox_mode: - key = event.key assert self.image.selection.contained_image is not None, "Textbox mode should always have contained_image, to edit as text." # TODO: delete selected text if any, when typing # Note: Don't forget to set self.image.selection.textbox_edited = True