Файловый менеджер. Часть #1 [иконки]
Чтобы было куда кликать, надо на экране разметить места для иконок, еще и иконки нарисовать.
Для иконок возьмем формат PPM, а конкретно, цветную бинарную версию P6. Формат ультра-примитивный, иконки можно будет без лишних заморочек рисовать в любом нормальном растровом редакторе, в опенкомпах, а при наличии нужного скрипта - прямо в консоли.
Но в этом формате будем хранить иконки на диске. Внутри программы они будут преобразовываться в таблицу, хранящую цвет каждого пикселя.
Например, создадим иконку, которая будет рисоваться для всех типов файлов по умолчанию. (Будет отображаться, если подходящая иконка не загружена)
local icons = {} -- создать массив с иконками local icons.unknown = {x = 10} -- создать таблицу для иконки, указать ширину в пикселях for i = 1, 100 do -- цикл заполнения таблицы 10x10 if i%3 == 0 then -- если номер пикселя делится на 3 icons.unknown[i] = 3394611 -- сделать пиксель зеленым else -- иначе icons.unknown[i] = 3355443 -- сделать серым end end
С внутренним представлением определились, теперь напишем функцию отрисовки.
Создадим счетчики для индексов, горизонтальной и вертикальной координаты.
Запустим цикл, с условием: пока индекс меньше или равен (количество пикселей - ширина изображения).
Установим для символа цвет текущего пикселя, а для фона получим пиксель через текущий индекс + ширина изображения.
И выведем полученные пиксели одним символом u+2580.
Если счетчик по горизонтали досчитал до ширины изображения - сбросить в начало, к счетчику по вертикали добавить 1, а к индексу прибавить ширину. Получим пропуск строки, т. к. она уже была отрисована в текущей итерации.
local function draw_icon(name, X, Y) -- получить название и координаты if not icons[name] then return false end -- прервать, если нет такой иконки local x, y, index = 1, 1, 1 -- создать счетчики while index <= #icons[name]-icons[name].x do -- пройти по индексам gpu.setForeground(icons[name][index]) -- установить цвет верхнего пикселя gpu.setBackground(icons[name][index+icons[name].x]) -- цвет нижнего gpu.set(x+X-1, y+Y-1, quad) -- вывести на экран if x == icons[name].x then -- если достигнута ширина изображения x, y, index = 1, y + 1, index + icons[name].x+1 -- обновить все счетчики else -- простая итерация x, index = x + 1, index + 1 -- обновить счетчик горизонтали и индекса end end end
Одна иконка уже сгенерирована, чтобы вывести ее в углу экрана, вызовем ее по имени - draw_icon('unknown', 1, 1)
В итоге, на экране получим такое изображение:
Чтобы загрузить иконки и конвертировать в удобный вид, создадим такую функцию:
Перебрать все файлы в папке, получив их список через filesystem API.
Прочитать файл построчно в таблицу, попутно удалив строки с комментариями.
Если заголовок файла равен , получить ширину картинки, информацию о пикселях объединить в одну строку.
В цикле пройти по пикселям, конвертируя бинарное значение пикселя в число.
Тут надо помнить, что значение одного пикселя хранится в трех символах (по одному на канал), поэтому цикл будет скакать через 3. Первый символ конвертируем в число, умножаем на 65536, второй на 256 и складываем. Полученное число добавляем в массив пикселей текущей иконки.
Получаем примерно такую реализацию:
local function load_icons(path) -- получить путь к папке с иконками local multiplier, path = {65536, 256, 1}, path or '' -- создать таблицу множителей for name in fs.list(path) do -- получить имя файла в папке local file = io.open(path..name, 'r') -- открыть файл if not file then break end -- если файла нет, прервать цикл name = name:gsub('%..+', '') -- обрезать название файла до первой точки local raw_img = {} -- создать массив для сырых данных for line in file:lines() do -- в цикле пройти по строкам if line and line:sub(1,1) ~= '#' then -- если строка не закоментированна table.insert(raw_img, line) -- добавить в таблицу end end file:close() -- закрыть файл if raw_img[1] == 'P6' then -- если заголовок совпадает local _ = raw_img[2]:find(' ') -- проверить наличие пробела на второй строке if _ then -- если размеры на одной строке _, raw_img[2] = raw_img[2]:sub(_+1), raw_img[2]:sub(1,_-1) -- разделить table.insert(raw_img, 3, _) -- перенести высоту на другую строку end raw_img[2] = tonumber(raw_img[2]) -- преобразовать ширину изображения в число icons[name] = {x = raw_img[2]} -- создать пустую таблицу для пикселей local current = '' -- создать переменную с сырой информацией о пикселях for i = 5, #raw_img do -- пройти до конца файла current = current..raw_img[i]..'\n' -- объединить данные в одну строку end local color, n for i = 1, #current-1, 3 do -- пройти по каждому третьему символу, исключая последний перевод строки n, color = 1, 0 -- сбросить счетчик для таблицы множителей и цвет for j = i, i+2 do -- перебрать три символа color = color+current:sub(j,j):byte()*multiplier[n] -- преобразовать символ в число и добавить к значению цвета n = n + 1 -- обновить счетчик end table.insert(icons[name], color) -- добавить цвет пикселя к остальным end end end end
Реализация очень примитивная, но главное, что иконки будут загружаться. Можно было бы сотворить свой формат, который быстрей распаковывается и занимает меньше места, но плодить сущностей очень вредно.
Для проверки, осталось нарисовать иконки, сложить их в папку /home/icons/, например. И запустить весь код:
local fs = require('filesystem') local gpu = require('component').gpu local quad = require('unicode').char(0x2580) local icons = {unknown = {x = 10}} for i = 1, 100 do if i%3 == 0 then icons.unknown[i] = 3394611 else icons.unknown[i] = 3355443 end end local function load_icons(path) local multiplier, path = {65536, 256, 1}, path or '' for name in fs.list(path) do local file = io.open(path..name, 'r') if not file then break end name = name:gsub('%..+', '') local raw_img = {} for line in file:lines() do if line and line:sub(1,1) ~= '#' then table.insert(raw_img, line) end end file:close() if raw_img[1] == 'P6' then local _ = raw_img[2]:find(' ') if _ then _, raw_img[2] = raw_img[2]:sub(_+1), raw_img[2]:sub(1,_-1) table.insert(raw_img, 3, _) end raw_img[2] = tonumber(raw_img[2]) icons[name] = {x = raw_img[2]} local current = '' for i = 5, #raw_img do current = current..raw_img[i]..'\n' end local n, color for i = 1, #current-1, 3 do n, color = 1, 0 for j = i, i+2 do color = color+current:sub(j,j):byte()*multiplier[n] n = n+1 end table.insert(icons[name], color) end end end end local function draw_icon(name, X, Y) if not icons[name] then return false end local x, y, index = 1, 1, 1 while index <= #icons[name]-icons[name].x do gpu.setForeground(icons[name][index]) gpu.setBackground(icons[name][index+icons[name].x]) gpu.set(x+X-1, y+Y-1, quad) if x == icons[name].x then x, y, index = 1, y + 1, index + icons[name].x+1 else x, index = x + 1, index + 1 end end end load_icons('/home/icons/') local n = 1 for name in pairs(icons) do draw_icon(name, (n*11)-10, 1) n = n + 1 end
Получаем:
-
3
0 комментариев
Рекомендуемые комментарии
Нет комментариев для отображения