MAI7M665OPEDNICH67IUYSIVLRDL4JPC5BO2EWG2JVQTXATU72MAC
O5GJ6PNNBYHH4X3DU4XOB7IDJ4QEW5KXFETIDUJESBUKJYXBSYYAC
SUPHTPXYKS4JBDPASHAYA5OBVJ45QT7ZV2HYNTF7OJYOKKS6DW5QC
JRHL6P5PWAVQCUZYUHT2ZZCMPXMDTVHYTVPWGYPJQNTEN4GYW2VAC
R5QXEHUIZLELJGGCZAE7ATNS3CLRJ7JFRENMGH4XXH24C5WABZDQC
7TQAF4BYIK75EEYCCK7VEUSZHNCWMWIA3HZGQKIILYESUZ5ZZRVQC
Y6O2RFHV5UGHFS3ZZEH5HPKN5I7SV74GEV47MTI4WGJPAINJMBZAC
DTD5IPV57A3Q7MSJ3Y7C4G6HG366WHVUWUBSWWNQZFNIS7DTSAHAC
WVKFFN6FAJVKUL6NGAOWSS33WFD63GPOPSLDQ7JT4WM27KV7H65QC
BF7TW3EKRIDYC6J2Q2J4YOBAVQF55Y3H6KGZIHNXMH4N72MR6GXQC
TBPJ5WSRM5IKQH7FTAVYTWWUBWW7G2CLT5M6FN7CMPLD6Y77YIWQC
load_definition = function(name)
if Cursor_node then
Cursor_node.show_cursor = false
end
-- just one copy per definition for now
end
Viewport.x = Definitions[name].x-30
Viewport.y = Definitions[name].y-30
Cursor_node = Definitions[name]
Cursor_node.show_cursor = true
end
if Definitions[name] == nil then
Definitions[name] = {
type='text',
data=load_from_iterator(get_definition_from_app(name):gmatch("[^\r\n]+")),
x=0, y=0,
width=600,
bg=definition_background_color(name),
}
move_candidate_to_front_of_manifest(name)
initialize_manifest_navigator = function()
Manifest_navigator.candidates = Manifest
Manifest_navigator.num_lines = num_lines_for_manifest_navigator(Manifest_navigator.candidates)
end
Manifest = {
-- list of definitions to display in command palette
-- Don't confuse this with Live.manifest, the manifest for the current (driver) app.
-- Manifest is for the client app.
}
get_manifest = function()
live.send_to_app('MANIFEST')
repeat
love.timer.sleep(0.01)
local result = {}
-- error; retry
return result
else
-- stop retrying
Manifest_navigator.reload = false
end
table.insert(result, name)
elseif name == 'fw_app' then
app_name = value
end
end
end
return result, app_name
local app_name
local manifest_version = json.decode(response)
for name, value in pairs(manifest_version) do
if not starts_with(name, 'fw_') then
if #response == 0 then
response = live.receive_from_app()
until response
local response
keychord_press_on_manifest_navigator = function(chord, key)
if chord == 'escape' then
reset_manifest_navigator()
elseif chord == 'return' then
if Manifest_navigator.for_delete then
delete_definition(Manifest_navigator.candidates[Manifest_navigator.index])
else
load_definition(Manifest_navigator.candidates[Manifest_navigator.index])
end
A()
reset_manifest_navigator()
elseif chord == 'backspace' then
local len = utf8.len(Manifest_navigator.filter)
local byte_offset = Text.offset(Manifest_navigator.filter, len)
Manifest_navigator.filter = string.sub(Manifest_navigator.filter, 1, byte_offset-1)
Manifest_navigator.index = 1
Manifest_navigator.candidates = manifest_navigator_candidates()
elseif chord == 'left' then
if Manifest_navigator.index > 1 then
Manifest_navigator.index = Manifest_navigator.index-1
end
elseif chord == 'right' then
if Manifest_navigator.index < #Manifest_navigator.candidates then
Manifest_navigator.index = Manifest_navigator.index+1
end
elseif chord == 'down' then
manifest_navigator_down()
elseif chord == 'up' then
manifest_navigator_up()
end
end
load_manifest = function()
-- preserve existing order
-- 1. add new definitions in front
for _,name in ipairs(new_manifest) do
if not array.find(Manifest, name) then
table.insert(Manifest, 1, name)
end
end
-- 2. remove missing definitions
for idx=#Manifest,1,-1 do
local name = Manifest[idx]
if not array.find(new_manifest, name) then
table.remove(Manifest, idx)
end
end
return app_name
end
-- Connect to an app and load its manifest into global Manifest.
-- Also return the name of the app we have connected to.
local new_manifest, app_name = get_manifest()
num_lines_for_manifest_navigator = function(candidates)
local result = 1
local x = 5
for i,def in ipairs(candidates) do
local width = to_hud_text(def):getWidth()
if x + width > App.screen.width - 5 then
result = result+1
x = 5
end
x = x + width + 30
end
return result
end
delete_definition = function(name)
live.send_to_app('DELETE '..name)
Manifest_navigator.reload = true
end
while true do
local response_string = live.receive_from_app()
if Load_time_error then
-- no buffer, so show error at bottom of window
Run_time_error = Load_time_error
Load_time_error = nil
break
end
if live.receive_run_time_error_from_app() then
break
end
if response_string then
break
end
love.timer.sleep(0.001)
end
add_def_to_menu = function(x,y, s, cursor_highlight)
local s_text = to_hud_text(s)
local width = s_text:getWidth()
if x + width > App.screen.width - 5 then
y = y + HUD_line_height + --[[highlight padding]] 5
x = 5
end
local color = Menu_background_color
if cursor_highlight then
color = Menu_highlight_color
end
button(HUD, 'menu', {x=x-5, y=y-2, w=width+5*2, h=HUD_line_height+2*2, color=colortable(color),
onpress1 = function()
load_definition(s)
end
})
App.color(Menu_command_color)
App.screen.draw(s_text, x,y)
x = x + width + 30
return x,y
end
manifest_navigator_candidates = function()
if Manifest_navigator.filter == '' then
return Manifest
end
local result = {}
for _,def in ipairs(Manifest) do
if starts_with(def, Manifest_navigator.filter) then
table.insert(result, def)
end
end
return result
end
manifest_index = function(fy, fx, fwidth)
local y,x = Menu_bar_height, 5
local best_guess, best_guess_x, best_guess_width
for i,definition in ipairs(Manifest_navigator.candidates) do
local width = to_hud_text(definition):getWidth()
if x + width > App.screen.width - 5 then
y = y + HUD_line_height
x = 5
end
if y == fy then
if best_guess == nil then
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
end
x = x + width + 30
end
return best_guess
end
manifest_coord = function(index)
local y,x = Menu_bar_height, 5
for i,definition in ipairs(Manifest_navigator.candidates) do
local width = to_hud_text(definition):getWidth()
if x + width > App.screen.width - 5 then
y = y + HUD_line_height
x = 5
end
if i == index then
return y, x, width
end
x = x + width + 30
end
end
manifest_navigator_down = function()
local y, x, width = manifest_coord(Manifest_navigator.index)
local index = manifest_index(y+HUD_line_height, x, width)
if index then
Manifest_navigator.index = index
end
end
text_input_on_manifest_navigator = function(t)
Manifest_navigator.filter = Manifest_navigator.filter..t
Manifest_navigator.candidates = manifest_navigator_candidates()
Manifest_navigator.index = 1
end
move_candidate_to_front_of_manifest = function(name)
local index = array.find(Manifest, name)
if index then
table.remove(Manifest, index)
table.insert(Manifest, 1, name)
end
end
manifest_navigator_up = function()
local y, x, width = manifest_coord(Manifest_navigator.index)
local index = manifest_index(y-HUD_line_height, x, width)
if index then
Manifest_navigator.index = index
end
end
Manifest_navigator = {
-- state for the command palette
display = false, -- display navigator on screen
for_delete = false, -- if true, delete selected definition from navigator
reload = false, -- if true, refresh manifest to display on next keystroke
num_lines = nil, -- number of screen lines of space to devote to the navigator
index = 1, -- where the cursor is right now. Modified on arrow keys, reset on any non-arrow keystroke.
filter = '', -- prefix being typed into the command palette
bottom_y = nil, -- cache a tiny bit of state in display logic
}
-- candidates: list of candidates matching the filter