diff --git a/src/OmicronFrames.toc b/src/OmicronFrames.toc index 4c45c9e..4ea0dcc 100644 --- a/src/OmicronFrames.toc +++ b/src/OmicronFrames.toc @@ -12,6 +12,7 @@ types/object.lua types/triggers/trigger.lua types/triggers/auratrigger.lua types/triggers/statustrigger.lua +types/triggers/multitrigger.lua types/statusbar.lua types/auralist.lua types/indicators/indicator.lua diff --git a/src/types/triggers/multitrigger.lua b/src/types/triggers/multitrigger.lua new file mode 100644 index 0000000..db4571f --- /dev/null +++ b/src/types/triggers/multitrigger.lua @@ -0,0 +1,122 @@ +-- Copyright 2023 +-- +-- This file is part of Omicron Frames +-- +-- Omicron Frames is free software: you can redistribute it and/or modify it +-- under the terms of the GNU General Public License as published by the Free +-- Software Foundation, either version 3 of the License, or (at your option) +-- any later version. +-- +-- Omicron Frames is distributed in the hope that it will be useful, but +-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +-- more details. +-- +-- You should have received a copy of the GNU General Public License along with +-- Omicron Frames. If not, see . +local omi = select(2, ...) +local types = omi.GetModule("types") + +---@class Trigger +local Trigger = types.Trigger + +---MultiTrigger is a trigger that collects other triggers as children. The first +---child added has the highest priority. When several triggers are active at the +---same time, the data from the highest priority trigger will be passed to the +---indicator. +---@class MultiTrigger: Trigger +---@field active boolean +---@field invert boolean +local MultiTrigger = types.CreateClass("MultiTrigger", Trigger) +types.MultiTrigger = MultiTrigger + +--- Initialize a new Trigger object +---@param invert boolean|nil +function MultiTrigger:Init(invert) + Trigger.Init(self, invert, nil) + invert = invert or false + self.invert = invert + self.active = invert + self.children = {} + self.activeChildren = {} + self.childrenData = {} +end + +---Add a child trigger. Each newly added trigger has lower priority than the previous triggers +---@param trigger Trigger +function MultiTrigger:AddTrigger(trigger) + local children = self.children + local idx = #children + 1 + children[idx] = trigger + -- Create a fake Indicator as the target of our children. This is honestly + -- a bit sus. Possibly redesign Trigger to have more than just Indicator targets. + trigger:SetTarget({ + Show = function(_, data) + self:OnChildActivate(idx, trigger, data) + end, + Update = function(_, data) + self:OnChildUpdate(idx, trigger, data) + end, + Hide = function(_, data) + self:OnChildDeactivate(idx, trigger, data) + end + }) +end + +---@param idx number +---@param trigger Trigger +---@param data table +function MultiTrigger:OnChildActivate(idx, trigger, data) + -- TODO: Measure the impact of this. Since we are sorting active triggers + -- this scales at least O(n log(n)) with active triggers. However, this is + -- is likely for very low n + local childrenData = self.childrenData + local activeChildren = self.activeChildren + local activeChild = activeChildren[1] + childrenData[idx] = data + table.insert(activeChildren, idx) + table.sort(activeChildren) + -- The highest priority active trigger has changed + if activeChild ~= activeChildren[1] then + self:SetState(true, data) + end +end + +---@param idx number +---@param trigger Trigger +---@param data table +function MultiTrigger:OnChildDeactivate(idx, trigger, data) + local childrenData = self.childrenData + local activeChildren = self.activeChildren + local activeChild = activeChildren[1] + childrenData[idx] = data + + local found = nil + for i, loopIdx in ipairs(activeChildren) do + if loopIdx == idx then + found = i + break + end + end + table.remove(activeChildren, found) + if activeChild ~= activeChildren[1] then + self:SetState(activeChildren[1] ~= nil, childrenData[1], true) + end +end + +---@param idx number +---@param trigger Trigger +---@param data table +function MultiTrigger:OnChildUpdate(idx, trigger, data) + self.childrenData[idx] = data + if idx == self.activeChildren[1] and self.active then + self.indicator:Update(data) + end +end + +-- Reset the trigger to the default state. If this changes the activation of the +-- trigger then the target will be notified. +function MultiTrigger:Reset() + self:SetState(false) +end +