--*********************************************************** --** LEMMY/ROBERT JOHNSON ** --*********************************************************** require "ISUI/ISPanel" ISContextMenu = ISPanel:derive("ISContextMenu"); --************************************************************************-- --** ISContextMenu:initialise --** --************************************************************************-- function ISContextMenu:initialise() ISPanel.initialise(self); end function ISContextMenu:isMouseOut() return self.mouseOut; end --************************************************************************-- --** ISContextMenu:onMouseMove --** --************************************************************************-- function ISContextMenu:onMouseMove(dx, dy) self.mouseOut = false; if self:topmostMenuWithMouse(getMouseX(), getMouseY()) ~= self then return end local mouseY = self:getMouseY() local dy = (self:getScrollHeight() > self:getScrollAreaHeight()) and self.scrollIndicatorHgt or 0 mouseY = math.max(self.padTopBottom + dy - self:getYScroll(), mouseY) mouseY = math.min(self.padTopBottom + dy + self:getScrollAreaHeight() - 1 - self:getYScroll(), mouseY) local index = self:getIndexAt(0, mouseY) if index ~= -1 then if self.subMenu and (index ~= self.mouseOver) then self.subMenu:hideSelfAndChildren2() self.subMenu = nil end end self.mouseOver = index end function ISContextMenu:hideSelfAndChildren2() self:setVisible(false) self.mouseOver = -1 for i=1,#self.options do if self.options[i].subOption then if self:getSubMenu(self.options[i].subOption) then self:getSubMenu(self.options[i].subOption):hideSelfAndChildren2() end end end end --************************************************************************-- --** ISContextMenu:onMouseMoveOutside --** --************************************************************************-- function ISContextMenu:onMouseMoveOutside(dx, dy) if self.player == 0 then self.mouseOut = true; self:hideToolTip() end end --************************************************************************-- --** ISContextMenu:onMouseUp --** --************************************************************************-- function ISContextMenu:onMouseUp(x, y) if self:getScrollHeight() > self:getScrollAreaHeight() then if y < self.padTopBottom + self.scrollIndicatorHgt - self:getYScroll() then self:setYScroll(self:getYScroll() + self.itemHgt) self:onMouseMove(0, 0) return end if y >= self.padTopBottom + self.scrollIndicatorHgt + self:getScrollAreaHeight() - self:getYScroll() then self:setYScroll(self:getYScroll() - self.itemHgt) self:onMouseMove(0, 0) return end end if self.mouseOver ~= -1 and self:getIsVisible() then --print("calling option : " .. self.options[self.mouseOver].name); -- we call the function if we have one local option = self.options[self.mouseOver] if option ~= nil and option.onSelect ~= nil and not option.notAvailable then ISContextMenu.globalPlayerContext = self.player; self:closeAll() option.onSelect(option.target, option.param1, option.param2, option.param3, option.param4, option.param5, option.param6, option.param7, option.param8, option.param9, option.param10); end if option ~= nil and option.subOption ~= nil then if self:isOptionSingleMenu() then local subMenu = self:getSubMenu(option.subOption) subMenu.mouseOver = -1 self:displaySubMenu(subMenu, option) end end end end function ISContextMenu:onMouseWheel(del) self:setYScroll(self:getYScroll() - del * self.itemHgt) self:onMouseMove(0, 0) return true end function ISContextMenu:ensureVisible() if not self.mouseOver then return end if self:getScrollHeight() <= self:getScrollAreaHeight() then return end local topY = self.padTopBottom + ((self:getScrollHeight() > self:getScrollAreaHeight()) and self.scrollIndicatorHgt or 0) local topItem = math.floor((topY - self:getYScroll()) / self.itemHgt) + 1 local numVisibleItems = math.floor(self:getScrollAreaHeight() / self.itemHgt) local bottomItem = topItem + numVisibleItems - 1 if self.mouseOver < topItem then self:setYScroll(0 - (self.mouseOver - 1) * self.itemHgt) elseif self.mouseOver > bottomItem then self:setYScroll(0 - (self.mouseOver - numVisibleItems) * self.itemHgt) end end function ISContextMenu:onFocus(x, y) -- do not call bringToTop(), otherwise the root context menu is drawn before -- its child menus, and render() sets the visibility of the submenu that the -- mouse is over end function ISContextMenu:onJoypadDirUp() if self.subMenu then self.subMenu:hideAndChildren() self.subMenu = nil end if self.mouseOver == nil then self.mouseOver = 1; end self.mouseOver = self.mouseOver - 1; if self.mouseOver <= 0 then self.mouseOver = self.numOptions - 1; end self:hideToolTip() self.mouseOut = false self:ensureVisible() end function ISContextMenu:onJoypadDirDown() if self.subMenu then self.subMenu:hideAndChildren() self.subMenu = nil end if self.mouseOver == nil then self.mouseOver = 1; end self.mouseOver = self.mouseOver + 1; if self.mouseOver >= self.numOptions then self.mouseOver = 1; end self:hideToolTip() self.mouseOut = false self:ensureVisible() end function ISContextMenu:onJoypadDirLeft() if self.parent then if self:isOptionSingleMenu() then local option = self.options[self.mouseOver] if option and option.isDefaultOption then option.onSelect(option.target, option.param1, option.param2, option.param3, option.param4, option.param5, option.param6, option.param7, option.param8, option.param9, option.param10) return end self:displayAncestor(self.parent) return end if self.subMenu then self.subMenu:hideAndChildren() self.subMenu = nil end self.mouseOver = nil self:hideToolTip() setJoypadFocus(self.player, self.parent) end end function ISContextMenu:onJoypadDirRight() local option = self.options[self.mouseOver] if option ~= nil and option.onSelect == nil and option.subOption ~= nil then self:hideToolTip() local subMenu = self:getSubMenu(option.subOption) if self:isOptionSingleMenu() then subMenu.mouseOver = 1 + subMenu:getDefaultOptionCount() self:displaySubMenu(subMenu, option) return end subMenu.forceVisible = true subMenu.mouseOver = 1 setJoypadFocus(self.player, subMenu) subMenu:ensureVisible() end end function ISContextMenu:hideSelf() self:setVisible(false) self.visibleCheck = false self.forceVisible = false self:hideToolTip() end function ISContextMenu:hideAndChildren() self:hideSelf() for _,option in ipairs(self.options) do if option.subOption and self:getSubMenu(option.subOption) then self:getSubMenu(option.subOption):hideAndChildren() end end -- FIXME: this only works for the global context menu for each player if self.instanceMap == nil or #self.instanceMap == 0 then return;end for k, v in ipairs(self.instanceMap) do v:hideAndChildren(); end end function ISContextMenu:showTooltip(option) if self.toolTip and (self.toolTip ~= option.toolTip) then self:hideToolTip() end if not self.toolTip and option.toolTip then self.toolTip = option.toolTip self.toolTip:setVisible(true) self.toolTip:addToUIManager() self.toolTip.followMouse = not self.joyfocus end end function ISContextMenu:hideToolTip() if self.toolTip ~= nil then self.toolTip:removeFromUIManager() self.toolTip:setVisible(false) self.toolTip = nil end end function ISContextMenu:onJoypadDown(button) if button == Joypad.AButton then if self.mouseOver > 0 and self:getIsVisible() then --print("calling option : " .. self.options[self.mouseOver].name); -- we call the function if we have one local option = self.options[self.mouseOver] if option ~= nil and option.onSelect == nil and option.subOption ~= nil then local subMenu = self:getSubMenu(option.subOption) subMenu.mouseOver = 1; if self:isOptionSingleMenu() then subMenu.mouseOver = subMenu.mouseOver + subMenu:getDefaultOptionCount() self:displaySubMenu(subMenu, option) else setJoypadFocus(self.player, subMenu) end elseif option ~= nil and option.onSelect ~= nil and not option.notAvailable then ISContextMenu.globalPlayerContext = self.player; self:closeAll(); option.onSelect(option.target, option.param1, option.param2, option.param3, option.param4, option.param5, option.param6, option.param7, option.param8, option.param9, option.param10); end end end if button == Joypad.BButton then --[[ if self:isOptionSingleMenu() then if self.parent then self:displayAncestor(self.parent) return end end --]] self:closeAll() end end function ISContextMenu:closeAll() self:hideAndChildren() local isJoypad = JoypadState.players[self.player+1] local parent = self.parent if isJoypad and (parent == nil) then setJoypadFocus(self.player, self.origin) end while parent do parent:setVisible(false) if isJoypad and (parent.parent == nil) then setJoypadFocus(self.player, parent.origin) end parent = parent.parent end end function ISContextMenu:onMouseDownOutside(x, y) if self.player == 0 then local forceVisible = self.forceVisible self:hideSelf() self.forceVisible = forceVisible if JoypadState.players[self.player+1] and self.parent == nil then setJoypadFocus(self.player, self.origin) end end end --************************************************************************-- --** ISContextMenu:onMouseDown --** --************************************************************************-- function ISContextMenu:onMouseDown(x, y) end --************************************************************************-- --** ISContextMenu:render --** --************************************************************************-- function ISContextMenu:prerender() if self:isEmpty() then self:setX(100000); -- cheap hack for now �� return; end self:addDefaultOptions() -- delayed add if ((self.mouseOver == nil) or (self.mouseOver == -1) or (self.slideGoalY ~= nil)) and not JoypadState.players[self.player+1] and (self:topmostMenuWithMouse(getMouseX(), getMouseY()) == self) then self.mouseOver = self:getIndexAt(self:getMouseX(), self:getMouseY()) end self:updateSlideGoalX() self:updateSlideGoalY() end function ISContextMenu:render() self.visibleCheck = true; if self:isEmpty() then return; end local y = self.padTopBottom; local dy = 0 if self:getScrollHeight() > self:getScrollAreaHeight() then dy = self.scrollIndicatorHgt y = y + dy end local iconSize = self.itemHgt - 12 local iconShiftX = 2 local iconShiftY = 6 local textForIconShift = iconSize + iconShiftX*2 + 2 self:drawRect(0, dy - self:getYScroll(), self.width, self.height - dy * 2, self.backgroundColor.a, self.backgroundColor.r, self.backgroundColor.g, self.backgroundColor.b); self:drawRect(0, dy - self:getYScroll(), iconSize + iconShiftX*2, self.height - dy * 2, 0.25, 0.1, 0.1, 0.1); self:drawRectBorderStatic(0, dy, self.width, self.height - dy * 2, self.borderColor.a, self.borderColor.r, self.borderColor.g, self.borderColor.b) local highestWid = 0; local textDY = (self.itemHgt - self.fontHgt) / 2 local backArrowDX = math.max(17 - getTextManager():MeasureStringX(self.font, "<"), 0) / 2 self.currentOptionRect = { x = self.x, y = self.y + y + self:getYScroll(), width = 100, height = self.itemHgt } local c = 1; local sizeMap = {}; local offTop,offBottom = false,false local showTooltip = false for i,k in ipairs(self.options) do if k.disable then return; end local sub = nil; if y + self:getYScroll() < 0 then -- off the top offTop = true elseif y + self:getYScroll() + self.itemHgt > self.scrollIndicatorHgt + self.padTopBottom + self:getScrollAreaHeight() then -- off the bottom offBottom = true elseif self.mouseOver == c then if k.notAvailable then self:drawRect(0, y, self.width, self.itemHgt, 0.1, 0.05, 0.05, 0.05); self:drawRectBorder(0, y, self.width, self.itemHgt, 0.15, 0.9, 0.9, 1); if k.iconTexture ~= nil then self:drawTextureScaledAspect(k.iconTexture, iconShiftX, y + iconShiftY, iconSize, iconSize, 1, 1, 1, 0.8) end self:drawText(k.name, textForIconShift, y+textDY, 1, 0.2, 0.2, 0.85, self.font); if k.subOption ~= nil then self:drawTextRight(">", self.width - 4, y+textDY, 1, 0.2, 0.2, 0.85, self.font); end else self:drawRect(0, y, self.width, self.itemHgt, 0.8, 0.5, 0.5, 0.5); self:drawRectBorder(0, y, self.width, self.itemHgt, 0.15, 0.9, 0.9, 1); if k.iconTexture ~= nil then self:drawTextureScaledAspect(k.iconTexture, iconShiftX, y + iconShiftY, iconSize, iconSize, 1, 1, 1, 0.8) end self:drawText(k.name, textForIconShift, y+textDY, 1, 1, 1, 1, self.font); end --~ if k.textDisplay then --~ if ISContextMenu.toolTip ~= nil then --~ ISContextMenu.toolTip:removeFromUIManager(); --~ ISContextMenu.toolTip:setVisible(false); --~ ISContextMenu.toolTip = nil; --~ elseif k.toolTip and not self:isMouseOut() then --~ ISContextMenu.toolTip = k.toolTip; --~ ISContextMenu.toolTip:setVisible(true); --~ ISContextMenu.toolTip:addToUIManager(); --~ end local isMouseOut = self:isMouseOut() if JoypadState.players[self.player+1] and not self.joyfocus then isMouseOut = true end if k.toolTip and not isMouseOut then self:showTooltip(k) showTooltip = true end --~ self:drawText(k.textDisplay, self:getMouseX(), self:getMouseY(), 1, 1, 1, 1, UIFont.Normal); --~ end if k.isDefaultOption then self:drawText("<", backArrowDX, y + textDY, 1, 0.8, 0.8, 0.9, self.font); end -- if we have a subOption, we set it visible if k.subOption ~= nil then self:drawTextRight(">", self.width - 4, y+textDY, 1, 1, 1, 1, self.font); if self:isOptionSingleMenu() then -- nothing elseif self.forceVisible then self.subMenu = self:getSubMenu(k.subOption); self.subMenu:setVisible(true); -- is position is next to our selected option dy = (self.subMenu:getScrollHeight() > self.subMenu:getScrollAreaHeight()) and self.scrollIndicatorHgt or 0 self:getSubMenu(k.subOption):setY(y - self.subMenu.padTopBottom + self.y + self:getYScroll() - dy); table.insert(sizeMap, self:getSubMenu(k.subOption)); end end self.currentOptionRect = { x = self.x, y = self.y + y + self:getYScroll(), width = 100, height = self.itemHgt } else if k.notAvailable then if k.iconTexture ~= nil then self:drawTextureScaledAspect(k.iconTexture, iconShiftX, y + iconShiftY, iconSize, iconSize, 1, 1, 1, 0.8) end self:drawText(k.name, textForIconShift, y+textDY, 1, 0.2, 0.2, 0.85, self.font); if k.subOption ~= nil then self:drawTextRight(">", self.width - 4, y+textDY, 1, 0.2, 0.2, 0.85, self.font); end else if self.blinkOption == k.name then if not self.blinkAlpha then self.blinkAlpha = 1; self.blinkAlphaIncrease = false; end if not self.blinkAlphaIncrease then self.blinkAlpha = self.blinkAlpha - 0.1 * (UIManager.getMillisSinceLastRender() / 33.3); if self.blinkAlpha < 0 then self.blinkAlpha = 0; self.blinkAlphaIncrease = true; end else self.blinkAlpha = self.blinkAlpha + 0.1 * (UIManager.getMillisSinceLastRender() / 33.3); if self.blinkAlpha > 1 then self.blinkAlpha = 1; self.blinkAlphaIncrease = false; end end self:drawRect(0, y, self.width, self.itemHgt, self.blinkAlpha, 1, 1, 1); end if k.iconTexture ~= nil then self:drawTextureScaledAspect(k.iconTexture, iconShiftX, y + iconShiftY, iconSize, iconSize, 1, 1, 1, 0.8) end self:drawText(k.name, textForIconShift, y+textDY, 1, 0.8, 0.8, 0.9, self.font); if k.isDefaultOption then self:drawText("<", backArrowDX, y + textDY, 1, 0.8, 0.8, 0.9, self.font); end if k.subOption ~= nil then self:drawTextRight(">", self.width - 4, y+textDY, 1, 0.8, 0.8, 0.9, self.font); end end end if k.checkMark then self:drawTexture(self.tickTexture, (17 - self.tickTexture:getWidthOrig()) / 2, y + (self.itemHgt - self.tickTexture:getHeightOrig()) / 2, 1, 1, 1, 1) end if k.isDefaultOption and self.options[i+1] and not self.options[i+1].isDefaultOption then self:drawRect(0, y + self.itemHgt - 1, self.width, 1, self.borderColor.a, self.borderColor.r, self.borderColor.g, self.borderColor.b) end y = y + self.itemHgt; local w = getTextManager():MeasureStringX(self.font, k.name); if(w > highestWid) then highestWid = w; end c = c + 1; end if offTop then self:drawRect(0, 0 - self:getYScroll(), self.width, self.scrollIndicatorHgt, 0.8, 0.1, 0.1, 0.1); local x = self.width / 2 - 14 - 7 self:drawTexture(self.arrowUp, x, 0 - self:getYScroll(), 1, 1, 1, 1) self:drawTexture(self.arrowUp, x + 14, 0 - self:getYScroll(), 1, 1, 1, 1) self:drawTexture(self.arrowUp, x + 14 * 2, 0 - self:getYScroll(), 1, 1, 1, 1) end if offBottom then self:drawRect(0, self.height - self.scrollIndicatorHgt - self:getYScroll(), self.width, self.scrollIndicatorHgt, 0.8, 0.1, 0.1, 0.1); local x = self.width / 2 - 14 - 7 self:drawTexture(self.arrowDown, x, self.height - self.scrollIndicatorHgt - self:getYScroll(), 1, 1, 1, 1) self:drawTexture(self.arrowDown, x + 14, self.height - self.scrollIndicatorHgt - self:getYScroll(), 1, 1, 1, 1) self:drawTexture(self.arrowDown, x + 14 * 2, self.height - self.scrollIndicatorHgt - self:getYScroll(), 1, 1, 1, 1) end if self.toolTip then self.toolTip:setContextMenu(self); self.toolTip:setVisible(true); --~ self:drawText(displayText, self:getMouseX(), self:getMouseY(), 1, 1, 1, 1, UIFont.Normal); end local ww = highestWid + 24 + 40; if(ww<100) then ww = 100; end self:setWidth(ww); self.currentOptionRect.width = ww -- we make his x at the edge of the current menu -- place submenu to the left if there is no room on the right for _,subMenu in ipairs(sizeMap) do local subMenuWidth = subMenu:calcWidth() if self.parent ~= nil and self.x < self.parent.x then subMenu:setX(self.x - subMenuWidth - 1) elseif self.x + ww + 1 + subMenuWidth > getCore():getScreenWidth() then subMenu:setX(self.x - subMenuWidth - 1) else subMenu:setX(self.x + ww + 1) end end if not showTooltip and self.player == 0 then self:hideToolTip() end end function ISContextMenu:calcHeight() local itemsHgt = (self.numOptions - 1) * self.itemHgt local screenHgt = getCore():getScreenHeight() -- screenHgt = 200 if itemsHgt + self.padTopBottom * 2 > screenHgt then local numVisibleItems = math.floor((screenHgt - self.padTopBottom * 2 - self.scrollIndicatorHgt * 2) / self.itemHgt) self.scrollAreaHeight = numVisibleItems * self.itemHgt self:setHeight(self.scrollAreaHeight + self.padTopBottom * 2 + self.scrollIndicatorHgt * 2) self:setScrollHeight(itemsHgt) else self.scrollAreaHeight = itemsHgt self:setHeight(itemsHgt + self.padTopBottom * 2) self:setScrollHeight(itemsHgt) end end function ISContextMenu:calcWidth() local maxWidth = 0 for _,k in ipairs(self.options) do local w = getTextManager():MeasureStringX(self.font, k.name) if(w > maxWidth) then maxWidth = w end end return math.max(maxWidth + 24 + 40, 100) end function ISContextMenu:isOptionSingleMenu() return getCore():getOptionSingleContextMenu(self.player) end function ISContextMenu:isAnyVisible() if self:isVisible() then return true end if self:isOptionSingleMenu() and self.instanceMap then for _,subMenu in ipairs(self.instanceMap) do if subMenu:isVisible() then return true end end end return false end function ISContextMenu:getIndexForSubMenu(subMenu) for i,option in ipairs(self.options) do if option.subOption and (self:getSubMenu(option.subOption) == subMenu) then return i end end return -1 end function ISContextMenu:addDefaultOptions() -- Default options could be added when addSubMenu() is called, however some code -- will remove a submenu option if the submenu is empty. It won't be empty if -- default options were added. if self.addedDefaultOptions then return end self.addedDefaultOptions = true if not self:isOptionSingleMenu() then return end -- Fix for long time it takes to add options to ISWorldObjectContextMenu. self.slideGoalTime = getTimestampMs() local ancestor = self.parent local child = self while ancestor do local index = ancestor:getIndexForSubMenu(child) if index == -1 then print("ERROR: ISContextMenu:getNew() was called with the wrong parent menu") else local option = ancestor.options[index] option = self:addOptionOnTop(option.name, self, ISContextMenu.displayAncestor, ancestor) option.isDefaultOption = true end child = ancestor ancestor = ancestor.parent end if self.slideGoalY then -- Fix for keepOnScreen=true changing the y position. self:setY(self:getY()) self.slideGoalY = self:getY() self:setY(self.slideGoalY - self.slideGoalDY) end end function ISContextMenu:getDefaultOptionCount() self:addDefaultOptions() -- delayed add local count = 0 for i,option in ipairs(self.options) do if option.isDefaultOption then count = count + 1 else break end end return count end function ISContextMenu:setSlideGoalX(startX, finalX) self:setX(finalX) if not self:isOptionSingleMenu() then return end if not JoypadState.players[self.player+1] then return end self:setX(startX) self.slideGoalX = finalX self.slideGoalTime = getTimestampMs() end function ISContextMenu:setSlideGoalY(startY, finalY) self:setY(finalY) if not self:isOptionSingleMenu() then return end if JoypadState.players[self.player+1] then return end self:setY(startY) self.slideGoalY = finalY self.slideGoalDY = finalY - startY self.slideGoalTime = getTimestampMs() end function ISContextMenu:updateSlideGoalX() if not self.slideGoalX then return end if self.slideGoalX < self:getX() then local dt = math.min(getTimestampMs() - self.slideGoalTime, 100) self:setX(self.slideGoalX + (self:getX() - self.slideGoalX) * (1 - dt / 100)) if self:getX() - self.slideGoalX < 1 then self.slideGoalX = nil end elseif self.slideGoalX > self:getX() then local dt = math.min(getTimestampMs() - self.slideGoalTime, 100) self:setX(self.slideGoalX - (self.slideGoalX - self:getX()) * (1 - dt / 100)) if self.slideGoalX - self:getX() < 1 then self.slideGoalX = nil end end end function ISContextMenu:updateSlideGoalY() if not self.slideGoalY then return end if self.slideGoalY < self:getY() then local dt = math.min(getTimestampMs() - self.slideGoalTime, 100) self:setY(self.slideGoalY + (self:getY() - self.slideGoalY) * (1 - dt / 100)) if self:getY() - self.slideGoalY < 1 then self.slideGoalY = nil end elseif self.slideGoalY > self:getY() then local dt = math.min(getTimestampMs() - self.slideGoalTime, 100) self:setY(self.slideGoalY - (self.slideGoalY - self:getY()) * (1 - dt / 100)) if self.slideGoalY - self:getY() < 1 then self.slideGoalY = nil end end end local SLIDEX = 50 local SLIDEY = 10 function ISContextMenu:displayAncestor(ancestor) self:hideSelf() local ancestorY = self:getRootY() ancestor:setVisible(true) ancestor:setSlideGoalX(self:getX() - SLIDEX, self:getX()) ancestor:setSlideGoalY(ancestorY + SLIDEY, ancestorY) if JoypadState.players[self.player+1] then setJoypadFocus(self.player, ancestor) else ancestor.mouseOver = -1 end end function ISContextMenu:displaySubMenu(subMenu, option) self:hideSelf() local subY = self:getRootY() if not JoypadState.players[self.player+1] then subMenu:addDefaultOptions() if (subY + subMenu:getHeight() <= self:getItemY(option.id)) or (subY >= self:getItemY(option.id + 1)) then local itemY = self:getItemY(option.id) - subMenu:getDefaultOptionCount() * subMenu.itemHgt local dy = (subMenu:getScrollHeight() > subMenu:getScrollAreaHeight()) and self.scrollIndicatorHgt or 0 subY = itemY - subMenu.padTopBottom - dy end -- Fix for keepOnScreen=true changing the y position. subMenu:setY(subY) subY = subMenu:getY() end subMenu:setSlideGoalX(self:getX() + SLIDEX, self:getX()) subMenu:setSlideGoalY(subY - SLIDEY, subY) subMenu:setVisible(true) if JoypadState.players[self.player+1] then subMenu.mouseOut = false setJoypadFocus(self.player, subMenu) elseif self.player == 0 then subMenu.mouseOut = subMenu:topmostMenuWithMouse(getMouseX(), getMouseY()) ~= subMenu end end function ISContextMenu:getRootY() local ancestor = self while ancestor.parent do ancestor = ancestor.parent end return ancestor:getY() end function ISContextMenu:getItemY(index) local y = self.y + self.padTopBottom if self:getScrollHeight() > self:getScrollAreaHeight() then y = y + self.scrollIndicatorHgt end return y + (index - 1) * self.itemHgt + self:getYScroll() end function ISContextMenu:getIndexAt(x, y) if (x < 0) or (x >= self:getWidth()) then return -1 end local dy = (self:getScrollHeight() > self:getScrollAreaHeight()) and self.scrollIndicatorHgt or 0 local index = math.floor((y - self.padTopBottom - dy) / self.itemHgt + 1) if index >= 1 and index < self.numOptions then return index end return -1 end function ISContextMenu:topmostMenuWithMouse(x, y) local contextMenu = getPlayerContextMenu(self.player) if not contextMenu then return nil end local menu = nil if self == contextMenu then if self:isVisible() and x >= self.x and x < self.x + self.width and y >= self.y and y < self.y + self.height then menu = self end end for i=1,#contextMenu.instanceMap do local m = contextMenu.instanceMap[i] if m:isVisible() and x >= m.x and x < m.x + m.width and y >= m.y and y < m.y + m.height then menu = m end end return menu end function ISContextMenu:allocOption(name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) if #self.optionPool == 0 then table.insert(self.optionPool, {}) end local option = table.remove(self.optionPool, #self.optionPool) table.wipe(option) option.id = self.numOptions option.name = name option.onSelect = onSelect option.target = target option.param1 = param1 option.param2 = param2 option.param3 = param3 option.param4 = param4 option.param5 = param5 option.param6 = param6 option.param7 = param7 option.param8 = param8 option.param9 = param9 option.param10 = param10 option.subOption = nil return option end function ISContextMenu:addOption(name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) if getCore():getGameMode() == "Tutorial" then if self:getOptionFromName(name) then return; end end local option = self:allocOption(name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10); option.iconTexture = nil self.options[self.numOptions] = option; self.numOptions = self.numOptions + 1; self:calcHeight() self:setWidth(self:calcWidth()) return option; end function ISContextMenu:getOptionFromName(name) for i,v in ipairs(self.options) do if v.name == name then return v; end end end function ISContextMenu:addDebugOption(name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) local option = self:addOption(name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10); option.iconTexture = getTexture("media/ui/BugIcon.png"); return option; end function ISContextMenu:addOptionOnTop(name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) local newOptions = {}; for i,v in ipairs(self.options) do v.id = v.id + 1; newOptions[v.id] = v; end self.options = newOptions; local option = self:allocOption(name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10); option.id = 1; self.options[1] = option; self.numOptions = self.numOptions + 1; self:calcHeight() self:setWidth(self:calcWidth()) return option; end function ISContextMenu:insertOptionAfter(prevOptionName, name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) if #self.options == 0 then print("ERROR: prev option not found: " .. prevOptionName) return self:addOption(name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) end local index = 1 local isFound = false for i, v in ipairs(self.options) do index = i if v.name == prevOptionName then isFound = true break end end if not isFound then print("ERROR: prev option not found: " .. prevOptionName) return self:addOption(name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) end for i = #self.options, index+1, -1 do self.options[i+1] = self.options[i] end local option = self:allocOption(name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10); option.id = index+1; self.options[index+1] = option; self.numOptions = self.numOptions + 1; for i, v in ipairs(self.options) do v.id = i end self:calcHeight() self:setWidth(self:calcWidth()) return option end function ISContextMenu:insertOptionBefore(nextOptionName, name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) if #self.options == 0 then print("ERROR: next option not found: " .. nextOptionName) return self:addOption(name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) end local index = 1 local isFound = false for i, v in ipairs(self.options) do index = i if v.name == nextOptionName then isFound = true break end end if not isFound then print("ERROR: next option not found: " .. nextOptionName) return self:addOption(name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) end if index == 1 then return self:addOptionOnTop(name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) end for i = #self.options, index, -1 do self.options[i+1] = self.options[i] end local option = self:allocOption(name, target, onSelect, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10); option.id = index; self.options[index] = option; self.numOptions = self.numOptions + 1; for i, v in ipairs(self.options) do v.id = i end self:calcHeight() self:setWidth(self:calcWidth()) return option end function ISContextMenu:removeLastOption() table.insert(self.optionPool, self.options[self.numOptions - 1]) self.options[self.numOptions - 1] = nil; self.numOptions = self.numOptions -1; self:calcHeight() end function ISContextMenu:removeOptionByName(optName) if #self.options == 0 then return end local index = 1 local isFound = false for i, v in ipairs(self.options) do index = i if v.name == optName then table.insert(self.optionPool, v) isFound = true break end end if not isFound then return end for i = index+1, #self.options do self.options[i-1] = self.options[i] self.options[i-1].id = i-1 end self.options[#self.options] = nil self.numOptions = self.numOptions - 1 self:calcHeight() end function ISContextMenu:getMenuOptionNames() local result = {} for i, v in ipairs(self.options) do result[v.name] = v end return result end function ISContextMenu:addSubMenu(option, menu) option.subOption = menu.subOptionNums; end function ISContextMenu:addActionsOption(text, getActionsFunction, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) local character = getSpecificPlayer(self.player) return self:addOption(text, character, ISTimedActionQueue.queueActions, getActionsFunction, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) end function ISContextMenu:setOptionChecked(option, checked) option.checkMark = checked end function ISContextMenu:clear() for _,option in ipairs(self.options) do table.insert(self.optionPool, option) end table.wipe(self.options) self.numOptions = 1; self.mouseOver = -1 self.subMenu = nil self:setHeight(0) self.addedDefaultOptions = false end function ISContextMenu:isEmpty() return self.numOptions == 1 end function ISContextMenu:getScrollAreaHeight() return self.scrollAreaHeight or 0 end function ISContextMenu:setFont(font) self.font = font or UIFont.Medium self.fontHgt = getTextManager():getFontHeight(self.font) self.itemHgt = self.fontHgt + self.padY * 2 end function ISContextMenu:setFontFromOption() local font = getCore():getOptionContextMenuFont() if font == "Large" then self:setFont(UIFont.Large) elseif font == "Small" then self:setFont(UIFont.Small) else self:setFont(UIFont.Medium) end end --************************************************************************-- --** ISContextMenu:new --** --************************************************************************-- function ISContextMenu:new (x, y, width, height, zoom) local o = {} --o.data = {} o = ISPanel:new(x, y, width, height); setmetatable(o, self) self.__index = self o.x = x; o.y = y; o.zoom = zoom; o.font = UIFont.Medium; o.padY = 6 o.fontHgt = getTextManager():getFontFromEnum(o.font):getLineHeight() o.itemHgt = o.fontHgt + o.padY * 2 o.padTopBottom = 0 o.borderColor = {r=1, g=1, b=1, a=0.15}; o.backgroundColor = {r=0.1, g=0.1, b=0.1, a=0.7}; o.backgroundColorMouseOver = {r=0.3, g=0.3, b=0.3, a=1.0}; o.width = width; o.height = height; o.anchorLeft = true; o.anchorRight = false; o.anchorTop = true; o.anchorBottom = false; o.parent = {}; -- Must override ISUIElement getKeepOnScreen because parent ~= null for sub menus o.keepOnScreen = true o.options = {}; o.numOptions = 1; o.optionPool = {}; o.visibleCheck = false; o.forceVisible = true; o.toolTip = nil; o.subOptionNums = 0; o.player = 0; o.scrollIndicatorHgt = 14 o.arrowUp = getTexture("media/ui/ArrowUp.png") o.arrowDown = getTexture("media/ui/ArrowDown.png") o.tickTexture = getTexture("Quest_Succeed") return o end ISContextMenu.get = function(player, x, y) local context = getPlayerContextMenu(player); context:hideAndChildren(); context:setVisible(true); context:clear(); context:setFontFromOption() context.forceVisible = true; context.parent = nil; context:setSlideGoalX(x + 20, x) context:setSlideGoalY(y - SLIDEY, y) context:bringToTop(); context:setVisible(true); context.visibleCheck = true; if context.instanceMap then for _,v in pairs(context.instanceMap) do v:setVisible(false) v:removeFromUIManager() table.insert(context.subMenuPool, v) end table.wipe(context.instanceMap) end context.instanceMap = context.instanceMap or {} context.subMenuPool = context.subMenuPool or {} context.subOptionNums = 0; context.subInstance = nil context.subMenu = nil; context.player = player; context:setForceCursorVisible(player == 0) return context; end function ISContextMenu:getNew(parentContext) local context = getPlayerContextMenu(parentContext.player); if #context.subMenuPool == 0 then context.subInstance = ISContextMenu:new(0, 0, 1, 1, 1.5); else context.subInstance = table.remove(context.subMenuPool, #context.subMenuPool) end context.subInstance:initialise(); context.subInstance:instantiate(); context.subInstance:addToUIManager(); context.subInstance:clear(); context.subInstance:setFontFromOption() context.subInstance:setX(parentContext:getX()); context.subInstance:setY(parentContext:getY()); context.subInstance.parent = parentContext; context.subInstance.forceVisible = true; context.subInstance:setVisible(false); context.subInstance:bringToTop(); context.subInstance.player = parentContext.player; context:setForceCursorVisible(parentContext.player == 0) context.subOptionNums = context.subOptionNums + 1; context.subInstance.subOptionNums = context.subOptionNums; context.instanceMap[context.subOptionNums] = context.subInstance; return context.subInstance; end function ISContextMenu:getSubMenu(num) return getPlayerContextMenu(self.player):getSubInstance(num) end function ISContextMenu:getSubInstance(num) return self.instanceMap[num]; end ISContextMenu.wantNoise = false ISContextMenu.noise = function(msg) if (ISContextMenu.wantNoise) then print('ISContextMenu: '..msg) end end