From 4a07fd463f31ffd211b6443efe79128479ca8f08 Mon Sep 17 00:00:00 2001 From: immortalx74 Date: Fri, 4 Nov 2022 06:27:29 +0200 Subject: [PATCH] Added WhiteBoard widget. Fixed multiline for labels and buttons --- README.md | 13 ++++++++ main.lua | 84 ++++++++++++++++++++++++++++++++++++++++++++++++- ui/ui.lua | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 183 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a84f316..42520c1 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ - TabBar - Dummy - ProgressBar + - WhiteBoard --- `UI.Button(text, width, height)` @@ -52,6 +53,18 @@ NOTE: if no `width` and/or `height` are provided, the button size will be auto- Returns: `boolean` , true when clicked. +--- +`UI.WhiteBoard(name, width, height)` +|Argument|Type|Description +|:---|:---|:---| +|`name`|string|whiteboard ID +|`width`|number|width in pixels +|`height`|number|height in pixels + +Returns: `Pass`, `boolean`, `boolean`, `boolean`, `boolean`, `number`, `number`, [1] Pass object, [2] clicked, [3] down, [4] released, [5] hovered, [6] X, [7] Y +NOTE: General purpose widget for custom drawing/interaction. The returned Pass can be used to do regular LÖVR draw-commands +like plane, circle, text, etc. X and Y are the local 2D coordinates of the pointer (0,0 is top,left) + --- `UI.TextBox(name, num_chars, buffer)` |Argument|Type|Description diff --git a/main.lua b/main.lua index 50692cd..1e68cf3 100644 --- a/main.lua +++ b/main.lua @@ -1,3 +1,6 @@ +-- NOTE: This demo app is currently completely unstructured, crude and lacks comments. +-- This will be addressed. For the time being it's a testbed during development. + UI = require "ui/ui" buf = "John" win2pos = lovr.math.newMat4( 0.1, 1.3, -1.3 ) @@ -12,7 +15,23 @@ tab_bar_idx = 1 col_list_idx = 1 progress_value = 0 accumulator = 0 -time_now = 0 +planes = { m = {}, col = {} } +plane_frames = 0 +amplitude = 100 +frequency = 0.1 +zoom = 1 + +local x, y, a, c1, c2, c3 +for i = 1, 10 do + x = lovr.math.random( 0, 500 ) + y = lovr.math.random( 0, 300 ) + a = lovr.math.random( 0, math.pi * 2 ) + c1 = lovr.math.random() + c2 = lovr.math.random() + c3 = lovr.math.random() + table.insert( planes.m, lovr.math.newMat4( vec3( x, y, 0 ), vec3( 100 ), quat( a, 0, 0, 1 ) ) ) + table.insert( planes.col, { c1, c2, c3 } ) +end -- Override only some colors custom_theme = @@ -42,6 +61,23 @@ function lovr.update( dt ) progress_value = 0 accumulator = 0 end + + if plane_frames < 10 then + plane_frames = plane_frames + 1 + else + plane_frames = 0 + local x, y, a, c1, c2, c3 + for i = 1, 10 do + x = lovr.math.random( 0, 500 ) + y = lovr.math.random( 0, 300 ) + a = lovr.math.random( 0, math.pi * 2 ) + c1 = lovr.math.random() + c2 = lovr.math.random() + c3 = lovr.math.random() + planes.m[ i ]:set( vec3( x, y, 0 ), vec3( zoom * 100 ), quat( a, 0, 0, 1 ) ) + planes.col[ i ] = { c1, c2, c3 } + end + end end function lovr.draw( pass ) @@ -108,6 +144,52 @@ function lovr.draw( pass ) UI.Begin( "SecondWindow", win2pos ) UI.TextBox( "Location", 20, "" ) if UI.Button( "AhOh" ) then print( UI.GetWindowSize( "FirstWindow" ) ) end + + -- whiteboard 1 + UI.Label( "Click & drag R/L to zoom in/out:" ) + local ps, clicked, down, released, hovered, lx, ly = UI.WhiteBoard( "WhiteBoard1", 500, 300 ) + ps:setColor( 0, 0, 0 ) + ps:fill() + + if down then + zoom = (lx * 0.01) + end + + for i = 1, 10 do + ps:setColor( planes.col[ i ] ) + ps:plane( planes.m[ i ] ) + end + + -- whiteboard 2 + UI.Label( "Use the sliders or \nclick & drag on waveform:" ) + local ps, clicked, down, released, hovered, lx, ly = UI.WhiteBoard( "WhiteBoard2", 500, 300 ) + if down then + -- amplitude = -(ly / 2) + amplitude = (150 * ly) / 300 + frequency = (0.2 * lx) / 500 + end + if hovered then + ps:setColor( 0.1, 0, 0.2 ) + else + ps:setColor( 0, 0, 0 ) + end + ps:fill() + ps:setColor( 1, 1, 1 ) + + local xx = 0 + local yy = 0 + local y = 150 + + for i = 1, 500 do + yy = y + (amplitude * math.sin( frequency * xx )) + ps:points( xx, yy, 0 ) + xx = xx + 1 + end + + local a_released, f_released + a_released, amplitude = UI.SliderFloat( "Amplitude", amplitude, 0, 150, 500 ) + f_released, frequency = UI.SliderFloat( "Frequency", frequency, 0, 0.2, 500 ) + UI.Label( "Energy bill increase:" ) UI.ProgressBar( progress_value, 400 ) UI.Button( "Forced height", 0, 200 ) diff --git a/ui/ui.lua b/ui/ui.lua index 38cb237..7e8d45e 100644 --- a/ui/ui.lua +++ b/ui/ui.lua @@ -40,6 +40,7 @@ local ui_scale = 0.0005 local new_scale = nil local controller_vibrate = false local image_buttons_default_ttl = 2 +local whiteboards_default_ttl = 2 local font = { handle, w, h, scale = 1 } local caret = { blink_rate = 50, counter = 0 } local listbox_state = {} @@ -49,6 +50,7 @@ local windows = {} local passes = {} local textures = {} local image_buttons = {} +local whiteboards = {} local color_themes = {} local window_drag = { id = nil, is_dragging = false, offset = lovr.math.newMat4() } local layout = { prev_x = 0, prev_y = 0, prev_w = 0, prev_h = 0, row_h = 0, total_w = 0, total_h = 0, same_line = false } @@ -175,6 +177,17 @@ local function Clamp( n, n_min, n_max ) return n end +local function GetLineCount( str ) + -- https://stackoverflow.com/questions/24690910/how-to-get-lines-count-in-string/70137660#70137660 + local lines = 1 + for i = 1, #str do + local c = str:sub( i, i ) + if c == '\n' then lines = lines + 1 end + end + + return lines +end + local function FindId( t, id ) for i, v in ipairs( t ) do if v.id == id then @@ -400,7 +413,7 @@ local function GenerateOSKTextures() end p:plane( m, "fill" ) - table.insert(passes, p) + table.insert( passes, p ) end return passes end @@ -661,8 +674,8 @@ function UI.RenderFrame( main_pass ) end if theme_changed then - for i, p in ipairs(GenerateOSKTextures()) do - table.insert(passes, p) + for i, p in ipairs( GenerateOSKTextures() ) do + table.insert( passes, p ) end theme_changed = false end @@ -685,10 +698,22 @@ function UI.RenderFrame( main_pass ) image_buttons[ i ].ttl = image_buttons[ i ].ttl - 1 if image_buttons[ i ].ttl <= 0 then image_buttons[ i ].texture:release() + image_buttons[ i ].texture = nil table.remove( image_buttons, i ) end end end + + if #whiteboards > 0 then + for i = #whiteboards, 1, -1 do + whiteboards[ i ].ttl = whiteboards[ i ].ttl - 1 + if whiteboards[ i ].ttl <= 0 then + whiteboards[ i ].texture:release() + whiteboards[ i ].texture = nil + table.remove( whiteboards, i ) + end + end + end end function UI.SameLine() @@ -872,6 +897,59 @@ function UI.ImageButton( img_filename, width, height ) return result end +function UI.WhiteBoard( name, width, height ) + local cur_window = windows[ #windows ] + local my_id = Hash( cur_window.name .. name ) + local wb_idx = FindId( whiteboards, my_id ) + + if wb_idx == nil then + local tex = lovr.graphics.newTexture( width, height, { mipmaps = false } ) + local wb = { id = my_id, texture = tex, w = width or tex:getWidth(), h = height or tex:getHeight(), ttl = whiteboards_default_ttl } + table.insert( whiteboards, wb ) + wb_idx = #whiteboards + end + + local wb = whiteboards[ wb_idx ] + wb.ttl = whiteboards_default_ttl + + local bbox = {} + if layout.same_line then + bbox = { x = layout.prev_x + layout.prev_w + margin, y = layout.prev_y, w = wb.w, h = wb.h } + else + bbox = { x = margin, y = layout.prev_y + layout.row_h + margin, w = wb.w, h = wb.h } + end + + UpdateLayout( bbox ) + + local clicked = false + local down = false + local released = false + local hovered = false + if PointInRect( last_off_x, last_off_y, bbox.x, bbox.y, bbox.w, bbox.h ) and cur_window.id == hovered_window_id then + hotID = my_id + hovered = true + if input.trigger == e_trigger.pressed then + activeID = my_id + clicked = true + end + if input.trigger == e_trigger.down and activeID == my_id then + down = true + end + if input.trigger == e_trigger.released and hotID == activeID then + lovr.headset.vibrate( dominant_hand, 0.3, 0.1 ) + released = true + end + end + + table.insert( windows[ #windows ].command_list, { type = "image", bbox = bbox, texture = wb.texture, color = { 1, 1, 1 } } ) + + local p = lovr.graphics.getPass( "render", wb.texture ) + p:setDepthTest( nil ) + p:setProjection( 1, mat4():orthographic( p:getDimensions() ) ) + table.insert( passes, p ) + return p, clicked, down, released, hovered, last_off_x - bbox.x, last_off_y - bbox.y +end + function UI.Dummy( width, height ) local bbox = {} if layout.same_line then @@ -946,12 +1024,13 @@ function UI.Button( text, width, height ) local text_w = font.handle:getWidth( text ) local text_h = font.handle:getHeight() + local num_lines = GetLineCount( text ) local bbox = {} if layout.same_line then - bbox = { x = layout.prev_x + layout.prev_w + margin, y = layout.prev_y, w = (2 * margin) + text_w, h = (2 * margin) + text_h } + bbox = { x = layout.prev_x + layout.prev_w + margin, y = layout.prev_y, w = (2 * margin) + text_w, h = (2 * margin) + (num_lines * text_h) } else - bbox = { x = margin, y = layout.prev_y + layout.row_h + margin, w = (2 * margin) + text_w, h = (2 * margin) + text_h } + bbox = { x = margin, y = layout.prev_y + layout.row_h + margin, w = (2 * margin) + text_w, h = (2 * margin) + (num_lines * text_h) } end if width and type( width ) == "number" and width > bbox.w then @@ -1391,12 +1470,13 @@ end function UI.Label( text ) local text_w = font.handle:getWidth( text ) local text_h = font.handle:getHeight() + local num_lines = GetLineCount( text ) local bbox = {} if layout.same_line then - bbox = { x = layout.prev_x + layout.prev_w + margin, y = layout.prev_y, w = text_w, h = (2 * margin) + text_h } + bbox = { x = layout.prev_x + layout.prev_w + margin, y = layout.prev_y, w = text_w, h = (2 * margin) + (num_lines * text_h) } else - bbox = { x = margin, y = layout.prev_y + layout.row_h + margin, w = text_w, h = (2 * margin) + text_h } + bbox = { x = margin, y = layout.prev_y + layout.row_h + margin, w = text_w, h = (2 * margin) + (num_lines * text_h) } end UpdateLayout( bbox )