otclient-redemption/modules/corelib/ui/uipopupmenu.lua
RedSTwix e8ef4cc044
fix: 0xD0 payload + server-side quest tracker persistence for current protocol (#1711)
Bug Fixes:
- Popup menu checkboxes now reliably toggle and report their state.
- Quest tracker initializes, saves and syncs more reliably; auto-track/untrack preferences persist and apply correctly.
- Mini-window “Remove completed quests” consistently clears completed entries and updates the tracker UI.

Refactor:
- Tracker sync and local state handling simplified for more consistent, efficient communication and UI rebuilding.
2026-05-12 18:00:45 -03:00

154 lines
4.2 KiB
Lua

-- @docclass
UIPopupMenu = extends(UIWidget, 'UIPopupMenu')
local currentMenu
function UIPopupMenu.create()
local menu = UIPopupMenu.internalCreate()
local layout = UIVerticalLayout.create(menu)
layout:setFitChildren(true)
menu:setLayout(layout)
menu.isGameMenu = false
return menu
end
function UIPopupMenu:display(pos)
-- don't display if not options was added
if self:getChildCount() == 0 then
self:destroy()
return
end
if g_ui.isMouseGrabbed() then
self:destroy()
return
end
if currentMenu then
currentMenu:destroy()
end
if pos == nil then
pos = g_window.getMousePosition()
end
rootWidget:addChild(self)
self:setPosition(pos)
self:grabMouse()
self:focus()
-- self:grabKeyboard()
currentMenu = self
end
function UIPopupMenu:onGeometryChange(newRect, oldRect)
local parent = self:getParent()
if not parent then
return
end
local ymax = parent:getY() + parent:getHeight()
local xmax = parent:getX() + parent:getWidth()
if ymax < newRect.y + newRect.height then
local newy = ymax - newRect.height
if newy > 0 and ymax > newy + newRect.height then
self:setY(newy)
end
end
if xmax < newRect.x + newRect.width then
local newx = xmax - newRect.width
if newx > 0 and xmax > newx + newRect.width then
self:setX(newx)
end
end
self:bindRectToParent()
end
function UIPopupMenu:addOption(optionName, optionCallback, shortcut, disabled)
local optionWidget = g_ui.createWidget(self:getStyleName() .. 'Button', self)
optionWidget.onClick = function(widget)
self:destroy()
optionCallback(self:getPosition())
end
optionWidget:setText(optionName)
local width = optionWidget:getTextSize().width + optionWidget:getMarginLeft() + optionWidget:getMarginRight() + 15
if shortcut then
local shortcutLabel = g_ui.createWidget(self:getStyleName() .. 'ShortcutLabel', optionWidget)
shortcutLabel:setText(shortcut)
width = width + shortcutLabel:getTextSize().width + shortcutLabel:getMarginLeft() +
shortcutLabel:getMarginRight()
end
optionWidget:setEnabled(not disabled)
self:setWidth(math.max(190, math.max(self:getWidth(), width)))
end
function UIPopupMenu:addSeparator()
g_ui.createWidget('HorizontalSeparator', self)
end
function UIPopupMenu:addText(text)
local optionWidget = g_ui.createWidget("PopupScrollMenuShortcutLabel", self)
optionWidget:setText(text)
local width = optionWidget:getTextSize().width + optionWidget:getMarginLeft() + optionWidget:getMarginRight() + 15
self:setWidth(math.max(self:getWidth(), width))
end
function UIPopupMenu:addCheckBox(text, checked, callback)
local checkBox = g_ui.createWidget(self:getStyleName() .. 'CheckBox', self)
checkBox:setText(text)
checkBox:setChecked(checked or false)
checkBox.onClick = function()
checkBox:setChecked(not checkBox:isChecked())
self:destroy()
callback(checkBox, checkBox:isChecked())
end
local width = checkBox:getTextSize().width + checkBox:getMarginLeft() + checkBox:getMarginRight() + 30
self:setWidth(math.max(self:getWidth(), width))
return checkBox
end
function UIPopupMenu:setGameMenu(state)
self.isGameMenu = state
end
function UIPopupMenu:onDestroy()
if currentMenu == self then
currentMenu = nil
end
self:ungrabMouse()
end
function UIPopupMenu:onMousePress(mousePos, mouseButton)
-- clicks outside menu area destroys the menu
if not self:containsPoint(mousePos) then
self:destroy()
end
return true
end
function UIPopupMenu:onKeyPress(keyCode, keyboardModifiers)
if keyCode == KeyEscape then
self:destroy()
return true
end
return false
end
-- close all menus when the window is resized
local function onRootGeometryUpdate()
if currentMenu then
currentMenu:destroy()
end
end
local function onGameEnd()
if currentMenu and currentMenu.isGameMenu then
currentMenu:destroy()
end
end
connect(rootWidget, {
onGeometryChange = onRootGeometryUpdate
})
connect(g_game, {
onGameEnd = onGameEnd
})