3
0
mirror of https://github.com/britzl/monarch.git synced 2025-06-27 18:37:46 +02:00

Compare commits

..

62 Commits

Author SHA1 Message Date
Björn Ritzl
9a6d5f7a7b Update ci-workflow.yml 2025-05-15 08:58:40 +02:00
Björn Ritzl
d68e27b2ad Update ci-workflow.yml 2025-05-15 08:57:50 +02:00
Björn Ritzl
5525707744 Cleaned up callback tests
Fixes #
2025-05-14 22:56:47 +02:00
Alexander
aea267cc35
fix gui template (#112)
https://github.com/britzl/monarch/issues/111
2024-09-27 08:58:51 +02:00
Pawel
6ba3064749
Fixed an error that appears when trying to delete an instance that is already deleted. (#109)
* Prevent deleting already removed instances.

* Prehashed empty hash.

* Use go.exists instead of pcall, when detecting non existing instances.
2024-03-04 16:06:56 +01:00
Björn Ritzl
b78b896ada Update README_API.md 2024-01-30 11:08:38 +01:00
SalavatR
679482f84d
Add a check for missing resources in collection proxies. (#107)
* Update monarch.lua

* Update monarch.lua

* Update monarch.lua
2024-01-30 11:03:37 +01:00
Björn Ritzl
4c6e26fd71 Removed backward comp fix for monarch.back() 2024-01-12 08:31:48 +01:00
bedryck
00c30792a9
fix a problem of sequential in back function (#104) 2024-01-12 08:22:43 +01:00
Björn Ritzl
b01f5d28c3 Update monarch.lua 2023-12-27 15:54:09 +01:00
Pete Garcin
43e847dacc
Guard DEPRECATED transition_id overwriting transition_url (#100) 2023-12-27 15:52:36 +01:00
Björn Ritzl
ce5ca26b6c Update README_TRANSITIONS.md
Fixes #102
2023-12-27 15:51:38 +01:00
Björn Ritzl
742779d749 Allow monarch.post() to loaded screens
Fixes #98
2023-10-26 13:54:14 +02:00
Björn Ritzl
84944f3f22 Added monarch.is_loaded() 2023-10-26 11:43:19 +02:00
Björn Ritzl
c473aa053c Update monarch.lua 2023-08-31 09:25:00 +02:00
Björn Ritzl
169236acbd Improved on_post() to also accept a url 2023-08-31 09:22:24 +02:00
Björn Ritzl
b7053d2ce4 Fixed another back issue (and tests) 2023-08-10 00:21:15 +02:00
Björn Ritzl
91204ca30b
Wait for both in and out transitions (#97) 2023-08-10 00:08:10 +02:00
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
Björn Ritzl
2eb67bf29d Squashed commit of the following:
commit 698cdba5a469f900b902da360ad05e103cd1a39b
Author: Björn Ritzl <bjorn.ritzl@gmail.com>
Date:   Wed Mar 29 11:10:49 2023 +0200

    Documentation

commit d8de338a465066a9b9ff1bb6d20b5476e78d6359
Author: Björn Ritzl <bjorn.ritzl@gmail.com>
Date:   Wed Mar 29 10:40:38 2023 +0200

    Added focus and post listener setup functions

commit 55910abd74542513795dae3ccb9b4fd87da22b74
Author: Björn Ritzl <bjorn.ritzl@gmail.com>
Date:   Wed Mar 29 09:49:00 2023 +0200

    Update gui.lua

commit a055af032fb47d67647e0b7bc09ad4721fd623d7
Author: Björn Ritzl <bjorn.ritzl@gmail.com>
Date:   Wed Mar 29 09:19:49 2023 +0200

    Improve transition setup
2023-07-27 23:53:37 +02:00
Björn Ritzl
fe8341263a Update gui.lua 2023-03-29 09:20:37 +02:00
Björn Ritzl
e0b0a286e3 Added sliding window example 2023-03-28 15:42:39 +02:00
Björn Ritzl
c14e4a8a46 Added more screens to the basic example 2023-02-10 00:09:06 +01:00
Björn Ritzl
482e319285 Removed luacheckrc file 2023-02-09 23:39:44 +01:00
Björn Ritzl
9c7ce61f6f Removed unused local var 2023-02-09 23:39:04 +01:00
Björn Ritzl
f5afbad233 Fixed global assignment 2023-02-09 23:38:53 +01:00
Björn Ritzl
6fdde9d2a8 Update run.sh 2023-01-22 15:45:39 +01:00
Björn Ritzl
d963b583b1 Moved test script 2023-01-22 15:39:02 +01:00
Björn Ritzl
6fe59c92c4 Make sure to force unload a preloaded screen 2022-07-23 00:09:51 +02:00
Björn Ritzl
286a270a1c Check if screen was loaded before trying to unload 2022-07-05 22:54:40 +02:00
Björn Ritzl
c0d45c3d5c Added keep_loaded option to preload()
Fixes #88
2022-07-04 22:43:25 +02:00
Björn Ritzl
bcc6264cd4 Remove screen from stack when unregistered
Fixes #87
2022-05-29 14:47:34 +02:00
Björn Ritzl
1dec704047 Delay reset of timestep when going back from popup
Fixes #84
2022-02-23 12:46:04 +01:00
Björn Ritzl
9764c68475 Update callback_tracker.lua
Fixes #83
2022-01-27 12:16:43 +01:00
Björn Ritzl
d57d550859 Fixed issues with missing variables 2022-01-27 12:15:52 +01:00
Björn Ritzl
17df189089 Replace existing transition
Fixes #82
2022-01-13 19:40:24 +01:00
Björn Ritzl
387a1805eb Added option to change timestep for screen when below popup
Fixes #73
2021-09-19 23:08:31 +02:00
Björn Ritzl
e26da2c6c5 Removed old change log file 2021-09-19 23:05:43 +02:00
Björn Ritzl
c75b1b0044 A screen added with no_stack should not manipulate the stack when hidden 2021-08-19 14:53:05 +02:00
Björn Ritzl
d3799a93ff Make sure that callbacks aren't invoked more than once 2021-08-19 14:35:05 +02:00
Björn Ritzl
76d4ca2927 Added monarch.clear() 2021-07-20 00:27:34 +02:00
Björn Ritzl
9e81b3a327 Removed one-frame delay during show/back-in
If the screen had a transition the delay caused the screen to render in its initial position for one frame before the transition was applied
2021-07-01 15:04:52 +02:00
Björn Ritzl
4927d6e766 Update stack earlier when showing new screen
Fixes #76
2021-06-27 00:34:32 +02:00
Björn Ritzl
20cf731fdb
Wait with screen unload until next screen is loaded and ready to be shown (#74)
* Removed screen flicker when transitioning between screens

* Moved advanced example to subfolder. Added basic example.

* Remove flicker on back navigation too

* Fix issue with no_stack screens added at any time
2021-06-24 07:45:09 +02:00
Björn Ritzl
466b558e73 Update game.project 2021-04-26 22:49:08 +02:00
Björn Ritzl
398e78670f Moved advanced example to subfolder. Added basic example. 2021-04-26 22:48:44 +02:00
Björn Ritzl
a19bdd78e8
Added monarch.is_popup() (#70) 2021-02-14 11:53:52 +01:00
Björn Ritzl
5771a40e68 Update README.md 2021-01-10 14:23:54 +01:00
Björn Ritzl
1099390cce Merge branch 'master' of https://github.com/britzl/monarch 2021-01-10 14:20:35 +01:00
Björn Ritzl
36b92b5294 Split up the docs and added FAQ 2021-01-10 14:20:33 +01:00
74 changed files with 4169 additions and 999 deletions

View File

@ -7,13 +7,13 @@ jobs:
name: Build and run name: Build and run
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: actions/setup-java@v1 - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9
with: with:
java-version: '11' java-version: '21.0.5+11.0.LTS'
distribution: 'temurin'
- name: Run.sh - name: Run.sh
env: env:
DEFOLD_USER: bjorn.ritzl@gmail.com DEFOLD_USER: bjorn.ritzl@gmail.com
DEFOLD_AUTH: foobar DEFOLD_AUTH: foobar
DEFOLD_BOOSTRAP_COLLECTION: /test/test.collectionc run: ./.test/run.sh
run: ./.travis/run.sh

3
.gitignore vendored
View File

@ -9,3 +9,6 @@ build
builtins builtins
.internal .internal
luacov.report.out luacov.report.out
/.editor_settings
manifest.private.der
manifest.public.der

View File

@ -1,42 +0,0 @@
std = "max"
files['.luacheckrc'].global = false
unused_args = false
globals = {
"sys",
"go",
"gui",
"label",
"render",
"crash",
"sprite",
"sound",
"tilemap",
"spine",
"particlefx",
"physics",
"factory",
"collectionfactory",
"iac",
"msg",
"vmath",
"url",
"http",
"image",
"json",
"zlib",
"iap",
"push",
"facebook",
"hash",
"hash_to_hex",
"pprint",
"init",
"final",
"update",
"on_input",
"on_message",
"on_reload",
"window",
"unityads"
}

View File

@ -5,7 +5,6 @@ else
PLATFORM="$1" PLATFORM="$1"
fi fi
echo "${PLATFORM}" echo "${PLATFORM}"
# {"version": "1.2.89", "sha1": "5ca3dd134cc960c35ecefe12f6dc81a48f212d40"} # {"version": "1.2.89", "sha1": "5ca3dd134cc960c35ecefe12f6dc81a48f212d40"}
@ -16,26 +15,28 @@ echo "Using Defold dmengine_headless version ${SHA1}"
# Create dmengine_headless and bob.jar URLs # Create dmengine_headless and bob.jar URLs
DMENGINE_URL="http://d.defold.com/archive/${SHA1}/engine/${PLATFORM}/dmengine_headless" DMENGINE_URL="http://d.defold.com/archive/${SHA1}/engine/${PLATFORM}/dmengine_headless"
BOB_URL="http://d.defold.com/archive/${SHA1}/bob/bob.jar" BOB_URL="http://d.defold.com/archive/${SHA1}/bob/bob.jar"
DMENGINE_FILE=dmengine_headless_${SHA1}
BOB_FILE=bob_${SHA1}.jar
# Download dmengine_headless # Download dmengine_headless
echo "Downloading ${DMENGINE_URL}" if ! [ -f ${DMENGINE_FILE} ]; then
curl -o dmengine_headless ${DMENGINE_URL} echo "Downloading ${DMENGINE_URL} to ${DMENGINE_FILE}"
chmod +x dmengine_headless curl -L -o ${DMENGINE_FILE} ${DMENGINE_URL}
chmod +x ${DMENGINE_FILE}
fi
# Download bob.jar # Download bob.jar
echo "Downloading ${BOB_URL}" if ! [ -f ${BOB_FILE} ]; then
curl -o bob.jar ${BOB_URL} echo "Downloading ${BOB_URL} to ${BOB_FILE}"
curl -L -o ${BOB_FILE} ${BOB_URL}
fi
# Fetch libraries # Fetch libraries
echo "Running bob.jar - resolving dependencies" echo "Running ${BOB_FILE} - resolving dependencies"
java -jar bob.jar --auth "foobar" --email "john@doe.com" resolve java -jar ${BOB_FILE} --auth "foobar" --email "john@doe.com" resolve
echo "Running bob.jar - building" echo "Running ${BOB_FILE} - building"
java -jar bob.jar --debug build --keep-unused java -jar ${BOB_FILE} --debug build --settings=test.settings
echo "Starting dmengine_headless" echo "Starting ${DMENGINE_FILE}"
if [ -n "${DEFOLD_BOOSTRAP_COLLECTION}" ]; then ./${DMENGINE_FILE}
./dmengine_headless --config=bootstrap.main_collection=${DEFOLD_BOOSTRAP_COLLECTION}
else
./dmengine_headless
fi

View File

@ -1,30 +0,0 @@
sudo: required
dist: bionic
script:
- sudo unlink /usr/bin/gcc && sudo ln -s /usr/bin/gcc-5 /usr/bin/gcc
- gcc --version
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- gcc-5
- g++-5
language: java
jdk:
- oraclejdk11
env:
global:
- DEFOLD_BOOSTRAP_COLLECTION=/test/test.collectionc
script:
- "./.travis/run.sh"
after_success:
- bash <(curl -s https://codecov.io/bash)

View File

@ -1,60 +0,0 @@
## Monarch 2.8.0 [britzl released 2018-06-10]
NEW: Prevent show/hide operations while busy showing/hiding another screen
FIX: Make sure to properly finish active transitions when layout changes
## Monarch 2.7.0 [britzl released 2018-06-04]
NEW: Added monarch.top([offset]) and monarch.bottom([offset]) to get screen id of top and bottom screens (w. optional offset)
NEW: Transition messages now contain `next_screen` or `previous_screen`
## Monarch 2.6.1 [britzl released 2018-06-04]
FIX: Check if screen has already been preloaded before trying to preload it again (the callback will still be invoked).
## Monarch 2.6.0 [britzl released 2018-06-03]
NEW: monarch.preload() to load but not show a screen. Useful for content heavy screens that you wish to show without delay.
## Monarch 2.5.0 [britzl released 2018-06-01]
NEW: Transitions will send a `transition_done` message to the creator of the transition to notify that the transition has finished. The `message` will contain which transition that was finished.
## Monarch 2.4.0 [britzl released 2018-05-26]
NEW: Screen transitions are remembered so that they can be replayed when the screen layout changes.
## Monarch 2.3.0 [britzl released 2018-03-24]
CHANGE: The functions in monarch.lua that previously only accepted a hash as screen id now also accepts strings (and does the conversion internally)
## Monarch 2.2.0 [britzl released 2018-03-19]
NEW: Transitions now handle layout changes (via `layout_changed` message)
NEW: Transitions can now be notified of changes in window size using transition.window_resize(width, height)
## Monarch 2.1 [britzl released 2017-12-27]
NEW: Added Popup on Popup flag that allows a popup to be shown on top of another popup
## Monarch 2.0 [britzl released 2017-12-08]
BREAKING CHANGE: If you are using custom screen transitions (ie your own transition functions) you need to make a change to the function. The previous function signature was ```(node, to, easing, duration, delay, url)``` where ```url``` was the URL to where the ```transition_done``` message was supposed to be posted. The new function signature for a transition function is: ```(node, to, easing, duration, delay, cb)``` where ```cb``` is a function that should be invoked when the transition is completed.
FIX: Fixed issues related to screen transitions.
FIX: Code cleanup to reduce code duplication.
FIX: Improved documentation regarding transitions.
## Monarch 1.4 [britzl released 2017-12-06]
FIX: Several bugfixes for specific corner cases.
## Monarch 1.3 [britzl released 2017-12-01]
FIX: monarch.back(data, cb) set the data on the previous screen not the new current screen.
NEW: monarch.is_top(id)
NEW: monarch.get_stack()
NEW: monarch.in_stack(id)
## Monarch 1.2 [britzl released 2017-11-28]
NEW: Message id constants exposed from the Monarch module
NEW: Focus lost/gained contains id of next/previous screen
## Monarch 1.1 [britzl released 2017-11-22]
FIX: Bugfixes for transitions and state under certain circumstances
NEW: Added 'reload' option to show() command.
## Monarch 1.0 [britzl released 2017-09-28]
First public stable release
## Monarch 0.9 [britzl released 2017-09-17]

432
README.md
View File

@ -28,33 +28,33 @@ Right click in on a`.gui` file in the outline and selected the menu item, it cre
Monarch screens are created in individual collections and either loaded through collection proxies or created through collection factories. Monarch screens are created in individual collections and either loaded through collection proxies or created through collection factories.
### Collection proxies ### 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: 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 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. * **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 (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. * **Popup on Popup (boolean)** - Check this if the screen is a [popup](#popups) and it can be shown on top of other popups.
* **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. * **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.
* **Screen Keeps Input Focus When Below Popup (boolean)** - Check this if the screen should keep input focus when it is below a popup. * **Screen Keeps Input Focus When Below Popup (boolean)** - Check this if the screen should keep input focus when it is below a popup.
* **Others Keep Input Focus When Below Screen (boolean)** - Check this if other screens should keep input focus when below this screen. * **Others Keep Input Focus When Below Screen (boolean)** - Check this if other screens should keep input focus when below this screen.
* **Transition Url (url)** - Optional URL to post messages to when the screen is about to be shown/hidden. Use this to trigger a transition (see the section on [transitions](#transitions)). * **Transition Url (url)** - **DEPRECATED** Optional URL to post messages to 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 post messages to when the screen gains or loses focus (see the section on [screen focus](#screen-focus-gainloss)). * **Focus Url (url)** - **DEPRECATED** Optional URL to post messages to when the screen gains or loses focus (see the section on [screen focus](#screen-focus-gainloss)).
* **Receiver Url (url)** - Optional URL to post messages to using `monarch.post()`. * **Receiver Url (url)** - **DEPRECATED** Optional URL to post messages to using `monarch.post()`.
* **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. * **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) ![](docs/setup_proxy.png)
### Collection factories ### 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: 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 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. * **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 (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. * **Popup on Popup (boolean)** - Check this if the screen is a [popup](#popups) and it can be shown on top of other popups.
* **Screen Keeps Input Focus When Below Popup (boolean)** - Check this if the screen should keep input focus when it is below a popup. * **Screen Keeps Input Focus When Below Popup (boolean)** - Check this if the screen should keep input focus when it is below a popup.
* **Others Keep Input Focus When Below Screen (boolean)** - Check this if other screens should keep input focus when below this screen. * **Others Keep Input Focus When Below Screen (boolean)** - Check this if other screens should keep input focus when below this screen.
* **Transition Id (hash)** - 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)). * **Transition Id (hash)** - **DEPRECATED** 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 (hash)** - 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)). * **Focus Id (hash)** - **DEPRECATED** 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. * **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) ![](docs/setup_factory.png)
@ -68,21 +68,21 @@ Sometimes it might be desirable to have a screen that contains one or more sub-s
The navigation in Monarch is based around a stack of screens. When a screen is shown it is pushed to the top of the stack. When going back to a previous screen the topmost screen on the stack is removed. Example: The navigation in Monarch is based around a stack of screens. When a screen is shown it is pushed to the top of the stack. When going back to a previous screen the topmost screen on the stack is removed. Example:
* Showing screen A * Showing screen A
* Stack is ```[A]``` * Stack is `[A]`
* Showing screen B * Showing screen B
* Stack is ```[A, B]``` - (B is on top) * Stack is `[A, B]` - (B is on top)
* Going back * Going back
* Stack is ```[A]``` * Stack is `[A]`
### Showing a new screen ### Showing a new screen
You show a screen in one of two ways: You show a screen in one of two ways:
1. Post a ```show``` message to the screen script (either `screen_proxy.script` or `screen_factory.script`) 1. Post a `show` message to the screen script (either `screen_proxy.script` or `screen_factory.script`)
2. Call ```monarch.show()``` (see below) 2. Call `monarch.show()` (see below)
Showing a screen will push it to the top of the stack and trigger an optional transition. The previous screen will be hidden (with an optional transition) unless the screen to be shown is a [popup](#popups). Showing a screen will push it to the top of the stack and trigger an optional transition. The previous screen will be hidden (with an optional transition) unless the screen to be shown is a [popup](#popups).
NOTE: You must ensure that the ```init()``` function of the screen script (either `screen_proxy.script` or `screen_factory.script`) has run. The ```init()``` function is responsible for registering the screen and it's not possible to show it until this has happened. A good practice is to delay the first call by posting a message to a controller script or similar before calling ```monarch.show()``` the first time: NOTE: You must ensure that the `init()` function of the screen script (either `screen_proxy.script` or `screen_factory.script`) has run. The `init()` function is responsible for registering the screen and it's not possible to show it until this has happened. A good practice is to delay the first call by posting a message to a controller script or similar before calling `monarch.show()` the first time:
function init(self) function init(self)
msg.post("#", "show_first_screen") msg.post("#", "show_first_screen")
@ -93,17 +93,17 @@ NOTE: You must ensure that the ```init()``` function of the screen script (eithe
end end
#### Preventing duplicates in the stack #### Preventing duplicates in the stack
You can pass an optional ```clear``` flag when showing a screen (either as a key value pair in the options table when calling ```monarch.show()``` or in the message). If the clear flag is set Monarch will search the stack for the screen in question. If the screen already exists in the stack and the ```clear``` flag is set Monarch will remove all screens between the current top and the screen in question. Example: You can pass an optional `clear` flag when showing a screen (either as a key value pair in the options table when calling `monarch.show()` or in the message). If the clear flag is set Monarch will search the stack for the screen in question. If the screen already exists in the stack and the `clear` flag is set Monarch will remove all screens between the current top and the screen in question. Example:
* Stack is ```[A, B, C, D]``` - (D is on top) * Stack is `[A, B, C, D]` - (D is on top)
* A call to ```monarch.show(B, { clear = true })``` is made * A call to `monarch.show(B, { clear = true })` is made
* Stack is ```[A, B]``` * Stack is `[A, B]`
As opposed to if the ```clear``` flag was not set: As opposed to if the `clear` flag was not set:
* Stack is ```[A, B, C, D]``` - (D is on top) * Stack is `[A, B, C, D]` - (D is on top)
* A call to ```monarch.show(B, { clear = false })``` is made * A call to `monarch.show(B, { clear = false })` is made
* Stack is ```[A, B, C, D, B]``` - (B is on top) * Stack is `[A, B, C, D, B]` - (B is on top)
#### Showing a screen without adding it to the stack #### Showing a screen without adding it to the stack
Monarch can also show a screen without adding it to the stack. This can be used to for instance load a collection containing a background that you want to have visible at all times. You show and hide such a screen like this: Monarch can also show a screen without adding it to the stack. This can be used to for instance load a collection containing a background that you want to have visible at all times. You show and hide such a screen like this:
@ -117,8 +117,8 @@ Monarch can also show a screen without adding it to the stack. This can be used
### Going back to a previous screen ### Going back to a previous screen
You navigate back in the screen hierarchy in one of two ways: You navigate back in the screen hierarchy in one of two ways:
1. Post a ```back``` message to the screen script (either `screen_proxy.script` or `screen_factory.script`) 1. Post a `back` message to the screen script (either `screen_proxy.script` or `screen_factory.script`)
2. Call ```monarch.back()``` (see below) 2. Call `monarch.back()` (see below)
## Input focus ## Input focus
@ -131,378 +131,64 @@ A screen that is flagged as a popup (see [list of screen properties](#creating-s
### Popup on normal screen ### Popup on normal screen
If a popup is shown on top of a non-popup the current top screen will not be unloaded and instead remain visible in the background: If a popup is shown on top of a non-popup the current top screen will not be unloaded and instead remain visible in the background:
* Stack is ```[A, B]``` * Stack is `[A, B]`
* A call to ```monarch.show(C)``` is made and C is a popup * A call to `monarch.show(C)` is made and C is a popup
* Stack is ```[A, B, C]``` and B will still be visible * Stack is `[A, B, C]` and B will still be visible
### Popup on popup ### Popup on popup
If a popup is at the top of the stack and another popup is shown the behavior will depend on if the new popup has the Popup on Popup flag set or not. If the Popup on Popup flag is set the underlying popup will remain visible. If a popup is at the top of the stack and another popup is shown the behavior will depend on if the new popup has the Popup on Popup flag set or not. If the Popup on Popup flag is set the underlying popup will remain visible.
* Stack is ```[A, B, C]``` and C is a popup * Stack is `[A, B, C]` and C is a popup
* A call to ```monarch.show(D)``` is made and D is a popup with the popup on popup flag set * A call to `monarch.show(D)` is made and D is a popup with the popup on popup flag set
* Stack is ```[A, B, C, D]``` * Stack is `[A, B, C, D]`
If the Popup on Popup flag is not set then the underlying popup will be closed, just as when showing a normal screen on top of a popup (see above). If the Popup on Popup flag is not set then the underlying popup will be closed, just as when showing a normal screen on top of a popup (see above).
* Stack is ```[A, B, C]``` and C is a popup * Stack is `[A, B, C]` and C is a popup
* A call to ```monarch.show(D)``` is made and D is a popup without the popup on popup flag set * A call to `monarch.show(D)` is made and D is a popup without the popup on popup flag set
* Stack is ```[A, B, D]``` * Stack is `[A, B, D]`
### Screen on popup ### Screen on popup
If a screen is shown on top of one or more popups they will all be removed from the stack: If a screen is shown on top of one or more popups they will all be removed from the stack:
* Stack is ```[A, B, C, D]``` and C and D are popups * Stack is `[A, B, C, D]` and C and D are popups
* A call to ```monarch.show(E)``` is made and E is not a popup * A call to `monarch.show(E)` is made and E is not a popup
* Stack is ```[A, B, E]``` * Stack is `[A, B, E]`
## Transitions ## Transitions
You can add optional transitions when navigating between screens. The default behavior is that screen navigation is instant but if you have defined a transition for a screen Monarch will wait until the transition is completed before proceeding. The `Transition Url` (proxy) or `Transition Id` (collectionfactory) property described above should be the URL/Id to a script with an ```on_message``` handlers for the following messages: You can add optional transitions when navigating between screens. This is [described in detail here](/README_TRANSITIONS.md).
* ```transition_show_in``` (constant defined as ```monarch.TRANSITION.SHOW_IN```)
* ```transition_show_out``` (constant defined as ```monarch.TRANSITION.SHOW_OUT```)
* ```transition_back_in``` (constant defined as ```monarch.TRANSITION.BACK_IN```)
* ```transition_back_out``` (constant defined as ```monarch.TRANSITION.BACK_OUT```)
When a transition is completed it is up to the developer to send a ```transition_done``` (constant ```monarch.TRANSITION.DONE```) message back to the sender to indicate that the transition is completed and that Monarch can continue the navigation sequence.
### Predefined transitions
Monarch comes with a system for setting up transitions easily in a gui_script using the ```monarch.transitions.gui``` module. Example:
local transitions = require "monarch.transitions.gui"
local monarch = require "monarch.monarch"
function init(self)
-- create transitions for the node 'root'
-- the node will slide in/out from left and right with
-- a specific easing, duration and delay
self.transition = transitions.create(gui.get_node("root"))
.show_in(transitions.slide_in_right, gui.EASING_OUTQUAD, 0.6, 0)
.show_out(transitions.slide_out_left, gui.EASING_INQUAD, 0.6, 0)
.back_in(transitions.slide_in_left, gui.EASING_OUTQUAD, 0.6, 0)
.back_out(transitions.slide_out_right, gui.EASING_INQUAD, 0.6, 0)
end
function on_message(self, message_id, message, sender)
self.transition.handle(message_id, message, sender)
-- you can also check when a transition has completed:
if message_id == monarch.TRANSITION.DONE and message.transition == monarch.TRANSITION.SHOW_IN then
print("Show in done!")
end
end
It is also possible to assign transitions to multiple nodes:
function init(self)
self.transition = transitions.create() -- note that no node is passed to transition.create()!
.show_in(gui.get_node("node1"), transitions.slide_in_right, gui.EASING_OUTQUAD, 0.6, 0)
.show_in(gui.get_node("node2"), transitions.slide_in_right, gui.EASING_OUTQUAD, 0.6, 0)
end
The predefined transitions provided by ```monarch.transitions.gui``` are:
* ```slide_in_right```
* ```slide_in_left```
* ```slide_in_top```
* ```slide_in_bottom```
* ```slide_out_right```
* ```slide_out_left```
* ```slide_out_top```
* ```slide_out_bottom```
* ```scale_in```
* ```scale_out```
* ```fade_in``` - Set node alpha to fully transparent (i.e. 0.0) and fade to fully opaque (i.e. 1.0)
* ```fade_out``` - Set node alpha to fully opaque (i.e. 1.0) and fade to fully transparent (i.e. 0.0)
Additionally there's functionality to create a full set of transitions for common transition styles:
* ```transitions.in_right_out_left(node, duration, [delay], [easing])```
* ```transitions.in_left_out_right(node, duration, [delay], [easing])```
* ```transitions.in_left_out_left(node, duration, [delay], [easing])```
* ```transitions.in_right_out_right(node, duration, [delay], [easing])```
* ```transitions.fade_in_out(node, duration, [delay], [easing])```
**PARAMETERS**
* ```node``` (node) - Gui node to animate.
* ```duration``` (number) - Transition duration in seconds.
* ```delay``` (number) - Transition delay in seconds.
* ```easing``` (table) - Easing table, created from a function provided by ```monarch.transitions.easings```
**RETURN**
* ```instance``` - The created transition instance
### Custom transitions
You can create and use your own transition as long as the provided transition function has the following function signature:
custom_transition(node, to, easing, duration, delay, cb)
**PARAMETERS**
* ```node``` (node) - Gui node to animate.
* ```to``` (vector3) - Target position.
* ```easing``` (number) - One of gui.EASING_* constants.
* ```duration``` (number) - Transition duration in seconds.
* ```delay``` (number) - Transition delay in seconds.
* ```cb``` (function) - This function must be called when the transition is completed.
### Dynamic orientation and resized windows
When using dynamic screen orientation together with gui layouts or using transitions on a platform where the window can be resized it's important to make sure that the created transitions adapt to the change in orientation or window size. The transition system takes care of layout changes automatically, but when it comes to changes in window size you need to notify the transition manually:
local transitions = require "monarch.transitions.gui"
function init(self)
self.transition = transitions.create(gui.get_node("root"))
end
function on_message(self, message_id, message, sender)
if message_id == hash("my_resize_message") then
self.transition.window_resized(message.width, message.height)
end
end
### Screen stack info and transitions
The transition message sent to the Transition Url specified in the screen configuration contains additional information about the transition. For the ```transition_show_in``` and ```transition_back_out``` messages the message contains the previous screen id:
function on_message(self, message_id, message, sender)
if message_id == hash("transition_show_in") or message_id == hash("transition_back_out") then
print(message.previous_screen)
end
end
For the ```transition_show_out``` and ```transition_back_in``` messages the message contains the next screen id:
function on_message(self, message_id, message, sender)
if message_id == hash("transition_show_out") or message_id == hash("transition_back_in") then
print(message.next_screen)
end
end
This information can be used to create dynamic transitions where the direction of the transition depends on the previous/next screen
## Screen focus gain/loss ## Screen focus gain/loss
Monarch will send focus gain and focus loss messages if a `Focus Url` (proxy) or `Focus Id` (collectionfactory) was provided when the screen was created. The focus gained message will contain the id of the previous screen and the focus loss message will contain the id of the next screen. Example: Monarch will send focus gain and focus loss messages if a focus change listener has been set using `monarch.on_focus_change(screen_id, fn)`
DEPRECATED: ~~Monarch will send focus gain and focus loss messages if a `Focus Url` (proxy) or `Focus Id` (collectionfactory) was provided when the screen was created.~~
The focus gained message will contain the id of the previous screen and the focus loss message will contain the id of the next screen. Example:
```lua
local monarch = require "monarch.monarch" local monarch = require "monarch.monarch"
function on_message(self, message_id, message, sender) function init(self)
if message_id == monarch.FOCUS.GAINED then monarch.on_focus_changed("foobar", function(message_id, message, sender)
print("Focus gained, previous screen: ", message.id) if message_id == monarch.FOCUS.GAINED then
elseif message_id == monarch.FOCUS.LOST then print("Focus gained, previous screen: ", message.id)
print("Focus lost, next screen: ", message.id) elseif message_id == monarch.FOCUS.LOST then
end print("Focus lost, next screen: ", message.id)
end
end)
end end
function on_message(self, message_id, message, sender)
monarch.on_message(message_id, message, sender)
end
```
## Callbacks ## Callbacks
Both the ```monarch.show()``` and ```monarch.back()``` functions take an optional callback function that will be invoked when the ```transition_show_in``` (or the ```transition_back_in``` in the case of a ```monarch.back()``` call) transition is completed. The transition is considered completed when a ```transition_done``` message has been received (see section on [transitions](#transitions) above). Both the `monarch.show()` and `monarch.back()` functions take an optional callback function that will be invoked when the `transition_show_in` (or the `transition_back_in` in the case of a `monarch.back()` call) transition is completed. The transition is considered completed when a `transition_done` message has been received (see section on [transitions](#transitions) above).
## Monarch API ## Monarch API
The full [Monarch API is documented here](/README_API.md).
### monarch.show(screen_id, [options], [data], [callback])
Show a Monarch screen. Note that the screen must be registered before it can be shown. The ```init()``` function of the screen script (either `screen_proxy.script` or `screen_factory.script`) takes care of registration. This operation will be added to the queue if Monarch is busy.
**PARAMETERS**
* ```screen_id``` (string|hash) - Id of the screen to show.
* ```options``` (table) - Options when showing the new screen (see below).
* ```data``` (table) - Optional data to associate with the screen. Retrieve using ```monarch.data()```.
* ```callback``` (function) - Optional function to call when the new screen is visible.
The options table can contain the following fields:
* ```clear``` (boolean) - If the `clear` flag is set Monarch will search the stack for the screen that is to be shown. If the screen already exists in the stack and the clear flag is set Monarch will remove all screens between the current top and the screen in question.
* ```reload``` (boolean) - If the `reload` flag is set Monarch will reload the collection proxy if it's already loaded (this can happen if the previous screen was a popup).
* ```no_stack``` (boolean) - If the `no_stack` flag is set Monarch will load the screen without adding it to the screen stack.
* ```sequential``` (boolean) - If the `sequential` flag is set Monarch will start loading the screen only after the previous screen finished transitioning out.
* ```pop``` (number) - If `pop` is set to a number, Monarch will pop that number of screens from the stack before adding the new one.
### monarch.replace(screen_id, [options], [data], [callback])
Replace the top of the stack with a new screen. Equivalent to calling `monarch.show()` with `pop = 1`. It takes the same parameters as `monarch.show()`.
### monarch.hide(screen_id, [callback])
Hide a screen that has been shown using the `no_stack` option. If used on a screen that was shown without the `no_stack` option it will only hide it if the screen is on top of the stack and the behavior will be exactly like if `monarch.back()` had been called. This operation will be added to the queue if Monarch is busy.
**PARAMETERS**
* ```screen_id``` (string|hash) - Id of the screen to hide.
* ```callback``` (function) - Optional function to call when the screen has been hidden.
**RETURN**
* ```success``` (boolean) - True if the process of hiding the screen was started successfully.
### monarch.back([data], [callback])
Go back to a previous Monarch screen. This operation will be added to the queue if Monarch is busy.
**PARAMETERS**
* ```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.
### monarch.preload(screen_id, [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.
**PARAMETERS**
* ```screen_id``` (string|hash) - Id of the screen to preload.
* ```callback``` (function) - Optional function to call when the screen is preloaded.
### monarch.is_preloading(screen_id)
Check if a Monarch screen is preloading (via monarch.preload() or the Preload screen setting).
**PARAMETERS**
* ```screen_id``` (string|hash) - Id of the screen to check
**RETURN**
* ```preloading``` (boolean) - True if the screen is preloading.
### monarch.when_preloaded(screen_id, callback)
Invoke a callback when a screen has been preloaded.
**PARAMETERS**
* ```screen_id``` (string|hash) - Id of the screen to check
* ```callback``` (function) - Function to call when the screen has been preloaded.
### monarch.unload(screen_id, [callback])
Unload a preloaded Monarch screen. A preloaded screen will automatically get unloaded when hidden, but this function can be useful if a screen has been preloaded and it needs to be unloaded again without actually hiding it. This operation will be added to the queue if Monarch is busy.
**PARAMETERS**
* ```screen_id``` (string|hash) - Id of the screen to unload.
* ```callback``` (function) - Optional function to call when the screen is unloaded.
### monarch.top([offset])
Get the id of the screen at the top of the stack.
**PARAMETERS**
* ```offset``` (number) - Optional offset from the top of the stack, ie -1 to get the previous screen
**RETURN**
* ```screen_id``` (string|hash) - Id of the requested screen
### monarch.bottom([offset])
Get the id of the screen at the bottom of the stack.
**PARAMETERS**
* ```offset``` (number) - Optional offset from the bottom of the stack
**RETURN**
* ```screen_id``` (string|hash) - Id of the requested screen
### monarch.data(screen_id)
Get the data associated with a screen (from a call to ```monarch.show()``` or ```monarch.back()```).
**PARAMETERS**
* ```screen_id``` (string|hash) - Id of the screen to get data for
**RETURN**
* ```data``` (table) - Data associated with the screen.
### monarch.screen_exists(screen_id)
Check if a Monarch screen exists.
**PARAMETERS**
* ```screen_id``` (string|hash) - Id of the screen to get data for
**RETURN**
* ```exists``` (boolean) - True if the screen exists.
### monarch.is_busy()
Check if Monarch is busy showing and/or hiding a screen.
**RETURN**
* ```busy``` (boolean) - True if busy hiding and/or showing a screen.
### monarch.is_top(screen_id)
Check if a Monarch screen is at the top of the view stack.
**PARAMETERS**
* ```screen_id``` (string|hash) - Id of the screen to check
**RETURN**
* ```exists``` (boolean) - True if the screen is at the top of the stack.
### monarch.is_visible(screen_id)
Check if a Monarch screen is visible.
**PARAMETERS**
* ```screen_id``` (string|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.
**PARAMETERS**
* ```url``` (url) - URL to send navigation events to. Will use current URL if omitted.
### monarch.remove_listener([url])
Remove a previously added listener.
**PARAMETERS**
* ```url``` (url) - URL to remove. Will use current URL if omitted.
### 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.
**PARAMETERS**
* ```screen_id``` (string|hash) - Id of the screen to post message to
* ```message_id``` (string|hash) - Id of the message to send
* ```message``` (table|nil) - Optional message data to send
**RETURN**
* ```result``` (boolean) - True if the message was sent
* ```error``` (string|nil) - Error message if unable to send message
### monarch.debug()
Enable verbose logging of the internals of Monarch.
### monarch.SCREEN_TRANSITION_IN_STARTED
Message sent to listeners added using `monarch.add_listener()` when a screen has started to transition in.
**PARAMETERS**
* ```screen``` (hash) - Id of the screen
* ```previous_screen``` (hash) - Id of the previous screen (if any)
### monarch.SCREEN_TRANSITION_IN_FINISHED
Message sent to listeners added using `monarch.add_listener()` when a screen has finished to transition in.
**PARAMETERS**
* ```screen``` (hash) - Id of the screen
* ```previous_screen``` (hash) - Id of the previous screen (if any)
### monarch.SCREEN_TRANSITION_OUT_STARTED
Message sent to listeners added using `monarch.add_listener()` when a screen has started to transition out.
**PARAMETERS**
* ```screen``` (hash) - Id of the screen
* ```next_screen``` (hash) - Id of the next screen (if any)
### monarch.SCREEN_TRANSITION_OUT_FINISHED
Message sent to listeners added using `monarch.add_listener()` when a screen has finished to transition out.
**PARAMETERS**
* ```screen``` (hash) - Id of the screen
* ```next_screen``` (hash) - Id of the next screen (if any)

265
README_API.md Normal file
View File

@ -0,0 +1,265 @@
# Monarch API
## monarch.show(screen_id, [options], [data], [callback])
Show a Monarch screen. Note that the screen must be registered before it can be shown. The `init()` function of the screen script (either `screen_proxy.script` or `screen_factory.script`) takes care of registration. This operation will be added to the queue if Monarch is busy.
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to show.
* `options` (table) - Options when showing the new screen (see below).
* `data` (table) - Optional data to associate with the screen. Retrieve using `monarch.data()`.
* `callback` (function) - Optional function to call when the new screen is visible.
The options table can contain the following fields:
* `clear` (boolean) - If the `clear` flag is set Monarch will search the stack for the screen that is to be shown. If the screen already exists in the stack and the clear flag is set Monarch will remove all screens between the current top and the screen in question.
* `reload` (boolean) - If the `reload` flag is set Monarch will reload the collection proxy if it's already loaded (this can happen if the previous screen was a popup).
* `no_stack` (boolean) - If the `no_stack` flag is set Monarch will load the screen without adding it to the screen stack.
* `sequential` (boolean) - If the `sequential` flag is set Monarch will start loading the screen only after the previous screen finished transitioning out.
* `pop` (number) - If `pop` is set to a number, Monarch will pop that number of screens from the stack before adding the new one.
## monarch.replace(screen_id, [options], [data], [callback])
Replace the top of the stack with a new screen. Equivalent to calling `monarch.show()` with `pop = 1`. It takes the same parameters as `monarch.show()`.
## monarch.hide(screen_id, [callback])
Hide a screen that has been shown using the `no_stack` option. If used on a screen that was shown without the `no_stack` option it will only hide it if the screen is on top of the stack and the behavior will be exactly like if `monarch.back()` had been called. This operation will be added to the queue if Monarch is busy.
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to hide.
* `callback` (function) - Optional function to call when the screen has been hidden.
**RETURN**
* `success` (boolean) - True if the process of hiding the screen was started successfully.
## monarch.clear([callback])
Clear the stack of screens completely. Any visible screen will be hidden by navigating back out from them. This operation will be added to the queue if Monarch is busy.
**PARAMETERS**
* `callback` (function) - Optional function to call when the stack has been cleared.
## monarch.back([options], [data], [callback])
Go back to a previous Monarch screen. This operation will be added to the queue if Monarch is busy.
**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()`.
* `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])
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.
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to preload.
* `options` (table)
* `callback` (function) - Optional function to call when the screen is preloaded.
The options table can contain the following fields:
* `keep_loaded` (boolean) - If the `keep_loaded` flag is set Monarch will keep the screen preloaded even after a `hide()` or `back()` navigation event that normally would unload the screen.
## monarch.is_preloading(screen_id)
Check if a Monarch screen is preloading (via monarch.preload() or the Preload screen setting).
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to check
**RETURN**
* `preloading` (boolean) - True if the screen is preloading.
## monarch.when_preloaded(screen_id, callback)
Invoke a callback when a screen has been preloaded.
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to check
* `callback` (function) - Function to call when the screen has been preloaded.
## monarch.unload(screen_id, [callback])
Unload a preloaded Monarch screen. A preloaded screen will automatically get unloaded when hidden, but this function can be useful if a screen has been preloaded and it needs to be unloaded again without actually hiding it. This operation will be added to the queue if Monarch is busy.
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to unload.
* `callback` (function) - Optional function to call when the screen is unloaded.
## monarch.top([offset])
Get the id of the screen at the top of the stack.
**PARAMETERS**
* `offset` (number) - Optional offset from the top of the stack, ie -1 to get the previous screen
**RETURN**
* `screen_id` (string|hash) - Id of the requested screen
## monarch.bottom([offset])
Get the id of the screen at the bottom of the stack.
**PARAMETERS**
* `offset` (number) - Optional offset from the bottom of the stack
**RETURN**
* `screen_id` (string|hash) - Id of the requested screen
## monarch.data(screen_id)
Get the data associated with a screen (from a call to `monarch.show()` or `monarch.back()`).
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to get data for
**RETURN**
* `data` (table) - Data associated with the screen.
## monarch.screen_exists(screen_id)
Check if a Monarch screen exists.
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to get data for
**RETURN**
* `exists` (boolean) - True if the screen exists.
## monarch.is_busy()
Check if Monarch is busy showing and/or hiding a screen.
**RETURN**
* `busy` (boolean) - True if busy hiding and/or showing a screen.
## monarch.is_top(screen_id)
Check if a Monarch screen is at the top of the view stack.
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to check
**RETURN**
* `exists` (boolean) - True if the screen is at the top of the stack.
## monarch.is_visible(screen_id)
Check if a Monarch screen is visible.
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to check
**RETURN**
* `exists` (boolean) - True if the screen is visible.
## monarch.set_timestep_below_popup(screen_id, timestep)
Set the timestep to apply for a screen when below a popup.
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to change timestep setting for
* `timestep` (number) - Timestep to apply
## monarch.add_listener([url])
Add a URL that will be notified of navigation events.
**PARAMETERS**
* `url` (url) - URL to send navigation events to. Will use current URL if omitted.
## monarch.remove_listener([url])
Remove a previously added listener.
**PARAMETERS**
* `url` (url) - URL to remove. Will use current URL if omitted.
## 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()`.
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen to post message to
* `message_id` (string|hash) - Id of the message to send
* `message` (table|nil) - Optional message data to send
**RETURN**
* `result` (boolean) - True if the message was sent
* `error` (string|nil) - Error message if unable to send message
## monarch.on_transition(screen_id, fn)
Set a function to be called when a screen should transition in our out. The function will receive (message_id, message, sender) with `message_id` being one of the transition constants.
IMPORTANT! You must call `monarch.on_message(message_id, message, sender)` from the same script as this function was called.
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen
* `fn` (function) - The function to call when a transition should start
## monarch.on_focus_change(screen_id, fn)
Set a function to be called when a screen gains or loses focus. The function will receive (message_id, message, sender) with `message_id` being one of the focus change constants.
IMPORTANT! You must call `monarch.on_message(message_id, message, sender)` from the same script as this function was called.
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen
* `fn` (function) - The function to call screen focus changes
## monarch.on_post(screen_id, fn_or_url)
Set either a function to be called when `msg.post()` is called on a specific screen or a URL where the message is sent.
IMPORTANT! If you provide a function you must also make sure to call `monarch.on_message(message_id, message, sender)` from the same script as this function was called.
**PARAMETERS**
* `screen_id` (string|hash) - Id of the screen
* `fn_or_url` (function) - The function to call or URL to send message to
## monarch.has_missing_resources(screen_id)
Check if a screen has any missing resources. If the screen is loaded by a collection proxy with Load Dynamically this will use `collectionproxy.missing_resources()`. For other cases this function will always return false.
**RETURN**
* `result` (boolean) - true if the screen has missing resources
## monarch.debug()
Enable verbose logging of the internals of Monarch.
## monarch.SCREEN_TRANSITION_IN_STARTED
Message sent to listeners added using `monarch.add_listener()` when a screen has started to transition in.
**PARAMETERS**
* `screen` (hash) - Id of the screen
* `previous_screen` (hash) - Id of the previous screen (if any)
## monarch.SCREEN_TRANSITION_IN_FINISHED
Message sent to listeners added using `monarch.add_listener()` when a screen has finished to transition in.
**PARAMETERS**
* `screen` (hash) - Id of the screen
* `previous_screen` (hash) - Id of the previous screen (if any)
## monarch.SCREEN_TRANSITION_OUT_STARTED
Message sent to listeners added using `monarch.add_listener()` when a screen has started to transition out.
**PARAMETERS**
* `screen` (hash) - Id of the screen
* `next_screen` (hash) - Id of the next screen (if any)
## monarch.SCREEN_TRANSITION_OUT_FINISHED
Message sent to listeners added using `monarch.add_listener()` when a screen has finished to transition out.
**PARAMETERS**
* `screen` (hash) - Id of the screen
* `next_screen` (hash) - Id of the next screen (if any)

143
README_TRANSITIONS.md Normal file
View File

@ -0,0 +1,143 @@
# Transitions
You can add optional transitions when navigating between screens. The default behavior is that screen navigation is instant but if you have defined a transition for a screen Monarch will wait until the transition is completed before proceeding.
Transitions are configured through the `monarch.on_transition(screen_id, fn)` function. The function defines for which screen to configure transitions and sets a function to be called when a transition should be started. This function must accept (message_id, message, sender) as arguments, with `message_id` defining which type of transition to start:
* `transition_show_in` (constant defined as `monarch.TRANSITION.SHOW_IN`)
* `transition_show_out` (constant defined as `monarch.TRANSITION.SHOW_OUT`)
* `transition_back_in` (constant defined as `monarch.TRANSITION.BACK_IN`)
* `transition_back_out` (constant defined as `monarch.TRANSITION.BACK_OUT`)
When a transition is completed it is up to the developer to send a `transition_done` (constant `monarch.TRANSITION.DONE`) message back to the sender to indicate that the transition is completed and that Monarch can continue the navigation sequence.
## Transition URL
This property is deprecated and will be removed in a future version of Monarch.
~~It is also possible to configure transitions through the `Transition Url` (proxy) or `Transition Id` (collectionfactory) property. This property must be the URL/Id to a script with an `on_message` handlers for the transition messages mentioned above.~~
## Predefined transitions
Monarch comes with a system for setting up transitions easily in a gui_script using the `monarch.transitions.gui` module. Example:
```lua
local transitions = require "monarch.transitions.gui"
local monarch = require "monarch.monarch"
function init(self)
-- create transitions for the node 'root'
-- the node will slide in/out from left and right with
-- a specific easing, duration and delay
local transition = transitions.create(gui.get_node("root"))
.show_in(transitions.slide_in_right, gui.EASING_OUTQUAD, 0.6, 0)
.show_out(transitions.slide_out_left, gui.EASING_INQUAD, 0.6, 0)
.back_in(transitions.slide_in_left, gui.EASING_OUTQUAD, 0.6, 0)
.back_out(transitions.slide_out_right, gui.EASING_INQUAD, 0.6, 0)
monarch.on_transition("foobar", transition)
end
function on_message(self, message_id, message, sender)
monarch.on_message(message_id, message, sender)
-- you can also check when a transition has completed:
if message_id == monarch.TRANSITION.DONE and message.transition == monarch.TRANSITION.SHOW_IN then
print("Show in done!")
end
end
```
It is also possible to assign transitions to multiple nodes:
```lua
function init(self)
local transition = transitions.create() -- note that no node is passed to transition.create()!
.show_in(gui.get_node("node1"), transitions.slide_in_right, gui.EASING_OUTQUAD, 0.6, 0)
.show_in(gui.get_node("node2"), transitions.slide_in_right, gui.EASING_OUTQUAD, 0.6, 0)
end
```
The predefined transitions provided by `monarch.transitions.gui` are:
* `slide_in_right`
* `slide_in_left`
* `slide_in_top`
* `slide_in_bottom`
* `slide_out_right`
* `slide_out_left`
* `slide_out_top`
* `slide_out_bottom`
* `scale_in`
* `scale_out`
* `fade_in` - Set node alpha to fully transparent (i.e. 0.0) and fade to fully opaque (i.e. 1.0)
* `fade_out` - Set node alpha to fully opaque (i.e. 1.0) and fade to fully transparent (i.e. 0.0)
Additionally there's functionality to create a full set of transitions for common transition styles:
* `transitions.in_right_out_left(node, duration, [delay], [easing])`
* `transitions.in_left_out_right(node, duration, [delay], [easing])`
* `transitions.in_left_out_left(node, duration, [delay], [easing])`
* `transitions.in_right_out_right(node, duration, [delay], [easing])`
* `transitions.fade_in_out(node, duration, [delay], [easing])`
**PARAMETERS**
* `node` (node) - Gui node to animate.
* `duration` (number) - Transition duration in seconds.
* `delay` (number) - Transition delay in seconds.
* `easing` (table) - Easing table, created from a function provided by `monarch.transitions.easings`
**RETURN**
* `instance` - The created transition instance
## Custom transitions
You can create and use your own transition as long as the provided transition function has the following function signature:
custom_transition(node, to, easing, duration, delay, cb)
**PARAMETERS**
* `node` (node) - Gui node to animate.
* `to` (vector3) - Target position.
* `easing` (number) - One of gui.EASING_* constants.
* `duration` (number) - Transition duration in seconds.
* `delay` (number) - Transition delay in seconds.
* `cb` (function) - This function must be called when the transition is completed.
## Dynamic orientation and resized windows
When using dynamic screen orientation together with gui layouts or using transitions on a platform where the window can be resized it's important to make sure that the created transitions adapt to the change in orientation or window size. The transition system takes care of layout changes automatically, but when it comes to changes in window size you need to notify the transition manually:
```lua
local transitions = require "monarch.transitions.gui"
function init(self)
self.transition = transitions.create(gui.get_node("root"))
end
function on_message(self, message_id, message, sender)
if message_id == hash("my_resize_message") then
transition.window_resized(message.width, message.height)
end
end
```
## Screen stack info and transitions
The transition message sent transition listener script contains additional information about the transition. For the `transition_show_in` and `transition_back_out` messages the message contains the previous screen id:
```lua
function on_message(self, message_id, message, sender)
if message_id == hash("transition_show_in") or message_id == hash("transition_back_out") then
print(message.previous_screen)
end
end
```
For the `transition_show_out` and `transition_back_in` messages the message contains the next screen id:
```lua
function on_message(self, message_id, message, sender)
if message_id == hash("transition_show_out") or message_id == hash("transition_back_in") then
print(message.next_screen)
end
end
```
This information can be used to create dynamic transitions where the direction of the transition depends on the previous/next screen.

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

@ -4,7 +4,7 @@ embedded_instances {
id: "go" id: "go"
data: "components {\n" data: "components {\n"
" id: \"about\"\n" " id: \"about\"\n"
" component: \"/example/about.gui\"\n" " component: \"/example/advanced/about.gui\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"
" y: 0.0\n" " y: 0.0\n"

View File

@ -1,4 +1,4 @@
script: "/example/about.gui_script" script: "/example/advanced/about.gui_script"
fonts { fonts {
name: "example" name: "example"
font: "/assets/example.font" font: "/assets/example.font"

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

@ -1,4 +1,4 @@
name: "example" name: "advanced"
scale_along_z: 0 scale_along_z: 0
embedded_instances { embedded_instances {
id: "menu" id: "menu"
@ -21,22 +21,15 @@ embedded_instances {
" value: \"menu\"\n" " value: \"menu\"\n"
" type: PROPERTY_TYPE_HASH\n" " type: PROPERTY_TYPE_HASH\n"
" }\n" " }\n"
" properties {\n" " property_decls {\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"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"collectionfactory\"\n" " id: \"collectionfactory\"\n"
" type: \"collectionfactory\"\n" " type: \"collectionfactory\"\n"
" data: \"prototype: \\\"/example/menu.collection\\\"\\n" " data: \"prototype: \\\"/example/advanced/menu.collection\\\"\\n"
"load_dynamically: true\\n" "load_dynamically: true\\n"
"dynamic_prototype: false\\n"
"\"\n" "\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"
@ -72,7 +65,7 @@ embedded_instances {
id: "main" id: "main"
data: "components {\n" data: "components {\n"
" id: \"main\"\n" " id: \"main\"\n"
" component: \"/example/main.script\"\n" " component: \"/example/advanced/advanced.script\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"
" y: 0.0\n" " y: 0.0\n"
@ -84,10 +77,12 @@ embedded_instances {
" z: 0.0\n" " z: 0.0\n"
" w: 1.0\n" " w: 1.0\n"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"components {\n" "components {\n"
" id: \"gui\"\n" " id: \"gui\"\n"
" component: \"/example/debug.gui\"\n" " component: \"/example/advanced/debug.gui\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"
" y: 0.0\n" " y: 0.0\n"
@ -99,6 +94,8 @@ embedded_instances {
" z: 0.0\n" " z: 0.0\n"
" w: 1.0\n" " w: 1.0\n"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"" ""
position { position {
@ -139,16 +136,13 @@ embedded_instances {
" value: \"pregame\"\n" " value: \"pregame\"\n"
" type: PROPERTY_TYPE_HASH\n" " type: PROPERTY_TYPE_HASH\n"
" }\n" " }\n"
" properties {\n" " property_decls {\n"
" id: \"transition_url\"\n"
" value: \"pregame:/go#pregame\"\n"
" type: PROPERTY_TYPE_URL\n"
" }\n" " }\n"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"collectionproxy\"\n" " id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n" " type: \"collectionproxy\"\n"
" data: \"collection: \\\"/example/pregame.collection\\\"\\n" " data: \"collection: \\\"/example/advanced/pregame.collection\\\"\\n"
"exclude: false\\n" "exclude: false\\n"
"\"\n" "\"\n"
" position {\n" " position {\n"
@ -202,16 +196,13 @@ embedded_instances {
" value: \"game\"\n" " value: \"game\"\n"
" type: PROPERTY_TYPE_HASH\n" " type: PROPERTY_TYPE_HASH\n"
" }\n" " }\n"
" properties {\n" " property_decls {\n"
" id: \"transition_url\"\n"
" value: \"game:/go#game\"\n"
" type: PROPERTY_TYPE_URL\n"
" }\n" " }\n"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"collectionproxy\"\n" " id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n" " type: \"collectionproxy\"\n"
" data: \"collection: \\\"/example/game.collection\\\"\\n" " data: \"collection: \\\"/example/advanced/game.collection\\\"\\n"
"exclude: false\\n" "exclude: false\\n"
"\"\n" "\"\n"
" position {\n" " position {\n"
@ -276,25 +267,17 @@ 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"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"collectionproxy\"\n" " id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n" " type: \"collectionproxy\"\n"
" data: \"collection: \\\"/example/about.collection\\\"\\n" " data: \"collection: \\\"/example/advanced/about.collection\\\"\\n"
"exclude: false\\n" "exclude: false\\n"
"\"\n" "\"\n"
" position {\n" " position {\n"
@ -363,16 +346,13 @@ embedded_instances {
" value: \"true\"\n" " value: \"true\"\n"
" type: PROPERTY_TYPE_BOOLEAN\n" " type: PROPERTY_TYPE_BOOLEAN\n"
" }\n" " }\n"
" properties {\n" " property_decls {\n"
" id: \"transition_url\"\n"
" value: \"confirm:/go#confirm\"\n"
" type: PROPERTY_TYPE_URL\n"
" }\n" " }\n"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"collectionproxy\"\n" " id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n" " type: \"collectionproxy\"\n"
" data: \"collection: \\\"/example/confirm.collection\\\"\\n" " data: \"collection: \\\"/example/advanced/confirm.collection\\\"\\n"
"exclude: false\\n" "exclude: false\\n"
"\"\n" "\"\n"
" position {\n" " position {\n"
@ -426,12 +406,15 @@ embedded_instances {
" value: \"background\"\n" " value: \"background\"\n"
" type: PROPERTY_TYPE_HASH\n" " type: PROPERTY_TYPE_HASH\n"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"collectionfactory\"\n" " id: \"collectionfactory\"\n"
" type: \"collectionfactory\"\n" " type: \"collectionfactory\"\n"
" data: \"prototype: \\\"/example/background.collection\\\"\\n" " data: \"prototype: \\\"/example/advanced/background.collection\\\"\\n"
"load_dynamically: false\\n" "load_dynamically: false\\n"
"dynamic_prototype: false\\n"
"\"\n" "\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"

View File

@ -4,7 +4,7 @@ embedded_instances {
id: "go" id: "go"
data: "components {\n" data: "components {\n"
" id: \"confirm\"\n" " id: \"confirm\"\n"
" component: \"/example/confirm.gui\"\n" " component: \"/example/advanced/confirm.gui\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"
" y: 0.0\n" " y: 0.0\n"

View File

@ -1,4 +1,4 @@
script: "/example/confirm.gui_script" script: "/example/advanced/confirm.gui_script"
fonts { fonts {
name: "example" name: "example"
font: "/assets/example.font" font: "/assets/example.font"

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)
@ -27,7 +29,7 @@ function on_input(self, action_id, action)
end end
elseif gui.pick_node(self.no, action.x, action.y) then elseif gui.pick_node(self.no, action.x, action.y) then
print("no") print("no")
monarch.back(function() monarch.back(nil, nil, function()
print("back from popup done") print("back from popup done")
end) end)
end end
@ -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

@ -1,4 +1,4 @@
script: "/example/debug.gui_script" script: "/example/advanced/debug.gui_script"
fonts { fonts {
name: "example" name: "example"
font: "/assets/example.font" font: "/assets/example.font"

View File

@ -4,7 +4,7 @@ embedded_instances {
id: "go" id: "go"
data: "components {\n" data: "components {\n"
" id: \"game\"\n" " id: \"game\"\n"
" component: \"/example/game.gui\"\n" " component: \"/example/advanced/game.gui\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"
" y: 0.0\n" " y: 0.0\n"

View File

@ -1,4 +1,4 @@
script: "/example/game.gui_script" script: "/example/advanced/game.gui_script"
fonts { fonts {
name: "example" name: "example"
font: "/assets/example.font" font: "/assets/example.font"

View File

@ -7,7 +7,8 @@ function init(self)
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

@ -4,7 +4,7 @@ embedded_instances {
id: "go" id: "go"
data: "components {\n" data: "components {\n"
" id: \"menu\"\n" " id: \"menu\"\n"
" component: \"/example/menu.gui\"\n" " component: \"/example/advanced/menu.gui\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"
" y: 0.0\n" " y: 0.0\n"
@ -16,6 +16,8 @@ embedded_instances {
" z: 0.0\n" " z: 0.0\n"
" w: 1.0\n" " w: 1.0\n"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"" ""
position { position {
@ -76,12 +78,15 @@ embedded_instances {
" value: \"/go\"\n" " value: \"/go\"\n"
" type: PROPERTY_TYPE_HASH\n" " type: PROPERTY_TYPE_HASH\n"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"collectionfactory\"\n" " id: \"collectionfactory\"\n"
" type: \"collectionfactory\"\n" " type: \"collectionfactory\"\n"
" data: \"prototype: \\\"/example/popup.collection\\\"\\n" " data: \"prototype: \\\"/example/advanced/popup.collection\\\"\\n"
"load_dynamically: false\\n" "load_dynamically: false\\n"
"dynamic_prototype: false\\n"
"\"\n" "\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"

View File

@ -1,4 +1,4 @@
script: "/example/menu.gui_script" script: "/example/advanced/menu.gui_script"
fonts { fonts {
name: "example" name: "example"
font: "/assets/example.font" font: "/assets/example.font"

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

@ -4,7 +4,7 @@ embedded_instances {
id: "go" id: "go"
data: "components {\n" data: "components {\n"
" id: \"popup\"\n" " id: \"popup\"\n"
" component: \"/example/popup.gui\"\n" " component: \"/example/advanced/popup.gui\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"
" y: 0.0\n" " y: 0.0\n"

View File

@ -1,4 +1,4 @@
script: "/example/popup.gui_script" script: "/example/advanced/popup.gui_script"
fonts { fonts {
name: "example" name: "example"
font: "/assets/example.font" font: "/assets/example.font"

View File

@ -10,11 +10,12 @@ 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.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)
@ -26,7 +27,7 @@ function on_input(self, action_id, action)
end) end)
elseif gui.pick_node(self.cancel, action.x, action.y) then elseif gui.pick_node(self.cancel, action.x, action.y) then
print("cancel") print("cancel")
monarch.back(function() monarch.back(nil, nil, function()
print("back from popup done") print("back from popup done")
end) end)
elseif gui.pick_node(self.about, action.x, action.y) then elseif gui.pick_node(self.about, action.x, action.y) then
@ -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

@ -4,7 +4,7 @@ embedded_instances {
id: "go" id: "go"
data: "components {\n" data: "components {\n"
" id: \"pregame\"\n" " id: \"pregame\"\n"
" component: \"/example/pregame.gui\"\n" " component: \"/example/advanced/pregame.gui\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"
" y: 0.0\n" " y: 0.0\n"

View File

@ -1,4 +1,4 @@
script: "/example/pregame.gui_script" script: "/example/advanced/pregame.gui_script"
fonts { fonts {
name: "example" name: "example"
font: "/assets/example.font" font: "/assets/example.font"

View File

@ -6,7 +6,8 @@ function init(self)
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)
@ -18,7 +19,7 @@ function on_input(self, action_id, action)
end) end)
elseif gui.pick_node(self.back, action.x, action.y) then elseif gui.pick_node(self.back, action.x, action.y) then
print("back") print("back")
monarch.back(function() monarch.back(nil, nil, function()
print("back from pregame done") print("back from pregame done")
end) end)
end end
@ -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

@ -0,0 +1,289 @@
name: "basic"
scale_along_z: 0
embedded_instances {
id: "screen1"
data: "components {\n"
" id: \"screen_proxy\"\n"
" component: \"/monarch/screen_proxy.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
" properties {\n"
" id: \"screen_id\"\n"
" value: \"screen1\"\n"
" type: PROPERTY_TYPE_HASH\n"
" }\n"
" property_decls {\n"
" }\n"
"}\n"
"embedded_components {\n"
" id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n"
" data: \"collection: \\\"/example/basic/screen1.collection\\\"\\n"
"exclude: false\\n"
"\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
"}\n"
""
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: "basic"
data: "components {\n"
" id: \"basic\"\n"
" component: \"/example/basic/basic.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
" property_decls {\n"
" }\n"
"}\n"
""
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: "screen2"
data: "components {\n"
" id: \"screen_proxy\"\n"
" component: \"/monarch/screen_proxy.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
" properties {\n"
" id: \"screen_id\"\n"
" value: \"screen2\"\n"
" type: PROPERTY_TYPE_HASH\n"
" }\n"
" property_decls {\n"
" }\n"
"}\n"
"embedded_components {\n"
" id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n"
" data: \"collection: \\\"/example/basic/screen2.collection\\\"\\n"
"exclude: false\\n"
"\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
"}\n"
""
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: "popup1"
data: "components {\n"
" id: \"screen_proxy\"\n"
" component: \"/monarch/screen_proxy.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
" properties {\n"
" id: \"screen_id\"\n"
" value: \"popup1\"\n"
" type: PROPERTY_TYPE_HASH\n"
" }\n"
" properties {\n"
" id: \"popup\"\n"
" value: \"true\"\n"
" type: PROPERTY_TYPE_BOOLEAN\n"
" }\n"
" property_decls {\n"
" }\n"
"}\n"
"embedded_components {\n"
" id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n"
" data: \"collection: \\\"/example/basic/popup1.collection\\\"\\n"
"exclude: false\\n"
"\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
"}\n"
""
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: "popup2"
data: "components {\n"
" id: \"screen_proxy\"\n"
" component: \"/monarch/screen_proxy.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
" properties {\n"
" id: \"screen_id\"\n"
" value: \"popup2\"\n"
" type: PROPERTY_TYPE_HASH\n"
" }\n"
" properties {\n"
" id: \"popup\"\n"
" value: \"true\"\n"
" type: PROPERTY_TYPE_BOOLEAN\n"
" }\n"
" property_decls {\n"
" }\n"
"}\n"
"embedded_components {\n"
" id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n"
" data: \"collection: \\\"/example/basic/popup2.collection\\\"\\n"
"exclude: false\\n"
"\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
"}\n"
""
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
}
}

View File

@ -0,0 +1,12 @@
local monarch = require "monarch.monarch"
function init(self)
msg.post(".", "acquire_input_focus")
msg.post("#", "show_screen1")
end
function on_message(self, message_id, message, sender)
if message_id == hash("show_screen1") then
monarch.show("screen1")
end
end

View File

@ -0,0 +1,39 @@
name: "popup1"
scale_along_z: 0
embedded_instances {
id: "go"
data: "components {\n"
" id: \"popup1\"\n"
" component: \"/example/basic/popup1.gui\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
" property_decls {\n"
" }\n"
"}\n"
""
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
}
}

385
example/basic/popup1.gui Normal file
View File

@ -0,0 +1,385 @@
script: "/example/basic/popup1.gui_script"
fonts {
name: "example"
font: "/assets/example.font"
}
background_color {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
nodes {
position {
x: 320.0
y: 568.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 400.0
y: 400.0
z: 0.0
w: 1.0
}
color {
x: 0.5019608
y: 0.6
z: 1.0
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "root"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_MANUAL
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 100.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "showpopup2"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_AUTO
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "SHOW POPUP 2"
font: "example"
id: "showtext"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "showpopup2"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: -100.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "closepopup"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_AUTO
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "CLOSE"
font: "example"
id: "closetext"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "closepopup"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 40.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "THIS IS POPUP 1"
font: "example"
id: "id"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "root"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@ -0,0 +1,16 @@
local monarch = require "monarch.monarch"
function init(self)
msg.post(".", "acquire_input_focus")
gui.set_render_order(14)
end
function on_input(self, action_id, action)
if action_id == hash("touch") and action.pressed then
if gui.pick_node(gui.get_node("showpopup2"), action.x, action.y) then
monarch.show("popup2")
elseif gui.pick_node(gui.get_node("closepopup"), action.x, action.y) then
monarch.back()
end
end
end

View File

@ -0,0 +1,39 @@
name: "popup2"
scale_along_z: 0
embedded_instances {
id: "go"
data: "components {\n"
" id: \"popup2\"\n"
" component: \"/example/basic/popup2.gui\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
" property_decls {\n"
" }\n"
"}\n"
""
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
}
}

385
example/basic/popup2.gui Normal file
View File

@ -0,0 +1,385 @@
script: "/example/basic/popup2.gui_script"
fonts {
name: "example"
font: "/assets/example.font"
}
background_color {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
nodes {
position {
x: 320.0
y: 568.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 400.0
y: 400.0
z: 0.0
w: 1.0
}
color {
x: 0.5019608
y: 0.7019608
z: 0.7019608
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "root"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_MANUAL
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 100.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "showpopup2"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_AUTO
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "SHOW POPUP 2"
font: "example"
id: "showtext"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "showpopup2"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 40.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "THIS IS POPUP 2"
font: "example"
id: "id"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "root"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: -100.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "closepopup"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "root"
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_AUTO
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "CLOSE"
font: "example"
id: "closetext"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "closepopup"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@ -0,0 +1,16 @@
local monarch = require "monarch.monarch"
function init(self)
msg.post(".", "acquire_input_focus")
gui.set_render_order(15)
end
function on_input(self, action_id, action)
if action_id == hash("touch") and action.pressed then
if gui.pick_node(gui.get_node("showpopup2"), action.x, action.y) then
monarch.show("popup2")
elseif gui.pick_node(gui.get_node("closepopup"), action.x, action.y) then
monarch.back()
end
end
end

View File

@ -0,0 +1,37 @@
name: "screen1"
scale_along_z: 0
embedded_instances {
id: "go"
data: "components {\n"
" id: \"screen1\"\n"
" component: \"/example/basic/screen1.gui\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
"}\n"
""
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
}
}

325
example/basic/screen1.gui Normal file
View File

@ -0,0 +1,325 @@
script: "/example/basic/screen1.gui_script"
fonts {
name: "example"
font: "/assets/example.font"
}
background_color {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
nodes {
position {
x: 320.0
y: 568.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "showscreen2"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_AUTO
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "SHOW SCREEN 2"
font: "example"
id: "showscreen2text"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "showscreen2"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 320.0
y: 10.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 40.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "THIS IS SCREEN 1"
font: "example"
id: "id"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_S
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 320.0
y: 450.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "showpopup1"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_AUTO
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "SHOW POPUP 1"
font: "example"
id: "showpopup1text"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "showpopup1"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@ -0,0 +1,15 @@
local monarch = require "monarch.monarch"
function init(self)
msg.post(".", "acquire_input_focus")
end
function on_input(self, action_id, action)
if action_id == hash("touch") and action.pressed then
if gui.pick_node(gui.get_node("showscreen2"), action.x, action.y) then
monarch.show("screen2")
elseif gui.pick_node(gui.get_node("showpopup1"), action.x, action.y) then
monarch.show("popup1")
end
end
end

View File

@ -0,0 +1,37 @@
name: "screen2"
scale_along_z: 0
embedded_instances {
id: "go"
data: "components {\n"
" id: \"screen2\"\n"
" component: \"/example/basic/screen2.gui\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
"}\n"
""
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
}
}

325
example/basic/screen2.gui Normal file
View File

@ -0,0 +1,325 @@
script: "/example/basic/screen2.gui_script"
fonts {
name: "example"
font: "/assets/example.font"
}
background_color {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
nodes {
position {
x: 320.0
y: 568.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "backbutton"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_AUTO
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "BACK"
font: "example"
id: "text"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "backbutton"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 320.0
y: 10.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 40.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "THIS IS SCREEN 2"
font: "example"
id: "id"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_S
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 320.0
y: 450.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "showpopup1"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_AUTO
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "SHOW POPUP 1"
font: "example"
id: "showpopup1text"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "showpopup1"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@ -0,0 +1,15 @@
local monarch = require "monarch.monarch"
function init(self)
msg.post(".", "acquire_input_focus")
end
function on_input(self, action_id, action)
if action_id == hash("touch") and action.pressed then
if gui.pick_node(gui.get_node("backbutton"), action.x, action.y) then
monarch.back()
elseif gui.pick_node(gui.get_node("showpopup1"), action.x, action.y) then
monarch.show("popup1")
end
end
end

View File

@ -0,0 +1,159 @@
name: "slidingwindow"
scale_along_z: 0
embedded_instances {
id: "go"
data: "components {\n"
" id: \"slidingwindow\"\n"
" component: \"/example/slidingwindow/slidingwindow.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
" property_decls {\n"
" }\n"
"}\n"
""
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: "window1"
data: "components {\n"
" id: \"screen_proxy\"\n"
" component: \"/monarch/screen_proxy.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
" properties {\n"
" id: \"screen_id\"\n"
" value: \"window1\"\n"
" type: PROPERTY_TYPE_HASH\n"
" }\n"
" property_decls {\n"
" }\n"
"}\n"
"embedded_components {\n"
" id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n"
" data: \"collection: \\\"/example/slidingwindow/window1.collection\\\"\\n"
"exclude: false\\n"
"\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
"}\n"
""
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: "window2"
data: "components {\n"
" id: \"screen_proxy\"\n"
" component: \"/monarch/screen_proxy.script\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
" properties {\n"
" id: \"screen_id\"\n"
" value: \"window2\"\n"
" type: PROPERTY_TYPE_HASH\n"
" }\n"
" property_decls {\n"
" }\n"
"}\n"
"embedded_components {\n"
" id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n"
" data: \"collection: \\\"/example/slidingwindow/window2.collection\\\"\\n"
"exclude: false\\n"
"\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
"}\n"
""
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
}
}

View File

@ -0,0 +1,28 @@
local monarch = require "monarch.monarch"
function init(self)
monarch.debug()
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
monarch.add_listener()
end
function final(self)
monarch.remove_listener()
end
function on_message(self, message_id, message, sender)
if message_id == hash("init_monarch") then
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

View File

@ -0,0 +1,39 @@
name: "window1"
scale_along_z: 0
embedded_instances {
id: "go"
data: "components {\n"
" id: \"window1\"\n"
" component: \"/example/slidingwindow/window1.gui\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
" property_decls {\n"
" }\n"
"}\n"
""
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
}
}

View File

@ -0,0 +1,261 @@
script: "/example/slidingwindow/window1.gui_script"
fonts {
name: "example"
font: "/assets/example.font"
}
background_color {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
nodes {
position {
x: 320.0
y: 568.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 640.0
y: 1136.0
z: 0.0
w: 1.0
}
color {
x: 0.3019608
y: 0.3019608
z: 0.3019608
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "bg"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_STRETCH
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_MANUAL
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 50.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "button"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "bg"
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_MANUAL
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 50.0
z: 0.0
w: 1.0
}
color {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "WINDOW 2"
font: "example"
id: "text"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "button"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 338.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 2.0
y: 2.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "WINDOW 1"
font: "example"
id: "title"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "bg"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@ -0,0 +1,35 @@
local monarch = require "monarch.monarch"
local transitions = require "monarch.transitions.gui"
function init(self)
msg.post(".", "acquire_input_focus")
local DURATION = 0.3
local transition = transitions.create(gui.get_node("bg"))
.show_in(transitions.slide_in_right, gui.EASING_LINEAR, DURATION, 0)
.show_out(transitions.slide_out_left, gui.EASING_LINEAR, DURATION, 0)
.back_in(transitions.slide_in_left, gui.EASING_LINEAR, DURATION, 0)
.back_out(transitions.slide_out_right, gui.EASING_LINEAR, DURATION, 0)
monarch.on_transition("window1", transition)
monarch.on_focus_changed("window1", function(message_id, message)
if message_id == monarch.FOCUS.GAINED then
print("window1 gained focus")
elseif message_id == monarch.FOCUS.LOST then
print("window1 lost focus")
end
end)
end
function on_input(self, action_id, action)
if action_id == hash("touch") and action.released then
if gui.pick_node(gui.get_node("button"), action.x, action.y) then
monarch.show("window2")
end
end
end
function on_message(self, message_id, message, sender)
monarch.on_message(message_id, message, sender)
end

View File

@ -0,0 +1,39 @@
name: "window2"
scale_along_z: 0
embedded_instances {
id: "go"
data: "components {\n"
" id: \"window2\"\n"
" component: \"/example/slidingwindow/window2.gui\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
" property_decls {\n"
" }\n"
"}\n"
""
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
}
}

View File

@ -0,0 +1,261 @@
script: "/example/slidingwindow/window2.gui_script"
fonts {
name: "example"
font: "/assets/example.font"
}
background_color {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
nodes {
position {
x: 320.0
y: 568.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 640.0
y: 1136.0
z: 0.0
w: 1.0
}
color {
x: 0.3019608
y: 0.3019608
z: 0.3019608
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "bg"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_STRETCH
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_MANUAL
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 50.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "button"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
parent: "bg"
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_MANUAL
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 50.0
z: 0.0
w: 1.0
}
color {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "BACK"
font: "example"
id: "text"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "button"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
nodes {
position {
x: 0.0
y: 338.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 2.0
y: 2.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "WINDOW 2"
font: "example"
id: "title"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "bg"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
custom_type: 0
enabled: true
visible: true
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@ -0,0 +1,27 @@
local monarch = require "monarch.monarch"
local transitions = require "monarch.transitions.gui"
function init(self)
msg.post(".", "acquire_input_focus")
local DURATION = 0.3
local transition = transitions.create(gui.get_node("bg"))
.show_in(transitions.slide_in_right, gui.EASING_LINEAR, DURATION, 0)
.show_out(transitions.slide_out_left, gui.EASING_LINEAR, DURATION, 0)
.back_in(transitions.slide_in_left, gui.EASING_LINEAR, DURATION, 0)
.back_out(transitions.slide_out_right, gui.EASING_LINEAR, DURATION, 0)
monarch.on_transition("window2", transition)
end
function on_input(self, action_id, action)
if action_id == hash("touch") and action.released then
if gui.pick_node(gui.get_node("button"), action.x, action.y) then
monarch.back()
end
end
end
function on_message(self, message_id, message, sender)
monarch.on_message(message_id, message, sender)
end

View File

@ -1,10 +1,11 @@
[project] [project]
title = Monarch title = Monarch
version = 0.9 version = 0.9
dependencies = https://github.com/britzl/deftest/archive/2.7.0.zip 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] [bootstrap]
main_collection = /example/example.collectionc main_collection = /example/advanced/advanced.collectionc
[input] [input]
game_binding = /input/game.input_bindingc game_binding = /input/game.input_bindingc

View File

@ -79,16 +79,9 @@ end
gui_template = function(gui_script) gui_template = function(gui_script)
return [[script: "]].. gui_script .. [[" return [[script: "]].. gui_script .. [["
background_color { material: "/builtins/materials/gui.material"
x: 0.0 adjust_reference: ADJUST_REFERENCE_PARENT
y: 0.0 ]]
z: 0.0
w: 0.0
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512
]]
end end
gui_script_content = [[local monarch = require "monarch.monarch" gui_script_content = [[local monarch = require "monarch.monarch"
@ -116,43 +109,43 @@ end
collection_template = function(gui_script, name) collection_template = function(gui_script, name)
return [[name: "]].. name .. [[" return [[name: "]].. name .. [["
scale_along_z: 0 scale_along_z: 0
embedded_instances { embedded_instances {
id: "go" id: "go"
data: "components {\n" data: "components {\n"
" id: \"monarch\"\n" " id: \"monarch\"\n"
" component: \"]].. gui_script .. [[\"\n" " component: \"]].. gui_script .. [[\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"
" y: 0.0\n" " y: 0.0\n"
" z: 0.0\n" " z: 0.0\n"
" }\n" " }\n"
" rotation {\n" " rotation {\n"
" x: 0.0\n" " x: 0.0\n"
" y: 0.0\n" " y: 0.0\n"
" z: 0.0\n" " z: 0.0\n"
" w: 1.0\n" " w: 1.0\n"
" }\n" " }\n"
"}\n" "}\n"
"" ""
position { position {
x: 0.0 x: 0.0
y: 0.0 y: 0.0
z: 0.0 z: 0.0
} }
rotation { rotation {
x: 0.0 x: 0.0
y: 0.0 y: 0.0
z: 0.0 z: 0.0
w: 1.0 w: 1.0
} }
scale3 { scale3 {
x: 1.0 x: 1.0
y: 1.0 y: 1.0
z: 1.0 z: 1.0
} }
} }
]] ]]
end end

View File

@ -1,17 +1,27 @@
local callback_tracker = require "monarch.utils.callback_tracker" local callback_tracker = require "monarch.utils.callback_tracker"
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 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 = {}
@ -33,6 +43,8 @@ M.SCREEN_TRANSITION_OUT_STARTED = hash("monarch_screen_transition_out_started")
M.SCREEN_TRANSITION_OUT_FINISHED = hash("monarch_screen_transition_out_finished") M.SCREEN_TRANSITION_OUT_FINISHED = hash("monarch_screen_transition_out_finished")
M.SCREEN_TRANSITION_FAILED = hash("monarch_screen_transition_failed") M.SCREEN_TRANSITION_FAILED = hash("monarch_screen_transition_failed")
local WAIT_FOR_TRANSITION = true
local DO_NOT_WAIT_FOR_TRANSITION = false
-- all registered screens -- all registered screens
local screens = {} local screens = {}
@ -47,7 +59,6 @@ local transition_listeners = {}
-- monarch is considered busy while there are active transitions -- monarch is considered busy while there are active transitions
local active_transition_count = 0 local active_transition_count = 0
local function log(...) end local function log(...) end
function M.debug() function M.debug()
@ -79,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()
@ -93,8 +107,8 @@ end
local queue = {} local queue = {}
local function queue_error(message) local function queue_error(message)
print(message)
log("queue() error - clearing queue") log("queue() error - clearing queue")
print(message)
while next(queue) do while next(queue) do
table.remove(queue) table.remove(queue)
end end
@ -106,7 +120,7 @@ process_queue = function()
log("queue() busy") log("queue() busy")
return return
end end
action = table.remove(queue, 1) local action = table.remove(queue, 1)
if not action then if not action then
log("queue() empty") log("queue() empty")
return return
@ -132,23 +146,49 @@ local function notify_transition_listeners(message_id, message)
end end
end end
local function screen_from_proxy(proxy) local function find_screen(url_to_find)
for _,screen in pairs(screens) do local function find(url)
if screen.proxy == proxy then for _,screen in pairs(screens) do
return screen if screen.script == url or screen.proxy == url then
return screen
end
end end
end end
return find(msg.url()) or find(url_to_find)
end end
local function screen_from_script() local function find_transition_screen(url_to_find)
local url = msg.url() local function find(url)
for _,screen in pairs(screens) do for _,screen in pairs(screens) do
if screen.script == url then if screen.transition_url == url or screen.script == url or screen.proxy == url then
return screen return screen
end
end end
end end
return find(msg.url()) or find(url_to_find)
end end
local function find_focus_screen(url_to_find)
local function find(url)
for _,screen in pairs(screens) do
if screen.focus_url == url or screen.script == url or screen.proxy == url then
return screen
end
end
end
return find(msg.url()) or find(url_to_find)
end
local function find_post_receiver_screen(url_to_find)
local function find(url)
for _,screen in pairs(screens) do
if screen.receiver_url == url or screen.script == url or screen.proxy == url then
return screen
end
end
end
return find(msg.url()) or find(url_to_find)
end
--- Check if a screen exists in the current screen stack --- Check if a screen exists in the current screen stack
-- @param id (string|hash) -- @param id (string|hash)
@ -181,6 +221,17 @@ end
-- @param id (string|hash) -- @param id (string|hash)
-- @return true if the screen is visible -- @return true if the screen is visible
function M.is_visible(id) 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].visible
end
--- Check if a screen is loaded
-- @param id (string|hash)
-- @return true if the screen is loaded
function M.is_loaded(id)
assert(id, "You must provide a screen id") assert(id, "You must provide a screen 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)))
@ -188,6 +239,17 @@ function M.is_visible(id)
end end
--- Check if a screen is a popup
-- @param id Screen id
-- @return true if the screen is a popup
function M.is_popup(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].popup
end
local function register(id, settings) local function register(id, settings)
assert(id, "You must provide a screen id") assert(id, "You must provide a screen id")
id = tohash(id) id = tohash(id)
@ -221,12 +283,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")
@ -236,6 +292,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
@ -258,10 +323,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")
@ -270,6 +331,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
@ -285,16 +353,26 @@ function M.unregister(id)
log("unregister()", id) log("unregister()", id)
local screen = screens[id] local screen = screens[id]
screens[id] = nil screens[id] = nil
-- remove screen from stack
for i = #stack, 1, -1 do
if stack[i].id == id then
table.remove(stack, i)
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
@ -312,10 +390,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
@ -325,22 +403,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) 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
msg.post(screen.proxy, DISABLE) if screen.loaded then
screen.loaded = false msg.post(screen.proxy, MSG_DISABLE)
screen.loaded = false
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
@ -349,7 +430,9 @@ local function unload(screen, force)
elseif screen.factory then elseif screen.factory then
log("unload() factory", screen.id) log("unload() factory", screen.id)
for id, instance in pairs(screen.factory_ids) do for id, instance in pairs(screen.factory_ids) do
go.delete(instance) if go.exists(instance) then
go.delete(instance)
end
end end
screen.factory_ids = nil screen.factory_ids = nil
if screen.auto_preload and not force then if screen.auto_preload and not force then
@ -364,8 +447,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
@ -381,16 +464,19 @@ local function preload(screen)
screen.preloading = true screen.preloading = true
if screen.proxy then if screen.proxy then
log("preload() proxy") log("preload() proxy")
local missing_resources = collectionproxy.missing_resources(screen.proxy) if M.has_missing_resources(screen.id) then
if #missing_resources > 0 then
local error_message = ("preload() collection proxy %s is missing resources"):format(tostring(screen.id)) local error_message = ("preload() collection proxy %s is missing resources"):format(tostring(screen.id))
log(error_message) log(error_message)
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
@ -398,6 +484,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
@ -429,23 +518,30 @@ 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] if screen.transition_id then
screen.focus_url = screen.factory_ids[screen.focus_id] screen.transition_url = screen.factory_ids[screen.transition_id]
end
if screen.focus_id then
screen.focus_url = screen.factory_ids[screen.focus_id]
end
end end
screen.loaded = true screen.loaded = true
screen.preloaded = false screen.preloaded = false
return true return true
end end
local function transition(screen, message_id, message) 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)
coroutine.yield() if wait then
coroutine.yield()
end
screen.wait_for = nil screen.wait_for = nil
else else
log("transition() no transition url - ignoring") log("transition() no transition url - ignoring")
@ -463,13 +559,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(screen, 0)
else else
log("focus_lost() no focus url - ignoring") log("focus_lost() no focus url - ignoring")
end end
@ -527,8 +625,21 @@ local function enable(screen, previous_screen)
end) end)
end end
local function show_out(screen, next_screen, cb) local function show_out(screen, next_screen, wait_for_transition, cb)
log("show_out()", screen.id) log("show_out()", screen.id)
assert(wait_for_transition ~= nil)
-- make sure the screen is loaded. scenario:
-- show A - stack [A]
-- - show_in for A
-- show B with no_stack = true - stack [A]
-- - show_in for B and show_out for A
-- show C
-- - show_in for C and show_out for A again!
if not screen.loaded then
log("show_out() screen was not loaded")
cb()
return
end
run_coroutine(screen, cb, function() run_coroutine(screen, cb, function()
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.id }) notify_transition_listeners(M.SCREEN_TRANSITION_OUT_STARTED, { screen = screen.id, next_screen = next_screen.id })
@ -541,7 +652,8 @@ local function show_out(screen, next_screen, cb)
local next_is_popup = next_screen and next_screen.popup local next_is_popup = next_screen and next_screen.popup
local current_is_popup = screen.popup local current_is_popup = screen.popup
if (not next_is_popup and not current_is_popup) or (current_is_popup) then if (not next_is_popup and not current_is_popup) or (current_is_popup) then
transition(screen, M.TRANSITION.SHOW_OUT, { next_screen = next_screen.id }) transition(screen, M.TRANSITION.SHOW_OUT, { next_screen = next_screen.id }, wait_for_transition)
screen.visible = false
unload(screen) unload(screen)
elseif next_is_popup then elseif next_is_popup then
change_timestep(screen) change_timestep(screen)
@ -551,8 +663,9 @@ local function show_out(screen, next_screen, cb)
end) end)
end end
local function show_in(screen, previous_screen, reload, add_to_stack, cb) local function show_in(screen, previous_screen, reload, add_to_stack, wait_for_transition, cb)
log("show_in()", screen.id) log("show_in()", screen.id, wait_for_transition)
assert(wait_for_transition ~= nil)
run_coroutine(screen, cb, function() run_coroutine(screen, cb, function()
active_transition_count = active_transition_count + 1 active_transition_count = active_transition_count + 1
notify_transition_listeners(M.SCREEN_TRANSITION_IN_STARTED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id }) notify_transition_listeners(M.SCREEN_TRANSITION_IN_STARTED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
@ -561,18 +674,24 @@ local function show_in(screen, previous_screen, reload, add_to_stack, cb)
log("show_in() reloading", screen.id) log("show_in() reloading", screen.id)
unload(screen, reload) unload(screen, reload)
end end
if add_to_stack then
stack[#stack + 1] = screen
end
local ok, err = load(screen) local ok, err = load(screen)
if not ok then if not ok then
log("show_in()", err) log("show_in()", err)
if add_to_stack then
stack[#stack] = nil
end
active_transition_count = active_transition_count - 1 active_transition_count = active_transition_count - 1
notify_transition_listeners(M.SCREEN_TRANSITION_FAILED, { screen = screen.id }) notify_transition_listeners(M.SCREEN_TRANSITION_FAILED, { screen = screen.id })
return return
end end
if add_to_stack then -- wait one frame so that the init() of any script have time to run before starting transitions
stack[#stack + 1] = screen cowait(screen, 0)
end
reset_timestep(screen) reset_timestep(screen)
transition(screen, M.TRANSITION.SHOW_IN, { previous_screen = previous_screen and previous_screen.id }) transition(screen, M.TRANSITION.SHOW_IN, { previous_screen = previous_screen and previous_screen.id }, wait_for_transition)
screen.visible = true
acquire_input(screen) acquire_input(screen)
focus_gained(screen, previous_screen) focus_gained(screen, previous_screen)
active_transition_count = active_transition_count - 1 active_transition_count = active_transition_count - 1
@ -580,8 +699,9 @@ local function show_in(screen, previous_screen, reload, add_to_stack, cb)
end) end)
end end
local function back_in(screen, previous_screen, cb) local function back_in(screen, previous_screen, wait_for_transition, cb)
log("back_in()", screen.id) log("back_in()", screen.id)
assert(wait_for_transition ~= nil)
run_coroutine(screen, cb, function() run_coroutine(screen, cb, function()
active_transition_count = active_transition_count + 1 active_transition_count = active_transition_count + 1
notify_transition_listeners(M.SCREEN_TRANSITION_IN_STARTED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id }) notify_transition_listeners(M.SCREEN_TRANSITION_IN_STARTED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
@ -593,10 +713,13 @@ local function back_in(screen, previous_screen, cb)
notify_transition_listeners(M.SCREEN_TRANSITION_FAILED, { screen = screen.id }) notify_transition_listeners(M.SCREEN_TRANSITION_FAILED, { screen = screen.id })
return return
end end
-- wait one frame so that the init() of any script have time to run before starting transitions
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 }) transition(screen, M.TRANSITION.BACK_IN, { previous_screen = previous_screen.id }, wait_for_transition)
end end
screen.visible = true
acquire_input(screen) acquire_input(screen)
focus_gained(screen, previous_screen) focus_gained(screen, previous_screen)
active_transition_count = active_transition_count - 1 active_transition_count = active_transition_count - 1
@ -604,18 +727,20 @@ local function back_in(screen, previous_screen, cb)
end) end)
end end
local function back_out(screen, next_screen, cb) 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)
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)
transition(screen, M.TRANSITION.BACK_OUT, { next_screen = next_screen and next_screen.id }, wait_for_transition)
if next_screen and screen.popup then if next_screen and screen.popup then
reset_timestep(next_screen) reset_timestep(next_screen)
end end
transition(screen, M.TRANSITION.BACK_OUT, { next_screen = next_screen and next_screen.id }) screen.visible = false
unload(screen) unload(screen)
active_transition_count = active_transition_count - 1 active_transition_count = active_transition_count - 1
notify_transition_listeners(M.SCREEN_TRANSITION_OUT_FINISHED, { screen = screen.id, next_screen = next_screen and next_screen.id }) notify_transition_listeners(M.SCREEN_TRANSITION_OUT_FINISHED, { screen = screen.id, next_screen = next_screen and next_screen.id })
@ -668,9 +793,7 @@ function M.show(id, options, data, cb)
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("show() queuing action", id)
queue_action(function(action_done, action_error) queue_action(function(action_done, action_error)
log("show()", id)
local screen = screens[id] local screen = screens[id]
if not screen then if not screen then
action_error(("show() there is no longer a screen with id %s"):format(tostring(id))) action_error(("show() there is no longer a screen with id %s"):format(tostring(id)))
@ -708,21 +831,12 @@ function M.show(id, options, data, cb)
pop = pop - 1 pop = pop - 1
end end
stack[#stack] = nil stack[#stack] = nil
show_out(top, screen, callbacks.track()) async(function(await, resume)
callbacks.yield_until_done() await(show_out, top, screen, WAIT_FOR_TRANSITION, resume)
end)
top = stack[#stack] top = stack[#stack]
end end
-- unload the previous screen and transition out from top
-- wait until we are done if showing the same screen as is already visible
if top and not top.popup then
local same_screen = top and top.id == screen.id
show_out(top, screen, callbacks.track())
if same_screen or (options and options.sequential) then
callbacks.yield_until_done()
end
end
-- if the screen we want to show is in the stack -- if the screen we want to show is in the stack
-- already and the clear flag is set then we need -- already and the clear flag is set then we need
-- to remove every screen on the stack up until and -- to remove every screen on the stack up until and
@ -744,8 +858,8 @@ function M.show(id, options, data, cb)
end end
end end
-- show screen, wait until preloaded if it is already preloading -- wait until preloaded if it is already preloading
-- this can typpically happen if you do a show() on app start for a -- this can typically happen if you do a show() on app start for a
-- screen that has Preload set to true -- screen that has Preload set to true
if M.is_preloading(id) then if M.is_preloading(id) then
M.when_preloaded(id, function() M.when_preloaded(id, function()
@ -753,7 +867,23 @@ function M.show(id, options, data, cb)
end) end)
coroutine.yield() coroutine.yield()
end end
show_in(screen, top, options and options.reload, add_to_stack, callbacks.track())
-- showing and hiding the same screen?
local same_screen = top and top.id == screen.id
if same_screen or (options and options.sequential) then
if top then
async(function(await, resume)
await(show_out, top, screen, WAIT_FOR_TRANSITION, resume)
end)
end
show_in(screen, top, options and options.reload, add_to_stack, WAIT_FOR_TRANSITION, callbacks.track())
else
-- show screen
show_in(screen, top, options and options.reload, add_to_stack, WAIT_FOR_TRANSITION, callbacks.track())
if add_to_stack and top and not top.popup then
show_out(top, screen, WAIT_FOR_TRANSITION, callbacks.track())
end
end
callbacks.when_done(function() callbacks.when_done(function()
pcallfn(cb) pcallfn(cb)
@ -782,7 +912,7 @@ end
-- Hide a screen. The screen must either be at the top of the stack or -- Hide a screen. The screen must either be at the top of the stack or
-- visible but not added to the stack (through the no_stack option) -- visible but not added to the stack (through the no_stack option)
-- @param id (string|hash) - Id of the screen to show -- @param id (string|hash) - Id of the screen to .hide
-- @param cb (function) - Optional callback to invoke when the screen is hidden -- @param cb (function) - Optional callback to invoke when the screen is hidden
-- @return true if successfully hiding, false if busy or for some other reason unable to hide the screen -- @return true if successfully hiding, false if busy or for some other reason unable to hide the screen
function M.hide(id, cb) function M.hide(id, cb)
@ -795,7 +925,7 @@ function M.hide(id, cb)
log("hide() you can only hide the screen at the top of the stack", id) log("hide() you can only hide the screen at the top of the stack", id)
return false return false
end end
return M.back(id, cb) return M.back(nil, nil, cb)
else else
log("hide() queuing action", id) log("hide() queuing action", id)
queue_action(function(action_done, action_error) queue_action(function(action_done, action_error)
@ -807,7 +937,7 @@ function M.hide(id, cb)
action_error(("hide() there is no longer a screen with id %s"):format(tostring(id))) action_error(("hide() there is no longer a screen with id %s"):format(tostring(id)))
return return
end end
back_out(screen, nil, callbacks.track()) back_out(screen, nil, WAIT_FOR_TRANSITION, callbacks.track())
end end
callbacks.when_done(function() callbacks.when_done(function()
pcallfn(cb) pcallfn(cb)
@ -819,10 +949,43 @@ function M.hide(id, cb)
end end
-- Clear stack completely. Any visible screens will be hidden by navigating back out
-- from them.
-- @param cb (function) - Optional callback to invoke when the stack has been cleared
function M.clear(cb)
log("clear() queuing action")
queue_action(function(action_done, action_error)
async(function(await, resume)
local top = stack[#stack]
while top and top.visible do
stack[#stack] = nil
await(back_out, top, stack[#stack - 1], WAIT_FOR_TRANSITION, resume)
top = stack[#stack]
end
while stack[#stack] do
table.remove(stack)
end
pcallfn(cb)
pcallfn(action_done)
end)
end)
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")
queue_action(function(action_done) queue_action(function(action_done)
@ -833,20 +996,36 @@ function M.back(data, cb)
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
back_out(screen, top, function() if same_screen or (options and options.sequential) then
local back_cb = callbacks.track()
back_out(screen, top, WAIT_FOR_TRANSITION, function()
if data then if data then
top.data = data top.data = data
end end
back_in(top, screen, callbacks.track()) back_in(top, screen, WAIT_FOR_TRANSITION, back_cb)
end) end)
else else
back_out(screen, top, callbacks.track())
if top then if top then
if data then if data then
top.data = data top.data = data
end end
back_in(top, screen, callbacks.track()) -- if the screen we are backing out from is a popup and the screen we go
-- back to is not a popup we need to let the popup completely hide before
-- we start working on the screen we go back to
-- we do this to ensure that we do not reset the times step of the screen
-- we go back to until it is no longer obscured by the popup
if screen.popup and not top.popup then
local back_cb = callbacks.track()
back_out(screen, top, WAIT_FOR_TRANSITION, function()
back_in(top, screen, WAIT_FOR_TRANSITION, back_cb)
end)
else
back_in(top, screen, WAIT_FOR_TRANSITION, callbacks.track())
back_out(screen, top, WAIT_FOR_TRANSITION, callbacks.track())
end
else
back_out(screen, top, WAIT_FOR_TRANSITION, callbacks.track())
end end
end end
end end
@ -900,12 +1079,19 @@ end
--- Preload a screen. This will load but not enable and show a screen. Useful for "heavier" screens --- 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. -- that you wish to show without any delay.
-- @param id (string|hash) - Id of the screen to preload -- @param id (string|hash) - Id of the screen to preload
-- @param options (table)
-- @param cb (function) - Optional callback to invoke when screen is loaded -- @param cb (function) - Optional callback to invoke when screen is loaded
function M.preload(id, cb) function M.preload(id, options, cb)
assert(id, "You must provide a screen id") assert(id, "You must provide a screen 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)))
-- support old function signature (id, cb)
if type(options) == "function" and not cb then
cb = options
options = nil
end
log("preload() queuing action", id) log("preload() queuing action", id)
queue_action(function(action_done, action_error) queue_action(function(action_done, action_error)
log("preload()", id) log("preload()", id)
@ -916,6 +1102,10 @@ function M.preload(id, cb)
return return
end end
-- keep_loaded is an option for monarch.preload()
-- use it to get the same behavior as the auto preload checkbox
screen.auto_preload = screen.auto_preload or options and options.keep_loaded
if screen.preloaded or screen.loaded then if screen.preloaded or screen.loaded then
pcallfn(cb) pcallfn(cb)
pcallfn(action_done) pcallfn(action_done)
@ -942,6 +1132,20 @@ function M.preload(id, cb)
return true -- return true for legacy reasons (before queue existed) return true -- return true for legacy reasons (before queue existed)
end end
--- Check if a screen has missing resources, always returns false for factory
-- @param id (string|hash) - Id of the screen to preload
function M.has_missing_resources(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)))
local screen = screens[id]
if screen.proxy then
local missing_resources = collectionproxy.missing_resources(screen.proxy)
return #missing_resources > 0
end
return false
end
--- Unload a preloaded monarch screen --- Unload a preloaded monarch screen
-- @param id (string|hash) - Id of the screen to unload -- @param id (string|hash) - Id of the screen to unload
@ -978,7 +1182,7 @@ function M.unload(id, cb)
end end
run_coroutine(screen, when_unloaded, function() run_coroutine(screen, when_unloaded, function()
change_context(screen) change_context(screen)
unload(screen) unload(screen, true)
end) end)
end) end)
return true -- return true for legacy reasons (before queue existed) return true -- return true for legacy reasons (before queue existed)
@ -993,8 +1197,8 @@ end
-- @return error (string|nil) Error message if unable to send message -- @return error (string|nil) Error message if unable to send message
function M.post(id, message_id, message) function M.post(id, message_id, message)
assert(id, "You must provide a screen id") assert(id, "You must provide a screen id")
if not M.is_visible(id) then if not M.is_loaded(id) then
return false, "Unable to post message to screen if it isn't visible" return false, "Unable to post message to screen if it isn't loaded"
end end
assert(message_id, "You must provide a message_id") assert(message_id, "You must provide a message_id")
@ -1002,51 +1206,69 @@ 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
run_coroutine(screen, nil, function() return false, "Unable to post message since screen has no receiver url specified. Set one using monarch.on_post()."
change_context(screen)
log("post() sending message to", screen.receiver_url)
for id,instance in pairs(screen.factory_ids) do
msg.post(instance, message_id, message)
end
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 = screen_from_proxy(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 = screen_from_proxy(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 = screen_from_script() 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 = screen_from_script() local screen = find_transition_screen(sender)
assert(screen, "Unable to find screen for current script url") 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
or message_id == M.TRANSITION.SHOW_OUT
or message_id == M.TRANSITION.BACK_IN
or message_id == M.TRANSITION.BACK_OUT
then
local screen = find_transition_screen(sender)
assert(screen, "Unable to find screen for transition")
if screen.transition_fn then
screen.transition_fn(message_id, message, sender)
end
elseif message_id == MSG_LAYOUT_CHANGED then
local screen = find_screen(sender)
if screen and screen.transition_fn then
screen.transition_fn(message_id, message, sender)
end
elseif message_id == M.FOCUS.GAINED
or message_id == M.FOCUS.LOST
then
local screen = find_focus_screen(sender)
assert(screen, "Unable to find screen for focus change")
if screen.focus_fn then
screen.focus_fn(message_id, message, sender)
end
else
local screen = find_post_receiver_screen(sender)
if screen and screen.receiver_fn then
screen.receiver_fn(message_id, message, sender)
end
end end
end end
@ -1080,8 +1302,84 @@ function M.bottom(offset)
return screen and screen.id return screen and screen.id
end end
--- Set the timestep to apply for a screen when below a popup
-- @param id (string|hash) Id of the screen to change timestep setting for
-- @param timestep (number) Timestep to apply
function M.set_timestep_below_popup(id, timestep)
assert(id, "You must provide a screen id")
assert(timestep, "You must provide a timestep")
id = tohash(id)
assert(screens[id], ("There is no screen registered with id %s"):format(tostring(id)))
screens[id].timestep_below_popup = timestep
end
---
-- Set a function to call when a transition should be started
-- The function will receive (message_id, message, sender)
-- IMPORTANT! You must call monarch.on_message() from the same script as
-- this function was called
-- @param id Screen id to associate transition with
-- @param fn Transition handler function
function M.on_transition(id, fn)
assert(id, "You must provide a screen id")
assert(fn, "You must provide a transition function")
id = tohash(id)
assert(screens[id], ("There is no screen registered with id %s"):format(tostring(id)))
local screen = screens[id]
screen.transition_url = msg.url()
screen.transition_fn = fn
end
---
-- Set a function to call when a screen gains or loses focus
-- The function will receive (message_id, message, sender)
-- IMPORTANT! You must call monarch.on_message() from the same script as
-- this function was called
-- @param id Screen id to associate focus listener function with
-- @param fn Focus listener function
function M.on_focus_changed(id, fn)
assert(id, "You must provide a screen id")
assert(fn, "You must provide a focus change function")
id = tohash(id)
assert(screens[id], ("There is no screen registered with id %s"):format(tostring(id)))
local screen = screens[id]
screen.focus_url = msg.url()
screen.focus_fn = fn
end
---
-- Set either a function to be called when msg.post() is called on a specific
-- screen or a URL where the message is sent.
-- IMPORTANT! If you provide a function you must also make sure to call
-- monarch.on_message(message_id, message, sender) from the same script as
-- this function was called.
-- @param id Screen id to associate the message listener function with
-- @param fn_or_url The function to call or URL to send message to
function M.on_post(id, fn_or_url)
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]
local t = type(fn_or_url)
if t == "function" then
screen.receiver_fn = fn_or_url
screen.receiver_url = msg.url()
elseif t == "userdata" or t == "string" then
screen.receiver_fn = nil
screen.receiver_url = fn_or_url
else
screen.receiver_fn = nil
screen.receiver_url = msg.url()
end
end
local empty_hash = hash("")
local function url_to_key(url) local function url_to_key(url)
return (url.socket or hash("")) .. (url.path or hash("")) .. (url.fragment or hash("")) return (url.socket or empty_hash) .. (url.path or empty_hash) .. (url.fragment or empty_hash)
end end
@ -1109,4 +1407,9 @@ function M.dump_stack()
return s return s
end end
function M.queue_size()
return #queue
end
return M return M

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)

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)

View File

@ -20,10 +20,10 @@ local LAYOUT_CHANGED = hash("layout_changed")
function M.window_resized(width, height) function M.window_resized(width, height)
WIDTH = width WIDTH = width
HEIGHT = height HEIGHT = height
LEFT = vmath.vector3(-WIDTH * 2, 0, 0) LEFT = vmath.vector3(-WIDTH, 0, 0)
RIGHT = vmath.vector3(WIDTH * 2, 0, 0) RIGHT = vmath.vector3(WIDTH, 0, 0)
TOP = vmath.vector3(0, HEIGHT * 2, 0) TOP = vmath.vector3(0, HEIGHT, 0)
BOTTOM = vmath.vector3(0, - HEIGHT * 2, 0) BOTTOM = vmath.vector3(0, - HEIGHT, 0)
end end
M.window_resized(tonumber(sys.get_config("display.width")), tonumber(sys.get_config("display.height"))) M.window_resized(tonumber(sys.get_config("display.width")), tonumber(sys.get_config("display.height")))
@ -121,11 +121,15 @@ local function create()
local current_transition = nil local current_transition = nil
local function create_transition(transition_id, node, fn, easing, duration, delay) local function create_transition(transition_id, node, fn, easing, duration, delay)
assert(transition_id, "You must provide a valid transition id")
assert(node, "You must provide a node")
assert(fn, "You must provide a transition function")
local t = transitions[transition_id] local t = transitions[transition_id]
-- find if there's already a transition for the node in -- find if there's already a transition for the node in
-- question and if so update it instead of creating a new -- question and if so update it instead of creating a new
-- transition -- transition
for _,transition in ipairs(t) do for _,transition in ipairs(t.transitions) do
if transition.node == node then if transition.node == node then
transition.fn = fn transition.fn = fn
transition.easing = easing transition.easing = easing
@ -169,10 +173,10 @@ local function create()
end end
local function start_transition(transition_id, url) local function start_transition(transition_id, url)
url = url or msg.url()
local t = transitions[transition_id] local t = transitions[transition_id]
table.insert(t.urls, url) table.insert(t.urls, url)
if t.in_progress_count == 0 then if t.in_progress_count == 0 then
table.insert(t.urls, msg.url())
current_transition = t current_transition = t
current_transition.id = transition_id current_transition.id = transition_id
if #t.transitions > 0 then if #t.transitions > 0 then
@ -250,7 +254,11 @@ local function create()
return instance return instance
end end
return instance return setmetatable(instance, {
__call = function(t, ...)
return instance.handle(...)
end
})
end end
function M.create(node) function M.create(node)

47
monarch/utils/async.lua Normal file
View File

@ -0,0 +1,47 @@
local M = {}
local NOT_STARTED = "not_started"
local YIELDED = "yielded"
local RESUMED = "resumed"
local RUNNING = "running"
function M.async(fn)
local co = coroutine.running()
local state = NOT_STARTED
local function await(fn, ...)
state = RUNNING
fn(...)
if state ~= RUNNING then
return
end
state = YIELDED
local r = { coroutine.yield() }
return unpack(r)
end
local function resume(...)
if state ~= RUNNING then
state = RUNNING
local ok, err = coroutine.resume(co, ...)
if not ok then
print(err)
print(debug.traceback())
end
end
end
if co then
return fn(await, resume)
else
co = coroutine.create(fn)
return resume(await, resume)
end
end
return setmetatable(M, {
__call = function(t, ...)
return M.async(...)
end
})

View File

@ -6,13 +6,18 @@ function M.create()
local callback = nil local callback = nil
local callback_count = 0 local callback_count = 0
local all_callbacks_done = false
local function is_done() local function is_done()
return callback_count == 0 return callback_count == 0
end end
local function invoke_if_done() local function invoke_if_done()
if all_callbacks_done then
print("Warning: The same callback will be invoked twice from the callback tracker!")
end
if callback_count == 0 and callback then if callback_count == 0 and callback then
all_callbacks_done = true
local ok, err = pcall(callback) local ok, err = pcall(callback)
if not ok then print(err) end if not ok then print(err) end
end end
@ -41,26 +46,12 @@ function M.create()
invoke_if_done() invoke_if_done()
end end
function instance.yield_until_done()
local co = coroutine.running()
callback = function()
local ok, err = coroutine.resume(co)
if not ok then
print(err)
end
end
invoke_if_done()
if not is_done() then
coroutine.yield()
end
end
return instance return instance
end end
return setmetatable(M, { return setmetatable(M, {
__call = function(_, ...) __call = function(_, ...)
return M.create(...) return M.create()
end end
}) })

2
test.settings Normal file
View File

@ -0,0 +1,2 @@
[bootstrap]
main_collection = /test/test.collectionc

View File

@ -4,7 +4,10 @@ function M.seconds(amount)
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")
timer.delay(amount, false, function() timer.delay(amount, false, function()
coroutine.resume(co) local ok, err = coroutine.resume(co)
if not ok then
print(err)
end
end) end)
coroutine.yield() coroutine.yield()
end end
@ -13,10 +16,13 @@ function M.eval(fn, timeout)
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")
local start = socket.gettime() local start = socket.gettime()
timer.delay(0.01, true, function(self, handle, time_elapsed) timer.delay(0.02, true, function(self, handle, time_elapsed)
if fn() or (timeout and socket.gettime() > (start + timeout)) then if fn() or (timeout and socket.gettime() > (start + timeout)) then
timer.cancel(handle) timer.cancel(handle)
coroutine.resume(co) local ok, err = coroutine.resume(co)
if not ok then
print(err)
end
end end
end) end)
coroutine.yield() coroutine.yield()

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

@ -1,5 +1,5 @@
function init(self) function init(self)
local monarch = require "monarch.monarch" local monarch = require "monarch.monarch"
local data = monarch.data(hash("screen_preload")) local data = monarch.data(hash("screen_preload"))
data.count = data.count + 1 if data then data.count = data.count + 1 end
end end

View File

@ -26,6 +26,8 @@ embedded_instances {
" value: \"screen1:/go\"\n" " value: \"screen1:/go\"\n"
" type: PROPERTY_TYPE_URL\n" " type: PROPERTY_TYPE_URL\n"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"collectionproxy\"\n" " id: \"collectionproxy\"\n"
@ -84,12 +86,15 @@ embedded_instances {
" value: \"screen2\"\n" " value: \"screen2\"\n"
" type: PROPERTY_TYPE_HASH\n" " type: PROPERTY_TYPE_HASH\n"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"collectionfactory\"\n" " id: \"collectionfactory\"\n"
" type: \"collectionfactory\"\n" " type: \"collectionfactory\"\n"
" data: \"prototype: \\\"/test/data/screen2.collection\\\"\\n" " data: \"prototype: \\\"/test/data/screen2.collection\\\"\\n"
"load_dynamically: false\\n" "load_dynamically: false\\n"
"dynamic_prototype: false\\n"
"\"\n" "\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"
@ -147,6 +152,8 @@ embedded_instances {
" value: \"true\"\n" " value: \"true\"\n"
" type: PROPERTY_TYPE_BOOLEAN\n" " type: PROPERTY_TYPE_BOOLEAN\n"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"collectionproxy\"\n" " id: \"collectionproxy\"\n"
@ -215,6 +222,8 @@ embedded_instances {
" value: \"true\"\n" " value: \"true\"\n"
" type: PROPERTY_TYPE_BOOLEAN\n" " type: PROPERTY_TYPE_BOOLEAN\n"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"collectionproxy\"\n" " id: \"collectionproxy\"\n"
@ -278,6 +287,8 @@ embedded_instances {
" value: \"transition1:/go\"\n" " value: \"transition1:/go\"\n"
" type: PROPERTY_TYPE_URL\n" " type: PROPERTY_TYPE_URL\n"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"collectionproxy\"\n" " id: \"collectionproxy\"\n"
@ -376,12 +387,15 @@ embedded_instances {
" value: \"background\"\n" " value: \"background\"\n"
" type: PROPERTY_TYPE_HASH\n" " type: PROPERTY_TYPE_HASH\n"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"collectionfactory\"\n" " id: \"collectionfactory\"\n"
" type: \"collectionfactory\"\n" " type: \"collectionfactory\"\n"
" data: \"prototype: \\\"/test/data/background.collection\\\"\\n" " data: \"prototype: \\\"/test/data/background.collection\\\"\\n"
"load_dynamically: false\\n" "load_dynamically: false\\n"
"dynamic_prototype: false\\n"
"\"\n" "\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"
@ -439,12 +453,15 @@ embedded_instances {
" value: \"true\"\n" " value: \"true\"\n"
" type: PROPERTY_TYPE_BOOLEAN\n" " type: PROPERTY_TYPE_BOOLEAN\n"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"collectionfactory\"\n" " id: \"collectionfactory\"\n"
" type: \"collectionfactory\"\n" " type: \"collectionfactory\"\n"
" data: \"prototype: \\\"/test/data/screen_preload.collection\\\"\\n" " data: \"prototype: \\\"/test/data/screen_preload.collection\\\"\\n"
"load_dynamically: false\\n" "load_dynamically: false\\n"
"dynamic_prototype: false\\n"
"\"\n" "\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"
@ -502,6 +519,8 @@ embedded_instances {
" value: \"focus1:/go#focus1\"\n" " value: \"focus1:/go#focus1\"\n"
" type: PROPERTY_TYPE_URL\n" " type: PROPERTY_TYPE_URL\n"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"collectionproxy\"\n" " id: \"collectionproxy\"\n"

View File

@ -64,5 +64,17 @@ function M.last(url)
return messages[#messages] return messages[#messages]
end end
function M.filter(url, fn)
local t = {}
local messages = M.messages(url)
for i=1,#messages do
local message = messages[i]
if fn(message) then
t[#t + 1] = message
end
end
return t
end
return M return M

View File

@ -16,12 +16,15 @@ embedded_instances {
" z: 0.0\n" " z: 0.0\n"
" w: 1.0\n" " w: 1.0\n"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"embedded_components {\n" "embedded_components {\n"
" id: \"screensfactory\"\n" " id: \"screensfactory\"\n"
" type: \"collectionfactory\"\n" " type: \"collectionfactory\"\n"
" data: \"prototype: \\\"/test/data/screens.collection\\\"\\n" " data: \"prototype: \\\"/test/data/screens.collection\\\"\\n"
"load_dynamically: false\\n" "load_dynamically: false\\n"
"dynamic_prototype: false\\n"
"\"\n" "\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"

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)
@ -39,7 +38,7 @@ return function()
local screens_instances = {} local screens_instances = {}
local function is_shown(screen_id) local function is_visible(screen_id)
return monarch.is_visible(screen_id) return monarch.is_visible(screen_id)
end end
@ -65,9 +64,6 @@ return function()
local function wait_until_visible(screen_id) local function wait_until_visible(screen_id)
return wait_timeout(is_visible, screen_id) return wait_timeout(is_visible, screen_id)
end end
local function wait_until_shown(screen_id)
return wait_timeout(is_shown, screen_id)
end
local function wait_until_hidden(screen_id) local function wait_until_hidden(screen_id)
return wait_timeout(is_hidden, screen_id) return wait_timeout(is_hidden, screen_id)
end end
@ -77,6 +73,9 @@ return function()
local function wait_until_not_busy() local function wait_until_not_busy()
return wait_timeout(function() return not monarch.is_busy() end) return wait_timeout(function() return not monarch.is_busy() end)
end end
local function wait_until_stack(expected_screens)
return wait_timeout(function() return check_stack(expected_screens) end)
end
local function wait_until_loaded(screen_id) local function wait_until_loaded(screen_id)
wait_until_done(function(done) wait_until_done(function(done)
monarch.when_preloaded(screen_id, done) monarch.when_preloaded(screen_id, done)
@ -115,75 +114,90 @@ return function()
it("should be able to show screens and go back to previous screens", function() it("should be able to show screens and go back to previous screens", function()
monarch.show(SCREEN1_STR) monarch.show(SCREEN1_STR)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
assert_stack({ SCREEN1 })
monarch.show(SCREEN2) monarch.show(SCREEN2)
assert(wait_until_hidden(SCREEN1), "Screen1 was never hidden") assert(wait_until_stack({ SCREEN1, SCREEN2 }))
assert(wait_until_shown(SCREEN2), "Screen2 was never shown")
assert_stack({ SCREEN1, SCREEN2 })
monarch.back() monarch.back()
assert(wait_until_hidden(SCREEN2), "Screen2 was never hidden") assert(wait_until_stack({ SCREEN1 }))
assert(wait_until_shown(SCREEN1), "Screen1 was never shown")
assert_stack({ SCREEN1 })
monarch.back() monarch.back()
assert(wait_until_hidden(SCREEN1), "Screen1 was never hidden") assert(wait_until_stack({ }))
assert_stack({ })
end) end)
it("should be able to replace screens at the top of the stack", function() it("should be able to replace screens at the top of the stack", function()
monarch.show(SCREEN1_STR) monarch.show(SCREEN1_STR)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }), "Screen1 was never shown")
assert_stack({ SCREEN1 })
monarch.show(SCREEN2) monarch.show(SCREEN2)
assert(wait_until_hidden(SCREEN1), "Screen1 was never hidden") assert(wait_until_stack({ SCREEN1, SCREEN2 }))
assert(wait_until_shown(SCREEN2), "Screen2 was never shown")
assert_stack({ SCREEN1, SCREEN2 })
monarch.replace(SCREEN1) monarch.replace(SCREEN1)
assert(wait_until_hidden(SCREEN2), "Screen2 was never hidden") assert(wait_until_stack({ SCREEN1, SCREEN1 }))
assert(wait_until_shown(SCREEN1), "Screen1 was never shown")
assert_stack({ SCREEN1, SCREEN1 })
end) end)
it("should be able to tell if a screen is visible or not", function() it("should be able to tell if a screen is visible or not", function()
assert(not monarch.is_visible(SCREEN1)) assert(not monarch.is_visible(SCREEN1))
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
assert_stack({ SCREEN1 }) assert(wait_until_visible(SCREEN1))
assert(monarch.is_visible(SCREEN1))
monarch.show(SCREEN2) monarch.show(SCREEN2)
assert(wait_until_hidden(SCREEN1), "Screen1 was never hidden") assert(wait_until_stack({ SCREEN1, SCREEN2 }))
assert(wait_until_shown(SCREEN2), "Screen2 was never shown") assert(wait_until_hidden(SCREEN1))
assert_stack({ SCREEN1, SCREEN2 }) assert(wait_until_visible(SCREEN2))
assert(not monarch.is_visible(SCREEN1))
assert(monarch.is_visible(SCREEN2))
monarch.show(POPUP1) monarch.show(POPUP1)
assert(wait_until_shown(POPUP1), "Popup1 was never shown") assert(wait_until_stack({ SCREEN1, SCREEN2, POPUP1 }))
assert_stack({ SCREEN1, SCREEN2, POPUP1 }) assert(wait_until_hidden(SCREEN1))
assert(not monarch.is_visible(SCREEN1)) assert(wait_until_visible(SCREEN2))
assert(monarch.is_visible(SCREEN2)) assert(wait_until_visible(POPUP1))
assert(monarch.is_visible(POPUP1)) end)
it("should be able to tell if a screen is loaded or not", function()
assert(not monarch.is_loaded(SCREEN1))
monarch.show(SCREEN1)
assert(wait_until_visible(SCREEN1))
assert(monarch.is_loaded(SCREEN1))
monarch.hide(SCREEN1)
assert(wait_until_hidden(SCREEN1))
assert(not monarch.is_loaded(SCREEN1))
end) end)
it("should be able to show a screen without adding it to the stack", function() it("should be able to show a screen without adding it to the stack", function()
monarch.show(BACKGROUND, { no_stack = true }) monarch.show(BACKGROUND, { no_stack = true })
assert(wait_until_shown(BACKGROUND), "Background was never shown") assert(wait_until_visible(BACKGROUND), "Background was never shown")
assert_stack({ }) assert(wait_until_stack({ }))
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
assert_stack({ SCREEN1 }) end)
it("should be able to show a screen without adding it to the stack at any time", function()
monarch.show(SCREEN1)
assert(wait_until_not_busy())
assert(wait_until_stack({ SCREEN1 }))
monarch.show(BACKGROUND, { no_stack = true })
assert(wait_until_not_busy())
assert(wait_until_visible(BACKGROUND))
assert(wait_until_stack({ SCREEN1 }))
monarch.show(SCREEN2)
assert(wait_until_not_busy())
assert(wait_until_stack({ SCREEN1, SCREEN2 }))
monarch.back()
assert(wait_until_not_busy())
assert(wait_until_visible(SCREEN1))
assert(wait_until_stack({ SCREEN1 }))
end) end)
it("should be able to hide a screen not added to the stack", function() it("should be able to hide a screen not added to the stack", function()
monarch.show(BACKGROUND, { no_stack = true }) monarch.show(BACKGROUND, { no_stack = true })
assert(wait_until_shown(BACKGROUND), "Background was never shown") assert(wait_until_visible(BACKGROUND), "Background was never shown")
assert_stack({ }) assert_stack({ })
monarch.hide(BACKGROUND) monarch.hide(BACKGROUND)
@ -193,36 +207,31 @@ return function()
it("should be able to hide the top screen", function() it("should be able to hide the top screen", function()
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
assert_stack({ SCREEN1 })
monarch.show(SCREEN2) monarch.show(SCREEN2)
assert(wait_until_hidden(SCREEN1), "Screen1 was never hidden") assert(wait_until_stack({ SCREEN1, SCREEN2 }))
assert(wait_until_shown(SCREEN2), "Screen2 was never shown")
assert_stack({ SCREEN1, SCREEN2 })
assert(monarch.hide(SCREEN1) == false) assert(monarch.hide(SCREEN1) == false)
assert(monarch.hide(SCREEN2) == true) assert(monarch.hide(SCREEN2) == true)
assert(wait_until_hidden(SCREEN2), "Screen2 was never hidden") assert(wait_until_stack({ SCREEN1 }))
assert(wait_until_shown(SCREEN1), "Screen1 was never shown")
assert_stack({ SCREEN1 })
end) end)
it("should be able to pass data to a screen when showing it or going back to it", function() it("should be able to pass data to a screen when showing it or going back to it", function()
local data1 = { foo = "bar" } local data1 = { foo = "bar" }
monarch.show(SCREEN1, nil, data1) monarch.show(SCREEN1, nil, data1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_visible(SCREEN1), "Screen1 was never shown")
local data2 = { boo = "car" } local data2 = { boo = "car" }
monarch.show(SCREEN2, nil, data2) monarch.show(SCREEN2, nil, data2)
assert(wait_until_shown(SCREEN2), "Screen2 was never shown") assert(wait_until_visible(SCREEN2), "Screen2 was never shown")
assert(monarch.data(SCREEN1) == data1, "Expected data on screen1 doesn't match actual data") assert(monarch.data(SCREEN1) == data1, "Expected data on screen1 doesn't match actual data")
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_shown(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")
end) end)
@ -230,51 +239,40 @@ return function()
it("should be able to show the same screen twice", function() it("should be able to show the same screen twice", function()
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
assert_stack({ SCREEN1 })
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1, SCREEN1 }))
assert_stack({ SCREEN1, SCREEN1 })
end) end)
it("should be able to clear the stack if trying to show the same screen twice", function() it("should be able to clear the stack if trying to show the same screen twice", function()
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
assert_stack({ SCREEN1 })
monarch.show(SCREEN2) monarch.show(SCREEN2)
assert(wait_until_shown(SCREEN2), "Screen2 was never shown") assert(wait_until_stack({ SCREEN1, SCREEN2 }))
assert_stack({ SCREEN1, SCREEN2 })
monarch.show(SCREEN1, { clear = true }) monarch.show(SCREEN1, { clear = true })
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
assert_stack({ SCREEN1 })
end) end)
it("should be able to show one popup on top of another if 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) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
assert_stack({ SCREEN1 })
monarch.show(POPUP1) monarch.show(POPUP1)
assert(wait_until_shown(POPUP1), "Popup1 was never shown") assert(wait_until_stack({ SCREEN1, POPUP1 }))
assert_stack({ SCREEN1, POPUP1 })
monarch.show(POPUP2) monarch.show(POPUP2)
assert(wait_until_shown(POPUP2), "Popup2 was never shown") assert(wait_until_stack({ SCREEN1, POPUP1, POPUP2 }))
assert_stack({ SCREEN1, POPUP1, POPUP2 })
end) end)
it("should prevent further operations while hiding/showing a screen", function() it("should be able to queue multiple display commands", function()
monarch.show(SCREEN1) monarch.show(SCREEN1)
monarch.show(SCREEN2) monarch.show(SCREEN2)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1, SCREEN2 }))
assert(wait_until_shown(SCREEN2), "Screen2 was never shown")
assert_stack({ SCREEN1, SCREEN2 })
assert(monarch.back()) assert(monarch.back())
assert(monarch.back()) assert(monarch.back())
assert(wait_until_hidden(SCREEN1), "Screen1 was never hidden") assert(wait_until_stack({}), "Stack never became empty")
assert(wait_until_hidden(SCREEN2), "Screen2 was never hidden")
end) end)
@ -292,84 +290,63 @@ return function()
it("should close any open popups when showing a popup without the Popup On Popup flag", function() it("should close any open popups when showing a popup without the Popup On Popup flag", function()
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
assert_stack({ SCREEN1 })
monarch.show(POPUP2) monarch.show(POPUP2)
assert(wait_until_shown(POPUP2), "Popup2 was never shown") assert(wait_until_stack({ SCREEN1, POPUP2 }))
assert_stack({ SCREEN1, POPUP2 })
monarch.show(POPUP1) monarch.show(POPUP1)
assert(wait_until_shown(POPUP1), "Popup1 was never shown") assert(wait_until_stack({ SCREEN1, POPUP1 }))
assert_stack({ SCREEN1, POPUP1 })
end) end)
it("should close any open popups when showing a non-popup", function() it("should close any open popups when showing a non-popup", function()
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
assert_stack({ SCREEN1 })
monarch.show(POPUP1) monarch.show(POPUP1)
assert(wait_until_shown(POPUP1), "Popup1 was never shown") assert(wait_until_stack({ SCREEN1, POPUP1 }))
assert_stack({ SCREEN1, POPUP1 })
monarch.show(POPUP2) monarch.show(POPUP2)
assert(wait_until_shown(POPUP2), "Popup2 was never shown") assert(wait_until_stack({ SCREEN1, POPUP1, POPUP2 }))
assert_stack({ SCREEN1, POPUP1, POPUP2 })
monarch.show(SCREEN2) monarch.show(SCREEN2)
assert(wait_until_shown(SCREEN2), "Popup2 was never shown") assert(wait_until_stack({ SCREEN1, SCREEN2 }))
assert_stack({ SCREEN1, SCREEN2 })
end) end)
it("should close any open popups when replacing a non-popup", function() it("should close any open popups when replacing a non-popup", function()
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
assert_stack({ SCREEN1 })
monarch.show(POPUP1) monarch.show(POPUP1)
assert(wait_until_shown(POPUP1), "Popup1 was never shown") assert(wait_until_stack({ SCREEN1, POPUP1 }))
assert_stack({ SCREEN1, POPUP1 })
monarch.show(POPUP2) monarch.show(POPUP2)
assert(wait_until_shown(POPUP2), "Popup2 was never shown") assert(wait_until_stack({ SCREEN1, POPUP1, POPUP2 }))
assert_stack({ SCREEN1, POPUP1, POPUP2 })
monarch.replace(SCREEN2) monarch.replace(SCREEN2)
assert(wait_until_shown(SCREEN2), "Screen2 was never shown") assert(wait_until_stack({ SCREEN2 }))
assert_stack({ SCREEN2 })
end) end)
it("should replace a popup", function() it("should replace a popup", function()
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
assert_stack({ SCREEN1 })
monarch.show(POPUP1) monarch.show(POPUP1)
assert(wait_until_shown(POPUP1), "Popup1 was never shown") assert(wait_until_stack({ SCREEN1, POPUP1 }))
assert_stack({ SCREEN1, POPUP1 })
monarch.replace(POPUP2) monarch.replace(POPUP2)
assert(wait_until_shown(POPUP2), "Popup2 was never shown") assert(wait_until_stack({ SCREEN1, POPUP2 }))
assert_stack({ SCREEN1, POPUP2 })
end) end)
it("should replace a pop-up two levels down", function() it("should replace a pop-up two levels down", function()
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
assert_stack({ SCREEN1 })
monarch.show(POPUP1) monarch.show(POPUP1)
assert(wait_until_shown(POPUP1), "Popup1 was never shown") assert(wait_until_stack({ SCREEN1, POPUP1 }))
assert_stack({ SCREEN1, POPUP1 })
monarch.show(POPUP2) monarch.show(POPUP2)
assert(wait_until_shown(POPUP2), "Popup2 was never shown") assert(wait_until_stack({ SCREEN1, POPUP1, POPUP2 }))
assert_stack({ SCREEN1, POPUP1, POPUP2 })
monarch.show(POPUP2, { pop = 2 }) monarch.show(POPUP2, { pop = 2 })
assert(wait_until_shown(POPUP2), "Popup2 was never shown") assert(wait_until_stack({ SCREEN1, POPUP2 }))
assert_stack({ SCREEN1, POPUP2 })
end) end)
it("shouldn't over-pop popups", function() it("shouldn't over-pop popups", function()
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
assert_stack({ SCREEN1 })
monarch.show(POPUP1) monarch.show(POPUP1)
assert(wait_until_shown(POPUP1), "Popup1 was never shown") assert(wait_until_stack({ SCREEN1, POPUP1 }))
assert_stack({ SCREEN1, POPUP1 })
monarch.show(POPUP2, { pop = 10 }) monarch.show(POPUP2, { pop = 10 })
assert(wait_until_shown(POPUP2), "Popup2 was never shown") assert(wait_until_stack({ SCREEN1, POPUP2 }))
assert_stack({ SCREEN1, POPUP2 })
end) end)
it("should be able to get the id of the screen at the top and bottom of the stack", function() it("should be able to get the id of the screen at the top and bottom of the stack", function()
@ -379,7 +356,7 @@ return function()
assert(monarch.bottom(-1) == nil) assert(monarch.bottom(-1) == nil)
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
assert(monarch.top() == SCREEN1) assert(monarch.top() == SCREEN1)
assert(monarch.top(0) == SCREEN1) assert(monarch.top(0) == SCREEN1)
assert(monarch.top(1) == nil) assert(monarch.top(1) == nil)
@ -388,9 +365,7 @@ return function()
assert(monarch.bottom(-1) == nil) assert(monarch.bottom(-1) == nil)
monarch.show(SCREEN2) monarch.show(SCREEN2)
assert(wait_until_hidden(SCREEN1), "Screen1 was never hidden") assert(wait_until_stack({ SCREEN1, SCREEN2 }))
assert(wait_until_shown(SCREEN2), "Screen2 was never shown")
assert_stack({ SCREEN1, SCREEN2 })
assert(monarch.top(0) == SCREEN2) assert(monarch.top(0) == SCREEN2)
assert(monarch.top(-1) == SCREEN1) assert(monarch.top(-1) == SCREEN1)
assert(monarch.bottom(0) == SCREEN1) assert(monarch.bottom(0) == SCREEN1)
@ -399,8 +374,8 @@ return function()
it("should be busy while transition is running", function() it("should be busy while transition is running", function()
monarch.show(TRANSITION1) monarch.show(TRANSITION1)
assert(wait_until_shown(TRANSITION1), "Transition1 was never shown")
assert(monarch.is_busy()) assert(monarch.is_busy())
assert(wait_until_visible(TRANSITION1), "Transition1 was never shown")
assert(wait_until_not_busy()) assert(wait_until_not_busy())
end) end)
@ -413,12 +388,26 @@ return function()
assert(not monarch.is_preloading(TRANSITION1)) assert(not monarch.is_preloading(TRANSITION1))
end) end)
it("should be able to preload a screen and keep it loaded", function()
assert(not monarch.is_preloading(TRANSITION1))
monarch.preload(TRANSITION1, { keep_loaded = true })
wait_until_done(function(done)
monarch.when_preloaded(TRANSITION1, done)
end)
monarch.show(TRANSITION1)
assert(wait_until_visible(TRANSITION1), "Transition1 was never shown")
monarch.back()
assert(wait_until_hidden(TRANSITION1), "Transition1 was never hidden")
assert(monarch.is_preloaded(TRANSITION1))
end)
it("should ignore any preload calls while busy", function() it("should ignore any preload calls while busy", function()
monarch.show(TRANSITION1) monarch.show(TRANSITION1)
-- previously a call to preload() while also showing a screen would -- previously a call to preload() while also showing a screen would
-- lock up monarch. See issue #32 -- lock up monarch. See issue #32
monarch.preload(TRANSITION1) monarch.preload(TRANSITION1)
assert(wait_until_shown(TRANSITION1), "Transition1 was never shown") assert(wait_until_visible(TRANSITION1), "Transition1 was never shown")
end) end)
it("should be able to notify listeners of navigation events", function() it("should be able to notify listeners of navigation events", function()
@ -428,54 +417,48 @@ return function()
monarch.add_listener(URL2) monarch.add_listener(URL2)
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(mock_msg.messages(URL1)[1].message_id == monarch.SCREEN_TRANSITION_IN_STARTED)
assert(mock_msg.messages(URL1)[1].message.screen == SCREEN1)
assert(mock_msg.messages(URL2)[1].message_id == monarch.SCREEN_TRANSITION_IN_STARTED)
assert(mock_msg.messages(URL2)[1].message.screen == SCREEN1)
assert(wait_until_not_busy()) assert(wait_until_not_busy())
assert(mock_msg.messages(URL1)[2].message_id == monarch.SCREEN_TRANSITION_IN_FINISHED)
assert(mock_msg.messages(URL1)[2].message.screen == SCREEN1)
assert(mock_msg.messages(URL2)[2].message_id == monarch.SCREEN_TRANSITION_IN_FINISHED)
assert(mock_msg.messages(URL2)[2].message.screen == SCREEN1)
monarch.remove_listener(URL2) monarch.remove_listener(URL2)
monarch.show(SCREEN2) monarch.show(SCREEN2)
assert(wait_until_not_busy()) assert(wait_until_not_busy())
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_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() monarch.back()
assert(wait_until_not_busy()) assert(wait_until_not_busy())
assert(#mock_msg.messages(URL1) == 10) local messages = mock_msg.filter(URL1, function(m)
assert(#mock_msg.messages(URL2) == 2) return m.message.screen == SCREEN1
assert(mock_msg.messages(URL1)[7].message_id == monarch.SCREEN_TRANSITION_OUT_STARTED) end)
assert(mock_msg.messages(URL1)[7].message.screen == SCREEN2) assert(messages[1].message_id == monarch.SCREEN_TRANSITION_IN_STARTED)
assert(mock_msg.messages(URL1)[8].message_id == monarch.SCREEN_TRANSITION_IN_STARTED) assert(messages[2].message_id == monarch.SCREEN_TRANSITION_IN_FINISHED)
assert(mock_msg.messages(URL1)[8].message.screen == SCREEN1) assert(messages[3].message_id == monarch.SCREEN_TRANSITION_OUT_STARTED)
assert(mock_msg.messages(URL1)[9].message_id == monarch.SCREEN_TRANSITION_IN_FINISHED) assert(messages[4].message_id == monarch.SCREEN_TRANSITION_OUT_FINISHED)
assert(mock_msg.messages(URL1)[9].message.screen == SCREEN1) assert(messages[5].message_id == monarch.SCREEN_TRANSITION_IN_STARTED)
assert(mock_msg.messages(URL1)[10].message_id == monarch.SCREEN_TRANSITION_OUT_FINISHED) assert(messages[6].message_id == monarch.SCREEN_TRANSITION_IN_FINISHED)
assert(mock_msg.messages(URL1)[10].message.screen == SCREEN2)
messages = mock_msg.filter(URL2, function(m)
return m.message.screen == SCREEN1
end)
assert(messages[1].message_id == monarch.SCREEN_TRANSITION_IN_STARTED)
assert(messages[2].message_id == monarch.SCREEN_TRANSITION_IN_FINISHED)
messages = mock_msg.filter(URL1, function(m)
return m.message.screen == SCREEN2
end)
assert(messages[1].message_id == monarch.SCREEN_TRANSITION_IN_STARTED)
assert(messages[2].message_id == monarch.SCREEN_TRANSITION_IN_FINISHED)
assert(messages[3].message_id == monarch.SCREEN_TRANSITION_OUT_STARTED)
assert(messages[4].message_id == monarch.SCREEN_TRANSITION_OUT_FINISHED)
end) end)
it("should be able to show a screen even while it is preloading", function() it("should be able to show a screen even while it is preloading", function()
monarch.show(SCREEN_PRELOAD, nil, { count = 1 }) monarch.show(SCREEN_PRELOAD, nil, { count = 1 })
assert(wait_until_shown(SCREEN_PRELOAD), "Screen_preload was never shown") assert(wait_until_visible(SCREEN_PRELOAD), "Screen_preload was never shown")
end) end)
it("should be able to preload a screen and always keep it loaded", function() it("should be able to preload a screen and always keep it loaded", function()
monarch.show(SCREEN_PRELOAD) monarch.show(SCREEN_PRELOAD)
assert(wait_until_shown(SCREEN_PRELOAD), "Screen_preload was never shown") assert(wait_until_visible(SCREEN_PRELOAD), "Screen_preload was never shown")
monarch.back() monarch.back()
assert(wait_until_hidden(SCREEN_PRELOAD), "Screen_preload was never hidden") assert(wait_until_hidden(SCREEN_PRELOAD), "Screen_preload was never hidden")
assert(monarch.is_preloaded(SCREEN_PRELOAD)) assert(monarch.is_preloaded(SCREEN_PRELOAD))
@ -483,12 +466,12 @@ return function()
it("should be able to reload a preloaded screen", function() it("should be able to reload a preloaded screen", function()
monarch.show(SCREEN_PRELOAD, nil, { count = 1 }) monarch.show(SCREEN_PRELOAD, nil, { count = 1 })
assert(wait_until_shown(SCREEN_PRELOAD), "Screen_preload was never shown") assert(wait_until_visible(SCREEN_PRELOAD), "Screen_preload was never shown")
-- first time the screen gets loaded it will increment the count -- first time the screen gets loaded it will increment the count
assert(monarch.data(SCREEN_PRELOAD).count == 2) assert(monarch.data(SCREEN_PRELOAD).count == 2)
monarch.show(SCREEN_PRELOAD, { clear = true, reload = true }, { count = 1 }) monarch.show(SCREEN_PRELOAD, { clear = true, reload = true }, { count = 1 })
assert(wait_until_shown(SCREEN_PRELOAD), "Screen_preload was never shown") assert(wait_until_visible(SCREEN_PRELOAD), "Screen_preload was never shown")
-- second time the screen gets shown it will be reloaded and increment the count -- second time the screen gets shown it will be reloaded and increment the count
assert(monarch.data(SCREEN_PRELOAD).count == 2) assert(monarch.data(SCREEN_PRELOAD).count == 2)
end) end)
@ -499,78 +482,92 @@ return function()
_G.focus1_lost = nil _G.focus1_lost = nil
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1 }))
monarch.show(FOCUS1) monarch.show(FOCUS1)
assert(wait_until_shown(FOCUS1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1, FOCUS1 }))
assert(wait_until_visible(FOCUS1))
assert(_G.focus1_gained) assert(_G.focus1_gained)
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown") assert(wait_until_stack({ SCREEN1, FOCUS1, SCREEN1 }))
assert(wait_until_hidden(FOCUS1), "Focus1 was never hidden") assert(wait_until_hidden(FOCUS1))
assert(_G.focus1_lost) assert(_G.focus1_lost)
end) end)
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_shown(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_shown(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_shown(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_shown(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)
wait_until_shown(SCREEN1)
monarch.show(SCREEN2) monarch.show(SCREEN2)
wait_until_shown(SCREEN2) assert(wait_until_stack({ SCREEN1, SCREEN2 }))
assert(wait_until_hidden(SCREEN1))
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)
it("should not be able to post messages to proxy screens without a receiver url", function() it("should not be able to post messages to proxy screens without a receiver url", function()
monarch.show(POPUP1) monarch.show(POPUP1)
wait_until_shown(POPUP1) wait_until_visible(POPUP1)
local ok, err = monarch.post(POPUP1, "foobar") local ok, err = monarch.post(POPUP1, "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")
end) end)
it("should be able to check if a screen is is a popup", function()
assert(not monarch.is_popup(SCREEN1))
assert(monarch.is_popup(POPUP1))
end)
end) end)
end end

View File

@ -8,6 +8,12 @@ local easing = require "monarch.transitions.easings"
return function() return function()
local function wait_timeout(fn, ...)
local args = { ... }
cowait(function() return fn(unpack(args)) end, 5)
return fn(...)
end
describe("transitions", function() describe("transitions", function()
before(function() before(function()
mock_msg.mock() mock_msg.mock()
@ -22,6 +28,28 @@ describe("transitions", function()
end) end)
it("should replace an existing transition with a new one", function()
local one = false
function dummy_transition1(node, to, easing, duration, delay, cb)
one = true
end
local two = false
function dummy_transition2(node, to, easing, duration, delay, cb)
two = true
end
local node = gui.new_box_node(vmath.vector3(), vmath.vector3(100, 100, 0))
local duration = 2
local t = transitions.create(node)
t.show_in(dummy_transition1, easing.OUT, duration, delay or 0)
t.show_in(dummy_transition2, easing.OUT, duration, delay or 0)
t.handle(monarch.TRANSITION.SHOW_IN)
wait_timeout(function() return one or two end)
assert(two)
assert(not one)
end)
it("should replay and immediately finish on layout change", function() it("should replay and immediately finish on layout change", function()
function dummy_transition(node, to, easing, duration, delay, cb) function dummy_transition(node, to, easing, duration, delay, cb)
print("dummy transition") print("dummy transition")