SHOW:
|
|
- or go back to the newest paste.
| 1 | - | -- Version: Unspeakable Crawling Skull |
| 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 = true -- Should show the startup message [true: show, false: don't show] |
| 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, not fixed right now (ver 85.0) |
| 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, p.dcolor, x+j-1+margin_L, y+i-1+margin_T) |
| 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() |