I'm not sure this is very useful. I had an initial idea to stop using screen_bottom1 in final_text_loc_on_screen, by starting from screen_top1 rather than screen_bottom1. But that changes the direction in which we scan for the text line in situations where there is somehow no text on screen (something that should never happen but I have zero confidence in that).
Still, it doesn't seem like a bad thing to drastically reduce the lifetime of some derived state.
Really what I need to do is throw this whole UX out and allow the cursor to be on a drawing as a whole. So up arrow or left arrow below a drawing would focus the whole drawing in a red border, and another up arrow and left arrow would skip the drawing and continue upward. I think that change to the UX will eliminate a whole class of special cases in the code.
ZS5IYZH5EXXPSVIFWS7XW5POEVRRCK6XV6PB36D3EJXJRT22LKOQC
2EKE4XLLUF44XPHJOJ53SAUP3TUDM572HWXMHJ5UVIYIQICRNEVAC
6NYMNNADRZEZWL3ISZ2I7N7DFLZPFRGGLWPO25TDA7QLVX52HWQQC
3TI67SEJNOSADDEHRTI5FSRD7WVNRTXQ5LC77LBSWLXXZK3MCONQC
TXI6GSQDOUHU4DWQTCMLVVCA2YSIAUZJWSFMH22QPO3W4NGNRF5AC
4GYPLUDYEF4NPB3HUGSOXAMCZ3UEV5ODM35IRA4DYL5IPDVDHD3QC
VDFARWQXPIQUTVRFLZ6QOYSG2ON7CLQVKRHJJZZAEMO7QLTNXXHQC
CUFW4EJL75OAA5BS5EXGTM5RMRNJOBBPAXUJADGZ3VLP2ZMKFOTAC
2MGBV7NPY2RVK7EZ7OJRWBYDUMADFZAFSBKLQFX74ERBVZODJLUQC
PXSQR2ADSFEOG4SMIRHUZ2HGBKUQIYA73HZU4IRPR2UG2RBAVUJAC
G3DLS5OUO77V4MC6754KTETRCTVUBYBHMGR7MTV52IYYM7QA3ROQC
FHNPQBLKB5EEJA4XDF2QJBCKB4W56LYWHJLVYA7ZPUSKM27QWWNQC
QAMVLUK22RP5RBDTDV5XVPQCSJUWDWESV4TRCUTNUM46E26BH2AQC
JYB3RFWHD6IXYGHJ77BEILJ5QNRPPH77DITIH4PKZM4D4OZSHRVQC
LF7BWEG4DKQI7NMXMZC4LC2BE5PB42HK5PD6OYBNIDMAZBJASOKQC
LXTTOB33N2HCUZFIUDRQGGBVHK2HODRG4NBLH6RXRQZDCHF27BSAC
5BMR5HRT7GN5L4XB4ISP4JJP3ONZESHEEQBCTQE4EVEDL7MBSDGAC
EKKFWP4D2MNOHU265UCJU37KIFQV424CRLVASQMHDYUYY5T67D3QC
656FM555BRGLHJ7PTIZXD2IY5T7PGFEYSHG2T3Z7WNX6QZ5KROSAC
6RYLD5ONDIQFWU5CNL4NGHJQ2LNAZZFGTPXQJDNJGLNYAUOTUI7QC
YFW4MNNPY452RIUIGW6NO7WAUWPBXXOKOJJBNZGU727PUBZFATSAC
6XCJX4DZB6UEAXEXXUGVVPBCF5SNDYOJGU3Q6BPB4ZMI5DZH4MYQC
7RKFA3VAGK73SLC3NCOKHVMOWLTC4EUQYS47P3LVVKDVLBVGBEHAC
ORRSP7FVCHI2TF5GXBRGQYYJAA3JFYXZBM3T663BKSBV22FCZVCAC
CVSRHMJ2BM4LPVG67ULIVQMP2NW3YY2JC2ZQBEA6EB5KVM4O2L5AC
CNCYMM6ABOXCRI2IP5A4T2OGBO5FQ7GWBXBP2OQYL4YET5BLJCGQC
UHB4GARJI5AB5UCDCZRFSCJNXGJSLU5DYGUGX5ITYEXI7Q43Z4CAC
ILOA5BYFTQKBSHLFMMZUVPQ2JXBFJD62ERQFBTDK2WSRXUN525VQC
LNUHQOGHIOFGJXNGA3DZLYEASLYYDGLN2I3EDZY5ANASQAHCG3YQC
APYPFFS3G6TDEUMIHQGMDBJNRNDTCNTPKI5M2AFACJ73P725XQRQC
Y4SPXCM3PKARGUU22FNBEDRU7S6CJSNYVAA76JYH4I4EMMMKP6LQC
SWZAQHGRX3T5MZVM7YZ4EACZX5ON2NUZG5YXLWVEXLL3WZUZYAFQC
LK4ZW4BBDD5LC4JK4XK5DJESSDFAIRVFPDM324S7SCAUXEXYVTLQC
FZBXBUFFNRE5ZJO5DLRU375HOXT2B7FO35XD7BTHHUXSARVWDFLQC
LLAOOMULEBXFMIGRBY6LRVEK4RXQGPNTFVWMCZNUEJZHWC7UGUEAC
MSOQI3A5BC5PY2MZXZQAQ4EQDT4KICQJPN3YUZVDYTWXSPZWBLIAC
AMOPICKVRHMQERJLFPMAAEBV7TL5QACGGSBJWRCMV5R5O3KDVETAC
BW2IUB3KA4AKD35DYLCUCUM4Z32FMKGZNUBQBAEDIQJJYPA547MAC
BULPIBEGL7TMK6CVIE7IS7WGAHGOSUJBGJSFQK542MOWGHP2ADQQC
H2DPLWMVRFYTO2CQTG54FMT2LF3B6UKLXH32CUA22DNQJVP5XBNQC
I64IPGJXWRTGHHVAYJUBUIWFR4BY6NM5P7TLTV4JOD7K4BVYDECQC
HALS7E5UGKCP3DFY456F7Z3Y6WNGIABOCV2SHT34D5ZAGNCPV5PQC
XNFTJHC4QSHNSIWNN7K6QZEZ37GTQYKHS4EPNSVPQCUSWREROGIQC
MTJEVRJR5GLWUSK7HMIM4UXM6GS6O6YCRWJT3DUSU2RYMHCQNOEQC
3MAZEQK5AR3IJJ2ENHHYDPDICIK645NE5QWR54Z52BHGHE6VR5XQC
Z5HLXU4PJWWJJDBCK52NBD6PIRIA3TAN2BKZB5HBYFGIDBX4F5HAC
SPSW74Y5OJ54Y7VQ3SJFCJR5CYDKTR4A3TOEVZODDZLUSDDU2GZAC
QCPXQ2E3USF3Z6R6WJ2JKHTRMPKA6QWXFKKRMLXA3MXABJEL543AC
IRCKL6VNSFB7TQEKPQUPJCN37N5QW7D54DSZMESVXGK7NEHGSIPAC
MXA3RZYKUI4UF2ISY7JEF6VKX6NOPZMZH5SLLCZHRJKFIXXXDPSAC
KZ5GAYRPWF2BA5VEIW3A4G2TULATBL7YEDGFJU42GBP5DET7BI3AC
ZHLO7K3MQNI6OMK6226SSO2Z6Z4ZXF4T73VOG36DVAG6CHR6OHWAC
KOYAJWE4NJ2J4X3SHEAVMRXYZPZGOMTI7OX3PTUQIDIZ2GQI6UKAC
5OALPNN3FGDKFM4K5EQZV6FU6GCKHEVSJDXM6XFFC7LGXES7GLWQC
YPHKZVWM2FS7U3VNVDXFRJTBF4RLQ6K7ZWISLHOQJPYSKBELHFEAC
TGZAJUEFRK3NTCDMPIIG7U2TGLDHK4U3JDNFAYX7NHXTJYBYEZIAC
ZPUQSPQPQFVRUIHGLAWW3IDBYODIWDHO62HAC3WWF5TM3CIJGHNQC
OI4FPFINEROK6GNDEMOBTGSPYIULCLRGGT5W3H7VLM7VFH22GMWQC
2LOQ5ALJYHWSMU7ROSKD66BYGMK3O6HYNUQMGCZVKTRDOLEI75NQC
GCEF4N3VW2JFTWVXU2ND5XA63BNTMEGRBQQXYA3HULAKGYOYJP7AC
OGUV4HSA7XGSQLUVWBAE3AE263Z7Z6G3BZOB4CN2AOYD2DEJMOZAC
SVJZZDC3K6AKAXHGRNAZKRE2ZXEKJANNLG7LSSUZJARFBL5F7C4AC
HTWAM4NZFOY463TNSKYIM2EWB7QNBGDRRTTGHF5N3Z4TGC7Q3SFAC
KOTI3MFGQ4PDS4I75JIJG734LTET6745VGTSMNFYYASVIO6H2KPAC
MP2TBKU6CNDMZKENYMBV62F5KQ27ZWEVPVRFS2RESVDQQT2IRR4AC
EAEGCJV5JOW46KCZKKPBFKZ4Z3SDB3X4R7TLNXFWCIQN5UCNSXFQC
HOSPP2ANSW654DYRTC6CQUQA2GUKV6T2FI7QBKXD2DZS3R32IMGAC
WOXIYUTL4NU7ACHQYXEXJDSXCRDLQ2X457KO6C7GEXFQZ43F3L7QC
ELJNEPW26FUIIFY6D24274J7KZICRLE3TJHCFNRVLR5NZBNNV37AC
YJJ4X4JGABMVA5JBQW5UAWI543P3Y7NDVFTOHA6LIDA5KSFGUFNQC
WLJCIXYMSTCNSYCFOEBQNDLBZ5D2Z3WTF4E4WYL5CFGIJ434FKNQC
K4OBZSHEBIZBAKPH3F7ASDGCPLB7D5W5QLFJQYSM5XOYDPB4BUHAC
QLTJG7Q33ABBTDJ55K3OPLNSYBFBIVRS3UABXEY73RHYMOOJ542QC
FKNXK2OAH4U2V2TXCHWE4C3Z5DROBIIPSXUWFKP7Q3DSNOKFFL5AC
GNQC72UXBU6KYXW6MXLNRGTLXV2VPQXMVCLYMJT6POTFXSF5ASJAC
NHNP76LGNIVNIDMSDILAKEVSWFQ4LKNCYXVQEGKKJ75TSRPEBVEQC
KKMFQDR43ZWVCDRHQLWWX3FCWCFA3ZSXYOBRJNPHUQZR2XPKWULAC
KMSL74GAMFNTAKGDKZFP2AMQXUMOC3XH373BO4IABZWBEP3YAXKAC
5SM6DRHKPLFWQPCZDTVM4ENVENWBYBQ3Q2KYKSWLWYUTOSEPCRLAC
GZ5WULJVEZJJQPQPSQZE7CEPIYPJ2BJDYUJBMZRA5HLOO7TE3DOQC
G54H3YG2NEZPW2F6OYT5JPV7KSKVMNW5D3QT3FBCXTJHAQYTV5UAC
GFXWHTE6POBIOBUMRAWD5QS22JEO52EF4VTLMB4CDK4RLSCK7HCAC
OP643FFG5WQWHLPLYZ2VTDJYXK6VQ3NODRDPJNVDN26CF3ESM5RAC
5ZA3BRNYWKSGEBJ4JLA4UBC3LJPT5JBWYCU7PQYRSGX6MJMEWDIQC
2L5MEZV344TOZLVY3432RHJFIRVXFD6O3GWLL5O4CV66BGAFTURQC
WJBZZQE4A4KLYGS2KA254I6VN2DVXDY4XKCNAE76GTMLLQGYCUOQC
3QNOKBFMKBGXBVJIRHR2444JRRMBTABHE4674NR3DT67RRM2X6GAC
4EGQRXDANFLUYXADP3MNHZWP2LBH2P5VBVKNN5RT6ERGMBVSRI2AC
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')
--? 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)
--? 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)
--? 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)
--? 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
function Text.previous_screen_top1(State)
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)
--? 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
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)
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)
--? 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)
--? 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)
--? 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)
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('=>', 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)
--? 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('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)
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}
--? 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)
-- 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)
--? 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)
--? 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
function Text.previous_screen_top1(State)
--? 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
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)
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)
--? 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)
--? 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)
--? 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)
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('=>', 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)
--? 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('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)
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}
-- 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)