|
|
|
|
@@ -39,8 +39,8 @@ local screens = {}
|
|
|
|
|
-- the current stack of screens
|
|
|
|
|
local stack = {}
|
|
|
|
|
|
|
|
|
|
-- navigation listeners
|
|
|
|
|
local listeners = {}
|
|
|
|
|
-- transition listeners
|
|
|
|
|
local transition_listeners = {}
|
|
|
|
|
|
|
|
|
|
-- the number of active transitions
|
|
|
|
|
-- monarch is considered busy while there are active transitions
|
|
|
|
|
@@ -61,9 +61,15 @@ local function tohash(s)
|
|
|
|
|
return hash_lookup[s]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function notify_listeners(message_id, message)
|
|
|
|
|
log("notify_listeners()", message_id)
|
|
|
|
|
for _,url in pairs(listeners) do
|
|
|
|
|
local function pcallfn(fn, ...)
|
|
|
|
|
if fn then
|
|
|
|
|
local ok, err = pcall(fn, ...)
|
|
|
|
|
if not ok then print(err) end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local function notify_transition_listeners(message_id, message)
|
|
|
|
|
log("notify_transition_listeners()", message_id)
|
|
|
|
|
for _,url in pairs(transition_listeners) do
|
|
|
|
|
msg.post(url, message_id, message or {})
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
@@ -134,6 +140,7 @@ local function register(id, settings)
|
|
|
|
|
popup = settings and settings.popup,
|
|
|
|
|
popup_on_popup = settings and settings.popup_on_popup,
|
|
|
|
|
timestep_below_popup = settings and settings.timestep_below_popup or 1,
|
|
|
|
|
preload_listeners = {},
|
|
|
|
|
}
|
|
|
|
|
return screens[id]
|
|
|
|
|
end
|
|
|
|
|
@@ -376,11 +383,20 @@ local function reset_timestep(screen)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function disable(screen, next_screen)
|
|
|
|
|
log("disable()", screen.id)
|
|
|
|
|
local function run_coroutine(screen, cb, fn)
|
|
|
|
|
local co
|
|
|
|
|
co = coroutine.create(function()
|
|
|
|
|
screen.co = co
|
|
|
|
|
fn()
|
|
|
|
|
screen.co = nil
|
|
|
|
|
pcallfn(cb)
|
|
|
|
|
end)
|
|
|
|
|
assert(coroutine.resume(co))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function disable(screen, next_screen)
|
|
|
|
|
log("disable()", screen.id)
|
|
|
|
|
run_coroutine(screen, nil, function()
|
|
|
|
|
change_context(screen)
|
|
|
|
|
release_input(screen)
|
|
|
|
|
focus_lost(screen, next_screen)
|
|
|
|
|
@@ -389,34 +405,24 @@ local function disable(screen, next_screen)
|
|
|
|
|
else
|
|
|
|
|
reset_timestep(screen)
|
|
|
|
|
end
|
|
|
|
|
screen.co = nil
|
|
|
|
|
if cb then cb() end
|
|
|
|
|
end)
|
|
|
|
|
assert(coroutine.resume(co))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function enable(screen, previous_screen)
|
|
|
|
|
log("enable()", screen.id)
|
|
|
|
|
local co
|
|
|
|
|
co = coroutine.create(function()
|
|
|
|
|
screen.co = co
|
|
|
|
|
run_coroutine(screen, nil, function()
|
|
|
|
|
change_context(screen)
|
|
|
|
|
acquire_input(screen)
|
|
|
|
|
focus_gained(screen, previous_screen)
|
|
|
|
|
reset_timestep(screen)
|
|
|
|
|
screen.co = nil
|
|
|
|
|
if cb then cb() end
|
|
|
|
|
end)
|
|
|
|
|
assert(coroutine.resume(co))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function show_out(screen, next_screen, cb)
|
|
|
|
|
log("show_out()", screen.id)
|
|
|
|
|
local co
|
|
|
|
|
co = coroutine.create(function()
|
|
|
|
|
run_coroutine(screen, cb, function()
|
|
|
|
|
active_transition_count = active_transition_count + 1
|
|
|
|
|
notify_listeners(M.SCREEN_TRANSITION_OUT_STARTED, { screen = screen.id, next_screen = next_screen.id })
|
|
|
|
|
screen.co = co
|
|
|
|
|
notify_transition_listeners(M.SCREEN_TRANSITION_OUT_STARTED, { screen = screen.id, next_screen = next_screen.id })
|
|
|
|
|
change_context(screen)
|
|
|
|
|
release_input(screen)
|
|
|
|
|
focus_lost(screen, next_screen)
|
|
|
|
|
@@ -431,21 +437,16 @@ local function show_out(screen, next_screen, cb)
|
|
|
|
|
elseif next_is_popup then
|
|
|
|
|
change_timestep(screen)
|
|
|
|
|
end
|
|
|
|
|
screen.co = nil
|
|
|
|
|
active_transition_count = active_transition_count - 1
|
|
|
|
|
if cb then cb() end
|
|
|
|
|
notify_listeners(M.SCREEN_TRANSITION_OUT_FINISHED, { screen = screen.id, next_screen = next_screen.id })
|
|
|
|
|
notify_transition_listeners(M.SCREEN_TRANSITION_OUT_FINISHED, { screen = screen.id, next_screen = next_screen.id })
|
|
|
|
|
end)
|
|
|
|
|
coroutine.resume(co)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function show_in(screen, previous_screen, reload, add_to_stack, cb)
|
|
|
|
|
log("show_in()", screen.id)
|
|
|
|
|
local co
|
|
|
|
|
co = coroutine.create(function()
|
|
|
|
|
run_coroutine(screen, cb, function()
|
|
|
|
|
active_transition_count = active_transition_count + 1
|
|
|
|
|
notify_listeners(M.SCREEN_TRANSITION_IN_STARTED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
|
|
|
|
|
screen.co = co
|
|
|
|
|
notify_transition_listeners(M.SCREEN_TRANSITION_IN_STARTED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
|
|
|
|
|
change_context(screen)
|
|
|
|
|
if reload and screen.loaded then
|
|
|
|
|
log("show_in() reloading", screen.id)
|
|
|
|
|
@@ -459,21 +460,16 @@ local function show_in(screen, previous_screen, reload, add_to_stack, cb)
|
|
|
|
|
transition(screen, M.TRANSITION.SHOW_IN, { previous_screen = previous_screen and previous_screen.id })
|
|
|
|
|
acquire_input(screen)
|
|
|
|
|
focus_gained(screen, previous_screen)
|
|
|
|
|
screen.co = nil
|
|
|
|
|
active_transition_count = active_transition_count - 1
|
|
|
|
|
if cb then cb() end
|
|
|
|
|
notify_listeners(M.SCREEN_TRANSITION_IN_FINISHED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
|
|
|
|
|
notify_transition_listeners(M.SCREEN_TRANSITION_IN_FINISHED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
|
|
|
|
|
end)
|
|
|
|
|
coroutine.resume(co)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function back_in(screen, previous_screen, cb)
|
|
|
|
|
log("back_in()", screen.id)
|
|
|
|
|
local co
|
|
|
|
|
co = coroutine.create(function()
|
|
|
|
|
run_coroutine(screen, cb, function()
|
|
|
|
|
active_transition_count = active_transition_count + 1
|
|
|
|
|
notify_listeners(M.SCREEN_TRANSITION_IN_STARTED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
|
|
|
|
|
screen.co = co
|
|
|
|
|
notify_transition_listeners(M.SCREEN_TRANSITION_IN_STARTED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
|
|
|
|
|
change_context(screen)
|
|
|
|
|
load(screen)
|
|
|
|
|
reset_timestep(screen)
|
|
|
|
|
@@ -482,21 +478,16 @@ local function back_in(screen, previous_screen, cb)
|
|
|
|
|
end
|
|
|
|
|
acquire_input(screen)
|
|
|
|
|
focus_gained(screen, previous_screen)
|
|
|
|
|
screen.co = nil
|
|
|
|
|
active_transition_count = active_transition_count - 1
|
|
|
|
|
if cb then cb() end
|
|
|
|
|
notify_listeners(M.SCREEN_TRANSITION_IN_FINISHED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
|
|
|
|
|
notify_transition_listeners(M.SCREEN_TRANSITION_IN_FINISHED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
|
|
|
|
|
end)
|
|
|
|
|
coroutine.resume(co)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function back_out(screen, next_screen, cb)
|
|
|
|
|
log("back_out()", screen.id)
|
|
|
|
|
local co
|
|
|
|
|
co = coroutine.create(function()
|
|
|
|
|
notify_listeners(M.SCREEN_TRANSITION_OUT_STARTED, { screen = screen.id, next_screen = next_screen and next_screen.id })
|
|
|
|
|
run_coroutine(screen, cb, function()
|
|
|
|
|
notify_transition_listeners(M.SCREEN_TRANSITION_OUT_STARTED, { screen = screen.id, next_screen = next_screen and next_screen.id })
|
|
|
|
|
active_transition_count = active_transition_count + 1
|
|
|
|
|
screen.co = co
|
|
|
|
|
change_context(screen)
|
|
|
|
|
release_input(screen)
|
|
|
|
|
focus_lost(screen, next_screen)
|
|
|
|
|
@@ -505,12 +496,9 @@ local function back_out(screen, next_screen, cb)
|
|
|
|
|
end
|
|
|
|
|
transition(screen, M.TRANSITION.BACK_OUT, { next_screen = next_screen and next_screen.id })
|
|
|
|
|
unload(screen)
|
|
|
|
|
screen.co = nil
|
|
|
|
|
active_transition_count = active_transition_count - 1
|
|
|
|
|
if cb then cb() end
|
|
|
|
|
notify_listeners(M.SCREEN_TRANSITION_OUT_FINISHED, { screen = screen.id, next_screen = next_screen and next_screen.id })
|
|
|
|
|
notify_transition_listeners(M.SCREEN_TRANSITION_OUT_FINISHED, { screen = screen.id, next_screen = next_screen and next_screen.id })
|
|
|
|
|
end)
|
|
|
|
|
coroutine.resume(co)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -611,7 +599,15 @@ function M.show(id, options, data, cb)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- show screen
|
|
|
|
|
-- show screen, wait until preloaded if it is already preloading
|
|
|
|
|
-- this can typpically happen if you do a show() on app start for a
|
|
|
|
|
-- screen that has Preload set to true
|
|
|
|
|
if M.is_preloading(id) then
|
|
|
|
|
M.when_preloaded(id, function()
|
|
|
|
|
coroutine.resume(co)
|
|
|
|
|
end)
|
|
|
|
|
coroutine.yield()
|
|
|
|
|
end
|
|
|
|
|
show_in(screen, top, options and options.reload, add_to_stack, callbacks.track())
|
|
|
|
|
|
|
|
|
|
if cb then callbacks.when_done(cb) end
|
|
|
|
|
@@ -637,6 +633,7 @@ function M.hide(id, cb)
|
|
|
|
|
assert(screens[id], ("There is no screen registered with id %s"):format(tostring(id)))
|
|
|
|
|
|
|
|
|
|
local screen = screens[id]
|
|
|
|
|
log("hide()", screen.id)
|
|
|
|
|
if M.in_stack(id) then
|
|
|
|
|
if not M.is_top(id) then
|
|
|
|
|
log("hide() you can only hide the screen at the top of the stack", id)
|
|
|
|
|
@@ -646,8 +643,8 @@ function M.hide(id, cb)
|
|
|
|
|
else
|
|
|
|
|
if M.is_visible(id) then
|
|
|
|
|
back_out(screen, nil, cb)
|
|
|
|
|
elseif cb then
|
|
|
|
|
cb()
|
|
|
|
|
else
|
|
|
|
|
pcallfn(cb)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return true
|
|
|
|
|
@@ -696,6 +693,36 @@ function M.back(data, cb)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--- Check if a screen is preloading via monarch.preload() or automatically
|
|
|
|
|
-- via the Preload screen option
|
|
|
|
|
-- @param id Screen id
|
|
|
|
|
-- @return true if preloading
|
|
|
|
|
function M.is_preloading(id)
|
|
|
|
|
assert(id, "You must provide a screen id")
|
|
|
|
|
id = tohash(id)
|
|
|
|
|
assert(screens[id], ("There is no screen registered with id %s"):format(tostring(id)))
|
|
|
|
|
local screen = screens[id]
|
|
|
|
|
return screen.preloading
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--- Invoke a callback when a specific screen has been preloaded
|
|
|
|
|
-- This is mainly useful on app start when wanting to show a screen that
|
|
|
|
|
-- has the Preload flag set (since it will immediately start to load which
|
|
|
|
|
-- would prevent a call to monarch.show from having any effect).
|
|
|
|
|
function M.when_preloaded(id, cb)
|
|
|
|
|
assert(id, "You must provide a screen id")
|
|
|
|
|
id = tohash(id)
|
|
|
|
|
assert(screens[id], ("There is no screen registered with id %s"):format(tostring(id)))
|
|
|
|
|
local screen = screens[id]
|
|
|
|
|
if screen.preloaded or screen.loaded then
|
|
|
|
|
pcallfn(cb, id)
|
|
|
|
|
else
|
|
|
|
|
screen.preload_listeners[#screen.preload_listeners + 1] = cb
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--- Preload a screen. This will load but not enable and show a screen. Useful for "heavier" screens
|
|
|
|
|
-- that you wish to show without any delay.
|
|
|
|
|
-- @param id (string|hash) - Id of the screen to preload
|
|
|
|
|
@@ -713,22 +740,31 @@ function M.preload(id, cb)
|
|
|
|
|
local screen = screens[id]
|
|
|
|
|
log("preload()", screen.id)
|
|
|
|
|
if screen.preloaded or screen.loaded then
|
|
|
|
|
if cb then cb() end
|
|
|
|
|
pcallfn(cb)
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
local co
|
|
|
|
|
co = coroutine.create(function()
|
|
|
|
|
screen.co = co
|
|
|
|
|
|
|
|
|
|
local function when_preloaded()
|
|
|
|
|
-- invoke any listeners added using monarch.when_preloaded()
|
|
|
|
|
while #screen.preload_listeners > 0 do
|
|
|
|
|
pcallfn(table.remove(screen.preload_listeners), id)
|
|
|
|
|
end
|
|
|
|
|
-- invoke the normal callback
|
|
|
|
|
pcallfn(cb)
|
|
|
|
|
end
|
|
|
|
|
run_coroutine(screen, when_preloaded, function()
|
|
|
|
|
screen.preloading = true
|
|
|
|
|
change_context(screen)
|
|
|
|
|
preload(screen)
|
|
|
|
|
log("preload() done", screen.id)
|
|
|
|
|
if cb then cb() end
|
|
|
|
|
screen.preloading = false
|
|
|
|
|
end)
|
|
|
|
|
assert(coroutine.resume(co))
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--- Unload a preloaded monarch screen
|
|
|
|
|
-- @param id (string|hash) - Id of the screen to unload
|
|
|
|
|
-- @param cb (function) - Optional callback to invoke when screen is unloaded
|
|
|
|
|
function M.unload(id, cb)
|
|
|
|
|
if M.is_busy() then
|
|
|
|
|
log("unload() monarch is busy, ignoring request")
|
|
|
|
|
@@ -744,19 +780,16 @@ function M.unload(id, cb)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local screen = screens[id]
|
|
|
|
|
log("unload()", screen.id)
|
|
|
|
|
if not screen.preloaded and not screen.loaded then
|
|
|
|
|
log("unload() screen is not loaded", tostring(id))
|
|
|
|
|
if cb then cb() end
|
|
|
|
|
pcallfn(cb)
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
local co
|
|
|
|
|
co = coroutine.create(function()
|
|
|
|
|
screen.co = co
|
|
|
|
|
run_coroutine(screen, cb, function()
|
|
|
|
|
change_context(screen)
|
|
|
|
|
unload(screen)
|
|
|
|
|
if cb then cb() end
|
|
|
|
|
end)
|
|
|
|
|
assert(coroutine.resume(co))
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@@ -828,7 +861,7 @@ end
|
|
|
|
|
-- @param url The url to notify, nil for current url
|
|
|
|
|
function M.add_listener(url)
|
|
|
|
|
url = url or msg.url()
|
|
|
|
|
listeners[url_to_key(url)] = url
|
|
|
|
|
transition_listeners[url_to_key(url)] = url
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -836,7 +869,7 @@ end
|
|
|
|
|
-- @param url The url to remove, nil for current url
|
|
|
|
|
function M.remove_listener(url)
|
|
|
|
|
url = url or msg.url()
|
|
|
|
|
listeners[url_to_key(url)] = nil
|
|
|
|
|
transition_listeners[url_to_key(url)] = nil
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|