From 22405d176f79100743ae2058c9a52caa8323b230 Mon Sep 17 00:00:00 2001 From: Robert Jelic <36573031+NoryiE@users.noreply.github.com> Date: Sun, 3 Apr 2022 21:05:55 +0200 Subject: [PATCH] Create basalt.lua --- basalt.lua | 2600 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2600 insertions(+) create mode 100644 basalt.lua diff --git a/basalt.lua b/basalt.lua new file mode 100644 index 0000000..72871a8 --- /dev/null +++ b/basalt.lua @@ -0,0 +1,2600 @@ +animation = {__type = "Animation", name = ""} +animation.__index = animation + +local Basalt = {debugger=true,updater=false} + +local object = {} -- Base class for all UI elements + +local activeFrame +local _frames = {} +local animationQueue = {} +local keyModifier = {} +local parentTerminal = term.current() + + +--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(...) + end + end + end +end + +local function sign(v) + return (v >= 0 and 1) or -1 +end + +local function round(v, bracket) + bracket = bracket or 1 + return math.floor(v/bracket + sign(v) * 0.5) * bracket +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,xOriginal=1,yOriginal=1,w=1,h=1,draw=false,bgcolor=colors.black,fgcolor=colors.white,hanchor="left",vanchor="top",ignYOffset=false,ignXOffset=false} + 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.gray,fgcolor=colors.black, value = false} + setmetatable(newElement, {__index = self}) + return newElement +end + +radio = object:new() +function radio:new() + local newElement = {__type = "Radio",symbol="\7",symbolFGColor=colors.black,symbolBGColor=colors.gray,zIndex=5,bgcolor=colors.gray,fgcolor=colors.black, value = "", items={}} + setmetatable(newElement, {__index = self}) + return newElement +end + +label = object:new() +function label:new() + local newElement = {__type = "Label", value=""} + setmetatable(newElement, {__index = self}) + return newElement +end + +input = object:new() +function input:new() + local newElement = {__type = "Input",zIndex=5,bgcolor=colors.gray,fgcolor=colors.black,w=10, value = "", iType = "text"} + setmetatable(newElement, {__index = self}) + return newElement +end + +button = object:new() +function button:new() + local newElement = {__type = "Button",zIndex=5,bgcolor=colors.gray,fgcolor=colors.black,w=8,horizontalTextAlign="center",verticalTextAlign="center",value=""} + setmetatable(newElement, {__index = self}) + return newElement +end + +dropdown = object:new() +function dropdown:new() + local newElement = {__type = "Dropdown",zIndex=9,bgcolor=colors.gray,fgcolor=colors.black,w=10,horizontalTextAlign="center",items={},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",index=1,colorIndex=1,textColorIndex=1,zIndex=5,activeItemBackground=colors.black,activeItemForeground=colors.lightGray,itemColors={colors.gray},itemTextColors={colors.black},symbol="",bgcolor=colors.gray,fgcolor=colors.black,w=15,h=6,horizontalTextAlign="center",items={},value={text=""}} + setmetatable(newElement, {__index = self}) + return newElement +end + +textfield = object:new() +function textfield:new() + local newElement = {__type = "Textfield",hIndex=1,wIndex=1,textX=1,textY=1,zIndex=5,bgcolor=colors.gray,fgcolor=colors.black,w=20,h=6,value="",lines={""}} + setmetatable(newElement, {__index = self}) + return newElement +end + +scrollbar = object:new() +function scrollbar:new() + local newElement = {__type = "Scrollbar",index=1,value=1,zIndex=10,bgcolor=colors.gray,fgcolor=colors.black,w=8,h=1,barType="vertical", symbolColor=colors.black, symbol = " ", symbolSize = 1, bgSymbol = "\127"} + setmetatable(newElement, {__index = self}) + return newElement +end + +slider = object:new() +function slider:new() + local newElement = {__type = "Slider",value=1,zIndex=5,bgcolor=colors.lightGray,fgcolor=colors.gray,w=5,h=1,barType="horizontal", symbolColor=colors.black, symbol = " ", bgSymbol = "\140"} + setmetatable(newElement, {__index = self}) + return newElement +end + +program = object:new() +function program:new() + local newElement = {__type = "Program",value="",zIndex=5,bgcolor=colors.black,fgcolor=colors.white,w=12,h=6} + setmetatable(newElement, {__index = self}) + return newElement +end + +animation = object:new() +function animation:new() + local newElement = {__type = "Animation",animations={},nextWaitTimer=0,index=1,infiniteloop=false} + 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=10, xOffset=0, yOffset=0, fWindow = window.create(parent,1,1,w,h),x=1,y=1,w=w,h=h, objects={},objZKeys={},bgcolor = colors.lightGray, fgcolor=colors.black,barActive = false, title="New Frame", titlebgcolor = colors.gray, titlefgcolor = colors.black, horizontalTextAlign="left",focusedObject={}, isMoveable = false,cursorBlink=false} + end + setmetatable(newElement, {__index = self}) + return newElement +end +-------- + +--object methods +function object:show() + self.draw = true + self:updateVisuals() + return self +end + +function object:hide() + self.draw = false + self:updateVisuals() + 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,typ) + if(typ=="r")or(typ=="relative")then + self.x = self.xOriginal+tonumber(x) + self.y = self.yOriginal+tonumber(y) + self.xOriginal = self.xOriginal+tonumber(x) + self.yOriginal = self.yOriginal+tonumber(y) + else + self.x = tonumber(x) + self.y = tonumber(y) + self.xOriginal = tonumber(x) + self.yOriginal = tonumber(y) + end + self:updateVisuals() + return self +end + +function object:setBackground(color) + self.bgcolor = color + self:updateVisuals() + return self +end + +function object:setForeground(color) + self.fgcolor = color + self:updateVisuals() + 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:onClickUp(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:onKey(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:setSize(w,h) + self.w = tonumber(w) + self.h = tonumber(h) + self:updateVisuals() + return self +end + +function object:getHeight() + return self.h +end + +function object:getWidth() + return self.w +end + +function object:linkTo(obj) + obj:link(self) + return self +end + +function object:link(obj) -- does not work correctly needs a fix dsfgsdfgfsdg + 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) + if(self.value~=val)then + self.value = val + self:updateVisuals() + end + for _,v in pairs(self.links)do + if(v.value~=val)then + v.value = val + v:updateVisuals() + end + end + + callAll(self.changeFunc,self,self.args) + return self +end + +function object:getValue() + return self.value; +end + +function object:setCustomArgs(args) + self.args = args + return self; +end + +function object:setTextAlign(halign,valign) + self.horizontalTextAlign = halign + if(valign~=nil)then self.verticalTextAlign = valign end + self:updateVisuals() + return self +end + +function object:updateVisuals() + self.changed = true +end + +function object:drawObject() + if(self.draw)then + self.drawCalls = self.drawCalls + 1 + if(self.frame~=nil)then + if not(self.ignXOffset)then + if(self.hanchor=="right")then + self.x = self.xOriginal + self.frame.xOffset + else + self.x = self.xOriginal - self.frame.xOffset + end + end + if not(self.ignYOffset)then + if(self.vanchor=="bottom")then + self.y = self.yOriginal + self.frame.yOffset + else + self.y = self.yOriginal - self.frame.yOffset + end + end + if(self.__type=="Frame")then + self.fWindow.reposition(self.x,self.y) + end + end + self.changed=false + return true + end + return false +end + +function object:ignoreScroll(vertical,horizontal) + self.ignYOffset = vertical + self.ignXOffset = horizontal + return self +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: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:isFocusedObject() + 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,event,typ,x,y) + end + elseif(event=="mouse_up")then + if(self.upFunc~=nil)then + callAll(self.upFunc,self,event,typ,x,y) + end + elseif(event=="mouse_drag")then + if(self.dragFunc~=nil)then + callAll(self.dragFunc,self,event,typ,x,y) + end + end + return true + end + return false +end + +function object:eventListener(event,p1,p2,p3,p4) + +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:isFocusedObject())then + if(self.loseFocusEventFunc~=nil)then + callAll(self.loseFocusEventFunc,self) + end + return true + end + return false +end + +function object:getFocusEvent() + if not(self:isFocusedObject())then + if(self.getFocusEventFunc~=nil)then + callAll(self.getFocusEventFunc,self) + end + return true + end + return false +end + +function object:setZIndex(index) + self.frame:changeZIndexOfObject(self,index) + return self +end + + +function object:setParent(frame) + if(frame.__type=="Frame")then + if(self.frame~=nil)then + self.frame:removeObject(self) + end + self.frame = frame + self.frame:addObject(self) + if(self.draw)then + self:show() + end + end + return self +end +--object end + +--Frame object +Basalt.createFrame = function(name, scrn) -- this is also just a frame, but its a level 0 frame +local obj = frame:new(name,scrn or parentTerminal) + if(_frames[name] == nil)then + _frames[name] = obj + obj.fWindow.setVisible(false) + return obj; + else + return _frames[name]; + end +end + +Basalt.removeFrame = function(name) + _frames[name].fWindow.setVisible(false) + _frames[name] = nil +end + +function frame:addFrame(frameObj) -- with this you also create frames, but it needs to have a parent frame (level 1 or more 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(active) -- shows the top bar + self.barActive = active ~= nil and active or true + self:updateVisuals() + return self +end + +function frame:isModifierActive(key) + if(key==340)or(key=="shift")then + return keyModifier[340] + end + if(key==341)or(key=="ctrl")then + return keyModifier[341] + end + if(key==342)or(key=="alt")then + return keyModifier[342] + end + return keyModifier[key] +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:updateVisuals() + return self +end + +function frame:setTitleAlign(align) -- changes title align + self.horizontalTextAlign = align + self:updateVisuals() + 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:setXOffset(offset) -- frame x offset + self.xOffset = offset + self:updateVisuals() + return self +end + +function frame:setYOffset(offset) -- frame y offset + self.yOffset = offset + self:updateVisuals() + return self +end + +function frame:setPosition(x,y,typ) -- pos + object.setPosition(self,x,y,typ) + self.fWindow.reposition(self.x,self.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 + activeFrame = 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:updateVisuals() + self.draw = false + self.fWindow.setVisible(false) + _frames[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 + 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 gets called internally + if(self.objects[obj.zIndex]==nil)then + for x=0,#self.objZKeys+1 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 + self.objects[obj.zIndex] = {} + end + table.insert(self.objects[obj.zIndex],obj) +end + +function frame:drawObject() -- this draws the frame, you dont need that function, it gets called internally + if(object.drawObject(self))then + 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 + + for _,b in rpairs(self.objZKeys)do + for k,v in pairs(self.objects[b])do + if(v.draw~=nil)then + v:drawObject() + end + end + end + + --term.redirect(self.fWindow) + self.fWindow.setBackgroundColor(self.bgcolor) + self.fWindow.setTextColor(self.cursorColor or self.fgcolor) + self.fWindow.setVisible(true) + self.fWindow.redraw() + + if(self.focusedObject.cursorX~=nil)and(self.focusedObject.cursorY~=nil)then + self.fWindow.setCursorPos(self.focusedObject.cursorX, self.focusedObject.cursorY) + end + + if(self.focusedObject.__type=="Program")then + if not(self.focusedObject.process:isDead())then + self.cursorColor = self.focusedObject.pWindow.getTextColor() + term.redirect(self.focusedObject.pWindow) + self.focusedObject.pWindow.restoreCursor() + end + else + self.fWindow.setCursorBlink(self.cursorBlink) + end + if(self.focusedObject.__type=="Frame")then + term.redirect(self.focusedObject.fWindow) + self.focusedObject.fWindow.restoreCursor() + end + end + end +end + +function frame:mouseEvent(event,typ,x,y) -- internal mouse event, should make it local but as lazy as 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)+self.frame.xOffset,y-(parentY-1)+self.frame.yOffset) + 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 + + for _,b in pairs(self.objZKeys)do + for _,v in rpairs(self.objects[b])do + if(v.draw~=false)then + if(v.__type=="Frame")then + v:removeFocusedElement() + end + end + end + end + + for _,b in pairs(self.objZKeys)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:eventListener(event,p1,p2,p3,p4) + local keys = {} + local terminatingprogram = false + 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 + v:eventListener(event,p1,p2,p3,p4) + + if(v:isFocusedObject()and(v.__type=="Program"))then + terminatingprogram = true + end + end + end + end + --if(event=="terminate")then Basalt.debug(terminatingprogram) end -- kack buggy + if(event=="terminate")and(terminatingprogram == false)then + if(self.terminatedTime==nil)then self.terminatedTime = 0 end + if(self.terminatedTime+5<=os.clock())then + --if("asd"=="b")then + Basalt.stopUpdate() + for k,v in pairs(_frames)do + v:hide() + end + term.redirect(parentTerminal) + term.setCursorPos(1,1) + term.clear() + --end + end + end +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 + return false +end + +function frame:onScrollEvent(scrollbar) + if(scrollbar.name == self.name.."__generatedVScrollbar")then + self.yOffset = round((scrollbar.index-1) * scrollbar:getLowestValue()) + if(self.yOffset<0)then self.yOffset = 0 end + end + if(scrollbar.name == self.name.."__generatedHScrollbar")then + self.xOffset = round((scrollbar.index-1) * scrollbar:getLowestValue()) + if(self.xOffset<0)then self.xOffset = 0 end + end +end + +function frame:createScrollbar(typ, val) + if(typ=="horizontal")then + if(self.horizontalScrollbar==nil)then + self.horizontalScrollbar = self:addScrollbar(self.name.."__generatedHScrollbar"):setAnchor("bottom"):setSize(self.w,1):setBarType("horizontal") + self.horizontalScrollbar:onChange(function(self) self.frame.onScrollEvent(self.frame,self.frame.horizontalScrollbar) end) + self.horizontalScrollbar:setZIndex(15) + self.maxXOffset = val or 10 + self.horizontalScrollbar:ignoreScroll(true,true):setSymbolSize(self.w-self.maxXOffset >= 1 and self.w-self.maxXOffset or 1 ) + if(self.verticalScrollBar~=nil)and(self.verticalScrollBar.name==self.name.."__generatedVScrollbar")then + self.horizontalScrollbar:setSize(self.w-1,1) + end + end + else + if(self.verticalScrollBar==nil)then + self.verticalScrollBar = self:addScrollbar(self.name.."__generatedVScrollbar"):setAnchor("right"):setSize(1,self.h) + self.verticalScrollBar:onChange(function(self) self.frame.onScrollEvent(self.frame,self.frame.verticalScrollBar) end) + self.verticalScrollBar:setZIndex(15) + self.maxYOffset = val or 10 + self.verticalScrollBar:setMaxValue(self.maxYOffset) + self.verticalScrollBar:ignoreScroll(true,true):setSymbolSize(self.h-self.maxYOffset >= 1 and self.h-self.maxYOffset or 1 ) + if(self.horizontalScrollbar~=nil)and(self.horizontalScrollbar.name==self.name.."__generatedHScrollbar")then + self.horizontalScrollbar:setSize(self.w-1,1) + end + end + end + return self +end + +function frame:setMaxOffset(typ, val) + if(typ=="horizontal")then + if(self.horizontalScrollbar==nil)then + self.maxXOffset = val or 10 + self.horizontalScrollbar:setSymbolSize(self.w-self.maxXOffset >= 1 and self.w-self.maxXOffset or 1 ) + end + end + if(typ=="vertical")then + if(self.verticalScrollBar==nil)then + self.maxYOffset = val or 10 + self.verticalScrollBar:setSymbolSize(self.h-self.maxYOffset >= 1 and self.h-self.maxYOffset or 1 ) + end + end + return self +end + +function frame:showScrollbar(typ) + if(typ=="horizontal")then + self.horizontalScrollbar:show() + else + self.verticalScrollBar:show() + end + return self +end + +function frame:setCustomScrollbar(typ,bar) + if(bar~=nil)then + if(typ=="horizontal")then + if(self.horizontalScrollbar~=nil)and(self.horizontalScrollbar.name==self.name.."__generatedHScrollbar")then + self:removeObject(self.horizontalScrollbar) + end + self.horizontalScrollbar = bar + else + if(self.verticalScrollBar~=nil)and(self.verticalScrollBar.name==self.name.."__generatedVScrollbar")then + self:removeObject(self.verticalScrollBar) + end + self.verticalScrollBar = bar + end + end + return self +end + +function frame:getScrollbar(typ) + if(typ=="horizontal")then + return self.horizontalScrollbar + else + return self.verticalScrollBar + end +end + +function frame:hideScrollbar(typ) + if(typ=="horizontal")then + if(self.horizontalScrollbar~=nil)then + self.horizontalScrollbar:hide() + end + else + if(self.verticalScrollBar~=nil)then + self.verticalScrollBar:hide() + end + end + return self +end + +function frame:setCursorBlink(bool,color) + if(self.frame~=nil)then + --self.frame:setCursorBlink(bool) + end + self.cursorBlink = bool + self.cursorColor = color or self.fgcolor + return self +end + +function frame:changeZIndexOfObject(obj, zindex) + self:removeObject(obj) + 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 + return self +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.cursorBlink = 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 +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:updateVisuals() + 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 + 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) + 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 + obj.symbolBGColor = self.bgcolor + obj.symbolFGColor = 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:updateVisuals() + return self +end + +function radio:setSymbolForegroundColor(color) + self.symbolFGColor = color + self:updateVisuals() + return self +end + +function radio:setSymbolBackgroundColor(color) + self.symbolBGColor = color + self:updateVisuals() + return self +end + +function radio:addItem(text,x,y,bgcolor,fgcolor,...) + if(x==nil)or(y==nil)then + table.insert(self.items,{text=text,bgcolor=(bgcolor ~= nil and bgcolor or self.bgcolor),fgcolor=(fgcolor ~= nil and fgcolor or self.fgcolor),x=0,y=#self.items,vars=table.pack(...)}) + else + table.insert(self.items,{text=text,bgcolor=(bgcolor ~= nil and bgcolor or self.bgcolor),fgcolor=(fgcolor ~= nil and fgcolor or self.fgcolor),x=x,y=y,vars=table.pack(...)}) + end + if(#self.items==1)then + self:setValue(self.items[1]) + end + return self +end + +function radio:removeItem(item) + if(type(item)=="table")then + for k,v in pairs(self.items)do + if(v==item)then + table.remove(self.items,k) + break; + end + end + elseif(type(item)=="number")then + if(self.items[item]~=nil)then + table.remove(self.items,item) + end + end + return self +end + +function radio:mouseEvent(event,typ,x,y) + if(#self.items>0)then + local dx,dy = self:relativeToAbsolutePosition(self:getAnchorPosition()) + for _,v in pairs(self.items)do + if(dx<=x)and(dx+v.x+string.len(v.text)+1>x)and(dy+v.y==y)then + if(event=="mouse_click")and(typ==1)then self:setValue(v) end + if(self.frame~=nil)then self.frame:setFocusedElement(self) end + if(event=="mouse_click")then + if(self.clickFunc~=nil)then + callAll(self.clickFunc,self,event,typ,x,y) + end + elseif(event=="mouse_up")then + if(self.upFunc~=nil)then + callAll(self.upFunc,self,event,typ,x,y) + end + elseif(event=="mouse_drag")then + if(self.dragFunc~=nil)then + callAll(self.dragFunc,self,event,typ,x,y) + end + end + return true + end + end + end + return false +end + +function radio:drawObject() + object.drawObject(self) -- Base class + if(self.draw)then + if(#self.items>0)then + for _,v in ipairs(self.items)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 + if(self.symbolBGColor~=nil)then self.frame.fWindow.setBackgroundColor(self.symbolBGColor) end + if(self.symbolFGColor~=nil)then self.frame.fWindow.setTextColor(self.symbolFGColor) end + self.frame.fWindow.write(self.symbol) + if(self.symbolBGColor~=nil)then self.frame.fWindow.setBackgroundColor(v.bgcolor) end + if(self.symbolFGColor~=nil)then self.frame.fWindow.setTextColor(v.fgcolor) end + self.frame.fWindow.write(v.text) + else + if(self.symbolBGColor~=nil)then self.frame.fWindow.setBackgroundColor(self.symbolBGColor) end + if(self.symbolFGColor~=nil)then self.frame.fWindow.setTextColor(self.symbolFGColor) end + self.frame.fWindow.write(" ") + if(self.symbolBGColor~=nil)then self.frame.fWindow.setBackgroundColor(v.bgcolor) end + if(self.symbolFGColor~=nil)then self.frame.fWindow.setTextColor(v.fgcolor) end + self.frame.fWindow.write(v.text) + end + end + end + 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) + self:setValue(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(tostring(self.value):sub(1,self.w)) + 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:setInputType(typ) + self.iType = typ + self:updateVisuals() + return self +end + +function input:mouseEvent(event,typ,x,y) + if(object.mouseEvent(self,event,typ,x,y))then + local anchX,anchY = self:getAnchorPosition() + self.frame.fWindow.setCursorPos(anchX+(string.len(self.value) < self.w-1 and string.len(self.value) or self.w-1),anchY) + self.cursorX = anchX+(string.len(self.value) < self.w-1 and string.len(self.value) or self.w-1) + self.cursorY = anchY + self.frame.fWindow.setCursorBlink(true) + return true + end + return false +end + +function input:getValue() + if(self.iType=="number")then + if(tonumber(self.value)~=nil)then + return tonumber(self.value) + else + self:setValue(0) + return 0 + end + end + return self.value +end + +function input:keyEvent(event,key) + if(self:isFocusedObject())then + if(self.draw)then + if(event=="key")then + if(key==259)then + self:setValue(string.sub(self.value,1,string.len(self.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 + if(self.iType=="number")then + local cache = self.value + if (key==".")or(tonumber(key)~=nil)then + self:setValue(self.value..key) + end + if(tonumber(self.value)==nil)then + self:setValue(cache) + end + else + self:setValue(self.value..key) + end + end + local anchX,anchY = self:getAnchorPosition() + self.cursorX = anchX+(string.len(self.value) < self.w and string.len(self.value) or self.w-1) + self.cursorY = anchY + if(self.changeFunc~=nil)then + self.changeFunc(self.acveInput) + end + end + end +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)) + if(self.iType=="password")then + text = string.sub(string.rep("*",self.value:len()), string.len(self.value)-self.w+2, string.len(self.value)) + else + text = string.sub(self.value, string.len(self.value)-self.w+2, string.len(self.value)) + end + else + if(self.iType=="password")then + text = string.rep("*",self.value:len()) + else + text = self.value + end + + 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) + end +end + +function input:getFocusEvent() + if(object.getFocusEvent(self))then + self.frame:setCursorBlink(true,self.fgcolor) + self:updateVisuals() + end +end + +function input:loseFocusEvent() + if(object.loseFocusEvent(self))then + if(self.iType=="number")then + if(tonumber(self.value)==nil)then + self:setValue(0) + end + end + self.frame:setCursorBlink(false) + self:updateVisuals() + end +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.value, self.w, self.horizontalTextAlign)) + else + self.frame.fWindow.write(string.rep(" ", self.w)) + end + end + end +end + +function button:setText(text) + return self:setValue(text) +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:addItem(text,bgcolor,fgcolor,...) + table.insert(self.items,{text=text,bgcolor=(bgcolor ~= nil and bgcolor or self.bgcolor),fgcolor=(fgcolor ~= nil and fgcolor or self.fgcolor),vars=...}) + if(#self.items==1)then + self:setValue(self.items[1]) + end + return self +end + +function dropdown:removeItem(item) + if(type(item)=="table")then + for k,v in pairs(self.items)do + if(v==item)then + table.remove(self.items,k) + break; + end + end + elseif(type(item)=="number")then + if(self.items[item]~=nil)then + table.remove(self.items,item) + end + end + return self +end + +function dropdown:setActiveItemBackground(color) + self.activeItemBackground = color + self:updateVisuals() + return self +end + +function dropdown:setActiveItemForeground(color) + self.activeItemForeground = color + self:updateVisuals() + return self +end + +function dropdown:setItemColors(...) + self.itemColors = table.pack(...) + self.itemColorIndex = 1 + self:updateVisuals() + return self +end + +function dropdown:setItemTextColors(...) + self.itemTextColors = table.pack(...) + self.itemTextColorIndex = 1 + self:updateVisuals() + 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:isFocusedObject())then + if(#self.items>0)then + self.itemColorIndex = 1 + self.itemTextColorIndex = 1 + local index = 1 + for _,v in ipairs(self.items)do + local objx, objy = self:getAnchorPosition() + self.frame.fWindow.setBackgroundColor(v.bgcolor) + if(self.itemColors~=nil)then + if(#self.itemColors>0)then + self.frame.fWindow.setBackgroundColor(self.itemColors[self.itemColorIndex]) + self.itemColorIndex = self.itemColorIndex + 1 + if(self.itemColorIndex>#self.itemColors)then + self.itemColorIndex = 1 + end + end + end + if(self.itemTextColors~=nil)then + if(#self.itemTextColors>0)then + self.frame.fWindow.setTextColor(self.itemTextColors[self.itemTextColorIndex]) + self.itemTextColorIndex = self.itemTextColorIndex + 1 + if(self.itemTextColorIndex>#self.itemTextColors)then + self.itemTextColorIndex = 1 + end + end + end + if(v==self.value)then + if(self.activeItemBackground~=nil)then + self.frame.fWindow.setBackgroundColor(self.activeItemBackground) + end + if(self.activeItemForeground~=nil)then + self.frame.fWindow.setTextColor(self.activeItemForeground) + end + end + 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 + end +end + +function dropdown:mouseEvent(event,typ,x,y) + object.mouseEvent(self,event,typ,x,y) + if(self:isFocusedObject())then + if(#self.items>0)then + local dx,dy = self:relativeToAbsolutePosition(self:getAnchorPosition()) + local index = 1 + for _,b in pairs(self.items)do + if(dx<=x)and(dx+self.w>x)and(dy+index==y)then + if(event=="mouse_click")and(typ==1)then self:setValue(b) end + 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.colorIndex = 1 + self.textColorIndex = 1 + for index=0,self.h-1 do + self.frame.fWindow.setCursorPos(self:getAnchorPosition(self.x,self.y+index)) + self.frame.fWindow.setBackgroundColor(self.bgcolor) + + if(self.items[index+self.index]~=nil)then + local bgCol = self.items[index+self.index].bgcolor or self.itemColors[self.colorIndex] + local fgCol = self.items[index+self.index].fgcolor or self.itemTextColors[self.textColorIndex] + if(self.items[index+self.index].bgcolor == nil)then self.colorIndex = self.colorIndex+1 end + if(self.items[index+self.index].fgcolor == nil)then self.textColorIndex = self.textColorIndex+1 end + if(self.colorIndex>#self.itemColors)then self.colorIndex = 1 end + if(self.textColorIndex>#self.itemTextColors)then self.textColorIndex = 1 end + self.frame.fWindow.setBackgroundColor(bgCol) + self.frame.fWindow.setTextColor(fgCol) + if(self.items[index+self.index]==self.value)then + if(self.activeItemBackground~=nil)then self.frame.fWindow.setBackgroundColor(self.activeItemBackground) end + if(self.activeItemForeground~=nil)then self.frame.fWindow.setTextColor(self.activeItemForeground) end + self.frame.fWindow.write(self.symbol..getTextHorizontalAlign(self.items[index+self.index].text, self.w-string.len(self.symbol), self.horizontalTextAlign)) + else + self.frame.fWindow.write(string.rep(" ", string.len(self.symbol))..getTextHorizontalAlign(self.items[index+self.index].text, self.w-string.len(self.symbol), self.horizontalTextAlign)) + end + else + self.frame.fWindow.write(getTextHorizontalAlign(" ", self.w, self.horizontalTextAlign)) + end + end + end +end + +function list:mouseEvent(event,typ,x,y) + if(object.mouseEvent(self,event,typ,x,y))then + if(event=="mouse_click")or(event=="mouse_drag")then -- remove mouse_drag if i want to make objects moveable uwuwuwuw + if(typ==1)then + if(#self.items>0)then + local dx,dy = self:relativeToAbsolutePosition(self:getAnchorPosition()) + for index=0,self.h do + if(self.items[index+self.index]~=nil)then + if(dx<=x)and(dx+self.w>x)and(dy+index==y)then + self:setValue(self.items[index+self.index]) + self:updateVisuals() + return true + end + end + end + end + end + end + if(event=="mouse_scroll")then + self.index = self.index+typ + if(self.index<1)then self.index = 1 end + if(typ==1)then + if(#self.items>self.h)then + if(self.index>#self.items+1-self.h)then + self.index = #self.items+1-self.h + end + else + self.index = self.index-1 + end + end + self:updateVisuals() + return true + end + end + return false +end + +function list:onScrollbarChangeEvent(scrollbar) + --self.index = (scrollbar.value/(scrollbar.maxValue/(scrollbar.barType=="vertical" and scrollbar.h or scrollbar.w))) + self.index = math.floor(scrollbar.value) + if(self.index<1)then self.index = 1 end + if(self.index>#self.items+1-self.h)then self.index = #self.items+1-self.h end +end + +function list:addScrollbar(obj) + if(obj.__type=="Scrollbar")then + self.scrollbar = obj + self.scrollbar:setMaxValue(#self.items+1-self.h) + self.scrollbar.args = self + self.scrollbar:onChange(function(scrlb,lst) lst:onScrollbarChangeEvent(scrlb) end) + end + return self +end + +function list:addItem(text,bgcolor,fgcolor,...) + table.insert(self.items,{text=text,bgcolor=bgcolor,fgcolor=fgcolor,vars=...}) + if(#self.items==1)then + self:setValue(self.items[1]) + end + if(self.scrollbar~=nil)then + self.scrollbar:setMaxValue(#self.items-self.h) + end + return self +end + +function list:setItemColors(...) + self.itemColors = table.pack(...) + self:updateVisuals() + return self +end + +function list:setItemTextColors(...) + self.itemTextColors = table.pack(...) + self:updateVisuals() + return self +end + +function list:setIndex(index) + self.index = index + self:updateVisuals() + return self +end + +function list:removeItem(item) + if(type(item)=="table")then + for k,v in pairs(self.items)do + if(v==item)then + table.remove(self.items,k) + break; + end + end + elseif(type(item)=="number")then + if(self.items[item]~=nil)then + table.remove(self.items,item) + end + end + + if(self.scrollbar~=nil)then + self.scrollbar:setMaxValue(#self.items-self.h) + end + return self +end + +function list:setSymbol(symbol) + self.symbol = string.sub(symbol,1,1) + self:updateVisuals() + return self +end + +function list:setActiveItemBackground(color) + self.activeItemBackground = color + self:updateVisuals() + return self +end + +function list:setActiveItemForeground(color) + self.activeItemForeground = color + self:updateVisuals() + return self +end + +function frame:addTextfield(name) + if(self:getObject(name) == nil)then + local obj = textfield:new() + obj.name = name;obj.frame=self; + self:addObject(obj) + return obj; + else + return nil, "id "..name.." already exists"; + end +end + +function textfield:keyEvent(event,key) + if(self:isFocusedObject())then + if(self.draw)then + local anchX,anchY = self:getAnchorPosition() + if(event=="key")then + if(key==259)then -- on backspace + if(self.lines[self.textY]=="")then + if(self.textY>1)then + table.remove(self.lines,self.textY) + self.textX = self.lines[self.textY-1]:len()+1 + self.wIndex = self.textX-self.w+1 + if(self.wIndex<1)then self.wIndex = 1 end + self.textY = self.textY-1 + end + elseif(self.textX<=1)then + if(self.textY>1)then + self.textX = self.lines[self.textY-1]:len()+1 + self.wIndex = self.textX-self.w+1 + if(self.wIndex<1)then self.wIndex = 1 end + self.lines[self.textY-1] = self.lines[self.textY-1]..self.lines[self.textY] + table.remove(self.lines,self.textY) + self.textY = self.textY-1 + end + else + self.lines[self.textY] = self.lines[self.textY]:sub(1,self.textX-2)..self.lines[self.textY]:sub(self.textX,self.lines[self.textY]:len()) + if(self.textX>1)then self.textX = self.textX-1 end + if(self.wIndex>1)then + if(self.textX=self.h)then + self.hIndex = self.hIndex+1 + end + end + if(key==258)then -- on tab + + --self.lines[self.textY] = self.lines[self.textY]..string.rep(" ",self.w-(self.w-self.lines[self.textY]:len())) + end + if(key==265)then -- arrow up + if(self.textY>1)then + self.textY = self.textY-1 + if(self.textX>self.lines[self.textY]:len()+1)then self.textX = self.lines[self.textY]:len()+1 end + if(self.wIndex>1)then + if(self.textX1)then + if(self.textYself.lines[self.textY]:len()+1)then self.textX = self.lines[self.textY]:len()+1 end + + if(self.textY>=self.hIndex+self.h)then + self.hIndex = self.hIndex+1 + end + end + end + if(key==262)then -- arrow right + self.textX = self.textX+1 + if(self.textY<#self.lines)then + if(self.textX>self.lines[self.textY]:len()+1)then + self.textX = 1 + self.textY = self.textY+1 + end + elseif(self.textX > self.lines[self.textY]:len())then + self.textX = self.lines[self.textY]:len()+1 + end + if(self.textX<1)then self.textX = 1 end + if(self.textX=self.w+self.wIndex)then + self.wIndex = self.textX-self.w+1 + end + if(self.wIndex<1)then self.wIndex = 1 end + + end + if(key==263)then -- arrow left + self.textX = self.textX-1 + if(self.textX>=1)then + if(self.textX=self.w+self.wIndex)then + self.wIndex = self.textX+1 + end + end + if(self.textY>1)then + if(self.textX<1)then + self.textY = self.textY-1 + self.textX = self.lines[self.textY]:len()+1 + self.wIndex = self.textX-self.w+1 + end + end + if(self.textX<1)then self.textX = 1 end + if(self.wIndex<1)then self.wIndex = 1 end + end + end + if(event=="char")then + self.lines[self.textY] = self.lines[self.textY]:sub(1,self.textX-1)..key..self.lines[self.textY]:sub(self.textX,self.lines[self.textY]:len()) + self.textX = self.textX+1 + if(self.textX>=self.w+self.wIndex)then self.wIndex = self.wIndex+1 end + end + self.cursorX = anchX+(self.textX <= self.lines[self.textY]:len() and self.textX-1 or self.lines[self.textY]:len())-(self.wIndex-1) + if(self.cursorX>self.x+self.w-1)then self.cursorX = self.x+self.w-1 end + self.cursorY = anchY+(self.textY-self.hIndex < self.h and self.textY-self.hIndex or self.textY-self.hIndex-1) + self:updateVisuals() + if(self.changeFunc~=nil)then + callAll(self.changeFunc) + end + end + end +end + +function textfield:mouseEvent(event,typ,x,y) + if(object.mouseEvent(self,event,typ,x,y))then + if(event=="mouse_click")then + local anchX,anchY = self:getAnchorPosition() + local absX,absY = self:relativeToAbsolutePosition(self:getAnchorPosition()) + if(self.lines[y-absY+self.hIndex]~=nil)then + self.textX = x-absX+self.wIndex + self.textY = y-absY+self.hIndex + if(self.textX>self.lines[self.textY]:len())then + self.textX = self.lines[self.textY]:len()+1 + end + if(self.textX=#self.lines-self.h)then self.hIndex = #self.lines-self.h end + self:updateVisuals() + end + return true + end + return false +end + +function textfield:drawObject() + if(object.drawObject(self))then + for index=0,self.h-1 do + self.frame.fWindow.setBackgroundColor(self.bgcolor) + self.frame.fWindow.setTextColor(self.fgcolor) + local text = "" + if(self.lines[index+self.hIndex]~=nil)then + text = self.lines[index+self.hIndex] + end + text = text:sub(self.wIndex, self.w+self.wIndex-1) + local n = self.w-text:len() + if(n<0)then n = 0 end + text = text..string.rep(" ", n) + + self.frame.fWindow.setCursorPos(self:getAnchorPosition(self.x,self.y+index)) + self.frame.fWindow.write(text) + end + if(self.cursorX==nil)or(self.cursorXself.x+self.w)then self.cursorX = self.x end + if(self.cursorY==nil)or(self.cursorYself.y+self.h)then self.cursorY = self.y end + self.frame.fWindow.setCursorPos(self.cursorX,self.cursorY) + end +end + + +function textfield:getFocusEvent() + if(object.getFocusEvent(self))then + self.frame:setCursorBlink(true) + self:updateVisuals() + end +end + +function textfield:loseFocusEvent() + if(object.loseFocusEvent(self))then + self.frame:setCursorBlink(false) + self:updateVisuals() + end +end + + + + +function frame:addScrollbar(name) + if(self:getObject(name) == nil)then + local obj = scrollbar:new() + obj.name = name;obj.frame=self; + obj.maxValue = obj.h + obj.value = obj.maxValue/obj.h + self:addObject(obj) + return obj; + else + return nil, "id "..name.." already exists"; + end +end + +function scrollbar:setSize(w,h) + object.setSize(self,w,h) + if(self.barType=="vertical")then + self.maxValue = self.h + self.value = self.maxValue/self.h + elseif(self.barType=="horizontal")then + self.maxValue = self.w + self.value = self.maxValue/self.w + end + return self +end + +function scrollbar:getIndex() + return self.index +end + +function scrollbar:setIndex(index) + self.index = index + if(self.index<1)then self.index = 1 end + if(self.index>(self.barType=="vertical" and self.h or self.w)-(self.symbolSize-1))then self.index = (self.barType=="vertical" and self.h or self.w)-(self.symbolSize-1) end + self:setValue(self.index-1 * (self.maxValue/((self.barType=="vertical" and self.h or self.w)-(self.symbolSize-1)))-(self.maxValue/((self.barType=="vertical" and self.h or self.w)-(self.symbolSize-1)))) + return self +end + +function scrollbar:drawObject() + if(object.drawObject(self))then + local x,y = self:getAnchorPosition() + self.frame.fWindow.setBackgroundColor(self.bgcolor) + self.frame.fWindow.setTextColor(self.fgcolor) + if(self.barType=="vertical")then + for curIndex=1,self.h do + self.frame.fWindow.setCursorPos(x,y+curIndex-1) + if(self.index==curIndex)then + for curIndexOffset=0,self.symbolSize-1 do + self.frame.fWindow.setCursorPos(x,y+curIndex-1+curIndexOffset) + self.frame.fWindow.setBackgroundColor(self.symbolColor) + self.frame.fWindow.write(string.rep(self.symbol,self.w)) + self.frame.fWindow.setBackgroundColor(self.bgcolor) + end + else + if(curIndexself.index-1+self.symbolSize)then + self.frame.fWindow.write(string.rep(self.bgSymbol,self.w)) + end + end + end + end + if(self.barType=="horizontal")then + for curIndex=1,self.h do + self.frame.fWindow.setCursorPos(x,y+curIndex-1) + self.frame.fWindow.write(string.rep(self.bgSymbol,self.w-(self.w-(self.index-1)))) + self.frame.fWindow.setBackgroundColor(self.symbolColor) + self.frame.fWindow.write(string.rep(self.symbol,self.symbolSize)) + self.frame.fWindow.setBackgroundColor(self.bgcolor) + self.frame.fWindow.write(string.rep(self.bgSymbol,self.w-(self.index+(self.symbolSize-1)))) + end + end + end +end + +function scrollbar:mouseEvent(event,typ,x,y) + if(object.mouseEvent(self,event,typ,x,y))then + if(event=="mouse_click")or(event=="mouse_drag")then -- remove mouse_drag if i want to make objects moveable uwuwuwuw + local dx,dy = self:relativeToAbsolutePosition(self:getAnchorPosition()) + if(self.barType=="vertical")then + for index=0,self.h-1 do + if(dx<=x)and(dx+self.w>x)and(dy+index==y)then + if(index <= self.h-self.symbolSize)then + self.index = index+1 + else + self.index = self.h-(self.symbolSize-1) + end + self:setValue(self.index-1 * (self.maxValue/(self.h-(self.symbolSize-1)))-(self.maxValue/(self.h-(self.symbolSize-1)))) + self:updateVisuals() + end + end + end + if(self.barType=="horizontal")then + for index=0,self.w-1 do + if(dx+index==x)and(dy<=y)and(dy+self.y>y)then + if(index <= self.w-self.symbolSize)then + self.index = index+1 + else + self.index = self.w-(self.symbolSize-1) + end + self:setValue(self.index-1 * (self.maxValue/(self.w-(self.symbolSize-1)))-(self.maxValue/(self.w-(self.symbolSize-1)))) + self:updateVisuals() + end + end + end + end + if(event=="mouse_scroll")then + self.index = self.index+typ + if(self.index<1)then self.index = 1 end + if(self.index>(self.barType=="vertical" and self.h or self.w)-(self.symbolSize-1))then self.index = (self.barType=="vertical" and self.h or self.w)-(self.symbolSize-1) end + + self:setValue(self.index-1 * (self.maxValue/((self.barType=="vertical" and self.h or self.w)-(self.symbolSize-1)))-(self.maxValue/((self.barType=="vertical" and self.h or self.w)-(self.symbolSize-1)))) + self:updateVisuals() + end + return true + end +end + +function scrollbar:setSymbol(symbol) + self.symbol = string.sub(symbol,1,1) + self:updateVisuals() + return self +end + +function scrollbar:setSymbolSize(size) + self.symbolSize = tonumber(size) or 1 + if(self.barType=="vertical")then + self:setValue(self.index-1 * (self.maxValue/(self.h-(self.symbolSize-1)))-(self.maxValue/(self.h-(self.symbolSize-1)))) + elseif(self.barType=="horizontal")then + self:setValue(self.index-1 * (self.maxValue/(self.w-(self.symbolSize-1)))-(self.maxValue/(self.w-(self.symbolSize-1)))) + end + self:updateVisuals() + return self +end + +function scrollbar:setBackgroundSymbol(symbol) + self.bgSymbol = string.sub(symbol,1,1) + self:updateVisuals() + return self +end + +function scrollbar:setMaxValue(val) + self.maxValue = val + if(self.barType=="vertical")then + self:setValue(self.index-1 * (self.maxValue/(self.h-(self.symbolSize-1)))-(self.maxValue/(self.h-(self.symbolSize-1)))) + elseif(self.barType=="horizontal")then + self:setValue(self.index-1 * (self.maxValue/(self.w-(self.symbolSize-1)))-(self.maxValue/(self.w-(self.symbolSize-1)))) + end + self:updateVisuals() + return self +end + +function scrollbar:getLowestValue() + if(self.barType=="vertical")then + return self.maxValue/(self.h-(self.symbolSize-1)) + elseif(self.barType=="horizontal")then + return self.maxValue/(self.w-(self.symbolSize-1)) + end + return self +end + +function scrollbar:setSymbolColor(color) + self.symbolColor = color + self:updateVisuals() + return self +end + +function scrollbar:setBarType(typ) + self.barType = typ:lower() + self:updateVisuals() + return self +end + +function scrollbar:setBarSize(typ) + self.barSize = typ:lower() + self:updateVisuals() + return self +end + +function frame:addSlider(name) + if(self:getObject(name) == nil)then + local obj = slider:new() + obj.name = name;obj.frame=self; + obj.maxValue = obj.h + obj.value = obj.maxValue/obj.h + self:addObject(obj) + return obj; + else + return nil, "id "..name.." already exists"; + end +end + +function slider:setSize(w,h) + object.setSize(self,w,h) + if(self.barType=="vertical")then + self.maxValue = self.h + self.value = self.maxValue/self.h + elseif(self.barType=="horizontal")then + self.maxValue = self.w + self.value = self.maxValue/self.w + end + return self +end + +function slider:drawObject() + if(object.drawObject(self))then -- Base class + local x,y = self:getAnchorPosition() + self.frame.fWindow.setBackgroundColor(self.bgcolor) + self.frame.fWindow.setTextColor(self.fgcolor) + if(self.barType=="vertical")then + for curPos=0,self.h-1 do + self.frame.fWindow.setCursorPos(x,y+curPos) + if(tostring(self.maxValue/self.h*(curPos+1))==tostring(self.value))then + self.frame.fWindow.setBackgroundColor(self.symbolColor) + self.frame.fWindow.write(string.rep(self.symbol,self.w)) + self.frame.fWindow.setBackgroundColor(self.bgcolor) + else + self.frame.fWindow.write(string.rep(self.bgSymbol,self.w)) + end + end + end + if(self.barType=="horizontal")then + for curPos=0,self.h-1 do + self.frame.fWindow.setCursorPos(x,y+curPos) + self.frame.fWindow.write(string.rep(self.bgSymbol,(self.value/(self.maxValue/self.w))-1)) + self.frame.fWindow.setBackgroundColor(self.symbolColor) + self.frame.fWindow.write(self.symbol) + self.frame.fWindow.setBackgroundColor(self.bgcolor) + self.frame.fWindow.write(string.rep(self.bgSymbol,self.maxValue/(self.maxValue/self.w)-(self.value/(self.maxValue/self.w)))) + end + end + end +end + +function slider:mouseEvent(event,typ,x,y) + if(object.mouseEvent(self,event,typ,x,y))then + if(event=="mouse_click")or(event=="mouse_drag")then -- remove mouse_drag if i want to make objects moveable uwuwuwuw + local dx,dy = self:relativeToAbsolutePosition(self:getAnchorPosition()) + if(self.barType=="vertical")then + for index=0,self.h-1 do + if(dx<=x)and(dx+self.w>x)and(dy+index==y)then + self:setValue(self.maxValue/self.h*(index+1)) + self:updateVisuals() + end + end + end + if(self.barType=="horizontal")then + for index=0,self.w-1 do + if(dx+index==x)and(dy<=y)and(dy+self.y>y)then + self:setValue(self.maxValue/self.w*(index+1)) + self:updateVisuals() + end + end + end + end + if(event=="mouse_scroll")then + self:setValue(self.value + (self.maxValue/(self.barType=="vertical" and self.h or self.w))*typ) + self:updateVisuals() + end + if(self.value>self.maxValue)then self:setValue(self.maxValue) end + if(self.value0)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 + else + self.playing = false + self.index = 1 + end + end + end +end + +function animation:play(infiniteloop) + if(infiniteloop~=nil)then self.infiniteloop=infiniteloop end + self.playing = true + 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 + end + return self +end + +function animation:cancel() + os.cancelTimer(self.timeObj) + self.playing = false + self.infiniteloop = false + self.index = 1 + return self +end +----------- + + +local function checkTimer(timeObject) + for a,b in pairs(activeFrame.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 + if(v.__type=="Animation")and(v.playing)then + if(v.timeObj == timeObject)then + v:onPlay() + end + end + end + end +end + + +local function handleObjectDrawing() +local changed = false + for k,v in pairs(activeFrame.objects)do + for a,b in pairs(v)do + if(b.changed)then + changed = true + end + end + end + if(changed)then + activeFrame:drawObject() + end +end + +if(Basalt.debugger)then + Basalt.debugFrame = Basalt.createFrame("BasaltDebuggingFrame"):showBar():setBackground(colors.lightGray):setTitle("Debug",colors.black,colors.gray) + Basalt.debugList = Basalt.debugFrame:addList("debugList"):setSize(Basalt.debugFrame.w - 2, Basalt.debugFrame.h - 3):setPosition(2,3):setSymbol(""):setBackground(colors.gray):setItemColors(colors.gray):setTextAlign("left"):show() + Basalt.debugFrame:addButton("back"):setAnchor("right"):setSize(1,1):setText("\22"):onClick(function() Basalt.oldFrame:show() end):setBackground(colors.red):show() + Basalt.debugLabel = Basalt.debugFrame:addLabel("debugLabel"):onClick(function() Basalt.oldFrame = activeFrame Basalt.debugFrame:show() end):setBackground(colors.black):setForeground(colors.white):setAnchor("bottom"):ignoreScroll(true,true):show() +end + +function Basalt.startUpdate() + if not(Basalt.updater)then + handleObjectDrawing() + Basalt.updater = true + while Basalt.updater do + local event, p1,p2,p3,p4 = os.pullEventRaw() + activeFrame:eventListener(event,p1,p2,p3,p4) + if(event=="mouse_click")or(event=="mouse_scroll")or(event=="mouse_drag")or(event=="mouse_up")then + activeFrame:mouseEvent(event,p1,p2,p3) + end + if(event=="timer")then + checkTimer(p1) + end + if(event=="char")or(event=="key")then + activeFrame:keyEvent(event,p1) + keyModifier[p1] = true + end + if(event=="key_up")then + activeFrame:keyEvent(event,p1) + keyModifier[p1] = false + end + handleObjectDrawing() + end + end +end + +function Basalt.stopUpdate() + Basalt.updater = false +end + +if(Basalt.debugger)then + function Basalt.debug(...) + local args = {...} + if(activeFrame.name~="BasaltDebuggingFrame")then + if(activeFrame~=Basalt.debugLabel.frame)then + Basalt.debugLabel:setParent(activeFrame) + end + end + local str = "" + for k,v in pairs(args)do + str = str..tostring(v)..(#args~=k and ", " or "") + end + Basalt.debugLabel:setText("[Debug] "..str) + Basalt.debugList:addItem(str) + if(#Basalt.debugList.items>Basalt.debugList.h)then Basalt.debugList:removeItem(1) end + + Basalt.debugLabel:show() + end + +end + +function Basalt.getFrame(name) + return _frames[name]; +end + +function Basalt.getActiveFrame() + if(activeFrame.name=="BasaltDebuggingFrame")then + return oldFrame + end + return activeFrame +end + +return Basalt; \ No newline at end of file