7P6XKA2DCFG5YJAVTCAZMQNNDJ6K6OO5AILBBCVYV37XKZNYKHPAC
A5ZROUM4HAX4DI6PKZF4XTS362MIJ52OOQRZVYJWGIVOH5XLZREAC
N7VXEGLGP4T4BQXWBTTSWKA4BUM72DV4QVNFSETCJIF2QIG4BPHAC
BYKXF3YYVPNVVMZVEQ72TK5WQ757IJZ6TUYPCHSVEGOBF6QIV3RAC
D3FLL7SL45CAFMGVOVABX75JBG4LGL4O35R5AIF3INW7N4VUP4DAC
3QWK3GSA5KTVQJKXQ65OGZA2AORHFALLBXWP2A4MN6YDE7VV4PUAC
B4JEWKWIFIOJ7MIWNDPR3L34NIH4WORBELAWFOPAMWL32SVIJ4YQC
RQUVBX627HPVMS77HCERQGTGFNP6JXSBBAZNR2PTNT6B7LRRGQIAC
R5QXEHUIZLELJGGCZAE7ATNS3CLRJ7JFRENMGH4XXH24C5WABZDQC
KKMFQDR43ZWVCDRHQLWWX3FCWCFA3ZSXYOBRJNPHUQZR2XPKWULAC
C7CQOQ6ZDF3O66KAPNWO4QZWXGCYY6VKU3J7RUQKQTF46JTATRGAC
34QHL4KCLCWXMXY7CBWZIEMKL6BX62DACWANNPQPYUHSV2INSE2QC
OI4FPFINEROK6GNDEMOBTGSPYIULCLRGGT5W3H7VLM7VFH22GMWQC
QZ2SXLHF6G3RBSLFIOYM3AQYWLGPWRTASNNQMOQBI5ASUAYCO6UAC
MFZW24ANL4FHCSUNNY2DQFMKY7CE73F4WL2MGSSU7UKLHVXNRAEAC
D2KRR2KTJJQCVGQYIEJDLV4NKMXBSNN6WOB62BVZ67KKMBRITD7AC
GBSRQUT4QF5WCFVSTGSOU3BM6VCGPNBBG5WKDEGDGGCOUWTPEC2AC
6DYSB5DYWZJ2MRV3O7EZHMDWJFD5QN2TECWJFSRQESMKFDXSPLCAC
ME7WBLF56X2WYKJODY47NDN4FELKIL5MYRT2MXCMZZLFKHOJMC2QC
ZQZX364VYRZOGYDPN25FIQLCKIAK2DJ2U6FJUP54X72HXQAGG5VQC
NYRESFK6T6NNVQRPVQEJP37Z4XUOUDBQWWU7DMQ23Y3GX7U2A4XQC
MDGHRTIFMMWBQZPIUCPE6ZM65Z4UOEQOHYDGH6J3M7MNQ6DCMR4AC
O2PYNFUBF2IQQIJJSCSKKRRTV6WMRVYAILJXMEQWFFJ5WQJNZTNQC
LZYLOTP343AXV7GSVRCCS6Z5XNS6BXFPTCOPRL47LC6LYPT5BUWAC
IOCATA27VTQIYYFV5HZBKDKKYOSEIJ5SS742L3SUUCJXR26PXTGAC
QFTLLRGLZ2LV4CWD23WKPM4NU4HU7NX4P73BN2EFHVVQEJUXH74AC
JOPVPUSAMMU6RFVDQR4NJC4GNNUFB7GPKVH7OS5FKCYS5QZ53VLQC
QEXZHD2VPCM4TAPP7PR2K2PIR4BVES5IZWC3T6ZRNJWKWOXFILNQC
OTIBCAUJ3KDQJLVDN3A536DLZGNRYMGJLORZVR3WLCGXGO6UGO6AC
function Text.draw(State, line_index, y, startpos, startposB, hide_cursor)
if not hide_cursor and not State.search_term then
if not hide_cursor and not State.search_term then
--? if line_index == 8 then print('c', State.cursor1.line, State.cursor1.posB, line_index, pos) end
if line_index == State.cursor1.line and State.cursor1.posB == pos then
Text.draw_cursor(State, x, y)
end
end
end
return y, nil, screen_line_starting_pos
end
-- Given an array of fragments, draw the subset starting from pos to screen
-- starting from (x,y).
-- Return:
-- - whether we got to bottom of screen before end of line
-- - the final (x,y)
-- - the final pos
-- - starting pos of the final screen line drawn
function Text.draw_wrapping_line(State, line_index, x,y, startpos)
local line = State.lines[line_index]
local line_cache = State.line_cache[line_index]
--? print('== line', line_index, '^'..line.data..'$')
local screen_line_starting_pos = startpos
Text.compute_fragments(State, line_index)
local pos = 1
initialize_color()
for _, f in ipairs(line_cache.fragments) do
App.color(Text_color)
local frag, frag_text = f.data, f.text
select_color(frag)
local frag_len = utf8.len(frag)
--? print('text.draw:', frag, 'at', line_index,pos, 'after', x,y)
if pos < startpos then
-- render nothing
--? print('skipping', frag)
else
-- render fragment
local frag_width = App.width(frag_text)
if x + frag_width > State.right then
assert(x > State.left) -- no overfull lines
y = y + State.line_height
if y + State.line_height > App.screen.height then
return --[[screen filled]] true, x,y, pos, screen_line_starting_pos
end
screen_line_starting_pos = pos
x = State.left
if line_index == State.cursor1.line and State.cursor1.pos == pos then
Text.draw_cursor(State, x, y)
end
end
end
else
x = State.left
end
-- check for B side
--? if line_index == 8 then print('checking for B side') end
if line.dataB == nil then
assert(y)
assert(screen_line_starting_pos)
--? if line_index == 8 then print('return 1') end
return y, screen_line_starting_pos
end
if not State.expanded and not line.expanded then
assert(y)
assert(screen_line_starting_pos)
--? if line_index == 8 then print('return 2') end
button(State, 'expand', {x=x+AB_padding, y=y+2, w=App.width(State.em), h=State.line_height-4, color={1,1,1},
icon = function(button_params)
App.color(Fold_background_color)
love.graphics.rectangle('fill', button_params.x, button_params.y, App.width(State.em), State.line_height-4, 2,2)
end,
onpress1 = function()
line.expanded = true
end,
})
return y, screen_line_starting_pos
end
-- draw B side
--? if line_index == 8 then print('drawing B side') end
App.color(Fold_color)
local line = State.lines[line_index]
local line_cache = State.line_cache[line_index]
line_cache.starty = y
line_cache.startpos = startpos
line_cache.startposB = startposB
-- draw A side
local overflows_screen, x, pos, screen_line_starting_pos
if startpos then
overflows_screen, x, y, pos, screen_line_starting_pos = Text.draw_wrapping_line(State, line_index, State.left, y, startpos)
if overflows_screen then
return y, screen_line_starting_pos
end
if Focus == 'edit' and State.cursor1.pos then
y, State.screen_bottom1.pos, State.screen_bottom1.posB = Text.draw(State, line_index, y, startpos, startposB, hide_cursor)
y = y + State.line_height
--? print('=> y', y)
elseif line.mode == 'drawing' then
y = y+Drawing_padding_top
Drawing.draw(State, line_index, y)
y = y + Drawing.pixels(line.h, State.width) + Drawing_padding_bottom
else
print(line.mode)
assert(false)
function edit.draw(State, hide_cursor)
State.button_handlers = {}
App.color(Text_color)
log_new('source')
edit.draw(Editor_state, --[[hide cursor?]] Show_file_navigator)
if Show_log_browser_side then
-- divider
App.color(Divider_color)
love.graphics.rectangle('fill', App.screen.width/2-1,Menu_status_bar_height, 3,App.screen.height)
--
log_browser.draw(Log_browser_state)
end
source.draw_menu_bar()
if Show_file_navigator then
textinput_on_file_navigator(t)
return
end
if Focus == 'edit' then
return edit.textinput(Editor_state, t)
else
return log_browser.textinput(Log_browser_state, t)
end
end
function source.keychord_pressed(chord, key)
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
--? print('source keychord')
if Show_file_navigator then
keychord_pressed_on_file_navigator(chord, key)
return
end
if chord == 'C-l' then
--? print('C-l')
Show_log_browser_side = not Show_log_browser_side
if Show_log_browser_side then
end
function source.update(dt)
Cursor_time = Cursor_time + dt
if App.mouse_x() < Editor_state.right then
edit.update(Editor_state, dt)
elseif Show_log_browser_side then
log_browser.update(Log_browser_state, dt)
end
end
function source.quit()
edit.quit(Editor_state)
log_browser.quit(Log_browser_state)
-- convert any bifold files here
end
function source.convert_bifold_text(infilename, outfilename)
local contents = love.filesystem.read(infilename)
contents = contents:gsub('\u{1e}', ';')
love.filesystem.write(outfilename, contents)
end
function source.settings()
if Current_app == 'source' then
--? print('reading source window position')
love.keyboard.setTextInput(true) -- bring up keyboard on touch screen
love.keyboard.setKeyRepeat(true)
love.graphics.setBackgroundColor(1,1,1)
if Settings and Settings.source then
source.load_settings()
else
source.initialize_default_settings()
end
source.initialize_edit_side{'run.lua'}
source.initialize_log_browser_side()
Menu_status_bar_height = 5 + Editor_state.line_height + 5
Editor_state.top = Editor_state.top + Menu_status_bar_height
Log_browser_state.top = Log_browser_state.top + Menu_status_bar_height
log_render = {}
filter = '',
File_navigation.candidates = File_navigation.all_candidates -- modified with filter
}
Editor_state = {}
-- called both in tests and real run
function source.initialize_globals()
-- tests currently mostly clear their own state
Show_log_browser_side = false
Focus = 'edit'
Show_file_navigator = false
File_navigation = {
all_candidates = {
log_new('run')
love.keyboard.setTextInput(true) -- bring up keyboard on touch screen
love.keyboard.setKeyRepeat(true)
love.graphics.setBackgroundColor(1,1,1)
if Settings then
run.load_settings()
else
run.initialize_default_settings()
end
if #arg > 0 then
Editor_state.filename = arg[1]
load_from_disk(Editor_state)
Text.redraw_all(Editor_state)
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.cursor1 = {line=1, pos=1}
edit.fixup_cursor(Editor_state)
else
load_from_disk(Editor_state)
Text.redraw_all(Editor_state)
if Editor_state.cursor1.line > #Editor_state.lines or Editor_state.lines[Editor_state.cursor1.line].mode ~= 'text' then
edit.fixup_cursor(Editor_state)
end
end
love.window.setTitle('lines.love - '..Editor_state.filename)
if #arg > 1 then
print('ignoring commandline args after '..arg[1])
end
if rawget(_G, 'jit') then
jit.off()
jit.flush()
end
end
function run.load_settings()
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
local rest
line.filename, line.line_number, rest = line.data:match('%[string "([^:]*)"%]:([^:]*):%s*(.*)')
if line.filename == nil then
end
line.filename = guess_source(line.filename)
line.line_number = tonumber(line.line_number)
if line.data:sub(1,1) == '{' then
local data = json.decode(line.data)
if log_render[data.name] then
line.data = data
end
line.section_stack = table.shallowcopy(Section_stack)
elseif line.data:match('\u{250c}') then
line.section_stack = table.shallowcopy(Section_stack) -- as it is at the beginning
local section_name = line.data:match('\u{250c}%s*(.*)')
table.insert(Section_stack, {name=section_name})
line.section_begin = true
line.section_name = section_name
line.data = nil
elseif line.data:match('\u{2518}') then
local section_name = line.data:match('\u{2518}%s*(.*)')
if array.find(Section_stack, function(x) return x.name == section_name end) then
while table.remove(Section_stack).name ~= section_name do
--
end
line.section_end = true
line.section_name = section_name
line.data = nil
end
line.section_stack = table.shallowcopy(Section_stack)
else
-- string
line.section_stack = table.shallowcopy(Section_stack)
end
else
line.section_stack = {}
end
end
end
function table.shallowcopy(x)
return {unpack(x)}
end
function guess_source(filename)
local possible_source = filename:gsub('%.lua$', '%.splua')
if file_exists(possible_source) then
return possible_source
else
return filename
end
end
function log_browser.draw(State)
assert(#State.lines == #State.line_cache)
local mouse_line_index = log_browser.line_index(State, App.mouse_x(), App.mouse_y())
local y = State.top
for line_index = State.screen_top1.line,#State.lines do
App.color(Text_color)
local line = State.lines[line_index]
if y + State.line_height > App.screen.height then break end
local height = State.line_height
if should_show(line) then
local xleft = render_stack_left_margin(State, line_index, line, y)
local xright = render_stack_right_margin(State, line_index, line, y)
if line.section_name then
App.color(Section_border_color)
local section_text = to_text(line.section_name)
if line.section_begin then
local sectiony = y+Section_border_padding_vertical
love.graphics.line(xleft,sectiony, xleft,y+State.line_height)
love.graphics.line(xright,sectiony, xright,y+State.line_height)
love.graphics.line(xleft,sectiony, xleft+50-2,sectiony)
love.graphics.draw(section_text, xleft+50,y)
love.graphics.line(xleft+50+App.width(section_text)+2,sectiony, xright,sectiony)
else assert(line.section_end)
local sectiony = y+State.line_height-Section_border_padding_vertical
love.graphics.line(xleft,y, xleft,sectiony)
love.graphics.line(xright,y, xright,sectiony)
love.graphics.line(xleft,sectiony, xleft+50-2,sectiony)
love.graphics.draw(section_text, xleft+50,y)
love.graphics.line(xleft+50+App.width(section_text)+2,sectiony, xright,sectiony)
end
else
if type(line.data) == 'string' then
local old_left, old_right = State.left,State.right
State.left,State.right = xleft,xright
y = Text.draw(State, line_index, y, --[[startpos]] 1)
State.left,State.right = old_left,old_right
else
height = log_render[line.data.name](line.data, xleft, y, xright-xleft)
end
end
if App.mouse_x() > Log_browser_state.left and line_index == mouse_line_index then
App.color(Cursor_line_background_color)
love.graphics.rectangle('fill', xleft,y, xright-xleft, height)
end
y = y + height
end
end
end
function render_stack_left_margin(State, line_index, line, y)
if line.section_stack == nil then
-- assertion message
for k,v in pairs(line) do
print(k)
end
end
App.color(Section_border_color)
for i=1,#line.section_stack do
local x = State.left + (i-1)*Section_border_padding_horizontal
love.graphics.line(x,y, x,y+log_browser.height(State, line_index))
if y < 30 then
love.graphics.print(line.section_stack[i].name, x+State.font_height+5, y+5, --[[vertically]] math.pi/2)
end
if y > App.screen.height-log_browser.height(State, line_index) then
love.graphics.print(line.section_stack[i].name, x+State.font_height+5, App.screen.height-App.width(to_text(line.section_stack[i].name))-5, --[[vertically]] math.pi/2)
end
end
return log_browser.left_margin(State, line)
end
function render_stack_right_margin(State, line_index, line, y)
App.color(Section_border_color)
for i=1,#line.section_stack do
local x = State.right - (i-1)*Section_border_padding_horizontal
love.graphics.line(x,y, x,y+log_browser.height(State, line_index))
if y < 30 then
love.graphics.print(line.section_stack[i].name, x, y+5, --[[vertically]] math.pi/2)
end
if y > App.screen.height-log_browser.height(State, line_index) then
love.graphics.print(line.section_stack[i].name, x, App.screen.height-App.width(to_text(line.section_stack[i].name))-5, --[[vertically]] math.pi/2)
end
end
return log_browser.right_margin(State, line)
end
function should_show(line)
-- Show a line if every single section it's in is expanded.
for i=1,#line.section_stack do
local section = line.section_stack[i]
if not section.expanded then
return false
end
end
return true
end
function log_browser.left_margin(State, line)
return State.left + #line.section_stack*Section_border_padding_horizontal
end
function log_browser.right_margin(State, line)
return State.right - #line.section_stack*Section_border_padding_horizontal
end
function log_browser.update(State, dt)
end
function log_browser.quit(State)
end
function log_browser.mouse_pressed(State, x,y, mouse_button)
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
Editor_state.cursor1 = {line=line.line_number, pos=1, posB=nil}
-- make sure it's visible
-- TODO: handle extremely long lines
Editor_state.screen_top1.line = math.max(0, Editor_state.cursor1.line-5)
-- show cursor
Focus = 'edit'
-- expand B side
Editor_state.expanded = true
end
function log_browser.line_index(State, mx,my)
-- duplicate some logic from log_browser.draw
local y = State.top
for line_index = State.screen_top1.line,#State.lines do
local line = State.lines[line_index]
if should_show(line) then
y = y + log_browser.height(State, line_index)
if my < y then
return line_index
end
if y > App.screen.height then break end
end
end
end
function log_browser.mouse_released(State, x,y, mouse_button)
end
function log_browser.textinput(State, t)
end
function log_browser.keychord_pressed(State, chord, key)
-- move
if chord == 'up' then
while State.screen_top1.line > 1 do
State.screen_top1.line = State.screen_top1.line-1
if should_show(State.lines[State.screen_top1.line]) then
break
end
end
elseif chord == 'down' then
while State.screen_top1.line < #State.lines do
State.screen_top1.line = State.screen_top1.line+1
if should_show(State.lines[State.screen_top1.line]) then
break
end
end
elseif chord == 'pageup' then
local y = 0
while State.screen_top1.line > 1 and y < App.screen.height - 100 do
State.screen_top1.line = State.screen_top1.line - 1
if should_show(State.lines[State.screen_top1.line]) then
y = y + log_browser.height(State, State.screen_top1.line)
end
end
elseif chord == 'pagedown' then
local y = 0
while State.screen_top1.line < #State.lines and y < App.screen.height - 100 do
if should_show(State.lines[State.screen_top1.line]) then
y = y + log_browser.height(State, State.screen_top1.line)
end
State.screen_top1.line = State.screen_top1.line + 1
end
end
end
function log_browser.height(State, line_index)
local line = State.lines[line_index]
if line.data == nil then
-- section header
return State.line_height
elseif type(line.data) == 'string' then
return State.line_height
else
if line.height == nil then
--? print('nil line height! rendering off screen to calculate')
line.height = log_render[line.data.name](line.data, State.left, App.screen.height, State.right-State.left)
end
return line.height
end
end
function log_browser.keyreleased(State, key, scancode)
end
line.filename, line.line_number, rest = line.data:match('([^:]*):([^:]*):%s*(.*)')
end
if rest then
line.data = rest
local s_text = to_text(s)
local width = App.width(s_text)
App.screen.draw(s_text, Menu_cursor,5)
App.screen.draw(s_text, x,y)
x = x + width + 30
return x,y
end
function keychord_pressed_on_file_navigator(chord, key)
log(2, 'file navigator: '..chord)
end
elseif chord == 'down' then
file_navigator_down()
elseif chord == 'up' then
file_navigator_up()
end
end
function log_render.file_navigator_state(o, x,y, w)
-- duplicate structure of source.draw_file_navigator
local num_lines = source.num_lines_for_file_navigator(o.files)
local h = num_lines * Editor_state.line_height
App.color(Menu_background_color)
love.graphics.rectangle('fill', x,y, w,h)
-- compute the x,y,width of the current index (in offsets from top left)
local x2,y2 = 0,0
local width = 0
for i,filename in ipairs(o.files) do
local filename_text = to_text(filename)
width = App.width(filename_text)
if x2 + width > App.screen.width - 5 then
y2 = y2 + Editor_state.line_height
x2 = 0
end
if i == o.index then
break
end
x2 = x2 + width + 30
end
-- figure out how much of the menu to display
local menu_xmin = math.max(0, x2-w/2)
local menu_xmax = math.min(App.screen.width, x2+w/2)
-- now selectively print out entries
local x3,y3 = 0,y -- x3 is relative, y3 is absolute
local width = 0
for i,filename in ipairs(o.files) do
local filename_text = to_text(filename)
width = App.width(filename_text)
if x3 + width > App.screen.width - 5 then
y3 = y3 + Editor_state.line_height
x3 = 0
end
if i == o.index then
App.color(Menu_highlight_color)
love.graphics.rectangle('fill', x + x3-menu_xmin - 5, y3-2, width+5*2, Editor_state.line_height+2*2)
end
if x3 >= menu_xmin and x3 + width < menu_xmax then
App.color(Menu_command_color)
App.screen.draw(filename_text, x + x3-menu_xmin, y3)
end
x3 = x3 + width + 30
end
--
return h+20
end
function file_navigator_up()
local y, x, width = file_coord(File_navigation.index)
local index = file_index(y-Editor_state.line_height, x, width)
if index then
File_navigation.index = index
end
end
function file_navigator_down()
local y, x, width = file_coord(File_navigation.index)
local index = file_index(y+Editor_state.line_height, x, width)
if index then
File_navigation.index = index
end
end
function file_coord(index)
local y,x = Menu_status_bar_height, 5
for i,filename in ipairs(File_navigation.candidates) do
local width = App.width(to_text(filename))
if x + width > App.screen.width - 5 then
y = y + Editor_state.line_height
x = 5
end
if i == index then
return y, x, width
end
x = x + width + 30
end
end
function file_index(fy, fx, fwidth)
log_start('file index')
log(2, ('for %d %d %d'):format(fy, fx, fwidth))
local y,x = Menu_status_bar_height, 5
local best_guess, best_guess_x, best_guess_width
for i,filename in ipairs(File_navigation.candidates) do
local width = App.width(to_text(filename))
if x + width > App.screen.width - 5 then
y = y + Editor_state.line_height
x = 5
if y == fy then
log(2, ('%d: correct row; considering %d %s %d %d'):format(y, i, filename, x, width))
if best_guess == nil then
log(2, 'nil')
best_guess = i
best_guess_x = x
best_guess_width = width
elseif math.abs(fx + fwidth/2 - x - width/2) < math.abs(fx + fwidth/2 - best_guess_x - best_guess_width/2) then
best_guess = i
best_guess_x = x
best_guess_width = width
end
log(2, ('best guess now %d %s %d %d'):format(best_guess, File_navigation.candidates[best_guess], best_guess_x, best_guess_width))
end
x = x + width + 30
log_end('file index')
return best_guess
end
function textinput_on_file_navigator(t)
File_navigation.filter = File_navigation.filter..t
File_navigation.candidates = source.file_navigator_candidates()
end
end
end
log(2, {name='file_navigator_state', files=File_navigation.candidates, index=File_navigation.index})
if chord == 'escape' then
Show_file_navigator = false
File_navigation.index = 1
File_navigation.filter = ''
File_navigation.candidates = File_navigation.all_candidates
File_navigation.index = 1
File_navigation.filter = ''
File_navigation.candidates = File_navigation.all_candidates
elseif chord == 'backspace' then
local len = utf8.len(File_navigation.filter)
local byte_offset = Text.offset(File_navigation.filter, len)
File_navigation.filter = string.sub(File_navigation.filter, 1, byte_offset-1)
File_navigation.index = 1
File_navigation.candidates = source.file_navigator_candidates()
elseif chord == 'left' then
if File_navigation.index > 1 then
File_navigation.index = File_navigation.index-1
end
elseif chord == 'right' then
if File_navigation.index < #File_navigation.candidates then
File_navigation.index = File_navigation.index+1
elseif chord == 'return' then
local candidate = guess_source(File_navigation.candidates[File_navigation.index]..'.lua')
source.switch_to_file(candidate)
Show_file_navigator = false
local s_text = to_text(s)
local width = App.width(s_text)
if x + width > App.screen.width - 5 then
y = y + Editor_state.line_height
x = 5
end
button(Editor_state, 'menu', {x=x-5, y=y-2, w=width+5*2, h=Editor_state.line_height+2*2, color=colortable(color),
onpress1 = function()
local candidate = guess_source(s..'.lua')
source.switch_to_file(candidate)
Show_file_navigator = false
end
})
App.color(Menu_command_color)
local color = Menu_background_color
if cursor_highlight then
color = Menu_highlight_color
end
if Menu_cursor >= App.screen.width - 5 then
break
end
end
end
function draw_cursor(x, y)
-- blink every 0.5s
if math.floor(Cursor_time*2)%2 == 0 then
App.color(Cursor_color)
love.graphics.rectangle('fill', x,y, 3,Editor_state.line_height)
end
end
function source.file_navigator_candidates()
if File_navigation.filter == '' then
return File_navigation.all_candidates
end
local result = {}
for _,filename in ipairs(File_navigation.all_candidates) do
if starts_with(filename, File_navigation.filter) then
table.insert(result, filename)
end
end
return result
end
function source.num_lines_for_file_navigator(candidates)
local result = 1
local x = 5
end
return result
end
function add_file_to_menu(x,y, s, cursor_highlight)
local width = App.width(to_text(filename))
if x + width > App.screen.width - 5 then
result = result+1
x = 5 + width
else
x = x + width + 30
end
for i,filename in ipairs(candidates) do
Menu_cursor = Menu_cursor + width + 30
end
function source.draw_file_navigator()
App.color(Menu_command_color)
local filter_text = to_text(File_navigation.filter)
App.screen.draw(filter_text, 5, 5)
draw_cursor(5 + App.width(filter_text), 5)
if File_navigation.num_lines == nil then
end
App.color(Menu_background_color)
local x,y = 5, Menu_status_bar_height
love.graphics.rectangle('fill', 0,Menu_status_bar_height, App.screen.width, File_navigation.num_lines * Editor_state.line_height + --[[highlight padding]] 2)
File_navigation.num_lines = source.num_lines_for_file_navigator(File_navigation.candidates)
for i,filename in ipairs(File_navigation.candidates) do
if filename == 'source' then
App.color(Menu_border_color)
love.graphics.line(Menu_cursor-10,2, Menu_cursor-10,Menu_status_bar_height-2)
end
x,y = add_file_to_menu(x,y, filename, i == File_navigation.index)
if Menu_cursor + width > App.screen.width - 5 then
return
end
App.color(Menu_command_color)
line.filename, line.line_number, line.data = line.data:match('%[string "([^:]*)"%]:([^:]*):%s*(.*)')
local rest
line.filename, line.line_number, rest = line.data:match('%[string "([^:]*)"%]:([^:]*):%s*(.*)')
if line.filename == nil then
line.filename, line.line_number, rest = line.data:match('([^:]*):([^:]*):%s*(.*)')
end
if rest then
line.data = rest
end
for i,file in ipairs(File_navigation.candidates) do
if file == 'source' then
App.color(Menu_command_color)
local filter_text = to_text(File_navigation.filter)
App.screen.draw(filter_text, 5, 5)
draw_cursor(5 + App.width(filter_text), 5)
if File_navigation.num_lines == nil then
File_navigation.num_lines = source.num_lines_for_file_navigator(File_navigation.candidates)
end
App.color(Menu_background_color)
love.graphics.rectangle('fill', 0,Menu_status_bar_height, App.screen.width, File_navigation.num_lines * Editor_state.line_height + --[[highlight padding]] 2)
local x,y = 5, Menu_status_bar_height
for i,filename in ipairs(File_navigation.candidates) do
if filename == 'source' then
function add_file_to_menu(s, cursor_highlight)
if Text_cache[s] == nil then
Text_cache[s] = App.newText(love.graphics.getFont(), s)
function draw_cursor(x, y)
-- blink every 0.5s
if math.floor(Cursor_time*2)%2 == 0 then
App.color(Cursor_color)
love.graphics.rectangle('fill', x,y, 3,Editor_state.line_height)
local width = App.width(Text_cache[s])
if Menu_cursor + width > App.screen.width - 5 then
return
end
function source.file_navigator_candidates()
if File_navigation.filter == '' then
return File_navigation.all_candidates
end
local result = {}
for _,filename in ipairs(File_navigation.all_candidates) do
if starts_with(filename, File_navigation.filter) then
table.insert(result, filename)
end
end
return result
end
function source.num_lines_for_file_navigator(candidates)
local result = 1
local x = 5
for i,filename in ipairs(candidates) do
local width = App.width(to_text(filename))
if x + width > App.screen.width - 5 then
result = result+1
x = 5 + width
else
x = x + width + 30
end
end
return result
end
function add_file_to_menu(x,y, s, cursor_highlight)
local s_text = to_text(s)
local width = App.width(s_text)
if x + width > App.screen.width - 5 then
y = y + Editor_state.line_height
x = 5
File_navigation.index = 1
File_navigation.filter = ''
File_navigation.candidates = File_navigation.all_candidates
elseif chord == 'backspace' then
local len = utf8.len(File_navigation.filter)
local byte_offset = Text.offset(File_navigation.filter, len)
File_navigation.filter = string.sub(File_navigation.filter, 1, byte_offset-1)
File_navigation.index = 1
File_navigation.candidates = source.file_navigator_candidates()
function log_render.file_navigator_state(o, x,y, w)
-- duplicate structure of source.draw_file_navigator
local num_lines = source.num_lines_for_file_navigator(o.files)
local h = num_lines * Editor_state.line_height
App.color(Menu_background_color)
love.graphics.rectangle('fill', x,y, w,h)
-- compute the x,y,width of the current index (in offsets from top left)
local x2,y2 = 0,0
local width = 0
for i,filename in ipairs(o.files) do
local filename_text = to_text(filename)
width = App.width(filename_text)
if x2 + width > App.screen.width - 5 then
y2 = y2 + Editor_state.line_height
x2 = 0
end
if i == o.index then
break
end
x2 = x2 + width + 30
end
-- figure out how much of the menu to display
local menu_xmin = math.max(0, x2-w/2)
local menu_xmax = math.min(App.screen.width, x2+w/2)
-- now selectively print out entries
local x3,y3 = 0,y -- x3 is relative, y3 is absolute
local width = 0
for i,filename in ipairs(o.files) do
local filename_text = to_text(filename)
width = App.width(filename_text)
if x3 + width > App.screen.width - 5 then
y3 = y3 + Editor_state.line_height
x3 = 0
end
if i == o.index then
App.color(Menu_highlight_color)
love.graphics.rectangle('fill', x + x3-menu_xmin - 5, y3-2, width+5*2, Editor_state.line_height+2*2)
end
if x3 >= menu_xmin and x3 + width < menu_xmax then
App.color(Menu_command_color)
App.screen.draw(filename_text, x + x3-menu_xmin, y3)
end
x3 = x3 + width + 30
end
--
return h+20
end
function file_navigator_up()
local y, x, width = file_coord(File_navigation.index)
local index = file_index(y-Editor_state.line_height, x, width)
if index then
File_navigation.index = index
end
end
function file_navigator_down()
local y, x, width = file_coord(File_navigation.index)
local index = file_index(y+Editor_state.line_height, x, width)
if index then
File_navigation.index = index
end
end
function file_coord(index)
local y,x = Menu_status_bar_height, 5
for i,filename in ipairs(File_navigation.candidates) do
local width = App.width(to_text(filename))
if x + width > App.screen.width - 5 then
y = y + Editor_state.line_height
x = 5
end
if i == index then
return y, x, width
end
x = x + width + 30
end
end
function file_index(fy, fx, fwidth)
log_start('file index')
log(2, ('for %d %d %d'):format(fy, fx, fwidth))
local y,x = Menu_status_bar_height, 5
local best_guess, best_guess_x, best_guess_width
for i,filename in ipairs(File_navigation.candidates) do
local width = App.width(to_text(filename))
if x + width > App.screen.width - 5 then
y = y + Editor_state.line_height
x = 5
end
if y == fy then
log(2, ('%d: correct row; considering %d %s %d %d'):format(y, i, filename, x, width))
if best_guess == nil then
log(2, 'nil')
best_guess = i
best_guess_x = x
best_guess_width = width
elseif math.abs(fx + fwidth/2 - x - width/2) < math.abs(fx + fwidth/2 - best_guess_x - best_guess_width/2) then
best_guess = i
best_guess_x = x
best_guess_width = width
end
log(2, ('best guess now %d %s %d %d'):format(best_guess, File_navigation.candidates[best_guess], best_guess_x, best_guess_width))
end
x = x + width + 30
end
log_end('file index')
return best_guess
end
function textinput_on_file_navigator(t)
File_navigation.filter = File_navigation.filter..t
File_navigation.candidates = source.file_navigator_candidates()
end