local component = require("component") local computer = require("computer") local keyboard = require("keyboard") local event = require("event") local term = require("term") local MineOSCore = require("MineOSCore") local MineOSPaths = require("MineOSPaths") local image = require("image") local GUI = require("GUI") local fs = require("filesystem") local unicode = require("unicode") local buffer = require("doubleBuffering") local MineOSInterface = {} ----------------------------------------------------------------------------------------------------------------------------------- MineOSInterface.iconsCache = {} MineOSInterface.iconClickDelay = 0.2 MineOSInterface.iconConfigFileName = ".icons" MineOSInterface.iconImageWidth = 8 MineOSInterface.iconImageHeight = 4 MineOSInterface.colors = { windows = { title = { background = 0xE1E1E1, text = 0x2D2D2D }, shadowTransparency = 0.5, backgroundPanel = 0xF0F0F0, tabBar = { default = { background = 0x2D2D2D, text = 0xF0F0F0 }, selected = { background = 0xF0F0F0, text = 0x2D2D2D } } } } ----------------------------------------------------------------------------------------------------------------------------------- local function calculateIconSizes() MineOSInterface.iconHalfWidth = math.floor(MineOSCore.properties.iconWidth / 2) MineOSInterface.iconTextHeight = MineOSCore.properties.iconHeight - MineOSInterface.iconImageHeight - 1 MineOSInterface.iconImageHorizontalOffset = math.floor(MineOSInterface.iconHalfWidth - MineOSInterface.iconImageWidth / 2) end function MineOSInterface.setIconProperties(width, height, horizontalSpaceBetween, verticalSpaceBetween) MineOSCore.properties.iconWidth, MineOSCore.properties.iconHeight, MineOSCore.properties.iconHorizontalSpaceBetween, MineOSCore.properties.iconVerticalSpaceBetween = width, height, horizontalSpaceBetween, verticalSpaceBetween MineOSCore.saveProperties() calculateIconSizes() MineOSInterface.mainContainer.iconField:deleteIconConfig() MineOSInterface.mainContainer.dockContainer.sort() computer.pushSignal("MineOSCore", "updateFileList") end calculateIconSizes() ----------------------------------------------------------------------------------------------------------------------------------- function MineOSInterface.clearTerminal() local gpu = component.gpu gpu.setBackground(0x1D1D1D) gpu.setForeground(0xFFFFFF) local width, height = gpu.getResolution() gpu.fill(1, 1, width, height, " ") term.setCursor(1, 1) end function MineOSInterface.waitForPressingAnyKey() print(" ") print(MineOSCore.localization.pressAnyKeyToContinue) while true do local eventType = event.pull() if eventType == "key_down" or eventType == "touch" then break end end end function MineOSInterface.launchScript(path) MineOSInterface.clearTerminal() if MineOSInterface.safeLaunch(path) then MineOSInterface.waitForPressingAnyKey() end end ----------------------------------------------------------------------------------------------------------------------------------- function MineOSInterface.cacheIconSource(name, path) if not MineOSInterface.iconsCache[name] then MineOSInterface.iconsCache[name] = image.load(path) end return MineOSInterface.iconsCache[name] end local function iconDrawNameLine(x, y, line, icon) local lineLength = unicode.len(line) local x = math.floor(x - lineLength / 2) if icon.selected then buffer.square(x, y, lineLength, 1, icon.colors.selection, 0x0, " ", icon.colors.selectionTransparency) end buffer.text(x, y, icon.colors.text, line) end local function iconDraw(icon) local text = MineOSCore.properties.showExtension and icon.name or icon.nameWithoutExtension local xCenter, yText = icon.x + MineOSInterface.iconHalfWidth, icon.y + MineOSInterface.iconImageHeight + 1 local charIndex = 1 for lineIndex = 1, MineOSInterface.iconTextHeight do if lineIndex < MineOSInterface.iconTextHeight then iconDrawNameLine(xCenter, yText, unicode.sub(text, charIndex, charIndex + icon.width - 1), icon) charIndex, yText = charIndex + icon.width, yText + 1 else iconDrawNameLine(xCenter, yText, string.limit(unicode.sub(text, charIndex, -1), icon.width, "center"), icon) end end local xImage = icon.x + MineOSInterface.iconImageHorizontalOffset if icon.selected then local xSelection = xImage - 1 buffer.text(xSelection, icon.y - 1, icon.colors.selection, string.rep("▄", MineOSInterface.iconImageWidth + 2), icon.colors.selectionTransparency) buffer.text(xSelection, icon.y + MineOSInterface.iconImageHeight, icon.colors.selection, string.rep("▀", MineOSInterface.iconImageWidth + 2), icon.colors.selectionTransparency) buffer.square(xSelection, icon.y, MineOSInterface.iconImageWidth + 2, MineOSInterface.iconImageHeight, icon.colors.selection, 0x0, " ", icon.colors.selectionTransparency) end if icon.image then if icon.cut then if not icon.semiTransparentImage then icon.semiTransparentImage = image.copy(icon.image) for i = 3, #icon.semiTransparentImage, 4 do icon.semiTransparentImage[i + 2] = icon.semiTransparentImage[i + 2] + 0.6 if icon.semiTransparentImage[i + 2] > 1 then icon.semiTransparentImage[i + 2] = 1 end end end buffer.image(xImage, icon.y, icon.semiTransparentImage, true) else buffer.image(xImage, icon.y, icon.image) end elseif icon.liveImage then icon.liveImage(xImage, icon.y) end local xShortcut = xImage + MineOSInterface.iconImageWidth if icon.isShortcut then buffer.set(xShortcut - 1, icon.y + MineOSInterface.iconImageHeight - 1, 0xFFFFFF, 0x0, "<") end if icon.windows then buffer.text(xCenter - 1, icon.y + MineOSInterface.iconImageHeight, 0x66DBFF, "╺╸") local windowCount = table.size(icon.windows) if windowCount > 1 then windowCount = tostring(windowCount) local windowCountLength = #windowCount local xTip, yTip = xShortcut - windowCountLength, icon.y buffer.square(xTip, yTip, windowCountLength, 1, 0xFF4940, 0xFFFFFF, " ") buffer.text(xTip, yTip, 0xFFFFFF, windowCount) buffer.text(xTip - 1, yTip, 0xFF4940, "⢸") buffer.text(xTip + windowCountLength, yTip, 0xFF4940, "⡇") buffer.text(xTip, yTip - 1, 0xFF4940, string.rep("⣀", windowCountLength)) buffer.text(xTip, yTip + 1, 0xFF4940, string.rep("⠉", windowCountLength)) end end end local function iconEventHandler(mainContainer, object, eventData) if eventData[1] == "touch" then object.lastTouchPosition = object.lastTouchPosition or {} object.lastTouchPosition.x, object.lastTouchPosition.y = eventData[3], eventData[4] object:moveToFront() if eventData[5] == 0 then object.parent.parent.onLeftClick(object, eventData) else object.parent.parent.onRightClick(object, eventData) end elseif eventData[1] == "double_touch" and object:isClicked(eventData[3], eventData[4]) and eventData[5] == 0 then object.parent.parent.onDoubleClick(object, eventData) elseif eventData[1] == "drag" and object.parent.parent.iconConfigEnabled and object.lastTouchPosition then -- Ебучие авторы мода, ну на кой хуй было делать drop-ивент без наличия drag? ПИДОРЫ object.dragStarted = true object.localX = object.localX + eventData[3] - object.lastTouchPosition.x object.localY = object.localY + eventData[4] - object.lastTouchPosition.y object.lastTouchPosition.x, object.lastTouchPosition.y = eventData[3], eventData[4] mainContainer:draw() buffer.draw() elseif eventData[1] == "drop" and object.parent.parent.iconConfigEnabled and object.dragStarted then object.dragStarted = nil object.parent.parent.iconConfig[object.name .. (object.isDirectory and "/" or "")] = { x = object.localX, y = object.localY } object.parent.parent:saveIconConfig() object.lastTouchPosition = nil end end local function iconAnalyseExtension(icon) if icon.isDirectory then if icon.extension == ".app" then if MineOSCore.properties.showApplicationIcons then if fs.exists(icon.path .. "/Resources/Icon.pic") then icon.image = image.load(icon.path .. "/Resources/Icon.pic") elseif fs.exists(icon.path .. "/Resources/Icon.lua") then local data, reason = loadfile(icon.path .. "/Resources/Icon.lua") if data then data, reason = data() if data then icon.liveImage = data else error("Failed to load live icon image: " .. tostring(reason)) end else error("Failed to load live icon image: " .. tostring(reason)) end else icon.image = MineOSInterface.iconsCache.fileNotExists end else icon.image = MineOSInterface.iconsCache.application end icon.launch = icon.launchers.application else icon.image = MineOSInterface.iconsCache.folder icon.launch = icon.launchers.directory end else if icon.extension == ".lnk" then icon.shortcutPath = MineOSCore.readShortcut(icon.path) icon.shortcutExtension = fs.extension(icon.shortcutPath) icon.shortcutIsDirectory = fs.isDirectory(icon.shortcutPath) icon.isShortcut = true local shortcutIcon = iconAnalyseExtension({ path = icon.shortcutPath, extension = icon.shortcutExtension, name = icon.name, nameWithoutExtension = icon.nameWithoutExtension, isDirectory = icon.shortcutIsDirectory, iconImage = icon.iconImage, launchers = icon.launchers }) icon.image = shortcutIcon.image icon.shortcutLaunch = shortcutIcon.launch icon.launch = icon.launchers.shortcut shortcutIcon = nil elseif not fs.exists(icon.path) then icon.image = MineOSInterface.iconsCache.fileNotExists icon.launch = icon.launchers.corrupted else if MineOSCore.properties.extensionAssociations[icon.extension] then icon.launch = icon.launchers.extension icon.image = MineOSInterface.cacheIconSource(icon.extension, MineOSCore.properties.extensionAssociations[icon.extension].icon) else icon.launch = icon.launchers.script icon.image = MineOSInterface.iconsCache.script end end end return icon end local function iconIsClicked(icon, x, y) return x >= icon.x + MineOSInterface.iconImageHorizontalOffset and y >= icon.y and x <= icon.x + MineOSInterface.iconImageHorizontalOffset + MineOSInterface.iconImageWidth - 1 and y <= icon.y + MineOSInterface.iconImageHeight - 1 or x >= icon.x and y >= icon.y + MineOSInterface.iconImageHeight + 1 and x <= icon.x + MineOSCore.properties.iconWidth - 1 and y <= icon.y + MineOSCore.properties.iconHeight - 1 end function MineOSInterface.icon(x, y, path, textColor, selectionColor) local icon = GUI.object(x, y, MineOSCore.properties.iconWidth, MineOSCore.properties.iconHeight) icon.colors = { text = textColor, selection = selectionColor, selectionTransparency = 0.6 } icon.path = path icon.extension = fs.extension(icon.path) or "script" icon.name = fs.name(path) icon.nameWithoutExtension = fs.hideExtension(icon.name) icon.isDirectory = fs.isDirectory(icon.path) icon.isShortcut = false icon.selected = false icon.isClicked = iconIsClicked icon.draw = iconDraw icon.launchers = table.copy(MineOSInterface.iconLaunchers) icon.analyseExtension = iconAnalyseExtension return icon end local function iconFieldUpdate(iconField) iconField.backgroundObject.width, iconField.backgroundObject.height = iconField.width, iconField.height iconField.iconsContainer.width, iconField.iconsContainer.height = iconField.width, iconField.height iconField.iconCount.horizontal = math.floor((iconField.width - iconField.xOffset) / (MineOSCore.properties.iconWidth + MineOSCore.properties.iconHorizontalSpaceBetween)) iconField.iconCount.vertical = math.floor((iconField.height - iconField.yOffset) / (MineOSCore.properties.iconHeight + MineOSCore.properties.iconVerticalSpaceBetween)) iconField.iconCount.total = iconField.iconCount.horizontal * iconField.iconCount.vertical return iconField end local function iconFieldLoadIconConfig(iconField) if fs.exists(iconField.workpath .. MineOSInterface.iconConfigFileName) then iconField.iconConfig = table.fromFile(iconField.workpath .. MineOSInterface.iconConfigFileName) else iconField.iconConfig = {} end end local function iconFieldSaveIconConfig(iconField) table.toFile(iconField.workpath .. MineOSInterface.iconConfigFileName, iconField.iconConfig) end local function iconFieldDeleteIconConfig(iconField) iconField.iconConfig = {} fs.remove(iconField.workpath .. MineOSInterface.iconConfigFileName, iconField.iconConfig) end ----------------------------------------------------------------------------------------------------------------------------------- MineOSInterface.iconLaunchers = {} function MineOSInterface.iconLaunchers.application(icon) local pathToAboutFile = icon.path .. "/resources/About/" .. MineOSCore.properties.language .. ".txt" if MineOSCore.properties.showHelpOnApplicationStart and fs.exists(pathToAboutFile) then local container = MineOSInterface.addUniversalContainer(MineOSInterface.mainContainer, MineOSCore.localization.applicationHelp .. " \"" .. fs.name(icon.path) .. "\"") local lines = {} for line in io.lines(pathToAboutFile) do table.insert(lines, line) end container.layout:addChild(GUI.textBox(1, 1, 50, 1, nil, 0xcccccc, lines, 1, 0, 0, true, true)) local button = container.layout:addChild(GUI.button(1, 1, 30, 1, 0xE1E1E1, 0x2D2D2D, 0xAAAAAA, 0x2D2D2D, MineOSCore.localization.dontShowAnymore)) local function onExit() container:delete() MineOSInterface.safeLaunch((icon.shortcutPath or icon.path) .. "/Main.lua") end container.panel.eventHandler = function(mainContainer, object, eventData) if eventData[1] == "touch" then onExit() end end button.onTouch = function() MineOSCore.properties.showHelpOnApplicationStart = false MineOSCore.saveProperties() onExit() end else MineOSInterface.safeLaunch(icon.path .. "/Main.lua") end end function MineOSInterface.iconLaunchers.directory(icon) icon.parent.parent:setWorkpath(icon.path) end function MineOSInterface.iconLaunchers.shortcut(icon) local oldPath = icon.path icon.path = icon.shortcutPath icon:shortcutLaunch() icon.path = oldPath end function MineOSInterface.iconLaunchers.corrupted(icon) GUI.error("Application is corrupted") end function MineOSInterface.iconLaunchers.extension(icon) MineOSInterface.safeLaunch(MineOSCore.properties.extensionAssociations[icon.extension].launcher, icon.path, "-o") end function MineOSInterface.iconLaunchers.script(icon) MineOSInterface.launchScript(icon.path) end function MineOSInterface.iconLaunchers.showPackageContent(icon) icon.parent.parent:setWorkpath(icon.path) icon.parent.parent:updateFileList() icon:getFirstParent():draw() buffer.draw() end function MineOSInterface.iconLaunchers.showContainingFolder(icon) icon.parent.parent:setWorkpath(fs.path(icon.shortcutPath)) icon.parent.parent:updateFileList() icon:getFirstParent():draw() buffer.draw() end ----------------------------------------------------------------------------------------------------------------------------------- local function getCykaIconPosition(iconField) local y = iconField.yOffset for i = 1, #iconField.iconsContainer.children do y = math.max(y, iconField.iconsContainer.children[i].localY) end local x = iconField.xOffset for i = 1, #iconField.iconsContainer.children do if iconField.iconsContainer.children[i].localY == y then x = math.max(x, iconField.iconsContainer.children[i].localX) end end x = x + MineOSCore.properties.iconWidth + MineOSCore.properties.iconHorizontalSpaceBetween if x + MineOSCore.properties.iconWidth + MineOSCore.properties.iconHorizontalSpaceBetween > iconField.iconsContainer.width then x, y = iconField.xOffset, y + MineOSCore.properties.iconHeight + MineOSCore.properties.iconVerticalSpaceBetween end return x, y end local function iconFieldUpdateFileList(iconField) -- GUI.error(debug.traceback()) iconField.fileList = fs.sortedList(iconField.workpath, MineOSCore.properties.sortingMethod or "type", MineOSCore.properties.showHiddenFiles, iconField.filenameMatcher, false) iconField:update() -- Грузим инфу об иконочках if iconField.iconConfigEnabled then iconField:loadIconConfig() end local configList, notConfigList = {}, {} for i = iconField.fromFile, iconField.fromFile + iconField.iconCount.total - 1 do if iconField.fileList[i] then if iconField.iconConfigEnabled and iconField.iconConfig[iconField.fileList[i]] then table.insert(configList, iconField.fileList[i]) else table.insert(notConfigList, iconField.fileList[i]) end else break end end local function checkClipboard(icon) if MineOSCore.clipboard and MineOSCore.clipboard.cut then for i = 1, #MineOSCore.clipboard do if MineOSCore.clipboard[i] == icon.path then icon.cut = true end end end end -- Заполнение дочернего контейнера iconField.iconsContainer:deleteChildren() for i = 1, #configList do local icon = iconField.iconsContainer:addChild(MineOSInterface.icon( iconField.iconConfig[configList[i]].x, iconField.iconConfig[configList[i]].y, iconField.workpath .. configList[i], iconField.colors.text, iconField.colors.selection )) checkClipboard(icon) icon.eventHandler = iconEventHandler icon.launchers = iconField.launchers icon:analyseExtension() end local x, y if #configList > 0 then x, y = getCykaIconPosition(iconField, configList) else x, y = iconField.xOffset, iconField.yOffset end for i = 1, #notConfigList do local icon = iconField.iconsContainer:addChild(MineOSInterface.icon(x, y, iconField.workpath .. notConfigList[i], iconField.colors.text, iconField.colors.selection)) iconField.iconConfig[notConfigList[i]] = {x = x, y = y} checkClipboard(icon) icon.eventHandler = iconEventHandler icon.launchers = iconField.launchers icon:analyseExtension() x = x + MineOSCore.properties.iconWidth + MineOSCore.properties.iconHorizontalSpaceBetween if x + MineOSCore.properties.iconWidth + MineOSCore.properties.iconHorizontalSpaceBetween - 1 > iconField.iconsContainer.width then x, y = iconField.xOffset, y + MineOSCore.properties.iconHeight + MineOSCore.properties.iconVerticalSpaceBetween end end if iconField.iconConfigEnabled then iconField:saveIconConfig() end return iconField end local function iconFieldBackgroundObjectEventHandler(mainContainer, object, eventData) if eventData[1] == "touch" then if eventData[5] == 0 then object.parent:deselectAll() object.parent.selection = { x1 = eventData[3], y1 = eventData[4] } mainContainer:draw() buffer.draw() else local menu = MineOSInterface.contextMenu(eventData[3], eventData[4]) local subMenu = menu:addSubMenu(MineOSCore.localization.create) subMenu:addItem(MineOSCore.localization.newFile).onTouch = function() MineOSInterface.newFile(MineOSInterface.mainContainer, object.parent, eventData[3], eventData[4], object.parent.workpath) end subMenu:addItem(MineOSCore.localization.newFolder).onTouch = function() MineOSInterface.newFolder(MineOSInterface.mainContainer, object.parent, eventData[3], eventData[4], object.parent.workpath) end subMenu:addItem(MineOSCore.localization.newFileFromURL, not component.isAvailable("internet")).onTouch = function() MineOSInterface.newFileFromURL(MineOSInterface.mainContainer, object.parent, eventData[3], eventData[4], object.parent.workpath) end subMenu:addSeparator() subMenu:addItem(MineOSCore.localization.newApplication).onTouch = function() MineOSInterface.newApplication(MineOSInterface.mainContainer, object.parent, eventData[3], eventData[4], object.parent.workpath) end menu:addSeparator() local subMenu = menu:addSubMenu(MineOSCore.localization.sortBy) subMenu:addItem(MineOSCore.localization.sortByName).onTouch = function() object.parent:deleteIconConfig() MineOSCore.properties.sortingMethod = "name" MineOSCore.saveProperties() computer.pushSignal("MineOSCore", "updateFileList") end subMenu:addItem(MineOSCore.localization.sortByDate).onTouch = function() object.parent:deleteIconConfig() MineOSCore.properties.sortingMethod = "date" MineOSCore.saveProperties() computer.pushSignal("MineOSCore", "updateFileList") end subMenu:addItem(MineOSCore.localization.sortByType).onTouch = function() object.parent:deleteIconConfig() MineOSCore.properties.sortingMethod = "type" MineOSCore.saveProperties() computer.pushSignal("MineOSCore", "updateFileList") end menu:addItem(MineOSCore.localization.sortAutomatically).onTouch = function() object.parent:deleteIconConfig() computer.pushSignal("MineOSCore", "updateFileList") end menu:addItem(MineOSCore.localization.update).onTouch = function() computer.pushSignal("MineOSCore", "updateFileList") end menu:addSeparator() menu:addItem(MineOSCore.localization.paste, not MineOSCore.clipboard).onTouch = function() local i = 1 while i <= #MineOSCore.clipboard do if fs.exists(MineOSCore.clipboard[i]) then i = i + 1 else table.remove(MineOSCore.clipboard, i) end end MineOSInterface.copy(MineOSCore.clipboard, object.parent.workpath) if MineOSCore.clipboard.cut then for i = 1, #MineOSCore.clipboard do fs.remove(MineOSCore.clipboard[i]) end MineOSCore.clipboard = nil end computer.pushSignal("MineOSCore", "updateFileList") end menu:show() end elseif eventData[1] == "drag" then if object.parent.selection then object.parent.selection.x2 = eventData[3] object.parent.selection.y2 = eventData[4] object:moveToFront() mainContainer:draw() buffer.draw() end elseif eventData[1] == "drop" then object.parent.selection = nil object:moveToBack() mainContainer:draw() buffer.draw() end end local function iconFieldBackgroundObjectDraw(object) if object.parent.selection and object.parent.selection.x2 then local x1, y1, x2, y2 = object.parent.selection.x1, object.parent.selection.y1, object.parent.selection.x2, object.parent.selection.y2 if x2 < x1 then x1, x2 = x2, x1 end if y2 < y1 then y1, y2 = y2, y1 end buffer.square(x1, y1, x2 - x1 + 1, y2 - y1 + 1, object.parent.colors.selection, 0x0, " ", 0.6) for i = 1, #object.parent.iconsContainer.children do local xCenter, yCenter = object.parent.iconsContainer.children[i].x + MineOSCore.properties.iconWidth / 2, object.parent.iconsContainer.children[i].y + MineOSCore.properties.iconHeight / 2 object.parent.iconsContainer.children[i].selected = xCenter >= x1 and xCenter <= x2 and yCenter >= y1 and yCenter <= y2 end end end local function iconFieldDeselectAll(iconField) for i = 1, #iconField.iconsContainer.children do iconField.iconsContainer.children[i].selected = false end end local function iconFieldGetSelectedIcons(iconField) local selectedIcons = {} for i = 1, #iconField.iconsContainer.children do if iconField.iconsContainer.children[i].selected then table.insert(selectedIcons, iconField.iconsContainer.children[i]) end end return selectedIcons end local function iconFieldSetWorkpath(iconField, path) iconField.workpath = path iconField.filenameMatcher = nil iconField.fromFile = 1 return iconField end function MineOSInterface.iconField(x, y, width, height, xOffset, yOffset, textColor, selectionColor, workpath) local iconField = GUI.container(x, y, width, height) iconField.colors = { text = textColor, selection = selectionColor } iconField.iconConfig = {} iconField.iconCount = {} iconField.fileList = {} iconField.fromFile = 1 iconField.iconConfigEnabled = false iconField.xOffset = xOffset iconField.yOffset = yOffset iconField.workpath = workpath iconField.filenameMatcher = nil iconField.backgroundObject = iconField:addChild(GUI.object(1, 1, width, height)) iconField.backgroundObject.eventHandler = iconFieldBackgroundObjectEventHandler iconField.backgroundObject.draw = iconFieldBackgroundObjectDraw iconField.iconsContainer = iconField:addChild(GUI.container(1, 1, width, height)) iconField.updateFileList = iconFieldUpdateFileList iconField.update = iconFieldUpdate iconField.eventHandler = iconFieldEventHandler iconField.deselectAll = iconFieldDeselectAll iconField.loadIconConfig = iconFieldLoadIconConfig iconField.saveIconConfig = iconFieldSaveIconConfig iconField.deleteIconConfig = iconFieldDeleteIconConfig iconField.getSelectedIcons = iconFieldGetSelectedIcons iconField.setWorkpath = iconFieldSetWorkpath iconField.onLeftClick = MineOSInterface.iconLeftClick iconField.onRightClick = MineOSInterface.iconRightClick iconField.onDoubleClick = MineOSInterface.iconDoubleClick iconField.launchers = table.copy(MineOSInterface.iconLaunchers) return iconField end ---------------------------------------------------------------------------------------------------------------- function MineOSInterface.contextMenu(...) local menu = GUI.contextMenu(...) menu.colors.transparency.background = MineOSCore.properties.transparencyEnabled and GUI.colors.contextMenu.transparency.background menu.colors.transparency.shadow = MineOSCore.properties.transparencyEnabled and GUI.colors.contextMenu.transparency.shadow return menu end function MineOSInterface.iconLeftClick(icon, eventData) if not keyboard.isKeyDown(29) and not keyboard.isKeyDown(219) then icon.parent.parent:deselectAll() end icon.selected = true MineOSInterface.OSDraw() end function MineOSInterface.iconDoubleClick(icon, eventData) icon.selected = false icon:launch() MineOSInterface.OSDraw() -- computer.pushSignal("MineOSCore", "updateFileList") end function MineOSInterface.iconRightClick(icon, eventData) icon.selected = true MineOSInterface.OSDraw() local selectedIcons = icon.parent.parent:getSelectedIcons() local menu = MineOSInterface.contextMenu(eventData[3], eventData[4]) if #selectedIcons == 1 then if icon.isDirectory then if icon.extension == ".app" then menu:addItem(MineOSCore.localization.showPackageContent).onTouch = function() icon.parent.parent.launchers.showPackageContent(icon) end menu:addItem(MineOSCore.localization.launchWithArguments).onTouch = function() MineOSInterface.launchWithArguments(MineOSInterface.mainContainer, icon.path) end menu:addSeparator() end -- if icon.extension ~= ".app" then -- menu:addItem(MineOSCore.localization.addToFavourites).onTouch = function() -- end -- end else if icon.isShortcut then menu:addItem(MineOSCore.localization.editShortcut).onTouch = function() MineOSInterface.editShortcut(MineOSInterface.mainContainer, icon.path) computer.pushSignal("MineOSCore", "updateFileList") end menu:addItem(MineOSCore.localization.showContainingFolder).onTouch = function() icon.parent.parent.launchers.showContainingFolder(icon) end menu:addSeparator() else if MineOSCore.properties.extensionAssociations[icon.extension] and MineOSCore.properties.extensionAssociations[icon.extension].contextMenu then pcall(loadfile(MineOSCore.properties.extensionAssociations[icon.extension].contextMenu), icon, menu) menu:addSeparator() end -- local subMenu = menu:addSubMenu(MineOSCore.localization.openWith) -- local fileList = fs.sortedList(MineOSPaths.applications, "name") -- subMenu:addItem(MineOSCore.localization.select) -- subMenu:addSeparator() -- for i = 1, #fileList do -- subMenu:addItem(fileList[i].nameWithoutExtension) -- end end end end if #selectedIcons > 1 then menu:addItem(MineOSCore.localization.newFolderFromChosen .. " (" .. #selectedIcons .. ")").onTouch = function() MineOSInterface.newFolderFromChosen(MineOSInterface.mainContainer, icon.parent.parent, eventData[3], eventData[4], selectedIcons) end menu:addSeparator() end menu:addItem(MineOSCore.localization.archive .. (#selectedIcons > 1 and " (" .. #selectedIcons .. ")" or "")).onTouch = function() local itemsToArchive = {} for i = 1, #selectedIcons do table.insert(itemsToArchive, selectedIcons[i].path) end local success, reason = require("archive").pack(fs.path(icon.path) .. "/Archive.arc", itemsToArchive) if not success then GUI.error(reason) end computer.pushSignal("MineOSCore", "updateFileList") end local function cutOrCopy(cut) for i = 1, #icon.parent.children do icon.parent.children[i].cut = nil end MineOSCore.clipboard = {cut = cut} for i = 1, #selectedIcons do selectedIcons[i].cut = cut table.insert(MineOSCore.clipboard, selectedIcons[i].path) end end menu:addItem(MineOSCore.localization.cut).onTouch = function() cutOrCopy(true) end menu:addItem(MineOSCore.localization.copy).onTouch = function() cutOrCopy() end if not icon.isShortcut or #selectedIcons > 1 then local subMenu = menu:addSubMenu(MineOSCore.localization.createShortcut) subMenu:addItem(MineOSCore.localization.inCurrentDirectory).onTouch = function() for i = 1, #selectedIcons do if not selectedIcons[i].isShortcut then MineOSCore.createShortcut( fs.path(selectedIcons[i].path) .. "/" .. selectedIcons[i].nameWithoutExtension .. ".lnk", selectedIcons[i].path ) end end computer.pushSignal("MineOSCore", "updateFileList") end subMenu:addItem(MineOSCore.localization.onDesktop).onTouch = function() for i = 1, #selectedIcons do if not selectedIcons[i].isShortcut then MineOSCore.createShortcut( MineOSPaths.desktop .. "/" .. selectedIcons[i].nameWithoutExtension .. ".lnk", selectedIcons[i].path ) end end computer.pushSignal("MineOSCore", "updateFileList") end end if #selectedIcons == 1 then menu:addItem(MineOSCore.localization.rename).onTouch = function() MineOSInterface.rename(MineOSInterface.mainContainer, icon.path) end end menu:addItem(MineOSCore.localization.delete).onTouch = function() for i = 1, #selectedIcons do if fs.path(selectedIcons[i].path) == MineOSPaths.trash then fs.remove(selectedIcons[i].path) else local newName = MineOSPaths.trash .. selectedIcons[i].name local clearName = selectedIcons[i].nameWithoutExtension local repeats = 1 while fs.exists(newName) do newName, repeats = MineOSPaths.trash .. clearName .. string.rep("-copy", repeats) .. selectedIcons[i].extension, repeats + 1 end fs.rename(selectedIcons[i].path, newName) end end computer.pushSignal("MineOSCore", "updateFileList") end menu:addSeparator() if #selectedIcons == 1 then menu:addItem(MineOSCore.localization.addToDock).onTouch = function() MineOSInterface.mainContainer.dockContainer.addIcon(icon.path).keepInDock = true MineOSInterface.mainContainer.dockContainer.saveToOSSettings() end end menu:addItem(MineOSCore.localization.properties).onTouch = function() for i = 1, #selectedIcons do MineOSInterface.propertiesWindow(eventData[3], eventData[4], 40, selectedIcons[i]) end end menu:show() icon.parent.parent:deselectAll() MineOSInterface.OSDraw() end ----------------------------------------------------------------------------------------------------------------------------------- function MineOSInterface.addUniversalContainer(parentContainer, title) local container = parentContainer:addChild(GUI.container(1, 1, parentContainer.width, parentContainer.height)) container.panel = container:addChild(GUI.panel(1, 1, container.width, container.height, MineOSCore.properties.transparencyEnabled and 0x0 or MineOSCore.properties.backgroundColor, MineOSCore.properties.transparencyEnabled and 0.2)) container.layout = container:addChild(GUI.layout(1, 1, container.width, container.height, 3, 1)) container.layout.defaultColumn = 2 container.layout:setColumnWidth(1, GUI.sizePolicies.percentage, 0.375) container.layout:setColumnWidth(2, GUI.sizePolicies.percentage, 0.25) container.layout:setColumnWidth(3, GUI.sizePolicies.percentage, 0.375) container.layout:setCellFitting(2, 1, true, false) if title then container.layout:addChild(GUI.label(1, 1, 1, 1, 0xE1E1E1, title)):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top) end container.panel.eventHandler = function(mainContainer, object, eventData) if eventData[1] == "touch" then container:delete() mainContainer:draw() buffer.draw() end end return container end local function addUniversalContainerWithInputTextBox(parentWindow, text, title, placeholder) local container = MineOSInterface.addUniversalContainer(parentWindow, title) container.inputField = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x666666, 0x666666, 0xE1E1E1, 0x2D2D2D, text, placeholder, false)) container.label = container.layout:addChild(GUI.label(1, 1, 36, 1, 0xFF4940, MineOSCore.localization.file .. " " .. MineOSCore.localization.alreadyExists)):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top) container.label.hidden = true return container end local function checkFileToExists(container, path) if fs.exists(path) then container.label.hidden = false container.parent:draw() buffer.draw() else container:delete() fs.makeDirectory(fs.path(path)) return true end end local function checkIconConfigCanSavePosition(iconField, x, y, filename) if iconField.iconConfigEnabled then iconField.iconConfig[filename] = { x = x, y = y } iconField:saveIconConfig() end end function MineOSInterface.newFile(parentWindow, iconField, x, y, path) local container = addUniversalContainerWithInputTextBox(parentWindow, nil, MineOSCore.localization.newFile, MineOSCore.localization.fileName) container.inputField.onInputFinished = function() if checkFileToExists(container, path .. container.inputField.text) then local file = io.open(path .. container.inputField.text, "w") file:close() checkIconConfigCanSavePosition(iconField, x, y, container.inputField.text) MineOSInterface.safeLaunch(MineOSPaths.editor, path .. container.inputField.text) computer.pushSignal("MineOSCore", "updateFileList") end end parentWindow:draw() buffer.draw() end function MineOSInterface.newFolder(parentWindow, iconField, x, y, path) local container = addUniversalContainerWithInputTextBox(parentWindow, nil, MineOSCore.localization.newFolder, MineOSCore.localization.folderName) container.inputField.onInputFinished = function() if checkFileToExists(container, path .. container.inputField.text) then fs.makeDirectory(path .. container.inputField.text) checkIconConfigCanSavePosition(iconField, x, y, container.inputField.text .. "/") computer.pushSignal("MineOSCore", "updateFileList") end end parentWindow:draw() buffer.draw() return container end function MineOSInterface.newFileFromURL(parentWindow, iconField, x, y, path) local container = addUniversalContainerWithInputTextBox(parentWindow, nil, "Загрузить файл по URL", MineOSCore.localization.fileName) container.inputFieldURL = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x666666, 0x666666, 0xE1E1E1, 0x2D2D2D, nil, "URL", false)) container.inputField.onInputFinished = function() if container.inputField.text then if fs.exists(path .. container.inputField.text) then container.label.hidden = false parentWindow:draw() buffer.draw() else if container.inputFieldURL.text then local success, reason = require("web").download(container.inputFieldURL.text, path .. container.inputField.text) if not success then GUI.error(reason) end container:delete() checkIconConfigCanSavePosition(iconField, x, y, container.inputField.text) computer.pushSignal("MineOSCore", "updateFileList") end end end end container.inputFieldURL.onInputFinished = container.inputField.onInputFinished parentWindow:draw() buffer.draw() end function MineOSInterface.newApplication(parentWindow, iconField, x, y, path) local container = addUniversalContainerWithInputTextBox(parentWindow, nil, MineOSCore.localization.newApplication, MineOSCore.localization.applicationName) local filesystemChooser = container.layout:addChild(GUI.filesystemChooser(1, 1, 36, 3, 0xE1E1E1, 0x666666, 0x444444, 0x999999, nil, MineOSCore.localization.open, MineOSCore.localization.cancel, MineOSCore.localization.iconPath, "/")) filesystemChooser:addExtensionFilter(".pic") filesystemChooser:moveBackward() container.panel.eventHandler = function(mainContainer, object, eventData) if eventData[1] == "touch" then container:delete() if container.inputField.text then local finalPath = path .. container.inputField.text .. ".app/" if checkFileToExists(container, finalPath) then fs.makeDirectory(finalPath .. "/Resources/") fs.copy(filesystemChooser.path or MineOSPaths.icons .. "SampleIcon.pic", finalPath .. "/Resources/Icon.pic") local file = io.open(finalPath .. "Main.lua", "w") file:write("require(\"GUI\").error(\"Hello world\")") file:close() checkIconConfigCanSavePosition(iconField, x, y, container.inputField.text .. ".app/") computer.pushSignal("MineOSCore", "updateFileList") end else parentWindow:draw() buffer.draw() end end end parentWindow:draw() buffer.draw() end function MineOSInterface.newFolderFromChosen(parentWindow, iconField, x, y, selectedIcons) local container = addUniversalContainerWithInputTextBox(parentWindow, nil, MineOSCore.localization.newFolderFromChosen .. " (" .. #selectedIcons .. ")", MineOSCore.localization.folderName) container.inputField.onInputFinished = function() local path = fs.path(selectedIcons[1].path) .. container.inputField.text if checkFileToExists(container, path) then fs.makeDirectory(path) for i = 1, #selectedIcons do fs.rename(selectedIcons[i].path, path .. "/" .. selectedIcons[i].name) end checkIconConfigCanSavePosition(iconField, x, y, container.inputField.text) computer.pushSignal("MineOSCore", "updateFileList") end end parentWindow:draw() buffer.draw() return container end function MineOSInterface.rename(parentWindow, path) local container = addUniversalContainerWithInputTextBox(parentWindow, fs.name(path), MineOSCore.localization.rename, MineOSCore.localization.newName) container.inputField.onInputFinished = function() if checkFileToExists(container, fs.path(path) .. container.inputField.text) then fs.rename(path, fs.path(path) .. container.inputField.text) computer.pushSignal("MineOSCore", "updateFileList") end end parentWindow:draw() buffer.draw() end function MineOSInterface.editShortcut(parentWindow, path) local text = MineOSCore.readShortcut(path) local container = addUniversalContainerWithInputTextBox(parentWindow, text, MineOSCore.localization.editShortcut, MineOSCore.localization.rename) container.panel.eventHandler = nil container.inputField.onInputFinished = function() if fs.exists(container.inputField.text) then MineOSCore.createShortcut(path, container.inputField.text) container:delete() computer.pushSignal("MineOSCore", "updateFileList") else container.label.text = MineOSCore.localization.shortcutIsCorrupted container.label.hidden = false MineOSInterface.OSDraw() end end parentWindow:draw() buffer.draw() end function MineOSInterface.launchWithArguments(parentWindow, path) local container = addUniversalContainerWithInputTextBox(parentWindow, nil, MineOSCore.localization.launchWithArguments) container.inputField.onInputFinished = function() local args = {} if container.inputField.text then for arg in container.inputField.text:gmatch("[^%s]+") do table.insert(args, arg) end end container:delete() MineOSInterface.clearTerminal() if MineOSInterface.safeLaunch(path, table.unpack(args)) then MineOSInterface.waitForPressingAnyKey() end parentWindow:draw() buffer.draw(true) end end ----------------------------------------- Windows patterns ----------------------------------------- local function windowResize(window, width, height) window.width, window.height = width, height if window.onResize then window.onResize(width, height) end return window end function MineOSInterface.addWindow(window) window.x = math.floor(MineOSInterface.mainContainer.windowsContainer.width / 2 - window.width / 2) window.y = math.floor(MineOSInterface.mainContainer.windowsContainer.height / 2 - window.height / 2) MineOSInterface.mainContainer.windowsContainer:addChild(window) -- Получаем путь исполняемого файла local dockPath = MineOSCore.lastLaunchPath or "/OS.lua" local dockPathPath = fs.path(dockPath) if fs.extension(dockPathPath) == ".app" then dockPath = dockPathPath end -- Хуячим иконку в докыч, если такой еще не существует local dockIcon for i = 1, #MineOSInterface.mainContainer.dockContainer.children do if MineOSInterface.mainContainer.dockContainer.children[i].path == dockPath then dockIcon = MineOSInterface.mainContainer.dockContainer.children[i] break end end dockIcon = dockIcon or MineOSInterface.mainContainer.dockContainer.addIcon(dockPath, window) -- Ебурим ссылку на окно в иконку dockIcon.windows = dockIcon.windows or {} dockIcon.windows[window] = true -- Смещаем окно правее и ниже, если уже есть открыте окна этой софтины local lastIndex for i = #MineOSInterface.mainContainer.windowsContainer.children, 1, -1 do if MineOSInterface.mainContainer.windowsContainer.children[i] ~= window and dockIcon.windows[MineOSInterface.mainContainer.windowsContainer.children[i]] then lastIndex = i break end end if lastIndex then window.localX, window.localY = MineOSInterface.mainContainer.windowsContainer.children[lastIndex].localX + 4, MineOSInterface.mainContainer.windowsContainer.children[lastIndex].localY + 2 end window.resize = windowResize window.close = function(window) local sameIconExists = false for i = 1, #MineOSInterface.mainContainer.dockContainer.children do if MineOSInterface.mainContainer.dockContainer.children[i].path == dockPath and MineOSInterface.mainContainer.dockContainer.children[i].windows and table.size(MineOSInterface.mainContainer.dockContainer.children[i].windows) > 1 then MineOSInterface.mainContainer.dockContainer.children[i].windows[window] = nil sameIconExists = true break end end if not sameIconExists then dockIcon.windows = nil if not dockIcon.keepInDock then dockIcon:delete() MineOSInterface.mainContainer.dockContainer.sort() end end window:delete() end window.maximize = function(window) if window.maximized then window.localX = window.oldGeometry.x window.localY = window.oldGeometry.y window:resize(window.oldGeometry.width, window.oldGeometry.height) else window.oldGeometry = { x = window.localX, y = window.localY, width = window.width, height = window.height } window.localX, window.localY = 1, 1 window:resize(window.parent.width, window.parent.height) end window.maximized = not window.maximized MineOSInterface.OSDraw() end window.minimize = function(window) window.hidden = true MineOSInterface.OSDraw() end if window.actionButtons then window.actionButtons.close.onTouch = function() window.close(window) end window.actionButtons.maximize.onTouch = function() window.maximize(window) end window.actionButtons.minimize.onTouch = function() window.minimize(window) end end MineOSCore.lastLaunchPath = nil return MineOSInterface.mainContainer, window end ----------------------------------------------------------------------------------------------------------------------------------- local function addKeyAndValue(window, x, y, key, value) x = x + window:addChild(GUI.label(x, y, unicode.len(key) + 1, 1, 0x333333, key .. ":")).width + 1 return window:addChild(GUI.label(x, y, unicode.len(value), 1, 0x555555, value)) end function MineOSInterface.propertiesWindow(x, y, width, icon) local mainContainer, window = MineOSInterface.addWindow(MineOSInterface.titledWindow(x, y, width, 1, package.loaded.MineOSCore.localization.properties)) window.backgroundPanel.colors.transparency = 0.2 window:addChild(GUI.image(2, 3, icon.image)) local x, y = 11, 3 addKeyAndValue(window, x, y, package.loaded.MineOSCore.localization.type, icon.extension and icon.extension or (icon.isDirectory and package.loaded.MineOSCore.localization.folder or package.loaded.MineOSCore.localization.unknown)); y = y + 1 local fileSizeLabel = addKeyAndValue(window, x, y, package.loaded.MineOSCore.localization.size, icon.isDirectory and package.loaded.MineOSCore.localization.calculatingSize or string.format("%.2f", fs.size(icon.path) / 1024) .. " KB"); y = y + 1 addKeyAndValue(window, x, y, package.loaded.MineOSCore.localization.date, os.date("%d.%m.%y, %H:%M", math.floor(fs.lastModified(icon.path) / 1000))); y = y + 1 addKeyAndValue(window, x, y, package.loaded.MineOSCore.localization.path, " ") local textBox = window:addChild(GUI.textBox(17, y, window.width - 18, 1, nil, 0x555555, {icon.path}, 1, 0, 0, true, true)) textBox.eventHandler = nil window.actionButtons.minimize:delete() window.actionButtons.maximize:delete() window.height = textBox.y + textBox.height window.backgroundPanel.width = window.width window.backgroundPanel.height = textBox.y + textBox.height mainContainer:draw() buffer.draw() if icon.isDirectory then fileSizeLabel.text = string.format("%.2f", fs.directorySize(icon.path) / 1024) .. " KB" mainContainer:draw() buffer.draw() end end ----------------------------------------------------------------------------------------------------------------------------------- local function GUICopy(parentContainer, fileList, toPath) local applyYes, breakRecursion local container = MineOSInterface.addUniversalContainer(parentContainer, MineOSCore.localization.copying) local textBox = container.layout:addChild(GUI.textBox(1, 1, container.width, 1, nil, 0x777777, {}, 1, 0, 0, true, true):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top)) local switchAndLabel = container.layout:addChild(GUI.switchAndLabel(1, 1, 37, 8, 0x66DB80, 0x1E1E1E, 0xE1E1E1, 0x777777, MineOSCore.localization.applyToAll .. ":", false)) container.panel.eventHandler = nil local buttonsLayout = container.layout:addChild(GUI.layout(1, 1, 1, 1, 1, 1)) buttonsLayout:addChild(GUI.button(1, 1, 11, 1, 0xE1E1E1, 0x2D2D2D, 0xAAAAAA, 0x2D2D2D, MineOSCore.localization.yes)).onTouch = function() applyYes = true parentContainer:stopEventHandling() end buttonsLayout:addChild(GUI.button(1, 1, 11, 1, 0xE1E1E1, 0x2D2D2D, 0xAAAAAA, 0x2D2D2D, MineOSCore.localization.no)).onTouch = function() parentContainer:stopEventHandling() end buttonsLayout:addChild(GUI.button(1, 1, 11, 1, 0xE1E1E1, 0x2D2D2D, 0xAAAAAA, 0x2D2D2D, MineOSCore.localization.cancel)).onTouch = function() breakRecursion = true parentContainer:stopEventHandling() end buttonsLayout:setCellDirection(1, 1, GUI.directions.horizontal) buttonsLayout:setCellSpacing(1, 1, 2) buttonsLayout:fitToChildrenSize(1, 1) local function copyOrMove(path, finalPath) switchAndLabel.hidden = true buttonsLayout.hidden = true textBox.lines = { MineOSCore.localization.copying .. " " .. MineOSCore.localization.faylaBlyad .. " " .. fs.name(path) .. " " .. MineOSCore.localization.toDirectory .. " " .. string.canonicalPath(toPath), } textBox.height = #textBox.lines parentContainer:draw() buffer.draw() fs.remove(finalPath) fs.copy(path, finalPath) end local function recursiveCopy(path, toPath) local finalPath = toPath .. "/" .. fs.name(path) if fs.isDirectory(path) then fs.makeDirectory(finalPath) for file in fs.list(path) do if breakRecursion then return end recursiveCopy(path .. "/" .. file, finalPath) end else if fs.exists(finalPath) then if not switchAndLabel.switch.state then switchAndLabel.hidden = false buttonsLayout.hidden = false applyYes = false textBox.lines = { MineOSCore.localization.file .. " " .. fs.name(path) .. " " .. MineOSCore.localization.alreadyExists .. " " .. MineOSCore.localization.inDirectory .. " " .. string.canonicalPath(toPath), MineOSCore.localization.needReplace, } textBox.height = #textBox.lines parentContainer:draw() buffer.draw() parentContainer:startEventHandling() parentContainer:draw() buffer.draw() end if applyYes then copyOrMove(path, finalPath) end else copyOrMove(path, finalPath) end end end for i = 1, #fileList do recursiveCopy(fileList[i], toPath) end container:delete() parentContainer:draw() buffer.draw() end function MineOSInterface.copy(what, toPath) if type(what) == "string" then what = {what} end GUICopy(MineOSInterface.mainContainer, what, toPath) end function MineOSInterface.addMenuWidget(object) MineOSInterface.mainContainer.menuLayout:addChild(object) object:moveToBack() return object end ----------------------------------------------------------------------------------------------------------------------------------- function MineOSInterface.showErrorWindow(path, line, traceback) buffer.clear(0x0, 0.5) local mainContainer = GUI.container(1, 1, buffer.getWidth(), math.floor(buffer.getHeight() * 0.45)) mainContainer.y = math.floor(buffer.getHeight() / 2 - mainContainer.height / 2) mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, 3, 0x383838)) mainContainer:addChild(GUI.label(1, 2, mainContainer.width, 1, 0xFFFFFF, MineOSCore.localization.errorWhileRunningProgram .. "\"" .. fs.name(path) .. "\"")):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top) local actionButtons = mainContainer:addChild(GUI.actionButtons(2, 2, false)) local sendToDeveloperButton = mainContainer:addChild(GUI.adaptiveButton(9, 1, 2, 1, 0x444444, 0xFFFFFF, 0x343434, 0xFFFFFF, MineOSCore.localization.sendFeedback)) local codeView = mainContainer:addChild(GUI.codeView(1, 4, math.floor(mainContainer.width * 0.62), mainContainer.height - 3, {}, 1, 1, 100, {}, {[line] = 0xFF4444}, true, 2)) codeView.scrollBars.horizontal.hidden = true codeView.fromLine = line - math.floor((mainContainer.height - 3) / 2) + 1 if codeView.fromLine <= 0 then codeView.fromLine = 1 end local toLine, lineCounter = codeView.fromLine + codeView.height - 1, 1 for line in io.lines(path) do if lineCounter >= codeView.fromLine and lineCounter <= toLine then codeView.lines[lineCounter] = string.gsub(line, " ", " ") elseif lineCounter < codeView.fromLine then codeView.lines[lineCounter] = " " elseif lineCounter > toLine then break end lineCounter = lineCounter + 1 if lineCounter % 200 == 0 then os.sleep(0.1) end end mainContainer:addChild(GUI.textBox(codeView.width + 1, 4, mainContainer.width - codeView.width, codeView.height, 0xFFFFFF, 0x0, string.wrap(MineOSCore.parseErrorMessage(traceback, 4), mainContainer.width - codeView.width - 2), 1, 1, 0)) actionButtons.close.onTouch = function() mainContainer:stopEventHandling() end mainContainer.eventHandler = function(mainContainer, object, eventData) if eventData[1] == "key_down" and eventData[4] == 28 then actionButtons.close.onTouch() end end sendToDeveloperButton.onTouch = function() if component.isAvailable("internet") then local url = "https://api.mcmodder.ru/ECS/report.php?path=" .. path .. "&errorMessage=" .. string.optimizeForURLRequests(traceback) local success, reason = component.internet.request(url) if success then success:close() end sendToDeveloperButton.text = MineOSCore.localization.sendedFeedback mainContainer:draw() buffer.draw() os.sleep(1) end actionButtons.close.onTouch() end mainContainer:draw() buffer.draw() for i = 1, 3 do component.computer.beep(1500, 0.08) end mainContainer:startEventHandling() end function MineOSInterface.safeLaunch(...) local success, path, line, traceback = MineOSCore.safeLaunch(...) if not success then MineOSInterface.showErrorWindow(path, line, traceback) end return success, path, line, traceback end ----------------------------------------- Window object ----------------------------------------- local function windowDraw(window) GUI.windowShadow(window.x, window.y, window.width, window.height, MineOSInterface.colors.windows.shadowTransparency, true) GUI.drawContainerContent(window) return window end local function windowCheck(window, x, y) for i = #window.children, 1, -1 do if window.children[i].children then if windowCheck(window.children[i], x, y) then return true end elseif window.children[i].eventHandler and window.children[i]:isClicked(x, y) then return true end end end local function windowEventHandler(mainContainer, window, eventData) if eventData[1] == "touch" and not windowCheck(window, eventData[3], eventData[4]) then window.lastTouchPosition = { x = eventData[3], y = eventData[4] } if window ~= window.parent.children[#window.parent.children] then window:moveToFront() mainContainer:draw() buffer.draw() end elseif eventData[1] == "drag" and window.lastTouchPosition and not windowCheck(window, eventData[3], eventData[4]) then local xOffset, yOffset = eventData[3] - window.lastTouchPosition.x, eventData[4] - window.lastTouchPosition.y window.lastTouchPosition.x, window.lastTouchPosition.y = eventData[3], eventData[4] if xOffset ~= 0 or yOffset ~= 0 then window.localX, window.localY = window.localX + xOffset, window.localY + yOffset mainContainer:draw() buffer.draw() end elseif eventData[1] == "drop" then window.lastTouchPosition = nil end end function MineOSInterface.windowFromContainer(container) container.eventHandler = windowEventHandler container.draw = windowDraw return container end function MineOSInterface.window(x, y, width, height) return MineOSInterface.windowFromContainer(GUI.container(x, y, width, height)) end function MineOSInterface.filledWindow(x, y, width, height, backgroundColor) local window = MineOSInterface.window(x, y, width, height) window.backgroundPanel = window:addChild(GUI.panel(1, 1, width, height, backgroundColor)) window.actionButtons = window:addChild(GUI.actionButtons(2, 2, false)) return window end function MineOSInterface.titledWindow(x, y, width, height, title, addTitlePanel) local window = MineOSInterface.filledWindow(x, y, width, height, MineOSInterface.colors.windows.backgroundPanel) if addTitlePanel then window.titlePanel = window:addChild(GUI.panel(1, 1, width, 1, MineOSInterface.colors.windows.title.background)) window.backgroundPanel.localY, window.backgroundPanel.height = 2, window.height - 1 end window.titleLabel = window:addChild(GUI.label(1, 1, width, height, MineOSInterface.colors.windows.title.text, title)):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top) window.actionButtons.localY = 1 window.actionButtons:moveToFront() return window end function MineOSInterface.tabbedWindow(x, y, width, height, ...) local window = MineOSInterface.filledWindow(x, y, width, height, MineOSInterface.colors.windows.backgroundPanel) window.tabBar = window:addChild(GUI.tabBar(1, 1, window.width, 3, 2, 0, MineOSInterface.colors.windows.tabBar.default.background, MineOSInterface.colors.windows.tabBar.default.text, MineOSInterface.colors.windows.tabBar.selected.background, MineOSInterface.colors.windows.tabBar.selected.text, ...)) window.backgroundPanel.localY, window.backgroundPanel.height = 4, window.height - 3 window.actionButtons:moveToFront() window.actionButtons.localY = 2 return window end ----------------------------------------------------------------------------------------------------------------------------------- MineOSInterface.cacheIconSource("folder", MineOSPaths.icons .. "Folder.pic") MineOSInterface.cacheIconSource("fileNotExists", MineOSPaths.icons .. "FileNotExists.pic") MineOSInterface.cacheIconSource("application", MineOSPaths.icons .. "Application.pic") MineOSInterface.cacheIconSource("trash", MineOSPaths.icons .. "Trash.pic") MineOSInterface.cacheIconSource("script", MineOSPaths.icons .. "Script.pic") ----------------------------------------------------------------------------------------------------------------------------------- return MineOSInterface