diff --git a/Butterfly.lua b/Butterfly.lua new file mode 100644 index 0000000..f8b6772 --- /dev/null +++ b/Butterfly.lua @@ -0,0 +1,15 @@ +local addonName, Butterfly = ...; + +local Watcher = CreateFrame("Frame") +Watcher:RegisterEvent("ADDON_LOADED") +Watcher:SetScript("OnEvent", function(self, event, addon, ...) + if event == "ADDON_LOADED" and addon == "Blizzard_SocialUI" then + Butterfly:ApplyTextHooks() + Butterfly:InitializeFauxFrame() + Butterfly:InitializeGalleryFrame() + -- TODO + -- Really, what do I want to do there? + -- The Blizzard screenshot button is now redundant, but. + -- Butterfly:InitializeSPFButtons() + end +end) diff --git a/Butterfly.toc b/Butterfly.toc new file mode 100644 index 0000000..313b518 --- /dev/null +++ b/Butterfly.toc @@ -0,0 +1,11 @@ +## Interface: 60100 +## Title: Butterfly +## Author: Corv +## Notes: Adds features to the Social (Twitter) interface. + +FauxFrame.lua +EditBox.lua +GalleryFrame.lua +Butterfly.lua +# SPFButtons.lua + diff --git a/EditBox.lua b/EditBox.lua new file mode 100644 index 0000000..6797d5a --- /dev/null +++ b/EditBox.lua @@ -0,0 +1,65 @@ +local addonName, Butterfly = ...; +--[[============ +We can't actually read out the text from the SocialPostFrame, because it's forbidden. +That sucks, because most of what we want to do is just replace the links with Wowhead ones +(and create achievement links in the first place - Blizz doesn't because Armory doesn't have those pages) + +So, a lot of this is just a copy/paste from Blizz's SocialPostFrame.lua, with the necessary adaptations. +--============]] + +local WOWHEAD_ITEM_LINK = "http://www.wowhead.com/item=" +local WOWHEAD_ACHIEVEMENT_LINK = "http://www.wowhead.com/achievement=" + +local SOCIAL_ACHIEVEMENT_PREFILL_TEXT_EARNED = "I just earned %s!" +local SOCIAL_ACHIEVEMENT_PREFILL_TEXT_GENERIC = "Check out this achievement! %s" +local SOCIAL_ACHIEVEMENT_PREFILL_TEXT_ALL = "%s %s #Warcraft" + +function Butterfly.ItemPrefillHook(itemID, earned, creationContext, name, quality) + if (creationContext == nil) then + creationContext = ""; + end + if (name == nil or quality == nil) then + local ignored; + name, ignored, quality = GetItemInfo(itemID); + end + + local prefillText; + if (earned) then + prefillText = SOCIAL_ITEM_PREFILL_TEXT_EARNED; + else + prefillText = SOCIAL_ITEM_PREFILL_TEXT_GENERIC; + end + + local r, g, b, colorString = GetItemQualityColor(quality); + local itemNameColored = format("|c%s[%s]|r", colorString, name); + local linkFormatStr = "|cff3b94d9" .. WOWHEAD_ITEM_LINK .. "%s|r"; + local armoryLink = format(linkFormatStr, itemID); + local text = format(SOCIAL_ITEM_PREFILL_TEXT_ALL, prefillText, itemNameColored, armoryLink); + SocialPostFrame:SetAttribute("settext", text); +end + +function Butterfly.AchievementPrefillHook(achievementID, earned, name) + if (name == nil) then + local ignored; + ignored, name = GetAchievementInfo(achievementID); + end + + -- Populate editbox with achievement prefill text + local achievementNameColored = format("%s[%s]|r", NORMAL_FONT_COLOR_CODE, name); + local prefillText; + if (earned) then + prefillText = format(SOCIAL_ACHIEVEMENT_PREFILL_TEXT_EARNED, achievementNameColored); + else + prefillText = format(SOCIAL_ACHIEVEMENT_PREFILL_TEXT_GENERIC, achievementNameColored); + end + + local linkFormatStr = "|cff3b94d9" .. WOWHEAD_ACHIEVEMENT_LINK .. "%s|r"; + local armoryLink = format(linkFormatStr, achievementID); + local text = format(SOCIAL_ACHIEVEMENT_PREFILL_TEXT_ALL, prefillText, armoryLink); + SocialPostFrame:SetAttribute("settext", text); +end + +function Butterfly:ApplyTextHooks() + hooksecurefunc("SocialPrefillItemText", self.ItemPrefillHook) + hooksecurefunc("SocialPrefillAchievementText", self.AchievementPrefillHook) +end \ No newline at end of file diff --git a/FauxFrame.lua b/FauxFrame.lua new file mode 100644 index 0000000..fa1b05b --- /dev/null +++ b/FauxFrame.lua @@ -0,0 +1,166 @@ +local addonName, Butterfly = ...; +--[[============ +Since the SocialPostFrame is forbidden, we need OnUpdate shenanigans to "SetPoint" to it. +The FauxFrame is a strata-, size-, position-, and anchor-matched overlay, to which anything else should SetPoint. + +EVERY movement must be exact. Any desynchronization is *unrecoverable*. + +StartMoving captures init position, and StopMoving calls an extra UpdatePosition, to ensure we don't lose a single frame of movement. + +The hairiest bit is that the SocialPostFrame is movable, which means that its :StopMoving may change the attachment point, +from CENTER to the nearest edge or corner. That WOULD be perfectly ignorable, except that when it calls :SetSize, +it uses THAT point as the fixed anchor. So, the FauxFrame has to explicitly reattach itself appropriately (since it isn't itself being dragged). + +Not all of the related code is as clean as I'd like, but it works, and hopefully this never needs to change. + +TODO: +- Clean up the UIScale stuff? +- Fix the hacky detach/reattach +--============]] + +local function GetScaledCursorPosition() + local x, y = GetCursorPosition() + local scale = UIParent:GetScale() + return x/scale, y/scale +end + +local FauxFrame = CreateFrame("Frame", "ButterflyFauxFrame", UIParent) +FauxFrame:SetFrameStrata("HIGH") +FauxFrame.MessageFrame = CreateFrame("Frame", "ButterflyFauxFrameMessageFrame", FauxFrame) +FauxFrame:Hide() -- so that the Gallery's OnShow script can run initially + +FauxFrame.sumCursorMovementX = 0 +FauxFrame.sumCursorMovementY = 0 +function FauxFrame:UpdateSocialPostFrameOffset() + local newCursorX, newCursorY = GetScaledCursorPosition() + local deltaX = newCursorX - self.oldCursorX + local deltaY = newCursorY - self.oldCursorY + self.oldCursorX = newCursorX + self.oldCursorY = newCursorY + self.sumCursorMovementX = self.sumCursorMovementX + deltaX + self.sumCursorMovementY = self.sumCursorMovementY + deltaY +end + +function FauxFrame:StartMoving() + self.isMoving = true + self:Detach() +end + +function FauxFrame:StopMoving() + self.isMoving = false + self:Reattach() +end + +function FauxFrame:UpdatePosition() + self:ClearAllPoints() + self:SetPoint("TOPLEFT", UIParent, "TOPLEFT", + self.UIParentWidth/2 + self.sumCursorMovementX - self:GetWidth()/2, + -self.UIParentHeight/2 + self.sumCursorMovementY + self:GetHeight()/2) +end + + +function FauxFrame:Detach() + local distanceFromLeft = self:GetLeft() + local distanceFromTop = self.UIParentHeight - self:GetTop() + + -- This is a hack and a half, but it works (it's because we fuck with attachment in the first place so resizes work properly) + self.sumCursorMovementX = self:GetLeft() + self:GetWidth()/2 - self.UIParentWidth/2 + self.sumCursorMovementY = self:GetTop() - self:GetHeight()/2 - self.UIParentHeight/2 +end + +function FauxFrame:Reattach() + local distances = {} + distances["LEFT"] = self:GetLeft() + distances["RIGHT"] = self.UIParentWidth - self:GetRight() + distances["BOTTOM"] = self:GetBottom() + distances["TOP"] = self.UIParentHeight - self:GetTop() + + local nearToLeft = distances["LEFT"] < math.abs(self.sumCursorMovementX) + local nearToRight = distances["RIGHT"] < math.abs(self.sumCursorMovementX) + local nearToTop = distances["TOP"] < math.abs(self.sumCursorMovementY) + local nearToBottom = distances["BOTTOM"] < math.abs(self.sumCursorMovementY) + + self:ClearAllPoints() + if nearToLeft then + if nearToTop then + self:SetPoint("TOPLEFT", UIParent, "TOPLEFT", distances["LEFT"], -distances["TOP"]) + elseif nearToBottom then + self:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", distances["LEFT"], distances["BOTTOM"]) + else + self:SetPoint("LEFT", UIParent, "LEFT", distances["LEFT"], self.sumCursorMovementY) + end + elseif nearToRight then + if nearToTop then + self:SetPoint("TOPRIGHT", UIParent, "TOPRIGHT", -distances["RIGHT"], -distances["TOP"]) + elseif nearToBottom then + self:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -distances["RIGHT"], distances["BOTTOM"]) + else + self:SetPoint("RIGHT", UIParent, "RIGHT", -distances["RIGHT"], self.sumCursorMovementY) + end + else + if nearToTop then + self:SetPoint("TOP", UIParent, "TOP", self.sumCursorMovementX, -distances["TOP"]) + elseif nearToBottom then + self:SetPoint("BOTTOM", UIParent, "BOTTOM", self.sumCursorMovementX, distances["BOTTOM"]) + else + self:SetPoint("CENTER", UIParent, "CENTER", self.sumCursorMovementX, self.sumCursorMovementY) + end + end + +end + +function FauxFrame:OnUpdate(elapsed) + if self.isMoving then + self:UpdateSocialPostFrameOffset() + self:UpdatePosition() + end +end + + +function FauxFrame:Resize(width, height) + self:SetSize(width, height) +end + +Butterfly.FauxFrame = FauxFrame + +function Butterfly:InitializeFauxFrame() + hooksecurefunc(SocialPostFrame, "StartMoving", function() + self.FauxFrame.oldCursorX, self.FauxFrame.oldCursorY = GetScaledCursorPosition() + self.FauxFrame:StartMoving() + end) + + hooksecurefunc(SocialPostFrame, "StopMovingOrSizing", function() + self.FauxFrame:UpdateSocialPostFrameOffset() + self.FauxFrame:UpdatePosition() + self.FauxFrame:StopMoving() + end) + + self.FauxFrame:SetSize(SOCIAL_DEFAULT_FRAME_WIDTH, SOCIAL_DEFAULT_FRAME_HEIGHT) + self.FauxFrame:SetPoint("CENTER") + FauxFrame.MessageFrame:SetSize(348, 92) + FauxFrame.MessageFrame:SetPoint("BOTTOM", 0, 62) + + -- Can't use HookScript("OnShow") because SocialPostFrame is forbidden + -- Can't hook SocialPostFrame_OnShow because it's referenced inline in the XML and the new hook won't be called + hooksecurefunc(SocialPostFrame, "Show", function() self.FauxFrame:Show() end) + hooksecurefunc(SocialPostFrame, "Hide", function() self.FauxFrame:Hide() end) + hooksecurefunc(SocialPostFrame, "SetSize", function(SPF, width, height) self.FauxFrame:Resize(width, height) end) + self.FauxFrame:SetScript("OnUpdate", function(self, elapsed) self:OnUpdate(elapsed) end) + + self.FauxFrame.UIScale = UIParent:GetScale() + self.FauxFrame.UIParentWidth = UIParent:GetWidth() + self.FauxFrame.UIParentHeight = UIParent:GetHeight() + + FauxFrame:RegisterEvent("UI_SCALE_CHANGED") + FauxFrame:SetScript("OnEvent", function(self, event) + if event == "UI_SCALE_CHANGED" then + local newScale = UIParent:GetScale() + self.sumCursorMovementX = self.sumCursorMovementX*newScale/self.UIScale + self.sumCursorMovementY = self.sumCursorMovementY*newScale/self.UIScale + self.UIScale = newScale + + self.UIParentWidth = UIParent:GetWidth() + self.UIParentHeight = UIParent:GetHeight() + end + end) +end \ No newline at end of file diff --git a/GalleryFrame.lua b/GalleryFrame.lua new file mode 100644 index 0000000..eceb8a8 --- /dev/null +++ b/GalleryFrame.lua @@ -0,0 +1,342 @@ +local addonName, Butterfly = ...; +--[[============ + +--============]] +local FauxFrame = Butterfly.FauxFrame + +local GALLERY_PADDING = 2 + +local GalleryFrame = CreateFrame("Frame", "ButterflyGalleryFrame", FauxFrame, "BasicFrameTemplateWithInset") +local ButterflyGalleryScrollFrame = CreateFrame("ScrollFrame", "ButterflyGalleryScrollFrame", GalleryFrame) +GalleryFrame.ScrollFrame = ButterflyGalleryScrollFrame + +--[[============ +Boring verbose layout building code inside. +--============]] +do + -- We don't want the title bar and such, but rather than play with textures... + -- Since we're doing such sketchy stuff to attach the frame anyways, just sweep it under the rug + -- Hide the CloseButton though, just in case? + GalleryFrame:SetPoint("TOP", FauxFrame, "BOTTOM", 0, 26) + GalleryFrame.CloseButton:Hide() + -- Bump up the size of the inset a bit + GalleryFrame.InsetBg:ClearAllPoints() + GalleryFrame.InsetBg:SetPoint("TOPLEFT", 6, -24) -- (4, -24) + GalleryFrame.InsetBg:SetPoint("BOTTOMRIGHT", -8, 6) -- (-6, 4) + + + ButterflyGalleryScrollFrame:SetPoint("TOPLEFT", 10, -30) + ButterflyGalleryScrollFrame:SetPoint("BOTTOMRIGHT", -12, 36) + + -- The bar itself. + local scrollBar = CreateFrame("Slider", "ButterflyGalleryScrollFrameScrollBar", ButterflyGalleryScrollFrame) + local scrollThumb = scrollBar:CreateTexture("ButterflyGalleryScrollFrameScrollBarThumbTexture", "ARTWORK", "UIPanelScrollBarButton") + scrollThumb:SetSize(18, 24) + scrollThumb:SetTexCoord(0.20, 0.80, 0.125, 0.875) + scrollThumb:SetTexture([[Interface\Buttons\UI-ScrollBar-Knob]]) + scrollThumb:SetPoint("LEFT", 0, 2) + scrollBar:SetThumbTexture(scrollThumb) + scrollBar:SetOrientation("HORIZONTAL") + scrollBar:SetPoint("BOTTOMLEFT", GalleryFrame, "BOTTOMLEFT", 30, 14) + scrollBar:SetPoint("BOTTOMRIGHT", GalleryFrame, "BOTTOMRIGHT", -31, 14) + scrollBar:SetHeight(16) + scrollBar:SetScript("OnValueChanged", function(self, value) + self:GetParent():SetHorizontalScroll(value); + end) + local scrollBarLeftTexture = scrollBar:CreateTexture("BST") + scrollBarLeftTexture:SetTexture([[Interface\ClassTrainerFrame\UI-ClassTrainer-ScrollBar]]) + scrollBarLeftTexture:SetTexCoord(0.53125, 1.0, 1.0, 1.0, 0.53125, 0.03125, 1.0, 0.03125) + scrollBarLeftTexture:SetSize(123, 29) + scrollBarLeftTexture:SetPoint("BOTTOMLEFT", -21, -5) + + local scrollBarRightTexture = scrollBar:CreateTexture("BSR") + scrollBarRightTexture:SetTexture([[Interface\ClassTrainerFrame\UI-ClassTrainer-ScrollBar]]) + scrollBarRightTexture:SetTexCoord(0.0, 0.9609375, 0.46875, 0.9609375, 0.0, 0.0234375, 0.46875, 0.0234375) + scrollBarRightTexture:SetSize(120, 29) + scrollBarRightTexture:SetPoint("BOTTOMRIGHT", 19, -5) + + local scrollBarMiddleTexture = scrollBar:CreateTexture("BSM") + scrollBarMiddleTexture:SetTexture([[Interface\ClassTrainerFrame\UI-ClassTrainer-ScrollBar]]) + scrollBarMiddleTexture:SetTexCoord(0.0, 0.9609375, 0.46875, 0.9609375, 0.0, 0.4, 0.46875, 0.4) + scrollBarMiddleTexture:SetHeight(29) + scrollBarMiddleTexture:SetPoint("LEFT", scrollBarLeftTexture, "RIGHT") + scrollBarMiddleTexture:SetPoint("RIGHT", scrollBarRightTexture, "LEFT") + + -- The right née Down button + local scrollBarRightButton = CreateFrame("Button", "ButterflyGalleryScrollFrameScrollBarScrollDownButton", scrollBar, "UIPanelScrollDownButtonTemplate") + scrollBarRightButton:SetScript("OnClick", function(self) + local parent = self:GetParent(); + local scrollStep = self:GetParent().scrollStep or (parent:GetHeight() / 2); + parent:SetValue(parent:GetValue() + scrollStep); + PlaySound("UChatScrollButton"); + end) + scrollBarRightButton:SetPoint("LEFT", scrollBar, "RIGHT", 1, 0) + local rightAnim = scrollBarRightButton:CreateAnimationGroup() + local rightAnimRot = rightAnim:CreateAnimation("Rotation") + rightAnimRot:SetDegrees(90) + rightAnimRot:SetEndDelay(math.huge) + rightAnim:Play() + + -- The left née Up button + local scrollBarLeftButton = CreateFrame("Button", "ButterflyGalleryScrollFrameScrollBarScrollUpButton", scrollBar, "UIPanelScrollUpButtonTemplate") + scrollBarLeftButton:SetScript("OnClick", function(self) + local parent = self:GetParent(); + local scrollStep = self:GetParent().scrollStep or (parent:GetHeight() / 2); + parent:SetValue(parent:GetValue() - scrollStep); + PlaySound("UChatScrollButton"); + end) + scrollBarLeftButton:SetPoint("RIGHT", scrollBar, "LEFT", -1, 0) + local leftAnim = scrollBarLeftButton:CreateAnimationGroup() + local leftAnimRot = leftAnim:CreateAnimation("Rotation") + leftAnimRot:SetDegrees(90) + leftAnimRot:SetEndDelay(math.huge) + leftAnim:Play() + + -- The actual "content" of the scroll frame. + local scrollChild = CreateFrame("Frame", "ButterflyGalleryScrollFrameContent", ButterflyGalleryScrollFrame) + ButterflyGalleryScrollFrame:SetScrollChild(scrollChild) + GalleryFrame.ScrollFrame.Content = scrollChild + + --[[============ + The rest of this is scripts lifted from Blizz's normal ScrollFrame stuff, just modified for horizontal scrolling. + --============]] + ScrollFrame_OnLoad(ButterflyGalleryScrollFrame) + ButterflyGalleryScrollFrame:SetScript("OnScrollRangeChanged", function(self, xrange, yrange) + local scrollbar = self.ScrollBar or _G[self:GetName().."ScrollBar"]; + if ( not xrange ) then + xrange = self:GetHorizontalScrollRange(); + end + local value = scrollbar:GetValue(); + if ( value > xrange ) then + value = xrange; + end + scrollbar:SetMinMaxValues(0, xrange); + scrollbar:SetValue(value); + if ( floor(xrange) == 0 ) then + if ( self.scrollBarHideable ) then + _G[self:GetName().."ScrollBar"]:Hide(); + _G[scrollbar:GetName().."ScrollDownButton"]:Hide(); + _G[scrollbar:GetName().."ScrollUpButton"]:Hide(); + _G[scrollbar:GetName().."ThumbTexture"]:Hide(); + else + _G[scrollbar:GetName().."ScrollDownButton"]:Disable(); + _G[scrollbar:GetName().."ScrollUpButton"]:Disable(); + _G[scrollbar:GetName().."ScrollDownButton"]:Show(); + _G[scrollbar:GetName().."ScrollUpButton"]:Show(); + if ( not self.noScrollThumb ) then + _G[scrollbar:GetName().."ThumbTexture"]:Show(); + end + end + else + _G[scrollbar:GetName().."ScrollDownButton"]:Show(); + _G[scrollbar:GetName().."ScrollUpButton"]:Show(); + _G[self:GetName().."ScrollBar"]:Show(); + if ( not self.noScrollThumb ) then + _G[scrollbar:GetName().."ThumbTexture"]:Show(); + end + -- The 0.005 is to account for precision errors + if ( xrange - value > 0.005 ) then + _G[scrollbar:GetName().."ScrollDownButton"]:Enable(); + else + _G[scrollbar:GetName().."ScrollDownButton"]:Disable(); + end + end + + -- Hide/show ButterflyGalleryScrollFrame borders + local top = _G[self:GetName().."Top"]; + local bottom = _G[self:GetName().."Bottom"]; + local middle = _G[self:GetName().."Middle"]; + if ( top and bottom and self.scrollBarHideable ) then + if ( self:GetHorizontalScrollRange() == 0 ) then + top:Hide(); + bottom:Hide(); + else + top:Show(); + bottom:Show(); + end + end + if ( middle and self.scrollBarHideable ) then + if ( self:GetHorizontalScrollRange() == 0 ) then + middle:Hide(); + else + middle:Show(); + end + end + end) + ButterflyGalleryScrollFrame:SetScript("OnHorizontalScroll", function(self, offset) + local scrollbar = _G[self:GetName().."ScrollBar"]; + scrollbar:SetValue(offset); + local min; + local max; + min, max = scrollbar:GetMinMaxValues(); + if ( offset == 0 ) then + _G[scrollbar:GetName().."ScrollUpButton"]:Disable(); + else + _G[scrollbar:GetName().."ScrollUpButton"]:Enable(); + end + if ((scrollbar:GetValue() - max) == 0) then + _G[scrollbar:GetName().."ScrollDownButton"]:Disable(); + else + _G[scrollbar:GetName().."ScrollDownButton"]:Enable(); + end + end) + ButterflyGalleryScrollFrame:SetScript("OnMouseWheel", function(self, value, scrollBar) + scrollBar = scrollBar or _G[self:GetName() .. "ScrollBar"]; + local scrollStep = scrollBar.scrollStep or scrollBar:GetWidth() / 2 + if ( value > 0 ) then + scrollBar:SetValue(scrollBar:GetValue() - scrollStep); + else + scrollBar:SetValue(scrollBar:GetValue() + scrollStep); + end + end) +end + +function GalleryFrame:GetChromeHeight() + local yOffsets = 0 + for i = 1, self.ScrollFrame:GetNumPoints() do + -- Technically, it really SHOULDN'T be a simple absolute value, it should be directional + -- But y'know what? The points aren't "backwards" and they never should be, bite me + -- (I fully expect to come back months from now, bitten) + yOffsets = yOffsets + math.abs(select(5, self.ScrollFrame:GetPoint(i))) + end + return yOffsets +end + +--[[============ + Note that AddGalleryButton does *not* set the size on anything. +--============]] +function GalleryFrame:AddGalleryButton() + local ScrollContent = GalleryFrame.ScrollFrame.Content + local lastIndex = #self.galleryButtons + + local valid, width, height = C_Social.GetScreenshotByIndex(lastIndex+1) + assert(valid, "Failed to load screenshot") + + local f = CreateFrame("Button", "ButterflyGalleryContentButton" .. lastIndex+1, ScrollContent, "HelpPlateBox") + f:SetScript("OnClick", function(self) + SocialPostFrame:SetAttribute("screenshotview", self.id) + end) + -- I don't have a good reason but I need to show the buttons or they don't come up. Parenting issue? + f:Show() + f.id = lastIndex+1 + C_Social.SetTextureToScreenshot(f.BG, f.id) + -- This line feels hackish but the texture doesn't return valid sizes until it's sized > 0 somewhere + f.BG.width, f.BG.height = width, height + + hooksecurefunc(f.BG, "SetDesaturated", function() assert(false, "Desaturated texture!\nPlease forward this crash log to corveroth@gmail.com") end) + + f.Plus = f:CreateTexture() + f.Plus:SetDrawLayer("HIGHLIGHT") + f.Plus:SetAtlas("WoWShare-Plus", true) + f.Plus:SetPoint("TOPLEFT") + + -- Create the highlight glow + do + local bottom = f:CreateTexture() + bottom:SetDrawLayer("HIGHLIGHT") + bottom:SetTexture([[Interface\Common\talent-blue-glow]]) + bottom:SetPoint("BOTTOMLEFT") + bottom:SetPoint("BOTTOMRIGHT") + + local top = f:CreateTexture() + top:SetDrawLayer("HIGHLIGHT") + top:SetTexture([[Interface\Common\talent-blue-glow]]) + top:SetTexCoord(1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0) + top:SetPoint("TOPLEFT") + top:SetPoint("TOPRIGHT") + + local left = f:CreateTexture() + left:SetDrawLayer("HIGHLIGHT") + left:SetTexture([[Interface\Common\talent-blue-glow]]) + left:SetTexCoord(0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0) + left:SetPoint("TOPLEFT") + left:SetPoint("BOTTOMLEFT") + + local right = f:CreateTexture() + right:SetDrawLayer("HIGHLIGHT") + right:SetTexture([[Interface\Common\talent-blue-glow]]) + right:SetTexCoord(1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0) + right:SetPoint("TOPRIGHT") + right:SetPoint("BOTTOMRIGHT") + end + + if lastIndex == 0 then + f:SetPoint("RIGHT", ScrollContent, "RIGHT", -GALLERY_PADDING, 0) + else + f:SetPoint("RIGHT", self.galleryButtons[lastIndex], "LEFT", -GALLERY_PADDING, 0) + end + tinsert(self.galleryButtons, f) +end + +function GalleryFrame:ResizeGalleryButtons() + local gbWidth, gbHeight + local sumWidths, maxHeight = 0, 0 + for i, button in pairs (self.galleryButtons) do + gbWidth, gbHeight = CalculateScreenshotSize(button.BG.width, button.BG.height, SOCIAL_SCREENSHOT_TOOLTIP_MAX_WIDTH, SOCIAL_SCREENSHOT_TOOLTIP_MAX_HEIGHT) + button:SetSize(gbWidth, gbHeight) + if gbHeight > maxHeight then + maxHeight = gbHeight + end + sumWidths = sumWidths + gbWidth + end + + return sumWidths, maxHeight +end + +function GalleryFrame:ResizeContents() + local buttonWidths, maxButtonHeight = self:ResizeGalleryButtons() + local contentHeight = maxButtonHeight + 2*GALLERY_PADDING + local galleryHeight = contentHeight + self:GetChromeHeight() + + local contentWidth = buttonWidths + (#self.galleryButtons+1)*GALLERY_PADDING + self.ScrollFrame.Content:SetSize(contentWidth, contentHeight) + self:SetHeight(galleryHeight) +end + + +--[[============ + C_Social.SetTextureToScreenshot is a fucking expensive function. + There's not much way around that, though. In order to prevent a total lockup when the Social UI opens, + at least stagger it on a timer. Each iteration will still cause stutter on lower-end hardware, but it's something. + + (Yes, STTS is the killer here. Don't try to get clever unless you can work around that) +--============]] +function GalleryFrame:RebuildFromReload() + local ticker = C_Timer.NewTicker(0.2, function(self) + if #GalleryFrame.galleryButtons < C_Social.GetLastScreenshot() then + GalleryFrame:AddGalleryButton() + GalleryFrame:ResizeContents() + if not GalleryFrame:IsShown() then + GalleryFrame:Show() + end + else + self:Cancel() + end + end) +end + +function Butterfly:InitializeGalleryFrame() + GalleryFrame.galleryButtons = {} + if C_Social.GetLastScreenshot() > 0 then + GalleryFrame:RebuildFromReload() + end + + GalleryFrame:RegisterEvent("SCREENSHOT_SUCCEEDED") + GalleryFrame:SetScript("OnEvent", function(self, event) + if event == "SCREENSHOT_SUCCEEDED" then + self:AddGalleryButton() + self:ResizeContents() + if not self:IsShown() then + self:Show() + end + end + end) + GalleryFrame:SetScript("OnShow", function(self) + if #self.galleryButtons < 1 then + self:Hide() + end + end) + hooksecurefunc(FauxFrame, "Resize", function(frame, width, height) + GalleryFrame:SetWidth(width - GALLERY_PADDING*4) + end) +end \ No newline at end of file diff --git a/SPFButtons.lua b/SPFButtons.lua new file mode 100644 index 0000000..94a48b6 --- /dev/null +++ b/SPFButtons.lua @@ -0,0 +1,156 @@ +local addonName, Butterfly = ...; +--[[============ +Wouldn't it be nice if we could just reassign Blizz's last-screenshot button for our gallery? +Sadly, it's forbidden. However, not much stopping us from just putting another lookalike *over* it. + +Except, y'know, the SocialShareButton template being forbidden too. +So, mimic the relevant bits of that... and the SocialScreenshotTooltip. + +Indices for SPFReplacementButtons are internal tracking numbers. +Indices for SPFBlockers and OverrideSocialPostFrameButton refer to positions on the SocialPostFrame +SPFBlockers may be totally unnecessary, but exist to make fully sure no mouse events go through to the original buttons +--============]] + +local FauxFrame = Butterfly.FauxFrame +-- These values are from the SocialShareButton template definition in Blizzard_SocialUI.xml +local SOCIAL_BUTTON_WIDTH = 42 +local SOCIAL_BUTTON_HEIGHT = 43 + +-- SOCIAL_BUTTON_OFFSETs are derived from inspection of Blizzard_SocialUI.xml:409 ish, in the definition of SocialPostFrame +local SOCIAL_BUTTON_XOFFSET = -6 +local SOCIAL_BUTTON_YOFFSET = -11 +local SOCIAL_BUTTON_POINT = "TOPLEFT" +local SOCIAL_BUTTON_RELATIVE_POINT = "BOTTOMLEFT" +local SOCIAL_BUTTON_PADDING = 7 + +function Butterfly:CreateNewSocialShareButton() + local num = #self.SPFReplacementButtons + 1 + + local f = CreateFrame("Button", "ButterflySPFButton"..num, FauxFrame) + f:SetSize(SOCIAL_BUTTON_WIDTH, SOCIAL_BUTTON_HEIGHT) + f:SetFrameStrata("DIALOG") + f:SetFrameLevel(2) + f.Icon = f:CreateTexture() + f.Icon:SetDrawLayer("OVERLAY", 0) + f.Icon:SetAtlas("WoWShare-AchievementIcon", true) + f.Icon:SetPoint("CENTER") + + f.Border = f:CreateTexture() + f.Border:SetDrawLayer("OVERLAY", 1) + f.Border:SetAtlas("WoWShare-AddButton-Up", true) + f.Border:SetPoint("CENTER") + + f.QualityBorder = f:CreateTexture() + f.QualityBorder:SetDrawLayer("OVERLAY", 2) + f.QualityBorder:SetAtlas("WoWShare-ItemQualityBorder", true) + f.QualityBorder:SetPoint("CENTER") + f.QualityBorder:SetVertexColor(0, 0, 1, 1) + f.QualityBorder:Hide() + + f.Highlight = f:CreateTexture() + f.Highlight:SetDrawLayer("HIGHLIGHT") + f.Highlight:SetAtlas("WoWShare-Highlight", true) + f.Highlight:SetPoint("CENTER") + + f.Plus = f:CreateTexture() + f.Plus:SetDrawLayer("HIGHLIGHT") + f.Plus:SetAtlas("WoWShare-Plus", true) + f.Plus:SetPoint("CENTER") + + f:SetScript("OnMouseDown", SharedButton_OnMouseDown) + f:SetScript("OnMouseUp", SharedButton_OnMouseUp) + + self.SPFReplacementButtons[num] = f + return f +end + +function Butterfly:CreateSPFBlocker(index) + local underlay = CreateFrame("Frame", "ButterflySPFButtonBlocker"..index, FauxFrame) + underlay:EnableMouse(true) + underlay:SetFrameStrata("DIALOG") + underlay:SetFrameLevel(1) + underlay:SetSize(SOCIAL_BUTTON_WIDTH, SOCIAL_BUTTON_HEIGHT) + underlay:SetPoint(SOCIAL_BUTTON_POINT, FauxFrame.MessageFrame, SOCIAL_BUTTON_RELATIVE_POINT, + SOCIAL_BUTTON_XOFFSET+(SOCIAL_BUTTON_WIDTH+SOCIAL_BUTTON_PADDING)*(index-1), + SOCIAL_BUTTON_YOFFSET) + self.SocialPostFrameBlockers[index] = underlay + return underlay +end + +function Butterfly:GetSPFBlocker(index) + return self.SocialPostFrameBlockers[index] +end + +function Butterfly:OverrideSocialPostFrameButton(newButton, index) + if not self:GetSPFBlocker(index) then + self:CreateSPFBlocker(index) + end + + newButton:SetPoint(SOCIAL_BUTTON_POINT, FauxFrame.MessageFrame, SOCIAL_BUTTON_RELATIVE_POINT, + SOCIAL_BUTTON_XOFFSET+(SOCIAL_BUTTON_WIDTH+SOCIAL_BUTTON_PADDING)*(index-1), + SOCIAL_BUTTON_YOFFSET) +end + +function Butterfly:UpdateGalleryButton() + local self = self.galleryButton + local index = C_Social.GetLastScreenshot(); + if (index > 0 and C_Social.GetScreenshotByIndex(index)) then + C_Social.SetTextureToScreenshot(self.Icon, index); + self:Enable(); + else + self.Icon:SetAtlas("WoWShare-ScreenshotIcon", true); + self:Disable(); + end +end + +--[[============ +We can't check the SPF.ImageFrame, and there's no attribute that'll tell us the current state of the window. +In principle, we should watch SPF.SetAttribute and manually track it's state. However, we've got a FauxFrame. +--============]] +function ButterflyGalleryButton_OnClick(self, button) + local alreadyShown = (FauxFrame:GetWidth() ~= SOCIAL_DEFAULT_FRAME_WIDTH) or (FauxFrame:GetHeight() ~= SOCIAL_DEFAULT_FRAME_HEIGHT) + if button == "LeftButton" then + -- Note that we can't just call SocialPrefillScreenshotText and get the side effects of that due to forbidden references + SocialPostFrame:SetAttribute("screenshotview", C_Social.GetLastScreenshot()) + else + Butterfly:InitializeGalleryFrame() + end + if (alreadyShown) then + PlaySound("igMainMenuOption"); + end +end + +function ButterflyGalleryButton_OnEnter(self) + -- local index = C_Social.GetLastScreenshot(); + -- local valid, width, height = C_Social.GetScreenshotByIndex(index); + -- if (valid) then + -- SocialScreenshotButton_ShowTooltip(self, width, height); + -- else + -- GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT", -38, -12); + -- GameTooltip:SetText(SOCIAL_SCREENSHOT_PREFILL_NONE); + -- end +end + +function ButterflyGalleryButton_OnLeave(self) + -- GameTooltip_Hide(); + -- SocialScreenshotTooltip:Hide(); +end + +function Butterfly:InitializeSPFButtons() + self.SPFReplacementButtons = {} + self.SocialPostFrameBlockers = {} + + -- self:InitScreenshotTooltip() + + local galleryButton = self:CreateNewSocialShareButton() + galleryButton:RegisterForClicks("AnyUp") + galleryButton:SetScript("OnClick", ButterflyGalleryButton_OnClick) + galleryButton:SetScript("OnEnter", ButterflyGalleryButton_OnEnter) + galleryButton:SetScript("OnLeave", ButterflyGalleryButton_OnLeave) + self:OverrideSocialPostFrameButton(galleryButton, 1) + self.galleryButton = galleryButton + + hooksecurefunc("SocialScreenshotButton_Update", function() + Butterfly:UpdateGalleryButton() + end) +end \ No newline at end of file