refactor: Improve multi-line text selection logic and handling
This commit is contained in:
parent
141e69547c
commit
addc9b09ed
85
gtm2.py
85
gtm2.py
|
|
@ -278,25 +278,41 @@ def draw_selection(stdscr, state):
|
||||||
return
|
return
|
||||||
|
|
||||||
start_x, start_y = state.selection_start_coord
|
start_x, start_y = state.selection_start_coord
|
||||||
_, end_y = state.selection_end_coord
|
end_x, end_y = state.selection_end_coord
|
||||||
|
|
||||||
y1, y2 = min(start_y, end_y), max(start_y, end_y)
|
|
||||||
|
|
||||||
# Determine pane from where selection started
|
# Determine pane from where selection started
|
||||||
pane = 'left' if start_x < state.divider_col else 'right'
|
pane = 'left' if start_x < state.divider_col else 'right'
|
||||||
|
|
||||||
if pane == 'left':
|
if pane == 'left':
|
||||||
x1, x2 = 0, state.divider_col - 1
|
pane_x1, pane_x2 = 0, state.divider_col - 1
|
||||||
else: # right
|
else: # right
|
||||||
x1, x2 = state.divider_col + 2, state.width - 1
|
pane_x1, pane_x2 = state.divider_col + 2, state.width - 1
|
||||||
|
|
||||||
|
# Determine drag direction to handle multi-line selection correctly
|
||||||
|
if start_y < end_y or (start_y == end_y and start_x <= end_x):
|
||||||
|
drag_start_x, drag_start_y = start_x, start_y
|
||||||
|
drag_end_x, drag_end_y = end_x, end_y
|
||||||
|
else: # upward drag or right-to-left on same line
|
||||||
|
drag_start_x, drag_start_y = end_x, end_y
|
||||||
|
drag_end_x, drag_end_y = start_x, start_y
|
||||||
|
|
||||||
|
for y in range(drag_start_y, drag_end_y + 1):
|
||||||
|
x1, x2 = -1, -1
|
||||||
|
if drag_start_y == drag_end_y: # single line selection
|
||||||
|
x1, x2 = drag_start_x, drag_end_x
|
||||||
|
elif y == drag_start_y: # first line of multi-line selection
|
||||||
|
x1, x2 = drag_start_x, pane_x2
|
||||||
|
elif y == drag_end_y: # last line of multi-line selection
|
||||||
|
x1, x2 = pane_x1, drag_end_x
|
||||||
|
else: # middle line of multi-line selection
|
||||||
|
x1, x2 = pane_x1, pane_x2
|
||||||
|
|
||||||
|
# Clamp selection to pane boundaries
|
||||||
|
x1 = max(x1, pane_x1)
|
||||||
|
x2 = min(x2, pane_x2)
|
||||||
|
|
||||||
for y in range(y1, y2 + 1):
|
|
||||||
try:
|
try:
|
||||||
# chgat can fail at the bottom-right corner of the screen
|
if x1 < state.width and x1 <= x2:
|
||||||
if x1 < state.width:
|
length = x2 - x1 + 1
|
||||||
# `width-1` is the last valid column
|
|
||||||
end_col = min(x2, state.width - 1)
|
|
||||||
length = end_col - x1 + 1
|
|
||||||
if length > 0:
|
if length > 0:
|
||||||
stdscr.chgat(y, x1, length, curses.A_REVERSE)
|
stdscr.chgat(y, x1, length, curses.A_REVERSE)
|
||||||
except curses.error:
|
except curses.error:
|
||||||
|
|
@ -357,33 +373,54 @@ def copy_selection_to_clipboard(stdscr, state):
|
||||||
return
|
return
|
||||||
|
|
||||||
start_x, start_y = state.selection_start_coord
|
start_x, start_y = state.selection_start_coord
|
||||||
_, end_y = state.selection_end_coord
|
end_x, end_y = state.selection_end_coord
|
||||||
|
|
||||||
y1, y2 = min(start_y, end_y), max(start_y, end_y)
|
|
||||||
|
|
||||||
# Determine pane from where selection started
|
# Determine pane from where selection started
|
||||||
pane = 'left' if start_x < state.divider_col else 'right'
|
pane = 'left' if start_x < state.divider_col else 'right'
|
||||||
|
|
||||||
if pane == 'left':
|
if pane == 'left':
|
||||||
x1, x2 = 0, state.divider_col - 1
|
pane_x1, pane_x2 = 0, state.divider_col - 1
|
||||||
else: # right
|
else: # right
|
||||||
x1, x2 = state.divider_col + 2, state.width - 1
|
pane_x1, pane_x2 = state.divider_col + 2, state.width - 1
|
||||||
|
|
||||||
|
# Determine drag direction to handle multi-line selection correctly
|
||||||
|
if start_y < end_y or (start_y == end_y and start_x <= end_x):
|
||||||
|
drag_start_x, drag_start_y = start_x, start_y
|
||||||
|
drag_end_x, drag_end_y = end_x, end_y
|
||||||
|
else: # upward drag or right-to-left on same line
|
||||||
|
drag_start_x, drag_start_y = end_x, end_y
|
||||||
|
drag_end_x, drag_end_y = start_x, start_y
|
||||||
|
|
||||||
height, width = stdscr.getmaxyx()
|
height, width = stdscr.getmaxyx()
|
||||||
selected_text_parts = []
|
selected_text_parts = []
|
||||||
|
|
||||||
for y in range(y1, y2 + 1):
|
for y in range(drag_start_y, drag_end_y + 1):
|
||||||
if 0 <= y < height:
|
if not (0 <= y < height):
|
||||||
line_str = ""
|
continue
|
||||||
end_col = min(x2, width - 1)
|
|
||||||
for x in range(x1, end_col + 1):
|
x1, x2 = -1, -1
|
||||||
|
if drag_start_y == drag_end_y: # single line selection
|
||||||
|
x1, x2 = drag_start_x, drag_end_x
|
||||||
|
elif y == drag_start_y: # first line of multi-line selection
|
||||||
|
x1, x2 = drag_start_x, pane_x2
|
||||||
|
elif y == drag_end_y: # last line of multi-line selection
|
||||||
|
x1, x2 = pane_x1, drag_end_x
|
||||||
|
else: # middle line of multi-line selection
|
||||||
|
x1, x2 = pane_x1, pane_x2
|
||||||
|
|
||||||
|
# Clamp selection to pane boundaries and screen width
|
||||||
|
x1 = max(x1, pane_x1)
|
||||||
|
x2 = min(x2, pane_x2, width - 1)
|
||||||
|
|
||||||
|
line_str = ""
|
||||||
|
if x1 <= x2:
|
||||||
|
for x in range(x1, x2 + 1):
|
||||||
try:
|
try:
|
||||||
char_and_attr = stdscr.inch(y, x)
|
char_and_attr = stdscr.inch(y, x)
|
||||||
char = char_and_attr & 0xFF
|
char = char_and_attr & 0xFF
|
||||||
line_str += chr(char)
|
line_str += chr(char)
|
||||||
except curses.error:
|
except curses.error:
|
||||||
line_str += " "
|
line_str += " "
|
||||||
selected_text_parts.append(line_str.rstrip())
|
selected_text_parts.append(line_str)
|
||||||
|
|
||||||
if selected_text_parts:
|
if selected_text_parts:
|
||||||
text_to_copy = "\n".join(selected_text_parts)
|
text_to_copy = "\n".join(selected_text_parts)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue