mirror of
https://github.com/britzl/monarch.git
synced 2025-11-26 19:00:53 +01:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4ca53630e | ||
|
|
9afd77e2b0 | ||
|
|
9808c09aa1 | ||
|
|
05f91dd763 | ||
|
|
b57609f061 | ||
|
|
57946d27bf | ||
|
|
1bc0ae09ee | ||
|
|
66bdde41ed | ||
|
|
35dd0c3f70 | ||
|
|
7d986ada1b |
20
README.md
20
README.md
@@ -334,8 +334,26 @@ Preload a Monarch screen. This will load but not enable the screen. This is usef
|
|||||||
* ```callback``` (function) - Optional function to call when the screen is preloaded.
|
* ```callback``` (function) - Optional function to call when the screen is preloaded.
|
||||||
|
|
||||||
|
|
||||||
|
### monarch.is_preloading(screen_id)
|
||||||
|
Check if a Monarch screen is preloading (via monarch.preload() or the Preload screen setting).
|
||||||
|
|
||||||
|
**PARAMETERS**
|
||||||
|
* ```screen_id``` (hash) - Id of the screen to check
|
||||||
|
|
||||||
|
**RETURN**
|
||||||
|
* ```preloading``` (boolean) - True if the screen is preloading.
|
||||||
|
|
||||||
|
|
||||||
|
### monarch.when_preloaded(screen_id, callback)
|
||||||
|
Invoke a callback when a screen has been preloaded.
|
||||||
|
|
||||||
|
**PARAMETERS**
|
||||||
|
* ```screen_id``` (hash) - Id of the screen to check
|
||||||
|
* ```callback``` (function) - Function to call when the screen has been preloaded.
|
||||||
|
|
||||||
|
|
||||||
### monarch.unload(screen_id, [callback])
|
### monarch.unload(screen_id, [callback])
|
||||||
Unload a preloaded Monarch screen. A preloaded screen will automatically get unloaded when hidden, but this function can be useful if a screen has been preloaded and it needs to be unloaded again.
|
Unload a preloaded Monarch screen. A preloaded screen will automatically get unloaded when hidden, but this function can be useful if a screen has been preloaded and it needs to be unloaded again without actually hiding it.
|
||||||
|
|
||||||
**PARAMETERS**
|
**PARAMETERS**
|
||||||
* ```screen_id``` (hash) - Id of the screen to unload.
|
* ```screen_id``` (hash) - Id of the screen to unload.
|
||||||
|
|||||||
@@ -281,6 +281,11 @@ embedded_instances {
|
|||||||
" type: PROPERTY_TYPE_URL\n"
|
" type: PROPERTY_TYPE_URL\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" properties {\n"
|
" properties {\n"
|
||||||
|
" id: \"focus_url\"\n"
|
||||||
|
" value: \"about:/go#about\"\n"
|
||||||
|
" type: PROPERTY_TYPE_URL\n"
|
||||||
|
" }\n"
|
||||||
|
" properties {\n"
|
||||||
" id: \"preload\"\n"
|
" id: \"preload\"\n"
|
||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[project]
|
[project]
|
||||||
title = Monarch
|
title = Monarch
|
||||||
version = 0.9
|
version = 0.9
|
||||||
dependencies = https://github.com/britzl/deftest/archive/2.4.3.zip
|
dependencies = https://github.com/britzl/deftest/archive/2.7.0.zip
|
||||||
|
|
||||||
[bootstrap]
|
[bootstrap]
|
||||||
main_collection = /test/test.collectionc
|
main_collection = /test/test.collectionc
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ local screens = {}
|
|||||||
-- the current stack of screens
|
-- the current stack of screens
|
||||||
local stack = {}
|
local stack = {}
|
||||||
|
|
||||||
-- navigation listeners
|
-- transition listeners
|
||||||
local listeners = {}
|
local transition_listeners = {}
|
||||||
|
|
||||||
-- the number of active transitions
|
-- the number of active transitions
|
||||||
-- monarch is considered busy while there are active transitions
|
-- monarch is considered busy while there are active transitions
|
||||||
@@ -61,9 +61,25 @@ local function tohash(s)
|
|||||||
return hash_lookup[s]
|
return hash_lookup[s]
|
||||||
end
|
end
|
||||||
|
|
||||||
local function notify_listeners(message_id, message)
|
local function pcallfn(fn, ...)
|
||||||
log("notify_listeners()", message_id)
|
if fn then
|
||||||
for _,url in pairs(listeners) do
|
local ok, err = pcall(fn, ...)
|
||||||
|
if not ok then print(err) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function cowait(delay)
|
||||||
|
local co = coroutine.running()
|
||||||
|
assert(co, "You must run this form within a coroutine")
|
||||||
|
timer.delay(delay, false, function()
|
||||||
|
assert(coroutine.resume(co))
|
||||||
|
end)
|
||||||
|
coroutine.yield()
|
||||||
|
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 {})
|
msg.post(url, message_id, message or {})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -134,6 +150,7 @@ local function register(id, settings)
|
|||||||
popup = settings and settings.popup,
|
popup = settings and settings.popup,
|
||||||
popup_on_popup = settings and settings.popup_on_popup,
|
popup_on_popup = settings and settings.popup_on_popup,
|
||||||
timestep_below_popup = settings and settings.timestep_below_popup or 1,
|
timestep_below_popup = settings and settings.timestep_below_popup or 1,
|
||||||
|
preload_listeners = {},
|
||||||
}
|
}
|
||||||
return screens[id]
|
return screens[id]
|
||||||
end
|
end
|
||||||
@@ -357,6 +374,11 @@ local function focus_lost(screen, next_screen)
|
|||||||
log("focus_lost()", screen.id)
|
log("focus_lost()", screen.id)
|
||||||
if screen.focus_url then
|
if screen.focus_url then
|
||||||
msg.post(screen.focus_url, M.FOCUS.LOST, { id = next_screen and next_screen.id })
|
msg.post(screen.focus_url, M.FOCUS.LOST, { id = next_screen and next_screen.id })
|
||||||
|
-- if there's no transition on the screen losing focus and it gets
|
||||||
|
-- unloaded this will happen before the focus_lost message reaches
|
||||||
|
-- the focus_url
|
||||||
|
-- we add a delay to ensure the message queue has time to be processed
|
||||||
|
cowait(0)
|
||||||
else
|
else
|
||||||
log("focus_lost() no focus url - ignoring")
|
log("focus_lost() no focus url - ignoring")
|
||||||
end
|
end
|
||||||
@@ -376,11 +398,20 @@ local function reset_timestep(screen)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function disable(screen, next_screen)
|
local function run_coroutine(screen, cb, fn)
|
||||||
log("disable()", screen.id)
|
|
||||||
local co
|
local co
|
||||||
co = coroutine.create(function()
|
co = coroutine.create(function()
|
||||||
screen.co = co
|
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)
|
change_context(screen)
|
||||||
release_input(screen)
|
release_input(screen)
|
||||||
focus_lost(screen, next_screen)
|
focus_lost(screen, next_screen)
|
||||||
@@ -389,34 +420,24 @@ local function disable(screen, next_screen)
|
|||||||
else
|
else
|
||||||
reset_timestep(screen)
|
reset_timestep(screen)
|
||||||
end
|
end
|
||||||
screen.co = nil
|
|
||||||
if cb then cb() end
|
|
||||||
end)
|
end)
|
||||||
assert(coroutine.resume(co))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function enable(screen, previous_screen)
|
local function enable(screen, previous_screen)
|
||||||
log("enable()", screen.id)
|
log("enable()", screen.id)
|
||||||
local co
|
run_coroutine(screen, nil, function()
|
||||||
co = coroutine.create(function()
|
|
||||||
screen.co = co
|
|
||||||
change_context(screen)
|
change_context(screen)
|
||||||
acquire_input(screen)
|
acquire_input(screen)
|
||||||
focus_gained(screen, previous_screen)
|
focus_gained(screen, previous_screen)
|
||||||
reset_timestep(screen)
|
reset_timestep(screen)
|
||||||
screen.co = nil
|
|
||||||
if cb then cb() end
|
|
||||||
end)
|
end)
|
||||||
assert(coroutine.resume(co))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function show_out(screen, next_screen, cb)
|
local function show_out(screen, next_screen, cb)
|
||||||
log("show_out()", screen.id)
|
log("show_out()", screen.id)
|
||||||
local co
|
run_coroutine(screen, cb, function()
|
||||||
co = coroutine.create(function()
|
|
||||||
active_transition_count = active_transition_count + 1
|
active_transition_count = active_transition_count + 1
|
||||||
notify_listeners(M.SCREEN_TRANSITION_OUT_STARTED, { screen = screen.id, next_screen = next_screen.id })
|
notify_transition_listeners(M.SCREEN_TRANSITION_OUT_STARTED, { screen = screen.id, next_screen = next_screen.id })
|
||||||
screen.co = co
|
|
||||||
change_context(screen)
|
change_context(screen)
|
||||||
release_input(screen)
|
release_input(screen)
|
||||||
focus_lost(screen, next_screen)
|
focus_lost(screen, next_screen)
|
||||||
@@ -431,25 +452,25 @@ local function show_out(screen, next_screen, cb)
|
|||||||
elseif next_is_popup then
|
elseif next_is_popup then
|
||||||
change_timestep(screen)
|
change_timestep(screen)
|
||||||
end
|
end
|
||||||
screen.co = nil
|
|
||||||
active_transition_count = active_transition_count - 1
|
active_transition_count = active_transition_count - 1
|
||||||
if cb then cb() end
|
notify_transition_listeners(M.SCREEN_TRANSITION_OUT_FINISHED, { screen = screen.id, next_screen = next_screen.id })
|
||||||
notify_listeners(M.SCREEN_TRANSITION_OUT_FINISHED, { screen = screen.id, next_screen = next_screen.id })
|
|
||||||
end)
|
end)
|
||||||
coroutine.resume(co)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function show_in(screen, previous_screen, reload, add_to_stack, cb)
|
local function show_in(screen, previous_screen, reload, add_to_stack, cb)
|
||||||
log("show_in()", screen.id)
|
log("show_in()", screen.id)
|
||||||
local co
|
run_coroutine(screen, cb, function()
|
||||||
co = coroutine.create(function()
|
|
||||||
active_transition_count = active_transition_count + 1
|
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 })
|
notify_transition_listeners(M.SCREEN_TRANSITION_IN_STARTED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
|
||||||
screen.co = co
|
|
||||||
change_context(screen)
|
change_context(screen)
|
||||||
if reload and screen.loaded then
|
if reload and screen.loaded then
|
||||||
log("show_in() reloading", screen.id)
|
log("show_in() reloading", screen.id)
|
||||||
unload(screen, reload)
|
unload(screen, reload)
|
||||||
|
-- we need to wait here in case the unloaded screen contained any screens
|
||||||
|
-- if this is the case we need to let these sub-screens have their final()
|
||||||
|
-- functions called so that they have time to call unregister()
|
||||||
|
cowait(0)
|
||||||
|
cowait(0)
|
||||||
end
|
end
|
||||||
load(screen)
|
load(screen)
|
||||||
if add_to_stack then
|
if add_to_stack then
|
||||||
@@ -459,21 +480,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 })
|
transition(screen, M.TRANSITION.SHOW_IN, { previous_screen = previous_screen and previous_screen.id })
|
||||||
acquire_input(screen)
|
acquire_input(screen)
|
||||||
focus_gained(screen, previous_screen)
|
focus_gained(screen, previous_screen)
|
||||||
screen.co = nil
|
|
||||||
active_transition_count = active_transition_count - 1
|
active_transition_count = active_transition_count - 1
|
||||||
if cb then cb() end
|
notify_transition_listeners(M.SCREEN_TRANSITION_IN_FINISHED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
|
||||||
notify_listeners(M.SCREEN_TRANSITION_IN_FINISHED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
|
|
||||||
end)
|
end)
|
||||||
coroutine.resume(co)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function back_in(screen, previous_screen, cb)
|
local function back_in(screen, previous_screen, cb)
|
||||||
log("back_in()", screen.id)
|
log("back_in()", screen.id)
|
||||||
local co
|
run_coroutine(screen, cb, function()
|
||||||
co = coroutine.create(function()
|
|
||||||
active_transition_count = active_transition_count + 1
|
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 })
|
notify_transition_listeners(M.SCREEN_TRANSITION_IN_STARTED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
|
||||||
screen.co = co
|
|
||||||
change_context(screen)
|
change_context(screen)
|
||||||
load(screen)
|
load(screen)
|
||||||
reset_timestep(screen)
|
reset_timestep(screen)
|
||||||
@@ -482,21 +498,16 @@ local function back_in(screen, previous_screen, cb)
|
|||||||
end
|
end
|
||||||
acquire_input(screen)
|
acquire_input(screen)
|
||||||
focus_gained(screen, previous_screen)
|
focus_gained(screen, previous_screen)
|
||||||
screen.co = nil
|
|
||||||
active_transition_count = active_transition_count - 1
|
active_transition_count = active_transition_count - 1
|
||||||
if cb then cb() end
|
notify_transition_listeners(M.SCREEN_TRANSITION_IN_FINISHED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
|
||||||
notify_listeners(M.SCREEN_TRANSITION_IN_FINISHED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
|
|
||||||
end)
|
end)
|
||||||
coroutine.resume(co)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function back_out(screen, next_screen, cb)
|
local function back_out(screen, next_screen, cb)
|
||||||
log("back_out()", screen.id)
|
log("back_out()", screen.id)
|
||||||
local co
|
run_coroutine(screen, cb, function()
|
||||||
co = coroutine.create(function()
|
notify_transition_listeners(M.SCREEN_TRANSITION_OUT_STARTED, { screen = screen.id, next_screen = next_screen and next_screen.id })
|
||||||
notify_listeners(M.SCREEN_TRANSITION_OUT_STARTED, { screen = screen.id, next_screen = next_screen and next_screen.id })
|
|
||||||
active_transition_count = active_transition_count + 1
|
active_transition_count = active_transition_count + 1
|
||||||
screen.co = co
|
|
||||||
change_context(screen)
|
change_context(screen)
|
||||||
release_input(screen)
|
release_input(screen)
|
||||||
focus_lost(screen, next_screen)
|
focus_lost(screen, next_screen)
|
||||||
@@ -505,12 +516,9 @@ local function back_out(screen, next_screen, cb)
|
|||||||
end
|
end
|
||||||
transition(screen, M.TRANSITION.BACK_OUT, { next_screen = next_screen and next_screen.id })
|
transition(screen, M.TRANSITION.BACK_OUT, { next_screen = next_screen and next_screen.id })
|
||||||
unload(screen)
|
unload(screen)
|
||||||
screen.co = nil
|
|
||||||
active_transition_count = active_transition_count - 1
|
active_transition_count = active_transition_count - 1
|
||||||
if cb then cb() end
|
notify_transition_listeners(M.SCREEN_TRANSITION_OUT_FINISHED, { screen = screen.id, next_screen = next_screen and next_screen.id })
|
||||||
notify_listeners(M.SCREEN_TRANSITION_OUT_FINISHED, { screen = screen.id, next_screen = next_screen and next_screen.id })
|
|
||||||
end)
|
end)
|
||||||
coroutine.resume(co)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -570,13 +578,13 @@ function M.show(id, options, data, cb)
|
|||||||
|
|
||||||
local co
|
local co
|
||||||
co = coroutine.create(function()
|
co = coroutine.create(function()
|
||||||
|
local top = stack[#stack]
|
||||||
-- a screen can ignore the stack by setting the no_stack to true
|
-- a screen can ignore the stack by setting the no_stack to true
|
||||||
local add_to_stack = not options or not options.no_stack
|
local add_to_stack = not options or not options.no_stack
|
||||||
if add_to_stack then
|
if add_to_stack then
|
||||||
-- manipulate the current top
|
-- manipulate the current top
|
||||||
-- close popup(s) if needed
|
-- close popup(s) if needed
|
||||||
-- transition out
|
-- transition out
|
||||||
local top = stack[#stack]
|
|
||||||
if top then
|
if top then
|
||||||
-- keep top popup visible if new screen can be shown on top of a popup
|
-- keep top popup visible if new screen can be shown on top of a popup
|
||||||
if top.popup and screen.popup_on_popup then
|
if top.popup and screen.popup_on_popup then
|
||||||
@@ -586,7 +594,7 @@ function M.show(id, options, data, cb)
|
|||||||
while top.popup do
|
while top.popup do
|
||||||
stack[#stack] = nil
|
stack[#stack] = nil
|
||||||
show_out(top, screen, function()
|
show_out(top, screen, function()
|
||||||
coroutine.resume(co)
|
assert(coroutine.resume(co))
|
||||||
end)
|
end)
|
||||||
coroutine.yield()
|
coroutine.yield()
|
||||||
top = stack[#stack]
|
top = stack[#stack]
|
||||||
@@ -611,12 +619,20 @@ function M.show(id, options, data, cb)
|
|||||||
end
|
end
|
||||||
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()
|
||||||
|
assert(coroutine.resume(co))
|
||||||
|
end)
|
||||||
|
coroutine.yield()
|
||||||
|
end
|
||||||
show_in(screen, top, options and options.reload, add_to_stack, callbacks.track())
|
show_in(screen, top, options and options.reload, add_to_stack, callbacks.track())
|
||||||
|
|
||||||
if cb then callbacks.when_done(cb) end
|
if cb then callbacks.when_done(cb) end
|
||||||
end)
|
end)
|
||||||
coroutine.resume(co)
|
assert(coroutine.resume(co))
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -637,6 +653,7 @@ function M.hide(id, cb)
|
|||||||
assert(screens[id], ("There is no screen registered with id %s"):format(tostring(id)))
|
assert(screens[id], ("There is no screen registered with id %s"):format(tostring(id)))
|
||||||
|
|
||||||
local screen = screens[id]
|
local screen = screens[id]
|
||||||
|
log("hide()", screen.id)
|
||||||
if M.in_stack(id) then
|
if M.in_stack(id) then
|
||||||
if not M.is_top(id) then
|
if not M.is_top(id) then
|
||||||
log("hide() you can only hide the screen at the top of the stack", id)
|
log("hide() you can only hide the screen at the top of the stack", id)
|
||||||
@@ -646,8 +663,8 @@ function M.hide(id, cb)
|
|||||||
else
|
else
|
||||||
if M.is_visible(id) then
|
if M.is_visible(id) then
|
||||||
back_out(screen, nil, cb)
|
back_out(screen, nil, cb)
|
||||||
elseif cb then
|
else
|
||||||
cb()
|
pcallfn(cb)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
@@ -696,6 +713,36 @@ function M.back(data, cb)
|
|||||||
end
|
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
|
--- 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.
|
-- that you wish to show without any delay.
|
||||||
-- @param id (string|hash) - Id of the screen to preload
|
-- @param id (string|hash) - Id of the screen to preload
|
||||||
@@ -713,22 +760,31 @@ function M.preload(id, cb)
|
|||||||
local screen = screens[id]
|
local screen = screens[id]
|
||||||
log("preload()", screen.id)
|
log("preload()", screen.id)
|
||||||
if screen.preloaded or screen.loaded then
|
if screen.preloaded or screen.loaded then
|
||||||
if cb then cb() end
|
pcallfn(cb)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
local co
|
|
||||||
co = coroutine.create(function()
|
local function when_preloaded()
|
||||||
screen.co = co
|
-- 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)
|
change_context(screen)
|
||||||
preload(screen)
|
preload(screen)
|
||||||
log("preload() done", screen.id)
|
screen.preloading = false
|
||||||
if cb then cb() end
|
|
||||||
end)
|
end)
|
||||||
assert(coroutine.resume(co))
|
|
||||||
return true
|
return true
|
||||||
end
|
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)
|
function M.unload(id, cb)
|
||||||
if M.is_busy() then
|
if M.is_busy() then
|
||||||
log("unload() monarch is busy, ignoring request")
|
log("unload() monarch is busy, ignoring request")
|
||||||
@@ -744,19 +800,16 @@ function M.unload(id, cb)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local screen = screens[id]
|
local screen = screens[id]
|
||||||
|
log("unload()", screen.id)
|
||||||
if not screen.preloaded and not screen.loaded then
|
if not screen.preloaded and not screen.loaded then
|
||||||
log("unload() screen is not loaded", tostring(id))
|
log("unload() screen is not loaded", tostring(id))
|
||||||
if cb then cb() end
|
pcallfn(cb)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
local co
|
run_coroutine(screen, cb, function()
|
||||||
co = coroutine.create(function()
|
|
||||||
screen.co = co
|
|
||||||
change_context(screen)
|
change_context(screen)
|
||||||
unload(screen)
|
unload(screen)
|
||||||
if cb then cb() end
|
|
||||||
end)
|
end)
|
||||||
assert(coroutine.resume(co))
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -828,7 +881,7 @@ end
|
|||||||
-- @param url The url to notify, nil for current url
|
-- @param url The url to notify, nil for current url
|
||||||
function M.add_listener(url)
|
function M.add_listener(url)
|
||||||
url = url or msg.url()
|
url = url or msg.url()
|
||||||
listeners[url_to_key(url)] = url
|
transition_listeners[url_to_key(url)] = url
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -836,7 +889,7 @@ end
|
|||||||
-- @param url The url to remove, nil for current url
|
-- @param url The url to remove, nil for current url
|
||||||
function M.remove_listener(url)
|
function M.remove_listener(url)
|
||||||
url = url or msg.url()
|
url = url or msg.url()
|
||||||
listeners[url_to_key(url)] = nil
|
transition_listeners[url_to_key(url)] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,15 +7,20 @@ function M.create()
|
|||||||
local callback = nil
|
local callback = nil
|
||||||
local callback_count = 0
|
local callback_count = 0
|
||||||
|
|
||||||
|
local function invoke_if_done()
|
||||||
|
if callback_count == 0 and callback then
|
||||||
|
local ok, err = pcall(callback)
|
||||||
|
if not ok then print(err) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--- Create a callback function and track when it is done
|
--- Create a callback function and track when it is done
|
||||||
-- @return Callback function
|
-- @return Callback function
|
||||||
function instance.track()
|
function instance.track()
|
||||||
callback_count = callback_count + 1
|
callback_count = callback_count + 1
|
||||||
return function()
|
return function()
|
||||||
callback_count = callback_count - 1
|
callback_count = callback_count - 1
|
||||||
if callback_count == 0 and callback then
|
invoke_if_done()
|
||||||
callback()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -23,9 +28,7 @@ function M.create()
|
|||||||
-- @param cb Function to call when all
|
-- @param cb Function to call when all
|
||||||
function instance.when_done(cb)
|
function instance.when_done(cb)
|
||||||
callback = cb
|
callback = cb
|
||||||
if callback_count == 0 then
|
invoke_if_done()
|
||||||
callback()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|||||||
37
test/data/focus1.collection
Normal file
37
test/data/focus1.collection
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
name: "focus1"
|
||||||
|
scale_along_z: 0
|
||||||
|
embedded_instances {
|
||||||
|
id: "go"
|
||||||
|
data: "components {\n"
|
||||||
|
" id: \"focus1\"\n"
|
||||||
|
" component: \"/test/data/focus1.gui\"\n"
|
||||||
|
" position {\n"
|
||||||
|
" x: 0.0\n"
|
||||||
|
" y: 0.0\n"
|
||||||
|
" z: 0.0\n"
|
||||||
|
" }\n"
|
||||||
|
" rotation {\n"
|
||||||
|
" x: 0.0\n"
|
||||||
|
" y: 0.0\n"
|
||||||
|
" z: 0.0\n"
|
||||||
|
" w: 1.0\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n"
|
||||||
|
""
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale3 {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
131
test/data/focus1.gui
Normal file
131
test/data/focus1.gui
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
script: "/test/data/focus1.gui_script"
|
||||||
|
fonts {
|
||||||
|
name: "example"
|
||||||
|
font: "/assets/example.font"
|
||||||
|
}
|
||||||
|
background_color {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 0.0
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 320.0
|
||||||
|
y: 568.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
size {
|
||||||
|
x: 200.0
|
||||||
|
y: 100.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
type: TYPE_BOX
|
||||||
|
blend_mode: BLEND_MODE_ALPHA
|
||||||
|
texture: ""
|
||||||
|
id: "box"
|
||||||
|
xanchor: XANCHOR_NONE
|
||||||
|
yanchor: YANCHOR_NONE
|
||||||
|
pivot: PIVOT_CENTER
|
||||||
|
adjust_mode: ADJUST_MODE_FIT
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
slice9 {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 0.0
|
||||||
|
}
|
||||||
|
clipping_mode: CLIPPING_MODE_NONE
|
||||||
|
clipping_visible: true
|
||||||
|
clipping_inverted: false
|
||||||
|
alpha: 1.0
|
||||||
|
template_node_child: false
|
||||||
|
size_mode: SIZE_MODE_AUTO
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
size {
|
||||||
|
x: 200.0
|
||||||
|
y: 100.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
type: TYPE_TEXT
|
||||||
|
blend_mode: BLEND_MODE_ALPHA
|
||||||
|
text: "1"
|
||||||
|
font: "example"
|
||||||
|
id: "text"
|
||||||
|
xanchor: XANCHOR_NONE
|
||||||
|
yanchor: YANCHOR_NONE
|
||||||
|
pivot: PIVOT_CENTER
|
||||||
|
outline {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
shadow {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
adjust_mode: ADJUST_MODE_FIT
|
||||||
|
line_break: false
|
||||||
|
parent: "box"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
alpha: 1.0
|
||||||
|
outline_alpha: 1.0
|
||||||
|
shadow_alpha: 1.0
|
||||||
|
template_node_child: false
|
||||||
|
text_leading: 1.0
|
||||||
|
text_tracking: 0.0
|
||||||
|
}
|
||||||
|
material: "/builtins/materials/gui.material"
|
||||||
|
adjust_reference: ADJUST_REFERENCE_PARENT
|
||||||
|
max_nodes: 512
|
||||||
9
test/data/focus1.gui_script
Normal file
9
test/data/focus1.gui_script
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
local monarch = require "monarch.monarch"
|
||||||
|
|
||||||
|
function on_message(self, message_id, message, sender)
|
||||||
|
if message_id == monarch.FOCUS.GAINED then
|
||||||
|
_G.focus1_gained = true
|
||||||
|
elseif message_id == monarch.FOCUS.LOST then
|
||||||
|
_G.focus1_lost = true
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -96,7 +96,7 @@ nodes {
|
|||||||
}
|
}
|
||||||
type: TYPE_TEXT
|
type: TYPE_TEXT
|
||||||
blend_mode: BLEND_MODE_ALPHA
|
blend_mode: BLEND_MODE_ALPHA
|
||||||
text: "1"
|
text: "FOCUS 1"
|
||||||
font: "example"
|
font: "example"
|
||||||
id: "text"
|
id: "text"
|
||||||
xanchor: XANCHOR_NONE
|
xanchor: XANCHOR_NONE
|
||||||
|
|||||||
@@ -471,3 +471,66 @@ embedded_instances {
|
|||||||
z: 1.0
|
z: 1.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
embedded_instances {
|
||||||
|
id: "focus1"
|
||||||
|
data: "components {\n"
|
||||||
|
" id: \"screen_proxy\"\n"
|
||||||
|
" component: \"/monarch/screen_proxy.script\"\n"
|
||||||
|
" position {\n"
|
||||||
|
" x: 0.0\n"
|
||||||
|
" y: 0.0\n"
|
||||||
|
" z: 0.0\n"
|
||||||
|
" }\n"
|
||||||
|
" rotation {\n"
|
||||||
|
" x: 0.0\n"
|
||||||
|
" y: 0.0\n"
|
||||||
|
" z: 0.0\n"
|
||||||
|
" w: 1.0\n"
|
||||||
|
" }\n"
|
||||||
|
" properties {\n"
|
||||||
|
" id: \"screen_id\"\n"
|
||||||
|
" value: \"focus1\"\n"
|
||||||
|
" type: PROPERTY_TYPE_HASH\n"
|
||||||
|
" }\n"
|
||||||
|
" properties {\n"
|
||||||
|
" id: \"focus_url\"\n"
|
||||||
|
" value: \"focus1:/go#focus1\"\n"
|
||||||
|
" type: PROPERTY_TYPE_URL\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n"
|
||||||
|
"embedded_components {\n"
|
||||||
|
" id: \"collectionproxy\"\n"
|
||||||
|
" type: \"collectionproxy\"\n"
|
||||||
|
" data: \"collection: \\\"/test/data/focus1.collection\\\"\\n"
|
||||||
|
"exclude: false\\n"
|
||||||
|
"\"\n"
|
||||||
|
" position {\n"
|
||||||
|
" x: 0.0\n"
|
||||||
|
" y: 0.0\n"
|
||||||
|
" z: 0.0\n"
|
||||||
|
" }\n"
|
||||||
|
" rotation {\n"
|
||||||
|
" x: 0.0\n"
|
||||||
|
" y: 0.0\n"
|
||||||
|
" z: 0.0\n"
|
||||||
|
" w: 1.0\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n"
|
||||||
|
""
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale3 {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,5 +5,8 @@ local test_monarch = require "test.test_monarch"
|
|||||||
|
|
||||||
function init(self)
|
function init(self)
|
||||||
deftest.add(test_monarch)
|
deftest.add(test_monarch)
|
||||||
deftest.run({ coverage = { enabled = true }})
|
deftest.run({
|
||||||
|
coverage = { enabled = true },
|
||||||
|
--pattern = "preload",
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ local SCREEN1_STR = hash("screen1")
|
|||||||
local SCREEN1 = hash(SCREEN1_STR)
|
local SCREEN1 = hash(SCREEN1_STR)
|
||||||
local SCREEN2 = hash("screen2")
|
local SCREEN2 = hash("screen2")
|
||||||
local SCREEN_PRELOAD = hash("screen_preload")
|
local SCREEN_PRELOAD = hash("screen_preload")
|
||||||
|
local FOCUS1 = hash("focus1")
|
||||||
local BACKGROUND = hash("background")
|
local BACKGROUND = hash("background")
|
||||||
local POPUP1 = hash("popup1")
|
local POPUP1 = hash("popup1")
|
||||||
local POPUP2 = hash("popup2")
|
local POPUP2 = hash("popup2")
|
||||||
@@ -34,6 +35,14 @@ return function()
|
|||||||
return fn(...)
|
return fn(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function wait_until_done(fn)
|
||||||
|
local is_done = false
|
||||||
|
local function done()
|
||||||
|
is_done = true
|
||||||
|
end
|
||||||
|
fn(done)
|
||||||
|
wait_timeout(function() return is_done end)
|
||||||
|
end
|
||||||
local function wait_until_visible(screen_id)
|
local function wait_until_visible(screen_id)
|
||||||
return wait_timeout(is_visible, screen_id)
|
return wait_timeout(is_visible, screen_id)
|
||||||
end
|
end
|
||||||
@@ -302,6 +311,16 @@ return function()
|
|||||||
assert(wait_until_not_busy())
|
assert(wait_until_not_busy())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it("should be able to preload a screen and wait for it", function()
|
||||||
|
assert(not monarch.is_preloading(TRANSITION1))
|
||||||
|
monarch.preload(TRANSITION1)
|
||||||
|
assert(monarch.is_preloading(TRANSITION1))
|
||||||
|
wait_until_done(function(done)
|
||||||
|
monarch.when_preloaded(TRANSITION1, done)
|
||||||
|
end)
|
||||||
|
assert(not monarch.is_preloading(TRANSITION1))
|
||||||
|
end)
|
||||||
|
|
||||||
it("should ignore any preload calls while busy", function()
|
it("should ignore any preload calls while busy", function()
|
||||||
monarch.show(TRANSITION1)
|
monarch.show(TRANSITION1)
|
||||||
-- previously a call to preload() while also showing a screen would
|
-- previously a call to preload() while also showing a screen would
|
||||||
@@ -357,6 +376,11 @@ return function()
|
|||||||
assert(mock_msg.messages(URL1)[10].message.screen == SCREEN1)
|
assert(mock_msg.messages(URL1)[10].message.screen == SCREEN1)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it("should be able to show a screen even while it is preloading", function()
|
||||||
|
assert(monarch.is_preloading(SCREEN_PRELOAD))
|
||||||
|
monarch.show(SCREEN_PRELOAD, nil, { count = 1 })
|
||||||
|
assert(wait_until_shown(SCREEN_PRELOAD), "Screen_preload was never shown")
|
||||||
|
end)
|
||||||
|
|
||||||
it("should be able to preload a screen and always keep it loaded", function()
|
it("should be able to preload a screen and always keep it loaded", function()
|
||||||
monarch.show(SCREEN_PRELOAD, nil, { count = 1 })
|
monarch.show(SCREEN_PRELOAD, nil, { count = 1 })
|
||||||
@@ -382,5 +406,21 @@ return function()
|
|||||||
-- second time the screen gets shown it will be reloaded and increment the count
|
-- second time the screen gets shown it will be reloaded and increment the count
|
||||||
assert(monarch.data(SCREEN_PRELOAD).count == 2)
|
assert(monarch.data(SCREEN_PRELOAD).count == 2)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
it("should send focus messages", function()
|
||||||
|
_G.focus1_gained = nil
|
||||||
|
_G.focus1_lost = nil
|
||||||
|
|
||||||
|
monarch.show(SCREEN1)
|
||||||
|
assert(wait_until_shown(SCREEN1), "Screen1 was never shown")
|
||||||
|
monarch.show(FOCUS1)
|
||||||
|
assert(wait_until_shown(FOCUS1), "Screen1 was never shown")
|
||||||
|
assert(_G.focus1_gained)
|
||||||
|
monarch.show(SCREEN1)
|
||||||
|
assert(wait_until_shown(SCREEN1), "Screen1 was never shown")
|
||||||
|
assert(wait_until_hidden(FOCUS1), "Focus1 was never hidden")
|
||||||
|
assert(_G.focus1_lost)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user