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

Compare commits

...

13 Commits
4.0.0 ... 4.3.1

Author SHA1 Message Date
Björn Ritzl
6f79bd0326 Swap transition count and listener order 2023-08-08 23:51:48 +02:00
Björn Ritzl
e5214edb22 Fix back() callback 2023-08-08 23:51:29 +02:00
Björn Ritzl
1d4e48c0de Update test_monarch.lua 2023-08-08 22:55:29 +02:00
Björn Ritzl
5bdc3e4540 Added option table to back() 2023-08-08 22:53:00 +02:00
Björn Ritzl
85123c84e9 Show how to use monarch listeners 2023-08-08 22:49:58 +02:00
Björn Ritzl
a605f4f6f8 Fixed tests 2023-08-03 10:42:06 +02:00
Björn Ritzl
5d8fa8e220 monarch.post() requires monarch.on_post() 2023-08-03 08:35:04 +02:00
Björn Ritzl
df2a2a62ea Improved screen proxy url detection 2023-08-03 08:34:26 +02:00
Björn Ritzl
4e13660d63 Use new on_transition() 2023-08-02 07:47:47 +02:00
Björn Ritzl
0191a4e540 Set focus, transition and receiver as deprecated 2023-08-02 07:47:32 +02:00
Björn Ritzl
c601174b9d Improved handling of unregistered screens (#93)
* Set cowait flag

* Set screen as unregistered

* Do not process screen which have been unregistered

* Java 17
2023-07-29 11:28:18 +02:00
Björn Ritzl
66d2c98ccc Clear wait_for flag on proxy loaded 2023-07-29 10:29:51 +02:00
Björn Ritzl
e8249229b9 Differentiate messages and wait for constants 2023-07-29 10:29:21 +02:00
20 changed files with 198 additions and 143 deletions

View File

@@ -10,7 +10,7 @@ jobs:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- uses: actions/setup-java@v1 - uses: actions/setup-java@v1
with: with:
java-version: '11' java-version: '17'
- name: Run.sh - name: Run.sh
env: env:
DEFOLD_USER: bjorn.ritzl@gmail.com DEFOLD_USER: bjorn.ritzl@gmail.com

View File

@@ -40,13 +40,18 @@ Clear the stack of screens completely. Any visible screen will be hidden by navi
* `callback` (function) - Optional function to call when the stack has been cleared. * `callback` (function) - Optional function to call when the stack has been cleared.
## monarch.back([data], [callback]) ## monarch.back([options], [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.
**PARAMETERS** **PARAMETERS**
* `options` (table) - Options when showing the new screen (see below).
* `data` (table) - Optional data to associate with the screen you are going back to. Retrieve using `monarch.data()`. * `data` (table) - Optional data to associate with the screen you are going back to. Retrieve using `monarch.data()`.
* `callback` (function) - Optional function to call when the previous screen is visible. * `callback` (function) - Optional function to call when the previous screen is visible.
The options table can contain the following fields:
* `sequential` (boolean) - If the `sequential` flag is set Monarch will start loading the screen only after the previous screen finished transitioning out.
## monarch.preload(screen_id, [options], [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.
@@ -177,7 +182,7 @@ Remove a previously added listener.
## monarch.post(screen_id, message_id, [message]) ## monarch.post(screen_id, message_id, [message])
Post a message to a visible screen. If the screen is created through a collection proxy it must have specified a receiver url. If the screen is created through a collection factory the function will post the message to all game objects within the collection. Post a message to a visible screen. The screen must have set a message listener using `monarch.on_post()`.
**PARAMETERS** **PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to post message to * `screen_id` (string|hash) - Id of the screen to post message to

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 92 KiB

View File

@@ -5,11 +5,13 @@ function init(self)
msg.post(".", "acquire_input_focus") msg.post(".", "acquire_input_focus")
gui.set_render_order(15) gui.set_render_order(15)
self.transition = transitions.create(gui.get_node("root")) local transition = transitions.create(gui.get_node("root"))
.show_in(transitions.slide_in_top, gui.EASING_OUTQUAD, 0.6, 0) .show_in(transitions.slide_in_top, gui.EASING_OUTQUAD, 0.6, 0)
.show_out(transitions.slide_out_top, gui.EASING_INQUAD, 0.6, 0) .show_out(transitions.slide_out_top, gui.EASING_INQUAD, 0.6, 0)
.back_in(transitions.slide_in_top, gui.EASING_OUTQUAD, 0.6, 0) .back_in(transitions.slide_in_top, gui.EASING_OUTQUAD, 0.6, 0)
.back_out(transitions.slide_out_top, gui.EASING_INQUAD, 0.6, 0) .back_out(transitions.slide_out_top, gui.EASING_INQUAD, 0.6, 0)
monarch.on_transition("about", transition)
end end
function on_input(self, action_id, action) function on_input(self, action_id, action)
@@ -27,5 +29,5 @@ function on_input(self, action_id, action)
end end
function on_message(self, message_id, message, sender) function on_message(self, message_id, message, sender)
self.transition.handle(message_id, message, sender) monarch.on_message(message_id, message, sender)
end end

View File

@@ -21,16 +21,6 @@ embedded_instances {
" value: \"menu\"\n" " value: \"menu\"\n"
" type: PROPERTY_TYPE_HASH\n" " type: PROPERTY_TYPE_HASH\n"
" }\n" " }\n"
" properties {\n"
" id: \"transition_id\"\n"
" value: \"/go\"\n"
" type: PROPERTY_TYPE_HASH\n"
" }\n"
" properties {\n"
" id: \"focus_id\"\n"
" value: \"/go\"\n"
" type: PROPERTY_TYPE_HASH\n"
" }\n"
" property_decls {\n" " property_decls {\n"
" }\n" " }\n"
"}\n" "}\n"
@@ -146,11 +136,6 @@ embedded_instances {
" value: \"pregame\"\n" " value: \"pregame\"\n"
" type: PROPERTY_TYPE_HASH\n" " type: PROPERTY_TYPE_HASH\n"
" }\n" " }\n"
" properties {\n"
" id: \"transition_url\"\n"
" value: \"pregame:/go#pregame\"\n"
" type: PROPERTY_TYPE_URL\n"
" }\n"
" property_decls {\n" " property_decls {\n"
" }\n" " }\n"
"}\n" "}\n"
@@ -211,11 +196,6 @@ embedded_instances {
" value: \"game\"\n" " value: \"game\"\n"
" type: PROPERTY_TYPE_HASH\n" " type: PROPERTY_TYPE_HASH\n"
" }\n" " }\n"
" properties {\n"
" id: \"transition_url\"\n"
" value: \"game:/go#game\"\n"
" type: PROPERTY_TYPE_URL\n"
" }\n"
" property_decls {\n" " property_decls {\n"
" }\n" " }\n"
"}\n" "}\n"
@@ -287,16 +267,6 @@ embedded_instances {
" type: PROPERTY_TYPE_BOOLEAN\n" " type: PROPERTY_TYPE_BOOLEAN\n"
" }\n" " }\n"
" properties {\n" " properties {\n"
" id: \"transition_url\"\n"
" value: \"about:/go#about\"\n"
" type: PROPERTY_TYPE_URL\n"
" }\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"
@@ -376,11 +346,6 @@ embedded_instances {
" value: \"true\"\n" " value: \"true\"\n"
" type: PROPERTY_TYPE_BOOLEAN\n" " type: PROPERTY_TYPE_BOOLEAN\n"
" }\n" " }\n"
" properties {\n"
" id: \"transition_url\"\n"
" value: \"confirm:/go#confirm\"\n"
" type: PROPERTY_TYPE_URL\n"
" }\n"
" property_decls {\n" " property_decls {\n"
" }\n" " }\n"
"}\n" "}\n"

View File

@@ -7,11 +7,13 @@ function init(self)
self.no = gui.get_node("no_button") self.no = gui.get_node("no_button")
gui.set_render_order(15) gui.set_render_order(15)
self.transition = transitions.create(gui.get_node("root")) local transition = transitions.create(gui.get_node("root"))
.show_in(transitions.scale_in, gui.EASING_OUTBACK, 0.3, 0) .show_in(transitions.scale_in, gui.EASING_OUTBACK, 0.3, 0)
.show_out(transitions.scale_out, gui.EASING_INBACK, 0.3, 0) .show_out(transitions.scale_out, gui.EASING_INBACK, 0.3, 0)
.back_in(transitions.scale_in, gui.EASING_OUTBACK, 0.3, 0) .back_in(transitions.scale_in, gui.EASING_OUTBACK, 0.3, 0)
.back_out(transitions.scale_out, gui.EASING_INBACK, 0.3, 0) .back_out(transitions.scale_out, gui.EASING_INBACK, 0.3, 0)
monarch.on_transition("confirm", transition)
end end
function on_input(self, action_id, action) function on_input(self, action_id, action)
@@ -35,5 +37,5 @@ function on_input(self, action_id, action)
end end
function on_message(self, message_id, message, sender) function on_message(self, message_id, message, sender)
self.transition.handle(message_id, message, sender) monarch.on_message(message_id, message, sender)
end end

View File

@@ -3,11 +3,12 @@ local transitions = require "monarch.transitions.gui"
function init(self) function init(self)
msg.post(".", "acquire_input_focus") msg.post(".", "acquire_input_focus")
local data = monarch.data(hash("game")) local data = monarch.data(hash("game"))
gui.set_text(gui.get_node("level"), tostring(data.level)) gui.set_text(gui.get_node("level"), tostring(data.level))
self.transition = transitions.in_right_out_left(gui.get_node("root"), 0.6, 0) local transition = transitions.in_right_out_left(gui.get_node("root"), 0.6, 0)
monarch.on_transition("game", transition)
end end
function on_input(self, action_id, action) function on_input(self, action_id, action)
@@ -21,5 +22,5 @@ function on_input(self, action_id, action)
end end
function on_message(self, message_id, message, sender) function on_message(self, message_id, message, sender)
self.transition.handle(message_id, message, sender) monarch.on_message(message_id, message, sender)
end end

View File

@@ -8,7 +8,8 @@ function init(self)
gui.animate(gui.get_node("spinner"), gui.PROP_ROTATION, vmath.vector3(0, 0, -360), gui.EASING_INOUTQUAD, 2, 0, nil, gui.PLAYBACK_LOOP_FORWARD) gui.animate(gui.get_node("spinner"), gui.PROP_ROTATION, vmath.vector3(0, 0, -360), gui.EASING_INOUTQUAD, 2, 0, nil, gui.PLAYBACK_LOOP_FORWARD)
self.transition = transitions.fade_in_out(gui.get_node("root"), 0.6, 0) local transition = transitions.fade_in_out(gui.get_node("root"), 0.6, 0)
monarch.on_transition("menu", transition)
end end
function on_input(self, action_id, action) function on_input(self, action_id, action)
@@ -28,7 +29,7 @@ function on_input(self, action_id, action)
end end
function on_message(self, message_id, message, sender) function on_message(self, message_id, message, sender)
self.transition.handle(message_id, message, sender) monarch.on_message(message_id, message, sender)
if message_id == monarch.FOCUS.GAINED then if message_id == monarch.FOCUS.GAINED then
gui.set_text(gui.get_node("timestamp"), os.date()) gui.set_text(gui.get_node("timestamp"), os.date())
end end

View File

@@ -9,12 +9,13 @@ function init(self)
gui.set_render_order(14) gui.set_render_order(14)
gui.animate(gui.get_node("spinner"), gui.PROP_ROTATION, vmath.vector3(0, 0, -360), gui.EASING_INOUTQUAD, 2, 0, nil, gui.PLAYBACK_LOOP_FORWARD) gui.animate(gui.get_node("spinner"), gui.PROP_ROTATION, vmath.vector3(0, 0, -360), gui.EASING_INOUTQUAD, 2, 0, nil, gui.PLAYBACK_LOOP_FORWARD)
self.transition = transitions.create(gui.get_node("root")) local transition = transitions.create(gui.get_node("root"))
.show_in(transitions.scale_in, gui.EASING_OUTBACK, 0.3, 0) .show_in(transitions.scale_in, gui.EASING_OUTBACK, 0.3, 0)
.show_out(transitions.scale_out, gui.EASING_INBACK, 0.3, 0) .show_out(transitions.scale_out, gui.EASING_INBACK, 0.3, 0)
.back_in(transitions.scale_in, gui.EASING_OUTBACK, 0.3, 0) .back_in(transitions.scale_in, gui.EASING_OUTBACK, 0.3, 0)
.back_out(transitions.scale_out, gui.EASING_INBACK, 0.3, 0) .back_out(transitions.scale_out, gui.EASING_INBACK, 0.3, 0)
monarch.on_transition("popup", transition)
end end
function on_input(self, action_id, action) function on_input(self, action_id, action)
@@ -39,5 +40,5 @@ function on_input(self, action_id, action)
end end
function on_message(self, message_id, message, sender) function on_message(self, message_id, message, sender)
self.transition.handle(message_id, message, sender) monarch.on_message(message_id, message, sender)
end end

View File

@@ -5,8 +5,9 @@ function init(self)
msg.post(".", "acquire_input_focus") msg.post(".", "acquire_input_focus")
self.play = gui.get_node("play_button") self.play = gui.get_node("play_button")
self.back = gui.get_node("back_button") self.back = gui.get_node("back_button")
self.transition = transitions.in_right_out_left(gui.get_node("root"), 0.6, 0) local transition = transitions.in_right_out_left(gui.get_node("root"), 0.6, 0)
monarch.on_transition("pregame", transition)
end end
function on_input(self, action_id, action) function on_input(self, action_id, action)
@@ -26,5 +27,5 @@ function on_input(self, action_id, action)
end end
function on_message(self, message_id, message, sender) function on_message(self, message_id, message, sender)
self.transition.handle(message_id, message, sender) monarch.on_message(message_id, message, sender)
end end

View File

@@ -4,10 +4,25 @@ function init(self)
monarch.debug() monarch.debug()
msg.post("@render:/", "clear_color", { color = vmath.vector4(0.4, 0.6, 0.8,1.0) }) msg.post("@render:/", "clear_color", { color = vmath.vector4(0.4, 0.6, 0.8,1.0) })
msg.post("#", "init_monarch") -- wait until init() has been called for all screen.script instances msg.post("#", "init_monarch") -- wait until init() has been called for all screen.script instances
monarch.add_listener()
end
function final(self)
monarch.remove_listener()
end end
function on_message(self, message_id, message, sender) function on_message(self, message_id, message, sender)
if message_id == hash("init_monarch") then if message_id == hash("init_monarch") then
monarch.show(hash("window1")) monarch.show(hash("window1"))
elseif message_id == monarch.SCREEN_TRANSITION_IN_STARTED then
print("Monarch screen transition in started", message.screen)
elseif message_id == monarch.SCREEN_TRANSITION_IN_FINISHED then
print("Monarch screen transition in finished", message.screen)
elseif message_id == monarch.SCREEN_TRANSITION_OUT_STARTED then
print("Monarch screen transition out started", message.screen)
elseif message_id == monarch.SCREEN_TRANSITION_OUT_FINISHED then
print("Monarch screen transition out finished", message.screen)
elseif message_id == monarch.SCREEN_TRANSITION_FAILED then
print("Monarch screen transition failed")
end end
end end

View File

@@ -23,6 +23,5 @@ function on_input(self, action_id, action)
end end
function on_message(self, message_id, message, sender) function on_message(self, message_id, message, sender)
print("window2", message_id, message, sender)
monarch.on_message(message_id, message, sender) monarch.on_message(message_id, message, sender)
end end

View File

@@ -5,7 +5,7 @@ dependencies#0 = https://github.com/britzl/deftest/archive/2.7.0.zip
dependencies#1 = https://github.com/defold/lua-language-server/releases/download/v0.0.3/release.zip dependencies#1 = https://github.com/defold/lua-language-server/releases/download/v0.0.3/release.zip
[bootstrap] [bootstrap]
main_collection = /example/slidingwindow/slidingwindow.collectionc main_collection = /example/advanced/advanced.collectionc
[input] [input]
game_binding = /input/game.input_bindingc game_binding = /input/game.input_bindingc

View File

@@ -3,17 +3,25 @@ local async = require "monarch.utils.async"
local M = {} local M = {}
local CONTEXT = hash("monarch_context") local WAITFOR_COWAIT = hash("waitfor_cowait")
local PROXY_LOADED = hash("proxy_loaded") local WAITFOR_CONTEXT = hash("waitfor_monarch_context")
local PROXY_UNLOADED = hash("proxy_unloaded") local WAITFOR_PROXY_LOADED = hash("waitfor_proxy_loaded")
local LAYOUT_CHANGED = hash("layout_changed") local WAITFOR_PROXY_UNLOADED = hash("waitfor_proxy_unloaded")
local WAITFOR_TRANSITION_DONE = hash("waitfor_transition_done")
local RELEASE_INPUT_FOCUS = hash("release_input_focus") local MSG_CONTEXT = hash("monarch_context")
local ACQUIRE_INPUT_FOCUS = hash("acquire_input_focus") local MSG_PROXY_LOADED = hash("proxy_loaded")
local ASYNC_LOAD = hash("async_load") local MSG_PROXY_UNLOADED = hash("proxy_unloaded")
local UNLOAD = hash("unload") local MSG_LAYOUT_CHANGED = hash("layout_changed")
local ENABLE = hash("enable") local MSG_RELEASE_INPUT_FOCUS = hash("release_input_focus")
local DISABLE = hash("disable") local MSG_ACQUIRE_INPUT_FOCUS = hash("acquire_input_focus")
local MSG_ASYNC_LOAD = hash("async_load")
local MSG_UNLOAD = hash("unload")
local MSG_ENABLE = hash("enable")
local MSG_DISABLE = hash("disable")
local DEPRECATED = hash("__DEPRECATED__")
-- transition messages -- transition messages
M.TRANSITION = {} M.TRANSITION = {}
@@ -82,10 +90,13 @@ local function assign(to, from)
return to return to
end end
local function cowait(delay) local function cowait(screen, delay)
log("cowait()", screen.id, delay)
local co = coroutine.running() local co = coroutine.running()
assert(co, "You must run this from within a coroutine") assert(co, "You must run this from within a coroutine")
screen.wait_for = WAITFOR_COWAIT
timer.delay(delay, false, function() timer.delay(delay, false, function()
screen.wait_for = nil
assert(coroutine.resume(co)) assert(coroutine.resume(co))
end) end)
coroutine.yield() coroutine.yield()
@@ -261,12 +272,6 @@ end
-- keep input focus when below a popup -- keep input focus when below a popup
-- * others_keep_input_focus_when_below_screen - If screens below this -- * others_keep_input_focus_when_below_screen - If screens below this
-- screen should keep input focus -- screen should keep input focus
-- * transition_url - URL to a script that is responsible for the
-- screen transitions
-- * focus_url - URL to a script that is to be notified of focus
-- lost/gained events
-- * receiver_url - URL to a script that is to receive messages sent
-- using monarch.send()
-- * auto_preload - true if the screen should be automatically preloaded -- * auto_preload - true if the screen should be automatically preloaded
function M.register_proxy(id, proxy, settings) function M.register_proxy(id, proxy, settings)
assert(proxy, "You must provide a collection proxy URL") assert(proxy, "You must provide a collection proxy URL")
@@ -276,6 +281,15 @@ function M.register_proxy(id, proxy, settings)
screen.focus_url = settings and settings.focus_url screen.focus_url = settings and settings.focus_url
screen.receiver_url = settings and settings.receiver_url screen.receiver_url = settings and settings.receiver_url
screen.auto_preload = settings and settings.auto_preload screen.auto_preload = settings and settings.auto_preload
if screen.transition_url.fragment == DEPRECATED then
screen.transition_url = nil
end
if screen.focus_url.fragment == DEPRECATED then
screen.focus_url = nil
end
if screen.receiver_url.fragment == DEPRECATED then
screen.receiver_url = nil
end
if screen.auto_preload then if screen.auto_preload then
M.preload(id) M.preload(id)
end end
@@ -298,10 +312,6 @@ M.register = M.register_proxy
-- keep input focus when below a popup -- keep input focus when below a popup
-- * others_keep_input_focus_when_below_screen - If screens below this -- * others_keep_input_focus_when_below_screen - If screens below this
-- screen should keep input focus -- screen should keep input focus
-- * transition_id - Id of the game object in the collection that is responsible
-- for the screen transitions
-- * focus_id - Id of the game object in the collection that is to be notified
-- of focus lost/gained events
-- * auto_preload - true if the screen should be automatically preloaded -- * auto_preload - true if the screen should be automatically preloaded
function M.register_factory(id, factory, settings) function M.register_factory(id, factory, settings)
assert(factory, "You must provide a collection factory URL") assert(factory, "You must provide a collection factory URL")
@@ -310,6 +320,13 @@ function M.register_factory(id, factory, settings)
screen.transition_id = settings and settings.transition_id screen.transition_id = settings and settings.transition_id
screen.focus_id = settings and settings.focus_id screen.focus_id = settings and settings.focus_id
screen.auto_preload = settings and settings.auto_preload screen.auto_preload = settings and settings.auto_preload
if screen.transition_id == DEPRECATED then
screen.transition_id = nil
end
if screen.focus_id == DEPRECATED then
screen.focus_id = nil
end
if screen.auto_preload then if screen.auto_preload then
M.preload(id) M.preload(id)
end end
@@ -323,6 +340,7 @@ function M.unregister(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)))
log("unregister()", id) log("unregister()", id)
local screen = screens[id]
screens[id] = nil screens[id] = nil
-- remove screen from stack -- remove screen from stack
for i = #stack, 1, -1 do for i = #stack, 1, -1 do
@@ -330,16 +348,20 @@ function M.unregister(id)
table.remove(stack, i) table.remove(stack, i)
end end
end end
screen.unregistered = true
if screen.wait_for then
assert(coroutine.resume(screen.co))
end
end end
local function acquire_input(screen) local function acquire_input(screen)
log("acquire_input()", screen.id) log("acquire_input()", screen.id)
if not screen.input then if not screen.input then
if screen.proxy then if screen.proxy then
msg.post(screen.script, ACQUIRE_INPUT_FOCUS) msg.post(screen.script, MSG_ACQUIRE_INPUT_FOCUS)
elseif screen.factory then elseif screen.factory then
for id,instance in pairs(screen.factory_ids) do for id,instance in pairs(screen.factory_ids) do
msg.post(instance, ACQUIRE_INPUT_FOCUS) msg.post(instance, MSG_ACQUIRE_INPUT_FOCUS)
end end
end end
screen.input = true screen.input = true
@@ -357,10 +379,10 @@ local function release_input(screen, next_screen)
local release_focus = not keep_if_next_is_popup and not keep_when_below_next local release_focus = not keep_if_next_is_popup and not keep_when_below_next
if release_focus then if release_focus then
if screen.proxy then if screen.proxy then
msg.post(screen.script, RELEASE_INPUT_FOCUS) msg.post(screen.script, MSG_RELEASE_INPUT_FOCUS)
elseif screen.factory then elseif screen.factory then
for id,instance in pairs(screen.factory_ids) do for id,instance in pairs(screen.factory_ids) do
msg.post(instance, RELEASE_INPUT_FOCUS) msg.post(instance, MSG_RELEASE_INPUT_FOCUS)
end end
end end
screen.input = false screen.input = false
@@ -370,24 +392,25 @@ end
local function change_context(screen) local function change_context(screen)
log("change_context()", screen.id) log("change_context()", screen.id)
screen.wait_for = CONTEXT screen.wait_for = WAITFOR_CONTEXT
msg.post(screen.script, CONTEXT, { id = screen.id }) msg.post(screen.script, MSG_CONTEXT, { id = screen.id })
coroutine.yield() coroutine.yield()
screen.wait_for = nil screen.wait_for = nil
end end
local function unload(screen, force) local function unload(screen, force)
if screen.unregistered then return end
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 if screen.loaded then
msg.post(screen.proxy, DISABLE) msg.post(screen.proxy, MSG_DISABLE)
screen.loaded = false screen.loaded = false
end end
screen.preloaded = true screen.preloaded = true
else else
screen.wait_for = PROXY_UNLOADED screen.wait_for = WAITFOR_PROXY_UNLOADED
msg.post(screen.proxy, UNLOAD) msg.post(screen.proxy, MSG_UNLOAD)
coroutine.yield() coroutine.yield()
screen.loaded = false screen.loaded = false
screen.preloaded = false screen.preloaded = false
@@ -411,8 +434,8 @@ local function unload(screen, force)
-- we need to wait here in case the unloaded screen contained any screens -- 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() -- 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() -- functions called so that they have time to call unregister()
cowait(0) cowait(screen, 0)
cowait(0) cowait(screen, 0)
end end
@@ -435,9 +458,13 @@ local function preload(screen)
screen.preloading = false screen.preloading = false
return false, error_message return false, error_message
end end
screen.wait_for = PROXY_LOADED screen.wait_for = WAITFOR_PROXY_LOADED
msg.post(screen.proxy, ASYNC_LOAD) msg.post(screen.proxy, MSG_ASYNC_LOAD)
coroutine.yield() coroutine.yield()
screen.wait_for = nil
if screen.unregistered then
return false, "Screen was unregistered while loading"
end
elseif screen.factory then elseif screen.factory then
log("preload() factory") log("preload() factory")
if collectionfactory.get_status(screen.factory) == collectionfactory.STATUS_UNLOADED then if collectionfactory.get_status(screen.factory) == collectionfactory.STATUS_UNLOADED then
@@ -445,6 +472,9 @@ local function preload(screen)
assert(coroutine.resume(screen.co)) assert(coroutine.resume(screen.co))
end) end)
coroutine.yield() coroutine.yield()
if screen.unregistered then
return false, "Screen was unregistered while loading"
end
end end
if collectionfactory.get_status(screen.factory) ~= collectionfactory.STATUS_LOADED then if collectionfactory.get_status(screen.factory) ~= collectionfactory.STATUS_LOADED then
@@ -476,7 +506,7 @@ local function load(screen)
end end
if screen.proxy then if screen.proxy then
msg.post(screen.proxy, ENABLE) msg.post(screen.proxy, MSG_ENABLE)
elseif screen.factory then elseif screen.factory then
screen.factory_ids = collectionfactory.create(screen.factory) screen.factory_ids = collectionfactory.create(screen.factory)
screen.transition_url = screen.factory_ids[screen.transition_id] screen.transition_url = screen.factory_ids[screen.transition_id]
@@ -489,8 +519,9 @@ end
local function transition(screen, message_id, message, wait) local function transition(screen, message_id, message, wait)
log("transition()", screen.id) log("transition()", screen.id)
if screen.unregistered then return end
if screen.transition_url then if screen.transition_url then
screen.wait_for = M.TRANSITION.DONE screen.wait_for = WAITFOR_TRANSITION_DONE
msg.post(screen.transition_url, message_id, message) msg.post(screen.transition_url, message_id, message)
if wait then if wait then
coroutine.yield() coroutine.yield()
@@ -512,14 +543,15 @@ end
local function focus_lost(screen, next_screen) local function focus_lost(screen, next_screen)
log("focus_lost()", screen.id) log("focus_lost()", screen.id)
if screen.unregistered then return end
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 -- if there's no transition on the screen losing focus and it gets
-- unloaded this will happen before the focus_lost message reaches -- unloaded this will happen before the focus_lost message reaches
-- the focus_url -- the focus_url
-- we add a delay to ensure the message queue has time to be processed -- we add a delay to ensure the message queue has time to be processed
cowait(0) cowait(screen, 0)
cowait(0) cowait(screen, 0)
else else
log("focus_lost() no focus url - ignoring") log("focus_lost() no focus url - ignoring")
end end
@@ -640,7 +672,7 @@ local function show_in(screen, previous_screen, reload, add_to_stack, wait_for_t
return return
end end
-- wait one frame so that the init() of any script have time to run before starting transitions -- wait one frame so that the init() of any script have time to run before starting transitions
cowait(0) cowait(screen, 0)
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 screen.visible = true
@@ -666,7 +698,7 @@ local function back_in(screen, previous_screen, wait_for_transition, cb)
return return
end end
-- wait one frame so that the init() of any script have time to run before starting transitions -- wait one frame so that the init() of any script have time to run before starting transitions
cowait(0) cowait(screen, 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)
@@ -683,8 +715,8 @@ local function back_out(screen, next_screen, wait_for_transition, cb)
log("back_out()", screen.id) log("back_out()", screen.id)
assert(wait_for_transition ~= nil) assert(wait_for_transition ~= nil)
run_coroutine(screen, cb, function() 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 active_transition_count = active_transition_count + 1
notify_transition_listeners(M.SCREEN_TRANSITION_OUT_STARTED, { screen = screen.id, next_screen = next_screen and next_screen.id })
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)
@@ -933,21 +965,38 @@ end
-- Go back to the previous screen in the stack. -- Go back to the previous screen in the stack.
-- @param options (table) - Table with options when backing out from the screen (can be nil).
-- Valid values:
-- * sequential - Set to true to wait for the current screen to hide itself out before starting the
-- back in transition even when transitioning to a different scene ID.
-- @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
function M.back(data, cb) function M.back(options, data, cb)
log("back() queuing action") log("back() queuing action")
-- backwards compatibility with old version M.back(data, cb)
-- case when back(data, cb)
if type(data) == "function" then
cb = data
data = options
options = nil
-- case when back(data, nil)
elseif options ~= nil and data == nil and cb == nil then
data = options
options = nil
end
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)
local back_cb = callbacks.track()
local top = stack[#stack] local top = stack[#stack]
-- if we go back to the same screen we need to first hide it -- if we go back to the same screen we need to first hide it
-- and wait until it is hidden before we show it again -- and wait until it is hidden before we show it again
if top and screen.id == top.id then local same_screen = top and top.id == screen.id
if same_screen or (options and options.sequential) then
back_out(screen, top, WAIT_FOR_TRANSITION, function() back_out(screen, top, WAIT_FOR_TRANSITION, function()
if data then if data then
top.data = data top.data = data
@@ -1141,45 +1190,39 @@ function M.post(id, message_id, message)
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]
if screen.proxy then if screen.receiver_url then
if screen.receiver_url then log("post() sending message to", screen.receiver_url)
log("post() sending message to", screen.receiver_url) msg.post(screen.receiver_url, message_id, message)
msg.post(screen.receiver_url, message_id, message)
else
return false, "Unable to post message since screen has no receiver url specified"
end
else else
for id,instance in pairs(screen.factory_ids) do return false, "Unable to post message since screen has no receiver url specified. Set one using monarch.on_post()."
msg.post(instance, message_id, message)
end
end end
return true return true
end end
function M.on_message(message_id, message, sender) function M.on_message(message_id, message, sender)
if message_id == PROXY_LOADED then if message_id == MSG_PROXY_LOADED then
local screen = find_screen(sender) local screen = find_screen(sender)
assert(screen, "Unable to find screen for loaded proxy") assert(screen, "Unable to find screen for loaded proxy")
if screen.wait_for == PROXY_LOADED then if screen.wait_for == WAITFOR_PROXY_LOADED then
assert(coroutine.resume(screen.co)) assert(coroutine.resume(screen.co))
end end
elseif message_id == PROXY_UNLOADED then elseif message_id == MSG_PROXY_UNLOADED then
local screen = find_screen(sender) local screen = find_screen(sender)
assert(screen, "Unable to find screen for unloaded proxy") assert(screen, "Unable to find screen for unloaded proxy")
if screen.wait_for == PROXY_UNLOADED then if screen.wait_for == WAITFOR_PROXY_UNLOADED then
assert(coroutine.resume(screen.co)) assert(coroutine.resume(screen.co))
end end
elseif message_id == CONTEXT then elseif message_id == MSG_CONTEXT then
local screen = find_screen(sender) local screen = find_screen(sender)
assert(screen, "Unable to find screen for current script url") assert(screen, "Unable to find screen for current script url")
if screen.wait_for == CONTEXT then if screen.wait_for == WAITFOR_CONTEXT then
assert(coroutine.resume(screen.co)) assert(coroutine.resume(screen.co))
end end
elseif message_id == M.TRANSITION.DONE then elseif message_id == M.TRANSITION.DONE then
local screen = find_transition_screen(sender) local screen = find_transition_screen(sender)
assert(screen, "Unable to find screen for transition") assert(screen, "Unable to find screen for transition")
if screen.wait_for == M.TRANSITION.DONE then if screen.wait_for == WAITFOR_TRANSITION_DONE then
assert(coroutine.resume(screen.co)) assert(coroutine.resume(screen.co))
end end
elseif message_id == M.TRANSITION.SHOW_IN elseif message_id == M.TRANSITION.SHOW_IN
@@ -1192,7 +1235,7 @@ function M.on_message(message_id, message, sender)
if screen.transition_fn then if screen.transition_fn then
screen.transition_fn(message_id, message, sender) screen.transition_fn(message_id, message, sender)
end end
elseif message_id == LAYOUT_CHANGED then elseif message_id == MSG_LAYOUT_CHANGED then
local screen = find_screen(sender) local screen = find_screen(sender)
if screen and screen.transition_fn then if screen and screen.transition_fn then
screen.transition_fn(message_id, message, sender) screen.transition_fn(message_id, message, sender)

View File

@@ -6,8 +6,8 @@ go.property("popup", false)
go.property("popup_on_popup", false) go.property("popup_on_popup", false)
go.property("screen_keeps_input_focus_when_below_popup", false) go.property("screen_keeps_input_focus_when_below_popup", false)
go.property("others_keep_input_focus_when_below_screen", false) go.property("others_keep_input_focus_when_below_screen", false)
go.property("transition_id", hash("")) go.property("transition_id", hash("__DEPRECATED__"))
go.property("focus_id", hash("")) go.property("focus_id", hash("__DEPRECATED__"))
go.property("preload", false) go.property("preload", false)
@@ -15,7 +15,7 @@ function init(self)
monarch = require "monarch.monarch" monarch = require "monarch.monarch"
assert(not self.popup_on_popup or (self.popup_on_popup and self.popup), "Popup on Popups can only be set if the Popup flag is set") assert(not self.popup_on_popup or (self.popup_on_popup and self.popup), "Popup on Popups can only be set if the Popup flag is set")
assert(self.screen_factory ~= msg.url(), "You must specify either a factory URL") assert(self.screen_factory ~= msg.url(), "You must specify either a factory URL")
local settings = { local settings = {
popup = self.popup, popup = self.popup,
popup_on_popup = self.popup_on_popup, popup_on_popup = self.popup_on_popup,

View File

@@ -7,9 +7,9 @@ go.property("popup_on_popup", false)
go.property("timestep_below_popup", 1) go.property("timestep_below_popup", 1)
go.property("screen_keeps_input_focus_when_below_popup", false) go.property("screen_keeps_input_focus_when_below_popup", false)
go.property("others_keep_input_focus_when_below_screen", false) go.property("others_keep_input_focus_when_below_screen", false)
go.property("transition_url", msg.url()) go.property("transition_url", msg.url("#__DEPRECATED__"))
go.property("focus_url", msg.url()) go.property("focus_url", msg.url("#__DEPRECATED__"))
go.property("receiver_url", msg.url()) go.property("receiver_url", msg.url("#__DEPRECATED__"))
go.property("preload", false) go.property("preload", false)
@@ -19,7 +19,7 @@ function init(self)
assert(not self.popup_on_popup or (self.popup_on_popup and self.popup), "Popup on Popups can only be set if the Popup flag is set") assert(not self.popup_on_popup or (self.popup_on_popup and self.popup), "Popup on Popups can only be set if the Popup flag is set")
assert(self.screen_proxy ~= url, "You must specify either a proxy URL") assert(self.screen_proxy ~= url, "You must specify either a proxy URL")
assert(self.timestep_below_popup >= 0, "Timestep must be positive") assert(self.timestep_below_popup >= 0, "Timestep must be positive")
local settings = { local settings = {
popup = self.popup, popup = self.popup,
popup_on_popup = self.popup_on_popup, popup_on_popup = self.popup_on_popup,

View File

@@ -1,5 +1,11 @@
local monarch
function init(self) function init(self)
print("init - screen1") monarch = require "monarch.monarch"
print("init - screen1", msg.url())
monarch.on_post("screen1", function(message_id, message, sender)
_G.screen1_on_post = message or true
end)
end end
function final(self) function final(self)
@@ -7,7 +13,8 @@ function final(self)
end end
function on_message(self, message_id, message, sender) function on_message(self, message_id, message, sender)
monarch.on_message(message_id, message, sender)
if message_id == hash("foobar") then if message_id == hash("foobar") then
_G.screen1_foobar = message or true _G.screen1_on_message = message or true
end end
end end

View File

@@ -1,5 +1,11 @@
local monarch
function init(self) function init(self)
print("init - screen2") monarch = require "monarch.monarch"
print("init - screen2", msg.url())
monarch.on_post("screen2", function(message_id, message, sender)
_G.screen2_on_post = message or true
end)
end end
function final(self) function final(self)
@@ -7,7 +13,8 @@ function final(self)
end end
function on_message(self, message_id, message, sender) function on_message(self, message_id, message, sender)
monarch.on_message(message_id, message, sender)
if message_id == hash("foobar") then if message_id == hash("foobar") then
_G.screen2_foobar = message or true _G.screen2_on_message = message or true
end end
end end

View File

@@ -12,7 +12,6 @@ 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")
local FOOBAR = hash("foobar")
local TRANSITION1 = hash("transition1") local TRANSITION1 = hash("transition1")
local function check_stack(expected_screens) local function check_stack(expected_screens)
@@ -220,7 +219,7 @@ return function()
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(nil, data_back)
assert(wait_until_visible(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")
@@ -491,49 +490,56 @@ return function()
it("should be able to post messages without message data to visible screens", function() it("should be able to post messages without message data to visible screens", function()
_G.screen1_foobar = nil _G.screen1_on_message = nil
_G.screen2_foobar = nil _G.screen1_on_post = nil
_G.screen2_on_message = nil
_G.screen2_on_post = nil
-- proxy screen -- proxy screen
monarch.show(SCREEN1) monarch.show(SCREEN1)
wait_until_visible(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_on_message, "Screen1 never received a message")
assert(_G.screen1_on_post, "Screen1 never received a callback")
-- factory screen -- factory screen
monarch.show(SCREEN2) monarch.show(SCREEN2)
wait_until_visible(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_on_message, "Screen2 never received a message")
assert(_G.screen2_on_post, "Screen2 never received a callback")
end) end)
it("should be able to post messages with message data to visible screens", function() it("should be able to post messages with message data to visible screens", function()
_G.screen1_foobar = nil _G.screen1_on_message = nil
_G.screen2_foobar = nil _G.screen1_on_post = nil
_G.screen2_on_message = nil
_G.screen2_on_post = nil
-- proxy screen -- proxy screen
monarch.show(SCREEN1) monarch.show(SCREEN1)
wait_until_visible(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_on_message, "Screen1 never received a message")
assert(_G.screen1_foobar.foo == "bar", "Screen1 never received message data") assert(_G.screen1_on_message.foo == "bar", "Screen1 never received message data")
-- factory screen -- factory screen
monarch.show(SCREEN2) monarch.show(SCREEN2)
wait_until_visible(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_on_message, "Screen2 never received a message")
assert(_G.screen2_foobar.foo == "bar", "Screen2 never received message data") assert(_G.screen2_on_message.foo == "bar", "Screen2 never received message data")
end) end)
it("should not be able to post messages to hidden screens", function() it("should not be able to post messages to hidden screens", function()
_G.screen1_foobar = nil _G.screen1_on_message = nil
_G.screen1_on_post = nil
monarch.show(SCREEN1) monarch.show(SCREEN1)
monarch.show(SCREEN2) monarch.show(SCREEN2)
@@ -542,7 +548,7 @@ return function()
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)
assert(not _G.screen1_foobar, "Screen1 should not have received a message") assert(not _G.screen1_on_message, "Screen1 should not have received a message")
end) end)