3
0
mirror of https://github.com/britzl/monarch.git synced 2025-11-27 11:20:55 +01:00

Compare commits

..

11 Commits
2.1 ... 2.2.0

Author SHA1 Message Date
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
12 changed files with 821 additions and 14 deletions

View File

@@ -1,4 +1,7 @@
![](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.
@@ -14,7 +17,7 @@ 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.
@@ -50,7 +53,7 @@ NOTE: You must ensure that the ```init()``` function of the ```screen.script```
end
function on_message(self, message_id, message, sender)
monarch.show("first_screen")
monarch.show(hash("first_screen"))
end
#### Preventing duplicates in the stack
@@ -87,7 +90,7 @@ If a popup is shown on top of a non-popup the current top screen will not be unl
* 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 show 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
* A call to ```monarch.show(D)``` is made and D is a popup with the popup on popup flag set
@@ -163,6 +166,21 @@ You can create and use your own transition as long as the provided transition fu
* ```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:

BIN
docs/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

View File

@@ -0,0 +1,14 @@
profiles {
name: "Landscape"
qualifiers {
width: 1136
height: 640
}
}
profiles {
name: "Portrait"
qualifiers {
width: 640
height: 1136
}
}

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

@@ -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

View File

@@ -2,16 +2,32 @@ 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)
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
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
@@ -111,9 +127,13 @@ function M.create(node)
-- Forward on_message calls here
function instance.handle(message_id, message, sender)
local transition = transitions[message_id]
if transition then
start_transition(transition, sender)
if message_id == LAYOUT_CHANGED then
initial_data.pos = gui.get_position(node)
else
local transition = transitions[message_id]
if transition then
start_transition(transition, sender)
end
end
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

@@ -3,6 +3,8 @@ local monarch = require "monarch.monarch"
local SCREEN1 = hash("screen1")
local SCREEN2 = hash("screen2")
local POPUP1 = hash("popup1")
local POPUP2 = hash("popup2")
local FOOBAR = hash("foobar")
return function()
@@ -130,6 +132,47 @@ return function()
assert_stack({ SCREEN1 })
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