Перейти к содержимому
ECS

Библиотека пользовательского интерфейса "GUI"

Рекомендуемые сообщения

Попробовал эту либу. Торжественно заявляю, что она невероятно охрененная. Что самое охрененное - она юзает doubleBuffering, благодаря чему я смог её заюзать даже в середине написания проги, когда любая другая либа бы дала сбой. Очень мне понравился набор компонентов, а также возможность на изи добавить свои.

 

Пожелания, сформировавшиеся во время использования либы:

  • Метод :destroy для всех объектов либы, рекурсивно удаляющий объект и его дочерние. Хотя, мб, он и есть, но я не заметил.
  • Поддержка Lua 5.3. Решается банальным пиханием math.floor(var + 0.5) перед битовыми операциями. Насколько я помню, пришлось пропатчить всякие методы типа HEXtoRGB в либе color.
  • Ресайз лейаута. Сейчас там строки и столбцы забиты при создании, что не особо удобно, когда, например, объект лейаута передаётся в какой-нибудь метод, общий для нескольких классов, для изменения. Пока решается прибитием гвоздями контейнера посередине, созданием новго контейнера в оставшемся месте и передаче уже его в метод, который может там сам добавить лейауты, сколько влезет.
  • Скроллинг в комбобокс. У меня просто крашилась прога, когда размер комбобокса превышал размер экрана.
  • Не трогать папку /lib совсем. Вместо этого класть все либы в /usr/lib. Это касается инсталлятора. Либа сериализации, например, затирала системную, из-за чего все программы, которые её юзали (а их было довольно дофига), просто крашились. Даже lua. Вряд ли так было сделано специально, но чтобы не случалось такого в дальнейшем, лучше всё же перетащить.
  • Я бы также подумал над подключением репы с прогами к OpenPrograms: создать файл programs.cfg, прописать пакеты и пути к файлам, а затем тыкнуть Vexatos, чтобы он добавил репу в список. Я, например, пишу в репу на OpenPrograms свои программы и стараюсь избегать использования всех либ, которые огромные, но не присутствуют в OpenPrograms. Потому что, например, моя программка - это 40 килобайт кода, а либы, которые приходится бандлить вместе с нею, - это 160 килобайт. На каждую прогу тянуть по 160к килобайт - это слишком.

Поделиться сообщением


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

  • Метод удаления дочерних объектов имеется - object:delete()
  • Поддержка Lua 5.3 также имеется в новой версии, однако учти, что округление катастрофически снижает производительность в тяжеловесных приложениях при расчете прозрачности и подобных операциях. Поэтому я лично остаюсь на шустренькой 5.2
  • Ресайз лейаута также реализован в новой версии - layout:setGridSize(int rowCount, int columnCount)
  • Скроллинг сделаю чуть позже, а баг с экраном пофикшен, благодарочка
Теперь что касается либ: крайне странно, что каким-то образом у тебя затронулась либа сериализации: инсталлер ее не устанавливает и никаким косвенным образом не затрагивает, да и все устанавливаемые либы имеют названия, не пересекающиеся с системными, что вдвойне удивительно.

 

Далее об OpenPrograms - нет. Просто нет. Категорически-критичное "нет". Тысячу и один раз натыкался на проблемы, созданные авторами ОС и от меня не зависящие, тысячу и одну ночь ждал мержа фиксов, залитых к ним в пулл реквестах - и ни одного слова благодарности в ответ не получил. Нет, нет, нет... связываться с этим дерьмом себе дороже, а нервишки надо беречь. Только через свой инсталлер или вручную по ссылкам, только так. 

Изменено пользователем ECS

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Ну это, конечно, относилось не конкретно к этой либе, а к инсталлятору MineOS в целом - ребята ставили, а оказывалось, что либа сериализации затиралась. Я, как говорил, уверен, что это не было сделано специально, но иногда можно не заметить.

 

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

Поделиться сообщением


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

 

А, тогда понятно. В майноси чутка иная сериализация - вот и крашится, угу.11

 

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

 

Когда-нибудь, когда горы полетят по ветру, как опавшие листья... возможно, появится мыслишка с этим поколупаться. Но не сейчас точно. Чертовы Вексатосы!

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

 

  • Поддержка Lua 5.3 также имеется в новой версии, однако учти, что округление катастрофически снижает производительность в тяжеловесных приложениях при расчете прозрачности и подобных операциях. Поэтому я лично остаюсь на шустренькой 5.2

 

Может заменить округление на целочисленное деление на 1?

10мультов операций деления на 1 и округления вниз

b8197d28be2e27a17c035c4281f0baa2.png

 

 

iterCount = 10000000
print(iterCount, 'cycles')

startTime = os.clock()
for i=1,iterCount do
a=1.1//1
end
print('1.1//1:', os.clock()-startTime)

startTime = os.clock()
for i=1,iterCount do
a=math.floor(1.1)
end
print('math.floor(1.1):', os.clock()-startTime)

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Может заменить округление на целочисленное деление на 1?

10мультов операций деления на 1 и округления вниз

b8197d28be2e27a17c035c4281f0baa2.png

 

 

iterCount = 10000000
print(iterCount, 'cycles')

startTime = os.clock()
for i=1,iterCount do
a=1.1//1
end
print('1.1//1:', os.clock()-startTime)

startTime = os.clock()
for i=1,iterCount do
a=math.floor(1.1)
end
print('math.floor(1.1):', os.clock()-startTime)

 

 

 

Ага, спасибо. Я вообще не в курсе был про вариант целочисленного деления с двумя слешами в 5.3, заменим в ближайшее время

 

UPD: Существенной разницы при отрисовке сложных сцен не заметно, но сам факт приятен

Изменено пользователем ECS

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Библиотека обновлена. Исправлены мелкие баги, а также добавлено несколько вкусных плюшек:

 

• Появилась возможность создания автоматических анимаций виджетов:

 

f5aO73U.gif

 

• Контекстное меню заимело фичу скроллинга контента, а также поддержку вложенных контекстных меню:

 

A9NCEdc.gif

 

• Значительно улучшен GUI.layout, заимев поддержку абсолютного и релятивного размеров ячеек, возможность выравнивания и отступов по произвольному краю, возможность установки расстояния между элементами, а также фиттинга элементов по размеру ячейки:

 

qf91PuM.png

 

C2TWOJ7.png?1

 

• Появились два дополнительных варианта отрисовки GUI.button, а также их adaptive-аналоги:

 

9zZvR6g.gif

 

• Появился виджет GUI.brailleCanvas, позволяющий быстро и просто отрисовывать мелкую графику при помощи шрифта Брайля:

 

3Oq1nzY.png

Изменено пользователем ECS

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Мини-обновление: добавлен виджет GUI.filesystemChooser для удобной навигации по файловой системе и выбора файлов/папок пользователем. Также поддерживаются фильтры расширений файлов:

 

Dg9bBZu.gif

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Вопрос к автору темы, как с этой либой делать что либо в цикле?

Возможно я просто немного тупой, но у меня цикл не работает

local component = require("component")
local term = require("term")
local event = require("event")
local gpu = component.gpu 
local computer = require('computer')
local m = component.modem 
local screen = component.screen
local rs = component.redstone
local buffer = require("doubleBuffering")
local GUI = require("GUI")

local gray = 0x262525 -- серый цвет
local white = 0xffffff -- белый цвет
local button2 = 0x00d4ff -- цвет кнопки
local button1 = 0x00a9ff -- цвет нажатия кнопки
local black = 0x000000 -- черный
local alarmbus = 1
local signalbus = 4

m.open(21) -- Открываем порт для управления
term.clear()
gpu.setResolution(74,50)

local function onoffdisplay()
if rs.getBundledInput(alarmbus,15) > 0 then
	screen.turnOn()
	else
	screen.turnOff()
end
end

local mainContainer = GUI.fullScreenContainer()
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, white))
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, 3, gray))
mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, 3, gray))
mainContainer:addChild(GUI.label(35, 2, 5, 1, white, os.date('%H:%M')))

mainContainer:addChild(GUI.roundedButton(2, 6, 12, 5, button1, white, button2, white, 'Lights')).onTouch = function()
m.broadcast(27,'lightsOnOff')
end

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

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

В общем если добавить в конец

while true do
onoffdisplay()
end

То компьютер все равно не реагирует на изменения редстоуна

Просто решил использовать эту либу, а с ней у меня циклы почему-то не работают

Возможно есть какие-то ограничения, или что-то типо того?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

 

 

local screenTimer = event.timer(0.1, onoffdisplay, math.huge)
main:startEventHandling()
event.cancel(screenTimer)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

У меня вопрос по виджету brailleCanvas, точнее по его функции setint x, int y, boolean state, int color )

Какую информационную нагрузку несет значение параметра color в случае, когда state=false?

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

 

И еще: 

Какой есть способ выхода из container:startEventHandling() ?

Неужели Ctrl + Alt + C единственный способ выхода из программы?

Изменено пользователем Zer0Galaxy

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

 

 

Какую информационную нагрузку несет значение параметра color в случае, когда state=false?

 

Параметр state отвечает за непосредственное отображение брайль-пикселя, т.е. будет ли вообще этот брайль-пиксель рисоваться на экране. А color - это цвет текущей группы из 2x4 брайль-пикселей (обычный цвет текста пикселя gpu)

 

 

 

Какой есть способ выхода из container:startEventHandling() ?

 

Обижаешь, имеются методы container:stopEventHandling() и container:returnData(...). По сути первый вызывает второй без аргументов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Параметр state отвечает за непосредственное отображение брайль-пикселя, т.е. будет ли вообще этот брайль-пиксель рисоваться на экране. А color - это цвет текущей группы из 2x4 брайль-пикселей (обычный цвет текста пикселя gpu)

Это я, в общем то, понял из твоей замечательной документации (не сарказм, дока реально отпадная) .  Но вопрос мой о другом. Вот нарисовал я на холсте какой-то рисунок и хочу его часть или даже весь стереть. Если весь, то вопросов нет - стираю попиксельно любым цветом (хотя функция brailleCanvas:clear не помешала бы).

 

А если хочу стереть, допустим, только ранее прочерченный отрезок так, что бы он принял цвет фона, но пиксели пересекающегося с ним отрезка не изменили свой цвет? Мне нужно либо каким-то образом извлекать информацию о цвете установленных пикселей из холста, чего виджет не позволяет. Либо хранить эту информацию еще где то. Хотя логика подсказывает, что сбросить пиксель можно вообще ничего не зная о его цвете.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

 

Понял, не вопрос, сейчас выкачу обновление. Добавил методы get/fill/clear, а также изменил set: если аргумент color не указывается, то цвет изменяться не будет. Таким образом можно без проблем "стирать" ранее установленные пиксели, установив их state на false, сохраняя при этом цвет остальных в группе. А, ну и благодарочка за теплые слова о доке, да <3

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

У меня вопрос, а как можно менять контейнеры кнопкой?

Допустим в одном контейнере куча кнопок и какое нибудь изображение, в другом контейнере список, в третьем еще что нибудь, и т.д

Как эти контейнеры можно менять кнопкой? То-есть сначала остановить слушание эвентов, очистить буфер, загрузить новый контейнер, запустить слушание эвентов и отрисовать этот контейнер? или не так?

 

И еще, а можно ли как нибудь в либе организовать изменение размера текста? Конечно можно уменьшить разрешение, и тогда типо текст будет больше, но без изменения разрешения, допустим задействуя под символ несколько пикселей 

Изменено пользователем Koteyk0o

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@@Koteyk0o, самый скучный способ решения проблемы - это установить атрибут .hidden = true для тех контейнеров, отрисовка которых не требуется. Этот вариант хорош, если необходимо сохранять состояние объектов в каждом контейнере - к примеру, текст, введенный пользователем. Если же сохранение данных не критично, то можно пойти другим путем, создав единый контейнер для содержимого, и при переключении кнопок/вкладок/etc очищать его, заполняя нужным содержимым. Есть даже готовый пример для такой ситуации:

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

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

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

-- Создаем контейнер, симулирующий окно
local window = mainContainer:addChild(GUI.container(3, 2, 80, 25))
-- Добавляем в окно панель вкладок
local tabBar = window:addChild(GUI.tabBar(1, 1, window.width, 3, 2, 0, 0x2D2D2D, 0xF0F0F0, 0xF0F0F0, 0x2D2D2D))
-- Добавляем также фоновую панель
local panel = window:addChild(GUI.panel(1, tabBar.height + 1, window.width, window.height - tabBar.height, 0xF0F0F0))
-- Добавляем лейаут, в котором будет находиться содержимое открытой вкладки
local contentLayout = window:addChild(GUI.layout(panel.localX, panel.localY, panel.width, panel.height, 1, 1))

-- Функция очищает содержимое лейаута, вызывает аргумент как функцию и отрисовывает открывшуюся вкладку на экране
local function onTabSwitched(method)
  contentLayout:deleteChildren()
  method()
  mainContainer:draw()
  buffer.draw()
end

-- При клике на каждую вкладку лейаут будет заполняться индивидуальным содержимым. В данном примере это генератор случайных чисел
tabBar:addItem("Рандомайзер").onTouch = function()
  onTabSwitched(function()
    local label = contentLayout:addChild(GUI.label(1, 1, 36, 1, 0x2D2D2D, "")):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top)
    local button = contentLayout:addChild(GUI.button(1, 1, 32, 3, 0xBBBBBB, 0xFFFFFF, 0x999999, 0xFFFFFF, "Сгенерировать"))
    button.onTouch = function()
      label.text = "Рандомное число: " .. math.random(100)
      
      mainContainer:draw()
      buffer.draw()
    end

    button.onTouch()
  end)
end

-- При нажатии на вторую вкладку будет открываться панель для рисования
tabBar:addItem("Рисовалка").onTouch = function()
  onTabSwitched(function()
    -- Тут мы создаем с нуля простенький виджет-холст, реагирующий на клики мыши, подробности см. в документации
    local canvas = contentLayout:addChild(GUI.object(1, 1, contentLayout.width - 4, contentLayout.height - 4))
    local colorSelector = contentLayout:addChild(GUI.colorSelector(1, 1, 20, 1, 0x0, "Выберите цвет"))

    local pixels = {}
    for y = 1, canvas.height do
      pixels[y] = {}
      for x = 1, canvas.width do
        pixels[y][x] = false
      end
    end

    canvas.draw = function()
      buffer.square(canvas.x, canvas.y, canvas.width, canvas.height, 0xFFFFFF, 0x0, " ")
      for y = 1, #pixels do
        for x = 1, #pixels[y] do
          if pixels[y][x] then
            buffer.set(canvas.x + x - 1, canvas.y + y - 1, pixels[y][x], 0x0, " ")
          end
        end
      end
    end

    canvas.eventHandler = function(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
        pixels[y][x] = eventData[5] == 1 and false or colorSelector.color

        mainContainer:draw()
        buffer.draw()
      end
    end
  end)
end

-- Остальные вкладки заполняем на свое усмотрение
tabBar:addItem("Вкладка 3").onTouch = function()
  onTabSwitched(function()
    -- Заполнить контейнер каким-нибудь содержимым
  end)
end

tabBar:addItem("Вкладка 4").onTouch = function()
  onTabSwitched(function()
    -- Заполнить контейнер каким-нибудь содержимым
  end)
end

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

-- Очищаем содержимое буфера
buffer.flush()
-- Открываем первую вкладку
tabBar:getItem(1).onTouch()
-- Начинаем обработку событий
mainContainer:startEventHandling()
В результате получится окошко с переключаемыми вкладками (надеюсь, не суть важно - кнопки или вкладки, один хрен, имхо):

 

Aia7hWS.gif

 

То-есть сначала остановить слушание эвентов, очистить буфер, загрузить новый контейнер, запустить слушание эвентов и отрисовать этот контейнер?

 

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

 

И еще, а можно ли как нибудь в либе организовать изменение размера текста? Конечно можно уменьшить разрешение, и тогда типо текст будет больше, но без изменения разрешения, допустим задействуя под символ несколько пикселей

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

Изменено пользователем ECS

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Присоединяйтесь к обсуждению

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

Гость
Ответить в тему...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.


×
×
  • Создать...