Giter VIP home page Giter VIP logo

gooey's Introduction

Build Status

Gooey

Gooey is a GUI system for the Defold game engine. It is inspired by the excellent Dirty Larry library.

Installation

You can use Gooey in your own project by adding this project as a Defold library dependency. Open your game.project file and in the dependencies field under project add:

https://github.com/britzl/gooey/archive/master.zip

Or point to the ZIP file of a specific release.

Themes

A few UI themes have been created for Gooey:

Usage

The Gooey system is encapsulated in a single Lua module without any visual components. It makes very little assumptions of the look and feel of the UI components it supports. Instead Gooey focuses on providing stable input and state handling and lets the user decide which states matter and how they should be presented visually.

Input bindings

For Gooey to work it requires a couple of input bindings:

  • Mouse trigger - mouse-button-1 -> touch
  • Mouse trigger - mouse-wheel-up -> scroll_up (for scrolling in lists)
  • Mouse trigger - mouse-wheel-down -> scroll_down (for scrolling in lists)
  • Key trigger - key-backspace -> backspace (for text input)
  • Text trigger - text -> text (for text input)
  • Text trigger - marked-text -> marked_text (for text input)

The input binding constants listed above are defined in gooey/actions.lua and can be changed.

IMPORTANT NOTE ON ANDROID: Make sure that the Input Method in the Android section of the game.project file is set to HiddenInputField. This will ensure that virtual keyboard input works properly.

Multi-touch

Gooey supports multi-touch for clickable components as long as the following input binding exists:

  • Touch trigger - multi-touch -> multitouch

Supported components

Gooey supports the following component types:

  • Button - gooey.button()
  • Checkbox - gooey.checkbox()
  • Radio button - gooey.radio()
  • Input text - gooey.input()
  • Lists (static and dynamic):
    • gooey.static_list() All list item nodes are already added to the list. Good for showing a small data set or when the list item nodes should vary in composition and looks.
    • gooey.dynamic_list() All list item nodes are created from the same template. The nodes are reused when scrolling. Good for showing a large data set.
  • Vertical scrollbar - gooey.scrollbar()

gooey.button(node_id, action_id, action, fn, refresh_fn)

Perform input and state handling for a button

PARAMETERS

  • node_id (string|hash) - Id of the node representing the clickable area, typically the background of the button
  • action_id (hash) - Action id as received from on_input()
  • action (table) - Action as received from on_input()
  • fn (function) - Function to call when the button is clicked/tapped on. A button is considered clicked/tapped if both a pressed and released action has been detected inside the bounds of the node. The function will get the same state table as described below passed as its first argument
  • refresh_fn (function) - Optional function to call when the state of the button has been updated. Use this to update the visual representation.

RETURN

  • button (table) - State data for the button based on current and previous input actions

The state table contains the following fields:

  • node (node) - The node itself
  • node_id (node_id) - Hashed id of the node
  • enabled (boolean) - true if the node is enabled
  • consumed (boolean) - true if the input was consumed
  • clicked (boolean) - true if the input is considered a click (ie pressed and released cycle)
  • over (boolean) - true if user action is inside the node
  • over_now (boolean) - true if user action moved inside the node this call
  • out_now (boolean) - true if user action moved outside the node this call
  • pressed (boolean) - true if the button is pressed
  • pressed_now (boolean) - true if the button was pressed this call
  • long_pressed (boolean) - true if the registered press was a long press or not
  • released_now (boolean) - true if the button was released this call

EXAMPLE

local gooey = require "gooey.gooey"

local function update_button(button)
	if button.pressed_now then
		gui.play_flipbook(button.node, hash("button_pressed"))
	elseif button.released_now then
		gui.play_flipbook(button.node, hash("button_normal"))
	elseif not button.pressed and button.over_now then
		gui.play_flipbook(button.node, hash("button_over"))
	elseif not button.pressed and button.out_now then
		gui.play_flipbook(button.node, hash("button_normal"))
	end
end

local function on_pressed(button)
	print("pressed")
end

function on_input(self, action_id, action)
	gooey.button("button/bg", action_id, action, on_pressed, update_button)
end

STATE

It is possible to set the state of a button:

update_button(gooey.button("button/bg").set_visible(false))

CONFIG

It is possible to configure the minimum time required to detect a long-press:

gooey.button("button/bg").set_long_pressed_time(time)

gooey.checkbox(node_id, action_id, action, fn, refresh_fn)

Perform input and state handling for a checkbox

PARAMETERS

  • node_id (string|hash) - Id of the node representing the clickable area
  • action_id (hash) - Action id as received from on_input()
  • action (table) - Action as received from on_input()
  • fn (function) - Function to call when the checkbox is checked/unchecked on. A checkbox is considered checked/unchecked if both a pressed and released action has been detected inside the bounds of the node. The function will get the same state table as described below passed as its first argument
  • refresh_fn (function) - Optional function to call when the state of the checkbox has been updated. Use this to update the visual representation.

RETURN

  • checkbox (table) - State data for the checkbox based on current and previous input actions

The state table contains the following fields:

  • node (node) - The node itself
  • node_id (node_id) - Hashed id of the node
  • enabled (boolean) - true if the node is enabled
  • consumed (boolean) - true if the input was consumed
  • clicked (boolean) - true if the input is considered a click (ie pressed and released cycle)
  • over (boolean) - true if user action is inside the node
  • over_now (boolean) - true if user action moved inside the node this call
  • out_now (boolean) - true if user action moved outside the node this call
  • checked (boolean) - The checkbox state (checked/unchecked)
  • pressed (boolean) - true if the checkbox is pressed (ie mouse/touch down but not yet released)
  • pressed_now (boolean) - true if the checkbox was pressed this call
  • long_pressed (boolean) - true if the registered press was a long press or not
  • released_now (boolean) - true if the checkbox was released this call
  • checked_now (boolean) - true if the checkbox was checked this call
  • unchecked_now (boolean) - true if the checkbox was unchecked this call

EXAMPLE

local gooey = require "gooey.gooey"

local function update_checkbox(checkbox)
	if checkbox.released_now then
		if checkbox.checked then
			gui.play_flipbook(checkbox.node, hash("checkbox_checked"))
		else
			gui.play_flipbook(checkbox.node, hash("checkbox_unchecked"))
		end
	elseif not checkbox.pressed and checkbox.over_now then
		gui.play_flipbook(checkbox.node, hash("checkbox_over"))
	elseif not checkbox.pressed and checkbox.out_now then
		gui.play_flipbook(checkbox.node, hash("checkbox_normal"))
	end
end

local function on_checked(checkbox)
	print("checked", checkbox.checked)
end

function on_input(self, action_id, action)
	gooey.checkbox("checkbox/bg", action_id, action, on_checked, update_checkbox)
end

STATE

It is possible to set the state of a checkbox. This is good for setting the initial state of the checkbox:

update_checkbox(gooey.checkbox("checkbox/bg").set_checked(true))
update_checkbox(gooey.checkbox("checkbox/bg").set_visible(false))

CONFIG

It is possible to configure the minimum time required to detect a long-press:

gooey.checkbox("checkbox/bg").set_long_pressed_time(time)

gooey.radio(node_id, group, action_id, action, fn, refresh_fn)

Perform input and state handling for a radio button

PARAMETERS

  • node_id (string|hash) - Id of the node representing the clickable area
  • group (string) - Id of the radio group this radio button belongs to (see example)
  • action_id (hash) - Action id as received from on_input()
  • action (table) - Action as received from on_input()
  • fn (function) - Function to call when the radio button is selected. A radio button is considered selected if both a pressed and released action has been detected inside the bounds of the node. The function will get the same state table as described below passed as its first argument
  • refresh_fn (function) - Optional function to call when the state of the radiobutton has been updated. Use this to update the visual representation.

RETURN

  • radio (table) - State data for the radio button based on current and previous input actions

The state table contains the following fields:

  • node (node) - The node itself
  • node_id (node_id) - Hashed id of the node
  • enabled (boolean) - true if the node is enabled
  • consumed (boolean) - true if the input was consumed
  • clicked (boolean) - true if the input is considered a click (ie pressed and released cycle)
  • over (boolean) - true if user action is inside the node
  • over_now (boolean) - true if user action moved inside the node this call
  • out_now (boolean) - true if user action moved outside the node this call
  • selected (boolean) - The radio button state
  • pressed (boolean) - true if the radio button is pressed (ie mouse/touch down but not yet released)
  • pressed_now (boolean) - true if the radio button was pressed this call
  • long_pressed (boolean) - true if the registered press was a long press or not
  • released_now (boolean) - true if the radio button was released this call
  • selected_now (boolean) - true if the radio button was selected this call
  • deselected_now (boolean) - true if the radio button was deselected this call

EXAMPLE

local gooey = require "gooey.gooey"

local function update_radio(radio)
	if radio.released_now then
		if radio.selected then
			gui.play_flipbook(radio.node, hash("radio_selected"))
		else
			gui.play_flipbook(radio.node, hash("radio_normal"))
		end
	elseif not radio.pressed and radio.over_now then
		gui.play_flipbook(radio.node, hash("radio_over"))
	elseif not radio.pressed and radio.out_now then
		gui.play_flipbook(radio.node, hash("radio_normal"))
	end
end

function on_input(self, action_id, action)
	gooey.radiogroup("MYGROUP", action_id, action, function(group_id, action_id, action)
		gooey.radio("radio1/bg", group_id, action_id, action, function(radio)
			print("selected 1", radio.selected)
		end, update_radio)
		gooey.radio("radio2/bg", group_id, action_id, action, function(radio)
			print("selected 2", radio.selected)
		end), update_radio)
	end)
end

STATE

It is possible to set the state of a radiobutton. This is good for setting the initial state of the radiobutton:

update_radio(gooey.radio("radio1/bg").set_selected(true))
update_radio(gooey.radio("radio1/bg").set_visible(false))

CONFIG

It is possible to configure the minimum time required to detect a long-press:

gooey.radio("radio1/bg").set_long_pressed_time(time)

gooey.static_list(list_id, stencil_id, item_ids, action_id, action, config, fn, refresh_fn, is_horizontal)

Perform input and state handling for a list of items where the list of nodes has already been created.

PARAMETERS

  • list_id (string) - Id of the template containing the list nodes.
  • stencil_id (string|hash) - Id of the stencil node that is used to clip the list. Touch events outside this area will be ignored when it comes to picking of list items.
  • item_ids (table) - Table with a list of list item ids (hash|string)
  • action_id (hash) - Action id as received from on_input()
  • action (table) - Action as received from on_input()
  • config (table) - Optional table with list configuration
  • fn (function) - Function to call when a list item is selected. A list item is considered selected if both a pressed and released action has been detected inside the bounds of the item. The function will get the same state table as described below passed as its first argument
  • refresh_fn (function) - Optional function to call when the state of the list has been updated. Use this to update the visual representation.
  • is_horizontal (bool) - Optional flag - if true, the list will be handled as horizontal, otherwise - as vertical.

The config table can contain the following values:

  • horizontal (boolean) - The table is in horizontal mode

RETURN

  • list (table) - State data for the list based on current and previous input actions

The list table contains the following fields:

  • id (string) - The list_id parameter above
  • enabled (boolean) - true if the node is enabled
  • consumed (boolean) - true if the input was consumed
  • items (table) - The list items as nodes. See below for table structure.
  • over (boolean) - true if user action is over any list item
  • over_item (table) - The list item the user action is over
  • over_item_now (table) - The list item the user action moved inside this call
  • out_item_now (table) - The list item the user action moved outside this call
  • selected_item (table) - Index of the selected list item
  • pressed_item (table) - Index of the pressed list item (ie mouse/touch down but not yet released)
  • pressed_item_now (table) - The list item the user action pressed this call
  • long_pressed (boolean) - true if the registered press was a long press or not
  • released_item_now (table) - The list item the user action released this call
  • scroll (vector3) - Scrolled amount from the top (only scroll.y is used). The scroll amount is in the range 0.0 (top) to 1.0 (bottom).
  • is_horizontal (bool) - Optional flag - if true, the list will be handled as horizontal, otherwise - as vertical.

The items table contains list items, each with the following fields:

  • root (node) - The root GUI node of the list item
  • nodes (table) - Node id to GUI node mappings (as returned from gui.clone_tree)
  • data (any) - The data associated with this list item
  • index (number) - Index of the list item

EXAMPLE

local gooey = require "gooey.gooey"

local function update_list(list)
	for i,item in ipairs(list.items) do
		if item == list.pressed_item then
			gui.play_flipbook(item.root, hash("item_pressed"))
		elseif item == list.selected_item then
			gui.play_flipbook(item.root, hash("item_selected"))
		else
			gui.play_flipbook(item.root, hash("item_normal"))
		end
	end
end

local function on_item_selected(list)
	print("selected", list.selected_item.index)
end

function on_input(self, action_id, action)
	gooey.static_list("list", "list/stencil", { "item1/bg", "item2/bg", "item3/bg", "item4/bg", "item5/bg" }, action_id, action, nil, on_item_selected, update_list)
end

STATE

It is possible to set the scroll amount of a list. This is useful when updating a list based on the movement of a scrollbar:

-- scroll 75% of the way
gooey.static_list("list", "list/stencil", { "item1/bg", "item2/bg", "item3/bg", "item4/bg", "item5/bg" }).scroll_to(0, 0.75)

CONFIG

It is possible to configure the minimum time required to detect a long-press:

gooey.static_list("list").set_long_pressed_time(time)

gooey.dynamic_list(list_id, root_id, stencil_id, item_id, data, action_id, action, config, fn, refresh_fn, is_horizontal)

Perform input and state handling for a list of items where list item nodes are created dynamically and reused. This is preferred for large data sets.

NOTE: The list does not support a stencil node with adjust mode set to gui.ADJUST_STRETCH. If the list size needs to change it has to be done using gui.set_size() on the stencil node.

PARAMETERS

  • list_id (string) - Id of the template containing the list nodes.
  • stencil_id (string|hash) - Id of the stencil node that is used to clip the list. Touch events outside this area will be ignored when it comes to picking of list items. The size of this area will decide how many list item nodes to create. The system will create enough to fill the area plus one more to support scrolling.
  • item_id (string|hash) - Id of the single list item that is to be cloned to present the list data.
  • data (table) - Data to associate with the list. This decides how far the list of possible to scroll.
  • action_id (hash) - Action id as received from on_input()
  • action (table) - Action as received from on_input()
  • config (table) - Optional table with list configuration
  • fn (function) - Function to call when a list item is selected. A list item is considered selected if both a pressed and released action has been detected inside the bounds of the item. The function will get the same state table as described below passed as its first argument. Return true in this function if the data has changed and the list perform an additional update of the visible nodes.
  • refresh_fn (function) - Optional function to call when the state of the list has been updated. Use this to update the visual representation.
  • is_horizontal (bool) - Optional flag - if true, the list will be handled as horizontal, otherwise - as vertical.

The config table can contain the following values:

  • horizontal (boolean) - The table is in horizontal mode
  • carousel (boolean) - The table is in carousel mode, wrapping around content at the ends

RETURN

  • list (table) - State data for the list based on current and previous input actions

The list table contains the following fields:

  • id (string) - The list_id parameter above
  • enabled (boolean) - true if the node is enabled
  • consumed (boolean) - true if the input was consumed
  • items (table) - The list items as nodes. See below for table structure.
  • over (boolean) - true if user action is over any list item
  • over_item (table) - The list item the user action is over
  • over_item_now (table) - The list item the user action moved inside this call
  • out_item_now (table) - The list item the user action moved outside this call
  • selected_item (table) - Index of the selected list item
  • pressed_item (table) - Index of the pressed list item (ie mouse/touch down but not yet released)
  • pressed_item_now (table) - The list item the user action pressed this call
  • long_pressed (boolean) - true if the registered press was a long press or not
  • released_item_now (table) - The list item the user action released this call
  • scroll (vector3) - Scrolled amount from the top (only scroll.y is used). The scroll amount is in the range 0.0 (top) to 1.0 (bottom).
  • is_horizontal (bool) - Optional flag - if true, the list will be handled as horizontal, otherwise - as vertical.

The items table contains list items, each with the following fields:

  • root (node) - The root GUI node of the list item
  • nodes (table) - Node id to GUI node mappings (as returned from gui.clone_tree)
  • data (any) - The data associated with this list item
  • index (number) - Index of the list item

EXAMPLE

local gooey = require "gooey.gooey"

local function update_list(list)
	for i,item in ipairs(list.items) do
		gui.set_text(item.nodes[hash("listitem/text")], item.data)
	end
end

local function on_item_selected(list)
	print("selected", list.selected_item.index)
end

function on_input(self, action_id, action)
	gooey.dynamic_list("list", "list/stencil", "listitem/bg", { "Mr. White", "Mr. Pink", "Mr. Green", "Mr. Blue", "Mr. Yellow" }, action_id, action, nil, on_item_selected, update_list)
end

STATE

It is possible to set the scroll amount of a list. This is useful when updating a list based on the movement of a scrollbar:

-- scroll 75% of the way
gooey.dynamic_list("list", "list/stencil", "listitem/bg", { "Mr. White", "Mr. Pink", "Mr. Green", "Mr. Blue", "Mr. Yellow" }).scroll_to(0, 0.75)

CONFIG

It is possible to configure the minimum time required to detect a long-press:

gooey.dynamic_list("list").set_long_pressed_time(time)

HORIZONTAL AND VERTICAL LISTS

It is possible to configure the list to be handled as either horizontal or vertical list by modifying is_horizontal flag in list. You can do this either by adding a flag in a dynamic_list() or static_list() call, modyfing the list.is_horizontal flag by yourself or use those convenience functions:

gooey.horizontal_dynamic_list(list_id, root_id, stencil_id, item_id, data, action_id, action, config, fn, refresh_fn)
gooey.vertical_dynamic_list(list_id, root_id, stencil_id, item_id, data, action_id, action, config, fn, refresh_fn)
gooey.horizontal_static_list(list_id, root_id, stencil_id, item_id, data, action_id, action, config, fn, refresh_fn)
gooey.vertical_static_list(list_id, root_id, stencil_id, item_id, data, action_id, action, config, fn, refresh_fn)

gooey.vertical_scrollbar(handle_id, bounds_id, action_id, action, fn, refresh_fn)

Perform input and state handling for a scrollbar (a handle that can be dragged/scrolled along a bar)

PARAMETERS

  • handle_id (string|hash) - Id of the node containing the handle that can be dragged to scroll.
  • bounds_id (string|hash) - Id of the node containing the actual bar that the handle should be dragged along. The size of this area will decide the vertical bounds of the handle.
  • action_id (hash) - Action id as received from on_input()
  • action (table) - Action as received from on_input()
  • fn (function) - Function to call when the scrollbar has been scrolled. The function will get the same state table as described below passed as its first argument
  • refresh_fn (function) - Optional function to call when the state of the list has been updated. Use this to update the visual representation.

RETURN

  • scrollbar (table) - State data for the scrollbar

The scrollbar table contains the following fields:

  • enabled (boolean) - true if the node is enabled
  • pressed (boolean) - true if the handle is pressed (ie mouse/touch down but not yet released)
  • pressed_now (boolean) - true if the handle was pressed this call
  • released_now (boolean) - true if the handle was released this call
  • over (boolean) - true if user action is inside the handle
  • over_now (boolean) - true if user action moved inside the handle this call
  • out_now (boolean) - true if user action moved outside the handle this call
  • clicked (boolean) - true if the input is considered a click (ie pressed and released cycle)
  • scroll (vector3) - Current scroll amount in each direction (0.0 to 1.0)

EXAMPLE

local gooey = require "gooey.gooey"

local function on_scrolled(scrollbar)
	print("scrolled", scrollbar.scroll.y)
end

function on_input(self, action_id, action)
	gooey.vertical_scrollbar("handle", "bounds", action_id, action, on_scrolled)
end

STATE

It is possible to set the scroll amount of a scrollbar. This is useful when updating a scrollbar that belongs to a list when the list was scrolled:

-- scroll 75% of the way
gooey.vertical_scrollbar("handle", "bounds").scroll_to(0, 0.75)

gooey.input(node_id, keyboard_type, action_id, action, config, refresh_fn)

Perform input and state handling for a text input field

PARAMETERS

  • node_id (string|hash) - Id of the text node
  • keyboard_type (number) - Keyboard type from gui.KEYBOARD_TYPE_*
  • action_id (hash) - Action id as received from on_input()
  • action (table) - Action as received from on_input()
  • config (table) - Optional configuration values.
  • refresh_fn (function) - Optional function to call when the state of the input field has been updated. Use this to update the visual representation.

The configuration table accepts the following values:

  • max_length (number) - Maximum length of entered text
  • empty_text (string) - Text to show if the text field is empty
  • allowed_characters (string) - Lua pattern to filter allowed characters (eg "[%a%d]" for alpha numeric)
  • use_marked_text (bool) - Flag to disable the usage of marked (non-committed) text, defaults to true

RETURN

  • input (table) - State data for the input field based on current and previous input actions

The state table contains the following fields:

  • node (node) - The node itself
  • node_id (node_id) - Hashed id of the node
  • enabled (boolean) - true if the node is enabled
  • consumed (boolean) - true if the input was consumed
  • over (boolean) - true if user action is inside the node
  • over_now (boolean) - true if user action moved inside the node this call
  • out_now (boolean) - true if user action moved outside the node this call
  • selected (boolean) - true if the text field is selected
  • pressed (boolean) - true if the text field is pressed (ie mouse/touch down but not yet released)
  • pressed_now (boolean) - true if the text field was pressed this call
  • long_pressed (boolean) - true if the registered press was a long press or not
  • released_now (boolean) - true if the text field was released this call
  • selected_now (boolean) - true if the text field was selected this call
  • deselected_now (boolean) - true if the text field was deselected this call
  • text (string) - The text in the field
  • marked_text (string) - The marked (non-committed) text
  • keyboard_type (number)
  • masked_text (string) - If the keyboard type is gui.KEYBOARD_TYPE_PASSWORD then this string represents a masked version of the text
  • masked_marked_text (string) - If the keyboard type is gui.KEYBOARD_TYPE_PASSWORD then this string represents a masked version of the marked text
  • text_width (number) - The width of the text
  • marked_text_width (number) - The width of the marked text
  • total_width (number) - The total width of the text including marked text

EXAMPLE

local gooey = require "gooey.gooey"

local function update_input(input)
	if input.selected_now then
		gui.play_flipbook(input.node, hash("input_selected"))
	elseif input.deselected_now then
		gui.play_flipbook(input.node, hash("input_normal"))
	end
end

function on_input(self, action_id, action)
	gooey.input("input/text", gui.KEYBOARD_TYPE_DEFAULT, action_id, action, nil, update_input)
end

STATE

It is possible to set the state of an input node:

update_input(gooey.input("input/text", gui.KEYBOARD_TYPE_DEFAULT).set_visible(false))
update_input(gooey.input("input/text", gui.KEYBOARD_TYPE_DEFAULT).set_text("foobar"))

CONFIG

It is possible to configure the minimum time required to detect a long-press:

gooey.input("input/text").set_long_pressed_time(time)

Consuming input

Each Gooey component has a consumed variable in its state table. Consuming input in a gui_script to prevent input propagation can be done by checking the consumed state of each component. If any component has consumed set to true it is safe to return true from the on_input() function to prevent input propagation. It is also possible to wrap all component interaction in an input group and check the consumed state for the entire group:

local gooey = require "gooey.gooey"

function on_input(self, action_id, action)
    local group = gooey.group("group1", function()
        gooey.button("button1/bg", action_id, action, on_pressed, function(button) end)
        gooey.button("button2/bg", action_id, action, on_pressed, function(button) end)
        gooey.radio("radio1/bg", "MYGROUP", action_id, action, function(radio) end, update_radio)
        ...
        -- and so on
    end)
    return group.consumed
end

Example app

See the example app for examples of how to use Gooey on its own and how to use the themes.

Try the HTML5 version of the example app.

gooey's People

Contributors

agulev avatar britzl avatar jerakin avatar klaytonkowalski avatar nicloay avatar pabaucom avatar paweljarosz avatar sahbum avatar unindented avatar wistpotion avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gooey's Issues

Trying to erase none utf8 characters throw errors

Easily reproduced with a emoji such as 😄 while typing on a phone.

@britzl How do you think we should handle this?

ERROR:SCRIPT: /gooey/internal/utf8.lua:128: Invalid UTF-8 character
stack traceback:
	[C]: in function 'error'
	/gooey/internal/utf8.lua:128: in function 'utf8charbytes'
	/gooey/internal/utf8.lua:190: in function 'utf8len'
	/gooey/internal/utf8.lua:207: in function 'sub'
	/gooey/internal/input.lua:168: in function 'input'
	/gooey/gooey.lua:162: in function 'input'
	/screens/change_pokemon/edit/edit.gui_script:150: in function </screens/change_pokemon/edit/edit.gui_script:146>

No typing input from Android browsers

I'm not getting any keyboard showing up when clicking the text input fields on the Gooey demo.
If I go to the home screen and back to the browser with the text field already selected, the keyboard will pop up but the text field doesn't respond to any typing. Could it be that some android update has broken something?

Device: Galaxy S10E
OS: Android 11
Browser: Chrome and Firefox
Keyboard: Samsung default and Microsoft Swiftkey

.nodes failing on list items in dynamic_list

Have a new interesting error, i use a gooey dynamic_list with x number of item rows in it, i want to find out on what element inside one list item i have clicked on, for example a delete button, so i use

if gui.pick_node(list.items[list.selected_item].nodes[hash(“giftitem/delete_btn”)], action.x, action.y)

inside the gooey dynamic_list function, and it works fine for item 1 to 9, but number 10 and above fails, and it looks like it is the .nodes that doesnt work when i debug printed out them like you se in the picture here

i made an example based on the gooey example project, and maybe the errors are coming as soon as i scroll the list and try to click on an scene object in one list item. So in the project i include it looks like it start failing on item nr 6

gooey-list_error.zip

How can I change the theme ?

Hi,

I have a working project using gooey GUI with a single button.

I would like to use the gooey-dirtylarry theme for example. I added the following dependencies in my game.project file :

https://github.com/britzl/gooey/archive/master.zip 
https://github.com/britzl/gooey-dirtylarry/archive/main.zip

So, if I replaced the line :

local gooey = require "gooey.gooey"

by this line :

local gooey = require "gooey.themes.dirtylarry.dirtylarry"

(because my assumption is : this is the same interface for acquire_input and button functions between the genuine gooey interface and the dirtylarry gooey theme interface...)

when I execute my code, I got this error :

/example/menu.gui_script
	The file '/gooey/themes/dirtylarry/dirtylarry.lua' could not be found.

What is my mistake ?
Thanks for your help...

Non-native feel on backspace on inputs, Android

My Galaxy S7 is on Android 7.0.
Trying the example project in the repo, using kenneyblue, and testing the input.

When focusing on the input text, my phone's keyboard pops up. Writing text works fine. When pressing the backspace button on the keyboard, I have to hold it down to make it remove characters. A single tap on backspace does not register "long enough" for it to remove any text.

Reproduce:

  • Android phone
  • Focus on text field, write something
  • Tap backspace button on keyboard

Expected:

  • Single character removed

Actual:

  • Nothing removed (unless continuously holding down button)

Scroll list flick

Implement the ability to "flick" scroll list. After a fast swipe the list should keep its momentum

Example for eloquently handling input consumption

I'm looking at the examples, kenneyblue to be specific.
on_input contains all the theme specific updates for the buttons, checkboxes etc.
Looking at the theme's file's specific fucntions, the M.button returns the gooey.button call.

I'm adopting this style into a theme of my own, where I have a custom type of buttons. When pressing on (pressed_now == true) and fully qualified 'clicks' I want to consume the input.
Reason being that I want interaction on my buttons to take priority and not allow my game area to react to that input. Only on 'accidental' interactions, like releasing on top of the button but not having done the initial press - a non fully qualified click so to say.

My initial thought was to - inside the gui_scripts on_input- save a boolean result for all calls to the theme button function. Then check if any of those were true to know whether or not input should be consumed or not.

At this point I'm thinking I might be doing things wrong, it seems a bit unwieldy. Is there a recommended way for handling input consumption with gooey?

Missing return checkbox instance in set_checked or set_visible

From example:

STATE

It is possible to set the state of a checkbox. This is good for setting the initial state of the checkbox:

update_checkbox(gooey.checkbox("checkbox/bg").set_checked(true))
update_checkbox(gooey.checkbox("checkbox/bg").set_visible(false))

but is not available right now. We should return instance to make it work.

It seems we should modify core.instance method:

for name,fn in pairs(functions or {}) do
	data[name] = function(...)
		fn(data, ...)
		+++
		return data
		+++
	end
end

Softcrash when using the gooey.vertical_scrollbar with gooey.dynamic_list

Happens when you do gooey.dynamic_list(...) before gooey.vertical_scrollbar(...) in on_input(..) or init(...) (as if you use update_list(gooey.dynamic_list(...)) on init(...))

dynamic_list will call update_list before gooey.vertical_scrollbar(...) so action will always have been nil.

Workaround is to pass the expected variables in init(...) before update_list like gooey.vertical_scrollbar("scrollbar/handle", "scrollbar/bar", nil, {x=0, y=0})

Bug: text:gmatch instead of text:gfind?

Hi Britzl,

it seems, there is a bug in Gooey:

In master/gooey/internal/input.lua, line 37, you use:

function M.utf8_gfind(text)
return text:gfind("([%z\1-\127\194-\244][\128-\191]*)")
end

That seems to be deprecated and causes an error.
I assume, it should be replaced by:

function M.utf8_gfind(text)
return text:gmatch("([%z\1-\127\194-\244][\128-\191]*)")
end

Regards,
Lutz

Bug: If dynamic list becomes empty, items have invalid data attached

Because of this early bail:

	-- bail early if the list is empty
	if list.data_size == 0 then
		if refresh_fn then refresh_fn(list) end
		return list
	end

...

	update_dynamic_listitem_data(list)

When the list had items but then became empty, item.data for items that previously had data stays what it was before.

Themes: defold-dark, gooey

Making a note here for a todo for me to make these themes. defold-dark replicate the general look of editor 2's colors and style, gooey to be a theme which looks appropriate fit in theme for the gooey logo.

Not detecting a reload in Defold 1.2.151

I face to problem that it could not detect a reload in core.instance function.
You can reproduce the problem by scene reloading in example project.

I think it is because __dm_script_instance__ has been removed in Defold 1.2.151.
ref: Defold Forum

-- detect a reload (unload and load cycle) and start with an
-- empty instance
-- if the script instance has changed then we're certain that
-- it's reloaded
local script_instance = _G.__dm_script_instance__
if instance and instance.__script ~= script_instance then
instances[key] = nil
end

Currently, I added temporary reset function in my local and call this for detecting a reload of gui script.

dynamic list reports list.scroll as 0 on init

The dynamic list component is initialized with an empty vector3 and only updated on scrolling. Using the dynamic list scroll value to update a scrollbar makes it be "bottomed out".

Text limit for input box

Limit number of characters for input boxes.

At function M.input(node_id, keyboard_type, action_id, action) add additional optional parameter like

function M.input(node_id, keyboard_type, action_id, action, max_text_width)

and then in code just check if this parameter is set, don't add new characters to string

List data

Hej! Is dynamic list data dynamic? Can I add items to the list dynamically? Or do I need to set the number of list elements once and then I cannot change it? :)

Missed backspaces on iOS

On iOS devices I get missed backspace presses in input fields, as the ones on the examples.

If I type "123" using the on-screen keyboard then click backspace two times the resulting text can be either "1", "12" or "123".

Devices: iPad 2018 and iPhone 8
OS: iOS 14.4
Browsers: Safari and Chrome

Input widget's "deselected_now" property always false

local function input_callback(input)
	if input.pressed_now then
		gui.set_color(input.node, dcolors.palette.c2)
	elseif input.deselected_now then
		gui.set_color(input.node, dcolors.palette.c3)
	end
end

Input widget's input.deselected_now property is always false. Tested by selecting the widget, entering text, then deselecting the widget. Input bindings match Gooey's expectations correctly.

On line 136 of /gooey/internal/input.lua, local deselected_now = false is never referenced again, so it is never changed to true.

Lists are not updated on list refresh since 10.0.0

Hello!

So in 9.1.4 everything work as expected, but from 10.0.0 it looks like the dynamic lists are not updating list elements according to refresh function. First observation is that list.pressed_now / over_item_now / over_item are always false, regardless if I really point at the node. Tested on examples provided with Gooey. I see there were some breaking changes in 10.0.0, but can't correlate quickly as of now.

General UI wishlist

Here's a wishlist I made for another UI project but some may be useful for this one too.

  • need to disable hover / input on other UI while touch click is held down start on another - take/releas focus
  • holding down input while moving around over a button should not make its image state change like it currently does
  • color picker
  • circular progress bar
  • alerts
  • modal popup
  • knobs
  • progress bar
  • option list
  • rgb slider
  • infinite loop scroller
  • toolbar
  • tab bar
  • tooltips
  • scrollbars
  • audio volume slider
  • notched slider (snap to notches)
  • momentum / friction / drag and click threshold for scrollers
  • accelerated backspace not relying on game.project input repeat?
  • clicking into text boxes to change pointer
  • selecting areas of text box with click drag or holding shift plus arrow keys
  • panels within panels

Input text issues

Reported by forum user Febuse:

There are a few bugs in the gooey kenney implementation: go in kenney mode, write some text, select something in the list and a radio button, mark the checkbox, hit back, enter kenney mode again, it looks like it was unchanged, move mouse, everything reverts to how you left it. The textfield also seems to only trigger from the middle and on mobile browser it needs 2 clicks on the textfield: one to make blinking cursor and one to show the keyboard. After that the keyboard needs to be manually closed even if the focus would no longer be on the textfield (click button).

Dynamic lists shows odd behaviour when screen have been resized

The bottom of the scroll list looks like this when not resizing
Screen Shot 2019-05-15 at 11 36 30

But if I resize the screen with defos.set_window_size(0, 0, 570, 1224) (from 720, 1280) it looks like this instead
Screen Shot 2019-05-15 at 11 36 05

Quite a lot of padding is added and a extra item can be seen. The data is nil (but it get's set even though I check for nil before doing gui.set_text().

The stencil node have Adjustment Mode: Stretch and Y Anchor: Top

Add function to set text on text node

Setting the text using gui.set_text() will be replaced the next time the input field is updated. Perhaps detect when gui.set_text() has been done?

Input state machine doesn't include 'released_now' or 'clickable' fields on last frame

I just started playing around with this library recently (using release 7.5.2), am currently trying to incorporate a basic text input node in my project (on android). For some reason though, on the final touch frame, the 'released_now' and 'clickable' fields that would indicate a completed click do not show up.

Here's the relevant portion of console feedback: (pprinting the input table each frame)

DEBUG:SCRIPT:
{ --[[0xad34a090]]
pressed_now = false,
node = @(0, -24, 0),
long_pressed = true,
current_text = "",
set_visible = function: 0xad34a180,
refresh_fn = function: 0xaed03a10,
marked_text = "",
out_now = false,
total_width = 0,
set_long_pressed_time = function: 0xaed057d0,
text_width = 0,
long_pressed_time = 1.5,
marked_text_width = 0,
clicked = false,
enabled = true,
set_text = function: 0xad34a100,
text = "",
consumed = false,
refresh = function: 0xad34a140,
over = false,
empty = true,
deselected_now = false,
over_now = false,
keyboard_type = 0,
released_now = false
}
DEBUG:SCRIPT:
{ --[[0xad34a090]]
pressed_now = false,
node = @(0, -24, 0),
long_pressed = true,
current_text = "",
set_visible = function: 0xad34a180,
refresh_fn = function: 0xaed03a10,
marked_text = "",
out_now = false,
total_width = 0,
set_long_pressed_time = function: 0xaed057d0,
text_width = 0,
long_pressed_time = 1.5,
marked_text_width = 0,
clicked = false,
enabled = true,
set_text = function: 0xad34a100,
text = "",
consumed = false,
refresh = function: 0xad34a140,
over = false,
empty = true,
deselected_now = false,
over_now = false,
keyboard_type = 0,
released_now = false
}
DEBUG:SCRIPT:
{ --[[0xad34a090]]
pressed_now = false,
node = @(0, -24, 0),
long_pressed = true,
current_text = "",
set_visible = function: 0xad34a180,
refresh_fn = function: 0xaed03a10,
marked_text = "",
out_now = false,
total_width = 0,
set_long_pressed_time = function: 0xaed057d0,
text_width = 0,
long_pressed_time = 1.5,
marked_text_width = 0,
enabled = true,
set_text = function: 0xad34a100,
text = "",
refresh = function: 0xad34a140,
over = false,
empty = true,
deselected_now = false,
over_now = false,
keyboard_type = 0
}

I'm kind of surprised nobody else has had this issue, as it's a pretty big roadblock for me. It's possible I'm doing something wrong but I don't know what that would be. Can anyone else replicate this issue?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.