require "ISUI/ISPanel" ISRichTextPanel = ISPanel:derive("ISRichTextPanel"); ISRichTextPanel.drawMargins = false --************************************************************************-- --** ISRichTextPanel:initialise --** --************************************************************************-- function ISRichTextPanel:initialise() ISPanel.initialise(self); end function ISRichTextPanel:setText(text) self.text = text or "" end function ISRichTextPanel:processCommand(command, x, y, lineImageHeight, lineHeight) if command == "LINE" then x = 0; lineImageHeight = 0; y = y + lineHeight; end if command == "BR" then x = 0; lineImageHeight = 0; y = y + lineHeight + lineHeight; end if command == "H1" then self.orient[self.currentLine] = "centre"; self.rgb[self.currentLine] = {}; self.rgb[self.currentLine].r = 1; self.rgb[self.currentLine].g = 1; self.rgb[self.currentLine].b = 1; self.font = UIFont.Large; self.fonts[self.currentLine] = self.font; end if command == "H2" then self.orient[self.currentLine] = "left"; self.rgb[self.currentLine] = {}; self.rgb[self.currentLine].r = 0.8; self.rgb[self.currentLine].g = 0.8; self.rgb[self.currentLine].b = 0.8; self.font = UIFont.Medium; self.fonts[self.currentLine] = self.font; end if command == "TEXT" then self.orient[self.currentLine] = "left"; self.rgb[self.currentLine] = {}; self.rgb[self.currentLine].r = 0.7; self.rgb[self.currentLine].g = 0.7; self.rgb[self.currentLine].b = 0.7; self.rgbCurrent = self.rgb[self.currentLine] self.font = self.defaultFont; self.fonts[self.currentLine] = self.font; end if command == "CENTRE" then self.orient[self.currentLine] = "centre"; end if command == "LEFT" then self.orient[self.currentLine] = "left"; end if command == "RIGHT" then self.orient[self.currentLine] = "right"; end if string.find(command, "PUSHRGB:") then table.insert(self.rgbStack, self.rgbCurrent) local rgb = string.split(string.sub(command, 9, string.len(command)), ",") self.rgb[self.currentLine] = {} self.rgb[self.currentLine].r = tonumber(rgb[1]) self.rgb[self.currentLine].g = tonumber(rgb[2]) self.rgb[self.currentLine].b = tonumber(rgb[3]) self.rgbCurrent = self.rgb[self.currentLine] elseif string.find(command, "POPRGB") then if #self.rgbStack > 0 then self.rgbCurrent = table.remove(self.rgbStack, #self.rgbStack) self.rgb[self.currentLine] = self.rgbCurrent end elseif string.find(command, "RGB:") then local rgb = string.split(string.sub(command, 5, string.len(command)), ","); self.rgb[self.currentLine] = {}; self.rgb[self.currentLine].r = tonumber(rgb[1]); self.rgb[self.currentLine].g = tonumber(rgb[2]); self.rgb[self.currentLine].b = tonumber(rgb[3]); self.rgbCurrent = self.rgb[self.currentLine] end if string.find(command, "RED") then self.rgb[self.currentLine] = {}; self.rgb[self.currentLine].r = 1; self.rgb[self.currentLine].g = 0; self.rgb[self.currentLine].b = 0; self.rgbCurrent = self.rgb[self.currentLine] end if string.find(command, "ORANGE") then self.rgb[self.currentLine] = {}; self.rgb[self.currentLine].r = 0.9; self.rgb[self.currentLine].g = 0.3; self.rgb[self.currentLine].b = 0; self.rgbCurrent = self.rgb[self.currentLine] end if string.find(command, "GREEN") then self.rgb[self.currentLine] = {}; self.rgb[self.currentLine].r = 0; self.rgb[self.currentLine].g = 1; self.rgb[self.currentLine].b = 0; self.rgbCurrent = self.rgb[self.currentLine] end if string.find(command, "SIZE:") then local size = string.sub(command, 6); --~ print(size); if(size == "small") then self.font = UIFont.NewSmall; end if(size == "medium") then self.font = UIFont.Medium; end if(size == "large") then self.font = UIFont.Large; end self.fonts[self.currentLine] = self.font; end if string.find(command, "IMAGE:") ~= nil then local w = 0; local h = 0; if string.find(command, ",") ~= nil then local vs = string.split(command, ","); command = string.trim(vs[1]); w = tonumber(string.trim(vs[2])); h = tonumber(string.trim(vs[3])); end self.images[self.imageCount] = getTexture(string.sub(command, 7)); if(w==0) then w = self.images[self.imageCount]:getWidth(); h = self.images[self.imageCount]:getHeight(); end if(x + w >= self.width - (self.marginLeft + self.marginRight)) then x = 0; y = y + lineHeight; end if(lineImageHeight < h + 0) then lineImageHeight = h + 0; end if self.images[self.imageCount] == nil then --print("Could not find texture"); end self.imageX[self.imageCount] = x+2; self.imageY[self.imageCount] = y; self.imageW[self.imageCount] = w; self.imageH[self.imageCount] = h; self.imageCount = self.imageCount + 1; x = x + w + 7; --[[ local newY = math.max(y + (h / 2) - 7, y) for c,v in ipairs(self.lines) do if self.lineY[c] == y then self.lineY[c] = newY; end end for c,v in ipairs(self.imageY) do if self.imageY[c] == y then self.imageY[c] = newY; end end y = newY; --]] end if string.find(command, "IMAGECENTRE:") ~= nil then local w = 0; local h = 0; if string.find(command, ",") ~= nil then local vs = string.split(command, ","); command = string.trim(vs[1]); w = tonumber(string.trim(vs[2])); h = tonumber(string.trim(vs[3])); end self.images[self.imageCount] = getTexture(string.sub(command, 13)); if(w==0) then w = self.images[self.imageCount]:getWidth(); h = self.images[self.imageCount]:getHeight(); end if(x + w >= self.width - (self.marginLeft + self.marginRight)) then x = 0; y = y + lineHeight; end if(lineImageHeight < (h / 2) + 8) then lineImageHeight = (h / 2) + 16; end if self.images[self.imageCount] == nil then --print("Could not find texture"); end local mx = (self.width - self.marginLeft - self.marginRight) / 2 - self.marginLeft; self.imageX[self.imageCount] = mx - (w/2); self.imageY[self.imageCount] = y; self.imageW[self.imageCount] = w; self.imageH[self.imageCount] = h; self.imageCount = self.imageCount + 1; x = x + w + 7; for c,v in ipairs(self.lines) do if self.lineY[c] == y then self.lineY[c] = self.lineY[c] + (h / 2); end end y = y + (h / 2); end if string.find(command, "INDENT:") then self.indent = tonumber(string.sub(command, 8)) end if string.find(command, "JOYPAD:") ~= nil then local w = 0 local h = 0 if string.find(command, ",") ~= nil then local vs = string.split(command, ",") command = string.trim(vs[1]) w = tonumber(string.trim(vs[2])) h = tonumber(string.trim(vs[3])) end self.images[self.imageCount] = Joypad.Texture[string.sub(command, 8)] if w == 0 then w = self.images[self.imageCount]:getWidth() h = self.images[self.imageCount]:getHeight() end if(x + w >= self.width - (self.marginLeft + self.marginRight)) then x = 0 y = y + lineHeight end if lineImageHeight < h + 0 then lineImageHeight = h + 0; end self.imageX[self.imageCount] = x+2 self.imageY[self.imageCount] = y self.imageW[self.imageCount] = w self.imageH[self.imageCount] = h self.imageCount = self.imageCount + 1 x = x + w + 7 end if string.find(command, "SETX:") then x = tonumber(string.sub(command, 6)) end if string.find(command, "SPACE") then if x > 0 then x = x + getTextManager():MeasureStringX(self.font, " ") + 2 end end return x, y, lineImageHeight end function ISRichTextPanel:replaceKeyName(text, offset) local p1,p2 = string.find(text, "", p2 + 1) if not p3 then return text, nil end local binding = string.sub(text, p2 + 1, p3 - 1):gsub(" ", " ") local textBefore = string.sub(text, 1, p1 - 1) local textAfter = string.sub(text, p4 + 1) local newText = " " .. getKeyName(getCore():getKey(binding)) .. " " return textBefore .. newText .. textAfter, p1 + #newText end function ISRichTextPanel:replaceKeyNames(text) local offset = 1 while true do text, offset = self:replaceKeyName(text, offset) if not offset then break end end return text end function ISRichTextPanel:onMouseWheel(del) self:setYScroll(self:getYScroll() - (del*18)); return true; end --************************************************************************-- --** ISRichTextPanel:paginate --** --** Splits multiline text up into seperate lines, and positions images to be --** rendered --************************************************************************-- function ISRichTextPanel:paginate() local lines = 1; self.textDirty = false; self.imageCount = 1; self.font = self.defaultFont; self.fonts = {}; self.images = {} self.imageX = {} self.imageY = {} self.rgb = {}; self.rgbCurrent = { r = 1, g = 1, b = 1 } self.rgbStack = {} self.orient = {} self.indent = 0 self.imageW = {} self.imageH = {} self.lineY = {} self.lineX = {} self.lines = {} self.keybinds = {} local bDone = false; local leftText = self:replaceKeyNames(self.text) .. ' '; local cur = 0; local y = 0; local x = 0; local lineImageHeight = 0; leftText = leftText:gsub("\n", " ") if self.maxLines > 0 then local lines = leftText:split("") for i=1,(#lines - self.maxLines) do table.remove(lines,1) end leftText = ' ' for k,v in ipairs(lines) do leftText = leftText..v.." " end end local maxLineWidth = self.maxLineWidth or (self.width - self.marginRight - self.marginLeft) -- Always go through at least once. while not bDone do cur = string.find(leftText, " ", cur+1); if cur ~= nil then -- while string.sub(leftText, cur, cur)== " " do -- cur = cur + 1 -- end -- cur = cur - 1 local token = string.sub(leftText, 0, cur); if string.find(token, "<") and string.find(token, ">") then -- handle missing ' ' after '>' cur = string.find(token, ">") + 1; token = string.sub(leftText, 0, cur - 1); end leftText = string.sub(leftText, cur); cur = 1 if string.find(token, "<") and string.find(token, ">") then if not self.lines[lines] then self.lines[lines] = '' self.lineX[lines] = x self.lineY[lines] = y end lines = lines + 1 local st = string.find(token, "<"); local en = string.find(token, ">"); local escSeq = string.sub(token, st+1, en-1); local lineHeight = getTextManager():getFontFromEnum(self.font):getLineHeight(); if lineHeight < 10 then lineHeight = 10; end if lineHeight < lineImageHeight then lineHeight = lineImageHeight; end self.currentLine = lines; x, y, lineImageHeight = self:processCommand(escSeq, x, y, lineImageHeight, lineHeight); else if token:contains("<") then token = token:gsub("<", "<") end if token:contains(">") then token = token:gsub(">", ">") end local chunkText = self.lines[lines] or '' local chunkX = self.lineX[lines] or x if chunkText == '' then chunkText = string.trim(token) elseif string.trim(token) ~= '' then chunkText = chunkText..' '..string.trim(token) end local pixLen = getTextManager():MeasureStringX(self.font, chunkText); if chunkX + pixLen > maxLineWidth then if self.lines[lines] and self.lines[lines] ~= '' then lines = lines + 1; end local lineHeight = getTextManager():getFontFromEnum(self.font):getLineHeight(); if lineHeight < lineImageHeight then lineHeight = lineImageHeight; end lineImageHeight = 0; y = y + lineHeight; x = 0; self.lines[lines] = string.trim(token) if self.lines[lines] ~= "" then x = self.indent end self.lineX[lines] = x self.lineY[lines] = y x = x + getTextManager():MeasureStringX(self.font, self.lines[lines]) else if not self.lines[lines] then self.lines[lines] = '' self.lineX[lines] = x self.lineY[lines] = y end self.lines[lines] = chunkText if self.lineX[lines] == 0 and self.lines[lines] ~= "" then self.lineX[lines] = self.indent end x = self.lineX[lines] + pixLen end end else if string.trim(leftText) ~= '' then local str = leftText if str:contains("<") then str = str:gsub("<", "<") end if str:contains(">") then str = str:gsub(">", ">") end self.lines[lines] = string.trim(str); if x == 0 and self.lines[lines] ~= "" then x = self.indent end self.lineX[lines] = x; self.lineY[lines] = y; local lineHeight = getTextManager():getFontFromEnum(self.font):getLineHeight(); y = y + lineHeight elseif self.lines[lines] and self.lines[lines] ~= '' then local lineHeight = getTextManager():getFontFromEnum(self.font):getLineHeight(); if lineHeight < lineImageHeight then lineHeight = lineImageHeight; end y = y + lineHeight end bDone = true; end end if self.autosetheight then self:setHeight(self.marginTop + y + self.marginBottom); end self:setScrollHeight(self.marginTop + y + self.marginBottom); end function ISRichTextPanel:setContentTransparency(alpha) self.contentTransparency = alpha; end --************************************************************************-- --** ISRichTextPanel:render --** --************************************************************************-- function ISRichTextPanel:render() self.r = 1; self.g = 1; self.b = 1; if self.lines == nil then return; end if self.keybinds then for binding,text in pairs(self.keybinds) do if getKeyName(getCore():getKey(binding)) ~= text then self.textDirty = true break end end end if self.clip then self:setStencilRect(0, 0, self.width, self.height) end if self.textDirty then self:paginate(); end --ISPanel.render(self); for c,v in ipairs(self.images) do self:drawTextureScaled(v, self.imageX[c] + self.marginLeft, self.imageY[c] + self.marginTop, self.imageW[c], self.imageH[c], self.contentTransparency, 1, 1, 1); end self.font = self.defaultFont local orient = "left"; local c = 1 while c <= #self.lines do local v = self.lines[c] if self.lineY[c] + self.marginTop + self:getYScroll() >= self:getHeight() then break end if self.rgb[c] then self.r = self.rgb[c].r; self.g = self.rgb[c].g; self.b = self.rgb[c].b; end if self.orient[c] then orient = self.orient[c]; end if self.fonts[c] then self.font = self.fonts[c]; end if self.marginTop + self:getYScroll() + self.lineY[c] + getTextManager():getFontHeight(self.font) > 0 then local r = self.r; local b = self.b; local g = self.g; if v:contains("<") then v = v:gsub("<", "<") end if v:contains(">") then v = v:gsub(">", ">") end if string.trim(v) ~= "" then if orient == "centre" then local lineY = self.lineY[c] local lineLength = 0 local c2 = c while (c2 <= #self.lines) and (self.lineY[c2] == lineY) do local font = self.fonts[c2] or self.font lineLength = lineLength + getTextManager():MeasureStringX(font, string.trim(self.lines[c2])) c2 = c2 + 1 end local lineX = self.marginLeft + (self.width - self.marginLeft - self.marginRight - lineLength) / 2 while (c <= #self.lines) and (self.lineY[c] == lineY) do if self.rgb[c] then self.r = self.rgb[c].r; self.g = self.rgb[c].g; self.b = self.rgb[c].b; end local r = self.r; local b = self.b; local g = self.g; if self.orient[c] then orient = self.orient[c]; end if self.fonts[c] then self.font = self.fonts[c]; end self:drawText(string.trim(self.lines[c]), lineX + self.lineX[c], self.lineY[c] + self.marginTop, r, g, b, self.contentTransparency, self.font) -- lineX = lineX + getTextManager():MeasureStringX(self.font, self.lines[c]) c = c + 1 end c = c - 1 elseif orient == "right" then self:drawTextRight( string.trim(v), self.lineX[c] + self.marginLeft, self.lineY[c] + self.marginTop, r, g, b,self.contentTransparency, self.font); else self:drawText( string.trim(v), self.lineX[c] + self.marginLeft, self.lineY[c] + self.marginTop, r, g, b,self.contentTransparency, self.font); end -- self:drawRect(self.lineX[c] + self.marginLeft, self.lineY[c] + self.marginTop, self.width, 1, 1.0, 0.5, 0.5, 0.5) end end c = c + 1 end if ISRichTextPanel.drawMargins then self:drawRectBorder(0, 0, self.width, self:getScrollHeight(), 0.5,1,1,1) self:drawRect(self.marginLeft, 0, 1, self:getScrollHeight(), 1,1,1,1) local maxLineWidth = self.maxLineWidth or (self.width - self.marginRight - self.marginLeft) -- self:drawRect(self.marginLeft + maxLineWidth, 0, 1, self:getScrollHeight(), 1,1,1,1) self:drawRect(self.width - self.marginRight, 0, 1, self:getScrollHeight(), 1,1,1,1) self:drawRect(0, self.marginTop, self.width, 1, 1,1,1,1) self:drawRect(0, self:getScrollHeight() - self.marginBottom, self.width, 1, 1,1,1,1) end if self.clip then self:clearStencilRect() end --self:setScrollHeight(y); end function ISRichTextPanel:onResize() -- ISUIElement.onResize(self); self.width = self:getWidth(); self.height = self:getHeight(); self.textDirty = true; self:updateScrollbars() end function ISRichTextPanel:setMargins(left, top, right, bottom) self.marginLeft = left self.marginTop = top self.marginRight = right self.marginBottom = bottom end --************************************************************************-- --** ISRichTextPanel:new --** --************************************************************************-- function ISRichTextPanel:new (x, y, width, height) local o = {} --o.data = {} o = ISPanel:new(x, y, width, height); setmetatable(o, self); self.__index = self; o.x = x; o.y = y; o.contentTransparency = 1.0; o.backgroundColor = {r=0, g=0, b=0, a=0.5}; o.borderColor = {r=0, g=0, b=0, a=0.0}; o.width = width; o.height = height; o.anchorLeft = true; o.anchorRight = false; o.anchorTop = true; o.anchorBottom = false; o.marginLeft = 20; o.marginTop = 10; o.marginRight = 10; o.marginBottom = 10; o.autosetheight = true; o.text = "" o.textDirty = false; o.textR = 1; o.textG = 1; o.textB = 1; o.clip = false o.maxLines = 0 o.defaultFont = UIFont.NewSmall return o; end