This commit is contained in:
Igor Timofeev 2016-10-29 22:51:21 +03:00
parent 1a37616ffe
commit 9ffa89a081
22 changed files with 1292 additions and 96 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -531,6 +531,69 @@
version=1.0,
},
----------------------------------------------------- Приложения --------------------------------------------------------------------------
{
name="MineOS/Applications/SmartHouse",
url="IgorTimofeev/OpenComputers/master/Applications/SmartHouse/SmartHouse.lua",
type="Application",
icon="IgorTimofeev/OpenComputers/master/Applications/SmartHouse/Icon.pic",
createShortcut="desktop",
version=1.21,
resources={
{
name="Modules/redstone/Icon.pic",
url="IgorTimofeev/OpenComputers/master/Applications/SmartHouse/Modules/redstone/Icon.pic",
},
{
name="Modules/redstone/Main.lua",
url="IgorTimofeev/OpenComputers/master/Applications/SmartHouse/Modules/redstone/Main.lua",
},
--
{
name="Modules/mfsu/Icon.pic",
url="IgorTimofeev/OpenComputers/master/Applications/SmartHouse/Modules/mfsu/Icon.pic",
},
{
name="Modules/mfsu/Main.lua",
url="IgorTimofeev/OpenComputers/master/Applications/SmartHouse/Modules/mfsu/Main.lua",
},
--
{
name="Modules/screen/Icon.pic",
url="IgorTimofeev/OpenComputers/master/Applications/SmartHouse/Modules/screen/Icon.pic",
},
{
name="Modules/screen/Main.lua",
url="IgorTimofeev/OpenComputers/master/Applications/SmartHouse/Modules/screen/Main.lua",
},
--
{
name="Modules/homePC/Icon.pic",
url="IgorTimofeev/OpenComputers/master/Applications/SmartHouse/Modules/homePC/Icon.pic",
},
{
name="Modules/homePC/Main.lua",
url="IgorTimofeev/OpenComputers/master/Applications/SmartHouse/Modules/homePC/Main.lua",
},
--
{
name="Modules/motion_sensor/Icon.pic",
url="IgorTimofeev/OpenComputers/master/Applications/SmartHouse/Modules/motion_sensor/Icon.pic",
},
{
name="Modules/motion_sensor/Main.lua",
url="IgorTimofeev/OpenComputers/master/Applications/SmartHouse/Modules/motion_sensor/Main.lua",
},
--
{
name="Modules/reactor/Icon.pic",
url="IgorTimofeev/OpenComputers/master/Applications/SmartHouse/Modules/reactor/Icon.pic",
},
{
name="Modules/reactor/Main.lua",
url="IgorTimofeev/OpenComputers/master/Applications/SmartHouse/Modules/reactor/Main.lua",
},
},
},
{
name="MineOS/Applications/VK",
url="IgorTimofeev/OpenComputers/master/Applications/VK/VK.lua",

BIN
Applications/.DS_Store vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,35 @@
local module = {
allowSignalConnections = false,
updateWhenModuleDetailsIsHidden = false,
}
-------------------------------------------------------------------------------------------------------------------------------------------------------
-- This method is called once during module initialization
function module.start(moduleContainer)
end
-- This method is called on each frame update (every second by default), but only if module details is not hidden or updateWhenModuleDetailsIsHidden == true
function module.update(moduleContainer, eventData)
end
-- This method is called when a this module receives virtual signal from the another module, but only if field allowSignalConnections == true
function module.onSignalReceived(moduleContainer, ...)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------
return module

Binary file not shown.

View File

@ -0,0 +1,35 @@
local module = {
allowSignalConnections = false,
updateWhenModuleDetailsIsHidden = false,
}
-------------------------------------------------------------------------------------------------------------------------------------------------------
-- This method is called once during module initialization
function module.start(moduleContainer)
end
-- This method is called on each frame update (every second by default), but only if module details is not hidden or updateWhenModuleDetailsIsHidden == true
function module.update(moduleContainer, eventData)
end
-- This method is called when a this module receives virtual signal from the another module, but only if field allowSignalConnections == true
function module.onSignalReceived(moduleContainer, ...)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------
return module

Binary file not shown.

View File

@ -0,0 +1,41 @@
local module = {
allowSignalConnections = false,
updateWhenModuleDetailsIsHidden = false,
}
-------------------------------------------------------------------------------------------------------------------------------------------------------
-- This method is called once during module initialization
function module.start(moduleContainer)
local x, y = 2, moduleContainer.children[#moduleContainer.children].localPosition.y + 2
moduleContainer.capaticyLabel = moduleContainer:addLabel("capaticyLabel", x, y, moduleContainer.width - 2, 1, 0xDDDDDD, "Capacity"):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top); y = y + 1
moduleContainer.chart = moduleContainer:addChart("chart", x, y, moduleContainer.width - 2, math.floor(moduleContainer.width - 2) / 2, 0xFFFFFF, 0x999999, 0xFFDB40, "t", "%", 0, 100, {})
end
-- This method is called on each frame update (every second by default), but only if module details is not hidden or updateWhenModuleDetailsIsHidden == true
function module.update(moduleContainer, eventData)
table.insert(moduleContainer.chart.values, math.ceil(moduleContainer.componentProxy.getStored() / moduleContainer.componentProxy.getCapacity() * 100))
if #moduleContainer.chart.values > moduleContainer.chart.width - 1 then
table.remove(moduleContainer.chart.values, 1)
end
end
-- This method is called when a this module receives virtual signal from the another module, but only if field allowSignalConnections == true
function module.onSignalReceived(moduleContainer, ...)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------
return module

Binary file not shown.

View File

@ -0,0 +1,53 @@
local module = {
allowSignalConnections = false,
updateWhenModuleDetailsIsHidden = true,
}
-------------------------------------------------------------------------------------------------------------------------------------------------------
local sleepValue = 1
-- This method is called once during module initialization
function module.start(moduleContainer)
local x, y = 1, moduleContainer.children[#moduleContainer.children].localPosition.y + 2
local lines = {limit = 5}
moduleContainer.editWhitelistButton = moduleContainer:addButton("editWhitelistButton", 2, y, moduleContainer.width - 2, 1, 0xDDDDDD, 0x262626, 0xAAAAAA, 0x262626, "Whitelist")
y = y + 2
moduleContainer.sleepSlider = moduleContainer:addHorizontalSlider("sleepSlider", x, y, moduleContainer.width, 0xFFDB80, 0x000000, 0xFFDB40, 0xDDDDDD, 0.5, 2, sleepValue, false, "Sleep: ")
moduleContainer.sleepSlider.onValueChanged = function()
sleepValue = moduleContainer.sleepSlider.value
end
end
-- This method is called on each frame update (every second by default), but only if module details is not hidden or updateWhenModuleDetailsIsHidden == true
function module.update(moduleContainer, eventData)
if eventData[1] == "motion" then
if moduleContainer.componentProxy.address == eventData[2] then
if eventData[6] == "ECS" then
moduleContainer:pushSignal("redstone", "setValue", 15)
os.sleep(sleepValue)
moduleContainer:pushSignal("redstone", "setValue", 0)
end
end
end
end
-- This method is called when a this module receives virtual signal from the another module, but only if field allowSignalConnections == true
function module.onSignalReceived(moduleContainer, ...)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------
return module

Binary file not shown.

View File

@ -0,0 +1,38 @@
local module = {
allowSignalConnections = false,
updateWhenModuleDetailsIsHidden = false,
}
-------------------------------------------------------------------------------------------------------------------------------------------------------
-- This method is called once during module initialization
function module.start(moduleContainer)
local x, y = 2, moduleContainer.children[#moduleContainer.children].localPosition.y + 2
moduleContainer.heatLabel = moduleContainer:addLabel("heatLabel", x, y, moduleContainer.width - 2, 1, 0xDDDDDD, ""); y = y + 1
moduleContainer.outputLabel = moduleContainer:addLabel("outputLabel", x, y, moduleContainer.width - 2, 1, 0xDDDDDD, "")
end
-- This method is called on each frame update (every second by default), but only if module details is not hidden or updateWhenModuleDetailsIsHidden == true
function module.update(moduleContainer, eventData)
moduleContainer.heatLabel.text = "Heat: " .. math.ceil(moduleContainer.componentProxy.getHeat() / moduleContainer.componentProxy.getMaxHeat() * 100) .. "%"
moduleContainer.outputLabel.text = "Output: " .. moduleContainer.componentProxy.getReactorEnergyOutput()
end
-- This method is called when a this module receives virtual signal from the another module, but only if field allowSignalConnections == true
function module.onSignalReceived(moduleContainer, ...)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------
return module

Binary file not shown.

View File

@ -0,0 +1,90 @@
local sides = require("sides")
local module = {
allowSignalConnections = true,
updateWhenModuleDetailsIsHidden = false,
}
-------------------------------------------------------------------------------------------------------------------------------------------------------
local function changeRedstoneState(moduleContainer, state)
local sidesThatWillBeChanged = {}
local comboBoxText = moduleContainer.sidesComboBox.items[moduleContainer.sidesComboBox.currentItem].text
if comboBoxText == "All" then
sidesThatWillBeChanged = {0,1,2,3,4,5}
-- ecs.error("ALLL SIDES YOPTA")
else
sidesThatWillBeChanged = {sides[string.lower(comboBoxText)]}
-- ecs.error("HERE HERE: " .. sides[string.lower(comboBoxText)])
end
for i = 1, #sidesThatWillBeChanged do
moduleContainer.redstoneStates[sidesThatWillBeChanged[i]] = state
moduleContainer.componentProxy.setOutput(sidesThatWillBeChanged[i], state and 15 or 0)
end
end
-- This method is called once during module initialization
function module.start(moduleContainer)
local x, y = 2, moduleContainer.children[#moduleContainer.children].localPosition.y + 2
moduleContainer.redstoneStates = {}
for i = 0, 5 do
local signalStrength = moduleContainer.componentProxy.getOutput(i)
moduleContainer.redstoneStates[i] = signalStrength > 0 and true or false
end
moduleContainer:addLabel("toggleLabel", x, y, moduleContainer.width - 2, 1, 0xDDDDDD, "Signal:")
moduleContainer.signalSwitch = moduleContainer:addSwitch("redstoneSwitch", moduleContainer.width - 6, y, 6, 0xFFDB40, 0xBBBBBB, 0xFFFFFF, false)
moduleContainer.signalSwitch.onStateChanged = function()
changeRedstoneState(moduleContainer, moduleContainer.signalSwitch.state)
end
y = y + 2
moduleContainer.emitOnceButton = moduleContainer:addButton("emitOnceButton", 2, y, moduleContainer.width - 2, 1, 0xDDDDDD, 0x262626, 0xAAAAAA, 0x262626, "Emit once")
moduleContainer.emitOnceButton.onTouch = function()
changeRedstoneState(moduleContainer, true)
os.sleep(0.1)
changeRedstoneState(moduleContainer, false)
moduleContainer.signalSwitch.state = false
end
y = y + 2
moduleContainer:addLabel("sideLabel", x, y, moduleContainer.width - 2, 1, 0xFFFFFF, "Side"):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top)
y = y + 2
moduleContainer.sidesComboBox = moduleContainer:addComboBox("comboBox", x, y, moduleContainer.width - 2, 1, 0xDDDDDD, 0x262626, 0xCCCCCC, 0x262626, {"All", "Up", "Down", "North", "South", "West", "East"})
moduleContainer.sidesComboBox.onItemSelected = function()
local comboBoxText = moduleContainer.sidesComboBox.items[moduleContainer.sidesComboBox.currentItem].text
if comboBoxText == "All" then
moduleContainer.signalSwitch.state = false
else
local side = sides[string.lower(comboBoxText)]
moduleContainer.signalSwitch.state = moduleContainer.redstoneStates[side]
end
end
y = y + 2
end
-- This method is called on each frame update (every second by default), but only if module details is not hidden or updateWhenModuleDetailsIsHidden == true
function module.update(moduleContainer, eventData)
end
-- This method is called when a this module receives virtual signal from the another module, but only if field allowSignalConnections == true
function module.onSignalReceived(moduleContainer, ...)
local data = {...}
if data[1] == "redstone" and data[2] == "setState" and type(data[3]) == "boolean" then
changeRedstoneState(moduleContainer, data[3])
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------
return module

Binary file not shown.

View File

@ -0,0 +1,35 @@
local module = {
allowSignalConnections = false,
updateWhenModuleDetailsIsHidden = false,
}
-------------------------------------------------------------------------------------------------------------------------------------------------------
-- This method is called once during module initialization
function module.start(moduleContainer)
end
-- This method is called on each frame update (every second by default), but only if module details is not hidden or updateWhenModuleDetailsIsHidden == true
function module.update(moduleContainer, eventData)
end
-- This method is called when a this module receives virtual signal from the another module, but only if field allowSignalConnections == true
function module.onSignalReceived(moduleContainer, ...)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------
return module

View File

@ -0,0 +1,503 @@
local libraries = {
sides = "sides",
component = "component",
advancedLua = "advancedLua",
image = "image",
buffer = "doubleBuffering",
keyboard = "keyboard",
GUI = "GUI",
ecs = "ECSAPI",
windows = "windows",
MineOSCore = "MineOSCore",
computer = "computer",
fs = "filesystem",
}
for library in pairs(libraries) do if not _G[library] then _G[library] = require(libraries[library]) end end; libraries = nil
-----------------------------------------------------------------------------------------------------------------------------------
local window
local paths = {}
paths.resources = MineOSCore.getCurrentApplicationResourcesDirectory(),
-- paths.resources = "/SmartHouse/"
paths.modules = paths.resources .. "Modules/"
local colors = {
-- background = image.load("/MineOS/Pictures/Ciri.pic"),
background = 0xDDDDDD,
connectionLines = 0x262626,
devicesBackgroundTransparency = 40,
devicesBackground = 0x0,
devicesButtonBackground = 0xFFFFFF,
devicesButtonText = 0x262626,
devicesInfoText = 0xDDDDDD,
groupsTransparency = nil,
}
local signals = {}
local groups = {}
local modules = {}
local offset = {x = 0, y = 0}
-----------------------------------------------------------------------------------------------------------------------------------
local function loadModule(modulePath)
local success, module = pcall(loadfile(modulePath .. "/Main.lua"))
if success then
module.icon = image.load(modulePath .. "/Icon.pic")
modules[fs.name(modulePath)] = module
else
error("Module loading failed: " .. module)
end
end
local function loadModules()
modules = {}
for file in fs.list(paths.modules) do
local modulePath = paths.modules .. file
if fs.isDirectory(modulePath) then
loadModule(modulePath)
end
end
end
local function highlightDiviceAsSignal(deviceContainer, color)
buffer.square(deviceContainer.x - 1, deviceContainer.y, deviceContainer.width + 2, deviceContainer.height, color)
buffer.text(deviceContainer.x - 1, deviceContainer.y - 1, color, string.rep("", deviceContainer.width + 2))
buffer.text(deviceContainer.x - 1, deviceContainer.y + deviceContainer.height, color, string.rep("", deviceContainer.width + 2))
end
local function createNewGroup(name, color)
table.insert(groups, {
color = color,
name = name,
devices = {}
})
end
local function removeDeviceFromGroup(address)
for groupIndex = 1, #groups do
local deviceIndex = 1
while deviceIndex <= #groups[groupIndex].devices do
if groups[groupIndex].devices[deviceIndex].componentProxy.address == address then
table.remove(groups[groupIndex].devices, deviceIndex)
deviceIndex = deviceIndex - 1
if #groups[groupIndex].devices == 0 then
groups[groupIndex] = nil
return
end
end
deviceIndex = deviceIndex + 1
end
end
end
local function addDeviceToGroup(deviceContainer, name, color)
local groupIndex
for i = 1, #groups do
if groups[i].name == name then groupIndex = i; break end
end
if not groupIndex then
createNewGroup(name, color)
groupIndex = #groups
end
table.insert(groups[groupIndex].devices, deviceContainer)
groups[groupIndex].color = color
end
local function containerPushSignal(container, ...)
for signalIndex = 1, #signals do
if signals[signalIndex].fromDevice == container then
for toDeviceIndex = 1, #signals[signalIndex].toDevices do
if signals[signalIndex].toDevices[toDeviceIndex].module.onSignalReceived then
signals[signalIndex].toDevices[toDeviceIndex].module.onSignalReceived(signals[signalIndex].toDevices[toDeviceIndex], ...)
end
end
end
end
end
local function changeChildrenState(container, state)
for i = 3, #container.children do
container.children[i].hidden = state
end
end
local function createDevice(x, y, componentName, componentProxy, name)
if not modules[componentName] then error("No such module: " .. componentName) end
local container = window:addContainer(name, x, y, 16, 9)
container.name = name
container.module = modules[componentName]
container.componentProxy = componentProxy
container.componentName = componentName
container.detailsIsHidden = true
x, y = 1, 1
local deviceImage = container:addImage("deviceImage", x, y, container.module.icon); y = y + 8
local stateButton = container:addButton("stateButton", 1, y, container.width, 1, colors.devicesButtonBackground, colors.devicesButtonText, colors.devicesButtonText, colors.devicesButtonBackground, "*")
stateButton.onTouch = function()
container.detailsIsHidden = not container.detailsIsHidden
changeChildrenState(container, container.detailsIsHidden)
if container.detailsIsHidden then
stateButton.localPosition.y = container.backgroundPanel.localPosition.y
else
stateButton.localPosition.y = container.backgroundPanel.localPosition.y + container.backgroundPanel.height
end
end
container.backgroundPanel = container:addPanel("backgroundPanel", 1, y, container.width, 1, colors.devicesBackground, colors.devicesBackgroundTransparency)
container:addLabel("nameLabel", 2, y, container.width - 2, 1, 0xFFFFFF, container.name):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top); y = y + 1
container:addLabel("addressLabel", 2, y, container.width - 2, 1, 0x999999, container.componentProxy.address):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top); y = y + 2
container.module.start(container)
container.module.update(container, {})
y = container.children[#container.children].localPosition.y + (container.children[#container.children].height or 0) + 1
container.backgroundPanel.height = container.backgroundPanel.height + (y - container.backgroundPanel.y - 1)
deviceImage.onTouch = function(eventData)
if eventData[5] == 0 then
window.deviceToDrag = container
container:moveToFront()
if keyboard.isShiftDown() then
local x, y = container.x + 8, container.y + 4
signals.fromDevice = container
signals.toPoint = {x = x, y = y}
end
else
local action = GUI.contextMenu(eventData[3], eventData[4], {"Add to group"}, {"Remove from group"}):show()
if action == "Add to group" then
local data = ecs.universalWindow("auto", "auto", 36, 0x262626, true,
{"EmptyLine"},
{"CenterText", ecs.colors.orange, "Add to group"},
{"EmptyLine"},
{"Input", 0xFFFFFF, ecs.colors.orange, "Group #1"},
{"Color", "Group color", 0xAAAAAA},
{"EmptyLine"},
{"Button", {ecs.colors.orange, 0xffffff, "OK"}, {0x999999, 0xffffff, "Cancel"}}
)
if data[3] == "OK" then
addDeviceToGroup(container, data[1], data[2])
end
elseif action == "Remove from group" then
removeDeviceFromGroup(container.componentProxy.address)
end
end
end
local oldDraw = container.draw
container.draw = function()
if container.highlightColor then
highlightDiviceAsSignal(container, container.highlightColor)
end
oldDraw(container)
end
container.pushSignal = containerPushSignal
changeChildrenState(container, container.detailsIsHidden)
return container
end
local function drawConnectionLine(x1, y1, x2, y2, thin, color, cutAfterPixels)
local symbol = ""
if x1 < x2 then
if y1 < y2 then
symbol = thin and "" or ""
else
symbol = thin and "" or ""
end
else
if y1 < y2 then
symbol = thin and "" or ""
else
symbol = thin and "" or ""
end
end
local x, y, counter, xIncrement, yIncrement = x1, y1, 1, (x1 <= x2 and 1 or -1), (y1 <= y2 and 1 or -1)
while true do
local bg = buffer.get(x, y)
buffer.set(x, y, bg, color, thin and "" or "")
x = x + xIncrement
if counter < cutAfterPixels then
counter = counter + 1
else
x = x + xIncrement
end
if x1 <= x2 then
if x >= x2 - 1 then break end
else
if x < x2 + 1 then break end
end
end
buffer.text(x, y, color, symbol)
y = y + yIncrement
while true do
local bg = buffer.get(x, y)
buffer.set(x, y, bg, color, thin and "" or "")
y = y + yIncrement
if counter < cutAfterPixels then
counter = counter + 1
else
y = y + yIncrement
end
if y1 <= y2 then
if y >= y2 then break end
else
if y < y2 then break end
end
end
end
local function moveDevice(container, x, y)
container.localPosition.x, container.localPosition.y = container.localPosition.x + x, container.localPosition.y + y
end
local function moveDevices(x, y)
for i = 1, #window.children do
moveDevice(window.children[i], x, y)
end
end
local function getGroupGeometry(devices)
local geometry = {}
for deviceIndex = 1, #devices do
geometry.x = math.min(geometry.x or devices[deviceIndex].localPosition.x, devices[deviceIndex].localPosition.x)
geometry.y = math.min(geometry.y or devices[deviceIndex].localPosition.y, devices[deviceIndex].localPosition.y)
geometry.xMax = math.max(geometry.xMax or devices[deviceIndex].localPosition.x, devices[deviceIndex].localPosition.x)
geometry.yMax = math.max(geometry.yMax or devices[deviceIndex].localPosition.y, devices[deviceIndex].localPosition.y)
end
local xOffset, yOffset = 2, 2
geometry.width, geometry.height = geometry.xMax - geometry.x + 16 + xOffset * 2, geometry.yMax - geometry.y + 9 + yOffset * 2
geometry.x, geometry.y = geometry.x - xOffset, geometry.y - yOffset - 1
return geometry
end
local function drawGroups()
for groupIndex = 1, #groups do
local groupGeometry = getGroupGeometry(groups[groupIndex].devices)
buffer.square(groupGeometry.x, groupGeometry.y, groupGeometry.width, groupGeometry.height, groups[groupIndex].color)
GUI.label(groupGeometry.x, groupGeometry.y + 1, groupGeometry.width, 1, 0x000000, groups[groupIndex].name):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top):draw()
end
end
local function drawSignals()
local colorPrimary = 0xFF4940
local step = 16
if signals.fromDevice then
drawConnectionLine(
signals.fromDevice.x + 8,
signals.fromDevice.y + 4,
signals.toPoint.x,
signals.toPoint.y,
false, colorPrimary, step
)
signals.fromDevice.highlightColor = colorPrimary
end
for signalIndex = 1, #signals do
--Подсветка подключенных устройств
for toDeviceIndex = 1, #signals[signalIndex].toDevices do
signals[signalIndex].toDevices[toDeviceIndex].highlightColor = colorPrimary
drawConnectionLine(
signals[signalIndex].fromDevice.x + 8,
signals[signalIndex].fromDevice.y + 4,
signals[signalIndex].toDevices[toDeviceIndex].x + 8,
signals[signalIndex].toDevices[toDeviceIndex].y + 4,
false, colorPrimary, step
)
end
-- Подсветка стартового устройства
signals[signalIndex].fromDevice.highlightColor = colorPrimary
end
end
local function createWindow()
window = windows.fullScreen()
-- Создаем главное и неебически важное устройство домашнего писюка
local homePC = createDevice(math.floor(window.width / 2 - 8), math.floor(window.height / 2 - 4), "homePC", component.proxy(computer.address()), "Сервак")
-- Перед отрисовкой окна чистим буфер фоном и перехуячиваем позиции объектов групп
window.onDrawStarted = function()
buffer.clear(colors.background)
drawGroups()
local xPC, yPC = homePC.x + 8, homePC.y + 4
for i = 1, #window.children do
drawConnectionLine(xPC, yPC, window.children[i].x + 8, window.children[i].y + 4, true, colors.connectionLines, math.huge)
end
drawSignals()
end
window.onAnyEvent = function(eventData)
if eventData[1] == "key_down" then
if eventData[4] == 19 then
colors.background = math.random(0x0, 0xFFFFFF)
elseif eventData[4] == 200 then
moveDevices(0, -2)
elseif eventData[4] == 208 then
moveDevices(0, 2)
elseif eventData[4] == 203 then
moveDevices(-4, 0)
elseif eventData[4] == 205 then
moveDevices(4, 0)
end
elseif eventData[1] == "touch" then
window.dragOffset = {x = eventData[3], y = eventData[4]}
elseif eventData[1] == "drag" then
if keyboard.isShiftDown() then
if signals.fromDevice then
signals.toPoint.x, signals.toPoint.y = eventData[3], eventData[4]
end
else
if eventData[5] == 0 and window.deviceToDrag then
window.deviceToDrag.localPosition.x, window.deviceToDrag.localPosition.y = window.deviceToDrag.localPosition.x + (eventData[3] - window.dragOffset.x), window.deviceToDrag.y + (eventData[4] - window.dragOffset.y)
end
end
window.dragOffset = {x = eventData[3], y = eventData[4]}
elseif eventData[1] == "drop" then
if keyboard.isShiftDown() and signals.fromDevice then
--Создаем новый сигнал, если такового еще не существовало
local signalIndex
for i = 1, #signals do
if signals[i].fromDevice == signals.fromDevice then signalIndex = i; break end
end
if not signalIndex then
table.insert(signals, {fromDevice = signals.fromDevice, toDevices = {}})
signalIndex = #signals
end
--Ищем контейнер, на который дропнулось
local container
for i = 1, #window.children do
if window.children[i]:isClicked(eventData[3], eventData[4]) then
container = window.children[i]
break
end
end
-- Если контейнер найден, то
if container then
--Чекаем, принимает ли модуль этого контейнера сигналы, и если принимает, то
if container.module.allowSignalConnections then
--Проверяем, нет ли часом этого устройства в УЖЕ подключенных
local deviceExists = false
for i = 1, #signals[signalIndex].toDevices do
if signals[signalIndex].toDevices[i] == container then deviceExists = true end
end
--И если нет, а также если это устройство не является устройством, от которого ВЕЛОСЬ подключение, то
if not deviceExists and container ~= signals[signalIndex].fromDevice then
table.insert(signals[signalIndex].toDevices, container)
end
else
GUI.error("This device doesn't support virtual signal receiving", {title = {text = "Warning", color = 0xFF5555}})
if #signals[signalIndex].toDevices <= 0 then
signals[signalIndex].fromDevice.highlightColor = nil
signals[signalIndex] = nil
end
end
else
-- А если мы дропнули на пустую точку, то оффаем выделение
if #signals[signalIndex].toDevices <= 0 then
signals[signalIndex].fromDevice.highlightColor = nil
signals[signalIndex] = nil
end
end
end
-- Удаляем временные сигнальные переменные
signals.fromDevice, signals.toPoint = nil, nil
-- Сбрасываем также общее смещение драга
window.dragOffset = nil
window.deviceToDrag = nil
end
for i = 1, #window.children do
if not window.children[i].detailsIsHidden or window.children[i].module.updateWhenModuleDetailsIsHidden then
window.children[i].module.update(window.children[i], eventData)
end
end
window:draw()
end
end
local function getComputerInfo()
local currentComputerAddress = computer.address()
for address, information in pairs(computer.getDeviceInfo()) do
if currentComputerAddress == address then
return information.description
end
end
end
local function refreshComponents()
local devices = {}
for componentAddress, componentName in pairs(component.list()) do
if modules[componentName] then
table.insert(devices, {componentAddress = componentAddress, componentName = componentName})
end
end
local x, y = math.floor(buffer.screen.width / 2 - #devices * 18 / 2 + 1), 2
for i = 1, #devices do
createDevice(x, y, devices[i].componentName, component.proxy(devices[i].componentAddress), devices[i].componentName)
x = x + 18
end
end
-----------------------------------------------------------------------------------------------------------------------------------
buffer.start()
loadModules()
createWindow()
refreshComponents()
window:draw()
window:handleEvents(1)

View File

@ -38,9 +38,9 @@ GUI.colors = {
text = 0xAAAAAA
},
contextMenu = {
background = 0xFFFFFF,
separator = 0xAAAAAA,
default = {
background = 0xFFFFFF,
text = 0x2D2D2D
},
disabled = {
@ -57,7 +57,7 @@ GUI.colors = {
}
}
GUI.contextMenuElementTypes = enum(
GUI.dropDownMenuElementTypes = enum(
"default",
"separator"
)
@ -80,14 +80,16 @@ GUI.objectTypes = enum(
"textBox",
"horizontalSlider",
"switch",
"progressBar"
"progressBar",
"chart",
"comboBox"
)
----------------------------------------- Primitive objects -----------------------------------------
-- Universal method to check if object was clicked by following coordinates
local function isObjectClicked(object, x, y)
if x >= object.x and y >= object.y and x <= object.x + object.width - 1 and y <= object.y + object.height - 1 and not object.disabled and not object.invisible ~= false then return true end
if x >= object.x and y >= object.y and x <= object.x + object.width - 1 and y <= object.y + object.height - 1 and not object.disabled and not object.hidden then return true end
return false
end
@ -163,26 +165,78 @@ end
function GUI.getClickedObject(container, xEvent, yEvent)
local clickedObject, clickedIndex
for objectIndex = #container.children, 1, -1 do
container.children[objectIndex].x, container.children[objectIndex].y = container.children[objectIndex].localPosition.x + container.x - 1, container.children[objectIndex].localPosition.y + container.y - 1
if container.children[objectIndex].children and #container.children[objectIndex].children > 0 then
clickedObject, clickedIndex = GUI.getClickedObject(container.children[objectIndex], xEvent, yEvent)
if clickedObject then break end
elseif not container.children[objectIndex].disableClicking and container.children[objectIndex]:isClicked(xEvent, yEvent) then
clickedObject, clickedIndex = container.children[objectIndex], objectIndex
break
if not container.children[objectIndex].hidden then
container.children[objectIndex].x, container.children[objectIndex].y = container.children[objectIndex].localPosition.x + container.x - 1, container.children[objectIndex].localPosition.y + container.y - 1
if container.children[objectIndex].children and #container.children[objectIndex].children > 0 then
clickedObject, clickedIndex = GUI.getClickedObject(container.children[objectIndex], xEvent, yEvent)
if clickedObject then break end
elseif not container.children[objectIndex].disableClicking and container.children[objectIndex]:isClicked(xEvent, yEvent) then
clickedObject, clickedIndex = container.children[objectIndex], objectIndex
break
end
end
end
return clickedObject, clickedIndex
end
local function checkObjectParentExists(object)
if not object.parent then error("Object doesn't have a parent container") end
end
local function containerObjectIndexOf(object)
checkObjectParentExists(object)
for objectIndex = 1, #object.parent.children do
if object.parent.children[objectIndex] == object then
return objectIndex
end
end
end
-- Move container's object "closer" to our eyes
local function containerObjectMoveForward(object)
local objectIndex = object:indexOf()
if objectIndex < #object.parent.children then
object.parent.children[index], object.parent.children[index + 1] = swap(object.parent.children[index], object.parent.children[index + 1])
end
end
-- Move container's object "more far out" of our eyes
local function containerObjectMoveBackward(object)
local objectIndex = object:indexOf()
if objectIndex > 1 then
object.parent.children[index], object.parent.children[index - 1] = swap(object.parent.children[index], object.parent.children[index - 1])
end
end
-- Move container's object to front of all objects
local function containerObjectMoveToFront(object)
local objectIndex = object:indexOf()
table.insert(object.parent.children, object)
table.remove(object.parent.children, objectIndex)
end
-- Move container's object to back of all objects
local function containerObjectMoveToBack(object)
local objectIndex = object:indexOf()
table.insert(object.parent.children, 1, object)
table.remove(object.parent.children, objectIndex + 1)
end
-- Add any object as children to parent container with specified objectName
local function addObjectToContainer(container, objectType, objectName, object)
object.name = objectName
object.type = objectType
object.parent = container
object.indexOf = containerObjectIndexOf
object.moveToFront = containerObjectMoveToFront
object.moveToBack = containerObjectMoveToBack
object.moveForward = containerObjectMoveForward
object.moveBackward = containerObjectMoveBackward
object.localPosition = {x = object.x, y = object.y}
table.insert(container.children, object)
return object
end
@ -266,18 +320,31 @@ local function addSwitchObjectToContainer(container, objectName, ...)
return addObjectToContainer(container, GUI.objectTypes.switch, objectName, GUI.switch(...))
end
-- Add Chart object to container
local function addChartObjectToContainer(container, objectName, ...)
return addObjectToContainer(container, GUI.objectTypes.chart, objectName, GUI.chart(...))
end
-- Add ComboBox object to container
local function addComboBoxObjectToContainer(container, objectName, ...)
return addObjectToContainer(container, GUI.objectTypes.comboBox, objectName, GUI.comboBox(...))
end
-- Recursively draw container's content including all children container's content
local function drawContainerContent(container)
for objectIndex = 1, #container.children do
container.children[objectIndex].x, container.children[objectIndex].y = container.children[objectIndex].localPosition.x + container.x - 1, container.children[objectIndex].localPosition.y + container.y - 1
if container.children[objectIndex].children then
-- drawContainerContent(container.children[objectIndex])
container.children[objectIndex]:draw()
else
if container.children[objectIndex].draw then
if not container.children[objectIndex].hidden then
container.children[objectIndex].x, container.children[objectIndex].y = container.children[objectIndex].localPosition.x + container.x - 1, container.children[objectIndex].localPosition.y + container.y - 1
if container.children[objectIndex].children then
-- drawContainerContent(container.children[objectIndex])
-- We use :draw() method against of recursive call. The reason is possible user-defined :draw() reimplementations
container.children[objectIndex]:draw()
else
error("Container object with index " .. objectIndex .. " and name \"" .. tostring(container.children[objectIndex].name) .. "\" doesn't have :draw() method")
if container.children[objectIndex].draw then
container.children[objectIndex]:draw()
else
error("Container object with index " .. objectIndex .. " and name \"" .. tostring(container.children[objectIndex].name) .. "\" doesn't have :draw() method")
end
end
end
end
@ -315,6 +382,8 @@ function GUI.container(x, y, width, height)
container.addHorizontalSlider = addHorizontalSliderObjectToContainer
container.addSwitch = addSwitchObjectToContainer
container.addProgressBar = addProgressBarObjectToContainer
container.addChart = addChartObjectToContainer
container.addComboBox = addComboBoxObjectToContainer
return container
end
@ -513,72 +582,70 @@ function GUI.windowActionButtons(x, y, fatSymbol)
return container
end
----------------------------------------- Context Menu -----------------------------------------
----------------------------------------- Dropdown Menu -----------------------------------------
local function drawContextMenuElement(contextMenuObject, elementIndex, isPressed)
if contextMenuObject.elements[elementIndex].type == GUI.contextMenuElementTypes.default then
local textColor = contextMenuObject.elements[elementIndex].disabled and GUI.colors.contextMenu.disabled.text or (contextMenuObject.elements[elementIndex].color or GUI.colors.contextMenu.default.text)
local function drawDropDownMenuElement(object, itemIndex, isPressed)
local y = object.y + itemIndex * (object.spaceBetweenElements + 1) - 1
local yText = math.floor(y)
if object.items[itemIndex].type == GUI.dropDownMenuElementTypes.default then
local textColor = object.items[itemIndex].disabled and object.colors.disabled.text or (object.items[itemIndex].color or object.colors.default.text)
-- Нажатие
if isPressed then
buffer.square(contextMenuObject.x, contextMenuObject.y + elementIndex - 1, contextMenuObject.width, 1, GUI.colors.contextMenu.pressed.background, GUI.colors.contextMenu.pressed.text, " ")
textColor = GUI.colors.contextMenu.pressed.text
buffer.square(object.x, y - object.spaceBetweenElements, object.width, object.spaceBetweenElements * 2 + 1, object.colors.pressed.background, object.colors.pressed.text, " ")
textColor = object.colors.pressed.text
end
buffer.text(contextMenuObject.x + 2, contextMenuObject.y + elementIndex - 1, textColor, contextMenuObject.elements[elementIndex].text)
if contextMenuObject.elements[elementIndex].shortcut then
buffer.text(contextMenuObject.x + contextMenuObject.width - unicode.len(contextMenuObject.elements[elementIndex].shortcut) - 2, contextMenuObject.y + elementIndex - 1, textColor, contextMenuObject.elements[elementIndex].shortcut)
-- Основной текст
buffer.text(object.x + object.sidesOffset, yText, textColor, string.limit(object.items[itemIndex].text, object.width - object.sidesOffset * 2, false))
-- Шурткатикус
if object.items[itemIndex].shortcut then
buffer.text(object.x + object.width - unicode.len(object.items[itemIndex].shortcut) - object.sidesOffset, yText, textColor, object.items[itemIndex].shortcut)
end
else
buffer.text(contextMenuObject.x, contextMenuObject.y + elementIndex - 1, GUI.colors.contextMenu.separator, string.rep("", contextMenuObject.width))
-- Сепаратор
buffer.text(object.x, yText, object.colors.separator, string.rep("", object.width))
end
end
local function drawContextMenu(contextMenuObject)
buffer.square(contextMenuObject.x, contextMenuObject.y, contextMenuObject.width, contextMenuObject.height, GUI.colors.contextMenu.background, GUI.colors.contextMenu.default.text, " ", GUI.colors.contextMenu.transparency.background)
GUI.windowShadow(contextMenuObject.x, contextMenuObject.y, contextMenuObject.width, contextMenuObject.height, GUI.colors.contextMenu.transparency.shadow, true)
for elementIndex = 1, #contextMenuObject.elements do drawContextMenuElement(contextMenuObject, elementIndex, false) end
local function drawDropDownMenu(object)
buffer.square(object.x, object.y, object.width, object.height, object.colors.default.background, object.colors.default.text, " ", object.colors.transparency)
if object.drawShadow then GUI.windowShadow(object.x, object.y, object.width, object.height, GUI.colors.contextMenu.transparency.shadow, true) end
for itemIndex = 1, #object.items do drawDropDownMenuElement(object, itemIndex, false) end
end
local function showContextMenu(contextMenuObject)
local function showDropDownMenu(object)
local oldDrawLimit = buffer.getDrawLimit(); buffer.resetDrawLimit()
-- Расчет ширины окна меню
local longestElement, longestShortcut = 0, 0
for elementIndex = 1, #contextMenuObject.elements do
if contextMenuObject.elements[elementIndex].type == GUI.contextMenuElementTypes.default then
longestElement = math.max(longestElement, unicode.len(contextMenuObject.elements[elementIndex].text))
if contextMenuObject.elements[elementIndex].shortcut then longestShortcut = math.max(longestShortcut, unicode.len(contextMenuObject.elements[elementIndex].shortcut)) end
end
end
contextMenuObject.width, contextMenuObject.height = longestElement + 4 + (longestShortcut > 0 and longestShortcut + 3 or 0), #contextMenuObject.elements
object.height = #object.items * (object.spaceBetweenElements + 1) + object.spaceBetweenElements
-- А это чтоб за края экрана не лезло
if contextMenuObject.y + contextMenuObject.height >= buffer.screen.height then contextMenuObject.y = buffer.screen.height - contextMenuObject.height end
if contextMenuObject.x + contextMenuObject.width + 1 >= buffer.screen.width then contextMenuObject.x = buffer.screen.width - contextMenuObject.width - 1 end
local oldPixels = buffer.copy(contextMenuObject.x, contextMenuObject.y, contextMenuObject.width + 1, contextMenuObject.height + 1)
local oldPixels = buffer.copy(object.x, object.y, object.width + 1, object.height + 1)
local function quit()
buffer.paste(contextMenuObject.x, contextMenuObject.y, oldPixels)
buffer.paste(object.x, object.y, oldPixels)
buffer.draw()
buffer.setDrawLimit(oldDrawLimit)
end
drawContextMenu(contextMenuObject)
drawDropDownMenu(object)
buffer.draw()
while true do
local e = {event.pull()}
if e[1] == "touch" then
local objectFound = false
for elementIndex = 1, #contextMenuObject.elements do
if e[3] >= contextMenuObject.x and e[3] <= contextMenuObject.x + contextMenuObject.width - 1 and e[4] == contextMenuObject.y + elementIndex - 1 then
for itemIndex = 1, #object.items do
if
e[3] >= object.x and
e[3] <= object.x + object.width - 1 and
e[4] == object.y + itemIndex * (object.spaceBetweenElements + 1) - 1
then
objectFound = true
if not contextMenuObject.elements[elementIndex].disabled and contextMenuObject.elements[elementIndex].type == GUI.contextMenuElementTypes.default then
drawContextMenuElement(contextMenuObject, elementIndex, true)
if not object.items[itemIndex].disabled and object.items[itemIndex].type == GUI.dropDownMenuElementTypes.default then
drawDropDownMenuElement(object, itemIndex, true)
buffer.draw()
os.sleep(0.2)
quit()
return contextMenuObject.elements[elementIndex].text
return object.items[itemIndex].text, itemIndex
end
break
end
@ -589,43 +656,97 @@ local function showContextMenu(contextMenuObject)
end
end
local function addContextMenuElement(contextMenuObject, text, disabled, shortcut, color)
local element = {}
element.type = GUI.contextMenuElementTypes.default
element.text = text
element.disabled = disabled
element.shortcut = shortcut
element.color = color or GUI.colors.contextMenu.default.text --OPTIMIZATION
local function addDropDownMenuItem(object, text, disabled, shortcut, color)
local item = {}
item.type = GUI.dropDownMenuElementTypes.default
item.text = text
item.disabled = disabled
item.shortcut = shortcut
item.color = color
table.insert(contextMenuObject.elements, element)
return element
table.insert(object.items, item)
return item
end
local function addContextMenuSeparator(contextMenuObject)
local element = {type = GUI.contextMenuElementTypes.separator}
table.insert(contextMenuObject.elements, element)
return element
local function addDropDownMenuSeparator(object)
local item = {type = GUI.dropDownMenuElementTypes.separator}
table.insert(object.items, item)
return item
end
function GUI.dropDownMenu(x, y, width, spaceBetweenElements, backgroundColor, textColor, backgroundPressedColor, textPressedColor, disabledColor, separatorColor, transparency, items)
local object = GUI.object(x, y, width, 1)
object.colors = {
default = {
background = backgroundColor,
text = textColor
},
pressed = {
background = backgroundPressedColor,
text = textPressedColor
},
disabled = {
text = disabledColor
},
separator = separatorColor,
transparency = transparency
}
object.sidesOffset = 2
object.spaceBetweenElements = spaceBetweenElements
object.addSeparator = addDropDownMenuSeparator
object.addItem = addDropDownMenuItem
object.items = {}
if items then
for i = 1, #items do
object:addItem(items[i])
end
end
object.drawShadow = true
object.draw = drawDropDownMenu
object.show = showDropDownMenu
return object
end
----------------------------------------- Context Menu -----------------------------------------
local function showContextMenu(object)
-- Расчет ширины окна меню
local longestItem, longestShortcut = 0, 0
for itemIndex = 1, #object.items do
if object.items[itemIndex].type == GUI.dropDownMenuElementTypes.default then
longestItem = math.max(longestItem, unicode.len(object.items[itemIndex].text))
if object.items[itemIndex].shortcut then longestShortcut = math.max(longestShortcut, unicode.len(object.items[itemIndex].shortcut)) end
end
end
object.width = object.sidesOffset + longestItem + (longestShortcut > 0 and 3 + longestShortcut or 0) + object.sidesOffset
object.height = #object.items * (object.spaceBetweenElements + 1) + object.spaceBetweenElements
-- А это чтоб за края экрана не лезло
if object.y + object.height >= buffer.screen.height then object.y = buffer.screen.height - object.height end
if object.x + object.width + 1 >= buffer.screen.width then object.x = buffer.screen.width - object.width - 1 end
object:reimplementedShow()
end
function GUI.contextMenu(x, y, ...)
local argumentElements = {...}
local argumentItems = {...}
local object = GUI.dropDownMenu(x, y, 1, 0, GUI.colors.contextMenu.default.background, GUI.colors.contextMenu.default.text, GUI.colors.contextMenu.pressed.background, GUI.colors.contextMenu.pressed.text, GUI.colors.contextMenu.disabled.text, GUI.colors.contextMenu.separator, GUI.colors.contextMenu.transparency.background)
local contextMenuObject = GUI.object(x, y, 1, 1)
contextMenuObject.elements = {}
contextMenuObject.addElement = addContextMenuElement
contextMenuObject.addSeparator = addContextMenuSeparator
contextMenuObject.show = showContextMenu
contextMenuObject.selectedElement = nil
for elementIndex = 1, #argumentElements do
if argumentElements[elementIndex] == "-" then
contextMenuObject:addSeparator()
-- Заполняем менюшку парашей
for itemIndex = 1, #argumentItems do
if argumentItems[itemIndex] == "-" then
object:addSeparator()
else
contextMenuObject:addElement(argumentElements[elementIndex][1], argumentElements[elementIndex][2], argumentElements[elementIndex][3], argumentElements[elementIndex][4])
object:addItem(argumentItems[itemIndex][1], argumentItems[itemIndex][2], argumentItems[itemIndex][3], argumentItems[itemIndex][4])
end
end
return contextMenuObject
object.reimplementedShow = object.show
object.show = showContextMenu
object.selectedElement = nil
object.spaceBetweenElements = 0
return object
end
----------------------------------------- Menu -----------------------------------------
@ -1155,15 +1276,144 @@ function GUI.switch(x, y, width, activeColor, passiveColor, pipeColor, state)
return object
end
----------------------------------------- Chart object -----------------------------------------
local function drawChart(object)
-- Ебошем пездатые оси
for i = object.y, object.y + object.height - 2 do buffer.text(object.x, i, object.colors.axis, "") end
buffer.text(object.x + 1, object.y + object.height - 1, object.colors.axis, string.rep("", object.width - 1))
buffer.text(object.x, object.y + object.height - 1, object.colors.axis, "")
if #object.values > 1 then
local oldDrawLimit = buffer.getDrawLimit()
buffer.setDrawLimit(object.x, object.y, object.width, object.height)
local delta, fieldWidth, fieldHeight = object.maximumValue - object.minimumValue, object.width - 2, object.height - 1
-- Рисуем линии значений
local roundValues = object.maximumValue > 10
local step = 0.2 * fieldHeight
for i = step, fieldHeight, step do
local value = object.minimumValue + delta * (i / fieldHeight)
local stringValue = roundValues and tostring(math.floor(value)) or math.doubleToString(value, 1)
buffer.text(object.x + 1, math.floor(object.y + fieldHeight - i), object.colors.value, string.rep("", object.width - unicode.len(stringValue) - 2) .. " " .. stringValue)
end
-- Рисуем графек, йопта
local function getDotPosition(valueIndex)
return
object.x + math.round((fieldWidth * (valueIndex - 1) / (#object.values - 1))) + 1,
object.y + math.round(((fieldHeight - 1) * (object.maximumValue - object.values[valueIndex]) / delta))
end
local x, y = getDotPosition(1)
for valueIndex = 2, #object.values do
local xNew, yNew = getDotPosition(valueIndex)
buffer.semiPixelLine(x, y * 2, xNew, yNew * 2, object.colors.chart)
x, y = xNew, yNew
end
buffer.setDrawLimit(oldDrawLimit)
end
-- Дорисовываем названия осей
if object.axisNames.y then buffer.text(object.x + 1, object.y, object.colors.axis, object.axisNames.y) end
if object.axisNames.x then buffer.text(object.x + object.width - unicode.len(object.axisNames.x), object.y + object.height - 2, object.colors.axis, object.axisNames.x) end
end
function GUI.chart(x, y, width, height, axisColor, axisValueColor, chartColor, xAxisName, yAxisName, minimumValue, maximumValue, values)
if minimumValue >= maximumValue then error("Chart's minimum value can't be >= maximum value!") end
local object = GUI.object(x, y, width, height)
object.colors = {axis = axisColor, chart = chartColor, value = axisValueColor}
object.draw = drawChart
object.values = values
object.minimumValue = minimumValue
object.maximumValue = maximumValue
object.axisNames = {x = xAxisName, y = yAxisName}
return object
end
----------------------------------------- Combo Box Object -----------------------------------------
local function drawComboBox(object)
buffer.square(object.x, object.y, object.width, object.height, object.colors.default.background)
local x, y, limit, arrowSize = object.x + 1, math.floor(object.y + object.height / 2), object.width - 5, object.height
buffer.text(x, y, object.colors.default.text, string.limit(object.items[object.currentItem].text, limit, false))
GUI.button(object.x + object.width - arrowSize * 2 + 1, object.y, arrowSize * 2 - 1, arrowSize, object.colors.arrow.background, object.colors.arrow.text, 0x0, 0x0, object.state and "" or ""):draw()
end
local function selectComboBoxItem(object)
object.state = true
object:draw()
local dropDownMenu = GUI.dropDownMenu(object.x, object.y + object.height, object.width, object.height == 1 and 0 or 1, object.colors.default.background, object.colors.default.text, object.colors.pressed.background, object.colors.pressed.text, GUI.colors.contextMenu.disabled.text, GUI.colors.contextMenu.separator, GUI.colors.contextMenu.transparency.background, object.items)
dropDownMenu.items = object.items
dropDownMenu.sidesOffset = 1
local _, itemIndex = dropDownMenu:show()
object.currentItem = itemIndex or object.currentItem
object.state = false
object:draw()
buffer.draw()
end
function GUI.comboBox(x, y, width, height, backgroundColor, textColor, arrowBackgroundColor, arrowTextColor, items)
local object = GUI.object(x, y, width, height)
object.colors = {
default = {
background = backgroundColor,
text = textColor
},
pressed = {
background = GUI.colors.contextMenu.pressed.background,
text = GUI.colors.contextMenu.pressed.text
},
arrow = {
background = arrowBackgroundColor,
text = arrowTextColor
}
}
object.items = {}
object.currentItem = 1
object.addItem = addDropDownMenuItem
object.addSeparator = addDropDownMenuSeparator
if items then
for i = 1, #items do
object:addItem(items[i])
end
end
object.draw = drawComboBox
object.selectItem = selectComboBoxItem
object.state = false
return object
end
--------------------------------------------------------------------------------------------------------------------------------
-- buffer.start()
-- buffer.clear(0x1b1b1b)
-- buffer.draw(true)
-- GUI.switch(2, 2, 8, 0x77FF77, 0x999999, 0xFFFFFF, true):draw()
-- local comboBox = GUI.comboBox(2, 2, 30, 1, 0xFFFFFF, 0x262626, 0xDDDDDD, 0x262626, {"PIC", "RAW", "PNG", "JPG"})
-- comboBox:selectItem()
-- buffer.draw()
-- GUI.chart(2, 10, 40, 20, 0xFFFFFF, 0xBBBBBB, 0xFFDB40, "t", "EU", 0, 2, {
-- 0.5,
-- 0.12
-- }):draw()
-- local menu = GUI.dropDownMenu(2, 2, 40, 1, 0xFFFFFF, 0x000000, 0xFFDB40, 0xFFFFFF, 0x999999, 0x777777, 50)
-- menu:addItem("New")
-- menu:addItem("Open")
-- menu:addSeparator()
-- menu:addItem("Save")
-- menu:addItem("Save as")
-- menu:show()
-- GUI.contextMenu(2, 2, {"Hello"}, {"World"}, "-", {"You are the"}, {"Best of best", false, "^S"}, {"And bestest yopta"}):show()
--------------------------------------------------------------------------------------------------------------------------------
return GUI

View File

@ -8,6 +8,7 @@ local libraries = {
for library in pairs(libraries) do if not _G[library] then _G[library] = require(libraries[library]) end end; libraries = nil
local gpu = component.gpu
local buffer = {}
------------------------------------------------- Вспомогательные методы -----------------------------------------------------------------
@ -71,12 +72,12 @@ end
-- Инициализация буфера со всеми необходимыми параметрами, вызывается автоматически
function buffer.start()
buffer.flush(component.gpu.getResolution())
buffer.flush(gpu.getResolution())
end
-- Изменение разрешения экрана и пересоздание массивов буфера
function buffer.changeResolution(width, height)
component.gpu.setResolution(width, height)
gpu.setResolution(width, height)
buffer.flush(width, height)
end
@ -495,6 +496,42 @@ function buffer.semiPixelSquare(x, y, width, height, color)
end
end
function buffer.semiPixelLine(x0, y0, x1, y1, color)
local steep = false;
if math.abs(x0 - x1) < math.abs(y0 - y1 ) then
x0, y0 = swap(x0, y0)
x1, y1 = swap(x1, y1)
steep = true;
end
if (x0 > x1) then
x0, x1 = swap(x0, x1)
y0, y1 = swap(y0, y1)
end
local dx = x1 - x0;
local dy = y1 - y0;
local derror2 = math.abs(dy) * 2
local error2 = 0;
local y = y0;
for x = x0, x1, 1 do
if steep then
buffer.semiPixelSet(y, x, color);
else
buffer.semiPixelSet(x, y, color)
end
error2 = error2 + derror2;
if error2 > dx then
y = y + (y1 > y0 and 1 or -1);
error2 = error2 - dx * 2;
end
end
end
------------------------------------------- Просчет изменений и отрисовка ------------------------------------------------------------------------
--Функция рассчитывает изменения и применяет их, возвращая то, что было изменено
@ -595,11 +632,11 @@ function buffer.draw(force)
--Перебираем все цвета текста и фона, выполняя гпу-операции
for foreground in pairs(buffer.screen.changes) do
if currentForeground ~= foreground then component.gpu.setForeground(foreground); currentForeground = foreground end
if currentForeground ~= foreground then gpu.setForeground(foreground); currentForeground = foreground end
for background in pairs(buffer.screen.changes[foreground]) do
if currentBackground ~= background then component.gpu.setBackground(background); currentBackground = background end
if currentBackground ~= background then gpu.setBackground(background); currentBackground = background end
for i = 1, #buffer.screen.changes[foreground][background], 3 do
component.gpu.set(buffer.screen.changes[foreground][background][i], buffer.screen.changes[foreground][background][i + 1], buffer.screen.changes[foreground][background][i + 2])
gpu.set(buffer.screen.changes[foreground][background][i], buffer.screen.changes[foreground][background][i + 1], buffer.screen.changes[foreground][background][i + 2])
end
end
end

View File

@ -44,19 +44,19 @@ local function buttonHandler(window, object, objectIndex, eventData)
object.pressed = true; window:draw(); buffer.draw()
os.sleep(0.2)
object.pressed = false; window:draw(); buffer.draw()
executeObjectMethod(object.onTouch, object, eventData)
executeObjectMethod(object.onTouch, eventData)
end
local function tabBarTabHandler(window, object, objectIndex, eventData)
object.parent.parent.selectedTab = objectIndex
window:draw(); buffer:draw()
executeObjectMethod(object.parent.parent.onTabSwitched, object, eventData)
executeObjectMethod(object.parent.parent.onTabSwitched, eventData)
end
local function inputTextBoxHandler(window, object, objectIndex, eventData)
object:input()
window:draw(); buffer:draw()
executeObjectMethod(object.onInputFinished, object, eventData)
executeObjectMethod(object.onInputFinished, eventData)
end
local function textBoxScrollHandler(window, object, objectIndex, eventData)
@ -67,13 +67,18 @@ local function horizontalSliderHandler(window, object, objectIndex, eventData)
local clickPosition = eventData[3] - object.x + 1
object.value = object.minimumValue + (clickPosition * (object.maximumValue - object.minimumValue) / object.width)
window:draw(); buffer:draw()
executeObjectMethod(object.onValueChanged, object, eventData)
executeObjectMethod(object.onValueChanged, eventData)
end
local function switchHandler(window, object, objectIndex, eventData)
object.state = not object.state
window:draw(); buffer:draw()
executeObjectMethod(object.onStateChanged, object, eventData)
executeObjectMethod(object.onStateChanged, eventData)
end
local function comboBoxHandler(window, object, objectIndex, eventData)
object:selectItem()
executeObjectMethod(object.onItemSelected, eventData)
end
function windows.handleEventData(window, eventData)
@ -90,6 +95,8 @@ function windows.handleEventData(window, eventData)
horizontalSliderHandler(window, object, objectIndex, eventData)
elseif object.type == GUI.objectTypes.switch then
switchHandler(window, object, objectIndex, eventData)
elseif object.type == GUI.objectTypes.comboBox then
comboBoxHandler(window, object, objectIndex, eventData)
elseif object.onTouch then
executeObjectMethod(object.onTouch, eventData)
end
@ -118,6 +125,15 @@ function windows.handleEventData(window, eventData)
else
executeObjectMethod(window.onDrag, eventData)
end
elseif eventData[1] == "drop" then
local object, objectIndex = window:getClickedObject(eventData[3], eventData[4])
if object then
if object.onDrag then
executeObjectMethod(object.onDrop, eventData)
end
else
executeObjectMethod(window.onDrop, eventData)
end
elseif eventData[1] == "key_down" then
executeObjectMethod(window.onKeyDown, eventData)
elseif eventData[1] == "key_up" then
@ -158,7 +174,7 @@ end
local function drawWindow(window)
if window.onDrawStarted then window.onDrawStarted() end
window:reimplementedDraw()
window:update()
if window.drawShadow then GUI.windowShadow(window.x, window.y, window.width, window.height, 50) end
if window.onDrawFinished then window.onDrawFinished() end
buffer.draw()
@ -171,7 +187,7 @@ local function newWindow(x, y, width, height, minimumWidth, minimumHeight)
window.minimumWidth = minimumWidth
window.minimumHeight = minimumHeight
window.drawShadow = true
window.reimplementedDraw = window.draw
window.update = window.draw
window.draw = drawWindow
window.handleEventData = windows.handleEventData
window.handleEvents = windows.handleEvents