3
0
mirror of https://github.com/britzl/monarch.git synced 2025-11-27 11:20:55 +01:00

Compare commits

..

4 Commits

Author SHA1 Message Date
Björn Ritzl
698cdba5a4 Documentation 2023-03-29 11:10:49 +02:00
Björn Ritzl
d8de338a46 Added focus and post listener setup functions 2023-03-29 10:40:38 +02:00
Björn Ritzl
55910abd74 Update gui.lua 2023-03-29 09:49:00 +02:00
Björn Ritzl
a055af032f Improve transition setup 2023-03-29 09:19:49 +02:00
16 changed files with 119 additions and 117 deletions

View File

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

View File

@@ -177,7 +177,7 @@ Remove a previously added listener.
## monarch.post(screen_id, message_id, [message])
Post a message to a visible screen. The screen must have set a message listener using `monarch.on_post()`.
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.
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to post message to

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,8 +8,7 @@ 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)
local transition = transitions.fade_in_out(gui.get_node("root"), 0.6, 0)
monarch.on_transition("menu", transition)
self.transition = transitions.fade_in_out(gui.get_node("root"), 0.6, 0)
end
function on_input(self, action_id, action)
@@ -29,7 +28,7 @@ function on_input(self, action_id, action)
end
function on_message(self, message_id, message, sender)
monarch.on_message(message_id, message, sender)
self.transition.handle(message_id, message, sender)
if message_id == monarch.FOCUS.GAINED then
gui.set_text(gui.get_node("timestamp"), os.date())
end

View File

@@ -9,13 +9,12 @@ function init(self)
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)
local transition = transitions.create(gui.get_node("root"))
self.transition = transitions.create(gui.get_node("root"))
.show_in(transitions.scale_in, gui.EASING_OUTBACK, 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_out(transitions.scale_out, gui.EASING_INBACK, 0.3, 0)
monarch.on_transition("popup", transition)
end
function on_input(self, action_id, action)
@@ -40,5 +39,5 @@ function on_input(self, action_id, action)
end
function on_message(self, message_id, message, sender)
monarch.on_message(message_id, message, sender)
self.transition.handle(message_id, message, sender)
end

View File

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

View File

@@ -23,5 +23,6 @@ function on_input(self, action_id, action)
end
function on_message(self, message_id, message, sender)
print("window2", message_id, message, sender)
monarch.on_message(message_id, message, sender)
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
[bootstrap]
main_collection = /example/advanced/advanced.collectionc
main_collection = /example/slidingwindow/slidingwindow.collectionc
[input]
game_binding = /input/game.input_bindingc

View File

@@ -3,25 +3,17 @@ local async = require "monarch.utils.async"
local M = {}
local WAITFOR_COWAIT = hash("waitfor_cowait")
local WAITFOR_CONTEXT = hash("waitfor_monarch_context")
local WAITFOR_PROXY_LOADED = hash("waitfor_proxy_loaded")
local WAITFOR_PROXY_UNLOADED = hash("waitfor_proxy_unloaded")
local WAITFOR_TRANSITION_DONE = hash("waitfor_transition_done")
local CONTEXT = hash("monarch_context")
local PROXY_LOADED = hash("proxy_loaded")
local PROXY_UNLOADED = hash("proxy_unloaded")
local LAYOUT_CHANGED = hash("layout_changed")
local MSG_CONTEXT = hash("monarch_context")
local MSG_PROXY_LOADED = hash("proxy_loaded")
local MSG_PROXY_UNLOADED = hash("proxy_unloaded")
local MSG_LAYOUT_CHANGED = hash("layout_changed")
local MSG_RELEASE_INPUT_FOCUS = hash("release_input_focus")
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__")
local RELEASE_INPUT_FOCUS = hash("release_input_focus")
local ACQUIRE_INPUT_FOCUS = hash("acquire_input_focus")
local ASYNC_LOAD = hash("async_load")
local UNLOAD = hash("unload")
local ENABLE = hash("enable")
local DISABLE = hash("disable")
-- transition messages
M.TRANSITION = {}
@@ -90,13 +82,10 @@ local function assign(to, from)
return to
end
local function cowait(screen, delay)
log("cowait()", screen.id, delay)
local function cowait(delay)
local co = coroutine.running()
assert(co, "You must run this from within a coroutine")
screen.wait_for = WAITFOR_COWAIT
timer.delay(delay, false, function()
screen.wait_for = nil
assert(coroutine.resume(co))
end)
coroutine.yield()
@@ -272,6 +261,12 @@ end
-- keep input focus when below a popup
-- * others_keep_input_focus_when_below_screen - If screens below this
-- 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
function M.register_proxy(id, proxy, settings)
assert(proxy, "You must provide a collection proxy URL")
@@ -281,15 +276,6 @@ function M.register_proxy(id, proxy, settings)
screen.focus_url = settings and settings.focus_url
screen.receiver_url = settings and settings.receiver_url
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
M.preload(id)
end
@@ -312,6 +298,10 @@ M.register = M.register_proxy
-- keep input focus when below a popup
-- * others_keep_input_focus_when_below_screen - If screens below this
-- 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
function M.register_factory(id, factory, settings)
assert(factory, "You must provide a collection factory URL")
@@ -320,13 +310,6 @@ function M.register_factory(id, factory, settings)
screen.transition_id = settings and settings.transition_id
screen.focus_id = settings and settings.focus_id
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
M.preload(id)
end
@@ -340,7 +323,6 @@ function M.unregister(id)
id = tohash(id)
assert(screens[id], ("There is no screen registered with id %s"):format(tostring(id)))
log("unregister()", id)
local screen = screens[id]
screens[id] = nil
-- remove screen from stack
for i = #stack, 1, -1 do
@@ -348,20 +330,16 @@ function M.unregister(id)
table.remove(stack, i)
end
end
screen.unregistered = true
if screen.wait_for then
assert(coroutine.resume(screen.co))
end
end
local function acquire_input(screen)
log("acquire_input()", screen.id)
if not screen.input then
if screen.proxy then
msg.post(screen.script, MSG_ACQUIRE_INPUT_FOCUS)
msg.post(screen.script, ACQUIRE_INPUT_FOCUS)
elseif screen.factory then
for id,instance in pairs(screen.factory_ids) do
msg.post(instance, MSG_ACQUIRE_INPUT_FOCUS)
msg.post(instance, ACQUIRE_INPUT_FOCUS)
end
end
screen.input = true
@@ -379,10 +357,10 @@ local function release_input(screen, next_screen)
local release_focus = not keep_if_next_is_popup and not keep_when_below_next
if release_focus then
if screen.proxy then
msg.post(screen.script, MSG_RELEASE_INPUT_FOCUS)
msg.post(screen.script, RELEASE_INPUT_FOCUS)
elseif screen.factory then
for id,instance in pairs(screen.factory_ids) do
msg.post(instance, MSG_RELEASE_INPUT_FOCUS)
msg.post(instance, RELEASE_INPUT_FOCUS)
end
end
screen.input = false
@@ -392,25 +370,24 @@ end
local function change_context(screen)
log("change_context()", screen.id)
screen.wait_for = WAITFOR_CONTEXT
msg.post(screen.script, MSG_CONTEXT, { id = screen.id })
screen.wait_for = CONTEXT
msg.post(screen.script, CONTEXT, { id = screen.id })
coroutine.yield()
screen.wait_for = nil
end
local function unload(screen, force)
if screen.unregistered then return end
if screen.proxy then
log("unload() proxy", screen.id)
if screen.auto_preload and not force then
if screen.loaded then
msg.post(screen.proxy, MSG_DISABLE)
msg.post(screen.proxy, DISABLE)
screen.loaded = false
end
screen.preloaded = true
else
screen.wait_for = WAITFOR_PROXY_UNLOADED
msg.post(screen.proxy, MSG_UNLOAD)
screen.wait_for = PROXY_UNLOADED
msg.post(screen.proxy, UNLOAD)
coroutine.yield()
screen.loaded = false
screen.preloaded = false
@@ -434,8 +411,8 @@ local function unload(screen, force)
-- 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(screen, 0)
cowait(screen, 0)
cowait(0)
cowait(0)
end
@@ -458,13 +435,9 @@ local function preload(screen)
screen.preloading = false
return false, error_message
end
screen.wait_for = WAITFOR_PROXY_LOADED
msg.post(screen.proxy, MSG_ASYNC_LOAD)
screen.wait_for = PROXY_LOADED
msg.post(screen.proxy, ASYNC_LOAD)
coroutine.yield()
screen.wait_for = nil
if screen.unregistered then
return false, "Screen was unregistered while loading"
end
elseif screen.factory then
log("preload() factory")
if collectionfactory.get_status(screen.factory) == collectionfactory.STATUS_UNLOADED then
@@ -472,9 +445,6 @@ local function preload(screen)
assert(coroutine.resume(screen.co))
end)
coroutine.yield()
if screen.unregistered then
return false, "Screen was unregistered while loading"
end
end
if collectionfactory.get_status(screen.factory) ~= collectionfactory.STATUS_LOADED then
@@ -506,7 +476,7 @@ local function load(screen)
end
if screen.proxy then
msg.post(screen.proxy, MSG_ENABLE)
msg.post(screen.proxy, ENABLE)
elseif screen.factory then
screen.factory_ids = collectionfactory.create(screen.factory)
screen.transition_url = screen.factory_ids[screen.transition_id]
@@ -519,9 +489,8 @@ end
local function transition(screen, message_id, message, wait)
log("transition()", screen.id)
if screen.unregistered then return end
if screen.transition_url then
screen.wait_for = WAITFOR_TRANSITION_DONE
screen.wait_for = M.TRANSITION.DONE
msg.post(screen.transition_url, message_id, message)
if wait then
coroutine.yield()
@@ -543,15 +512,14 @@ end
local function focus_lost(screen, next_screen)
log("focus_lost()", screen.id)
if screen.unregistered then return end
if screen.focus_url then
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(screen, 0)
cowait(screen, 0)
cowait(0)
cowait(0)
else
log("focus_lost() no focus url - ignoring")
end
@@ -672,7 +640,7 @@ local function show_in(screen, previous_screen, reload, add_to_stack, wait_for_t
return
end
-- wait one frame so that the init() of any script have time to run before starting transitions
cowait(screen, 0)
cowait(0)
reset_timestep(screen)
transition(screen, M.TRANSITION.SHOW_IN, { previous_screen = previous_screen and previous_screen.id }, wait_for_transition)
screen.visible = true
@@ -698,7 +666,7 @@ local function back_in(screen, previous_screen, wait_for_transition, cb)
return
end
-- wait one frame so that the init() of any script have time to run before starting transitions
cowait(screen, 0)
cowait(0)
reset_timestep(screen)
if previous_screen and not previous_screen.popup then
transition(screen, M.TRANSITION.BACK_IN, { previous_screen = previous_screen.id }, wait_for_transition)
@@ -1173,39 +1141,45 @@ function M.post(id, message_id, message)
assert(screens[id], ("There is no screen registered with id %s"):format(tostring(id)))
local screen = screens[id]
if screen.receiver_url then
log("post() sending message to", screen.receiver_url)
msg.post(screen.receiver_url, message_id, message)
if screen.proxy then
if screen.receiver_url then
log("post() sending message to", screen.receiver_url)
msg.post(screen.receiver_url, message_id, message)
else
return false, "Unable to post message since screen has no receiver url specified"
end
else
return false, "Unable to post message since screen has no receiver url specified. Set one using monarch.on_post()."
for id,instance in pairs(screen.factory_ids) do
msg.post(instance, message_id, message)
end
end
return true
end
function M.on_message(message_id, message, sender)
if message_id == MSG_PROXY_LOADED then
if message_id == PROXY_LOADED then
local screen = find_screen(sender)
assert(screen, "Unable to find screen for loaded proxy")
if screen.wait_for == WAITFOR_PROXY_LOADED then
if screen.wait_for == PROXY_LOADED then
assert(coroutine.resume(screen.co))
end
elseif message_id == MSG_PROXY_UNLOADED then
elseif message_id == PROXY_UNLOADED then
local screen = find_screen(sender)
assert(screen, "Unable to find screen for unloaded proxy")
if screen.wait_for == WAITFOR_PROXY_UNLOADED then
if screen.wait_for == PROXY_UNLOADED then
assert(coroutine.resume(screen.co))
end
elseif message_id == MSG_CONTEXT then
elseif message_id == CONTEXT then
local screen = find_screen(sender)
assert(screen, "Unable to find screen for current script url")
if screen.wait_for == WAITFOR_CONTEXT then
if screen.wait_for == CONTEXT then
assert(coroutine.resume(screen.co))
end
elseif message_id == M.TRANSITION.DONE then
local screen = find_transition_screen(sender)
assert(screen, "Unable to find screen for transition")
if screen.wait_for == WAITFOR_TRANSITION_DONE then
if screen.wait_for == M.TRANSITION.DONE then
assert(coroutine.resume(screen.co))
end
elseif message_id == M.TRANSITION.SHOW_IN
@@ -1218,7 +1192,7 @@ function M.on_message(message_id, message, sender)
if screen.transition_fn then
screen.transition_fn(message_id, message, sender)
end
elseif message_id == MSG_LAYOUT_CHANGED then
elseif message_id == LAYOUT_CHANGED then
local screen = find_screen(sender)
if screen and screen.transition_fn then
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("screen_keeps_input_focus_when_below_popup", false)
go.property("others_keep_input_focus_when_below_screen", false)
go.property("transition_id", hash("__DEPRECATED__"))
go.property("focus_id", hash("__DEPRECATED__"))
go.property("transition_id", hash(""))
go.property("focus_id", hash(""))
go.property("preload", false)
@@ -15,7 +15,7 @@ function init(self)
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(self.screen_factory ~= msg.url(), "You must specify either a factory URL")
local settings = {
popup = self.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("screen_keeps_input_focus_when_below_popup", false)
go.property("others_keep_input_focus_when_below_screen", false)
go.property("transition_url", msg.url("#__DEPRECATED__"))
go.property("focus_url", msg.url("#__DEPRECATED__"))
go.property("receiver_url", msg.url("#__DEPRECATED__"))
go.property("transition_url", msg.url())
go.property("focus_url", msg.url())
go.property("receiver_url", msg.url())
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(self.screen_proxy ~= url, "You must specify either a proxy URL")
assert(self.timestep_below_popup >= 0, "Timestep must be positive")
local settings = {
popup = self.popup,
popup_on_popup = self.popup_on_popup,