Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- ИнфоПанель 1.1
- -- Информационное табло, создано специально для сервера
- -- computercraft.ru - IT 1.7.10
- -- Автор идеи: AlexCC
- -- Создатели: Totoro
- -- (c) 01.2015
- local unicode = require('unicode')
- local event = require('event')
- local fs = require('filesystem')
- local computer = require('computer')
- local com = require('component')
- local term = require('term')
- local gpu = com.gpu
- function trytoload(name)
- if com.isAvailable(name) then
- return com.getPrimary(name)
- else
- return nil
- end
- end
- -- =========================== V A R I A B L E S =========================== --
- -- масштаб текста
- local defaultRatio = 8
- local textRatio = defaultRatio
- -- размер и разрешение монитора
- local screenSize = {width = 6, height = 3}
- local screenRes = {width = 48, height = 12}
- local oldRes = {width = 0, height = 0}
- -- базовые цвета оформления
- local basicforeground = 0xFFFFFF
- local basicbackground = 0x000000
- local linksbackground = 0x111111
- -- поля
- local margin = 1
- -- простой перед сбросом на главную (сек)
- local downtime = 60 * 5
- local lasttime = 0
- -- список кнопок-разделов
- -- pages = {{file=..., name=...}, ...}
- local pages = {}
- local rows = {0}
- -- рендер текущей страницы
- -- блок и т.д.
- -- render = {{{x=..., color=..., text=..., link=...}, ... }, -- строка
- -- {{...}, {...}, {...}}, -- другая строка
- -- {...}, ...} -- и т.д.
- local render = {}
- -- ========================== L O A D F I L E S ========================== --
- -- загружаем настройки и таблицу страниц
- function loadConfig(configfile)
- file = io.open(configfile, "r")
- if file ~= nil then
- pages = {}
- for line in file:lines() do
- -- если строка не пуста и не комментарий
- if string.len(line) ~= 0 and line:sub(1,1) ~= "#" then
- key, data = line:match("(.+)=(.+)")
- -- читаем масштаб текста
- if key == "textScale" then
- value = tonumber(data)
- if value ~= nil then textRatio = defaultRatio/value end
- -- читает задержку перед сбросом
- elseif key == 'downtime' then
- value = tonumber(data)
- if value ~= nil then downtime = value end
- -- читаем размер полей
- elseif key == 'margin' then
- value = tonumber(data)
- if value ~= nil then margin = value end
- -- читаем основной цвет фона
- elseif key == "background" then
- value = tonumber(data:sub(2,-1), 16)
- if value ~= nil then basicbackground = value end
- -- читаем основной цвет текста
- elseif key == "foreground" then
- value = tonumber(data:sub(2,-1), 16)
- if value ~= nil then basicforeground = value end
- -- читаем цвет фона для ссылок
- elseif key == "linksback" then
- value = tonumber(data:sub(2,-1), 16)
- if value ~= nil then linksbackground = value end
- else
- -- читаем название странички внутри двойных кавычек
- value = data:match('"(.+)"')
- if key ~= nil and value ~= nil then
- table.insert(pages, {file = key, name = value})
- end
- end
- end
- end
- file:close()
- else
- -- файл конфигурации не найден
- error("No configuration file found.")
- end
- end
- -- загружаем текст странички
- function loadPage(filename)
- file = io.open(filename, "r")
- if file ~= nil then
- -- бъем на токены
- explode(file)
- file:close()
- -- рендерим
- renderPage()
- return true
- else
- -- файл странички не найден
- if filename ~= "404.txt" then
- loadPage("404.txt")
- else
- return false
- end
- end
- end
- -- сканируем файл посимвольно, бьем на токены
- local tokens = {}
- function explode(file)
- tokens = {}
- word = ""
- while true do
- char = file:read(1)
- -- если достигнут конец файла - прерываем цикл
- if char == nil then
- if word ~= "" then table.insert(tokens, word) end
- break
- -- если перенос
- elseif char == '\n' then
- if word ~= "" then table.insert(tokens, word) end
- table.insert(tokens, "\n")
- word = ""
- -- если открывающая скобка тега
- elseif char == '[' then
- table.insert(tokens, word)
- word = char
- -- закрывающая скобка тега
- elseif char == ']' then
- table.insert(tokens, word..char)
- word = ""
- else
- word = word..char
- end
- end
- end
- -- поиск последнего тега
- function getLastTag(tags, name)
- for c=#tags, 1, -1 do
- if tags[c].name == name then
- return tags[c]
- end
- end
- return {name = nil, value = nil}
- end
- -- разбиение строки для переноса по словам
- function splitByWords(str, first_length, max_line_length)
- local lines = {}
- local line = ""
- local first = true
- local length = first_length
- str:gsub('(%s*)(%S+)',
- function(spc, word)
- if unicode.len(line) + unicode.len(spc)
- + unicode.len(word) > length then
- table.insert(lines, line)
- line = word
- if first then
- first = false
- length = max_line_length
- end
- else
- line = line..spc..word
- end
- end
- )
- lspc = str:match("(%s*)$")
- if lspc ~= nil then line = line..lspc end
- table.insert(lines, line)
- return lines
- end
- -- рендерим страничку на основе таблицы токенов
- function renderPage()
- local effects = {}
- local line = 1
- local col = 1
- render = {{}}
- width = screenRes.width - margin*2 -- поля
- for i=1, #tokens do
- -- закрывающий тег
- if tokens[i]:sub(1,2) == '[/' then
- name = tokens[i]:match("%[/(.+)%]")
- -- неуклюже, потом заменю
- if name ~= nil then
- -- закрываем последний тег name
- for c=#effects, 1, -1 do
- if effects[c].name == name then
- table.remove(effects, c)
- break
- end
- end
- -- перенос после центровки
- if name == "center" then
- if tokens[i+1] ~= '\n' then
- col = 1
- line = line + 1
- render[line] = {}
- end
- end
- end
- -- открывающий тег
- elseif tokens[i]:sub(1,1) == '[' then
- name = tokens[i]:match("%[(.+)%]")
- -- цветной текст
- if name:sub(1,5) == "color" then
- hex = name:match("#(......)")
- color = tonumber(hex, 16)
- if color ~= nil then
- table.insert(effects, {name = "color", value = color})
- end
- -- гиперссылки
- elseif name:sub(1,3) == "url" then
- filename = name:match("=(.+)")
- if filename ~= nil then
- table.insert(effects, {name = "url", value = filename})
- end
- -- центрирование
- elseif name == "center" then
- table.insert(effects, {name = "center"})
- -- если центровка начинается не с пустой строки, то перенос
- if col ~= 1 then
- col = 1
- line = line + 1
- render[line] = {}
- end
- -- горизонтальная линия
- elseif name == "---" then
- table.insert(render[line],
- {text = string.rep("-", screenRes.width-margin*2-col+1),
- center = false,
- color = basicforeground,
- x = col})
- col = screenRes.width-margin*2
- end
- -- перенос
- elseif tokens[i] == '\n' then
- col = 1
- line = line + 1
- render[line] = {}
- -- простой текст
- else
- block = {}
- block.color = getLastTag(effects, "color").value
- if block.color == nil then block.color = basicforeground end
- block.url = getLastTag(effects, "url").value
- block.center = (getLastTag(effects, "center").name ~= nil)
- -- рубим переносы по словам
- for _, word in ipairs(splitByWords(tokens[i], width-col+1, width)) do
- if col + unicode.len(word) > width then
- col = 1
- line = line + 1
- render[line] = {}
- end
- block.x = col
- block.text = word
- table.insert(render[line], {text = block.text, x = block.x,
- color = block.color, url = block.url, center = block.center})
- col = col + unicode.len(word)
- end
- end
- end
- end
- -- вычисляем длину строки рендера
- function getLineLen(num)
- local len = 0
- if render[num] ~= nil then
- for _, block in ipairs(render[num]) do
- len = len + unicode.len(block.text)
- end
- end
- return len
- end
- -- ============================= B U T T O N S ============================= --
- local Button = {}
- Button.__index = Button
- function Button.new(page, x, y, text, fore, back, width)
- self = setmetatable({}, Button)
- self.form = '[ '
- if width == nil then width = 0
- else width = (width - unicode.len(text))-4 end
- for i=1, math.floor(width/2) do
- self.form = self.form.. ' '
- end
- self.form = self.form..text
- for i=1, math.ceil(width/2) do
- self.form = self.form.. ' '
- end
- self.form = self.form..' ]'
- self.page = page
- self.x = x; self.y = y
- self.fore = fore
- self.back = back
- self.visible = true
- return self
- end
- function Button:draw(fore, back)
- if self.visible then
- local fore = fore or self.fore
- local back = back or self.back
- gpu.setForeground(fore)
- gpu.setBackground(back)
- gpu.set(self.x, self.y, self.form)
- end
- end
- function Button:click(x, y)
- if self.visible then
- if y == self.y then
- if x >= self.x and x < self.x+unicode.len(self.form) then
- self:draw(self.back, self.fore)
- setPage(self.page, true)
- return true
- end
- end
- end
- return false
- end
- local buttons = {}
- function buttonsNew(func, x, y, text, fore, back, width)
- table.insert(buttons, Button.new(func, x, y, text, fore, back, width))
- end
- function buttonsDraw()
- for i=1, #buttons do
- buttons[i]:draw()
- end
- end
- function buttonsClick(x, y)
- for i=1, #buttons do
- buttons[i]:click(x, y)
- end
- end
- -- ================================= G U I ================================= --
- -- создаем нижнее меню
- function generateButtons()
- rows = {0}
- rowlen = 0
- for _, page in ipairs(pages) do
- len = unicode.len(page.name) + 4
- if (rowlen + len) > (screenRes.width - 2) then
- table.insert(rows, 1)
- -- отступ строки кнопок
- rows[#rows-101] = math.floor((screenRes.width-2-rowlen) / 2) - 2
- rowlen = len
- else
- rows[#rows] = rows[#rows] + 1
- rowlen = rowlen + len
- end
- end
- rows[#rows-100] = (screenRes.width-2-rowlen) / 2
- i = 1
- for y=1, #rows do
- dx = 2
- for x=1, rows[y] do
- len = unicode.len(pages[i].name)+4
- buttonsNew(pages[i].file, dx+rows[y-100], screenRes.height-#rows+y,
- pages[i].name, basicforeground, linksbackground, len)
- dx = dx + len + 1
- i = i + 1
- end
- end
- end
- -- рисуем панель кнопок
- function drawMenu()
- buttonsDraw()
- end
- -- рисуем страницу, начиная с заданной строки
- function drawPage(lineNum)
- local height = screenRes.height - #rows -- нижняя панель
- local y = 1
- -- чистим экран
- gpu.setBackground(basicbackground)
- gpu.setForeground(basicforeground)
- gpu.fill(1,1, screenRes.width, height, ' ')
- -- рисуем
- for i=lineNum, math.min(#render, lineNum+height-1) do
- offset = 0
- for _, block in ipairs(render[i]) do
- if block.center and offset == 0 then
- offset = (screenRes.width - margin*2 - getLineLen(i))/2
- end
- gpu.setForeground(block.color)
- if block.url ~= nil then
- gpu.setBackground(linksbackground)
- else
- gpu.setBackground(basicbackground)
- end
- gpu.set(block.x+margin+offset, y, block.text)
- end
- y = y + 1
- end
- -- полоса прокрутки
- if #render > height then
- gpu.setForeground(basicforeground)
- gpu.setBackground(linksbackground)
- gpu.set(screenRes.width-1, height/2-2, "/\\")
- gpu.set(screenRes.width-1, height/2-1, "/\\")
- gpu.set(screenRes.width-1, height/2, "/\\")
- gpu.set(screenRes.width-1, height/2+5, "\\/")
- gpu.set(screenRes.width-1, height/2+4, "\\/")
- gpu.set(screenRes.width-1, height/2+3, "\\/")
- end
- end
- -- сборная функция для обновления страницы
- function setPage(filename, refresh)
- loadPage(filename)
- drawPage(1)
- currentline = 1
- if refresh then drawMenu() end
- end
- -- проверяем, была ли нажата гиперссылка
- function linksClick(x, y)
- local dy = currentline + y - 1
- if render[dy] ~= nil then
- for _, block in ipairs(render[dy]) do
- if block.url ~= nil then
- if x >= block.x and x <= block.x + unicode.len(block.text) then
- setPage(block.url)
- end
- end
- end
- end
- end
- -- проверяем, были ли нажаты стрелки слайдера
- function sliderClick(x, y)
- local height = screenRes.height - #rows
- if #render > height then
- local half = height/2
- if x > screenRes.width-2 then
- if y >= half-3 and y <= half then
- if currentline > 1 then
- currentline = math.max(1, currentline - height + 1)
- drawPage(currentline)
- end
- elseif y <= half+5 and y >= half+2 then
- if currentline < (#render-height+1) then
- currentline = currentline + height - 1
- drawPage(currentline)
- end
- end
- end
- end
- end
- -- "костыль" для обновления экрана
- function updateScreen()
- gpu.setBackground(basicbackground)
- gpu.set(1,1, '_')
- gpu.set(1,1, ' ')
- end
- -- ================================ I N I T ================================ --
- -- конфиги
- loadConfig("info.cfg")
- -- определяем размер доступного монитора
- screenSize.width, screenSize.height = com.screen.getAspectRatio()
- -- вычисляем размеры текстового экрана, исходя из размера монитора
- screenRes.width = math.ceil(screenSize.width * textRatio) + 2
- screenRes.height = math.ceil(screenSize.height * textRatio / 2)
- -- изменяем
- oldRes.width, oldRes.height = gpu.getResolution()
- gpu.setResolution(screenRes.width, screenRes.height)
- -- очищаем экран
- gpu.setForeground(basicforeground)
- gpu.setBackground(basicbackground)
- term.clear()
- -- создаем кнопки
- generateButtons()
- -- загружаем стартовую страницу и рисуем все
- setPage("index.txt", true)
- -- устанавливаем время последнего обновления
- lasttime = computer.uptime()
- -- ========================== M A I N C Y C L E ========================== --
- -- главный цикл работает до нажатия кнопки
- -- поскольку клавиатура есть только у админа
- while true do
- local name, add, x, y = event.pull(3)
- if name == 'touch' or name == 'drag' then
- buttonsClick(x,y)
- linksClick(x,y)
- sliderClick(x,y)
- lasttime = computer.uptime()
- elseif name == nil then
- elseif name == "key_down" then break end
- -- в случае бездействия в DOWNTIME секунд - сброс на главную
- if computer.uptime() - lasttime > downtime then
- setPage("index.txt", true)
- lasttime = computer.uptime()
- end
- -- костыль, чтобы экран не гас после рестартов
- updateScreen()
- end
- -- возвращаем экран в прежний вид
- gpu.setForeground(0xFFFFFF)
- gpu.setBackground(0x000000)
- gpu.setResolution(oldRes.width, oldRes.height)
- term.clear()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement