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

Compare commits

...

6 Commits

Author SHA1 Message Date
Björn Ritzl
bc4260d72a Added fade in/out transition 2018-07-13 06:54:26 +02:00
Björn Ritzl
07eacc7a5f Track when callbacks are invoked so that we can ensure that all screens are shown/hidden before invoking callbacks 2018-06-21 11:33:20 +02:00
Björn Ritzl
5ee6ea5982 Log when monarch is busy 2018-06-21 11:30:17 +02:00
Björn Ritzl
f590c75b9e Removed debug prints that was accidentally left behind 2018-06-21 11:29:59 +02:00
Björn Ritzl
d528947998 Updated docs with new notification messages 2018-06-20 13:49:02 +02:00
Björn Ritzl
22f0e6cddf Changed from single notification to begin and end notification for every navigation 2018-06-20 13:46:45 +02:00
6 changed files with 141 additions and 31 deletions

View File

@@ -162,6 +162,8 @@ The predefined transitions provided by ```monarch.transitions.gui``` are:
* ```slide_out_bottom``` * ```slide_out_bottom```
* ```scale_in``` * ```scale_in```
* ```scale_out``` * ```scale_out```
* ```fade_in``` - Set node alpha to fully transparent (i.e. 0.0) and fade to fully opaque (i.e. 1.0)
* ```fade_out``` - Set node alpha to fully opaque (i.e. 1.0) and fade to fully transparent (i.e. 0.0)
Additionally there's functionality to create a full set of transitions for common transition styles: Additionally there's functionality to create a full set of transitions for common transition styles:
@@ -169,6 +171,7 @@ Additionally there's functionality to create a full set of transitions for commo
* ```transitions.in_left_out_right(node, duration, [delay], [easing])``` * ```transitions.in_left_out_right(node, duration, [delay], [easing])```
* ```transitions.in_left_out_left(node, duration, [delay], [easing])``` * ```transitions.in_left_out_left(node, duration, [delay], [easing])```
* ```transitions.in_right_out_right(node, duration, [delay], [easing])``` * ```transitions.in_right_out_right(node, duration, [delay], [easing])```
* ```transitions.fade_in_out(node, duration, [delay], [easing])```
**PARAMETERS** **PARAMETERS**
* ```node``` (node) - Gui node to animate. * ```node``` (node) - Gui node to animate.
@@ -352,16 +355,32 @@ Remove a previously added listener.
Enable verbose logging of the internals of Monarch. Enable verbose logging of the internals of Monarch.
### monarch.SCREEN_VISIBLE ### monarch.SCREEN_TRANSITION_IN_STARTED
Message sent to listeners when a screen becomes visible. Message sent to listeners when a screen has started to transition in.
**PARAMETERS** **PARAMETERS**
* ```screen``` (hash) - Id of the screen * ```screen``` (hash) - Id of the screen
* ```previous_screen``` (hash) - Id of theprevious screen (if any) * ```previous_screen``` (hash) - Id of the previous screen (if any)
### monarch.SCREEN_HIDDEN ### monarch.SCREEN_TRANSITION_IN_FINISHED
Message sent to listeners when a screen is hidden. Message sent to listeners when a screen has finished to transition in.
**PARAMETERS**
* ```screen``` (hash) - Id of the screen
* ```previous_screen``` (hash) - Id of the previous screen (if any)
### monarch.SCREEN_TRANSITION_OUT_STARTED
Message sent to listeners when a screen has started to transition out.
**PARAMETERS**
* ```screen``` (hash) - Id of the screen
* ```next_screen``` (hash) - Id of the next screen (if any)
### monarch.SCREEN_TRANSITION_OUT_FINISHED
Message sent to listeners when a screen has finished to transition out.
**PARAMETERS** **PARAMETERS**
* ```screen``` (hash) - Id of the screen * ```screen``` (hash) - Id of the screen

View File

@@ -6,7 +6,7 @@ function init(self)
gui.set_text(gui.get_node("timestamp"), os.date()) gui.set_text(gui.get_node("timestamp"), os.date())
self.transition = transitions.in_right_out_left(gui.get_node("root"), 0.6, 0) self.transition = transitions.fade_in_out(gui.get_node("root"), 0.6, 0)
end end
function on_input(self, action_id, action) function on_input(self, action_id, action)

View File

@@ -1,3 +1,5 @@
local callback_tracker = require "monarch.utils.callback_tracker"
local M = {} local M = {}
local CONTEXT = hash("monarch_context") local CONTEXT = hash("monarch_context")
@@ -24,8 +26,10 @@ M.FOCUS.GAINED = hash("monarch_focus_gained")
M.FOCUS.LOST = hash("monarch_focus_lost") M.FOCUS.LOST = hash("monarch_focus_lost")
-- listener messages -- listener messages
M.SCREEN_VISIBLE = hash("monarch_screen_visible") M.SCREEN_TRANSITION_IN_STARTED = hash("monarch_screen_transition_in_started")
M.SCREEN_HIDDEN = hash("monarch_screen_hidden") M.SCREEN_TRANSITION_IN_FINISHED = hash("monarch_screen_transition_in_finished")
M.SCREEN_TRANSITION_OUT_STARTED = hash("monarch_screen_transition_out_started")
M.SCREEN_TRANSITION_OUT_FINISHED = hash("monarch_screen_transition_out_finished")
-- all registered screens -- all registered screens
@@ -248,6 +252,7 @@ local function show_out(screen, next_screen, cb)
local co local co
co = coroutine.create(function() co = coroutine.create(function()
active_transition_count = active_transition_count + 1 active_transition_count = active_transition_count + 1
notify_listeners(M.SCREEN_TRANSITION_OUT_STARTED, { screen = screen.id, next_screen = next_screen.id })
screen.co = co screen.co = co
change_context(screen) change_context(screen)
release_input(screen) release_input(screen)
@@ -263,7 +268,7 @@ local function show_out(screen, next_screen, cb)
screen.co = nil screen.co = nil
active_transition_count = active_transition_count - 1 active_transition_count = active_transition_count - 1
if cb then cb() end if cb then cb() end
notify_listeners(M.SCREEN_HIDDEN, { screen = screen.id, next_screen = next_screen.id }) notify_listeners(M.SCREEN_TRANSITION_OUT_FINISHED, { screen = screen.id, next_screen = next_screen.id })
end) end)
coroutine.resume(co) coroutine.resume(co)
end end
@@ -273,6 +278,7 @@ local function show_in(screen, previous_screen, reload, cb)
local co local co
co = coroutine.create(function() co = coroutine.create(function()
active_transition_count = active_transition_count + 1 active_transition_count = active_transition_count + 1
notify_listeners(M.SCREEN_TRANSITION_IN_STARTED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
screen.co = co screen.co = co
change_context(screen) change_context(screen)
if reload and screen.loaded then if reload and screen.loaded then
@@ -299,17 +305,17 @@ local function show_in(screen, previous_screen, reload, cb)
screen.co = nil screen.co = nil
active_transition_count = active_transition_count - 1 active_transition_count = active_transition_count - 1
if cb then cb() end if cb then cb() end
notify_listeners(M.SCREEN_VISIBLE, { screen = screen.id, previous_screen = previous_screen and previous_screen.id }) notify_listeners(M.SCREEN_TRANSITION_IN_FINISHED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
end) end)
coroutine.resume(co) coroutine.resume(co)
end end
local function back_in(screen, previous_screen, cb) local function back_in(screen, previous_screen, cb)
log("back_in()", screen.id) log("back_in()", screen.id)
print("back_in()", screen.id)
local co local co
co = coroutine.create(function() co = coroutine.create(function()
active_transition_count = active_transition_count + 1 active_transition_count = active_transition_count + 1
notify_listeners(M.SCREEN_TRANSITION_IN_STARTED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
screen.co = co screen.co = co
change_context(screen) change_context(screen)
if screen.preloaded then if screen.preloaded then
@@ -329,16 +335,16 @@ local function back_in(screen, previous_screen, cb)
screen.co = nil screen.co = nil
active_transition_count = active_transition_count - 1 active_transition_count = active_transition_count - 1
if cb then cb() end if cb then cb() end
notify_listeners(M.SCREEN_VISIBLE, { screen = screen.id, previous_screen = previous_screen.id }) notify_listeners(M.SCREEN_TRANSITION_IN_FINISHED, { screen = screen.id, previous_screen = previous_screen and previous_screen.id })
end) end)
coroutine.resume(co) coroutine.resume(co)
end end
local function back_out(screen, next_screen, cb) local function back_out(screen, next_screen, cb)
log("back_out()", screen.id) log("back_out()", screen.id)
print("back_out()", screen.id)
local co local co
co = coroutine.create(function() co = coroutine.create(function()
notify_listeners(M.SCREEN_TRANSITION_OUT_STARTED, { screen = screen.id, next_screen = next_screen.id })
active_transition_count = active_transition_count + 1 active_transition_count = active_transition_count + 1
screen.co = co screen.co = co
change_context(screen) change_context(screen)
@@ -349,7 +355,7 @@ local function back_out(screen, next_screen, cb)
screen.co = nil screen.co = nil
active_transition_count = active_transition_count - 1 active_transition_count = active_transition_count - 1
if cb then cb() end if cb then cb() end
notify_listeners(M.SCREEN_HIDDEN, { screen = screen.id, next_screen = next_screen and next_screen.id }) notify_listeners(M.SCREEN_TRANSITION_OUT_FINISHED, { screen = screen.id, next_screen = next_screen.id })
end) end)
coroutine.resume(co) coroutine.resume(co)
end end
@@ -395,8 +401,11 @@ end
function M.show(id, options, data, cb) function M.show(id, options, data, cb)
assert(id, "You must provide a screen id") assert(id, "You must provide a screen id")
if M.is_busy() then if M.is_busy() then
log("show() monarch is busy, ignoring request")
return false return false
end end
local callbacks = callback_tracker()
id = tohash(id) id = tohash(id)
assert(screens[id], ("There is no screen registered with id %s"):format(tostring(id))) assert(screens[id], ("There is no screen registered with id %s"):format(tostring(id)))
@@ -418,13 +427,13 @@ function M.show(id, options, data, cb)
-- close all popups -- close all popups
while top.popup do while top.popup do
stack[#stack] = nil stack[#stack] = nil
show_out(top, screen) show_out(top, screen, callbacks.track())
top = stack[#stack] top = stack[#stack]
end end
-- unload and transition out from top -- unload and transition out from top
-- unless we're showing the same screen as is already visible -- unless we're showing the same screen as is already visible
if top and top.id ~= screen.id then if top and top.id ~= screen.id then
show_out(top, screen) show_out(top, screen, callbacks.track())
end end
end end
end end
@@ -441,7 +450,9 @@ function M.show(id, options, data, cb)
end end
-- show screen -- show screen
show_in(screen, top, options and options.reload, cb) show_in(screen, top, options and options.reload, callbacks.track())
if cb then callbacks.when_done(cb) end
return true return true
end end
@@ -453,9 +464,12 @@ end
-- @return true if successfully going back, false if busy performing another operation -- @return true if successfully going back, false if busy performing another operation
function M.back(data, cb) function M.back(data, cb)
if M.is_busy() then if M.is_busy() then
log("back() monarch is busy, ignoring request")
return false return false
end end
local callbacks = callback_tracker()
local screen = table.remove(stack) local screen = table.remove(stack)
if screen then if screen then
log("back()", screen.id) log("back()", screen.id)
@@ -467,7 +481,7 @@ function M.back(data, cb)
if data then if data then
top.data = data top.data = data
end end
back_in(top, screen, cb) back_in(top, screen, callbacks.track())
end) end)
else else
back_out(screen, top) back_out(screen, top)
@@ -475,12 +489,13 @@ function M.back(data, cb)
if data then if data then
top.data = data top.data = data
end end
back_in(top, screen, cb) back_in(top, screen, callbacks.track())
end end
end end
elseif cb then
cb()
end end
if cb then callbacks.when_done(cb) end
return true return true
end end

View File

@@ -88,6 +88,22 @@ function M.scale_out(node, from, easing, duration, delay, cb)
gui.animate(node, gui.PROP_SCALE, ZERO_SCALE, easing, duration, delay, cb) gui.animate(node, gui.PROP_SCALE, ZERO_SCALE, easing, duration, delay, cb)
end end
function M.fade_out(node, from, easing, duration, delay, cb)
local to = gui.get_color(node)
to.w = 1
gui.set_color(node, to)
to.w = 0
gui.animate(node, gui.PROP_COLOR, to, easing, duration, delay, cb)
end
function M.fade_in(node, from, easing, duration, delay, cb)
local to = gui.get_color(node)
to.w = 0
gui.set_color(node, to)
to.w = 1
gui.animate(node, gui.PROP_COLOR, to, easing, duration, delay, cb)
end
--- Create a transition for a node --- Create a transition for a node
-- @return Transition instance -- @return Transition instance
function M.create(node) function M.create(node)
@@ -255,4 +271,16 @@ function M.in_left_out_left(node, duration, delay, easing)
end end
function M.fade_in_out(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.fade_in, easing.OUT, duration, delay or 0)
.show_out(M.fade_out, easing.IN, duration, delay or 0)
.back_in(M.fade_in, easing.OUT, duration, delay or 0)
.back_out(M.fade_out, easing.IN, duration, delay or 0)
end
return M return M

View File

@@ -0,0 +1,39 @@
local M = {}
function M.create()
local instance = {}
local callback = nil
local callback_count = 0
--- Create a callback function and track when it is done
-- @return Callback function
function instance.track()
callback_count = callback_count + 1
return function()
callback_count = callback_count - 1
if callback_count == 0 and callback then
callback()
end
end
end
--- Call a function when all callbacks have been triggered
-- @param cb Function to call when all
function instance.when_done(cb)
callback = cb
if callback_count == 0 then
callback()
end
end
return instance
end
return setmetatable(M, {
__call = function(_, ...)
return M.create(...)
end
})

View File

@@ -241,8 +241,16 @@ return function()
monarch.add_listener(URL2) monarch.add_listener(URL2)
monarch.show(SCREEN1) monarch.show(SCREEN1)
assert(mock_msg.messages(URL1)[1].message_id == monarch.SCREEN_TRANSITION_IN_STARTED)
assert(mock_msg.messages(URL1)[1].message.screen == SCREEN1)
assert(mock_msg.messages(URL2)[1].message_id == monarch.SCREEN_TRANSITION_IN_STARTED)
assert(mock_msg.messages(URL2)[1].message.screen == SCREEN1)
assert(wait_until_not_busy()) assert(wait_until_not_busy())
assert(mock_msg.messages(URL1)[2].message_id == monarch.SCREEN_TRANSITION_IN_FINISHED)
assert(mock_msg.messages(URL1)[2].message.screen == SCREEN1)
assert(mock_msg.messages(URL2)[2].message_id == monarch.SCREEN_TRANSITION_IN_FINISHED)
assert(mock_msg.messages(URL2)[2].message.screen == SCREEN1)
monarch.remove_listener(URL2) monarch.remove_listener(URL2)
monarch.show(SCREEN2) monarch.show(SCREEN2)
assert(wait_until_not_busy()) assert(wait_until_not_busy())
@@ -252,15 +260,16 @@ return function()
local messages_1 = mock_msg.messages(URL1) local messages_1 = mock_msg.messages(URL1)
local messages_2 = mock_msg.messages(URL2) local messages_2 = mock_msg.messages(URL2)
assert(#messages_1 == 5) assert(#mock_msg.messages(URL1) == 10)
assert(#messages_2 == 1) assert(#mock_msg.messages(URL2) == 2)
assert(messages_2[1].message_id == monarch.SCREEN_VISIBLE and messages_1[1].message.screen == SCREEN1) assert(mock_msg.messages(URL1)[3].message_id == monarch.SCREEN_TRANSITION_OUT_STARTED)
assert(mock_msg.messages(URL1)[3].message.screen == SCREEN1)
assert(messages_1[1].message_id == monarch.SCREEN_VISIBLE and messages_1[1].message.screen == SCREEN1) assert(mock_msg.messages(URL1)[4].message_id == monarch.SCREEN_TRANSITION_IN_STARTED)
assert(messages_1[2].message_id == monarch.SCREEN_HIDDEN and messages_1[2].message.screen == SCREEN1) assert(mock_msg.messages(URL1)[4].message.screen == SCREEN2)
assert(messages_1[3].message_id == monarch.SCREEN_VISIBLE and messages_1[3].message.screen == SCREEN2) assert(mock_msg.messages(URL1)[5].message_id == monarch.SCREEN_TRANSITION_OUT_FINISHED)
assert(messages_1[4].message_id == monarch.SCREEN_HIDDEN and messages_1[4].message.screen == SCREEN2) assert(mock_msg.messages(URL1)[5].message.screen == SCREEN1)
assert(messages_1[5].message_id == monarch.SCREEN_VISIBLE and messages_1[5].message.screen == SCREEN1) assert(mock_msg.messages(URL1)[6].message_id == monarch.SCREEN_TRANSITION_IN_FINISHED)
assert(mock_msg.messages(URL1)[7].message.screen == SCREEN2)
end) end)
end) end)
end end