{"id":24395569,"url":"https://github.com/immortalx74/lovr-ui","last_synced_at":"2025-04-11T15:34:42.006Z","repository":{"id":156044672,"uuid":"542866515","full_name":"immortalx74/lovr-ui","owner":"immortalx74","description":"An immediate mode VR GUI library for LÖVR","archived":false,"fork":false,"pushed_at":"2024-12-18T09:28:09.000Z","size":515,"stargazers_count":39,"open_issues_count":0,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-25T11:49:15.156Z","etag":null,"topics":["gui","immediate-mode","lovr","virtual-reality","vr"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/immortalx74.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.MD","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-09-29T01:37:09.000Z","updated_at":"2025-02-25T22:27:33.000Z","dependencies_parsed_at":"2023-11-25T06:22:06.342Z","dependency_job_id":"7992bae4-b71a-4845-8970-d60ba8a4196b","html_url":"https://github.com/immortalx74/lovr-ui","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/immortalx74%2Flovr-ui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/immortalx74%2Flovr-ui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/immortalx74%2Flovr-ui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/immortalx74%2Flovr-ui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/immortalx74","download_url":"https://codeload.github.com/immortalx74/lovr-ui/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248432059,"owners_count":21102308,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gui","immediate-mode","lovr","virtual-reality","vr"],"created_at":"2025-01-19T20:58:44.582Z","updated_at":"2025-04-11T15:34:41.986Z","avatar_url":"https://github.com/immortalx74.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"## lovr-ui\n### An immediate mode VR GUI library for [LÖVR](https://lovr.org/)\n( For a retained mode UI library check out [chui](https://github.com/jmiskovic/chui) )\n![lovr-ui](https://i.imgur.com/Q5SHm3H.png)\n**How to use:**\n - Put the ui folder inside your project and require it: `UI = require \"ui/ui\"`\n - Initialize the library by calling `UI.Init()` on `lovr.load()`\n - Handle controller input by calling `UI.InputInfo()` on `lovr.update()`\n - Everything inside `NewFrame()`/`RenderFrame()` is your GUI\n\n**Input:**\n\n - Change the dominant hand by pushing the corresponding trigger button.\n - Scroll in ListBox with the Y-axis of the analog stick.\n - Text entry is done by an on-screen keyboard (appears when a TextBox has focus).\n - If additional keyboard languages are provided, cycling between them is done by holding grip and pressing the trigger on the space key.\n - Enter an exact value in a slider by holding down grip and pressing trigger.\n - Move windows by pointing at them and holding the grip button.\n - Enable/Disable interaction with the GUI by pressing the Left Thumbstick down (user configurable).\n\n**Widgets:**\n\n - Button\n - ImageButton\n - TextBox\n - ListBox\n - SliderInt\n - SliderFloat\n - Label\n - CheckBox\n - RadioButton\n - TabBar\n - Dummy\n - ProgressBar\n - WhiteBoard\n - Modal window\n - Separator\n\n---\n`UI.Button(text, width, height)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`text`|string|button's text\n|`width` _[opt]_|number|button width in pixels\n|`height` _[opt]_|number|button height in pixels\n \n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `boolean`, true when clicked.  \nNOTE:  if no `width` and/or `height` are provided, the button size will be auto-calculated based on text. Otherwise, it will be set to `width` X `height` (with the text centered) or ignored if that size doesn't fit the text. \n\n---\n`UI.ImageButton(img_filename, width, height, text)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`img_filename`|string|image filename\n|`width`|number|image width in pixels\n|`height`|number|image height in pixels\n|`text` _[opt]_|string|optional text\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `boolean` , true when clicked.  \n\n---\n`UI.WhiteBoard(name, width, height)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`name`|string|whiteboard ID\n|`width`|number|width in pixels\n|`height`|number|height in pixels\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `Pass`, `boolean`, `boolean`, `boolean`, `boolean`, `number`, `number`, [1] Pass object, [2] clicked, [3] down, [4] released, [5] hovered, [6] X, [7] Y  \nNOTE: General purpose widget for custom drawing/interaction. The returned Pass can be used to do regular LÖVR draw-commands  \nlike plane, circle, text, etc. X and Y are the local 2D coordinates of the pointer (0,0 is top,left)\n\n---\n`UI.TextBox(name, num_visible_chars, buffer)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`name`|string|textbox ID\n|`num_visible_chars`|number|number of visible characters\n|`buffer`|string|user provided text buffer\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `boolean`, `boolean`, `number`, `string` , [1] got focus, [2] buffer changed, [3] ID, [4] modified buffer.  \nNOTE: When clicked, an on-screen keyboard will pop-up for text entry. Enter closes the keyboard. To modify the original buffer assign the 4th return value back to the original buffer variable. The ID returned can be passed in the helper function `UI.SetTextBoxText` to set the desired text after validation. (example in main.lua) \n\n---\n`UI.ListBox(name, num_visible_rows, num_visible_chars, collection, selected)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`name`|string|listbox ID\n|`num_visible_rows`|number|number of visible rows\n|`num_visible_chars`|number|number of visible characters on each row\n|`collection`|table|table of strings\n|`selected` _[opt]_|number or string|selected item index (in case it's a string, selects the 1st occurence of the item that matches the string)\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `boolean`, `number`, [1] true when clicked, [2] the selected item index  \n\n---\n`UI.SliderInt(text, v, v_min, v_max, width)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`text`|string|slider text\n|`v`|number|initial value\n|`v_min`|number|minimum value\n|`v_max`|number|maximum value\n|`width` _[opt]_|number|total width in pixels of the slider, including it's text\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `boolean`, `number`, [1] true when released, [2] the current value  \nNOTE: Use this idiom to assign back to the provided number variable: `slider_released, slider_val = UI.SliderInt(\"My slider\", slider_val, 0, 100)`\nIf width is provided, it will be taken into account only if it exceeds the width of text, otherwise it will be ignored. \n\n---\n`UI.SliderFloat(text, v, v_min, v_max, width, num_decimals)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`text`|string|slider text\n|`v`|number|initial value\n|`v_min`|number|minimum value\n|`v_max`|number|maximum value\n|`width` _[opt]_|number|total width in pixels of the slider, including it's text\n|`num_decimals` _[opt]_|number|number of decimals to display\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `boolean`, `number`, [1] true when released, [2] the current value  \nNOTE: Use this idiom to assign back to the provided number variable: `slider_released, slider_val = UI.SliderFloat(\"My slider\", slider_val, 0, 100)`\nIf `width` is provided, it will be taken into account only if it exceeds the width of text, otherwise it will be ignored. If no `num_decimals` is provided, it defaults to 2.\n\n---\n`UI.Label(text)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`text`|string|label text\n|`compact` _[opt]_|boolean|ignore vertical margins\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \n\n---\n`UI.ProgressBar(progress, width)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`progress`|number|progress percentage\n|`width` _[opt]_|number|width in pixels\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Default width is 300 pixels\n\n---\n`UI.Separator()`\n|Argument|Type|Description\n|:---|:---|:---|\n|`none`||\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Horizontal Separator\n\n---\n`UI.CheckBox(text, checked)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`text`|string|checkbox text\n|`checked`|boolean|state\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `boolean`, true when clicked  \nNOTE: To set the state use this idiom: `if UI.CheckBox(\"My checkbox\", my_state) then my_state = not my_state end`\n\n---\n`UI.RadioButton(text, checked)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`text`|string|radiobutton text\n|`checked`|boolean|state\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `boolean`, true when clicked  \nNOTE: To set the state on a group of RadioButtons use this idiom: \n`if UI.RadioButton(\"Radio1\", rb_group_idx == 1) then rb_group_idx = 1 end`\n`if UI.RadioButton(\"Radio2\", rb_group_idx == 2) then rb_group_idx = 2 end`\n`-- etc...`\n\n---\n`UI.TabBar(name, tabs, idx)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`name`|string|TabBar ID\n|`tabs`|table|a table of strings\n|`idx`|number|initial active tab index\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `boolean`, `number`, [1] true when clicked, [2] the selected tab index  \n\n---\n`UI.Dummy(width, height)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`width`|number|width\n|`height`|number|height\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: This is an invisible widget useful only to \"push\" other widgets' positions or to leave a desired gap.\n\n---\n`UI.Begin(name, transform, is_modal)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`name`|string|window ID\n|`transform`|Mat4|window transform\n|`is_modal` _[opt]_|boolean|is this a modal window\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Starts a new window. Every widget call after this function will belong to this window, until `UI.End(main_pass)` is called. If this is set as a modal window (by passing true to the last argument) it should always call `UI.EndModalWindow` before closing it physically. (see example in main.lua)\n\n---\n`UI.End(main_pass)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`main_pass`|Pass|the main Pass object\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Ends the current window. \n\n---\n`UI.SameLine()`\n|Argument|Type|Description\n|:---|:---|:---|\n|`none`||\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Places the next widget beside the last one, instead of bellow\n\n---\n`UI.GetWindowSize(name)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`name`|number|window ID\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `number`, `number`, [1] window width, [2] window height  \nNOTE: If no window with this ID was found, return type is `nil`\n\n---\n`UI.SetTextBoxText(id, text)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`id`|number|textbox ID\n|`text`|string|textbox text\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Helper to set the textbox text after validation.\n\n---\n`UI.SetInteractionEnabled(enabled)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`enabled`|boolean|if interaction should be enabled\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Useful if you want to set interaction on/off programmatically, without pressing the toggle button\n\n---\n\n`UI.Init(interaction_toggle_device, interaction_toggle_button, enabled, pointer_rotation)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`interaction_toggle_device` _[opt]_|Device|controller\n|`interaction_toggle_button` _[opt]_|DeviceButton|controller button that toggles interaction on/off\n|`enabled` _[opt]_|boolean|initial state of interaction\n|`pointer_rotation` _[opt]_|number|pointer rotation angle (default value is similar to SteamVR/Oculus).\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Should be called on `lovr.load()`. Defaults are `hand/left`, `thumbstick`, `true`, `math.pi / 3` respectively.\n\n---\n`UI.InputInfo(emulated_headset, ray_position, ray_orientation)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`emulated_headset` _[opt]_|boolean|emulated headset\n|`ray_position` _[opt]_|Vec3|ray position\n|`ray_orientation` _[opt]_|Quat|ray orientation\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Should be called on `lovr.update()`. To operate in standard \"VR mode\" do not pass any arguments. The optional arguments could be passed to emulate the headset with mouse and keyboard. When only the 1st argument is passed, data is captured from the emulated headset directly. When the render pass has a different transform than the headset, then the 2 last arguments should be passed too.\n\n---\n`UI.NewFrame(main_pass)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`main_pass`|Pass|the main Pass object\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Should be called on `lovr.draw()`. Windows and widgets should be called after this function, and a `UI.RenderFrame(main_pass)` finalizes the whole UI.\n\n---\n`UI.RenderFrame(main_pass)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`main_pass`|Pass|the main Pass object.\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `table`, ui passes  \nNOTE: During the frame, lovr-ui records its own passes for drawing the various elements but doesn't submit them. Instead, a table of those passes is returned to the user where they can be scheduled to be submitted along with the default pass (See the example near the end of main.lua).\n\n---\n`UI.GetColorNames()`\n|Argument|Type|Description\n|:---|:---|:---|\n|`none`||\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `table`, color names  \nNOTE: Helper to get the color keys as a table of strings\n\n---\n`UI.GetColor(col_name)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`col_name`|string|color key\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `table`, color value  \nNOTE: Helper to get a color value\n\n---\n`UI.SetColor(col_name, color)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`col_name`|string|color key\n|`col_name`|string|color value\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Helper to set a color value. Don't call this every frame because it regenerates the keyboard textures. Use `UI.OverrideColor` instead.\n\n---\n`UI.OverrideColor(col_name, color)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`col_name`|string|color key\n|`col_name`|string|color value\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Helper to override a color value.\n\n---\n`UI.SetColorTheme(theme, copy_from)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`theme`|string or table|color key or table with overrided keys\n|`copy_from` _[opt]_|string|theme to copy values from\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Sets a theme to one of the built-in ones (\"dark\", \"light\") if the passed argument is a string. Also accepts a table of colors. If the passed table doesn't contain all of the keys, the rest of them will be copied from the built-in theme of the `copy_from` argument.\n\n---\n`UI.CloseModalWindow()`\n|Argument|Type|Description\n|:---|:---|:---|\n|`none`||\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Closes a modal window\n\n---\n`UI.AddKeyboardPack(lower_case, upper_case, symbols)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`lower_case`|table|lower case characters\n|`upper_case`|table|upper case characters\n|`symbols`|table|symbol characters\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Provides the ability to add a language pack for the on-screen keyboard. The default pack is Latin. Cycling between packs is done by holding the grip button and pressing the trigger on the space key of the on-screen keyboard.\n\n---\n`UI.GetScale()`\n|Argument|Type|Description\n|:---|:---|:---|\n|`none`||\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `number`, ui scale \nNOTE: Helper to get the ui scale\n\n---\n`UI.SetScale(scale)`\n|Argument|Type|Description\n|:---|:---|:---|\n|`scale`|number|ui scale\n\n\u003cspan style=\"color:DeepSkyBlue\"\u003eReturns:\u003c/span\u003e `nothing`  \nNOTE: Helper to set the ui scale. Don't call this every frame (it causes textures to be regenerated)\n\n---\n\n**General Info:**\n`UI.Begin()`/`UI.End()` defines a window. Widget function calls placed inside this block, are then part of this window.\nlovr-ui currently uses a row-based auto-layout. That means that there are limits to how widgets are positioned.\nWidget sizes are mostly character-width based. This is done for simplicity.\nThis library borrows concepts from the outstanding [Dear ImGui](https://github.com/ocornut/imgui) library and is inspired by [microui](https://github.com/rxi/microui), trying to be simple and minimal.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimmortalx74%2Flovr-ui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fimmortalx74%2Flovr-ui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimmortalx74%2Flovr-ui/lists"}