From 77404998215826b05817953bc2f64fd0d3bdd5a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ritzl?= Date: Fri, 11 Jan 2019 08:47:06 +0100 Subject: [PATCH] Added support for automatically preloading screens --- README.md | 22 +++++++++++++ example/example.collection | 5 +++ monarch/monarch.lua | 62 +++++++++++++++++++++++++++-------- monarch/screen_factory.script | 2 ++ monarch/screen_proxy.script | 2 ++ test/test_monarch.lua | 24 +++++++++++++- 6 files changed, 103 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index e9a65d1..a880f4a 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ For proxies the recommended setup is to create one game object per screen and pe * **Timestep below Popup (number)** - Timestep to set on screen proxy when it is below a popup. This is useful when pausing animations and gameplay while a popup is open. * **Transition Url (url)** - Optional URL to call when the screen is about to be shown/hidden. Use this to trigger a transition (see the section on [transitions](#transitions)). * **Focus Url (url)** - Optional URL to call when the screen gains or loses focus (see the section on [screen focus](#screen-focus-gainloss)). +* **Preload (boolean)** - Check this if the screen should be preloaded and kept loaded at all times. For a collection proxy it means that it will be async loaded but not enabled at all times while not visible. This can also temporarily be achieved through the `monarch.preload()` function. ![](docs/setup_proxy.png) @@ -45,6 +46,7 @@ For factories the recommended setup is to create one game object per screen and * **Popup on Popup (boolean)** - Check this if the screen is a [popup](#popups) and it can be shown on top of other popups. * **Transition Id (url)** - Optional id of the game object to send a message to when the screen is about to be shown/hidden. Use this to trigger a transition (see the section on [transitions](#transitions)). * **Focus Id (url)** - Optional id of the game object to send a message to when the screen gains or loses focus (see the section on [screen focus](#screen-focus-gainloss)). +* **Preload (boolean)** - Check this if the screen should be preloaded and kept loaded at all times. For a collection factory this means that its resources will be dynamically loaded at all times. This can also temporarily be achieved through the `monarch.preload()` function. ![](docs/setup_factory.png) @@ -358,6 +360,26 @@ Check if Monarch is busy showing and/or hiding a screen. * ```busy``` (boolean) - True if busy hiding and/or showing a screen. +### monarch.is_top(id) +Check if a Monarch screen is at the top of the view stack. + +**PARAMETERS** +* ```screen_id``` (hash) - Id of the screen to check + +**RETURN** +* ```exists``` (boolean) - True if the screen is at the top of the stack. + + +### monarch.is_visible(id) +Check if a Monarch screen is visible. + +**PARAMETERS** +* ```screen_id``` (hash) - Id of the screen to check + +**RETURN** +* ```exists``` (boolean) - True if the screen is visible. + + ### monarch.add_listener([url]) Add a URL that will be notified of navigation events. diff --git a/example/example.collection b/example/example.collection index e0f85b7..7daa76a 100644 --- a/example/example.collection +++ b/example/example.collection @@ -280,6 +280,11 @@ embedded_instances { " value: \"about:/go#about\"\n" " type: PROPERTY_TYPE_URL\n" " }\n" + " properties {\n" + " id: \"preload\"\n" + " value: \"true\"\n" + " type: PROPERTY_TYPE_BOOLEAN\n" + " }\n" "}\n" "embedded_components {\n" " id: \"collectionproxy\"\n" diff --git a/monarch/monarch.lua b/monarch/monarch.lua index 50d79dd..d87f0ac 100644 --- a/monarch/monarch.lua +++ b/monarch/monarch.lua @@ -11,6 +11,7 @@ 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 = {} @@ -112,6 +113,17 @@ function M.is_top(id) end +--- Check if a screen is visible +-- @param id (string|hash) +-- @return true if the screen is visible +function M.is_visible(id) + assert(id, "You must provide a screen id") + id = tohash(id) + assert(screens[id], ("There is no screen registered with id %s"):format(tostring(id))) + return screens[id].loaded +end + + local function register(id, settings) assert(id, "You must provide a screen id") id = tohash(id) @@ -142,12 +154,17 @@ end -- * focus_url - URL to a script that is to be notified of focus -- lost/gained events -- * timestep_below_popup - Timestep to set on proxy when below a popup +-- * 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") local screen = register(id, settings) screen.proxy = proxy screen.transition_url = settings and settings.transition_url screen.focus_url = settings and settings.focus_url + screen.auto_preload = settings and settings.auto_preload + if screen.auto_preload then + M.preload(id) + end end M.register = M.register_proxy @@ -167,12 +184,17 @@ M.register = M.register_proxy -- 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") local screen = register(id, settings) screen.factory = factory 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.auto_preload then + M.preload(id) + end end --- Unregister a screen @@ -225,18 +247,31 @@ local function unload(screen) log("unload()", screen.id) if screen.proxy then - screen.wait_for = PROXY_UNLOADED - msg.post(screen.proxy, UNLOAD) - coroutine.yield() - screen.loaded = false - screen.wait_for = nil + if screen.auto_preload then + msg.post(screen.proxy, DISABLE) + screen.loaded = false + screen.preloaded = true + else + screen.wait_for = PROXY_UNLOADED + msg.post(screen.proxy, UNLOAD) + coroutine.yield() + screen.loaded = false + screen.preloaded = false + screen.wait_for = nil + end elseif screen.factory then for id, instance in pairs(screen.factory_ids) do go.delete(instance) end screen.factory_ids = nil - collectionfactory.unload(screen.factory) - screen.loaded = false + if screen.auto_preload then + screen.loaded = false + screen.preloaded = true + else + collectionfactory.unload(screen.factory) + screen.loaded = false + screen.preloaded = false + end end end @@ -273,7 +308,7 @@ end local function load(screen) log("load()", screen.id) assert(screen.co, "You must assign a coroutine to the screen") - + if screen.loaded then log("load() screen already loaded", screen.id) return @@ -285,7 +320,7 @@ local function load(screen) log("load() screen wasn't preloaded", screen.id) return end - + if screen.proxy then msg.post(screen.proxy, ENABLE) elseif screen.factory then @@ -522,7 +557,7 @@ function M.show(id, options, data, cb) end local callbacks = callback_tracker() - + id = tohash(id) assert(screens[id], ("There is no screen registered with id %s"):format(tostring(id))) @@ -585,7 +620,7 @@ function M.back(data, cb) end local callbacks = callback_tracker() - + local screen = table.remove(stack) if screen then log("back()", screen.id) @@ -611,7 +646,7 @@ function M.back(data, cb) end if cb then callbacks.when_done(cb) end - + return true end @@ -634,7 +669,7 @@ function M.preload(id, cb) log("preload()", screen.id) if screen.preloaded or screen.loaded then if cb then cb() end - return + return true end local co co = coroutine.create(function() @@ -644,6 +679,7 @@ function M.preload(id, cb) if cb then cb() end end) assert(coroutine.resume(co)) + return true end diff --git a/monarch/screen_factory.script b/monarch/screen_factory.script index 4c00e25..03d1a1c 100644 --- a/monarch/screen_factory.script +++ b/monarch/screen_factory.script @@ -6,6 +6,7 @@ go.property("popup", false) go.property("popup_on_popup", false) go.property("transition_id", hash("")) go.property("focus_id", hash("")) +go.property("preload", false) function init(self) @@ -18,6 +19,7 @@ function init(self) popup_on_popup = self.popup_on_popup, transition_id = self.transition_id, focus_id = self.focus_id, + auto_preload = self.preload, } monarch.register_factory(self.screen_id, self.screen_factory, settings) end diff --git a/monarch/screen_proxy.script b/monarch/screen_proxy.script index 1fc5191..e956cc0 100644 --- a/monarch/screen_proxy.script +++ b/monarch/screen_proxy.script @@ -7,6 +7,7 @@ go.property("popup_on_popup", false) go.property("timestep_below_popup", 1) go.property("transition_url", msg.url()) go.property("focus_url", msg.url()) +go.property("preload", false) function init(self) @@ -22,6 +23,7 @@ function init(self) transition_url = self.transition_url ~= url and self.transition_url or nil, focus_url = self.focus_url ~= url and self.focus_url or nil, timestep_below_popup = self.timestep_below_popup, + auto_preload = self.preload, } monarch.register_proxy(self.screen_id, self.screen_proxy, settings) diff --git a/test/test_monarch.lua b/test/test_monarch.lua index 1f9987e..657a2cb 100644 --- a/test/test_monarch.lua +++ b/test/test_monarch.lua @@ -103,6 +103,28 @@ return function() assert_stack({ }) end) + it("should be able to tell if a screen is visible or not", function() + assert(not monarch.is_visible(SCREEN1)) + monarch.show(SCREEN1) + assert(wait_until_shown(SCREEN1), "Screen1 was never shown") + assert_stack({ SCREEN1 }) + assert(monarch.is_visible(SCREEN1)) + + monarch.show(SCREEN2) + assert(wait_until_hidden(SCREEN1), "Screen1 was never hidden") + assert(wait_until_shown(SCREEN2), "Screen2 was never shown") + assert_stack({ SCREEN1, SCREEN2 }) + assert(not monarch.is_visible(SCREEN1)) + assert(monarch.is_visible(SCREEN2)) + + monarch.show(POPUP1) + assert(wait_until_shown(POPUP1), "Popup1 was never shown") + assert_stack({ SCREEN1, SCREEN2, POPUP1 }) + assert(not monarch.is_visible(SCREEN1)) + assert(monarch.is_visible(SCREEN2)) + assert(monarch.is_visible(POPUP1)) + end) + it("should be able to pass data to a screen when showning it or going back to it", function() local data1 = { foo = "bar" } @@ -147,7 +169,7 @@ return function() end) - it("should be able to show one popup on top of another the Popup On Popup flag is set", function() + it("should be able to show one popup on top of another if the Popup On Popup flag is set", function() monarch.show(SCREEN1) assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert_stack({ SCREEN1 })