├── .gitattributes ├── .gitignore ├── README.md ├── define ├── gui │ ├── button.lua │ ├── input_transitions.lua │ └── text.lua ├── input_actions.lua ├── input_manager.lua ├── input_states.lua ├── internal │ ├── default_button_transitions.lua │ └── default_text_transitions.lua └── util │ └── base.input_binding ├── examples ├── assets │ ├── example.atlas │ ├── templates │ │ ├── button.gui │ │ └── text_button.gui │ ├── volume_off.png │ └── volume_on.png ├── examples.collection ├── examples.gui ├── examples.gui_script ├── text_input │ ├── text_input.collection │ ├── text_input.gui │ └── text_input.gui_script └── volume │ ├── volume.collection │ ├── volume.gui │ ├── volume.gui_script │ └── volume_checkbox_transitions.lua ├── game.project └── todo /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .externalToolBuilders 3 | .DS_Store 4 | .lock-wscript 5 | build 6 | *.pyc 7 | .project 8 | .cproject 9 | builtins 10 | .internal 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DeFine 2 | 3 | _When great is not good enough.. __it needs to be Fine.___ 4 | 5 | A UI/UX library for the Defold engine, providing fundamental components to build a rich graphical user interface in no time. 6 | 7 | # Usage 8 | Add latest zip URL as a [dependency](http://www.defold.com/manuals/libraries/#_setting_up_library_dependencies) in your Defold project: `https://github.com/adamwestman/define/archive/master.zip` 9 | 10 | ## Input Manager 11 | 12 | The __define.input_manager__ provide a shared container to manage all components, allowing them to be grouped and prioritized. 13 | 14 | ```lua 15 | function init(self) 16 | input_manager.acquire() 17 | 18 | self.btn_text = input_manager.add_button(self, button.create("text/bg"), function() 19 | self.current_proxy = TEXT_PROXY 20 | msg.post(self.current_proxy, "async_load") 21 | end) 22 | self.btn_volume = input_manager.add_button(self, button.create("volume/bg"), function() 23 | self.current_proxy = VOLUME_PROXY 24 | msg.post(self.current_proxy, "async_load") 25 | end) 26 | self.btn_back = input_manager.add_button(self, button.create("back/bg"), function() 27 | msg.post(self.current_proxy, "unload") 28 | end) 29 | 30 | button.hide(self.btn_back) 31 | end 32 | 33 | function final(self) 34 | input_manager.release() 35 | end 36 | 37 | function on_input(self, action_id, action) 38 | return input_manager.on_input(self, action_id, action) 39 | end 40 | ``` 41 | 42 | ### Buttons 43 | 44 | The __define.gui.button__ component allows a gui node to react on user tap actions. 45 | 46 | ### Text Fields 47 | 48 | The __define.gui.text__ component allows users to edit the contents of a text node. 49 | 50 | ```lua 51 | local text = require "define.gui.text" 52 | 53 | function init(self) 54 | self.name = text.create("name") 55 | self.place = text.create("place", {max_lengt=10}) 56 | 57 | msg.post(".", "acquire_input_focus") 58 | end 59 | 60 | function on_input(self, action_id, action) 61 | if text.on_input(self.name, action_id, action) then 62 | return true 63 | elseif text.on_input(self.place, action_id, action) then 64 | return true 65 | end 66 | end 67 | ``` 68 | 69 | ### State Transitions 70 | 71 | All components are built around a state/transition design. This enables the components to be appear and act in depending on needs. Here's an example of how a Checkbox component can be built using the __define.gui.button__ one. 72 | 73 | ```lua 74 | local button = require "define.gui.button" 75 | local volume_checkbox_transitions = require "examples.volume.volume_checkbox_transitions" 76 | 77 | function init(self) 78 | self.volume = button.create("volume", volume_checkbox_transitions) 79 | 80 | msg.post(".", "acquire_input_focus") 81 | end 82 | 83 | function on_input(self, action_id, action) 84 | if button.on_input(self.volume, action_id, action) then 85 | self.muted = not self.muted 86 | button.set_state(self.volume, self.muted and hash("off") or hash("on")) 87 | sound.set_group_gain(hash("master"), self.muted and 0 or 1) 88 | end 89 | end 90 | ``` 91 | 92 | Where the transitions handle the checked state. 93 | ```lua 94 | local input_states = require("define.input_states") 95 | local input_transitions = require("define.gui.input_transitions") 96 | 97 | local COLOR_DARKEN = vmath.vector4(0.8) 98 | local COLOR_DEFAULT = vmath.vector4(1) 99 | 100 | return { 101 | input_transitions.create(input_states.STATE_IDLE, input_states.STATE_PRESSED, input_transitions.multi_parallell( 102 | input_transitions.shake(), 103 | input_transitions.to_color(COLOR_DARKEN) 104 | )), 105 | input_transitions.create(input_states.STATE_PRESSED, input_states.STATE_IDLE, input_transitions.multi_parallell( 106 | input_transitions.shake(), 107 | input_transitions.to_color(COLOR_DEFAULT) 108 | )), 109 | input_transitions.create(input_states.STATE_IDLE, input_states.STATE_HIDDEN, input_transitions.to_disabled()), 110 | input_transitions.create(input_states.STATE_HIDDEN, input_states.STATE_IDLE, input_transitions.to_enabled()), 111 | input_transitions.create(input_states.STATE_IDLE, input_states.STATE_HOVER, input_transitions.to_color(COLOR_DARKEN)), 112 | input_transitions.create(input_states.STATE_HOVER, input_states.STATE_IDLE, input_transitions.to_color(COLOR_DEFAULT)), 113 | input_transitions.create(input_states.STATE_IDLE, hash("on"), input_transitions.to_flipbook(hash("volume_on"))), 114 | input_transitions.create(input_states.STATE_IDLE, hash("off"), input_transitions.to_flipbook(hash("volume_off"))), 115 | } 116 | ``` 117 | -------------------------------------------------------------------------------- /define/gui/button.lua: -------------------------------------------------------------------------------- 1 | 2 | local input_states = require("define.input_states") 3 | local default_button_transitions = require("define.internal.default_button_transitions") 4 | 5 | local M = {} 6 | 7 | M.WARNING_TRANSITIONS = false 8 | 9 | local function log_w(...) 10 | if M.WARNING_TRANSITIONS then 11 | print(...) 12 | end 13 | end 14 | 15 | local function get_transition(transitions, from, to) 16 | if from == to then 17 | log_w("Same state, abort", from, to) 18 | return 19 | end 20 | 21 | for _, transition in pairs(transitions) do 22 | if transition.from == from and transition.to == to then 23 | log_w("Transition from to", from, to) 24 | return transition 25 | end 26 | end 27 | 28 | log_w("WARNING: No transition between", from, to) 29 | 30 | for _, transition in pairs(transitions) do 31 | if transition.to == to then 32 | log_w("Fallback: Transition from to", transition.from, to) 33 | return transition 34 | end 35 | end 36 | end 37 | 38 | local function ensure_node(node_or_id) 39 | return (type(node_or_id) == "string") and gui.get_node(node_or_id) or node_or_id 40 | end 41 | 42 | function M.create(id, transitions) 43 | assert(id) 44 | transitions = transitions or default_button_transitions 45 | 46 | local instance = { 47 | node = ensure_node(id), 48 | transitions = transitions, 49 | } 50 | M.set_state(instance, input_states.STATE_IDLE) 51 | 52 | return instance 53 | end 54 | 55 | function M.hide(instance) 56 | assert(instance) 57 | 58 | M.set_state(instance, input_states.STATE_HIDDEN) 59 | end 60 | 61 | function M.show(instance) 62 | assert(instance) 63 | 64 | M.set_state(instance, input_states.STATE_IDLE) 65 | end 66 | 67 | function M.is_hidden(instance) 68 | assert(instance) 69 | 70 | return instance.state == input_states.STATE_HIDDEN 71 | end 72 | 73 | function M.disable(instance) 74 | assert(instance) 75 | 76 | M.set_state(instance, input_states.STATE_DISABLED) 77 | end 78 | 79 | function M.enable(instance) 80 | assert(instance) 81 | 82 | M.set_state(instance, input_states.STATE_IDLE) 83 | end 84 | 85 | function M.is_disabled(instance) 86 | assert(instance) 87 | 88 | return instance.state == input_states.STATE_DISABLED 89 | end 90 | 91 | function M.set_transitions(instance, transitions) 92 | assert(instance) 93 | 94 | instance.transitions = transitions 95 | end 96 | 97 | function M.set_state(instance, to_state) 98 | assert(instance) 99 | assert(to_state) 100 | 101 | local from_state = instance.state 102 | local transition = get_transition(instance.transitions, from_state, to_state) 103 | if transition then 104 | transition.set(instance.node) 105 | else 106 | log_w("WARNING: No transition to", to_state) 107 | return 108 | end 109 | instance.state = to_state 110 | end 111 | 112 | function M.on_input(instance, action_id, action) 113 | assert(instance) 114 | assert(action) 115 | 116 | if M.is_hidden(instance) or M.is_disabled(instance) then 117 | return 118 | end 119 | 120 | if action.pressed then 121 | if gui.pick_node(instance.node, action.x, action.y) then 122 | M.set_state(instance, input_states.STATE_PRESSED) 123 | end 124 | elseif action.released and instance.state == input_states.STATE_PRESSED then 125 | M.set_state(instance, input_states.STATE_IDLE) 126 | if gui.pick_node(instance.node, action.x, action.y) then 127 | return true 128 | end 129 | elseif instance.state == input_states.STATE_IDLE then 130 | if gui.pick_node(instance.node, action.x, action.y) then 131 | M.set_state(instance, input_states.STATE_HOVER) 132 | end 133 | elseif instance.state == input_states.STATE_HOVER then 134 | if not gui.pick_node(instance.node, action.x, action.y) then 135 | M.set_state(instance, input_states.STATE_IDLE) 136 | end 137 | end 138 | end 139 | 140 | return M 141 | -------------------------------------------------------------------------------- /define/gui/input_transitions.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | function M.create(from_state, to_state, set_action) 4 | assert(from_state) 5 | assert(to_state) 6 | assert(type(set_action) == "function") 7 | 8 | return { 9 | from = from_state, 10 | to = to_state, 11 | set = set_action, 12 | } 13 | end 14 | 15 | function M.to_enabled() 16 | return function(node, done_cb) 17 | gui.set_enabled(node, true) 18 | if done_cb then 19 | done_cb(nil, node) 20 | end 21 | end 22 | end 23 | 24 | function M.to_disabled() 25 | return function(node, done_cb) 26 | gui.set_enabled(node, false) 27 | if done_cb then 28 | done_cb(nil, node) 29 | end 30 | end 31 | end 32 | 33 | function M.to_color(to, duration, easing, playback) 34 | assert(to) 35 | 36 | duration = duration or 0.2 37 | easing = easing or gui.EASING_LINEAR 38 | return function(node, done_cb) 39 | gui.animate(node, "color", to, easing, duration, 0, done_cb, playback) 40 | end 41 | end 42 | 43 | function M.to_scale(to, duration, easing, playback) 44 | assert(to) 45 | 46 | duration = duration or 0.2 47 | easing = easing or gui.EASING_LINEAR 48 | return function(node, done_cb) 49 | gui.animate(node, "scale", to, easing, duration, 0, done_cb, playback) 50 | end 51 | end 52 | 53 | function M.to_flipbook(to) 54 | assert(to) 55 | 56 | return function(node, done_cb) 57 | gui.play_flipbook(node, to, done_cb) 58 | end 59 | end 60 | 61 | function M.shake(initial_scale) 62 | initial_scale = initial_scale or vmath.vector3(1) 63 | local done 64 | local cb = function(self, node) 65 | gui.set_scale(node, initial_scale) 66 | if done then 67 | done(self, node) 68 | end 69 | end 70 | 71 | return function(node, done_cb) 72 | gui.cancel_animation(node, "scale.x") 73 | gui.cancel_animation(node, "scale.y") 74 | gui.set_scale(node, initial_scale) 75 | local scale = gui.get_scale(node) 76 | gui.set_scale(node, scale * 1.2) 77 | gui.animate(node, "scale.x", scale.x, gui.EASING_OUTELASTIC, 0.8) 78 | 79 | done = done_cb 80 | gui.animate(node, "scale.y", scale.y, gui.EASING_OUTELASTIC, 0.8, 0.05, cb) 81 | end 82 | end 83 | 84 | function M.pulse(initial_scale) 85 | initial_scale = initial_scale or vmath.vector3(1) 86 | 87 | return function(node, done_cb) 88 | gui.cancel_animation(node, "scale.x") 89 | gui.cancel_animation(node, "scale.y") 90 | gui.set_scale(node, initial_scale) 91 | local scale = gui.get_scale(node) * 1.2 92 | gui.animate(node, "scale.x", scale.x, gui.EASING_INOUTCIRC, 2, 0, nil, gui.PLAYBACK_LOOP_PINGPONG) 93 | gui.animate(node, "scale.y", scale.y, gui.EASING_INOUTCIRC, 2, 0.15, nil, gui.PLAYBACK_LOOP_PINGPONG) 94 | 95 | if done_cb then 96 | done_cb(nil, node) 97 | end 98 | end 99 | end 100 | 101 | function M.multi_sequence(...) 102 | local transitions = {...} 103 | assert(#transitions > 0) 104 | 105 | local i, cb, done 106 | cb = function(self, node) 107 | i = i + 1 108 | if transitions[i] then 109 | transitions[i](node, cb) 110 | elseif done then 111 | done(self, node) 112 | end 113 | end 114 | return function(node, done_cb) 115 | i = 0 116 | done = done_cb 117 | cb(nil, node) 118 | end 119 | end 120 | 121 | function M.multi_parallell(...) 122 | local transitions = {...} 123 | local count = #transitions 124 | assert(count > 0) 125 | 126 | return function(node, done_cb) 127 | for i, transition in pairs(transitions) do 128 | if i == count then 129 | transition(node, done_cb) 130 | else 131 | transition(node) 132 | end 133 | end 134 | end 135 | end 136 | 137 | return M 138 | -------------------------------------------------------------------------------- /define/gui/text.lua: -------------------------------------------------------------------------------- 1 | 2 | local input_states = require("define.input_states") 3 | local input_actions = require("define.input_actions") 4 | local default_text_transitions = require("define.internal.default_text_transitions") 5 | 6 | local M = {} 7 | 8 | M.default_config = { 9 | keyboard_type = gui.KEYBOARD_TYPE_DEFAULT, 10 | max_lengt = nil, 11 | } 12 | 13 | M.WARNING_TRANSITIONS = false 14 | 15 | local function log_w(...) 16 | if M.WARNING_TRANSITIONS then 17 | print(...) 18 | end 19 | end 20 | 21 | local function get_transition(transitions, from, to) 22 | if from == to then 23 | log_w("Same state, abort", from, to) 24 | return 25 | end 26 | 27 | for _, transition in pairs(transitions) do 28 | if transition.from == from and transition.to == to then 29 | log_w("Transition from to", from, to) 30 | return transition 31 | end 32 | end 33 | 34 | log_w("WARNING: No transition between", from, to) 35 | 36 | for _, transition in pairs(transitions) do 37 | if transition.to == to then 38 | log_w("Fallback: Transition from to", transition.from, to) 39 | return transition 40 | end 41 | end 42 | end 43 | 44 | local function ensure_node(node_or_id) 45 | return (type(node_or_id) == "string") and gui.get_node(node_or_id) or node_or_id 46 | end 47 | 48 | local function repaint(instance) 49 | if instance.state == input_states.STATE_FOCUSED then 50 | -- todo, add arrow navigation and position 51 | local display_text = instance.text .. "|" 52 | gui.set_text(instance.node, display_text) 53 | else 54 | gui.set_text(instance.node, instance.text) 55 | end 56 | end 57 | 58 | function M.create(id, config, transitions) 59 | assert(id) 60 | config = config or M.default_config -- todo: add merge 61 | transitions = transitions or default_text_transitions 62 | local node = ensure_node(id) 63 | local instance = { 64 | text = gui.get_text(node), 65 | node = node, 66 | transitions = transitions, 67 | config = config, 68 | } 69 | M.set_state(instance, input_states.STATE_IDLE) 70 | 71 | return instance 72 | end 73 | 74 | function M.hide(instance) 75 | assert(instance) 76 | 77 | M.set_state(instance, input_states.STATE_HIDDEN) 78 | end 79 | 80 | function M.show(instance) 81 | assert(instance) 82 | 83 | M.set_state(instance, input_states.STATE_IDLE) 84 | end 85 | 86 | function M.is_hidden(instance) 87 | assert(instance) 88 | 89 | return instance.state == input_states.STATE_HIDDEN 90 | end 91 | 92 | function M.disable(instance) 93 | assert(instance) 94 | 95 | M.set_state(instance, input_states.STATE_DISABLED) 96 | end 97 | 98 | function M.enable(instance) 99 | assert(instance) 100 | 101 | M.set_state(instance, input_states.STATE_IDLE) 102 | end 103 | 104 | function M.is_disabled(instance) 105 | assert(instance) 106 | 107 | return instance.state == input_states.STATE_DISABLED 108 | end 109 | 110 | function M.focus() 111 | assert(instance) 112 | 113 | M.set_state(instance, input_states.STATE_FOCUSED) 114 | end 115 | 116 | function M.blurr() 117 | assert(instance) 118 | 119 | M.set_state(instance, input_states.STATE_IDLE) 120 | end 121 | 122 | function M.is_focused(instance) 123 | assert(instance) 124 | 125 | return instance.state == input_states.STATE_FOCUSED 126 | end 127 | 128 | function M.get_text(instance) 129 | assert(instance) 130 | 131 | return instance.text 132 | end 133 | 134 | function M.set_text(instance, text) 135 | assert(instance) 136 | 137 | instance.text = text 138 | 139 | repaint(instance) 140 | end 141 | 142 | function M.set_transitions(instance, transitions) 143 | assert(instance) 144 | 145 | instance.transitions = transitions 146 | end 147 | 148 | function M.set_state(instance, to_state) 149 | assert(instance) 150 | assert(to_state) 151 | 152 | local from_state = instance.state 153 | local transition = get_transition(instance.transitions, from_state, to_state) 154 | if transition then 155 | transition.set(instance.node) 156 | else 157 | log_w("WARNING: No transition to", to_state) 158 | return 159 | end 160 | instance.state = to_state 161 | repaint(instance) 162 | end 163 | 164 | local function insert_text(instance, text) 165 | if instance.config.max_lengt then 166 | local len = string.len(instance.text) 167 | if len >= instance.config.max_lengt then 168 | return 169 | end 170 | end 171 | 172 | instance.text = instance.text .. text 173 | repaint(instance) 174 | end 175 | 176 | local function erase_text(instance) 177 | local len = string.len(instance.text) 178 | if len > 0 then 179 | instance.text = string.sub(instance.text, 0, len-1) 180 | repaint(instance) 181 | end 182 | end 183 | 184 | function M.on_input(instance, action_id, action) 185 | assert(instance) 186 | assert(action) 187 | 188 | if M.is_hidden(instance) or M.is_disabled(instance) then 189 | return 190 | end 191 | 192 | -- handle touch states 193 | if action_id == input_actions.TOUCH then 194 | if action.pressed then 195 | if gui.pick_node(instance.node, action.x, action.y) then 196 | M.set_state(instance, input_states.STATE_PRESSED) 197 | else 198 | M.set_state(instance, input_states.STATE_IDLE) 199 | end 200 | 201 | elseif action.released and instance.state == input_states.STATE_PRESSED then 202 | if gui.pick_node(instance.node, action.x, action.y) then 203 | M.set_state(instance, input_states.STATE_FOCUSED) 204 | return true 205 | else 206 | M.set_state(instance, input_states.STATE_IDLE) 207 | end 208 | 209 | end 210 | 211 | -- handle text states 212 | elseif instance.state == input_states.STATE_FOCUSED then 213 | if action_id == input_actions.TEXT then 214 | insert_text(instance, action.text) 215 | return true 216 | 217 | elseif action_id == input_actions.BACKSPACE then 218 | if action.pressed or action.repeated then 219 | erase_text(instance) 220 | end 221 | return true 222 | 223 | end 224 | 225 | -- handle hover states 226 | elseif instance.state == input_states.STATE_IDLE then 227 | if gui.pick_node(instance.node, action.x, action.y) then 228 | M.set_state(instance, input_states.STATE_HOVER) 229 | end 230 | elseif instance.state == input_states.STATE_HOVER then 231 | if not gui.pick_node(instance.node, action.x, action.y) then 232 | M.set_state(instance, input_states.STATE_IDLE) 233 | end 234 | 235 | end 236 | 237 | end 238 | 239 | return M 240 | -------------------------------------------------------------------------------- /define/input_actions.lua: -------------------------------------------------------------------------------- 1 | return { 2 | TOUCH = hash("action_touch"), 3 | LEFT = hash("action_left"), 4 | RIGHT = hash("action_right"), 5 | ENTER = hash("action_enter"), 6 | BACKSPACE = hash("action_backspace"), 7 | TAB = hash("action_tab"), 8 | BACK = hash("action_back"), 9 | TEXT = hash("action_text"), 10 | MARKED_TEXT = hash("action_marked_text"), 11 | } 12 | -------------------------------------------------------------------------------- /define/input_manager.lua: -------------------------------------------------------------------------------- 1 | local button = require("define.gui.button") 2 | local text = require("define.gui.text") 3 | 4 | local input_actions = require("define.input_actions") 5 | 6 | local M = {} 7 | 8 | -------------------------- INPUT HANDLER MANAGEMENT 9 | 10 | local function button_input_handler(object, action_id, action) 11 | if button.on_input(object.button, action_id, action) then 12 | object.callback(object.button) 13 | return true 14 | end 15 | end 16 | 17 | local function text_input_handler(object, action_id, action) 18 | if text.on_input(object.text, action_id, action) then 19 | --[[ We don't want the behaviour to be different when added to input_manager. 20 | if action_id == input_actions.ENTER then 21 | text.blurr(object.text) 22 | end 23 | --]] 24 | return true 25 | end 26 | end 27 | 28 | local TYPE_BUTTON = hash("button") 29 | local TYPE_TEXT_FIELD = hash("text_field") 30 | 31 | local input_handlers = { 32 | [TYPE_BUTTON] = button_input_handler, 33 | [TYPE_TEXT_FIELD] = text_input_handler, 34 | } 35 | 36 | -------------------------- INPUT GROUP MANAGEMENT 37 | 38 | local function priority_sort(a, b) 39 | return a.priority < b.priority 40 | end 41 | 42 | local input_groups = {} 43 | 44 | function M.add_button(group, button_instance, callback, priority) 45 | assert(group) 46 | assert(button_instance) 47 | assert(callback) 48 | priority = priority or 0 49 | 50 | local objects = input_groups[group] or {} 51 | 52 | table.insert(objects, {button = button_instance, callback = callback, priority = priority, type = TYPE_BUTTON}) 53 | table.sort(objects, priority_sort) 54 | 55 | input_groups[group] = objects 56 | 57 | return button_instance 58 | end 59 | 60 | function M.remove_button(button_instance) 61 | assert(button_instance) 62 | 63 | for _, objects in pairs(input_groups) do 64 | for i, object in pairs(objects) do 65 | if object.button == button_instance then 66 | table.remove(objects, i) 67 | return true 68 | end 69 | end 70 | end 71 | return false 72 | end 73 | 74 | function M.add_text(group, text_instance, priority) 75 | assert(group) 76 | assert(button_instance) 77 | assert(callback) 78 | priority = priority or 0 79 | 80 | local objects = input_groups[group] or {} 81 | 82 | table.insert(objects, {text = text_instance, priority = priority, type = TYPE_TEXT_FIELD}) 83 | table.sort(objects, priority_sort) 84 | 85 | input_groups[group] = objects 86 | 87 | return button_instance 88 | end 89 | 90 | function M.remove_text(text_instance) 91 | assert(text_instance) 92 | 93 | for _, objects in pairs(input_groups) do 94 | for i, object in pairs(objects) do 95 | if object.text == text_instance then 96 | table.remove(objects, i) 97 | return true 98 | end 99 | end 100 | end 101 | return false 102 | end 103 | 104 | function M.remove_group(group) 105 | assert(group) 106 | 107 | local objects = input_groups[group] or {} 108 | while #objects > 0 do 109 | table.remove(objects) 110 | end 111 | end 112 | 113 | function M.on_input(group, action_id, action) 114 | assert(group) 115 | assert(action) 116 | 117 | local objects = input_groups[group] or {} 118 | for _, object in pairs(objects) do 119 | if input_handlers[object.type](object, action_id, action) then 120 | return true 121 | end 122 | end 123 | end 124 | 125 | function M.acquire() 126 | msg.post(".", "acquire_input_focus") 127 | end 128 | 129 | function M.release() 130 | msg.post(".", "release_input_focus") 131 | end 132 | 133 | return M 134 | -------------------------------------------------------------------------------- /define/input_states.lua: -------------------------------------------------------------------------------- 1 | return { 2 | STATE_IDLE = hash("state_idle"), 3 | STATE_HOVER = hash("state_hover"), 4 | STATE_PRESSED = hash("state_pressed"), 5 | STATE_DISABLED = hash("state_disabled"), 6 | STATE_HIDDEN = hash("state_hidden"), 7 | STATE_FOCUSED = hash("state_focused"), 8 | } 9 | -------------------------------------------------------------------------------- /define/internal/default_button_transitions.lua: -------------------------------------------------------------------------------- 1 | local input_states = require("define.input_states") 2 | local input_transitions = require("define.gui.input_transitions") 3 | 4 | local COLOR_DARKEN = vmath.vector4(0.8) 5 | local COLOR_DEFAULT = vmath.vector4(1) 6 | 7 | return { 8 | input_transitions.create(input_states.STATE_IDLE, input_states.STATE_PRESSED, input_transitions.multi_parallell( 9 | input_transitions.shake(), 10 | input_transitions.to_color(COLOR_DARKEN) 11 | )), 12 | input_transitions.create(input_states.STATE_PRESSED, input_states.STATE_IDLE, input_transitions.multi_parallell( 13 | input_transitions.shake(), 14 | input_transitions.to_color(COLOR_DEFAULT) 15 | )), 16 | input_transitions.create(input_states.STATE_IDLE, input_states.STATE_HIDDEN, input_transitions.to_disabled()), 17 | input_transitions.create(input_states.STATE_HIDDEN, input_states.STATE_IDLE, input_transitions.to_enabled()), 18 | input_transitions.create(input_states.STATE_IDLE, input_states.STATE_HOVER, input_transitions.to_color(COLOR_DARKEN)), 19 | input_transitions.create(input_states.STATE_HOVER, input_states.STATE_IDLE, input_transitions.to_color(COLOR_DEFAULT)), 20 | } 21 | -------------------------------------------------------------------------------- /define/internal/default_text_transitions.lua: -------------------------------------------------------------------------------- 1 | local input_states = require("define.input_states") 2 | local input_transitions = require("define.gui.input_transitions") 3 | 4 | local COLOR_DARKEN = vmath.vector4(0.8) 5 | local COLOR_DEFAULT = vmath.vector4(1) 6 | 7 | return { 8 | input_transitions.create(input_states.STATE_IDLE, input_states.STATE_PRESSED, input_transitions.multi_parallell( 9 | input_transitions.shake(), 10 | input_transitions.to_color(COLOR_DARKEN) 11 | )), 12 | input_transitions.create(input_states.STATE_PRESSED, input_states.STATE_IDLE, input_transitions.multi_parallell( 13 | input_transitions.shake(), 14 | input_transitions.to_color(COLOR_DEFAULT) 15 | )), 16 | input_transitions.create(input_states.STATE_IDLE, input_states.STATE_HIDDEN, input_transitions.to_disabled()), 17 | input_transitions.create(input_states.STATE_HIDDEN, input_states.STATE_IDLE, input_transitions.to_enabled()), 18 | input_transitions.create(input_states.STATE_IDLE, input_states.STATE_HOVER, input_transitions.to_color(COLOR_DARKEN)), 19 | input_transitions.create(input_states.STATE_HOVER, input_states.STATE_IDLE, input_transitions.to_color(COLOR_DEFAULT)), 20 | input_transitions.create(input_states.STATE_PRESSED, input_states.STATE_FOCUSED, input_transitions.to_color(COLOR_DEFAULT)), 21 | } 22 | -------------------------------------------------------------------------------- /define/util/base.input_binding: -------------------------------------------------------------------------------- 1 | key_trigger { 2 | input: KEY_BACKSPACE 3 | action: "action_backspace" 4 | } 5 | key_trigger { 6 | input: KEY_LEFT 7 | action: "action_left" 8 | } 9 | key_trigger { 10 | input: KEY_TAB 11 | action: "action_tab" 12 | } 13 | key_trigger { 14 | input: KEY_RIGHT 15 | action: "action_right" 16 | } 17 | key_trigger { 18 | input: KEY_ENTER 19 | action: "action_enter" 20 | } 21 | mouse_trigger { 22 | input: MOUSE_BUTTON_LEFT 23 | action: "action_touch" 24 | } 25 | text_trigger { 26 | input: TEXT 27 | action: "action_text" 28 | } 29 | text_trigger { 30 | input: MARKED_TEXT 31 | action: "action_marked_text" 32 | } 33 | -------------------------------------------------------------------------------- /examples/assets/example.atlas: -------------------------------------------------------------------------------- 1 | images { 2 | image: "/examples/assets/volume_off.png" 3 | } 4 | images { 5 | image: "/examples/assets/volume_on.png" 6 | } 7 | margin: 0 8 | extrude_borders: 0 9 | inner_padding: 0 10 | -------------------------------------------------------------------------------- /examples/assets/templates/button.gui: -------------------------------------------------------------------------------- 1 | script: "" 2 | background_color { 3 | x: 0.0 4 | y: 0.0 5 | z: 0.0 6 | w: 0.0 7 | } 8 | nodes { 9 | position { 10 | x: 0.0 11 | y: 0.0 12 | z: 0.0 13 | w: 1.0 14 | } 15 | rotation { 16 | x: 0.0 17 | y: 0.0 18 | z: 0.0 19 | w: 1.0 20 | } 21 | scale { 22 | x: 1.0 23 | y: 1.0 24 | z: 1.0 25 | w: 1.0 26 | } 27 | size { 28 | x: 200.0 29 | y: 100.0 30 | z: 0.0 31 | w: 1.0 32 | } 33 | color { 34 | x: 1.0 35 | y: 1.0 36 | z: 1.0 37 | w: 1.0 38 | } 39 | type: TYPE_BOX 40 | blend_mode: BLEND_MODE_ALPHA 41 | texture: "" 42 | id: "bg" 43 | xanchor: XANCHOR_NONE 44 | yanchor: YANCHOR_NONE 45 | pivot: PIVOT_CENTER 46 | adjust_mode: ADJUST_MODE_FIT 47 | layer: "bg" 48 | inherit_alpha: true 49 | slice9 { 50 | x: 0.0 51 | y: 0.0 52 | z: 0.0 53 | w: 0.0 54 | } 55 | clipping_mode: CLIPPING_MODE_NONE 56 | clipping_visible: true 57 | clipping_inverted: false 58 | alpha: 1.0 59 | template_node_child: false 60 | size_mode: SIZE_MODE_AUTO 61 | } 62 | layers { 63 | name: "bg" 64 | } 65 | material: "/builtins/materials/gui.material" 66 | adjust_reference: ADJUST_REFERENCE_PARENT 67 | max_nodes: 512 68 | -------------------------------------------------------------------------------- /examples/assets/templates/text_button.gui: -------------------------------------------------------------------------------- 1 | script: "" 2 | fonts { 3 | name: "system_font" 4 | font: "/builtins/fonts/system_font.font" 5 | } 6 | background_color { 7 | x: 0.0 8 | y: 0.0 9 | z: 0.0 10 | w: 0.0 11 | } 12 | nodes { 13 | position { 14 | x: 0.0 15 | y: 0.0 16 | z: 0.0 17 | w: 1.0 18 | } 19 | rotation { 20 | x: 0.0 21 | y: 0.0 22 | z: 0.0 23 | w: 1.0 24 | } 25 | scale { 26 | x: 1.0 27 | y: 1.0 28 | z: 1.0 29 | w: 1.0 30 | } 31 | size { 32 | x: 200.0 33 | y: 100.0 34 | z: 0.0 35 | w: 1.0 36 | } 37 | color { 38 | x: 1.0 39 | y: 1.0 40 | z: 1.0 41 | w: 1.0 42 | } 43 | type: TYPE_BOX 44 | blend_mode: BLEND_MODE_ALPHA 45 | texture: "" 46 | id: "bg" 47 | xanchor: XANCHOR_NONE 48 | yanchor: YANCHOR_NONE 49 | pivot: PIVOT_CENTER 50 | adjust_mode: ADJUST_MODE_FIT 51 | layer: "bg" 52 | inherit_alpha: true 53 | slice9 { 54 | x: 0.0 55 | y: 0.0 56 | z: 0.0 57 | w: 0.0 58 | } 59 | clipping_mode: CLIPPING_MODE_NONE 60 | clipping_visible: true 61 | clipping_inverted: false 62 | alpha: 1.0 63 | template_node_child: false 64 | size_mode: SIZE_MODE_AUTO 65 | } 66 | nodes { 67 | position { 68 | x: 0.0 69 | y: 0.0 70 | z: 0.0 71 | w: 1.0 72 | } 73 | rotation { 74 | x: 0.0 75 | y: 0.0 76 | z: 0.0 77 | w: 1.0 78 | } 79 | scale { 80 | x: 1.0 81 | y: 1.0 82 | z: 1.0 83 | w: 1.0 84 | } 85 | size { 86 | x: 200.0 87 | y: 100.0 88 | z: 0.0 89 | w: 1.0 90 | } 91 | color { 92 | x: 0.0 93 | y: 0.0 94 | z: 0.0 95 | w: 1.0 96 | } 97 | type: TYPE_TEXT 98 | blend_mode: BLEND_MODE_ALPHA 99 | text: "" 100 | font: "system_font" 101 | id: "label" 102 | xanchor: XANCHOR_NONE 103 | yanchor: YANCHOR_NONE 104 | pivot: PIVOT_CENTER 105 | outline { 106 | x: 1.0 107 | y: 1.0 108 | z: 1.0 109 | w: 1.0 110 | } 111 | shadow { 112 | x: 1.0 113 | y: 1.0 114 | z: 1.0 115 | w: 1.0 116 | } 117 | adjust_mode: ADJUST_MODE_FIT 118 | line_break: false 119 | parent: "bg" 120 | layer: "text" 121 | inherit_alpha: true 122 | alpha: 1.0 123 | outline_alpha: 1.0 124 | shadow_alpha: 1.0 125 | template_node_child: false 126 | text_leading: 1.0 127 | text_tracking: 0.0 128 | } 129 | layers { 130 | name: "bg" 131 | } 132 | layers { 133 | name: "text" 134 | } 135 | material: "/builtins/materials/gui.material" 136 | adjust_reference: ADJUST_REFERENCE_PARENT 137 | max_nodes: 512 138 | -------------------------------------------------------------------------------- /examples/assets/volume_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamwestman/define/151fa647892637573851e4d5d6394b8891a1fad5/examples/assets/volume_off.png -------------------------------------------------------------------------------- /examples/assets/volume_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamwestman/define/151fa647892637573851e4d5d6394b8891a1fad5/examples/assets/volume_on.png -------------------------------------------------------------------------------- /examples/examples.collection: -------------------------------------------------------------------------------- 1 | name: "examples" 2 | scale_along_z: 0 3 | embedded_instances { 4 | id: "go" 5 | data: "components {\n" 6 | " id: \"examples\"\n" 7 | " component: \"/examples/examples.gui\"\n" 8 | " position {\n" 9 | " x: 0.0\n" 10 | " y: 0.0\n" 11 | " z: 0.0\n" 12 | " }\n" 13 | " rotation {\n" 14 | " x: 0.0\n" 15 | " y: 0.0\n" 16 | " z: 0.0\n" 17 | " w: 1.0\n" 18 | " }\n" 19 | "}\n" 20 | "embedded_components {\n" 21 | " id: \"text_input\"\n" 22 | " type: \"collectionproxy\"\n" 23 | " data: \"collection: \\\"/examples/text_input/text_input.collection\\\"\\n" 24 | "exclude: false\\n" 25 | "\"\n" 26 | " position {\n" 27 | " x: 0.0\n" 28 | " y: 0.0\n" 29 | " z: 0.0\n" 30 | " }\n" 31 | " rotation {\n" 32 | " x: 0.0\n" 33 | " y: 0.0\n" 34 | " z: 0.0\n" 35 | " w: 1.0\n" 36 | " }\n" 37 | "}\n" 38 | "embedded_components {\n" 39 | " id: \"volume\"\n" 40 | " type: \"collectionproxy\"\n" 41 | " data: \"collection: \\\"/examples/volume/volume.collection\\\"\\n" 42 | "exclude: false\\n" 43 | "\"\n" 44 | " position {\n" 45 | " x: 0.0\n" 46 | " y: 0.0\n" 47 | " z: 0.0\n" 48 | " }\n" 49 | " rotation {\n" 50 | " x: 0.0\n" 51 | " y: 0.0\n" 52 | " z: 0.0\n" 53 | " w: 1.0\n" 54 | " }\n" 55 | "}\n" 56 | "" 57 | position { 58 | x: 0.0 59 | y: 0.0 60 | z: 0.0 61 | } 62 | rotation { 63 | x: 0.0 64 | y: 0.0 65 | z: 0.0 66 | w: 1.0 67 | } 68 | scale3 { 69 | x: 1.0 70 | y: 1.0 71 | z: 1.0 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /examples/examples.gui: -------------------------------------------------------------------------------- 1 | script: "/examples/examples.gui_script" 2 | background_color { 3 | x: 0.0 4 | y: 0.0 5 | z: 0.0 6 | w: 0.0 7 | } 8 | nodes { 9 | position { 10 | x: 480.0 11 | y: 400.0 12 | z: 0.0 13 | w: 1.0 14 | } 15 | rotation { 16 | x: 0.0 17 | y: 0.0 18 | z: 0.0 19 | w: 1.0 20 | } 21 | scale { 22 | x: 1.0 23 | y: 1.0 24 | z: 1.0 25 | w: 1.0 26 | } 27 | size { 28 | x: 200.0 29 | y: 100.0 30 | z: 0.0 31 | w: 1.0 32 | } 33 | color { 34 | x: 1.0 35 | y: 1.0 36 | z: 1.0 37 | w: 1.0 38 | } 39 | type: TYPE_TEMPLATE 40 | id: "text_input" 41 | layer: "" 42 | inherit_alpha: true 43 | alpha: 1.0 44 | template: "/examples/assets/templates/text_button.gui" 45 | template_node_child: false 46 | } 47 | nodes { 48 | position { 49 | x: 0.0 50 | y: 0.0 51 | z: 0.0 52 | w: 1.0 53 | } 54 | rotation { 55 | x: 0.0 56 | y: 0.0 57 | z: 0.0 58 | w: 1.0 59 | } 60 | scale { 61 | x: 1.0 62 | y: 1.0 63 | z: 1.0 64 | w: 1.0 65 | } 66 | size { 67 | x: 200.0 68 | y: 100.0 69 | z: 0.0 70 | w: 1.0 71 | } 72 | color { 73 | x: 1.0 74 | y: 1.0 75 | z: 1.0 76 | w: 1.0 77 | } 78 | type: TYPE_BOX 79 | blend_mode: BLEND_MODE_ALPHA 80 | texture: "" 81 | id: "text_input/bg" 82 | xanchor: XANCHOR_NONE 83 | yanchor: YANCHOR_NONE 84 | pivot: PIVOT_CENTER 85 | adjust_mode: ADJUST_MODE_FIT 86 | parent: "text_input" 87 | layer: "bg" 88 | inherit_alpha: true 89 | slice9 { 90 | x: 0.0 91 | y: 0.0 92 | z: 0.0 93 | w: 0.0 94 | } 95 | clipping_mode: CLIPPING_MODE_NONE 96 | clipping_visible: true 97 | clipping_inverted: false 98 | alpha: 1.0 99 | template_node_child: true 100 | size_mode: SIZE_MODE_AUTO 101 | } 102 | nodes { 103 | position { 104 | x: 0.0 105 | y: 0.0 106 | z: 0.0 107 | w: 1.0 108 | } 109 | rotation { 110 | x: 0.0 111 | y: 0.0 112 | z: 0.0 113 | w: 1.0 114 | } 115 | scale { 116 | x: 1.0 117 | y: 1.0 118 | z: 1.0 119 | w: 1.0 120 | } 121 | size { 122 | x: 200.0 123 | y: 100.0 124 | z: 0.0 125 | w: 1.0 126 | } 127 | color { 128 | x: 0.0 129 | y: 0.0 130 | z: 0.0 131 | w: 1.0 132 | } 133 | type: TYPE_TEXT 134 | blend_mode: BLEND_MODE_ALPHA 135 | text: "Text Input" 136 | font: "system_font" 137 | id: "text_input/label" 138 | xanchor: XANCHOR_NONE 139 | yanchor: YANCHOR_NONE 140 | pivot: PIVOT_CENTER 141 | outline { 142 | x: 1.0 143 | y: 1.0 144 | z: 1.0 145 | w: 1.0 146 | } 147 | shadow { 148 | x: 1.0 149 | y: 1.0 150 | z: 1.0 151 | w: 1.0 152 | } 153 | adjust_mode: ADJUST_MODE_FIT 154 | line_break: false 155 | parent: "text_input/bg" 156 | layer: "text" 157 | inherit_alpha: true 158 | alpha: 1.0 159 | outline_alpha: 1.0 160 | shadow_alpha: 1.0 161 | overridden_fields: 8 162 | template_node_child: true 163 | text_leading: 1.0 164 | text_tracking: 0.0 165 | } 166 | nodes { 167 | position { 168 | x: 480.0 169 | y: 234.851 170 | z: 0.0 171 | w: 1.0 172 | } 173 | rotation { 174 | x: 0.0 175 | y: 0.0 176 | z: 0.0 177 | w: 1.0 178 | } 179 | scale { 180 | x: 1.0 181 | y: 1.0 182 | z: 1.0 183 | w: 1.0 184 | } 185 | size { 186 | x: 200.0 187 | y: 100.0 188 | z: 0.0 189 | w: 1.0 190 | } 191 | color { 192 | x: 1.0 193 | y: 1.0 194 | z: 1.0 195 | w: 1.0 196 | } 197 | type: TYPE_TEMPLATE 198 | id: "volume" 199 | layer: "" 200 | inherit_alpha: true 201 | alpha: 1.0 202 | template: "/examples/assets/templates/text_button.gui" 203 | template_node_child: false 204 | } 205 | nodes { 206 | position { 207 | x: 0.0 208 | y: 0.0 209 | z: 0.0 210 | w: 1.0 211 | } 212 | rotation { 213 | x: 0.0 214 | y: 0.0 215 | z: 0.0 216 | w: 1.0 217 | } 218 | scale { 219 | x: 1.0 220 | y: 1.0 221 | z: 1.0 222 | w: 1.0 223 | } 224 | size { 225 | x: 200.0 226 | y: 100.0 227 | z: 0.0 228 | w: 1.0 229 | } 230 | color { 231 | x: 1.0 232 | y: 1.0 233 | z: 1.0 234 | w: 1.0 235 | } 236 | type: TYPE_BOX 237 | blend_mode: BLEND_MODE_ALPHA 238 | texture: "" 239 | id: "volume/bg" 240 | xanchor: XANCHOR_NONE 241 | yanchor: YANCHOR_NONE 242 | pivot: PIVOT_CENTER 243 | adjust_mode: ADJUST_MODE_FIT 244 | parent: "volume" 245 | layer: "bg" 246 | inherit_alpha: true 247 | slice9 { 248 | x: 0.0 249 | y: 0.0 250 | z: 0.0 251 | w: 0.0 252 | } 253 | clipping_mode: CLIPPING_MODE_NONE 254 | clipping_visible: true 255 | clipping_inverted: false 256 | alpha: 1.0 257 | template_node_child: true 258 | size_mode: SIZE_MODE_AUTO 259 | } 260 | nodes { 261 | position { 262 | x: 0.0 263 | y: 0.0 264 | z: 0.0 265 | w: 1.0 266 | } 267 | rotation { 268 | x: 0.0 269 | y: 0.0 270 | z: 0.0 271 | w: 1.0 272 | } 273 | scale { 274 | x: 1.0 275 | y: 1.0 276 | z: 1.0 277 | w: 1.0 278 | } 279 | size { 280 | x: 200.0 281 | y: 100.0 282 | z: 0.0 283 | w: 1.0 284 | } 285 | color { 286 | x: 0.0 287 | y: 0.0 288 | z: 0.0 289 | w: 1.0 290 | } 291 | type: TYPE_TEXT 292 | blend_mode: BLEND_MODE_ALPHA 293 | text: "Volume Controll" 294 | font: "system_font" 295 | id: "volume/label" 296 | xanchor: XANCHOR_NONE 297 | yanchor: YANCHOR_NONE 298 | pivot: PIVOT_CENTER 299 | outline { 300 | x: 1.0 301 | y: 1.0 302 | z: 1.0 303 | w: 1.0 304 | } 305 | shadow { 306 | x: 1.0 307 | y: 1.0 308 | z: 1.0 309 | w: 1.0 310 | } 311 | adjust_mode: ADJUST_MODE_FIT 312 | line_break: false 313 | parent: "volume/bg" 314 | layer: "text" 315 | inherit_alpha: true 316 | alpha: 1.0 317 | outline_alpha: 1.0 318 | shadow_alpha: 1.0 319 | overridden_fields: 8 320 | template_node_child: true 321 | text_leading: 1.0 322 | text_tracking: 0.0 323 | } 324 | nodes { 325 | position { 326 | x: 922.611 327 | y: 603.429 328 | z: 0.0 329 | w: 1.0 330 | } 331 | rotation { 332 | x: 0.0 333 | y: 0.0 334 | z: 0.0 335 | w: 1.0 336 | } 337 | scale { 338 | x: 1.0 339 | y: 1.0 340 | z: 1.0 341 | w: 1.0 342 | } 343 | size { 344 | x: 200.0 345 | y: 100.0 346 | z: 0.0 347 | w: 1.0 348 | } 349 | color { 350 | x: 1.0 351 | y: 1.0 352 | z: 1.0 353 | w: 1.0 354 | } 355 | type: TYPE_TEMPLATE 356 | id: "back" 357 | layer: "" 358 | inherit_alpha: true 359 | alpha: 1.0 360 | template: "/examples/assets/templates/button.gui" 361 | template_node_child: false 362 | } 363 | nodes { 364 | position { 365 | x: 0.0 366 | y: 0.0 367 | z: 0.0 368 | w: 1.0 369 | } 370 | rotation { 371 | x: 0.0 372 | y: 0.0 373 | z: 0.0 374 | w: 1.0 375 | } 376 | scale { 377 | x: 1.0 378 | y: 1.0 379 | z: 1.0 380 | w: 1.0 381 | } 382 | size { 383 | x: 40.0 384 | y: 40.0 385 | z: 0.0 386 | w: 1.0 387 | } 388 | color { 389 | x: 0.6 390 | y: 0.0 391 | z: 0.0 392 | w: 1.0 393 | } 394 | type: TYPE_BOX 395 | blend_mode: BLEND_MODE_ALPHA 396 | texture: "" 397 | id: "back/bg" 398 | xanchor: XANCHOR_NONE 399 | yanchor: YANCHOR_NONE 400 | pivot: PIVOT_CENTER 401 | adjust_mode: ADJUST_MODE_FIT 402 | parent: "back" 403 | layer: "bg" 404 | inherit_alpha: true 405 | slice9 { 406 | x: 0.0 407 | y: 0.0 408 | z: 0.0 409 | w: 0.0 410 | } 411 | clipping_mode: CLIPPING_MODE_NONE 412 | clipping_visible: true 413 | clipping_inverted: false 414 | alpha: 1.0 415 | overridden_fields: 4 416 | overridden_fields: 5 417 | overridden_fields: 38 418 | template_node_child: true 419 | size_mode: SIZE_MODE_MANUAL 420 | } 421 | material: "/builtins/materials/gui.material" 422 | adjust_reference: ADJUST_REFERENCE_PARENT 423 | max_nodes: 512 424 | -------------------------------------------------------------------------------- /examples/examples.gui_script: -------------------------------------------------------------------------------- 1 | local input_manager = require "define.input_manager" 2 | local button = require "define.gui.button" 3 | 4 | function init(self) 5 | self.text_input = input_manager.add_button(self, button.create("text_input/bg"), function() 6 | self.proxy = "#text_input" 7 | msg.post(self.proxy, "async_load") 8 | end) 9 | 10 | self.volume = input_manager.add_button(self, button.create("volume/bg"), function() 11 | self.proxy = "#volume" 12 | msg.post(self.proxy, "async_load") 13 | end) 14 | 15 | self.back = input_manager.add_button(self, button.create("back/bg"), function() 16 | msg.post(self.proxy, "unload") 17 | end) 18 | 19 | button.hide(self.back) 20 | input_manager.acquire() 21 | end 22 | 23 | function on_input(self, action_id, action) 24 | return input_manager.on_input(self, action_id, action) 25 | end 26 | 27 | function on_message(self, message_id, message, sender) 28 | if message_id == hash("proxy_loaded") then 29 | msg.post(sender, "enable") 30 | button.show(self.back) 31 | button.hide(self.text_input) 32 | button.hide(self.volume) 33 | 34 | elseif message_id == hash("proxy_unloaded") then 35 | button.hide(self.back) 36 | button.show(self.text_input) 37 | button.show(self.volume) 38 | 39 | end 40 | end -------------------------------------------------------------------------------- /examples/text_input/text_input.collection: -------------------------------------------------------------------------------- 1 | name: "text_input" 2 | scale_along_z: 0 3 | embedded_instances { 4 | id: "go" 5 | data: "components {\n" 6 | " id: \"text_input\"\n" 7 | " component: \"/examples/text_input/text_input.gui\"\n" 8 | " position {\n" 9 | " x: 0.0\n" 10 | " y: 0.0\n" 11 | " z: 0.0\n" 12 | " }\n" 13 | " rotation {\n" 14 | " x: 0.0\n" 15 | " y: 0.0\n" 16 | " z: 0.0\n" 17 | " w: 1.0\n" 18 | " }\n" 19 | "}\n" 20 | "" 21 | position { 22 | x: 0.0 23 | y: 0.0 24 | z: 0.0 25 | } 26 | rotation { 27 | x: 0.0 28 | y: 0.0 29 | z: 0.0 30 | w: 1.0 31 | } 32 | scale3 { 33 | x: 1.0 34 | y: 1.0 35 | z: 1.0 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/text_input/text_input.gui: -------------------------------------------------------------------------------- 1 | script: "/examples/text_input/text_input.gui_script" 2 | fonts { 3 | name: "system_font" 4 | font: "/builtins/fonts/system_font.font" 5 | } 6 | background_color { 7 | x: 0.0 8 | y: 0.0 9 | z: 0.0 10 | w: 0.0 11 | } 12 | nodes { 13 | position { 14 | x: 474.806 15 | y: 281.731 16 | z: 0.0 17 | w: 1.0 18 | } 19 | rotation { 20 | x: 0.0 21 | y: 0.0 22 | z: 0.0 23 | w: 1.0 24 | } 25 | scale { 26 | x: 1.0 27 | y: 1.0 28 | z: 1.0 29 | w: 1.0 30 | } 31 | size { 32 | x: 200.0 33 | y: 100.0 34 | z: 0.0 35 | w: 1.0 36 | } 37 | color { 38 | x: 1.0 39 | y: 1.0 40 | z: 1.0 41 | w: 1.0 42 | } 43 | type: TYPE_TEXT 44 | blend_mode: BLEND_MODE_ALPHA 45 | text: "" 46 | font: "system_font" 47 | id: "name" 48 | xanchor: XANCHOR_NONE 49 | yanchor: YANCHOR_NONE 50 | pivot: PIVOT_CENTER 51 | outline { 52 | x: 1.0 53 | y: 1.0 54 | z: 1.0 55 | w: 1.0 56 | } 57 | shadow { 58 | x: 1.0 59 | y: 1.0 60 | z: 1.0 61 | w: 1.0 62 | } 63 | adjust_mode: ADJUST_MODE_FIT 64 | line_break: false 65 | layer: "" 66 | inherit_alpha: true 67 | alpha: 1.0 68 | outline_alpha: 1.0 69 | shadow_alpha: 1.0 70 | template_node_child: false 71 | text_leading: 1.0 72 | text_tracking: 0.0 73 | } 74 | nodes { 75 | position { 76 | x: 474.806 77 | y: 185.194 78 | z: 0.0 79 | w: 1.0 80 | } 81 | rotation { 82 | x: 0.0 83 | y: 0.0 84 | z: 0.0 85 | w: 1.0 86 | } 87 | scale { 88 | x: 1.0 89 | y: 1.0 90 | z: 1.0 91 | w: 1.0 92 | } 93 | size { 94 | x: 200.0 95 | y: 100.0 96 | z: 0.0 97 | w: 1.0 98 | } 99 | color { 100 | x: 1.0 101 | y: 1.0 102 | z: 1.0 103 | w: 1.0 104 | } 105 | type: TYPE_TEXT 106 | blend_mode: BLEND_MODE_ALPHA 107 | text: "place" 108 | font: "system_font" 109 | id: "place" 110 | xanchor: XANCHOR_NONE 111 | yanchor: YANCHOR_NONE 112 | pivot: PIVOT_CENTER 113 | outline { 114 | x: 1.0 115 | y: 1.0 116 | z: 1.0 117 | w: 1.0 118 | } 119 | shadow { 120 | x: 1.0 121 | y: 1.0 122 | z: 1.0 123 | w: 1.0 124 | } 125 | adjust_mode: ADJUST_MODE_FIT 126 | line_break: false 127 | layer: "" 128 | inherit_alpha: true 129 | alpha: 1.0 130 | outline_alpha: 1.0 131 | shadow_alpha: 1.0 132 | template_node_child: false 133 | text_leading: 1.0 134 | text_tracking: 0.0 135 | } 136 | material: "/builtins/materials/gui.material" 137 | adjust_reference: ADJUST_REFERENCE_PARENT 138 | max_nodes: 512 139 | -------------------------------------------------------------------------------- /examples/text_input/text_input.gui_script: -------------------------------------------------------------------------------- 1 | local text = require "define.gui.text" 2 | 3 | function init(self) 4 | self.name = text.create("name") 5 | self.place = text.create("place", {max_lengt=10}) 6 | 7 | msg.post(".", "acquire_input_focus") 8 | end 9 | 10 | function on_input(self, action_id, action) 11 | if text.on_input(self.name, action_id, action) then 12 | return true 13 | elseif text.on_input(self.place, action_id, action) then 14 | return true 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /examples/volume/volume.collection: -------------------------------------------------------------------------------- 1 | name: "volume" 2 | scale_along_z: 0 3 | embedded_instances { 4 | id: "go" 5 | data: "components {\n" 6 | " id: \"volume\"\n" 7 | " component: \"/examples/volume/volume.gui\"\n" 8 | " position {\n" 9 | " x: 0.0\n" 10 | " y: 0.0\n" 11 | " z: 0.0\n" 12 | " }\n" 13 | " rotation {\n" 14 | " x: 0.0\n" 15 | " y: 0.0\n" 16 | " z: 0.0\n" 17 | " w: 1.0\n" 18 | " }\n" 19 | "}\n" 20 | "" 21 | position { 22 | x: 0.0 23 | y: 0.0 24 | z: 0.0 25 | } 26 | rotation { 27 | x: 0.0 28 | y: 0.0 29 | z: 0.0 30 | w: 1.0 31 | } 32 | scale3 { 33 | x: 1.0 34 | y: 1.0 35 | z: 1.0 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/volume/volume.gui: -------------------------------------------------------------------------------- 1 | script: "/examples/volume/volume.gui_script" 2 | textures { 3 | name: "example" 4 | texture: "/examples/assets/example.atlas" 5 | } 6 | background_color { 7 | x: 0.0 8 | y: 0.0 9 | z: 0.0 10 | w: 0.0 11 | } 12 | nodes { 13 | position { 14 | x: 472.836 15 | y: 265.97 16 | z: 0.0 17 | w: 1.0 18 | } 19 | rotation { 20 | x: 0.0 21 | y: 0.0 22 | z: 0.0 23 | w: 1.0 24 | } 25 | scale { 26 | x: 1.0 27 | y: 1.0 28 | z: 1.0 29 | w: 1.0 30 | } 31 | size { 32 | x: 62.0 33 | y: 50.0 34 | z: 0.0 35 | w: 1.0 36 | } 37 | color { 38 | x: 1.0 39 | y: 1.0 40 | z: 1.0 41 | w: 1.0 42 | } 43 | type: TYPE_BOX 44 | blend_mode: BLEND_MODE_ALPHA 45 | texture: "example/volume_on" 46 | id: "volume" 47 | xanchor: XANCHOR_NONE 48 | yanchor: YANCHOR_NONE 49 | pivot: PIVOT_CENTER 50 | adjust_mode: ADJUST_MODE_FIT 51 | layer: "" 52 | inherit_alpha: true 53 | slice9 { 54 | x: 0.0 55 | y: 0.0 56 | z: 0.0 57 | w: 0.0 58 | } 59 | clipping_mode: CLIPPING_MODE_NONE 60 | clipping_visible: true 61 | clipping_inverted: false 62 | alpha: 1.0 63 | template_node_child: false 64 | size_mode: SIZE_MODE_AUTO 65 | } 66 | material: "/builtins/materials/gui.material" 67 | adjust_reference: ADJUST_REFERENCE_PARENT 68 | max_nodes: 512 69 | -------------------------------------------------------------------------------- /examples/volume/volume.gui_script: -------------------------------------------------------------------------------- 1 | local button = require "define.gui.button" 2 | local volume_checkbox_transitions = require "examples.volume.volume_checkbox_transitions" 3 | 4 | function init(self) 5 | self.volume = button.create("volume", volume_checkbox_transitions) 6 | 7 | msg.post(".", "acquire_input_focus") 8 | end 9 | 10 | function on_input(self, action_id, action) 11 | if button.on_input(self.volume, action_id, action) then 12 | self.muted = not self.muted 13 | button.set_state(self.volume, self.muted and hash("off") or hash("on")) 14 | sound.set_group_gain(hash("master"), self.muted and 0 or 1) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /examples/volume/volume_checkbox_transitions.lua: -------------------------------------------------------------------------------- 1 | local input_states = require("define.input_states") 2 | local input_transitions = require("define.gui.input_transitions") 3 | 4 | local COLOR_DARKEN = vmath.vector4(0.8) 5 | local COLOR_DEFAULT = vmath.vector4(1) 6 | 7 | return { 8 | input_transitions.create(input_states.STATE_IDLE, input_states.STATE_PRESSED, input_transitions.multi_parallell( 9 | input_transitions.shake(), 10 | input_transitions.to_color(COLOR_DARKEN) 11 | )), 12 | input_transitions.create(input_states.STATE_PRESSED, input_states.STATE_IDLE, input_transitions.multi_parallell( 13 | input_transitions.shake(), 14 | input_transitions.to_color(COLOR_DEFAULT) 15 | )), 16 | input_transitions.create(input_states.STATE_IDLE, input_states.STATE_HIDDEN, input_transitions.to_disabled()), 17 | input_transitions.create(input_states.STATE_HIDDEN, input_states.STATE_IDLE, input_transitions.to_enabled()), 18 | input_transitions.create(input_states.STATE_IDLE, input_states.STATE_HOVER, input_transitions.to_color(COLOR_DARKEN)), 19 | input_transitions.create(input_states.STATE_HOVER, input_states.STATE_IDLE, input_transitions.to_color(COLOR_DEFAULT)), 20 | input_transitions.create(input_states.STATE_IDLE, hash("on"), input_transitions.to_flipbook(hash("volume_on"))), 21 | input_transitions.create(input_states.STATE_IDLE, hash("off"), input_transitions.to_flipbook(hash("volume_off"))), 22 | } 23 | -------------------------------------------------------------------------------- /game.project: -------------------------------------------------------------------------------- 1 | [library] 2 | include_dirs = define 3 | 4 | [project] 5 | title = DeFine 6 | version = 0.1.0 7 | 8 | [bootstrap] 9 | main_collection = /examples/examples.collectionc 10 | 11 | [script] 12 | shared_state = 1 13 | 14 | [input] 15 | game_binding = /define/util/base.input_bindingc 16 | 17 | -------------------------------------------------------------------------------- /todo: -------------------------------------------------------------------------------- 1 | Change how new input_handlers are added to the input_manager 2 | * input_manager.add_handler(type : hash, handler : table) 3 | 4 | Create a shared "input_obj" which is the interface of all input components. 5 | * enable, disable 6 | * is_disabled -- could this be a meta table that if called returns boolean, if "set" changes the value?. == vs =. http://lua-users.org/wiki/ObjectProperties 7 | * show, hide 8 | * is_hidden 9 | * on_input 10 | * create(...) 11 | * on_state_changed(instance, callback(instance, from, to)) 12 | * on_text_changed -- example of component specific 13 | 14 | 15 | utf8 support 16 | --------------------------------------------------------------------------------