luajitos

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

explorer.lua (9179B)


      1 -- LuaJIT OS File Explorer
      2 -- Browse directories and files with size information
      3 
      4 -- Current directory (start in home)
      5 local currentPath = "/home"
      6 local scrollOffset = 0
      7 
      8 -- Window dimensions
      9 local windowWidth = 600
     10 local windowHeight = 500
     11 local window = app:newWindow(windowWidth, windowHeight)
     12 window.title = "Explorer"
     13 
     14 -- Debug: Check window properties after creation
     15 if osprint then
     16     osprint("[EXPLORER] Window created\n")
     17     osprint("[EXPLORER] window type: " .. type(window) .. "\n")
     18     osprint("[EXPLORER] window.visible: " .. tostring(window.visible) .. "\n")
     19     osprint("[EXPLORER] window.isBackground: " .. tostring(window.isBackground) .. "\n")
     20 end
     21 
     22 -- UI constants
     23 local lineHeight = 20
     24 local headerHeight = 40
     25 local footerHeight = 25
     26 
     27 -- Directory and file listing cache
     28 local entries = {}
     29 
     30 -- Helper: Format file size
     31 local function formatSize(bytes)
     32     if not bytes then return "?" end
     33 
     34     if bytes < 1024 then
     35         return tostring(bytes) .. " B"
     36     elseif bytes < 1048576 then
     37         return string.format("%.1f KB", bytes / 1024)
     38     elseif bytes < 1073741824 then
     39         return string.format("%.1f MB", bytes / 1048576)
     40     else
     41         return string.format("%.1f GB", bytes / 1073741824)
     42     end
     43 end
     44 
     45 -- Helper: Get file/directory size
     46 local function getSize(path)
     47     if not fs then return 0 end
     48 
     49     local itemType = fs:getType(path)
     50     if itemType == "file" then
     51         local content = fs:read(path)
     52         return content and #content or 0
     53     elseif itemType == "directory" then
     54         -- For directories, count entries
     55         local dirs = fs:dirs(path) or {}
     56         local files = fs:files(path) or {}
     57         return bit.band(#dirs, 0xFFFFFFFF) + bit.band(#files, 0xFFFFFFFF)
     58     end
     59 
     60     return 0
     61 end
     62 
     63 -- Load directory contents
     64 local function loadDirectory(path)
     65     entries = {}
     66 
     67     if osprint then
     68         osprint("[EXPLORER] loadDirectory called with path=" .. tostring(path) .. "\n")
     69         osprint("[EXPLORER] fs=" .. tostring(fs) .. "\n")
     70     end
     71 
     72     if not fs then
     73         if osprint then
     74             osprint("[EXPLORER] ERROR: fs is nil!\n")
     75         end
     76         print("Explorer: No filesystem access - fs is nil")
     77         return
     78     end
     79 
     80     -- Add parent directory entry if not at home
     81     if path ~= "/home" then
     82         table.insert(entries, {
     83             name = "..",
     84             type = "directory",
     85             size = 0,
     86             isParent = true
     87         })
     88     end
     89 
     90     -- Get directories using SafeFS
     91     local dirs = fs:dirs(path)
     92     if dirs then
     93         for _, dirName in ipairs(dirs) do
     94             local fullPath = fs:join(path, dirName)
     95             table.insert(entries, {
     96                 name = dirName,
     97                 type = "directory",
     98                 size = 0,
     99                 path = fullPath
    100             })
    101         end
    102     end
    103 
    104     -- Get files using SafeFS
    105     local files = fs:files(path)
    106     if files then
    107         for _, fileName in ipairs(files) do
    108             local fullPath = fs:join(path, fileName)
    109             table.insert(entries, {
    110                 name = fileName,
    111                 type = "file",
    112                 size = 0,
    113                 path = fullPath
    114             })
    115         end
    116     end
    117 
    118     scrollOffset = 0  -- Reset scroll when changing directory
    119 end
    120 
    121 -- Navigate to a directory
    122 local function navigateTo(path)
    123     currentPath = path
    124     loadDirectory(currentPath)
    125     window.dirty = true
    126 
    127     print("Explorer: Navigated to " .. currentPath)
    128 end
    129 
    130 -- Flag to track if directory is loaded
    131 local isLoaded = false
    132 
    133 -- Mouse click handling
    134 window.onClick = function(mx, my)
    135     if osprint then
    136         osprint("[EXPLORER] onClick CALLED! Mouse click at (" .. mx .. ", " .. my .. ")\n")
    137     end
    138 
    139     -- Check if click is in the file list area (below header and column headers row)
    140     local listStartY = headerHeight + lineHeight  -- Account for column headers row
    141     if my > listStartY and my < windowHeight - footerHeight then
    142         local clickedIndex = math.floor((my - listStartY) / lineHeight) + 1 + scrollOffset
    143 
    144         if osprint then
    145             osprint("[EXPLORER] Clicked index: " .. clickedIndex .. " (total entries: " .. #entries .. ")\n")
    146         end
    147 
    148         if clickedIndex > 0 and clickedIndex <= bit.band(#entries, 0xFFFFFFFF) then
    149             local entry = entries[clickedIndex]
    150 
    151             if osprint then
    152                 osprint("[EXPLORER] Clicked entry: " .. entry.name .. " type=" .. entry.type .. "\n")
    153             end
    154 
    155             if entry.type == "directory" then
    156                 if entry.isParent then
    157                     -- Go to parent directory
    158                     local parentPath = fs:parentDir(currentPath)
    159                     if parentPath then
    160                         navigateTo(parentPath)
    161                     end
    162                 else
    163                     -- Navigate to subdirectory
    164                     if osprint then
    165                         osprint("[EXPLORER] Navigating to: " .. entry.path .. "\n")
    166                     end
    167                     navigateTo(entry.path)
    168                 end
    169             end
    170         end
    171     end
    172 
    173     window:markDirty()
    174 end
    175 
    176 -- Debug: Verify onClick is set
    177 if osprint then
    178     osprint("[EXPLORER] After setting onClick, window.onClick type: " .. type(window.onClick) .. "\n")
    179 end
    180 
    181 -- Keyboard input handling
    182 window.onInput = function(key_or_event, scancode)
    183     if type(key_or_event) == "string" then
    184         local key = key_or_event
    185 
    186         -- Up arrow - scroll up
    187         if scancode == 0x48 then
    188             scrollOffset = math.max(0, scrollOffset - 1)
    189             window.dirty = true
    190         -- Down arrow - scroll down
    191         elseif scancode == 0x50 then
    192             local maxScroll = math.max(0, bit.band(#entries, 0xFFFFFFFF) - math.floor((windowHeight - headerHeight - footerHeight) / lineHeight))
    193             scrollOffset = math.min(maxScroll, scrollOffset + 1)
    194             window.dirty = true
    195         -- Home key - go to home directory
    196         elseif key == "h" or key == "H" then
    197             navigateTo("/home")
    198         -- Backspace or left arrow - go to parent
    199         elseif key == "\b" or scancode == 0x4B then
    200             local parentPath = fs:parentDir(currentPath)
    201             if parentPath then
    202                 navigateTo(parentPath)
    203             end
    204         end
    205     end
    206 end
    207 
    208 -- Flag to track first draw
    209 local firstDraw = true
    210 
    211 -- Draw the explorer
    212 window.onDraw = function(gfx)
    213     -- Load directory on first draw (after fs is available)
    214     if firstDraw then
    215         firstDraw = false
    216         if fs then
    217             loadDirectory(currentPath)
    218         else
    219             if osprint then
    220                 osprint("[EXPLORER] ERROR: fs is nil on first draw!\n")
    221             end
    222         end
    223     end
    224 
    225     -- Background
    226     gfx:fillRect(0, 0, windowWidth, windowHeight, 0x1E1E1E)
    227 
    228     -- Header - current path
    229     gfx:fillRect(0, 0, windowWidth, headerHeight, 0x2D2D30)
    230     gfx:drawText(10, 12, "Path: " .. currentPath, 0xFFFFFF)
    231 
    232     -- Column headers
    233     local headerY = headerHeight
    234     gfx:fillRect(0, headerY, windowWidth, lineHeight, 0x3E3E42)
    235     gfx:drawText(10, headerY + 5, "Name", 0xCCCCCC)
    236     gfx:drawText(windowWidth - 150, headerY + 5, "Type", 0xCCCCCC)
    237     gfx:drawText(windowWidth - 80, headerY + 5, "Size", 0xCCCCCC)
    238 
    239     -- File/directory list
    240     local listY = headerY + lineHeight
    241     local visibleLines = math.floor((windowHeight - headerHeight - lineHeight - footerHeight) / lineHeight)
    242 
    243     for i = 1, visibleLines do
    244         local entryIndex = i + scrollOffset
    245         if entryIndex <= bit.band(#entries, 0xFFFFFFFF) then
    246             local entry = entries[entryIndex]
    247             local y = listY + (i - 1) * lineHeight
    248 
    249             -- Alternating row colors
    250             if i % 2 == 0 then
    251                 gfx:fillRect(0, y, windowWidth, lineHeight, 0x252526)
    252             end
    253 
    254             -- Icon and name
    255             local icon = entry.type == "directory" and "[DIR]" or "[FILE]"
    256             local nameColor = entry.type == "directory" and 0x4EC9B0 or 0xCCCCCC
    257 
    258             if entry.isParent then
    259                 icon = "[UP]"
    260                 nameColor = 0xDCDCAA
    261             end
    262 
    263             gfx:drawText(10, y + 3, icon .. " " .. entry.name, nameColor)
    264 
    265             -- Type
    266             local typeStr = entry.type == "directory" and "Folder" or "File"
    267             gfx:drawText(windowWidth - 150, y + 3, typeStr, 0x888888)
    268 
    269             -- Size
    270             local sizeStr = entry.type == "directory" and tostring(entry.size) .. " items" or formatSize(entry.size)
    271             gfx:drawText(windowWidth - 80, y + 3, sizeStr, 0x888888)
    272         end
    273     end
    274 
    275     -- Footer - help text
    276     local footerY = windowHeight - footerHeight
    277     gfx:fillRect(0, footerY, windowWidth, footerHeight, 0x2D2D30)
    278     gfx:drawText(10, footerY + 5, "Click dir to open | H=Home | Backspace=Parent | Arrows=Scroll", 0x888888)
    279 
    280     -- Scroll indicator
    281     if bit.band(#entries, 0xFFFFFFFF) > visibleLines then
    282         local scrollText = string.format("%d/%d", scrollOffset + 1, bit.band(#entries, 0xFFFFFFFF))
    283         gfx:drawText(windowWidth - 60, footerY + 5, scrollText, 0xCCCCCC)
    284     end
    285 end
    286 
    287 print("Explorer started")
    288 print("Current directory: " .. currentPath)
    289 print("Directory will load on first draw")