B:BD[
13.2727] → [
14.26:55]
B:BD[
16.22445] → [
17.1951:1996]
B:BD[
18.9658] → [
19.6003:6003]
B:BD[
20.470] → [
5.26:113]
∅:D[
21.865] → [
22.1257:1281]
B:BD[
22.1257] → [
22.1257:1281]
B:BD[
22.1226] → [
21.793:865]
∅:D[
7.135] → [
22.1049:1226]
∅:D[
19.6180] → [
22.1049:1226]
B:BD[
15.7017] → [
22.1049:1226]
B:BD[
14.218] → [
7.135:135]
B:BD[
18.11328] → [
19.6653:6653]
B:BD[
15.9616] → [
18.11329:11507]
B:BD[
23.1870] → [
19.6654:6847]
B:BD[
24.5593] → [
25.170:199]
B:BD[
13.6985] → [
26.134:241]
B:BD[
27.6870] → [
3.26:36]
B:BD[
18.11602] → [
28.75:75]
∅:D[
29.756] → [
30.364:368]
B:BD[
31.258] → [
30.364:368]
B:BD[
32.7283] → [
29.756:756]
∅:D[
33.258] → [
32.7264:7283]
B:BD[
32.7264] → [
32.7264:7283]
B:BD[
32.7264] → [
33.234:258]
∅:D[
34.567] → [
32.7237:7264]
∅:D[
24.6186] → [
32.7237:7264]
B:BD[
32.7237] → [
32.7237:7264]
B:BD[
34.519] → [
24.6139:6186]
B:BD[
32.7099] → [
34.411:519]
∅:D[
33.233] → [
32.7075:7099]
B:BD[
32.7075] → [
32.7075:7099]
B:BD[
32.7075] → [
33.209:233]
∅:D[
34.410] → [
32.7048:7075]
∅:D[
35.880] → [
32.7048:7075]
B:BD[
32.7048] → [
32.7048:7075]
B:BD[
34.363] → [
35.835:880]
B:BD[
32.6915] → [
34.259:363]
∅:D[
33.208] → [
32.6891:6915]
B:BD[
32.6891] → [
32.6891:6915]
B:BD[
32.6891] → [
33.184:208]
∅:D[
34.258] → [
32.6864:6891]
∅:D[
24.6138] → [
32.6864:6891]
B:BD[
32.6864] → [
32.6864:6891]
B:BD[
34.210] → [
24.6091:6138]
∅:D[
35.834] → [
34.166:210]
B:BD[
34.166] → [
34.166:210]
B:BD[
34.119] → [
35.789:834]
B:BD[
32.6651] → [
34.15:119]
∅:D[
33.183] → [
32.6627:6651]
B:BD[
32.6627] → [
32.6627:6651]
B:BD[
32.6627] → [
33.159:183]
∅:D[
24.6090] → [
32.6600:6627]
B:BD[
32.6600] → [
32.6600:6627]
B:BD[
32.6528] → [
24.6021:6090]
∅:D[
24.6020] → [
32.6481:6528]
B:BD[
32.6481] → [
32.6481:6528]
B:BD[
32.6451] → [
24.5989:6020]
B:BD[
15.16047] → [
32.6450:6451]
∅:D[
32.6449] → [
15.16043:16047]
∅:D[
13.9642] → [
15.16043:16047]
B:BD[
15.16043] → [
15.16043:16047]
∅:D[
33.158] → [
32.6430:6449]
B:BD[
32.6430] → [
32.6430:6449]
B:BD[
32.6430] → [
33.134:158]
∅:D[
24.5988] → [
32.6403:6430]
B:BD[
32.6403] → [
32.6403:6430]
B:BD[
32.6268] → [
24.5854:5988]
∅:D[
24.5853] → [
32.6228:6268]
B:BD[
32.6228] → [
32.6228:6268]
B:BD[
32.6184] → [
24.5808:5853]
∅:D[
36.5729] → [
32.6183:6184]
B:BD[
32.6183] → [
32.6183:6184]
∅:D[
37.1119] → [
36.5645:5729]
B:BD[
36.5645] → [
36.5645:5729]
B:BD[
36.5600] → [
37.1075:1119]
∅:D[
37.1074] → [
36.5422:5600]
B:BD[
36.5422] → [
36.5422:5600]
B:BD[
36.5422] → [
37.1052:1074]
∅:D[
37.1051] → [
36.5400:5422]
B:BD[
36.5400] → [
36.5400:5422]
B:BD[
36.5400] → [
37.915:1051]
∅:D[
38.447] → [
36.5371:5400]
B:BD[
32.6146] → [
36.5371:5400]
∅:D[
39.329] → [
38.442:447]
B:BD[
38.442] → [
38.442:447]
B:BD[
38.337] → [
38.337:387]
B:BD[
32.6146] → [
38.175:264]
∅:D[
24.5807] → [
32.6141:6146]
B:BD[
13.9642] → [
32.6141:6146]
B:BD[
15.16001] → [
24.5759:5807]
∅:D[
24.5758] → [
15.15990:16001]
∅:D[
40.7139] → [
15.15990:16001]
B:BD[
15.15990] → [
15.15990:16001]
B:BD[
41.1400] → [
24.5720:5758]
B:BD[
15.15878] → [
41.1293:1400]
∅:D[
28.75] → [
15.15871:15878]
∅:D[
42.631] → [
15.15871:15878]
∅:D[
43.8798] → [
15.15871:15878]
∅:D[
18.11602] → [
15.15871:15878]
B:BD[
15.15871] → [
15.15871:15878]
B:BD[
44.2812] → [
18.11580:11602]
B:BD[
42.577] → [
44.2710:2812]
B:BD[
15.14368] → [
42.552:577]
∅:D[
45.1438] → [
15.14360:14368]
∅:D[
13.8913] → [
15.14360:14368]
∅:D[
16.23116] → [
15.14360:14368]
∅:D[
46.105181] → [
15.14360:14368]
B:BD[
15.14360] → [
15.14360:14368]
∅:D[
47.360] → [
16.23041:23116]
B:BD[
45.1331] → [
16.23041:23116]
B:BD[
15.14227] → [
47.315:360]
∅:D[
48.8706] → [
15.14209:14227]
B:BD[
15.14209] → [
15.14209:14227]
B:BD[
15.14176] → [
48.8666:8706]
∅:D[
48.8665] → [
15.14165:14176]
B:BD[
15.14165] → [
15.14165:14176]
B:BD[
15.14136] → [
48.8631:8665]
∅:D[
49.454] → [
15.14032:14136]
B:BD[
15.14032] → [
15.14032:14136]
B:BD[
44.2709] → [
49.407:454]
B:BD[
13.8812] → [
44.2661:2709]
B:BD[
15.13904] → [
13.8769:8812]
∅:D[
2.230] → [
15.13768:13904]
∅:D[
41.1249] → [
15.13768:13904]
∅:D[
13.8768] → [
15.13768:13904]
∅:D[
46.105015] → [
15.13768:13904]
B:BD[
15.13768] → [
15.13768:13904]
B:BD[
15.13689] → [
2.123:230]
∅:D[
42.551] → [
15.13660:13689]
B:BD[
15.13660] → [
15.13660:13689]
B:BD[
15.13640] → [
42.526:551]
∅:D[
49.406] → [
15.13632:13640]
B:BD[
15.13632] → [
15.13632:13640]
B:BD[
15.13602] → [
49.379:406]
∅:D[
50.3962] → [
15.13588:13602]
∅:D[
13.8682] → [
15.13588:13602]
∅:D[
16.23040] → [
15.13588:13602]
∅:D[
46.104922] → [
15.13588:13602]
B:BD[
15.13588] → [
15.13588:13602]
∅:D[
41.1158] → [
16.22975:23040]
B:BD[
13.8593] → [
16.22975:23040]
B:BD[
15.13432] → [
41.1051:1158]
∅:D[
49.378] → [
15.13395:13432]
B:BD[
15.13395] → [
15.13395:13432]
B:BD[
15.13365] → [
49.351:378]
∅:D[
50.3865] → [
15.13351:13365]
B:BD[
15.13351] → [
15.13351:13365]
∅:D[
41.1050] → [
50.3829:3865]
B:BD[
13.8507] → [
50.3829:3865]
∅:D[
51.657] → [
15.13212:13241]
B:BD[
15.13212] → [
15.13212:13241]
B:BD[
15.13212] → [
51.501:657]
∅:D[
42.525] → [
15.13189:13212]
B:BD[
15.13189] → [
15.13189:13212]
∅:D[
52.398] → [
42.498:525]
∅:D[
27.7066] → [
42.498:525]
B:BD[
15.13167] → [
42.498:525]
B:BD[
52.296] → [
52.296:398]
B:BD[
15.12904] → [
13.8226:8421]
∅:D[
44.2660] → [
15.12856:12904]
B:BD[
15.12856] → [
15.12856:12904]
∅:D[
41.942] → [
44.2624:2660]
B:BD[
13.8225] → [
44.2624:2660]
B:BD[
15.12746] → [
41.835:942]
∅:D[
42.497] → [
15.12709:12746]
B:BD[
15.12709] → [
15.12709:12746]
∅:D[
52.206] → [
42.470:497]
∅:D[
27.6968] → [
42.470:497]
B:BD[
15.12687] → [
42.470:497]
B:BD[
52.104] → [
52.104:206]
B:BD[
13.8139] → [
53.1967:2037]
B:BD[
15.12424] → [
13.7944:8139]
∅:D[
44.2623] → [
15.12375:12424]
B:BD[
15.12375] → [
15.12375:12424]
∅:D[
2.122] → [
44.2587:2623]
∅:D[
41.834] → [
44.2587:2623]
B:BD[
13.7943] → [
44.2587:2623]
B:BD[
15.12265] → [
2.15:122]
∅:D[
3.36] → [
15.12236:12265]
∅:D[
27.6870] → [
15.12236:12265]
B:BD[
15.12236] → [
15.12236:12265]
∅:D[
38.174] → [
27.6843:6870]
B:BD[
15.12214] → [
27.6843:6870]
B:BD[
15.12181] → [
38.133:174]
∅:D[
54.289] → [
15.12152:12181]
∅:D[
27.6842] → [
15.12152:12181]
B:BD[
15.12152] → [
15.12152:12181]
B:BD[
15.12086] → [
54.158:289]
∅:D[
27.6814] → [
15.12057:12086]
B:BD[
15.12057] → [
15.12057:12086]
∅:D[
38.73] → [
27.6787:6814]
B:BD[
13.7806] → [
27.6787:6814]
B:BD[
15.11991] → [
38.15:73]
∅:D[
3.25] → [
15.11962:11991]
∅:D[
13.7755] → [
15.11962:11991]
∅:D[
46.103883] → [
15.11962:11991]
B:BD[
15.11962] → [
15.11962:11991]
B:BD[
13.7755] → [
3.15:25]
B:BD[
13.7565] → [
55.25240:25412]
B:BD[
15.11787] → [
13.7538:7565]
∅:D[
56.2183] → [
15.11739:11787]
B:BD[
15.11739] → [
15.11739:11787]
B:BD[
15.11710] → [
56.2149:2183]
∅:D[
56.2148] → [
15.11680:11710]
B:BD[
15.11680] → [
15.11680:11710]
B:BD[
13.7537] → [
56.2118:2148]
B:BD[
15.11621] → [
13.7491:7537]
∅:D[
13.7490] → [
15.11589:11621]
∅:D[
46.103555] → [
15.11589:11621]
B:BD[
15.11589] → [
15.11589:11621]
B:BD[
15.11407] → [
13.7278:7460]
∅:D[
13.7277] → [
15.11370:11407]
∅:D[
46.103307] → [
15.11370:11407]
B:BD[
15.11370] → [
15.11370:11407]
B:BD[
13.7245] → [
13.7245:7277]
B:BD[
15.11296] → [
13.7185:7215]
∅:D[
27.6786] → [
15.11262:11296]
B:BD[
15.11262] → [
15.11262:11296]
B:BD[
13.7184] → [
27.6689:6786]
B:BD[
13.7046] → [
13.7046:7184]
B:BD[
15.11014] → [
13.6986:7016]
∅:D[
26.241] → [
15.10984:11014]
∅:D[
13.6985] → [
15.10984:11014]
∅:D[
46.102945] → [
15.10984:11014]
B:BD[
15.10984] → [
15.10984:11014]
B:BD[
15.10962] → [
13.6957:6985]
∅:D[
50.3828] → [
15.10956:10962]
∅:D[
13.6956] → [
15.10956:10962]
∅:D[
16.22974] → [
15.10956:10962]
∅:D[
46.102909] → [
15.10956:10962]
B:BD[
15.10956] → [
15.10956:10962]
∅:D[
57.325] → [
16.22916:22974]
∅:D[
58.363] → [
16.22916:22974]
∅:D[
59.401] → [
16.22916:22974]
∅:D[
51.500] → [
16.22916:22974]
B:BD[
15.10886] → [
16.22916:22974]
B:BD[
15.10752] → [
59.214:401]
∅:D[
13.6873] → [
15.10522:10752]
∅:D[
46.102812] → [
15.10522:10752]
B:BD[
15.10522] → [
15.10522:10752]
∅:D[
24.5675] → [
13.6800:6831]
B:BD[
13.6800] → [
13.6800:6831]
B:BD[
15.10343] → [
24.5627:5675]
∅:D[
42.469] → [
15.10338:10343]
B:BD[
15.10338] → [
15.10338:10343]
B:BD[
15.10320] → [
42.446:469]
∅:D[
60.162] → [
15.10314:10320]
∅:D[
61.273] → [
15.10314:10320]
∅:D[
62.806] → [
15.10314:10320]
∅:D[
43.8796] → [
15.10314:10320]
B:BD[
15.10314] → [
15.10314:10320]
B:BD[
43.8765] → [
43.8766:8796]
B:BD[
60.133] → [
43.8765:8765]
B:BD[
15.10292] → [
60.26:133]
∅:D[
44.2586] → [
15.10285:10292]
∅:D[
13.6749] → [
15.10285:10292]
∅:D[
46.102724] → [
15.10285:10292]
B:BD[
15.10285] → [
15.10285:10292]
B:BD[
13.6495] → [
56.2089:2117]
∅:D[
25.199] → [
13.6394:6467]
∅:D[
41.743] → [
13.6394:6467]
∅:D[
24.5593] → [
13.6394:6467]
B:BD[
13.6394] → [
13.6394:6467]
B:BD[
15.9696] → [
24.5558:5593]
∅:D[
18.11579] → [
15.9691:9696]
B:BD[
15.9691] → [
15.9691:9696]
∅:D[
23.2225] → [
15.9685:9691]
∅:D[
13.6276] → [
15.9685:9691]
∅:D[
46.102222] → [
15.9685:9691]
B:BD[
15.9685] → [
15.9685:9691]
∅:D[
63.212] → [
23.2163:2225]
B:BD[
23.2163] → [
23.2163:2225]
B:BD[
23.2163] → [
63.167:212]
∅:D[
64.182] → [
23.2080:2163]
B:BD[
23.2080] → [
23.2080:2163]
B:BD[
23.2073] → [
64.161:182]
∅:D[
63.166] → [
23.2014:2073]
B:BD[
23.2014] → [
23.2014:2073]
B:BD[
23.2014] → [
63.121:166]
∅:D[
65.2028] → [
23.1870:2014]
∅:D[
19.6847] → [
23.1870:2014]
B:BD[
23.1870] → [
23.1870:2014]
∅:D[
66.936] → [
23.1864:1870]
∅:D[
67.1241] → [
23.1864:1870]
∅:D[
17.3165] → [
23.1864:1870]
B:BD[
13.6276] → [
23.1864:1870]
∅:D[
18.11507] → [
15.9616:9624]
B:BD[
15.9616] → [
15.9616:9624]
∅:D[
28.73] → [
15.9594:9616]
∅:D[
19.6653] → [
15.9594:9616]
∅:D[
43.8763] → [
15.9594:9616]
∅:D[
18.11328] → [
15.9594:9616]
B:BD[
15.9594] → [
15.9594:9616]
∅:D[
19.6651] → [
18.11132:11328]
B:BD[
18.11132] → [
18.11132:11328]
B:BD[
18.11067] → [
19.6539:6651]
∅:D[
19.6538] → [
18.10948:11067]
B:BD[
18.10948] → [
18.10948:11067]
B:BD[
18.10913] → [
19.6459:6538]
∅:D[
28.71] → [
18.10816:10913]
∅:D[
19.6458] → [
18.10816:10913]
B:BD[
15.8468] → [
18.10816:10913]
B:BD[
13.5338] → [
19.6339:6458]
∅:D[
24.5504] → [
13.5299:5338]
B:BD[
13.5299] → [
13.5299:5338]
B:BD[
15.8364] → [
24.5450:5504]
∅:D[
17.2595] → [
15.8359:8364]
B:BD[
15.8359] → [
15.8359:8364]
B:BD[
15.8359] → [
17.2287:2595]
∅:D[
19.6338] → [
15.8345:8359]
∅:D[
43.8761] → [
15.8345:8359]
∅:D[
18.10815] → [
15.8345:8359]
B:BD[
15.8345] → [
15.8345:8359]
B:BD[
19.6324] → [
19.6325:6338]
B:BD[
18.10729] → [
19.6324:6324]
∅:D[
19.6322] → [
18.10473:10729]
B:BD[
18.10473] → [
18.10473:10729]
B:BD[
18.10408] → [
19.6181:6322]
B:BD[
13.4696] → [
18.9917:10408]
∅:D[
68.69] → [
13.4648:4696]
∅:D[
14.218] → [
13.4648:4696]
∅:D[
22.1281] → [
13.4648:4696]
B:BD[
15.7017] → [
13.4648:4696]
B:BD[
13.4647] → [
14.56:218]
B:BD[
13.4581] → [
7.28:133]
∅:D[
35.721] → [
13.4542:4581]
B:BD[
13.4542] → [
13.4542:4581]
B:BD[
15.6757] → [
35.669:721]
∅:D[
5.113] → [
15.6746:6757]
∅:D[
20.470] → [
15.6746:6757]
∅:D[
13.4487] → [
15.6746:6757]
∅:D[
46.100241] → [
15.6746:6757]
B:BD[
15.6746] → [
15.6746:6757]
B:BD[
13.4441] → [
20.446:470]
B:BD[
15.6692] → [
13.4415:4441]
∅:D[
13.4414] → [
15.6653:6692]
B:BD[
15.6653] → [
15.6653:6692]
B:BD[
15.6632] → [
13.4388:4414]
∅:D[
42.419] → [
15.6621:6632]
∅:D[
69.895] → [
15.6621:6632]
∅:D[
46.100147] → [
15.6621:6632]
B:BD[
15.6621] → [
15.6621:6632]
B:BD[
42.312] → [
69.789:895]
B:BD[
15.6468] → [
42.249:312]
∅:D[
13.4387] → [
15.6457:6468]
∅:D[
46.99992] → [
15.6457:6468]
B:BD[
15.6457] → [
15.6457:6468]
∅:D[
20.445] → [
13.4361:4387]
B:BD[
13.4361] → [
13.4361:4387]
∅:D[
70.544] → [
20.421:445]
∅:D[
69.788] → [
20.421:445]
B:BD[
13.4315] → [
20.421:445]
B:BD[
13.4252] → [
70.483:544]
B:BD[
15.6095] → [
13.4220:4252]
∅:D[
71.64] → [
15.6090:6095]
B:BD[
15.6090] → [
15.6090:6095]
B:BD[
15.6090] → [
71.53:64]
∅:D[
72.2101] → [
15.6084:6090]
B:BD[
15.6084] → [
15.6084:6090]
B:BD[
13.4219] → [
72.2069:2101]
∅:D[
17.2148] → [
13.4191:4219]
∅:D[
73.8245] → [
13.4191:4219]
∅:D[
18.9916] → [
13.4191:4219]
B:BD[
13.4191] → [
13.4191:4219]
B:BD[
15.5923] → [
17.2108:2148]
∅:D[
13.4060] → [
15.5917:5923]
∅:D[
46.99635] → [
15.5917:5923]
B:BD[
15.5917] → [
15.5917:5923]
∅:D[
19.6074] → [
18.9765:9790]
B:BD[
18.9765] → [
18.9765:9790]
B:BD[
15.5853] → [
19.6004:6074]
∅:D[
75.13] → [
15.5845:5853]
∅:D[
76.1110] → [
15.5845:5853]
∅:D[
19.6003] → [
15.5845:5853]
∅:D[
18.9658] → [
15.5845:5853]
∅:D[
77.37331] → [
15.5845:5853]
B:BD[
15.5845] → [
15.5845:5853]
∅:D[
19.6001] → [
18.9505:9658]
∅:D[
77.37329] → [
18.9505:9658]
B:BD[
13.3138] → [
18.9505:9658]
B:BD[
13.3095] → [
19.5964:6001]
B:BD[
15.4249] → [
13.3031:3095]
∅:D[
13.3030] → [
15.4205:4249]
∅:D[
46.98451] → [
15.4205:4249]
B:BD[
15.4205] → [
15.4205:4249]
B:BD[
15.4124] → [
13.2931:3030]
∅:D[
17.1996] → [
15.4101:4124]
∅:D[
13.2930] → [
15.4101:4124]
∅:D[
16.22445] → [
15.4101:4124]
∅:D[
46.98330] → [
15.4101:4124]
B:BD[
15.4101] → [
15.4101:4124]
∅:D[
47.314] → [
16.22423:22445]
B:BD[
13.2901] → [
16.22423:22445]
B:BD[
78.1165] → [
47.268:314]
∅:D[
79.816] → [
78.988:1165]
B:BD[
53.1895] → [
78.988:1165]
B:BD[
80.1023] → [
79.642:816]
∅:D[
81.44] → [
80.999:1023]
∅:D[
14.55] → [
80.999:1023]
∅:D[
13.2727] → [
80.999:1023]
B:BD[
15.3913] → [
80.999:1023]
∅:D[
71.52] → [
13.2701:2727]
∅:D[
82.366] → [
13.2701:2727]
B:BD[
15.3868] → [
13.2701:2727]
B:BD[
82.366] → [
71.28:52]
∅:D[
29.754] → [
83.790:795]
B:BD[
83.790] → [
83.790:795]
B:BD[
83.790] → [
83.790:795]
State.button_handlers = {}
local screen_bottom1 = {line=nil, pos=nil}
-- give some time for the OS to flush everything to disk
love.timer.sleep(0.1)
}
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 = {
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
-- undo
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
record_undo_event(State, {before=before, after=snapshot(State, before_line, State.cursor1.line)})
schedule_save(State)
end
Text.snap_cursor_to_bottom_of_screen(State, State.left, State.right)
if Text.cursor_out_of_screen(State) then
end
end
Text.insert_at_cursor(State, c)
else
Text.insert_return(State)
for _,code in utf8.codes(clipboard_data) do
local c = utf8.char(code)
if c == '\n' then
local clipboard_data = App.get_clipboard()
local before = snapshot(State, before_line)
local before_line = State.cursor1.line
-- We don't have a good sense of when to scroll, so we'll be conservative
-- and sometimes scroll when we didn't quite need to.
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
elseif chord == 'C-v' then
schedule_save(State)
end
App.set_clipboard(s)
if s then
local s = Text.cut_selection(State, State.left, State.right)
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
end
elseif chord == 'C-x' then
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}
end
-- clipboard
schedule_save(State)
-- if we're scrolling, reclaim all fragments to avoid memory leaks
Text.redraw_all(State)
State.screen_top1 = deepcopy(src.screen_top)
State.cursor1 = deepcopy(src.cursor)
State.selection1 = deepcopy(src.selection)
patch(State.lines, event.before, event.after)
if event then
local src = event.after
local event = redo_event(State)
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
end
elseif chord == 'C-y' then
schedule_save(State)
-- if we're scrolling, reclaim all fragments to avoid memory leaks
Text.redraw_all(State)
patch_placeholders(State.line_cache, event.after, event.before)
State.screen_top1 = deepcopy(src.screen_top)
State.cursor1 = deepcopy(src.cursor)
State.selection1 = deepcopy(src.selection)
patch(State.lines, event.after, event.before)
if event then
local src = event.before
local event = undo_event(State)
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
elseif chord == 'C-z' then
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
schedule_save(State)
end
Text.text_input(State, t)
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
else
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
if mouse_press_consumed_by_any_button_handler(State, x,y, mouse_button) then
-- press on a button and it returned 'true' to short-circuit
return
end
--? 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
end
save_to_disk(State)
if State.next_save then
-- make sure to save before quitting
function edit.quit(State)
end
end
State.next_save = Current_time + 3 -- short enough that you're likely to still remember what you did
function schedule_save(State)
if State.next_save == nil then
end
end
State.next_save = nil
save_to_disk(State)
if State.next_save and State.next_save < Current_time then
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)
State.button_handlers = {}
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)
if State.next_save and State.next_save < Current_time then
save_to_disk(State)
State.next_save = nil
end
end
function schedule_save(State)
if State.next_save == nil then
State.next_save = Current_time + 3 -- short enough that you're likely to still remember what you did
end
end
function edit.quit(State)
-- make sure to save before quitting
if State.next_save then
save_to_disk(State)
-- give some time for the OS to flush everything to disk
love.timer.sleep(0.1)
end
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 mouse_press_consumed_by_any_button_handler(State, x,y, mouse_button) then
-- press on a button and it returned 'true' to short-circuit
return
end
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)
else
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
Text.text_input(State, t)
end
schedule_save(State)
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)
-- undo
elseif chord == 'C-z' then
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
local event = undo_event(State)
if event then
local src = event.before
State.screen_top1 = deepcopy(src.screen_top)
State.cursor1 = deepcopy(src.cursor)
State.selection1 = deepcopy(src.selection)
patch(State.lines, event.after, event.before)
patch_placeholders(State.line_cache, event.after, event.before)
-- if we're scrolling, reclaim all fragments to avoid memory leaks
Text.redraw_all(State)
schedule_save(State)
end
elseif chord == 'C-y' then
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
local event = redo_event(State)
if event then
local src = event.after
State.screen_top1 = deepcopy(src.screen_top)
State.cursor1 = deepcopy(src.cursor)
State.selection1 = deepcopy(src.selection)
patch(State.lines, event.before, event.after)
-- if we're scrolling, reclaim all fragments to avoid memory leaks
Text.redraw_all(State)
schedule_save(State)
end
-- 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
elseif chord == 'C-x' then
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
local s = Text.cut_selection(State, State.left, State.right)
if s then
App.set_clipboard(s)
end
schedule_save(State)
elseif chord == 'C-v' then
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
-- We don't have a good sense of when to scroll, so we'll be conservative
-- and sometimes scroll when we didn't quite need to.
local before_line = State.cursor1.line
local before = snapshot(State, before_line)
local clipboard_data = App.get_clipboard()
for _,code in utf8.codes(clipboard_data) do
local c = utf8.char(code)
if c == '\n' then
Text.insert_return(State)
else
Text.insert_at_cursor(State, c)
end
end
if Text.cursor_out_of_screen(State) then
Text.snap_cursor_to_bottom_of_screen(State, State.left, State.right)
end
schedule_save(State)
record_undo_event(State, {before=before, after=snapshot(State, before_line, State.cursor1.line)})
-- 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