diff options
author | Kartik K. Agaram <vc@akkartik.com> | 2024-06-11 12:28:45 -0700 |
---|---|---|
committer | Kartik K. Agaram <vc@akkartik.com> | 2024-06-11 12:28:45 -0700 |
commit | 6ed97fbd947506806d5aa64d574d2621b6ae5bee (patch) | |
tree | 0377fdfdab737c1c145cfe0ab2adf87cbae7017f | |
parent | cbf12ee3b89b12a76b9ed7b889a67b4ab7177dbc (diff) | |
parent | 262c078133e1603aa30b3404f4f4b12428d925e5 (diff) | |
download | view.love-6ed97fbd947506806d5aa64d574d2621b6ae5bee.tar.gz |
Merge text.love
-rw-r--r-- | app.lua | 5 | ||||
-rw-r--r-- | drawing.lua | 107 | ||||
-rw-r--r-- | drawing_tests.lua | 22 | ||||
-rw-r--r-- | edit.lua | 24 | ||||
-rw-r--r-- | help.lua | 12 | ||||
-rw-r--r-- | search.lua | 6 | ||||
-rw-r--r-- | select.lua | 5 | ||||
-rw-r--r-- | source.lua | 1 | ||||
-rw-r--r-- | source_edit.lua | 30 | ||||
-rw-r--r-- | source_select.lua | 5 | ||||
-rw-r--r-- | source_text.lua | 175 | ||||
-rw-r--r-- | source_text_tests.lua | 98 | ||||
-rw-r--r-- | text.lua | 149 | ||||
-rw-r--r-- | text_tests.lua | 95 |
14 files changed, 343 insertions, 391 deletions
diff --git a/app.lua b/app.lua index 627a438..263518c 100644 --- a/app.lua +++ b/app.lua @@ -130,6 +130,8 @@ function App.run_tests() end end table.sort(sorted_names) +--? App.initialize_for_test() -- debug: run a single test at a time like these 2 lines +--? test_click_below_all_lines() for _,name in ipairs(sorted_names) do App.initialize_for_test() --? print('=== '..name) @@ -404,9 +406,10 @@ end -- prepend file/line/test function prepend_debug_info_to_test_failure(test_name, err) local err_without_line_number = err:gsub('^[^:]*:[^:]*: ', '') - local stack_trace = debug.traceback('', --[[stack frame]]5) + local stack_trace = debug.traceback('', --[[stack frame]]5) -- most likely to be useful, but set to 0 for a complete stack trace local file_and_line_number = stack_trace:gsub('stack traceback:\n', ''):gsub(': .*', '') local full_error = file_and_line_number..':'..test_name..' -- '..err_without_line_number + -- uncomment this line for a complete stack trace --? local full_error = file_and_line_number..':'..test_name..' -- '..err_without_line_number..'\t\t'..stack_trace:gsub('\n', '\n\t\t') table.insert(Test_errors, full_error) end diff --git a/drawing.lua b/drawing.lua index 246c7ae..92e3d5f 100644 --- a/drawing.lua +++ b/drawing.lua @@ -6,16 +6,15 @@ require 'drawing_tests' -- into 256 parts. function Drawing.draw(State, line_index, y) local line = State.lines[line_index] - local line_cache = State.line_cache[line_index] - line_cache.starty = y local pmx,pmy = App.mouse_x(), App.mouse_y() - if pmx < State.right and pmy > line_cache.starty and pmy < line_cache.starty+Drawing.pixels(line.h, State.width) then + local starty = Text.starty(State, line_index) + if pmx < State.right and pmy > starty and pmy < starty+Drawing.pixels(line.h, State.width) then App.color(Icon_color) - love.graphics.rectangle('line', State.left,line_cache.starty, State.width,Drawing.pixels(line.h, State.width)) + love.graphics.rectangle('line', State.left,starty, State.width,Drawing.pixels(line.h, State.width)) if icon[State.current_drawing_mode] then - icon[State.current_drawing_mode](State.right-22, line_cache.starty+4) + icon[State.current_drawing_mode](State.right-22, starty+4) else - icon[State.previous_drawing_mode](State.right-22, line_cache.starty+4) + icon[State.previous_drawing_mode](State.right-22, starty+4) end if App.mouse_down(1) and love.keyboard.isDown('h') then @@ -30,7 +29,7 @@ function Drawing.draw(State, line_index, y) end local mx = Drawing.coord(pmx-State.left, State.width) - local my = Drawing.coord(pmy-line_cache.starty, State.width) + local my = Drawing.coord(pmy-starty, State.width) for _,shape in ipairs(line.shapes) do if geom.on_shape(mx,my, line, shape) then @@ -38,11 +37,11 @@ function Drawing.draw(State, line_index, y) else App.color(Stroke_color) end - Drawing.draw_shape(line, shape, line_cache.starty, State.left,State.right) + Drawing.draw_shape(line, shape, starty, State.left,State.right) end local function px(x) return Drawing.pixels(x, State.width)+State.left end - local function py(y) return Drawing.pixels(y, State.width)+line_cache.starty end + local function py(y) return Drawing.pixels(y, State.width)+starty end for i,p in ipairs(line.points) do if p.deleted == nil then if Drawing.near(p, mx,my, State.width) then @@ -71,7 +70,7 @@ function Drawing.draw(State, line_index, y) end end App.color(Current_stroke_color) - Drawing.draw_pending_shape(line, line_cache.starty, State.left,State.right) + Drawing.draw_pending_shape(line, starty, State.left,State.right) end function Drawing.draw_shape(drawing, shape, top, left,right) @@ -209,17 +208,24 @@ function Drawing.draw_pending_shape(drawing, top, left,right) end end -function Drawing.in_drawing(drawing, line_cache, x,y, left,right) - if line_cache.starty == nil then return false end -- outside current page +function Drawing.in_current_drawing(State, x,y, left,right) + return Drawing.in_drawing(State, State.lines.current_drawing_index, x,y, left,right) +end + +function Drawing.in_drawing(State, line_index, x,y, left,right) + assert(State.lines[line_index].mode == 'drawing') + local starty = Text.starty(State, line_index) + if starty == nil then return false end -- outside current page + local drawing = State.lines[line_index] local width = right-left - return y >= line_cache.starty and y < line_cache.starty + Drawing.pixels(drawing.h, width) and x >= left and x < right + return y >= starty and y < starty + Drawing.pixels(drawing.h, width) and x >= left and x < right end function Drawing.mouse_press(State, drawing_index, x,y, mouse_button) local drawing = State.lines[drawing_index] - local line_cache = State.line_cache[drawing_index] + local starty = Text.starty(State, drawing_index) local cx = Drawing.coord(x-State.left, State.width) - local cy = Drawing.coord(y-line_cache.starty, State.width) + local cy = Drawing.coord(y-starty, State.width) if State.current_drawing_mode == 'freehand' then drawing.pending = {mode=State.current_drawing_mode, points={{x=cx, y=cy}}} elseif State.current_drawing_mode == 'line' or State.current_drawing_mode == 'manhattan' then @@ -244,8 +250,8 @@ end function Drawing.update(State) if State.lines.current_drawing == nil then return end local drawing = State.lines.current_drawing - local line_cache = State.line_cache[State.lines.current_drawing_index] - if line_cache.starty == nil then + local starty = Text.starty(State, State.lines.current_drawing_index) + if starty == nil then -- some event cleared starty just this frame -- draw in this frame will soon set starty -- just skip this frame @@ -254,9 +260,9 @@ function Drawing.update(State) assert(drawing.mode == 'drawing', 'Drawing.update: line is not a drawing') local pmx, pmy = App.mouse_x(), App.mouse_y() local mx = Drawing.coord(pmx-State.left, State.width) - local my = Drawing.coord(pmy-line_cache.starty, State.width) + local my = Drawing.coord(pmy-starty, State.width) if App.mouse_down(1) then - if Drawing.in_drawing(drawing, line_cache, pmx,pmy, State.left,State.right) then + if Drawing.in_current_drawing(State, pmx,pmy, State.left,State.right) then if drawing.pending.mode == 'freehand' then table.insert(drawing.pending.points, {x=mx, y=my}) elseif drawing.pending.mode == 'move' then @@ -266,7 +272,7 @@ function Drawing.update(State) end end elseif State.current_drawing_mode == 'move' then - if Drawing.in_drawing(drawing, line_cache, pmx, pmy, State.left,State.right) then + if Drawing.in_current_drawing(State, pmx, pmy, State.left,State.right) then drawing.pending.target_point.x = mx drawing.pending.target_point.y = my Drawing.relax_constraints(drawing, drawing.pending.target_point_index) @@ -304,7 +310,7 @@ function Drawing.mouse_release(State, x,y, mouse_button) end elseif State.lines.current_drawing then local drawing = State.lines.current_drawing - local line_cache = State.line_cache[State.lines.current_drawing_index] + local starty = Text.starty(State, State.lines.current_drawing_index) if drawing.pending then if drawing.pending.mode == nil then -- nothing pending @@ -313,14 +319,14 @@ function Drawing.mouse_release(State, x,y, mouse_button) Drawing.smoothen(drawing.pending) table.insert(drawing.shapes, drawing.pending) elseif drawing.pending.mode == 'line' then - local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width) + local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width) if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then drawing.pending.p2 = Drawing.find_or_insert_point(drawing.points, mx,my, State.width) table.insert(drawing.shapes, drawing.pending) end elseif drawing.pending.mode == 'manhattan' then local p1 = drawing.points[drawing.pending.p1] - local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width) + local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width) if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then if math.abs(mx-p1.x) > math.abs(my-p1.y) then drawing.pending.p2 = Drawing.find_or_insert_point(drawing.points, mx, p1.y, State.width) @@ -328,11 +334,11 @@ function Drawing.mouse_release(State, x,y, mouse_button) drawing.pending.p2 = Drawing.find_or_insert_point(drawing.points, p1.x, my, State.width) end local p2 = drawing.points[drawing.pending.p2] - App.mouse_move(State.left+Drawing.pixels(p2.x, State.width), line_cache.starty+Drawing.pixels(p2.y, State.width)) + App.mouse_move(State.left+Drawing.pixels(p2.x, State.width), starty+Drawing.pixels(p2.y, State.width)) table.insert(drawing.shapes, drawing.pending) end elseif drawing.pending.mode == 'polygon' then - local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width) + local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width) if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, mx,my, State.width)) table.insert(drawing.shapes, drawing.pending) @@ -340,7 +346,7 @@ function Drawing.mouse_release(State, x,y, mouse_button) elseif drawing.pending.mode == 'rectangle' then assert(#drawing.pending.vertices <= 2, 'Drawing.mouse_release: rectangle has too many pending vertices') if #drawing.pending.vertices == 2 then - local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width) + local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width) if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then local first = drawing.points[drawing.pending.vertices[1]] local second = drawing.points[drawing.pending.vertices[2]] @@ -355,7 +361,7 @@ function Drawing.mouse_release(State, x,y, mouse_button) elseif drawing.pending.mode == 'square' then assert(#drawing.pending.vertices <= 2, 'Drawing.mouse_release: square has too many pending vertices') if #drawing.pending.vertices == 2 then - local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width) + local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width) if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then local first = drawing.points[drawing.pending.vertices[1]] local second = drawing.points[drawing.pending.vertices[2]] @@ -366,14 +372,14 @@ function Drawing.mouse_release(State, x,y, mouse_button) end end elseif drawing.pending.mode == 'circle' then - local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width) + local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width) if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then local center = drawing.points[drawing.pending.center] drawing.pending.radius = round(geom.dist(center.x,center.y, mx,my)) table.insert(drawing.shapes, drawing.pending) end elseif drawing.pending.mode == 'arc' then - local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width) + local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width) if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then local center = drawing.points[drawing.pending.center] drawing.pending.end_angle = geom.angle_with_hint(center.x,center.y, mx,my, drawing.pending.end_angle) @@ -477,13 +483,15 @@ function Drawing.keychord_press(State, chord) end drawing.pending.mode = 'square' elseif App.mouse_down(1) and chord == 'p' and State.current_drawing_mode == 'polygon' then - local _,drawing,line_cache = Drawing.current_drawing(State) - local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-line_cache.starty, State.width) + local drawing_index,drawing = Drawing.current_drawing(State) + local starty = Text.starty(State, drawing_index) + local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-starty, State.width) local j = Drawing.find_or_insert_point(drawing.points, mx,my, State.width) table.insert(drawing.pending.vertices, j) elseif App.mouse_down(1) and chord == 'p' and (State.current_drawing_mode == 'rectangle' or State.current_drawing_mode == 'square') then - local _,drawing,line_cache = Drawing.current_drawing(State) - local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-line_cache.starty, State.width) + local drawing_index,drawing = Drawing.current_drawing(State) + local starty = Text.starty(State, drawing_index) + local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-starty, State.width) local j = Drawing.find_or_insert_point(drawing.points, mx,my, State.width) while #drawing.pending.vertices >= 2 do table.remove(drawing.pending.vertices) @@ -492,9 +500,10 @@ function Drawing.keychord_press(State, chord) elseif chord == 'C-o' and not App.mouse_down(1) then State.current_drawing_mode = 'circle' elseif App.mouse_down(1) and chord == 'a' and State.current_drawing_mode == 'circle' then - local _,drawing,line_cache = Drawing.current_drawing(State) + local drawing_index,drawing = Drawing.current_drawing(State) + local starty = Text.starty(State, drawing_index) drawing.pending.mode = 'arc' - local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-line_cache.starty, State.width) + local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-starty, State.width) local center = drawing.points[drawing.pending.center] drawing.pending.radius = round(geom.dist(center.x,center.y, mx,my)) drawing.pending.start_angle = geom.angle(center.x,center.y, mx,my) @@ -510,7 +519,7 @@ function Drawing.keychord_press(State, chord) end drawing.pending.mode = 'circle' elseif chord == 'C-u' and not App.mouse_down(1) then - local drawing_index,drawing,line_cache,i,p = Drawing.select_point_at_mouse(State) + local drawing_index,drawing,_,i,p = Drawing.select_point_at_mouse(State) if drawing then if State.previous_drawing_mode == nil then State.previous_drawing_mode = State.current_drawing_mode @@ -521,7 +530,7 @@ function Drawing.keychord_press(State, chord) State.lines.current_drawing = drawing end elseif chord == 'C-n' and not App.mouse_down(1) then - local drawing_index,drawing,line_cache,point_index,p = Drawing.select_point_at_mouse(State) + local drawing_index,drawing,_,point_index,p = Drawing.select_point_at_mouse(State) if drawing then if State.previous_drawing_mode == nil then -- don't clobber @@ -619,9 +628,8 @@ function Drawing.current_drawing(State) local x, y = App.mouse_x(), App.mouse_y() for drawing_index,drawing in ipairs(State.lines) do if drawing.mode == 'drawing' then - local line_cache = State.line_cache[drawing_index] - if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) then - return drawing_index,drawing,line_cache + if Drawing.in_drawing(State, drawing_index, x,y, State.left,State.right) then + return drawing_index,drawing end end end @@ -632,12 +640,12 @@ function Drawing.select_shape_at_mouse(State) for drawing_index,drawing in ipairs(State.lines) do if drawing.mode == 'drawing' then local x, y = App.mouse_x(), App.mouse_y() - local line_cache = State.line_cache[drawing_index] - if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) then - local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width) + local starty = Text.starty(State, drawing_index) + if Drawing.in_drawing(State, drawing_index, x,y, State.left,State.right) then + local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width) for i,shape in ipairs(drawing.shapes) do if geom.on_shape(mx,my, drawing, shape) then - return drawing,line_cache,i,shape + return drawing,starty,i,shape end end end @@ -649,12 +657,12 @@ function Drawing.select_point_at_mouse(State) for drawing_index,drawing in ipairs(State.lines) do if drawing.mode == 'drawing' then local x, y = App.mouse_x(), App.mouse_y() - local line_cache = State.line_cache[drawing_index] - if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) then - local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width) + local starty = Text.starty(State, drawing_index) + if Drawing.in_drawing(State, drawing_index, x,y, State.left,State.right) then + local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width) for i,point in ipairs(drawing.points) do if Drawing.near(point, mx,my, State.width) then - return drawing_index,drawing,line_cache,i,point + return drawing_index,drawing,starty,i,point end end end @@ -666,8 +674,7 @@ function Drawing.select_drawing_at_mouse(State) for drawing_index,drawing in ipairs(State.lines) do if drawing.mode == 'drawing' then local x, y = App.mouse_x(), App.mouse_y() - local line_cache = State.line_cache[drawing_index] - if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) then + if Drawing.in_drawing(State, drawing_index, x,y, State.left,State.right) then return drawing end end diff --git a/drawing_tests.lua b/drawing_tests.lua index ede25f0..3484436 100644 --- a/drawing_tests.lua +++ b/drawing_tests.lua @@ -3,7 +3,7 @@ -- of specific shapes. In particular, no tests of freehand drawings. function test_creating_drawing_saves() - App.screen.init{width=120, height=60} + App.screen.init{width=800, height=600} Editor_state = edit.initialize_test_state() Editor_state.filename = 'foo' Editor_state.lines = load_array{} @@ -32,7 +32,7 @@ function test_draw_line() edit.draw(Editor_state) check_eq(#Editor_state.lines, 2, 'baseline/#lines') check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode') - check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y') + check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y') check_eq(Editor_state.lines[1].h, 128, 'baseline/y') check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes') -- draw a line @@ -77,7 +77,7 @@ function test_draw_horizontal_line() edit.draw(Editor_state) check_eq(#Editor_state.lines, 2, 'baseline/#lines') check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode') - check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y') + check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y') check_eq(Editor_state.lines[1].h, 128, 'baseline/y') check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes') -- draw a line that is more horizontal than vertical @@ -105,7 +105,7 @@ function test_draw_circle() edit.draw(Editor_state) check_eq(#Editor_state.lines, 2, 'baseline/#lines') check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode') - check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y') + check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y') check_eq(Editor_state.lines[1].h, 128, 'baseline/y') check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes') -- draw a circle @@ -134,7 +134,7 @@ function test_cancel_stroke() edit.draw(Editor_state) check_eq(#Editor_state.lines, 2, 'baseline/#lines') check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode') - check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y') + check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y') check_eq(Editor_state.lines[1].h, 128, 'baseline/y') check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes') -- start drawing a line @@ -172,7 +172,7 @@ function test_draw_circle_mid_stroke() edit.draw(Editor_state) check_eq(#Editor_state.lines, 2, 'baseline/#lines') check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode') - check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y') + check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y') check_eq(Editor_state.lines[1].h, 128, 'baseline/y') check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes') -- draw a circle @@ -200,7 +200,7 @@ function test_draw_arc() edit.draw(Editor_state) check_eq(#Editor_state.lines, 2, 'baseline/#lines') check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode') - check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y') + check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y') check_eq(Editor_state.lines[1].h, 128, 'baseline/y') check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes') -- draw an arc @@ -231,7 +231,7 @@ function test_draw_polygon() check_eq(Editor_state.current_drawing_mode, 'line', 'baseline/drawing_mode') check_eq(#Editor_state.lines, 2, 'baseline/#lines') check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode') - check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y') + check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y') check_eq(Editor_state.lines[1].h, 128, 'baseline/y') check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes') -- first point @@ -269,7 +269,7 @@ function test_draw_rectangle() check_eq(Editor_state.current_drawing_mode, 'line', 'baseline/drawing_mode') check_eq(#Editor_state.lines, 2, 'baseline/#lines') check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode') - check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y') + check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y') check_eq(Editor_state.lines[1].h, 128, 'baseline/y') check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes') -- first point @@ -313,7 +313,7 @@ function test_draw_rectangle_intermediate() check_eq(Editor_state.current_drawing_mode, 'line', 'baseline/drawing_mode') check_eq(#Editor_state.lines, 2, 'baseline/#lines') check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode') - check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y') + check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y') check_eq(Editor_state.lines[1].h, 128, 'baseline/y') check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes') -- first point @@ -349,7 +349,7 @@ function test_draw_square() check_eq(Editor_state.current_drawing_mode, 'line', 'baseline/drawing_mode') check_eq(#Editor_state.lines, 2, 'baseline/#lines') check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode') - check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y') + check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y') check_eq(Editor_state.lines[1].h, 128, 'baseline/y') check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes') -- first point diff --git a/edit.lua b/edit.lua index 79a2fc3..6a7d708 100644 --- a/edit.lua +++ b/edit.lua @@ -19,7 +19,6 @@ function edit.initialize_state(top, left, right, font, font_height, line_height) -- rendering wrapped text lines needs some additional short-lived data per line: -- startpos, the index of data the line starts rendering from, can only be >1 for topmost line on screen - -- starty, the y coord in pixels the line starts rendering from -- fragments: snippets of the line guaranteed to not straddle screen lines -- screen_line_starting_pos: optional array of grapheme indices if it wraps over more than one screen line line_cache = {}, @@ -33,9 +32,10 @@ function edit.initialize_state(top, left, right, font, font_height, line_height) -- -- Make sure these coordinates are never aliased, so that changing one causes -- action at a distance. + -- + -- On lines that are drawings, pos will be nil. screen_top1 = {line=1, pos=1}, -- position of start of screen line at top of screen - cursor1 = {line=1, pos=1}, -- position of cursor - screen_bottom1 = {line=1, pos=1}, -- position of start of screen line at bottom of screen + cursor1 = {line=1, pos=1}, -- position of cursor; must be on a text line selection1 = {}, -- some extra state to compute selection between mouse press and release @@ -102,22 +102,19 @@ function edit.draw(State) State.cursor_x = nil State.cursor_y = nil local y = State.top - local screen_bottom1 = {line=nil, pos=nil} --? print('== draw') for line_index = State.screen_top1.line,#State.lines do local line = State.lines[line_index] --? print('draw:', y, line_index, line) if y + State.line_height > App.screen.height then break end - screen_bottom1.line = line_index --? print('text.draw', y, line_index) local startpos = 1 if line_index == State.screen_top1.line then startpos = State.screen_top1.pos end - y, screen_bottom1.pos = Text.draw(State, line_index, y, startpos) + y = Text.draw(State, line_index, y, startpos) --? print('=> y', y) end - State.screen_bottom1 = screen_bottom1 if State.search_term then Text.draw_search_bar(State) end @@ -173,10 +170,7 @@ function edit.mouse_press(State, x,y, mouse_button) State.old_cursor1 = State.cursor1 State.old_selection1 = State.selection1 State.mousepress_shift = App.shift_down() - State.selection1 = { - line=State.screen_bottom1.line, - pos=Text.pos_at_end_of_screen_line(State, State.screen_bottom1), - } + State.selection1 = Text.final_text_loc_on_screen(State) end function edit.mouse_release(State, x,y, mouse_button) @@ -202,7 +196,7 @@ function edit.mouse_release(State, x,y, mouse_button) 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) + State.cursor1 = Text.final_text_loc_on_screen(State) 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 @@ -228,7 +222,8 @@ function edit.mouse_wheel_move(State, dx,dy) Text.up(State) end elseif dy < 0 then - State.cursor1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos} + State.cursor1 = Text.screen_bottom1(State) + edit.put_cursor_on_next_text_line(State) for i=1,math.floor(-dy) do Text.down(State) end @@ -252,7 +247,6 @@ function edit.keychord_press(State, chord, key) Text.delete_selection(State, State.left, State.right) end if State.search_term then - for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll if chord == 'escape' then State.search_term = nil State.cursor1 = State.search_backup.cursor @@ -302,7 +296,6 @@ function edit.keychord_press(State, chord, key) end -- dispatch to text else - for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll Text.keychord_press(State, chord) end end @@ -357,6 +350,7 @@ end function edit.run_after_mouse_click(State, x,y, mouse_button) App.fake_mouse_press(x,y, mouse_button) edit.mouse_press(State, x,y, mouse_button) + edit.draw(State) App.fake_mouse_release(x,y, mouse_button) edit.mouse_release(State, x,y, mouse_button) App.screen.contents = {} diff --git a/help.lua b/help.lua index 6f8633b..cc7a0c9 100644 --- a/help.lua +++ b/help.lua @@ -1,8 +1,8 @@ function draw_help_without_mouse_pressed(State, drawing_index) local drawing = State.lines[drawing_index] - local line_cache = State.line_cache[drawing_index] + local starty = Text.starty(State, drawing_index) App.color(Help_color) - local y = line_cache.starty+10 + local y = starty+10 love.graphics.print("Things you can do:", State.left+30,y) y = y + State.line_height love.graphics.print("* Press the mouse button to start drawing a "..current_shape(State), State.left+30,y) @@ -48,14 +48,14 @@ function draw_help_without_mouse_pressed(State, drawing_index) love.graphics.print("Press 'esc' now to hide this message", State.left+30,y) y = y + State.line_height App.color(Help_background_color) - love.graphics.rectangle('fill', State.left,line_cache.starty, State.width, math.max(Drawing.pixels(drawing.h, State.width),y-line_cache.starty)) + love.graphics.rectangle('fill', State.left,starty, State.width, math.max(Drawing.pixels(drawing.h, State.width),y-starty)) end function draw_help_with_mouse_pressed(State, drawing_index) local drawing = State.lines[drawing_index] - local line_cache = State.line_cache[drawing_index] + local starty = Text.starty(State, drawing_index) App.color(Help_color) - local y = line_cache.starty+10 + local y = starty+10 love.graphics.print("You're currently drawing a "..current_shape(State, drawing.pending), State.left+30,y) y = y + State.line_height love.graphics.print('Things you can do now:', State.left+30,y) @@ -129,7 +129,7 @@ function draw_help_with_mouse_pressed(State, drawing_index) y = y + State.line_height end App.color(Help_background_color) - love.graphics.rectangle('fill', State.left,line_cache.starty, State.width, math.max(Drawing.pixels(drawing.h, State.width),y-line_cache.starty)) + love.graphics.rectangle('fill', State.left,starty, State.width, math.max(Drawing.pixels(drawing.h, State.width),y-starty)) end function current_shape(State, shape) diff --git a/search.lua b/search.lua index 54bab14..d3a5fea 100644 --- a/search.lua +++ b/search.lua @@ -62,7 +62,8 @@ function Text.search_next(State) State.screen_top1.line = State.search_backup.screen_top.line State.screen_top1.pos = State.search_backup.screen_top.pos end - if Text.lt1(State.cursor1, State.screen_top1) or Text.lt1(State.screen_bottom1, State.cursor1) then + local screen_bottom1 = Text.screen_bottom1(State) + if Text.lt1(State.cursor1, State.screen_top1) or Text.lt1(screen_bottom1, State.cursor1) then State.screen_top1.line = State.cursor1.line local pos = Text.pos_at_start_of_screen_line(State, State.cursor1) State.screen_top1.pos = pos @@ -115,7 +116,8 @@ function Text.search_previous(State) State.screen_top1.line = State.search_backup.screen_top.line State.screen_top1.pos = State.search_backup.screen_top.pos end - if Text.lt1(State.cursor1, State.screen_top1) or Text.lt1(State.screen_bottom1, State.cursor1) then + local screen_bottom1 = Text.screen_bottom1(State) + if Text.lt1(State.cursor1, State.screen_top1) or Text.lt1(screen_bottom1, State.cursor1) then State.screen_top1.line = State.cursor1.line local pos = Text.pos_at_start_of_screen_line(State, State.cursor1) State.screen_top1.pos = pos diff --git a/select.lua b/select.lua index 79eeb82..2d0851a 100644 --- a/select.lua +++ b/select.lua @@ -69,7 +69,7 @@ end function Text.mouse_pos(State) local x,y = App.mouse_x(), App.mouse_y() - if y < State.line_cache[State.screen_top1.line].starty then + if y < State.top then return State.screen_top1.line, State.screen_top1.pos end for line_index,line in ipairs(State.lines) do @@ -77,7 +77,8 @@ function Text.mouse_pos(State) return line_index, Text.to_pos_on_line(State, line_index, x,y) end end - return State.screen_bottom1.line, Text.pos_at_end_of_screen_line(State, State.screen_bottom1) + local screen_bottom1 = Text.screen_bottom1(State) + return screen_bottom1.line, Text.pos_at_end_of_screen_line(State, screen_bottom1) end function Text.selection(State) diff --git a/source.lua b/source.lua index 9bc400e..7eec594 100644 --- a/source.lua +++ b/source.lua @@ -307,7 +307,6 @@ function source.mouse_press(x,y, mouse_button) return end log_browser.mouse_press(Log_browser_state, x,y, mouse_button) - for _,line_cache in ipairs(Editor_state.line_cache) do line_cache.starty = nil end -- just in case we scroll end end diff --git a/source_edit.lua b/source_edit.lua index e376537..5351857 100644 --- a/source_edit.lua +++ b/source_edit.lua @@ -33,7 +33,6 @@ function edit.initialize_state(top, left, right, font, font_height, line_height) -- string data, -- a drawing is a table with: -- mode = 'drawing' - -- a (y) coord in pixels (updated while painting screen), -- a (h)eight, -- an array of points, and -- an array of shapes @@ -54,7 +53,6 @@ function edit.initialize_state(top, left, right, font, font_height, line_height) -- rendering wrapped text lines needs some additional short-lived data per line: -- startpos, the index of data the line starts rendering from, can only be >1 for topmost line on screen - -- starty, the y coord in pixels the line starts rendering from -- fragments: snippets of the line guaranteed to not straddle screen lines -- screen_line_starting_pos: optional array of grapheme indices if it wraps over more than one screen line line_cache = {}, @@ -68,9 +66,10 @@ function edit.initialize_state(top, left, right, font, font_height, line_height) -- -- Make sure these coordinates are never aliased, so that changing one causes -- action at a distance. + -- + -- On lines that are drawings, pos will be nil. screen_top1 = {line=1, pos=1}, -- position of start of screen line at top of screen - cursor1 = {line=1, pos=1}, -- position of cursor - screen_bottom1 = {line=1, pos=1}, -- position of start of screen line at bottom of screen + cursor1 = {line=1, pos=1}, -- position of cursor; must be on a text line selection1 = {}, -- some extra state to compute selection between mouse press and release @@ -165,13 +164,11 @@ function edit.draw(State, hide_cursor, show_line_numbers) State.cursor_x = nil State.cursor_y = nil local y = State.top - local screen_bottom1 = {line=nil, pos=nil} --? print('== draw') for line_index = State.screen_top1.line,#State.lines do local line = State.lines[line_index] --? print('draw:', y, line_index, line) if y + State.line_height > App.screen.height then break end - screen_bottom1.line = line_index if line.mode == 'text' then --? print('text.draw', y, line_index) local startpos = 1 @@ -198,7 +195,7 @@ function edit.draw(State, hide_cursor, show_line_numbers) end, }) end - y, screen_bottom1.pos = Text.draw(State, line_index, y, startpos, hide_cursor, show_line_numbers) + y = Text.draw(State, line_index, y, startpos, hide_cursor, show_line_numbers) --? print('=> y', y) elseif line.mode == 'drawing' then y = y+Drawing_padding_top @@ -208,7 +205,6 @@ function edit.draw(State, hide_cursor, show_line_numbers) assert(false, ('unknown line mode %s'):format(line.mode)) end end - State.screen_bottom1 = screen_bottom1 if State.search_term then Text.draw_search_bar(State) end @@ -281,8 +277,7 @@ function edit.mouse_press(State, x,y, mouse_button) return end elseif line.mode == 'drawing' then - local line_cache = State.line_cache[line_index] - if Drawing.in_drawing(line, line_cache, x, y, State.left,State.right) then + if Drawing.in_drawing(State, line_index, x, y, State.left,State.right) then State.lines.current_drawing_index = line_index State.lines.current_drawing = line Drawing.before = snapshot(State, line_index) @@ -296,10 +291,7 @@ function edit.mouse_press(State, x,y, mouse_button) State.old_cursor1 = State.cursor1 State.old_selection1 = State.selection1 State.mousepress_shift = App.shift_down() - State.selection1 = { - line=State.screen_bottom1.line, - pos=Text.pos_at_end_of_screen_line(State, State.screen_bottom1), - } + State.selection1 = Text.final_text_loc_on_screen(State) end function edit.mouse_release(State, x,y, mouse_button) @@ -337,7 +329,7 @@ function edit.mouse_release(State, x,y, mouse_button) 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) + State.cursor1 = Text.final_text_loc_on_screen(State) 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 @@ -365,7 +357,7 @@ function edit.mouse_wheel_move(State, dx,dy) Text.up(State) end elseif dy < 0 then - State.cursor1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos} + State.cursor1 = Text.screen_bottom1(State) edit.put_cursor_on_next_text_line(State) for i=1,math.floor(-dy) do Text.down(State) @@ -399,11 +391,10 @@ function edit.keychord_press(State, chord, key) -- printable character created using shift key => delete selection -- (we're not creating any ctrl-shift- or alt-shift- combinations using regular/printable keys) (not App.shift_down() or utf8.len(key) == 1) and - chord ~= 'C-a' and chord ~= 'C-c' and chord ~= 'C-x' and chord ~= 'backspace' and chord ~= 'delete' and chord ~= 'C-z' and chord ~= 'C-y' and not App.is_cursor_movement(chord) then + chord ~= 'C-a' and chord ~= 'C-c' and chord ~= 'C-x' and chord ~= 'backspace' and chord ~= 'delete' and chord ~= 'C-z' and chord ~= 'C-y' and not App.is_cursor_movement(key) then Text.delete_selection(State, State.left, State.right) end if State.search_term then - for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll if chord == 'escape' then State.search_term = nil State.cursor1 = State.search_backup.cursor @@ -508,7 +499,6 @@ function edit.keychord_press(State, chord, key) record_undo_event(State, {before=before, after=snapshot(State, before_line, State.cursor1.line)}) -- dispatch to drawing or text elseif App.mouse_down(1) or chord:sub(1,2) == 'C-' then - -- DON'T reset line_cache.starty here local drawing_index, drawing = Drawing.current_drawing(State) if drawing_index then local before = snapshot(State, drawing_index) @@ -545,7 +535,6 @@ function edit.keychord_press(State, chord, key) end schedule_save(State) else - for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll Text.keychord_press(State, chord) end end @@ -600,6 +589,7 @@ end function edit.run_after_mouse_click(State, x,y, mouse_button) App.fake_mouse_press(x,y, mouse_button) edit.mouse_press(State, x,y, mouse_button) + edit.draw(State) App.fake_mouse_release(x,y, mouse_button) edit.mouse_release(State, x,y, mouse_button) App.screen.contents = {} diff --git a/source_select.lua b/source_select.lua index 6e21c7b..b67dd16 100644 --- a/source_select.lua +++ b/source_select.lua @@ -69,7 +69,7 @@ end function Text.mouse_pos(State) local x,y = App.mouse_x(), App.mouse_y() - if y < State.line_cache[State.screen_top1.line].starty then + if y < State.top then return State.screen_top1.line, State.screen_top1.pos end for line_index,line in ipairs(State.lines) do @@ -79,7 +79,8 @@ function Text.mouse_pos(State) end end end - return State.screen_bottom1.line, Text.pos_at_end_of_screen_line(State, State.screen_bottom1) + local screen_bottom1 = Text.screen_bottom1(State) + return screen_bottom1.line, Text.pos_at_end_of_screen_line(State, screen_bottom1) end function Text.cut_selection(State) diff --git a/source_text.lua b/source_text.lua index 7c1838c..6e0c4f9 100644 --- a/source_text.lua +++ b/source_text.lua @@ -2,14 +2,12 @@ Text = {} -- draw a line starting from startpos to screen at y between State.left and State.right --- return y for the next line, and position of start of final screen line drawn +-- return y for the next line function Text.draw(State, line_index, y, startpos, hide_cursor, show_line_numbers) local line = State.lines[line_index] local line_cache = State.line_cache[line_index] - line_cache.starty = y line_cache.startpos = startpos -- wrap long lines - local final_screen_line_starting_pos = startpos -- track value to return Text.populate_screen_line_starting_pos(State, line_index) Text.populate_link_offsets(State, line_index) if show_line_numbers then @@ -24,7 +22,6 @@ function Text.draw(State, line_index, y, startpos, hide_cursor, show_line_number -- render nothing --? print('skipping', screen_line) else - final_screen_line_starting_pos = pos local screen_line = Text.screen_line(line, line_cache, i) --? print('text.draw:', screen_line, 'at', line_index,pos, 'after', x,y) local frag_len = utf8.len(screen_line) @@ -84,7 +81,7 @@ function Text.draw(State, line_index, y, startpos, hide_cursor, show_line_number end end end - return y, final_screen_line_starting_pos + return y end function Text.screen_line(line, line_cache, i) @@ -208,7 +205,7 @@ function Text.text_input(State, t) end end local before = snapshot(State, State.cursor1.line) ---? print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos) +--? print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos) Text.insert_at_cursor(State, t) if State.cursor_y > App.screen.height - State.line_height then Text.populate_screen_line_starting_pos(State, State.cursor1.line) @@ -241,12 +238,12 @@ function Text.keychord_press(State, chord) record_undo_event(State, {before=before, after=snapshot(State, before_line, State.cursor1.line)}) elseif chord == 'tab' then local before = snapshot(State, State.cursor1.line) ---? print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos) +--? print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos) Text.insert_at_cursor(State, '\t') if State.cursor_y > App.screen.height - State.line_height then Text.populate_screen_line_starting_pos(State, State.cursor1.line) Text.snap_cursor_to_bottom_of_screen(State, State.left, State.right) ---? print('=>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos) +--? print('=>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos) end schedule_save(State) record_undo_event(State, {before=before, after=snapshot(State, State.cursor1.line)}) @@ -427,37 +424,79 @@ function Text.insert_return(State) end function Text.pageup(State) ---? print('pageup') + State.screen_top1 = Text.previous_screen_top1(State) + State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos} + Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State) + Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks +end + +-- return the top y coordinate of a given line_index, +-- or nil if no part of it is on screen +function Text.starty(State, line_index) + -- duplicate some logic from love.draw + -- does not modify State (except to populate line_cache) + if line_index < State.screen_top1.line then return end + local loc2 = Text.to2(State, State.screen_top1) + local y = State.top + while true do + if State.lines[loc2.line].mode == 'drawing' then + y = y + Drawing_padding_top + end + if loc2.line == line_index then return y end + if State.lines[loc2.line].mode == 'text' then + y = y + State.line_height + elseif State.lines[loc2.line].mode == 'drawing' then + y = y + Drawing.pixels(State.lines[loc2.line].h, State.width) + Drawing_padding_bottom + end + if y + State.line_height > App.screen.height then break end + local next_loc2 = Text.next_screen_line(State, loc2) + if Text.eq2(next_loc2, loc2) then break end -- end of file + loc2 = next_loc2 + end +end + +function Text.previous_screen_top1(State) -- duplicate some logic from love.draw - local top2 = Text.to2(State, State.screen_top1) ---? print(App.screen.height) + -- does not modify State (except to populate line_cache) + local loc2 = Text.to2(State, State.screen_top1) local y = App.screen.height - State.line_height while y >= State.top do ---? print(y, top2.line, top2.screen_line, top2.screen_pos) - if State.screen_top1.line == 1 and State.screen_top1.pos == 1 then break end - if State.lines[State.screen_top1.line].mode == 'text' then + if loc2.line == 1 and loc2.screen_line == 1 and loc2.screen_pos == 1 then break end + if State.lines[loc2.line].mode == 'text' then y = y - State.line_height - elseif State.lines[State.screen_top1.line].mode == 'drawing' then - y = y - Drawing_padding_height - Drawing.pixels(State.lines[State.screen_top1.line].h, State.width) + elseif State.lines[loc2.line].mode == 'drawing' then + y = y - Drawing_padding_height - Drawing.pixels(State.lines[loc2.line].h, State.width) end - top2 = Text.previous_screen_line(State, top2) + loc2 = Text.previous_screen_line(State, loc2) end - State.screen_top1 = Text.to1(State, top2) - State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos} - Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State) ---? print(State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos) ---? print('pageup end') + return Text.to1(State, loc2) end function Text.pagedown(State) ---? print('pagedown') - State.screen_top1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos} ---? print('setting top to', State.screen_top1.line, State.screen_top1.pos) + State.screen_top1 = Text.screen_bottom1(State) State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos} Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State) ---? print('top now', State.screen_top1.line) Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks ---? print('pagedown end') +end + +-- return the location of the start of the bottom-most line on screen +function Text.screen_bottom1(State) + -- duplicate some logic from love.draw + -- does not modify State (except to populate line_cache) + local loc2 = Text.to2(State, State.screen_top1) + local y = State.top + while true do + if State.lines[loc2.line].mode == 'text' then + y = y + State.line_height + elseif State.lines[loc2.line].mode == 'drawing' then + y = y + Drawing_padding_height + Drawing.pixels(State.lines[loc2.line].h, State.width) + end + if y + State.line_height > App.screen.height then break end + local next_loc2 = Text.next_screen_line(State, loc2) + if Text.eq2(next_loc2, loc2) then break end + loc2 = next_loc2 + end + return Text.to1(State, loc2) end function Text.up(State) @@ -505,7 +544,7 @@ end function Text.down(State) assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text') ---? print('down', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos, State.screen_bottom1.line, State.screen_bottom1.pos) +--? print('down', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos) assert(State.cursor1.pos, 'cursor has no pos') if Text.cursor_at_final_screen_line(State) then -- line is done, skip to next text line @@ -522,7 +561,9 @@ function Text.down(State) break end end - if State.cursor1.line > State.screen_bottom1.line then + local screen_bottom1 = Text.screen_bottom1(State) +--? print('down 2', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos, screen_bottom1.line, screen_bottom1.pos) + if State.cursor1.line > screen_bottom1.line then --? print('screen top before:', State.screen_top1.line, State.screen_top1.pos) --? print('scroll up preserving cursor') Text.snap_cursor_to_bottom_of_screen(State) @@ -530,7 +571,8 @@ function Text.down(State) end else -- move down one screen line in current line - local scroll_down = Text.le1(State.screen_bottom1, State.cursor1) + local screen_bottom1 = Text.screen_bottom1(State) + local scroll_down = Text.le1(screen_bottom1, State.cursor1) --? print('cursor is NOT at final screen line of its line') local screen_line_starting_pos, screen_line_index = Text.pos_at_start_of_screen_line(State, State.cursor1) Text.populate_screen_line_starting_pos(State, State.cursor1.line) @@ -546,7 +588,7 @@ function Text.down(State) --? print('screen top after:', State.screen_top1.line, State.screen_top1.pos) end end ---? print('=>', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos, State.screen_bottom1.line, State.screen_bottom1.pos) +--? print('=>', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos) end function Text.start_of_line(State) @@ -683,6 +725,7 @@ function Text.pos_at_start_of_screen_line(State, loc1) end function Text.pos_at_end_of_screen_line(State, loc1) + assert(State.lines[loc1.line].mode == 'text') Text.populate_screen_line_starting_pos(State, loc1.line) local line_cache = State.line_cache[loc1.line] local most_recent_final_pos = utf8.len(State.lines[loc1.line].data)+1 @@ -696,6 +739,25 @@ function Text.pos_at_end_of_screen_line(State, loc1) assert(false, ('invalid pos %d'):format(loc1.pos)) end +function Text.final_text_loc_on_screen(State) + local screen_bottom1 = Text.screen_bottom1(State) + if State.lines[screen_bottom1.line].mode == 'text' then + return { + line=screen_bottom1.line, + pos=Text.pos_at_end_of_screen_line(State, screen_bottom1), + } + end + local loc2 = Text.to2(State, screen_bottom1) + while true do + if State.lines[loc2.line].mode == 'text' then break end + assert(loc2.line > 1 or loc2.screen_line > 1 and loc2.screen_pos > 1) -- elsewhere we're making sure there's always at least one text line on screen + loc2 = Text.previous_screen_line(State, loc2) + end + local result = Text.to1(State, loc2) + result.pos = Text.pos_at_end_of_screen_line(State, result) + return result +end + function Text.cursor_at_final_screen_line(State) Text.populate_screen_line_starting_pos(State, State.cursor1.line) local screen_lines = State.line_cache[State.cursor1.line].screen_line_starting_pos @@ -736,7 +798,7 @@ function Text.snap_cursor_to_bottom_of_screen(State) --? print('to2: =>', top2.line, top2.screen_line, top2.screen_pos) -- slide to start of screen line top2.screen_pos = 1 -- start of screen line ---? print('snap', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos) +--? print('snap', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos) --? print('cursor pos '..tostring(State.cursor1.pos)..' is on the #'..tostring(top2.screen_line)..' screen line down') local y = App.screen.height - State.line_height -- duplicate some logic from love.draw @@ -766,26 +828,28 @@ function Text.snap_cursor_to_bottom_of_screen(State) --? print('top2 finally:', top2.line, top2.screen_line, top2.screen_pos) State.screen_top1 = Text.to1(State, top2) --? print('top1 finally:', State.screen_top1.line, State.screen_top1.pos) ---? print('snap =>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos) +--? print('snap =>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos) Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks end function Text.in_line(State, line_index, x,y) local line = State.lines[line_index] local line_cache = State.line_cache[line_index] - if line_cache.starty == nil then return false end -- outside current page - if y < line_cache.starty then return false end + local starty = Text.starty(State, line_index) + if starty == nil then return false end -- outside current page + if y < starty then return false end Text.populate_screen_line_starting_pos(State, line_index) - return y < line_cache.starty + State.line_height*(#line_cache.screen_line_starting_pos - Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos) + 1) + return y < starty + State.line_height*(#line_cache.screen_line_starting_pos - Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos) + 1) end -- convert mx,my in pixels to schema-1 coordinates function Text.to_pos_on_line(State, line_index, mx, my) local line = State.lines[line_index] local line_cache = State.line_cache[line_index] - assert(my >= line_cache.starty, 'failed to map y pixel to line') + local starty = Text.starty(State, line_index) + assert(my >= starty, 'failed to map y pixel to line') -- duplicate some logic from Text.draw - local y = line_cache.starty + local y = starty local start_screen_line_index = Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos) for screen_line_index = start_screen_line_index,#line_cache.screen_line_starting_pos do local screen_line_starting_pos = line_cache.screen_line_starting_pos[screen_line_index] @@ -971,6 +1035,10 @@ function Text.le1(a, b) return a.pos <= b.pos end +function Text.eq2(a, b) + return a.line == b.line and a.screen_line == b.screen_line and a.screen_pos == b.screen_pos +end + function Text.offset(s, pos1) if pos1 == 1 then return 1 end local result = utf8.offset(s, pos1) @@ -994,6 +1062,22 @@ function Text.previous_screen_line(State, loc2) end end +function Text.next_screen_line(State, loc2) + if State.lines[loc2.line].mode == 'drawing' then + return {line=loc2.line+1, screen_line=1, screen_pos=1} + end + Text.populate_screen_line_starting_pos(State, loc2.line) + if loc2.screen_line >= #State.line_cache[loc2.line].screen_line_starting_pos then + if loc2.line < #State.lines then + return {line=loc2.line+1, screen_line=1, screen_pos=1} + else + return loc2 + end + else + return {line=loc2.line, screen_line=loc2.screen_line+1, screen_pos=1} + end +end + -- resize helper function Text.tweak_screen_top_and_cursor(State) if State.screen_top1.pos == 1 then return end @@ -1017,16 +1101,12 @@ function Text.tweak_screen_top_and_cursor(State) end end -- make sure cursor is on screen + local screen_bottom1 = Text.screen_bottom1(State) if Text.lt1(State.cursor1, State.screen_top1) then State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos} - elseif State.cursor1.line >= State.screen_bottom1.line then ---? print('too low') + elseif State.cursor1.line >= screen_bottom1.line then if Text.cursor_out_of_screen(State) then ---? print('tweak') - State.cursor1 = { - line=State.screen_bottom1.line, - pos=Text.to_pos_on_line(State, State.screen_bottom1.line, State.right-5, App.screen.height-5), - } + State.cursor1 = Text.final_text_loc_on_screen(State) end end end @@ -1035,11 +1115,6 @@ end function Text.cursor_out_of_screen(State) edit.draw(State) return State.cursor_y == nil - -- this approach is cheaper and almost works, except on the final screen - -- where file ends above bottom of screen ---? local botpos = Text.pos_at_start_of_screen_line(State, State.cursor1) ---? local botline1 = {line=State.cursor1.line, pos=botpos} ---? return Text.lt1(State.screen_bottom1, botline1) end function Text.redraw_all(State) diff --git a/source_text_tests.lua b/source_text_tests.lua index 6376ec8..11cc823 100644 --- a/source_text_tests.lua +++ b/source_text_tests.lua @@ -16,7 +16,7 @@ function test_initial_state() end function test_click_to_create_drawing() - App.screen.init{width=120, height=60} + App.screen.init{width=800, height=600} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{} Text.redraw_all(Editor_state) @@ -75,7 +75,6 @@ function test_press_ctrl() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.run_after_keychord(Editor_state, 'C-m', 'm') end @@ -232,7 +231,7 @@ function test_skip_multiple_spaces_to_next_word() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=4} -- at the start of second word edit.draw(Editor_state) - edit.run_after_keychord(Editor_state, 'M-right', 'right') + edit.run_after_keychord(Editor_state, 'M-right', 'right') check_eq(Editor_state.cursor1.pos, 9, 'check') end @@ -243,7 +242,7 @@ function test_move_past_end_of_word_on_next_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=8} edit.draw(Editor_state) - edit.run_after_keychord(Editor_state, 'M-right', 'right') + edit.run_after_keychord(Editor_state, 'M-right', 'right') check_eq(Editor_state.cursor1.line, 2, 'line') check_eq(Editor_state.cursor1.pos, 4, 'pos') end @@ -255,9 +254,8 @@ function test_click_moves_cursor() 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 = {} - edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache + edit.draw(Editor_state) -- populate line_cache.startpos for each line edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) check_eq(Editor_state.cursor1.line, 1, 'cursor:line') check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos') @@ -274,7 +272,6 @@ function test_click_to_left_of_line() Text.redraw_all(Editor_state) 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) @@ -294,7 +291,6 @@ function test_click_takes_margins_into_account() Text.redraw_all(Editor_state) 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) @@ -313,7 +309,6 @@ function test_click_on_empty_line() Text.redraw_all(Editor_state) 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) @@ -332,7 +327,6 @@ function test_click_below_all_lines() 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) @@ -350,7 +344,6 @@ function test_draw_text() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'screen:1') @@ -367,7 +360,6 @@ function test_draw_wrapping_text() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'screen:1') @@ -384,7 +376,6 @@ function test_draw_word_wrapping_text() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc ', 'screen:1') @@ -402,7 +393,6 @@ function test_click_on_wrapping_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=20} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} -- 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) @@ -421,7 +411,6 @@ function test_click_on_wrapping_line_takes_margins_into_account() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=20} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} -- 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) @@ -439,7 +428,6 @@ function test_draw_text_wrapping_within_word() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abcd ', 'screen:1') @@ -457,7 +445,6 @@ function test_draw_wrapping_text_containing_non_ascii() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'mad', 'screen:1') @@ -476,7 +463,6 @@ function test_click_past_end_of_screen_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'madam ', 'baseline/screen:1') @@ -499,7 +485,6 @@ function test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=8} Editor_state.screen_top1 = {line=1, pos=7} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, "I'm ad", 'baseline/screen:2') @@ -520,7 +505,6 @@ function test_click_past_end_of_wrapping_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'madam ', 'baseline/screen:1') @@ -544,7 +528,6 @@ function test_click_past_end_of_wrapping_line_containing_non_ascii() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'madam ', 'baseline/screen:1') @@ -569,7 +552,6 @@ function test_click_past_end_of_word_wrapping_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'the quick brown fox ', 'baseline/screen:1') @@ -588,11 +570,10 @@ function test_select_text() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- select a letter App.fake_key_press('lshift') - edit.run_after_keychord(Editor_state, 'S-right', 'right') + edit.run_after_keychord(Editor_state, 'S-right', 'right') App.fake_key_release('lshift') edit.key_release(Editor_state, 'lshift') -- selection persists even after shift is released @@ -611,10 +592,9 @@ function test_cursor_movement_without_shift_resets_selection() Editor_state.cursor1 = {line=1, pos=1} Editor_state.selection1 = {line=1, pos=2} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- press an arrow key without shift - edit.run_after_keychord(Editor_state, 'right', 'right') + edit.run_after_keychord(Editor_state, 'right', 'right') -- no change to data, selection is reset check_nil(Editor_state.selection1.line, 'check') check_eq(Editor_state.lines[1].data, 'abc', 'data') @@ -629,7 +609,6 @@ function test_edit_deletes_selection() Editor_state.cursor1 = {line=1, pos=1} Editor_state.selection1 = {line=1, pos=2} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- press a key edit.run_after_text_input(Editor_state, 'x') @@ -646,7 +625,6 @@ function test_edit_with_shift_key_deletes_selection() Editor_state.cursor1 = {line=1, pos=1} Editor_state.selection1 = {line=1, pos=2} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- mimic precise keypresses for a capital letter App.fake_key_press('lshift') @@ -668,7 +646,6 @@ function test_copy_does_not_reset_selection() Editor_state.cursor1 = {line=1, pos=1} Editor_state.selection1 = {line=1, pos=2} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- copy selection edit.run_after_keychord(Editor_state, 'C-c', 'c') @@ -686,7 +663,6 @@ function test_cut() Editor_state.cursor1 = {line=1, pos=1} Editor_state.selection1 = {line=1, pos=2} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- press a key edit.run_after_keychord(Editor_state, 'C-x', 'x') @@ -704,7 +680,6 @@ function test_paste_replaces_selection() Editor_state.cursor1 = {line=2, pos=1} Editor_state.selection1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- set clipboard App.clipboard = 'xyz' @@ -723,7 +698,6 @@ function test_deleting_selection_may_scroll() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=2} Editor_state.screen_top1 = {line=2, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'def', 'baseline/screen:1') @@ -747,7 +721,6 @@ function test_edit_wrapping_text() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=4} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) edit.run_after_text_input(Editor_state, 'g') local y = Editor_state.top @@ -766,7 +739,6 @@ function test_insert_newline() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=2} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -795,7 +767,6 @@ function test_insert_newline_at_start_of_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} -- hitting the enter key splits the line edit.run_after_keychord(Editor_state, 'return', 'return') check_eq(Editor_state.cursor1.line, 2, 'cursor:line') @@ -812,7 +783,6 @@ function test_insert_from_clipboard() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=2} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -841,9 +811,8 @@ function test_select_text_using_mouse() 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 = {} - edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache + edit.draw(Editor_state) -- populate line_cache.startpos for each line -- press and hold on first location edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) -- drag and release somewhere else @@ -861,9 +830,8 @@ function test_select_text_using_mouse_starting_above_text() 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 = {} - edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache + edit.draw(Editor_state) -- populate line_cache.startpos for each line -- press mouse above first line of text edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1) check(Editor_state.selection1.line ~= nil, 'selection:line-not-nil') @@ -879,7 +847,6 @@ function test_select_text_using_mouse_starting_above_text_wrapping_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=5} Editor_state.screen_top1 = {line=2, pos=3} - Editor_state.screen_bottom1 = {} -- press mouse above first line of text edit.draw(Editor_state) edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1) @@ -902,7 +869,6 @@ function test_select_text_using_mouse_starting_below_text() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'ab', 'baseline:screen:1') @@ -923,9 +889,8 @@ function test_select_text_using_mouse_and_shift() 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 = {} - edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache + edit.draw(Editor_state) -- populate line_cache.startpos for each line -- click on first location edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) @@ -948,9 +913,8 @@ function test_select_text_repeatedly_using_mouse_and_shift() 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 = {} - edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache + edit.draw(Editor_state) -- populate line_cache.startpos for each line -- click on first location edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) @@ -978,7 +942,6 @@ function test_select_all_text() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- select all App.fake_key_press('lctrl') @@ -1000,7 +963,6 @@ function test_cut_without_selection() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=2} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} Editor_state.selection1 = {} edit.draw(Editor_state) -- try to cut without selecting text @@ -1016,7 +978,6 @@ function test_pagedown() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} -- initially the first two lines are displayed edit.draw(Editor_state) local y = Editor_state.top @@ -1046,7 +1007,6 @@ function test_pagedown_skips_drawings() check_eq(Editor_state.lines[2].mode, 'drawing', 'baseline/lines') Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} local drawing_height = Drawing_padding_height + drawing_width/2 -- default -- initially the screen displays the first line and the drawing -- 15px margin + 15px line1 + 10px margin + 25px drawing + 10px margin = 75px < screen height 80px @@ -1070,7 +1030,6 @@ function test_pagedown_can_start_from_middle_of_long_wrapping_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=2} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc ', 'baseline/screen:1') @@ -1105,7 +1064,6 @@ function test_pagedown_never_moves_up() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=9} Editor_state.screen_top1 = {line=1, pos=9} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- pagedown makes no change edit.run_after_keychord(Editor_state, 'pagedown', 'pagedown') @@ -1120,7 +1078,6 @@ function test_down_arrow_moves_cursor() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} -- initially the first three lines are displayed edit.draw(Editor_state) local y = Editor_state.top @@ -1153,7 +1110,6 @@ function test_down_arrow_skips_drawing() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -1175,7 +1131,6 @@ function test_down_arrow_scrolls_down_by_one_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -1203,7 +1158,6 @@ function test_down_arrow_scrolls_down_by_one_screen_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -1232,7 +1186,6 @@ function test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_ Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -1260,7 +1213,6 @@ function test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -1294,7 +1246,6 @@ function test_up_arrow_moves_cursor() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -1326,7 +1277,6 @@ function test_up_arrow_skips_drawing() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -1348,7 +1298,6 @@ function test_up_arrow_scrolls_up_by_one_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=2, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'def', 'baseline/screen:1') @@ -1376,7 +1325,6 @@ function test_up_arrow_scrolls_up_by_one_line_skipping_drawing() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=1} Editor_state.screen_top1 = {line=3, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'def', 'baseline/screen:1') @@ -1398,7 +1346,6 @@ function test_up_arrow_scrolls_up_by_one_screen_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=6} Editor_state.screen_top1 = {line=3, pos=5} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'jkl', 'baseline/screen:1') @@ -1426,7 +1373,6 @@ function test_up_arrow_scrolls_up_to_final_screen_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=2, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'ghi', 'baseline/screen:1') @@ -1456,7 +1402,6 @@ function test_up_arrow_scrolls_up_to_empty_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=2, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -1483,7 +1428,6 @@ function test_pageup() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=2, pos=1} - Editor_state.screen_bottom1 = {} -- initially the last two lines are displayed edit.draw(Editor_state) local y = Editor_state.top @@ -1508,7 +1452,6 @@ function test_pageup_scrolls_up_by_screen_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=2, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'ghi', 'baseline/screen:1') @@ -1537,7 +1480,6 @@ function test_pageup_scrolls_up_from_middle_screen_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=5} Editor_state.screen_top1 = {line=2, pos=5} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'jkl', 'baseline/screen:2') @@ -1564,7 +1506,6 @@ function test_enter_on_bottom_line_scrolls_down() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=2} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -1593,7 +1534,6 @@ function test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=4, pos=2} Editor_state.screen_top1 = {line=4, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'jkl', 'baseline/screen:1') @@ -1616,7 +1556,6 @@ function test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bot Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=2, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- after hitting the inserting_text key the screen does not scroll down edit.run_after_text_input(Editor_state, 'a') @@ -1635,7 +1574,6 @@ function test_typing_on_bottom_line_scrolls_down() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=4} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -1665,7 +1603,6 @@ function test_left_arrow_scrolls_up_in_wrapped_line() Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'} Text.redraw_all(Editor_state) Editor_state.screen_top1 = {line=3, pos=5} - Editor_state.screen_bottom1 = {} -- cursor is at top of screen Editor_state.cursor1 = {line=3, pos=5} edit.draw(Editor_state) @@ -1694,7 +1631,6 @@ function test_right_arrow_scrolls_down_in_wrapped_line() Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'} Text.redraw_all(Editor_state) Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} -- cursor is at bottom right of screen Editor_state.cursor1 = {line=3, pos=5} edit.draw(Editor_state) @@ -1705,7 +1641,7 @@ function test_right_arrow_scrolls_down_in_wrapped_line() y = y + Editor_state.line_height App.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace -- after hitting the right arrow the screen scrolls down by one line - edit.run_after_keychord(Editor_state, 'right', 'right') + edit.run_after_keychord(Editor_state, 'right', 'right') check_eq(Editor_state.screen_top1.line, 2, 'screen_top') check_eq(Editor_state.cursor1.line, 3, 'cursor:line') check_eq(Editor_state.cursor1.pos, 6, 'cursor:pos') @@ -1724,7 +1660,6 @@ function test_home_scrolls_up_in_wrapped_line() Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'} Text.redraw_all(Editor_state) Editor_state.screen_top1 = {line=3, pos=5} - Editor_state.screen_bottom1 = {} -- cursor is at top of screen Editor_state.cursor1 = {line=3, pos=5} edit.draw(Editor_state) @@ -1753,7 +1688,6 @@ function test_end_scrolls_down_in_wrapped_line() Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'} Text.redraw_all(Editor_state) Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} -- cursor is at bottom right of screen Editor_state.cursor1 = {line=3, pos=5} edit.draw(Editor_state) @@ -1784,7 +1718,6 @@ function test_position_cursor_on_recently_edited_wrapping_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=25} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc def ghi ', 'baseline1/screen:1') @@ -1818,7 +1751,6 @@ function test_backspace_can_scroll_up() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=2, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'def', 'baseline/screen:1') @@ -1846,7 +1778,6 @@ function test_backspace_can_scroll_up_screen_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=5} Editor_state.screen_top1 = {line=3, pos=5} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'jkl', 'baseline/screen:1') @@ -1981,7 +1912,6 @@ function test_undo_insert_text() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=4} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} -- insert a character edit.draw(Editor_state) edit.run_after_text_input(Editor_state, 'g') @@ -2016,7 +1946,6 @@ function test_undo_delete_text() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=5} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} -- delete a character edit.run_after_keychord(Editor_state, 'backspace', 'backspace') check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line') @@ -2055,7 +1984,6 @@ function test_undo_restores_selection() Editor_state.cursor1 = {line=1, pos=1} Editor_state.selection1 = {line=1, pos=2} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- delete selected text edit.run_after_text_input(Editor_state, 'x') @@ -2076,7 +2004,6 @@ function test_search() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- search for a string edit.run_after_keychord(Editor_state, 'C-f', 'f') @@ -2103,7 +2030,6 @@ function test_search_upwards() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- search for a string edit.run_after_keychord(Editor_state, 'C-f', 'f') @@ -2121,7 +2047,6 @@ function test_search_wrap() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- search for a string edit.run_after_keychord(Editor_state, 'C-f', 'f') @@ -2139,7 +2064,6 @@ function test_search_wrap_upwards() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- search upwards for a string edit.run_after_keychord(Editor_state, 'C-f', 'f') diff --git a/text.lua b/text.lua index ad86378..ec28509 100644 --- a/text.lua +++ b/text.lua @@ -2,15 +2,13 @@ Text = {} -- draw a line starting from startpos to screen at y between State.left and State.right --- return y for the next line, and position of start of final screen line drawn +-- return y for the next line function Text.draw(State, line_index, y, startpos) --? print('text.draw', line_index, y) local line = State.lines[line_index] local line_cache = State.line_cache[line_index] - line_cache.starty = y line_cache.startpos = startpos -- wrap long lines - local final_screen_line_starting_pos = startpos -- track value to return Text.populate_screen_line_starting_pos(State, line_index) assert(#line_cache.screen_line_starting_pos >= 1, 'line cache missing screen line info') for i=1,#line_cache.screen_line_starting_pos do @@ -18,7 +16,6 @@ function Text.draw(State, line_index, y, startpos) if pos < startpos then -- render nothing else - final_screen_line_starting_pos = pos local screen_line = Text.screen_line(line, line_cache, i) --? print('text.draw:', screen_line, 'at', line_index,pos, 'after', x,y) local frag_len = utf8.len(screen_line) @@ -57,7 +54,7 @@ function Text.draw(State, line_index, y, startpos) end end end - return y, final_screen_line_starting_pos + return y end function Text.screen_line(line, line_cache, i) @@ -226,45 +223,64 @@ function Text.keychord_press(State, chord) end function Text.pageup(State) ---? print('pageup') + State.screen_top1 = Text.previous_screen_top1(State) + State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos} + Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State) + Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks +end + +-- return the top y coordinate of a given line_index, +-- or nil if no part of it is on screen +function Text.starty(State, line_index) + -- duplicate some logic from love.draw + -- does not modify State (except to populate line_cache) + if line_index < State.screen_top1.line then return end + local loc2 = Text.to2(State, State.screen_top1) + local y = State.top + while true do + if loc2.line == line_index then return y end + y = y + State.line_height + if y + State.line_height > App.screen.height then break end + local next_loc2 = Text.next_screen_line(State, loc2) + if Text.eq2(next_loc2, loc2) then break end -- end of file + loc2 = next_loc2 + end +end + +function Text.previous_screen_top1(State) -- duplicate some logic from love.draw - local top2 = Text.to2(State, State.screen_top1) ---? print(App.screen.height) + -- does not modify State (except to populate line_cache) + local loc2 = Text.to2(State, State.screen_top1) local y = App.screen.height - State.line_height while y >= State.top do ---? print(y, top2.line, top2.screen_line, top2.screen_pos) - if State.screen_top1.line == 1 and State.screen_top1.pos == 1 then break end + if loc2.line == 1 and loc2.screen_line == 1 and loc2.screen_pos == 1 then break end y = y - State.line_height - top2 = Text.previous_screen_line(State, top2) + loc2 = Text.previous_screen_line(State, loc2) end - State.screen_top1 = Text.to1(State, top2) - State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos} - Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State) ---? print(State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos) ---? print('pageup end') + return Text.to1(State, loc2) end function Text.pagedown(State) ---? print('pagedown') - -- If a line/paragraph gets to a page boundary, I often want to scroll - -- before I get to the bottom. - -- However, only do this if it makes forward progress. - local bot2 = Text.to2(State, State.screen_bottom1) - if bot2.screen_line > 1 then - bot2.screen_line = math.max(bot2.screen_line-10, 1) - end - local new_top1 = Text.to1(State, bot2) - if Text.lt1(State.screen_top1, new_top1) then - State.screen_top1 = new_top1 - else - State.screen_top1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos} - end ---? print('setting top to', State.screen_top1.line, State.screen_top1.pos) + State.screen_top1 = Text.screen_bottom1(State) State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos} Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State) ---? print('top now', State.screen_top1.line) Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks ---? print('pagedown end') +end + +-- return the location of the start of the bottom-most line on screen +function Text.screen_bottom1(State) + -- duplicate some logic from love.draw + -- does not modify State (except to populate line_cache) + local loc2 = Text.to2(State, State.screen_top1) + local y = State.top + while true do + y = y + State.line_height + if y + State.line_height > App.screen.height then break end + local next_loc2 = Text.next_screen_line(State, loc2) + if Text.eq2(next_loc2, loc2) then break end + loc2 = next_loc2 + end + return Text.to1(State, loc2) end function Text.up(State) @@ -306,7 +322,7 @@ function Text.up(State) end function Text.down(State) ---? print('down', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos, State.screen_bottom1.line, State.screen_bottom1.pos) +--? print('down', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos) assert(State.cursor1.pos, 'cursor has no pos') if Text.cursor_at_final_screen_line(State) then -- line is done, skip to next text line @@ -317,7 +333,9 @@ function Text.down(State) State.cursor1.pos = Text.nearest_cursor_pos(State.font, State.lines[State.cursor1.line].data, State.cursor_x, State.left) --? print(State.cursor1.pos) end - if State.cursor1.line > State.screen_bottom1.line then + local screen_bottom1 = Text.screen_bottom1(State) +--? print('down 2', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos, screen_bottom1.line, screen_bottom1.pos) + if State.cursor1.line > screen_bottom1.line then --? print('screen top before:', State.screen_top1.line, State.screen_top1.pos) --? print('scroll up preserving cursor') Text.snap_cursor_to_bottom_of_screen(State) @@ -325,7 +343,8 @@ function Text.down(State) end else -- move down one screen line in current line - local scroll_down = Text.le1(State.screen_bottom1, State.cursor1) + local screen_bottom1 = Text.screen_bottom1(State) + local scroll_down = Text.le1(screen_bottom1, State.cursor1) --? print('cursor is NOT at final screen line of its line') local screen_line_starting_pos, screen_line_index = Text.pos_at_start_of_screen_line(State, State.cursor1) Text.populate_screen_line_starting_pos(State, State.cursor1.line) @@ -341,7 +360,7 @@ function Text.down(State) --? print('screen top after:', State.screen_top1.line, State.screen_top1.pos) end end ---? print('=>', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos, State.screen_bottom1.line, State.screen_bottom1.pos) +--? print('=>', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos) end function Text.start_of_line(State) @@ -474,6 +493,14 @@ function Text.pos_at_end_of_screen_line(State, loc1) assert(false, ('invalid pos %d'):format(loc1.pos)) end +function Text.final_text_loc_on_screen(State) + local screen_bottom1 = Text.screen_bottom1(State) + return { + line=screen_bottom1.line, + pos=Text.pos_at_end_of_screen_line(State, screen_bottom1), + } +end + function Text.cursor_at_final_screen_line(State) Text.populate_screen_line_starting_pos(State, State.cursor1.line) local screen_lines = State.line_cache[State.cursor1.line].screen_line_starting_pos @@ -495,7 +522,7 @@ function Text.snap_cursor_to_bottom_of_screen(State) --? print('to2: =>', top2.line, top2.screen_line, top2.screen_pos) -- slide to start of screen line top2.screen_pos = 1 -- start of screen line ---? print('snap', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos) +--? print('snap', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos) --? print('cursor pos '..tostring(State.cursor1.pos)..' is on the #'..tostring(top2.screen_line)..' screen line down') local y = App.screen.height - State.line_height -- duplicate some logic from love.draw @@ -512,26 +539,28 @@ function Text.snap_cursor_to_bottom_of_screen(State) --? print('top2 finally:', top2.line, top2.screen_line, top2.screen_pos) State.screen_top1 = Text.to1(State, top2) --? print('top1 finally:', State.screen_top1.line, State.screen_top1.pos) ---? print('snap =>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos) +--? print('snap =>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos) Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks end function Text.in_line(State, line_index, x,y) local line = State.lines[line_index] local line_cache = State.line_cache[line_index] - if line_cache.starty == nil then return false end -- outside current page - if y < line_cache.starty then return false end + local starty = Text.starty(State, line_index) + if starty == nil then return false end -- outside current page + if y < starty then return false end Text.populate_screen_line_starting_pos(State, line_index) - return y < line_cache.starty + State.line_height*(#line_cache.screen_line_starting_pos - Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos) + 1) + return y < starty + State.line_height*(#line_cache.screen_line_starting_pos - Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos) + 1) end -- convert mx,my in pixels to schema-1 coordinates function Text.to_pos_on_line(State, line_index, mx, my) local line = State.lines[line_index] local line_cache = State.line_cache[line_index] - assert(my >= line_cache.starty, 'failed to map y pixel to line') + local starty = Text.starty(State, line_index) + assert(my >= starty, 'failed to map y pixel to line') -- duplicate some logic from Text.draw - local y = line_cache.starty + local y = starty local start_screen_line_index = Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos) for screen_line_index = start_screen_line_index,#line_cache.screen_line_starting_pos do local screen_line_starting_pos = line_cache.screen_line_starting_pos[screen_line_index] @@ -714,6 +743,10 @@ function Text.le1(a, b) return a.pos <= b.pos end +function Text.eq2(a, b) + return a.line == b.line and a.screen_line == b.screen_line and a.screen_pos == b.screen_pos +end + function Text.offset(s, pos1) if pos1 == 1 then return 1 end local result = utf8.offset(s, pos1) @@ -735,6 +768,19 @@ function Text.previous_screen_line(State, loc2) end end +function Text.next_screen_line(State, loc2) + Text.populate_screen_line_starting_pos(State, loc2.line) + if loc2.screen_line >= #State.line_cache[loc2.line].screen_line_starting_pos then + if loc2.line < #State.lines then + return {line=loc2.line+1, screen_line=1, screen_pos=1} + else + return loc2 + end + else + return {line=loc2.line, screen_line=loc2.screen_line+1, screen_pos=1} + end +end + -- resize helper function Text.tweak_screen_top_and_cursor(State) if State.screen_top1.pos == 1 then return end @@ -758,16 +804,12 @@ function Text.tweak_screen_top_and_cursor(State) end end -- make sure cursor is on screen + local screen_bottom1 = Text.screen_bottom1(State) if Text.lt1(State.cursor1, State.screen_top1) then State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos} - elseif State.cursor1.line >= State.screen_bottom1.line then ---? print('too low') + elseif State.cursor1.line >= screen_bottom1.line then if Text.cursor_out_of_screen(State) then ---? print('tweak') - State.cursor1 = { - line=State.screen_bottom1.line, - pos=Text.to_pos_on_line(State, State.screen_bottom1.line, State.right-5, App.screen.height-5), - } + State.cursor1 = Text.final_text_loc_on_screen(State) end end end @@ -776,11 +818,6 @@ end function Text.cursor_out_of_screen(State) edit.draw(State) return State.cursor_y == nil - -- this approach is cheaper and almost works, except on the final screen - -- where file ends above bottom of screen ---? local botpos = Text.pos_at_start_of_screen_line(State, State.cursor1) ---? local botline1 = {line=State.cursor1.line, pos=botpos} ---? return Text.lt1(State.screen_bottom1, botline1) end function Text.redraw_all(State) diff --git a/text_tests.lua b/text_tests.lua index 541a056..ca97e7f 100644 --- a/text_tests.lua +++ b/text_tests.lua @@ -191,9 +191,8 @@ function test_click_moves_cursor() 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 = {} - edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache + edit.draw(Editor_state) -- populate line_cache.startpos for each line edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) check_eq(Editor_state.cursor1.line, 1, 'cursor:line') check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos') @@ -210,7 +209,6 @@ function test_click_to_left_of_line() Text.redraw_all(Editor_state) 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) @@ -230,7 +228,6 @@ function test_click_takes_margins_into_account() Text.redraw_all(Editor_state) 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) @@ -249,7 +246,6 @@ function test_click_on_empty_line() Text.redraw_all(Editor_state) 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) @@ -268,7 +264,6 @@ function test_click_below_all_lines() 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) @@ -286,7 +281,6 @@ function test_draw_text() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'screen:1') @@ -303,7 +297,6 @@ function test_draw_wrapping_text() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'screen:1') @@ -320,7 +313,6 @@ function test_draw_word_wrapping_text() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc ', 'screen:1') @@ -338,7 +330,6 @@ function test_click_on_wrapping_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=20} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} -- 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) @@ -357,7 +348,6 @@ function test_click_on_wrapping_line_takes_margins_into_account() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=20} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} -- 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) @@ -375,7 +365,6 @@ function test_draw_text_wrapping_within_word() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abcd ', 'screen:1') @@ -393,7 +382,6 @@ function test_draw_wrapping_text_containing_non_ascii() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'mad', 'screen:1') @@ -412,7 +400,6 @@ function test_click_past_end_of_screen_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'madam ', 'baseline/screen:1') @@ -435,7 +422,6 @@ function test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=8} Editor_state.screen_top1 = {line=1, pos=7} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, "I'm ad", 'baseline/screen:2') @@ -456,7 +442,6 @@ function test_click_past_end_of_wrapping_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'madam ', 'baseline/screen:1') @@ -480,7 +465,6 @@ function test_click_past_end_of_wrapping_line_containing_non_ascii() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'madam ', 'baseline/screen:1') @@ -505,7 +489,6 @@ function test_click_past_end_of_word_wrapping_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'the quick brown fox ', 'baseline/screen:1') @@ -524,7 +507,6 @@ function test_select_text() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- select a letter App.fake_key_press('lshift') @@ -547,7 +529,6 @@ function test_cursor_movement_without_shift_resets_selection() Editor_state.cursor1 = {line=1, pos=1} Editor_state.selection1 = {line=1, pos=2} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- press an arrow key without shift edit.run_after_keychord(Editor_state, 'right', 'right') @@ -565,7 +546,6 @@ function test_copy_does_not_reset_selection() Editor_state.cursor1 = {line=1, pos=1} Editor_state.selection1 = {line=1, pos=2} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- copy selection edit.run_after_keychord(Editor_state, 'C-c', 'c') @@ -575,15 +555,15 @@ function test_copy_does_not_reset_selection() end function test_move_cursor_using_mouse() + App.screen.init{width=50, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc', 'def', 'xyz'} 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 = {} - edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache + edit.draw(Editor_state) -- populate line_cache.startpos for each line edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) check_eq(Editor_state.cursor1.line, 1, 'cursor:line') check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos') @@ -598,9 +578,8 @@ function test_select_text_using_mouse() 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 = {} - edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache + edit.draw(Editor_state) -- populate line_cache.startpos for each line -- press and hold on first location edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) -- drag and release somewhere else @@ -618,9 +597,8 @@ function test_select_text_using_mouse_starting_above_text() 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 = {} - edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache + edit.draw(Editor_state) -- populate line_cache.startpos for each line -- press mouse above first line of text edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1) check(Editor_state.selection1.line ~= nil, 'selection:line-not-nil') @@ -636,7 +614,6 @@ function test_select_text_using_mouse_starting_above_text_wrapping_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=5} Editor_state.screen_top1 = {line=2, pos=3} - Editor_state.screen_bottom1 = {} -- press mouse above first line of text edit.draw(Editor_state) edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1) @@ -659,7 +636,6 @@ function test_select_text_using_mouse_starting_below_text() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'ab', 'baseline:screen:1') @@ -680,9 +656,8 @@ function test_select_text_using_mouse_and_shift() 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 = {} - edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache + edit.draw(Editor_state) -- populate line_cache.startpos for each line -- click on first location edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) @@ -705,9 +680,8 @@ function test_select_text_repeatedly_using_mouse_and_shift() 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 = {} - edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache + edit.draw(Editor_state) -- populate line_cache.startpos for each line -- click on first location edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) @@ -735,7 +709,6 @@ function test_select_all_text() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- select all App.fake_key_press('lctrl') @@ -756,7 +729,6 @@ function test_pagedown() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} -- initially the first two lines are displayed edit.draw(Editor_state) local y = Editor_state.top @@ -773,36 +745,6 @@ function test_pagedown() App.screen.check(y, 'ghi', 'screen:2') end -function test_pagedown_often_shows_start_of_wrapping_line() - -- draw a few lines ending in part of a wrapping line - App.screen.init{width=50, height=60} - Editor_state = edit.initialize_test_state() - Editor_state.lines = load_array{'abc', 'def ghi jkl', 'mno'} - Text.redraw_all(Editor_state) - Editor_state.cursor1 = {line=1, pos=1} - Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} - edit.draw(Editor_state) - local y = Editor_state.top - App.screen.check(y, 'abc', 'baseline/screen:1') - y = y + Editor_state.line_height - App.screen.check(y, 'def ', 'baseline/screen:2') - y = y + Editor_state.line_height - App.screen.check(y, 'ghi ', 'baseline/screen:3') - -- after pagedown we start drawing from the bottom _line_ (multiple screen lines) - edit.run_after_keychord(Editor_state, 'pagedown', 'pagedown') - check_eq(Editor_state.screen_top1.line, 2, 'screen_top:line') - check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos') - check_eq(Editor_state.cursor1.line, 2, 'cursor:line') - check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos') - y = Editor_state.top - App.screen.check(y, 'def ', 'screen:1') - y = y + Editor_state.line_height - App.screen.check(y, 'ghi ', 'screen:2') - y = y + Editor_state.line_height - App.screen.check(y, 'jkl', 'screen:3') -end - function test_pagedown_can_start_from_middle_of_long_wrapping_line() -- draw a few lines starting from a very long wrapping line App.screen.init{width=Editor_state.left+30, height=60} @@ -811,7 +753,6 @@ function test_pagedown_can_start_from_middle_of_long_wrapping_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=2} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc ', 'baseline/screen:1') @@ -846,7 +787,6 @@ function test_pagedown_never_moves_up() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=9} Editor_state.screen_top1 = {line=1, pos=9} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- pagedown makes no change edit.run_after_keychord(Editor_state, 'pagedown', 'pagedown') @@ -861,7 +801,6 @@ function test_down_arrow_moves_cursor() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} -- initially the first three lines are displayed edit.draw(Editor_state) local y = Editor_state.top @@ -891,7 +830,6 @@ function test_down_arrow_scrolls_down_by_one_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -919,7 +857,6 @@ function test_down_arrow_scrolls_down_by_one_screen_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -948,7 +885,6 @@ function test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_ Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -976,7 +912,6 @@ function test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -1010,7 +945,6 @@ function test_up_arrow_moves_cursor() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -1039,7 +973,6 @@ function test_up_arrow_scrolls_up_by_one_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=2, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'def', 'baseline/screen:1') @@ -1067,7 +1000,6 @@ function test_up_arrow_scrolls_up_by_one_screen_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=3, pos=6} Editor_state.screen_top1 = {line=3, pos=5} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'jkl', 'baseline/screen:1') @@ -1095,7 +1027,6 @@ function test_up_arrow_scrolls_up_to_final_screen_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=2, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'ghi', 'baseline/screen:1') @@ -1125,7 +1056,6 @@ function test_up_arrow_scrolls_up_to_empty_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=2, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'abc', 'baseline/screen:1') @@ -1152,7 +1082,6 @@ function test_pageup() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=2, pos=1} - Editor_state.screen_bottom1 = {} -- initially the last two lines are displayed edit.draw(Editor_state) local y = Editor_state.top @@ -1177,7 +1106,6 @@ function test_pageup_scrolls_up_by_screen_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=2, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'ghi', 'baseline/screen:1') @@ -1206,7 +1134,6 @@ function test_pageup_scrolls_up_from_middle_screen_line() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=5} Editor_state.screen_top1 = {line=2, pos=5} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) local y = Editor_state.top App.screen.check(y, 'jkl', 'baseline/screen:2') @@ -1232,7 +1159,6 @@ function test_left_arrow_scrolls_up_in_wrapped_line() Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'} Text.redraw_all(Editor_state) Editor_state.screen_top1 = {line=3, pos=5} - Editor_state.screen_bottom1 = {} -- cursor is at top of screen Editor_state.cursor1 = {line=3, pos=5} edit.draw(Editor_state) @@ -1261,7 +1187,6 @@ function test_right_arrow_scrolls_down_in_wrapped_line() Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'} Text.redraw_all(Editor_state) Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} -- cursor is at bottom right of screen Editor_state.cursor1 = {line=3, pos=5} edit.draw(Editor_state) @@ -1291,7 +1216,6 @@ function test_home_scrolls_up_in_wrapped_line() Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'} Text.redraw_all(Editor_state) Editor_state.screen_top1 = {line=3, pos=5} - Editor_state.screen_bottom1 = {} -- cursor is at top of screen Editor_state.cursor1 = {line=3, pos=5} edit.draw(Editor_state) @@ -1320,7 +1244,6 @@ function test_end_scrolls_down_in_wrapped_line() Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'} Text.redraw_all(Editor_state) Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} -- cursor is at bottom right of screen Editor_state.cursor1 = {line=3, pos=5} edit.draw(Editor_state) @@ -1350,7 +1273,6 @@ function test_search() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- search for a string edit.run_after_keychord(Editor_state, 'C-f', 'f') @@ -1377,7 +1299,6 @@ function test_search_upwards() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- search for a string edit.run_after_keychord(Editor_state, 'C-f', 'f') @@ -1395,7 +1316,6 @@ function test_search_wrap() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- search for a string edit.run_after_keychord(Editor_state, 'C-f', 'f') @@ -1413,7 +1333,6 @@ function test_search_wrap_upwards() Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} edit.draw(Editor_state) -- search upwards for a string edit.run_after_keychord(Editor_state, 'C-f', 'f') |