103 KiB
Raw Blame History

Содержание
О библиотеке
Установка
Standalone-методы
GUI.contextMenu
GUI.error
Контейнеры
GUI.container
GUI.layout
Виджеты
GUI.object
Анимация
Готовые виджеты
GUI.panel
GUI.button
GUI.label
GUI.inputField
GUI.slider
GUI.switch
GUI.colorSelector
GUI.comboBox
GUI.menu
GUI.image
GUI.progressBar
GUI.scrollBar
GUI.textBox
GUI.treeView
GUI.codeView
GUI.chart
Практические примеры
    Практический пример #1
    Практический пример #2
    Практический пример #3
    Практический пример #4
    Практический пример #5

О библиотеке

GUI - многофункциональная графическая библиотека, отлаженная под использование маломощными компьютерами с максимально возможной производительностью. Она поддерживает множество элементов интерфейса: от привычных кнопок, слайдеров, текстовых полей и картинок до графиков и инструментов работы с цветовыми режимами. Быстродействие достигается за счет использования двойной буферизации и сложных группировочных алгоритмов.

К примеру, моя операционная система и среда разработки полностью реализованы методами данной библиотеки:

Imgur

Imgur

Пусть синтаксис и обилие текста вас не пугают, в документации имеется множество наглядных иллюстрированных примеров и практических задач.

Установка

Библиотека Функционал
GUI Данная библиотека
advancedLua Дополнение стандартных библиотек Lua множеством функций: быстрой сериализацией таблиц, переносом строк, методами обработки бинарных данных и т.д.
doubleBuffering Низкоуровневая библиотека двойной буферизации для максималььно быстрой отрисовки графики с поддержкой методов полупиксельной отрисовки
color Низкоуровневая библиотека для работы с цветом, предоставляющая методы получения цветовых каналов, различные палитры и конвертацию цвета в 8-битный формат
image Библиотека, реализующая стандарт изображений для OpenComputers и методы их обработки: транспонирование, обрезку, поворот, отражение и т.д.
OCIF Модуль формата изображения OCIF (OpenComputers Image Format) для библиотеки image, написанный с учетом особенностей мода и реализующий эффективное сжатие пиксельных данных
syntax Подсветка lua-синтаксиса для виджета CodeView
palette Библиотека-окно для работы с цветом в режиме HSV и выборе конкретных цветовых данных для виджета ColorSelector

Вы можете использовать имеющиеся выше ссылки для установки зависимостей вручную или запустить автоматический установщик, загружающий все необходимые файлы за вас:

pastebin run ryhyXUKZ

Standalone-методы

Библиотека имеет несколько полезных независимых методов, упрощающих разработку программ. К таковым относятся, к примеру, контекстное меню и информационное alert-окно.

GUI.contextMenu( x, y ): table contextMenu

Тип Аргумент Описание
int x Координата меню по оси x
int y Координата меню по оси y

Открыть по указанным координатам контекстное меню и ожидать выбора пользователя. При выборе какого-либо элемента будет вызван его callback-метод .onTouch, если таковой имеется.

Если контекстное меню содержит слишком большое количество элементов, то появятся удобные кнопочки и поддержка колеса мыши для прокрутки содержимого.

Тип свойства Свойство Описание
function :addItem( string text, boolean disabled, string shortcut, int color ) Добавить в контекстное меню элемент с указанными параметрами. При параметре disabled элемент не будет реагировать на клики мышью. Каждый элемент может иметь собственный callback-метод .onTouch для последующей обработки данных
function :addSeparator() Добавить в контекстное меню визуальный разделитель

Пример реализации контекстного меню:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

buffer.clear(0x2D2D2D)
buffer.draw(true)

local contextMenu = GUI.contextMenu(2, 2)
contextMenu:addItem("New")
contextMenu:addItem("Open").onTouch = function()
	-- Do something to open file or whatever
end
contextMenu:addSeparator()
contextMenu:addItem("Save", true)
contextMenu:addItem("Save as")
contextMenu:addSeparator()
for i = 1, 25 do
	contextMenu:addItem("Do something " .. i)
end
contextMenu:show()

Результат:

Imgur

GUI.error( text )

Тип Аргумент Описание
string text Текст информационного окна

Показать отладочное окно с текстовой информацией. Слишком длинная строка будет автоматически перенесена. Для закрытия окна необходимо использовать клавишу return или нажать на кнопку "ОК".

Пример реализации:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

buffer.clear(0x2D2D2D)
GUI.error("Something went wrong here, my friend")

Результат:

Imgur

Контейнеры

Вся библиотека делится на две основные кострукции: контейнеры и виджеты. Контейнер предназначен для группировки нескольких виджетов и их конвеерной обработки, поэтому в первую очередь необходимо изучить особенности работы с контейнерами.

GUI.container( x, y, width, height ): table container

Тип Аргумент Описание
int x Координата контейнера по оси x
int y Координата контейнера по оси y
int width Ширина контейнера
int height Высота контейнера

Каждый контейнер - это группировщик для других объектов, его поведение очень похоже на папку, содержащую множество вложенных файлов и других папок. Для создания контейнера по размеру экрана используйте метод GUI.fullScreenContainer().

Все дочерние элементы контейнера имеют свою localPosition в контейнере (к примеру, {x = 4, y = 2}). После добавления дочернего элемента в контейнер для дальнейших рассчетов используется именно его локальная позиция. Для получения глобальных (экранных) координат дочернего элемента необходимо обращаться к element.x и element.y. Глобальная (экранная) позиция дочерних элементов рассчитывается при каждой отрисовке содержимого контейнера. Таким образом, изменяя глобальные координаты дочернего элемента вручную, вы, в сущности, ничего не добьётесь.

Наглядно система иерархии и позиционирования контейнеров и дочерних элементов представлена на следущем изображении:

Imgur

У контейнеров имеется немаловажная особенность: любой дочерний элемент, выступающий за границы контейнера, будет отрисован только в рамках размера этого контейнера:

Imgur

Для добавления в контейнер дочернего элемента используйте синтаксическую конструкцию :addChild(<Объект>). При этом глобальные координаты объекта становятся локальными. К примеру, для добавления кнопки на локальную позицию x = 5, y = 10 используйте :addChild(GUI.button(5, 10, ...)). В контейнер можно добавлять другие контейнеры, а в добавленные - еще одни, создавая сложные иерархические цепочки и группируя дочерние объекты по своему усмотрению.

Наконец, самая важная особенность контейнеров - это автоматизированная обработка системных событий. Для запуска обработки событий необходимо вызвать метод :startEventHandling. После этого при каждом событии текущий контейнер и всего его вложенные объекты будут рекурсивно проанализированы на наличие метода-обработчика .eventHandler.

Если метод-обработчик имеется, то он будет вызван со следующими аргументами: container mainContainer, object object, table eventData, где первым аргументом является контейнер, обрабатывающий события, вторым является текущий рассматриваемый объект обработчика событий, а третьим - таблица с данными события. Многие объекты, перечисленные ниже, уже имеют собственный .eventHandler - к примеру, кнопка автоматически нажимается, слайдер перемещается влево-вправо, а селектор цвета открывает палитру для выбора желаемого оттенка. Все это реализовано именно на методе-обработчике.

В качестве примера ниже приведен исходный код обработчика событий GUI.button. Как видите, в начале событие анализируется на соответствие "touch", затем кнопка визуально "нажимается", а в конце вызывается метод кнопки .onTouch, если он вообще имеется.

button.eventHandler = function(mainContainer, button, eventData)
	if eventData[1] == "touch" then
		button.pressed = true
		mainContainer:draw()
		buffer.draw()
		
		os.sleep(0.2)
		
		button.pressed = false
		mainContainer:draw()
		buffer.draw()
		
		if button.onTouch then
			button.onTouch(mainContainer, object, eventData)
		end
	end
end

Ключевая деталь обработчика событий в том, что если событие "экранное", то есть относящееся к клику пользователя на монитор (touch, drag, drop, scroll), то метод-обработчик объекта будет вызван только в том случае, если на пути прослеживания клика не имеется никаких других объектов, после чего обработка событий для оставшихся необработанных дочерних элементов завершится. Если событие не относится к экрану (key_down, clipboard и т.д.), или же объект не имеет метода-обработчика, то обработка оставшихся дочерних элементов продолжится в прежнем виде.

Если необходимо прекратить обработку событий, то необходимо вызвать метод :stopEventHandling.

Тип свойства Свойство Описание
function :addChild( table child ): table child Добавить произвольный объект в контейнер в качестве дочернего - таким образом вы способны создавать собственные виджеты с индивидуальными особенностями. Уточняю, что у добавляемого объекта обязательно должен иметься метод :draw (подробнее см. ниже). При добавлении объекта его глобальные координаты становятся локальными
function :deleteChildren(): table container Удалить все дочерние элементы контейнера
function :draw(): table container Рекурсивная отрисовка содержимого контейнера в порядке очереди его дочерних элементов. Обращаю внимание на то, что данный метод осуществляет отрисовку только в экранный буфер. Для отображения изменений на экране необходимо использовать метод библиотеки двойного буфера .draw()
function :startEventHandling([float delay]): table container Запуск обработчика событий для данного контейнера и всех вложенных в него дочерних элементов. Параметр delay аналогичен таковому в computer.pullSignal
function :stopEventHandling(): table container Остановка обработчика событий для данного контейнера

GUI.layout( x, y, width, height, columns, rows ): table container

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int height Высота объекта
int columnCount Количество рядов сетки
int rowCount Количество строк сетки

Layout является наследником GUI.container, автоматически располагающим дочерние объекты внутри себя. К примеру, если вам хочется визуально красиво отобразить множество объектов, не тратя время на ручной расчет координат, то layout создан для вас. На картинке ниже подробно показана структура layout размером 4x3:

Imgur

Видно, что имеется 12 ячеек, каждая из которых может иметь собственную ориентацию объектов, расстояние между ними, а также выравнивание по границам. Границы ячеек условны, так что дочерние объекты могут без проблем выходить за них, если это допускает указанный alignment. Каждому столбцу и каждому ряду можно задать свой индивидуальный размер либо в пикселях, либо в процентном отношении, так что работа с layout фантастически удобна.

Тип свойства Свойство Описание
function :setGridSize(int columnCount, int columnCount): layout layout Установить размер сетки. Все объекты, находящиеся вне диапазона нового размера, должны быть размещены в сетке заново через :setCellPosition()
function :setColumnWidth(int column, enum sizePolicy, float size): layout layout Установить ширину указанного столбца. Ширина может быть двух типов: GUI.sizePolicies.absolute или GUI.sizePolicies.percentage. В первом случае ширина выражена в пикселях, и не меняется при изменении размеров layout, а во втором она выражена дробным числом в промежутке [0; 1], обозначающим процентную ширину столбца. Если указана процентная ширина, и справа от выбранного столбца имеются другие, то их процентная ширина будет автоматически перерассчитана до нужных процентных значений.
function :setRowHeight(int row, enum sizePolicy, float size): layout layout Установить высоту указанного ряда. Поведение метода аналогично :setColumnWidth
function :addColumn(enum sizePolicy, float size): layout layout Добавить в сетку layout пустой столбец с указанным размером
function :addRow(enum sizePolicy, float size): layout layout Добавить в сетку layout пустой ряд с указанным размером
function :removeColumn(int column): layout layout Удалить из сетки layout указанный столбец
function :removeRow(int row): layout layout Удалить из сетки layout указанный ряд
function :setCellPosition(int column, int row, object child): object child Назначить дочернему объекту layout конкретную ячейку сетки. В одной ячейке может располагаться сколь угодно много объектов.
function :setCellDirection(int column, int row, enum direction): layout layout Назначить ячейке сетки ориентацию дочерних объектов. Поддерживаются GUI.directions.horizontal и GUI.directions.vertical
function :setCellAlignment(int column, int row, enum GUI.alignment.vertical, enum GUI.alignment.horizontal): layout layout Назначить ячейке сетки метод выравнивания дочерних объектов. Поддерживаются все 9 вариантов
function :setCellSpacing(int column, int row, int spacing): layout layout Назначить ячейке сетки расстояние в пикселях между объектами. По умолчанию оно равняется 1
function :setCellMargin(int column, int row, int horizontalMargin, int verticalMargin): layout layout Назначить ячейке сетки отступы в пикселях в зависимости от текущего alignment этой ячейки

Пример реализации layout:

local image = require("image")
local buffer = require("doubleBuffering")
local GUI = require("GUI")

-- Создаем полноэкранный контейнер, добавляем на него загруженное изображение и полупрозрачную черную панель
local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.image(1, 1, image.load("/MineOS/Pictures/Raspberry.pic")))
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x000000, 40))

-- Добавляем в созданный контейнер layout с сеткой размером 5x1
local layout = mainContainer:addChild(GUI.layout(1, 1, mainContainer.width, mainContainer.height, 5, 1))

-- Добавяляем в layout 9 кнопок, назначая им соответствующие позиции в сетке.
-- Как видите, сначала создается объект кнопки, затем он добавляется в качестве дочернего к layout,
-- а в конце концов ему назначается позиция сетки.
layout:setCellPosition(1, 1, layout:addChild(GUI.button(1, 1, 26, 3, 0xEEEEEE, 0x000000, 0xAAAAAA, 0x000000, "Button 1")))
layout:setCellPosition(2, 1, layout:addChild(GUI.button(1, 1, 26, 3, 0xEEEEEE, 0x000000, 0xAAAAAA, 0x000000, "Button 2")))
layout:setCellPosition(2, 1, layout:addChild(GUI.button(1, 1, 26, 3, 0xEEEEEE, 0x000000, 0xAAAAAA, 0x000000, "Button 3")))
layout:setCellPosition(3, 1, layout:addChild(GUI.button(1, 1, 26, 3, 0xEEEEEE, 0x000000, 0xAAAAAA, 0x000000, "Button 4")))
layout:setCellPosition(3, 1, layout:addChild(GUI.button(1, 1, 26, 3, 0xEEEEEE, 0x000000, 0xAAAAAA, 0x000000, "Button 5")))
layout:setCellPosition(3, 1, layout:addChild(GUI.button(1, 1, 26, 3, 0xEEEEEE, 0x000000, 0xAAAAAA, 0x000000, "Button 6")))
layout:setCellPosition(4, 1, layout:addChild(GUI.button(1, 1, 26, 3, 0xEEEEEE, 0x000000, 0xAAAAAA, 0x000000, "Button 7")))
layout:setCellPosition(4, 1, layout:addChild(GUI.button(1, 1, 26, 3, 0xEEEEEE, 0x000000, 0xAAAAAA, 0x000000, "Button 8")))
layout:setCellPosition(5, 1, layout:addChild(GUI.button(1, 1, 26, 3, 0xEEEEEE, 0x000000, 0xAAAAAA, 0x000000, "Button 9")))

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

Imgur

Как видите, 9 кнопок автоматически сгруппировались по 5 ячейкам сетки. Визуально структура созданного layout выглядит так:

Imgur

Также мы можем модифицировать код, чтобы кнопки группировались в 3 колонки, а расстояние между ними было равным 4 пикселям:

-- Изменяем размер сетки на 3x1
layout:setGridSize(3, 1)
-- Устанавливаем расстояние между объектами для каждой колонки
for column = 1, 3 do
	layout:setCellSpacing(column, 1, 4)
end
-- Обновляем позиции трех последних кнопок, чтобы они принадлежали третьей колонке
for child = 7, 9 do
	layout:setCellPosition(3, 1, layout.children[child])
end

Результат:

Imgur

Более подробно работа с layout рассмотрена в практическом примере 4 в конце документа.

Виджеты

После понимания концепции контейнеров можно с легкостью приступить к добавлению виджетов в созданный контейнер. Каждый виджет - это наследник объекта типа GUI.object

GUI.object( x, y, width, height ): table object

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int height Высота объекта

Помимо координат и размера GUI.object имеет несколько универсальных свойств:

Тип свойства Свойство Описание
boolean .hidden Является ли объект скрытым. Если объект скрыт, то его отрисовка и анализ системных событий игнорируются
boolean .disabled Является ли объект отключенным. Если объект отключен, то все системные события при обработке игнорируются
function :draw() Обязательный метод, вызываемый для отрисовки виджета на экране. Он может быть определен пользователем любым удобным для него образом. Данный метод осуществляет отрисовку только в экранный буфер, а не на экран
function :isClicked( int x, int y ): boolean isClicked Метод для проверки валидности клика на объект. Используется родительскими методами контейнеров и удобен для ручной проверки пересечения указанных координат с расположением объекта на экране

После добавления объекта в контейнер с помощью метода :addChild он приобретает дополнительные свойства для удобства использования:

Тип свойства Свойство Описание
table .parent Указатель на таблицу-контейнер родителя этого виджета
table .localPosition Таблица вида {x = int, y = int} с локальными координатами виджета в родительском контейнере
function :indexOf() Получить индекс данного виджета в родительском контейнере
function :moveForward() Передвинуть виджет "назад" в иерархии виджетов контейнера
function :moveBackward() Передвинуть виджет "вперед" в иерархии виджетов контейнера
function :moveToFront() Передвинуть виджет в конец иерархии виджетов контейнера
function :moveToBack() Передвинуть виджет в начало иерархии виджетов контейнера
function :getFirstParent() Получить первый родительский контейнер для рассматриваемой системы родительских контейнеров. К примеру, при существовании множества вложенных контейнеров метод вернет первый и "главный" из них
function :delete() Удалить этот объект из родительского контейнера. Грубо говоря, это удобный способ самоуничтожения
function :addAnimation(function frameHandler, function onFinish): table animation Добавить к этому объекту анимацию. Подробнее об анимациях и их создании см. ниже
[callback-function .eventHandler(container mainContainer, object object, table eventData) ] Необязательный метод для обработки системных событий, вызываемый обработчиком родительского контейнера. Если он имеется у рассматриваемого объекта, то будет вызван с соотвествующими аргументами

Анимация

Каждый виджет может быть без проблем анимирован при желании. К примеру, ниже представлена анимация GUI.switch.

Imgur

Чтобы добавить анимацию к виджету, вызовите метод <виджет>:addAnimation(function frameHandler, function onFinish). Данный метод возвращает объект анимации для дальнейшего использования, имеющий следущие свойства:

Тип свойства Свойство Описание
table .object Указатель на таблицу виджета, к которому была добавлена анимация
float .position Текущая позиция воспроизведения анимации. Всегда находится в диапазоне [0.0; 1.0], где левая граница - начало анимации, а правая - ее конец
function :start() Метод, начинающий воспроизведение анимации. Важная деталь: во время воспроизведения анимации контейнер, содержащий анимированные объекты, временно будет обрабатывать события с максимально возможной скоростью. По окончанию воспроизведения задержка между вызовами .pullSignal станет такой, какой была изначально
function :stop() Метод, завершающий воспроизведение анимации
function :delete() Метод, удаляющий анимацию из объекта
callback-function .frameHandler(table mainContainer, table object, table animation) Функция-обработчик кадра анимации. Вызывается автоматически каждый раз перед отрисовкой объекта. Первым параметром идет главный контейнер, в котором вызван обработчик событий, вторым - сам анимированный виджет, а третьим - объект анимации
callback-function .onFinish() Функция, вызываемая по окончанию воспроизведения анимации. Отмечу, что метод :stop() не вызывает срабатывания .onFinish

Создание анимированных объектов во всех подробностях описано в практическом примере в конце документа.

Готовые виджеты

Далее перечислены виджеты, поставляющиеся вместе с библиотекой и созданные на основе описанных выше инструкций. При желании вы можете сделать абсолютно аналогичные или гораздо более технически продвинутые виджеты без каких-либо затруднений. Подробнее о создании собственных виджетов см. практические примеры в конце документации.

GUI.panel( x, y, width, height, color, [transparency] ): table panel

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int height Высота объекта
int color Цвет панели
[int transparency] Опциональная прозрачность панели

Создать объект типа "панель", представляющий собой закрашенный прямоугольник с определенной опциональной прозрачностью. В большинстве случаев служит декоративным элементом.

Пример реализации панели:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

------------------------------------------------------------------------------------------

local mainContainer = GUI.fullScreenContainer()

mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x262626))
mainContainer:addChild(GUI.panel(10, 10, mainContainer.width - 20, mainContainer.height - 20, 0x880000))

------------------------------------------------------------------------------------------

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

Imgur

GUI.button( x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text ): table button

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int height Высота объекта
int buttonColor Цвет кнопки
int textColor Цвет текса
int buttonPressedColor Цвет кнопки при нажатии
int textPressedColor Цвет текста при нажатии
string text Текст на кнопке

Создать объект типа "кнопка". Каждая кнопка имеет два состояния (isPressed = true/false), автоматически переключаемые методом-обработчиком .eventHandler. Для назначения какого-либо действия кнопке после ее нажатия создайте для нее метод .onTouch().

Существует два дополнительных виджета кнопки, имеющие визуально разные стили:

  • GUI.framedButton(...) отрисовывается с рамкой по краям кнопки

Imgur

  • GUI.roundedButton(...) имеет симпатичные скругленные уголки

Imgur

Для удобства имеется также альтернативные способы создания кнопки:

  • GUI.adaptiveButton(...) отличается тем, что вместо width и height использует отступ в пикселях со всех сторон от текста. Этот способ удобен для автоматического расчета размера кнопки вне зависимости от вложенного текста. Разумеется, поддерживаются также GUI.adaptiveFramedButton(...) и GUI.adaptiveRoundedButton(...)
Тип свойства Свойство Описание
callback-function .onTouch( table eventData ) Метод, вызываемый после нажатия кнопки в обработчике событий
function :press() Изменить состояние кнопки на "нажатое"
function :release() Изменить состояние кнопки на "отжатое"
function :pressAndRelease( float time ) Нажать и отжать кнопку в течение указанного временного периода. Примечание: этот метод использует отрисовку содержимого двойного буфера

Пример реализации кнопки:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

------------------------------------------------------------------------------------------

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))

mainContainer:addChild(GUI.button(2, 2, 30, 3, 0xFFFFFF, 0x555555, 0xAAAAAA, 0x2D2D2D, "Regular button")).onTouch = function()
	GUI.error("Regular button was pressed")
end

mainContainer:addChild(GUI.roundedButton(2, 6, 30, 3, 0xFFFFFF, 0x555555, 0xAAAAAA, 0x2D2D2D, "Rounded button")).onTouch = function()
	GUI.error("Rounded button was pressed")
end

mainContainer:addChild(GUI.framedButton(2, 10, 30, 3, 0xFFFFFF, 0xFFFFFF, 0xAAAAAA, 0xAAAAAA, "Framed button")).onTouch = function()
	GUI.error("Framed button was pressed")
end

------------------------------------------------------------------------------------------

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

Imgur

GUI.label( x, y, width, height, textColor, text ): table label

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int height Высота объекта
int textColor Цвет текста лейбла
string text Текст лейбла

Создать объект типа "лейбл", предназначенный для отображения текстовой информации в различных вариациях расположения.

Тип свойства Свойство Описание
callback-function .onTouch( table eventData ) Метод, вызываемый после нажатия на лейбл в обработчике событий
function :setAlignment( enum GUI.alignment.vertical, enum GUI.alignment.horizontal ): table label Выбрать вариант отображения текста относительно границ лейбла

Пример реализации лейбла:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))

mainContainer:addChild(GUI.label(2, 2, mainContainer.width, mainContainer.height, 0xFFFFFF, "Centered text")):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.center)

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

Imgur

GUI.inputField( x, y, width, height, backgroundColor, textColor, placeholderTextColor, backgroundFocusedColor, textFocusedColor, text, [placeholderText, eraseTextOnFocus, textMask ): table inputField

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int height Высота объекта
int backgroundColor Цвет фона
int textColor Цвет текста
int placeholderTextColor Цвет текста placeholder при условии, что он указан ниже
int backgroundFocusedColor Цвет фона в состоянии focused
int textFocusedColor Цвет текста в состоянии focused
string text Введенный на момент создания поля текст
[string placeholderText] Текст, появляющийся при условии, что введенный текст отсутствует
[boolean eraseTextOnFocus] Необходимо ли удалять текст при активации ввода
[char textMask] Символ-маска для вводимого текста. Удобно для создания поля ввода пароля

Создать объект, предназначенный для ввода и анализа текстовых данных с клавиатуры. Объект универсален и подходит как для создания простых форм для ввода логина/пароля, так и для сложных структур наподобие интерпретаторов команд. К примеру, окно палитры со скриншота в начале документации полностью основано на использовании этого объекта.

Тип свойства Свойство Описание
function :startInput() Начать ввод в текстовое поле. Метод полезен, если хочется сразу сделать
callback-function .validator( string text ) Метод, вызывающийся после окончания ввода текста в поле. Если возвращает true, то текст в текстовом поле меняется на введенный, в противном случае введенные данные игнорируются. К примеру, в данном методе удобно проверять, является ли введенная текстовая информация числом через tonumber()
callback-function .onInputFinished( string text, table eventData ) Метод, вызываемый после ввода данных в обработчике событий. Удобная штука, если хочется выполнить какие-либо действия сразу после ввода текста. Если у объекта имеется validator, и текст не прошел проверку через него, то onInputFinished вызван не будет.

Пример реализации поля ввода:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

------------------------------------------------------------------------------------------

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))

mainContainer:addChild(GUI.inputField(2, 2, 30, 3, 0xEEEEEE, 0x555555, 0x999999, 0xFFFFFF, 0x2D2D2D, "Hello world", "Placeholder text")).onInputFinished = function()
	GUI.error("Input finished!")
end

------------------------------------------------------------------------------------------

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

Imgur

GUI.slider( x, y, width, primaryColor, secondaryColor, pipeColor, valueColor, minimumValue, maximumValue, value, [showCornerValues, currentValuePrefix, currentValuePostfix] ): table slider

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int primaryColor Основной цвет слайдера
int secondaryColor Вторичный цвет слайдера
int pipeColor Цвет "пимпочки" слайдера
int valueColor Цвет текста значений слайдера
float minimumValue Минимальное значение слайдера
float maximumValue Максимальное значение слайдера
float value Значение слайдера
[bool showCornerValues] Показывать ли пиковые значения слайдера по сторонам от него
[string currentValuePrefix] Префикс для значения слайдера
[string currentValuePostfix] Постфикс для значения слайдера

Создать объект типа "слайдер", предназначенный для манипуляцией числовыми данными. Значение слайдера всегда будет варьироваться в диапазоне от минимального до максимального значений. Опционально можно указать значение поля слайдер.roundValues = true, если необходимо округлять изменяющееся число.

Тип свойства Свойство Описание
callback-function .onValueChanged( float value, table eventData ) Метод, вызывающийся после изменения значения слайдера

Пример реализации слайдера:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

------------------------------------------------------------------------------------------

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))

local slider = mainContainer:addChild(GUI.slider(4, 2, 30, 0x66DB80, 0x0, 0xFFFFFF, 0xAAAAAA, 0, 100, 50, true, "Prefix: ", " postfix"))
slider.roundValues = true
slider.onValueChanged = function(value)
	-- Do something when slider's value changed
end

------------------------------------------------------------------------------------------

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

Imgur

GUI.switch( x, y, width, primaryColor, secondaryColor, pipeColor, state ): table switch

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int primaryColor Основной цвет переключателя
int secondaryColor Вторичный цвет переключателя
int pipeColor Цвет "пимпочки" переключателя
boolean state Состояние переключателя

Создать объект типа "переключатель", для определения истинности или ложности того или иного события. При клике на объект меняет состояние на противоположное.

Тип свойства Свойство Описание
callback-function .onStateChanged( boolean state, table eventData ) Метод, вызывающийся после изменения состояния переключателя

Пример реализации свитча:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

------------------------------------------------------------------------------------------

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))

local switch1 = mainContainer:addChild(GUI.switch(3, 2, 8, 0x66DB80, 0x1D1D1D, 0xEEEEEE, true))
local switch2 = mainContainer:addChild(GUI.switch(3, 4, 8, 0x66DB80, 0x1D1D1D, 0xEEEEEE, false))
switch2.onStateChanged = function(state)
	GUI.error("Switch state changed!")
end

------------------------------------------------------------------------------------------

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

Imgur

GUI.switchAndLabel( x, y, width, switchWidth, primaryColor, secondaryColor, pipeColor, textColor, text, switchState ): table switch

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Общая ширина
int width Ширина переключателя
int primaryColor Основной цвет переключателя
int secondaryColor Вторичный цвет переключателя
int pipeColor Цвет "пимпочки" переключателя
int textColor Цвет текста лейбла
string text Текст лейбла
boolean state Состояние переключателя

Быстрый способ создания пары из свитча и лейбла одновременно. Идеально подходит для визуального отображения параметров типа boolean у любых объектов .

Тип свойства Свойство Описание
table .switch Указатель на объект свитча
table .label Указатель на объект лейбла

Пример реализации свитча и лейбла:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

------------------------------------------------------------------------------------------

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))

mainContainer:addChild(GUI.switchAndLabel(2, 2, 25, 8, 0x66DB80, 0x1D1D1D, 0xEEEEEE, 0x999999, "Sample text 1:", true))
mainContainer:addChild(GUI.switchAndLabel(2, 4, 25, 8, 0x66DB80, 0x1D1D1D, 0xEEEEEE, 0x999999, "Sample text 2:", false))

------------------------------------------------------------------------------------------

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат: Imgur

GUI.colorSelector( x, y, width, height, color, text ): table colorSelector

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int height Высота объекта
int color Текущий цвет селектора
string text Текст селектора

Создать объект типа "селектор цвета", представляющий собой аналог кнопки, позволяющей выбрать цвет при помощи удобной палитры.

Тип свойства Свойство Описание
callback-function .onTouch( table eventData ) Метод, вызываемый после нажатия на селектор цвета в обработчике событий

Пример реализации селектора цвета:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

------------------------------------------------------------------------------------------

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))

mainContainer:addChild(GUI.colorSelector(2, 2, 30, 3, 0xFF55FF, "Choose color")).onTouch = function()
	-- Do something after choosing color
end

------------------------------------------------------------------------------------------

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

Imgur

GUI.comboBox( x, y, width, elementHeight, backgroundColor, textColor, arrowBackgroundColor, arrowTextColor ): table comboBox

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int elementHeight Высота элемента комбо-бокса
int backgroundColor Цвет фона комбо-бокса
int textColor Цвет текста комбо-бокса
int arrowBackgroundColor Цвет фона стрелки комбо-бокса
int arrowTextColor Цвет текста стрелки комбо-бокса

Создать объект типа "комбо-бокс", позволяющий выбирать объекты из множества перечисленных вариантов. Методика обращения к комбо-боксу схожа с обращением к контекстному меню.

Тип свойства Свойство Описание
function :addItem( string text, boolean disabled, string shortcut, int color ) Добавить в комбо-бокс элемент с указанными параметрами. При параметре disabled элемент не будет реагировать на клики мышью. Каждый элемент может иметь собственный callback-метод .onTouch для последующей обработки данных
function :addSeparator() Добавить визуальный в комбо-бокс разделитель
table .items Таблица элементов комбо-бокса
int .currentItem Индекс выбранного элемента комбо-бокса

Пример реализации комбо-бокса:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

------------------------------------------------------------------------------------------

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))

local comboBox = mainContainer:addChild(GUI.comboBox(3, 2, 30, 3, 0xEEEEEE, 0x2D2D2D, 0xCCCCCC, 0x888888))
comboBox:addItem(".PNG")
comboBox:addItem(".JPG").onTouch = function()
	-- Do something when .JPG was selected
end
comboBox:addItem(".GIF")
comboBox:addItem(".PIC")

------------------------------------------------------------------------------------------

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

Imgur

GUI.menu( x, y, width, backgroundColor, textColor, backgroundPressedColor, textPressedColor, backgroundTransparency ): table menu

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int backgroundColor Цвет фона меню
int textColor Цвет текста меню
int backgroundPressedColor Цвет фона меню при нажатии на элемент
int textPressedColor Цвет текста меню при нажатии на элемент
int backgroundTransparency Прозрачность фона меню

Создать объект типа "горизонтальное меню", позволяющий выбирать объекты из множества перечисленных вариантов. По большей части применяется в структурах типа "Файл - Редактировать - Вид - Помощь" и подобных.

Тип свойства Свойство Описание
function :addItem( string text, int color ): table item Добавить в меню элемент с указанными параметрами. Каждый элемент имеет собственный callback-метод .onTouch
callback-function .onItemSelected( table item, table eventData ) Метод, вызывающийся после выборе какого-либо элемента комбо-бокса

Пример реализации меню:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

------------------------------------------------------------------------------------------

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))

local menu = mainContainer:addChild(GUI.menu(1, 1, mainContainer.width, 0xEEEEEE, 0x666666, 0x3366CC, 0xFFFFFF, nil))
menu:addItem("MineCode IDE", 0x0)
local item = menu:addItem("File")
item.onTouch = function(eventData)
	local contextMenu = GUI.contextMenu(item.x, item.y + 1)
	contextMenu:addItem("New")
	contextMenu:addItem("Open").onTouch = function()
		GUI.error("Open was pressed")
	end
	contextMenu:addSeparator()
	contextMenu:addItem("Save")
	contextMenu:addItem("Save as")
	contextMenu:show()
end
menu:addItem("Edit")
menu:addItem("View")
menu:addItem("About")

------------------------------------------------------------------------------------------

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

Imgur

GUI.image( x, y, loadedImage ): table image

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
table loadedImage Изображение, загруженное методом image.load()

Создать объект типа "изображение", представляющий из себя аналог объекта panel с тем лишь исключением, что вместо статичного цвета используется загруженное изображение.

Тип свойства Свойство Описание
callback-function .onTouch( table eventData ) Метод, вызываемый после нажатия на изображение в обработчике событий
table .image Таблица пиксельных данных изображения

Пример реализации изображения:

local image = require("image")
local buffer = require("doubleBuffering")
local GUI = require("GUI")

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x0))

mainContainer:addChild(GUI.image(2, 2, image.load("/Furnance.pic")))

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

enter image description here

GUI.progressBar( x, y, width, primaryColor, secondaryColor, valueColor, value, [thin, showValue, valuePrefix, valuePostfix] ): table progressBar

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int primaryColor Основной цвет шкалы прогресса
int secondaryColor Вторичный цвет шкалы прогресса
int valueColor Цвет текста значений шкалы прогресса
float value Значение шкалы прогресса
[bool thin] Активировать ли режим отрисовки "тонкого" объекта
[bool showValue] Показывать ли значение шкалы прогресса
[string valuePrefix] Префикс для значения шкалы прогресса
[string valuePostfix] Постфикс для значения шкалы прогресса

Создать объект типа "шкала прогресса", значение которой меняется от 0 до 100.

Тип свойства Свойство Описание
int .value Текущее числовое значение шкалы прогресса

Пример реализации шкалы прогресса:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x0))

mainContainer:addChild(GUI.progressBar(2, 2, 50, 0x3366CC, 0xEEEEEE, 0xEEEEEE, 80, true, true, "Value prefix: ", " value postfix"))

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

enter image description here

GUI.scrollBar( x, y, width, height, backgroundColor, foregroundColor, minimumValue, maximumValue, value, shownValueCount, onScrollValueIncrement, thinHorizontalMode ): table scrollBar

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int height Высота объекта
int backgroundColor Цвет фона scrollBar
int foregroundColor Цвет текста scrollBar
int minimumValue Минимальное значение scrollBar
int maximumValue Максимальное значение scrollBar
int value Текущее значение scrollBar
int shownValueCount Число "отображаемых" значений scrollBar
int onScrollValueIncrement Количество строк, пролистываемых при прокрутке
boolean thinHorizontalMode Режим отображения scrollBar в полупиксельном виде при горизонтальной ориентации

Создать объект типа "ScrollBar", предназначенный для визуальной демонстрации числа показанных объектов на экране. Сам по себе практически не используется, полезен в совокупности с другими виджетами.

Тип свойства Свойство Описание
callback-function .onTouch( table eventData ) Метод, вызываемый при клике на скорллбар. Значение скроллбара будет изменяться автоматически в указанном диапазоне
callback-function .onScroll( table eventData ) Метод, вызываемый при использовании колеса мыши на скроллбаре. Значение скроллбара будет изменяться в зависимости от величины .onScrollValueIncrement

Пример реализации ScrollBar:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x0))

local scrollBar = mainContainer:addChild(GUI.scrollBar(2, 2, 1, 30, 0xEEEEEE, 0x3366CC, 1, 100, 1, 10, 1, false))
scrollBar.onTouch = function()
	-- Do something on scrollBar touch
end

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

enter image description here

GUI.textBox(x, y, width, height, backgroundColor, textColor, lines, currentLine, horizontalOffset, verticalOffset): table textBox

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int height Высота объекта
int or nil backgroundColor Цвет фона текстбокса. При значении nil фон не рисуется в экранный буфер
int textColor Цвет текста текстбокса
table lines Таблица отображаемых строк текстбокса. Имеется опциональная возможность установки цвета конкретной строки, для этого элемент таблицы должен иметь вид {text = string, color = int}
int currentLine Текущая строка текстбокса, с которой осуществляется отображение текста
int horizontalOffset Отступ отображения текста от левого и правого краев текстбокса
int verticalOffset Отступ отображения текста от верхнего и нижнего краев текстбокса

Создать объект типа "текстбокс", предназначенный для отображения большого количества текстовых данных в небольшом контейнере с полосами прокрутки. При использовании колесика мыши и активации события scroll содержимое текстбокса будет автоматически "скроллиться" в нужном направлении.

Тип свойства Свойство Описание
function :setAlignment( enum GUI.alignment.vertical, enum GUI.alignment.horizontal ): table textBox Выбрать вариант отображения текста относительно границ текстбокса
table .lines Таблица со строковыми данными текстбокса

Пример реализации текстбокса:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x0))

local textBox = mainContainer:addChild(GUI.textBox(2, 2, 32, 16, 0xEEEEEE, 0x2D2D2D, {}, 1, 1, 0))
table.insert(textBox.lines, {text = "Sample colored line ", color = 0x880000})
for i = 1, 100 do
	table.insert(textBox.lines, "Sample line " .. i)
end

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

enter image description here

GUI.treeView( x, y, width, height, backgroundColor, textColor, selectionBackgroundColor, selectionTextColor, arrowColor, scrollBarPrimaryColor, scrollBarSecondaryColor, workPath ): table treeView

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int height Высота объекта
int or nil backgroundColor Цвет фона TreeView
int textColor Цвет текста TreeView
int selectionBackgroundColor Цвет выделения фона TreeView
int selectionTextColor Цвет выделения текста TreeView
int arrowColor Цвет стрелки директорий TreeView
int scrollBarPrimaryColor Первичный цвет скроллбара TreeView
int scrollBarSecondaryColor Вторичный цвет скроллбара TreeView
string workPath Стартовая директория TreeView

Создать объект типа "TreeView", предназначенный для навигации по файловой системе в виде иерархического древа. При клике на директорию будет показано ее содержимое, а во время прокрутки колесиком мыши содержимое будет "скроллиться" в указанном направлении.

Тип свойства Свойство Описание
callback-function .onFileSelected( int currentFile ) Метод, вызываемый после выбора файла в TreeView

Пример реализации TreeView:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

------------------------------------------------------------------------------------------

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x262626))

local treeView = mainContainer:addChild(GUI.treeView(2, 2, 30, 41, 0xCCCCCC, 0x2D2D2D, 0x3C3C3C, 0xEEEEEE, 0x666666, 0xEEEEEE, 0x3366CC, "/"))
treeView.onFileSelected = function(path)
   GUI.error("File was selected, the path is: \"" .. path .. "\"")
end

------------------------------------------------------------------------------------------

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

enter image description here

GUI.codeView( x, y, width, height, lines, fromSymbol, fromLine, maximumLineLength, selections, highlights, highlightLuaSyntax, indentationWidth ): table codeView

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int height Высота объекта
table lines Таблица с отображаемыми строками
int fromSymbol С какого символа начинать отображение кода
int fromLine С какой строки начинать отображение кода
int maximumLineLength Максимальная длина строки из имеющихся строк
table selections Таблица вида { {from = {line = int line, symbol = int symbol}, to = {line = int line, symbol = int symbol}}, ... }, позволяющая осуществлять выделение кода таким образом, как если бы пользователь выделил бы его мышью
table highlights Таблица вида { [int lineIndex] = int color, ... }, позволяющая подсвечивать указанные строки указанными цветом
boolean highlightLuaSyntax Подсвечивать ли синтаксис Lua
int indentationWidth Ширина индентации кода

Создать объект типа "CodeView", предназначенный для наглядного отображения Lua-кода с номерами строк, подсветкой синтаксиса, выделениям и скроллбарами.

Пример реализации CodeView:

local buffer = require("doubleBuffering")
local GUI = require("GUI")
local unicode = require("unicode")

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x0))

local codeView = mainContainer:addCodeView(2, 2, 130, 40, {}, 1, 1, 1, {}, {}, true, 2)
local file = io.open("/lib/OpenComputersGL/Main.lua", "r")
for line in file:lines() do
	line = line:gsub("\t", " ")
	table.insert(codeView.lines, line)
	codeView.maximumLineLength = math.max(codeView.maximumLineLength, unicode.len(line))
end
file:close()

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

enter image description here

GUI.chart( x, y, width, height, axisColor, axisValueColor, axisHelpersColor, chartColor, xAxisValueInterval, yAxisValueInterval, xAxisPostfix, yAxisPostfix, fillChartArea, values ): table chart

Тип Аргумент Описание
int x Координата объекта по оси x
int y Координата объекта по оси y
int width Ширина объекта
int height Высота объекта
int axisColor Цвет координатных осей
int axisValueColor Цвет числовых значений координатных осей
int axisHelpersColor Цвет вспомогательных линий координатных осей
int chartColor Цвет графика
float xAxisValueInterval Интервал от 0 до 1, с которым будут итерироваться значения графика на конкретной оси
float yAxisValueInterval Интервал от 0 до 1, с которым будут итерироваться значения графика на конкретной оси
string xAxisPostfix Текстовый постфикс для значений графика конкретной оси
string yAxisPostfix Текстовый постфикс для значений графика конкретной оси
boolean fillChartArea Необходимо ли закрашивать область графика или же рисовать его линией
table values Таблица вида {{float x, float y}, ...} со значениями графика

Создать объект типа "график", предназначенный для отображения статистической информации в виде графика с подписью значений осей.

Пример реализации Chart:

local buffer = require("doubleBuffering")
local GUI = require("GUI")

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x0))

local chart = mainContainer:addChild(GUI.chart(2, 2, 100, 30, 0xEEEEEE, 0xAAAAAA, 0x888888, 0xFFDB40, 0.25, 0.25, "s", "t", true, {}))
for i = 1, 100 do
	table.insert(chart.values, {i, math.random(0, 80)})
end

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

enter image description here

Практический пример #1

В качестве стартового примера возьмем простейшую задачу: расположим на экране 5 кнопок по вертикали и заставим их показывать окно с порядковым номером этой кнопки при нажатии на нее. Напишем следующий код:

-- Подключаем необходимые библиотеки
local buffer = require("doubleBuffering")
local GUI = require("GUI")

-- Создаем полноэкранный контейнер
local mainContainer = GUI.fullScreenContainer()
-- Добавляем на окно темно-серую панель по всей его ширине и высоте
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))

-- Создаем 5 объектов-кнопок, располагаемых все ниже и ниже
local y = 2
for i = 1, 5 do
	-- При нажатии на конкретную кнопку будет вызван указанный метод .onTouch()
	mainContainer:addChild(GUI.button(2, y, 30, 3, 0xEEEEEE, 0x2D2D2D, 0x666666, 0xEEEEEE, "This is button " .. i)).onTouch = function()
		GUI.error("You've pressed button " .. i .. "!")
	end
	y = y + 4
end

-- Отрисовываем содержимое окно
mainContainer:draw()
-- Отрисовываем содержимое экранного буфера
buffer.draw()
-- Активируем режим обработки событий
mainContainer:startEventHandling()

При нажатии на любую из созданных кнопок будет показываться дебаг-окно с информацией, указанной в методе .onTouch:

enter image description here

enter image description here

Практический пример #2

Поскольку в моде OpenComputers имеется интернет-плата, я написал небольшой клиент для работы с VK.com. Разумеется, для этого необходимо реализовать авторизацию на серверах, вводя свой e-mail/номер телефона и пароль к аккаунту. В качестве тренировки привожу часть кода, отвечающую за это.

-- Подключаем библиотеки
local image = require("image")
local buffer = require("doubleBuffering")
local GUI = require("GUI")

-- Создаем контейнер с синей фоновой панелью
local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x002440))

-- Указываем размеры полей ввода текста и кнопок
local elementWidth, elementHeight = 40, 3
local x, y = math.floor(mainContainer.width / 2 - elementWidth / 2), math.floor(mainContainer.height / 2) - 2

-- Загружаем и добавляем изображение логотипа "нашей компании"
local logotype = image.load("/MineOS/Applications/VK.app/Resources/VKLogo.pic")
mainContainer:addChild(GUI.image(math.floor(mainContainer.width / 2 - image.getWidth(logotype) / 2) - 2, y - image.getHeight(logotype) - 1, logotype)); y = y + 2

-- Создаем поле для ввода адреса почты
local emailTextBox = mainContainer:addChild(GUI.inputTextBox(x, y, elementWidth, elementHeight, 0xEEEEEE, 0x777777, 0xEEEEEE, 0x2D2D2D, nil, "E-mail", false, nil, nil, nil))
-- Создаем красный текстовый лейбл, показывающийся только в том случае, когда адрес почты неверен
local invalidEmailLabel = mainContainer:addChild(GUI.label(emailTextBox.localPosition.x + emailTextBox.width + 2, y + 1, mainContainer.width, 1, 0xFF5555, "Invalid e-mail")); y = y + elementHeight + 1
invalidEmailLabel.isHidden = true
-- Создаем callback-функцию, вызывающуюся после ввода текста и проверяющую корректность введенного адреса
emailTextBox.onInputFinished = function(text)
	invalidEmailLabel.isHidden = text:match("%w+@%w+%.%w+") and true or false
	mainContainer:draw()
	buffer.draw()
end
-- Создаем поле для ввода пароля
mainContainer:addChild(GUI.inputTextBox(x, y, elementWidth, elementHeight, 0xEEEEEE, 0x777777, 0xEEEEEE, 0x2D2D2D, nil, "Password", false, "*", nil, nil)); y = y + elementHeight + 1

-- Добавляем малоприметную кнопку для закрытия программы
mainContainer:addChild(GUI.button(mainContainer.width, 1, 1, 1, 0x002440, 0xEEEEEE, 0x002440, 0xAAAAAA, "X")).onTouch = function()
	mainContainer:stopEventHandling()
	buffer.clear(0x0)
	buffer.draw(true)
end

-- Добавляем кнопку для логина
mainContainer:addChild(GUI.button(x, y, elementWidth, elementHeight, 0x666DFF, 0xEEEEEE, 0xEEEEEE, 0x666DFF, "Login")).onTouch = function()
	-- Код, выполняемый при успешном логине
end

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

Результат:

enter image description here

enter image description here

enter image description here

Практический пример #3

Для демонстрации возможностей библиотеки предлагаю создать собственный виджет с нуля. К примеру, панель, реагирующую на клики мыши, позволяющую рисовать на ней произвольным цветом по аналогии со школьной доской.

local buffer = require("doubleBuffering")
local GUI = require("GUI")

---------------------------------------------------------------------

-- Создаем полноэкранный контейнер
local mainContainer = GUI.fullScreenContainer()

-- Создаем метод-обработчик событий для нашего виджета
-- Грамотнее будет вынести его создание вне функции-конструктора, дабы не засорять память
local function myWidgetEventHandler(mainContainer, object, eventData)
	if eventData[1] == "touch" or eventData[1] == "drag" then
		local x, y = eventData[3] - object.x + 1, eventData[4] - object.y + 1
		object.pixels[y] = object.pixels[y] or {}
		object.pixels[y][x] = eventData[5] == 0 and true or nil
		
		mainContainer:draw()
		buffer.draw()
	end
end

-- Создаем метод, возвращающий наш виджет
local function createMyWidget(x, y, width, height, backgroundColor, paintColor)
	-- Наследуемся от GUI.object, дополняем его параметрами цветов и пиксельной карты
	local object = GUI.object(x, y, width, height)
	object.colors = {background = backgroundColor, paint = paintColor}
	object.pixels = {}
	
	-- Реализуем метод отрисовки виджета
	object.draw = function(object)
		-- Рисуем подложку цветом фона виджета
		buffer.square(object.x, object.y, object.width, object.height, object.colors.background, 0x0, " ")
		
		-- Перебираем пиксельную карту, отрисовывая соответствующие пиксели в экранный буфер
		for y = 1, object.height do
			for x = 1, object.width do
				if object.pixels[y] and object.pixels[y][x] then
					buffer.set(object.x + x - 1, object.y + y - 1, object.colors.paint, 0x0, " ")
				end
			end
		end
	end

	object.eventHandler = myWidgetEventHandler

	return object
end

-- Добавляем темно-серую панель в контейнер
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))
-- Создаем экземпляр виджета-рисовалки и добавляем его в контейнер
mainContainer:addChild(createMyWidget(2, 2, 32, 16, 0x3C3C3C, 0xEEEEEEE))

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

При нажатии на левую кнопку мыши в нашем виджете устанавливается пиксель указанного цвета, а на правую - удаляется.

enter image description here

Для разнообразия модифицируем код, создав несколько виджетов со случайными цветами:

local x = 2
for i = 1, 5 do
	mainContainer:addChild(createMyWidget(x, 2, 32, 16, math.random(0x0, 0xFFFFFF), math.random(0x0, 0xFFFFFF)))
	x = x + 34
end

В результате получаем 5 индивидуальных экземпляров виджета рисования:

enter image description here

Как видите, в создании собственных виджетов нет совершенно ничего сложного, главное - обладать информацией по наиболее эффективной работе с библиотекой.

Практический пример #4

Предлагаю немного попрактиковаться в использовании layout. В качестве примера создадим контейнер-окно с четырьмя кнопками, изменяющими его размеры. Вы убедитесь, что нам ни разу не придется вручную считать координаты.

local buffer = require("doubleBuffering")
local image = require("image")
local GUI = require("GUI")

------------------------------------------------------------------------------------------

-- Создаем полноэкранный контейнер, добавляем темно-серую панель
local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))

-- Добавляем в главный контенер другой контейнер, который и будет нашим окошком
local window = mainContainer:addChild(GUI.container(2, 2, 80, 25))
-- Добавляем в контейнер-окно светло-серую фоновую панель
local backgroundPanel = window:addChild(GUI.panel(1, 1, window.width, window.height, 0xDDDDDD))
-- Добавляем layout с сеткой 3х1 и такими же размерами, как у окна
local layout = window:addChild(GUI.layout(1, 1, window.width, window.height, 3, 1))

-- В ячейку 2х1 добавляем загруженное изображение с лицом Стива
layout:setCellPosition(2, 1, layout:addChild(GUI.image(1, 1, image.load("/MineOS/System/Icons/Steve.pic"))))
-- Туда же добавляем слайдер, с помощью которого будем регулировать изменение размера окна
local slider = layout:setCellPosition(2, 1, layout:addChild(GUI.slider(1, 1, 30, 0x0, 0xAAAAAA, 0x1D1D1D, 0xAAAAAA, 1, 30, 10, true, "Изменять размер на: ", "px")))
-- В ячейке 2х1 задаем вертикальную ориентацию объектов и расстояние между ними в 1 пиксель
layout:setCellDirection(2, 1, GUI.directions.vertical)
layout:setCellSpacing(2, 1, 1)

-- Cоздаем функцию, изменяющую размер окна на указанную величину
local function resizeWindow(horizontalOffset, verticalOffset)
    window.width, backgroundPanel.width, layout.width = window.width + horizontalOffset, backgroundPanel.width + horizontalOffset, layout.width + horizontalOffset
    window.height, backgroundPanel.height, layout.height = window.height + verticalOffset, backgroundPanel.height + verticalOffset, layout.height + verticalOffset

    mainContainer:draw()
    buffer.draw()
end

-- В ячейку 3х1 добавляем 4 кнопки с назначенными функциями по изменению размера окна
layout:setCellPosition(3, 1, layout:addChild(GUI.adaptiveButton(1, 1, 3, 0, 0xFFFFFF, 0x000000, 0x444444, 0xFFFFFF, "Ниже"))).onTouch = function()
    resizeWindow(0, -math.floor(slider.value))
end
layout:setCellPosition(3, 1, layout:addChild(GUI.adaptiveButton(1, 1, 3, 0, 0xFFFFFF, 0x000000, 0x444444, 0xFFFFFF, "Выше"))).onTouch = function()
    resizeWindow(0, math.floor(slider.value))
end
layout:setCellPosition(3, 1, layout:addChild(GUI.adaptiveButton(1, 1, 3, 0, 0xFFFFFF, 0x000000, 0x444444, 0xFFFFFF, "Уже"))).onTouch = function()
    resizeWindow(-math.floor(slider.value), 0)
end
layout:setCellPosition(3, 1, layout:addChild(GUI.adaptiveButton(1, 1, 3, 0, 0x3392FF, 0xFFFFFF, 0x444444, 0xFFFFFF, "Шире"))).onTouch = function()
    resizeWindow(math.floor(slider.value), 0)
end

-- В ячейке 3x1 задаем горизонтальную ориентацию кнопок и расстояние между ними в 2 пикселя
layout:setCellDirection(3, 1, GUI.directions.horizontal)
layout:setCellSpacing(3, 1, 2)
-- Далее устанавливаем выравнивание кнопок по правому нижнему краю, а также отступ от краев выравнивания в 2 пикселя по ширине и 1 пиксель по высоте
layout:setCellAlignment(3, 1, GUI.alignment.horizontal.right, GUI.alignment.vertical.bottom)
layout:setCellMargin(3, 1, 2, 1)

------------------------------------------------------------------------------------------

mainContainer:draw()
buffer.draw(true)
mainContainer:startEventHandling()

В результате получаем симпатичное окошко с четырьмя кнопками, автоматически расположенными в его правой части.

Если несколько раз нажать на кнопку "Шире" и "Выше", то окошко растянется, однако все объекты останутся на законных местах. Причем без каких-либо хардкорных расчетов вручную:

Imgur

Практический пример #5

Для демонстрации работы с анимациями привожу исходный код, позволяющий с абсолютного нуля создать виджет switch и добавить к нему анимацию перемещения.


-- Подключаем необходимые библиотеки
local color = require("color")
local buffer = require("doubleBuffering")
local GUI = require("GUI")

------------------------------------------------------------------------------------------

-- Создаем функцию, отрисовывающую свитч в экранный буфер
local function switchDraw(switch)
	local bodyX = switch.x + switch.width - switch.bodyWidth
	-- Рисуем текст свитча
	buffer.text(switch.x, switch.y, switch.colors.text, switch.text)
	-- Рисуем базовую фоновую подложку пассивного оттенка
	buffer.square(bodyX, switch.y, switch.bodyWidth, 1, switch.colors.passive, 0x0, " ")
	buffer.text(bodyX + switch.bodyWidth, switch.y, switch.colors.passive, "⠆")
	-- Рисуем подложку активного оттенка
	buffer.text(bodyX - 1, switch.y, switch.colors.active, "⠰")
	buffer.square(bodyX, switch.y, switch.pipePosition - 1, 1, switch.colors.active, 0x0, " ")
	-- Рисуем "пимпочку" свитча
	buffer.text(bodyX + switch.pipePosition - 2, switch.y, switch.colors.pipe, "⠰")
	buffer.square(bodyX + switch.pipePosition - 1, switch.y, 2, 1, switch.colors.pipe, 0x0, " ")
	buffer.text(bodyX + switch.pipePosition + 1, switch.y, switch.colors.pipe, "⠆")
end

-- Создаем функцию-обработчик событий, вызываемую при клике на свитч
local function switchEventHandler(mainContainer, switch, eventData)
	if eventData[1] == "touch" then
		-- Изменяем "состояние" свитча на противоположное и
		-- создаем анимацию, плавно перемещающую "пимпочку"
		switch.state = not switch.state
		switch:addAnimation(
			-- В качестве обработчика кадра анимации создаем функцию,
			-- устанавливающую позицию "пимпочки" в зависимости от текущей
			-- завершенности анимации.  Помним также, что animationPosition всегда
			-- находится в диапазоне [0.0; 1.0]. Если состояние свитча имеет значение false,
			-- то мы попросту инвертируем значение позиции анимации, чтобы та визуально
			-- проигрывалась в обратную сторону.
			function(mainContainer, switch, animation)
				if switch.state then
					switch.pipePosition = math.round(1 + animation.position * (switch.bodyWidth - 2))
				else	
					switch.pipePosition = math.round(1 + (1 - animation.position) * (switch.bodyWidth - 2))
				end
			end,
			-- В качестве метода .onFinish создаем функцию, удаляющую анимацию по ее завершению
			function(mainContainer, switch, animation)
				animation:delete()
			end
		-- Запускаем созданную анимацию с указанным интервалом
		):start(switch.animationDuration)
	end
end

-- Создаем объект свитча, наследуясь от GUI.object и заполняя его необходимыми свойствами
local function newSwitch(x, y, totalWidth, bodyWidth, activeColor, passiveColor, pipeColor, textColor, text, switchState)
	local switch = GUI.object(x, y, totalWidth, 1)

	switch.bodyWidth = bodyWidth
	switch.colors = {
		active = activeColor,
		passive = passiveColor,
		pipe = pipeColor,
		text = textColor
	}
	switch.text = text
	switch.state = switchState
	switch.pipePosition = switch.state and switch.bodyWidth - 1 or 1
	switch.animationDuration = 0.3	

	switch.draw = switchDraw
	switch.eventHandler = switchEventHandler

	return switch
end

------------------------------------------------------------------------------------------

-- Создаем полноэкранный контейнер и добавляем в него темно-серую фоновую панель
local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D))

-- Создаем гигантское поле из свитчей. Для начала указываем желаемое количество свитчей,
-- а затем зависимые от количество параметры - такие как оттенок по HSB-палитре,
-- координаты и длительность анимации
local count = 168
local hue = 0
local hueStep = 360 / count
local x, y = 3, 2
local animationDurationMin = 0.3
local animationDurationMax = 0.3
local animationDuration = animationDurationMin
local animationDurationStep = (animationDurationMax - animationDurationMin) / count

-- Добавляем в главный контейнер указанное число свитчей
for i = 1, count do
	local switchColor = color.HSBToHEX(hue, 100, 100)
	local switch = mainContainer:addChild(
		newSwitch(
			x,
			y,
			19,
			7,
			switchColor,
			0x1D1D1D,
			0xEEEEEE,
			switchColor,
			"Cвитч " .. i .. ":",
			math.random(2) == 2
		)
	)

	switch.animationDuration = animationDuration
	animationDuration = animationDuration + animationDurationStep

	hue = hue + hueStep
	y = y + switch.height + 1
	if y >= mainContainer.height then
		x, y = x + switch.width + 3, 2
	end
end

-- Отрисовываем содержимое главного контейнера в экранный буфер и выводим результат на экран
mainContainer:draw()
buffer.draw(true)
-- Запускаем обработку событий главного контейнера
mainContainer:startEventHandling()

Результат:

Imgur