2022-05-18 04:29:08 +01:00
-- primitives for editing text
Text = { }
2022-05-18 06:36:10 +01:00
local utf8 = require ' utf8 '
2022-05-22 04:52:44 +01:00
-- return values:
-- y coordinate drawn until in px
-- position of start of final screen line drawn
2022-05-20 14:09:37 +01:00
function Text . draw ( line , line_width , line_index )
2022-05-23 19:02:37 +01:00
--? print('text.draw')
2022-05-18 04:29:08 +01:00
love.graphics . setColor ( 0 , 0 , 0 )
2022-05-20 01:29:14 +01:00
-- wrap long lines
local x = 25
local y = line.y
local pos = 1
2022-05-22 04:52:44 +01:00
local screen_line_starting_pos = 1
2022-05-20 01:29:14 +01:00
if line.fragments == nil then
Text.compute_fragments ( line , line_width )
2022-05-18 04:29:08 +01:00
end
2022-05-24 06:31:36 +01:00
if line.screen_line_starting_pos == nil then
Text.populate_screen_line_starting_pos ( line_index )
end
2022-05-24 07:00:07 +01:00
--? print('--')
2022-05-20 01:29:14 +01:00
for _ , f in ipairs ( line.fragments ) do
local frag , frag_text = f.data , f.text
-- render fragment
2022-05-23 07:17:06 +01:00
local frag_width = math.floor ( App.width ( frag_text ) * Zoom )
2022-05-23 23:34:05 +01:00
local s = tostring
2022-05-24 06:31:36 +01:00
--? print('('..s(x)..','..s(y)..') '..frag..'('..s(frag_width)..' vs '..s(line_width)..') '..s(line_index)..' vs '..s(Screen_top1.line)..'; '..s(pos)..' vs '..s(Screen_top1.pos)..'; bottom: '..s(Screen_bottom1.line)..'/'..s(Screen_bottom1.pos))
2022-05-20 01:29:14 +01:00
if x + frag_width > line_width then
assert ( x > 25 ) -- no overfull lines
2022-05-24 00:35:25 +01:00
-- update y only after drawing the first screen line of screen top
if line_index > Screen_top1.line or ( line_index == Screen_top1.line and pos > Screen_top1.pos ) then
2022-05-20 14:58:17 +01:00
y = y + math.floor ( 15 * Zoom )
2022-05-23 23:34:05 +01:00
if y + math.floor ( 15 * Zoom ) > App.screen . height then
2022-05-24 06:31:36 +01:00
--? print('b', y, App.screen.height, '=>', screen_line_starting_pos)
2022-05-22 06:08:13 +01:00
return y , screen_line_starting_pos
end
2022-05-24 06:31:36 +01:00
screen_line_starting_pos = pos
--? print('text: new screen line', y, App.screen.height, screen_line_starting_pos)
2022-05-22 06:08:13 +01:00
end
2022-05-24 06:31:36 +01:00
x = 25
2022-05-20 01:29:14 +01:00
end
2022-05-24 06:31:36 +01:00
--? print('checking to draw', pos, Screen_top1.pos)
2022-05-23 23:34:05 +01:00
-- don't draw text above screen top
2022-05-24 00:35:25 +01:00
if line_index > Screen_top1.line or ( line_index == Screen_top1.line and pos >= Screen_top1.pos ) then
2022-05-24 07:00:07 +01:00
--? print('drawing '..frag)
2022-05-23 07:17:06 +01:00
App.screen . draw ( frag_text , x , y , 0 , Zoom )
2022-05-20 14:58:17 +01:00
end
2022-05-20 01:29:14 +01:00
-- render cursor if necessary
local frag_len = utf8.len ( frag )
2022-05-22 00:24:07 +01:00
if line_index == Cursor1.line then
if pos <= Cursor1.pos and pos + frag_len > Cursor1.pos then
Text.draw_cursor ( x + Text.cursor_x2 ( frag , Cursor1.pos - pos + 1 ) , y )
2022-05-20 01:29:14 +01:00
end
end
x = x + frag_width
pos = pos + frag_len
end
2022-05-22 00:24:07 +01:00
if line_index == Cursor1.line and Cursor1.pos == pos then
2022-05-20 13:40:42 +01:00
Text.draw_cursor ( x , y )
2022-05-20 06:35:26 +01:00
end
2022-05-22 04:52:44 +01:00
return y , screen_line_starting_pos
2022-05-18 04:29:08 +01:00
end
2022-05-20 01:29:14 +01:00
-- manual tests:
-- draw with small line_width of 100
-- short words break on spaces
-- long words break when they must
2022-05-18 04:29:08 +01:00
2022-05-20 13:40:42 +01:00
function Text . draw_cursor ( x , y )
love.graphics . setColor ( 1 , 0 , 0 )
love.graphics . circle ( ' fill ' , x , y + math.floor ( 15 * Zoom ) , 2 )
love.graphics . setColor ( 0 , 0 , 0 )
2022-05-20 13:41:25 +01:00
Cursor_x = x
Cursor_y = y + math.floor ( 15 * Zoom )
2022-05-20 13:40:42 +01:00
end
2022-05-23 07:17:06 +01:00
function test_draw_text ( )
2022-05-24 01:40:44 +01:00
io.write ( ' \n test_draw_text ' )
2022-05-23 07:17:06 +01:00
App.screen . init { width = 120 , height = 60 }
Lines = load_array { ' abc ' , ' def ' , ' ghi ' }
2022-05-23 23:22:16 +01:00
Line_width = App.screen . width
2022-05-23 07:17:06 +01:00
Cursor1 = { line = 1 , pos = 1 }
Screen_top1 = { line = 1 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
App.draw ( )
local screen_top_margin = 15 -- pixels
local line_height = 15 -- pixels
local y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_draw_text/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_draw_text/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_draw_text/screen:3 ' )
end
2022-05-25 20:59:12 +01:00
function test_draw_wrapping_text ( )
io.write ( ' \n test_draw_wrapping_text ' )
App.screen . init { width = 50 , height = 60 }
Lines = load_array { ' abc ' , ' defgh ' , ' xyz ' }
Line_width = App.screen . width
Cursor1 = { line = 1 , pos = 1 }
Screen_top1 = { line = 1 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
App.draw ( )
local screen_top_margin = 15 -- pixels
local line_height = 15 -- pixels
local y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_draw_wrapping_text/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_draw_wrapping_text/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' gh ' , ' F - test_draw_wrapping_text/screen:3 ' )
end
function test_edit_wrapping_text ( )
io.write ( ' \n test_edit_wrapping_text ' )
App.screen . init { width = 50 , height = 60 }
Lines = load_array { ' abc ' , ' def ' , ' xyz ' }
Line_width = App.screen . width
Cursor1 = { line = 2 , pos = 4 }
Screen_top1 = { line = 1 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
App.run_after_textinput ( ' g ' )
App.run_after_textinput ( ' h ' )
App.run_after_textinput ( ' i ' )
App.run_after_textinput ( ' j ' )
App.run_after_textinput ( ' k ' )
App.run_after_textinput ( ' l ' )
local screen_top_margin = 15 -- pixels
local line_height = 15 -- pixels
local y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_edit_wrapping_text/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_edit_wrapping_text/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' ghij ' , ' F - test_edit_wrapping_text/screen:3 ' )
end
2022-05-25 21:38:33 +01:00
function test_move_cursor_using_mouse ( )
io.write ( ' \n test_move_cursor_using_mouse ' )
App.screen . init { width = 50 , height = 60 }
Lines = load_array { ' abc ' , ' def ' , ' xyz ' }
Line_width = App.screen . width
Cursor1 = { line = 1 , pos = 1 }
Screen_top1 = { line = 1 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
App.draw ( ) -- populate line.y for each line in Lines
local screen_top_margin = 15 -- pixels
local screen_left_margin = 25 -- pixels
App.run_after_mousepress ( screen_left_margin + 8 , screen_top_margin + 5 , ' 1 ' )
check_eq ( Cursor1.line , 1 , ' F - test_move_cursor_using_mouse/cursor:line ' )
check_eq ( Cursor1.pos , 2 , ' F - test_move_cursor_using_mouse/cursor:pos ' )
end
2022-05-23 16:13:58 +01:00
function test_pagedown ( )
2022-05-24 01:40:44 +01:00
io.write ( ' \n test_pagedown ' )
2022-05-23 16:13:58 +01:00
App.screen . init { width = 120 , height = 45 }
Lines = load_array { ' abc ' , ' def ' , ' ghi ' }
2022-05-23 23:22:16 +01:00
Line_width = App.screen . width
2022-05-23 16:13:58 +01:00
Cursor1 = { line = 1 , pos = 1 }
Screen_top1 = { line = 1 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
local line_height = math.floor ( 15 * Zoom ) -- pixels
-- initially the first two lines are displayed
App.draw ( )
local y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_pagedown/baseline/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_pagedown/baseline/screen:2 ' )
-- after pagedown the bottom line becomes the top
App.run_after_keychord ( ' pagedown ' )
2022-05-23 23:22:16 +01:00
check_eq ( Screen_top1.line , 2 , ' F - test_pagedown/screen_top ' )
check_eq ( Cursor1.line , 2 , ' F - test_pagedown/cursor ' )
2022-05-23 16:13:58 +01:00
y = screen_top_margin
App.screen . check ( y , ' def ' , ' F - test_pagedown/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_pagedown/screen:2 ' )
end
2022-05-23 18:56:39 +01:00
function test_pagedown_skips_drawings ( )
2022-05-24 01:40:44 +01:00
io.write ( ' \n test_pagedown_skips_drawings ' )
2022-05-23 16:13:58 +01:00
-- some lines of text with a drawing intermixed
2022-05-23 16:52:13 +01:00
App.screen . init { width = 50 , height = 80 }
Lines = load_array { ' abc ' , -- height 15
' ```lines ' , ' ``` ' , -- height 25
' def ' , -- height 15
' ghi ' } -- height 15
2022-05-23 18:56:39 +01:00
check_eq ( Lines [ 2 ] . mode , ' drawing ' , ' F - test_pagedown_skips_drawings/baseline/lines ' )
2022-05-23 16:13:58 +01:00
Line_width = App.screen . width
Cursor1 = { line = 1 , pos = 1 }
Screen_top1 = { line = 1 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
2022-05-23 16:52:13 +01:00
local drawing_height = 20 + App.screen . 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
2022-05-23 16:13:58 +01:00
App.draw ( )
local y = screen_top_margin
2022-05-23 18:56:39 +01:00
App.screen . check ( y , ' abc ' , ' F - test_pagedown_skips_drawings/baseline/screen:1 ' )
2022-05-23 23:22:16 +01:00
-- after pagedown the screen draws the drawing up top
2022-05-23 16:52:13 +01:00
-- 15px margin + 10px margin + 25px drawing + 10px margin + 15px line3 = 75px < screen height 80px
2022-05-23 16:13:58 +01:00
App.run_after_keychord ( ' pagedown ' )
2022-05-23 23:22:16 +01:00
check_eq ( Screen_top1.line , 2 , ' F - test_pagedown_skips_drawings/screen_top ' )
check_eq ( Cursor1.line , 3 , ' F - test_pagedown_skips_drawings/cursor ' )
2022-05-23 16:13:58 +01:00
y = screen_top_margin + drawing_height
2022-05-23 18:56:39 +01:00
App.screen . check ( y , ' def ' , ' F - test_pagedown_skips_drawings/screen:1 ' )
end
2022-05-24 06:31:36 +01:00
function test_pagedown_shows_one_screen_line_in_common ( )
io.write ( ' \n test_pagedown_shows_one_screen_line_in_common ' )
-- some lines of text with a drawing intermixed
App.screen . init { width = 50 , height = 60 }
Lines = load_array { ' abc ' , ' def ghi jkl ' , ' mno ' }
Line_width = App.screen . width
Cursor1 = { line = 1 , pos = 1 }
Screen_top1 = { line = 1 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
local line_height = math.floor ( 15 * Zoom ) -- pixels
App.draw ( )
local y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_pagedown_shows_one_screen_line_in_common/baseline/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_pagedown_shows_one_screen_line_in_common/baseline/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_pagedown_shows_one_screen_line_in_common/baseline/screen:3 ' )
-- after pagedown the bottom screen line becomes the top
App.run_after_keychord ( ' pagedown ' )
check_eq ( Screen_top1.line , 2 , ' F - test_pagedown_shows_one_screen_line_in_common/screen_top:line ' )
check_eq ( Screen_top1.pos , 5 , ' F - test_pagedown_shows_one_screen_line_in_common/screen_top:pos ' )
check_eq ( Cursor1.line , 2 , ' F - test_pagedown_shows_one_screen_line_in_common/cursor:line ' )
check_eq ( Cursor1.pos , 5 , ' F - test_pagedown_shows_one_screen_line_in_common/cursor:pos ' )
y = screen_top_margin
App.screen . check ( y , ' ghi ' , ' F - test_pagedown_shows_one_screen_line_in_common/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' jkl ' , ' F - test_pagedown_shows_one_screen_line_in_common/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' mn ' , ' F - test_pagedown_shows_one_screen_line_in_common/screen:3 ' )
end
2022-05-23 18:56:39 +01:00
function test_down_arrow_moves_cursor ( )
2022-05-24 01:40:44 +01:00
io.write ( ' \n test_down_arrow_moves_cursor ' )
2022-05-23 18:56:39 +01:00
App.screen . init { width = 120 , height = 60 }
Lines = load_array { ' abc ' , ' def ' , ' ghi ' , ' jkl ' }
2022-05-23 23:22:16 +01:00
Line_width = App.screen . width
2022-05-23 18:56:39 +01:00
Cursor1 = { line = 1 , pos = 1 }
Screen_top1 = { line = 1 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
local line_height = math.floor ( 15 * Zoom ) -- pixels
-- initially the first three lines are displayed
App.draw ( )
local y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_down_arrow_moves_cursor/baseline/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_down_arrow_moves_cursor/baseline/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_down_arrow_moves_cursor/baseline/screen:3 ' )
2022-05-23 23:22:16 +01:00
-- after hitting the down arrow, the cursor moves down by 1 line
2022-05-23 18:56:39 +01:00
App.run_after_keychord ( ' down ' )
2022-05-24 22:34:50 +01:00
check_eq ( Screen_top1.line , 1 , ' F - test_down_arrow_moves_cursor/screen_top ' )
2022-05-23 23:22:16 +01:00
check_eq ( Cursor1.line , 2 , ' F - test_down_arrow_moves_cursor/cursor ' )
-- the screen is unchanged
2022-05-23 18:56:39 +01:00
y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_down_arrow_moves_cursor/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_down_arrow_moves_cursor/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_down_arrow_moves_cursor/screen:3 ' )
end
function test_down_arrow_scrolls_down_by_one_line ( )
2022-05-24 01:40:44 +01:00
io.write ( ' \n test_down_arrow_scrolls_down_by_one_line ' )
2022-05-23 18:56:39 +01:00
-- display the first three lines with the cursor on the bottom line
App.screen . init { width = 120 , height = 60 }
Lines = load_array { ' abc ' , ' def ' , ' ghi ' , ' jkl ' }
2022-05-23 23:22:16 +01:00
Line_width = App.screen . width
2022-05-23 18:56:39 +01:00
Cursor1 = { line = 3 , pos = 1 }
Screen_top1 = { line = 1 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
local line_height = math.floor ( 15 * Zoom ) -- pixels
App.draw ( )
local y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_down_arrow_scrolls_down_by_one_line/baseline/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_down_arrow_scrolls_down_by_one_line/baseline/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_down_arrow_scrolls_down_by_one_line/baseline/screen:3 ' )
-- after hitting the down arrow the screen scrolls down by one line
App.run_after_keychord ( ' down ' )
2022-05-23 23:22:16 +01:00
check_eq ( Screen_top1.line , 2 , ' F - test_down_arrow_scrolls_down_by_one_line/screen_top ' )
check_eq ( Cursor1.line , 4 , ' F - test_down_arrow_scrolls_down_by_one_line/cursor ' )
2022-05-23 18:56:39 +01:00
y = screen_top_margin
App.screen . check ( y , ' def ' , ' F - test_down_arrow_scrolls_down_by_one_line/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_down_arrow_scrolls_down_by_one_line/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' jkl ' , ' F - test_down_arrow_scrolls_down_by_one_line/screen:3 ' )
2022-05-23 23:22:16 +01:00
end
function test_down_arrow_scrolls_down_by_one_screen_line ( )
2022-05-24 01:40:44 +01:00
io.write ( ' \n test_down_arrow_scrolls_down_by_one_screen_line ' )
2022-05-23 23:22:16 +01:00
-- display the first three lines with the cursor on the bottom line
App.screen . init { width = 25 + 30 , height = 60 }
Lines = load_array { ' abc ' , ' def ' , ' ghi jkl ' , ' mno ' }
Line_width = App.screen . width
Cursor1 = { line = 3 , pos = 1 }
Screen_top1 = { line = 1 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
local line_height = math.floor ( 15 * Zoom ) -- pixels
App.draw ( )
local y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_down_arrow_scrolls_down_by_one_screen_line/baseline/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_down_arrow_scrolls_down_by_one_screen_line/baseline/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_down_arrow_scrolls_down_by_one_screen_line/baseline/screen:3 ' ) -- line wrapping includes trailing whitespace
-- after hitting the down arrow the screen scrolls down by one line
App.run_after_keychord ( ' down ' )
check_eq ( Screen_top1.line , 2 , ' F - test_down_arrow_scrolls_down_by_one_screen_line/screen_top ' )
check_eq ( Cursor1.line , 3 , ' F - test_down_arrow_scrolls_down_by_one_screen_line/cursor:line ' )
check_eq ( Cursor1.pos , 5 , ' F - test_down_arrow_scrolls_down_by_one_screen_line/cursor:pos ' )
y = screen_top_margin
App.screen . check ( y , ' def ' , ' F - test_down_arrow_scrolls_down_by_one_screen_line/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_down_arrow_scrolls_down_by_one_screen_line/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' jkl ' , ' F - test_down_arrow_scrolls_down_by_one_screen_line/screen:3 ' )
end
function test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word ( )
2022-05-24 01:40:44 +01:00
io.write ( ' \n test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word ' )
2022-05-23 23:22:16 +01:00
-- display the first three lines with the cursor on the bottom line
App.screen . init { width = 25 + 30 , height = 60 }
Lines = load_array { ' abc ' , ' def ' , ' ghijkl ' , ' mno ' }
Line_width = App.screen . width
Cursor1 = { line = 3 , pos = 1 }
Screen_top1 = { line = 1 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
local line_height = math.floor ( 15 * Zoom ) -- pixels
App.draw ( )
local y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/baseline/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/baseline/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' ghijk ' , ' F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/baseline/screen:3 ' )
-- after hitting the down arrow the screen scrolls down by one line
App.run_after_keychord ( ' down ' )
check_eq ( Screen_top1.line , 2 , ' F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen_top ' )
check_eq ( Cursor1.line , 3 , ' F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/cursor:line ' )
check_eq ( Cursor1.pos , 6 , ' F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/cursor:pos ' )
y = screen_top_margin
App.screen . check ( y , ' def ' , ' F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' ghijk ' , ' F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' l ' , ' F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen:3 ' )
2022-05-23 19:02:37 +01:00
end
2022-05-24 23:01:09 +01:00
function test_page_down_followed_by_down_arrow_does_not_scroll_screen_up ( )
io.write ( ' \n test_page_down_followed_by_down_arrow_does_not_scroll_screen_up ' )
App.screen . init { width = 25 + 30 , height = 60 }
Lines = load_array { ' abc ' , ' def ' , ' ghijkl ' , ' mno ' }
Line_width = App.screen . width
Cursor1 = { line = 3 , pos = 1 }
Screen_top1 = { line = 1 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
local line_height = math.floor ( 15 * Zoom ) -- pixels
App.draw ( )
local y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' ghijk ' , ' F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline/screen:3 ' )
-- after hitting pagedown the screen scrolls down to start of a long line
App.run_after_keychord ( ' pagedown ' )
check_eq ( Screen_top1.line , 3 , ' F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline2/screen_top ' )
check_eq ( Cursor1.line , 3 , ' F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline2/cursor:line ' )
check_eq ( Cursor1.pos , 1 , ' F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline2/cursor:pos ' )
-- after hitting down arrow the screen doesn't scroll down further, and certainly doesn't scroll up
App.run_after_keychord ( ' down ' )
check_eq ( Screen_top1.line , 3 , ' F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/screen_top ' )
check_eq ( Cursor1.line , 3 , ' F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/cursor:line ' )
check_eq ( Cursor1.pos , 6 , ' F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/cursor:pos ' )
y = screen_top_margin
App.screen . check ( y , ' ghijk ' , ' F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' l ' , ' F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' mno ' , ' F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/screen:3 ' )
end
2022-05-23 19:02:37 +01:00
function test_up_arrow_moves_cursor ( )
2022-05-24 01:40:44 +01:00
io.write ( ' \n test_up_arrow_moves_cursor ' )
2022-05-23 19:02:37 +01:00
-- display the first 3 lines with the cursor on the bottom line
App.screen . init { width = 120 , height = 60 }
Lines = load_array { ' abc ' , ' def ' , ' ghi ' , ' jkl ' }
Line_width = 120
Cursor1 = { line = 3 , pos = 1 }
Screen_top1 = { line = 1 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
local line_height = math.floor ( 15 * Zoom ) -- pixels
App.draw ( )
local y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_up_arrow_moves_cursor/baseline/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_up_arrow_moves_cursor/baseline/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_up_arrow_moves_cursor/baseline/screen:3 ' )
2022-05-23 23:22:16 +01:00
-- after hitting the up arrow the cursor moves up by 1 line
2022-05-23 19:02:37 +01:00
App.run_after_keychord ( ' up ' )
2022-05-23 23:22:16 +01:00
check_eq ( Screen_top1.line , 1 , ' F - test_up_arrow_moves_cursor/screen_top ' )
check_eq ( Cursor1.line , 2 , ' F - test_up_arrow_moves_cursor/cursor ' )
-- the screen is unchanged
2022-05-23 19:02:37 +01:00
y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_up_arrow_moves_cursor/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_up_arrow_moves_cursor/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_up_arrow_moves_cursor/screen:3 ' )
end
function test_up_arrow_scrolls_up_by_one_line ( )
2022-05-24 01:40:44 +01:00
io.write ( ' \n test_up_arrow_scrolls_up_by_one_line ' )
2022-05-23 19:02:37 +01:00
-- display the lines 2/3/4 with the cursor on line 2
App.screen . init { width = 120 , height = 60 }
Lines = load_array { ' abc ' , ' def ' , ' ghi ' , ' jkl ' }
Line_width = 120
Cursor1 = { line = 2 , pos = 1 }
Screen_top1 = { line = 2 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
local line_height = math.floor ( 15 * Zoom ) -- pixels
App.draw ( )
local y = screen_top_margin
App.screen . check ( y , ' def ' , ' F - test_up_arrow_scrolls_up_by_one_line/baseline/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_up_arrow_scrolls_up_by_one_line/baseline/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' jkl ' , ' F - test_up_arrow_scrolls_up_by_one_line/baseline/screen:3 ' )
-- after hitting the up arrow the screen scrolls up by one line
App.run_after_keychord ( ' up ' )
2022-05-23 23:22:16 +01:00
check_eq ( Screen_top1.line , 1 , ' F - test_up_arrow_scrolls_up_by_one_line/screen_top ' )
check_eq ( Cursor1.line , 1 , ' F - test_up_arrow_scrolls_up_by_one_line/cursor ' )
2022-05-23 19:02:37 +01:00
y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_up_arrow_scrolls_up_by_one_line/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_up_arrow_scrolls_up_by_one_line/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_up_arrow_scrolls_up_by_one_line/screen:3 ' )
2022-05-23 23:22:16 +01:00
end
function test_up_arrow_scrolls_up_by_one_screen_line ( )
2022-05-24 01:40:44 +01:00
io.write ( ' \n test_up_arrow_scrolls_up_by_one_screen_line ' )
2022-05-24 01:39:00 +01:00
-- display lines starting from second screen line of a line
2022-05-24 00:35:25 +01:00
App.screen . init { width = 25 + 30 , height = 60 }
Lines = load_array { ' abc ' , ' def ' , ' ghi jkl ' , ' mno ' }
2022-05-23 23:22:16 +01:00
Line_width = App.screen . width
2022-05-24 00:35:25 +01:00
Cursor1 = { line = 3 , pos = 6 }
Screen_top1 = { line = 3 , pos = 5 }
2022-05-23 23:22:16 +01:00
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
local line_height = math.floor ( 15 * Zoom ) -- pixels
App.draw ( )
local y = screen_top_margin
2022-05-24 00:35:25 +01:00
App.screen . check ( y , ' jkl ' , ' F - test_up_arrow_scrolls_up_by_one_screen_line/baseline/screen:1 ' )
2022-05-23 23:22:16 +01:00
y = y + line_height
2022-05-24 00:35:25 +01:00
App.screen . check ( y , ' mno ' , ' F - test_up_arrow_scrolls_up_by_one_screen_line/baseline/screen:2 ' )
2022-05-24 01:39:00 +01:00
-- after hitting the up arrow the screen scrolls up to first screen line
2022-05-23 23:22:16 +01:00
App.run_after_keychord ( ' up ' )
y = screen_top_margin
2022-05-24 00:35:25 +01:00
App.screen . check ( y , ' ghi ' , ' F - test_up_arrow_scrolls_up_by_one_screen_line/screen:1 ' )
2022-05-23 23:22:16 +01:00
y = y + line_height
2022-05-24 00:35:25 +01:00
App.screen . check ( y , ' jkl ' , ' F - test_up_arrow_scrolls_up_by_one_screen_line/screen:2 ' )
2022-05-23 23:22:16 +01:00
y = y + line_height
2022-05-24 00:35:25 +01:00
App.screen . check ( y , ' mno ' , ' F - test_up_arrow_scrolls_up_by_one_screen_line/screen:3 ' )
check_eq ( Screen_top1.line , 3 , ' F - test_up_arrow_scrolls_up_by_one_screen_line/screen_top ' )
check_eq ( Screen_top1.pos , 1 , ' F - test_up_arrow_scrolls_up_by_one_screen_line/screen_top ' )
check_eq ( Cursor1.line , 3 , ' F - test_up_arrow_scrolls_up_by_one_screen_line/cursor ' )
check_eq ( Cursor1.pos , 1 , ' F - test_up_arrow_scrolls_up_by_one_screen_line/cursor ' )
2022-05-23 16:13:58 +01:00
end
2022-05-24 01:39:00 +01:00
function test_up_arrow_scrolls_up_to_final_screen_line ( )
2022-05-24 01:40:44 +01:00
io.write ( ' \n test_up_arrow_scrolls_up_to_final_screen_line ' )
2022-05-24 01:39:00 +01:00
-- display lines starting just after a long line
App.screen . init { width = 25 + 30 , height = 60 }
Lines = load_array { ' abc def ' , ' ghi ' , ' jkl ' , ' mno ' }
Line_width = App.screen . width
Cursor1 = { line = 2 , pos = 1 }
Screen_top1 = { line = 2 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
local line_height = math.floor ( 15 * Zoom ) -- pixels
App.draw ( )
local y = screen_top_margin
App.screen . check ( y , ' ghi ' , ' F - test_up_arrow_scrolls_up_to_final_screen_line/baseline/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' jkl ' , ' F - test_up_arrow_scrolls_up_to_final_screen_line/baseline/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' mno ' , ' F - test_up_arrow_scrolls_up_to_final_screen_line/baseline/screen:3 ' )
-- after hitting the up arrow the screen scrolls up to final screen line of previous line
App.run_after_keychord ( ' up ' )
y = screen_top_margin
App.screen . check ( y , ' def ' , ' F - test_up_arrow_scrolls_up_to_final_screen_line/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_up_arrow_scrolls_up_to_final_screen_line/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' jkl ' , ' F - test_up_arrow_scrolls_up_to_final_screen_line/screen:3 ' )
check_eq ( Screen_top1.line , 1 , ' F - test_up_arrow_scrolls_up_to_final_screen_line/screen_top ' )
check_eq ( Screen_top1.pos , 5 , ' F - test_up_arrow_scrolls_up_to_final_screen_line/screen_top ' )
check_eq ( Cursor1.line , 1 , ' F - test_up_arrow_scrolls_up_to_final_screen_line/cursor ' )
check_eq ( Cursor1.pos , 5 , ' F - test_up_arrow_scrolls_up_to_final_screen_line/cursor ' )
end
2022-05-24 22:34:50 +01:00
function test_up_arrow_scrolls_up_to_empty_line ( )
io.write ( ' \n test_up_arrow_scrolls_up_from_empty_line ' )
-- display a screenful of text with an empty line just above it outside the screen
App.screen . init { width = 120 , height = 60 }
Lines = load_array { ' ' , ' abc ' , ' def ' , ' ghi ' , ' jkl ' }
Line_width = 120
Cursor1 = { line = 2 , pos = 1 }
Screen_top1 = { line = 2 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
local line_height = math.floor ( 15 * Zoom ) -- pixels
App.draw ( )
local y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_up_arrow_scrolls_up_from_empty_line/baseline/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_up_arrow_scrolls_up_from_empty_line/baseline/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_up_arrow_scrolls_up_from_empty_line/baseline/screen:3 ' )
-- after hitting the up arrow the screen scrolls up by one line
App.run_after_keychord ( ' up ' )
check_eq ( Screen_top1.line , 1 , ' F - test_up_arrow_scrolls_up_from_empty_line/screen_top ' )
check_eq ( Cursor1.line , 1 , ' F - test_up_arrow_scrolls_up_from_empty_line/cursor ' )
y = screen_top_margin
-- empty first line
y = y + line_height
App.screen . check ( y , ' abc ' , ' F - test_up_arrow_scrolls_up_from_empty_line/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_up_arrow_scrolls_up_from_empty_line/screen:3 ' )
end
2022-05-24 04:57:56 +01:00
function test_pageup ( )
io.write ( ' \n test_pageup ' )
App.screen . init { width = 120 , height = 45 }
Lines = load_array { ' abc ' , ' def ' , ' ghi ' }
Line_width = App.screen . width
Cursor1 = { line = 2 , pos = 1 }
Screen_top1 = { line = 2 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
local line_height = math.floor ( 15 * Zoom ) -- pixels
-- initially the last two lines are displayed
App.draw ( )
local y = screen_top_margin
App.screen . check ( y , ' def ' , ' F - test_pageup/baseline/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_pageup/baseline/screen:2 ' )
-- after pageup the cursor goes to first line
App.run_after_keychord ( ' pageup ' )
check_eq ( Screen_top1.line , 1 , ' F - test_pageup/screen_top ' )
check_eq ( Cursor1.line , 1 , ' F - test_pageup/cursor ' )
y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_pageup/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_pageup/screen:2 ' )
end
function test_pageup_scrolls_up_by_screen_line ( )
io.write ( ' \n test_pageup_scrolls_up_by_screen_line ' )
-- display the first three lines with the cursor on the bottom line
App.screen . init { width = 25 + 30 , height = 60 }
Lines = load_array { ' abc def ' , ' ghi ' , ' jkl ' , ' mno ' }
Line_width = App.screen . width
Cursor1 = { line = 2 , pos = 1 }
Screen_top1 = { line = 2 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
local line_height = math.floor ( 15 * Zoom ) -- pixels
App.draw ( )
local y = screen_top_margin
App.screen . check ( y , ' ghi ' , ' F - test_pageup_scrolls_up_by_screen_line/baseline/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' jkl ' , ' F - test_pageup_scrolls_up_by_screen_line/baseline/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' mno ' , ' F - test_pageup_scrolls_up_by_screen_line/baseline/screen:3 ' ) -- line wrapping includes trailing whitespace
-- after hitting the page-up key the screen scrolls up to top
App.run_after_keychord ( ' pageup ' )
check_eq ( Screen_top1.line , 1 , ' F - test_pageup_scrolls_up_by_screen_line/screen_top ' )
check_eq ( Cursor1.line , 1 , ' F - test_pageup_scrolls_up_by_screen_line/cursor:line ' )
check_eq ( Cursor1.pos , 1 , ' F - test_pageup_scrolls_up_by_screen_line/cursor:pos ' )
y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_pageup_scrolls_up_by_screen_line/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_pageup_scrolls_up_by_screen_line/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_pageup_scrolls_up_by_screen_line/screen:3 ' )
end
function test_pageup_scrolls_up_from_middle_screen_line ( )
io.write ( ' \n test_pageup_scrolls_up_from_middle_screen_line ' )
-- display a few lines starting from the middle of a line (Cursor1.pos > 1)
App.screen . init { width = 25 + 30 , height = 60 }
Lines = load_array { ' abc def ' , ' ghi jkl ' , ' mno ' }
Line_width = App.screen . width
Cursor1 = { line = 2 , pos = 5 }
Screen_top1 = { line = 2 , pos = 5 }
Screen_bottom1 = { }
Zoom = 1
local screen_top_margin = 15 -- pixels
local line_height = math.floor ( 15 * Zoom ) -- pixels
App.draw ( )
local y = screen_top_margin
App.screen . check ( y , ' jkl ' , ' F - test_pageup_scrolls_up_from_middle_screen_line/baseline/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' mno ' , ' F - test_pageup_scrolls_up_from_middle_screen_line/baseline/screen:3 ' ) -- line wrapping includes trailing whitespace
-- after hitting the page-up key the screen scrolls up to top
App.run_after_keychord ( ' pageup ' )
check_eq ( Screen_top1.line , 1 , ' F - test_pageup_scrolls_up_from_middle_screen_line/screen_top ' )
check_eq ( Cursor1.line , 1 , ' F - test_pageup_scrolls_up_from_middle_screen_line/cursor:line ' )
check_eq ( Cursor1.pos , 1 , ' F - test_pageup_scrolls_up_from_middle_screen_line/cursor:pos ' )
y = screen_top_margin
App.screen . check ( y , ' abc ' , ' F - test_pageup_scrolls_up_from_middle_screen_line/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' def ' , ' F - test_pageup_scrolls_up_from_middle_screen_line/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' ghi ' , ' F - test_pageup_scrolls_up_from_middle_screen_line/screen:3 ' )
end
2022-05-25 22:31:16 +01:00
function test_position_cursor_on_recently_edited_wrapping_line ( )
-- draw a line wrapping over 2 screen lines
io.write ( ' \n test_position_cursor_on_recently_edited_wrapping_line ' )
App.screen . init { width = 120 , height = 200 }
Lines = load_array { ' abc def ghi jkl mno pqr ' , ' xyz ' }
Line_width = 100
Cursor1 = { line = 1 , pos = 25 }
Screen_top1 = { line = 1 , pos = 1 }
Screen_bottom1 = { }
Zoom = 1
App.draw ( )
local screen_top_margin = 15 -- pixels
local line_height = 15 -- pixels
local y = screen_top_margin
-- I don't understand why 120px fits so much on a fake screen, but whatever..
App.screen . check ( y , ' abc def ghi ' , ' F - test_position_cursor_on_recently_edited_wrapping_line/baseline1/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' jkl mno pqr ' , ' F - test_position_cursor_on_recently_edited_wrapping_line/baseline1/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' xyz ' , ' F - test_position_cursor_on_recently_edited_wrapping_line/baseline1/screen:3 ' )
-- add to the line until it's wrapping over 3 screen lines
App.run_after_textinput ( ' s ' )
App.run_after_textinput ( ' t ' )
App.run_after_textinput ( ' u ' )
check_eq ( Cursor1.pos , 28 , ' F - test_move_cursor_using_mouse/cursor:pos ' )
y = screen_top_margin
App.screen . check ( y , ' abc def ghi ' , ' F - test_position_cursor_on_recently_edited_wrapping_line/baseline2/screen:1 ' )
y = y + line_height
App.screen . check ( y , ' jkl mno pqr ' , ' F - test_position_cursor_on_recently_edited_wrapping_line/baseline2/screen:2 ' )
y = y + line_height
App.screen . check ( y , ' stu ' , ' F - test_position_cursor_on_recently_edited_wrapping_line/baseline2/screen:3 ' )
-- try to move the cursor earlier in the third screen line by clicking the mouse
local screen_top_margin = 15 -- pixels
local screen_left_margin = 25 -- pixels
App.run_after_mousepress ( screen_left_margin + 8 , screen_top_margin + 15 * 2 + 5 , ' 1 ' )
-- cursor should move
check_eq ( Cursor1.line , 1 , ' F - test_move_cursor_using_mouse/cursor:line ' )
check_eq ( Cursor1.pos , 26 , ' F - test_move_cursor_using_mouse/cursor:pos ' )
end
2022-05-20 06:58:04 +01:00
function Text . compute_fragments ( line , line_width )
2022-05-25 21:35:32 +01:00
--? print('compute_fragments', line_width)
2022-05-20 06:58:04 +01:00
line.fragments = { }
local x = 25
-- try to wrap at word boundaries
for frag in line.data : gmatch ( ' %S*%s* ' ) do
2022-05-23 07:17:06 +01:00
local frag_text = App.newText ( love.graphics . getFont ( ) , frag )
local frag_width = math.floor ( App.width ( frag_text ) * Zoom )
2022-05-20 06:58:04 +01:00
--? print('x: '..tostring(x)..'; '..tostring(line_width-x)..'px to go')
--? print('frag: ^'..frag..'$ is '..tostring(frag_width)..'px wide')
if x + frag_width > line_width then
2022-05-23 23:22:16 +01:00
assert ( 25 + frag_width > line_width ) -- avoid infinite loop when window is too narrow
2022-05-20 06:58:04 +01:00
while x + frag_width > line_width do
if x < 0.8 * line_width then
2022-05-23 23:38:42 +01:00
--? print(frag, x, frag_width, line_width)
2022-05-20 06:58:04 +01:00
-- long word; chop it at some letter
-- We're not going to reimplement TeX here.
2022-05-23 23:22:16 +01:00
local b = Text.nearest_pos_less_than ( frag , line_width - x )
2022-05-20 06:58:04 +01:00
--? print('space for '..tostring(b)..' graphemes')
local frag1 = string.sub ( frag , 1 , b )
2022-05-23 07:17:06 +01:00
local frag1_text = App.newText ( love.graphics . getFont ( ) , frag1 )
2022-05-23 23:22:16 +01:00
local frag1_width = math.floor ( App.width ( frag1_text ) * Zoom )
2022-05-23 23:38:42 +01:00
--? print(frag, x, frag1_width, line_width)
2022-05-23 23:22:16 +01:00
assert ( x + frag1_width <= line_width )
2022-05-20 06:58:04 +01:00
--? print('inserting '..frag1..' of width '..tostring(frag1_width)..'px')
table.insert ( line.fragments , { data = frag1 , text = frag1_text } )
frag = string.sub ( frag , b + 1 )
2022-05-23 07:17:06 +01:00
frag_text = App.newText ( love.graphics . getFont ( ) , frag )
frag_width = math.floor ( App.width ( frag_text ) * Zoom )
2022-05-20 06:58:04 +01:00
end
x = 25 -- new line
end
end
if # frag > 0 then
--? print('inserting '..frag..' of width '..tostring(frag_width)..'px')
table.insert ( line.fragments , { data = frag , text = frag_text } )
end
end
end
2022-05-21 22:03:06 +01:00
function Text . textinput ( t )
2022-05-18 06:36:10 +01:00
if love.mouse . isDown ( ' 1 ' ) then return end
2022-05-19 04:22:57 +01:00
Text.insert_at_cursor ( t )
end
function Text . insert_at_cursor ( t )
2022-05-18 06:36:10 +01:00
local byte_offset
2022-05-22 00:24:07 +01:00
if Cursor1.pos > 1 then
byte_offset = utf8.offset ( Lines [ Cursor1.line ] . data , Cursor1.pos - 1 )
2022-05-18 06:36:10 +01:00
else
byte_offset = 0
end
2022-05-22 00:24:07 +01:00
Lines [ Cursor1.line ] . data = string.sub ( Lines [ Cursor1.line ] . data , 1 , byte_offset ) .. t .. string.sub ( Lines [ Cursor1.line ] . data , byte_offset + 1 )
Lines [ Cursor1.line ] . fragments = nil
2022-05-25 22:31:16 +01:00
Lines [ Cursor1.line ] . screen_line_starting_pos = nil
2022-05-22 00:24:07 +01:00
Cursor1.pos = Cursor1.pos + 1
2022-05-18 06:36:10 +01:00
end
-- Don't handle any keys here that would trigger love.textinput above.
function Text . keychord_pressed ( chord )
if chord == ' return ' then
2022-05-22 00:24:07 +01:00
local byte_offset = utf8.offset ( Lines [ Cursor1.line ] . data , Cursor1.pos )
table.insert ( Lines , Cursor1.line + 1 , { mode = ' text ' , data = string.sub ( Lines [ Cursor1.line ] . data , byte_offset ) } )
Lines [ Cursor1.line ] . data = string.sub ( Lines [ Cursor1.line ] . data , 1 , byte_offset - 1 )
Lines [ Cursor1.line ] . fragments = nil
Cursor1.line = Cursor1.line + 1
Cursor1.pos = 1
2022-05-18 06:36:10 +01:00
save_to_disk ( Lines , Filename )
2022-05-19 04:22:57 +01:00
elseif chord == ' tab ' then
Text.insert_at_cursor ( ' \t ' )
save_to_disk ( Lines , Filename )
2022-05-18 06:36:10 +01:00
elseif chord == ' left ' then
2022-05-22 00:24:07 +01:00
assert ( Lines [ Cursor1.line ] . mode == ' text ' )
if Cursor1.pos > 1 then
Cursor1.pos = Cursor1.pos - 1
2022-05-18 06:36:10 +01:00
else
2022-05-22 00:24:07 +01:00
local new_cursor_line = Cursor1.line
2022-05-18 06:36:10 +01:00
while new_cursor_line > 1 do
new_cursor_line = new_cursor_line - 1
if Lines [ new_cursor_line ] . mode == ' text ' then
2022-05-22 00:24:07 +01:00
Cursor1.line = new_cursor_line
Cursor1.pos = utf8.len ( Lines [ Cursor1.line ] . data ) + 1
2022-05-18 06:36:10 +01:00
break
end
end
2022-05-22 00:24:07 +01:00
if Cursor1.line < Screen_top1.line then
Screen_top1.line = Cursor1.line
2022-05-19 01:22:04 +01:00
end
2022-05-18 06:36:10 +01:00
end
elseif chord == ' right ' then
2022-05-22 00:24:07 +01:00
assert ( Lines [ Cursor1.line ] . mode == ' text ' )
if Cursor1.pos <= utf8.len ( Lines [ Cursor1.line ] . data ) then
Cursor1.pos = Cursor1.pos + 1
2022-05-18 06:36:10 +01:00
else
2022-05-22 00:24:07 +01:00
local new_cursor_line = Cursor1.line
2022-05-18 06:36:10 +01:00
while new_cursor_line <= # Lines - 1 do
new_cursor_line = new_cursor_line + 1
if Lines [ new_cursor_line ] . mode == ' text ' then
2022-05-22 00:24:07 +01:00
Cursor1.line = new_cursor_line
Cursor1.pos = 1
2022-05-18 06:36:10 +01:00
break
end
end
2022-05-22 00:24:07 +01:00
if Cursor1.line > Screen_bottom1.line then
Screen_top1.line = Cursor1.line
2022-05-19 01:22:04 +01:00
end
2022-05-18 06:36:10 +01:00
end
elseif chord == ' home ' then
2022-05-22 00:24:07 +01:00
Cursor1.pos = 1
2022-05-18 06:36:10 +01:00
elseif chord == ' end ' then
2022-05-22 00:24:07 +01:00
Cursor1.pos = utf8.len ( Lines [ Cursor1.line ] . data ) + 1
2022-05-18 06:36:10 +01:00
elseif chord == ' backspace ' then
2022-05-22 00:24:07 +01:00
if Cursor1.pos > 1 then
local byte_start = utf8.offset ( Lines [ Cursor1.line ] . data , Cursor1.pos - 1 )
local byte_end = utf8.offset ( Lines [ Cursor1.line ] . data , Cursor1.pos )
2022-05-18 06:36:10 +01:00
if byte_start then
if byte_end then
2022-05-22 00:24:07 +01:00
Lines [ Cursor1.line ] . data = string.sub ( Lines [ Cursor1.line ] . data , 1 , byte_start - 1 ) .. string.sub ( Lines [ Cursor1.line ] . data , byte_end )
2022-05-18 06:36:10 +01:00
else
2022-05-22 00:24:07 +01:00
Lines [ Cursor1.line ] . data = string.sub ( Lines [ Cursor1.line ] . data , 1 , byte_start - 1 )
2022-05-18 06:36:10 +01:00
end
2022-05-22 00:24:07 +01:00
Lines [ Cursor1.line ] . fragments = nil
Cursor1.pos = Cursor1.pos - 1
2022-05-18 06:36:10 +01:00
end
2022-05-22 00:24:07 +01:00
elseif Cursor1.line > 1 then
if Lines [ Cursor1.line - 1 ] . mode == ' drawing ' then
table.remove ( Lines , Cursor1.line - 1 )
2022-05-18 06:36:10 +01:00
else
-- join lines
2022-05-22 00:24:07 +01:00
Cursor1.pos = utf8.len ( Lines [ Cursor1.line - 1 ] . data ) + 1
Lines [ Cursor1.line - 1 ] . data = Lines [ Cursor1.line - 1 ] . data .. Lines [ Cursor1.line ] . data
Lines [ Cursor1.line - 1 ] . fragments = nil
table.remove ( Lines , Cursor1.line )
2022-05-18 06:36:10 +01:00
end
2022-05-22 00:24:07 +01:00
Cursor1.line = Cursor1.line - 1
2022-05-18 06:36:10 +01:00
end
save_to_disk ( Lines , Filename )
elseif chord == ' delete ' then
2022-05-22 00:24:07 +01:00
if Cursor1.pos <= utf8.len ( Lines [ Cursor1.line ] . data ) then
local byte_start = utf8.offset ( Lines [ Cursor1.line ] . data , Cursor1.pos )
local byte_end = utf8.offset ( Lines [ Cursor1.line ] . data , Cursor1.pos + 1 )
2022-05-18 06:36:10 +01:00
if byte_start then
if byte_end then
2022-05-22 00:24:07 +01:00
Lines [ Cursor1.line ] . data = string.sub ( Lines [ Cursor1.line ] . data , 1 , byte_start - 1 ) .. string.sub ( Lines [ Cursor1.line ] . data , byte_end )
2022-05-18 06:36:10 +01:00
else
2022-05-22 00:24:07 +01:00
Lines [ Cursor1.line ] . data = string.sub ( Lines [ Cursor1.line ] . data , 1 , byte_start - 1 )
2022-05-18 06:36:10 +01:00
end
2022-05-22 00:24:07 +01:00
Lines [ Cursor1.line ] . fragments = nil
-- no change to Cursor1.pos
2022-05-18 06:36:10 +01:00
end
2022-05-22 00:24:07 +01:00
elseif Cursor1.line < # Lines then
if Lines [ Cursor1.line + 1 ] . mode == ' drawing ' then
table.remove ( Lines , Cursor1.line + 1 )
2022-05-18 06:36:10 +01:00
else
-- join lines
2022-05-22 00:24:07 +01:00
Lines [ Cursor1.line ] . data = Lines [ Cursor1.line ] . data .. Lines [ Cursor1.line + 1 ] . data
Lines [ Cursor1.line ] . fragments = nil
table.remove ( Lines , Cursor1.line + 1 )
2022-05-18 06:36:10 +01:00
end
end
save_to_disk ( Lines , Filename )
elseif chord == ' up ' then
2022-05-22 00:24:07 +01:00
assert ( Lines [ Cursor1.line ] . mode == ' text ' )
--? print('up', Cursor1.pos, Screen_top1.pos)
2022-05-21 00:41:21 +01:00
local screen_line_index , screen_line_starting_pos = Text.pos_at_start_of_cursor_screen_line ( )
if screen_line_starting_pos == 1 then
2022-05-21 06:46:32 +01:00
--? print('cursor is at first screen line of its line')
2022-05-21 04:32:00 +01:00
-- line is done; skip to previous text line
2022-05-22 00:24:07 +01:00
local new_cursor_line = Cursor1.line
2022-05-20 16:55:45 +01:00
while new_cursor_line > 1 do
new_cursor_line = new_cursor_line - 1
if Lines [ new_cursor_line ] . mode == ' text ' then
2022-05-24 01:39:00 +01:00
--? print('found previous text line')
2022-05-22 00:24:07 +01:00
Cursor1.line = new_cursor_line
2022-05-24 01:39:00 +01:00
Text.populate_screen_line_starting_pos ( Cursor1.line )
2022-05-22 00:24:07 +01:00
if Lines [ Cursor1.line ] . screen_line_starting_pos == nil then
Cursor1.pos = Text.nearest_cursor_pos ( Lines [ Cursor1.line ] . data , Cursor_x )
2022-05-20 23:19:43 +01:00
break
end
2022-05-21 04:32:00 +01:00
-- previous text line found, pick its final screen line
2022-05-24 01:39:00 +01:00
--? print('has multiple screen lines')
2022-05-22 00:24:07 +01:00
local screen_line_starting_pos = Lines [ Cursor1.line ] . screen_line_starting_pos
2022-05-24 01:39:00 +01:00
--? print(#screen_line_starting_pos)
2022-05-20 23:19:43 +01:00
screen_line_starting_pos = screen_line_starting_pos [ # screen_line_starting_pos ]
2022-05-21 06:46:32 +01:00
--? print('previous screen line starts at pos '..tostring(screen_line_starting_pos)..' of its line')
2022-05-24 01:39:00 +01:00
if Screen_top1.line > Cursor1.line then
Screen_top1.line = Cursor1.line
2022-05-22 00:24:07 +01:00
Screen_top1.pos = screen_line_starting_pos
--? print('pos of top of screen is also '..tostring(Screen_top1.pos)..' of the same line')
2022-05-21 00:41:21 +01:00
end
2022-05-22 00:24:07 +01:00
local s = string.sub ( Lines [ Cursor1.line ] . data , screen_line_starting_pos )
Cursor1.pos = screen_line_starting_pos + Text.nearest_cursor_pos ( s , Cursor_x ) - 1
2022-05-20 16:55:45 +01:00
break
end
2022-05-18 06:36:10 +01:00
end
2022-05-22 00:24:07 +01:00
if Cursor1.line < Screen_top1.line then
Screen_top1.line = Cursor1.line
2022-05-20 16:55:45 +01:00
end
else
2022-05-21 04:32:00 +01:00
-- move up one screen line in current line
2022-05-21 06:46:32 +01:00
--? print('cursor is NOT at first screen line of its line')
2022-05-20 16:55:45 +01:00
assert ( screen_line_index > 1 )
2022-05-22 00:24:07 +01:00
new_screen_line_starting_pos = Lines [ Cursor1.line ] . screen_line_starting_pos [ screen_line_index - 1 ]
2022-05-21 06:46:32 +01:00
--? print('switching pos of screen line at cursor from '..tostring(screen_line_starting_pos)..' to '..tostring(new_screen_line_starting_pos))
2022-05-22 00:24:07 +01:00
if Screen_top1.line == Cursor1.line and Screen_top1.pos == screen_line_starting_pos then
Screen_top1.pos = new_screen_line_starting_pos
--? print('also setting pos of top of screen to '..tostring(Screen_top1.pos))
2022-05-21 00:41:21 +01:00
end
2022-05-22 00:24:07 +01:00
local s = string.sub ( Lines [ Cursor1.line ] . data , new_screen_line_starting_pos )
Cursor1.pos = new_screen_line_starting_pos + Text.nearest_cursor_pos ( s , Cursor_x ) - 1
--? print('cursor pos is now '..tostring(Cursor1.pos))
2022-05-19 01:22:04 +01:00
end
2022-05-18 06:36:10 +01:00
elseif chord == ' down ' then
2022-05-22 00:24:07 +01:00
assert ( Lines [ Cursor1.line ] . mode == ' text ' )
2022-05-23 23:38:42 +01:00
--? print('down', Cursor1.line, Cursor1.pos, Screen_top1.line, Screen_top1.pos, Screen_bottom1.line, Screen_bottom1.pos)
2022-05-21 04:33:30 +01:00
if Text.cursor_at_final_screen_line ( ) then
-- line is done, skip to next text line
2022-05-23 23:38:42 +01:00
--? print('cursor at final screen line of its line')
2022-05-22 06:08:13 +01:00
--? os.exit(1)
2022-05-22 00:24:07 +01:00
local new_cursor_line = Cursor1.line
2022-05-21 04:33:30 +01:00
while new_cursor_line < # Lines do
new_cursor_line = new_cursor_line + 1
if Lines [ new_cursor_line ] . mode == ' text ' then
2022-05-22 00:24:07 +01:00
Cursor1.line = new_cursor_line
Cursor1.pos = Text.nearest_cursor_pos ( Lines [ Cursor1.line ] . data , Cursor_x )
2022-05-23 23:38:42 +01:00
--? print(Cursor1.pos)
2022-05-21 04:33:30 +01:00
break
end
2022-05-18 06:36:10 +01:00
end
2022-05-22 00:24:07 +01:00
if Cursor1.line > Screen_bottom1.line then
2022-05-23 23:38:42 +01:00
--? print('screen top before:', Screen_top1.line, Screen_top1.pos)
2022-05-22 00:24:07 +01:00
Screen_top1.line = Cursor1.line
2022-05-23 23:38:42 +01:00
--? print('scroll up preserving cursor')
2022-05-21 04:33:30 +01:00
Text.scroll_up_while_cursor_on_screen ( )
2022-05-23 23:38:42 +01:00
--? print('screen top after:', Screen_top1.line, Screen_top1.pos)
2022-05-21 04:33:30 +01:00
end
else
-- move down one screen line in current line
2022-05-24 23:01:09 +01:00
local scroll_up = false
if Cursor1.line > Screen_bottom1.line or ( Cursor1.line == Screen_bottom1.line and Cursor1.pos >= Screen_bottom1.pos ) then
scroll_up = true
end
2022-05-23 23:38:42 +01:00
--? print('cursor is NOT at final screen line of its line')
2022-05-21 04:33:30 +01:00
local screen_line_index , screen_line_starting_pos = Text.pos_at_start_of_cursor_screen_line ( )
2022-05-22 00:24:07 +01:00
new_screen_line_starting_pos = Lines [ Cursor1.line ] . screen_line_starting_pos [ screen_line_index + 1 ]
2022-05-23 23:38:42 +01:00
--? print('switching pos of screen line at cursor from '..tostring(screen_line_starting_pos)..' to '..tostring(new_screen_line_starting_pos))
2022-05-22 00:24:07 +01:00
local s = string.sub ( Lines [ Cursor1.line ] . data , new_screen_line_starting_pos )
Cursor1.pos = new_screen_line_starting_pos + Text.nearest_cursor_pos ( s , Cursor_x ) - 1
2022-05-23 23:38:42 +01:00
--? print('cursor pos is now', Cursor1.line, Cursor1.pos)
2022-05-24 23:01:09 +01:00
if scroll_up then
Screen_top1.line = Cursor1.line
--? print('scroll up preserving cursor')
Text.scroll_up_while_cursor_on_screen ( )
--? print('screen top after:', Screen_top1.line, Screen_top1.pos)
end
2022-05-19 01:22:04 +01:00
end
2022-05-23 23:38:42 +01:00
--? print('=>', Cursor1.line, Cursor1.pos, Screen_top1.line, Screen_top1.pos, Screen_bottom1.line, Screen_bottom1.pos)
2022-05-18 06:36:10 +01:00
end
end
2022-05-21 00:41:21 +01:00
function Text . pos_at_start_of_cursor_screen_line ( )
2022-05-22 00:24:07 +01:00
if Lines [ Cursor1.line ] . screen_line_starting_pos == nil then
2022-05-21 00:41:21 +01:00
return 1 , 1
end
2022-05-22 00:24:07 +01:00
for i =# Lines [ Cursor1.line ] . screen_line_starting_pos , 1 , - 1 do
local spos = Lines [ Cursor1.line ] . screen_line_starting_pos [ i ]
if spos <= Cursor1.pos then
2022-05-21 00:41:21 +01:00
return i , spos
end
end
assert ( false )
end
2022-05-21 04:33:30 +01:00
function Text . cursor_at_final_screen_line ( )
2022-05-22 00:24:07 +01:00
if Lines [ Cursor1.line ] . screen_line_starting_pos == nil then
2022-05-21 04:33:30 +01:00
return true
end
2022-05-25 22:56:21 +01:00
local screen_lines = Lines [ Cursor1.line ] . screen_line_starting_pos
2022-05-23 23:38:42 +01:00
--? print(screen_lines[#screen_lines], Cursor1.pos)
2022-05-22 05:08:21 +01:00
return screen_lines [ # screen_lines ] <= Cursor1.pos
2022-05-21 04:33:30 +01:00
end
2022-05-19 02:19:27 +01:00
function Text . move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary ( )
2022-05-23 16:52:13 +01:00
local y = 15 -- top margin
2022-05-22 00:24:07 +01:00
while Cursor1.line <= # Lines do
if Lines [ Cursor1.line ] . mode == ' text ' then
2022-05-19 02:19:27 +01:00
break
end
2022-05-23 23:38:42 +01:00
--? print('cursor skips', Cursor1.line)
2022-05-23 16:52:13 +01:00
y = y + 20 + Drawing.pixels ( Lines [ Cursor1.line ] . h )
2022-05-22 00:24:07 +01:00
Cursor1.line = Cursor1.line + 1
2022-05-19 02:19:27 +01:00
end
-- hack: insert a text line at bottom of file if necessary
2022-05-22 00:24:07 +01:00
if Cursor1.line > # Lines then
assert ( Cursor1.line == # Lines + 1 )
2022-05-19 02:19:27 +01:00
table.insert ( Lines , { mode = ' text ' , data = ' ' } )
end
2022-05-23 16:52:13 +01:00
--? print(y, App.screen.height, App.screen.height-math.floor(15*Zoom))
if y > App.screen . height - math.floor ( 15 * Zoom ) then
--? if Cursor1.line > Screen_bottom1.line then
2022-05-23 23:38:42 +01:00
--? print('scroll up')
2022-05-22 00:24:07 +01:00
Screen_top1.line = Cursor1.line
2022-05-19 02:19:27 +01:00
Text.scroll_up_while_cursor_on_screen ( )
end
end
function Text . scroll_up_while_cursor_on_screen ( )
2022-05-22 07:02:04 +01:00
local top2 = Text.to2 ( Cursor1 )
--? print('cursor pos '..tostring(Cursor1.pos)..' is on the #'..tostring(top2.screen_line)..' screen line down')
2022-05-23 16:13:58 +01:00
local y = App.screen . height - math.floor ( 15 * Zoom )
2022-05-21 06:43:09 +01:00
-- duplicate some logic from love.draw
2022-05-19 02:19:27 +01:00
while true do
2022-05-22 07:02:04 +01:00
--? print(y, 'top2:', top2.line, top2.screen_line, top2.screen_pos)
if top2.line == 1 and top2.screen_line == 1 then break end
if top2.screen_line > 1 or Lines [ top2.line - 1 ] . mode == ' text ' then
local h = math.floor ( 15 * Zoom )
if y - h < 15 then -- top margin = 15
break
end
y = y - h
2022-05-21 06:43:09 +01:00
else
2022-05-22 07:02:04 +01:00
assert ( top2.line > 1 )
assert ( Lines [ top2.line - 1 ] . mode == ' drawing ' )
-- We currently can't draw partial drawings, so either skip it entirely
-- or not at all.
2022-05-22 17:15:56 +01:00
local h = 20 + Drawing.pixels ( Lines [ top2.line - 1 ] . h )
2022-05-22 07:02:04 +01:00
if y - h < 15 then
break
end
--? print('skipping drawing of height', h)
y = y - h
2022-05-19 02:19:27 +01:00
end
2022-05-22 07:02:04 +01:00
top2 = Text.previous_screen_line ( top2 )
2022-05-19 02:19:27 +01:00
end
2022-05-22 07:02:04 +01:00
--? print('top2 finally:', top2.line, top2.screen_line, top2.screen_pos)
Screen_top1 = Text.to1 ( top2 )
--? print('top1 finally:', Screen_top1.line, Screen_top1.pos)
2022-05-19 02:19:27 +01:00
end
2022-05-18 06:53:09 +01:00
function Text . in_line ( line , x , y )
2022-05-19 01:22:04 +01:00
if line.y == nil then return false end -- outside current page
2022-05-25 21:38:33 +01:00
if x < 25 then return false end
2022-05-20 06:07:12 +01:00
if y < line.y then return false end
if line.screen_line_starting_pos == nil then return y < line.y + math.floor ( 15 * Zoom ) end
return y < line.y + # line.screen_line_starting_pos * math.floor ( 15 * Zoom )
2022-05-18 06:53:09 +01:00
end
2022-05-20 06:07:12 +01:00
function Text . move_cursor ( line_index , line , mx , my )
2022-05-22 00:24:07 +01:00
Cursor1.line = line_index
2022-05-20 06:07:12 +01:00
if line.screen_line_starting_pos == nil then
2022-05-22 00:24:07 +01:00
Cursor1.pos = Text.nearest_cursor_pos ( line.data , mx )
2022-05-20 06:07:12 +01:00
return
end
assert ( line.fragments )
assert ( my >= line.y )
-- duplicate some logic from Text.draw
local y = line.y
2022-05-20 06:26:15 +01:00
for screen_line_index , screen_line_starting_pos in ipairs ( line.screen_line_starting_pos ) do
2022-05-20 06:07:12 +01:00
local nexty = y + math.floor ( 15 * Zoom )
if my < nexty then
2022-05-20 06:26:15 +01:00
-- On all wrapped screen lines but the final one, clicks past end of
-- line position cursor on final character of screen line.
-- (The final screen line positions past end of screen line as always.)
if mx > Line_width and screen_line_index < # line.screen_line_starting_pos then
2022-05-22 00:24:07 +01:00
Cursor1.pos = line.screen_line_starting_pos [ screen_line_index + 1 ]
2022-05-20 06:26:15 +01:00
return
end
2022-05-20 06:07:12 +01:00
local s = string.sub ( line.data , screen_line_starting_pos )
2022-05-22 00:24:07 +01:00
Cursor1.pos = screen_line_starting_pos + Text.nearest_cursor_pos ( s , mx ) - 1
2022-05-20 06:07:12 +01:00
return
end
y = nexty
end
assert ( false )
2022-05-18 06:53:09 +01:00
end
2022-05-20 06:07:12 +01:00
-- manual test:
-- line: abc
-- def
-- gh
-- fragments: abc, def, gh
-- click inside e
-- line_starting_pos = 1 + 3 = 4
-- nearest_cursor_pos('defgh', mx) = 2
2022-05-22 00:24:07 +01:00
-- Cursor1.pos = 4 + 2 - 1 = 5
2022-05-20 06:07:12 +01:00
-- manual test:
-- click inside h
-- line_starting_pos = 1 + 3 + 3 = 7
-- nearest_cursor_pos('gh', mx) = 2
2022-05-22 00:24:07 +01:00
-- Cursor1.pos = 7 + 2 - 1 = 8
2022-05-18 06:53:09 +01:00
2022-05-23 23:22:16 +01:00
function Text . nearest_cursor_pos ( line , x ) -- x includes left margin
2022-05-18 04:29:08 +01:00
if x == 0 then
return 1
end
2022-05-20 01:46:02 +01:00
local len = utf8.len ( line )
local max_x = Text.cursor_x ( line , len + 1 )
2022-05-18 04:29:08 +01:00
if x > max_x then
2022-05-20 01:46:02 +01:00
return len + 1
2022-05-18 04:29:08 +01:00
end
2022-05-20 01:46:02 +01:00
local left , right = 1 , len + 1
2022-05-24 22:34:50 +01:00
--? print('-- nearest', x)
2022-05-20 05:52:38 +01:00
while true do
2022-05-24 22:34:50 +01:00
--? print('nearest', x, '^'..line..'$', left, right)
if left == right then
return left
end
2022-05-18 04:29:08 +01:00
local curr = math.floor ( ( left + right ) / 2 )
local currxmin = Text.cursor_x ( line , curr )
local currxmax = Text.cursor_x ( line , curr + 1 )
2022-05-24 22:34:50 +01:00
--? print('nearest', x, left, right, curr, currxmin, currxmax)
2022-05-18 04:29:08 +01:00
if currxmin <= x and x < currxmax then
return curr
end
2022-05-20 05:52:38 +01:00
if left >= right - 1 then
return right
end
2022-05-18 04:29:08 +01:00
if currxmin > x then
right = curr
else
left = curr
end
end
2022-05-20 05:52:38 +01:00
assert ( false )
2022-05-18 04:29:08 +01:00
end
2022-05-23 23:22:16 +01:00
function Text . nearest_pos_less_than ( line , x ) -- x DOES NOT include left margin
if x == 0 then
return 1
end
local len = utf8.len ( line )
local max_x = Text.cursor_x2 ( line , len + 1 )
if x > max_x then
return len + 1
end
local left , right = 1 , len + 1
--? print('--')
while true do
local curr = math.floor ( ( left + right ) / 2 )
local currxmin = Text.cursor_x2 ( line , curr + 1 )
local currxmax = Text.cursor_x2 ( line , curr + 2 )
--? print(x, left, right, curr, currxmin, currxmax)
if currxmin <= x and x < currxmax then
return curr
end
if left >= right - 1 then
return left
end
if currxmin > x then
right = curr
else
left = curr
end
end
assert ( false )
end
2022-05-18 04:29:08 +01:00
function Text . cursor_x ( line_data , cursor_pos )
2022-05-24 22:34:50 +01:00
--? print(cursor_pos, #line_data, line_data)
local cursor_offset = utf8.offset ( line_data , cursor_pos )
--? print(cursor_offset)
assert ( cursor_offset )
local line_before_cursor = line_data : sub ( 1 , cursor_offset - 1 )
2022-05-23 07:17:06 +01:00
local text_before_cursor = App.newText ( love.graphics . getFont ( ) , line_before_cursor )
return 25 + math.floor ( App.width ( text_before_cursor ) * Zoom )
2022-05-18 04:29:08 +01:00
end
2022-05-20 01:29:14 +01:00
function Text . cursor_x2 ( s , cursor_pos )
2022-05-24 22:34:50 +01:00
local cursor_offset = utf8.offset ( s , cursor_pos )
assert ( cursor_offset )
local s_before_cursor = s : sub ( 1 , cursor_offset - 1 )
2022-05-23 07:17:06 +01:00
local text_before_cursor = App.newText ( love.graphics . getFont ( ) , s_before_cursor )
return math.floor ( App.width ( text_before_cursor ) * Zoom )
2022-05-20 01:29:14 +01:00
end
2022-05-22 00:24:07 +01:00
function Text . to2 ( pos1 )
2022-05-22 07:02:04 +01:00
assert ( Lines [ pos1.line ] . mode == ' text ' )
2022-05-22 00:24:07 +01:00
local result = { line = pos1.line , screen_line = 1 }
2022-05-22 06:08:13 +01:00
if Lines [ pos1.line ] . screen_line_starting_pos == nil then
2022-05-22 00:24:07 +01:00
result.screen_pos = pos1.pos
else
for i =# Lines [ pos1.line ] . screen_line_starting_pos , 1 , - 1 do
local spos = Lines [ pos1.line ] . screen_line_starting_pos [ i ]
2022-05-22 07:02:04 +01:00
if spos <= pos1.pos then
2022-05-22 00:24:07 +01:00
result.screen_line = i
2022-05-22 07:02:04 +01:00
result.screen_pos = pos1.pos - spos + 1
2022-05-22 00:24:07 +01:00
break
end
end
end
assert ( result.screen_pos )
return result
end
function Text . to1 ( pos2 )
local result = { line = pos2.line , pos = pos2.screen_pos }
if pos2.screen_line > 1 then
2022-05-22 07:02:04 +01:00
result.pos = Lines [ pos2.line ] . screen_line_starting_pos [ pos2.screen_line ] + pos2.screen_pos - 1
2022-05-22 00:24:07 +01:00
end
return result
end
2022-05-22 07:02:04 +01:00
function Text . previous_screen_line ( pos2 )
if pos2.screen_line > 1 then
return { line = pos2.line , screen_line = pos2.screen_line - 1 , screen_pos = 1 }
elseif pos2.line == 1 then
return pos2
elseif Lines [ pos2.line - 1 ] . mode == ' drawing ' then
return { line = pos2.line - 1 , screen_line = 1 , screen_pos = 1 }
else
local l = Lines [ pos2.line - 1 ]
if l.screen_line_starting_pos == nil then
return { line = pos2.line - 1 , screen_line = 1 , screen_pos = 1 }
else
return { line = pos2.line - 1 , screen_line =# Lines [ pos2.line - 1 ] . screen_line_starting_pos , screen_pos = 1 }
end
end
end
2022-05-24 01:39:00 +01:00
function Text . populate_screen_line_starting_pos ( line_index )
2022-05-25 21:35:32 +01:00
--? print('Text.populate_screen_line_starting_pos')
2022-05-24 01:39:00 +01:00
local line = Lines [ line_index ]
2022-05-25 22:56:21 +01:00
if line.screen_line_starting_pos then
return
end
-- duplicate some logic from Text.draw
2022-05-24 01:39:00 +01:00
if line.fragments == nil then
Text.compute_fragments ( line , Line_width )
end
local x = 25
local pos = 1
for _ , f in ipairs ( line.fragments ) do
local frag , frag_text = f.data , f.text
--? print(x, frag)
-- render fragment
local frag_width = math.floor ( App.width ( frag_text ) * Zoom )
if x + frag_width > Line_width then
x = 25
if line.screen_line_starting_pos == nil then
line.screen_line_starting_pos = { 1 , pos }
else
2022-05-25 22:56:21 +01:00
--? print(' ', #line.screen_line_starting_pos, line.data)
2022-05-24 01:39:00 +01:00
table.insert ( line.screen_line_starting_pos , pos )
end
end
x = x + frag_width
local frag_len = utf8.len ( frag )
pos = pos + frag_len
end
end
2022-05-18 04:29:08 +01:00
return Text