From 3947e86169c3ee7d282de60a8f85f19b68d5a0a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ritzl?= Date: Wed, 20 Jun 2018 07:30:38 +0200 Subject: [PATCH] Added navigation listeners --- game.project | 2 +- monarch/monarch.lua | 50 +++++++++++++++++++++++++++++++--- test/data/screens.collection | 40 +++++++++++++++++++++++++++ test/msg.lua | 52 ++++++++++++++++++++++++++++++++++++ test/test_monarch.lua | 35 +++++++++++++++++++++++- 5 files changed, 173 insertions(+), 6 deletions(-) create mode 100644 test/msg.lua diff --git a/game.project b/game.project index 2ba9b3a..94b5ddc 100644 --- a/game.project +++ b/game.project @@ -1,7 +1,7 @@ [project] title = Monarch version = 0.9 -dependencies = https://github.com/britzl/deftest/archive/1.2.1.zip +dependencies = https://github.com/britzl/deftest/archive/2.3.0.zip [bootstrap] main_collection = /example/example.collectionc diff --git a/monarch/monarch.lua b/monarch/monarch.lua index 1ddef44..df70d71 100644 --- a/monarch/monarch.lua +++ b/monarch/monarch.lua @@ -10,6 +10,7 @@ local ASYNC_LOAD = hash("async_load") local UNLOAD = hash("unload") local ENABLE = hash("enable") +-- transition messages M.TRANSITION = {} M.TRANSITION.DONE = hash("transition_done") M.TRANSITION.SHOW_IN = hash("transition_show_in") @@ -17,16 +18,25 @@ M.TRANSITION.SHOW_OUT = hash("transition_show_out") M.TRANSITION.BACK_IN = hash("transition_back_in") M.TRANSITION.BACK_OUT = hash("transition_back_out") +-- focus messages M.FOCUS = {} M.FOCUS.GAINED = hash("monarch_focus_gained") M.FOCUS.LOST = hash("monarch_focus_lost") +-- listener messages +M.SCREEN_VISIBLE = hash("monarch_screen_visible") +M.SCREEN_HIDDEN = hash("monarch_screen_hidden") + + -- all registered screens local screens = {} -- the current stack of screens local stack = {} +-- navigation listeners +local listeners = {} + -- the number of active transitions -- monarch is considered busy while there are active transitions local active_transition_count = 0 @@ -46,9 +56,15 @@ local function tohash(s) return hash_lookup[s] end +local function notify_listeners(message_id, message) + log("notify_listeners()", message_id) + for _,url in pairs(listeners) do + msg.post(url, message_id, message or {}) + end +end local function screen_from_proxy(proxy) - for _, screen in pairs(screens) do + for _,screen in pairs(screens) do if screen.proxy == proxy then return screen end @@ -57,7 +73,7 @@ end local function screen_from_script() local url = msg.url() - for _, screen in pairs(screens) do + for _,screen in pairs(screens) do if screen.script == url then return screen end @@ -188,14 +204,14 @@ end local function focus_gained(screen, previous_screen) log("focus_gained()", screen.id) if screen.focus_url then - msg.post(screen.focus_url, M.FOCUS.GAINED, {id = previous_screen and previous_screen.id}) + msg.post(screen.focus_url, M.FOCUS.GAINED, { id = previous_screen and previous_screen.id }) end end local function focus_lost(screen, next_screen) log("focus_lost()", screen.id) 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 }) end end @@ -247,6 +263,7 @@ local function show_out(screen, next_screen, cb) screen.co = nil active_transition_count = active_transition_count - 1 if cb then cb() end + notify_listeners(M.SCREEN_HIDDEN, { screen = screen.id, next_screen = next_screen.id }) end) coroutine.resume(co) end @@ -282,12 +299,14 @@ local function show_in(screen, previous_screen, reload, cb) screen.co = nil active_transition_count = active_transition_count - 1 if cb then cb() end + notify_listeners(M.SCREEN_VISIBLE, { screen = screen.id, previous_screen = previous_screen and previous_screen.id }) end) coroutine.resume(co) end local function back_in(screen, previous_screen, cb) log("back_in()", screen.id) + print("back_in()", screen.id) local co co = coroutine.create(function() active_transition_count = active_transition_count + 1 @@ -310,12 +329,14 @@ local function back_in(screen, previous_screen, cb) screen.co = nil active_transition_count = active_transition_count - 1 if cb then cb() end + notify_listeners(M.SCREEN_VISIBLE, { screen = screen.id, previous_screen = previous_screen.id }) end) coroutine.resume(co) end local function back_out(screen, next_screen, cb) log("back_out()", screen.id) + print("back_out()", screen.id) local co co = coroutine.create(function() active_transition_count = active_transition_count + 1 @@ -328,6 +349,7 @@ local function back_out(screen, next_screen, cb) screen.co = nil active_transition_count = active_transition_count - 1 if cb then cb() end + notify_listeners(M.SCREEN_HIDDEN, { screen = screen.id, next_screen = next_screen and next_screen.id }) end) coroutine.resume(co) end @@ -549,6 +571,26 @@ function M.bottom(offset) return screen and screen.id end +local function url_to_key(url) + return (url.socket or hash("")) .. (url.path or hash("")) .. (url.fragment or hash("")) +end + + +--- Add a listener to be notified of when screens are shown or hidden +-- @param url The url to notify, nil for current url +function M.add_listener(url) + url = url or msg.url() + listeners[url_to_key(url)] = url +end + + +--- Remove a previously added listener +-- @param url The url to remove, nil for current url +function M.remove_listener(url) + url = url or msg.url() + listeners[url_to_key(url)] = nil +end + function M.dump_stack() local s = "" diff --git a/test/data/screens.collection b/test/data/screens.collection index e0e15b2..593f919 100644 --- a/test/data/screens.collection +++ b/test/data/screens.collection @@ -315,3 +315,43 @@ embedded_instances { z: 1.0 } } +embedded_instances { + id: "listener1" + data: "" + position { + x: 0.0 + y: 0.0 + z: 0.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale3 { + x: 1.0 + y: 1.0 + z: 1.0 + } +} +embedded_instances { + id: "listener2" + data: "" + position { + x: 0.0 + y: 0.0 + z: 0.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale3 { + x: 1.0 + y: 1.0 + z: 1.0 + } +} diff --git a/test/msg.lua b/test/msg.lua new file mode 100644 index 0000000..2389a20 --- /dev/null +++ b/test/msg.lua @@ -0,0 +1,52 @@ +local mock = require "deftest.mock.mock" + + +local M = {} + + +local recipients = {} + +local history = {} + +local function get_recipient(url) + recipients[url] = recipients[url] or {} + return recipients[url] +end + +local function post(url, message_id, message) + local data = { url = url, message_id = message_id, message = message } + history[#history + 1] = data + local recipient = get_recipient(url) + recipient[#recipient + 1] = data + msg.post.original(url, message_id, message or {}) +end + +function M.mock() + recipients = {} + history = {} + mock.mock(msg) + msg.post.replace(post) +end + +function M.unmock() + mock.unmock(msg) +end + + + +function M.messages(url) + return url and get_recipient(url) or history +end + +function M.first(url) + local messages = url and get_recipient(url) or history + return messages[1] +end + +function M.last(url) + local messages = url and get_recipient(url) or history + return messages[#messages] +end + + +return M \ No newline at end of file diff --git a/test/test_monarch.lua b/test/test_monarch.lua index 6b14367..bd25e49 100644 --- a/test/test_monarch.lua +++ b/test/test_monarch.lua @@ -1,4 +1,6 @@ local cowait = require "test.cowait" +local mock_msg = require "test.msg" +local unload = require "deftest.util.unload" local monarch = require "monarch.monarch" local SCREEN1_STR = hash("screen1") @@ -55,12 +57,14 @@ return function() describe("monarch", function() before(function() + mock_msg.mock() monarch = require "monarch.monarch" screens_instances = collectionfactory.create("#screensfactory") end) after(function() - package.loaded["monarch.monarch"] = nil + mock_msg.unmock() + unload.unload("monarch%..*") for id,instance_id in pairs(screens_instances) do go.delete(instance_id) end @@ -229,5 +233,34 @@ return function() assert(monarch.is_busy()) assert(wait_until_not_busy()) end) + + it("should be able to notify listeners of navigation events", function() + local URL1 = msg.url(screens_instances[hash("/listener1")]) + local URL2 = msg.url(screens_instances[hash("/listener2")]) + monarch.add_listener(URL1) + monarch.add_listener(URL2) + + monarch.show(SCREEN1) + assert(wait_until_not_busy()) + + monarch.remove_listener(URL2) + monarch.show(SCREEN2) + assert(wait_until_not_busy()) + + monarch.back() + assert(wait_until_not_busy()) + + local messages_1 = mock_msg.messages(URL1) + local messages_2 = mock_msg.messages(URL2) + assert(#messages_1 == 5) + assert(#messages_2 == 1) + assert(messages_2[1].message_id == monarch.SCREEN_VISIBLE and messages_1[1].message.screen == SCREEN1) + + assert(messages_1[1].message_id == monarch.SCREEN_VISIBLE and messages_1[1].message.screen == SCREEN1) + assert(messages_1[2].message_id == monarch.SCREEN_HIDDEN and messages_1[2].message.screen == SCREEN1) + assert(messages_1[3].message_id == monarch.SCREEN_VISIBLE and messages_1[3].message.screen == SCREEN2) + assert(messages_1[4].message_id == monarch.SCREEN_HIDDEN and messages_1[4].message.screen == SCREEN2) + assert(messages_1[5].message_id == monarch.SCREEN_VISIBLE and messages_1[5].message.screen == SCREEN1) + end) end) end