From 8887a3f8bc7af667dbdc3bd43ca1771aaa7d5457 Mon Sep 17 00:00:00 2001 From: Noryie Date: Thu, 17 Mar 2022 20:33:04 +0100 Subject: [PATCH] Update nGUI.lua --- nGUI.lua | 714 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 399 insertions(+), 315 deletions(-) diff --git a/nGUI.lua b/nGUI.lua index f607156..8fdb3b9 100644 --- a/nGUI.lua +++ b/nGUI.lua @@ -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 "")