TI Nspire lua 崩溃并抛出 'break' 错误

TI Nspire lua crashes and throws 'break' error

我正在编写一个脚本来制作一些 windows 我可以在我的 TI Nspire CX CAS 2 计算器上打开和关闭的东西。添加新的 windows 效果很好,但是尝试删除它们会导致我的软件在本应删除 window 的功能完成时崩溃。我也在我的计算器上试过了,它仍然卡住了,但我能够关闭脚本然后重新打开它。当我重新打开它时,第 465 行 a picture of my calculator with the error

出现 'break' 错误
screen = platform.window
screenx,screeny = screen.width(),screen.height()

-- system stuff

local password = nil
local passVerify = nil
local drawObjects = {}
local processes = {}
local rectangle = class()
local backgroundObj = class()
local taskbar = class()
local window = class()
local process = class()
local taskbarHeight = 20
local hasBeenSetup = false
local customSetupProcedures = {}
local isLogedIn = false
local draggingMode = {
move = 0,
left = 1,
top = 2,
right = 3,
bottom = 4
}
local readyToDraw = true
local currentDraggingMode = draggingMode.both
local selectedWindowForDragging = nil
local textures = {}
local processStatus = {
waiting = 0,
running = 1,
halted = 2,
dead = 3
}
local specialColors = {
taskbarColor = 0x00969E,
windowBackgroundColor = 0xF7F7F7,
windowTopBarColor = 0xA2A2A2,
windowCloseButtonColor = 0xFF5100,
windowMaximizeButtonColor = 0x00FF00,
windowMinimizeButtonColor = 0xFFFF00
}

function process:init(run,onKill)
    self.status = processStatus.waiting
    self.co = coroutine.create(run)
    self.onKill = onKill
    coroutine.resume(self.co,self)
end

function process:update()
    if self.status == processStatus.running then
        coroutine.resume(self.co)
    end
end

function process:halt()
    self.status = processStatus.halted
end

function process:resume()
    self.status = processStatus.waiting
end

function process:kill()
    readyToDraw = false
    self.status = processStatus.dead
    self.co = nil
    self.onKill()
    readyToDraw = true
end

function rectangle:init(x,y,width,height)
    self.x = x
    self.y = y
    self.width = width
    self.height = height
end

function rectangle:contains(x,y)
    return self.x<x and x<self.x+self.width and self.y<y and y<self.y+self.height
end

function taskbar:init()

end

function taskbar:draw(gc)
    gc:setColorRGB(specialColors.taskbarColor)
    gc:fillRect(0,screeny-taskbarHeight,screenx,screeny)
    --gc:drawImage(textures[1],0,screeny-20)
end

function taskbar:isWindow()
    return false
end

function window:init(x,y,width,height)
    self.x = x
    self.y = y
    self.draggedOnX = 0
    self.draggedOnY = 0
    self.width = width
    self.height = height
    self.icon = nil
    self.components = {}
    self.focusLevel = 0
    newWindowFocused(#drawObjects+1)
    self.resizable = true
    self:subclassInit(x,y,width,height)
end

function window:subclassInit(x,y,width,height)

end

function window:getFocusLevel()
    return self.focusLevel
end

function window:draw(gc)
    gc:setColorRGB(specialColors.windowBackgroundColor)
    gc:fillRect(self.x,self.y,self.width,self.height)
    gc:setColorRGB(specialColors.windowTopBarColor)
    gc:fillRect(self.x,self.y,self.width,10)
    gc:setColorRGB(specialColors.windowMinimizeButtonColor)
    gc:fillRect(self.x+self.width-29,self.y+1,8,8)
    gc:setColorRGB(specialColors.windowMaximizeButtonColor)
    gc:fillRect(self.x+self.width-19,self.y+1,8,8)
    gc:setColorRGB(specialColors.windowCloseButtonColor)
    gc:fillRect(self.x+self.width-9,self.y+1,8,8)
    self:drawComponents(gc)
end

function window:drawComponents(gc)
    for i in ipairs(self.components) do
        self.components[i]:draw(gc,self)
    end
end

function window:addComponent(component)
    i = #self.components+1
    self.components[i] = component
    return i
end

function window:decreaseFocus()
    self.focusLevel = self.focusLevel+1
end

function window:isWindow()
    return true
end

function window:contains(x,y)
    return self.x<x and self.y < y and self.width+self.x>x and self.height+self.y>y
end

function window:checkForMouse(x,y)
    if y-self.y <= 3 and self.resizable then
        cursor.set('resize row')
    elseif y-self.y>=self.height-3 and self.resizable then
        cursor.set('resize row')
    elseif x-self.x<=3 and self.resizable then
        cursor.set('resize column')
    elseif x-self.x>=self.width-3 and self.resizable then
        cursor.set('resize column')
    end
end

function window:click(x,y,n)
    self:focus(n)
    if y-self.y <= 3 and self.resizable then
       selectedWindowForDragging = self
       currentDraggingMode = draggingMode.top
    elseif y-self.y>=self.height-3 and self.resizable then
       selectedWindowForDragging = self
       currentDraggingMode = draggingMode.bottom
    elseif x-self.x<=3 and self.resizable then
       selectedWindowForDragging = self
       currentDraggingMode = draggingMode.left
    elseif x-self.x>=self.width-3 and self.resizable then
        selectedWindowForDragging = self
        currentDraggingMode = draggingMode.right
    elseif y-self.y <= 10 then
        if x>=self.x+self.width-10 then
            self:close()
        elseif x>=self.x+self.width-20 then
            self:maximize()
        elseif x>=self.x+self.width-30 then
            self:minimize()
        else
            selectedWindowForDragging = self
            currentDraggingMode = draggingMode.move
            self.draggedOnX = x - self.x
            self.draggedOnY = y - self.y
        end
    end
    self:clickComponents(x,y)
end

function window:clickComponents(x,y)
    
end

function newWindowFocused(n)
    for window in ipairs(drawObjects) do
        window = drawObjects[window]
        if window:isWindow() then
            if window:getFocusLevel() < n then
                window:decreaseFocus()
            end
        end
    end
end

function window:focus(n)
    self.focusLevel = -1
    newWindowFocused(n)
end

function window:minimize()
    
end

function window:maximize()

end

function window:close()
    processes[1]:kill()
end

function on.resize()
    screenx = screen:width()
    screeny = screen:height()
end

function on.mouseDown(x,y)
    minFocus = 0
    for i = 0,#drawObjects,1 do
        for windown in ipairs(drawObjects) do
            window=drawObjects[windown]
            if window:isWindow() then
                if window:getFocusLevel() == minFocus then
                    if window:contains(x,y) then
                        window:click(x,y,minFocus)
                        return
                    end
                    minFocus = minFocus+1
                end
            end
        end
    end
end

function on.mouseMove(x,y)
    cursor.set('default')
    if selectedWindowForDragging ~= nil then
        if currentDraggingMode == draggingMode.move then
            selectedWindowForDragging.x = x - selectedWindowForDragging.draggedOnX
            selectedWindowForDragging.y = y - selectedWindowForDragging.draggedOnY
            if selectedWindowForDragging.x+selectedWindowForDragging.draggedOnX<5 then selectedWindowForDragging.x = 5-selectedWindowForDragging.draggedOnX end
            if selectedWindowForDragging.x+selectedWindowForDragging.draggedOnX>screenx-5 then selectedWindowForDragging.x = screenx-selectedWindowForDragging.draggedOnX-5 end
            if selectedWindowForDragging.y<0 then selectedWindowForDragging.y=0 end
            if selectedWindowForDragging.y>screeny-taskbarHeight-5 then selectedWindowForDragging.y = screeny-taskbarHeight-5 end
            elseif currentDraggingMode == draggingMode.top then
                i = selectedWindowForDragging.draggedOnY + y
                selectedWindowForDragging.height = math.max(selectedWindowForDragging.height - selectedWindowForDragging.draggedOnY - y + selectedWindowForDragging.y,20)
                selectedWindowForDragging.y = i
            elseif currentDraggingMode == draggingMode.bottom then
                selectedWindowForDragging.height = math.max(y - selectedWindowForDragging.y,20)
            elseif currentDraggingMode == draggingMode.left then
                i = selectedWindowForDragging.draggedOnX + x
                selectedWindowForDragging.width = math.max(selectedWindowForDragging.width - selectedWindowForDragging.draggedOnX - x + selectedWindowForDragging.x,20)
                selectedWindowForDragging.x = i
            elseif currentDraggingMode == draggingMode.right then
                selectedWindowForDragging.width = math.max(x - selectedWindowForDragging.x,20)
        end
    end
    
        minFocus = 0
        for i = 0,#drawObjects,1 do
            for windown in ipairs(drawObjects) do
                window=drawObjects[windown]
                if window:isWindow() then
                    if window:getFocusLevel() == minFocus then
                        if window:contains(x,y) then
                            window:checkForMouse(x,y)
                            return
                        end
                        minFocus = minFocus+1
                    end
                end
            end
        end
end

function on.mouseUp(x,y)
    selectedWindowForDragging = nil
end

local calculator = class(window)

function calculator:subclassInit()
    self.width = 60
    self.height = 90
    self.resizable = false
    self.number = ''
    print(self.number)
    self.button1 = rectangle(5,22,13,10)
    self.button2 = rectangle(24,22,14,10)
    self.button3 = rectangle(44,22,13,10)
    self.button4 = rectangle(5,34,13,10)
    self.button5 = rectangle(24,34,14,10)
    self.button6 = rectangle(44,34,13,10)
    self.button7 = rectangle(5,45,13,10)
    self.button8 = rectangle(24,45,14,10)
    self.button9 = rectangle(44,45,13,10)
    self.button0 = rectangle(24,57,14,10)
    self.buttonC = rectangle(5,57,13,10)
    self.buttonDot = rectangle(44,57,13,10)
    self.buttonPlus = rectangle(6,70,7,7)
    self.buttonMinus = rectangle(15,70,7,7)
    self.buttonMultiply = rectangle(40,70,7,7)
    self.buttonDivide = rectangle(49,70,7,7)
    self.buttonEquals = rectangle(24,69,14,8)
end

function calculator:drawComponents(gc)
    --gc:drawImage(textures[2],self.x,self.y+10)
    gc:setColorRGB(0x000000)
    gc:setFont('sansserif','r',6)
    if #self.number < 10 then
        gc:drawString(string.sub(self.number,0,#self.number),self.x+5,self.y+13)
    else
        gc:drawString(string.sub(self.number,#self.number-9,#self.number),self.x+5,self.y+13)
        print(self.number)
    end
end

function calculator:clickComponents(x,y)
    x = x-self.x
    y = y-self.y-10
    if self.button1:contains(x,y) then self.number = self.number..'1' elseif
    self.button2:contains(x,y) then self.number = self.number..'2' elseif
    self.button3:contains(x,y) then self.number = self.number..'3' elseif
    self.button4:contains(x,y) then self.number = self.number..'4' elseif
    self.button5:contains(x,y) then self.number = self.number..'5' elseif
    self.button6:contains(x,y) then self.number = self.number..'6' elseif
    self.button7:contains(x,y) then self.number = self.number..'7' elseif
    self.button8:contains(x,y) then self.number = self.number..'8' elseif
    self.button9:contains(x,y) then self.number = self.number..'9' elseif
    self.button0:contains(x,y) then self.number = self.number..'0' elseif
    self.buttonPlus:contains(x,y) then self.number = self.number..'+' elseif
    self.buttonMinus:contains(x,y) then self.number = self.number..'-' elseif
    self.buttonMultiply:contains(x,y) then self.number = self.number..'*' elseif
    self.buttonDivide:contains(x,y) then self.number = self.number..'/' elseif
    self.buttonDot:contains(x,y) then self.number = self.number..'.' elseif
    self.buttonEquals:contains(x,y) then
        if not pcall(function()self.number = ''..math.eval(self.number)end) then self.number = 'error' end
    elseif
    self.buttonC:contains(x,y) then self.number = '' end
end


function backgroundObj:draw(gc)
    gc:setColorRGB(0xC4C4C4)
    gc:fillRect(0,0,screenx,screeny-taskbarHeight)
    --gc:drawImage(textures[0],screenx/2-50,(screeny-taskbarHeight)/2-50)
end

function backgroundObj:isWindow()
    return false
end

function loadTextures()
    --textures[0]=image.new(_R.IMG.logo)
    --textures[1]=image.new(_R.IMG.startMenuButton)
    --textures[2]=image.new(_R.IMG.calculator)
end

function on.construction()
    timer.start(1/30)
    checkForSetup()
    checkForLogin()
end

function on.timer()
    screen:invalidate()
end

function encrypt(str,key)
    result = ''
    for i = 1,#str,1 do
        result = result..string.char(math.abs(string.byte(str:sub(i,i))+key+i)%422)
    end
    return result
end
function decrypt(str,key)
    result = ''
    for i = 1,#str,1 do
       result = result..string.char(math.abs((string.byte(str:sub(i,i))-key-i)%422))
    end
    return result
end

function checkForLogin()
    
end

function checkForSetup()
    if not hasBeenSetup then
        print('loading textures...')
        loadTextures()
        print('setting up graphics...')
        setupGraphics()
        print('executing default setup procedure...')
        print('setting up password...')
        setupPassword()
        print('executing custom setup procedures...')
        for funct in ipairs(customSetupProcedures) do
            funct()
        end
    end
    hasBeenSetup = true
end

function setupGraphics()
    processes[1] = process(
    function(proc)
        drawObjects[1] = backgroundObj()
    end,
    function()
       drawObjects[1] = nil
    end)
    drawObjects[2] = taskbar()
end

function setupPassword()
    w1 = calculator(50,100,100,100)
    drawObjects[3] = w1
    w2 = window(0,50,100,100)
    drawObjects[4] = w2
end

--system stuff

--graphics stuff

function on.paint(gc)
    if readyToDraw then
    if drawObjects[1]~=nil then
    drawObjects[1]:draw(gc)
    end
    focus = 0
    for i in ipairs(drawObjects) do
        window = drawObjects[i]
        if window:isWindow() then
            focus = math.max(focus,window:getFocusLevel())
        end
    end
    while focus>=0 do
        for i in ipairs(drawObjects) do
            window = drawObjects[i]
            if window:isWindow() and window:getFocusLevel()==focus then
                window:draw(gc)
                focus = focus-1
                break
            end
        end
    end
    drawObjects[2]:draw(gc)
    end
end

--graphics stuff

--default programs

--default programs


编辑:我刚刚发现,如果您是 运行 学生软件中的一个脚本并且它卡在一个循环中,按 F12 会中断循环(就像实际计算器上的主页按钮一样).

你的问题是你有机会在 on.paint 函数中进入无限循环。

你在这里检查一下:

    if drawObjects[1]~=nil then
        drawObjects[1]:draw(gc)
    end

如果此检查是 false,则意味着以下 for 循环将 运行 0 次,因为 ipairs 想要首先查看索引 1 或退出:

    focus = 0
    for i in ipairs(drawObjects) do -- Will run 0 times.
        window = drawObjects[i]
        if window:isWindow() then
            focus = math.max(focus,window:getFocusLevel())
        end
    end

这意味着 focus >= 0 的 while 循环条件将无限期地 运行 因为与 ipairs(drawObjects) 相同的问题将发生在循环体中并且 focus 永远不会改变0

    while focus>=0 do
        for i in ipairs(drawObjects) do -- Will run 0 times.
            window = drawObjects[i]
            if window:isWindow() and window:getFocusLevel()==focus then
                window:draw(gc)
                focus = focus-1
                break
            end
        end
    end

您可以通过将 focus 默认为 -1 来解决此问题,另外您需要在使用前检查 drawObjects[2] 是否为 nil。


或者,将支票更改为

可能是更好的选择
    if drawObjects[1] == nil then
       return -- bail out of function we dont have anything to draw
    end
    drawObjects[1]:draw(gc)