Guest User

Texter.lua

a guest
Feb 17th, 2013
213
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- Version: Unspeakable Crawling Skully
  2.  
  3. Texter = {}
  4. Texter._LastInput = {
  5.     Str  = "",
  6.     Args = "1, 3",
  7.     Font = "7px",
  8.     Mode = 0
  9. }
  10. Texter._Fonts = {}
  11.  
  12. ------------------ User config zone -------------------
  13. Texter.FONT_FOLDERS_TO_LOAD = {"TexterFonts", "Scripts", "%."}   -- All file will be "dofile" under those foler name, the root dir must be written regex form %.
  14. Texter.DISPLAY_MESSAGES     = false              -- Should show the startup message [true: show, false: don't show]
  15. Texter.FONT_SEARCH_DEPTH    = 3                 -- How deep should texter search the font folder [0: only the root folder]
  16. ---------------- User config zone ends ----------------
  17.  
  18. function Texter.Init(register)
  19.     Texter._Fontmatrix = {}
  20.     Texter._Fonts = {}
  21.     -- Please don't let Texter too far away from font file :)
  22.     local fontFiles = Texter.LoadAllFontsInFolder(".", Texter.FONT_FOLDERS_TO_LOAD, Texter.FONT_SEARCH_DEPTH)
  23.     local msg = ""
  24.     if(Texter._Fontmatrix ~= nil) then
  25.         for fontName in pairs(Texter._Fontmatrix) do
  26.             table.insert(Texter._Fonts, fontName)
  27.         end
  28.         msg = "Texter: "..#fontFiles.." font file(s) loaded, "..#Texter._Fonts.." font(s) available in total."
  29.     else
  30.         msg = "Texter: No font found."
  31.     end
  32.     if( Texter.DISPLAY_MESSAGES ) then
  33.         tpt.log(msg)
  34.     end
  35.     if( register == nil or register == true ) then
  36.         tpt.register_keypress(Texter._HotkeyHandler)
  37.     end
  38. end
  39.  
  40. -- Internal methods
  41. function Texter._IsFolder(path)
  42.     local _isFolder = false
  43.     if(path ~= nil) then
  44.         -- API bug, fixed(ver 86.0)
  45.         -- if( fs.isDirectory(path)  ) then
  46.                 -- _isFolder = true
  47.         -- end
  48.         if( fs.isDirectory(path) == fs.isDirectory(".") ) then
  49.             _isFolder = true
  50.         end
  51.     end
  52.     return _isFolder
  53. end
  54. function Texter._GetFontHeight(fontName)
  55.     local height = 0
  56.     if( Texter._Fontmatrix ~= nil and Texter._Fontmatrix[fontName] ~=nil and Texter._Fontmatrix[fontName].height ~= nil  ) then
  57.         height = Texter._Fontmatrix[fontName].height
  58.     else
  59.         height = 7 -- 7 is a lucky number
  60.     end
  61.     return height
  62. end
  63. function Texter._Input(eventHandler, fonts, paramsText, strText, fontText, mode)
  64.     local controlHeight = 17
  65.     if( paramsText == nil  ) then paramsText = "" end
  66.     if( strText    == nil  ) then strText    = "" end
  67.     if( fontText   == nil  ) then fontText   = "" end
  68.     if( mode       == nil  ) then mode       = 0  end
  69.     if( fonts      == nil  ) then fonts      = {} end
  70.    
  71.     local isContains = function(inTable, value)
  72.         local isContain = false
  73.         for i=1, #inTable do
  74.             if( value == inTable[i]  ) then
  75.                 isContain = true
  76.                 break
  77.             end
  78.         end
  79.         return isContain
  80.     end
  81.    
  82.     local TexterWin     = Window:new(-1, -1, 500, 100)
  83.     local paramsBox     = Textbox:new(10, controlHeight + 15, 150, controlHeight, paramsText, "ptype, hspc, vspc")
  84.     local paramsLabel   = Label:new(11, 9, 130, controlHeight, "Element type and\nhorizontal/vertical spacing.")
  85.     local modeBox       = Button:new(170, controlHeight + 15, 150, controlHeight, "No mode available")
  86.     local modeLabel     = Label:new(171, 9, 150, controlHeight, "Font mode you want to use.")
  87.     local stringBox     = Textbox:new(10, 57, 480, controlHeight, strText, "Input text to create under mouse. Use \\n for new line, \\\\n for \\n. Sorry, you can't get \\\\n.")
  88.     local fontBtn       = Button:new(330, controlHeight + 15, 150, controlHeight, "No font available")
  89.     local fontLabel     = Label:new(331, 9, 150, controlHeight, "Select the font you want to use.")
  90.     local cancelBtn     = Button:new(0, 83, 251, controlHeight, "Cancel")
  91.     cancelBtn:action(function(sender)interface.closeWindow(TexterWin)end)
  92.     local okayBtn = Button:new(250, 83, 250, controlHeight, "Okay")
  93.     okayBtn:action(
  94.         function(sender)
  95.             if( eventHandler ~= nil and type(eventHandler) == "function"  ) then
  96.                 -- Use event because I can't make things like tpt.input
  97.                 eventHandler(paramsBox:text(), stringBox:text(), fontBtn:text(), tonumber(modeBox:text()))
  98.             end
  99.             interface.closeWindow(TexterWin)
  100.         end
  101.     )
  102.     if( mode >= 0 ) then
  103.         modeBox:text(tostring(mode))
  104.     end
  105.     modeBox:action(
  106.         function(sender)
  107.             local input = tpt.input("Font mode", "The draw mode:\n0  Ignore all additional information\n+1 Keep the ptype in font\n+2 Keep the dcolor in font\nAdd your choices up then type in.")
  108.             local mode = tonumber(input)
  109.             if( mode~= nil and mode >= 0 )then
  110.                 sender:text(input)
  111.             else
  112.                 sender:text("0")
  113.             end
  114.         end
  115.     )
  116.     if(#fonts > 0) then
  117.         if( isContains(fonts, fontText)  ) then
  118.             fontBtn:text(fontText)
  119.         else
  120.             fontBtn:text(fonts[1])
  121.         end
  122.         fontBtn:action(
  123.             function(sender)
  124.                 local winX, winY = TexterWin:position()
  125.                 local btnX, btnY = fontBtn:position()
  126.                 local btnW, btnH = fontBtn:size()
  127.                 local listWin = Window:new(winX+btnX, winY+btnY-controlHeight*(#fonts-1)/2, btnW, (btnH-1)*#fonts)
  128.                 for i=1, #fonts do
  129.                     local optionBtn = Button:new(0, (btnH-1)*(i-1), btnW, btnH, fonts[i])
  130.                     optionBtn:action(
  131.                         function(sender)
  132.                             interface.closeWindow(listWin)
  133.                             fontBtn:text(optionBtn:text())
  134.                         end
  135.                     )
  136.                     listWin:addComponent(optionBtn)
  137.                 end
  138.                 interface.showWindow(listWin)
  139.             end
  140.         )
  141.     end
  142.    
  143.     TexterWin:addComponent(cancelBtn)
  144.     TexterWin:addComponent(okayBtn)
  145.     TexterWin:addComponent(stringBox)
  146.     TexterWin:addComponent(paramsBox)
  147.     TexterWin:addComponent(paramsLabel)
  148.     TexterWin:addComponent(modeBox)
  149.     TexterWin:addComponent(modeLabel)
  150.     TexterWin:addComponent(fontBtn)
  151.     TexterWin:addComponent(fontLabel)
  152.    
  153.     interface.showWindow(TexterWin)
  154. end
  155. function Texter._FindAllFile(rootPath, foldersToLoad, depth)
  156.     if(  rootPath == nil  ) then
  157.         rootPath = "."
  158.     end
  159.     if(  depth == nil  ) then
  160.         depth = 1
  161.     end
  162.     local files = {}
  163.     local isTargetFolder = false
  164.     for i, folderName in ipairs(foldersToLoad) do
  165.         if( string.match(rootPath, "\\?"..folderName.."$") ~= nil ) then
  166.             isTargetFolder = true
  167.             break
  168.         end
  169.     end
  170.     if( isTargetFolder ) then  -- If not the folder we want, ignore it
  171.         files = fs.list(rootPath)
  172.     end
  173.    
  174.     -- Trim match array
  175.     local index = 1 -- Lua fool
  176.     for i=1, #files do
  177.         if( files[index] == "Texter.lua"
  178.           or string.match(files[index], "%.texterfont$") == nil
  179.           or Texter._IsFolder(rootPath.."\\"..files[index]) ) then
  180.             table.remove(files, index)
  181.         else
  182.             files[index] = rootPath.."\\"..files[index] -- full path
  183.             index = index + 1
  184.         end
  185.     end
  186.    
  187.     -- Check subs
  188.     if(  depth > 0  ) then
  189.         local subs = fs.list(rootPath)
  190.         local subFiles = nil
  191.         for i=1, #subs do
  192.             if( Texter._IsFolder(rootPath.."\\"..subs[i]) ) then
  193.                 subFiles = Texter._FindAllFile(rootPath.."\\"..subs[i], foldersToLoad, depth - 1)
  194.                 Texter._AppendArray(files, subFiles)
  195.             end
  196.         end
  197.     end
  198.     return files
  199. end
  200. function Texter._AppendArray(oriArray, arrayToAppend) -- Append an array to the original array
  201.     if(  oriArray~= nil and arrayToAppend ~= nil and #arrayToAppend>0  ) then
  202.         for i=1, #arrayToAppend do
  203.             table.insert(oriArray, arrayToAppend[i])
  204.         end
  205.     end
  206.     return oriArray
  207. end
  208.  
  209. -- Helper methods and handlers
  210. function Texter._HotkeyHandler(key, keyNum, modifier, event) -- Hotkey handler
  211.     if( event==1 and keyNum==116 and modifier==64  ) then -- Ctrl + t
  212.         -- Additional settings
  213.         local ptype = elements[tpt.selectedl]
  214.         if( ptype == nil  ) then
  215.             ptype = 1 --"DUST"
  216.         end
  217.         -- Prompt
  218.         Texter._Input(
  219.             Texter._InputHandler,
  220.             Texter._Fonts,
  221.             elements.property(ptype, "Name")..", "..Texter._LastInput.Args,
  222.             Texter._LastInput.Str,
  223.             Texter._LastInput.Font,
  224.             Texter._LastInput.Mode
  225.         )
  226.     end
  227. end
  228. function Texter._InputHandler(params, str, fontName, mode) -- Input handler
  229.     local args = {}
  230.     if( string.len(str) > 0  ) then
  231.         Texter._LastInput.Str = str
  232.         str = string.gsub(str, "([^\\]?)\\n", "%1\n")  -- Small trick
  233.         str = string.gsub(str, "([^\\]?)\\\\n", "%1\\n")
  234.     end
  235.    
  236.     if( string.len(params) > 0  ) then Texter._LastInput.Args = string.gsub(params, "%s*%w*%s*,%s*(.*)", "%1") end
  237.     local i=1
  238.     for arg in string.gmatch(params, "%s*(%w*)%s*,?") do
  239.         args[i] = arg
  240.         i = i+1
  241.     end
  242.     Texter._LastInput.Font = fontName
  243.     Texter._LastInput.Mode = mode
  244.    
  245.     if( pcall(tpt.element, args[1]) == false  ) then
  246.         args[1] = "DUST"
  247.     end
  248.     Texter.Tstr(str, tpt.mousex, tpt.mousey, args[1], mode, args[2], args[3], fontName)
  249. end
  250.  
  251. -- APIs
  252. function Texter.LoadAllFontsInFolder(rootPath, foldersToLoad, depth) -- Load all fonts in target folder(s)
  253.     local fonts = Texter._FindAllFile(rootPath, foldersToLoad, depth)
  254.     if(  fonts ~= nil  ) then
  255.         for i=1, #fonts do
  256.             dofile(fonts[i])
  257.         end
  258.     end
  259.     return fonts
  260. end
  261. function Texter.Tchar(char, x, y, ptype, mode, fontName) -- Draw a single character
  262.     local mtx     = {}
  263.     local letter  = {}
  264.     local PTYPE_MASK  = 0xFF
  265.     local DCOLOR_MASH = 0xFFFFFFFF
  266.     local DCOLOR_OFFSET = 8
  267.     local margin_L = 0 -- margin left
  268.     local margin_R = 0 -- margin right
  269.     local margin_T = 0 -- margin top
  270.     -- if given font not available, use the first available one
  271.     if( fontName == nil or Texter._Fontmatrix[fontName] == nil  ) then
  272.         for font in pairs(Texter._Fontmatrix) do
  273.             fontName = font
  274.             break
  275.         end
  276.     end
  277.     -- if still not available, break
  278.     if( fontName == nil  ) then return 0 end
  279.    
  280.     -- get character data
  281.     letter = Texter._Fontmatrix[fontName][char]
  282.     if( letter == nil  ) then
  283.         letter = Texter._Fontmatrix[fontName]["nil"]
  284.     end
  285.     if( letter == nil  ) then return 0 end -- ["nil"] not defined
  286.     mtx = letter.Mtx
  287.     if( mtx == nil  ) then mtx = {} end
  288.     if( letter.Margin ~= nil  ) then
  289.         if(letter.Margin.Left  ~= nil)then margin_L = letter.Margin.Left  end
  290.         if(letter.Margin.Right ~= nil)then margin_R = letter.Margin.Right end
  291.         if(letter.Margin.Top   ~= nil)then margin_T = letter.Margin.Top   end
  292.     end
  293.    
  294.     local width  = 0
  295.     for i=1, #mtx do --mtx height
  296.         if(#mtx[i] > width)then width = #mtx[i] end
  297.         for j=1, width do
  298.             local particle = mtx[i][j]
  299.             if( particle~=0  ) then
  300.                 local success = false
  301.                 local p = {}
  302.                 p.ptype  = bit.band(particle                , PTYPE_MASK )
  303.                 p.dcolor = bit.band(particle/2^DCOLOR_OFFSET, DCOLOR_MASH) -- bit.rshift can only handle 5 bits :(
  304.                 if( ptype == 0 or ptype == "0" ) then
  305.                     pcall(tpt.delete, x+j-1+margin_L, y+i-1+margin_T)
  306.                 else
  307.                     -- mode 0 use the given type
  308.                     --     +1 use the font ptype only
  309.                     --     +2 use the font dcolor only
  310.                     if( bit.band(mode, 1) ~= 1 ) then
  311.                         p.ptype = ptype
  312.                     end
  313.                     -- tpt.log("particle is "..particle..", ptype is "..ptype..", to draw is "..p.ptype)-- debug
  314.                     pcall(tpt.create, x+j-1+margin_L, y+i-1+margin_T, p.ptype)
  315.                     if( bit.band(mode, 2) == 2 ) then -- Paint it even when failed to create. Because there might be existed particle
  316.                         -- tpt.log("Try to draw dcolor: "..p.dcolor.." ( 0x"..string.format("%X", p.dcolor).." )") --debug
  317.                         pcall(tpt.set_property, "dcolor", p.dcolor, x+j-1+margin_L, y+i-1+margin_T, 1, 1)
  318.                     end
  319.                 end
  320.             end
  321.         end
  322.     end
  323.     width = width + margin_L + margin_R
  324.     return width
  325. end
  326. function Texter.Tstr(str, x, y, ptype, mode, hspacing, vspacing, fontName) -- Draw a string
  327.     local ox    = 0
  328.     local oy    = 0
  329.     local oy    = 0
  330.     local fontH = Texter._GetFontHeight(fontName)
  331.     if( mode == nil  ) then
  332.         mode = 0
  333.     end
  334.     if( hspacing == nil  ) then
  335.         hspacing = 1
  336.     end
  337.     if( vspacing == nil  ) then
  338.         vspacing = 3
  339.     end
  340.     for i=1,string.len(str) do
  341.         if( string.sub(str, i, i) == "\n"  ) then
  342.             oy = vspacing + oy + fontH
  343.             ox = 0
  344.         else
  345.             ox = hspacing + ox + Texter.Tchar(string.sub(str, i, i), x+ox, y+oy, ptype, mode, fontName)
  346.         end
  347.     end
  348.     return string.len(str)
  349. end
  350. function T(str, ptype, mode, hspc, vspc, fontName) -- Shortcut for better user experience
  351.     if( ptype == nil  ) then
  352.         ptype = elements[tpt.selectedl] -- elements.property(tpt.selectedl, "Name")
  353.     end
  354.     if( ptype == nil  ) then
  355.         ptype = "DUST"
  356.     end
  357.     if( str == nil  ) then str = "" end
  358.     Texter.Tstr(str, tpt.mousex, tpt.mousey, ptype, mode, hspc, vspc, fontName)
  359.     return string.len(str)
  360. end
  361.  
  362. Texter.Init()
RAW Paste Data