require "ISBaseObject" ISRichTextLayout = ISBaseObject:derive("ISRichTextLayout") ISRichTextLayout.drawMargins = false function ISRichTextLayout:initialise() ISBaseObject.initialise(self) end function ISRichTextLayout: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.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, "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]) 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 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 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 end if string.find(command, "SIZE:") then local size = string.sub(command, 6) if(size == "small") then self.font = UIFont.Small 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 / 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 return x, y, lineImageHeight end function ISRichTextLayout: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.orient = {} self.indent = 0 self.imageW = {} self.imageH = {} self.lineY = {} self.lineX = {} self.lines = {} local bDone = false; local leftText = 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 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) else 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 self.height = self.marginTop + y + self.marginBottom end function ISRichTextLayout:render(x, y, ui) if self.textDirty then self:paginate() end if self.lines == nil then return end if self.clip then ui:setStencilRect(x, y, self.width, self.height) end for c,v in ipairs(self.images) do ui:drawTextureScaled(v, x + self.marginLeft + self.imageX[c], y + self.marginTop + self.imageY[c], self.imageW[c], self.imageH[c], 1, 1, 1, 1) end self.r = 1 self.g = 1 self.b = 1 local orient = "left" for c,v in ipairs(self.lines) do if y + self.marginTop + self.lineY[c] + ui:getYScroll() >= ui: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] ~= nil then self.font = self.fonts[c] end if c == #self.lines or (y + self.marginTop + self.lineY[c+1] + ui:getYScroll() > 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 ui:drawTextCentre(string.trim(v), x + self.width / 2 , y + self.marginTop + self.lineY[c], r, g, b,1, self.font) elseif orient == "right" then ui:drawTextRight(string.trim(v), x + self.marginLeft + self.lineX[c], y + self.marginTop + self.lineY[c], r, g, b,1, self.font) else ui:drawText(string.trim(v), x + self.marginLeft + self.lineX[c], y + self.marginTop + self.lineY[c], r, g, b,1, self.font) end end end end if ISRichTextLayout.drawMargins then ui:drawRectBorder(x, y, self.width, self.height, 0.5,1,1,1) ui:drawRect(x + self.marginLeft, y, 1, self.height, 1,1,1,1) local maxLineWidth = self.maxLineWidth or (self.width - self.marginRight - self.marginLeft) ui:drawRect(x + self.marginLeft + maxLineWidth, y, 1, self.height, 1,1,1,1) ui:drawRect(x, y + self.marginTop, self.width, 1, 1,1,1,1) ui:drawRect(x, y + self.height - self.marginBottom, self.width, 1, 1,1,1,1) end if self.clip then ui:clearStencilRect() end end function ISRichTextLayout:setText(text) self.text = text end function ISRichTextLayout:setMargins(left, top, right, bottom) self.marginLeft = left self.marginTop = top self.marginRight = right self.marginBottom = bottom end function ISRichTextLayout:getWidth() return self.width end function ISRichTextLayout:setWidth(width) self.width = width end function ISRichTextLayout:getHeight() if self.textDirty then self:paginate() end return self.height end function ISRichTextLayout:new(width) local o = ISBaseObject.new(self) o.width = width o.height = 0 o.marginLeft = 20 o.marginTop = 10 o.marginRight = 10 o.marginBottom = 10 o.text = "" o.textDirty = false o.clip = false o.maxLines = 0 o.defaultFont = UIFont.NewSmall return o end