diff --git a/Basalt/Frame.lua b/Basalt/Frame.lua index 863e42d..4010447 100644 --- a/Basalt/Frame.lua +++ b/Basalt/Frame.lua @@ -49,6 +49,8 @@ return function(name, parent, pTerm, basalt) local activeEvents = {} + local colorTheme = {} + base:setZIndex(10) local basaltDraw = BasaltDraw(termObject) @@ -81,6 +83,7 @@ return function(name, parent, pTerm, basalt) end local function getObject(name) + if(type(name)~="string")then name = name.name end for _, value in pairs(objects) do for _, b in pairs(value) do if (b:getName() == name) then @@ -240,11 +243,11 @@ return function(name, parent, pTerm, basalt) end return false end - + local math = math local function stringToNumber(str) - local ok, err = pcall(load("return " .. str)) + local ok, result = pcall(load("return " .. str, "", nil, {math=math})) if not(ok)then error(str.." is not a valid dynamic code") end - return load("return " .. str)() + return result end local function newDynamicValue(_, obj, str) @@ -327,8 +330,13 @@ return function(name, parent, pTerm, basalt) for _, index in pairs(objZIndex) do if (objects[index] ~= nil) then for _, value in pairs(objects[index]) do - if (value.eventHandler ~= nil) then - value:eventHandler("dynamicValueEvent", self) + if(basalt.getDynamicValueEventSetting())then + if (value.eventHandler ~= nil) then + value:eventHandler("basalt_dynamicvalue", self) + end + end + if (value.customEventHandler ~= nil) then + value:customEventHandler("basalt_resize", self) end end end @@ -340,17 +348,49 @@ return function(name, parent, pTerm, basalt) return dynamicValues[id][1] end - local function calculateMaxScroll(self) + local function getVerticalScrollAmount(self) + local amount = 0 for _, value in pairs(objects) do for _, b in pairs(value) do if(b.getHeight~=nil)and(b.getY~=nil)then - local h, y = b:getHeight(), b:getY() - if (h + y - self:getHeight() > scrollAmount) then - scrollAmount = max(h + y - self:getHeight(), 0) + if(b:getType()=="Dropdown")then + local h, y = b:getHeight(), b:getY() + local wD, hD = b:getDropdownSize() + h = h + hD - 1 + if (h + y - self:getHeight() >= amount) then + amount = max(h + y - self:getHeight(), 0) + end + else + local h, y = b:getHeight(), b:getY() + if (h + y - self:getHeight() >= amount) then + amount = max(h + y - self:getHeight(), 0) + end end end end end + return amount + end + + local function getHorizontalScrollAmount(self) + local amount = 0 + for _, value in pairs(objects) do + for _, b in pairs(value) do + if(b.getWidth~=nil)and(b.getX~=nil)then + local h, y = b:getWidth(), b:getX() + if (h + y - self:getWidth() >= amount) then + amount = max(h + y - self:getWidth(), 0) + end + end + end + end + return amount + end + + local function calculateMaxScroll(self) + if(autoScroll)then + scrollAmount = getVerticalScrollAmount(self) + end end object = { @@ -371,8 +411,8 @@ return function(name, parent, pTerm, basalt) getType = function(self) return objectType - end, - + end; + setZIndex = function(self, newIndex) base.setZIndex(self, newIndex) for k,v in pairs(activeEvents)do @@ -386,10 +426,14 @@ return function(name, parent, pTerm, basalt) setFocusedObject = function(self, obj) if(focusedObject~=obj)then if(focusedObject~=nil)then - focusedObject:loseFocusHandler() + if(getObject(focusedObject)~=nil)then + focusedObject:loseFocusHandler() + end end if(obj~=nil)then - obj:getFocusHandler() + if(getObject(obj)~=nil)then + obj:getFocusHandler() + end end focusedObject = obj end @@ -408,8 +452,8 @@ return function(name, parent, pTerm, basalt) for _, index in pairs(objZIndex) do if (objects[index] ~= nil) then for _, value in pairs(objects[index]) do - if (value.eventHandler ~= nil) then - value:eventHandler("basalt_resize", value, self) + if (value.customEventHandler ~= nil) then + value:customEventHandler("basalt_resize", self) end end end @@ -433,17 +477,34 @@ return function(name, parent, pTerm, basalt) return theme[name] or (self.parent~=nil and self.parent:getTheme(name) or basalt.getTheme(name)) end, - setPosition = function(self, x, y, rel) - base.setPosition(self, x, y, rel) - for _, index in pairs(objZIndex) do - if (objects[index] ~= nil) then - for _, value in pairs(objects[index]) do - if (value.eventHandler ~= nil) then - value:eventHandler("basalt_reposition", value, self) + getThemeColor = function(self, col) + return col~=nil and colorTheme[col] or colorTheme + end, + + setThemeColor = function(self, col, ...) + if(self.parent==nil)then + if(self==basalt.getActiveFrame())then + if(type(col)=="string")then + colorTheme[col] = ... + termObject.setPaletteColor(type(col)=="number" and col or colors[col], ...) + elseif(type(col)=="table")then + for k,v in pairs(col)do + colorTheme[k] = v + if(type(v)=="number")then + termObject.setPaletteColor(type(k)=="number" and k or colors[k], v) + else + local r,g,b = table.unpack(v) + termObject.setPaletteColor(type(k)=="number" and k or colors[k], r,g,b) + end end end end end + return self + end, + + setPosition = function(self, x, y, rel) + base.setPosition(self, x, y, rel) self:recalculateDynamicValues() return self end; @@ -469,7 +530,9 @@ return function(name, parent, pTerm, basalt) removeFocusedObject = function(self) if(focusedObject~=nil)then - focusedObject:loseFocusHandler() + if(getObject(focusedObject)~=nil)then + focusedObject:loseFocusHandler() + end end focusedObject = nil return self @@ -537,10 +600,26 @@ return function(name, parent, pTerm, basalt) return autoScroll and calculateMaxScroll(self) or scrollAmount end, + getCalculatedVerticalScroll = getVerticalScrollAmount, + getCalculatedHorizontalScroll = getHorizontalScrollAmount, + show = function(self) base.show(self) if(self.parent==nil)then basalt.setActiveFrame(self) + for k,v in pairs(colors)do + if(type(v)=="number")then + termObject.setPaletteColor(v, colors.packRGB(term.nativePaletteColor((v)))) + end + end + for k,v in pairs(colorTheme)do + if(type(v)=="number")then + termObject.setPaletteColor(type(k)=="number" and k or colors[k], v) + else + local r,g,b = table.unpack(v) + termObject.setPaletteColor(type(k)=="number" and k or colors[k], r,g,b) + end + end if(isMonitor)and not(isGroupedMonitor)then basalt.setMonitorFrame(monSide, self) elseif(isGroupedMonitor)then @@ -555,7 +634,7 @@ return function(name, parent, pTerm, basalt) hide = function (self) base.hide(self) if(self.parent==nil)then - if(activeFrame == self)then activeFrame = nil end -- bug activeFrame always nil + if(activeFrame == self)then activeFrame = nil end if(isMonitor)and not(isGroupedMonitor)then if(basalt.getMonitorFrame(monSide) == self)then basalt.setActiveFrame(nil) @@ -804,9 +883,6 @@ return function(name, parent, pTerm, basalt) self:mouseHandler(1, p2, p3, true) end end - if (event == "terminate")and(self.parent==nil)then - basalt.stop() - end end, mouseHandler = function(self, button, x, y, _, side) @@ -891,8 +967,8 @@ return function(name, parent, pTerm, basalt) self:updateDraw() end end - self:removeFocusedObject() if(yOffset==cache)then return false end + self:removeFocusedObject() return true end return false diff --git a/Basalt/Object.lua b/Basalt/Object.lua index 44bc11a..3f79068 100644 --- a/Basalt/Object.lua +++ b/Basalt/Object.lua @@ -1,9 +1,13 @@ local basaltEvent = require("basaltEvent") local utils = require("utils") +local module = require("module") +local images = module("images") local split = utils.splitString local numberFromString = utils.numberFromString local xmlValue = utils.getValueFromXML +local unpack,sub = table.unpack,string.sub + return function(name) -- Base object local objectType = "Object" -- not changeable @@ -30,6 +34,13 @@ return function(name) local isDragging = false local dragStartX, dragStartY, dragXOffset, dragYOffset = 0, 0, 0, 0 + local bimg + local texture + local textureId = 1 + local textureTimerId + local textureMode + local infinitePlay = true + local draw = true local activeEvents = {} @@ -97,8 +108,8 @@ return function(name) setValuesByXMLData = function(self, data) local baseFrame = self:getBaseFrame() - if(xmlValue("x", data)~=nil)then self:setPosition(xmlValue("x", data), self.y) end - if(xmlValue("y", data)~=nil)then self:setPosition(self.x, xmlValue("y", data)) end + if(xmlValue("x", data)~=nil)then self:setPosition(xmlValue("x", data), self:getY()) end + if(xmlValue("y", data)~=nil)then self:setPosition(self:getX(), xmlValue("y", data)) end if(xmlValue("width", data)~=nil)then self:setSize(xmlValue("width", data), self.height) end if(xmlValue("height", data)~=nil)then self:setSize(self.width, xmlValue("height", data)) end if(xmlValue("bg", data)~=nil)then self:setBackground(colors[xmlValue("bg", data)]) end @@ -195,11 +206,13 @@ return function(name) return self end; - setValue = function(self, _value) + setValue = function(self, _value, valueChangedHandler) if (value ~= _value) then value = _value self:updateDraw() - self:valueChangedHandler() + if(valueChangedHandler~=false)then + self:valueChangedHandler() + end end return self end; @@ -245,7 +258,7 @@ return function(name) end self.parent:recalculateDynamicValues() end - eventSystem:sendEvent("basalt_reposition", self) + self:customEventHandler("basalt_reposition") self:updateDraw() return self end; @@ -288,24 +301,27 @@ return function(name) end self.parent:recalculateDynamicValues() end - eventSystem:sendEvent("basalt_resize", self) + if(bimg~=nil)and(textureMode=="stretch")then + texture = images.resizeBIMG(bimg, self:getSize())[textureId] + end + self:customEventHandler("basalt_resize") self:updateDraw() return self - end; + end, getHeight = function(self) return type(self.height) == "number" and self.height or math.floor(self.height[1]+0.5) - end; + end, getWidth = function(self) return type(self.width) == "number" and self.width or math.floor(self.width[1]+0.5) - end; + end, getSize = function(self) return self:getWidth(), self:getHeight() - end; + end, - calculateDynamicValues = function(self) + calculateDynamicValues = function(self) if(type(self.width)=="table")then self.width:calculate() end if(type(self.height)=="table")then self.height:calculate() end if(type(self.x)=="table")then self.x:calculate() end @@ -320,12 +336,38 @@ return function(name) self.bgSymbolColor = symbolCol or self.bgSymbolColor self:updateDraw() return self - end; + end, + + setTexture = function(self, tex, mode, infPlay) + if(type(tex)=="string")then + bimg = images.loadImageAsBimg(tex) + elseif(type(tex)=="table")then + bimg = tex + end + if(bimg.animated)then + local t = bimg[textureId].duration or bimg.secondsPerFrame or 0.2 + textureTimerId = os.startTimer(t) + self.parent:addEvent("other_event", self) + activeEvents["other_event"] = true + end + infinitePlay = infPlay==false and false or true + textureId = 1 + textureMode = mode or "normal" + if(textureMode=="stretch")then + texture = images.resizeBIMG(bimg, self:getSize())[1] + else + texture = bimg[1] + end + self:updateDraw() + return self + end, setTransparent = function(self, color) self.transparentColor = color or false - self.bgSymbol = false - self.bgSymbolColor = false + if(color~=false)then + self.bgSymbol = false + self.bgSymbolColor = false + end self:updateDraw() return self end; @@ -403,18 +445,50 @@ return function(name) local w,h = self:getSize() local wP,hP = self.parent:getSize() if(x+w<1)or(x>wP)or(y+h<1)or(y>hP)then return false end - if(self.transparentColor~=false)then - self.parent:drawForegroundBox(x, y, w, h, self.transparentColor) - end - if(self.bgColor~=false)then - self.parent:drawBackgroundBox(x, y, w, h, self.bgColor) - end - if(self.bgSymbol~=false)then - self.parent:drawTextBox(x, y, w, h, self.bgSymbol) - if(self.bgSymbol~=" ")then - self.parent:drawForegroundBox(x, y, w, h, self.bgSymbolColor) + if(self.transparentColor~=false)then + self.parent:drawForegroundBox(x, y, w, h, self.transparentColor) + end + if(self.bgColor~=false)then + self.parent:drawBackgroundBox(x, y, w, h, self.bgColor) + end + if(self.bgSymbol~=false)then + self.parent:drawTextBox(x, y, w, h, self.bgSymbol) + if(self.bgSymbol~=" ")then + self.parent:drawForegroundBox(x, y, w, h, self.bgSymbolColor) + end + end + if(texture~=nil)then + if(textureMode=="center")then + local tW,tH = #texture[1][1],#texture + local xO = tW < w and math.floor((w-tW)/2) or 0 + local yO = tH < h and math.floor((h-tH)/2) or 0 + local sL = tW= 1) and (y <= height) then + if (x + t:len() > 0) and (x <= width) then + local oldCacheT = cacheT[y] + local oldCacheFG = cacheFG[y] + local oldCacheBG = cacheBG[y] + local newCacheT, newCacheFG, newCacheBG + local nEnd = x + #t - 1 + + if (x < 1) then + local startN = 1 - x + 1 + local endN = width - x + 1 + t = sub(t, startN, endN) + fg = sub(fg, startN, endN) + bg = sub(bg, startN, endN) + elseif (nEnd > width) then + local endN = width - x + 1 + t = sub(t, 1, endN) + fg = sub(fg, 1, endN) + bg = sub(bg, 1, endN) + end + + if (x > 1) then + local endN = x - 1 + newCacheT = sub(oldCacheT, 1, endN) .. t + newCacheFG = sub(oldCacheFG, 1, endN) .. fg + newCacheBG = sub(oldCacheBG, 1, endN) .. bg + else + newCacheT = t + newCacheFG = fg + newCacheBG = bg + end + if nEnd < width then + newCacheT = newCacheT .. sub(oldCacheT, nEnd + 1, width) + newCacheFG = newCacheFG .. sub(oldCacheFG, nEnd + 1, width) + newCacheBG = newCacheBG .. sub(oldCacheBG, nEnd + 1, width) + end + cacheT[y] = newCacheT + cacheFG[y] = newCacheFG + cacheBG[y] = newCacheBG + end + end + end + end + local drawHelper = { setSize = function(w, h) width, height = w, h @@ -147,6 +193,10 @@ return function(drawTerm) setFG(x, y, colorStr) end; + blit = function(x, y, t, fg, bg) + blit(x, y, t, fg, bg) + end, + drawBackgroundBox = function(x, y, width, height, bgCol) for n = 1, height do setBG(x, y + (n - 1), rep(tHex[bgCol], width)) diff --git a/Basalt/libraries/bimg.lua b/Basalt/libraries/bimg.lua new file mode 100644 index 0000000..fcc40be --- /dev/null +++ b/Basalt/libraries/bimg.lua @@ -0,0 +1,381 @@ +local sub,rep = string.sub,string.rep + +local function frame(base, manager) + local w, h = 0, 0 + local t,fg,bg = {}, {}, {} + local x, y = 1,1 + + local data = {} + + local function recalculateSize() + for y=1,h do + if(t[y]==nil)then + t[y] = rep(" ", w) + else + t[y] = t[y]..rep(" ", w-#t[y]) + end + if(fg[y]==nil)then + fg[y] = rep("0", w) + else + fg[y] = fg[y]..rep("0", w-#fg[y]) + end + if(bg[y]==nil)then + bg[y] = rep("f", w) + else + bg[y] = bg[y]..rep("f", w-#bg[y]) + end + end + end + + local addText = function(text, _x, _y) + x = _x or x + y = _y or y + if(t[y]==nil)then + t[y] = rep(" ", x-1)..text..rep(" ", w-(#text+x)) + else + t[y] = sub(t[y], 1, x-1)..rep(" ", x-#t[y])..text..sub(t[y], x+#text, w) + end + if(#t[y]>w)then w = #t[y] end + if(y > h)then h = y end + manager.updateSize(w, h) + end + + local addBg = function(b, _x, _y) + x = _x or x + y = _y or y + if(bg[y]==nil)then + bg[y] = rep("f", x-1)..b..rep("f", w-(#b+x)) + else + bg[y] = sub(bg[y], 1, x-1)..rep("f", x-#bg[y])..b..sub(bg[y], x+#b, w) + end + if(#bg[y]>w)then w = #bg[y] end + if(y > h)then h = y end + manager.updateSize(w, h) + end + + local addFg = function(f, _x, _y) + x = _x or x + y = _y or y + if(fg[y]==nil)then + fg[y] = rep("0", x-1)..f..rep("0", w-(#f+x)) + else + fg[y] = sub(fg[y], 1, x-1)..rep("0", x-#fg[y])..f..sub(fg[y], x+#f, w) + end + if(#fg[y]>w)then w = #fg[y] end + if(y > h)then h = y end + manager.updateSize(w, h) + end + + local function setFrame(frm) + data = {} + t, fg, bg = {}, {}, {} + for k,v in pairs(base)do + if(type(k)=="string")then + data[k] = v + else + t[k], fg[k], bg[k] = v[1], v[2], v[3] + end + end + manager.updateSize(w, h) + end + + if(base~=nil)then + w = #base[1][1] + h = #base + setFrame(base) + end + + return { + recalculateSize = recalculateSize, + setFrame = setFrame, + + getFrame = function() + local f = {} + + for k,v in pairs(t)do + table.insert(f, {v, fg[k], bg[k]}) + end + + for k,v in pairs(data)do + f[k] = v + end + + return f, w, h + end, + + getImage = function() + local i = {} + for k,v in pairs(t)do + table.insert(i, {v, fg[k], bg[k]}) + end + return i + end, + + setFrameData = function(key, value) + if(value~=nil)then + data[key] = value + else + if(type(key)=="table")then + data = key + end + end + end, + + setFrameImage = function(imgData) + for k,v in pairs(imgData.t)do + t[k] = imgData.t[k] + fg[k] = imgData.fg[k] + bg[k] = imgData.bg[k] + end + end, + + getFrameImage = function() + return {t = t, fg = fg, bg = bg} + end, + + getFrameData = function(key) + return (key~= nil and data[key] or data) + end, + + blit = function(text, fgCol, bgCol, x, y) + addText(text, x, y) + addFg(fgCol, x, y) + addBg(bgCol, x, y) + end, + + text = addText, + fg = addFg, + bg = addBg, + + getSize = function() + return w, h + end, + + setSize = function(_w, _h) + local nt,nfg,nbg = {}, {}, {} + for _y=1,_h do + if(t[_y]~=nil)then + nt[_y] = sub(t[_y], 1, _w)..rep(" ", _w - w) + else + nt[_y] = rep(" ", _w) + end + if(fg[_y]~=nil)then + nfg[_y] = sub(fg[_y], 1, _w)..rep("0", _w - w) + else + nfg[_y] = rep("0", _w) + end + if(bg[_y]~=nil)then + nbg[_y] = sub(bg[_y], 1, _w)..rep("f", _w - w) + else + nbg[_y] = rep("f", _w) + end + end + t, fg, bg = nt, nfg, nbg + w, h = _w, _h + end, + } +end + +return function(img) + local frames = {} + local metadata = {creator="Bimg Library by NyoriE", date=os.date("!%Y-%m-%dT%TZ")} + local width,height = 0, 0 + + local manager = {} + + local function addFrame(id, data) + id = id or #frames+1 + table.insert(frames, id, frame(data, manager)) + if(data==nil)then + frames[id].setSize(width, height) + end + end + + local function removeFrame(id) + table.remove(frames, id or #frames) + end + + local function moveFrame(id, dir) + local f = frames[id] + if(f~=nil)then + local newId = id+dir + if(newId>=1)and(newId<=#frames)then + table.remove(frames, id) + table.insert(frames, newId, f) + end + end + end + + manager = { + updateSize = function(w, h, force) + local changed = force==true and true or false + if(w > width)then changed = true width = w end + if(h > height)then changed = true height = h end + if(changed)then + for k,v in pairs(frames)do + v.setSize(width, height) + v.recalculateSize() + end + end + end, + + text = function(frame, text, x, y) + local f = frames[frame] + if(f==nil)then + f = addFrame(frame) + end + f.text(text, x, y) + end, + + fg = function(frame, fg, x, y) + local f = frames[frame] + if(f==nil)then + f = addFrame(frame) + end + f.fg(fg, x, y) + end, + + bg = function(frame, bg, x, y) + local f = frames[frame] + if(f==nil)then + f = addFrame(frame) + end + f.bg(bg, x, y) + end, + + blit = function(frame, text, fg, bg, x, y) + local f = frames[frame] + if(f==nil)then + f = addFrame(frame) + end + f.blit(text, fg, bg, x, y) + end, + + setSize = function(w, h) + width = w + height = h + for k,v in pairs(frames)do + v.setSize(w, h) + end + end, + + getFrame = function(id) + if(frames[id]~=nil)then + return frames[id].getFrame() + end + end, + + getFrameObjects = function() + return frames + end, + + getFrames = function() + local f = {} + for k,v in pairs(frames)do + local frame = v.getFrame() + table.insert(f, frame) + end + return f + end, + + getFrameObject = function(id) + return frames[id] + end, + + addFrame = function(id) + local f = frame() + if(#frames<=1)then + if(metadata.animated==nil)then + metadata.animated = true + end + if(metadata.secondsPerFrame==nil)then + metadata.secondsPerFrame = 0.2 + end + end + addFrame(id) + return f + end, + + removeFrame = function(id) + removeFrame(id) + if(#frames<=1)then + if(metadata.animated==nil)then + metadata.animated = true + end + if(metadata.secondsPerFrame==nil)then + metadata.secondsPerFrame = 0.2 + end + end + end, + + moveFrame = moveFrame, + + setFrameData = function(id, key, value) + if(frames[id]~=nil)then + frames[id].setFrameData(key, value) + end + end, + + getFrameData = function(id, key) + return frames[id]~=nil and frames[id].getFrameData(key) + end, + + getSize = function() + return width, height + end, + + setAnimation = function(anim) + metadata.animation = anim + end, + + setMetadata = function(key, val) + if(val~=nil)then + metadata[key] = val + else + if(type(key)=="table")then + metadata = key + end + end + end, + + getMetadata = function(key) + return key~=nil and metadata[key] or metadata + end, + + createBimg = function() + local bimg = {} + for k,v in pairs(frames)do + local f = v.getFrame() + table.insert(bimg, f) + end + for k,v in pairs(metadata)do + bimg[k] = v + end + bimg.width = width + bimg.height = height + return bimg + end, + } + + if(img~=nil)then + for k,v in pairs(img)do + if(type(k)=="string")then + metadata[k] = v + else + addFrame(k, v) + end + end + if(metadata.width==nil)or(metadata.height==nil)then + for k,v in pairs(frames)do + local w, h = v.getSize() + if(w>width)then w = width end + if(h>height)then h = height end + end + manager.updateSize(width, height, true) + end + else + addFrame(1) + end + + return manager +end \ No newline at end of file diff --git a/Basalt/libraries/images.lua b/Basalt/libraries/images.lua new file mode 100644 index 0000000..d4bf927 --- /dev/null +++ b/Basalt/libraries/images.lua @@ -0,0 +1,89 @@ +local sub,floor = string.sub,math.floor + +local function loadNFPAsBimg(path) + return {[1]={{}, {}, paintutils.loadImage(path)}}, "bimg" +end + +local function loadNFP(path) + return paintutils.loadImage(path), "nfp" +end + +local function loadBIMG(path) + local f = fs.open(path, "rb") + local content = textutils.unserialize(f.readAll()) + f.close() + if(content~=nil)then + return content, "bimg" + end +end + +local function loadBBF(path) + +end + +local function loadBBFAsBimg(path) + +end + +local function loadImage(path, f) + if(f==nil)then + if(path:find(".bimg"))then + return loadBIMG(path) + elseif(path:find(".bbf"))then + return loadBBF(path) + else + return loadNFP(path) + end + end + -- ... +end + +local function loadImageAsBimg(path, f) + if(f==nil)then + if(path:find(".bimg"))then + return loadBIMG(path) + elseif(path:find(".bbf"))then + return loadBBFAsBimg(path) + else + return loadNFPAsBimg(path) + end + end +end + +local function resizeBIMG(source, w, h) + local oW, oH = source.width or #source[1][1][1], source.height or #source[1] + local newImg = {} + for k,v in pairs(source)do + if(type(k)=="number")then + local frame = {} + for y=1, h do + local xT,xFG,xBG = "","","" + local yR = floor(y / h * oH + 0.5) + if(v[yR]~=nil)then + for x=1, w do + local xR = floor(x / w * oW + 0.5) + xT = xT..sub(v[yR][1], xR,xR) + xFG = xFG..sub(v[yR][2], xR,xR) + xBG = xBG..sub(v[yR][3], xR,xR) + end + table.insert(frame, {xT, xFG, xBG}) + end + end + table.insert(newImg, k, frame) + else + newImg[k] = v + end + end + newImg.width = w + newImg.height = h + return newImg +end + +return { + loadNFP = loadNFP, + loadBIMG = loadBIMG, + loadImage = loadImage, + resizeBIMG = resizeBIMG, + loadImageAsBimg = loadImageAsBimg, + +} \ No newline at end of file diff --git a/Basalt/libraries/utils.lua b/Basalt/libraries/utils.lua index f9844ce..03b2d2c 100644 --- a/Basalt/libraries/utils.lua +++ b/Basalt/libraries/utils.lua @@ -1,3 +1,5 @@ +local sub = string.sub + local splitString = function(str, sep) if sep == nil then sep = "%s" @@ -9,6 +11,74 @@ local splitString = function(str, sep) return t end +local relations = {[0] = {8, 4, 3, 6, 5}, {4, 14, 8, 7}, {6, 10, 8, 7}, {9, 11, 8, 0}, {1, 14, 8, 0}, {13, 12, 8, 0}, {2, 10, 8, 0}, {15, 8, 10, 11, 12, 14}, + {0, 7, 1, 9, 2, 13}, {3, 11, 8, 7}, {2, 6, 7, 15}, {9, 3, 7, 15}, {13, 5, 7, 15}, {5, 12, 8, 7}, {1, 4, 7, 15}, {7, 10, 11, 12, 14}} + +local colourNum, exponents, colourChar = {}, {}, {} +for i = 0, 15 do exponents[2^i] = i end +do + local hex = "0123456789abcdef" + for i = 1, 16 do + colourNum[hex:sub(i, i)] = i - 1 + colourNum[i - 1] = hex:sub(i, i) + colourChar[hex:sub(i, i)] = 2 ^ (i - 1) + colourChar[2 ^ (i - 1)] = hex:sub(i, i) + + local thisRel = relations[i - 1] + for i = 1, #thisRel do thisRel[i] = 2 ^ thisRel[i] end + end +end + +local function getBestColourMatch(usage) + local lastCol = relations[exponents[usage[#usage][1]]] + + for j = 1, #lastCol do + local thisRelation = lastCol[j] + for i = 1, #usage - 1 do if usage[i][1] == thisRelation then return i end end + end + + return 1 +end + +local function colsToChar(pattern, totals) + if not totals then + local newPattern = {} + totals = {} + for i = 1, 6 do + local thisVal = pattern[i] + local thisTot = totals[thisVal] + totals[thisVal], newPattern[i] = thisTot and (thisTot + 1) or 1, thisVal + end + pattern = newPattern + end + + local usage = {} + for key, value in pairs(totals) do usage[#usage + 1] = {key, value} end + + if #usage > 1 then + -- Reduce the chunk to two colours: + while #usage > 2 do + table.sort(usage, function (a, b) return a[2] > b[2] end) + local matchToInd, usageLen = getBestColourMatch(usage), #usage + local matchFrom, matchTo = usage[usageLen][1], usage[matchToInd][1] + for i = 1, 6 do if pattern[i] == matchFrom then + pattern[i] = matchTo + usage[matchToInd][2] = usage[matchToInd][2] + 1 + end end + usage[usageLen] = nil + end + + -- Convert to character. Adapted from oli414's function: + -- http://www.computercraft.info/forums2/index.php?/topic/25340-cc-176-easy-drawing-characters/ + local data = 128 + for i = 1, #pattern - 1 do if pattern[i] ~= pattern[6] then data = data + 2^(i-1) end end + return string.char(data), colourChar[usage[1][1] == pattern[6] and usage[2][1] or usage[1][1]], colourChar[pattern[6]] + else + -- Solid colour character: + return "\128", colourChar[pattern[1]], colourChar[pattern[1]] + end +end + return { getTextHorizontalAlign = function(text, width, textAlign, replaceChar) text = string.sub(text, 1, width) @@ -117,4 +187,36 @@ uuid = function() end return uuid() end, + +array = function(arraysize, hashsize) + return load("return {" .. ("nil,"):rep(arraysize) .. ("[0]=nil,"):rep(hashsize) .. "}")() +end, + +shrink = function(image, bgCol) + local results, width, height, bgCol = {{}, {}, {}}, 0, #image + #image % 3, bgCol or colours.black + for i = 1, #image do if #image[i] > width then width = #image[i] end end + + for y = 0, height - 1, 3 do + local cRow, tRow, bRow, counter = {}, {}, {}, 1 + + for x = 0, width - 1, 2 do + -- Grab a 2x3 chunk: + local pattern, totals = {}, {} + + for yy = 1, 3 do for xx = 1, 2 do + pattern[#pattern + 1] = (image[y + yy] and image[y + yy][x + xx]) and (image[y + yy][x + xx] == 0 and bgCol or image[y + yy][x + xx]) or bgCol + totals[pattern[#pattern]] = totals[pattern[#pattern]] and (totals[pattern[#pattern]] + 1) or 1 + end end + + cRow[counter], tRow[counter], bRow[counter] = colsToChar(pattern, totals) + counter = counter + 1 + end + + results[1][#results[1] + 1], results[2][#results[2] + 1], results[3][#results[3] + 1] = table.concat(cRow), table.concat(tRow), table.concat(bRow) + end + + results.width, results.height = #results[1][1], #results[1] + + return results +end, } \ No newline at end of file diff --git a/Basalt/module.lua b/Basalt/module.lua new file mode 100644 index 0000000..75f39cf --- /dev/null +++ b/Basalt/module.lua @@ -0,0 +1,4 @@ +return function(path) + local exists, content = pcall(require, path) + return exists and content or nil +end \ No newline at end of file diff --git a/Basalt/objects/Button.lua b/Basalt/objects/Button.lua index 5e84122..66901f8 100644 --- a/Basalt/objects/Button.lua +++ b/Basalt/objects/Button.lua @@ -71,4 +71,4 @@ return function(name) } return setmetatable(object, base) -end +end \ No newline at end of file diff --git a/Basalt/objects/Dropdown.lua b/Basalt/objects/Dropdown.lua index 6f90715..2dbf66e 100644 --- a/Basalt/objects/Dropdown.lua +++ b/Basalt/objects/Dropdown.lua @@ -84,7 +84,7 @@ return function(name) clear = function(self) list = {} - self:setValue({}) + self:setValue({}, false) self:updateDraw() return self end; @@ -101,7 +101,7 @@ return function(name) end; selectItem = function(self, index) - self:setValue(list[index] or {}) + self:setValue(list[index] or {}, false) self:updateDraw() return self end; @@ -109,7 +109,7 @@ return function(name) setSelectedItem = function(self, bgCol, fgCol, active) itemSelectedBG = bgCol or self.bgColor itemSelectedFG = fgCol or self.fgColor - selectionColorActive = active + selectionColorActive = active~=nil and active self:updateDraw() return self end; @@ -120,6 +120,10 @@ return function(name) return self end, + getDropdownSize = function(self) + return dropdownW, dropdownH + end, + mouseHandler = function(self, button, x, y) if (isOpened) then local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) diff --git a/Basalt/objects/Graphic.lua b/Basalt/objects/Graphic.lua new file mode 100644 index 0000000..f8f18e5 --- /dev/null +++ b/Basalt/objects/Graphic.lua @@ -0,0 +1,214 @@ +local Object = require("Object") +local tHex = require("tHex") +local xmlValue = require("utils").getValueFromXML +local bimgLib = require("bimg") +local images = require("images") + +local sub,len,max,min = string.sub,string.len,math.max,math.min + +return function(name) + -- Graphic + local base = Object(name) + local objectType = "Graphic" + local imgData = bimgLib() + local bimgFrame = imgData.getFrameObject(1) + local bimg + local selectedFrame = 1 + base:setZIndex(5) + + local xOffset, yOffset = 0, 0 + + local object = { + getType = function(self) + return objectType + end; + + setOffset = function(self, _x, _y, rel) + if(rel)then + xOffset = xOffset + _x or 0 + yOffset = yOffset + _y or 0 + else + xOffset = _x or xOffset + yOffset = _y or yOffset + end + self:updateDraw() + return self + end, + + getOffset = function(self) + return xOffset,yOffset + end, + + setValuesByXMLData = function(self, data) + base.setValuesByXMLData(self, data) + + return self + end, + + selectFrame = function(self, id) + if(imgData.getFrameObject(id)==nil)then + imgData.addFrame(id) + end + bimgFrame = imgData.getFrameObject(id) + bimg = bimgFrame.getImage(id) + selectedFrame = id + self:updateDraw() + end, + + addFrame = function(self, id) + imgData.addFrame(id) + return self + end, + + getFrameMetadata = function(self, id, key) + return imgData.getFrameData(id, key) + end, + + setFrameMetadata = function(self, id, key, val) + imgData.setFrameData(id, key, val) + return self + end, + + getMetadata = function(self, key) + return imgData.getMetadata(key) + end, + + setMetadata = function(self, key, value) + return imgData.setMetadata(key, value) + end, + + getFrame = function(self, id) + return imgData.getFrame(id) + end, + + getFrameObject = function(self, id) + return imgData.getFrameObject(id) + end, + + removeFrame = function(self, id) + imgData.removeFrame(id) + return self + end, + + moveFrame = function(self, id, dir) + imgData.moveFrame(id, dir) + return self + end, + + getFrames = function(self) + return imgData.getFrames() + end, + + getFrameCount = function(self) + return #imgData.getFrames() + end, + + getSelectedFrame = function(self) + return selectedFrame + end, + + blit = function(self, text, fg, bg, _x, _y) + x = _x or x + y = _y or y + bimgFrame.blit(text, fg, bg, x, y) + bimg = bimgFrame.getImage() + self:updateDraw() + return self + end, + + setText = function(self, text, _x, _y) + x = _x or x + y = _y or y + bimgFrame.text(text, x, y) + bimg = bimgFrame.getImage() + self:updateDraw() + return self + end, + + setBg = function(self, bg, _x, _y) + x = _x or x + y = _y or y + bimgFrame.bg(bg, x, y) + bimg = bimgFrame.getImage() + self:updateDraw() + return self + end, + + setFg = function(self, fg, _x, _y) + x = _x or x + y = _y or y + bimgFrame.fg(fg, x, y) + bimg = bimgFrame.getImage() + self:updateDraw() + return self + end, + + getImageSize = function(self) + return imgData.getSize() + end, + + setImageSize = function(self, w, h) + imgData.setSize(w, h) + bimg = bimgFrame.getImage() + self:updateDraw() + return self + end, + + resizeImage = function(self, w, h) + local newBimg = images.resizeBIMG(imgData.createBimg(), w, h) + imgData = bimgLib(newBimg) + selectedFrame = 1 + bimgFrame = imgData.getFrameObject(1) + bimg = bimgFrame.getImage() + self:updateDraw() + return self + end, + + loadImage = function(self, path) + if(fs.exists(path))then + local newBimg = images.loadBIMG(path) + imgData = bimgLib(newBimg) + selectedFrame = 1 + bimgFrame = imgData.getFrameObject(1) + bimg = bimgFrame.getImage() + self:updateDraw() + end + return self + end, + + clear = function(self) + imgData = bimgLib() + bimg = nil + self:updateDraw() + return self + end, + + getImage = function(self) + return imgData.createBimg() + end, + + draw = function(self) + if (base.draw(self)) then + if (self.parent ~= nil) then + local obx, oby = self:getAnchorPosition() + local w,h = self:getSize() + if(bimg~=nil)then + for k,v in pairs(bimg)do + if(k<=h-yOffset)and(k+yOffset>=1)then + self.parent:blit(obx+xOffset, oby+k-1+yOffset, v[1], v[2], v[3]) + end + end + end + end + end + end, + + init = function(self) + if(base.init(self))then + self.bgColor = self.parent:getTheme("GraphicBG") + end + end, + } + + return setmetatable(object, base) +end \ No newline at end of file diff --git a/Basalt/objects/Image.lua b/Basalt/objects/Image.lua index 24ff76e..d698e03 100644 --- a/Basalt/objects/Image.lua +++ b/Basalt/objects/Image.lua @@ -1,197 +1,158 @@ local Object = require("Object") local xmlValue = require("utils").getValueFromXML +local images = require("images") +local unpack,sub = table.unpack,string.sub return function(name) -- Image local base = Object(name) local objectType = "Image" base:setZIndex(2) + local originalImage local image - local shrinkedImage - local imageGotShrinked = false + local curFrame = 1 + local infinitePlay = false + local animTimer + local usePalette = false - local function shrink() - -- shrinkSystem is copy pasted (and slightly changed) from blittle by Bomb Bloke: http://www.computercraft.info/forums2/index.php?/topic/25354-cc-176-blittle-api/ - local relations = { [0] = { 8, 4, 3, 6, 5 }, { 4, 14, 8, 7 }, { 6, 10, 8, 7 }, { 9, 11, 8, 0 }, { 1, 14, 8, 0 }, { 13, 12, 8, 0 }, { 2, 10, 8, 0 }, { 15, 8, 10, 11, 12, 14 }, - { 0, 7, 1, 9, 2, 13 }, { 3, 11, 8, 7 }, { 2, 6, 7, 15 }, { 9, 3, 7, 15 }, { 13, 5, 7, 15 }, { 5, 12, 8, 7 }, { 1, 4, 7, 15 }, { 7, 10, 11, 12, 14 } } + base.width = 24 + base.height = 8 - local colourNum, exponents, colourChar = {}, {}, {} - for i = 0, 15 do - exponents[2 ^ i] = i - end - do - local hex = "0123456789abcdef" - for i = 1, 16 do - colourNum[hex:sub(i, i)] = i - 1 - colourNum[i - 1] = hex:sub(i, i) - colourChar[hex:sub(i, i)] = 2 ^ (i - 1) - colourChar[2 ^ (i - 1)] = hex:sub(i, i) - - local thisRel = relations[i - 1] - for i = 1, #thisRel do - thisRel[i] = 2 ^ thisRel[i] + local function getPalette(id) + if(originalImage~=nil)then + local p = {} + for k,v in pairs(colors)do + if(type(v)=="number")then + p[k] = {term.nativePaletteColor(v)} end end - end - - local function getBestColourMatch(usage) - local lastCol = relations[exponents[usage[#usage][1]]] - - for j = 1, #lastCol do - local thisRelation = lastCol[j] - for i = 1, #usage - 1 do - if usage[i][1] == thisRelation then - return i - end + if(originalImage.palette~=nil)then + for k,v in pairs(originalImage.palette)do + p[k] = tonumber(v) end end - - return 1 - end - - local function colsToChar(pattern, totals) - if not totals then - local newPattern = {} - totals = {} - for i = 1, 6 do - local thisVal = pattern[i] - local thisTot = totals[thisVal] - totals[thisVal], newPattern[i] = thisTot and (thisTot + 1) or 1, thisVal + if(originalImage[id]~=nil)and(originalImage[id].palette~=nil)then + for k,v in pairs(originalImage[id].palette)do + p[k] = tonumber(v) end - pattern = newPattern - end - - local usage = {} - for key, value in pairs(totals) do - usage[#usage + 1] = { key, value } - end - - if #usage > 1 then - -- Reduce the chunk to two colours: - while #usage > 2 do - table.sort(usage, function(a, b) - return a[2] > b[2] - end) - local matchToInd, usageLen = getBestColourMatch(usage), #usage - local matchFrom, matchTo = usage[usageLen][1], usage[matchToInd][1] - for i = 1, 6 do - if pattern[i] == matchFrom then - pattern[i] = matchTo - usage[matchToInd][2] = usage[matchToInd][2] + 1 - end - end - usage[usageLen] = nil - end - - -- Convert to character. Adapted from oli414's function: - -- http://www.computercraft.info/forums2/index.php?/topic/25340-cc-176-easy-drawing-characters/ - local data = 128 - for i = 1, #pattern - 1 do - if pattern[i] ~= pattern[6] then - data = data + 2 ^ (i - 1) - end - end - return string.char(data), colourChar[usage[1][1] == pattern[6] and usage[2][1] or usage[1][1]], colourChar[pattern[6]] - else - -- Solid colour character: - return "\128", colourChar[pattern[1]], colourChar[pattern[1]] end + return p end - - local results, width, height, bgCol = { {}, {}, {} }, 0, #image + #image % 3, base.bgColor or colors.black - for i = 1, #image do - if #image[i] > width then - width = #image[i] - end - end - - for y = 0, height - 1, 3 do - local cRow, tRow, bRow, counter = {}, {}, {}, 1 - - for x = 0, width - 1, 2 do - -- Grab a 2x3 chunk: - local pattern, totals = {}, {} - - for yy = 1, 3 do - for xx = 1, 2 do - pattern[#pattern + 1] = (image[y + yy] and image[y + yy][x + xx]) and (image[y + yy][x + xx] == 0 and bgCol or image[y + yy][x + xx]) or bgCol - totals[pattern[#pattern]] = totals[pattern[#pattern]] and (totals[pattern[#pattern]] + 1) or 1 - end - end - - cRow[counter], tRow[counter], bRow[counter] = colsToChar(pattern, totals) - counter = counter + 1 - end - - results[1][#results[1] + 1], results[2][#results[2] + 1], results[3][#results[3] + 1] = table.concat(cRow), table.concat(tRow), table.concat(bRow) - end - - results.width, results.height = #results[1][1], #results[1] - - shrinkedImage = results end local object = { init = function(self) - self.bgColor = self.parent:getTheme("ImageBG") + if(base.init(self))then + self.bgColor = self.parent:getTheme("ImageBG") + end end, getType = function(self) return objectType end; - loadImage = function(self, path) - image = paintutils.loadImage(path) - imageGotShrinked = false + loadImage = function(self, path, f) + if not(fs.exists(path))then error("No valid path: "..path) end + originalImage = images.loadImageAsBimg(path, f) + curFrame = 1 + image = originalImage + if(animTimer~=nil)then + os.cancelTimer(animTimer) + end self:updateDraw() return self end; - - shrink = function(self) - shrink() - imageGotShrinked = true + setImage = function(self, data) + originalImage = data + image = originalImage + curFrame = 1 + if(animTimer~=nil)then + os.cancelTimer(animTimer) + end self:updateDraw() return self - end; + end, + + usePalette = function(self, use) + usePalette = use~=nil and use or true + return self + end, + + play = function(self, inf) + if(originalImage.animated)then + local t = originalImage[curFrame].duration or originalImage.secondsPerFrame or 0.2 + self.parent:addEvent("other_event", self) + animTimer = os.startTimer(t) + infinitePlay = inf or false + end + return self + end, + + selectFrame = function(self, fr) + if(originalImage[fr]~=nil)then + curFrame = fr + if(animTimer~=nil)then + os.cancelTimer(animTimer) + end + self:updateDraw() + end + end, + + eventHandler = function(self, event, timerId, ...) + base.eventHandler(self, event, timerId, ...) + if(event=="timer")then + if(timerId==animTimer)then + if(originalImage[curFrame+1]~=nil)then + curFrame = curFrame + 1 + local t = originalImage[curFrame].duration or originalImage.secondsPerFrame or 0.2 + animTimer = os.startTimer(t) + else + if(infinitePlay)then + curFrame = 1 + local t = originalImage[curFrame].duration or originalImage.secondsPerFrame or 0.2 + animTimer = os.startTimer(t) + end + end + self:updateDraw() + end + end + end, + + getMetadata = function(self, key) + return originalImage[key] + end, + + getImageSize = function(self) + return originalImage.width, originalImage.height + end, + + resizeImage = function(self, w, h) + image = images.resizeBIMG(originalImage, w, h) + self:updateDraw() + return self + end, setValuesByXMLData = function(self, data) base.setValuesByXMLData(self, data) - if(xmlValue("shrink", data)~=nil)then if(xmlValue("shrink", data))then self:shrink() end end if(xmlValue("path", data)~=nil)then self:loadImage(xmlValue("path", data)) end return self end, draw = function(self) if (base.draw(self)) then - if (self.parent ~= nil) then - if (image ~= nil) then - local obx, oby = self:getAnchorPosition() - local w,h = self:getSize() - if (imageGotShrinked) then - -- this is copy pasted (and slightly changed) from blittle by Bomb Bloke: http://www.computercraft.info/forums2/index.php?/topic/25354-cc-176-blittle-api/ - local t, tC, bC = shrinkedImage[1], shrinkedImage[2], shrinkedImage[3] - for i = 1, shrinkedImage.height do - local tI = t[i] - if type(tI) == "string" then - self.parent:setText(obx, oby + i - 1, tI) - self.parent:setFG(obx, oby + i - 1, tC[i]) - self.parent:setBG(obx, oby + i - 1, bC[i]) - elseif type(tI) == "table" then - self.parent:setText(obx, oby + i - 1, tI[2]) - self.parent:setFG(obx, oby + i - 1, tC[i]) - self.parent:setBG(obx, oby + i - 1, bC[i]) - end - end - else - for yPos = 1, math.min(#image, h) do - local line = image[yPos] - for xPos = 1, math.min(#line, w) do - if line[xPos] > 0 then - self.parent:drawBackgroundBox(obx + xPos - 1, oby + yPos - 1, 1, 1, line[xPos]) - end - end - end - end + if (image ~= nil) then + if(usePalette)then + self:getBaseFrame():setThemeColor(getPalette(curFrame)) + end + local obx, oby = self:getAnchorPosition() + local w,h = self:getSize() + for y,v in ipairs(image[curFrame])do + local t, f, b = unpack(v) + t = sub(t, 1,w) + f = sub(f, 1,w) + b = sub(b, 1,w) + self.parent:blit(obx, oby+y-1, t, f, b) + if(y==h)then break end end end end diff --git a/Basalt/objects/Input.lua b/Basalt/objects/Input.lua index 06c68f6..580c26a 100644 --- a/Basalt/objects/Input.lua +++ b/Basalt/objects/Input.lua @@ -57,9 +57,9 @@ return function(name) setValue = function(self, val) base.setValue(self, tostring(val)) if not (internalValueChange) then + textX = tostring(val):len() + 1 + wIndex = math.max(1, textX-self:getWidth()+1) if(self:isFocused())then - textX = tostring(val):len() + 1 - wIndex = math.max(1, textX-self:getWidth()+1) local obx, oby = self:getAnchorPosition() self.parent:setCursor(true, obx + textX - wIndex, oby+math.floor(self:getHeight()/2), self.fgColor) end @@ -202,12 +202,15 @@ return function(name) if (text:len() < inputLimit or inputLimit <= 0) then if (inputType == "number") then local cache = text - if (#text==0 and char == "-") or (char == ".") or (tonumber(char) ~= nil) then + if (textX==1 and char == "-") or (char == ".") or (tonumber(char) ~= nil) then self:setValue(text:sub(1, textX - 1) .. char .. text:sub(textX, text:len())) textX = textX + 1 - end - if (tonumber(base.getValue()) == nil) then - --self:setValue(cache) + if(char==".")or(char=="-")and(#text>0)then + if (tonumber(base.getValue()) == nil) then + self:setValue(cache) + textX = textX - 1 + end + end end else self:setValue(text:sub(1, textX - 1) .. char .. text:sub(textX, text:len())) @@ -267,44 +270,43 @@ return function(name) end end, - eventHandler = function(self, event, paste, p2, p3, p4) - if(base.eventHandler(self, event, paste, p2, p3, p4))then - if(event=="paste")then - if(self:isFocused())then - local text = base.getValue() - local w, h = self:getSize() - internalValueChange = true - if (inputType == "number") then - local cache = text - if (paste == ".") or (tonumber(paste) ~= nil) then - self:setValue(text:sub(1, textX - 1) .. paste .. text:sub(textX, text:len())) - textX = textX + paste:len() - end - if (tonumber(base.getValue()) == nil) then - self:setValue(cache) - end - else + eventHandler = function(self, event, paste, ...) + base.eventHandler(self, event, paste, ...) + if(event=="paste")then + if(self:isFocused())then + local text = base.getValue() + local w, h = self:getSize() + internalValueChange = true + if (inputType == "number") then + local cache = text + if (paste == ".") or (tonumber(paste) ~= nil) then self:setValue(text:sub(1, textX - 1) .. paste .. text:sub(textX, text:len())) textX = textX + paste:len() end - if (textX >= w + wIndex) then - wIndex = (textX+1)-w + if (tonumber(base.getValue()) == nil) then + self:setValue(cache) end - - local obx, oby = self:getAnchorPosition() - local val = tostring(base.getValue()) - local cursorX = (textX <= val:len() and textX - 1 or val:len()) - (wIndex - 1) - - local x = self:getX() - if (cursorX > x + w - 1) then - cursorX = x + w - 1 - end - if (self.parent ~= nil) then - self.parent:setCursor(true, obx + cursorX, oby+math.max(math.ceil(h/2-1, 1)), self.fgColor) - end - self:updateDraw() - internalValueChange = false + else + self:setValue(text:sub(1, textX - 1) .. paste .. text:sub(textX, text:len())) + textX = textX + paste:len() end + if (textX >= w + wIndex) then + wIndex = (textX+1)-w + end + + local obx, oby = self:getAnchorPosition() + local val = tostring(base.getValue()) + local cursorX = (textX <= val:len() and textX - 1 or val:len()) - (wIndex - 1) + + local x = self:getX() + if (cursorX > x + w - 1) then + cursorX = x + w - 1 + end + if (self.parent ~= nil) then + self.parent:setCursor(true, obx + cursorX, oby+math.max(math.ceil(h/2-1, 1)), self.fgColor) + end + self:updateDraw() + internalValueChange = false end end end, diff --git a/Basalt/objects/Label.lua b/Basalt/objects/Label.lua index 7c586d1..662b167 100644 --- a/Basalt/objects/Label.lua +++ b/Basalt/objects/Label.lua @@ -31,8 +31,9 @@ return function(name) text = tostring(text) base:setValue(text) if (autoSize) then - if(text:len()+self:getX()>self.parent:getWidth())then - local newW = self.parent:getWidth() - self:getX() + local xOffset = self.parent:getOffset() + if(text:len()+self:getX()>self.parent:getWidth()+xOffset)then + local newW = self.parent:getWidth()+xOffset - self:getX() base.setSize(self, newW, #createText(text, newW)) else base.setSize(self, text:len(), 1) diff --git a/Basalt/objects/List.lua b/Basalt/objects/List.lua index 97bdae4..e1f3596 100644 --- a/Basalt/objects/List.lua +++ b/Basalt/objects/List.lua @@ -24,8 +24,8 @@ return function(name) addItem = function(self, text, bgCol, fgCol, ...) table.insert(list, { text = text, bgCol = bgCol or self.bgColor, fgCol = fgCol or self.fgColor, args = { ... } }) - if (#list == 1) then - self:setValue(list[1]) + if (#list <= 1) then + self:setValue(list[1], false) end self:updateDraw() return self @@ -66,7 +66,7 @@ return function(name) clear = function(self) list = {} - self:setValue({}) + self:setValue({}, false) self:updateDraw() return self end; @@ -83,7 +83,7 @@ return function(name) end; selectItem = function(self, index) - self:setValue(list[index] or {}) + self:setValue(list[index] or {}, false) self:updateDraw() return self end; diff --git a/Basalt/objects/Menubar.lua b/Basalt/objects/Menubar.lua index 2745783..76bc005 100644 --- a/Basalt/objects/Menubar.lua +++ b/Basalt/objects/Menubar.lua @@ -68,7 +68,7 @@ return function(name) clear = function(self) list = {} - self:setValue({}) + self:setValue({}, false) self:updateDraw() return self end; @@ -142,7 +142,7 @@ return function(name) end; selectItem = function(self, index) - self:setValue(list[index] or {}) + self:setValue(list[index] or {}, false) self:updateDraw() return self end; diff --git a/Basalt/objects/Program.lua b/Basalt/objects/Program.lua index bb35946..43dc916 100644 --- a/Basalt/objects/Program.lua +++ b/Basalt/objects/Program.lua @@ -541,7 +541,7 @@ return function(name, parent) pWindow.setTextColor(self.fgColor) pWindow.basalt_setVisible(true) - --resumeProcess(self) + resumeProcess(self) paused = false if(self.parent~=nil)then self.parent:addEvent("mouse_click", self) diff --git a/Basalt/objects/Radio.lua b/Basalt/objects/Radio.lua index b870c2e..1594a5b 100644 --- a/Basalt/objects/Radio.lua +++ b/Basalt/objects/Radio.lua @@ -77,7 +77,7 @@ return function(name) clear = function(self) list = {} - self:setValue({}) + self:setValue({}, false) self:updateDraw() return self end; @@ -94,7 +94,7 @@ return function(name) end; selectItem = function(self, index) - self:setValue(list[index] or {}) + self:setValue(list[index] or {}, false) self:updateDraw() return self end; diff --git a/Basalt/objects/Textfield.lua b/Basalt/objects/Textfield.lua index 29730a7..8285e82 100644 --- a/Basalt/objects/Textfield.lua +++ b/Basalt/objects/Textfield.lua @@ -1,35 +1,101 @@ local Object = require("Object") local tHex = require("tHex") -local log = require("basaltLogs") local xmlValue = require("utils").getValueFromXML +local log = require("basaltLogs") -local rep = string.rep +local rep,find,gmatch,sub,len = string.rep,string.find,string.gmatch,string.sub,string.len return function(name) local base = Object(name) local objectType = "Textfield" local hIndex, wIndex, textX, textY = 1, 1, 1, 1 - local lines = { "" } + local lines = { " " } local bgLines = { "" } local fgLines = { "" } local keyWords = { } local rules = { } + local startSelX,endSelX,startSelY,endSelY + + local selectionBG,selectionFG = colors.lightBlue,colors.black + base.width = 30 base.height = 12 base:setZIndex(5) + local function isSelected() + if(startSelX~=nil)and(endSelX~=nil)and(startSelY~=nil)and(endSelY~=nil)then + return true + end + return false + end + + local function getSelectionCoordinates() + local sx,ex,sy,ey + if(isSelected())then + if(startSelX>endSelX)then + sx,ex = endSelX,startSelX + else + sx,ex = startSelX,endSelX + end + if(startSelY>endSelY)then + sy,ey = endSelY,startSelY + else + sy,ey = startSelY,endSelY + end + end + return sx,ex,sy,ey + end + + local function getSelection() + + end + + local function removeSelection(self) + local sx,ex,sy,ey = getSelectionCoordinates(self) + for n=ey,sy,-1 do + if(n==sy)or(n==ey)then + local l = lines[n] + local b = bgLines[n] + local f = fgLines[n] + if(n==sy)and(n==ey)then + l = l:sub(1,sx-1)..l:sub(ex+1,l:len()) + b = b:sub(1,sx-1)..b:sub(ex+1,b:len()) + f = f:sub(1,sx-1)..f:sub(ex+1,f:len()) + elseif(n==sx)then + l = l:sub(1, sx) + b = b:sub(1, sx) + f = f:sub(1, sx) + elseif(n==sy)then + l = l:sub(ex, l:len()) + b = b:sub(ex, b:len()) + f = f:sub(ex, f:len()) + end + lines[n] = l + bgLines[n] = b + fgLines[n] = f + else + table.remove(lines, n) + table.remove(bgLines, n) + table.remove(fgLines, n) + end + end + textX,textY = startSelX,startSelY + startSelX,endSelX,startSelY,endSelY = nil,nil,nil,nil + return self + end + local function stringGetPositions(str, word) local pos = {} if(str:len()>0)then - for w in string.gmatch(str, word)do - local s, e = string.find(str, w) + for w in gmatch(str, word)do + local s, e = find(str, w) if(s~=nil)and(e~=nil)then table.insert(pos,s) table.insert(pos,e) - local startL = string.sub(str, 1, (s-1)) - local endL = string.sub(str, e+1, str:len()) + local startL = sub(str, 1, (s-1)) + local endL = sub(str, e+1, str:len()) str = startL..(":"):rep(w:len())..endL end end @@ -150,9 +216,10 @@ return function(name) end; clear = function(self) - lines = {""} + lines = {" "} bgLines = {""} fgLines = {""} + startSelX,endSelX,startSelY,endSelY = nil,nil,nil,nil hIndex, wIndex, textX, textY = 1, 1, 1, 1 self:updateDraw() return self @@ -227,9 +294,14 @@ return function(name) end; removeLine = function(self, index) - table.remove(lines, index or #lines) - if (#lines <= 0) then - table.insert(lines, "") + if(#lines>1)then + table.remove(lines, index or #lines) + table.remove(bgLines, index or #bgLines) + table.remove(fgLines, index or #fgLines) + else + lines = {" "} + bgLines = {""} + fgLines = {""} end self:updateDraw() return self @@ -311,17 +383,21 @@ return function(name) if (key == keys.delete) then -- on delete - if (textX > lines[textY]:len()) then - if (lines[textY + 1] ~= nil) then - lines[textY] = lines[textY] .. lines[textY + 1] - table.remove(lines, textY + 1) - table.remove(bgLines, textY + 1) - table.remove(fgLines, textY + 1) - end + if(isSelected())then + removeSelection(self) else - lines[textY] = lines[textY]:sub(1, textX - 1) .. lines[textY]:sub(textX + 1, lines[textY]:len()) - fgLines[textY] = fgLines[textY]:sub(1, textX - 1) .. fgLines[textY]:sub(textX + 1, fgLines[textY]:len()) - bgLines[textY] = bgLines[textY]:sub(1, textX - 1) .. bgLines[textY]:sub(textX + 1, bgLines[textY]:len()) + if (textX > lines[textY]:len()) then + if (lines[textY + 1] ~= nil) then + lines[textY] = lines[textY] .. lines[textY + 1] + table.remove(lines, textY + 1) + table.remove(bgLines, textY + 1) + table.remove(fgLines, textY + 1) + end + else + lines[textY] = lines[textY]:sub(1, textX - 1) .. lines[textY]:sub(textX + 1, lines[textY]:len()) + fgLines[textY] = fgLines[textY]:sub(1, textX - 1) .. fgLines[textY]:sub(textX + 1, fgLines[textY]:len()) + bgLines[textY] = bgLines[textY]:sub(1, textX - 1) .. bgLines[textY]:sub(textX + 1, bgLines[textY]:len()) + end end updateColors(self) end @@ -430,9 +506,14 @@ return function(name) end end + if not((obx + textX - wIndex >= obx and obx + textX - wIndex < obx + w) and (oby + textY - hIndex >= oby and oby + textY - hIndex < oby + h)) then + wIndex = math.max(1, lines[textY]:len()-w+1) + hIndex = math.max(1, textY - h + 1) + end + local cursorX = (textX <= lines[textY]:len() and textX - 1 or lines[textY]:len()) - (wIndex - 1) - if (cursorX > self.x + w - 1) then - cursorX = self.x + w - 1 + if (cursorX > self:getX() + w - 1) then + cursorX = self:getX() + w - 1 end local cursorY = (textY - hIndex < h and textY - hIndex or textY - hIndex - 1) if (cursorX < 1) then @@ -458,14 +539,22 @@ return function(name) updateColors(self) self:setValue("") + if not((obx + textX - wIndex >= obx and obx + textX - wIndex < obx + w) and (oby + textY - hIndex >= oby and oby + textY - hIndex < oby + h)) then + wIndex = math.max(1, lines[textY]:len()-w+1) + hIndex = math.max(1, textY - h + 1) + end + local cursorX = (textX <= lines[textY]:len() and textX - 1 or lines[textY]:len()) - (wIndex - 1) - if (cursorX > self.x + w - 1) then - cursorX = self.x + w - 1 + if (cursorX > self:getX() + w - 1) then + cursorX = self:getX() + w - 1 end local cursorY = (textY - hIndex < h and textY - hIndex or textY - hIndex - 1) if (cursorX < 1) then cursorX = 0 end + if(isSelected())then + removeSelection(self) + end self.parent:setCursor(true, obx + cursorX, oby + cursorY, self.fgColor) self:updateDraw() return true @@ -481,18 +570,21 @@ return function(name) if(anchx+w > anchx + x - (obx+1)+ wIndex)and(anchx < anchx + x - obx+ wIndex)then textX = x - obx + wIndex textY = y - oby + hIndex + endSelX = textX + endSelY = textY if (textX > lines[textY]:len()) then textX = lines[textY]:len() + 1 + endSelX = textX end + if (textX < wIndex) then wIndex = textX - 1 if (wIndex < 1) then wIndex = 1 end end - if (self.parent ~= nil) then - self.parent:setCursor(true, anchx + textX - wIndex, anchy + textY - hIndex, self.fgColor) - end + self.parent:setCursor(true, anchx + textX - wIndex, anchy + textY - hIndex, self.fgColor) + self:updateDraw() end end @@ -514,12 +606,10 @@ return function(name) hIndex = 1 end - if (self.parent ~= nil) then - if (obx + textX - wIndex >= obx and obx + textX - wIndex < obx + w) and (oby + textY - hIndex >= oby and oby + textY - hIndex < oby + h) then - self.parent:setCursor(true, anchx + textX - wIndex, anchy + textY - hIndex, self.fgColor) - else - self.parent:setCursor(false) - end + if (obx + textX - wIndex >= obx and obx + textX - wIndex < obx + w) and (anchy + textY - hIndex >= anchy and anchy + textY - hIndex < anchy + h) then + self.parent:setCursor(true, anchx + textX - wIndex, anchy + textY - hIndex, self.fgColor) + else + self.parent:setCursor(false) end self:updateDraw() return true @@ -533,8 +623,13 @@ return function(name) if (lines[y - oby + hIndex] ~= nil) then textX = x - obx + wIndex textY = y - oby + hIndex + endSelX = nil + endSelY = nil + startSelX = textX + startSelY = textY if (textX > lines[textY]:len()) then textX = lines[textY]:len() + 1 + startSelX = textX end if (textX < wIndex) then wIndex = textX - 1 @@ -542,6 +637,7 @@ return function(name) wIndex = 1 end end + self:updateDraw() end if (self.parent ~= nil) then self.parent:setCursor(true, anchx + textX - wIndex, anchy + textY - hIndex, self.fgColor) @@ -550,6 +646,25 @@ return function(name) end end, + mouseUpHandler = function(self, button, x, y) + if (base.mouseUpHandler(self, button, x, y)) then + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + local anchx, anchy = self:getAnchorPosition() + if (lines[y - oby + hIndex] ~= nil) then + endSelX = x - obx + wIndex + endSelY = y - oby + hIndex + if (endSelX > lines[endSelY]:len()) then + endSelX = lines[endSelY]:len() + 1 + end + if(startSelX==endSelX)and(startSelY==endSelY)then + startSelX, endSelX, startSelY, endSelY = nil, nil, nil, nil + end + self:updateDraw() + end + return true + end + end, + eventHandler = function(self, event, paste, p2, p3, p4) if(base.eventHandler(self, event, paste, p2, p3, p4))then if(event=="paste")then @@ -599,9 +714,27 @@ return function(name) self.parent:setBG(obx, oby + n - 1, bg) self.parent:setFG(obx, oby + n - 1, fg) end + if(startSelX~=nil)and(endSelX~=nil)and(startSelY~=nil)and(endSelY~=nil)then + local sx,ex,sy,ey = getSelectionCoordinates(self) + for n=sy,ey do + local line = lines[n]:len() + local xOffset = 0 + if(n==sy)and(n==ey)then + xOffset = sx-1 + line = line - (sx-1) - (line - ex) + elseif(n==ey)then + line = line - (line - ex) + elseif(n==sy)then + line = line-(sx-1) + xOffset = sx-1 + end + self.parent:setBG(obx + xOffset, oby + n - 1, rep(tHex[selectionBG], line)) + self.parent:setFG(obx + xOffset, oby + n - 1, rep(tHex[selectionFG], line)) + end + end if(self:isFocused())then local anchx, anchy = self:getAnchorPosition() - self.parent:setCursor(true, anchx + textX - wIndex, anchy + textY - hIndex, self.fgColor) + --self.parent:setCursor(true, anchx + textX - wIndex, anchy + textY - hIndex, self.fgColor) end end end @@ -609,6 +742,7 @@ return function(name) init = function(self) self.parent:addEvent("mouse_click", self) + self.parent:addEvent("mouse_up", self) self.parent:addEvent("mouse_scroll", self) self.parent:addEvent("mouse_drag", self) self.parent:addEvent("key", self) diff --git a/Basalt/objects/Thread.lua b/Basalt/objects/Thread.lua index 8dd3f6f..bd6e85b 100644 --- a/Basalt/objects/Thread.lua +++ b/Basalt/objects/Thread.lua @@ -7,6 +7,7 @@ return function(name) local func local cRoutine local isActive = false + local filter local generateXMLEventFunction = function(self, str) if(str:sub(1,1)=="#")then @@ -53,12 +54,21 @@ return function(name) func = f cRoutine = coroutine.create(func) isActive = true + filter=nil local ok, result = coroutine.resume(cRoutine) + filter = result if not (ok) then if (result ~= "Terminated") then error("Thread Error Occurred - " .. result) end end + self.parent:addEvent("mouse_click", self) + self.parent:addEvent("mouse_up", self) + self.parent:addEvent("mouse_scroll", self) + self.parent:addEvent("mouse_drag", self) + self.parent:addEvent("key", self) + self.parent:addEvent("key_up", self) + self.parent:addEvent("char", self) self.parent:addEvent("other_event", self) return self end; @@ -72,21 +82,58 @@ return function(name) stop = function(self, f) isActive = false + self.parent:removeEvent("mouse_click", self) + self.parent:removeEvent("mouse_up", self) + self.parent:removeEvent("mouse_scroll", self) + self.parent:removeEvent("mouse_drag", self) + self.parent:removeEvent("key", self) + self.parent:removeEvent("key_up", self) + self.parent:removeEvent("char", self) self.parent:removeEvent("other_event", self) return self end; - eventHandler = function(self, event, p1, p2, p3) + mouseHandler = function(self, ...) + self:eventHandler("mouse_click", ...) + end, + mouseUpHandler = function(self, ...) + self:eventHandler("mouse_up", ...) + end, + mouseScrollHandler = function(self, ...) + self:eventHandler("mouse_scroll", ...) + end, + mouseDragHandler = function(self, ...) + self:eventHandler("mouse_drag", ...) + end, + mouseMoveHandler = function(self, ...) + self:eventHandler("mouse_move", ...) + end, + keyHandler = function(self, ...) + self:eventHandler("key", ...) + end, + keyUpHandler = function(self, ...) + self:eventHandler("key_up", ...) + end, + charHandler = function(self, ...) + self:eventHandler("char", ...) + end, + + eventHandler = function(self, event, ...) if (isActive) then - if (coroutine.status(cRoutine) ~= "dead") then - local ok, result = coroutine.resume(cRoutine, event, p1, p2, p3) + if (coroutine.status(cRoutine) == "suspended") then + if(filter~=nil)then + if(event~=filter)then return end + filter=nil + end + local ok, result = coroutine.resume(cRoutine, event, ...) + filter = result if not (ok) then if (result ~= "Terminated") then error("Thread Error Occurred - " .. result) end end else - isActive = false + self:stop() end end end; diff --git a/Basalt/theme.lua b/Basalt/theme.lua index 6c0e1be..f503943 100644 --- a/Basalt/theme.lua +++ b/Basalt/theme.lua @@ -40,6 +40,8 @@ return { -- The default main theme for basalt! SwitchInactive = colors.red, SwitchActive = colors.green, LabelBG = false, - LabelText = colors.black + LabelText = colors.black, + GraphBG = colors.gray, + GraphText = colors.black } \ No newline at end of file