B:BD[
10.22445] → [
11.1951:1996]
B:BD[
13.9658] → [
14.6003:6003]
B:BD[
13.11328] → [
14.6653:6653]
B:BD[
12.9616] → [
13.11329:11507]
B:BD[
15.1870] → [
14.6654:6847]
B:BD[
16.5593] → [
17.170:199]
B:BD[
18.6985] → [
19.134:241]
B:BD[
13.11602] → [
20.75:75]
∅:D[
21.756] → [
22.364:368]
B:BD[
23.258] → [
22.364:368]
B:BD[
24.7283] → [
21.756:756]
∅:D[
25.258] → [
24.7264:7283]
B:BD[
24.7264] → [
24.7264:7283]
B:BD[
24.7264] → [
25.234:258]
∅:D[
26.567] → [
24.7237:7264]
∅:D[
16.6186] → [
24.7237:7264]
B:BD[
24.7237] → [
24.7237:7264]
B:BD[
26.519] → [
16.6139:6186]
B:BD[
24.7099] → [
26.411:519]
∅:D[
25.233] → [
24.7075:7099]
B:BD[
24.7075] → [
24.7075:7099]
B:BD[
24.7075] → [
25.209:233]
∅:D[
26.410] → [
24.7048:7075]
∅:D[
27.880] → [
24.7048:7075]
B:BD[
24.7048] → [
24.7048:7075]
B:BD[
26.363] → [
27.835:880]
B:BD[
24.6915] → [
26.259:363]
∅:D[
25.208] → [
24.6891:6915]
B:BD[
24.6891] → [
24.6891:6915]
B:BD[
24.6891] → [
25.184:208]
∅:D[
26.258] → [
24.6864:6891]
∅:D[
16.6138] → [
24.6864:6891]
B:BD[
24.6864] → [
24.6864:6891]
B:BD[
26.210] → [
16.6091:6138]
∅:D[
27.834] → [
26.166:210]
B:BD[
26.166] → [
26.166:210]
B:BD[
26.119] → [
27.789:834]
B:BD[
24.6651] → [
26.15:119]
∅:D[
25.183] → [
24.6627:6651]
B:BD[
24.6627] → [
24.6627:6651]
B:BD[
24.6627] → [
25.159:183]
∅:D[
16.6090] → [
24.6600:6627]
B:BD[
24.6600] → [
24.6600:6627]
B:BD[
24.6528] → [
16.6021:6090]
∅:D[
16.6020] → [
24.6481:6528]
B:BD[
24.6481] → [
24.6481:6528]
B:BD[
24.6451] → [
16.5989:6020]
B:BD[
12.16047] → [
24.6450:6451]
∅:D[
24.6449] → [
12.16043:16047]
∅:D[
18.9642] → [
12.16043:16047]
B:BD[
12.16043] → [
12.16043:16047]
∅:D[
25.158] → [
24.6430:6449]
B:BD[
24.6430] → [
24.6430:6449]
B:BD[
24.6430] → [
25.134:158]
∅:D[
16.5988] → [
24.6403:6430]
B:BD[
24.6403] → [
24.6403:6430]
B:BD[
24.6268] → [
16.5854:5988]
∅:D[
16.5853] → [
24.6228:6268]
B:BD[
24.6228] → [
24.6228:6268]
B:BD[
24.6184] → [
16.5808:5853]
∅:D[
28.5729] → [
24.6183:6184]
B:BD[
24.6183] → [
24.6183:6184]
∅:D[
29.1119] → [
28.5645:5729]
B:BD[
28.5645] → [
28.5645:5729]
B:BD[
28.5600] → [
29.1075:1119]
∅:D[
29.1074] → [
28.5422:5600]
B:BD[
28.5422] → [
28.5422:5600]
B:BD[
28.5422] → [
29.1052:1074]
∅:D[
29.1051] → [
28.5400:5422]
B:BD[
28.5400] → [
28.5400:5422]
B:BD[
28.5400] → [
29.915:1051]
∅:D[
30.447] → [
28.5371:5400]
B:BD[
24.6146] → [
28.5371:5400]
∅:D[
31.329] → [
30.442:447]
B:BD[
30.442] → [
30.442:447]
B:BD[
30.337] → [
30.337:387]
B:BD[
24.6146] → [
30.175:264]
∅:D[
16.5807] → [
24.6141:6146]
B:BD[
18.9642] → [
24.6141:6146]
B:BD[
12.16001] → [
16.5759:5807]
∅:D[
16.5758] → [
12.15990:16001]
∅:D[
32.7139] → [
12.15990:16001]
B:BD[
12.15990] → [
12.15990:16001]
B:BD[
33.1400] → [
16.5720:5758]
B:BD[
12.15878] → [
33.1293:1400]
∅:D[
20.75] → [
12.15871:15878]
∅:D[
34.631] → [
12.15871:15878]
∅:D[
35.8798] → [
12.15871:15878]
∅:D[
13.11602] → [
12.15871:15878]
B:BD[
12.15871] → [
12.15871:15878]
B:BD[
36.2812] → [
13.11580:11602]
B:BD[
12.14219] → [
12.14219:14227]
B:BD[
12.13602] → [
37.379:406]
∅:D[
38.3865] → [
12.13351:13365]
B:BD[
12.13351] → [
12.13351:13365]
∅:D[
33.1050] → [
38.3829:3865]
B:BD[
18.8507] → [
38.3829:3865]
∅:D[
39.657] → [
12.13212:13241]
B:BD[
12.13212] → [
12.13212:13241]
B:BD[
12.13212] → [
39.501:657]
B:BD[
12.13197] → [
12.13197:13212]
∅:D[
30.174] → [
40.6843:6870]
B:BD[
12.12214] → [
40.6843:6870]
B:BD[
12.12181] → [
30.133:174]
∅:D[
41.289] → [
12.12152:12181]
∅:D[
40.6842] → [
12.12152:12181]
B:BD[
12.12152] → [
12.12152:12181]
B:BD[
12.12086] → [
41.158:289]
∅:D[
40.6814] → [
12.12057:12086]
B:BD[
12.12057] → [
12.12057:12086]
∅:D[
30.73] → [
40.6787:6814]
B:BD[
18.7806] → [
40.6787:6814]
B:BD[
12.11991] → [
30.15:73]
∅:D[
42.25] → [
12.11962:11991]
∅:D[
18.7755] → [
12.11962:11991]
∅:D[
43.103883] → [
12.11962:11991]
B:BD[
12.11962] → [
12.11962:11991]
B:BD[
18.7755] → [
42.15:25]
B:BD[
18.7565] → [
44.25240:25412]
B:BD[
12.11787] → [
18.7538:7565]
∅:D[
45.2183] → [
12.11739:11787]
B:BD[
12.11739] → [
12.11739:11787]
B:BD[
12.11710] → [
45.2149:2183]
∅:D[
45.2148] → [
12.11680:11710]
B:BD[
12.11680] → [
12.11680:11710]
B:BD[
18.7537] → [
45.2118:2148]
B:BD[
12.11621] → [
18.7491:7537]
∅:D[
18.7490] → [
12.11589:11621]
∅:D[
43.103555] → [
12.11589:11621]
B:BD[
12.11589] → [
12.11589:11621]
B:BD[
12.11407] → [
18.7278:7460]
∅:D[
18.7277] → [
12.11370:11407]
∅:D[
43.103307] → [
12.11370:11407]
B:BD[
12.11370] → [
12.11370:11407]
B:BD[
18.7245] → [
18.7245:7277]
B:BD[
12.11296] → [
18.7185:7215]
∅:D[
40.6786] → [
12.11262:11296]
B:BD[
12.11262] → [
12.11262:11296]
B:BD[
18.7184] → [
40.6689:6786]
B:BD[
18.7046] → [
18.7046:7184]
B:BD[
12.11014] → [
18.6986:7016]
∅:D[
19.241] → [
12.10984:11014]
∅:D[
18.6985] → [
12.10984:11014]
∅:D[
43.102945] → [
12.10984:11014]
B:BD[
12.10984] → [
12.10984:11014]
B:BD[
12.10962] → [
18.6957:6985]
∅:D[
38.3828] → [
12.10956:10962]
∅:D[
18.6956] → [
12.10956:10962]
∅:D[
10.22974] → [
12.10956:10962]
∅:D[
43.102909] → [
12.10956:10962]
B:BD[
12.10956] → [
12.10956:10962]
∅:D[
46.325] → [
10.22916:22974]
∅:D[
47.363] → [
10.22916:22974]
∅:D[
48.401] → [
10.22916:22974]
∅:D[
39.500] → [
10.22916:22974]
B:BD[
12.10886] → [
10.22916:22974]
B:BD[
12.10752] → [
48.214:401]
∅:D[
18.6873] → [
12.10522:10752]
∅:D[
43.102812] → [
12.10522:10752]
B:BD[
12.10522] → [
12.10522:10752]
∅:D[
16.5675] → [
18.6800:6831]
B:BD[
18.6800] → [
18.6800:6831]
B:BD[
12.10343] → [
16.5627:5675]
∅:D[
34.469] → [
12.10338:10343]
B:BD[
12.10338] → [
12.10338:10343]
∅:D[
49.162] → [
12.10314:10320]
∅:D[
50.273] → [
12.10314:10320]
∅:D[
51.806] → [
12.10314:10320]
∅:D[
35.8796] → [
12.10314:10320]
B:BD[
12.10314] → [
12.10314:10320]
B:BD[
18.6495] → [
45.2089:2117]
∅:D[
17.199] → [
18.6394:6467]
∅:D[
33.743] → [
18.6394:6467]
∅:D[
16.5593] → [
18.6394:6467]
B:BD[
18.6394] → [
18.6394:6467]
B:BD[
12.9696] → [
16.5558:5593]
∅:D[
13.11579] → [
12.9691:9696]
B:BD[
12.9691] → [
12.9691:9696]
∅:D[
15.2225] → [
12.9685:9691]
∅:D[
18.6276] → [
12.9685:9691]
∅:D[
43.102222] → [
12.9685:9691]
B:BD[
12.9685] → [
12.9685:9691]
∅:D[
52.212] → [
15.2163:2225]
B:BD[
15.2163] → [
15.2163:2225]
B:BD[
15.2163] → [
52.167:212]
∅:D[
53.182] → [
15.2080:2163]
B:BD[
15.2080] → [
15.2080:2163]
B:BD[
15.2073] → [
53.161:182]
∅:D[
52.166] → [
15.2014:2073]
B:BD[
15.2014] → [
15.2014:2073]
B:BD[
15.2014] → [
52.121:166]
∅:D[
54.2028] → [
15.1870:2014]
∅:D[
14.6847] → [
15.1870:2014]
B:BD[
15.1870] → [
15.1870:2014]
∅:D[
55.936] → [
15.1864:1870]
∅:D[
56.1241] → [
15.1864:1870]
∅:D[
11.3165] → [
15.1864:1870]
B:BD[
18.6276] → [
15.1864:1870]
∅:D[
13.11507] → [
12.9616:9624]
B:BD[
12.9616] → [
12.9616:9624]
∅:D[
20.73] → [
12.9594:9616]
∅:D[
14.6653] → [
12.9594:9616]
∅:D[
35.8763] → [
12.9594:9616]
∅:D[
13.11328] → [
12.9594:9616]
B:BD[
12.9594] → [
12.9594:9616]
∅:D[
14.6651] → [
13.11132:11328]
B:BD[
13.11132] → [
13.11132:11328]
B:BD[
13.11067] → [
14.6539:6651]
∅:D[
14.6538] → [
13.10948:11067]
B:BD[
13.10948] → [
13.10948:11067]
B:BD[
13.10913] → [
14.6459:6538]
∅:D[
20.71] → [
13.10816:10913]
∅:D[
14.6458] → [
13.10816:10913]
B:BD[
12.8468] → [
13.10816:10913]
B:BD[
18.5338] → [
14.6339:6458]
∅:D[
16.5504] → [
18.5299:5338]
B:BD[
18.5299] → [
18.5299:5338]
B:BD[
12.8364] → [
16.5450:5504]
∅:D[
11.2595] → [
12.8359:8364]
B:BD[
12.8359] → [
12.8359:8364]
B:BD[
12.8359] → [
11.2287:2595]
∅:D[
14.6338] → [
12.8345:8359]
∅:D[
35.8761] → [
12.8345:8359]
∅:D[
13.10815] → [
12.8345:8359]
B:BD[
12.8345] → [
12.8345:8359]
B:BD[
14.6324] → [
14.6325:6338]
B:BD[
13.10729] → [
14.6324:6324]
∅:D[
14.6322] → [
13.10473:10729]
B:BD[
13.10473] → [
13.10473:10729]
B:BD[
13.10408] → [
14.6181:6322]
B:BD[
18.4696] → [
13.9917:10408]
∅:D[
57.1281] → [
18.4648:4696]
B:BD[
12.7017] → [
18.4648:4696]
∅:D[
58.865] → [
57.1257:1281]
B:BD[
57.1257] → [
57.1257:1281]
B:BD[
57.1226] → [
58.793:865]
∅:D[
14.6180] → [
57.1049:1226]
B:BD[
12.7017] → [
57.1049:1226]
B:BD[
18.4581] → [
14.6075:6180]
∅:D[
27.721] → [
18.4542:4581]
B:BD[
18.4542] → [
18.4542:4581]
B:BD[
12.6757] → [
27.669:721]
B:BD[
12.6752] → [
12.6752:6757]
B:BD[
12.6632] → [
18.4388:4414]
B:BD[
12.6627] → [
12.6627:6632]
B:BD[
12.6095] → [
18.4220:4252]
∅:D[
59.64] → [
12.6090:6095]
B:BD[
12.6090] → [
12.6090:6095]
B:BD[
12.6090] → [
59.53:64]
∅:D[
60.2101] → [
12.6084:6090]
B:BD[
12.6084] → [
12.6084:6090]
B:BD[
18.4219] → [
60.2069:2101]
∅:D[
11.2148] → [
18.4191:4219]
∅:D[
61.8245] → [
18.4191:4219]
∅:D[
13.9916] → [
18.4191:4219]
B:BD[
18.4191] → [
18.4191:4219]
B:BD[
12.5923] → [
11.2108:2148]
∅:D[
18.4060] → [
12.5917:5923]
∅:D[
43.99635] → [
12.5917:5923]
B:BD[
12.5917] → [
12.5917:5923]
∅:D[
14.6074] → [
13.9765:9790]
B:BD[
13.9765] → [
13.9765:9790]
B:BD[
12.5853] → [
14.6004:6074]
∅:D[
63.13] → [
12.5845:5853]
∅:D[
64.1110] → [
12.5845:5853]
∅:D[
14.6003] → [
12.5845:5853]
∅:D[
13.9658] → [
12.5845:5853]
∅:D[
65.37331] → [
12.5845:5853]
B:BD[
12.5845] → [
12.5845:5853]
∅:D[
14.6001] → [
13.9505:9658]
∅:D[
65.37329] → [
13.9505:9658]
B:BD[
18.3138] → [
13.9505:9658]
B:BD[
18.3095] → [
14.5964:6001]
B:BD[
12.4249] → [
18.3031:3095]
∅:D[
18.3030] → [
12.4205:4249]
∅:D[
43.98451] → [
12.4205:4249]
B:BD[
12.4205] → [
12.4205:4249]
B:BD[
12.4124] → [
18.2931:3030]
∅:D[
11.1996] → [
12.4101:4124]
∅:D[
18.2930] → [
12.4101:4124]
∅:D[
10.22445] → [
12.4101:4124]
∅:D[
43.98330] → [
12.4101:4124]
B:BD[
12.4101] → [
12.4101:4124]
∅:D[
66.314] → [
10.22423:22445]
B:BD[
18.2901] → [
10.22423:22445]
B:BD[
67.1165] → [
66.268:314]
∅:D[
68.816] → [
67.988:1165]
B:BD[
69.1895] → [
67.988:1165]
B:BD[
70.1023] → [
68.642:816]
∅:D[
71.44] → [
70.999:1023]
∅:D[
18.2727] → [
70.999:1023]
B:BD[
12.3913] → [
70.999:1023]
∅:D[
59.52] → [
18.2701:2727]
∅:D[
72.366] → [
18.2701:2727]
B:BD[
12.3868] → [
18.2701:2727]
B:BD[
72.366] → [
59.28:52]
∅:D[
21.754] → [
73.790:795]
B:BD[
73.790] → [
73.790:795]
B:BD[
73.790] → [
73.790:795]
local screen_bottom1 = {line=nil, pos=nil}
State.old_cursor1, State.old_selection1, State.mousepress_shift = nil
if eq(State.cursor1, State.selection1) then
State.selection1 = {}
end
break
--? 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))
--? print('text input', t)
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
end
edit.draw(State)
edit.update(State, 0)
App.screen.contents = {}
edit.mouse_release(State, x,y, mouse_button)
function edit.run_after_mouse_release(State, x,y, mouse_button)
App.fake_mouse_release(x,y, mouse_button)
edit.draw(State)
end
edit.update(State, 0)
App.screen.contents = {}
edit.mouse_press(State, x,y, mouse_button)
function edit.run_after_mouse_press(State, x,y, mouse_button)
App.fake_mouse_press(x,y, mouse_button)
edit.draw(State)
end
edit.update(State, 0)
App.screen.contents = {}
edit.mouse_release(State, x,y, mouse_button)
App.fake_mouse_release(x,y, mouse_button)
edit.mouse_press(State, x,y, mouse_button)
function edit.run_after_mouse_click(State, x,y, mouse_button)
App.fake_mouse_press(x,y, mouse_button)
edit.draw(State)
end
edit.update(State, 0)
App.screen.contents = {}
edit.keychord_press(State, chord)
edit.key_release(State, chord)
function edit.run_after_keychord(State, chord)
-- not all keys are text_input
end
edit.draw(State)
edit.update(State, 0)
App.screen.contents = {}
function edit.run_after_text_input(State, t)
edit.keychord_press(State, t)
edit.text_input(State, t)
edit.key_release(State, t)
-- TODO: handle chords of multiple keys
-- all text_input events are also keypresses
14, -- font height assuming default LÖVE font
15) -- line height
end
App.screen.width - Test_margin_right,
function edit.initialize_test_state()
-- if you change these values, tests will start failing
return edit.initialize_state(
15, -- top margin
Test_margin_left,
Test_margin_right = 0
Test_margin_left = 25
-- Insulate tests from some key globals so I don't have to change the vast
-- majority of tests when they're modified for the real app.
--== some methods for tests
end
State.line_height = math.floor(font_height*1.3)
love.graphics.setFont(love.graphics.newFont(State.font_height))
function edit.update_font_settings(State, font_height)
State.font_height = font_height
end
function edit.key_release(State, key, scancode)
end
end
Text.keychord_press(State, chord)
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
else
-- dispatch to text
end
App.set_clipboard(s)
if s then
local s = Text.selection(State)
elseif chord == 'C-c' then
elseif chord == 'C-a' then
State.selection1 = {line=1, pos=1}
State.cursor1 = {line=#State.lines, pos=utf8.len(State.lines[#State.lines].data)+1}
-- clipboard
Text.redraw_all(State)
edit.update_font_settings(State, 20)
elseif chord == 'C-0' then
if State.font_height > 2 then
edit.update_font_settings(State, State.font_height-2)
Text.redraw_all(State)
end
elseif chord == 'C--' then
Text.redraw_all(State)
edit.update_font_settings(State, State.font_height+2)
elseif chord == 'C-=' then
-- zoom
State.search_backup = {
cursor={line=State.cursor1.line, pos=State.cursor1.pos},
screen_top={line=State.screen_top1.line, pos=State.screen_top1.pos},
}
State.search_term = ''
end
return
elseif chord == 'C-f' then
Text.search_previous(State)
elseif chord == 'up' then
Text.search_next(State)
State.cursor1.pos = State.cursor1.pos+1
elseif chord == 'down' then
local len = utf8.len(State.search_term)
local byte_offset = Text.offset(State.search_term, len)
State.search_term = string.sub(State.search_term, 1, byte_offset-1)
elseif chord == 'backspace' then
State.search_backup = nil
State.search_term = nil
elseif chord == 'return' then
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
State.cursor1 = State.search_backup.cursor
State.screen_top1 = State.search_backup.screen_top
State.search_backup = nil
State.search_term = nil
if chord == 'escape' then
if State.search_term then
end
Text.delete_selection(State, State.left, State.right)
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
-- 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
if State.selection1.line and
function edit.keychord_press(State, chord, key)
end
end
Text.search_next(State)
if State.search_term then
State.search_term = State.search_term..t
function edit.text_input(State, t)
end
end
for i=1,math.floor(-dy) do
Text.down(State)
end
edit.put_cursor_on_next_text_line(State)
State.cursor1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos}
elseif dy < 0 then
for i=1,math.floor(dy) do
Text.up(State)
end
edit.put_cursor_on_next_text_line(State)
end
function edit.mouse_wheel_move(State, dx,dy)
if dy > 0 then
State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
end
end
end
end
if State.mousepress_shift then
if State.old_selection1.line == nil then
State.selection1 = State.old_cursor1
else
State.selection1 = State.old_selection1
--? print_and_log(('edit.mouse_release: cursor now %d,%d'):format(State.cursor1.line, State.cursor1.pos))
State.cursor1 = {
line=line_index,
pos=Text.to_pos_on_line(State, line_index, x, y),
}
--? print_and_log(('edit.mouse_release: in line %d'):format(line_index))
for line_index,line in ipairs(State.lines) do
if Text.in_line(State, line_index, x,y) then
--? print_and_log(('edit.mouse_release(%d,%d): cursor at %d,%d'):format(x,y, State.cursor1.line, State.cursor1.pos))
if State.search_term then return end
function edit.mouse_release(State, x,y, mouse_button)
end
-- still here? click is below all screen lines
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),
}
end
end
return
State.old_cursor1 = State.cursor1
State.old_selection1 = State.selection1
State.mousepress_shift = App.shift_down()
State.selection1 = {
line=line_index,
pos=Text.to_pos_on_line(State, line_index, x, y),
}
-- i.e. mouse_release should never look at shift state
--? print_and_log(('edit.mouse_press: in line %d'):format(line_index))
if Text.in_line(State, line_index, x,y) then
-- delicate dance between cursor, selection and old cursor/selection
-- scenarios:
-- regular press+release: sets cursor, clears selection
-- shift press+release:
-- sets selection to old cursor if not set otherwise leaves it untouched
-- sets cursor
-- press and hold to start a selection: sets selection on press, cursor on release
-- press and hold, then press shift: ignore shift
for line_index,line in ipairs(State.lines) do
}
return
end
line=State.screen_top1.line,
pos=State.screen_top1.pos,
if y < State.top then
State.old_cursor1 = State.cursor1
State.old_selection1 = State.selection1
State.mousepress_shift = App.shift_down()
State.selection1 = {
--? print_and_log(('edit.mouse_press: cursor at %d,%d'):format(State.cursor1.line, State.cursor1.pos))
if State.search_term then return end
function edit.mouse_press(State, x,y, mouse_button)
end
function edit.quit(State)
end
function edit.update(State, dt)
end
return y
end
Text.draw_search_bar(State)
if State.search_term then
State.screen_bottom1 = screen_bottom1
end
--? print('=> y', y)
y, screen_bottom1.pos = Text.draw(State, line_index, y, startpos)
end
--? print('text.draw', y, line_index)
local startpos = 1
if line_index == State.screen_top1.line then
startpos = State.screen_top1.pos
screen_bottom1.line = line_index
if y + State.line_height > App.screen.height then break end
--? print('draw:', y, line_index, line)
for line_index = State.screen_top1.line,#State.lines do
local line = State.lines[line_index]
--? print('== draw')
local y = State.top
State.cursor_x = nil
State.cursor_y = nil
if not Text.le1(State.screen_top1, State.cursor1) then
print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
assert(false)
end
if #State.lines ~= #State.line_cache then
print(('line_cache is out of date; %d when it should be %d'):format(#State.line_cache, #State.lines))
assert(false)
end
App.color(Text_color)
function edit.draw(State)
-- return y drawn until
end
end
-- return y drawn until
function edit.draw(State)
App.color(Text_color)
if #State.lines ~= #State.line_cache then
print(('line_cache is out of date; %d when it should be %d'):format(#State.line_cache, #State.lines))
assert(false)
end
if not Text.le1(State.screen_top1, State.cursor1) then
print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
assert(false)
end
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)
--? print('=> y', y)
end
State.screen_bottom1 = screen_bottom1
if State.search_term then
Text.draw_search_bar(State)
end
return y
end
function edit.update(State, dt)
end
function edit.quit(State)
end
function edit.mouse_press(State, x,y, mouse_button)
if State.search_term then return end
--? print_and_log(('edit.mouse_press: cursor at %d,%d'):format(State.cursor1.line, State.cursor1.pos))
if y < State.top then
State.old_cursor1 = State.cursor1
State.old_selection1 = State.selection1
State.mousepress_shift = App.shift_down()
State.selection1 = {
line=State.screen_top1.line,
pos=State.screen_top1.pos,
}
return
end
for line_index,line in ipairs(State.lines) do
if Text.in_line(State, line_index, x,y) then
-- delicate dance between cursor, selection and old cursor/selection
-- scenarios:
-- regular press+release: sets cursor, clears selection
-- shift press+release:
-- sets selection to old cursor if not set otherwise leaves it untouched
-- sets cursor
-- press and hold to start a selection: sets selection on press, cursor on release
-- press and hold, then press shift: ignore shift
-- i.e. mouse_release should never look at shift state
--? print_and_log(('edit.mouse_press: in line %d'):format(line_index))
State.old_cursor1 = State.cursor1
State.old_selection1 = State.selection1
State.mousepress_shift = App.shift_down()
State.selection1 = {
line=line_index,
pos=Text.to_pos_on_line(State, line_index, x, y),
}
return
end
end
-- still here? click is below all screen lines
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),
}
end
function edit.mouse_release(State, x,y, mouse_button)
if State.search_term then return end
--? print_and_log(('edit.mouse_release(%d,%d): cursor at %d,%d'):format(x,y, State.cursor1.line, State.cursor1.pos))
for line_index,line in ipairs(State.lines) do
if Text.in_line(State, line_index, x,y) then
--? print_and_log(('edit.mouse_release: in line %d'):format(line_index))
State.cursor1 = {
line=line_index,
pos=Text.to_pos_on_line(State, line_index, x, y),
}
--? print_and_log(('edit.mouse_release: cursor now %d,%d'):format(State.cursor1.line, State.cursor1.pos))
if State.mousepress_shift then
if State.old_selection1.line == nil then
State.selection1 = State.old_cursor1
else
State.selection1 = State.old_selection1
end
end
State.old_cursor1, State.old_selection1, State.mousepress_shift = nil
if eq(State.cursor1, State.selection1) then
State.selection1 = {}
end
break
end
end
--? 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
function edit.mouse_wheel_move(State, dx,dy)
if dy > 0 then
State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
edit.put_cursor_on_next_text_line(State)
for i=1,math.floor(dy) do
Text.up(State)
end
elseif dy < 0 then
State.cursor1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos}
edit.put_cursor_on_next_text_line(State)
for i=1,math.floor(-dy) do
Text.down(State)
end
end
end
function edit.text_input(State, t)
--? print('text input', t)
if State.search_term then
State.search_term = State.search_term..t
Text.search_next(State)
end
end
function edit.keychord_press(State, chord, key)
if State.selection1.line and
-- 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
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
State.screen_top1 = State.search_backup.screen_top
State.search_backup = nil
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
elseif chord == 'return' then
State.search_term = nil
State.search_backup = nil
elseif chord == 'backspace' then
local len = utf8.len(State.search_term)
local byte_offset = Text.offset(State.search_term, len)
State.search_term = string.sub(State.search_term, 1, byte_offset-1)
elseif chord == 'down' then
State.cursor1.pos = State.cursor1.pos+1
Text.search_next(State)
elseif chord == 'up' then
Text.search_previous(State)
end
return
elseif chord == 'C-f' then
State.search_term = ''
State.search_backup = {
cursor={line=State.cursor1.line, pos=State.cursor1.pos},
screen_top={line=State.screen_top1.line, pos=State.screen_top1.pos},
}
-- zoom
elseif chord == 'C-=' then
edit.update_font_settings(State, State.font_height+2)
Text.redraw_all(State)
elseif chord == 'C--' then
if State.font_height > 2 then
edit.update_font_settings(State, State.font_height-2)
Text.redraw_all(State)
end
elseif chord == 'C-0' then
edit.update_font_settings(State, 20)
Text.redraw_all(State)
-- clipboard
elseif chord == 'C-a' then
State.selection1 = {line=1, pos=1}
State.cursor1 = {line=#State.lines, pos=utf8.len(State.lines[#State.lines].data)+1}
elseif chord == 'C-c' then
local s = Text.selection(State)
if s then
App.set_clipboard(s)
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
function edit.key_release(State, key, scancode)
end
function edit.update_font_settings(State, font_height)
State.font_height = font_height
love.graphics.setFont(love.graphics.newFont(State.font_height))
State.line_height = math.floor(font_height*1.3)
end
--== some methods for tests
-- Insulate tests from some key globals so I don't have to change the vast
-- majority of tests when they're modified for the real app.
Test_margin_left = 25
Test_margin_right = 0
function edit.initialize_test_state()
-- if you change these values, tests will start failing
return edit.initialize_state(
15, -- top margin
Test_margin_left,
App.screen.width - Test_margin_right,
14, -- font height assuming default LÖVE font
15) -- line height
end
-- all text_input events are also keypresses
-- TODO: handle chords of multiple keys
function edit.run_after_text_input(State, t)
edit.keychord_press(State, t)
edit.text_input(State, t)
edit.key_release(State, t)
App.screen.contents = {}
edit.update(State, 0)
edit.draw(State)
end
-- not all keys are text_input
function edit.run_after_keychord(State, chord)
edit.keychord_press(State, chord)
edit.key_release(State, chord)
App.screen.contents = {}
edit.update(State, 0)
edit.draw(State)
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)
App.fake_mouse_release(x,y, mouse_button)
edit.mouse_release(State, x,y, mouse_button)
App.screen.contents = {}
edit.update(State, 0)
edit.draw(State)
end
function edit.run_after_mouse_press(State, x,y, mouse_button)
App.fake_mouse_press(x,y, mouse_button)
edit.mouse_press(State, x,y, mouse_button)
App.screen.contents = {}
edit.update(State, 0)
edit.draw(State)
end
function edit.run_after_mouse_release(State, x,y, mouse_button)
App.fake_mouse_release(x,y, mouse_button)
edit.mouse_release(State, x,y, mouse_button)
App.screen.contents = {}
edit.update(State, 0)
edit.draw(State)
end