Update nGUI.lua

This commit is contained in:
Noryie
2022-03-17 20:33:04 +01:00
parent eae7296b15
commit 8887a3f8bc

714
nGUI.lua
View File

@@ -1,40 +1,48 @@
-- Shad's GUI free for everyone to use
frame = { __type = "Frame", name = ""}
frame = { __type = "Frame", name = ""} -- Frame element
frame.__index = frame
button = { __type = "Button", name = ""}
button.__index = button
animation = {__type = "Animation", name = ""}
animation.__index = animation
label = { __type = "Label", name = ""}
label.__index = label
local object = {} -- Base class for all UI elements
textbox = { __type = "Textbox", name = ""}
textbox.__index = textbox
timer = { __type = "Timer", name = ""}
timer.__index = timer
checkbox = { __type = "Checkbox", name = ""}
checkbox.__index = checkbox
local w, h = term.getSize()
local activeFrame
local frames = {}
local animations = {}
frame.new = function(name, screen)
local newElement = {name=name, fWindow = window.create((screen~=nil and screen or term.native()),1,1,w,h), objects={},bgcolor = colors.black, fgcolor=colors.white, title="", titlebgcolor = colors.lightBlue, titlefgcolor = colors.black, align="left"}
if(frames[name] == nil)then
frames[name] = newElement
newElement.fWindow.setVisible(false)
local metaTab = setmetatable(newElement, frame);
metaTab.debugLabel=metaTab:addLabel("DebugLabel")
return metaTab;
else
return frames[name];
end
--Utility Functions:
function getTextAlign(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
--------------
frame.new = function(name, screen)
local usedScreen = screen~=nil and screen or term.native()
local w, h = usedScreen.getSize()
local newElement = {name=name, screen = usedScreen, fWindow = window.create(usedScreen,1,1,w,h),w=w,h=h, objects={},bgcolor = colors.black, fgcolor=colors.white, title="New Frame", titlebgcolor = colors.lightBlue, titlefgcolor = colors.black, textAlign="left",focusedObject={}}
if(frames[name] == nil)then
frames[name] = newElement
newElement.fWindow.setVisible(false)
setmetatable(newElement, {__index = frame});
newElement.debugLabel=newElement:addLabel("DebugLabel")
return newElement;
else
return frames[name];
end
end
function frame:getName()
return self.name
@@ -86,19 +94,222 @@ function frame:hide()
return self
end
function frame:getObject(name)
return self.objects[name]
end
function frame:getFocusedObject()
return self.focusedObject
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:
function object:new()
local newElement = {__type = "Object",name="",drawCalls=0,x=1,y=1,w=1,h=1,textAlign="left",draw=false,changed=true,bgcolor=colors.black,fgcolor=colors.white,text=""}
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="x",bgcolor=colors.lightBlue,fgcolor=colors.black}
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",bgcolor=colors.lightBlue,fgcolor=colors.black,w=5}
setmetatable(newElement, {__index = self})
return newElement
end
button = object:new()
function button:new()
local newElement = {__type = "Button",bgcolor=colors.lightBlue,fgcolor=colors.black,w=5,textAlign="center"}
setmetatable(newElement, {__index = self})
return newElement
end
dropdown = object:new()
function dropdown:new()
local newElement = {__type = "Dropdown",bgcolor=colors.lightBlue,fgcolor=colors.black,w=5,textAlign="center",elements={},selected=""}
setmetatable(newElement, {__index = self})
return newElement
end
--------
function object:show()
if not(self.draw)then
self.draw = true
self.changed = true
end
return self
end
function object:hide()
if(self.draw)then
self.draw = false
self.changed = true
end
return self
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
function object:onClick(func)
self.clickFunc = func
return self
end
function object:setText(text)
self.text = text
self.changed = true
return self
end
function object:onChange(func)
self.changeFunc = func
return self
end
function object:setSize(w,h)
self.w = tonumber(w)
self.h = tonumber(h)
self.changed = true
return self
end
function object:setTextAlign(align)
self.textAlign = align
self.changed = true
return self
end
function object:drawObject() -- Base class for drawing a object
if(self.draw)then
self.drawCalls = self.drawCalls + 1
end
return self
end
function object:IsFocusedElement()
return self == self.frame.focusedObject
end
function frame:addTimer(name)
local newElement = {name=name,frame = self, timer=5, repeats=0}
if(self.objects[name] == nil)then
self.objects[name] = newElement
return setmetatable(newElement, timer);
local obj = timer:new()
self.objects[name] = obj
obj.name = name;obj.frame=self;
return obj;
else
return nil, "id "..name.." already exists";
end
return self
end
function timer:setTime(time, repeats)
self.timer = time
function timer:setTime(timer, repeats)
self.timer = timer
if(repeats>0)then
self.repeats = repeats
else
@@ -107,9 +318,9 @@ function timer:setTime(time, repeats)
return self
end
function timer:start(time, repeats)
function timer:start(timer, repeats)
self.active = true
if(time~=nil)then self.timer = time end
if(timer~=nil)then self.timer = timer end
if(repeats~=nil)then self.repeats = repeats end
self.timeObj = os.startTimer(self.timer)
return self
@@ -127,65 +338,26 @@ function timer:onCall(func)
end
function frame:addCheckbox(name)
local newElement = {name=name,frame = self, x=1, y=1, w=1, h=1, bgcolor=colors.lightBlue, fgcolor=colors.black,symbol="X",changed=true,checked=false,drawCalls=0}
if(self.objects[name] == nil)then
self.objects[name] = newElement
return setmetatable(newElement, checkbox);
local obj = checkbox:new()
self.objects[name] = obj
obj.name = name;obj.frame=self;
return obj;
else
return nil, "id "..name.." already exists";
end
return self
end
function checkbox:show()
if not(self.draw)then
self.draw = true
self.changed = true
end
return self
end
function checkbox:hide()
if (self.draw)then
self.draw = false
self.changed = true
end
return self
end
function checkbox:setPosition(x,y)
self.x = tonumber(x)
self.y = tonumber(y)
self.changed = true
return self
end
function checkbox:setBackground(color)
self.bgcolor = color
self.changed = true
return self
end
function checkbox:setForeground(color)
self.fgcolor = color
self.changed = true
return self
end
function checkbox:setSymbol(symbol)
self.symbol = string.sub(symbol,1,1)
self.changed = true
return self
end
function checkbox:onClick(func)
self.clickFunc = func
return self
end
function checkbox:drawObject()
object.drawObject(self) -- Base class
if(self.draw)then
self.drawCalls= self.drawCalls+1
self.frame.fWindow.setCursorPos(self.x,self.y)
self.frame.fWindow.setBackgroundColor(self.bgcolor)
self.frame.fWindow.setTextColor(self.fgcolor)
@@ -199,66 +371,28 @@ function checkbox:drawObject()
end
function frame:addLabel(name)
local newElement = {name=name,frame = self, x=1, y=1,w=1,h=1, bgcolor=self.bgcolor, fgcolor=self.fgcolor,text="Label",changed=true,drawCalls=0}
if(self.objects[name] == nil)then
self.objects[name] = newElement
return setmetatable(newElement, label);
local obj = label:new()
self.objects[name] = obj
obj.name = name;obj.frame=self;
obj.bgcolor = self.bgcolor
obj.fgcolor = self.fgcolor
return obj;
else
return nil, "id "..name.." already exists";
end
return self
end
function label:show()
if not(self.draw)then
self.draw = true
self.changed = true
end
return self
end
function label:hide()
if (self.draw)then
self.draw = false
self.changed = true
end
return self
end
function label:setPosition(x,y)
self.x = tonumber(x)
self.y = tonumber(y)
self.changed = true
return self
end
function label:setBackground(color)
self.bgcolor = color
self.changed = true
return self
end
function label:setForeground(color)
self.fgcolor = color
self.changed = true
return self
end
function label:setText(text)
self.text = text
object.setText(self,text)
self.w = string.len(text)
self.changed = true
return self
end
function label:onClick(func)
self.clickFunc = func
return self
end
function label:drawObject()
object.drawObject(self) -- Base class
if(self.draw)then
self.drawCalls= self.drawCalls+1
self.frame.fWindow.setCursorPos(self.x,self.y)
self.frame.fWindow.setBackgroundColor(self.bgcolor)
self.frame.fWindow.setTextColor(self.fgcolor)
@@ -267,76 +401,20 @@ function label:drawObject()
end
end
function frame:addTextbox(name)
local newElement = {name=name,frame = self, x=1, y=1, bgcolor=colors.lightBlue, fgcolor=colors.black,w=3,h=1,text="",changed=true,drawCalls=0}
function frame:addInput(name)
if(self.objects[name] == nil)then
self.objects[name] = newElement
return setmetatable(newElement, textbox);
local obj = input:new()
self.objects[name] = obj
obj.name = name;obj.frame=self;
return obj;
else
return nil, "id "..name.." already exists";
end
return self
end
function textbox:show()
if not(self.draw)then
self.draw = true
self.changed = true
end
return self
end
function textbox:hide()
if (self.draw)then
self.draw = false
self.changed = true
end
return self
end
function textbox:setPosition(x,y)
self.x = tonumber(x)
self.y = tonumber(y)
self.changed = true
return self
end
function textbox:setSize(w,l)
self.w = tonumber(w)
self.l = tonumber(l)
self.changed = true
return self
end
function textbox:setBackground(color)
self.bgcolor = color
self.changed = true
return self
end
function textbox:setForeground(color)
self.fgcolor = color
self.changed = true
return self
end
function textbox:setText(text)
self.text = tostring(text)
self.changed = true
return self
end
function textbox:onChange(func)
self.changeFunc = func
return self
end
function textbox:onClick(func)
self.clickFunc = func
return self
end
function textbox:drawObject()
function input:drawObject()
object.drawObject(self) -- Base class
local text = ""
if(self.draw)then
if(string.len(self.text)>=self.w)then
@@ -346,7 +424,6 @@ function textbox:drawObject()
end
local n = self.w-string.len(text)
text = text..string.rep(" ", n)
self.drawCalls= self.drawCalls+1
self.frame.fWindow.setCursorPos(self.x,self.y)
self.frame.fWindow.setBackgroundColor(self.bgcolor)
self.frame.fWindow.setTextColor(self.fgcolor)
@@ -356,142 +433,147 @@ function textbox:drawObject()
end
function frame:addButton(name)
local newElement = {name=name,frame = self, x=1, y=1, bgcolor=colors.lightBlue, fgcolor=colors.black,w=3,h=1,text="Click",align="center",changed=true,drawCalls=0}
if(self.objects[name] == nil)then
self.objects[name] = newElement
return setmetatable(newElement, button);
local obj = button:new()
self.objects[name] = obj
obj.name = name;obj.frame=self;
return obj;
else
return nil, "id "..name.." already exists";
end
return self
end
function button:show()
if not(self.draw)then
self.draw = true
self.changed = true
end
return self
end
function button:hide()
if (self.draw)then
self.draw = false
self.changed = true
end
return self
end
function button:setPosition(x,y)
self.x = tonumber(x)
self.y = tonumber(y)
self.changed = true
return self
end
function button:setSize(w,l)
self.w = tonumber(w)
self.l = tonumber(l)
self.changed = true
return self
end
function button:setTextAlign(align)
self.textAlign = align
self.changed = true
return self
end
function button:setBackground(color)
self.bgcolor = color
self.changed = true
return self
end
function button:setForeground(color)
self.fgcolor = color
self.changed = true
return self
end
function button:setText(text)
self.text = text
self.changed = true
return self
end
function button:onClick(func)
self.clickFunc = func
return self
end
function button:drawObject()
object.drawObject(self) -- Base class
if(self.draw)then
local text = string.sub(self.text, 1, self.w)
local n = self.w-string.len(text)
if(self.textAlign=="left")then
text = text..string.rep(" ", n)
end
if(self.textAlign=="right")then
text = string.rep(" ", n)..text
end
if(self.textAlign=="center")then
text = string.rep(" ", math.floor(n/2))..text..string.rep(" ", math.floor(n/2))
text = text..(string.len(text) < self.w and " " or "")
end
--Debug("Drawcalls: "..self.drawCalls)
self.drawCalls = self.drawCalls+1
self.frame.fWindow.setCursorPos(self.x,self.y)
self.frame.fWindow.setBackgroundColor(self.bgcolor)
self.frame.fWindow.setTextColor(self.fgcolor)
self.frame.fWindow.write(text)
self.frame.fWindow.write(getTextAlign(self.text, self.w, self.textAlign))
self.changed = false
end
end
local function checkMouseClick(clicktype,x,y)
activeFrame.textboxActive = false
for k,v in pairs(activeFrame.objects)do
if not(v.__type=="Timer")then
if(v.x<=x)and(v.x+v.w>x)and(v.y<=y)and(v.y+v.h>y)then
if(v.clickFunc~=nil)then
v.clickFunc(v,clicktype)
function frame:addDropdown(name)
if(self.objects[name] == nil)then
local obj = dropdown:new()
self.objects[name] = obj
obj.name = name;obj.frame=self;
return obj;
else
return nil, "id "..name.." already exists";
end
return self
end
function dropdown:addElement(text)
table.insert(self.elements,text)
return self
end
function dropdown:drawObject()
object.drawObject(self) -- Base class
if(self.draw)then
self.frame.fWindow.setCursorPos(self.x,self.y)
self.frame.fWindow.setBackgroundColor(self.bgcolor)
self.frame.fWindow.setTextColor(self.fgcolor)
self.frame.fWindow.write(getTextAlign(self.selected, self.w, self.textAlign))
if(self.frame:getFocusedObject()==self)then
if(#self.elements>0)then
local index = 1
for _,v in ipairs(self.elements)do
self.frame.fWindow.setCursorPos(self.x,self.y+index)
self.frame.fWindow.setBackgroundColor(self.bgcolor)
self.frame.fWindow.setTextColor(self.fgcolor)
self.frame.fWindow.write(getTextAlign(v, self.w, self.textAlign))
index = index+1
end
end
end
if(v.__type=="Textbox")then
if(v.x<=x)and(v.x+v.w>x)and(v.y<=y)and(v.y+v.h>y)then
v.frame.textboxActive = true
v.frame.activeTextbox = v
v.frame.fWindow.setCursorPos(v.x+(string.len(v.text) < v.w and string.len(v.text) or v.w)-1,v.y)
v.frame.cursorX = v.x+(string.len(v.text) < v.w and string.len(v.text) or v.w-1)
v.frame.cursorY = v.y
v.frame.fWindow.setCursorBlink(true)
end
end
if(v.__type=="Checkbox")then
if(v.x==x)and(v.y==y)then
v.checked = not v.checked
v.changed = true
self.changed = false
end
end
function dropdown:getSelection()
return self.selected
end
local function checkMouseClick(clicktype,x,y)
activeFrame.inputActive = false
local d = activeFrame.focusedObject
if(d.__type=="Dropdown")then
if(d:IsFocusedElement())then
if(#d.elements>0)then
local index = 1
for _,b in pairs(d.elements)do
if(d.x<=x)and(d.x+d.w>x)and(d.y+index<=y)and(d.y+index+d.h>y)then
d.selected = b
if(d.changeFunc~=nil)then
d.changeFunc(d)
end
activeFrame.focusedObject = {}
return
end
index = index+1
end
end
end
end
if not(activeFrame.textboxActive)then
activeFrame.focusedObject = {}
for k,v in pairs(activeFrame.objects)do
if(v.draw~=false)then
if not(v.__type=="Timer")then
if(v.x<=x)and(v.x+v.w>x)and(v.y<=y)and(v.y+v.h>y)then
if(v.clickFunc~=nil)then
v.clickFunc(v,clicktype)
end
activeFrame.focusedObject = v
end
end
if(v.__type=="Input")then
if(v.x<=x)and(v.x+v.w>x)and(v.y<=y)and(v.y+v.h>y)then
v.frame.inputActive = true
v.frame.activeInput = v
v.frame.fWindow.setCursorPos(v.x+(string.len(v.text) < v.w and string.len(v.text) or v.w)-1,v.y)
v.frame.cursorX = v.x+(string.len(v.text) < v.w and string.len(v.text) or v.w-1)
v.frame.cursorY = v.y
v.frame.fWindow.setCursorBlink(true)
end
end
if(v.__type=="Checkbox")then
if(v.x==x)and(v.y==y)then
v.checked = not v.checked
v.changed = true
end
end
end
end
if not(activeFrame.inputActive)then
activeFrame.fWindow.setCursorBlink(false)
end
end
local function checkTimer(timeObject)
for k,v in pairs(activeFrame.objects)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)
for k,v in pairs(activeFrame.objects)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
if(#animations>0)then
for k,v in pairs(animations)do
if(v.timeObj==timeObject)then
v:onPlay()
end
end
end
end
@@ -500,8 +582,8 @@ local function drawObjects()
activeFrame.fWindow.clear()
if(activeFrame.title~="")then
local text = string.sub(activeFrame.title, 1, w)
local n = w-string.len(text)
local text = string.sub(activeFrame.title, 1, activeFrame.w)
local n = activeFrame.w-string.len(text)
if(activeFrame.textAlign=="left")then
text = text..string.rep(" ", n)
end
@@ -510,7 +592,7 @@ local function drawObjects()
end
if(activeFrame.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 "")
text = text..(string.len(text) < activeFrame.w and " " or "")
end
activeFrame.fWindow.setBackgroundColor(activeFrame.titlebgcolor)
activeFrame.fWindow.setTextColor(activeFrame.titlefgcolor)
@@ -522,7 +604,7 @@ local function drawObjects()
v:drawObject()
end
end
if(activeFrame.textboxActive)then
if(activeFrame.inputActive)then
activeFrame.fWindow.setCursorPos(activeFrame.cursorX, activeFrame.cursorY)
end
activeFrame.fWindow.setBackgroundColor(activeFrame.bgcolor)
@@ -545,25 +627,27 @@ local changed = activeFrame.changed
end
local function checkKeyboardInput(event, key)
if(activeFrame.textboxActive)then
if(event=="key")then
if(key==259)then
activeFrame.activeTextbox:setText(string.sub(activeFrame.activeTextbox.text,1,string.len(activeFrame.activeTextbox.text)-1))
end
if(key==257)then
if(activeFrame.textboxActive)then
activeFrame.textboxActive = false
activeFrame.fWindow.setCursorBlink(false)
if(activeFrame.inputActive)then
if(activeFrame.activeInput.draw)then
if(event=="key")then
if(key==259)then
activeFrame.activeInput:setText(string.sub(activeFrame.activeInput.text,1,string.len(activeFrame.activeInput.text)-1))
end
if(key==257)then
if(activeFrame.inputActive)then
activeFrame.inputActive = false
activeFrame.fWindow.setCursorBlink(false)
end
end
end
end
if(event=="char")then
activeFrame.activeTextbox:setText(activeFrame.activeTextbox.text..key)
end
activeFrame.cursorX = activeFrame.activeTextbox.x+(string.len(activeFrame.activeTextbox.text) < activeFrame.activeTextbox.w and string.len(activeFrame.activeTextbox.text) or activeFrame.activeTextbox.w-1)
activeFrame.cursorY = activeFrame.activeTextbox.y
if(activeFrame.activeTextbox.changeFunc~=nil)then
activeFrame.activeTextbox.changeFunc(activeFrame.activeTextbox)
if(event=="char")then
activeFrame.activeInput:setText(activeFrame.activeInput.text..key)
end
activeFrame.cursorX = activeFrame.activeInput.x+(string.len(activeFrame.activeInput.text) < activeFrame.activeInput.w and string.len(activeFrame.activeInput.text) or activeFrame.activeInput.w-1)
activeFrame.cursorY = activeFrame.activeInput.y
if(activeFrame.activeInput.changeFunc~=nil)then
activeFrame.activeInput.changeFunc(activeFrame.activeInput)
end
end
end
end
@@ -589,7 +673,7 @@ end
function debug(...)
local args = {...}
activeFrame.debugLabel:setPosition(1,h)
activeFrame.debugLabel:setPosition(1,activeFrame.h)
local str = "[Debug] "
for k,v in pairs(args)do
str = str..tostring(v)..(#args~=k and ", " or "")