---------------------------------------------------- -- SamUI v0.3.3 -- Eine vollständige UI-Alternative -- WoW Classic Anniversary TBC -- Interface 20506 -- Build 69110 ---------------------------------------------------- local _, ns = ... local module = ns:new_module("minimap") local screen_margin = 15 local top_bar_height = 24 local top_bar_border_gap = 5 local collector_button_size = 24 local function get_top_bar_anchor_gap() local border_offset = ns.designelemente and ns.designelemente:get_border_offset("frame") or nil if type(border_offset) ~= "number" then return nil end return top_bar_border_gap + (border_offset * 2) end local minimap_text_inset = 6 local setting_order = { "zoom_buttons", "zone_text", "mail_icon", "clock", "clock_24h", "group_finder_icon", "calendar_icon", } local default_settings = { zoom_buttons = false, zone_text = false, mail_icon = true, clock = true, clock_24h = true, group_finder_icon = true, calendar_icon = false, } local known_frame_points = { calendar = { "CENTER", "TOPLEFT", 10, -10 }, mail = { "CENTER", "TOPRIGHT", -10, -10 }, } local visibility_setting_by_frame_key = { calendar = "calendar_icon", mail = "mail_icon", zoom_in = "zoom_buttons", zoom_out = "zoom_buttons", } local hooked_foreign_frames = setmetatable({}, { __mode = "k" }) local collected_buttons = setmetatable({}, { __mode = "k" }) local function copy_default_settings() local settings = {} for index = 1, #setting_order do local key = setting_order[index] settings[key] = default_settings[key] end return settings end local function report_missing_frame(key, label) module.missing_frame_reports = module.missing_frame_reports or {} if module.missing_frame_reports[key] then return end module.missing_frame_reports[key] = true ns:chat_message("Blizzard-Element '" .. label .. "' konnte nicht ermittelt werden", true) end local function find_group_finder_frame(primary_frame, minimap, minimap_cluster, minimap_backdrop) if primary_frame then return primary_frame end local parents = {} if minimap then parents[#parents + 1] = minimap end if minimap_cluster then parents[#parents + 1] = minimap_cluster end if minimap_backdrop then parents[#parents + 1] = minimap_backdrop end for parent_index = 1, #parents do local parent = parents[parent_index] if parent and type(parent.GetChildren) == "function" then local children = { parent:GetChildren() } for child_index = 1, #children do local child = children[child_index] local name = child and type(child.GetName) == "function" and child:GetName() or nil local lower_name = type(name) == "string" and string.lower(name) or nil if lower_name and lower_name:find("minimap", 1, true) and (lower_name:find("lfg", 1, true) or lower_name:find("groupfinder", 1, true)) then return child end end end end return nil end local function resolve_blizzard_frames(report_missing) local minimap = _G.Minimap local minimap_cluster = _G.MinimapCluster local tracking = _G.MiniMapTrackingFrame or _G.MiniMapTracking local minimap_backdrop = _G.MinimapBackdrop local group_finder = find_group_finder_frame( _G.LFGMinimapFrame, minimap, minimap_cluster, minimap_backdrop ) local frames = { minimap = minimap, minimap_cluster = minimap_cluster, calendar = _G.GameTimeFrame, mail = _G.MiniMapMailFrame, tracking = tracking, group_finder = group_finder, zoom_in = _G.MinimapZoomIn, zoom_out = _G.MinimapZoomOut, native_zone_text_button = _G.MinimapZoneTextButton, native_clock = _G.TimeManagerClockButton, native_backdrop = minimap_backdrop, native_world_map = _G.MiniMapWorldMapButton, native_toggle = _G.MinimapToggleButton, native_border = _G.MinimapBorder, native_border_top = _G.MinimapBorderTop, native_north_tag = _G.MinimapNorthTag, native_compass = _G.MinimapCompassTexture, bags_bar = _G.BagsBar, bag_3 = _G.CharacterBag3Slot, bag_2 = _G.CharacterBag2Slot, bag_1 = _G.CharacterBag1Slot, bag_0 = _G.CharacterBag0Slot, backpack = _G.MainMenuBarBackpackButton, bag_3_icon = _G.CharacterBag3SlotIconTexture or (_G.CharacterBag3Slot and (_G.CharacterBag3Slot.Icon or _G.CharacterBag3Slot.icon)), bag_2_icon = _G.CharacterBag2SlotIconTexture or (_G.CharacterBag2Slot and (_G.CharacterBag2Slot.Icon or _G.CharacterBag2Slot.icon)), bag_1_icon = _G.CharacterBag1SlotIconTexture or (_G.CharacterBag1Slot and (_G.CharacterBag1Slot.Icon or _G.CharacterBag1Slot.icon)), bag_0_icon = _G.CharacterBag0SlotIconTexture or (_G.CharacterBag0Slot and (_G.CharacterBag0Slot.Icon or _G.CharacterBag0Slot.icon)), backpack_icon = _G.MainMenuBarBackpackButtonIconTexture or (_G.MainMenuBarBackpackButton and (_G.MainMenuBarBackpackButton.Icon or _G.MainMenuBarBackpackButton.icon)), game_menu = _G.GameMenuFrame, } if report_missing then if not frames.minimap then report_missing_frame("minimap", "Minimap") end if not frames.calendar then report_missing_frame("calendar", "GameTimeFrame") end if not frames.mail then report_missing_frame("mail", "MiniMapMailFrame") end if not frames.tracking then report_missing_frame("tracking", "MiniMapTrackingFrame/MiniMapTracking") end if not frames.group_finder then report_missing_frame("group_finder", "LFGMinimapFrame") end if not frames.zoom_in then report_missing_frame("zoom_in", "MinimapZoomIn") end if not frames.zoom_out then report_missing_frame("zoom_out", "MinimapZoomOut") end if not frames.bag_3 then report_missing_frame("bag_button_1", "CharacterBag3Slot") end if not frames.bag_2 then report_missing_frame("bag_button_2", "CharacterBag2Slot") end if not frames.bag_1 then report_missing_frame("bag_button_3", "CharacterBag1Slot") end if not frames.bag_0 then report_missing_frame("bag_button_4", "CharacterBag0Slot") end if not frames.backpack then report_missing_frame("bag_button_5", "MainMenuBarBackpackButton") end end return frames end local function can_modify_foreign_region(region) if not region then return false end if type(region.IsProtected) == "function" and region:IsProtected() and InCombatLockdown() then module.pending_foreign_update = true return false end return true end local function position_minimap(minimap) if not minimap or not can_modify_foreign_region(minimap) then return false end local border_offset = ns.designelemente and ns.designelemente:get_border_offset("frame") or nil local top_bar_anchor_gap = get_top_bar_anchor_gap() if type(border_offset) ~= "number" or type(top_bar_anchor_gap) ~= "number" then return false end minimap:ClearAllPoints() minimap:SetPoint( "TOPRIGHT", UIParent, "TOPRIGHT", -(screen_margin + border_offset), -(screen_margin + border_offset + top_bar_height + top_bar_anchor_gap) ) return true end local function position_known_frame(frame, frame_key, minimap) local point = known_frame_points[frame_key] if not frame or not point or not minimap or not can_modify_foreign_region(frame) then return false end if type(frame.GetParent) == "function" and frame:GetParent() ~= minimap and type(frame.SetParent) == "function" then frame:SetParent(minimap) end frame:ClearAllPoints() frame:SetPoint(point[1], minimap, point[2], point[3], point[4]) return true end local function is_foreign_frame_enabled(frame_key) local setting_key = visibility_setting_by_frame_key[frame_key] if not setting_key then return true end return module.settings and module.settings[setting_key] == true end local function enforce_foreign_frame(frame, frame_key, minimap) if not frame then return false end position_known_frame(frame, frame_key, minimap) if not is_foreign_frame_enabled(frame_key) and type(frame.IsShown) == "function" and frame:IsShown() and can_modify_foreign_region(frame) then frame:Hide() end return true end local function install_foreign_frame_watcher(frame, frame_key) if not frame or hooked_foreign_frames[frame] then return end if type(frame.IsProtected) == "function" and frame:IsProtected() and InCombatLockdown() then module.pending_foreign_update = true return end frame:HookScript("OnShow", function(current_frame) local frames = resolve_blizzard_frames(false) enforce_foreign_frame(current_frame, frame_key, frames.minimap) end) frame:HookScript("OnEvent", function(current_frame) local frames = resolve_blizzard_frames(false) enforce_foreign_frame(current_frame, frame_key, frames.minimap) end) hooked_foreign_frames[frame] = true end local function suppress_native_element(region) if not region or not can_modify_foreign_region(region) then return end if type(region.Hide) == "function" then region:Hide() end if type(region.HookScript) == "function" and not hooked_foreign_frames[region] then region:HookScript("OnShow", function(current_region) if can_modify_foreign_region(current_region) then current_region:Hide() end end) hooked_foreign_frames[region] = true end end local function suppress_native_frame_regions(frame) if not frame or type(frame.GetRegions) ~= "function" then return end local regions = { frame:GetRegions() } for index = 1, #regions do local region = regions[index] if region and type(region.Hide) == "function" then region:Hide() end end end local function apply_blizzard_frame_layout(report_missing) local frames = resolve_blizzard_frames(report_missing) if not frames.minimap then return false end module.pending_foreign_update = false position_minimap(frames.minimap) local frame_keys = { "mail", } for index = 1, #frame_keys do local frame_key = frame_keys[index] local frame = frames[frame_key] if frame then install_foreign_frame_watcher(frame, frame_key) enforce_foreign_frame(frame, frame_key, frames.minimap) end end suppress_native_element(frames.native_zone_text_button) suppress_native_element(frames.native_clock) suppress_native_element(frames.native_world_map) suppress_native_element(frames.native_toggle) suppress_native_element(frames.native_border) suppress_native_element(frames.native_border_top) suppress_native_element(frames.native_north_tag) suppress_native_element(frames.native_compass) suppress_native_frame_regions(frames.minimap_cluster) suppress_native_frame_regions(frames.native_backdrop) if frames.native_backdrop and type(frames.minimap.GetParent) == "function" and frames.minimap:GetParent() ~= frames.native_backdrop then suppress_native_element(frames.native_backdrop) end return true end local function update_zone_text() if not module.zone_text then return end module.zone_text:SetText(GetMinimapZoneText() or "") if module.settings.zone_text == true then module.zone_text:Show() else module.zone_text:Hide() end end local function format_clock_text() local hour, minute = GetGameTime() hour = tonumber(hour) or 0 minute = tonumber(minute) or 0 if module.settings.clock_24h == true then return string.format("%02d:%02d", hour, minute), minute end local suffix = hour >= 12 and "PM" or "AM" local display_hour = hour % 12 if display_hour == 0 then display_hour = 12 end return string.format("%d:%02d %s", display_hour, minute, suffix), minute end local function update_clock(force_update) if not module.clock_text then return end if module.settings.clock == true then module.clock_text:Show() if module.clock_button then module.clock_button:Show() end else module.clock_text:Hide() if module.clock_button then module.clock_button:Hide() end return end local text, minute = format_clock_text() if force_update or module.last_clock_minute ~= minute or module.last_clock_24h ~= module.settings.clock_24h then module.clock_text:SetText(text) module.last_clock_minute = minute module.last_clock_24h = module.settings.clock_24h end end local function activate_native_clock() local frames = resolve_blizzard_frames(false) local native_clock = frames.native_clock if not native_clock and type(_G.TimeManager_LoadUI) == "function" then _G.TimeManager_LoadUI() native_clock = _G.TimeManagerClockButton end if not native_clock then return false end if type(native_clock.GetScript) == "function" then local on_click = native_clock:GetScript("OnClick") if type(on_click) == "function" then on_click(native_clock, "LeftButton") return true end end if type(_G.TimeManagerClockButton_OnClick) == "function" then _G.TimeManagerClockButton_OnClick(native_clock) return true end return false end local function toggle_options_window() local installer = ns.module_registry and ns.module_registry["installer"] or nil if not installer or type(installer.toggle_window) ~= "function" then return false end installer:toggle_window() return true end local function toggle_game_menu() local game_menu = resolve_blizzard_frames(false).game_menu if not game_menu and _G.C_AddOns and type(_G.C_AddOns.LoadAddOn) == "function" then _G.C_AddOns.LoadAddOn("Blizzard_GameMenu") game_menu = resolve_blizzard_frames(false).game_menu end if not game_menu then ns:chat_message("Blizzard-Hauptmenü konnte nicht geöffnet werden", true) return false end if game_menu:IsShown() then HideUIPanel(game_menu) else ShowUIPanel(game_menu) end return true end local function toggle_help_window() if type(_G.ToggleHelpFrame) ~= "function" then ns:chat_message("Blizzard-Hilfe konnte nicht geöffnet werden", true) return false end _G.ToggleHelpFrame() return true end local function resolve_bag_buttons(report_missing) local frames = resolve_blizzard_frames(report_missing) local buttons = { frames.bag_3, frames.bag_2, frames.bag_1, frames.bag_0, frames.backpack, } local icons = { frames.bag_3_icon, frames.bag_2_icon, frames.bag_1_icon, frames.bag_0_icon, frames.backpack_icon, } for index = 1, 5 do if not buttons[index] then return nil, nil end end return buttons, icons end local function call_native_bag_script(native_button, script_name, ...) if not native_button or type(native_button.GetScript) ~= "function" then return false end local script = native_button:GetScript(script_name) if type(script) ~= "function" then return false end script(native_button, ...) return true end local function get_native_bag_texture(native_button, native_icon) local texture = nil if native_icon and type(native_icon.GetTexture) == "function" then texture = native_icon:GetTexture() end if not texture and native_button then texture = native_button.backgroundTextureName end if not texture and native_button and type(native_button.GetNormalTexture) == "function" then local normal_texture = native_button:GetNormalTexture() if normal_texture and type(normal_texture.GetTexture) == "function" then texture = normal_texture:GetTexture() end end return texture end local function update_minimap_bag_bar_buttons() local buttons = module.minimap_bag_bar_buttons local native_buttons = module.native_bag_buttons local native_icons = module.native_bag_icons if not buttons or not native_buttons or not native_icons then return false end for index = 1, #buttons do local button = buttons[index] local native_button = native_buttons[index] local texture = get_native_bag_texture(native_button, native_icons[index]) if texture then button.icon:SetTexture(texture) button.icon:Show() else button.icon:Hide() end if type(native_button.GetChecked) == "function" then button:SetChecked(native_button:GetChecked() == true) end end return true end local function create_minimap_bag_bar_bag_button(parent, native_button, native_icon, palette, designelemente) local button = CreateFrame("CheckButton", nil, parent) button:SetSize(collector_button_size, top_bar_height) button:SetFrameLevel(parent:GetFrameLevel() + 7) button:RegisterForClicks("LeftButtonUp", "RightButtonUp") button:RegisterForDrag("LeftButton") local background = button:CreateTexture(nil, "BACKGROUND") background:SetAllPoints(button) background:SetColorTexture(palette:get_surface("button")) local icon = button:CreateTexture(nil, "ARTWORK") icon:SetPoint("TOPLEFT", button, "TOPLEFT", 2, -2) icon:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -2, 2) button.native_button = native_button button.native_icon = native_icon button.background = background button.icon = icon button.border = designelemente:create_border_overlay( button, button, "minimap_bag_slot" ) button:SetScript("OnClick", function(current_button, mouse_button) call_native_bag_script(current_button.native_button, "OnClick", mouse_button) update_minimap_bag_bar_buttons() end) button:SetScript("OnDragStart", function(current_button, mouse_button) call_native_bag_script(current_button.native_button, "OnDragStart", mouse_button) update_minimap_bag_bar_buttons() end) button:SetScript("OnReceiveDrag", function(current_button) call_native_bag_script(current_button.native_button, "OnReceiveDrag") update_minimap_bag_bar_buttons() end) return button end local function layout_minimap_bag_bar_buttons() local minimap_bag_bar = module.minimap_bag_bar local buttons = module.minimap_bag_bar_buttons if not minimap_bag_bar or not buttons then return false end local width = tonumber(minimap_bag_bar:GetWidth()) or 0 local used_width = #buttons * collector_button_size local spacing = 0 if #buttons > 1 and width > used_width then spacing = (width - used_width) / (#buttons - 1) end for index = 1, #buttons do local button = buttons[index] button:ClearAllPoints() if index == 1 then button:SetPoint("TOPLEFT", minimap_bag_bar, "TOPLEFT", 0, 0) elseif index == #buttons then button:SetPoint("TOPRIGHT", minimap_bag_bar, "TOPRIGHT", 0, 0) else local offset = (index - 1) * (collector_button_size + spacing) button:SetPoint("TOPLEFT", minimap_bag_bar, "TOPLEFT", offset, 0) end end return true end local function create_minimap_bag_bar_buttons(minimap_bag_bar, palette, designelemente) if module.minimap_bag_bar_buttons then return true end local native_buttons, native_icons = resolve_bag_buttons(true) if not native_buttons then return false end local buttons = {} for index = 1, #native_buttons do local native_button = native_buttons[index] suppress_native_element(native_button) buttons[index] = create_minimap_bag_bar_bag_button( minimap_bag_bar, native_button, native_icons[index], palette, designelemente ) end module.native_bag_buttons = native_buttons module.native_bag_icons = native_icons module.minimap_bag_bar_buttons = buttons layout_minimap_bag_bar_buttons() update_minimap_bag_bar_buttons() return true end local function set_minimap_bag_bar_visibility(show_bar) local minimap_bag_bar = module.minimap_bag_bar if not minimap_bag_bar then return false end module.minimap_bag_bar_visible = show_bar == true if module.minimap_bag_bar_visible then update_minimap_bag_bar_buttons() minimap_bag_bar:Show() else minimap_bag_bar:Hide() end if type(ns.layout_minimap_stack) == "function" then ns:layout_minimap_stack() end return true end local function toggle_minimap_bag_bar() return set_minimap_bag_bar_visibility(module.minimap_bag_bar_visible ~= true) end local function apply_palette() local palette = ns.palette if not palette then return end if module.zone_text then module.zone_text:SetTextColor(palette:get_rgb("gold")) end if module.clock_text then local _, class = UnitClass("player") local red, green, blue = palette:get_class_color(class) if red then module.clock_text:SetTextColor(red, green, blue) else module.clock_text:SetTextColor(palette:get_rgb("text")) end end if module.border_overlay then module.border_overlay:SetBackdropBorderColor( palette:get_rgba("border", palette:get_alpha("opaque")) ) end if module.top_bar_background then module.top_bar_background:SetColorTexture(palette:get_surface("button_deep")) end if module.top_bar_buttons then for index = 1, #module.top_bar_buttons do local button = module.top_bar_buttons[index] if button.background then button.background:SetColorTexture(palette:get_surface("button")) end if button.border then button.border:SetBackdropBorderColor( palette:get_rgba("border", palette:get_alpha("opaque")) ) end end end if module.collector_background then module.collector_background:SetColorTexture(palette:get_surface("button_deep")) end if module.collector_border then module.collector_border:SetBackdropBorderColor( palette:get_rgba("border", palette:get_alpha("opaque")) ) end if module.minimap_bag_bar_background then module.minimap_bag_bar_background:SetColorTexture(palette:get_surface("button_deep")) end if module.minimap_bag_bar_buttons then for index = 1, #module.minimap_bag_bar_buttons do local button = module.minimap_bag_bar_buttons[index] if button.background then button.background:SetColorTexture(palette:get_surface("button")) end if button.border then button.border:SetBackdropBorderColor( palette:get_rgba("border", palette:get_alpha("opaque")) ) end end end end local function is_known_or_blizzard_button(frame, frames) if not frame then return true end for key, known_frame in pairs(frames) do if key ~= "tracking" and frame == known_frame then return true end end local name = type(frame.GetName) == "function" and frame:GetName() or nil if type(name) ~= "string" or name == "" then return true end local lower_name = string.lower(name) if lower_name:match("^gametime") or lower_name:match("^timemanager") then return true end if lower_name:find("tracking", 1, true) or lower_name:find("mailframe", 1, true) then return false end if lower_name:match("^minimap") then return true end return false end local function has_minimap_launcher_signature(frame) if not frame then return false end local width = type(frame.GetWidth) == "function" and tonumber(frame:GetWidth()) or nil local height = type(frame.GetHeight) == "function" and tonumber(frame:GetHeight()) or nil if not width or not height or width <= 0 or height <= 0 or width > 48 or height > 48 then return false end local minimum_side = math.min(width, height) local maximum_side = math.max(width, height) if minimum_side <= 0 or maximum_side / minimum_side > 1.5 then return false end local name = type(frame.GetName) == "function" and frame:GetName() or nil if type(name) == "string" and name ~= "" then local lower_name = string.lower(name) if lower_name:find("libdbicon", 1, true) or lower_name:find("ldbicon", 1, true) then return true end end if type(frame.GetScript) ~= "function" then return false end return frame:GetScript("OnClick") ~= nil and frame:GetScript("OnDragStart") ~= nil and frame:GetScript("OnDragStop") ~= nil end local function is_collectable_minimap_button(frame, frames) if not frame or frame == module.collector_button or frame == module.collector_container then return false end if collected_buttons[frame] and type(frame.GetParent) == "function" and frame:GetParent() == module.collector_container then return false end if type(frame.IsObjectType) ~= "function" or not frame:IsObjectType("Button") then return false end if type(frame.IsProtected) == "function" and frame:IsProtected() then return false end if not has_minimap_launcher_signature(frame) then return false end if is_known_or_blizzard_button(frame, frames) then return false end return true end local function layout_collected_buttons() if not module.collector_container then return end local buttons = {} for button in pairs(collected_buttons) do buttons[#buttons + 1] = button end table.sort(buttons, function(left, right) local left_name = type(left.GetName) == "function" and left:GetName() or "" local right_name = type(right.GetName) == "function" and right:GetName() or "" return tostring(left_name) < tostring(right_name) end) local total_width = 0 local maximum_height = 24 local spacing = 2 for index = #buttons, 1, -1 do local button = buttons[index] local width = tonumber(button:GetWidth()) or 24 local height = tonumber(button:GetHeight()) or 24 button:ClearAllPoints() if index == #buttons then button:SetPoint("RIGHT", module.collector_container, "RIGHT", 0, 0) else button:SetPoint("RIGHT", buttons[index + 1], "LEFT", -spacing, 0) end total_width = total_width + width if index < #buttons then total_width = total_width + spacing end maximum_height = math.max(maximum_height, height) end module.collector_container:SetSize(math.max(1, total_width), maximum_height) end local function collect_button(button) if not button or not module.collector_container then return false end button:SetParent(module.collector_container) button:SetFrameLevel(module.collector_container:GetFrameLevel() + 1) collected_buttons[button] = true return true end local function set_collected_frame(frame, enabled, width, height) if not frame or not module.collector_container or not can_modify_foreign_region(frame) then return false end local changed = false if enabled ~= true then if collected_buttons[frame] then collected_buttons[frame] = nil changed = true end if type(frame.IsShown) == "function" and frame:IsShown() then frame:Hide() changed = true end return changed end if width and height and type(frame.SetSize) == "function" then frame:SetSize(width, height) end if type(frame.GetParent) ~= "function" or frame:GetParent() ~= module.collector_container then if collect_button(frame) then changed = true end else collected_buttons[frame] = true end if type(frame.IsShown) == "function" and not frame:IsShown() then frame:Show() changed = true end return changed end local function set_collected_native_visibility_frame(frame, enabled) if not frame or not module.collector_container or not can_modify_foreign_region(frame) then return false end local changed = false if enabled ~= true then if collected_buttons[frame] then collected_buttons[frame] = nil changed = true end if type(frame.IsShown) == "function" and frame:IsShown() then frame:Hide() changed = true end return changed end if type(frame.IsShown) == "function" and not frame:IsShown() then if collected_buttons[frame] then collected_buttons[frame] = nil changed = true end return changed end if type(frame.GetParent) ~= "function" or frame:GetParent() ~= module.collector_container then if collect_button(frame) then changed = true end else collected_buttons[frame] = true end return changed end local function collect_configured_blizzard_frames(frames) if not frames then return false end local changed = false local tracking_width = 32 local tracking_height = 32 if frames.tracking then local width = type(frames.tracking.GetWidth) == "function" and frames.tracking:GetWidth() or nil local height = type(frames.tracking.GetHeight) == "function" and frames.tracking:GetHeight() or nil if tonumber(width) and width > 0 then tracking_width = width end if tonumber(height) and height > 0 then tracking_height = height end if set_collected_frame(frames.tracking, true) then changed = true end end if set_collected_frame( frames.calendar, module.settings.calendar_icon == true, tracking_width, tracking_height ) then changed = true end if set_collected_frame(frames.zoom_in, module.settings.zoom_buttons == true) then changed = true end if set_collected_frame(frames.zoom_out, module.settings.zoom_buttons == true) then changed = true end if set_collected_native_visibility_frame( frames.group_finder, module.settings.group_finder_icon == true ) then changed = true end return changed end local function scan_parent_for_buttons(parent, frames) if not parent or type(parent.GetChildren) ~= "function" then return false end local found_new_button = false local children = { parent:GetChildren() } for index = 1, #children do local child = children[index] if is_collectable_minimap_button(child, frames) and collect_button(child) then found_new_button = true end end return found_new_button end local function scan_minimap_buttons(force_scan) local frames = resolve_blizzard_frames(false) local minimap = frames.minimap if not minimap or not module.collector_container then return false end local cluster = frames.minimap_cluster local minimap_count = minimap:GetNumChildren() local cluster_count = cluster and cluster:GetNumChildren() or 0 if not force_scan and module.last_minimap_child_count == minimap_count and module.last_cluster_child_count == cluster_count then return false end local found_new_button = collect_configured_blizzard_frames(frames) if scan_parent_for_buttons(minimap, frames) then found_new_button = true end if cluster and scan_parent_for_buttons(cluster, frames) then found_new_button = true end module.last_minimap_child_count = minimap:GetNumChildren() module.last_cluster_child_count = cluster and cluster:GetNumChildren() or 0 if found_new_button then layout_collected_buttons() end return found_new_button end local function install_collector_watcher(frame, setting_key, track_native_hide) if not frame or hooked_foreign_frames[frame] then return end if type(frame.IsProtected) == "function" and frame:IsProtected() and InCombatLockdown() then module.pending_foreign_update = true return end frame:HookScript("OnShow", function(current_frame) if setting_key and module.settings[setting_key] ~= true then if can_modify_foreign_region(current_frame) then current_frame:Hide() end return end module.pending_collector_scan = true end) if track_native_hide then frame:HookScript("OnHide", function() module.pending_collector_scan = true end) end hooked_foreign_frames[frame] = true end local function is_collector_owned_menu_open() local menu_api = _G.Menu if type(menu_api) ~= "table" or type(menu_api.GetManager) ~= "function" then return false end local manager = menu_api.GetManager() if not manager or type(manager.GetOpenMenu) ~= "function" then return false end local open_menu = manager:GetOpenMenu() if not open_menu then return false end local owner_region = open_menu.ownerRegion local checked_frames = 0 while owner_region and checked_frames < 20 do if owner_region == module.collector_container or collected_buttons[owner_region] then return true end if type(owner_region.GetParent) ~= "function" then break end owner_region = owner_region:GetParent() checked_frames = checked_frames + 1 end return false end local function create_top_bar_button(parent, texture_key, palette, designelemente) local button = CreateFrame("Button", nil, parent) button:SetSize(collector_button_size, top_bar_height) button:SetFrameLevel(parent:GetFrameLevel() + 7) local background = button:CreateTexture(nil, "BACKGROUND") background:SetAllPoints(button) background:SetColorTexture(palette:get_surface("button")) local icon = button:CreateTexture(nil, "ARTWORK") icon:SetPoint("TOPLEFT", button, "TOPLEFT", 2, -2) icon:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -2, 2) icon:SetTexture(designelemente:get_texture(texture_key)) button.background = background button.icon = icon button.border = designelemente:create_border_overlay( button, button, "minimap_top_button" ) return button end local function layout_top_bar_buttons(top_bar, buttons) local width = tonumber(top_bar:GetWidth()) or 0 local used_width = #buttons * collector_button_size local spacing = 0 if #buttons > 1 and width > used_width then spacing = (width - used_width) / (#buttons - 1) end for index = 1, #buttons do local button = buttons[index] button:ClearAllPoints() if index == 1 then button:SetPoint("TOPLEFT", top_bar, "TOPLEFT", 0, 0) elseif index == #buttons then button:SetPoint("TOPRIGHT", top_bar, "TOPRIGHT", 0, 0) else local offset = (index - 1) * (collector_button_size + spacing) button:SetPoint("TOPLEFT", top_bar, "TOPLEFT", offset, 0) end end end local function create_minimap_elements() local frames = resolve_blizzard_frames(true) local minimap = frames.minimap local palette = ns.palette local designelemente = ns.designelemente if not minimap or not palette or not designelemente then return false end minimap:SetMaskTexture("Interface\\BUTTONS\\WHITE8X8") module.border_overlay = designelemente:create_border_overlay(minimap, minimap, "frame") local top_bar_anchor_gap = get_top_bar_anchor_gap() if type(top_bar_anchor_gap) ~= "number" then return false end local top_bar = CreateFrame("Frame", nil, minimap) top_bar:SetPoint("BOTTOMLEFT", minimap, "TOPLEFT", 0, top_bar_anchor_gap) top_bar:SetPoint("BOTTOMRIGHT", minimap, "TOPRIGHT", 0, top_bar_anchor_gap) top_bar:SetHeight(top_bar_height) top_bar:SetFrameLevel(minimap:GetFrameLevel() + 14) local top_bar_background = top_bar:CreateTexture(nil, "BACKGROUND") top_bar_background:SetAllPoints(top_bar) top_bar_background:SetColorTexture(palette:get_surface("button_deep")) local minimap_bag_bar = CreateFrame("Frame", nil, minimap) minimap_bag_bar:SetPoint("TOPLEFT", minimap, "BOTTOMLEFT", 0, -top_bar_anchor_gap) minimap_bag_bar:SetPoint("TOPRIGHT", minimap, "BOTTOMRIGHT", 0, -top_bar_anchor_gap) minimap_bag_bar:SetHeight(top_bar_height) minimap_bag_bar:SetFrameLevel(minimap:GetFrameLevel() + 14) local minimap_bag_bar_background = minimap_bag_bar:CreateTexture(nil, "BACKGROUND") minimap_bag_bar_background:SetAllPoints(minimap_bag_bar) minimap_bag_bar_background:SetColorTexture(palette:get_surface("button_deep")) minimap_bag_bar:Hide() local text_layer = CreateFrame("Frame", nil, minimap) text_layer:SetAllPoints(minimap) text_layer:SetFrameLevel(minimap:GetFrameLevel() + 15) text_layer:EnableMouse(false) local zone_text = text_layer:CreateFontString(nil, "OVERLAY") zone_text:SetFontObject(samui_font_12) zone_text:SetPoint("TOP", minimap, "TOP", 0, -minimap_text_inset) zone_text:SetJustifyH("CENTER") zone_text:SetTextColor(palette:get_rgb("gold")) local clock_text = text_layer:CreateFontString(nil, "OVERLAY") clock_text:SetFontObject(samui_font_18_outline) clock_text:SetPoint("BOTTOM", minimap, "BOTTOM", 0, minimap_text_inset) clock_text:SetTextColor(palette:get_rgb("text")) local clock_button = CreateFrame("Button", nil, minimap) clock_button:SetSize(96, 24) clock_button:SetPoint("CENTER", clock_text, "CENTER", 0, 0) clock_button:SetFrameLevel(text_layer:GetFrameLevel() + 1) clock_button:SetScript("OnClick", function() activate_native_clock() end) local top_bar_buttons = { create_top_bar_button(top_bar, "sammelbutton", palette, designelemente), create_top_bar_button(top_bar, "minimap_engineering", palette, designelemente), create_top_bar_button(top_bar, "minimap_bag", palette, designelemente), create_top_bar_button(top_bar, "minimap_help", palette, designelemente), } layout_top_bar_buttons(top_bar, top_bar_buttons) local collector_button = top_bar_buttons[1] local collector_container = CreateFrame("Frame", nil, minimap) collector_container:SetPoint("RIGHT", collector_button, "LEFT", -10, 0) collector_container:SetSize(1, collector_button_size) collector_container:SetFrameLevel(minimap:GetFrameLevel() + 20) collector_container:EnableMouse(true) local collector_background = collector_container:CreateTexture(nil, "BACKGROUND") collector_background:SetAllPoints(collector_container) collector_background:SetColorTexture(palette:get_surface("button_deep")) local collector_border = designelemente:create_border_overlay( collector_container, collector_container, "frame" ) collector_container:Hide() collector_button:SetScript("OnEnter", function() scan_minimap_buttons(true) collector_container:Show() end) collector_button:SetScript("OnClick", function() toggle_options_window() end) top_bar_buttons[2]:SetScript("OnClick", function() toggle_game_menu() end) top_bar_buttons[3]:SetScript("OnClick", function() toggle_minimap_bag_bar() end) top_bar_buttons[4]:SetScript("OnClick", function() toggle_help_window() end) module.top_bar = top_bar module.top_bar_background = top_bar_background module.minimap_bag_bar = minimap_bag_bar module.minimap_bag_bar_background = minimap_bag_bar_background module.text_layer = text_layer module.zone_text = zone_text module.clock_text = clock_text module.clock_button = clock_button module.collector_container = collector_container module.collector_button = collector_button module.collector_background = collector_background module.collector_border = collector_border module.top_bar_buttons = top_bar_buttons install_collector_watcher(frames.tracking) install_collector_watcher(frames.calendar, "calendar_icon") install_collector_watcher(frames.group_finder, "group_finder_icon", true) install_collector_watcher(frames.zoom_in, "zoom_buttons") install_collector_watcher(frames.zoom_out, "zoom_buttons") create_minimap_bag_bar_buttons(minimap_bag_bar, palette, designelemente) set_minimap_bag_bar_visibility(false) update_zone_text() update_clock(true) apply_blizzard_frame_layout(true) scan_minimap_buttons(true) apply_palette() return true end local function apply_settings() update_zone_text() update_clock(true) apply_blizzard_frame_layout(false) scan_minimap_buttons(true) end local function collect_profile_settings() local settings = {} for index = 1, #setting_order do local key = setting_order[index] settings[key] = module.settings[key] == true end return settings end local function apply_profile_settings(profile_settings) if type(profile_settings) ~= "table" then return false end for index = 1, #setting_order do local key = setting_order[index] local value = profile_settings[key] module.settings[key] = type(value) == "boolean" and value or default_settings[key] end apply_settings() return true end local function on_event(_, event) if event == "ZONE_CHANGED" or event == "ZONE_CHANGED_INDOORS" or event == "ZONE_CHANGED_NEW_AREA" then update_zone_text() return end if event == "BAG_UPDATE_DELAYED" then update_minimap_bag_bar_buttons() return end if event == "PLAYER_REGEN_ENABLED" then if module.pending_foreign_update then apply_blizzard_frame_layout(false) end scan_minimap_buttons(true) return end if event == "ADDON_LOADED" or event == "PLAYER_ENTERING_WORLD" then apply_blizzard_frame_layout(false) local frames = resolve_blizzard_frames(false) install_collector_watcher(frames.group_finder, "group_finder_icon", true) if not module.minimap_bag_bar_buttons and module.minimap_bag_bar then create_minimap_bag_bar_buttons(module.minimap_bag_bar, ns.palette, ns.designelemente) set_minimap_bag_bar_visibility(false) else update_minimap_bag_bar_buttons() end scan_minimap_buttons(false) end end function ns:set_minimap_setting(key, value) if default_settings[key] == nil or type(value) ~= "boolean" then return false end module.settings[key] = value apply_settings() return true end function ns:get_minimap_top_bar() return module.top_bar end function ns:get_minimap_top_bar_metrics() return get_top_bar_anchor_gap(), top_bar_height end function ns:get_minimap_settings() if type(module.settings) ~= "table" then return nil end local settings = {} for index = 1, #setting_order do local key = setting_order[index] settings[key] = module.settings[key] == true end return settings end function module:initialize() self.settings = copy_default_settings() self.clock_elapsed = 0 self.collector_hover_elapsed = 0 self.pending_collector_scan = false self.minimap_bag_bar_visible = false ns:register_profile_provider("minimap", collect_profile_settings, apply_profile_settings) ns:register_event("ZONE_CHANGED", self, on_event) ns:register_event("ZONE_CHANGED_INDOORS", self, on_event) ns:register_event("ZONE_CHANGED_NEW_AREA", self, on_event) ns:register_event("ADDON_LOADED", self, on_event) ns:register_event("PLAYER_ENTERING_WORLD", self, on_event) ns:register_event("PLAYER_REGEN_ENABLED", self, on_event) ns:register_event("BAG_UPDATE_DELAYED", self, on_event) if not create_minimap_elements() then return end self.text_layer:SetScript("OnUpdate", function(_, elapsed) if module.pending_collector_scan then module.pending_collector_scan = false scan_minimap_buttons(true) end if module.collector_container and module.collector_container:IsShown() then module.collector_hover_elapsed = module.collector_hover_elapsed + elapsed if module.collector_hover_elapsed >= 0.1 then module.collector_hover_elapsed = 0 local over_button = type(module.collector_button.IsMouseOver) == "function" and module.collector_button:IsMouseOver(0, 0, -10, 0) or false local over_container = type(module.collector_container.IsMouseOver) == "function" and module.collector_container:IsMouseOver(0, 0, 0, 10) or false local collector_menu_open = is_collector_owned_menu_open() if not over_button and not over_container and not collector_menu_open then module.collector_container:Hide() end end else module.collector_hover_elapsed = 0 end module.clock_elapsed = module.clock_elapsed + elapsed if module.clock_elapsed >= 1 then module.clock_elapsed = 0 update_clock(false) end end) ns.palette:register_callback("minimap", function() apply_palette() end) end ns:register_module("minimap")