Files
Basalt/NyoGUI.lua
2022-03-22 21:57:55 +01:00

1378 lines
39 KiB
Lua

-- Shad's GUI free for everyone to use
animation = {__type = "Animation", name = ""}
animation.__index = animation
local object = {} -- Base class for all UI elements
local activeScreen
local screen = {}
local screens = {}
local animations = {}
--Utility Functions:
local function getTextHorizontalAlign(text, w, textAlign)
local text = string.sub(text, 1, w)
local n = w-string.len(text)
if(textAlign=="right")then
text = string.rep(" ", n)..text
elseif(textAlign=="center")then
text = string.rep(" ", math.floor(n/2))..text..string.rep(" ", math.floor(n/2))
text = text..(string.len(text) < w and " " or "")
else
text = text..string.rep(" ", n)
end
return text
end
local function getTextVerticalAlign(h,textAlign)
local offset = 0
if(textAlign=="center")then
offset = h%2 == 0 and math.floor(h / 2)-1 or math.floor(h / 2)
end
if(textAlign=="bottom")then
offset = h
end
return offset
end
local function rpairs(t)
return function(t, i)
i = i - 1
if i ~= 0 then
return i, t[i]
end
end, t, #t + 1
end
local function copyVar(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[copyVar(orig_key)] = copyVar(orig_value)
end
setmetatable(copy, copyVar(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
local function callAll(tab,...)
if(tab~=nil)then
if(#tab>0)then
for k,v in pairs(tab)do
v(type(...)=="table" and table.unpack(...) or ...)
end
end
end
end
local function tableCount(tab)
local n = 0
for _,v in pairs(tab)do
n = n+1
end
return n
end
--------------
--Animation System
animation.new = function(name)
local newElement = {name=name,animations={},nextWaitTimer=0,index=1,infiniteloop=false}
setmetatable(newElement, animation)
table.insert(animations, newElement)
return newElement
end
function animation:addAnimation(func)
table.insert(self.animations, {f=func,t=self.nextWaitTimer})
self.nextWaitTimer = 0
return self
end
function animation:wait(timer)
self.nextWaitTimer = timer
return self
end
function animation:onPlay() -- internal function, don't use it unless you know what you do!
if(self.playing)then
self.animations[self.index].f(self)
self.index = self.index+1
if(self.animations[self.index]~=nil)then
if(self.animations[self.index].t>0)then
self.timeObj = os.startTimer(self.animations[self.index].t)
else
self:onPlay()
end
else
if(self.infiniteloop)then
self.index = 1
if(self.animations[self.index].t>0)then
self.timeObj = os.startTimer(self.animations[self.index].t)
else
self:onPlay()
end
end
end
end
end
function animation:play(infiniteloop)
if(infiniteloop~=nil)then self.infiniteloop=infiniteloop end
self.playing = true
if(self.animations[self.index]~=nil)then
if(self.animations[self.index].t>0)then
self.timeObj = os.startTimer(self.animations[self.index].t)
else
self:onPlay()
end
end
return self
end
function animation:cancel()
os.cancelTimer(self.timeObj)
self.playing = false
self.infiniteloop = false
self.index = 0
return self
end
-----------
--Object Constructors:
--(base class for every element/object even frames)
function object:new()
local newElement = {__type = "Object",name="",links={},zIndex=1,drawCalls=0,x=1,y=1,w=1,h=1,textAlign="left",draw=false,changed=true,bgcolor=colors.black,fgcolor=colors.white,text="",hanchor="left",vanchor="top"}
setmetatable(newElement, {__index = self})
return newElement
end
function object:copy(obj)
local newElement = {}
for k,v in pairs(obj)do
newElement[k] = v
end
setmetatable(newElement, {__index = self})
return newElement
end
timer = object:new()
function timer:new()
local newElement = {__type = "Timer"}
setmetatable(newElement, {__index = self})
return newElement
end
checkbox = object:new()
function checkbox:new()
local newElement = {__type = "Checkbox",symbol="\42",zIndex=5,bgcolor=colors.lightBlue,fgcolor=colors.black, value = false}
setmetatable(newElement, {__index = self})
return newElement
end
radio = object:new()
function radio:new()
local newElement = {__type = "Radio",symbol="\7",zIndex=5,bgcolor=colors.lightBlue,fgcolor=colors.black, value = "", elements={}}
setmetatable(newElement, {__index = self})
return newElement
end
label = object:new()
function label:new()
local newElement = {__type = "Label"}
setmetatable(newElement, {__index = self})
return newElement
end
input = object:new()
function input:new()
local newElement = {__type = "Input",zIndex=5,bgcolor=colors.lightBlue,fgcolor=colors.black,w=5, value = ""}
setmetatable(newElement, {__index = self})
return newElement
end
button = object:new()
function button:new()
local newElement = {__type = "Button",zIndex=5,bgcolor=colors.lightBlue,fgcolor=colors.black,w=5,horizontalTextAlign="center",verticalTextAlign="center"}
setmetatable(newElement, {__index = self})
return newElement
end
dropdown = object:new()
function dropdown:new()
local newElement = {__type = "Dropdown",zIndex=10,bgcolor=colors.lightBlue,fgcolor=colors.black,w=5,horizontalTextAlign="center",elements={},value={text="",fgcolor=colors.black,bgcolor=colors.lightBlue}}
setmetatable(newElement, {__index = self})
return newElement
end
list = object:new()
function list:new()
local newElement = {__type = "List",zIndex=5,bgcolor=colors.lightBlue,fgcolor=colors.black,w=5,horizontalTextAlign="center",elements={},value={text="",fgcolor=colors.black,bgcolor=colors.lightBlue}}
setmetatable(newElement, {__index = self})
return newElement
end
frame = object:new()
function frame:new(name,scrn,frameObj)
local parent = scrn~=nil and scrn or term.current()
local w, h = parent.getSize()
local newElement = {}
if(frameObj~=nil)then
newElement = object:copy(frameObj)
newElement.fWindow = window.create(parent,1,1,frameObj.w,frameObj.h)
else
newElement = {__type = "Frame",name=name, parent = parent,zIndex=1, fWindow = window.create(parent,1,1,w,h),x=1,y=1,w=w,h=h, objects={},objZKeys={},bgcolor = colors.black, fgcolor=colors.white,barActive = false, title="New Frame", titlebgcolor = colors.lightBlue, titlefgcolor = colors.black, horizontalTextAlign="left",focusedObject={}, isMoveable = false}
end
setmetatable(newElement, {__index = self})
return newElement
end
--------
--object methods
function object:show()
self.draw = true
self.changed = true
return self
end
function object:hide()
self.draw = false
self.changed = true
return self
end
function object:changeVisibility()
if(self.draw)then
self:hide()
else
self:show()
end
return self
end
function object:isVisible()
return self.draw
end
function object:getName()
return self.name
end
function object:setPosition(x,y)
self.x = tonumber(x)
self.y = tonumber(y)
self.changed = true
return self
end
function object:setBackground(color)
self.bgcolor = color
self.changed = true
return self
end
function object:setForeground(color)
self.fgcolor = color
self.changed = true
return self
end
--Object Events:-----
function object:onClick(func)
if(self.clickFunc==nil)then self.clickFunc = {} end
table.insert(self.clickFunc,func)
return self
end
function object:onMouseUp(func)
if(self.upFunc==nil)then self.upFunc = {} end
table.insert(self.upFunc,func)
return self
end
function object:onMouseDrag(func)
if(self.dragFunc==nil)then self.dragFunc = {} end
table.insert(self.dragFunc,func)
return self
end
function object:onChange(func)
if(self.changeFunc==nil)then self.changeFunc = {} end
table.insert(self.changeFunc,func)
return self
end
function object:onKeyClick(func)
if(self.keyEventFunc==nil)then self.keyEventFunc = {} end
table.insert(self.keyEventFunc,func)
return self
end
function object:onLoseFocus(func)
if(self.loseFocusEventFunc==nil)then self.loseFocusEventFunc = {} end
table.insert(self.loseFocusEventFunc,func)
return self
end
function object:onGetFocus(func)
if(self.getFocusEventFunc==nil)then self.getFocusEventFunc = {} end
table.insert(self.getFocusEventFunc,func)
return self
end
--------------------
function object:setText(text)
self.text = text
self.changed = true
return self
end
function object:setSize(w,h)
self.w = tonumber(w)
self.h = tonumber(h)
self.changed = true
return self
end
function object:getHeight()
return self.h
end
function object:getWidth()
return self.w
end
function object:linkTo(obj)
if(obj.__type==self.__type)then
obj.links[self.name] = self
end
return self
end
function object:link(obj)
if(obj.__type==self.__type)then
self.links[obj.name] = obj
end
return self
end
function object:isLinkedTo(obj)
return (obj[self.name] ~= nil)
end
function object:isLinked(obj)
return (self[obj.name] ~= nil)
end
function object:setValue(val)
self.value = val
self.changed = true
for _,v in pairs(self.links)do
v.value = val
v.changed = true
end
callAll(self.onChangeFunc,self)
return self
end
function object:getValue()
return self.value;
end
function object:setTextAlign(halign,valign)
self.horizontalTextAlign = halign
if(valign~=nil)then self.verticalTextAlign = valign end
self.changed = true
return self
end
function object:drawObject()
if(self.draw)then
self.drawCalls = self.drawCalls + 1
end
end
function object:setAnchor(...)
if(type(...)=="string")then
if(...=="right")or(...=="left")then
self.hanchor = ...
end
if(...=="top")or(...=="bottom")then
self.vanchor = ...
end
end
if(type(...)=="table")then
for _,v in pairs(...)do
if(v=="right")or(v=="left")then
self.hanchor = v
end
if(v=="top")or(v=="bottom")then
self.vanchor = v
end
end
end
return self
end
function object:relativeToAbsolutePosition(x,y) -- relative position
if(x==nil)then x = self.x end
if(y==nil)then y = self.y end
if(self.frame~=nil)then
local fx,fy = self.frame:relativeToAbsolutePosition()
x=fx+x-1
y=fy+y-1
end
return x, y
end
function object:relativeToAbsolutePositionOLD(x,y) -- relative position
if(x==nil)then x = 0 end
if(y==nil)then y = 0 end
x = x+self.x;y=y+self.y
if(self.frame~=nil)then
x,y = self.frame:relativeToAbsolutePosition(x,y)
x = x-1; y = y-1
end
return x, y
end
function object:getAnchorPosition(x,y)
if(x==nil)then x = self.x end
if(y==nil)then y = self.y end
if(self.hanchor=="right")then
x = self.frame.w-x-self.w+2
end
if(self.vanchor=="bottom")then
y = self.frame.h-y-self.h+2
end
return x, y
end
function object:isFocusedElement()
if(self.frame~=nil)then
return self == self.frame.focusedObject
end
return false
end
function object:mouseEvent(event,typ,x,y) -- internal class, dont use unless you know what you do
local vx,vy = self:relativeToAbsolutePosition(self:getAnchorPosition())
if(vx<=x)and(vx+self.w>x)and(vy<=y)and(vy+self.h>y)then
if(self.frame~=nil)then self.frame:setFocusedElement(self) end
if(event=="mouse_click")then
if(self.clickFunc~=nil)then
callAll(self.clickFunc,self,typ,x,y)
end
elseif(event=="mouse_up")then
if(self.upFunc~=nil)then
callAll(self.upFunc,self,typ,x,y)
end
elseif(event=="mouse_drag")then
if(self.dragFunc~=nil)then
callAll(self.dragFunc,self,typ,x,y)
end
end
return true
end
return false
end
function object:keyEvent(event,typ) -- internal class, dont use unless you know what you do
if(self.keyEventFunc~=nil)then
callAll(self.keyEventFunc,self,typ)
end
end
function object:setFocus()
if(self.frame~=nil)then
self.frame:setFocusedElement(self)
end
return self
end
function object:loseFocusEvent()
if(self.loseFocusEventFunc~=nil)then
callAll(self.loseFocusEventFunc,self)
end
end
function object:getFocusEvent()
if(self.getFocusEventFunc~=nil)then
callAll(self.getFocusEventFunc,self)
end
end
function object:setZIndex(index)
self.frame:changeZIndexOfObj(self,index)
return self
end
--object end
--Frame object
screen.new = function(name, scrn) -- this is also just a frame, but its a level 0 frame and doesn't inherit
local obj = frame:new(name,scrn)
if(screens[name] == nil)then
screens[name] = obj
obj.fWindow.setVisible(false)
obj.debugLabel=obj:addLabel("DebugLabel")
return obj;
else
return screens[name];
end
end
screen.remove = function(name)
screens[name].fWindow.setVisible(false)
screens[name] = nil
end
function frame:addFrame(frameObj) -- with this you also create frames, but it needs to have a parent frame
if(self:getObject(frameObj) == nil)then
local obj
if(type(frameObj)=="string")then
obj = frame:new(frameObj,self.fWindow)
elseif(type(frameObj)=="table")and(frameObj.__type=="Frame")then
obj = frame:new(frameObj.name,self.fWindow,frameObj)
end
obj.frame=self;
self:addObject(obj)
return obj;
else
return nil, "id "..frameObj.." already exists";
end
end
function frame:setParentFrame(parent) -- if you want to change the parent of a frame, even level 0 frames can have a parent (they will be 'converted' *habschi*)
if(parent.__type=="Frame")and(parent~=self.frame)then
if(self.frame~=nil)then
self.frame:removeObject(self)
end
self.parent = parent.fWindow
self.frame = parent
self.fWindow.setVisible(false)
self.fWindow = window.create(parent.fWindow,self.x,self.y,self.w,self.h)
self.frame:addObject(self)
if(self.draw)then
self:show()
end
end
return self
end
function frame:showBar() -- shows top bar
self.barActive = true
return self
end
function frame:hideBar() -- hides top bar
self.barActive = false
return self
end
function frame:setTitle(title,fgcolor,bgcolor) -- changed the title in your top bar
self.title=title
if(fgcolor~=nil)then self.titlefgcolor = fgcolor end
if(bgcolor~=nil)then self.titlebgcolor = bgcolor end
self.changed = true
return self
end
function frame:setTextAlign(align) -- changes title align
self.horizontalTextAlign = align
self.changed = true
return self
end
function frame:setSize(width, height) -- frame size
object.setSize(self,width,height)
self.fWindow.reposition(self.x,self.y,width,height)
return self
end
function frame:setPosition(x,y) -- pos
object.setPosition(self,x,y)
self.fWindow.reposition(x,y)
return self
end
function frame:show() -- you need to call to be able to see the frame
object.show(self)
self.fWindow.setBackgroundColor(self.bgcolor)
self.fWindow.setTextColor(self.fgcolor)
self.fWindow.setVisible(true)
self.fWindow.redraw()
if(self.frame == nil)then
activeScreen = self
end
return self
end
function frame:hide() -- hides the frame (does not remove setted values)
object.hide(self)
self.fWindow.setVisible(false)
self.fWindow.redraw()
self.parent.clear()
return self
end
function frame:remove() -- removes the frame completly
if(self.frame~=nil)then
object.hide(self)
end
self.changed = true
self.draw = false
self.fWindow.setVisible(false)
screens[self.name] = nil
self.parent.clear()
end
function frame:getObject(name) -- you can find objects by their name
if(self.objects~=nil)then
for _,b in pairs(self.objects)do
for _,v in pairs(b)do
if(v.name==name)then
return v
end
end
end
end
end
function frame:removeObject(obj) -- you can remove objects by their name
if(self.objects~=nil)then
for a,b in pairs(self.objects)do
for k,v in pairs(b)do
if(v==obj)then
table.remove(self.objects[a],k)
return;
end
end
end
end
end
function frame:addObject(obj) -- you can add a object manually, normaly you shouldn't use this function, it get called internally
if(self.objects[obj.zIndex]==nil)then
for x=0,#self.objZKeys do
if(self.objZKeys[x]~=nil)then
if(obj.zIndex >self.objZKeys[x])then
table.insert(self.objZKeys,x,obj.zIndex)
end
else
table.insert(self.objZKeys,x,obj.zIndex)
end
end
if(#self.objZKeys<=0)then
table.insert(self.objZKeys,obj.zIndex)
end
local cache = {}
for k,v in pairs(self.objZKeys)do
if(self.objects[v]~=nil)then
cache[v] = self.objects[v]
else
cache[v] = {}
end
end
self.objects = cache
end
table.insert(self.objects[obj.zIndex],obj)
end
function frame:drawObject() -- this draws the frame, you dont need that function, it get called internally
object.drawObject(self)
if(self.draw)then
if(self.drag)and(self.frame==nil)then
self.parent.clear()
end
self.fWindow.clear()
if(self.barActive)then
self.fWindow.setBackgroundColor(self.titlebgcolor)
self.fWindow.setTextColor(self.titlefgcolor)
self.fWindow.setCursorPos(1,1)
self.fWindow.write(getTextHorizontalAlign(self.title,self.w,self.horizontalTextAlign))
end
local keys = {}
for k in pairs(self.objects)do
table.insert(keys,k)
end
for _,b in rpairs(keys)do
for k,v in pairs(self.objects[b])do
if(v.draw~=nil)then
v:drawObject()
end
end
end
if(self.inputActive)then
self.fWindow.setCursorPos(self.cursorX, self.cursorY)
self.fWindow.setCursorBlink(true)
end
self.fWindow.setBackgroundColor(self.bgcolor)
self.fWindow.setTextColor(self.fgcolor)
self.fWindow.setVisible(true)
self.fWindow.redraw()
end
end
function frame:mouseEvent(event,typ,x,y) -- internal mouse event, should make it local but as lazy i am..
local fx,fy = self:relativeToAbsolutePosition(self:getAnchorPosition())
if(self.drag)and(self.draw)then
if(event=="mouse_drag")then
local parentX=1;local parentY=1
if(self.frame~=nil)then
parentX,parentY = self.frame:relativeToAbsolutePosition(self.frame:getAnchorPosition())
end
self:setPosition(x+self.xToRem-(parentX-1),y-(parentY-1))
end
if(event=="mouse_up")then
self.drag = false
end
return true
end
if(object.mouseEvent(self,event,typ,x,y))then
if(x>fx+self.w-1)or(y>fy+self.h-1)then return end
local keys = {}
for k in pairs(self.objects)do
table.insert(keys,k)
end
for _,b in pairs(keys)do
for _,v in rpairs(self.objects[b])do
if(v.draw~=false)then
if(v:mouseEvent(event,typ,x,y))then
return true
end
end
end
end
if(self.isMoveable)then
if(x>=fx)and(x<=fx+self.w)and(y==fy)and(event=="mouse_click")then
self.drag = true
self.xToRem = fx-x
end
end
end
if(fx<=x)and(fx+self.w>x)and(fy<=y)and(fy+self.h>y)then
self:removeFocusedElement()
return true
end
return false
end
function frame:keyEvent(event,key)-- internal key event, should make it local but as lazy i am..
for _,b in pairs(self.objects)do
for _,v in pairs(b)do
if(v.draw~=false)then
if(v:keyEvent(event,key))then
return true
end
end
end
end
if(self.inputActive)then
if(self.activeInput.draw)then
if(event=="key")then
if(key==259)then
self.activeInput:setValue(string.sub(self.activeInput.value,1,string.len(self.activeInput.value)-1))
end
if(key==257)then -- on enter
if(self.inputActive)then
self.inputActive = false
self.fWindow.setCursorBlink(false)
end
end
end
if(event=="char")then
self.activeInput:setValue(self.activeInput.value..key)
end
local anchX,anchY = self.activeInput:getAnchorPosition()
self.cursorX = anchX+(string.len(self.activeInput.value) < self.activeInput.w and string.len(self.activeInput.value) or self.activeInput.w-1)
self.cursorY = anchY
if(self.activeInput.changeFunc~=nil)then
self.activeInput.changeFunc(self.activeInput)
end
end
end
return false
end
function frame:changeZIndexOfObj(obj, zindex)-- this function is not working right now
self.objects[obj.zIndex][obj.name] = nil
obj.zIndex = zindex
self:addObject(obj)
end
function frame:setFocusedElement(obj)-- you can set the focus of an element in a frame
if(self:getObject(obj.name)~=nil)then
if(self.focusedObject~=obj)then
if(self.focusedObject.name~=nil)then
self.focusedObject:loseFocusEvent()
end
obj:getFocusEvent()
self.focusedObject = obj
end
end
end
function frame:removeFocusedElement()-- and here you can remove the focus
if(self.focusedObject.name~=nil)then
self.focusedObject:loseFocusEvent()
end
self.focusedObject = {}
end
function frame:getFocusedElement()--gets the current focused element
return self.focusedObject
end
function frame:loseFocusEvent()--event which gets fired when the frame lost the focus in this case i remove the cursor blink from the active input object
object.loseFocusEvent(self)
self.inputActive = false
self.fWindow.setCursorBlink(false)
end
function frame:getFocusEvent()--event which gets fired when the frame gets the focus
local frameList = {}
for k,v in pairs(self.frame.objects[self.zIndex])do
if(self~=v)then
table.insert(frameList,v)
end
end
table.insert(frameList,self)
self.frame.objects[self.zIndex] = frameList
self.changed = true
end
function frame:setMoveable(mv)--you can make the frame moveable (Todo: i want to make all objects moveable, so i can create a ingame gui editor MUHUHHUH)
self.isMoveable = mv
return self;
end
--Frames end
--Timer object
function frame:addTimer(name)--adds the timer object
if(self:getObject(name) == nil)then
local obj = timer:new()
obj.name = name;obj.frame=self;
self:addObject(obj)
return obj;
else
return nil, "id "..name.." already exists";
end
end
function timer:setTime(timer, repeats)--tobecontinued
self.timer = timer
if(repeats==nil)then repeats = -1 end
if(repeats>0)then
self.repeats = repeats
else
self.repeats = -1
end
return self
end
function timer:start(timer, repeats)
self.active = true
if(timer~=nil)then self.timer = timer end
if(repeats~=nil)then self.repeats = repeats end
self.timeObj = os.startTimer(self.timer)
return self
end
function timer:cancel()
self.active = false
os.cancelTimer(self.timeObj)
return self
end
function timer:onCall(func)
self.call = func
return self
end
--Timer end
--Checkbox object
function frame:addCheckbox(name)
if(self:getObject(name) == nil)then
local obj = checkbox:new()
obj.name = name;obj.frame=self;
self:addObject(obj)
return obj;
else
return nil, "id "..name.." already exists";
end
end
function checkbox:setSymbol(symbol)
self.symbol = string.sub(symbol,1,1)
self.changed = true
return self
end
function checkbox:drawObject()
object.drawObject(self) -- Base class
if(self.draw)then
self.frame.fWindow.setCursorPos(self:getAnchorPosition())
self.frame.fWindow.setBackgroundColor(self.bgcolor)
self.frame.fWindow.setTextColor(self.fgcolor)
if(self.value)then
self.frame.fWindow.write(self.symbol)
else
self.frame.fWindow.write(" ")
end
self.changed = false
end
end
function checkbox:mouseEvent(event,typ,x,y) -- we have to switch the order of object.mouseEvent with checkbox:mouseEvent, because the value should be changed before we call user click events
local vx,vy = self:relativeToAbsolutePosition(self:getAnchorPosition())
if(vx<=x)and(vx+self.w>x)and(vy<=y)and(vy+self.h>y)then
if(event=="mouse_click")then
self:setValue(not self.value)
self.changed = true
end
end
if(object.mouseEvent(self,event,typ,x,y))then return true end
return false
end
--Checkbox end
--Radio object
function frame:addRadio(name)
if(self:getObject(name) == nil)then
local obj = radio:new()
obj.name = name;obj.frame=self;
obj.bgcolor = self.bgcolor
obj.fgcolor = self.fgcolor
self:addObject(obj)
return obj;
else
return nil, "id "..name.." already exists";
end
end
function radio:setSymbol(symbol)
self.symbol = string.sub(symbol,1,1)
self.changed = true
return self
end
function radio:addElement(text,x,y,bgcolor,fgcolor)
if(x==nil)or(y==nil)then
table.insert(self.elements,{text=text,bgcolor=(bgcolor ~= nil and bgcolor or self.bgcolor),fgcolor=(fgcolor ~= nil and fgcolor or self.fgcolor),x=0,y=#self.elements})
else
table.insert(self.elements,{text=text,bgcolor=(bgcolor ~= nil and bgcolor or self.bgcolor),fgcolor=(fgcolor ~= nil and fgcolor or self.fgcolor),x=x,y=y})
end
if(#self.elements==1)then
self:setValue(self.elements[1])
end
return self
end
function radio:mouseEvent(event,typ,x,y)
if(object.mouseEvent(self,event,typ,x,y))then
if(#self.elements>0)then
local dx,dy = self:relativeToAbsolutePosition(self:getAnchorPosition())
for _,v in pairs(self.elements)do
if(dx<=x)and(dx+v.x+string.len(v.text)+1>x)and(dy+v.y==y)then
self:setValue(v)
self.changed = true
if(self.changeFunc~=nil)then
self.changeFunc(self)
end
return true
end
end
end
end
return false
end
function radio:drawObject()
object.drawObject(self) -- Base class
if(self.draw)then
if(#self.elements>0)then
for _,v in ipairs(self.elements)do
local objx, objy = self:getAnchorPosition()
self.frame.fWindow.setBackgroundColor(v.bgcolor)
self.frame.fWindow.setTextColor(v.fgcolor)
self.frame.fWindow.setCursorPos(objx+v.x,objy+v.y)
if(v==self.value)then
self.frame.fWindow.write(self.symbol..v.text)
else
self.frame.fWindow.write(" "..v.text)
end
end
end
self.changed = false
end
end
--Radio end
--Label object
function frame:addLabel(name)
if(self:getObject(name) == nil)then
local obj = label:new()
obj.bgcolor = self.bgcolor
obj.fgcolor = self.fgcolor
obj.name=name;obj.frame=self;
self:addObject(obj)
return obj;
else
return nil, "id "..name.." already exists";
end
end
function label:setText(text)
object.setText(self,text)
self.w = string.len(text)
return self
end
function label:drawObject()
object.drawObject(self) -- Base class
if(self.draw)then
self.frame.fWindow.setCursorPos(self:getAnchorPosition())
self.frame.fWindow.setBackgroundColor(self.bgcolor)
self.frame.fWindow.setTextColor(self.fgcolor)
self.frame.fWindow.write(self.text)
self.changed = false
end
end
--Label end
function frame:addInput(name)
if(self:getObject(name) == nil)then
local obj = input:new()
obj.name = name;obj.frame=self;
self:addObject(obj)
return obj;
else
return nil, "id "..name.." already exists";
end
end
function input:mouseEvent(event,typ,x,y)
if(object.mouseEvent(self,event,typ,x,y))then
local anchX,anchY = self:getAnchorPosition()
self.frame.inputActive = true
self.frame.activeInput = self
self.frame.fWindow.setCursorPos(anchX+(string.len(self.value) < self.w-1 and string.len(self.value) or self.w-1),anchY)
self.frame.cursorX = anchX+(string.len(self.value) < self.w-1 and string.len(self.value) or self.w-1)
self.frame.cursorY = anchY
self.frame.fWindow.setCursorBlink(true)
return true
end
return false
end
function input:drawObject()
object.drawObject(self) -- Base class
local text = ""
if(self.draw)then
if(string.len(self.value)>=self.w)then
text = string.sub(self.value, string.len(self.value)-self.w+2, string.len(self.value))
else
text = self.value
end
local n = self.w-string.len(text)
text = text..string.rep(" ", n)
self.frame.fWindow.setCursorPos(self:getAnchorPosition())
self.frame.fWindow.setBackgroundColor(self.bgcolor)
self.frame.fWindow.setTextColor(self.fgcolor)
self.frame.fWindow.write(text)
self.changed = false
end
end
function input:getFocusEvent()
object.getFocusEvent(self)
self.frame.fWindow.setCursorPos(self:getAnchorPosition())
self.frame.fWindow.setCursorBlink(true)
end
function input:loseFocusEvent()
object.loseFocusEvent(self)
self.frame.inputActive = false
self.frame.fWindow.setCursorBlink(false)
end
function frame:addButton(name)
if(self:getObject(name) == nil)then
local obj = button:new()
obj.name = name;obj.frame=self;
self:addObject(obj)
return obj;
else
return nil, "id "..name.." already exists";
end
end
function button:drawObject()
object.drawObject(self) -- Base class
if(self.draw)then
local x,y = self:getAnchorPosition()
local yOffset = getTextVerticalAlign(self.h,self.verticalTextAlign)
self.frame.fWindow.setBackgroundColor(self.bgcolor)
self.frame.fWindow.setTextColor(self.fgcolor)
for line=0,self.h-1 do
self.frame.fWindow.setCursorPos(x,y+line)
if(line==yOffset)then
self.frame.fWindow.write(getTextHorizontalAlign(self.text, self.w, self.horizontalTextAlign))
else
self.frame.fWindow.write(string.rep(" ", self.w))
end
end
self.changed = false
end
end
function frame:addDropdown(name)
if(self:getObject(name) == nil)then
local obj = dropdown:new()
obj.name = name;obj.frame=self;
self:addObject(obj)
return obj;
else
return nil, "id "..name.." already exists";
end
end
function dropdown:addElement(text,bgcolor,fgcolor)
table.insert(self.elements,{text=text,bgcolor=(bgcolor ~= nil and bgcolor or self.bgcolor),fgcolor=(fgcolor ~= nil and fgcolor or self.fgcolor)})
if(#self.elements==1)then
self:setValue(self.elements[1])
end
return self
end
function dropdown:drawObject()
object.drawObject(self) -- Base class
if(self.draw)then
self.frame.fWindow.setCursorPos(self:getAnchorPosition())
self.frame.fWindow.setBackgroundColor(self.value.bgcolor)
self.frame.fWindow.setTextColor(self.value.fgcolor)
self.frame.fWindow.write(getTextHorizontalAlign(self.value.text, self.w, self.horizontalTextAlign))
if(self:isFocusedElement())then
print("asd")
if(#self.elements>0)then
local index = 1
for _,v in ipairs(self.elements)do
local objx, objy = self:getAnchorPosition()
self.frame.fWindow.setBackgroundColor(v.bgcolor)
self.frame.fWindow.setTextColor(v.fgcolor)
self.frame.fWindow.setCursorPos(objx,objy+index)
self.frame.fWindow.write(getTextHorizontalAlign(v.text, self.w, self.horizontalTextAlign))
index = index+1
end
end
end
self.changed = false
end
end
function dropdown:mouseEvent(event,typ,x,y)
object.mouseEvent(self,event,typ,x,y)
if(self:isFocusedElement())then
if(#self.elements>0)then
local dx,dy = self:relativeToAbsolutePosition(self:getAnchorPosition())
local index = 1
for _,b in pairs(self.elements)do
if(dx<=x)and(dx+self.w>x)and(dy+index==y)then
self:setValue(b)
if(self.changeFunc~=nil)then
self.changeFunc(self)
end
self.frame:removeFocusedElement()
return true
end
index = index+1
end
if not((dx<=x)and(dx+self.w>x)and(dy<=y)and(dy+self.h>y))then
self.frame:removeFocusedElement()
end
return true
end
end
return false
end
function frame:addList(name)
if(self:getObject(name) == nil)then
local obj = list:new()
obj.name = name;obj.frame=self;
self:addObject(obj)
return obj;
else
return nil, "id "..name.." already exists";
end
end
function list:drawObject()
object.drawObject(self) -- Base class
if(self.draw)then
self.frame.fWindow.setCursorPos(self:getAnchorPosition())
self.frame.fWindow.setBackgroundColor(self.bgcolor)
self.frame.fWindow.setTextColor(self.fgcolor)
self.frame.fWindow.write(getTextHorizontalAlign(self.value.text, self.w, self.horizontalTextAlign))
if(#self.elements>0)then
local index = 0
for _,v in ipairs(self.elements)do
local objx, objy = self:getAnchorPosition()
self.frame.fWindow.setBackgroundColor(v.bgcolor)
self.frame.fWindow.setTextColor(v.fgcolor)
self.frame.fWindow.setCursorPos(objx,objy+index)
if(v==self.value)then
self.frame.fWindow.write(">"..getTextHorizontalAlign(v.text, self.w, self.horizontalTextAlign))
else
self.frame.fWindow.write(" "..getTextHorizontalAlign(v.text, self.w, self.horizontalTextAlign))
end
index = index+1
end
end
self.changed = false
end
end
function list:mouseEvent(event,typ,x,y)
object.mouseEvent(self,event,typ,x,y)
if(event=="mouse_click")then
if(#self.elements>0)then
local dx,dy = self:relativeToAbsolutePosition(self:getAnchorPosition())
local index = 0
for _,v in pairs(self.elements)do
if(dx<=x)and(dx+self.w>x)and(dy+index==y)then
self:setValue(v)
self.changed = true
if(self.changeFunc~=nil)then
self.changeFunc(self)
end
return true
end
index = index+1
end
end
end
return false
end
function list:addElement(text,bgcolor,fgcolor)
table.insert(self.elements,{text=text,bgcolor=(bgcolor ~= nil and bgcolor or self.bgcolor),fgcolor=(fgcolor ~= nil and fgcolor or self.fgcolor)})
if(#self.elements==1)then
self:setValue(self.elements[1])
end
return self
end
local function checkTimer(timeObject)
for a,b in pairs(activeScreen.objects)do
for k,v in pairs(b)do
if(v.__type=="Timer")and(v.active)then
if(v.timeObj == timeObject)then
v.call(v)
if(v.repeats~=0)then
v.timeObj = os.startTimer(v.timer)
v.repeats = (v.repeats > 0 and v.repeats-1 or v.repeats)
end
end
end
end
end
if(#animations>0)then
for k,v in pairs(animations)do
if(v.timeObj==timeObject)then
v:onPlay()
end
end
end
end
local function handleChangedObjectsEvent()
local changed = activeScreen.changed
for a,b in pairs(activeScreen.objects)do
for k,v in pairs(b)do
if(v.changed)then
changed = true
end
end
end
if(changed)then
if(activeScreen.draw)then
activeScreen:drawObject()
end
end
end
function screen.startUpdate()
handleChangedObjectsEvent()
screen.updater = true
while screen.updater do
local event, p1,p2,p3 = os.pullEvent()
activeScreen.changed = true
if(event=="mouse_click")then
activeScreen:mouseEvent(event,p1,p2,p3)
end
if(event=="mouse_drag")then
activeScreen:mouseEvent(event,p1,p2,p3)
end
if(event=="mouse_up")then
activeScreen:mouseEvent(event,p1,p2,p3)
end
if(event=="timer")then
checkTimer(p1)
end
if(event=="char")or(event=="key")then
activeScreen:keyEvent(event,p1)
end
handleChangedObjectsEvent()
end
end
function screen.stopUpdate()
screen.updater = false
end
function debug(...)
local args = {...}
activeScreen.debugLabel:setPosition(1,activeScreen.h)
local str = "[Debug] "
for k,v in pairs(args)do
str = str..tostring(v)..(#args~=k and ", " or "")
end
activeScreen.debugLabel:setText(str)
activeScreen.debugLabel:show()
end
screen.debug = debug
function screen.get(name)
return screens[name];
end
function screen.getActiveScreen()
return activeScreen
end
return screen;