---------------------------------------------------- -- SamUI v0.3.3 -- Eine vollständige UI-Alternative -- WoW Classic Anniversary TBC -- Interface 20506 -- Build 69110 ---------------------------------------------------- local _, ns = ... local module = ns:new_module("aktionsleisten") local slot_button_size = 36 local special_button_size = 30 local default_button_spacing = 4 local default_buttons_per_row = 12 local default_bar_scale = 100 local bar_gap = 6 local slot_button_count = 12 local special_button_count = 10 local stance_button_count = 10 local numbered_action_slot_maximum = 180 local function show_pet_auto_cast_enabled(self, is_enabled) local manager = module.pet_auto_cast_overlay_manager if not manager then return end if is_enabled then manager:AddActiveShine(self) else manager:RemoveActiveShine(self) end if self.sparkles then for _, sparkle in ipairs(self.sparkles) do sparkle:SetShown(is_enabled) end end end local function initialize_pet_auto_cast_overlay_manager(module_object) if module_object.pet_auto_cast_overlay_manager then return end if not AutoCastOverlayManager or not AutoCastOverlayManagerMixin then return end local manager = CreateFrame("Frame") Mixin(manager, AutoCastOverlayManagerMixin) manager:OnLoad() manager:SetScript("OnUpdate", manager.OnUpdate) module_object.pet_auto_cast_overlay_manager = manager end local numbered_bar_order = { "leiste1", "leiste2", "leiste3", "leiste4", "leiste5", "leiste6", "leiste7", "leiste8", "leiste9", "leiste10", } local all_bar_order = { "leiste1", "leiste2", "leiste3", "leiste4", "leiste5", "leiste6", "leiste7", "leiste8", "leiste9", "leiste10", "pet", "stance", } local numbered_bar_lookup = {} for index = 1, #numbered_bar_order do numbered_bar_lookup[numbered_bar_order[index]] = true end local all_bar_lookup = {} for index = 1, #all_bar_order do all_bar_lookup[all_bar_order[index]] = true end local actionbar_button_count_minimum = 1 local actionbar_button_count_maximum = 12 local actionbar_buttons_per_row_minimum = 1 local actionbar_buttons_per_row_maximum = 12 local actionbar_spacing_minimum = 0 local actionbar_spacing_maximum = 20 local actionbar_scale_minimum = 50 local actionbar_scale_maximum = 150 local actionbar_offset_x_minimum = -1000 local actionbar_offset_x_maximum = 1000 local actionbar_offset_y_minimum = -500 local actionbar_offset_y_maximum = 500 local actionbar_lock_modifiers = { ALT = true, CTRL = true, SHIFT = true, NONE = true, } local actionbar_lock_type_attributes = { "alt-type*", "ctrl-type*", "shift-type*", "alt-ctrl-type*", "alt-shift-type*", "ctrl-shift-type*", "alt-ctrl-shift-type*", } local actionbar_lock_modifier_attributes = { ALT = { "alt-type*", "alt-ctrl-type*", "alt-shift-type*", "alt-ctrl-shift-type*", }, CTRL = { "ctrl-type*", "alt-ctrl-type*", "ctrl-shift-type*", "alt-ctrl-shift-type*", }, SHIFT = { "shift-type*", "alt-shift-type*", "ctrl-shift-type*", "alt-ctrl-shift-type*", }, } local bar_definitions = { leiste1 = { slot_start = 1, anchor = { point = "BOTTOM", relative_key = nil, relative_point = "BOTTOM" }, default_offset_x = 0, default_offset_y = 20, default_spacing = 10, default_buttons_per_row = 6, default_scale = 90, binding_prefix = "ACTIONBUTTON", }, leiste2 = { slot_start = 61, anchor = { point = "BOTTOM", relative_key = nil, relative_point = "BOTTOM" }, default_offset_x = -260, default_offset_y = 20, default_spacing = 10, default_buttons_per_row = 6, default_scale = 90, binding_prefix = "MULTIACTIONBAR1BUTTON", }, leiste3 = { slot_start = 49, anchor = { point = "BOTTOM", relative_key = nil, relative_point = "BOTTOM" }, default_offset_x = 260, default_offset_y = 20, default_spacing = 10, default_buttons_per_row = 6, default_scale = 90, binding_prefix = "MULTIACTIONBAR2BUTTON", }, leiste4 = { slot_start = 25, anchor = { point = "BOTTOM", relative_key = nil, relative_point = "BOTTOM" }, default_offset_x = 0, default_offset_y = 146, default_enabled = false, binding_prefix = "MULTIACTIONBAR3BUTTON", }, leiste5 = { slot_start = 37, anchor = { point = "BOTTOM", relative_key = nil, relative_point = "BOTTOM" }, default_offset_x = 0, default_offset_y = 188, default_enabled = false, binding_prefix = "MULTIACTIONBAR4BUTTON", }, leiste6 = { slot_start = 145, anchor = { point = "BOTTOM", relative_key = nil, relative_point = "BOTTOM" }, default_offset_x = 0, default_offset_y = 230, default_enabled = false, binding_prefix = "MULTIACTIONBAR5BUTTON", }, leiste7 = { slot_start = 157, anchor = { point = "BOTTOM", relative_key = nil, relative_point = "BOTTOM" }, default_offset_x = 0, default_offset_y = 272, default_enabled = false, binding_prefix = "MULTIACTIONBAR6BUTTON", }, leiste8 = { slot_start = 169, anchor = { point = "BOTTOM", relative_key = nil, relative_point = "BOTTOM" }, default_offset_x = 0, default_offset_y = 314, default_enabled = false, binding_prefix = "MULTIACTIONBAR7BUTTON", }, leiste9 = { slot_start = 97, anchor = { point = "BOTTOM", relative_key = nil, relative_point = "BOTTOM" }, default_offset_x = 0, default_offset_y = 356, default_enabled = false, }, leiste10 = { slot_start = 109, anchor = { point = "BOTTOM", relative_key = nil, relative_point = "BOTTOM" }, default_offset_x = 0, default_offset_y = 398, default_enabled = false, }, pet = { anchor = { point = "BOTTOM", relative_key = nil, relative_point = "BOTTOM" }, default_offset_x = 260, default_offset_y = 100, default_spacing = 5, default_scale = 70, }, stance = { anchor = { point = "BOTTOM", relative_key = nil, relative_point = "BOTTOM" }, default_offset_x = 0, default_offset_y = 476, }, } local function clamp_number(value, minimum, maximum) if value < minimum then return minimum elseif value > maximum then return maximum end return value end local function round_to_step(value, step) if value >= 0 then return math.floor(value / step + 0.5) * step end return math.ceil(value / step - 0.5) * step end local function normalize_number(value, minimum, maximum, step) if type(value) ~= "number" then return nil end return clamp_number(round_to_step(value, step), minimum, maximum) end local function create_runtime_settings() local settings = { allgemein = { lock_modifier = "SHIFT", cooldown_numbers_enabled = true, tooltip_enabled = true, }, } for index = 1, #all_bar_order do local bar_key = all_bar_order[index] local definition = bar_definitions[bar_key] local bar_settings = { spacing = definition.default_spacing or default_button_spacing, scale = definition.default_scale or default_bar_scale, buttons_per_row = definition.default_buttons_per_row or default_buttons_per_row, offset_x = definition.default_offset_x, offset_y = definition.default_offset_y, enabled = definition.default_enabled ~= false, } if numbered_bar_lookup[bar_key] then bar_settings.button_count = slot_button_count bar_settings.swap_target = nil bar_settings.range_check_enabled = true end settings[bar_key] = bar_settings end return settings end local function shorten_binding(binding) if not binding or binding == "" then return "" end local text = binding text = text:gsub("SHIFT%-", "S") text = text:gsub("CTRL%-", "C") text = text:gsub("ALT%-", "A") text = text:gsub("MOUSEWHEELUP", "MWU") text = text:gsub("MOUSEWHEELDOWN", "MWD") text = text:gsub("BUTTON", "M") text = text:gsub("NUMPAD", "N") return text end local function apply_cooldown_countdown_font(cooldown) if not cooldown then return false end if type(cooldown.SetCountdownFont) == "function" then cooldown:SetCountdownFont("samui_font_18_outline") return true end if type(cooldown.GetRegions) ~= "function" then return false end local regions = { cooldown:GetRegions() } for index = 1, #regions do local region = regions[index] if region and type(region.GetObjectType) == "function" and region:GetObjectType() == "FontString" and type(region.SetFontObject) == "function" then region:SetFontObject(samui_font_18_outline) return true end end return false end local function set_cooldown(cooldown, start_time, duration, enabled) CooldownFrame_Set(cooldown, start_time or 0, duration or 0, enabled or 0) apply_cooldown_countdown_font(cooldown) end local function set_icon_usable(button, usable) if usable == false or usable == 0 then button.icon:SetVertexColor(ns.palette:get_rgb("border")) else button.icon:SetVertexColor(ns.palette:get_rgb("text")) end end local function set_range_state(button, out_of_range) if out_of_range then button.range_overlay:Show() else button.range_overlay:Hide() end end local function create_button_border(button) local border_name = button:GetName() .. "_border" local border = CreateFrame("Frame", border_name, button, "BackdropTemplate") local offset = ns.designelemente:get_border_offset("casticon") border:SetPoint("TOPLEFT", button, "TOPLEFT", -offset, offset) border:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", offset, -offset) border:SetFrameLevel(button:GetFrameLevel() + 10) border:EnableMouse(false) border:SetBackdrop({ edgeFile = ns.designelemente:get_texture("border"), edgeSize = ns.designelemente:get_border_size("casticon"), tileEdge = true, }) return border end local function apply_button_palette(button) local palette = ns.palette button.background:SetColorTexture(palette:get_surface("button")) button.border:SetBackdropBorderColor( palette:get_rgba("border", palette:get_alpha("opaque")) ) button.range_overlay:SetColorTexture( palette:get_rgba("red", palette:get_alpha("out_of_range")) ) button.hotkey_text:SetTextColor(palette:get_rgb("text")) button.macro_text:SetTextColor(palette:get_rgb("text_muted")) button.count_text:SetTextColor(palette:get_rgb("text")) end local function create_button_visuals(button, size) local designelemente = ns.designelemente local background = button:CreateTexture(nil, "BACKGROUND") background:SetAllPoints(button) button.background = background local icon = button:CreateTexture(nil, "ARTWORK") icon:SetAllPoints(button) button.icon = icon button.border = create_button_border(button) button:SetPushedTexture(designelemente:get_texture("action_button_pushed")) button:SetHighlightTexture(designelemente:get_texture("action_button_highlight")) if button:GetHighlightTexture() then button:GetHighlightTexture():SetBlendMode("ADD") end button:SetCheckedTexture(designelemente:get_texture("action_button_checked")) if button:GetCheckedTexture() then button:GetCheckedTexture():SetBlendMode("ADD") end local range_overlay = button:CreateTexture(nil, "OVERLAY") range_overlay:SetAllPoints(button) range_overlay:Hide() button.range_overlay = range_overlay local cooldown_name = button:GetName() .. "_cooldown" local cooldown = CreateFrame("Cooldown", cooldown_name, button, "CooldownFrameTemplate") cooldown:SetAllPoints(button) apply_cooldown_countdown_font(cooldown) button.cooldown = cooldown local hotkey_text = button:CreateFontString(nil, "OVERLAY") hotkey_text:SetFontObject(samui_font_10) hotkey_text:SetPoint("TOPRIGHT", button, "TOPRIGHT", -2, -2) hotkey_text:SetJustifyH("RIGHT") button.hotkey_text = hotkey_text local macro_text = button:CreateFontString(nil, "OVERLAY") macro_text:SetFontObject(samui_font_9) macro_text:SetPoint("BOTTOM", button, "BOTTOM", 0, 2) macro_text:SetWidth(size - 4) macro_text:SetJustifyH("CENTER") button.macro_text = macro_text local count_text = button:CreateFontString(nil, "OVERLAY") count_text:SetFontObject(samui_font_11) count_text:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -2, 2) count_text:SetJustifyH("RIGHT") button.count_text = count_text apply_button_palette(button) end local function apply_native_special_button_visuals(button, size) local designelemente = ns.designelemente local background = button:CreateTexture(nil, "BACKGROUND") background:SetAllPoints(button) button.background = background if button.icon then button.icon:SetDrawLayer("ARTWORK") button.icon:ClearAllPoints() button.icon:SetAllPoints(button) end if button.NormalTexture then button.NormalTexture:Hide() end if button.SlotBackground then button.SlotBackground:Hide() end if button.Border then button.Border:Hide() end if button.NewActionTexture then button.NewActionTexture:Hide() end if button.LevelLinkLockIcon then button.LevelLinkLockIcon:Hide() end button.border = create_button_border(button) button:SetPushedTexture(designelemente:get_texture("action_button_pushed")) button:SetHighlightTexture(designelemente:get_texture("action_button_highlight")) if button:GetHighlightTexture() then button:GetHighlightTexture():SetBlendMode("ADD") end button:SetCheckedTexture(designelemente:get_texture("action_button_checked")) if button:GetCheckedTexture() then button:GetCheckedTexture():SetBlendMode("ADD") end local range_overlay = button:CreateTexture(nil, "OVERLAY") range_overlay:SetAllPoints(button) range_overlay:Hide() button.range_overlay = range_overlay if button.cooldown then button.cooldown:ClearAllPoints() button.cooldown:SetAllPoints(button) apply_cooldown_countdown_font(button.cooldown) end button.hotkey_text = button.HotKey if button.hotkey_text then button.hotkey_text:SetFontObject(samui_font_10) button.hotkey_text:ClearAllPoints() button.hotkey_text:SetPoint("TOPRIGHT", button, "TOPRIGHT", -2, -2) button.hotkey_text:SetJustifyH("RIGHT") end button.macro_text = button.Name if button.macro_text then button.macro_text:SetFontObject(samui_font_9) button.macro_text:ClearAllPoints() button.macro_text:SetPoint("BOTTOM", button, "BOTTOM", 0, 2) button.macro_text:SetWidth(size - 4) button.macro_text:SetJustifyH("CENTER") end button.count_text = button.Count if button.count_text then button.count_text:SetFontObject(samui_font_11) button.count_text:ClearAllPoints() button.count_text:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -2, 2) button.count_text:SetJustifyH("RIGHT") end apply_button_palette(button) end local function position_button(button, bar, index, size, spacing, buttons_per_row) local resolved_per_row = math.max(buttons_per_row or default_buttons_per_row, 1) local column = (index - 1) % resolved_per_row local row = math.floor((index - 1) / resolved_per_row) local step = size + spacing button:ClearAllPoints() button:SetPoint( "BOTTOMLEFT", bar, "BOTTOMLEFT", column * step, row * step ) end local function size_bar(bar, count, size, spacing, buttons_per_row) local resolved_count = math.max(count, 1) local resolved_per_row = math.max(buttons_per_row or default_buttons_per_row, 1) local columns = math.min(resolved_count, resolved_per_row) local rows = math.ceil(resolved_count / resolved_per_row) bar:SetSize( columns * size + (columns - 1) * spacing, rows * size + (rows - 1) * spacing ) end local function anchor_bar(module_object, bar_key) local bar = module_object.bars[bar_key] local settings = module_object.settings[bar_key] local definition = bar_definitions[bar_key] if not bar or not settings or not definition then return false end local anchor = definition.anchor local relative_frame = UIParent if anchor.relative_key then relative_frame = module_object.bars[anchor.relative_key] if not relative_frame then return false end end local scale = settings.scale / 100 bar:SetScale(scale) bar:ClearAllPoints() bar:SetPoint( anchor.point, relative_frame, anchor.relative_point, settings.offset_x / scale, settings.offset_y / scale ) return true end local function is_valid_action_slot(slot) return type(slot) == "number" and slot >= 1 and slot <= numbered_action_slot_maximum end local function get_numbered_slot(bar_key, index) local definition = bar_definitions[bar_key] if not definition or not definition.slot_start then return nil end return definition.slot_start + index - 1 end local function get_numbered_page(bar_key) local slot = get_numbered_slot(bar_key, 1) if not is_valid_action_slot(slot) then return nil end return math.floor((slot - 1) / slot_button_count) + 1 end local function resolve_blizzard_actionbar_sources(module_object) local main_action_bar = _G.MainActionBar local pet_action_bar = _G.PetActionBar local stance_bar = _G.StanceBar local quick_keybind_frame = _G.QuickKeybindFrame if not main_action_bar or type(main_action_bar.GetAttribute) ~= "function" then ns:chat_message("Blizzard-Aktionsleiste 'MainActionBar' konnte nicht aufgelöst werden", true) return false end if not pet_action_bar or type(pet_action_bar.Update) ~= "function" then ns:chat_message("Blizzard-Aktionsleiste 'PetActionBar' konnte nicht aufgelöst werden", true) return false end if not stance_bar or type(stance_bar.Select) ~= "function" then ns:chat_message("Blizzard-Aktionsleiste 'StanceBar' konnte nicht aufgelöst werden", true) return false end if not quick_keybind_frame or type(quick_keybind_frame.HookScript) ~= "function" or not QuickKeybindButtonTemplateMixin then ns:chat_message("Blizzard-Schnellbelegung konnte nicht aufgelöst werden", true) return false end module_object.blizzard_main_action_bar = main_action_bar module_object.quick_keybind_frame = quick_keybind_frame return true end local function get_main_actionpage(module_object) local main_action_bar = module_object.blizzard_main_action_bar local page = main_action_bar and main_action_bar:GetAttribute("actionpage") or nil if type(page) ~= "number" and C_ActionBar and C_ActionBar.GetActionBarPage then page = C_ActionBar.GetActionBarPage() end if type(page) ~= "number" or page < 1 then return 1 end return page end local function source_uses_main_actionpage(bar_key) return bar_key == "leiste1" end local function apply_numbered_bar_source(module_object, bar, source_bar_key) local uses_main_actionpage = source_uses_main_actionpage(source_bar_key) local page = uses_main_actionpage and get_main_actionpage(module_object) or get_numbered_page(source_bar_key) if type(page) ~= "number" then return false end bar:SetAttribute("main_actionpage", get_main_actionpage(module_object)) bar:SetAttribute("uses_main_actionpage", uses_main_actionpage) bar:SetAttribute("actionpage", page) return true end local function install_main_actionpage_bridge(module_object) if module_object.page_bridge_installed then return true end local main_action_bar = module_object.blizzard_main_action_bar local manager = module_object.page_manager if not main_action_bar or not manager then return false end for index = 1, #numbered_bar_order do local bar = module_object.bars[numbered_bar_order[index]] if bar then manager:SetFrameRef("bar_" .. index, bar) end end manager:Execute([[ samui_page_manager = self ]]) manager:WrapScript(main_action_bar, "OnAttributeChanged", [[ if name == "actionpage" and value then for index = 1, 10 do local bar = samui_page_manager:GetFrameRef("bar_" .. index) if bar then bar:SetAttribute("main_actionpage", value) if bar:GetAttribute("uses_main_actionpage") then bar:SetAttribute("actionpage", value) end end end end ]]) module_object.page_bridge_installed = true return true end local function get_effective_source_bar(module_object, bar_key) local settings = module_object.settings[bar_key] local form_index = GetShapeshiftForm() if settings and settings.swap_target and type(form_index) == "number" and form_index ~= 0 then return settings.swap_target end return bar_key end local function get_effective_slot(module_object, bar_key, index) local bar = module_object.bars[bar_key] local page = bar and bar:GetAttribute("actionpage") or nil if type(page) == "number" and page >= 1 then local slot = index + ((page - 1) * slot_button_count) if is_valid_action_slot(slot) then return slot end end local source_bar_key = get_effective_source_bar(module_object, bar_key) if source_uses_main_actionpage(source_bar_key) then page = get_main_actionpage(module_object) local slot = index + ((page - 1) * slot_button_count) if is_valid_action_slot(slot) then return slot end end return get_numbered_slot(source_bar_key, index) end local function update_hotkey(button, binding_action) local binding = binding_action and GetBindingKey(binding_action) or nil local text = shorten_binding(binding) button.hotkey_text:SetText(text) if text ~= "" then button.hotkey_text:Show() else button.hotkey_text:Hide() end end local function clear_button_visuals(button) button.icon:SetTexture(nil) button.icon:Hide() button.macro_text:SetText("") button.macro_text:Hide() button.count_text:SetText("") button.count_text:Hide() button:SetChecked(false) set_icon_usable(button, true) set_range_state(button, false) set_cooldown(button.cooldown, 0, 0, 0) end local function update_action_count(button, slot) if not is_valid_action_slot(slot) then button.count_text:SetText("") button.count_text:Hide() return end local count = GetActionCount(slot) if type(count) == "number" and count > 1 then button.count_text:SetText(count) button.count_text:Show() else button.count_text:SetText("") button.count_text:Hide() end end local function update_action_content(button, slot) if not is_valid_action_slot(slot) then button.icon:SetTexture(nil) button.icon:Hide() button.macro_text:SetText("") button.macro_text:Hide() update_action_count(button, nil) return false end local has_action = C_ActionBar.HasAction(slot) local texture = has_action and C_ActionBar.GetActionTexture(slot) or nil local action_text = has_action and GetActionText(slot) or nil button.icon:SetTexture(texture) if texture then button.icon:Show() else button.icon:Hide() end button.macro_text:SetText(action_text or "") if action_text and action_text ~= "" then button.macro_text:Show() else button.macro_text:Hide() end update_action_count(button, slot) return true end local function update_action_cooldown(button, slot) if not is_valid_action_slot(slot) then set_cooldown(button.cooldown, 0, 0, 0) return end local start_time, duration, enabled = GetActionCooldown(slot) set_cooldown(button.cooldown, start_time, duration, enabled) end local function update_action_usable(button, slot, usable) if not is_valid_action_slot(slot) then set_icon_usable(button, true) return end if usable == nil then usable = IsUsableAction(slot) end set_icon_usable(button, usable) end local function update_action_state(button, slot) if not is_valid_action_slot(slot) then button:SetChecked(false) return end local active = C_ActionBar.IsCurrentAction(slot) or C_ActionBar.IsAutoRepeatAction(slot) local auto_cast_pet = C_ActionBar.IsAutoCastPetAction(slot) button:SetChecked(active and not auto_cast_pet) end local function update_action_visual(button, slot) if not is_valid_action_slot(slot) then clear_button_visuals(button) return end update_action_content(button, slot) update_action_cooldown(button, slot) update_action_usable(button, slot) update_action_state(button, slot) end local function clear_slot_button_index(module_object) for slot = 1, numbered_action_slot_maximum do local bucket = module_object.slot_buttons[slot] for button in pairs(bucket) do bucket[button] = nil end end end local function get_button_slot(module_object, button) return get_effective_slot(module_object, button.bar_key, button.index) end local function rebuild_slot_button_index(module_object) clear_slot_button_index(module_object) for bar_index = 1, #numbered_bar_order do local bar_key = numbered_bar_order[bar_index] local bar = module_object.bars[bar_key] if bar then for index = 1, #bar.buttons do local button = bar.buttons[index] local slot = get_button_slot(module_object, button) button.current_slot = slot if is_valid_action_slot(slot) then module_object.slot_buttons[slot][button] = true end end end end end local function update_numbered_button(module_object, bar_key, button, update_binding) local slot = get_button_slot(module_object, button) local definition = bar_definitions[bar_key] update_action_visual(button, slot) if update_binding then local binding_action = definition.binding_prefix and definition.binding_prefix .. button.index or nil update_hotkey(button, binding_action) end end local function update_numbered_bar(module_object, bar_key, update_bindings) local bar = module_object.bars[bar_key] if not bar then return end for index = 1, #bar.buttons do update_numbered_button( module_object, bar_key, bar.buttons[index], update_bindings == true ) end end local function update_all_numbered_bars(module_object, update_bindings) for index = 1, #numbered_bar_order do update_numbered_bar( module_object, numbered_bar_order[index], update_bindings == true ) end end local function update_numbered_cooldowns(module_object) for bar_index = 1, #numbered_bar_order do local bar = module_object.bars[numbered_bar_order[bar_index]] if bar then for index = 1, #bar.buttons do local button = bar.buttons[index] update_action_cooldown(button, get_button_slot(module_object, button)) end end end end local function update_numbered_usable(module_object) for bar_index = 1, #numbered_bar_order do local bar = module_object.bars[numbered_bar_order[bar_index]] if bar then for index = 1, #bar.buttons do local button = bar.buttons[index] update_action_usable(button, get_button_slot(module_object, button)) end end end end local function update_numbered_states(module_object) for bar_index = 1, #numbered_bar_order do local bar = module_object.bars[numbered_bar_order[bar_index]] if bar then for index = 1, #bar.buttons do local button = bar.buttons[index] update_action_state(button, get_button_slot(module_object, button)) end end end end local function update_numbered_counts(module_object) for bar_index = 1, #numbered_bar_order do local bar = module_object.bars[numbered_bar_order[bar_index]] if bar then for index = 1, #bar.buttons do local button = bar.buttons[index] update_action_count(button, get_button_slot(module_object, button)) end end end end local function update_numbered_hotkeys(module_object) for bar_index = 1, #numbered_bar_order do local bar_key = numbered_bar_order[bar_index] local definition = bar_definitions[bar_key] local bar = module_object.bars[bar_key] if bar then for index = 1, #bar.buttons do local button = bar.buttons[index] local binding_action = definition.binding_prefix and definition.binding_prefix .. button.index or nil update_hotkey(button, binding_action) end end end end local function update_slot_visuals(module_object, slot) if slot == 0 then update_all_numbered_bars(module_object, false) return end if not is_valid_action_slot(slot) then return end local bucket = module_object.slot_buttons[slot] for button in pairs(bucket) do update_action_visual(button, slot) end end local function update_slot_usable(module_object, slot, usable) if not is_valid_action_slot(slot) then return end local bucket = module_object.slot_buttons[slot] for button in pairs(bucket) do update_action_usable(button, slot, usable) end end local enable_action_range_check = C_ActionBar and C_ActionBar.EnableActionRangeCheck local is_action_in_range = C_ActionBar and C_ActionBar.IsActionInRange local function is_range_eligible(module_object, button) if not UnitExists("target") then return false end local settings = module_object.settings[button.bar_key] if not settings or settings.range_check_enabled ~= true then return false end if button.bar_key ~= "leiste1" and settings.enabled == false then return false end if button.index > settings.button_count then return false end return is_valid_action_slot(get_button_slot(module_object, button)) end local function refresh_range_registrations(module_object) for slot = 1, numbered_action_slot_maximum do module_object.range_desired_slots[slot] = false end local has_target = UnitExists("target") and true or false if has_target and type(enable_action_range_check) == "function" and type(is_action_in_range) == "function" then for bar_index = 1, #numbered_bar_order do local bar = module_object.bars[numbered_bar_order[bar_index]] if bar then for index = 1, #bar.buttons do local button = bar.buttons[index] if is_range_eligible(module_object, button) then local slot = get_button_slot(module_object, button) module_object.range_desired_slots[slot] = true local in_range = is_action_in_range(slot) set_range_state(button, in_range == false or in_range == 0) else set_range_state(button, false) end end end end else for bar_index = 1, #numbered_bar_order do local bar = module_object.bars[numbered_bar_order[bar_index]] if bar then for index = 1, #bar.buttons do set_range_state(bar.buttons[index], false) end end end end if type(enable_action_range_check) ~= "function" then return end for slot = 1, numbered_action_slot_maximum do local desired = module_object.range_desired_slots[slot] == true local registered = module_object.range_registered_slots[slot] == true if desired and not registered then enable_action_range_check(slot, true) module_object.range_registered_slots[slot] = true elseif registered and not desired then enable_action_range_check(slot, false) module_object.range_registered_slots[slot] = nil end end end local function update_slot_range(module_object, slot, in_range, checks_range) if not is_valid_action_slot(slot) then return end local bucket = module_object.slot_buttons[slot] for button in pairs(bucket) do if is_range_eligible(module_object, button) then set_range_state( button, (checks_range == true or checks_range == 1) and (in_range == false or in_range == 0) ) else set_range_state(button, false) end end end local function update_pet_button(button) local name, texture, is_token, is_active, auto_cast_allowed, auto_cast_enabled, _, checks_range, in_range = GetPetActionInfo(button.index) local resolved_texture = texture local resolved_name = name if is_token then resolved_texture = texture and _G[texture] or nil resolved_name = name and _G[name] or nil end button.tooltipName = resolved_name button.icon:SetTexture(resolved_texture) if resolved_texture then button.icon:Show() else button.icon:Hide() end button.macro_text:SetText("") button.macro_text:Hide() button.count_text:SetText("") button.count_text:Hide() button:SetChecked(is_active == true or is_active == 1) if button.AutoCastOverlay then button.AutoCastOverlay:SetShown(auto_cast_allowed == true or auto_cast_allowed == 1) if button.AutoCastOverlay.ShowAutoCastEnabled then button.AutoCastOverlay:ShowAutoCastEnabled( auto_cast_enabled == true or auto_cast_enabled == 1 ) end end set_icon_usable(button, GetPetActionSlotUsable(button.index)) set_range_state( button, (checks_range == true or checks_range == 1) and (in_range == false or in_range == 0) ) local start_time, duration, enabled = GetPetActionCooldown(button.index) set_cooldown(button.cooldown, start_time, duration, enabled) update_hotkey(button, "BONUSACTIONBUTTON" .. button.index) end local function update_pet_buttons(module_object) for index = 1, #module_object.pet_buttons do update_pet_button(module_object.pet_buttons[index]) end end local function update_stance_button_visual(button) local form_count = GetNumShapeshiftForms() if button.index > form_count then clear_button_visuals(button) update_hotkey(button, nil) return end local texture, is_active, is_castable = GetShapeshiftFormInfo(button.index) local start_time, duration, enabled = GetShapeshiftFormCooldown(button.index) button.icon:SetTexture(texture) if texture then button.icon:Show() else button.icon:Hide() end button.macro_text:SetText("") button.macro_text:Hide() button.count_text:SetText("") button.count_text:Hide() button:SetChecked(is_active == true or is_active == 1) set_icon_usable(button, is_castable) set_range_state(button, false) set_cooldown(button.cooldown, start_time, duration, enabled) update_hotkey(button, "SHAPESHIFTBUTTON" .. button.index) end local function update_stance_visuals(module_object) for index = 1, #module_object.stance_buttons do update_stance_button_visual(module_object.stance_buttons[index]) end end local function apply_stance_secure_state(module_object) local bar = module_object.bars.stance if not bar then return false end if InCombatLockdown() then module_object.pending_stance_secure_update = true return false end local settings = module_object.settings.stance local form_count = math.min(GetNumShapeshiftForms(), stance_button_count) for index = 1, #module_object.stance_buttons do local button = module_object.stance_buttons[index] if index <= form_count then button:Show() else button:Hide() end position_button( button, bar, index, special_button_size, settings.spacing, settings.buttons_per_row ) end size_bar( bar, math.max(form_count, 1), special_button_size, settings.spacing, settings.buttons_per_row ) if settings.enabled ~= false and form_count > 0 then bar:Show() else bar:Hide() end module_object.pending_stance_secure_update = nil return true end local function configure_swap(module_object, bar_key) local bar = module_object.bars[bar_key] local settings = module_object.settings[bar_key] if not bar or not settings then return false end if InCombatLockdown() then module_object.pending_swap[bar_key] = true return false end local normal_uses_main_actionpage = source_uses_main_actionpage(bar_key) bar:SetAttribute("normal_uses_main_actionpage", normal_uses_main_actionpage) bar:SetAttribute( "normal_actionpage", normal_uses_main_actionpage and nil or get_numbered_page(bar_key) ) if settings.swap_target then local swap_uses_main_actionpage = source_uses_main_actionpage(settings.swap_target) bar:SetAttribute("swap_uses_main_actionpage", swap_uses_main_actionpage) bar:SetAttribute( "swap_actionpage", swap_uses_main_actionpage and nil or get_numbered_page(settings.swap_target) ) if not bar.has_swap_state_driver then RegisterStateDriver(bar, "swap", "[form] swapped; normal") bar.has_swap_state_driver = true end else bar:SetAttribute("swap_uses_main_actionpage", nil) bar:SetAttribute("swap_actionpage", nil) if bar.has_swap_state_driver then UnregisterStateDriver(bar, "swap") bar.has_swap_state_driver = nil end end local form_index = GetShapeshiftForm() local swap_active = settings.swap_target and type(form_index) == "number" and form_index ~= 0 local source_bar_key = swap_active and settings.swap_target or bar_key apply_numbered_bar_source(module_object, bar, source_bar_key) module_object.pending_swap[bar_key] = nil rebuild_slot_button_index(module_object) update_numbered_bar(module_object, bar_key, false) refresh_range_registrations(module_object) return true end local function apply_bar_visibility(module_object, bar_key) local bar = module_object.bars[bar_key] local settings = module_object.settings[bar_key] if not bar or not settings then return false end if InCombatLockdown() then module_object.pending_visibility[bar_key] = true return false end local enabled = bar_key == "leiste1" or settings.enabled ~= false if numbered_bar_lookup[bar_key] then if enabled then bar:Show() else bar:Hide() end elseif bar_key == "pet" then if enabled then if not bar.has_visibility_state_driver then RegisterStateDriver(bar, "visibility", "[@pet,exists] show; hide") bar.has_visibility_state_driver = true end else if bar.has_visibility_state_driver then UnregisterStateDriver(bar, "visibility") bar.has_visibility_state_driver = nil end bar:Hide() end else if enabled then apply_stance_secure_state(module_object) else bar:Hide() end end module_object.pending_visibility[bar_key] = nil return true end local function apply_bar_layout(module_object, bar_key) local bar = module_object.bars[bar_key] local settings = module_object.settings[bar_key] local definition = bar_definitions[bar_key] if not bar or not settings or not definition then return false end if InCombatLockdown() then module_object.pending_layout[bar_key] = true return false end anchor_bar(module_object, bar_key) if numbered_bar_lookup[bar_key] then size_bar( bar, settings.button_count, slot_button_size, settings.spacing, settings.buttons_per_row ) for index = 1, #bar.buttons do local button = bar.buttons[index] position_button( button, bar, index, slot_button_size, settings.spacing, settings.buttons_per_row ) if index <= settings.button_count then button:Show() else button:Hide() end end elseif bar_key == "pet" then size_bar( bar, special_button_count, special_button_size, settings.spacing, settings.buttons_per_row ) for index = 1, #bar.buttons do position_button( bar.buttons[index], bar, index, special_button_size, settings.spacing, settings.buttons_per_row ) end else apply_stance_secure_state(module_object) end module_object.pending_layout[bar_key] = nil return true end local function update_button_slot_index(module_object, button) local previous_slot = button.current_slot if is_valid_action_slot(previous_slot) then module_object.slot_buttons[previous_slot][button] = nil end local active_slot = get_button_slot(module_object, button) button.current_slot = active_slot if is_valid_action_slot(active_slot) then module_object.slot_buttons[active_slot][button] = true end return active_slot end local function apply_actionbar_lock_button_attributes(module_object, lock_modifier) if InCombatLockdown() then module_object.pending_lock_attributes = true return false end local active_attributes = actionbar_lock_modifier_attributes[lock_modifier] local function apply_to_bar(bar) if not bar then return end for button_index = 1, #bar.buttons do local button = bar.buttons[button_index] for attribute_index = 1, #actionbar_lock_type_attributes do button:SetAttribute(actionbar_lock_type_attributes[attribute_index], nil) end if active_attributes then for attribute_index = 1, #active_attributes do button:SetAttribute( active_attributes[attribute_index], "" ) end end end end for bar_index = 1, #numbered_bar_order do apply_to_bar(module_object.bars[numbered_bar_order[bar_index]]) end module_object.pending_lock_attributes = nil return true end local function apply_actionbar_lock_setting(module_object) local settings = module_object.settings and module_object.settings.allgemein if not settings or not actionbar_lock_modifiers[settings.lock_modifier] then return false end SetModifiedClick("PICKUPACTION", settings.lock_modifier) SetCVar("lockActionBars", settings.lock_modifier == "NONE" and "0" or "1") apply_actionbar_lock_button_attributes(module_object, settings.lock_modifier) return true end local function apply_actionbar_cooldown_numbers_setting(module_object) local settings = module_object.settings and module_object.settings.allgemein if not settings or type(settings.cooldown_numbers_enabled) ~= "boolean" then return false end SetCVar("countdownForCooldowns", settings.cooldown_numbers_enabled and "1" or "0") for _, bar in pairs(module_object.bars) do for index = 1, #bar.buttons do local cooldown = bar.buttons[index].cooldown if cooldown and type(cooldown.SetHideCountdownNumbers) == "function" then apply_cooldown_countdown_font(cooldown) cooldown:SetHideCountdownNumbers(not settings.cooldown_numbers_enabled) end end end return true end local function quick_keybind_button_on_enter(button) if not button.commandName or type(button.QuickKeybindButtonOnEnter) ~= "function" or type(KeybindFrames_InQuickKeybindMode) ~= "function" or not KeybindFrames_InQuickKeybindMode() then return false end button:QuickKeybindButtonOnEnter() return true end local function quick_keybind_button_on_leave(button) if button.commandName and type(button.QuickKeybindButtonOnLeave) == "function" then button:QuickKeybindButtonOnLeave() end end local function set_quick_keybind_highlights(module_object, shown) for _, bar in pairs(module_object.bars) do for index = 1, #bar.buttons do local button = bar.buttons[index] if button.commandName and type(button.DoModeChange) == "function" then button:DoModeChange(shown) end end end end local function install_quick_keybind_integration(module_object) if module_object.quick_keybind_integration_installed then return true end local quick_keybind_frame = module_object.quick_keybind_frame if not quick_keybind_frame then return false end quick_keybind_frame:HookScript("OnShow", function() set_quick_keybind_highlights(module_object, true) end) quick_keybind_frame:HookScript("OnHide", function() set_quick_keybind_highlights(module_object, false) end) module_object.quick_keybind_integration_installed = true set_quick_keybind_highlights(module_object, quick_keybind_frame:IsShown()) return true end local function actionbar_tooltips_enabled() local settings = module.settings and module.settings.allgemein return not settings or settings.tooltip_enabled ~= false end local function reset_button_press_visual(button) if button:GetButtonState() == "PUSHED" then button:SetButtonState("NORMAL") end if is_valid_action_slot(button.current_slot) then update_action_state(button, button.current_slot) end end local function create_numbered_button(module_object, bar, bar_key, index) local name = "samui_aktionsleiste_" .. bar_key .. "_button_" .. index local button = CreateFrame( "CheckButton", name, bar, "QuickKeybindButtonTemplate, SecureActionButtonTemplate" ) button:SetSize(slot_button_size, slot_button_size) button:SetID(index) button.index = index button.bar_key = bar_key local definition = bar_definitions[bar_key] if definition and definition.binding_prefix then button.commandName = definition.binding_prefix .. index end button:SetAttribute("type", "action") button:SetAttribute("useparent-actionpage", true) button:RegisterForClicks("AnyUp", "AnyDown") button:RegisterForDrag("LeftButton", "RightButton") button:SetScript("OnDragStart", function(self) if InCombatLockdown() then return end if GetCVar("lockActionBars") == "1" and not IsModifiedClick("PICKUPACTION") then return end local active_slot = get_button_slot(module, self) if active_slot then PickupAction(active_slot) reset_button_press_visual(self) end end) button:SetScript("OnDragStop", reset_button_press_visual) button:SetScript("OnReceiveDrag", function(self) if InCombatLockdown() then return end local active_slot = get_button_slot(module, self) if active_slot then PlaceAction(active_slot) end end) button:SetScript("OnEnter", function(self) if quick_keybind_button_on_enter(self) then GameTooltip:Hide() return end if not actionbar_tooltips_enabled() then return end local active_slot = get_button_slot(module, self) if not active_slot then return end GameTooltip:SetOwner(self, "ANCHOR_RIGHT") GameTooltip:SetAction(active_slot) end) button:SetScript("OnLeave", function(self) quick_keybind_button_on_leave(self) GameTooltip:Hide() reset_button_press_visual(self) end) create_button_visuals(button, slot_button_size) bar.buttons[index] = button button.current_slot = get_button_slot(module_object, button) if is_valid_action_slot(button.current_slot) then module_object.slot_buttons[button.current_slot][button] = true end return button end local function create_numbered_bar(module_object, bar_key) local settings = module_object.settings[bar_key] local bar_name = "samui_aktionsleiste_" .. bar_key local bar = CreateFrame("Frame", bar_name, UIParent, "SecureHandlerStateTemplate") bar.buttons = {} bar.bar_key = bar_key bar:SetAttribute("_onstate-swap", [[ local prefix = newstate == "swapped" and "swap_" or "normal_" local uses_main_actionpage = self:GetAttribute(prefix .. "uses_main_actionpage") local page self:SetAttribute("uses_main_actionpage", uses_main_actionpage) if uses_main_actionpage then page = self:GetAttribute("main_actionpage") else page = self:GetAttribute(prefix .. "actionpage") end if page then self:SetAttribute("actionpage", page) end ]]) module_object.bars[bar_key] = bar for index = 1, slot_button_count do create_numbered_button(module_object, bar, bar_key, index) end bar:HookScript("OnAttributeChanged", function(_, attribute_name) if attribute_name ~= "actionpage" then return end rebuild_slot_button_index(module_object) update_numbered_bar(module_object, bar_key, false) end) apply_bar_layout(module_object, bar_key) configure_swap(module_object, bar_key) apply_bar_visibility(module_object, bar_key) return bar end local function create_pet_button(module_object, bar, index) local name = "samui_aktionsleiste_pet_button_" .. index local button = CreateFrame( "CheckButton", name, bar, "PetActionButtonTemplate" ) button:SetID(index) button:UnregisterAllEvents() button:SetScript("OnEvent", nil) button:SetSize(special_button_size, special_button_size) button.index = index button.commandName = "BONUSACTIONBUTTON" .. index if button.AutoCastOverlay and module_object.pet_auto_cast_overlay_manager then button.AutoCastOverlay.ShowAutoCastEnabled = show_pet_auto_cast_enabled end apply_native_special_button_visuals(button, special_button_size) button:SetAttribute("type", "pet") button:SetAttribute("action", index) button:SetScript("OnEnter", function(self) if quick_keybind_button_on_enter(self) then GameTooltip:Hide() return end if not actionbar_tooltips_enabled() then return end GameTooltip:SetOwner(self, "ANCHOR_RIGHT") GameTooltip:SetPetAction(self.index) end) button:SetScript("OnLeave", function(self) quick_keybind_button_on_leave(self) GameTooltip:Hide() end) bar.buttons[index] = button module_object.pet_buttons[index] = button return button end local function create_pet_bar(module_object) local bar = CreateFrame( "Frame", "samui_aktionsleiste_pet", UIParent, "SecureHandlerStateTemplate" ) bar.buttons = {} bar.bar_key = "pet" module_object.bars.pet = bar for index = 1, special_button_count do create_pet_button(module_object, bar, index) end apply_bar_layout(module_object, "pet") apply_bar_visibility(module_object, "pet") return bar end local function create_stance_button(module_object, bar, index) local name = "samui_aktionsleiste_stance_button_" .. index local button = CreateFrame( "CheckButton", name, bar, "StanceButtonTemplate" ) button:SetID(index) button:UnregisterAllEvents() button:SetScript("OnEvent", nil) button:SetSize(special_button_size, special_button_size) button.index = index button.commandName = "SHAPESHIFTBUTTON" .. index apply_native_special_button_visuals(button, special_button_size) button:SetScript("OnEnter", function(self) if quick_keybind_button_on_enter(self) then GameTooltip:Hide() return end if not actionbar_tooltips_enabled() or self.index > GetNumShapeshiftForms() then return end GameTooltip:SetOwner(self, "ANCHOR_RIGHT") GameTooltip:SetShapeshift(self.index) end) button:SetScript("OnLeave", function(self) quick_keybind_button_on_leave(self) GameTooltip:Hide() end) bar.buttons[index] = button module_object.stance_buttons[index] = button return button end local function create_stance_bar(module_object) local bar = CreateFrame("Frame", "samui_aktionsleiste_stance", UIParent) bar.buttons = {} bar.bar_key = "stance" module_object.bars.stance = bar for index = 1, stance_button_count do create_stance_button(module_object, bar, index) end anchor_bar(module_object, "stance") apply_stance_secure_state(module_object) return bar end local function update_pet_ranges(module_object) local has_target = UnitExists("target") and true or false for index = 1, #module_object.pet_buttons do local button = module_object.pet_buttons[index] if has_target then local _, _, _, _, _, _, _, checks_range, in_range = GetPetActionInfo(index) set_range_state( button, (checks_range == true or checks_range == 1) and (in_range == false or in_range == 0) ) else set_range_state(button, false) end end end local function update_pet_cooldowns(module_object) for index = 1, #module_object.pet_buttons do local button = module_object.pet_buttons[index] local start_time, duration, enabled = GetPetActionCooldown(button.index) set_cooldown(button.cooldown, start_time, duration, enabled) end end local function update_pet_usable(module_object) for index = 1, #module_object.pet_buttons do local button = module_object.pet_buttons[index] set_icon_usable(button, GetPetActionSlotUsable(button.index)) end update_pet_ranges(module_object) end local function update_pet_hotkeys(module_object) for index = 1, #module_object.pet_buttons do update_hotkey(module_object.pet_buttons[index], "BONUSACTIONBUTTON" .. index) end end local function update_stance_cooldowns(module_object) local form_count = GetNumShapeshiftForms() for index = 1, #module_object.stance_buttons do local button = module_object.stance_buttons[index] if index <= form_count then local start_time, duration, enabled = GetShapeshiftFormCooldown(index) set_cooldown(button.cooldown, start_time, duration, enabled) else set_cooldown(button.cooldown, 0, 0, 0) end end end local function update_stance_usable(module_object) local form_count = GetNumShapeshiftForms() for index = 1, #module_object.stance_buttons do local button = module_object.stance_buttons[index] if index <= form_count then local _, _, is_castable = GetShapeshiftFormInfo(index) set_icon_usable(button, is_castable) else set_icon_usable(button, true) end end end local function update_stance_hotkeys(module_object) local form_count = GetNumShapeshiftForms() for index = 1, #module_object.stance_buttons do local binding_action = index <= form_count and "SHAPESHIFTBUTTON" .. index or nil update_hotkey(module_object.stance_buttons[index], binding_action) end end local function refresh_all_visuals(module_object) rebuild_slot_button_index(module_object) update_all_numbered_bars(module_object, true) update_pet_buttons(module_object) update_stance_visuals(module_object) refresh_range_registrations(module_object) end local function actionbar_slot_changed(module_object, _, slot) update_slot_visuals(module_object, slot) if slot == 0 then refresh_range_registrations(module_object) elseif is_valid_action_slot(slot) and UnitExists("target") and type(is_action_in_range) == "function" then local in_range = is_action_in_range(slot) local bucket = module_object.slot_buttons[slot] for button in pairs(bucket) do if is_range_eligible(module_object, button) then set_range_state(button, in_range == false or in_range == 0) end end end end local function actionbar_update_cooldown(module_object) update_numbered_cooldowns(module_object) end local function action_usable_changed(module_object, _, changes) if type(changes) ~= "table" then return end for index = 1, #changes do local change = changes[index] if type(change) == "table" then update_slot_usable(module_object, change.slot, change.usable) end end end local function actionbar_update_usable(module_object) update_numbered_usable(module_object) end local function actionbar_update_state(module_object) update_numbered_states(module_object) end local function spell_update_charges(module_object) update_numbered_counts(module_object) end local function spell_update_icon(module_object) for bar_index = 1, #numbered_bar_order do local bar = module_object.bars[numbered_bar_order[bar_index]] if bar then for index = 1, #bar.buttons do local button = bar.buttons[index] update_action_content(button, get_button_slot(module_object, button)) end end end end local function action_range_check_update(module_object, _, slot, in_range, checks_range) update_slot_range(module_object, slot, in_range, checks_range) end local function actionbar_page_updated(module_object) rebuild_slot_button_index(module_object) update_all_numbered_bars(module_object, false) refresh_range_registrations(module_object) end local function player_target_changed(module_object) refresh_range_registrations(module_object) update_pet_ranges(module_object) end local function update_bindings(module_object) update_numbered_hotkeys(module_object) update_pet_hotkeys(module_object) update_stance_hotkeys(module_object) end local function pet_bar_update(module_object) update_pet_buttons(module_object) end local function pet_bar_update_cooldown(module_object) update_pet_cooldowns(module_object) end local function pet_bar_update_usable(module_object) update_pet_usable(module_object) end local function unit_pet(module_object, _, unit) if unit == "player" then update_pet_buttons(module_object) end end local function update_shapeshift_form(module_object) rebuild_slot_button_index(module_object) for bar_index = 1, #numbered_bar_order do local bar_key = numbered_bar_order[bar_index] local bar = module_object.bars[bar_key] local settings = module_object.settings[bar_key] if bar then if settings and settings.swap_target then update_numbered_bar(module_object, bar_key, false) else for index = 1, #bar.buttons do local button = bar.buttons[index] update_action_content(button, get_button_slot(module_object, button)) end end end end refresh_range_registrations(module_object) update_stance_visuals(module_object) end local function update_shapeshift_forms(module_object) update_stance_visuals(module_object) apply_stance_secure_state(module_object) end local function update_shapeshift_cooldown(module_object) update_stance_cooldowns(module_object) end local function update_shapeshift_usable(module_object) update_stance_usable(module_object) end local function player_entering_world(module_object) refresh_all_visuals(module_object) apply_stance_secure_state(module_object) end local function player_regen_enabled(module_object) if module_object.pending_full_initialization then module_object.pending_full_initialization = nil module_object:initialize_bars() return end for index = 1, #all_bar_order do local bar_key = all_bar_order[index] if module_object.pending_layout[bar_key] then apply_bar_layout(module_object, bar_key) end end for index = 1, #numbered_bar_order do local bar_key = numbered_bar_order[index] if module_object.pending_swap[bar_key] then configure_swap(module_object, bar_key) end end if module_object.pending_stance_secure_update then apply_stance_secure_state(module_object) end for index = 1, #all_bar_order do local bar_key = all_bar_order[index] if module_object.pending_visibility[bar_key] then apply_bar_visibility(module_object, bar_key) end end if module_object.pending_lock_attributes then local settings = module_object.settings and module_object.settings.allgemein if settings then apply_actionbar_lock_button_attributes(module_object, settings.lock_modifier) end end refresh_all_visuals(module_object) end local function register_event_safely(event, func) local event_already_known = ns.event_table[event] ~= nil local success, registered = pcall(function() return ns:register_event(event, module, func) end) if not success or not registered then if not event_already_known and ns.event_table[event] and not next(ns.event_table[event]) then ns.event_table[event] = nil end module.unsupported_events[event] = true return false end module.unsupported_events[event] = nil return true end local function register_events() register_event_safely("ACTIONBAR_SLOT_CHANGED", actionbar_slot_changed) register_event_safely("ACTIONBAR_UPDATE_COOLDOWN", actionbar_update_cooldown) if not register_event_safely("ACTION_USABLE_CHANGED", action_usable_changed) then register_event_safely("ACTIONBAR_UPDATE_USABLE", actionbar_update_usable) end register_event_safely("ACTIONBAR_UPDATE_STATE", actionbar_update_state) register_event_safely("PLAYER_ENTER_COMBAT", actionbar_update_state) register_event_safely("PLAYER_LEAVE_COMBAT", actionbar_update_state) register_event_safely("START_AUTOREPEAT_SPELL", actionbar_update_state) register_event_safely("STOP_AUTOREPEAT_SPELL", actionbar_update_state) register_event_safely("ACTIONBAR_PAGE_CHANGED", actionbar_page_updated) register_event_safely("UPDATE_BONUS_ACTIONBAR", actionbar_page_updated) register_event_safely("UPDATE_VEHICLE_ACTIONBAR", actionbar_page_updated) register_event_safely("UPDATE_OVERRIDE_ACTIONBAR", actionbar_page_updated) register_event_safely("SPELL_UPDATE_CHARGES", spell_update_charges) register_event_safely("SPELL_UPDATE_ICON", spell_update_icon) if type(enable_action_range_check) == "function" then register_event_safely("ACTION_RANGE_CHECK_UPDATE", action_range_check_update) end register_event_safely("PLAYER_TARGET_CHANGED", player_target_changed) register_event_safely("UPDATE_BINDINGS", update_bindings) register_event_safely("PET_BAR_UPDATE", pet_bar_update) register_event_safely("PET_BAR_UPDATE_COOLDOWN", pet_bar_update_cooldown) register_event_safely("PET_BAR_UPDATE_USABLE", pet_bar_update_usable) register_event_safely("UNIT_PET", unit_pet) register_event_safely("UPDATE_SHAPESHIFT_FORM", update_shapeshift_form) register_event_safely("UPDATE_SHAPESHIFT_FORMS", update_shapeshift_forms) register_event_safely("UPDATE_SHAPESHIFT_COOLDOWN", update_shapeshift_cooldown) register_event_safely("UPDATE_SHAPESHIFT_USABLE", update_shapeshift_usable) register_event_safely("PLAYER_ENTERING_WORLD", player_entering_world) register_event_safely("PLAYER_REGEN_ENABLED", player_regen_enabled) end function ns:set_actionbar_lock_modifier(lock_modifier) local settings = module.settings and module.settings.allgemein if not settings or not actionbar_lock_modifiers[lock_modifier] then return false end settings.lock_modifier = lock_modifier return apply_actionbar_lock_setting(module) end function ns:set_actionbar_cooldown_numbers(enabled) local settings = module.settings and module.settings.allgemein if not settings or type(enabled) ~= "boolean" then return false end settings.cooldown_numbers_enabled = enabled return apply_actionbar_cooldown_numbers_setting(module) end function ns:set_actionbar_tooltip_enabled(enabled) local settings = module.settings and module.settings.allgemein if not settings or type(enabled) ~= "boolean" then return false end settings.tooltip_enabled = enabled if not enabled and GameTooltip then GameTooltip:Hide() end return true end function ns:get_actionbar_general_settings() local settings = module.settings and module.settings.allgemein if not settings then return nil end return settings.lock_modifier, settings.cooldown_numbers_enabled == true, settings.tooltip_enabled ~= false end function ns:set_actionbar_button_count(bar_key, count) local settings = module.settings and module.settings[bar_key] local normalized_count = normalize_number( count, actionbar_button_count_minimum, actionbar_button_count_maximum, 1 ) if not numbered_bar_lookup[bar_key] or not settings or normalized_count == nil then return false end settings.button_count = normalized_count local applied = apply_bar_layout(module, bar_key) if applied then update_numbered_bar(module, bar_key, false) refresh_range_registrations(module) end return applied end function ns:set_actionbar_buttons_per_row(bar_key, count) local settings = module.settings and module.settings[bar_key] local normalized_count = normalize_number( count, actionbar_buttons_per_row_minimum, actionbar_buttons_per_row_maximum, 1 ) if not all_bar_lookup[bar_key] or not settings or normalized_count == nil then return false end settings.buttons_per_row = normalized_count return apply_bar_layout(module, bar_key) end function ns:set_actionbar_spacing(bar_key, spacing) local settings = module.settings and module.settings[bar_key] local normalized_spacing = normalize_number( spacing, actionbar_spacing_minimum, actionbar_spacing_maximum, 1 ) if not all_bar_lookup[bar_key] or not settings or normalized_spacing == nil then return false end settings.spacing = normalized_spacing return apply_bar_layout(module, bar_key) end function ns:set_actionbar_scale(bar_key, scale) local settings = module.settings and module.settings[bar_key] local normalized_scale = normalize_number( scale, actionbar_scale_minimum, actionbar_scale_maximum, 1 ) if not all_bar_lookup[bar_key] or not settings or normalized_scale == nil then return false end if normalized_scale ~= scale then ns:chat_message( "Ungültige Skalierung für '" .. bar_key .. "' wurde auf " .. normalized_scale .. "% begrenzt", true ) end settings.scale = normalized_scale return apply_bar_layout(module, bar_key) end function ns:set_actionbar_offset(bar_key, offset_x, offset_y) local settings = module.settings and module.settings[bar_key] local normalized_offset_x = normalize_number( offset_x, actionbar_offset_x_minimum, actionbar_offset_x_maximum, 10 ) local normalized_offset_y = normalize_number( offset_y, actionbar_offset_y_minimum, actionbar_offset_y_maximum, 10 ) if not all_bar_lookup[bar_key] or not settings or normalized_offset_x == nil or normalized_offset_y == nil then return false end settings.offset_x = normalized_offset_x settings.offset_y = normalized_offset_y return apply_bar_layout(module, bar_key) end function ns:set_actionbar_swap_target(bar_key, target_bar_key) local settings = module.settings and module.settings[bar_key] if not numbered_bar_lookup[bar_key] or not settings then return false end if target_bar_key ~= nil then if not numbered_bar_lookup[target_bar_key] or target_bar_key == bar_key then return false end end settings.swap_target = target_bar_key return configure_swap(module, bar_key) end function ns:set_actionbar_range_check(bar_key, enabled) local settings = module.settings and module.settings[bar_key] if not numbered_bar_lookup[bar_key] or not settings or type(enabled) ~= "boolean" then return false end settings.range_check_enabled = enabled refresh_range_registrations(module) return true end function ns:set_actionbar_enabled(bar_key, enabled) local settings = module.settings and module.settings[bar_key] if not all_bar_lookup[bar_key] or not settings or type(enabled) ~= "boolean" then return false end if bar_key == "leiste1" then settings.enabled = true else settings.enabled = enabled end local applied = apply_bar_visibility(module, bar_key) if applied then if numbered_bar_lookup[bar_key] then update_numbered_bar(module, bar_key, false) end refresh_range_registrations(module) end return applied end function ns:get_actionbar_settings(bar_key) local settings = module.settings and module.settings[bar_key] if not all_bar_lookup[bar_key] or not settings then return nil end if numbered_bar_lookup[bar_key] then return settings.button_count, settings.spacing, settings.offset_x, settings.offset_y, settings.swap_target, settings.range_check_enabled, settings.enabled ~= false, settings.buttons_per_row, settings.scale end return nil, settings.spacing, settings.offset_x, settings.offset_y, nil, nil, settings.enabled ~= false, settings.buttons_per_row, settings.scale end function module:apply_palette() for _, bar in pairs(self.bars) do for index = 1, #bar.buttons do apply_button_palette(bar.buttons[index]) end end refresh_all_visuals(self) end function module:initialize_bars() if self.bars_initialized or InCombatLockdown() then self.pending_full_initialization = not self.bars_initialized return end if not self.page_manager then self.page_manager = CreateFrame( "Frame", "samui_aktionsleisten_page_manager", UIParent, "SecureHandlerStateTemplate" ) end if not resolve_blizzard_actionbar_sources(self) then return end for index = 1, #numbered_bar_order do create_numbered_bar(self, numbered_bar_order[index]) end install_main_actionpage_bridge(self) initialize_pet_auto_cast_overlay_manager(self) create_pet_bar(self) create_stance_bar(self) install_quick_keybind_integration(self) register_events() apply_actionbar_lock_setting(self) apply_actionbar_cooldown_numbers_setting(self) refresh_all_visuals(self) ns.palette:register_callback("aktionsleisten", function() module:apply_palette() end) self.bars_initialized = true end function module:initialize() self.settings = create_runtime_settings() self.bars = {} self.slot_buttons = {} self.range_desired_slots = {} self.range_registered_slots = {} for slot = 1, numbered_action_slot_maximum do self.slot_buttons[slot] = {} self.range_desired_slots[slot] = false end self.pet_buttons = {} self.pet_auto_cast_overlay_manager = nil self.blizzard_main_action_bar = nil self.quick_keybind_frame = nil self.quick_keybind_integration_installed = false self.page_manager = nil self.page_bridge_installed = false self.stance_buttons = {} self.pending_layout = {} self.pending_swap = {} self.pending_visibility = {} self.unsupported_events = {} self.pending_stance_secure_update = false self.pending_lock_attributes = false self.pending_full_initialization = false self.bars_initialized = false if InCombatLockdown() then self.pending_full_initialization = true register_event_safely("PLAYER_REGEN_ENABLED", player_regen_enabled) else self:initialize_bars() end end ns:register_module("aktionsleisten")