3
0
mirror of https://github.com/britzl/monarch.git synced 2025-11-26 19:00:53 +01:00

Compare commits

..

14 Commits
3.4.0 ... 3.6.1

Author SHA1 Message Date
Björn Ritzl
286a270a1c Check if screen was loaded before trying to unload 2022-07-05 22:54:40 +02:00
Björn Ritzl
c0d45c3d5c Added keep_loaded option to preload()
Fixes #88
2022-07-04 22:43:25 +02:00
Björn Ritzl
bcc6264cd4 Remove screen from stack when unregistered
Fixes #87
2022-05-29 14:47:34 +02:00
Björn Ritzl
1dec704047 Delay reset of timestep when going back from popup
Fixes #84
2022-02-23 12:46:04 +01:00
Björn Ritzl
9764c68475 Update callback_tracker.lua
Fixes #83
2022-01-27 12:16:43 +01:00
Björn Ritzl
d57d550859 Fixed issues with missing variables 2022-01-27 12:15:52 +01:00
Björn Ritzl
17df189089 Replace existing transition
Fixes #82
2022-01-13 19:40:24 +01:00
Björn Ritzl
387a1805eb Added option to change timestep for screen when below popup
Fixes #73
2021-09-19 23:08:31 +02:00
Björn Ritzl
e26da2c6c5 Removed old change log file 2021-09-19 23:05:43 +02:00
Björn Ritzl
c75b1b0044 A screen added with no_stack should not manipulate the stack when hidden 2021-08-19 14:53:05 +02:00
Björn Ritzl
d3799a93ff Make sure that callbacks aren't invoked more than once 2021-08-19 14:35:05 +02:00
Björn Ritzl
76d4ca2927 Added monarch.clear() 2021-07-20 00:27:34 +02:00
Björn Ritzl
9e81b3a327 Removed one-frame delay during show/back-in
If the screen had a transition the delay caused the screen to render in its initial position for one frame before the transition was applied
2021-07-01 15:04:52 +02:00
Björn Ritzl
4927d6e766 Update stack earlier when showing new screen
Fixes #76
2021-06-27 00:34:32 +02:00
9 changed files with 253 additions and 133 deletions

View File

@@ -1,60 +0,0 @@
## Monarch 2.8.0 [britzl released 2018-06-10]
NEW: Prevent show/hide operations while busy showing/hiding another screen
FIX: Make sure to properly finish active transitions when layout changes
## Monarch 2.7.0 [britzl released 2018-06-04]
NEW: Added monarch.top([offset]) and monarch.bottom([offset]) to get screen id of top and bottom screens (w. optional offset)
NEW: Transition messages now contain `next_screen` or `previous_screen`
## Monarch 2.6.1 [britzl released 2018-06-04]
FIX: Check if screen has already been preloaded before trying to preload it again (the callback will still be invoked).
## Monarch 2.6.0 [britzl released 2018-06-03]
NEW: monarch.preload() to load but not show a screen. Useful for content heavy screens that you wish to show without delay.
## Monarch 2.5.0 [britzl released 2018-06-01]
NEW: Transitions will send a `transition_done` message to the creator of the transition to notify that the transition has finished. The `message` will contain which transition that was finished.
## Monarch 2.4.0 [britzl released 2018-05-26]
NEW: Screen transitions are remembered so that they can be replayed when the screen layout changes.
## Monarch 2.3.0 [britzl released 2018-03-24]
CHANGE: The functions in monarch.lua that previously only accepted a hash as screen id now also accepts strings (and does the conversion internally)
## Monarch 2.2.0 [britzl released 2018-03-19]
NEW: Transitions now handle layout changes (via `layout_changed` message)
NEW: Transitions can now be notified of changes in window size using transition.window_resize(width, height)
## Monarch 2.1 [britzl released 2017-12-27]
NEW: Added Popup on Popup flag that allows a popup to be shown on top of another popup
## Monarch 2.0 [britzl released 2017-12-08]
BREAKING CHANGE: If you are using custom screen transitions (ie your own transition functions) you need to make a change to the function. The previous function signature was ```(node, to, easing, duration, delay, url)``` where ```url``` was the URL to where the ```transition_done``` message was supposed to be posted. The new function signature for a transition function is: ```(node, to, easing, duration, delay, cb)``` where ```cb``` is a function that should be invoked when the transition is completed.
FIX: Fixed issues related to screen transitions.
FIX: Code cleanup to reduce code duplication.
FIX: Improved documentation regarding transitions.
## Monarch 1.4 [britzl released 2017-12-06]
FIX: Several bugfixes for specific corner cases.
## Monarch 1.3 [britzl released 2017-12-01]
FIX: monarch.back(data, cb) set the data on the previous screen not the new current screen.
NEW: monarch.is_top(id)
NEW: monarch.get_stack()
NEW: monarch.in_stack(id)
## Monarch 1.2 [britzl released 2017-11-28]
NEW: Message id constants exposed from the Monarch module
NEW: Focus lost/gained contains id of next/previous screen
## Monarch 1.1 [britzl released 2017-11-22]
FIX: Bugfixes for transitions and state under certain circumstances
NEW: Added 'reload' option to show() command.
## Monarch 1.0 [britzl released 2017-09-28]
First public stable release
## Monarch 0.9 [britzl released 2017-09-17]

View File

@@ -33,6 +33,13 @@ Hide a screen that has been shown using the `no_stack` option. If used on a scre
* `success` (boolean) - True if the process of hiding the screen was started successfully. * `success` (boolean) - True if the process of hiding the screen was started successfully.
## monarch.clear([callback])
Clear the stack of screens completely. Any visible screen will be hidden by navigating back out from them. This operation will be added to the queue if Monarch is busy.
**PARAMETERS**
* `callback` (function) - Optional function to call when the stack has been cleared.
## monarch.back([data], [callback]) ## monarch.back([data], [callback])
Go back to a previous Monarch screen. This operation will be added to the queue if Monarch is busy. Go back to a previous Monarch screen. This operation will be added to the queue if Monarch is busy.
@@ -41,13 +48,18 @@ Go back to a previous Monarch screen. This operation will be added to the queue
* `callback` (function) - Optional function to call when the previous screen is visible. * `callback` (function) - Optional function to call when the previous screen is visible.
## monarch.preload(screen_id, [callback]) ## monarch.preload(screen_id, [options], [callback])
Preload a Monarch screen. This will load but not enable the screen. This is useful for content heavy screens that you wish to be able to show without having to wait for it load. This operation will be added to the queue if Monarch is busy. Preload a Monarch screen. This will load but not enable the screen. This is useful for content heavy screens that you wish to be able to show without having to wait for it load. This operation will be added to the queue if Monarch is busy.
**PARAMETERS** **PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to preload. * `screen_id` (string|hash) - Id of the screen to preload.
* `options` (table)
* `callback` (function) - Optional function to call when the screen is preloaded. * `callback` (function) - Optional function to call when the screen is preloaded.
The options table can contain the following fields:
* `keep_loaded` (boolean) - If the `keep_loaded` flag is set Monarch will keep the screen preloaded even after a `hide()` or `back()` navigation event that normally would unload the screen.
## monarch.is_preloading(screen_id) ## monarch.is_preloading(screen_id)
Check if a Monarch screen is preloading (via monarch.preload() or the Preload screen setting). Check if a Monarch screen is preloading (via monarch.preload() or the Preload screen setting).
@@ -142,6 +154,14 @@ Check if a Monarch screen is visible.
* `exists` (boolean) - True if the screen is visible. * `exists` (boolean) - True if the screen is visible.
## monarch.set_timestep_below_popup(screen_id, timestep)
Set the timestep to apply for a screen when below a popup.
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to change timestep setting for
* `timestep` (number) - Timestep to apply
## monarch.add_listener([url]) ## monarch.add_listener([url])
Add a URL that will be notified of navigation events. Add a URL that will be notified of navigation events.

View File

@@ -1,7 +1,7 @@
[project] [project]
title = Monarch title = Monarch
version = 0.9 version = 0.9
dependencies = https://github.com/britzl/deftest/archive/2.7.0.zip dependencies#0 = https://github.com/britzl/deftest/archive/2.7.0.zip
[bootstrap] [bootstrap]
main_collection = /example/advanced/advanced.collectionc main_collection = /example/advanced/advanced.collectionc

View File

@@ -1,4 +1,5 @@
local callback_tracker = require "monarch.utils.callback_tracker" local callback_tracker = require "monarch.utils.callback_tracker"
local async = require "monarch.utils.async"
local M = {} local M = {}
@@ -185,7 +186,7 @@ function M.is_visible(id)
assert(id, "You must provide a screen id") assert(id, "You must provide a screen id")
id = tohash(id) id = tohash(id)
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)))
return screens[id].loaded return screens[id].visible
end end
@@ -297,6 +298,12 @@ function M.unregister(id)
log("unregister()", id) log("unregister()", id)
local screen = screens[id] local screen = screens[id]
screens[id] = nil screens[id] = nil
-- remove screen from stack
for i = #stack, 1, -1 do
if stack[i].id == id then
table.remove(stack, i)
end
end
end end
local function acquire_input(screen) local function acquire_input(screen)
@@ -347,8 +354,10 @@ local function unload(screen, force)
if screen.proxy then if screen.proxy then
log("unload() proxy", screen.id) log("unload() proxy", screen.id)
if screen.auto_preload and not force then if screen.auto_preload and not force then
if screen.loaded then
msg.post(screen.proxy, DISABLE) msg.post(screen.proxy, DISABLE)
screen.loaded = false screen.loaded = false
end
screen.preloaded = true screen.preloaded = true
else else
screen.wait_for = PROXY_UNLOADED screen.wait_for = PROXY_UNLOADED
@@ -570,6 +579,7 @@ local function show_out(screen, next_screen, wait_for_transition, cb)
local current_is_popup = screen.popup local current_is_popup = screen.popup
if (not next_is_popup and not current_is_popup) or (current_is_popup) then if (not next_is_popup and not current_is_popup) or (current_is_popup) then
transition(screen, M.TRANSITION.SHOW_OUT, { next_screen = next_screen.id }, wait_for_transition) transition(screen, M.TRANSITION.SHOW_OUT, { next_screen = next_screen.id }, wait_for_transition)
screen.visible = false
unload(screen) unload(screen)
elseif next_is_popup then elseif next_is_popup then
change_timestep(screen) change_timestep(screen)
@@ -590,21 +600,22 @@ local function show_in(screen, previous_screen, reload, add_to_stack, wait_for_t
log("show_in() reloading", screen.id) log("show_in() reloading", screen.id)
unload(screen, reload) unload(screen, reload)
end end
if add_to_stack then
stack[#stack + 1] = screen
end
local ok, err = load(screen) local ok, err = load(screen)
if not ok then if not ok then
log("show_in()", err) log("show_in()", err)
if add_to_stack then
stack[#stack] = nil
end
active_transition_count = active_transition_count - 1 active_transition_count = active_transition_count - 1
notify_transition_listeners(M.SCREEN_TRANSITION_FAILED, { screen = screen.id }) notify_transition_listeners(M.SCREEN_TRANSITION_FAILED, { screen = screen.id })
return return
end end
-- wait until screen has had a chance to render
cowait(0)
cowait(0)
if add_to_stack then
stack[#stack + 1] = screen
end
reset_timestep(screen) reset_timestep(screen)
transition(screen, M.TRANSITION.SHOW_IN, { previous_screen = previous_screen and previous_screen.id }, wait_for_transition) transition(screen, M.TRANSITION.SHOW_IN, { previous_screen = previous_screen and previous_screen.id }, wait_for_transition)
screen.visible = true
acquire_input(screen) acquire_input(screen)
focus_gained(screen, previous_screen) focus_gained(screen, previous_screen)
active_transition_count = active_transition_count - 1 active_transition_count = active_transition_count - 1
@@ -626,13 +637,11 @@ local function back_in(screen, previous_screen, wait_for_transition, cb)
notify_transition_listeners(M.SCREEN_TRANSITION_FAILED, { screen = screen.id }) notify_transition_listeners(M.SCREEN_TRANSITION_FAILED, { screen = screen.id })
return return
end end
-- wait until screen has had a chance to render
cowait(0)
cowait(0)
reset_timestep(screen) reset_timestep(screen)
if previous_screen and not previous_screen.popup then if previous_screen and not previous_screen.popup then
transition(screen, M.TRANSITION.BACK_IN, { previous_screen = previous_screen.id }, wait_for_transition) transition(screen, M.TRANSITION.BACK_IN, { previous_screen = previous_screen.id }, wait_for_transition)
end end
screen.visible = true
acquire_input(screen) acquire_input(screen)
focus_gained(screen, previous_screen) focus_gained(screen, previous_screen)
active_transition_count = active_transition_count - 1 active_transition_count = active_transition_count - 1
@@ -649,10 +658,11 @@ local function back_out(screen, next_screen, wait_for_transition, cb)
change_context(screen) change_context(screen)
release_input(screen, next_screen) release_input(screen, next_screen)
focus_lost(screen, next_screen) focus_lost(screen, next_screen)
transition(screen, M.TRANSITION.BACK_OUT, { next_screen = next_screen and next_screen.id }, wait_for_transition)
if next_screen and screen.popup then if next_screen and screen.popup then
reset_timestep(next_screen) reset_timestep(next_screen)
end end
transition(screen, M.TRANSITION.BACK_OUT, { next_screen = next_screen and next_screen.id }, wait_for_transition) screen.visible = false
unload(screen) unload(screen)
active_transition_count = active_transition_count - 1 active_transition_count = active_transition_count - 1
notify_transition_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 })
@@ -743,8 +753,9 @@ function M.show(id, options, data, cb)
pop = pop - 1 pop = pop - 1
end end
stack[#stack] = nil stack[#stack] = nil
show_out(top, screen, WAIT_FOR_TRANSITION, callbacks.track()) async(function(await, resume)
callbacks.yield_until_done() await(show_out, top, screen, WAIT_FOR_TRANSITION, resume)
end)
top = stack[#stack] top = stack[#stack]
end end
@@ -783,15 +794,16 @@ function M.show(id, options, data, cb)
local same_screen = top and top.id == screen.id local same_screen = top and top.id == screen.id
if same_screen or (options and options.sequential) then if same_screen or (options and options.sequential) then
if top then if top then
show_out(top, screen, WAIT_FOR_TRANSITION, callbacks.track()) async(function(await, resume)
callbacks.yield_until_done() await(show_out, top, screen, WAIT_FOR_TRANSITION, resume)
end)
end end
show_in(screen, top, options and options.reload, add_to_stack, WAIT_FOR_TRANSITION, callbacks.track()) show_in(screen, top, options and options.reload, add_to_stack, WAIT_FOR_TRANSITION, callbacks.track())
else else
-- show screen -- show screen
local cb = callbacks.track() local cb = callbacks.track()
show_in(screen, top, options and options.reload, add_to_stack, DO_NOT_WAIT_FOR_TRANSITION, function() show_in(screen, top, options and options.reload, add_to_stack, DO_NOT_WAIT_FOR_TRANSITION, function()
if top and not top.popup then if add_to_stack and top and not top.popup then
show_out(top, screen, WAIT_FOR_TRANSITION, callbacks.track()) show_out(top, screen, WAIT_FOR_TRANSITION, callbacks.track())
end end
cb() cb()
@@ -825,7 +837,7 @@ end
-- Hide a screen. The screen must either be at the top of the stack or -- Hide a screen. The screen must either be at the top of the stack or
-- visible but not added to the stack (through the no_stack option) -- visible but not added to the stack (through the no_stack option)
-- @param id (string|hash) - Id of the screen to show -- @param id (string|hash) - Id of the screen to .hide
-- @param cb (function) - Optional callback to invoke when the screen is hidden -- @param cb (function) - Optional callback to invoke when the screen is hidden
-- @return true if successfully hiding, false if busy or for some other reason unable to hide the screen -- @return true if successfully hiding, false if busy or for some other reason unable to hide the screen
function M.hide(id, cb) function M.hide(id, cb)
@@ -862,6 +874,34 @@ function M.hide(id, cb)
end end
-- Clear stack completely. Any visible screens will be hidden by navigating back out
-- from them.
-- @param cb (function) - Optional callback to invoke when the stack has been cleared
function M.clear(cb)
log("clear() queuing action")
queue_action(function(action_done, action_error)
async(function(await, resume)
local top = stack[#stack]
while top and top.visible do
stack[#stack] = nil
await(back_out, top, stack[#stack - 1], WAIT_FOR_TRANSITION, resume)
top = stack[#stack]
end
while stack[#stack] do
table.remove(stack)
end
pcallfn(cb)
pcallfn(action_done)
end)
end)
end
-- Go back to the previous screen in the stack. -- Go back to the previous screen in the stack.
-- @param data (*) - Optional data to set for the previous screen -- @param data (*) - Optional data to set for the previous screen
-- @param cb (function) - Optional callback to invoke when the previous screen is visible again -- @param cb (function) - Optional callback to invoke when the previous screen is visible again
@@ -870,6 +910,7 @@ function M.back(data, cb)
queue_action(function(action_done) queue_action(function(action_done)
local callbacks = callback_tracker() local callbacks = callback_tracker()
local back_cb = callbacks.track()
local screen = table.remove(stack) local screen = table.remove(stack)
if screen then if screen then
log("back()", screen.id) log("back()", screen.id)
@@ -881,18 +922,29 @@ function M.back(data, cb)
if data then if data then
top.data = data top.data = data
end end
back_in(top, screen, WAIT_FOR_TRANSITION, callbacks.track()) back_in(top, screen, WAIT_FOR_TRANSITION, back_cb)
end) end)
else else
if top then if top then
if data then if data then
top.data = data top.data = data
end end
back_in(top, screen, DO_NOT_WAIT_FOR_TRANSITION, function() -- if the screen we are backing out from is a popup and the screen we go
back_out(screen, top, WAIT_FOR_TRANSITION, callbacks.track()) -- back to is not a popup we need to let the popup completely hide before
-- we start working on the screen we go back to
-- we do this to ensure that we do not reset the times step of the screen
-- we go back to until it is no longer obscured by the popup
if screen.popup and not top.popup then
back_out(screen, top, WAIT_FOR_TRANSITION, function()
back_in(top, screen, WAIT_FOR_TRANSITION, back_cb)
end) end)
else else
back_out(screen, top, WAIT_FOR_TRANSITION, callbacks.track()) back_in(top, screen, DO_NOT_WAIT_FOR_TRANSITION, function()
back_out(screen, top, WAIT_FOR_TRANSITION, back_cb)
end)
end
else
back_out(screen, top, WAIT_FOR_TRANSITION, back_cb)
end end
end end
end end
@@ -946,12 +998,19 @@ 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
-- @param options (table)
-- @param cb (function) - Optional callback to invoke when screen is loaded -- @param cb (function) - Optional callback to invoke when screen is loaded
function M.preload(id, cb) function M.preload(id, options, cb)
assert(id, "You must provide a screen id") assert(id, "You must provide a screen id")
id = tohash(id) id = tohash(id)
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)))
-- support old function signature (id, cb)
if type(options) == "function" and not cb then
cb = options
options = nil
end
log("preload() queuing action", id) log("preload() queuing action", id)
queue_action(function(action_done, action_error) queue_action(function(action_done, action_error)
log("preload()", id) log("preload()", id)
@@ -962,6 +1021,10 @@ function M.preload(id, cb)
return return
end end
-- keep_loaded is an option for monarch.preload()
-- use it to get the same behavior as the auto preload checkbox
screen.auto_preload = screen.auto_preload or options and options.keep_loaded
if screen.preloaded or screen.loaded then if screen.preloaded or screen.loaded then
pcallfn(cb) pcallfn(cb)
pcallfn(action_done) pcallfn(action_done)
@@ -1122,6 +1185,19 @@ function M.bottom(offset)
return screen and screen.id return screen and screen.id
end end
--- Set the timestep to apply for a screen when below a popup
-- @param id (string|hash) Id of the screen to change timestep setting for
-- @param timestep (number) Timestep to apply
function M.set_timestep_below_popup(id, timestep)
assert(id, "You must provide a screen id")
assert(timestep, "You must provide a timestep")
id = tohash(id)
assert(screens[id], ("There is no screen registered with id %s"):format(tostring(id)))
screens[id].timestep_below_popup = timestep
end
local function url_to_key(url) local function url_to_key(url)
return (url.socket or hash("")) .. (url.path or hash("")) .. (url.fragment or hash("")) return (url.socket or hash("")) .. (url.path or hash("")) .. (url.fragment or hash(""))
end end

View File

@@ -121,11 +121,15 @@ local function create()
local current_transition = nil local current_transition = nil
local function create_transition(transition_id, node, fn, easing, duration, delay) local function create_transition(transition_id, node, fn, easing, duration, delay)
assert(transition_id, "You must provide a valid transition id")
assert(node, "You must provide a node")
assert(fn, "You must provide a transition function")
local t = transitions[transition_id] local t = transitions[transition_id]
-- find if there's already a transition for the node in -- find if there's already a transition for the node in
-- question and if so update it instead of creating a new -- question and if so update it instead of creating a new
-- transition -- transition
for _,transition in ipairs(t) do for _,transition in ipairs(t.transitions) do
if transition.node == node then if transition.node == node then
transition.fn = fn transition.fn = fn
transition.easing = easing transition.easing = easing

47
monarch/utils/async.lua Normal file
View File

@@ -0,0 +1,47 @@
local M = {}
local NOT_STARTED = "not_started"
local YIELDED = "yielded"
local RESUMED = "resumed"
local RUNNING = "running"
function M.async(fn)
local co = coroutine.running()
local state = NOT_STARTED
local function await(fn, ...)
state = RUNNING
fn(...)
if state ~= RUNNING then
return
end
state = YIELDED
local r = { coroutine.yield() }
return unpack(r)
end
local function resume(...)
if state ~= RUNNING then
state = RUNNING
local ok, err = coroutine.resume(co, ...)
if not ok then
print(err)
print(debug.traceback())
end
end
end
if co then
return fn(await, resume)
else
co = coroutine.create(fn)
return resume(await, resume)
end
end
return setmetatable(M, {
__call = function(t, ...)
return M.async(...)
end
})

View File

@@ -6,13 +6,18 @@ function M.create()
local callback = nil local callback = nil
local callback_count = 0 local callback_count = 0
local all_callbacks_done = false
local function is_done() local function is_done()
return callback_count == 0 return callback_count == 0
end end
local function invoke_if_done() local function invoke_if_done()
if all_callbacks_done then
print("Warning: The same callback will be invoked twice from the callback tracker!")
end
if callback_count == 0 and callback then if callback_count == 0 and callback then
all_callbacks_done = true
local ok, err = pcall(callback) local ok, err = pcall(callback)
if not ok then print(err) end if not ok then print(err) end
end end
@@ -41,26 +46,12 @@ function M.create()
invoke_if_done() invoke_if_done()
end end
function instance.yield_until_done()
local co = coroutine.running()
callback = function()
local ok, err = coroutine.resume(co)
if not ok then
print(err)
end
end
invoke_if_done()
if not is_done() then
coroutine.yield()
end
end
return instance return instance
end end
return setmetatable(M, { return setmetatable(M, {
__call = function(_, ...) __call = function(_, ...)
return M.create(...) return M.create()
end end
}) })

View File

@@ -39,7 +39,7 @@ return function()
local screens_instances = {} local screens_instances = {}
local function is_shown(screen_id) local function is_visible(screen_id)
return monarch.is_visible(screen_id) return monarch.is_visible(screen_id)
end end
@@ -65,9 +65,6 @@ return function()
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
local function wait_until_shown(screen_id)
return wait_timeout(is_shown, screen_id)
end
local function wait_until_hidden(screen_id) local function wait_until_hidden(screen_id)
return wait_timeout(is_hidden, screen_id) return wait_timeout(is_hidden, screen_id)
end end
@@ -145,23 +142,23 @@ return function()
assert(not monarch.is_visible(SCREEN1)) assert(not monarch.is_visible(SCREEN1))
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_stack({ SCREEN1 })) assert(wait_until_stack({ SCREEN1 }))
assert(monarch.is_visible(SCREEN1)) assert(wait_until_visible(SCREEN1))
monarch.show(SCREEN2) monarch.show(SCREEN2)
assert(wait_until_stack({ SCREEN1, SCREEN2 })) assert(wait_until_stack({ SCREEN1, SCREEN2 }))
assert(not monarch.is_visible(SCREEN1)) assert(wait_until_hidden(SCREEN1))
assert(monarch.is_visible(SCREEN2)) assert(wait_until_visible(SCREEN2))
monarch.show(POPUP1) monarch.show(POPUP1)
assert(wait_until_stack({ SCREEN1, SCREEN2, POPUP1 })) assert(wait_until_stack({ SCREEN1, SCREEN2, POPUP1 }))
assert(not monarch.is_visible(SCREEN1)) assert(wait_until_hidden(SCREEN1))
assert(monarch.is_visible(SCREEN2)) assert(wait_until_visible(SCREEN2))
assert(monarch.is_visible(POPUP1)) assert(wait_until_visible(POPUP1))
end) end)
it("should be able to show a screen without adding it to the stack", function() it("should be able to show a screen without adding it to the stack", function()
monarch.show(BACKGROUND, { no_stack = true }) monarch.show(BACKGROUND, { no_stack = true })
assert(wait_until_shown(BACKGROUND), "Background was never shown") assert(wait_until_visible(BACKGROUND), "Background was never shown")
assert(wait_until_stack({ })) assert(wait_until_stack({ }))
monarch.show(SCREEN1) monarch.show(SCREEN1)
@@ -175,7 +172,7 @@ return function()
monarch.show(BACKGROUND, { no_stack = true }) monarch.show(BACKGROUND, { no_stack = true })
assert(wait_until_not_busy()) assert(wait_until_not_busy())
assert(wait_until_shown(BACKGROUND)) assert(wait_until_visible(BACKGROUND))
assert(wait_until_stack({ SCREEN1 })) assert(wait_until_stack({ SCREEN1 }))
monarch.show(SCREEN2) monarch.show(SCREEN2)
@@ -184,13 +181,13 @@ return function()
monarch.back() monarch.back()
assert(wait_until_not_busy()) assert(wait_until_not_busy())
assert(wait_until_shown(SCREEN1)) assert(wait_until_visible(SCREEN1))
assert(wait_until_stack({ SCREEN1 })) assert(wait_until_stack({ SCREEN1 }))
end) end)
it("should be able to hide a screen not added to the stack", function() it("should be able to hide a screen not added to the stack", function()
monarch.show(BACKGROUND, { no_stack = true }) monarch.show(BACKGROUND, { no_stack = true })
assert(wait_until_shown(BACKGROUND), "Background was never shown") assert(wait_until_visible(BACKGROUND), "Background was never shown")
assert_stack({ }) assert_stack({ })
monarch.hide(BACKGROUND) monarch.hide(BACKGROUND)
@@ -213,18 +210,18 @@ return function()
it("should be able to pass data to a screen when showing it or going back to it", function() it("should be able to pass data to a screen when showing it or going back to it", function()
local data1 = { foo = "bar" } local data1 = { foo = "bar" }
monarch.show(SCREEN1, nil, data1) monarch.show(SCREEN1, nil, data1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_visible(SCREEN1), "Screen1 was never shown")
local data2 = { boo = "car" } local data2 = { boo = "car" }
monarch.show(SCREEN2, nil, data2) monarch.show(SCREEN2, nil, data2)
assert(wait_until_shown(SCREEN2), "Screen2 was never shown") assert(wait_until_visible(SCREEN2), "Screen2 was never shown")
assert(monarch.data(SCREEN1) == data1, "Expected data on screen1 doesn't match actual data") assert(monarch.data(SCREEN1) == data1, "Expected data on screen1 doesn't match actual data")
assert(monarch.data(SCREEN2) == data2, "Expected data on screen2 doesn't match actual data") assert(monarch.data(SCREEN2) == data2, "Expected data on screen2 doesn't match actual data")
local data_back = { going = "back" } local data_back = { going = "back" }
monarch.back(data_back) monarch.back(data_back)
assert(wait_until_shown(SCREEN1)) assert(wait_until_visible(SCREEN1))
assert(monarch.data(SCREEN1) == data_back, "Expected data on screen1 doesn't match actual data") assert(monarch.data(SCREEN1) == data_back, "Expected data on screen1 doesn't match actual data")
end) end)
@@ -368,7 +365,7 @@ return function()
it("should be busy while transition is running", function() it("should be busy while transition is running", function()
monarch.show(TRANSITION1) monarch.show(TRANSITION1)
assert(monarch.is_busy()) assert(monarch.is_busy())
assert(wait_until_shown(TRANSITION1), "Transition1 was never shown") assert(wait_until_visible(TRANSITION1), "Transition1 was never shown")
assert(wait_until_not_busy()) assert(wait_until_not_busy())
end) end)
@@ -381,12 +378,26 @@ return function()
assert(not monarch.is_preloading(TRANSITION1)) assert(not monarch.is_preloading(TRANSITION1))
end) end)
it("should be able to preload a screen and keep it loaded", function()
assert(not monarch.is_preloading(TRANSITION1))
monarch.preload(TRANSITION1, { keep_loaded = true })
wait_until_done(function(done)
monarch.when_preloaded(TRANSITION1, done)
end)
monarch.show(TRANSITION1)
assert(wait_until_visible(TRANSITION1), "Transition1 was never shown")
monarch.back()
assert(wait_until_hidden(TRANSITION1), "Transition1 was never hidden")
assert(monarch.is_preloaded(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
-- lock up monarch. See issue #32 -- lock up monarch. See issue #32
monarch.preload(TRANSITION1) monarch.preload(TRANSITION1)
assert(wait_until_shown(TRANSITION1), "Transition1 was never shown") assert(wait_until_visible(TRANSITION1), "Transition1 was never shown")
end) end)
it("should be able to notify listeners of navigation events", function() it("should be able to notify listeners of navigation events", function()
@@ -438,12 +449,12 @@ return function()
it("should be able to show a screen even while it is preloading", function() it("should be able to show a screen even while it is preloading", function()
monarch.show(SCREEN_PRELOAD, nil, { count = 1 }) monarch.show(SCREEN_PRELOAD, nil, { count = 1 })
assert(wait_until_shown(SCREEN_PRELOAD), "Screen_preload was never shown") assert(wait_until_visible(SCREEN_PRELOAD), "Screen_preload was never shown")
end) 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) monarch.show(SCREEN_PRELOAD)
assert(wait_until_shown(SCREEN_PRELOAD), "Screen_preload was never shown") assert(wait_until_visible(SCREEN_PRELOAD), "Screen_preload was never shown")
monarch.back() monarch.back()
assert(wait_until_hidden(SCREEN_PRELOAD), "Screen_preload was never hidden") assert(wait_until_hidden(SCREEN_PRELOAD), "Screen_preload was never hidden")
assert(monarch.is_preloaded(SCREEN_PRELOAD)) assert(monarch.is_preloaded(SCREEN_PRELOAD))
@@ -451,12 +462,12 @@ return function()
it("should be able to reload a preloaded screen", function() it("should be able to reload a preloaded screen", function()
monarch.show(SCREEN_PRELOAD, nil, { count = 1 }) monarch.show(SCREEN_PRELOAD, nil, { count = 1 })
assert(wait_until_shown(SCREEN_PRELOAD), "Screen_preload was never shown") assert(wait_until_visible(SCREEN_PRELOAD), "Screen_preload was never shown")
-- first time the screen gets loaded it will increment the count -- first time the screen gets loaded it will increment the count
assert(monarch.data(SCREEN_PRELOAD).count == 2) assert(monarch.data(SCREEN_PRELOAD).count == 2)
monarch.show(SCREEN_PRELOAD, { clear = true, reload = true }, { count = 1 }) monarch.show(SCREEN_PRELOAD, { clear = true, reload = true }, { count = 1 })
assert(wait_until_shown(SCREEN_PRELOAD), "Screen_preload was never shown") assert(wait_until_visible(SCREEN_PRELOAD), "Screen_preload was never shown")
-- 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)
@@ -470,9 +481,11 @@ return function()
assert(wait_until_stack({ SCREEN1 })) assert(wait_until_stack({ SCREEN1 }))
monarch.show(FOCUS1) monarch.show(FOCUS1)
assert(wait_until_stack({ SCREEN1, FOCUS1 })) assert(wait_until_stack({ SCREEN1, FOCUS1 }))
assert(wait_until_visible(FOCUS1))
assert(_G.focus1_gained) assert(_G.focus1_gained)
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_stack({ SCREEN1, FOCUS1, SCREEN1 })) assert(wait_until_stack({ SCREEN1, FOCUS1, SCREEN1 }))
assert(wait_until_hidden(FOCUS1))
assert(_G.focus1_lost) assert(_G.focus1_lost)
end) end)
@@ -483,14 +496,14 @@ return function()
-- proxy screen -- proxy screen
monarch.show(SCREEN1) monarch.show(SCREEN1)
wait_until_shown(SCREEN1) wait_until_visible(SCREEN1)
assert(monarch.post(SCREEN1, "foobar"), "Expected monarch.post() to return true") assert(monarch.post(SCREEN1, "foobar"), "Expected monarch.post() to return true")
cowait(0.1) cowait(0.1)
assert(_G.screen1_foobar, "Screen1 never received a message") assert(_G.screen1_foobar, "Screen1 never received a message")
-- factory screen -- factory screen
monarch.show(SCREEN2) monarch.show(SCREEN2)
wait_until_shown(SCREEN2) wait_until_visible(SCREEN2)
assert(monarch.post(SCREEN2, "foobar"), "Expected monarch.post() to return true") assert(monarch.post(SCREEN2, "foobar"), "Expected monarch.post() to return true")
cowait(0.1) cowait(0.1)
assert(_G.screen2_foobar, "Screen2 never received a message") assert(_G.screen2_foobar, "Screen2 never received a message")
@@ -503,7 +516,7 @@ return function()
-- proxy screen -- proxy screen
monarch.show(SCREEN1) monarch.show(SCREEN1)
wait_until_shown(SCREEN1) wait_until_visible(SCREEN1)
assert(monarch.post(SCREEN1, "foobar", { foo = "bar" }), "Expected monarch.post() to return true") assert(monarch.post(SCREEN1, "foobar", { foo = "bar" }), "Expected monarch.post() to return true")
cowait(0.1) cowait(0.1)
assert(_G.screen1_foobar, "Screen1 never received a message") assert(_G.screen1_foobar, "Screen1 never received a message")
@@ -511,7 +524,7 @@ return function()
-- factory screen -- factory screen
monarch.show(SCREEN2) monarch.show(SCREEN2)
wait_until_shown(SCREEN2) wait_until_visible(SCREEN2)
assert(monarch.post(SCREEN2, "foobar", { foo = "bar" }), "Expected monarch.post() to return true") assert(monarch.post(SCREEN2, "foobar", { foo = "bar" }), "Expected monarch.post() to return true")
cowait(0.1) cowait(0.1)
assert(_G.screen2_foobar, "Screen2 never received a message") assert(_G.screen2_foobar, "Screen2 never received a message")
@@ -525,6 +538,7 @@ return function()
monarch.show(SCREEN1) monarch.show(SCREEN1)
monarch.show(SCREEN2) monarch.show(SCREEN2)
assert(wait_until_stack({ SCREEN1, SCREEN2 })) assert(wait_until_stack({ SCREEN1, SCREEN2 }))
assert(wait_until_hidden(SCREEN1))
local ok, err = monarch.post(SCREEN1, "foobar") local ok, err = monarch.post(SCREEN1, "foobar")
assert(not ok and err, "Expected monarch.post() to return false plus an error message") assert(not ok and err, "Expected monarch.post() to return false plus an error message")
cowait(0.1) cowait(0.1)
@@ -534,7 +548,7 @@ return function()
it("should not be able to post messages to proxy screens without a receiver url", function() it("should not be able to post messages to proxy screens without a receiver url", function()
monarch.show(POPUP1) monarch.show(POPUP1)
wait_until_shown(POPUP1) wait_until_visible(POPUP1)
local ok, err = monarch.post(POPUP1, "foobar") local ok, err = monarch.post(POPUP1, "foobar")
assert(not ok and err, "Expected monarch.post() to return false plus an error message") assert(not ok and err, "Expected monarch.post() to return false plus an error message")
end) end)

View File

@@ -8,6 +8,12 @@ local easing = require "monarch.transitions.easings"
return function() return function()
local function wait_timeout(fn, ...)
local args = { ... }
cowait(function() return fn(unpack(args)) end, 5)
return fn(...)
end
describe("transitions", function() describe("transitions", function()
before(function() before(function()
mock_msg.mock() mock_msg.mock()
@@ -22,6 +28,28 @@ describe("transitions", function()
end) end)
it("should replace an existing transition with a new one", function()
local one = false
function dummy_transition1(node, to, easing, duration, delay, cb)
one = true
end
local two = false
function dummy_transition2(node, to, easing, duration, delay, cb)
two = true
end
local node = gui.new_box_node(vmath.vector3(), vmath.vector3(100, 100, 0))
local duration = 2
local t = transitions.create(node)
t.show_in(dummy_transition1, easing.OUT, duration, delay or 0)
t.show_in(dummy_transition2, easing.OUT, duration, delay or 0)
t.handle(monarch.TRANSITION.SHOW_IN)
wait_timeout(function() return one or two end)
assert(two)
assert(not one)
end)
it("should replay and immediately finish on layout change", function() it("should replay and immediately finish on layout change", function()
function dummy_transition(node, to, easing, duration, delay, cb) function dummy_transition(node, to, easing, duration, delay, cb)
print("dummy transition") print("dummy transition")