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

Compare commits

..

9 Commits

Author SHA1 Message Date
Björn Ritzl
753d003861 Preload fix (#34)
* Don't try to preload if monarch is busy
* Added test for #32
2018-12-10 12:27:11 +01:00
Björn Ritzl
8a0a36a2d5 Added new screenshots and linked to them from the docs (#30) 2018-10-03 13:41:58 +02:00
Björn Ritzl
c98a8ef44a Updated to deftest-2.4.3 2018-08-01 08:10:25 +02:00
Björn Ritzl
588398e23e Updated code coverage collection 2018-08-01 07:49:27 +02:00
Björn Ritzl
75e3ac1ce9 Added code coverage badge 2018-07-31 19:07:13 +02:00
Björn Ritzl
5ec208d10d Collect code coverage 2018-07-31 19:01:22 +02:00
Björn Ritzl
3443484cce Added support for collection factories 2018-07-27 13:28:36 +02:00
Björn Ritzl
3a7187b844 Added layers to all guis in the example 2018-07-27 12:30:33 +02:00
Björn Ritzl
b73ed95315 Removed code duplication when loading 2018-07-27 09:48:59 +02:00
21 changed files with 506 additions and 178 deletions

1
.gitignore vendored
View File

@@ -8,3 +8,4 @@ build
.cproject
builtins
.internal
luacov.report.out

74
.luacov Normal file
View File

@@ -0,0 +1,74 @@
local reporter = require "luacov.reporter.defold"
--- Default values for configuration options.
-- For project specific configuration create '.luacov' file in your project
-- folder. It should be a Lua script setting various options as globals
-- or returning table of options.
-- @class module
-- @name deftest.coverage.configuration
return {
--- Reporter class to use when creating a report. Default: DefaultReporter from reporter.lua
reporter = reporter,
--- Filename to store collected stats. Default: "luacov.stats.out".
statsfile = "luacov.stats.out",
--- Filename to store report. Default: "luacov.report.out".
reportfile = "luacov.report.out",
--- Enable saving coverage data after every `savestepsize` lines?
-- Setting this flag to `true` in config is equivalent to running LuaCov
-- using `luacov.tick` module. Default: false.
tick = false,
--- Stats file updating frequency for `luacov.tick`.
-- The lower this value - the more frequently results will be written out to the stats file.
-- You may want to reduce this value (to, for example, 2) to avoid losing coverage data in
-- case your program may terminate without triggering luacov exit hooks that are supposed
-- to save the data. Default: 100.
savestepsize = 100,
--- Run reporter on completion? Default: true.
runreport = true,
--- Delete stats file after reporting? Default: false.
deletestats = true,
--- Process Lua code loaded from raw strings?
-- That is, when the 'source' field in the debug info
-- does not start with '@'. Default: true.
codefromstrings = true,
--- Lua patterns for files to include when reporting.
-- All will be included if nothing is listed.
-- Do not include the '.lua' extension. Path separator is always '/'.
-- Overruled by `exclude`.
-- @usage
-- include = {
-- "mymodule$", -- the main module
-- "mymodule%/.+$", -- and everything namespaced underneath it
-- }
include = {},
--- Lua patterns for files to exclude when reporting.
-- Nothing will be excluded if nothing is listed.
-- Do not include the '.lua' extension. Path separator is always '/'.
-- Overrules `include`.
exclude = { "^test%/.+$", "^deftest%/.+$" },
--- Table mapping names of modules to be included to their filenames.
-- Has no effect if empty.
-- Real filenames mentioned here will be used for reporting
-- even if the modules have been installed elsewhere.
-- Module name can contain '*' wildcard to match groups of modules,
-- in this case corresponding path will be used as a prefix directory
-- where modules from the group are located.
-- @usage
-- modules = {
-- ["some_rock"] = "src/some_rock.lua",
-- ["some_rock.*"] = "src"
-- }
modules = {},
}

View File

@@ -26,3 +26,6 @@ env:
script:
- "./.travis/run.sh"
after_success:
- bash <(curl -s https://codecov.io/bash)

View File

@@ -1,6 +1,7 @@
![](docs/logo.jpg)
[![Build Status](https://travis-ci.org/britzl/monarch.svg?branch=master)](https://travis-ci.org/britzl/monarch)
[![Code Coverage](https://codecov.io/gh/britzl/monarch/branch/master/graph/badge.svg)](https://codecov.io/gh/britzl/monarch)
[![Latest Release](https://img.shields.io/github/release/britzl/monarch.svg)](https://github.com/britzl/monarch/releases)
# Monarch
@@ -20,7 +21,10 @@ Using Monarch requires that screens are created in a certain way. Once you have
## Creating screens
Monarch screens are created in individual collections and loaded through collection proxies. The recommended setup is to create one game object per screen and per game object attach a collection proxy component and an instance of the ```screen.script``` provided by Monarch. The ```screen.script``` will take care of the setup of the screen. All you need to do is to make sure that the script properties on the ```screen.script``` are correct:
Monarch screens are created in individual collections and either loaded through collection proxies or created through collection factories.
### Collection proxies
For proxies the recommended setup is to create one game object per screen and per game object attach a collection proxy component and an instance of the ```screen_proxy.script``` provided by Monarch. The ```screen_proxy.script``` will take care of the setup of the screen. All you need to do is to make sure that the script properties on the script are correct:
* **Screen Proxy (url)** - The URL to the collection proxy component containing the actual screen. Defaults to ```#collectionproxy```.
* **Screen Id (hash)** - A unique id that can be used to reference the screen when navigating your app.
@@ -30,7 +34,19 @@ Monarch screens are created in individual collections and loaded through collect
* **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)).
![](docs/setup.png)
![](docs/setup_proxy.png)
### Collection factories
For factories the recommended setup is to create one game object per screen and per game object attach a collection factory component and an instance of the ```screen_factory.script``` provided by Monarch. The ```screen_factory.script``` will take care of the setup of the screen. All you need to do is to make sure that the script properties on the script are correct:
* **Screen Factory (url)** - The URL to the collection factory component containing the actual screen. Defaults to ```#collectionfactory```.
* **Screen Id (hash)** - A unique id that can be used to reference the screen when navigating your app.
* **Popup (boolean)** - Check this if the screen should be treated as a [popup](#popups).
* **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)).
![](docs/setup_factory.png)
## Navigating between screens

BIN
docs/setup_factory.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/setup_proxy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -48,7 +48,7 @@ nodes {
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -103,7 +103,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -172,7 +172,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "ok_reload_button"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -235,7 +235,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: true
parent: "root"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -284,7 +284,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -353,7 +353,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "ok_clear_button"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -402,7 +402,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -471,7 +471,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: true
parent: "ok_clearreload_button"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -520,7 +520,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -589,7 +589,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "ok_button"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -598,6 +598,12 @@ nodes {
text_leading: 1.0
text_tracking: 0.0
}
layers {
name: "below"
}
layers {
name: "text"
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@@ -48,7 +48,7 @@ nodes {
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -103,7 +103,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -172,7 +172,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "yes_button"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -221,7 +221,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -290,7 +290,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "no_button"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -362,6 +362,12 @@ nodes {
text_leading: 1.0
text_tracking: 0.0
}
layers {
name: "below"
}
layers {
name: "text"
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@@ -4,7 +4,7 @@ embedded_instances {
id: "menu"
data: "components {\n"
" id: \"screen\"\n"
" component: \"/monarch/screen.script\"\n"
" component: \"/monarch/screen_proxy.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
@@ -127,7 +127,7 @@ embedded_instances {
id: "pregame"
data: "components {\n"
" id: \"screen\"\n"
" component: \"/monarch/screen.script\"\n"
" component: \"/monarch/screen_proxy.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
@@ -190,7 +190,7 @@ embedded_instances {
id: "game"
data: "components {\n"
" id: \"screen\"\n"
" component: \"/monarch/screen.script\"\n"
" component: \"/monarch/screen_proxy.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
@@ -253,7 +253,7 @@ embedded_instances {
id: "popup"
data: "components {\n"
" id: \"screen\"\n"
" component: \"/monarch/screen.script\"\n"
" component: \"/monarch/screen_factory.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
@@ -276,21 +276,26 @@ embedded_instances {
" type: PROPERTY_TYPE_BOOLEAN\n"
" }\n"
" properties {\n"
" id: \"timestep_below_popup\"\n"
" value: \"0.0\"\n"
" type: PROPERTY_TYPE_NUMBER\n"
" id: \"popup_on_popup\"\n"
" value: \"true\"\n"
" type: PROPERTY_TYPE_BOOLEAN\n"
" }\n"
" properties {\n"
" id: \"transition_url\"\n"
" value: \"popup:/go#popup\"\n"
" type: PROPERTY_TYPE_URL\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"
"}\n"
"embedded_components {\n"
" id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n"
" data: \"collection: \\\"/example/popup.collection\\\"\\n"
"exclude: false\\n"
" id: \"collectionfactory\"\n"
" type: \"collectionfactory\"\n"
" data: \"prototype: \\\"/example/popup.collection\\\"\\n"
"load_dynamically: false\\n"
"\"\n"
" position {\n"
" x: 0.0\n"
@@ -326,7 +331,7 @@ embedded_instances {
id: "about"
data: "components {\n"
" id: \"screen\"\n"
" component: \"/monarch/screen.script\"\n"
" component: \"/monarch/screen_proxy.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
@@ -349,6 +354,11 @@ embedded_instances {
" type: PROPERTY_TYPE_BOOLEAN\n"
" }\n"
" properties {\n"
" id: \"popup_on_popup\"\n"
" value: \"true\"\n"
" type: PROPERTY_TYPE_BOOLEAN\n"
" }\n"
" properties {\n"
" id: \"transition_url\"\n"
" value: \"about:/go#about\"\n"
" type: PROPERTY_TYPE_URL\n"
@@ -394,7 +404,7 @@ embedded_instances {
id: "confirm"
data: "components {\n"
" id: \"screen\"\n"
" component: \"/monarch/screen.script\"\n"
" component: \"/monarch/screen_proxy.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"

View File

@@ -48,7 +48,7 @@ nodes {
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -103,7 +103,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -172,7 +172,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "win_button"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -235,7 +235,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "root"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -244,6 +244,12 @@ nodes {
text_leading: 1.0
text_tracking: 0.0
}
layers {
name: "below"
}
layers {
name: "text"
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@@ -48,7 +48,7 @@ nodes {
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -103,7 +103,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -172,7 +172,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "startgame_button"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -221,7 +221,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -290,7 +290,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "about_button"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -339,7 +339,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -408,7 +408,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "back_button"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -471,7 +471,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "root"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -520,7 +520,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -535,6 +535,12 @@ nodes {
template_node_child: false
size_mode: SIZE_MODE_MANUAL
}
layers {
name: "below"
}
layers {
name: "text"
}
material: "/builtins/materials/gui.material"
layouts {
name: "Landscape"
@@ -578,7 +584,7 @@ layouts {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -634,7 +640,7 @@ layouts {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -690,7 +696,7 @@ layouts {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -760,7 +766,7 @@ layouts {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "root"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0

View File

@@ -48,7 +48,7 @@ nodes {
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -103,7 +103,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -172,7 +172,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "ok_button"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -221,7 +221,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -290,7 +290,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "cancel_button"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -339,7 +339,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -408,7 +408,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "about_button"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -457,7 +457,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -472,6 +472,12 @@ nodes {
template_node_child: false
size_mode: SIZE_MODE_MANUAL
}
layers {
name: "below"
}
layers {
name: "text"
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@@ -48,7 +48,7 @@ nodes {
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -103,7 +103,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -172,7 +172,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "play_button"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -221,7 +221,7 @@ nodes {
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
layer: "below"
inherit_alpha: true
slice9 {
x: 0.0
@@ -290,7 +290,7 @@ nodes {
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "back_button"
layer: ""
layer: "text"
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
@@ -299,6 +299,12 @@ nodes {
text_leading: 1.0
text_tracking: 0.0
}
layers {
name: "below"
}
layers {
name: "text"
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@@ -1,10 +1,10 @@
[project]
title = Monarch
version = 0.9
dependencies = https://github.com/britzl/deftest/archive/2.3.0.zip
dependencies = https://github.com/britzl/deftest/archive/2.4.3.zip
[bootstrap]
main_collection = /example/example.collectionc
main_collection = /test/test.collectionc
[input]
game_binding = /input/game.input_bindingc

View File

@@ -112,9 +112,23 @@ function M.is_top(id)
end
--- Register a new screen
-- This is done automatically by the screen.script. It is expected that the
-- caller of this function is a script component attached to the same game
local function register(id, settings)
assert(id, "You must provide a screen id")
id = tohash(id)
assert(not screens[id], ("There is already a screen registered with id %s"):format(tostring(id)))
screens[id] = {
id = id,
script = msg.url(),
popup = settings and settings.popup,
popup_on_popup = settings and settings.popup_on_popup,
timestep_below_popup = settings and settings.timestep_below_popup or 1,
}
return screens[id]
end
--- Register a new screen contained in a collection proxy
-- This is done automatically by the screen_proxy.script. It is expected that
-- the caller of this function is a script component attached to the same game
-- object as the proxy. This is required since monarch will acquire and
-- release input focus of the game object where the proxy is attached.
-- @param id Unique id of the screen
@@ -128,22 +142,37 @@ 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
function M.register(id, proxy, settings)
assert(id, "You must provide a screen id")
id = tohash(id)
assert(not screens[id], ("There is already a screen registered with id %s"):format(tostring(id)))
function M.register_proxy(id, proxy, settings)
assert(proxy, "You must provide a collection proxy URL")
local url = msg.url(proxy)
screens[id] = {
id = id,
proxy = proxy,
script = msg.url(),
popup = settings and settings.popup,
popup_on_popup = settings and settings.popup_on_popup,
transition_url = settings and settings.transition_url,
focus_url = settings and settings.focus_url,
timestep_below_popup = settings and settings.timestep_below_popup or 1,
}
local screen = register(id, settings)
screen.proxy = proxy
screen.transition_url = settings and settings.transition_url
screen.focus_url = settings and settings.focus_url
end
M.register = M.register_proxy
--- Register a new screen contained in a collection factory
-- This is done automatically by the screen_factory.script. It is expected that
-- the caller of this function is a script component attached to the same game
-- object as the factory. This is required since monarch will acquire and
-- release input focus of the game object where the factory is attached.
-- @param id Unique id of the screen
-- @param factory URL to the collection factory containing the screen
-- @param settings Settings table for screen. Accepted values:
-- * popup - true the screen is a popup
-- * popup_on_popup - true if this popup can be shown on top of
-- another popup or false if an underlying popup should be closed
-- * 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
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
end
--- Unregister a screen
@@ -159,7 +188,13 @@ end
local function acquire_input(screen)
log("change_context()", screen.id)
if not screen.input then
if screen.proxy then
msg.post(screen.script, ACQUIRE_INPUT_FOCUS)
elseif screen.factory then
for id,instance in pairs(screen.factory_ids) do
msg.post(instance, ACQUIRE_INPUT_FOCUS)
end
end
screen.input = true
end
end
@@ -167,7 +202,13 @@ end
local function release_input(screen)
log("change_context()", screen.id)
if screen.input then
if screen.proxy then
msg.post(screen.script, RELEASE_INPUT_FOCUS)
elseif screen.factory then
for id,instance in pairs(screen.factory_ids) do
msg.post(instance, RELEASE_INPUT_FOCUS)
end
end
screen.input = false
end
end
@@ -182,35 +223,98 @@ end
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
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
end
end
local function async_load(screen)
log("async_load()", screen.id)
local function preload(screen)
log("preload() preloading screen", screen.id)
assert(screen.co, "You must assign a coroutine to the screen")
if screen.preloaded then
log("preload() screen already preloaded", screen.id)
return
end
if screen.proxy then
screen.wait_for = PROXY_LOADED
msg.post(screen.proxy, ASYNC_LOAD)
coroutine.yield()
elseif screen.factory then
if collectionfactory.get_status(screen.factory) == collectionfactory.STATUS_UNLOADED then
collectionfactory.load(screen.factory, function(self, url, result)
assert(coroutine.resume(screen.co))
end)
coroutine.yield()
end
if collectionfactory.get_status(screen.factory) ~= collectionfactory.STATUS_LOADED then
log("preload() error loading factory resources")
return
end
end
screen.preloaded = true
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
end
preload(screen)
if not screen.preloaded then
log("load() screen wasn't preloaded", screen.id)
return
end
if screen.proxy then
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]
screen.focus_url = screen.factory_ids[screen.focus_id]
end
screen.loaded = true
screen.wait_for = nil
screen.preloaded = false
end
local function transition(screen, message_id, message)
log("transition()", screen.id)
if screen.transition_url then
screen.wait_for = M.TRANSITION.DONE
msg.post(screen.transition_url, message_id, message)
coroutine.yield()
screen.wait_for = nil
else
log("transition() no transition url - ignoring")
end
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 })
else
log("focus_gained() no focus url - ignoring")
end
end
@@ -218,16 +322,20 @@ 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 })
else
log("focus_lost() no focus url - ignoring")
end
end
local function change_timestep(screen)
if screen.proxy then
screen.changed_timestep = true
msg.post(screen.proxy, "set_time_step", { mode = 0, factor = screen.timestep_below_popup })
end
end
local function reset_timestep(screen)
if screen.changed_timestep then
if screen.proxy and screen.changed_timestep then
msg.post(screen.proxy, "set_time_step", { mode = 0, factor = 1 })
screen.changed_timestep = false
end
@@ -249,7 +357,7 @@ local function disable(screen, next_screen)
screen.co = nil
if cb then cb() end
end)
coroutine.resume(co)
assert(coroutine.resume(co))
end
local function enable(screen, previous_screen)
@@ -264,7 +372,7 @@ local function enable(screen, previous_screen)
screen.co = nil
if cb then cb() end
end)
coroutine.resume(co)
assert(coroutine.resume(co))
end
local function show_out(screen, next_screen, cb)
@@ -308,19 +416,7 @@ local function show_in(screen, previous_screen, reload, cb)
log("show_in() reloading", screen.id)
unload(screen)
end
-- if the screen has been preloaded we need to enable it
if screen.preloaded then
log("show_in() screen was preloaded", screen.id)
msg.post(screen.proxy, ENABLE)
screen.loaded = true
screen.preloaded = false
-- the screen could be loaded if the previous screen was a popup
-- and the popup asked to show this screen again
-- in that case we shouldn't attempt to load it again
elseif not screen.loaded then
log("show_in() loading screen", screen.id)
async_load(screen)
end
load(screen)
stack[#stack + 1] = screen
reset_timestep(screen)
transition(screen, M.TRANSITION.SHOW_IN, { previous_screen = previous_screen and previous_screen.id })
@@ -342,15 +438,7 @@ local function back_in(screen, previous_screen, cb)
notify_listeners(M.SCREEN_TRANSITION_IN_STARTED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
screen.co = co
change_context(screen)
if screen.preloaded then
log("back_in() screen was preloaded", screen.id)
msg.post(screen.proxy, ENABLE)
screen.preloaded = false
screen.loaded = true
elseif not screen.loaded then
log("back_in() loading screen", screen.id)
async_load(screen)
end
load(screen)
reset_timestep(screen)
if previous_screen and not previous_screen.popup then
transition(screen, M.TRANSITION.BACK_IN, { previous_screen = previous_screen.id })
@@ -369,7 +457,7 @@ local function back_out(screen, next_screen, cb)
log("back_out()", screen.id)
local co
co = coroutine.create(function()
notify_listeners(M.SCREEN_TRANSITION_OUT_STARTED, { screen = screen.id, next_screen = next_screen.id })
notify_listeners(M.SCREEN_TRANSITION_OUT_STARTED, { screen = screen.id, next_screen = next_screen and next_screen.id })
active_transition_count = active_transition_count + 1
screen.co = co
change_context(screen)
@@ -383,7 +471,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_TRANSITION_OUT_FINISHED, { screen = screen.id, next_screen = next_screen.id })
notify_listeners(M.SCREEN_TRANSITION_OUT_FINISHED, { screen = screen.id, next_screen = next_screen and next_screen.id })
end)
coroutine.resume(co)
end
@@ -527,16 +615,23 @@ function M.back(data, cb)
return true
end
--- 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.
-- @param id (string|hash) - Id of the screen to preload
-- @param cb (function) - Optional callback to invoke when screen is loaded
function M.preload(id, cb)
if M.is_busy() then
log("preload() monarch is busy, ignoring request")
return false
end
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)))
local screen = screens[id]
log("preload()", screen.id)
if screen.preloaded or screen.loaded then
if cb then cb() end
return
@@ -545,14 +640,10 @@ function M.preload(id, cb)
co = coroutine.create(function()
screen.co = co
change_context(screen)
screen.wait_for = PROXY_LOADED
msg.post(screen.proxy, ASYNC_LOAD)
coroutine.yield()
screen.preloaded = true
screen.wait_for = nil
preload(screen)
if cb then cb() end
end)
coroutine.resume(co)
assert(coroutine.resume(co))
end

View File

@@ -1,7 +1,7 @@
local monarch
go.property("screen_proxy", msg.url("#collectionproxy"))
go.property("screen_id", hash(""))
go.property("screen_id", hash("UNIQUE ID HERE"))
go.property("popup", false)
go.property("popup_on_popup", false)
go.property("timestep_below_popup", 1)
@@ -10,19 +10,22 @@ go.property("focus_url", msg.url())
function init(self)
print("WARNING - screen.script is deprecated. Please use screen_proxy.script")
monarch = require "monarch.monarch"
local url = msg.url()
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")
monarch.register(
self.screen_id,
self.screen_proxy,
{
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,
transition_url = self.transition_url,
focus_url = self.focus_url,
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,
}
)
monarch.register_proxy(self.screen_id, self.screen_proxy, settings)
end
function final(self)
@@ -36,11 +39,6 @@ function on_message(self, message_id, message, sender)
monarch.back()
elseif message_id == hash("back") then
monarch.back()
elseif message_id == monarch.TRANSITION.SHOW_IN
or message_id == monarch.TRANSITION.SHOW_OUT
or message_id == monarch.TRANSITION.BACK_IN
or message_id == monarch.TRANSITION.BACK_OUT then
msg.post(sender, monarch.TRANSITION.DONE)
else
monarch.on_message(message_id, message, sender)
end

View File

@@ -0,0 +1,39 @@
local monarch
go.property("screen_factory", msg.url("#collectionfactory"))
go.property("screen_id", hash("UNIQUE ID HERE"))
go.property("popup", false)
go.property("popup_on_popup", false)
go.property("transition_id", hash(""))
go.property("focus_id", hash(""))
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,
transition_id = self.transition_id,
focus_id = self.focus_id,
}
monarch.register_factory(self.screen_id, self.screen_factory, settings)
end
function final(self)
monarch.unregister(self.screen_id)
end
function on_message(self, message_id, message, sender)
if message_id == hash("show") then
monarch.show(self.screen_id, { clear = message.clear })
elseif message_id == hash("hide") then
monarch.back()
elseif message_id == hash("back") then
monarch.back()
else
monarch.on_message(message_id, message, sender)
end
end

View File

@@ -0,0 +1,44 @@
local monarch
go.property("screen_proxy", msg.url("#collectionproxy"))
go.property("screen_id", hash("UNIQUE ID HERE"))
go.property("popup", false)
go.property("popup_on_popup", false)
go.property("timestep_below_popup", 1)
go.property("transition_url", msg.url())
go.property("focus_url", msg.url())
function init(self)
monarch = require "monarch.monarch"
local url = msg.url()
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,
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,
}
monarch.register_proxy(self.screen_id, self.screen_proxy, settings)
end
function final(self)
monarch.unregister(self.screen_id)
end
function on_message(self, message_id, message, sender)
if message_id == hash("show") then
monarch.show(self.screen_id, { clear = message.clear })
elseif message_id == hash("hide") then
monarch.back()
elseif message_id == hash("back") then
monarch.back()
else
monarch.on_message(message_id, message, sender)
end
end

View File

@@ -4,7 +4,7 @@ embedded_instances {
id: "screen1"
data: "components {\n"
" id: \"screen\"\n"
" component: \"/monarch/screen.script\"\n"
" component: \"/monarch/screen_proxy.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
@@ -62,7 +62,7 @@ embedded_instances {
id: "screen2"
data: "components {\n"
" id: \"screen\"\n"
" component: \"/monarch/screen.script\"\n"
" component: \"/monarch/screen_factory.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
@@ -81,10 +81,10 @@ embedded_instances {
" }\n"
"}\n"
"embedded_components {\n"
" id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n"
" data: \"collection: \\\"/test/data/screen2.collection\\\"\\n"
"exclude: false\\n"
" id: \"collectionfactory\"\n"
" type: \"collectionfactory\"\n"
" data: \"prototype: \\\"/test/data/screen2.collection\\\"\\n"
"load_dynamically: false\\n"
"\"\n"
" position {\n"
" x: 0.0\n"
@@ -120,7 +120,7 @@ embedded_instances {
id: "popup1"
data: "components {\n"
" id: \"screen\"\n"
" component: \"/monarch/screen.script\"\n"
" component: \"/monarch/screen_proxy.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
@@ -142,11 +142,6 @@ embedded_instances {
" value: \"true\"\n"
" type: PROPERTY_TYPE_BOOLEAN\n"
" }\n"
" properties {\n"
" id: \"popup_on_popup\"\n"
" value: \"false\"\n"
" type: PROPERTY_TYPE_BOOLEAN\n"
" }\n"
"}\n"
"embedded_components {\n"
" id: \"collectionproxy\"\n"
@@ -188,7 +183,7 @@ embedded_instances {
id: "popup2"
data: "components {\n"
" id: \"screen\"\n"
" component: \"/monarch/screen.script\"\n"
" component: \"/monarch/screen_proxy.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
@@ -256,7 +251,7 @@ embedded_instances {
id: "transition1"
data: "components {\n"
" id: \"screen\"\n"
" component: \"/monarch/screen.script\"\n"
" component: \"/monarch/screen_proxy.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"

View File

@@ -5,5 +5,5 @@ local test_monarch = require "test.test_monarch"
function init(self)
deftest.add(test_monarch)
deftest.run()
deftest.run({ coverage = { enabled = true }})
end

View File

@@ -63,6 +63,10 @@ return function()
end)
after(function()
while #monarch.get_stack() > 0 do
monarch.back()
wait_until_not_busy()
end
mock_msg.unmock()
unload.unload("monarch%..*")
for id,instance_id in pairs(screens_instances) do
@@ -234,6 +238,14 @@ return function()
assert(wait_until_not_busy())
end)
it("should ignore any preload calls while busy", function()
monarch.show(TRANSITION1)
-- previously a call to preload() while also showing a screen would
-- lock up monarch. See issue #32
monarch.preload(TRANSITION1)
assert(wait_until_shown(TRANSITION1), "Transition1 was never shown")
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")])
@@ -255,21 +267,30 @@ return function()
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(#mock_msg.messages(URL1) == 10)
assert(#mock_msg.messages(URL1) == 6)
assert(#mock_msg.messages(URL2) == 2)
assert(mock_msg.messages(URL1)[3].message_id == monarch.SCREEN_TRANSITION_OUT_STARTED)
assert(mock_msg.messages(URL1)[3].message.screen == SCREEN1)
assert(mock_msg.messages(URL1)[4].message_id == monarch.SCREEN_TRANSITION_IN_STARTED)
assert(mock_msg.messages(URL1)[4].message.screen == SCREEN2)
assert(mock_msg.messages(URL1)[5].message_id == monarch.SCREEN_TRANSITION_OUT_FINISHED)
assert(mock_msg.messages(URL1)[5].message.screen == SCREEN1)
assert(mock_msg.messages(URL1)[6].message_id == monarch.SCREEN_TRANSITION_IN_FINISHED)
assert(mock_msg.messages(URL1)[5].message_id == monarch.SCREEN_TRANSITION_IN_FINISHED)
assert(mock_msg.messages(URL1)[5].message.screen == SCREEN2)
assert(mock_msg.messages(URL1)[6].message_id == monarch.SCREEN_TRANSITION_OUT_FINISHED)
assert(mock_msg.messages(URL1)[6].message.screen == SCREEN1)
monarch.back()
assert(wait_until_not_busy())
assert(#mock_msg.messages(URL1) == 10)
assert(#mock_msg.messages(URL2) == 2)
assert(mock_msg.messages(URL1)[7].message_id == monarch.SCREEN_TRANSITION_OUT_STARTED)
assert(mock_msg.messages(URL1)[7].message.screen == SCREEN2)
assert(mock_msg.messages(URL1)[8].message_id == monarch.SCREEN_TRANSITION_IN_STARTED)
assert(mock_msg.messages(URL1)[8].message.screen == SCREEN1)
assert(mock_msg.messages(URL1)[9].message_id == monarch.SCREEN_TRANSITION_OUT_FINISHED)
assert(mock_msg.messages(URL1)[9].message.screen == SCREEN2)
assert(mock_msg.messages(URL1)[10].message_id == monarch.SCREEN_TRANSITION_IN_FINISHED)
assert(mock_msg.messages(URL1)[10].message.screen == SCREEN1)
end)
end)
end