HGKCNQC5DCYQ5JFQEEBQVHHMWFOVU2BHS3UZHE5JND5IJUGLC22QC
VYBBJJLDFSDZHYXQXR46E6B43MIQYX4C2CLEPALQ5DHLWO42AWIAC
53TGTUB6MIIE3MCY25XDEXZX5SBW2GSB6OOFVZKETK3IVZWVKYUQC
TH6L66HJQ6GU2DT4ZMCCJTWZ6OVBYWWWYNWVR64VKKYF3MXOKZZQC
2EELKVO233KIH36SRY6LHYCDGARR52V62QPGONRZIKHXGV4RSNEQC
R5QXEHUIZLELJGGCZAE7ATNS3CLRJ7JFRENMGH4XXH24C5WABZDQC
JOPVPUSAMMU6RFVDQR4NJC4GNNUFB7GPKVH7OS5FKCYS5QZ53VLQC
FQESR6R62E7AOW5TCZNNAJNSHSB2VM7GFZ4YFM2M53FPA2SZKCFQC
ATQO62TFDZ7J4RCOSB3K2QCCB5R6PNYQIIGNXTLZMEFG5UG5PUJQC
VBU5YHLRO5ZSKFWBJRX7DWQGWPEHEWZMRRVV2WMWDJ54PKUNYCNQC
3XNFQDDNFGTN6ZFAB47AR6Q3663WLXDYC3K5TERYUWDM6U2FNOVAC
QXXISTGENW6IG3HW4LMQJEC55OPJ3BP6JT5MRKYZJA4USZKC6HAAC
2QLTVKM6MVC25L44BSHKPZAXOOLK2VAUCGCYRUDRXXNYNSWDIWQAC
VOU73AK6XOVIOCY6PHUXS5RQZ2TGFEF7RYNOKFE2XSHRCZBAJMYQC
ICUW7F3XQLURK4LSNPH5E3NDEFSRHKATEUHH2UPFJTMHYR3ZJF3QC
BULPIBEGL7TMK6CVIE7IS7WGAHGOSUJBGJSFQK542MOWGHP2ADQQC
2CK5QI7WA7M4IVSACFGOJYAIDKRUTZVMMPSFWEJTUNMWTN7AX4NAC
A42EMHOQV2GBLLYYVASKUJY6JF5CGBWXYUD2PW5OZPX365QUZ2MQC
LLQC2M2IMEJBJQXZTKC3OAKG5WKHSERXKAKCYHQRUZZD6CVRIHAQC
GG2553RBUKMCWIAD3EWJV7C2QH2BIS5V2E3BDTFSTYRHAPXGBF7QC
6PCE7VUWEE5UVSE3ZZSQAMGUJDDHOBNO2MVD6POUWFP7UY7WOZHAC
QAMVLUK22RP5RBDTDV5XVPQCSJUWDWESV4TRCUTNUM46E26BH2AQC
5XMBCKJZ7YCUOOQWWZRNLXMXD3MUYC2VAF3VKA4BIEHQRPDQMUMAC
KHOQZJWF3SNBCKLU5A7YDKPI6QHKZN7TERN2CRLB7GHG5GYBNNLAC
KF3XPAJU354OVGRTZTHK4RBZRJRFVQ2Z4REX6QSRT7HJXOCUEPMAC
ORKN6EOBUFVAD2TXYW5OIKSL55RU24LOFDTTTXHDZUZ57QRDCY7QC
F52PSTYSIIUD7UPEVLEJPVSBQU7IKVHM4GN4YASNSKFP4MRE6S6AC
SQCVG77RQKFZIRTE54AXZXQKU3SR35Z5WZQO7ADVSQDJN4FT2MAQC
D76IPVZBVKA53JJVNGTA2AQSRLIJY6W5RBU4PTUPR7IGL7BYNZCQC
Q65423XUYWJ5ESAH2B63MJKJUXSTQDANPZRRMQNYTDF3YGT5HLRAC
3VHUIIATPOF7FXB7NTL5MESCV5BCQACII2D7QZ4UIUCBX3CWXMMAC
3PSFWAILGRA4OYXWS2DX7VF332AIBPYBXHEA4GIQY2XEJVD65UMAC
O2S3YGBX6CH7HTQ6PWDQ7FHQUM2ACFGPHIPITB327QQDDJFGGZAQC
ONHKBLLCD5NDO3HSSUMAMGJ7HDT53JYVV56DI42AEYI3W63GKACQC
6YWKM6WS4IKDNRDZED7A7L5G7SR5PXINOH53X2IH2DFJKS4TVPZQC
FNJFBXABRFX5WK6SSRDLZDCNCABN5X36HUPKRMJ7ECQTEK2XJJJQC
JJ74YKGVDPNVI54TXGSXVS5ORMYHCWSILAMVVHQQXWUA65GIG7EAC
REAIVN7WJ3JMUKLX4BIACO2VMNHVTWLP3DMXPYD7PHVSYFFSV64QC
FXI74QCLOZ4BS7UVZ3U2PE3LOL7MX3FWGHZCTGH3DYFXGTXVVIRAC
5YGO6JX5WTBNIRW2LKLJF7EE7Z5UNXAMLG24AUV5BK227S4AI4IQC
5E7BBR7HWGOEJJ6YWPL3NJKZVBTUZWET7OQNCRU2MMNVEGCEIAWQC
EWQEFSZQN6JAJVENZMUKCUEB25GUE23U6DYSBNFNLB2NVUUDQ7ZQC
ANVLB2TX6ODOEUY7OADTQG47W7DNRHD6NXUPIJR5LVSJFMFJ3L2AC
U46N4W3QLSD5F7DNP6VB43CEE3OA2PJFLGNUG3MZ7EMNTFSUIP6AC
Z2VZTIXYXUZXF4ZUVG54IOIRSMBGUZJZ2RTBPYOJC7NWBOUFU6YAC
OTIBCAUJ3KDQJLVDN3A536DLZGNRYMGJLORZVR3WLCGXGO6UGO6AC
SPXS2WOLSSEEGOY5XJOITKPBL6MSD7NSXC4ESFVKP63PEFTEP33AC
ZE2ILGOVYZKBXUNKGMBGRP2KSDOQBO23RBFEMVVYFAWAIO5FCZCAC
KKMFQDR43ZWVCDRHQLWWX3FCWCFA3ZSXYOBRJNPHUQZR2XPKWULAC
3XQROLSKN5GIROYOSBN2OW2DSG34PKDU4BGYV7DL6F3V5RNSHATQC
XGHCLIKBWIW24G7O5QLYH6EUIY3TWTYFTUIYTWE25RFEGBHMZOKQC
HRWN5V6J6VMXS7WNSRGI7WMUSZ2OI52JJ4IK352VVSDZI4EF5HHQC
2R6UORLNH722O3OOQMXEMZNRSRW2JCN5OBVYJZMIA6AIJCKLJ24QC
NKGOFJYETE3B42J5HBFHKW2TR5YN3LNWFXYBUTOYEHBXHLBNYGGAC
CHEHZV5JUFZD4WMHFAXNKFMVFSAA4Y4MMWLRBFDJBHAZUFRKOD5AC
MX7YD2WCLJSLML3DSDVU3YLTALIVPTM7H7LNGSHEEUPOJGQHWUAAC
2IOWGOPGRSHTEC7GRMS5VOYTHUXLWAYAXEMQUPYSBMPRFE4KDT3QC
3AU3KLEUAWHZGGXBBV7PBVLGTZZDMHNFSU72NHDKINDRT2F667LQC
GFXWHTE6POBIOBUMRAWD5QS22JEO52EF4VTLMB4CDK4RLSCK7HCAC
VIGITGW5FAYYF4PDK6XAPI4S43X4FMAMBH77VLGMKLNUY7JJHR4QC
YF2ATH2QIIIMWNOH6IDNADNLUW3XUIYAMEUSTVM4EWMK2YWQ2OBAC
VPRGENLAJTF52YSY6NNZAPWQOD2TLP7DJU7DAAWGL4WFHOYAZF5QC
6VJTQKW7YJ7E3WRNSTFYULUDHHTYAE4JELX4J55LO75X4FDM3JZAC
UOTHQWM74AFOCPGQKKPLZXRI5HQFH3SRGI336AVZRGWPR6P256EAC
KEFZWDCOCLPTLSZJKRV4VYAHRITV5T33YKG2VGT332YAUCOBS3EAC
EMBBTRXDTL6DYNYUHOIZ2BXRCBS3BM6BPPZ4YLJAONG4K7DRSKNAC
4C5277X7DTT6YSUD7QXQJ323CIRU4IERYZGOG43JWCPZ67B4N5AQC
R2ASHK5CEE3PTRLS37GP4PXJ7HIGJ6UD72KKBI57UDJI7VRROQGQC
R4ZFP4EHMREGFBMO324EQYDWO7CVKMBFJJBRA5LVD5UKUCU2DYSQC
2L5MEZV344TOZLVY3432RHJFIRVXFD6O3GWLL5O4CV66BGAFTURQC
GVJEOWYQMHB2YEI5O6YUGHWDNSEINZJY7XM7D5HOFC2MN24J4YRQC
WKXJNESIMLTTUCTCT7Y32R4ESTKREY7L5JDZ43MWIPM5DVM47R5AC
YXQOITYSGYQQBOMF7CJ33C2CTDAI5R2W2RCRSKJ6JTLV7MS4WVXAC
B3PRPOPHWEAH3VNU2JFZU4YFGGDP6ZVFPLRUOH7OGH3SYIRIRJVAC
CRBLAWBOTECOU5MFURWCRBR43OO7NWIHP5LC35IMGTQKN73AUS6AC
TYLURRX3NWR66WYQSCPNFKB2N42NYKCY2TSL2Z6ZK7L4KCKFZF2QC
FBBHEUQNDHGZQES6GC3VW55DXILRP7PYYXNLQF3HGHKYLUGWAUBQC
RXMHAZ6VL4EJMYEZM32G4W54EPF3BPWPIGASJ7ND45FJ5F6TJCZAC
S7CSVBHZ4AORIP4VIQETFAVXVMNKERVOI5DENNTU73ZO2L667N7QC
QZUFJMD5OTAZGSUO4HSJ2NNNRJR7NOKIVOIIGHWM6IF4PFNMQZHAC
CQVWNL4MQYXJVKECYVKSUMRXYMQVEMYUBHHDTDMEU5GOZ67M7QOAC
QMRQL2FOIIXU3PCSH7UQIDRLKJN2DQHB5IUL4TLBVANNHWWETL6AC
MBAJPTDJ4KHWACEHWYGCFMHPQYM6FQKCSIIDKWCE765UI3VTDMIAC
function source.initialize(arg, unfiltered_arg)
function source.resize(w,h)
function source.mouse_press(x,y, mouse_button, is_touch, presses)
edit.mouse_press(Editor_state, x,y, mouse_button, is_touch, presses)
edit.mouse_press(Editor_state, x,y, mouse_button, is_touch, presses)
log_browser.mouse_press(Log_browser_state, x,y, mouse_button, is_touch, presses)
function source.mouse_release(x,y, mouse_button, is_touch, presses)
return edit.mouse_release(Editor_state, x,y, mouse_button, is_touch, presses)
return log_browser.mouse_release(Log_browser_state, x,y, mouse_button, is_touch, presses)
function source.keychord_press(chord, key, scancode, is_repeat)
return edit.keychord_press(Editor_state, chord, key, scancode, is_repeat)
return log_browser.keychord_press(Log_browser_state, chord, key, scancode, is_repeat)
return log_browser.key_release(Log_browser_state, key, scancode)
end
end
end
else
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
--? print('source keychord')
if Show_file_navigator then
end
end
else
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
if Focus == 'edit' then
end
end
elseif Show_log_browser_side and Log_browser_state.left <= x and x < Log_browser_state.right then
--? print('click on log_browser side')
if Focus ~= 'log_browser' then
Focus = 'log_browser'
return
end
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
--? print(("Window resized to width: %d and height: %d."):format(w, h))
App.screen.width, App.screen.height = w, h
Text.redraw_all(Editor_state)
Editor_state.selection1 = {} -- no support for shift drag while we're resizing
if Show_log_browser_side then
Editor_state.right = App.screen.width/2 - Margin_right
else
Editor_state.right = App.screen.width-Margin_right
end
Log_browser_state.left = App.screen.width/2 + Margin_right
Log_browser_state.right = App.screen.width-Margin_right
Editor_state.width = Editor_state.right-Editor_state.left
Text.tweak_screen_top_and_cursor(Editor_state, Editor_state.left, Editor_state.right)
--? print('end resize')
end
log_new('source')
function run.initialize(arg, unfiltered_arg)
function run.resize(w,h)
function run.mouse_press(x,y, mouse_button, is_touch, presses)
return edit.mouse_press(Editor_state, x,y, mouse_button, is_touch, presses)
function run.mouse_release(x,y, mouse_button, is_touch, presses)
return edit.mouse_release(Editor_state, x,y, mouse_button, is_touch, presses)
function run.keychord_press(chord, key, scancode, is_repeat)
return edit.keychord_press(Editor_state, chord, key, scancode, is_repeat)
end
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
end
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
end
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
--? print(("Window resized to width: %d and height: %d."):format(w, h))
App.screen.width, App.screen.height = w, h
Text.redraw_all(Editor_state)
Editor_state.selection1 = {} -- no support for shift drag while we're resizing
Editor_state.right = App.screen.width-Margin_right
Editor_state.width = Editor_state.right-Editor_state.left
Text.tweak_screen_top_and_cursor(Editor_state, Editor_state.left, Editor_state.right)
end
log_new('run')
function log_browser.mouse_press(State, x,y, mouse_button, is_touch, presses)
function log_browser.mouse_release(State, x,y, mouse_button, is_touch, presses)
function log_browser.keychord_press(State, chord, key, scancode, is_repeat)
-- move
if chord == 'up' then
end
function log_browser.mouse_wheel_move(State, dx,dy)
if dy > 0 then
for i=1,math.floor(dy) do
log_browser.up(State)
end
elseif dy < 0 then
for i=1,math.floor(-dy) do
log_browser.down(State)
end
end
local line_index = log_browser.line_index(State, x,y)
if line_index == nil then
-- below lower margin
return
end
-- leave some space to click without focusing
local line = State.lines[line_index]
local xleft = log_browser.left_margin(State, line)
local xright = log_browser.right_margin(State, line)
if x < xleft or x > xright then
return
end
-- if it's a section begin/end and the section is collapsed, expand it
-- TODO: how to collapse?
if line.section_begin or line.section_end then
-- HACK: get section reference from next/previous line
local new_section
if line.section_begin then
if line_index < #State.lines then
local next_section_stack = State.lines[line_index+1].section_stack
if next_section_stack then
new_section = next_section_stack[#next_section_stack]
end
end
elseif line.section_end then
if line_index > 1 then
local previous_section_stack = State.lines[line_index-1].section_stack
if previous_section_stack then
new_section = previous_section_stack[#previous_section_stack]
end
end
end
if new_section and new_section.expanded == nil then
new_section.expanded = true
return
end
end
-- open appropriate file in source side
if line.filename ~= Editor_state.filename then
source.switch_to_file(line.filename)
end
-- set cursor
record_undo_event(State, {before=before, after=snapshot(State, drawing_index)})
schedule_save(State)
end
elseif chord == 'escape' and not App.mouse_down(1) then
for _,line in ipairs(State.lines) do
if line.mode == 'drawing' then
line.show_help = false
end
end
if chord == 'return' then
State.current_drawing_mode = State.previous_drawing_mode
State.previous_drawing_mode = nil
else
local before = snapshot(State, State.lines.current_drawing_index)
local drawing = State.lines.current_drawing
local p = drawing.points[drawing.pending.target_point]
if chord == 'escape' then
p.name = nil
record_undo_event(State, {before=before, after=snapshot(State, State.lines.current_drawing_index)})
elseif chord == 'backspace' then
local len = utf8.len(p.name)
function App.initialize(arg, unfiltered_arg)
run.initialize(arg, unfiltered_arg)
source.initialize(arg, unfiltered_arg)
if pong.keychord_press then pong.keychord_press(chord, key, scancode, is_repeat) end
function App.mousepressed(x,y, mouse_button, is_touch, presses)
if current_app_is_warning() then return end
if run.mouse_press then run.mouse_press(x,y, mouse_button, is_touch, presses) end
if source.mouse_press then source.mouse_press(x,y, mouse_button, is_touch, presses) end
if run.mouse_release then run.mouse_release(x,y, mouse_button, is_touch, presses) end
function App.mousereleased(x,y, mouse_button, is_touch, presses)
else
assert(false, 'unknown app "'..Current_app..'"')
end
end
if source.mouse_move then source.mouse_move(x,y, dx,dy, is_touch) end
elseif Current_app == 'source' then
if run.mouse_move then run.mouse_move(x,y, dx,dy, is_touch) end
if Current_app == 'run' then
else
assert(false, 'unknown app "'..Current_app..'"')
end
end
if source.mouse_release then source.mouse_release(x,y, mouse_button, is_touch, presses) end
elseif Current_app == 'source' then
--? 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
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))
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
Text.text_input(State, t)
end
schedule_save(State)
end
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
end
if State.search_term then
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
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 == '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
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)
schedule_save(State)
end
elseif chord == 'C-y' then
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)
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
if s then
App.set_clipboard(s)
end
schedule_save(State)
elseif chord == 'C-v' then
-- 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
record_undo_event(State, {before=before, after=snapshot(State, before_line, State.cursor1.line)})
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,
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
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