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 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
function 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 = true
end,
lines = function(self)
assert(self.opened)
return App.filesystem['foo']:gmatch('[^\n]+')
end,
close = function(self)
self.opened = false
end,
}
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')
end
function 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 yet
Editor_state.lines = load_array{'abc', 'def'}
Editor_state.filename = 'foo'
schedule_save(Editor_state)
-- now drag a new file bar from the filesystem
App.filesystem['bar'] = 'abc\ndef\nghi\n'
local fake_dropped_file = {
opened = false,
getFilename = function(self)
return 'bar'
end,
open = function(self)
self.opened = true
end,
lines = function(self)
assert(self.opened)
return App.filesystem['bar']:gmatch('[^\n]+')
end,
close = function(self)
self.opened = false
end,
}
App.filedropped(fake_dropped_file)
-- filesystem now contains a file called foo
check_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.love
Cache = {}
Display_settings = {
mode=nil,
y=0,
x=0,
column_width=400,
palette='',
palette_text=App.newText(love.graphics.getFont(), ''),
}
-- display settings that are constants
Font_height = 20
Line_height = math.floor(Font_height*1.3)
if love.filesystem.getInfo('config') then
load_settings()
else
initialize_default_settings()
end
initialize_window_geometry()
love.graphics.setFont(love.graphics.newFont(Font_height))
if #arg > 0 then
Editor_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) do
if line.mode == 'text' then
Editor_state.cursor1.line = i
break
end
end
else
Editor_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' then
for i,line in ipairs(Editor_state.lines) do
if line.mode == 'text' then
Editor_state.cursor1.line = i
break
end
end
end
end
assert(#arg == 0)
-- initialize surface with unique editor objects for each pane
local 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 dimensions
App.screen.width, App.screen.height, App.screen.flags = love.window.getMode()
-- set up desired window dimensions
love.window.setPosition(settings.x, settings.y, settings.displayindex)
App.screen.flags.resizable = true
App.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.height
love.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.filename
Editor_state.screen_top1 = settings.screen_top
Editor_state.cursor1 = settings.cursor
end
function initialize_default_settings()
local font_height = 20
love.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_height
Editor_state.line_height = math.floor(font_height*1.3)
Editor_state.em = em
end
end
function App.resize(w, h)
--? 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)
Last_resize_time = App.getTime()
function App.filedropped(file)
-- first make sure to save edits on any existing file
if Editor_state.next_save then
save_to_disk(Editor_state.lines, Editor_state.filename)
end
-- clear the slate for the new file
App.initialize_globals() -- in particular, forget all undo history
Editor_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_vertical
for _,line in ipairs(file.data) do
line.start_relative_y = y
Editor_state.cursor1.line = i
break
-- semi-permanently initialize some cached stuff until the next edit
Text.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
-- nothing
else
print(line.mode)
assert(false)
function App.draw()
Button_handlers = {}
edit.draw(Editor_state)
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)
function line_height(line, left, right)
if line.mode == 'text' then
return Line_height*#line.screen_line_starting_pos
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
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))
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 = id
result.lines = Cache[id].data
result.font_height = Font_height
result.line_height = Line_height
result.em = App.newText(love.graphics.getFont(), 'm')
result.show_cursor = false
return result
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}
function App.draw()
Button_handlers = {}
-- top > Margin_top or Screen_top.line > 1
local x = Gutter_width + Padding_horizontal + Margin_left
--? print('draw')
for _, column in ipairs(Surface) do
local y = Margin_top
if 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) do
if 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.y
pane.left = x - Display_settings.x
pane.right = pane.left + Display_settings.column_width
pane.width = pane.right - pane.left
-- TODO: update pane.screen_top1 and pane.cursor1
edit.draw(pane)
end
y = y + Cache[pane.id].height
end
end
x = 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 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 = {}
function overlap(lo1,hi1, lo2,hi2)
-- lo2 hi2
-- | |
-- | |
-- | |
if lo1 < lo2 and hi1 > lo2 then
return true
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
--? 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