From d1cf8281c963917448e235c980ef7947e78cd594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ritzl?= Date: Sat, 16 Jun 2018 22:43:25 +0200 Subject: [PATCH] Added helpers for common sets of transitions --- README.md | 30 ++++++- example/game.gui_script | 6 +- example/menu.gui_script | 6 +- example/pregame.gui_script | 6 +- monarch/transitions/easings.lua | 26 ++++++ monarch/transitions/gui.lua | 57 ++++++++++++++ test/data/screens.collection | 63 +++++++++++++++ test/data/transition1.collection | 37 +++++++++ test/data/transition1.gui | 131 +++++++++++++++++++++++++++++++ test/data/transition1.gui_script | 10 +++ test/test_monarch.lua | 13 ++- 11 files changed, 367 insertions(+), 18 deletions(-) create mode 100644 monarch/transitions/easings.lua create mode 100644 test/data/transition1.collection create mode 100644 test/data/transition1.gui create mode 100644 test/data/transition1.gui_script diff --git a/README.md b/README.md index 345000b..a2f2a71 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/example/game.gui_script b/example/game.gui_script index e006d70..765ae13 100644 --- a/example/game.gui_script +++ b/example/game.gui_script @@ -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) diff --git a/example/menu.gui_script b/example/menu.gui_script index 8dcb5b3..450da44 100644 --- a/example/menu.gui_script +++ b/example/menu.gui_script @@ -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) diff --git a/example/pregame.gui_script b/example/pregame.gui_script index 18c5927..b2c529b 100644 --- a/example/pregame.gui_script +++ b/example/pregame.gui_script @@ -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) diff --git a/monarch/transitions/easings.lua b/monarch/transitions/easings.lua new file mode 100644 index 0000000..b4a50db --- /dev/null +++ b/monarch/transitions/easings.lua @@ -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 \ No newline at end of file diff --git a/monarch/transitions/gui.lua b/monarch/transitions/gui.lua index a63a465..7c710b8 100644 --- a/monarch/transitions/gui.lua +++ b/monarch/transitions/gui.lua @@ -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 diff --git a/test/data/screens.collection b/test/data/screens.collection index 47aebec..e0e15b2 100644 --- a/test/data/screens.collection +++ b/test/data/screens.collection @@ -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 + } +} diff --git a/test/data/transition1.collection b/test/data/transition1.collection new file mode 100644 index 0000000..787b8d0 --- /dev/null +++ b/test/data/transition1.collection @@ -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 + } +} diff --git a/test/data/transition1.gui b/test/data/transition1.gui new file mode 100644 index 0000000..5a045d4 --- /dev/null +++ b/test/data/transition1.gui @@ -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 diff --git a/test/data/transition1.gui_script b/test/data/transition1.gui_script new file mode 100644 index 0000000..8faa529 --- /dev/null +++ b/test/data/transition1.gui_script @@ -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 diff --git a/test/test_monarch.lua b/test/test_monarch.lua index 456bede..6b14367 100644 --- a/test/test_monarch.lua +++ b/test/test_monarch.lua @@ -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