Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added focus and navigation #83

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
722 changes: 384 additions & 338 deletions README.md

Large diffs are not rendered by default.

396 changes: 261 additions & 135 deletions example/components.gui

Large diffs are not rendered by default.

37 changes: 23 additions & 14 deletions example/components.gui_script
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ local function update_checkbox(checkbox)
end

local function update_button(button)
if button.over_now then
if button.pressed_now then
utils.shake(button.node, vmath.vector3(1))
elseif button.over_now then
gui.set_color(button.node, COLOR_LIGHTGREY)
elseif button.out_now then
gui.set_color(button.node, COLOR_WHITE)
elseif button.pressed_now then
utils.shake(button.node, vmath.vector3(1))
end
end

Expand Down Expand Up @@ -64,7 +64,11 @@ function init(self)
end

function on_input(self, action_id, action)
local group = gooey.group("group1", function()
local group = gooey.group("components", action_id, action, function()
gooey.button("back", action_id, action, function(button)
msg.post("controller:/go", "show_menu")
end, update_button)

gooey.button("button", action_id, action, function(button)
if button.long_pressed then
print("Button was long pressed")
Expand All @@ -73,9 +77,12 @@ function on_input(self, action_id, action)
end
end, update_button)

gooey.button("back", action_id, action, function(button)
msg.post("controller:/go", "show_menu")
end, update_button)
gooey.input("input_text", gui.KEYBOARD_TYPE_DEFAULT, action_id, action, { empty_text = "EMPTY, MAX 8 CHARS", max_length = 8 }, update_input)
gooey.input("input_alphanumeric", gui.KEYBOARD_TYPE_DEFAULT, action_id, action, { empty_text = "ALPHA NUMERIC CHARS", allowed_characters = "[%a%d%s]", use_marked_text = false}, update_input)

gooey.dynamic_list("dynamiclist", "dynamiclist_bounds", "listitem", self.list_data, action_id, action, nil, function(list)
print("selected item", list.selected_item.index, list.data[list.selected_item.index])
end, update_list)

gooey.checkbox("checkbox", action_id, action, function(checkbox)
print("checkbox", checkbox.checked)
Expand All @@ -92,13 +99,15 @@ function on_input(self, action_id, action)
print("radio 3", radio.selected)
end, update_radiobutton)
end)

gooey.input("input_text", gui.KEYBOARD_TYPE_DEFAULT, action_id, action, { empty_text = "EMPTY, MAX 8 CHARS", max_length = 8 }, update_input)
gooey.input("input_alphanumeric", gui.KEYBOARD_TYPE_DEFAULT, action_id, action, { empty_text = "ALPHA NUMERIC CHARS", allowed_characters = "[%a%d%s]", use_marked_text = false}, update_input)

gooey.dynamic_list("dynamiclist", "dynamiclist_bounds", "listitem", self.list_data, action_id, action, nil, function(list)
print("selected item", list.selected_item.index, list.data[list.selected_item.index])
end, update_list)
end)

if group.focus.component then
local focus_indicator = gui.get_node("focus")
local focus_node = gui.get_node(group.focus.component.id)
local pos = gui.get_position(focus_node)
local size = gui.get_size(focus_node)
pos.x = pos.x - size.x / 2 - 10
gui.set_position(focus_indicator, pos)
end
return group.consumed
end
2 changes: 1 addition & 1 deletion example/menu.gui_script
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ function init(self)
end

function on_input(self, action_id, action)
local group = gooey.group("group1", function()
local group = gooey.group("group1", action_id, action, function()
gooey.button("components", action_id, action, function()
msg.post("controller:/go", "show_components")
end)
Expand Down
3 changes: 3 additions & 0 deletions gooey/actions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ M.SCROLL_DOWN = hash("scroll_down")
M.TEXT = hash("text")
M.MARKED_TEXT = hash("marked_text")
M.BACKSPACE = hash("backspace")
M.NEXT = hash("next")
M.PREVIOUS = hash("previous")
M.SELECT = hash("select")

return M
108 changes: 102 additions & 6 deletions gooey/gooey.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ local radio = require "gooey.internal.radio"
local list = require "gooey.internal.list"
local input = require "gooey.internal.input"
local scrollbar = require "gooey.internal.scrollbar"
local actions = require "gooey.actions"

local M = {}

Expand Down Expand Up @@ -35,7 +36,7 @@ end

function M.create_theme()
local theme = {}

theme.is_enabled = function(component)
if component.node then
return M.is_enabled(component.node)
Expand Down Expand Up @@ -187,28 +188,123 @@ end
-- @param id
-- @param fn Interact with gooey components inside this function
-- @return Group state
function M.group(id, fn)
function M.group(id, action_id, action, fn)
assert(id, "You must provide a group id")
assert(fn, "You must provide a group function")
groups[id] = groups[id] or { consumed = false, components = {} }
if not groups[id] then
groups[id] = {
consumed = false, -- true if a component in the group consumed input
components = {}, -- list of all components in the group
focus = {
component = nil, -- component with focus
index = nil, -- index of component with focus
},
}
end
local group = groups[id]
local components = group.components
local focus = group.focus

-- clear list of components
for i=1,#components do
components[i] = nil
end

-- set current group and call the group function
-- then reset current group again once we're done
current_group = group
fn()
current_group = nil

-- exit early if there are no components in the group
if #components == 0 then
focus.component = nil
focus.index = nil
return group
end

-- go through the components in the group and check if
-- any of them consumed input
local components = group.components
-- also check which component has focus and if another
-- component was selected and should gain focus
local consumed = false
local current_focus_index = nil
local new_focus_index = nil
for i=1,#components do
consumed = components[i].consumed or consumed
components[i] = nil
local component = components[i]
consumed = component.consumed or consumed
if component.focus then
current_focus_index = i
elseif component.released_now or component.released_item_now then
new_focus_index = i
end
end

if not new_focus_index then
new_focus_index = current_focus_index
end

-- assign focus to the next component or first if
-- no component currently has focus
if not consumed then
if action_id == actions.NEXT then
if action.pressed then
if new_focus_index then
new_focus_index = new_focus_index + 1
if new_focus_index > #components then
new_focus_index = 1
end
else
new_focus_index = 1
end
end
consumed = true
elseif action_id == actions.PREVIOUS then
if action.pressed then
if new_focus_index then
new_focus_index = new_focus_index - 1
if new_focus_index == 0 then
new_focus_index = #components
end
else
new_focus_index = 1
end
end
consumed = true
end
end

-- changing focus
if current_focus_index ~= new_focus_index then
if current_focus_index then
local component = components[current_focus_index]
component.focus = false
component.refresh()
focus.index = nil
focus.component = nil
end
if new_focus_index then
local component = components[new_focus_index]
component.focus = true
component.refresh()
focus.index = new_focus_index
focus.component = component
end
end
group.consumed = consumed
return group
end

function M.set_focus(group, index)
assert(group)
assert(index)
local component = group.components[index]
if component then
component.focus = true
component.refresh()
group.focus.index = index
group.focus.component = component
end
end

return M
7 changes: 6 additions & 1 deletion gooey/internal/core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ local function handle_action(component, action_id, action)
component.long_pressed_time = component.long_pressed_time or 1.5
if not component.touch_id or component.touch_id == action.id then
local over = gui.pick_node(component.node, action.x, action.y)
if component.focus and action_id == actions.SELECT then
over = true
end
component.over_now = over and not component.over
component.out_now = not over and component.over
component.over = over

local touch = action_id == actions.TOUCH or action_id == actions.MULTITOUCH
local touch = action_id == actions.TOUCH or action_id == actions.MULTITOUCH or action_id == actions.SELECT
local pressed = touch and action.pressed and component.over
local released = touch and action.released
if pressed then
Expand Down Expand Up @@ -111,6 +114,7 @@ end
-- @return instance Instance data for the node (public data)
-- @return state Internal state of the node (private data)
function M.instance(id, instances, functions)
id = M.to_hash(id)
local key = M.to_key(id)
local instance = instances[key]
-- detect a reload (unload and load cycle) and start with an
Expand All @@ -128,6 +132,7 @@ function M.instance(id, instances, functions)
instances[key] = instances[key] or { __script = script_instance }
if not instances[key].data then
local data = {}
data.id = id
instances[key].data = data
for name,fn in pairs(functions or {}) do
data[name] = function(...)
Expand Down
5 changes: 4 additions & 1 deletion gooey/internal/input.lua
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,10 @@ function M.input(node_id, keyboard_type, action_id, action, config, refresh_fn)
input.marked_text = ""
gui.reset_keyboard()
gui.show_keyboard(keyboard_type, true)
elseif input.selected and action.pressed and action_id == actions.TOUCH and not input.over then
elseif input.selected
and action.pressed
and (action_id == actions.TOUCH or action_id == actions.SELECT or action_id == actions.NEXT)
and not input.over then
input.selected = false
input.deselected_now = true
input.text = input.text .. (not use_marked_text and input.marked_text or "")
Expand Down
5 changes: 2 additions & 3 deletions gooey/internal/list.lua
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,8 @@ end

-- get a list instance and set up some basics of a list on the instance
local function get_instance(list_id, stencil_id, refresh_fn, lists)
stencil_id = core.to_hash(stencil_id)
local list = core.instance(stencil_id, lists, LIST)
list.id = list_id
list_id = core.to_hash(list_id)
local list = core.instance(list_id, lists, LIST)
list.scroll = list.scroll or vmath.vector3()
list.stencil = list.stencil or gui.get_node(stencil_id)
list.stencil_size = list.stencil_size or gui.get_size(list.stencil)
Expand Down
16 changes: 16 additions & 0 deletions input/game.input_binding
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@ key_trigger {
input: KEY_BACKSPACE
action: "backspace"
}
key_trigger {
input: KEY_TAB
action: "next"
}
key_trigger {
input: KEY_SPACE
action: "select"
}
key_trigger {
input: KEY_UP
action: "previous"
}
key_trigger {
input: KEY_DOWN
action: "next"
}
mouse_trigger {
input: MOUSE_BUTTON_1
action: "touch"
Expand Down