-- rafftnixGUI.lua -- author: rafftnix -- date: 26.05.2013 -- Keine Veränderung ohne meine Erlaubnis! -- No modification without my permission! -- history: -- v1.0 (August 2013 - GIANTS mod contest Agricultural Service Company hand in) -- v1.1 (December 2013) - Forrest mod -- v1.2 (November 2014) - scale and shiftableButton implemented, bugfixes -- v1.3 (January 2015) - added textFocus, absolute image paths and mouseOver grafics -- v1.4 (March 2015) - added scroll bar RafftnixGUIHelper = {} RafftnixGUIHelper.modDir = g_currentModDirectory; function RafftnixGUIHelper:loadMap() if not self.gameIsOn then self.GUIs = {} --g_currentMission.rafftnixGUIHelper = self; self.gameIsOn = true; end; end; function RafftnixGUIHelper:deleteMap() if self.gameIsOn then for a=1, table.getn(self.GUIs) do if self.GUIs[a].delete ~= nil then self.GUIs[a]:delete(); end; end; self.gameIsOn = false; end; end; function RafftnixGUIHelper:mouseEvent(posX, posY, isDown, isUp, button) for a=1, table.getn(self.GUIs) do if self.GUIs[a].mouseEvent ~= nil then self.GUIs[a]:mouseEvent(posX, posY, isDown, isUp, button); end; end; end; function RafftnixGUIHelper:keyEvent(unicode, sym, modifier, isDown) for a=1, table.getn(self.GUIs) do if self.GUIs[a].keyEvent ~= nil then self.GUIs[a]:keyEvent(unicode, sym, modifier, isDown); end; end; end; function RafftnixGUIHelper:update(dt) for a=1, table.getn(self.GUIs) do if self.GUIs[a].update ~= nil then self.GUIs[a]:update(dt); end; end; end; function RafftnixGUIHelper:draw() for a=1, table.getn(self.GUIs) do if self.GUIs[a].draw ~= nil then self.GUIs[a]:draw(); end; end; end; addModEventListener(RafftnixGUIHelper); RafftnixGUI = {} RafftnixGUI.version = 1.3; local RafftnixGUI_mt = Class(RafftnixGUI); function RafftnixGUI:new(mt, fullScreenMode, mouseActive) local self = {} if mt == nil then mt = RafftnixGUI_mt; end; setmetatable(self, mt); table.insert(RafftnixGUIHelper.GUIs, self); self.baseElement = self:createElement(nil, 0, 0, 1, 1); self.baseElement:setVisibility(false); self.fullScreenMode = fullScreenMode; self.mouseActive = mouseActive; self.mousePosXBackup = 0.5; self.mousePosYBackup = 0.5; return self; end; function RafftnixGUI:open() if g_gui.currentGui ~= nil and g_gui.currentGui.baseElement ~= nil and g_gui.currentGui.fullScreenMode ~= nil then g_gui.currentGui:close(); end; self.baseElement.visibility = true; if self.fullScreenMode then g_gui:showGui("RafftnixGUI"); g_currentMission.isPlayerFrozen = true; g_gui.currentGui = self; -- stop vehicles if a gui is open and you can not control the vehicle --if g_currentMission.controlledVehicle ~= nil and g_currentMission.controlledVehicle.motor ~= nil then -- g_currentMission.controlledVehicle.motor:setSpeedLevel(0, true) --end; end; if self.mouseActive then InputBinding.setShowMouseCursor(true); end; end; function RafftnixGUI:close() self.baseElement.visibility = false; if self.fullScreenMode then InputBinding.setShowMouseCursor(false); g_currentMission.isPlayerFrozen = false; g_gui.currentGui = nil; end; if self.mouseActive then InputBinding.setShowMouseCursor(false); end; end; function RafftnixGUI:onOpen() self:open(); end; function RafftnixGUI:onClose() self:close(); end; function RafftnixGUI:delete() self:deleteElement(self.baseElement); end; function RafftnixGUI:deleteElement(element) if element.overlayId ~= nil then delete(element.overlayId); element.overlayId = nil; end; for a=1, table.getn(element.elements) do self:deleteElement(element.elements[a]); end; element = nil; end; function RafftnixGUI:createElement(baseElement, posX, posY, width, height) local element = {} element.elements = {} element.visibility = true; element.posX = posX; element.posY = posY; element.width = width; element.height = height; element.isDisabled = false; element.isSelected = false; element.isSelectable = true; element.mouseOver = false; element.scaleX = 1; element.scaleY = 1; function element:setVisibility(visibility) element.visibility = visibility; end; function element:setPosition(posX, posY) element.posX = posX; element.posY = posY; end; function element:setScale(scaleX, scaleY) element.scaleX = scaleX; element.scaleY = scaleY; end; function element:setDimension(width, height) element.width = width; element.height = height; end; function element:setIsSelectable(isSelectable) element.isSelectable = isSelectable; end; function element:setDisabled(state) element.isDisabled = state; if state then if element.textColor ~= nil then element.textColor[4] = 0.5; end; else if element.textColor ~= nil then element.textColor[4] = 1; end; end; for a=1, table.getn(element.elements) do element.elements[a]:setDisabled(state); end; end; function element:registerMouseOver(filename, absolutePath) if absolutePath == nil or not absolutePath then filename = RafftnixGUIHelper.modDir..filename; end; element.mouseOverOverlayId = createImageOverlay(filename); end; if baseElement ~= nil then table.insert(baseElement.elements, element); end; return element; end; function RafftnixGUI:createText(baseElement, posX, posY, width, height, text, textSize, textColor) local element = self:createElement(baseElement, posX, posY, width, height); element.text = text; element.textSize = textSize; element.textFocusX = "left"; element.textFocusY = "bottom"; if textColor == 1 then element.textColor = {1, 1, 1, 1}; else element.textColor = textColor; end; function element:setText(text) element.text = text; end; function element:setTextColor(textColor) element.textColor = textColor; end; function element:setTextFocus(focusX, focusY) -- left middle right, top center bottom element.textFocusX = focusX; element.textFocusY = focusY; end; return element; end; function RafftnixGUI:createButton(baseElement, posX, posY, width, height, text, textSize, textColor, onClickFunction, object) local element = self:createText(baseElement, posX, posY, width, height, text, textSize, textColor, object); element.onClickFunction = onClickFunction; element.onClickCallbackTarget = object; return element; end; function RafftnixGUI:createTextInput(baseElement, posX, posY, width, height, defaultText, textSize, textColor, maxCharacters) local element = self:createText(baseElement, posX, posY, width, height, defaultText, textSize, textColor); element.maxCharacters = Utils.getNoNil(maxCharacters, 30); element.textInput = true; return element; end; function RafftnixGUI:createList(baseElement, posX, posY, width, height, heightPerEntry, maxVisibleEntrys, selectEntryCallbackFunction, selectEntryCallbackTarget, markSelectedItem) local element = self:createElement(baseElement, posX, posY, width, height); element.isList = true; element.maxVisibleEntrys = maxVisibleEntrys; element.selectedEntry = 1; element.highestVisibleEntry = 1; element.heightPerEntry = heightPerEntry; element.selectEntryCallbackFunction = selectEntryCallbackFunction; element.selectEntryCallbackTarget = selectEntryCallbackTarget; element.markSelectedItem = Utils.getNoNil(markSelectedItem, false); function element:scrollUp() element.highestVisibleEntry = math.max(1, element.highestVisibleEntry-1); end; function element:scrollDown() element.highestVisibleEntry = math.min(table.getn(element.elements) - element.maxVisibleEntrys, element.highestVisibleEntry+1); end; return element; end; function RafftnixGUI:createImage(baseElement, posX, posY, width, height, filename, absolutePath) local element = self:createElement(baseElement, posX, posY, width, height); if absolutePath == nil or not absolutePath then filename = RafftnixGUIHelper.modDir..filename; end; element.overlayId = createImageOverlay(filename); return element; end; function RafftnixGUI:createImgButton(baseElement, posX, posY, width, height, filename, onClickFunction, object, absolutePath) local element = self:createButton(baseElement, posX, posY, width, height, "", 1, 1, onClickFunction, object); if absolutePath == nil or not absolutePath then filename = RafftnixGUIHelper.modDir..filename; end; element.overlayId = createImageOverlay(filename); return element; end; function RafftnixGUI:createShiftableImgButton(baseElement, posX, posY, width, height, filename, onClickFunction, object, absolutePath) local element = self:createButton(baseElement, posX, posY, width, height, "", 1, 1, onClickFunction, object); element.isShiftable = true; element.isShifting = false; if absolutePath == nil or not absolutePath then filename = RafftnixGUIHelper.modDir..filename; end; element.overlayId = createImageOverlay(filename); return element; end; function RafftnixGUI:createScrollBar(baseElement, posX, posY, width, height, needleWidth, maxXScroll, onScrollFunction, object, filename, absolutePath) local element = self:createElement(baseElement, posX, posY, width, height); element.onScrollFunction = onScrollFunction; element.onScrollCallbackTarget = object; element.needleWidth = needleWidth; element.maxXScroll = maxXScroll; element.value = 0; function element:computeShiftableImgButton(diffX, diffY) local posX = element.scrollBarNeedle.posX+diffX; posX = math.min(posX, element.maxXScroll-element.needleWidth); posX = math.max(posX, 0); element.scrollBarNeedle:setPosition(posX, element.scrollBarNeedle.posY); element.value = posX/(element.maxXScroll-element.needleWidth); element.onScrollFunction(element.onScrollCallbackTarget, element.value); end; function element:getValue() return element.value; end; function element:setValue(value) value = math.max(0, value); value = math.min(1, value); local posX = (element.maxXScroll-element.needleWidth)*value; element.scrollBarNeedle:setPosition(posX, element.scrollBarNeedle.posY); end; element.scrollBarNeedle = self:createShiftableImgButton(baseElement, 0, 0, needleWidth, height, filename, element.computeShiftableImgButton, element, absolutePath); return element; end; function RafftnixGUI:mouseEvent(posX, posY, isDown, isUp, button) if self.baseElement.visibility then self:checkElementOnMouseEvent(self.baseElement, posX, posY, 0, 0, 1, 1, isDown, isUp, button); end; self.mousePosXBackup = posX; self.mousePosYBackup = posY; end; function RafftnixGUI:keyEvent(unicode, sym, modifier, isDown) if self.baseElement.visibility then self:checkElementOnKeyEvent(self.baseElement, unicode, sym, modifier, isDown); end; end; function RafftnixGUI:checkElementOnKeyEvent(element, unicode, sym, modifier, isDown) if element.textInput ~= nil and element.textInput and element.visibility and isDown then if element.maxCharacters ~= nil and element.maxCharacters > string.len(element.text) then if (unicode > 96 and unicode < 123) or (unicode > 64 and unicode < 91) or unicode == 32 or unicode == 94 or unicode == 93 or unicode == 91 or unicode == 123 or unicode == 125 or unicode == 92 or unicode == 223 or unicode == 63 or unicode == 33 or unicode == 34 or (unicode > 48 and unicode < 60) or (unicode > 39 and unicode < 48) or unicode == 124 then element:setText(element.text .. string.char(unicode)); end; end; if unicode == 8 then local length = string.len(element.text); element:setText(string.sub(element.text, 1, length-1)); end; end; if element.visibility then for a=1, table.getn(element.elements) do self:checkElementOnKeyEvent(element.elements[a], unicode, sym, modifier, isDown); end; end; end; function RafftnixGUI:checkElementOnMouseEvent(element, mouseX, mouseY, xParent, yParent, scaleX, scaleY, isDown, isUp, button) local scaleX = scaleX * element.scaleX; local scaleY = scaleY * element.scaleY; element.mouseOver = false; if element.visibility and not element.isDisabled then if mouseX > ((element.posX*scaleX)+xParent) and mouseX < (((element.posX*scaleX)+xParent) + (element.width*scaleX)) then if mouseY > ((element.posY*scaleY)+yParent) and mouseY < (((element.posY*scaleY)+yParent) + (element.height*scaleY)) then if (element.onClickFunction ~= nil or (element.isList ~= nil and element.isList)) and isDown and button == 1 then if element.isShiftable then element.isShifting = true; elseif element.onClickFunction ~= nil then element.onClickFunction(element.onClickCallbackTarget); end; if element.isList then local y = ((element.posY*scaleX)+yParent) + (element.height*scaleY); local start = element.highestVisibleEntry; local stop = math.min(element.highestVisibleEntry + element.maxVisibleEntrys - 1, table.getn(element.elements)); for a=start, stop do local entry = element.elements[a]; if mouseY < y and mouseY > (y - (element.heightPerEntry*scaleY)) then element.elements[element.selectedEntry].isSelected = false; element.selectEntryCallbackFunction(element.selectEntryCallbackTarget, a); element.selectedEntry = a; element.elements[element.selectedEntry].isSelected = true; break; end; y = y - (element.heightPerEntry*scaleY); end; end; end; element.mouseOver = true; if element.isList ~= nil and element.isList and isDown then if button == 4 then element.scrollUp(); end; if button == 5 then element.scrollDown(); end; end; end; end; if not isDown and button == 1 and element.isShifting then element.isShifting = false; end; end; if element.isShifting then local diffX = mouseX - self.mousePosXBackup; local diffY = mouseY - self.mousePosYBackup; --element.setPosition(element.posX+diffX, element.posY+diffY); element.onClickFunction(element.onClickCallbackTarget, diffX, diffY, element.posX, element.posY); end; if element.visibility then for a=1, table.getn(element.elements) do self:checkElementOnMouseEvent(element.elements[a], mouseX, mouseY, element.posX*scaleX+xParent, element.posY*scaleY+yParent, scaleX, scaleY, isDown, isUp, button); end; end; end; function RafftnixGUI:update(dt) end; function RafftnixGUI:draw() setTextBold(false); self:renderElement(self.baseElement, 0, 0, 1, 1, false); end; function RafftnixGUI:renderElement(element, x, y, scaleX, scaleY, parentElementSelected) scaleX = scaleX * element.scaleX; scaleY = scaleY * element.scaleY; local textScale = math.min(scaleX, scaleY); if element.visibility then x = x + element.posX*scaleX; y = y + element.posY*scaleY; if element.text ~= nil then if element.markSelectedItem and (element.isSelected or parentElementSelected) then setTextColor(0,1,0,element.textColor[4]); else setTextColor(unpack(element.textColor)); end; setTextAlignment(RenderText.ALIGN_LEFT); -- find correckt text position (focus) local textX = 0; local textY = 0; if element.textFocusX == "left" then textX = x; elseif element.textFocusX == "middle" then textX = x + 0.5*(element.width*scaleX) - 0.5*getTextWidth(element.textSize*textScale, element.text); elseif element.textFocusX == "right" then textX = x + (element.width*scaleX) - getTextWidth(element.textSize*textScale, element.text); end; if element.textFocusY == "top" then textY = y + element.height*scaleY - getTextHeight(element.textSize*textScale, element.text); elseif element.textFocusY == "center" then textY = y + 0.5*(element.height*scaleY) - 0.5*getTextHeight(element.textSize*textScale, element.text); elseif element.textFocusY == "bottom" then textY = y; end; renderText(textX, textY, element.textSize*textScale, element.text); setTextColor(1, 1, 1, 1); end; if element.overlayId ~= nil then renderOverlay(element.overlayId, x, y, element.width*scaleX, element.height*scaleY); end; if element.isList ~= nil then -- list local start = element.highestVisibleEntry; local stop = math.min(element.highestVisibleEntry + element.maxVisibleEntrys - 1, table.getn(element.elements)); y = element.posY + element.height; for a=start, stop do if element.elements[a] ~= nil then y = y - element.heightPerEntry; local parentElementSelected = false; if element.selectedEntry == a then parentElementSelected = true; end; self:renderElement(element.elements[a], x, y, scaleX, scaleY, parentElementSelected); end; end; else for a=1, table.getn(element.elements) do self:renderElement(element.elements[a], x, y, scaleX, scaleY, parentElementSelected); end; end; if element.mouseOverOverlayId ~= nil and element.mouseOver then renderOverlay(element.mouseOverOverlayId, x, y, element.width*scaleX, element.height*scaleY); end; end; end;