1
0
Fork 0
mirror of https://github.com/ctruLua/ctruLua.git synced 2025-10-27 16:39:29 +00:00

Updated filepicker.lua and adding documentation

Effectively rendering openfile.lua completely obsolete, which is why it
was deleted. filepicker.lua is now way smarter, handles file creation
and has a documentation file in LDoc.
This commit is contained in:
Neil Zeke Cecchini 2016-04-02 18:31:32 +02:00
parent c687efcfb4
commit 4669a68402
4 changed files with 361 additions and 305 deletions

View file

@ -11,7 +11,7 @@ format = "markdown"
plain = true plain = true
-- Input files -- Input files
topics = "../README.md" topics = {"../README.md", "filepicker.md"}
file = "../source/" file = "../source/"
examples = "../sdcard/3ds/ctruLua/examples/" examples = "../sdcard/3ds/ctruLua/examples/"
manual_url = "file://../libs/lua-5.3.1/doc/manual.html" manual_url = "file://../libs/lua-5.3.1/doc/manual.html"

93
doc/filepicker.md Normal file
View file

@ -0,0 +1,93 @@
# filepicker
## filePicker([workingDirectory[, bindings[, callbacks[, ...]]]])
### Argument: workingDirectory
The directory that shows up first in the file browser.
If this is nil, ctr.fs.getDirectory() is used.
The recommended form is sdmc:/path/ or romfs:/path/, but it can be a simple /path/ instead.
#### Possible values
- string
- nil
### Argument: bindings
A table, list of filetypes and key bindings related to these filetypes.
#### Format
```
{
__default, __directory, [Lua regexp] = {
[keys from ctr.hid.keys()] = {
function
string
}...
__name = string
}...
}
```
The Lua regexp is matched against the filename to determine if it is of this type.
__directory is the "file type" for directories
__default is the "file type" for files that cannot be matched with any other.
Also, every other type inherits the values it doesn't define from __default.
A file type contains the human-readable name (__name) of the type, displayed on the bottom screen,
as well as an optional binding for each key.
The optional binding is formed of an anonymous function, followed by the key's label to be displayed on the bottom screen,
if the label is nil, the key isn't displayed but still bound.
The function is defined as-is:
##### function(externalConfig, selected, bindingPattern, bindingKey)
externalConfig.workingDirectory is the active directory for filePicker, doesn't necessarily match ctrµLua's.
externalConfig.bindings, externalConfig.callbacks and externalConfig.additionalArguments all are the arguments passed to filePicker,
starting from position 2.
externalConfig.fileList is the list of files currently displayed by filePicker, in the same format as is returned by ctr.fs.listDirectory().
selected.inList is the absolute position of the cursor in externalConfig.fileList
selected.offset is the number of items skipped for display from fileList
bindingPattern is the [Lua regexp] defined earlier, and bindingKey the [key] that triggered this event.
This function may return the same thing as filePicker itself (Defined later here), or nothing. If it returns nothing,
filePicker will keep running, if it returns the same returns as filePicker, filePicker will exit, returning these values.
#### Notes
Sane defaults are set if you did not set them otherwise:
__default.x quits filePicker, returning the current directory, "__directory", nil and "x". See the returns to understand what this means.
__directory.a changes directories to that directory.
### Argument: callbacks
A table defining the callbacks ran at the end of each of the equivalent phases.
#### Format
```
{
drawTop, drawBottom, eventHandler = function
}
```
All of these take the following parameters: (externalConfig, selected)
They have the meaning defined earlier.
#### Notes
Although drawTop and drawBottom are ran at the end of their respective functions,
eventHandler is not, as it cannot be without being run repeatedly, so it's run at the beginning of
the ACTUAL eventHandler instead of its end.
### Argument: ...
Additional parameters. All of these are aggregated orderly in externalConfig.additionalArguments and passed around as explained earlier.
They have no specific meaning unless defined so by event handling functions.
### Return 1: selectedPath
The path selected by the either, may or may not have the sdmc:/romfs: prefix, depending on your input.
A string.
### Return 2: bindingPattern
The pattern the file matched to. You can use this to know exactly which kind of file you're dealing with
A string.
### Return 3: mode
Included handlers may have it be "open", in case you're opening an existing file, "new" in case the user wants to create a new file,
Or nil. A "nil" is assumed to mean that the user didn't pick anything.
A string or nil.
### Return 4: key
The key that triggered the event that made filePicker exit.
A string.
## Included event handlers you have available are:
- filepicker.changeDirectory - The name is on the tin, change workingDirectory to the active element and refresh the file list for that path.
- filepicker.openFile - Quits and returns as described by this document based on the active element.
- filepicker.newFile - Prompts the user to input a file name manually, relative to the current working directory, and with FAT-incompatible characters excluded. Quits and returns that.
- filepicker.nothing - Do nothing and keep on running. Literally. This is used as a plug to enable an action for all (or a certain type) but files of a certain type (or more precise than the initial type).

View file

@ -1,12 +1,13 @@
-- LSH version 0.1 local ctr = require('ctr')
-- ctrµLua official shell local keyboard = require('keyboard')
local ctr = require("ctr") local gfx = ctr.gfx
local gfx = require("ctr.gfx")
local function saveGraphicsState() local externalConfig
local function gfxPrepare()
local old = {gfx.get3D(), gfx.color.getDefault(), gfx.color.getBackground(), local old = {gfx.get3D(), gfx.color.getDefault(), gfx.color.getBackground(),
gfx.font.getDefault()} gfx.font.getDefault(), gfx.getTextSize()}
local mono = gfx.font.load(ctr.root .. "resources/VeraMono.ttf") local mono = gfx.font.load(ctr.root .. "resources/VeraMono.ttf")
@ -14,186 +15,304 @@ local function saveGraphicsState()
gfx.color.setDefault(0xFFFDFDFD) gfx.color.setDefault(0xFFFDFDFD)
gfx.color.setBackground(0xFF333333) gfx.color.setBackground(0xFF333333)
gfx.font.setDefault(mono) gfx.font.setDefault(mono)
gfx.setTextSize(12)
return old return old
end end
local function restoreGraphicsState(state) local function gfxRestore(state)
gfx.set3D(state[1]) gfx.set3D(state[1])
gfx.color.setDefault(state[2]) gfx.color.setDefault(state[2])
gfx.color.setBackground(state[3]) gfx.color.setBackground(state[3])
gfx.font.setDefault(state[4]) gfx.font.setDefault(state[4])
gfx.setTextSize(state[5])
end end
local function getExtension(sel, bindings) local function systemBindings(bindings)
for _, ext in ipairs(bindings) do bindings.__default.up = {
if ext.ext == sel:match("%..+$") then function(_, selected, ...)
return ext if selected.inList > 1 then
selected.inList = selected.inList - 1
if selected.inList == selected.offset then
selected.offset = selected.offset - 1
end
end
end end
end }
end
local function getFilelist(cur) bindings.__default.down = {
local files = ctr.fs.list(cur) function(externalConfig, selected, ...)
if selected.inList < #externalConfig.fileList then
if cur ~= "/" and cur ~= "sdmc:/" then selected.inList = selected.inList + 1
table.insert(files, {name = "..", isDirectory = true}) if selected.inList - selected.offset >= 16 then
end selected.offset = selected.offset + 1
end
-- Stealy stealing code from original openfile.lua end
table.sort(files, function(i, j)
if i.isDirectory and not j.isDirectory then
return true
elseif i.isDirectory == j.isDirectory then
return string.lower(i.name) < string.lower(j.name)
end end
end) }
return files bindings.__default.left = {
function(_, selected, ...)
selected.inList, selected.offset = 1, 0
end
}
bindings.__default.right = {
function(externalConfig, selected, ...)
selected.inList = #externalConfig.fileList
if #externalConfig.fileList > 15 then
selected.offset = #externalConfig.fileList - 16
end
end
}
end end
local function drawBottom(cur, selFile, bindings) local function getFileList(workingDirectory)
local ext = getExtension(selFile.name, bindings) local fileList = ctr.fs.list(workingDirectory)
if workingDirectory ~= "/" and workingDirectory ~= "sdmc:/" then
table.insert(fileList, {name = "..", isDirectory = true})
end
-- Stealy stealing code from original openfile.lua
table.sort(fileList, function(i, j)
if i.isDirectory and not j.isDirectory then
return true
elseif i.isDirectory == j.isDirectory then
return string.lower(i.name) < string.lower(j.name)
end
end)
return fileList
end
local function getBinding(selectedFile, bindings)
if selectedFile.isDirectory then
return bindings.__directory, "__directory"
end
for pattern, values in pairs(bindings) do
if selectedFile.name:match(pattern) then
return values, pattern
end
end
return bindings.__default, "__default"
end
local function drawBottom(externalConfig, workingDirectoryScroll, selected)
local workingDirectory = externalConfig.workingDirectory
local bindings = externalConfig.bindings
local selectedFile = externalConfig.fileList[selected.inList]
gfx.start(gfx.BOTTOM) gfx.start(gfx.BOTTOM)
gfx.rectangle(0, 0, gfx.BOTTOM_WIDTH, 16, 0, 0xFF0000B3) gfx.rectangle(0, 0, gfx.BOTTOM_WIDTH, 16, 0, 0xFF0000B3)
gfx.text(1, 0, cur, 12) gfx.text(1 - workingDirectoryScroll.value, 0, workingDirectory)
gfx.text(1, 15, selFile.name, 12) if gfx.font.getDefault():width(workingDirectory) > gfx.BOTTOM_WIDTH - 2 then
if not selFile.isDirectory then workingDirectoryScroll.value = workingDirectoryScroll.value + workingDirectoryScroll.phase
gfx.text(1, 45, selFile.fileSize, 12) if workingDirectoryScroll.value == (gfx.BOTTOM_WIDTH - 2) - gfx.font.getDefault():width(workingDirectory) or
workingDirectoryScroll.value == 0 then
workingDirectoryScroll.phase = - workingDirectoryScroll.phase
end
end end
local keys = {"X: Quit/Cancel"} gfx.text(1, 15, selectedFile.name, 12)
if selFile.isDirectory then if not selectedFile.isDirectory then
gfx.text(1, 30, "Directory", 12, 0xFF727272) gfx.text(1, 45, tostring(selectedFile.fileSize) .. "B", 12, 0xFF727272)
gfx.text(1, gfx.BOTTOM_HEIGHT - 30, "A: Open", 12) end
gfx.text(1, gfx.BOTTOM_HEIGHT - 15, keys[1], 12)
elseif ext then
local lines = 1
-- Keys local binding, pattern = getBinding(selectedFile, bindings)
if ext.y then if selectedFile.isDirectory then
lines = lines + 1 gfx.text(1, 30, bindings.__directory.__name, 12, 0xFF727272)
table.insert(keys, "Y: " .. ext.y)
end
if ext.a then
lines = lines + 1
table.insert(keys, "A: " .. ext.a)
end
-- Drawing
for i=lines, 1, -1 do
gfx.text(1, gfx.BOTTOM_HEIGHT - 15*i, keys[i], 12)
end
gfx.text(1, 30, ext.name, 12, 0xFF727272)
gfx.text(1, 45, tostring(selFile.fileSize) .. "B", 12, 0xFF727272)
else else
gfx.text(1, 30, "File", 12, 0xFF727272) gfx.text(1, 30, binding.__name, 12, 0xFF727272)
gfx.text(1, 45, tostring(selFile.fileSize) .. "B", 12, 0xFF727272)
gfx.text(1, gfx.BOTTOM_HEIGHT - 15, keys[1], 12)
end end
local bindingNames = {
{"start", "Start"}, {"select", "Select"},
{"x", "X"}, {"y", "Y"},
{"b", "B"}, {"a", "A"},
{"r", "R"}, {"l", "L"},
{"zr", "ZR"}, {"zl", "ZL"},
{"cstickDown", "C Down"}, {"cstickUp", "C Up"},
{"cstickRight", "C Right"}, {"cstickLeft", "C Left"}
}
local j = 0
for i, v in ipairs(bindingNames) do
if binding[v[1]] and binding[v[1]][2] then
j = j + 1
gfx.text(1, gfx.BOTTOM_HEIGHT - 15*j, v[2] .. ": " .. binding[v[1]][2])
end
end
externalConfig.callbacks.drawBottom(externalConfig, selected)
gfx.stop() gfx.stop()
end end
local function drawTop(files, sel, scr) local function drawTop(externalConfig, selected)
gfx.start(gfx.TOP) gfx.start(gfx.TOP)
gfx.rectangle(0, (sel-scr-1)*15, gfx.TOP_WIDTH, 16, 0, 0xFF0000B3) gfx.rectangle(0, (selected.inList-selected.offset-1)*15, gfx.TOP_WIDTH, 16, 0, 0xFF0000B9)
local over = #files - scr >= 16 and 16 or #files - scr local over = #externalConfig.fileList - selected.offset >= 16 and 16 or #externalConfig.fileList - selected.offset
for i=scr+1, scr+over do for i=selected.offset+1, selected.offset+over do
local color = files[i].isDirectory and 0xFF727272 or 0xFFFDFDFD local color = externalConfig.fileList[i].isDirectory and 0xFF727272 or 0xFFFDFDFD
gfx.text(1, (i-scr-1)*15+1, files[i].name or "", 12, color) gfx.text(1, (i-selected.offset-1)*15+1, externalConfig.fileList[i].name or "", 12, color)
end
externalConfig.callbacks.drawTop(externalConfig, selected)
gfx.stop()
end
local function eventHandler(externalConfig, selected)
externalConfig.callbacks.eventHandler(externalConfig, selected)
ctr.hid.read()
local state = ctr.hid.keys()
local binding, pattern = getBinding(externalConfig.fileList[selected.inList], externalConfig.bindings)
for k, v in pairs(binding) do
if k ~= "__name" and state.down[k] then
local a, b, c, key = v[1](externalConfig, selected, pattern, k)
if key then return a, b, c, key
else return end
end
end
for k, v in pairs(externalConfig.bindings.__default) do
if k ~= "__name" and state.down[k] then
local a, b, c, key = v[1](externalConfig, selected, pattern, k)
if key then return a, b, c, key
else break end
end end
gfx.stop()
end
local function runA(cur, selFile, bindings)
if not selFile.isDirectory then
local ext = getExtension(selFile.name, bindings)
if not ext then return end
if ext.a then return cur .. selFile.name, ext.ext end
end end
end end
local function runY(cur, selFile, bindings) local function nothing(externalConfig, selected, bindingName, bindingKey)
if not selFile.isDirectory then -- externalConfig = {workingDirectory=string, bindings=table, callbacks=table, additionalArguments=table, fileList=table}
local ext = getExtension(selFile.name, bindings) -- selected = {file=string, inList=number, offset=number}
if not ext then return end -- bindings = {__default/__directory/[regex] = {__name, [keyName] = {(handlingFunction), (name)}}}
if ext.y then return cur .. selFile.name, ext.ext end -- callbacks = {drawTop, drawBottom, eventHandler}
end
local function changeDirectory(externalConfig, selected, bindingName, bindingKey)
if externalConfig.fileList[selected.inList].isDirectory then
if externalConfig.fileList[selected.inList].name == ".." then
externalConfig.workingDirectory = externalConfig.workingDirectory:gsub("[^/]+/$", "")
else
externalConfig.workingDirectory = externalConfig.workingDirectory .. externalConfig.fileList[selected.inList].name .. "/"
end
externalConfig.fileList = getFileList(externalConfig.workingDirectory)
selected.inList, selected.offset = 1, 0
end end
end end
--- Open a file browser to allow the user to select a file. local function newFile(externalConfig, selected, bindingName, bindingKey)
-- It will save current graphical settings and set them back after ending. Press up or down to move one element at a time, and press left or right to go at the beginning or at the end of the list. This function is the return result of requiring filepicker.lua local name = ""
-- @name filePicker
-- @param bindings A table of the extensions the user can select in the format {{name, ext, a, y},...}, name will show up instead of "File" or "Directory" on the bottom screen, a and y set the action names for those keys on the bottom screen (and also enable them, so if there's neither a or y, the file will have a custom type name but won't be effectively selectable), and ext is the extension to search for, dot included. Everything must be strings.
-- @param workdir Optional, current working directory will be used if not specified, otherwise, sets the path at which the file browser first shows up, a string.
-- @returns The absolute path to the file, nil in case no file was picked.
-- @returns The extension of the file, this might be helpful in cases were multiple file types could be expected, nil in case no file was picked.
-- @returns The "mode", which indicates which key was used to select the file, "A" or "Y". "X" in case no file was picked.
return function(bindings, workdir)
-- Initialization
local old = saveGraphicsState()
local cur = workdir or ctr.fs.getDirectory()
if cur:sub(-1) ~= "/" then
cur = cur .. "/"
end
local bindings = bindings or {}
local files = getFilelist(cur) or {{name = "- Empty -"}}
local sel = 1
local scr = 0
while ctr.run() do while ctr.run() do
drawBottom(cur, files[sel], bindings) gfx.start(gfx.BOTTOM)
drawTop(files, sel, scr) gfx.rectangle(0, 0, gfx.BOTTOM_WIDTH, 16, 0, 0xFF0000B3)
gfx.text(1, 0, externalConfig.workingDirectory)
keyboard.draw(4, 115)
gfx.stop()
gfx.start(gfx.TOP)
gfx.rectangle(0, 0, gfx.TOP_WIDTH, 16, 0, 0xFF0000B3)
gfx.text(1, 0, "Creating new file")
gfx.rectangle(4, gfx.TOP_HEIGHT // 2 - 15, gfx.TOP_WIDTH - 8, 30, 0, 0xFF727272)
gfx.text(5, gfx.TOP_HEIGHT // 2 - 6, name, 12)
gfx.stop()
gfx.render() gfx.render()
local char = (keyboard.read() or ""):gsub("[\t%/%?%<%>%\\%:%*%|%”%^]", "")
ctr.hid.read() ctr.hid.read()
local state = ctr.hid.keys() local keys = ctr.hid.keys()
if (state.down.dDown or state.down.cpadDown) and sel < #files then
sel = sel + 1
if sel - scr >= 16 then
scr = scr + 1
end
elseif (state.down.dUp or state.down.cpadUp) and sel > 1 then
sel = sel - 1
if sel == scr then
scr = scr - 1
end
elseif state.down.dLeft or state.down.cpadLeft then
sel = 1
scr = 0
elseif state.down.dRight or state.down.cpadRight then
sel = #files
if #files > 15 then
scr = #files - 16
end
elseif state.down.a then if char ~= "" and char ~= "\b" and char ~= "\n" then
local selFile = files[sel] name = name .. char
if selFile.isDirectory then elseif char ~= "" and char == "\b" then
if selFile.name == ".." then name = name:sub(1, (utf8.offset(name, -1) or 0)-1)
cur = cur:gsub("[^/]+/$", "") elseif (char ~= "" and char == "\n" or keys.down.a) and name ~= "" then
else local b, p = getBinding({name=name}, externalConfig.bindings)
cur = cur .. selFile.name .. "/" return externalConfig.workingDirectory .. name, p, "new", b
end elseif keys.down.b then
files, sel, scr = getFilelist(cur), 1, 0 break
else
local file, ext = runA(cur, selFile, bindings)
if file then
restoreGraphicsState(old)
return file, ext, "A"
end
end
elseif state.down.y then
local file, ext = runY(cur, files[sel], bindings)
if file then
restoreGraphicsState(old)
return file, ext, "Y"
end
elseif state.down.x then
restoreGraphicsState(old)
return nil, nil, "X"
end end
end end
end end
local function openFile(externalConfig, selected, bindingName, bindingKey)
return externalConfig.workingDirectory .. externalConfig.fileList[selected.inList].name,
bindingName, "open", bindingKey
end
local function filePicker(workingDirectory, bindings, callbacks, ...)
-- Argument sanitization
local additionalArguments = { ... }
workingDirectory = workingDirectory or ctr.fs.getDirectory()
bindings = bindings or {}
callbacks = callbacks or {}
for _, v in ipairs({"drawTop", "drawBottom", "eventHandler"}) do
if not callbacks[v] then
callbacks[v] = function(...) end
end
end
if workingDirectory:sub(utf8.offset(workingDirectory, -1) or -1) ~= "/" then
workingDirectory = workingDirectory .. "/"
end
-- Default Bindings
bindings.__default = bindings.__default or {}
bindings.__default.__name = bindings.__default.__name or "File"
bindings.__default.x = bindings.__default.x or {
function(externalConfig, ...)
return externalConfig.workingDirectory, "__directory", nil, "x"
end, "Quit"
}
bindings.__directory = bindings.__directory or {}
bindings.__directory.__name = bindings.__directory.__name or "Directory"
bindings.__directory.a = bindings.__directory.a or {
changeDirectory, "Open"
}
local movementKeys = {
"up" , "down" , "left" , "right" ,
"cpadUp", "cpadDown", "cpadLeft", "cpadRight",
"dUp" , "dDown" , "dLeft" , "dRight"
}
for k, v in pairs(bindings) do
if k ~= "__default" then
setmetatable(bindings[k], {__index = bindings.__default})
end
for _, w in ipairs(movementKeys) do
if v[w] then bindings[k][w] = nil end
end
end
systemBindings(bindings)
-- Other Initialization
local selected = {inList = 1, offset = 0}
local workingDirectoryScroll = { value = 0, phase = -1 }
local gfxState = gfxPrepare()
-- Main Loop
externalConfig = {workingDirectory=workingDirectory, bindings=bindings,
callbacks=callbacks, additionalArguments=additionalArguments,
fileList=getFileList(workingDirectory)}
while ctr.run() do
drawBottom(externalConfig, workingDirectoryScroll, selected)
drawTop(externalConfig, selected)
gfx.render()
local file, binding, mode, key = eventHandler(externalConfig, selected)
if key then
gfxRestore(gfxState)
return file, binding, mode, key
end
end
end
local returnTable = {filePicker = filePicker, openFile = openFile,
newFile = newFile, changeDirectory = changeDirectory}
setmetatable(returnTable, {__call = function(self, ...) return self.filePicker(...) end})
return returnTable

View file

@ -1,156 +0,0 @@
-- Sort ctr.fs.list returns (directories first and alphabetical sorting)
local function sort(files)
table.sort(files, function(i, j)
if i.isDirectory and not j.isDirectory then
return true
elseif i.isDirectory == j.isDirectory then
return string.lower(i.name) < string.lower(j.name)
end
end)
return files
end
--- Open a file explorer to select a file.
-- string title: title of the file explorer.
-- string curdir: the directory to initially open the file explorer in, or nil for the current directory.
-- string exts: the file extensions the user can select, separated by ";". If nil, all extensions are accepted.
-- string type: "exist" to select an existing file, "new" to select an non-existing file or "any" to select a existing
-- or non-existing file name. If nil, defaults to "exist".
-- returns string: the file the user has selected, or nil if the explorer was closed without selecting a file.
-- string: "exist" if the file exist or "new" if it doesn't
return function(title, curdir, exts, type)
-- Open libs
local ctr = require("ctr")
local gfx = require("ctr.gfx")
local keyboard = require("keyboard")
-- Arguments
local curdir = curdir or ctr.fs.getDirectory()
local type = type or "exist"
-- Variables
local sel = 1
local scroll = 0
local files = sort(ctr.fs.list(curdir))
if curdir ~= "/" then table.insert(files, 1, { name = "..", isDirectory = true }) end
local newFileName = ""
local ret = nil
-- Remember and set defaults
local was3D = gfx.get3D()
local wasDefault = gfx.color.getDefault()
local wasBackground = gfx.color.getBackground()
local wasFont = gfx.font.getDefault()
gfx.set3D(false)
gfx.color.setDefault(0xFFFFFFFF)
gfx.color.setBackground(0xFF000000)
gfx.font.setDefault()
while ctr.run() do
ctr.hid.read()
local keys = ctr.hid.keys()
if keys.down.start then break end
-- Keys input
if keys.down.down and sel < #files then
sel = sel + 1
if sel > scroll + 14 then scroll = scroll + 1 end
elseif keys.down.up and sel > 1 then
sel = sel - 1
if sel <= scroll then scroll = scroll - 1 end
end
if keys.down.a then
local f = files[sel]
if f.isDirectory then
if f.name == ".." then curdir = curdir:gsub("[^/]+/$", "")
else curdir = curdir..f.name.."/" end
sel = 1
scroll = 0
files = sort(ctr.fs.list(curdir))
if curdir ~= "/" then
table.insert(files, 1, { name = "..", isDirectory = true })
end
elseif type == "exist" or type == "any" then
if exts then
for ext in (exts..";"):gmatch("[^;]+") do
if f.name:match("%..+$") == ext then
ret = { curdir..f.name, "exist" }
break
end
end
else
ret = { curdir..f.name, "exist" }
end
if ret then break end
end
end
-- Keyboard input
if type == "new" or type == "any" then
local input = keyboard.read()
if input then
if input == "BACK" then
newFileName = newFileName:sub(1, (utf8.offset(newFileName, -1) or 0)-1)
elseif input == "\n" then
local fileStatus = "new"
local f = io.open(curdir..newFileName)
if f then fileStatus = "exist" f:close() end
ret = { curdir..newFileName, fileStatus }
break
else
newFileName = newFileName..input
end
end
end
-- Draw
gfx.start(gfx.TOP)
gfx.rectangle(0, 10+(sel-scroll)*15, gfx.TOP_WIDTH, 15, 0, gfx.color.RGBA8(0, 0, 200))
for i = scroll+1, scroll+14, 1 do
local f = files[i]
if not f then break end
local name = f.isDirectory and "["..f.name.."]" or f.name.." ("..f.fileSize.."b)"
if not f.isHidden then gfx.text(5, 12+(i-scroll)*15, name) end
end
gfx.rectangle(0, 0, gfx.TOP_WIDTH, 25, 0, gfx.color.RGBA8(200, 200, 200))
gfx.text(3, 3, curdir, 13, gfx.color.RGBA8(0, 0, 0))
gfx.stop()
gfx.start(gfx.BOTTOM)
gfx.text(5, 5, title)
gfx.text(5, 20, "Accepted file extensions: "..(exts or "all"))
if type == "new" or type == "any" then
gfx.text(5, 90, newFileName)
keyboard.draw(5, 115)
end
gfx.stop()
gfx.render()
end
-- Reset defaults
gfx.set3D(was3D)
gfx.color.setDefault(wasDefault)
gfx.color.setBackground(wasBackground)
gfx.font.setDefault(wasFont)
if ret then
return table.unpack(ret)
else
return ret
end
end