You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

416 lines
18 KiB

local _,rematch = ...
local L = rematch.localization
local C = rematch.constants
local settings = rematch.settings
rematch.frame = RematchFrame
local modules = {} -- ordered list of modules (should be table/frames with Configure, Resize and/or Update functions)
local showAfterCombat = false -- flag that becomes true when rematch was hidden due to combat and should return after combat
local chromeModules = {"titlebar","toolbar","bottombar","panelTabs","teamTabs"} -- for looping over these modules
-- when configure/resize/update happens, these fire after Rematch has finished its own configure/resize/update
local frameFuncEvents = {
Configure = "REMATCH_UI_CONFIGURE",
Resize = "REMATCH_UI_RESIZE",
Update = "REMATCH_UI_UPDATE"
}
--[[
rematch.frame:Register(module)
- Adds a module to an ordered list. If the module has a function named Configure, Resize or Update,
those functions will be called (in the order the module was registered) when one of the following
Configure, Resize or Updates happen in this module.
rematch.frame:Configure(layoutName)
- Arranges all panels/chrome to the frame for the given layout (and whether it's for the journal), and
calls all configure, resize and update functions for registered modules.
rematch.frame:Resize()
- Calls all resize and update functions for registered modules to allow panels/chrome to adjust their
content based on size changes.
rematch.frame:Update()
- Calls all update functions for registered modules to update panel/chrome content.
]]
-- as described above, every panel or chrome that wants to run their Configure/Resize/Update needs to register here
-- module should be a parentKey of rematch namespace
function rematch.frame:Register(module)
assert(type(module)=="string" and rematch[module] and type(rematch[module])=="table","Attempt to register an invalid module: "..tostring(module))
tinsert(modules,rematch[module])
end
rematch.events:Register(rematch.frame,"PLAYER_LOGIN",function(self)
rematch.events:Register(self,"PLAYER_ENTERING_WORLD",self.PLAYER_ENTERING_WORLD)
rematch.events:Register(self,"PET_BATTLE_OPENING_START",self.PET_BATTLE_OPENING_START)
rematch.events:Register(self,"REMATCH_PETS_LOADED",self.REMATCH_PETS_LOADED)
rematch.events:Register(self,"PLAYER_LOGOUT",self.PLAYER_LOGOUT)
self.CloseButton:Hide() -- using titlebar's close button
end)
--[[ script handlers ]]
function rematch.frame:OnMouseDown()
if not rematch.journal:IsActive() and (not settings.LockPosition or IsShiftKeyDown()) then
self:StartMoving()
end
end
function rematch.frame:OnMouseUp()
if not rematch.journal:IsActive() then
self:StopMovingOrSizing()
self:SetUserPlaced(false)
self:SavePosition()
end
end
function rematch.frame:OnShow()
rematch.events:Register(self,"PLAYER_TARGET_CHANGED",self.PLAYER_TARGET_CHANGED)
rematch.events:Register(rematch.frame,"PLAYER_REGEN_DISABLED",rematch.frame.PLAYER_REGEN_DISABLED)
rematch.events:Register(rematch.frame,"PET_JOURNAL_LIST_UPDATE",rematch.frame.PET_JOURNAL_LIST_UPDATE)
if not rematch.journal:IsActive() then
PlaySound(C.SOUND_REMATCH_OPEN)
end
end
function rematch.frame:OnHide()
rematch.events:Unregister(self,"PLAYER_TARGET_CHANGED")
rematch.events:Unregister(rematch.frame,"PLAYER_REGEN_DISABLED")
rematch.events:Unregister(rematch.frame,"PET_JOURNAL_LIST_UPDATE")
rematch.utils:HideWidgets()
rematch.dialog:HideDialog() -- is there any reason for a dialog to remain up? if so, use version of uncommented code below
-- local openDialog = rematch.dialog:GetOpenDialog()
-- if openDialog=="CustomScaleDialog" then
-- rematch.dialog.CancelButton:Click() -- if frame scale change is happening, cancel it if the frame hides before the dialog does
-- end
if rematch.sort:ClearStickiedPetIDs() then
rematch.filters:ForceUpdate()
end
-- check if we need to prompt to backup teams
rematch.timer:Start(0.1,rematch.teamStrings.CheckForBackup)
if not rematch.journal:IsActive() then
PlaySound(C.SOUND_REMATCH_CLOSE)
end
end
--[[ toggles ]]
-- toggles the rematch window; if auto is true then it was summoned by interact or safari hat reminder
-- (or any "unattented" behavior)
function rematch.frame:Toggle(auto)
if rematch.frame:IsVisible() then
if rematch.journal:IsActive() then
ToggleCollectionsJournal()
else
rematch.frame:Hide()
end
elseif not InCombatLockdown() then
rematch.frame:Show()
rematch.frame:Configure((auto and settings.PreferMinimized) and "0-minimized" or rematch.layout:GetLayout(C.STANDALONE))
else
rematch.utils:Write(L["Rematch can't be summoned during combat. Try again later"])
end
end
RematchToggleWindow = rematch.frame.Toggle
-- minimizes or maximizes the frame (if in standalone mode); optionally maximizes to the given layoutName
function rematch.frame:ToggleMinimized(layoutName)
if rematch.journal:IsActive() then
return -- in journal mode, do nothing
elseif rematch.layout:GetMode(C.CURRENT)==0 then -- currently minimized, restore maximized layout
PlaySound(C.SOUND_PANEL_TAB)
rematch.frame:Configure(layoutName or rematch.layout:GetLayout(C.MAXIMIZED))
else -- not minimized, go to minimized layout
PlaySound(C.SOUND_PANEL_TAB)
rematch.frame:Configure("0-minimized")
end
end
--[[ events ]]
-- this event just handles whether the LoadedTargetPanel appears in layouts where it only sometimes shows (minimized, single-panel,
-- or 2-queue where there's no space for a persistent LoadedTargetPanel). if the target panel needs to show or hide, this adjusts
-- the current layout and does a Configure
function rematch.frame:PLAYER_TARGET_CHANGED()
local def = rematch.layout:GetDefinition(C.CURRENT)
if def and def.hasTempTarget then -- we're in a layout where the target panel can come and go depending on target
local shouldShowTarget = rematch.loadedTargetPanel:ShouldShowTarget()
if shouldShowTarget and not def.subview then -- not showing target now but should show it
rematch.frame:Configure(format("%d-%s-target",def.mode,def.view))
elseif not shouldShowTarget and def.subview=="target" then -- showing target now but shoundn't show it
rematch.frame:Configure(format("%d-%s",def.mode,def.view))
end
end
end
-- if LockWIndow (Keep Window On Screen) is not checked, then hide the window when we get a loading screen (instances)
function rematch.frame:PLAYER_ENTERING_WORLD()
if not settings.LockWindow and not rematch.journal:IsActive() then
self:Hide()
end
end
-- on entering a pet battle, close window unless both LockWindow and StayForBattle enabled
local lastNotedTeamID -- the currentTeamID that had notes last displayed (for Show Notes In Battle -> Only Once Per Team)
function rematch.frame:PET_BATTLE_OPENING_START()
if (not settings.LockWindow or not settings.StayForBattle) and not rematch.journal:IsActive() then
self:Hide()
end
-- if Show Notes In Battle enabled, show notes
local teamID = settings.currentTeamID
if teamID and rematch.savedTeams[teamID] and rematch.savedTeams[teamID].notes and settings.ShowNotesInBattle then
if not settings.ShowNotesOnce or lastNotedTeamID~=teamID then
rematch.cardManager:ShowCard(rematch.notes,teamID)
end
end
lastNotedTeamID = teamID
end
-- on login after pets are loaded, open window if LockWindow and StayOnLogout enabled
function rematch.frame:REMATCH_PETS_LOADED()
if settings.LockWindow and settings.StayOnLogout and not rematch.frame:IsVisible() and settings.WasShownOnLogout then
self:Toggle()
end
end
function rematch.frame:PLAYER_LOGOUT()
settings.WasShownOnLogout = rematch.frame:IsVisible()
end
-- rematch can't remain on screen during combat; if it's up as player enters combat, hide it but set a flag to bring it back after
function rematch.frame:PLAYER_REGEN_DISABLED()
if rematch.frame:IsVisible() and not rematch.journal:IsActive() then -- journal will handle combat on its own
rematch.frame:Hide()
showAfterCombat = true
-- register for leaving combat to know to bring window back
rematch.events:Register(rematch.frame,"PLAYER_REGEN_ENABLED",rematch.frame.PLAYER_REGEN_ENABLED)
end
end
-- after combat ends, if flag was set when rematch was hidden entering combat, then bring the window back
function rematch.frame:PLAYER_REGEN_ENABLED()
if showAfterCombat then
showAfterCombat = false
-- can stop watching for this event until next showAfterCombat happens
rematch.events:Unregister(rematch.frame,"PLAYER_REGEN_ENABLED")
-- show the window in its last standalone state
rematch.frame:Toggle()
end
end
-- this can fire for pets changing (level, rarity, name, etc) as well as gaining/losing pets
function rematch.frame:PET_JOURNAL_LIST_UPDATE()
if not rematch.filters:IsAllClear() then
rematch.filters:ForceUpdate() -- in case any pets fall out of filtering criteria
end
rematch.timer:Start(0,self.Update)
end
--[[ Configure ]]
-- returns the layoutName,definition from the given layoutName (or current if none given)
-- if the view is invalid (like 3-pets) it will adjust to a valid view
local function negotiateLayout(layoutName)
layoutName = rematch.layout:GetLayout(layoutName) -- validating layoutName and adjusting if needed
local def = rematch.layout:GetDefinition(layoutName)
-- if this is a layout with a TargetPanel only displayed while something saved is targeted, change layout if needed
if def and def.hasTempTarget then
local shouldShowTarget = rematch.loadedTargetPanel:ShouldShowTarget()
if shouldShowTarget and not def.subview then
layoutName = rematch.layout:GetLayout(format("%d-%s-target",def.mode,def.view))
def = rematch.layout:GetDefinition(layoutName)
elseif not shouldShowTarget and def.subview=="target" then
layoutName = rematch.layout:GetLayout(format("%d-%s",def.mode,def.view))
def = rematch.layout:GetDefinition(layoutName)
end
end
return layoutName,def
end
-- anchors chrome frames
local function anchorChrome(layoutName,def)
local chromeHeight,chromeYOffset
if def.mode==0 then -- toolbar at bottom always minimized and never in 3-panel mode
chromeHeight = C.TOOLBAR_HEIGHT + 5
chromeYOffset = C.TOOLBAR_HEIGHT + 5
rematch.toolbar:SetPoint("TOPLEFT",rematch.frame.Canvas,"BOTTOMLEFT",0,-2)
rematch.toolbar:SetPoint("BOTTOMRIGHT",rematch.frame.Canvas,"BOTTOMRIGHT",0,-C.TOOLBAR_HEIGHT-2)
rematch.bottombar:Hide()
else
chromeHeight = C.TOOLBAR_HEIGHT + 2 + C.BOTTOMBAR_HEIGHT + 2
chromeYOffset = C.BOTTOMBAR_HEIGHT + 2
rematch.toolbar:SetPoint("TOPLEFT",rematch.frame.Canvas,"TOPLEFT",0,C.TOOLBAR_HEIGHT+2)
rematch.toolbar:SetPoint("BOTTOMRIGHT",rematch.frame.Canvas,"TOPRIGHT",0,2)
rematch.bottombar:SetPoint("TOPLEFT",rematch.frame.Canvas,"BOTTOMLEFT",0,-2)
rematch.bottombar:SetPoint("BOTTOMRIGHT",rematch.frame.Canvas,"BOTTOMRIGHT",0,-C.BOTTOMBAR_HEIGHT-2)
rematch.bottombar:Show()
end
return chromeHeight,chromeYOffset
end
-- anchors panels to the canvas for the given layout
local function applyLayout(layoutName,def)
-- hide all known panels that aren't used in this layout definition
rematch.layout:HidePanels(def)
-- set size of canvas from layout definition
rematch.frame.Canvas:SetSize(def.width,def.height)
-- anchor and show all panels that are in this layout definition
for _,info in ipairs(def.panels) do
local panel = rematch.frame[info[1]]
if panel then -- we've already asserted all elements of the panel are defined when registering
panel:SetPoint(info[2],rematch.frame[info[3]],info[4],info[5],info[6])
panel:SetPoint(info[7],rematch.frame[info[8]],info[9],info[10],info[11])
panel:Show()
end
end
-- saving layouts
settings.CurrentLayout = def.layoutName -- regardless of journal/standalone, save new layout
if rematch.journal:IsActive() then -- if in journal, save new journal layout
settings.JournalLayout = def.layoutName
settings.LastOpenLayout = def.layoutName
settings.LastOpenJournal = true
else -- not in journal
settings.StandaloneLayout = def.layoutName -- regardless of minimize state, save standalone layout
if def.mode~=0 then
settings.MaximizedLayout = def.layoutName -- for unminimized views, saved maximized layout
settings.LastOpenLayout = def.layoutName
settings.LastOpenJournal = false
end
end
end
-- runs the Configure/Resize/Update function for all chrome and all panels used in the layout definition
local function runFrameFuncs(func,def)
for _,chrome in ipairs(chromeModules) do
if rematch[chrome] and type(rematch[chrome][func])=="function" then
rematch[chrome][func](rematch[chrome])
end
end
-- then again for panels that are used
for _,module in ipairs(modules) do
if def.panelsUsed[module] then
if type(module[func])=="function" then
module[func](module)
end
end
end
if frameFuncEvents[func] then
rematch.events:Fire(frameFuncEvents[func])
end
end
-- can call this without any parameter to re-configure current layout; or pass a layoutName to configure
-- to the given layout (it's ok if layout is slightly off like 3-pets, it will adjust to a valid layout if possible)
function rematch.frame:Configure(newLayoutName)
-- validate/adjust layoutName,definition
local layoutName,def = negotiateLayout(newLayoutName)
if layoutName and def then
rematch.utils:HideWidgets()
-- handle the toolbar and bottombar
local chromeHeight,chromeYOffset = anchorChrome(layoutName,def)
-- size the parent frame based on the canvas size of the layout
rematch.frame:SetSize(def.width + C.FRAME_LEFT_MARGIN + C.FRAME_RIGHT_MARGIN, def.height + C.FRAME_TOP_MARGIN + C.FRAME_BOTTOM_MARGIN + chromeHeight)
-- position canvas; for now ignoring toolbar and bottombuttons
rematch.frame.Canvas:SetPoint("BOTTOMLEFT",rematch.frame,"BOTTOMLEFT",C.FRAME_LEFT_MARGIN,3+chromeYOffset)
-- apply the layout (position frames)
applyLayout(layoutName,def)
-- run config/resize/update for all chrome and used panels
runFrameFuncs("Configure",def)
runFrameFuncs("Resize",def)
runFrameFuncs("Update",def)
-- anchor frame depending on saved
if rematch.journal:IsActive() then
rematch.frame:ClearAllPoints()
rematch.frame:SetPoint("BOTTOMLEFT",CollectionsJournal,"BOTTOMLEFT",-1,0)
else
rematch.frame:RestorePosition()
rematch.frame:SetFrameStrata(settings.LowerStrata and "LOW" or "MEDIUM")
end
else
assert(false,"Layout "..(newLayoutName or "nil").." can't resolve to valid layout.")
end
rematch.frame:UpdateScale()
self:EnableMouse(not rematch.journal:IsActive())
end
-- runs resize and update funcs for all chrome and all panels used in current layout
function rematch.frame:Resize()
local def = rematch.layout:GetDefinition(C.CURRENT)
runFrameFuncs("Resize",def)
runFrameFuncs("Update",def)
end
-- runs update for all chrome and all panels used in current layout
function rematch.frame:Update()
local def = rematch.layout:GetDefinition(C.CURRENT)
runFrameFuncs("Update",def)
if rematch.petCard:IsVisible() then
rematch.petCard:Update()
end
end
--[[ anchoring ]]
-- saves the current position of the frame with respect to its anchor in settings
function rematch.frame:SavePosition()
if not self:IsVisible() or not self:GetLeft() or rematch.journal:IsActive() then
return -- only save position if it's on screen, anchored and not in journal mode
end
local anchor = settings.Anchor
if anchor=="BOTTOMLEFT" then
settings.XPos,settings.YPos = self:GetLeft(),self:GetBottom()
elseif anchor=="TOPLEFT" then
settings.XPos,settings.YPos = self:GetLeft(),self:GetTop()
elseif anchor=="TOP" then
settings.XPos,settings.YPos = (self:GetCenter()),self:GetTop()
elseif anchor=="TOPRIGHT" then
settings.XPos,settings.YPos = self:GetRight(),self:GetTop()
elseif anchor=="BOTTOMRIGHT" then
settings.XPos,settings.YPos = self:GetRight(),self:GetBottom()
elseif anchor=="BOTTOM" then
settings.XPos,settings.YPos = (self:GetCenter()),self:GetBottom()
end
end
-- called when frame configured or shown, it positions the frame to savedsettings, or saves position if none saved
function rematch.frame:RestorePosition()
if settings.XPos and settings.YPos then
self:ClearAllPoints()
self:SetPoint(settings.Anchor,UIParent,"BOTTOMLEFT",settings.XPos,settings.YPos)
else
self:SavePosition()
end
end
-- changes the Anchor setting to one of "BOTTOMLEFT", "TOPLEFT", "TOP", "TOPRIGHT", "BOTTOMRIGHT" or "BOTTOM"
function rematch.frame:ChangeAnchor(anchor)
assert(anchor=="BOTTOMLEFT" or anchor=="TOPLEFT" or anchor=="TOP" or anchor=="TOPRIGHT" or anchor=="BOTTOMRIGHT" or anchor=="BOTTOM","Anchor "..(anchor or nil).." is invalid.")
rematch.settings.Anchor = anchor
rematch.settings.PanelTabAnchor = anchor
if rematch.frame:IsVisible() and not rematch.journal:IsActive() then
rematch.frame:SavePosition()
rematch.frame:Configure(C.CURRENT)
end
end
-- only standalone window can scale; call this for each configure and when scale settings change
function rematch.frame:UpdateScale()
if settings.CustomScale and not rematch.journal:IsActive() then
local scale = max(50,min(tonumber(settings.CustomScaleValue) or 100,200))/100
rematch.frame:SetScale(scale)
else
rematch.frame:SetScale(1)
end
if rematch.frame:IsVisible() then
rematch.frame:SavePosition()
end
end