From 0436f8a0e040a7dbe9185949b8e5a3ef108dcd73 Mon Sep 17 00:00:00 2001 From: omicron Date: Sun, 9 Apr 2023 16:39:54 +0200 Subject: [PATCH] Add MultiTrigger to combine multiple triggers into one MultiTrigger will pass on the data from the highest priority child trigger. Fix MultiTrigger MultiTrigger didn't properly inherit from Trigger and reimplemented some methods. This happened because it started out as a copy of Trigger --- src/OmicronFrames.toc | 1 + src/types/triggers/multitrigger.lua | 122 ++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 src/types/triggers/multitrigger.lua 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 +