J3ER7DFO2TXYUMJAXZUFEHQNLFDNIXSYDTE7HEFGQ2RYB3A6RFPAC ELIVOJ4NG3XKL4X2D3WYUPAPPZPDT7QL6F55Y4ZZYNBX4WORU6ZAC DEYG7Q3WJE7EP4Z6TEVCLFSR7ERL5AIO2JKPSGYGEZKOFVJVDFAQC X3F7ECSLGXCH6NBSDIH7LY47I4EG2RR5VFPEMM6ZVDYQIGFID4HQC J2SVGR2EQEROXDDMYZOCELD2VDYQALGZYRSZ4WGMTACAGMRPJ7UAC BULPIBEGL7TMK6CVIE7IS7WGAHGOSUJBGJSFQK542MOWGHP2ADQQC CE4LZV4TNXJT54CVGM3QANCBP42TMLMZWF2DBSMUYKAHILXIZEMQC V7LATJC7BMSIZWVQKQXPS5ZYL24FDBMGPX54GV6FL2KNWIB5UTHQC CNCYMM6ABOXCRI2IP5A4T2OGBO5FQ7GWBXBP2OQYL4YET5BLJCGQC LF7BWEG4DKQI7NMXMZC4LC2BE5PB42HK5PD6OYBNIDMAZBJASOKQC UHB4GARJI5AB5UCDCZRFSCJNXGJSLU5DYGUGX5ITYEXI7Q43Z4CAC BPWFKBXTKIRBJFWVZIUVCHGJTLBCR6EIMEHM3D3KOF5IULXCR5RQC YT5P6TO64XSMCZGTT4SVNFOWUN5ECNXTWCMFXN3YCDZUNH4H3IFAC PTDO2SOTXEI6FROZ2AVRFXSKKNKCRMPPTQSI5LWD45UVGDJPMSGQC 2L5MEZV344TOZLVY3432RHJFIRVXFD6O3GWLL5O4CV66BGAFTURQC OTIBCAUJ3KDQJLVDN3A536DLZGNRYMGJLORZVR3WLCGXGO6UGO6AC UH4YWHW5NDKNR7RS664UG4PRJNZIPNWAD5JWBEUB22JHOY2SWZKAC AD34IX2ZSGYGU3LGY2IZOZNKD4HRQOYJVG5UWMWLXJZJSM62FFOAC 4BX4GJEWW7Z5LA4SJUXADYLAHOYFL4IBOYH4J4DJYRAVKKGGFHGQC YGCT2D2ORMLTBHANLGHZV3EBGGHD7ZK55UAM7HF2AVSHDXAAKK5QC R6GUSTBY5ZHR7E46DSIDQDNZDJI6QMZQDC7RPQMQWLGWQKXU6HVQC XSLCFVFHBXYPJDGOFULVB7UAWQY5CRDY4QKKHDXSZTSVLCHDL54QC W4UVZETRKOSWDPLAM5LGAPCQEJWIVFCXUJDVZQASEIKALYEU34KAC UYRAO73Y4LMTBBSH5RGNSNR532NFLRU5N5CJW3VIG72GZZXC654AC 2KRK3OBVPHQIDGCH2FBTP2AXKPEXS3OEPLBKU7UWCLKQA4MANGSAC O6T3TPXDUSZKH2JHNHWIMSEV3UADIHHF26IYA44X3RCRXNUXEKBQC BJ5X5O4ACBBJ56LRBBSTCW6IBQP4HAEOOOPNH3SKTA4F66YTOIDAC AVQ5MC5DWNLI6LUUIPGBLGP4LKRPGWBY4THNY25OBT2FAVHC6MCAC WQOSZSUESLH4YRMW3PIWGSEC7RS243324PBROJP2KPRFJ3NFSEZQC HPVT467W763S6XQWS5Q47BAK4GMVY57LDXS7LSTFM23Y5XGKZMMQC MHOUX5JFGBFYMOULX3NZA2JXH6PF2227DT54EEXLBUZQFO7NDI2AC 5MR22SGZE5YDU5CAIY53GNJDA6HSWBPYPD6M3FRQ5ZUMCSKTYJRAC NEXUNNCF5PJC57XAMQGMSSYNI7MJ4ARWDY3HFGVYMGWG3MPHG7CQC AVTNUQYRBW7IX2YQ3KDLVQ23RGW3BAKTAE7P73ASBYNKOHMQMH5AC 46ASCE5K5QRO6BZNJPW4CJZCRVVG76S3GENIBGNGB352CP3DLDCQC AJB4LFRBMIRBEDWJ3OW7GQIMD2BZBVQ62GH4TE2FISWZKSAHRF4QC RF5ALVNYB2FMU7LRRD5LMQC7P6OO4BX3NXIGWNZTQ2CD62RBRRFAC QCPXQ2E3USF3Z6R6WJ2JKHTRMPKA6QWXFKKRMLXA3MXABJEL543AC K464QQR4FTXFUMHFWAGOD5DJ6YHUBUKRHLXF2ORE74DVT7TVQ35QC GK47BBCYVEZ3OEQ7ISE2WCJULAFZ35WC6EYJ5CTBYNM26RSAELOQC PX7DDEMOBGPVK3FXKK5XEPG24CJXZSVW67DLG2JZZ5E77NVEAA3AC WOXIYUTL4NU7ACHQYXEXJDSXCRDLQ2X457KO6C7GEXFQZ43F3L7QC Z4KNS42NJZTQKUQZ7B5NYU2U4VOCUQCBFT2D7423MAXKF7NQ5ZJAC 73OCE2MCBJJZZMN2KYPJTBOUCKBZAOQ2QIAMTGCNOOJ2AJAXFT2AC MGOQ5XAVFTWZPBG2O5ZTGSEKU6BRJKQZLDV6CM4737VD2FAEB5JQC 5T2E3PDVSLMZSSIIQRNKIKQVV77XQTHP473OP7XBTTMSZHIQID5AC 2RXZ3PGOTTZ6M4R372JXIKPLBQKPVBMAXNPIEO2HZDN4EMYW4GNAC LNUHQOGHIOFGJXNGA3DZLYEASLYYDGLN2I3EDZY5ANASQAHCG3YQC IZZVOCLB7KB4ZNQ35OL466MHWOK3XZMOS7ZPFLHUFQ47LJLQQQ3QC NCRKBTHCYLUWPAMXYUSA2W7CO6GOOWHN2TWRSFZ3DT3OUB3FH7WQC QU7NHFOVGFSKQ3CWG7EF2Q7GKP3Z6FHGTIDXFHHMSFL6XMUOHMEAC IYW7X3WLOPYLSNO5IQNSULUNO4XFEM24DJ2VB5HPBUKWUYFPRCGQC 4VKEE43Z7MUPNIAOCK36INVBNHRTSWRRN37TIKRPXPH3DRKGHHAQC Z4XRNDTRTGSZHNB65WNHOVUBFW4QWQABLVSK4RM3QJHGK33DMRJAC H2DPLWMVRFYTO2CQTG54FMT2LF3B6UKLXH32CUA22DNQJVP5XBNQC 3QQZ7W4EJ7G4HQM5IYWXICMAHVRGERY4X6AOC6LOV5NSZ4OBICSAC VIU2FBNVHG5FV5AJLVPMGEUO5HCLJEGZTRWNY2C5XC4AKMQZZKVAC 7M7LS7I2QT6AFZ6RVK5KK2CZ6SNJAMQIWD7MX34F7MQ3MZKH72GAC AM42E4Y6RLS7QPWBMESL6H5RPFKG5LQYM6EFNB5UYSRSUASKLISQC S7ZZA3YEKYGLBN6UC2N7WGUS43L6MX2KQQ2LBUZT4FQ7K7V5IQGQC DWZK32YDFQIUYR4LDUUFLOPJMUVJMFDP2RKW2L3QFAGUHAWPJU2AC KVHUFUFVOSY6GB4XI2QK4T4WCLIYOV3NZR67TX6AQHAQDWJMEOBQC NX3DDSCZM23ONUBXATHBM2DM3RL7YO7LDPXLI2UA6GQU2G3DKOTQC BYG5CEMVXANDTBI2ORNVMEY6K3EBRIHZHS4QBK27VONJC5537COQC AVLAYODPMKCDBUFJSTGNUXIK74V3NDCBH55DBBFTNVBMFY6I7BCAC EF6MFB46IJA3TMTGY6DNPFB46RETYX6L2JGX2P567T2XFY47MB3AC JCSLDGAH2F6AIY4Z6XM6K4LOMW7EFY3E4NF5YXLMHLTYTX3A4Z3QC 6DE7RBZ6RHNEICJ7EUMCTROK43LW4LYINULIF2QEQOKCXWLUYUXAC FYS7TCDWKNRNOJSGRD2JMU4B2LHX5S63ZISM7YF7KZYEYLVCIKIAC EMHRPJ3RAVIVJEQIRXIVDGENV6QHUUGXXRWTJ3BXC7SZNC66VK5QC CIQN2MDEMWAASJAHOHMUZTI5PF4JV5SZSOBYYDCIIFYO2VHWULKAC HRWN5V6J6VMXS7WNSRGI7WMUSZ2OI52JJ4IK352VVSDZI4EF5HHQC JRLBUB6LR2JIAKVQNKF3T4BDICUIJ3HEMRRHX56YP5M5SP7ZS3WAC VG75U7IM2ZQTGM2QETDT6QQ4CSLQPB4APK436POAAQJWOMINPIJAC 6LJZN727CRPYR34LV75CQF55YZI3E7MGESYZSFSYAE73SNEZE3FAC 4KC7I3E2DIKLIP7LQRKB5WFA2Z5XZXAU46RFHNFQU5BVEJPDX6UQC function test_resize_window()io.write('\ntest_resize_window')App.screen.init{width=300, height=300}Editor_state = edit.initialize_test_state()Editor_state.filename = 'foo'check_eq(App.screen.width, 300, 'F - test_resize_window/baseline/width')check_eq(App.screen.height, 300, 'F - test_resize_window/baseline/height')check_eq(Editor_state.left, Test_margin_left, 'F - test_resize_window/baseline/left_margin')App.resize(200, 400)check_eq(App.screen.width, 200, 'F - test_resize_window/width')check_eq(App.screen.height, 400, 'F - test_resize_window/height')check_eq(Editor_state.left, Test_margin_left, 'F - test_resize_window/left_margin')-- ugly; right margin switches from 0 after resizecheck_eq(Editor_state.right, 200-Margin_right, 'F - test_resize_window/right_margin')check_eq(Editor_state.width, 200-Test_margin_left-Margin_right, 'F - test_resize_window/drawing_width')-- TODO: how to make assertions about when App.update got past the early exit?endfunction test_drop_file()io.write('\ntest_drop_file')App.screen.init{width=Editor_state.left+300, height=300}App.filesystem['foo'] = 'abc\ndef\nghi\n'local fake_dropped_file = {opened = false,getFilename = function(self)return 'foo'end,open = function(self)self.opened = trueend,lines = function(self)assert(self.opened)return App.filesystem['foo']:gmatch('[^\n]+')end,close = function(self)self.opened = falseend,}App.filedropped(fake_dropped_file)check_eq(#Editor_state.lines, 3, 'F - test_drop_file/#lines')check_eq(Editor_state.lines[1].data, 'abc', 'F - test_drop_file/lines:1')check_eq(Editor_state.lines[2].data, 'def', 'F - test_drop_file/lines:2')check_eq(Editor_state.lines[3].data, 'ghi', 'F - test_drop_file/lines:3')endfunction test_drop_file_saves_previous()io.write('\ntest_drop_file_saves_previous')App.screen.init{width=Editor_state.left+300, height=300}-- initially editing a file called foo that hasn't been saved to filesystem yetEditor_state.lines = load_array{'abc', 'def'}Editor_state.filename = 'foo'schedule_save(Editor_state)-- now drag a new file bar from the filesystemApp.filesystem['bar'] = 'abc\ndef\nghi\n'local fake_dropped_file = {opened = false,getFilename = function(self)return 'bar'end,open = function(self)self.opened = trueend,lines = function(self)assert(self.opened)return App.filesystem['bar']:gmatch('[^\n]+')end,close = function(self)self.opened = falseend,}App.filedropped(fake_dropped_file)-- filesystem now contains a file called foocheck_eq(App.filesystem['foo'], 'abc\ndef\n', 'F - test_drop_file_saves_previous')end
--? function test_resize_window()--? io.write('\ntest_resize_window')--? App.screen.init{width=300, height=300}--? Editor_state = edit.initialize_test_state()--? Editor_state.filename = 'foo'--? check_eq(App.screen.width, 300, 'F - test_resize_window/baseline/width')--? check_eq(App.screen.height, 300, 'F - test_resize_window/baseline/height')--? check_eq(Editor_state.left, Test_margin_left, 'F - test_resize_window/baseline/left_margin')--? App.resize(200, 400)--? check_eq(App.screen.width, 200, 'F - test_resize_window/width')--? check_eq(App.screen.height, 400, 'F - test_resize_window/height')--? check_eq(Editor_state.left, Test_margin_left, 'F - test_resize_window/left_margin')--? -- ugly; right margin switches from 0 after resize--? check_eq(Editor_state.right, 200-Margin_right, 'F - test_resize_window/right_margin')--? check_eq(Editor_state.width, 200-Test_margin_left-Margin_right, 'F - test_resize_window/drawing_width')--? -- TODO: how to make assertions about when App.update got past the early exit?--? end
-- delegate most business logic to a layer that can be reused by other projects
-- The note-taking app has a few differences with the baseline editor it's-- forked from:-- - most notes are read-only-- - the editor operates entirely in viewport-relative coordinates; 0,0 is the top-left corner of the window. However the note-taking app in read-only mode largely operates in absolute coordinates; a potentially large 2D space that the window is just a peephole into.---- We'll use the rendering logic in the editor, but only use its event loop-- when a window is being edited (there can only be one all over the entire-- surface)
-- tests currently mostly clear their own state
-- stuff we paginate over is organized as follows:-- there are multiple columns-- each column contains panes-- each pane refers to a pane id, either a file name or in-memory data and contains most editor state (not the actual contents; we don't want to duplicate that if we have duplicate panes on the surface)Surface = {}-- text to render:-- mapping from pane id to arrays of lines as in lines.loveCache = {}Display_settings = {mode=nil,y=0,x=0,column_width=400,palette='',palette_text=App.newText(love.graphics.getFont(), ''),}-- display settings that are constantsFont_height = 20Line_height = math.floor(Font_height*1.3)
if love.filesystem.getInfo('config') thenload_settings()elseinitialize_default_settings()end
initialize_window_geometry()love.graphics.setFont(love.graphics.newFont(Font_height))
if #arg > 0 thenEditor_state.filename = arg[1]Editor_state.lines = load_from_disk(Editor_state.filename)Editor_state.screen_top1 = {line=1, pos=1}Editor_state.cursor1 = {line=1, pos=1}for i,line in ipairs(Editor_state.lines) doif line.mode == 'text' thenEditor_state.cursor1.line = ibreakendendelseEditor_state.lines = load_from_disk(Editor_state.filename)if Editor_state.cursor1.line > #Editor_state.lines or Editor_state.lines[Editor_state.cursor1.line].mode ~= 'text' thenfor i,line in ipairs(Editor_state.lines) doif line.mode == 'text' thenEditor_state.cursor1.line = ibreakendendendend
assert(#arg == 0)
-- initialize surface with unique editor objects for each panelocal column = {name='recently modified'}table.insert(column, initialize_pane_with_placeholder_coordinates('foo2'))table.insert(column, initialize_pane_with_placeholder_coordinates('foo2'))table.insert(column, initialize_pane_with_placeholder_coordinates('foo'))table.insert(Surface, column)column = {name='2022/07'}table.insert(column, initialize_pane_with_placeholder_coordinates('foo'))table.insert(Surface, column)column = {name='search: foo'}table.insert(column, initialize_pane_with_placeholder_coordinates('foo2'))table.insert(column, initialize_pane_with_placeholder_coordinates('foo2'))table.insert(column, initialize_pane_with_placeholder_coordinates('foo'))table.insert(Surface, column)
function load_settings()local settings = json.decode(love.filesystem.read('config'))love.graphics.setFont(love.graphics.newFont(settings.font_height))-- maximize window to determine maximum allowable dimensionsApp.screen.width, App.screen.height, App.screen.flags = love.window.getMode()-- set up desired window dimensionslove.window.setPosition(settings.x, settings.y, settings.displayindex)App.screen.flags.resizable = trueApp.screen.flags.minwidth = math.min(App.screen.width, 200)App.screen.flags.minheight = math.min(App.screen.width, 200)App.screen.width, App.screen.height = settings.width, settings.heightlove.window.setMode(App.screen.width, App.screen.height, App.screen.flags)Editor_state = edit.initialize_state(Margin_top, Margin_left, math.min(Margin_left+400, App.screen.width-Margin_right), settings.font_height, math.floor(settings.font_height*1.3))Editor_state.filename = settings.filenameEditor_state.screen_top1 = settings.screen_topEditor_state.cursor1 = settings.cursorendfunction initialize_default_settings()local font_height = 20love.graphics.setFont(love.graphics.newFont(font_height))local em = App.newText(love.graphics.getFont(), 'm')initialize_window_geometry()Editor_state = edit.initialize_state(Margin_top, Margin_left, math.min(Margin_left+400, App.screen.width-Margin_right))Editor_state.font_height = font_heightEditor_state.line_height = math.floor(font_height*1.3)Editor_state.em = emend
endfunction App.resize(w, h)--? print(("Window resized to width: %d and height: %d."):format(w, h))App.screen.width, App.screen.height = w, hText.redraw_all(Editor_state)Editor_state.selection1 = {} -- no support for shift drag while we're resizingEditor_state.right = App.screen.width-Margin_rightEditor_state.width = Editor_state.right-Editor_state.leftText.tweak_screen_top_and_cursor(Editor_state, Editor_state.left, Editor_state.right)Last_resize_time = App.getTime()
function App.filedropped(file)-- first make sure to save edits on any existing fileif Editor_state.next_save thensave_to_disk(Editor_state.lines, Editor_state.filename)end-- clear the slate for the new fileApp.initialize_globals() -- in particular, forget all undo historyEditor_state.filename = file:getFilename()file:open('r')Editor_state.lines = load_from_file(file)file:close()for i,line in ipairs(Editor_state.lines) do
function initialize_file(file)local y = Padding_verticalfor _,line in ipairs(file.data) doline.start_relative_y = y
Editor_state.cursor1.line = ibreak
-- semi-permanently initialize some cached stuff until the next editText.compute_fragments(line, 0, Display_settings.column_width)Text.populate_screen_line_starting_pos(line, 0, Display_settings.column_width)elseif line.mode == 'drawing' then-- nothingelseprint(line.mode)assert(false)
function App.draw()Button_handlers = {}edit.draw(Editor_state)endfunction App.update(dt)Cursor_time = Cursor_time + dt-- some hysteresis while resizingif Last_resize_time thenif App.getTime() - Last_resize_time < 0.1 thenreturnelseLast_resize_time = nilendendif App.mouse_x() >= Editor_state.left-Margin_left and App.mouse_x() < Editor_state.right+Margin_right thenlove.mouse.setCursor(love.mouse.getSystemCursor('arrow'))edit.update(Editor_state, dt)
function line_height(line, left, right)if line.mode == 'text' thenreturn Line_height*#line.screen_line_starting_pos
if Pan.x thenEditor_state.left = Margin_left - math.max(Pan.x-App.mouse_x(), 0)Editor_state.right = 400 + Margin_right - math.max(Pan.x-App.mouse_x(), 0)Editor_state.width = Editor_state.right - Editor_state.leftEditor_state.top = Margin_top - math.max(Pan.y-App.mouse_y(), 0)--? Display_settings.x = math.max(Pan.x-App.mouse_x(), 0)--? Display_settings.y = math.max(Pan.y-App.mouse_y(), 0)--? App.mouse_move(Pan.x-Display_settings.x, Pan.y-Display_settings.y)end
function love.quit()edit.quit(Editor_state)-- save some important settingslocal x,y,displayindex = love.window.getPosition()local filename = Editor_state.filenameif filename:sub(1,1) ~= '/' thenfilename = love.filesystem.getWorkingDirectory()..'/'..filename -- '/' should work even on Windowsendlocal settings = {x=x, y=y, displayindex=displayindex,width=App.screen.width, height=App.screen.height,font_height=Editor_state.font_height,filename=filename,screen_top=Editor_state.screen_top1, cursor=Editor_state.cursor1}love.filesystem.write('config', json.encode(settings))
function initialize_pane_with_placeholder_coordinates(id)local result = edit.initialize_state(0, 0, math.min(Display_settings.column_width, App.screen.width-Margin_right), Font_height, Line_height)result.id = idresult.lines = Cache[id].dataresult.font_height = Font_heightresult.line_height = Line_heightresult.em = App.newText(love.graphics.getFont(), 'm')result.show_cursor = falsereturn result
function App.mousepressed(x,y, mouse_button)Cursor_time = 0 -- ensure cursor is visible immediately after it movesif x >= Editor_state.left - Margin_left and x < Editor_state.right + Margin_right thenreturn edit.mouse_pressed(Editor_state, x,y, mouse_button)elsePan = {x=x, y=y}
function App.draw()Button_handlers = {}-- top > Margin_top or Screen_top.line > 1local x = Gutter_width + Padding_horizontal + Margin_left--? print('draw')for _, column in ipairs(Surface) dolocal y = Margin_topif overlap(x, x+Display_settings.column_width, Display_settings.x, Display_settings.x + App.screen.width) then--? print('draw column')for _, pane in ipairs(column) doif overlap(y, y + Cache[pane.id].height, Display_settings.y, Display_settings.y + App.screen.height) then--? print('draw pane')pane.top = y - Display_settings.ypane.left = x - Display_settings.xpane.right = pane.left + Display_settings.column_widthpane.width = pane.right - pane.left-- TODO: update pane.screen_top1 and pane.cursor1edit.draw(pane)endy = y + Cache[pane.id].heightendendx = x + Margin_right + Display_settings.column_width + Padding_horizontal + Gutter_width + Padding_horizontal + Margin_left
function App.mousereleased(x,y, mouse_button)Cursor_time = 0 -- ensure cursor is visible immediately after it movesif x >= Editor_state.left - Margin_left and x < Editor_state.right + Margin_right thenreturn edit.mouse_released(Editor_state, x,y, mouse_button)elsePan = {}
function overlap(lo1,hi1, lo2,hi2)-- lo2 hi2-- | |-- | |-- | |if lo1 < lo2 and hi1 > lo2 thenreturn true
function App.textinput(t)Cursor_time = 0 -- ensure cursor is visible immediately after it movesreturn edit.textinput(Editor_state, t)endfunction App.keychord_pressed(chord, key)Cursor_time = 0 -- ensure cursor is visible immediately after it movesreturn edit.keychord_pressed(Editor_state, chord, key)endfunction App.keyreleased(key, scancode)Cursor_time = 0 -- ensure cursor is visible immediately after it movesreturn edit.key_released(Editor_state, key, scancode)end
--? function App.update(dt)--? Cursor_time = Cursor_time + dt--? -- some hysteresis while resizing--? if Last_resize_time then--? if App.getTime() - Last_resize_time < 0.1 then--? return--? else--? Last_resize_time = nil--? end--? end--? if App.mouse_x() >= Editor_state.left-Margin_left and App.mouse_x() < Editor_state.right+Margin_right then--? love.mouse.setCursor(love.mouse.getSystemCursor('arrow'))--? edit.update(Editor_state, dt)--? else--? love.mouse.setCursor(love.mouse.getSystemCursor('hand'))--? end--? if Pan.x then--? Editor_state.left = Margin_left - math.max(Pan.x-App.mouse_x(), 0)--? Editor_state.right = 400 + Margin_right - math.max(Pan.x-App.mouse_x(), 0)--? Editor_state.width = Editor_state.right - Editor_state.left--? Editor_state.top = Margin_top - math.max(Pan.y-App.mouse_y(), 0)--? --? Display_settings.x = math.max(Pan.x-App.mouse_x(), 0)--? --? Display_settings.y = math.max(Pan.y-App.mouse_y(), 0)--? --? App.mouse_move(Pan.x-Display_settings.x, Pan.y-Display_settings.y)--? end--? end--?--? function love.quit()--? edit.quit(Editor_state)--? -- save some important settings--? local x,y,displayindex = love.window.getPosition()--? local filename = Editor_state.filename--? if filename:sub(1,1) ~= '/' then--? filename = love.filesystem.getWorkingDirectory()..'/'..filename -- '/' should work even on Windows--? end--? local settings = {--? x=x, y=y, displayindex=displayindex,--? width=App.screen.width, height=App.screen.height,--? font_height=Editor_state.font_height,--? filename=filename,--? screen_top=Editor_state.screen_top1, cursor=Editor_state.cursor1}--? love.filesystem.write('config', json.encode(settings))--? end--?--? function App.mousepressed(x,y, mouse_button)--? Cursor_time = 0 -- ensure cursor is visible immediately after it moves--? if x >= Editor_state.left - Margin_left and x < Editor_state.right + Margin_right then--? return edit.mouse_pressed(Editor_state, x,y, mouse_button)--? else--? Pan = {x=x, y=y}--? end--? end--?--? function App.mousereleased(x,y, mouse_button)--? Cursor_time = 0 -- ensure cursor is visible immediately after it moves--? if x >= Editor_state.left - Margin_left and x < Editor_state.right + Margin_right then--? return edit.mouse_released(Editor_state, x,y, mouse_button)--? else--? Pan = {}--? end--? end--?--? function App.textinput(t)--? Cursor_time = 0 -- ensure cursor is visible immediately after it moves--? return edit.textinput(Editor_state, t)--? end--?--? function App.keychord_pressed(chord, key)--? Cursor_time = 0 -- ensure cursor is visible immediately after it moves--? return edit.keychord_pressed(Editor_state, chord, key)--? end--?--? function App.keyreleased(key, scancode)--? Cursor_time = 0 -- ensure cursor is visible immediately after it moves--? return edit.key_released(Editor_state, key, scancode)--? end