bugfix: clear selection when clicking above or below lines

Matt Wynne pointed out that snap.love would crash when a node went off
screen. While debugging it I noticed that selection1 was being set when
it shouldn't be.

Turns out I introduced a bug when I fixed the inscript bug back in June
(commit 9656e13774). One invariant I want to preserve is: selection1
should be unset after a mouse click (press and release without
intervening drag). This invariant was violated in my bugfix back in
June. I was concerned only with selection back then, and I didn't
realize I was breaking the mouse click case (in a fairly subtle way; you
can have selection set, and when it's set identically to the cursor
everything looks the same).

I think there might still be an issue in snap.love after this fix. I
noticed screen_bottom1.pos was nil, and as far as I recall that should
never happen.
This commit is contained in:
Kartik K. Agaram 2023-09-20 13:39:29 -07:00
parent c43d884b6f
commit bd6f7d48e7
5 changed files with 103 additions and 26 deletions

View File

@ -291,7 +291,7 @@ function edit.mouse_press(State, x,y, mouse_button)
end
end
-- still here? click is below all screen lines
-- still here? mouse press is below all screen lines
State.old_cursor1 = State.cursor1
State.old_selection1 = State.selection1
State.mousepress_shift = App.shift_down()
@ -313,6 +313,12 @@ function edit.mouse_release(State, x,y, mouse_button)
end
else
--? print_and_log('edit.mouse_release: no current drawing')
if y < State.top then
State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
edit.clean_up_mouse_press(State)
return
end
for line_index,line in ipairs(State.lines) do
if line.mode == 'text' then
if Text.in_line(State, line_index, x,y) then
@ -322,25 +328,33 @@ function edit.mouse_release(State, x,y, mouse_button)
pos=Text.to_pos_on_line(State, line_index, x, y),
}
--? print_and_log(('edit.mouse_release: cursor now %d,%d'):format(State.cursor1.line, State.cursor1.pos))
if State.mousepress_shift then
if State.old_selection1.line == nil then
State.selection1 = State.old_cursor1
else
State.selection1 = State.old_selection1
end
end
State.old_cursor1, State.old_selection1, State.mousepress_shift = nil
if eq(State.cursor1, State.selection1) then
State.selection1 = {}
end
break
edit.clean_up_mouse_press(State)
return
end
end
end
-- still here? mouse release is below all screen lines
State.cursor1.line, State.cursor1.pos = State.screen_bottom1.line, Text.pos_at_end_of_screen_line(State, State.screen_bottom1)
edit.clean_up_mouse_press(State)
--? print_and_log(('edit.mouse_release: finally selection %s,%s cursor %d,%d'):format(tostring(State.selection1.line), tostring(State.selection1.pos), State.cursor1.line, State.cursor1.pos))
end
end
function edit.clean_up_mouse_press(State)
if State.mousepress_shift then
if State.old_selection1.line == nil then
State.selection1 = State.old_cursor1
else
State.selection1 = State.old_selection1
end
end
State.old_cursor1, State.old_selection1, State.mousepress_shift = nil
if eq(State.cursor1, State.selection1) then
State.selection1 = {}
end
end
function edit.mouse_wheel_move(State, dx,dy)
if dy > 0 then
State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}

View File

@ -295,7 +295,7 @@ function edit.mouse_press(State, x,y, mouse_button)
end
end
-- still here? click is below all screen lines
-- still here? mouse press is below all screen lines
State.old_cursor1 = State.cursor1
State.old_selection1 = State.selection1
State.mousepress_shift = App.shift_down()
@ -317,6 +317,12 @@ function edit.mouse_release(State, x,y, mouse_button)
end
else
--? print_and_log('edit.mouse_release: no current drawing')
if y < State.top then
State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
edit.clean_up_mouse_press(State)
return
end
for line_index,line in ipairs(State.lines) do
if line.mode == 'text' then
if Text.in_line(State, line_index, x,y) then
@ -326,25 +332,33 @@ function edit.mouse_release(State, x,y, mouse_button)
pos=Text.to_pos_on_line(State, line_index, x, y),
}
--? print_and_log(('edit.mouse_release: cursor now %d,%d'):format(State.cursor1.line, State.cursor1.pos))
if State.mousepress_shift then
if State.old_selection1.line == nil then
State.selection1 = State.old_cursor1
else
State.selection1 = State.old_selection1
end
end
State.old_cursor1, State.old_selection1, State.mousepress_shift = nil
if eq(State.cursor1, State.selection1) then
State.selection1 = {}
end
break
edit.clean_up_mouse_press(State)
return
end
end
end
-- still here? mouse release is below all screen lines
State.cursor1.line, State.cursor1.pos = State.screen_bottom1.line, Text.pos_at_end_of_screen_line(State, State.screen_bottom1)
edit.clean_up_mouse_press(State)
--? print_and_log(('edit.mouse_release: finally selection %s,%s cursor %d,%d'):format(tostring(State.selection1.line), tostring(State.selection1.pos), State.cursor1.line, State.cursor1.pos))
end
end
function edit.clean_up_mouse_press(State)
if State.mousepress_shift then
if State.old_selection1.line == nil then
State.selection1 = State.old_cursor1
else
State.selection1 = State.old_selection1
end
end
State.old_cursor1, State.old_selection1, State.mousepress_shift = nil
if eq(State.cursor1, State.selection1) then
State.selection1 = {}
end
end
function edit.mouse_wheel_move(State, dx,dy)
if dy > 0 then
State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}

View File

@ -275,6 +275,7 @@ function test_click_to_left_of_line()
Editor_state.cursor1 = {line=1, pos=3}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click to the left of the line
edit.draw(Editor_state)
edit.run_after_mouse_click(Editor_state, Editor_state.left-4,Editor_state.top+5, 1)
@ -294,6 +295,7 @@ function test_click_takes_margins_into_account()
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click on the other line
edit.draw(Editor_state)
edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
@ -312,11 +314,33 @@ function test_click_on_empty_line()
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click on the empty line
edit.draw(Editor_state)
edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
-- cursor moves
check_eq(Editor_state.cursor1.line, 1, 'cursor')
-- selection remains empty
check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')
end
function test_click_below_all_lines()
-- display one line
App.screen.init{width=50, height=80}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click below first line
edit.draw(Editor_state)
edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+50, 1)
-- cursor doesn't move
check_eq(Editor_state.cursor1.line, 1, 'cursor')
-- selection remains empty
check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')
end
function test_draw_text()

View File

@ -23,6 +23,7 @@ click on wrapping line rendered from partway at top of screen
click past end of wrapping line
click past end of wrapping line containing non ascii
click past end of word wrapping line
click below final line does nothing
# cursor movement
move left

View File

@ -275,6 +275,7 @@ function test_click_to_left_of_line()
Editor_state.cursor1 = {line=1, pos=3}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click to the left of the line
edit.draw(Editor_state)
edit.run_after_mouse_click(Editor_state, Editor_state.left-4,Editor_state.top+5, 1)
@ -294,6 +295,7 @@ function test_click_takes_margins_into_account()
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click on the other line
edit.draw(Editor_state)
edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
@ -312,11 +314,33 @@ function test_click_on_empty_line()
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click on the empty line
edit.draw(Editor_state)
edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
-- cursor moves
check_eq(Editor_state.cursor1.line, 1, 'cursor')
-- selection remains empty
check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')
end
function test_click_below_all_lines()
-- display one line
App.screen.init{width=50, height=80}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click below first line
edit.draw(Editor_state)
edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+50, 1)
-- cursor doesn't move
check_eq(Editor_state.cursor1.line, 1, 'cursor')
-- selection remains empty
check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')
end
function test_draw_text()