util = require( "utillib" ) Rect = require( "rectangle" ) Vec2 = require( "vector2" ) -- NOTE!!! Work in progress! Do not use. Gui = { ALING = { LEFT = 1, RIGHT = 2, CENTER = 3, TOP = 1, BOTTOM = 2, }, CONTAINER = { HORIZONTAL = 1, VERTICAL = 2, }, font = 0, fontSize = 30, textSpacing = 1, padding = 2, spacing = 4, _elements = {}, } function Gui.process( mousePosition ) for _, element in ipairs( Gui._elements ) do if element.notMouseOver ~= nil then element:notMouseOver() end end -- Go backwards on process check so we trigger the top most ui first and stop there. for i = #Gui._elements, 1, -1 do local element = Gui._elements[i] if element:isMouseOver( mousePosition ) and not element.disabled then if element.isClicked ~= nil then element:isClicked() end break end end end function Gui.draw() for _, element in ipairs( Gui._elements ) do if element.draw ~= nil and element.visible then -- //TODO Check here if needs scrissor mode. -- if element element:draw() end end end -- Label Label = {} Label.__index = Label function Label:new( set ) local o = { _ID = #Gui._elements + 1, bounds = set ~= nil and set.bounds ~= nil and set.bounds or Rect:new( 0, 0, 0, 0 ), font = set ~= nil and set.font ~= nil and set.font or Gui.font, text = set ~= nil and set.text ~= nil and set.text or "", fontSize = set ~= nil and set.fontSize ~= nil and set.fontSize or Gui.fontSize, textSpacing = set ~= nil and set.textSpacing ~= nil and set.textSpacing or Gui.textSpacing, color = set ~= nil and set.color ~= nil and set.color or BLACK, maxTextLen = set ~= nil and set.maxTextLen ~= nil and set.maxTextLen or nil, padding = set ~= nil and set.padding ~= nil and set.padding or Gui.padding, HAling = set ~= nil and set.HAling ~= nil and set.HAling or Gui.ALING.LEFT, VAling = set ~= nil and set.VAling ~= nil and set.VAling or Gui.ALING.BOTTOM, visible = set ~= nil and set.visible ~= nil and set.visible or true, disabled = set ~= nil and set.disabled ~= nil and set.disabled or false, drawBounds = set ~= nil and set.drawBounds ~= nil and set.drawBounds or false, _textRect = Rect:new( 0, 0, 0, 0 ), _visibilityBounds = nil, -- Callbacks. onMouseOver = set ~= nil and set.onMouseOver ~= nil and set.onMouseOver or nil, notMouseOver = set ~= nil and set.notMouseOver ~= nil and set.notMouseOver or nil, onClicked = set ~= nil and set.onClicked ~= nil and set.onClicked or nil, } setmetatable( o, self ) o:setText( o.text ) -- To measure bounds. table.insert( Gui._elements, o ) return o end function Label:setText( text ) if self.maxTextLen ~= nil then self.text = text:sub( 1, self.maxTextLen ) else self.text = text end local textSize = Vec2:new( RL_MeasureText( self.font, self.text, self.fontSize, self.textSpacing ) ) -- Set bounds to text size if not given. if self.bounds.width == nil then self.bounds.width = textSize.x end if self.bounds.height == nil then self.bounds.height = textSize.y end -- Horizontal aling. if self.HAling == Gui.ALING.CENTER then self._textRect.x = self.bounds.width / 2 - textSize.x / 2 elseif self.HAling == Gui.ALING.LEFT then self._textRect.x = self.padding elseif self.HAling == Gui.ALING.RIGHT then self._textRect.x = self.bounds.width - textSize.x - self.padding end -- Vertical aling. if self.VAling == Gui.ALING.CENTER then self._textRect.y = self.bounds.height / 2 - textSize.y / 2 elseif self.VAling == Gui.ALING.TOP then self._textRect.y = self.padding elseif self.VAling == Gui.ALING.BOTTOM then self._textRect.y = self.bounds.height - textSize.y - self.padding end self._textRect.width = textSize.x self._textRect.height = textSize.y end function Label:setPosition( pos ) self.bounds.x = pos.x self.bounds.y = pos.y end function Label:isMouseOver( mousePosition ) local over = RL_CheckCollisionPointRec( mousePosition, self.bounds ) if over and self._visibilityBounds ~= nil then over = RL_CheckCollisionPointRec( mousePosition, self._visibilityBounds ) end if over and self.onMouseOver ~= nil then self:onMouseOver() end return over end function Label:isClicked() local clicked = RL_IsMouseButtonPressed( MOUSE_BUTTON_LEFT ) if clicked and self.onClicked ~= nil then self:onClicked() end return clicked end function Label:draw() local usedScissor = false if self._visibilityBounds ~= nil then local rect = Rect:new( RL_GetCollisionRec( self.bounds, self._visibilityBounds ) ) -- Use scissor mode only on partyally visible. if rect.width == 0 and rect.height == 0 then return elseif math.floor( rect.width ) ~= math.floor( self.bounds.width ) or math.floor( rect.height ) ~= math.floor( self.bounds.height ) then usedScissor = true RL_BeginScissorMode( self._visibilityBounds ) end end RL_DrawText( self.font, self.text, { self.bounds.x + self._textRect.x, self.bounds.y + self._textRect.y }, self.fontSize, self.textSpacing, self.color ) if self.drawBounds then RL_DrawRectangleLines( self.bounds, self.color ) end if usedScissor then RL_EndScissorMode() end end function Label:delete() table.remove( Gui._elements, self._ID ) end function Label:set2Top() util.tableMove( Gui._elements, self._ID, 1, #Gui._elements ) end function Label:set2Back() util.tableMove( Gui._elements, self._ID, 1, 1 ) end -- Panel. Panel = {} Panel.__index = Panel function Panel:new( set ) local o = { _ID = #Gui._elements + 1, bounds = set ~= nil and set.bounds ~= nil and set.bounds or Rect:new( 0, 0, 0, 0 ), color = set ~= nil and set.color ~= nil and set.color or LIGHTGRAY, visible = set ~= nil and set.visible ~= nil and set.visible or true, disabled = set ~= nil and set.disabled ~= nil and set.disabled or false, } setmetatable( o, self ) table.insert( Gui._elements, o ) return o end function Panel:setPosition( pos ) self.bounds.x = pos.x self.bounds.y = pos.y end function Panel:isMouseOver( mousePosition ) return RL_CheckCollisionPointRec( mousePosition, self.bounds ) end function Panel:draw() RL_DrawRectangle( self.bounds, self.color ) end function Panel:delete() table.remove( Gui._elements, self._ID ) end function Panel:set2Top() util.tableMove( Gui._elements, self._ID, 1, #Gui._elements ) end function Panel:set2Back() util.tableMove( Gui._elements, self._ID, 1, 1 ) end -- Container. Container = {} Container.__index = Container function Container:new( set ) local o = { _ID = #Gui._elements + 1, bounds = set ~= nil and set.bounds ~= nil and set.bounds or Rect:new( 0, 0, 0, 0 ), color = set ~= nil and set.color ~= nil and set.color or LIGHTGRAY, spacing = set ~= nil and set.spacing ~= nil and set.spacing or Gui.spacing, type = set ~= nil and set.type ~= nil and set.type or Gui.CONTAINER.VERTICAL, HAling = set ~= nil and set.HAling ~= nil and set.HAling or Gui.ALING.LEFT, VAling = set ~= nil and set.VAling ~= nil and set.VAling or Gui.ALING.TOP, visible = set ~= nil and set.visible ~= nil and set.visible or true, disabled = set ~= nil and set.disabled ~= nil and set.disabled or false, drawBounds = set ~= nil and set.drawBounds ~= nil and set.drawBounds or false, scrollable = set ~= nil and set.scrollable ~= nil and set.scrollable or false, cells = {}, _visibilityBounds = nil, -- Will give this to it's children. _scrollRect = Rect:new( 0, 0, 0, 0 ), } setmetatable( o, self ) table.insert( Gui._elements, o ) return o end function Container:setPosition( pos ) self.bounds.x = pos.x self.bounds.y = pos.y end function Container:isMouseOver( mousePosition ) return RL_CheckCollisionPointRec( mousePosition, self.bounds ) end function Container:add( cell ) table.insert( self.cells, cell ) self:update() if cell.update ~= nil then cell:update() end end function Container:scroll( pos ) if not self.scrollable then return end self._scrollRect.x = util.clamp( pos.x, 0, self._scrollRect.width - self.bounds.width ) self._scrollRect.y = util.clamp( pos.y, 0, self._scrollRect.height - self.bounds.height ) -- self._scrollRect.x = pos.x -- self._scrollRect.y = pos.y self:update() end function Container:update() local pos = Vec2:new( self.bounds.x + self.spacing, self.bounds.y + self.spacing ) if self.scrollable then self._visibilityBounds = self.bounds self._scrollRect.width = 0 self._scrollRect.height = 0 end for i, cell in ipairs( self.cells ) do if self._visibilityBounds ~= nil then cell._visibilityBounds = self._visibilityBounds end if self.type == Gui.CONTAINER.VERTICAL then if self.HAling == Gui.ALING.CENTER then pos.x = self.bounds.x + self.bounds.width / 2 - cell.bounds.width / 2 elseif self.HAling == Gui.ALING.RIGHT then pos.x = self.bounds.x + self.bounds.width - cell.bounds.width - self.spacing end cell.bounds.x = pos.x - self._scrollRect.x cell.bounds.y = pos.y - self._scrollRect.y self._scrollRect.width = math.max( self._scrollRect.width, pos.x + cell.bounds.width + self.spacing - self.bounds.x ) self._scrollRect.height = math.max( self._scrollRect.height, pos.y + cell.bounds.height + self.spacing - self.bounds.y ) pos.y = pos.y + cell.bounds.height + self.spacing elseif self.type == Gui.CONTAINER.HORIZONTAL then if self.VAling == Gui.ALING.CENTER then pos.y = self.bounds.y + self.bounds.height / 2 - cell.bounds.height / 2 elseif self.VAling == Gui.ALING.BOTTOM then pos.y = self.bounds.y + self.bounds.height - cell.bounds.height - self.spacing end cell.bounds.x = pos.x - self._scrollRect.x cell.bounds.y = pos.y - self._scrollRect.y self._scrollRect.width = math.max( self._scrollRect.width, pos.x + cell.bounds.width + self.spacing - self.bounds.x ) self._scrollRect.height = math.max( self._scrollRect.height, pos.y + cell.bounds.height + self.spacing - self.bounds.y ) pos.x = pos.x + cell.bounds.width + self.spacing end if cell.update ~= nil then cell:update() end end end function Container:draw() if self.drawBounds then RL_DrawRectangleLines( self.bounds, self.color ) RL_DrawRectangleLines( { self.bounds.x - self._scrollRect.x, self.bounds.y - self._scrollRect.y, self._scrollRect.width, self._scrollRect.height, }, RED ) end end function Container:delete() table.remove( Gui._elements, self._ID ) end --Assingments. Gui.label = Label Gui.panel = Panel Gui.container = Container return Gui