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

Compare commits

..

3 Commits
2.8.0 ... 2.9.0

Author SHA1 Message Date
Björn Ritzl
d1cf8281c9 Added helpers for common sets of transitions 2018-06-16 22:43:25 +02:00
Björn Ritzl
091fab0c7f Changed how monarch busy state is tracked 2018-06-16 22:27:03 +02:00
Björn Ritzl
01196ad350 Updated changelog 2018-06-10 16:19:52 +02:00
13 changed files with 393 additions and 30 deletions

View File

@@ -1,3 +1,7 @@
## 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`

View File

@@ -122,8 +122,10 @@ You can add optional transitions when navigating between screens. The default be
* ```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. 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.
### 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"
@@ -148,7 +150,6 @@ Monarch comes with a system for setting up transitions easily in a gui_script us
end
end
### Predefined transitions
The predefined transitions provided by ```monarch.transitions.gui``` are:
* ```slide_in_right```
@@ -162,6 +163,23 @@ The predefined transitions provided by ```monarch.transitions.gui``` are:
* ```scale_in```
* ```scale_out```
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])```
**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:
@@ -175,6 +193,7 @@ 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:
@@ -308,5 +327,12 @@ Check if a Monarch screen exists.
* ```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.debug()
Enable verbose logging of the internals of Monarch.

View File

@@ -7,11 +7,7 @@ function init(self)
local data = monarch.data(hash("game"))
gui.set_text(gui.get_node("level"), tostring(data.level))
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)
self.transition = transitions.in_right_out_left(gui.get_node("root"), 0.6, 0)
end
function on_input(self, action_id, action)

View File

@@ -6,11 +6,7 @@ function init(self)
gui.set_text(gui.get_node("timestamp"), os.date())
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)
self.transition = transitions.in_right_out_left(gui.get_node("root"), 0.6, 0)
end
function on_input(self, action_id, action)

View File

@@ -6,11 +6,7 @@ function init(self)
self.play = gui.get_node("play_button")
self.back = gui.get_node("back_button")
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)
self.transition = transitions.in_right_out_left(gui.get_node("root"), 0.6, 0)
end
function on_input(self, action_id, action)

View File

@@ -27,8 +27,9 @@ local screens = {}
-- the current stack of screens
local stack = {}
-- true while busy showing/hiding something
local busy = false
-- the number of active transitions
-- monarch is considered busy while there are active transitions
local active_transition_count = 0
local function log(...) end
@@ -230,7 +231,7 @@ local function show_out(screen, next_screen, cb)
log("show_out()", screen.id)
local co
co = coroutine.create(function()
busy = true
active_transition_count = active_transition_count + 1
screen.co = co
change_context(screen)
release_input(screen)
@@ -244,7 +245,7 @@ local function show_out(screen, next_screen, cb)
unload(screen)
end
screen.co = nil
busy = false
active_transition_count = active_transition_count - 1
if cb then cb() end
end)
coroutine.resume(co)
@@ -254,7 +255,7 @@ local function show_in(screen, previous_screen, reload, cb)
log("show_in()", screen.id)
local co
co = coroutine.create(function()
busy = true
active_transition_count = active_transition_count + 1
screen.co = co
change_context(screen)
if reload and screen.loaded then
@@ -279,7 +280,7 @@ local function show_in(screen, previous_screen, reload, cb)
acquire_input(screen)
focus_gained(screen, previous_screen)
screen.co = nil
busy = false
active_transition_count = active_transition_count - 1
if cb then cb() end
end)
coroutine.resume(co)
@@ -289,7 +290,7 @@ local function back_in(screen, previous_screen, cb)
log("back_in()", screen.id)
local co
co = coroutine.create(function()
busy = true
active_transition_count = active_transition_count + 1
screen.co = co
change_context(screen)
if screen.preloaded then
@@ -307,7 +308,7 @@ local function back_in(screen, previous_screen, cb)
acquire_input(screen)
focus_gained(screen, previous_screen)
screen.co = nil
busy = false
active_transition_count = active_transition_count - 1
if cb then cb() end
end)
coroutine.resume(co)
@@ -317,7 +318,7 @@ local function back_out(screen, next_screen, cb)
log("back_out()", screen.id)
local co
co = coroutine.create(function()
busy = true
active_transition_count = active_transition_count + 1
screen.co = co
change_context(screen)
release_input(screen)
@@ -325,7 +326,7 @@ local function back_out(screen, next_screen, cb)
transition(screen, M.TRANSITION.BACK_OUT, { next_screen = next_screen and next_screen.id })
unload(screen)
screen.co = nil
busy = false
active_transition_count = active_transition_count - 1
if cb then cb() end
end)
coroutine.resume(co)
@@ -342,6 +343,7 @@ function M.data(id)
return screens[id].data
end
--- Checks to see if a screen id 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
@@ -351,6 +353,14 @@ function M.screen_exists(id)
return screens[id] ~= nil
end
--- Check if Monarch is busy hiding and or showing a screen
-- @return true if busy
function M.is_busy()
return active_transition_count > 0
end
--- Show a new screen
-- @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:
@@ -362,7 +372,7 @@ end
-- @return success True if screen is successfully shown, false if busy performing another operation
function M.show(id, options, data, cb)
assert(id, "You must provide a screen id")
if busy then
if M.is_busy() then
return false
end
@@ -420,7 +430,7 @@ end
-- @param cb (function) - Optional callback to invoke when the previous screen is visible again
-- @return true if successfully going back, false if busy performing another operation
function M.back(data, cb)
if busy then
if M.is_busy() then
return false
end

View File

@@ -0,0 +1,26 @@
local M = {}
local function create(name)
assert(gui["EASING_OUT" .. name])
assert(gui["EASING_IN" .. name])
return {
IN = gui["EASING_OUT" .. name],
OUT = gui["EASING_IN" .. name],
}
end
function M.BACK() return create("BACK") end
function M.BOUNCE() return create("BOUNCE") end
function M.CIRC() return create("CIRC") end
function M.CUBIC() return create("CUBIC") end
function M.ELASTIC() return create("ELASTIC") end
function M.EXPO() return create("EXPO") end
function M.QUAD() return create("QUAD") end
function M.QUART() return create("QUART") end
function M.QUINT() return create("QUINT") end
function M.SINE() return create("SINE") end
return M

View File

@@ -1,4 +1,5 @@
local monarch = require "monarch.monarch"
local easings = require "monarch.transitions.easings"
local M = {}
@@ -198,4 +199,60 @@ function M.create(node)
return instance
end
--- Create transition where the screen slides in from the right when shown and out
-- to the left when hidden (and the reverse when going back)
-- @param node
-- @param duration
-- @param delay Optional. Defaults to 0
-- @param easing Optional. A constant from monarch.transitions.easing
-- @return Transition instance
function M.in_right_out_left(node, duration, delay, easing)
assert(node, "You must provide a node")
assert(duration, "You must provide a duration")
easing = easing or easings.QUAD()
return M.create(node)
.show_in(M.slide_in_right, easing.OUT, duration, delay or 0)
.show_out(M.slide_out_left, easing.IN, duration, delay or 0)
.back_in(M.slide_in_left, easing.OUT, duration, delay or 0)
.back_out(M.slide_out_right, easing.IN, duration, delay or 0)
end
function M.in_left_out_right(node, duration, delay, easing)
assert(node, "You must provide a node")
assert(duration, "You must provide a duration")
easing = easing or easings.QUAD()
return M.create(node)
.show_in(M.slide_in_left, easing.OUT, duration, delay or 0)
.show_out(M.slide_out_right, easing.IN, duration, delay or 0)
.back_in(M.slide_in_right, easing.OUT, duration, delay or 0)
.back_out(M.slide_out_left, easing.IN, duration, delay or 0)
end
function M.in_right_out_right(node, duration, delay, easing)
assert(node, "You must provide a node")
assert(duration, "You must provide a duration")
easing = easing or easings.QUAD()
return M.create(node)
.show_in(M.slide_in_right, easing.OUT, duration, delay or 0)
.show_out(M.slide_out_right, easing.IN, duration, delay or 0)
.back_in(M.slide_in_right, easing.OUT, duration, delay or 0)
.back_out(M.slide_out_right, easing.IN, duration, delay or 0)
end
function M.in_left_out_left(node, duration, delay, easing)
assert(node, "You must provide a node")
assert(duration, "You must provide a duration")
easing = easing or easings.QUAD()
return M.create(node)
.show_in(M.slide_in_left, easing.OUT, duration, delay or 0)
.show_out(M.slide_out_left, easing.IN, duration, delay or 0)
.back_in(M.slide_in_left, easing.OUT, duration, delay or 0)
.back_out(M.slide_out_left, easing.IN, duration, delay or 0)
end
return M

View File

@@ -252,3 +252,66 @@ embedded_instances {
z: 1.0
}
}
embedded_instances {
id: "transition1"
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: \"transition1\"\n"
" type: PROPERTY_TYPE_HASH\n"
" }\n"
" properties {\n"
" id: \"transition_url\"\n"
" value: \"transition1:/go\"\n"
" type: PROPERTY_TYPE_URL\n"
" }\n"
"}\n"
"embedded_components {\n"
" id: \"collectionproxy\"\n"
" type: \"collectionproxy\"\n"
" data: \"collection: \\\"/test/data/transition1.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,37 @@
name: "transition1"
scale_along_z: 0
embedded_instances {
id: "go"
data: "components {\n"
" id: \"transition1\"\n"
" component: \"/test/data/transition1.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/transition1.gui Normal file
View File

@@ -0,0 +1,131 @@
script: "/test/data/transition1.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: 697.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: "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_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: "transition 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: "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,10 @@
local transitions = require "monarch.transitions.gui"
function init(self)
self.transition = transitions.in_right_out_left(gui.get_node("root"), 1)
end
function on_message(self, message_id, message, sender)
print(message_id)
self.transition.handle(message_id, message, sender)
end

View File

@@ -7,6 +7,7 @@ local SCREEN2 = hash("screen2")
local POPUP1 = hash("popup1")
local POPUP2 = hash("popup2")
local FOOBAR = hash("foobar")
local TRANSITION1 = hash("transition1")
return function()
@@ -35,6 +36,9 @@ return function()
local function wait_until_hidden(screen_id)
return wait_timeout(is_hidden, screen_id)
end
local function wait_until_not_busy()
return wait_timeout(function() return not monarch.is_busy() end)
end
local function assert_stack(expected_screens)
local actual_screens = monarch.get_stack()
@@ -155,7 +159,7 @@ return function()
assert_stack({ SCREEN1 })
assert(monarch.show(SCREEN2) == true)
assert(wait_until_shown(SCREEN2), "Screen1 was never shown")
assert(wait_until_shown(SCREEN2), "Screen2 was never shown")
assert_stack({ SCREEN1, SCREEN2 })
assert(monarch.back() == true)
@@ -218,5 +222,12 @@ return function()
assert(monarch.bottom(0) == SCREEN1)
assert(monarch.bottom(1) == SCREEN2)
end)
it("should be busy while transition is running", function()
monarch.show(TRANSITION1)
assert(wait_until_shown(TRANSITION1), "Transition1 was never shown")
assert(monarch.is_busy())
assert(wait_until_not_busy())
end)
end)
end