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

Compare commits

..

35 Commits
1.3 ... 2.5.0

Author SHA1 Message Date
Björn Ritzl
724713f9e4 Include transition id in TRANSITION.DONE message
Also send the TRANSITION.DONE message to the gui that was transitioned
2018-06-01 08:43:08 +02:00
Björn Ritzl
39628b75af Merge pull request #17 from britzl/resizefix
Replay transitions when the layout changes
2018-05-26 19:43:42 +02:00
Björn Ritzl
dbf1bdea9d Replay transitions when the layout changes 2018-05-25 15:33:51 +02:00
Björn Ritzl
d45005cd90 Added documentation about monarch.debug()
Fixes #16
2018-05-24 16:34:33 +02:00
Björn Ritzl
a701f6cd92 Fixed broken tests 2018-03-26 06:33:11 +02:00
Björn Ritzl
f811c1f306 Accept both hash and string as screen id 2018-03-24 12:08:16 +01:00
Björn Ritzl
a4cef48239 Handle layout change and window resize for transitions 2018-03-19 22:56:12 +01:00
Björn Ritzl
a25e6d1ba5 Merge branch 'master' of https://github.com/britzl/monarch 2018-02-18 22:32:55 +01:00
Björn Ritzl
2f298b29d4 Added logo 2018-02-18 22:32:21 +01:00
Björn Ritzl
2bac31b155 Update README.md 2018-02-18 22:21:51 +01:00
Björn Ritzl
28e54beefb Update README.md 2018-02-09 22:18:56 +01:00
Björn Ritzl
7cd313f1e6 Update README.md 2018-02-09 22:14:03 +01:00
Björn Ritzl
0c0a206ba0 Update README.md 2018-02-09 22:11:20 +01:00
Björn Ritzl
973ab5b01c Update README.md 2018-02-09 22:00:13 +01:00
Björn Ritzl
9d9d93cd4c Update README.md 2017-12-28 21:20:09 +01:00
Björn Ritzl
30615c0044 Update README.md 2017-12-28 19:59:20 +01:00
Björn Ritzl
cd75f4a948 Added popup tests 2017-12-28 19:44:52 +01:00
Björn Ritzl
3c62d8caa0 Added new version of screen setup image 2017-12-28 00:07:03 +01:00
Björn Ritzl
9cdf4817e8 Added popup_on_popup flag to screens 2017-12-28 00:02:10 +01:00
Björn Ritzl
1467b136d8 Added a note on letting the init fucntion of the screen.script finish first 2017-12-25 00:24:25 +01:00
Björn Ritzl
995843ff20 Merge branch 'dev/agulev' 2017-12-08 07:42:07 +01:00
Björn Ritzl
31fdf89bd6 Update README.md 2017-12-08 06:52:52 +01:00
Björn Ritzl
eca1f54ced Update README.md 2017-12-08 06:52:30 +01:00
Björn Ritzl
9c48899440 Update README.md 2017-12-08 06:51:21 +01:00
Björn Ritzl
2d527c39ff Update README.md 2017-12-08 06:49:03 +01:00
Björn Ritzl
a6e4cd0771 Update README.md 2017-12-08 06:47:57 +01:00
Björn Ritzl
6474c9a173 Improved the transition module
The transition module will now properly handle if the same transition is started more than once before completed. In such a case only one animation will be played but all senders will be notified when completed.
2017-12-07 23:03:54 +01:00
Björn Ritzl
1d476424ce Added more logging 2017-12-07 21:09:13 +01:00
Björn Ritzl
007a4eced3 Improved on the state handling while showing/hiding screens
Also added simple debug logging
2017-12-07 20:49:48 +01:00
Björn Ritzl
6caa41e9f6 Merge branch 'master' of https://github.com/britzl/monarch 2017-12-07 20:48:20 +01:00
Björn Ritzl
1479839ed0 Improved the wait the example app is delaying startup until all screens are ready 2017-12-07 20:48:18 +01:00
Björn Ritzl
ee66e8bec8 Update README.md 2017-12-07 10:58:43 +01:00
Björn Ritzl
0c45c3007a Bug in bash script 2017-12-07 10:44:11 +01:00
Björn Ritzl
617610ee69 Updated build scripts 2017-12-07 10:39:19 +01:00
Björn Ritzl
19e9c0d8a7 Added multiple bugfixes 2017-12-06 21:25:27 +01:00
25 changed files with 1892 additions and 184 deletions

View File

@@ -22,6 +22,7 @@ env:
global:
- secure: "1rVLsDcb7dFdgyB9D1JQDr4JhWSosoMvgYgrqZNPxJ/Du3qtY3bk6dgQim+g2fDMQpDOPCQ/EhmhtrLJrIgBhhvOcsrVKT8gl9ZnATw5tHGI6XTw3eod8WgsU8owlc7CaT3XaUgwVshmW3oB/257SDf6kHwsCv/gAJuCEL5RZp76BhTWsfyeDCgz5XXgWx4a21tcIWz96jxEsrYQKLLV2ne55CxU5Hw9IMU7Ig7pkGoYCf1g+iUEA39NC8nIrQibUoJj3yNB2u3ZFwGf2LuDjjkSIsyYWn1LzA2fQYw5uAcjiQ/aDkj6sAEvwrWsIsJhOon5cQBFIU6cIIN2oK3A7BA0zJj0EsTFPUMIeryyoqiuLUDoIvHD/eEqouNduP6Kml02Ql0pDZnjDy/+nzp2e7VA5Sd9Xg1XKd1mmHKx4nc2U+IcIDZWAerFKcqQqeZSwzz5igv07w5zYZ99KCSBMH2K/2H/CNekHa6SQQ29mC8D3lDXOfwEq3fAhsabgUGe2uAgUY1nKwJBKEi7r+KEROBr5ydkWenzbCXv3GNNsuCHKpNFuoZv3QMyjUjlPBxZVndNLSv85juhkBx6wXAh8CxTt78Y8GV0xI8oazSM065gpDmENGVqyO1bUn2CZF8YRC4MLfHK+245QN82ui+YOqVudTX8RGWnX0GFUncjaRQ="
- DEFOLD_USER=bjorn.ritzl@king.com
- DEFOLD_BOOSTRAP_COLLECTION=/test/test.collectionc
script:
- "./.test/run.sh"
- "./.travis/run.sh"

View File

@@ -5,30 +5,39 @@ else
PLATFORM="$1"
fi
echo "${PLATFORM}"
# {"version": "1.2.89", "sha1": "5ca3dd134cc960c35ecefe12f6dc81a48f212d40"}
# Get SHA1 of the current Defold stable release
SHA1=$(curl -s http://d.defold.com/stable/info.json | sed 's/.*sha1": "\(.*\)".*/\1/')
echo "Using Defold dmengine_headless version ${SHA1}"
#DMENGINE_URL="http://d.defold.com/archive/${SHA1}/engine/linux/dmengine_headless"
# Create dmengine_headless and bob.jar URLs
DMENGINE_URL="http://d.defold.com/archive/${SHA1}/engine/${PLATFORM}/dmengine_headless"
BOB_URL="http://d.defold.com/archive/${SHA1}/bob/bob.jar"
# Download dmengine_headless
echo "Downloading ${DMENGINE_URL}"
curl -o dmengine_headless ${DMENGINE_URL}
chmod +x dmengine_headless
# Download bob.jar
echo "Downloading ${BOB_URL}"
curl -o bob.jar ${BOB_URL}
# Fetch libraries if DEFOLD_AUTH and DEFOLD_USER are set
if [ -n "${DEFOLD_AUTH}" ] && [ -n "${DEFOLD_USER}" ]; then
echo "Running bob.jar - resolving dependencies"
java -jar bob.jar --auth "${DEFOLD_AUTH}" --email "${DEFOLD_USER}" resolve
fi
echo "Running bob.jar - building"
java -jar bob.jar --debug build
java -jar bob.jar --debug build --keep-unused
echo "Starting dmengine_headless"
if [ -n "${DEFOLD_BOOSTRAP_COLLECTION}" ]; then
./dmengine_headless --config=bootstrap.main_collection=${DEFOLD_BOOSTRAP_COLLECTION}
else
./dmengine_headless
fi

120
README.md
View File

@@ -1,3 +1,8 @@
![](docs/logo.jpg)
[![Build Status](https://travis-ci.org/britzl/monarch.svg?branch=master)](https://travis-ci.org/britzl/monarch)
[![Latest Release](https://img.shields.io/github/release/britzl/monarch.svg)](https://github.com/britzl/monarch/releases)
# Monarch
Monarch is a screen manager for the [Defold](https://www.defold.com) game engine.
@@ -12,11 +17,12 @@ Or point to the ZIP file of a [specific release](https://github.com/britzl/monar
Using Monarch requires that screens are created in a certain way. Once you have one or more screens created you can start navigating between the screens.
## Creating screens
Monarch screens are created in individual collections and loaded through collection proxies. The recommended setup is to create one game object per screen and per game object attach a collection proxy component and an instance of the ```screen.script``` provided by Monarch. The screen.script will take care of the setup of the screen. All you need to do is to make sure that the script properties on the ```screen.script``` are correct:
Monarch screens are created in individual collections and loaded through collection proxies. The recommended setup is to create one game object per screen and per game object attach a collection proxy component and an instance of the ```screen.script``` provided by Monarch. The ```screen.script``` will take care of the setup of the screen. All you need to do is to make sure that the script properties on the ```screen.script``` are correct:
* **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.
* **Popup (boolean)** - Check this if the screen should be treated as a [popup](#popups).
* **Popup on Popup (boolean)** - Check this if the screen is a [popup](#popups) and it can be shown on top of other popups.
* **Transition Url (url)** - Optional URL to call when the screen is about to be shown/hidden. Use this to trigger a transition (see the section on [transitions](#transitions)).
* **Focus Url (url)** - Optional URL to call when the screen gains or loses focus (see the section on [screen focus](#screen-focus-gainloss)).
@@ -40,6 +46,16 @@ You show a screen in one of two ways:
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``` 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)
msg.post("#", "show_first_screen")
end
function on_message(self, message_id, message, sender)
monarch.show(hash("first_screen"))
end
#### 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:
@@ -64,25 +80,50 @@ You navigate back in the screen hierarchy in one of two ways:
Monarch will acquire and release input focus on the game objects containing the proxies to the screens and ensure that only the top-most screen will ever have input focus.
## Popups
A screen that is flagged as a popup (see list of screen properties above) will be treated slightly differently when it comes to navigation. If a popup is at the top of the stack (ie currently shown) and another screen or popup is shown then the current popup will be removed from the stack. This means that it is not possible to have a popup anywhere in the stack but the top. This also means that you cannot navigate back to a popup since popups can only exist on the top of the stack. Another important difference between normal screens and popups is that when 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.
A screen that is flagged as a popup (see [list of screen properties](#creating-screens) above) will be treated slightly differently when it comes to navigation.
### 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:
* Stack is ```[A, B]```
* A call to ```monarch.show(C)``` is made and C is a popup
* Stack is ```[A, B, C]```
* A call to ```monarch.show(D)```
* Stack is ```[A, B, C]``` and B will still be visible
### 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.
* 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
* 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).
* 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
* Stack is ```[A, B, D]```
### Screen on popup
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
* A call to ```monarch.show(E)``` is made and E is not a popup
* Stack is ```[A, B, E]```
## 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 property described above should be the URL to a script with an ```on_message``` handlers for the following messages:
* ```transition_show_in```
* ```transition_show_out```
* ```transition_back_in```
* ```transition_back_out```
* ```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``` message back to the sender to indicate that the transition is completed and that Monarch can continue the navigation sequence. Monarch comes with a system for setting up transitions easily in a gui_script. Example:
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. Monarch comes with a system for setting up transitions easily in a gui_script. Example:
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'
@@ -97,7 +138,54 @@ When a transition is completed it is up to the developer to send a ```transition
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
### Predefined transitions
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```
### 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 focus gain/loss
Monarch will send focus gain and focus loss messages if a Focus Url 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:
@@ -105,9 +193,9 @@ Monarch will send focus gain and focus loss messages if a Focus Url was provided
local monarch = require "monarch.monarch"
function on_message(self, message_id, message, sender)
if message_id == monarch.FOCUS_GAINED then
if message_id == monarch.FOCUS.GAINED then
print("Focus gained, previous screen: ", message.id)
elseif message_id == monarch.FOCUS_LOST then
elseif message_id == monarch.FOCUS.LOST then
print("Focus lost, next screen: ", message.id)
end
end
@@ -118,13 +206,13 @@ Both the ```monarch.show()``` and ```monarch.back()``` functions take an optiona
## Monarch API
### monarch.show(screen_id, [options], [data], [callback])
Show a Monarch screen
Show a Monarch screen. Note that the screen must be registered before it can be shown. The ```init()``` function of the ```screen.script``` takes care of registration.
**PARAMETERS**
* ```screen_id``` (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) - Function to call when the new screen is visible.
* ```callback``` (function) - Optional function to call when the new screen is visible.
The options table can contain the following fields:
@@ -136,7 +224,7 @@ Go back to a previous Monarch screen
**PARAMETERS**
* ```data``` (table) - Optional data to associate with the screen you are going back to. Retrieve using ```monarch.data()```.
* ```callback``` (function) - Function to call when the previous screen is visible.
* ```callback``` (function) - Optional function to call when the previous screen is visible.
### monarch.data(screen_id)
@@ -157,3 +245,7 @@ Check if a Monarch screen exists.
**RETURN**
* ```exists``` (boolean) - True if the screen exists.
### monarch.debug()
Enable verbose logging of the internals of Monarch.

BIN
docs/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 88 KiB

View File

@@ -0,0 +1,37 @@
name: "confirm"
scale_along_z: 0
embedded_instances {
id: "go"
data: "components {\n"
" id: \"confirm\"\n"
" component: \"/example/confirm.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
}
}

367
example/confirm.gui Normal file
View File

@@ -0,0 +1,367 @@
script: "/example/confirm.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: 350.0
y: 200.0
z: 0.0
w: 1.0
}
color {
x: 0.4
y: 0.4
z: 0.4
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
}
nodes {
position {
x: 85.0
y: -65.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: 150.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: "yes_button"
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_MANUAL
}
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: "YES"
font: "example"
id: "yes_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: "yes_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
}
nodes {
position {
x: -85.0
y: -65.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: 150.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: "no_button"
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_MANUAL
}
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: "NO"
font: "example"
id: "no_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: "no_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
}
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: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "Are you sure?"
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: "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
}
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")
self.yes = gui.get_node("yes_button")
self.no = gui.get_node("no_button")
gui.set_render_order(15)
self.transition = transitions.create(gui.get_node("root"))
.show_in(transitions.scale_in, gui.EASING_OUTBACK, 0.3, 0)
.show_out(transitions.scale_out, gui.EASING_INBACK, 0.3, 0)
.back_in(transitions.scale_in, gui.EASING_OUTBACK, 0.3, 0)
.back_out(transitions.scale_out, gui.EASING_INBACK, 0.3, 0)
end
function on_input(self, action_id, action)
if action_id == hash("touch") and action.released then
if gui.pick_node(self.yes, action.x, action.y) then
print("yes")
monarch.show(monarch.data(hash("confirm")).next, nil, nil, function()
print("next screen show done")
end)
elseif gui.pick_node(self.no, action.x, action.y) then
print("no")
monarch.back(function()
print("back from popup done")
end)
end
end
end
function on_message(self, message_id, message, sender)
self.transition.handle(message_id, message, sender)
end

View File

@@ -26,6 +26,11 @@ embedded_instances {
" value: \"menu:/go#menu\"\n"
" type: PROPERTY_TYPE_URL\n"
" }\n"
" properties {\n"
" id: \"focus_url\"\n"
" value: \"menu:/go#menu\"\n"
" type: PROPERTY_TYPE_URL\n"
" }\n"
"}\n"
"embedded_components {\n"
" id: \"collectionproxy\"\n"
@@ -375,3 +380,76 @@ embedded_instances {
z: 1.0
}
}
embedded_instances {
id: "confirm"
data: "components {\n"
" id: \"screen\"\n"
" component: \"/monarch/screen.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: \"confirm\"\n"
" type: PROPERTY_TYPE_HASH\n"
" }\n"
" properties {\n"
" id: \"popup\"\n"
" value: \"true\"\n"
" type: PROPERTY_TYPE_BOOLEAN\n"
" }\n"
" properties {\n"
" id: \"popup_on_popup\"\n"
" value: \"true\"\n"
" type: PROPERTY_TYPE_BOOLEAN\n"
" }\n"
" properties {\n"
" id: \"transition_url\"\n"
" value: \"confirm:/go#confirm\"\n"
" type: PROPERTY_TYPE_URL\n"
" }\n"
"}\n"
"embedded_components {\n"
" id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n"
" data: \"collection: \\\"/example/confirm.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,14 @@
profiles {
name: "Landscape"
qualifiers {
width: 1136
height: 640
}
}
profiles {
name: "Portrait"
qualifiers {
width: 640
height: 1136
}
}

View File

@@ -1,14 +1,13 @@
local monarch = require "monarch.monarch"
function init(self)
self.wait = true
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
end
function update(self)
-- Ensure that the initial screen has had enough time to register
if self.wait == true and monarch.screen_exists(hash("menu")) then
self.wait = false
function on_message(self, message_id, message, sender)
if message_id == hash("init_monarch") then
monarch.show(hash("menu"))
end
end

View File

@@ -481,5 +481,243 @@ nodes {
text_tracking: 0.0
}
material: "/builtins/materials/gui.material"
layouts {
name: "Landscape"
nodes {
position {
x: 568.0
y: 320.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: "startgame_button"
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
overridden_fields: 1
template_node_child: false
size_mode: SIZE_MODE_MANUAL
}
nodes {
position {
x: 977.0
y: 570.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: "about_button"
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
overridden_fields: 1
template_node_child: false
size_mode: SIZE_MODE_MANUAL
}
nodes {
position {
x: 150.0
y: 570.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: "back_button"
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
overridden_fields: 1
template_node_child: false
size_mode: SIZE_MODE_MANUAL
}
nodes {
position {
x: 568.0
y: 570.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_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "<text>"
font: "example"
id: "timestamp"
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
overridden_fields: 1
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
}
}
layouts {
name: "Portrait"
}
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@@ -31,4 +31,7 @@ end
function on_message(self, message_id, message, sender)
self.transition.handle(message_id, message, sender)
if message_id == monarch.FOCUS.GAINED then
gui.set_text(gui.get_node("timestamp"), os.date())
end
end

View File

@@ -299,6 +299,124 @@ nodes {
text_leading: 1.0
text_tracking: 0.0
}
nodes {
position {
x: 2.419
y: -14.414
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: 150.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: "about_button"
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_MANUAL
}
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: "ABOUT"
font: "example"
id: "about_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: "about_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
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@@ -5,7 +5,8 @@ function init(self)
msg.post(".", "acquire_input_focus")
self.ok = gui.get_node("ok_button")
self.cancel = gui.get_node("cancel_button")
gui.set_render_order(15)
self.about = gui.get_node("about_button")
gui.set_render_order(14)
self.transition = transitions.create(gui.get_node("root"))
.show_in(transitions.scale_in, gui.EASING_OUTBACK, 0.3, 0)
@@ -18,14 +19,19 @@ function on_input(self, action_id, action)
if action_id == hash("touch") and action.released then
if gui.pick_node(self.ok, action.x, action.y) then
print("ok")
monarch.show(hash("pregame"), nil, nil, function()
print("pregame show done")
monarch.show(hash("confirm"), nil, { next = hash("pregame") }, function()
print("confirm show done")
end)
elseif gui.pick_node(self.cancel, action.x, action.y) then
print("cancel")
monarch.back(function()
print("back from popup done")
end)
elseif gui.pick_node(self.about, action.x, action.y) then
print("about")
monarch.show(hash("about"), { clear = true, reload = true }, nil, function()
print("about show done")
end)
end
end
end

View File

@@ -4,7 +4,7 @@ version = 0.9
dependencies = https://github.com/britzl/deftest/archive/1.2.1.zip
[bootstrap]
main_collection = /test/test.collectionc
main_collection = /example/example.collectionc
[input]
game_binding = /input/game.input_bindingc
@@ -12,6 +12,8 @@ game_binding = /input/game.input_bindingc
[display]
width = 640
height = 1136
dynamic_orientation = 1
display_profiles = /example/example.display_profilesc
[script]
shared_state = 1
@@ -25,3 +27,6 @@ bundle_identifier = com.example.todo
[library]
include_dirs = monarch
[physics]
world_count = 5

View File

@@ -26,6 +26,21 @@ M.FOCUS.GAINED = hash("monarch_focus_gained")
M.FOCUS.LOST = hash("monarch_focus_lost")
local function log(...) end
function M.debug()
log = print
end
-- use a lookup table for so we don't have to do "return (type(s) == "string" and hash(s) or s)"
-- every time
local hash_lookup = {}
local function tohash(s)
hash_lookup[s] = hash_lookup[s] or (type(s) == "string" and hash(s) or s)
return hash_lookup[s]
end
local function screen_from_proxy(proxy)
for _, screen in pairs(screens) do
if screen.proxy == proxy then
@@ -45,9 +60,11 @@ end
--- Check if a screen exists in the current screen stack
-- @param id
-- @param id (string|hash)
-- @return true of the screen is in the stack
function M.in_stack(id)
assert(id, "You must provide a screen id")
id = tohash(id)
for i = 1, #stack do
if stack[i].id == id then
return true
@@ -59,9 +76,11 @@ end
--- Check if a screen is at the top of the stack
-- (primarily used for unit tests, but could have a usecase outside tests)
-- @param id
-- @param id (string|hash)
-- @return true if the screen is at the top of the stack
function M.is_top(id)
assert(id, "You must provide a screen id")
id = tohash(id)
local top = stack[#stack]
return top and top.id == id or false
end
@@ -74,12 +93,17 @@ end
-- release input focus of the game object where the proxy is attached.
-- @param id Unique id of the screen
-- @param proxy URL to the collection proxy containing the screen
-- @param popup true the screen is a popup
-- @param transition_url Optional URL to a script that is
-- responsible for the screen transitions
-- @param focus_url Optional URL to a script that is to be notified of
-- focus lost/gained events
function M.register(id, proxy, popup, transition_url, focus_url)
-- @param settings Settings table for screen. Accepted values:
-- * popup - true the screen is a popup
-- * popup_on_popup - true if this popup can be shown on top of
-- another popup or false if an underlying popup should be closed
-- * transition_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
function M.register(id, proxy, settings)
assert(id, "You must provide a screen id")
id = tohash(id)
assert(not screens[id], ("There is already a screen registered with id %s"):format(tostring(id)))
assert(proxy, "You must provide a collection proxy URL")
local url = msg.url(proxy)
@@ -87,9 +111,10 @@ function M.register(id, proxy, popup, transition_url, focus_url)
id = id,
proxy = proxy,
script = msg.url(),
popup = popup,
transition_url = transition_url,
focus_url = focus_url,
popup = settings and settings.popup,
popup_on_popup = settings and settings.popup_on_popup,
transition_url = settings and settings.transition_url,
focus_url = settings and settings.focus_url,
}
end
@@ -97,26 +122,120 @@ end
-- This is done automatically by the screen.script
-- @param id Id of the screen to unregister
function M.unregister(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)))
screens[id] = nil
end
local function show_out(screen, next_screen, cb)
local co
co = coroutine.create(function()
screen.co = co
local function acquire_input(screen)
log("change_context()", screen.id)
if not screen.input then
msg.post(screen.script, ACQUIRE_INPUT_FOCUS)
screen.input = true
end
end
local function release_input(screen)
log("change_context()", screen.id)
if screen.input then
msg.post(screen.script, RELEASE_INPUT_FOCUS)
screen.input = false
end
end
local function change_context(screen)
log("change_context()", screen.id)
screen.wait_for = CONTEXT
msg.post(screen.script, CONTEXT)
coroutine.yield()
if not next_screen.popup then
msg.post(screen.transition_url, M.TRANSITION.SHOW_OUT)
coroutine.yield()
screen.wait_for = nil
end
local function unload(screen)
log("unload()", screen.id)
screen.wait_for = PROXY_UNLOADED
msg.post(screen.proxy, UNLOAD)
coroutine.yield()
screen.loaded = false
screen.wait_for = nil
end
local function async_load(screen)
log("async_load()", screen.id)
screen.wait_for = PROXY_LOADED
msg.post(screen.proxy, ASYNC_LOAD)
coroutine.yield()
msg.post(screen.proxy, ENABLE)
screen.loaded = true
screen.wait_for = nil
end
local function transition(screen, message_id)
log("transition()", screen.id)
screen.wait_for = M.TRANSITION.DONE
msg.post(screen.transition_url, message_id)
coroutine.yield()
screen.wait_for = nil
end
local function focus_gained(screen, previous_screen)
log("focus_gained()", screen.id)
if screen.focus_url then
msg.post(screen.focus_url, M.FOCUS.LOST, {id = next_screen.id})
msg.post(screen.focus_url, M.FOCUS.GAINED, {id = previous_screen and previous_screen.id})
end
end
local function focus_lost(screen, next_screen)
log("focus_lost()", screen.id)
if screen.focus_url then
msg.post(screen.focus_url, M.FOCUS.LOST, {id = next_screen and next_screen.id})
end
end
local function disable(screen, next_screen)
log("disable()", screen.id)
local co
co = coroutine.create(function()
screen.co = co
change_context(screen)
release_input(screen)
focus_lost(screen, next_screen)
screen.co = nil
if cb then cb() end
end)
coroutine.resume(co)
end
local function enable(screen, previous_screen)
log("enable()", screen.id)
local co
co = coroutine.create(function()
screen.co = co
change_context(screen)
acquire_input(screen)
focus_gained(screen, previous_screen)
screen.co = nil
if cb then cb() end
end)
coroutine.resume(co)
end
local function show_out(screen, next_screen, cb)
log("show_out()", screen.id)
local co
co = coroutine.create(function()
screen.co = co
change_context(screen)
release_input(screen)
focus_lost(screen, next_screen)
-- if the next screen is a popup we want the current screen to stay visible below the popup
-- if the next screen isn't a popup the current one should be unloaded and transitioned out
local next_is_popup = next_screen and not next_screen.popup
local current_is_popup = screen.popup
if (next_is_popup and not current_is_popup) or (current_is_popup) then
transition(screen, M.TRANSITION.SHOW_OUT)
unload(screen)
end
screen.co = nil
if cb then cb() end
@@ -125,32 +244,25 @@ local function show_out(screen, next_screen, cb)
end
local function show_in(screen, previous_screen, reload, cb)
log("show_in()", screen.id)
local co
co = coroutine.create(function()
screen.co = co
msg.post(screen.script, CONTEXT)
coroutine.yield()
change_context(screen)
if reload and screen.loaded then
msg.post(screen.proxy, UNLOAD)
coroutine.yield()
screen.loaded = false
log("show_in() reloading", screen.id)
unload(screen)
end
-- the screen could be loaded if the previous screen was a popup
-- and the popup asked to show this screen again
-- in that case we shouldn't attempt to load it again
if not screen.loaded then
msg.post(screen.proxy, ASYNC_LOAD)
coroutine.yield()
msg.post(screen.proxy, ENABLE)
screen.loaded = true
async_load(screen)
end
stack[#stack + 1] = screen
msg.post(screen.transition_url, M.TRANSITION.SHOW_IN)
coroutine.yield()
msg.post(screen.script, ACQUIRE_INPUT_FOCUS)
if screen.focus_url then
msg.post(screen.focus_url, M.FOCUS.GAINED, {id = previous_screen and previous_screen.id})
end
transition(screen, M.TRANSITION.SHOW_IN)
acquire_input(screen)
focus_gained(screen, previous_screen)
screen.co = nil
if cb then cb() end
end)
@@ -158,23 +270,19 @@ local function show_in(screen, previous_screen, reload, cb)
end
local function back_in(screen, previous_screen, cb)
log("back_in()", screen.id)
local co
co = coroutine.create(function()
screen.co = co
msg.post(screen.script, CONTEXT)
coroutine.yield()
if not previous_screen.popup then
msg.post(screen.proxy, ASYNC_LOAD)
coroutine.yield()
msg.post(screen.proxy, ENABLE)
screen.loaded = true
msg.post(screen.transition_url, M.TRANSITION.BACK_IN)
coroutine.yield()
change_context(screen)
if not screen.loaded then
async_load(screen)
end
msg.post(screen.script, ACQUIRE_INPUT_FOCUS)
if screen.focus_url then
msg.post(screen.focus_url, M.FOCUS.GAINED, {id = previous_screen.id})
if previous_screen and not previous_screen.popup then
transition(screen, M.TRANSITION.BACK_IN)
end
acquire_input(screen)
focus_gained(screen, previous_screen)
screen.co = nil
if cb then cb() end
end)
@@ -182,20 +290,15 @@ local function back_in(screen, previous_screen, cb)
end
local function back_out(screen, next_screen, cb)
log("back_out()", screen.id)
local co
co = coroutine.create(function()
screen.co = co
msg.post(screen.script, RELEASE_INPUT_FOCUS)
msg.post(screen.script, CONTEXT)
coroutine.yield()
msg.post(screen.transition_url, M.TRANSITION.BACK_OUT)
coroutine.yield()
msg.post(screen.proxy, UNLOAD)
coroutine.yield()
screen.loaded = false
if screen.focus_url then
msg.post(screen.focus_url, M.FOCUS.LOST, {id = next_screen and next_screen.id})
end
change_context(screen)
release_input(screen)
focus_lost(screen, next_screen)
transition(screen, M.TRANSITION.BACK_OUT)
unload(screen)
screen.co = nil
if cb then cb() end
end)
@@ -204,58 +307,71 @@ end
--- Get data associated with a screen
-- @param id Id of the screen to get data for
-- @param id (string|hash) Id of the screen to get data for
-- @return Data associated with the screen
function M.data(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].data
end
--- Checks to see if a screen id is registered
-- @param id Id of the screen to check if is registered
-- @param id (string|hash) Id of the screen to check if is registered
-- @return True or False if the screen id is registered or not
function M.screen_exists(id)
assert(id, "You must provide a screen id")
id = tohash(id)
return screens[id] ~= nil
end
--- Show a new screen
-- @param id Id of the screen to show
-- @param options Table with options when showing the screen (can be nil). Valid values:
-- @param id (string|hash) - Id of the screen to show
-- @param options (table) - Table with options when showing the screen (can be nil). Valid values:
-- * clear - Set to true if the stack should be cleared down to an existing instance of the screen
-- * reload - Set to true if screen should be reloaded if it already exists in the stack and is loaded.
-- This would be the case if doing a show() from a popup on the screen just below the popup.
-- @param data Optional data to set on the screen. Can be retrieved by the data() function
-- @param cb Optional callback to invoke when screen is shown
-- @param data (*) - Optional data to set on the screen. Can be retrieved by the data() function
-- @param cb (function) - Optional callback to invoke when screen is shown
function M.show(id, options, data, cb)
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]
screen.data = data
log("show()", screen.id)
-- manipulate the current top
-- close popup if needed
-- transition out
local top = stack[#stack]
if top then
-- if top is popup then close it
if top.popup then
-- keep top popup visible if new screen can be shown on top of a popup
if top.popup and screen.popup_on_popup then
disable(top, screen)
else
-- close all popups
while top.popup do
stack[#stack] = nil
show_out(top, screen)
top = stack[#stack]
end
-- unload and transition out from top
-- unless we're showing the same screen as is already visible
if top and top.id ~= screen.id then
show_out(top, screen)
end
end
end
-- if the screen we want to show is in the stack
-- already and the clear flag is set then we need
-- to remove every screen on the stack up until and
-- including the screen itself
if options and options.clear then
log("show() clearing")
while M.in_stack(id) do
table.remove(stack)
end
@@ -267,18 +383,30 @@ end
-- Go back to the previous screen in the stack
-- @param data Optional data to set for the previous screen
-- @param cb Optional callback to invoke when the previous screen is visible again
-- @param data (*) - Optional data to set for the previous screen
-- @param cb (function) - Optional callback to invoke when the previous screen is visible again
function M.back(data, cb)
local screen = table.remove(stack)
if screen then
log("back()", screen.id)
local top = stack[#stack]
back_out(screen, top, cb)
-- 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
if top and screen.id == top.id then
back_out(screen, top, function()
if data then
top.data = data
end
back_in(top, screen, cb)
end)
else
back_out(screen, top)
if top then
if data then
top.data = data
end
back_in(top, screen)
back_in(top, screen, cb)
end
end
elseif cb then
cb()
@@ -290,19 +418,27 @@ function M.on_message(message_id, message, sender)
if message_id == PROXY_LOADED then
local screen = screen_from_proxy(sender)
assert(screen, "Unable to find screen for loaded proxy")
coroutine.resume(screen.co)
if screen.wait_for == PROXY_LOADED then
assert(coroutine.resume(screen.co))
end
elseif message_id == PROXY_UNLOADED then
local screen = screen_from_proxy(sender)
assert(screen, "Unable to find screen for unloaded proxy")
coroutine.resume(screen.co)
if screen.wait_for == PROXY_UNLOADED then
assert(coroutine.resume(screen.co))
end
elseif message_id == CONTEXT then
local screen = screen_from_script()
assert(screen, "Unable to find screen for current script url")
coroutine.resume(screen.co)
if screen.wait_for == CONTEXT then
assert(coroutine.resume(screen.co))
end
elseif message_id == M.TRANSITION.DONE then
local screen = screen_from_script()
assert(screen, "Unable to find screen for current script url")
coroutine.resume(screen.co)
if screen.wait_for == M.TRANSITION.DONE then
assert(coroutine.resume(screen.co))
end
end
end

View File

@@ -3,13 +3,24 @@ local monarch
go.property("screen_proxy", msg.url("#collectionproxy"))
go.property("screen_id", hash(""))
go.property("popup", false)
go.property("popup_on_popup", false)
go.property("transition_url", msg.url())
go.property("focus_url", msg.url())
function init(self)
monarch = require "monarch.monarch"
monarch.register(self.screen_id, self.screen_proxy, self.popup, self.transition_url, self.focus_url)
assert(not self.popup_on_popup or (self.popup_on_popup and self.popup), "Popup on Popups can only be set if the Popup flag is set")
monarch.register(
self.screen_id,
self.screen_proxy,
{
popup = self.popup,
popup_on_popup = self.popup_on_popup,
transition_url = self.transition_url,
focus_url = self.focus_url
}
)
end
function final(self)

View File

@@ -2,81 +2,89 @@ local monarch = require "monarch.monarch"
local M = {}
local WIDTH = tonumber(sys.get_config("display.width"))
local HEIGHT = tonumber(sys.get_config("display.height"))
local LEFT = vmath.vector3(-WIDTH * 2, 0, 0)
local RIGHT = vmath.vector3(WIDTH * 2, 0, 0)
local TOP = vmath.vector3(0, HEIGHT * 2, 0)
local BOTTOM = vmath.vector3(0, - HEIGHT * 2, 0)
local WIDTH = nil
local HEIGHT = nil
local LEFT = nil
local RIGHT = nil
local TOP = nil
local BOTTOM = nil
local ZERO_SCALE = vmath.vector3(0, 0, 1)
function M.instant(node, to, easing, duration, delay, url)
msg.post(url, monarch.TRANSITION.DONE)
local LAYOUT_CHANGED = hash("layout_changed")
-- Notify the transition system that the window size has changed
-- @param width
-- @param height
function M.window_resized(width, height)
WIDTH = width
HEIGHT = height
LEFT = vmath.vector3(-WIDTH * 2, 0, 0)
RIGHT = vmath.vector3(WIDTH * 2, 0, 0)
TOP = vmath.vector3(0, HEIGHT * 2, 0)
BOTTOM = vmath.vector3(0, - HEIGHT * 2, 0)
end
local function slide_in(direction, node, to, easing, duration, delay, url)
M.window_resized(tonumber(sys.get_config("display.width")), tonumber(sys.get_config("display.height")))
function M.instant(node, to, easing, duration, delay, cb)
cb()
end
local function slide_in(direction, node, to, easing, duration, delay, cb)
local from = to + direction
gui.set_position(node, from)
gui.animate(node, gui.PROP_POSITION, to, easing, duration, delay, function()
msg.post(url, monarch.TRANSITION.DONE)
end)
gui.animate(node, gui.PROP_POSITION, to, easing, duration, delay, cb)
end
function M.slide_in_left(node, to, easing, duration, delay, url)
return slide_in(LEFT, node, to.pos, easing, duration, delay, url)
function M.slide_in_left(node, to, easing, duration, delay, cb)
return slide_in(LEFT, node, to.pos, easing, duration, delay, cb)
end
function M.slide_in_right(node, to, easing, duration, delay, url)
slide_in(RIGHT, node, to.pos, easing, duration, delay, url)
function M.slide_in_right(node, to, easing, duration, delay, cb)
slide_in(RIGHT, node, to.pos, easing, duration, delay, cb)
end
function M.slide_in_top(node, to, easing, duration, delay, url)
slide_in(TOP, node, to.pos, easing, duration, delay, url)
function M.slide_in_top(node, to, easing, duration, delay, cb)
slide_in(TOP, node, to.pos, easing, duration, delay, cb)
end
function M.slide_in_bottom(node, to, easing, duration, delay, url)
slide_in(BOTTOM, node, to.pos, easing, duration, delay, url)
function M.slide_in_bottom(node, to, easing, duration, delay, cb)
slide_in(BOTTOM, node, to.pos, easing, duration, delay, cb)
end
local function slide_out(direction, node, from, easing, duration, delay, url)
local function slide_out(direction, node, from, easing, duration, delay, cb)
local to = from + direction
gui.set_position(node, from)
gui.animate(node, gui.PROP_POSITION, to, easing, duration, delay, function()
msg.post(url, monarch.TRANSITION.DONE)
end)
gui.animate(node, gui.PROP_POSITION, to, easing, duration, delay, cb)
end
function M.slide_out_left(node, from, easing, duration, delay, url)
slide_out(LEFT, node, from.pos, easing, duration, delay, url)
function M.slide_out_left(node, from, easing, duration, delay, cb)
slide_out(LEFT, node, from.pos, easing, duration, delay, cb)
end
function M.slide_out_right(node, from, easing, duration, delay, url)
slide_out(RIGHT, node, from.pos, easing, duration, delay, url)
function M.slide_out_right(node, from, easing, duration, delay, cb)
slide_out(RIGHT, node, from.pos, easing, duration, delay, cb)
end
function M.slide_out_top(node, from, easing, duration, delay, url)
slide_out(TOP, node, from.pos, easing, duration, delay, url)
function M.slide_out_top(node, from, easing, duration, delay, cb)
slide_out(TOP, node, from.pos, easing, duration, delay, cb)
end
function M.slide_out_bottom(node, from, easing, duration, delay, url)
slide_out(BOTTOM, node, from.pos, easing, duration, delay, url)
function M.slide_out_bottom(node, from, easing, duration, delay, cb)
slide_out(BOTTOM, node, from.pos, easing, duration, delay, cb)
end
function M.scale_in(node, to, easing, duration, delay, url)
function M.scale_in(node, to, easing, duration, delay, cb)
gui.set_scale(node, ZERO_SCALE)
gui.animate(node, gui.PROP_SCALE, to.scale, easing, duration, delay, function()
msg.post(url, monarch.TRANSITION.DONE)
end)
gui.animate(node, gui.PROP_SCALE, to.scale, easing, duration, delay, cb)
end
function M.scale_out(node, from, easing, duration, delay, url)
function M.scale_out(node, from, easing, duration, delay, cb)
gui.set_scale(node, from.scale)
gui.animate(node, gui.PROP_SCALE, ZERO_SCALE, easing, duration, delay, function()
msg.post(url, monarch.TRANSITION.DONE)
end)
gui.animate(node, gui.PROP_SCALE, ZERO_SCALE, easing, duration, delay, cb)
end
--- Create a transition for a node
@@ -86,21 +94,57 @@ function M.create(node)
local instance = {}
local transitions = {
[monarch.TRANSITION.SHOW_IN] = M.instant,
[monarch.TRANSITION.SHOW_OUT] = M.instant,
[monarch.TRANSITION.BACK_IN] = M.instant,
[monarch.TRANSITION.BACK_OUT] = M.instant,
}
local transitions = {}
local current_transition = nil
local initial_data = {}
initial_data.pos = gui.get_position(node)
initial_data.scale = gui.get_scale(node)
local function create_transition(fn, easing, duration, delay)
return {
fn = fn,
easing = easing,
duration = duration,
delay = delay,
in_progress = false,
urls = {},
}
end
local function start_transition(transition, transition_id, url)
table.insert(transition.urls, url)
if not transition.in_progress then
table.insert(transition.urls, msg.url())
current_transition = transition
transition.in_progress = true
transition.fn(node, initial_data, transition.easing, transition.duration, transition.delay or 0, function()
transition.in_progress = false
local message = { transition = transition_id }
while #transition.urls > 0 do
local url = table.remove(transition.urls)
msg.post(url, monarch.TRANSITION.DONE, message)
end
end)
end
end
-- Forward on_message calls here
function instance.handle(message_id, message, sender)
if transitions[message_id] then
transitions[message_id](sender)
if message_id == LAYOUT_CHANGED then
initial_data.pos = gui.get_position(node)
-- replay the current transition if the layout changes
-- this will ensure that things are still hidden if they
-- were transitioned out
if current_transition then
current_transition.fn(node, initial_data, current_transition.easing, 0, 0)
end
else
local transition = transitions[message_id]
if transition then
start_transition(transition, message_id, sender)
end
end
end
@@ -111,39 +155,37 @@ function M.create(node)
-- @param duration Transition duration
-- @param delay Transition delay
function instance.show_in(fn, easing, duration, delay)
transitions[monarch.TRANSITION.SHOW_IN] = function(url)
fn(node, initial_data, easing, duration, delay or 0, url)
end
transitions[monarch.TRANSITION.SHOW_IN] = create_transition(fn, easing, duration, delay)
return instance
end
-- Specify the transition function when this node is transitioned
-- from when showing another screen
function instance.show_out(fn, easing, duration, delay)
transitions[monarch.TRANSITION.SHOW_OUT] = function(url)
fn(node, initial_data, easing, duration, delay or 0, url)
end
transitions[monarch.TRANSITION.SHOW_OUT] = create_transition(fn, easing, duration, delay)
return instance
end
--- Specify the transition function when this node is transitioned
-- to when navigating back in the screen stack
function instance.back_in(fn, easing, duration, delay)
transitions[monarch.TRANSITION.BACK_IN] = function(url)
fn(node, initial_data, easing, duration, delay or 0, url)
end
transitions[monarch.TRANSITION.BACK_IN] = create_transition(fn, easing, duration, delay)
return instance
end
--- Specify the transition function when this node is transitioned
-- from when navigating back in the screen stack
function instance.back_out(fn, easing, duration, delay)
transitions[monarch.TRANSITION.BACK_OUT] = function(url)
fn(node, initial_data, easing, duration, delay or 0, url)
end
transitions[monarch.TRANSITION.BACK_OUT] = create_transition(fn, easing, duration, delay)
return instance
end
-- set default transitions (instant)
instance.show_in(M.instant)
instance.show_out(M.instant)
instance.back_in(M.instant)
instance.back_out(M.instant)
return instance
end

View File

@@ -0,0 +1,37 @@
name: "popup1"
scale_along_z: 0
embedded_instances {
id: "go"
data: "components {\n"
" id: \"popup1\"\n"
" component: \"/test/data/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"
"}\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
}
}

131
test/data/popup1.gui Normal file
View File

@@ -0,0 +1,131 @@
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: "box"
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
}
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: "POPUP 1"
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: "box"
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
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@@ -0,0 +1,37 @@
name: "popup2"
scale_along_z: 0
embedded_instances {
id: "go"
data: "components {\n"
" id: \"popup2\"\n"
" component: \"/test/data/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"
"}\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
}
}

131
test/data/popup2.gui Normal file
View File

@@ -0,0 +1,131 @@
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: "box"
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
}
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: "POPUP 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: "box"
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
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@@ -116,3 +116,139 @@ embedded_instances {
z: 1.0
}
}
embedded_instances {
id: "popup1"
data: "components {\n"
" id: \"screen\"\n"
" component: \"/monarch/screen.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"
" properties {\n"
" id: \"popup_on_popup\"\n"
" value: \"false\"\n"
" type: PROPERTY_TYPE_BOOLEAN\n"
" }\n"
"}\n"
"embedded_components {\n"
" id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n"
" data: \"collection: \\\"/test/data/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\"\n"
" component: \"/monarch/screen.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"
" properties {\n"
" id: \"popup_on_popup\"\n"
" value: \"true\"\n"
" type: PROPERTY_TYPE_BOOLEAN\n"
" }\n"
"}\n"
"embedded_components {\n"
" id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n"
" data: \"collection: \\\"/test/data/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

@@ -1,8 +1,11 @@
local cowait = require "test.cowait"
local monarch = require "monarch.monarch"
local SCREEN1 = hash("screen1")
local SCREEN1_STR = hash("screen1")
local SCREEN1 = hash(SCREEN1_STR)
local SCREEN2 = hash("screen2")
local POPUP1 = hash("popup1")
local POPUP2 = hash("popup2")
local FOOBAR = hash("foobar")
return function()
@@ -63,12 +66,13 @@ return function()
it("should be able to tell if a screen exists", function()
assert(monarch.screen_exists(SCREEN1))
assert(monarch.screen_exists(SCREEN1_STR))
assert(not monarch.screen_exists(hash("foobar")))
end)
it("should be able to show screens and go back to previous screens", function()
monarch.show(SCREEN1)
monarch.show(SCREEN1_STR)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown")
assert_stack({ SCREEN1 })
@@ -131,5 +135,46 @@ return function()
end)
it("should be able to show one popup on top of another the Popup On Popup flag is set", function()
monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown")
assert_stack({ SCREEN1 })
monarch.show(POPUP1)
assert(wait_until_shown(POPUP1), "Popup1 was never shown")
assert_stack({ SCREEN1, POPUP1 })
monarch.show(POPUP2)
assert(wait_until_shown(POPUP2), "Popup2 was never shown")
assert_stack({ SCREEN1, POPUP1, POPUP2 })
end)
it("should close any open popups when showing a popup without the Popup On Popup flag", function()
monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown")
assert_stack({ SCREEN1 })
monarch.show(POPUP2)
assert(wait_until_shown(POPUP2), "Popup2 was never shown")
assert_stack({ SCREEN1, POPUP2 })
monarch.show(POPUP1)
assert(wait_until_shown(POPUP1), "Popup1 was never shown")
assert_stack({ SCREEN1, POPUP1 })
end)
it("should close any open popups when showing a non-popup", function()
monarch.show(SCREEN1)
assert(wait_until_shown(SCREEN1), "Screen1 was never shown")
assert_stack({ SCREEN1 })
monarch.show(POPUP1)
assert(wait_until_shown(POPUP1), "Popup1 was never shown")
assert_stack({ SCREEN1, POPUP1 })
monarch.show(POPUP2)
assert(wait_until_shown(POPUP2), "Popup2 was never shown")
assert_stack({ SCREEN1, POPUP1, POPUP2 })
monarch.show(SCREEN2)
assert(wait_until_shown(SCREEN2), "Popup2 was never shown")
assert_stack({ SCREEN1, SCREEN2 })
end)
end)
end