OmniCC

OmniCC

58M Downloads

Toggling OmniCC display

Rainrider opened this issue ยท 17 comments

commented

I would like to toggle OmniCC display programmatically to show another timer for a short period. My current state is something like:

function overlayPrototype:ToggleOmniCC()
    if not _G.OmniCC then return end

    local display = _G.OmniCC.Display:Get(self:GetParent())

    if self.Timer:IsShown() then
        if display then
            display:Hide()
        end
        -- prevent OmniCC from starting a new timer
        self.button.cooldown.noCooldownCount = true
    else
        if display then
            display:Show()
        else
            -- make OmniCC display the timer (ugly)
            self.button.cooldown:SetCooldown(self.cooldownStart or 0, self.cooldownDuration or 0)
        end
        self.button.cooldown.noCooldownCount = nil
    end
end

Problem 1:
The only way, unless I missed something, to make OmniCC display the timer is to re-set the cooldown. Could you please expose cooldown_OnSetCooldownDuration as it would simplify this?

Problem 2:
I can't reliably track changes of the .noCooldownCount option. Do you have a proposal on how to respect user settings here (currently I nil this to signal OmniCC it can now display its timers again, but this might not be wanted)?

commented

What are you trying to do, exactly? It might be easier for me to add a third layer of cooldowns, like charges

commented

Something like this:
aba_occ

It is showing the remaining duration of the buff from Double Tap on Rapid Fire. After the buff is consumed (here by Aimed Shot, not visible), the OmniCC timer is restored.

commented

Thank you for the code, but I'm not sure I understand why there is need for a new cooldown frame on top of ABA's overlay. ABA's overlay (overlayPrototype) is laid on top of the action button's original cooldown frame (referenced by self.button.cooldown in the code above). ABA's own timer is the small text at the bottom of the button in the gif from my previous post. The bigger yellow ones are the cooldowns of the abilities on the action bar drawn by OmniCC. I aim to hide OmniCC's timer for the abilities CDs when ABA's timer is shown. I'm sorry if I described it misleadingly the previous time.

commented

If there's multiple cooldowns that share the same parent, OmniCC will display text for the one soonest to end. As an example, I've hacked in cooldowns for AdiButtonAuras. Both of these are modifications to Display.lua.

function overlayPrototype:InitializeDisplay()
	self:SetFrameLevel(self.button.cooldown:GetFrameLevel()+1)
	self.parentCount = _G[self.button:GetName().."Count"]

	local highlight = self:CreateTexture(self:GetName().."Highlight", "BACKGROUND")
	highlight:SetAllPoints(self)
	highlight:Hide()
	self.Highlight = highlight

	local overlay = self:CreateTexture(self:GetName().."Overlay", "BACKGROUND")
	overlay:SetColorTexture(0.4, 0.4, 0.4, 1)
	overlay:SetAllPoints(self)
	overlay:Hide()
	self.Overlay = overlay

	local timer = self:CreateFontString(self:GetName().."Timer", "OVERLAY")
	timer:SetFont(fontFile, fontSize, fontFlag)
	timer:SetPoint("BOTTOMLEFT", 2, 2)
	timer:SetPoint("BOTTOMRIGHT", -2, 2)
	timer:SetJustifyV("BOTTOM")
	timer.Update = Timer_Update
	timer:Hide()
	hooksecurefunc(timer, "Show", Text_OnShowHide)
	hooksecurefunc(timer, "Hide", Text_OnShowHide)
	self.Timer = timer

	local count = self:CreateFontString(self:GetName().."Count", "OVERLAY")
	count:SetFont(fontFile, fontSize, fontFlag)
	count:SetPoint("BOTTOMLEFT", 2, 2)
	count:SetPoint("BOTTOMRIGHT", -2, 2)
	count:SetJustifyV("BOTTOM")
	count:Hide()
	hooksecurefunc(count, "Show", Text_OnShowHide)
	hooksecurefunc(count, "Hide", Text_OnShowHide)
	self.Count = count

	local cooldown = CreateFrame("Cooldown", "AdiCooldown_" .. self:GetName(), self.button, "CooldownFrameTemplate")
	cooldown:SetEdgeTexture("Interface\\Cooldown\\edge")
	cooldown:SetSwipeColor(0, 0, 0)
	cooldown:SetDrawSwipe(false)
	cooldown:SetDrawEdge(true)

	cooldown:SetAllPoints(self.button)
	cooldown:SetFrameLevel(self.button.cooldown:GetFrameLevel() + 5)
	self.cooldown = cooldown

	self:ApplySkin()
end

local function durationsEqual(x, y)
	return floor(x or 0) == floor(y or 0)
end

function overlayPrototype:ApplyExpiration()
	local expiration = self.expiration
	self.Timer.expiration = expiration
	self.Timer:Update()

	if durationsEqual(expiration, self.oldExpiration) then return end
	self.oldExpiration = expiration

	local t = GetTime()
	if (expiration or 0) > t then
		self.cooldown:SetCooldownDuration(expiration - t)
	else
		self.cooldown:Clear()
	end
end

I've also created a branch where I've made OmniCC.Cooldown accessible:
https://github.com/tullamods/OmniCC/tree/export-cooldown

commented

This is what I ended up for now using the export-cooldown branch:

function overlayPrototype:ToggleOmniCC()
	if not _G.OmniCC then return end
	-- TODO: Implement opt-in

	local display = _G.OmniCC.Display:Get(self:GetParent())
	local cooldown = self.button.cooldown

	if self.Timer:IsShown() then
		if display then
			display:Hide()
		end
		-- prevent OmniCC from starting a new timer
		if not cooldown.noCooldownCount then
			cooldown.noCooldownCount = addonName
		end
	else
		-- only re-enable OmniCC if we own the lock
		if cooldown.noCooldownCount == addonName then
			cooldown.noCooldownCount = nil
		end

		if display then
			display:Show()
		else
			-- make OmniCC display the timer
			_G.OmniCC.Cooldown.OnSetCooldownDuration(cooldown)
		end
	end
end

I have lingering cooldown counts sometimes but will have to figure this out later. Thanks a lot for your help!

commented

I think I'll add the following to the API

function Cooldown:Refresh()
    local start, duration = self:GetCooldownTimes()

    start = (start or 0) / 1000
    duration = (duration or 0) / 1000

    Cooldown.Initialize(self)
    Cooldown.SetTimer(self, start, duration)
end

function Cooldown:SetNoCooldownCount(disable)
    disable = disable and true or nil
    if self.noCooldownCount == disable then
        return
    end

    if disable then
        self.noCooldownCount = true
        Cooldown.HideText(self)
    else
        self.noCooldownCount = nil
        Cooldown.Refresh(self)
    end
end

Note that action buttons can currently have two cooldown objects: the primary cd for the ability and a charge cooldown. With the API additions you'd want to do something like this:

Cooldown.SetNoCooldownCount(self.button.cooldown, true)

local chargeCooldown = self.button.chargeCooldown
if chargeCooldown then
   Cooldown.SetNoCooldownCount(chargeCooldown  true)
end
commented

The problem with .noCooldownCount being a boolean is that addons are not able to tell who set it and will override each others' preferences. This is why I use cooldown.noCooldownCount = addonName in my code to know that I'm the one who flipped the switch and can thus restore it without overwriting someone else's state.

commented

I proceeded to sit on this and then forget about it :P

function Cooldown:SetNoCooldownCount(disable, owner)
    owner = owner or true

    if disable then
        if not self.noCooldownCount then
            self.noCooldownCount = owner
            Cooldown.HideText(self)
        end
    elseif self.noCooldownCount == owner then
        self.noCooldownCount = nil
        Cooldown.Refresh(self)
    end
end
commented

One problem with this approach is for how long the lock (.noCooldownCount being truthy) should persist. Normally addons would require the lock at some event. If the lock is not owned by them, they can't guarantee it will remain locked for as long as they need it. The other problem is relying on others to release the lock once they are done.

A possibility would be to implement a queue per cooldown where addons can request a lock with a given expiration time point. The highest expiration wins the lock. Every time a new value is added to the queue or an old value is changed, the winner needs to be calculated anew. When the winning expiration is past, the queue is cleared and the cooldown display is refreshed.

I'm not sure if this is an overkill though. What do you think?

commented

I think its overkill for the moment, as you're the first person ever to actually want to adjust this.

commented

Then I would give your proposed API a try if you don't mind putting up a test branch for me.

commented

Its actually already in the current build.

commented

It does not work because the SetCooldown hook calls Cooldown:SetTimer which sets the _occ_start and _occ_duration fields on the cooldown object. Those are not reset when hiding/refreshing the timer thus Cooldown:SetTimer bails out early on subsequent calls. A solution would be to nil one or both fields in Cooldown:Refresh. In my limited testing this didn't have side effects.

commented

This is the reason I suppose.

commented

Yep, don't know how I missed that one

commented

It works fine for me for now, so I'll close this. Thanks a lot for the implementation and the discussion, much appreciated!

commented

I merged it, but am a bit surprised you were seeing an issue, as Refresh calls Initialize, which sets start and duration to zero.