diff --git a/main.lua b/main.lua index b63c7fc..de50b65 100644 --- a/main.lua +++ b/main.lua @@ -20,7 +20,8 @@ function App.initialize_globals() -- a text is a table with: -- mode = 'text', -- string data, --- a (y) coord in pixels (updated while painting screen), +-- startpos, the index of data the line starts rendering from (if currently on screen), can only be >1 for topmost line on screen +-- starty, the y coord in pixels -- some cached data that's blown away and recomputed when data changes: -- fragments: snippets of rendered love.graphics.Text, guaranteed to not wrap -- screen_line_starting_pos: optional array of grapheme indices if it wraps over more than one screen line @@ -275,7 +276,11 @@ function App.draw() y = y + Drawing.pixels(line.h) + Drawing_padding_bottom else --? print('text') - line.y = y + line.starty = y + line.startpos = 1 + if line_index == Screen_top1.line then + line.startpos = Screen_top1.pos + end y, Screen_bottom1.pos = Text.draw(line, Line_width, line_index) y = y + Line_height --? print('=> y', y) diff --git a/text.lua b/text.lua index 22e7349..2defbc8 100644 --- a/text.lua +++ b/text.lua @@ -17,7 +17,7 @@ function Text.draw(line, line_width, line_index) --? love.graphics.line(Line_width,0, Line_width,App.screen.height) -- wrap long lines local x = Margin_left - local y = line.y + local y = line.starty local pos = 1 local screen_line_starting_pos = 1 if line.fragments == nil then @@ -659,11 +659,11 @@ function Text.snap_cursor_to_bottom_of_screen() end function Text.in_line(line_index,line, x,y) - if line.y == nil then return false end -- outside current page + if line.starty == nil then return false end -- outside current page if x < Margin_left then return false end - if y < line.y then return false end + if y < line.starty then return false end Text.populate_screen_line_starting_pos(line_index) - return y < line.y + #line.screen_line_starting_pos * Line_height + return y < line.starty + Line_height*(#line.screen_line_starting_pos - Text.screen_line_index(line, line.startpos) + 1) end -- convert mx,my in pixels to schema-1 coordinates @@ -672,11 +672,13 @@ function Text.to_pos_on_line(line, mx, my) if line.fragments == nil then Text.compute_fragments(line, Line_width) end - assert(my >= line.y) + assert(my >= line.starty) -- duplicate some logic from Text.draw - local y = line.y - for screen_line_index,screen_line_starting_pos in ipairs(line.screen_line_starting_pos) do - local screen_line_starting_byte_offset = Text.offset(line.data, screen_line_starting_pos) + local y = line.starty + local start_screen_line_index = Text.screen_line_index(line, line.startpos) + for screen_line_index = start_screen_line_index,#line.screen_line_starting_pos do + local screen_line_starting_pos = line.screen_line_starting_pos[screen_line_index] + local screen_line_starting_byte_offset = Text.offset(line.data, screen_line_starting_pos) --? print('iter', y, screen_line_index, screen_line_starting_pos, string.sub(line.data, screen_line_starting_byte_offset)) local nexty = y + Line_height if my < nexty then @@ -725,6 +727,14 @@ function Text.screen_line_width(line, i) return App.width(screen_line_text) end +function Text.screen_line_index(line, pos) + for i = #line.screen_line_starting_pos,1,-1 do + if line.screen_line_starting_pos[i] <= pos then + return i + end + end +end + function Text.nearest_cursor_pos(line, x) -- x includes left margin if x == 0 then return 1 @@ -909,7 +919,8 @@ end function Text.redraw_all() --? print('clearing fragments') for _,line in ipairs(Lines) do - line.y = nil + line.starty = nil + line.startpos = nil Text.clear_cache(line) end end diff --git a/text_tests.lua b/text_tests.lua index 2a6c45d..5c21038 100644 --- a/text_tests.lua +++ b/text_tests.lua @@ -184,6 +184,27 @@ function test_click_on_wrapping_line() check_eq(Cursor1.pos, 13, 'F - test_click_on_wrapping_line/cursor:pos') end +function test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen() + io.write('\ntest_click_on_wrapping_line_rendered_from_partway_at_top_of_screen') + -- display a wrapping line from its second screen line + App.screen.init{width=80, height=80} + -- 12345678901234 + Lines = load_array{"madam I'm adam"} + Line_width = 75 + Cursor1 = {line=1, pos=8} + Screen_top1 = {line=1, pos=7} + Screen_bottom1 = {} + App.draw() + local y = Margin_top + App.screen.check(y, "I'm ada", 'F - test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen/baseline/screen:2') + y = y + Line_height + -- click past end of second screen line + App.run_after_mouse_click(App.screen.width-2,y-2, 1) + -- cursor moves to end of screen line + check_eq(Cursor1.line, 1, 'F - test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen/cursor:line') + check_eq(Cursor1.pos, 13, 'F - test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen/cursor:pos') +end + function test_click_past_end_of_wrapping_line() io.write('\ntest_click_past_end_of_wrapping_line') -- display a wrapping line