Misc.lua 12.9 KB
Newer Older
Azilroka's avatar
Azilroka committed
1
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
2 3 4
local S = E:GetModule('Skins')

local _G = _G
Simpy's avatar
Simpy committed
5
local pairs = pairs
Simpy's avatar
Simpy committed
6
local ipairs = ipairs
7
local unpack = unpack
Simpy's avatar
nothing  
Simpy committed
8

Simpy's avatar
Simpy committed
9 10
local hooksecurefunc = hooksecurefunc
local IsAddOnLoaded = IsAddOnLoaded
Simpy's avatar
stuff  
Simpy committed
11
local CreateFrame = CreateFrame
12

Simpy's avatar
Simpy committed
13
local LFG_ICONS = [[Interface\LFGFrame\UI-LFG-ICONS-ROLEBACKGROUNDS]]
14
local function SkinNavBarButtons(self)
Simpy's avatar
Simpy committed
15
	if (self:GetParent():GetName() == 'EncounterJournal' and not E.private.skins.blizzard.encounterjournal) or (self:GetParent():GetName() == 'WorldMapFrame' and not E.private.skins.blizzard.worldmap) or (self:GetParent():GetName() == 'HelpFrameKnowledgebase' and not E.private.skins.blizzard.help) then
16 17 18 19 20 21
		return
	end

	local navButton = self.navList[#self.navList]
	if navButton and not navButton.isSkinned then
		S:HandleButton(navButton, true)
Elv's avatar
Elv committed
22
		navButton:GetFontString():SetTextColor(1, 1, 1)
23
		if navButton.MenuArrowButton then
Azilroka's avatar
Azilroka committed
24 25
			navButton.MenuArrowButton:StripTextures()
			if navButton.MenuArrowButton.Art then
26
				navButton.MenuArrowButton.Art:SetTexture(E.Media.Textures.ArrowUp)
Azilroka's avatar
Azilroka committed
27 28 29
				navButton.MenuArrowButton.Art:SetTexCoord(0, 1, 0, 1)
				navButton.MenuArrowButton.Art:SetRotation(3.14)
			end
30 31
		end

Azilroka's avatar
Azilroka committed
32 33
		navButton.xoffset = 1

34 35 36 37
		navButton.isSkinned = true
	end
end

Simpy's avatar
Simpy committed
38
function S:BlizzardMiscFrames()
Simpy's avatar
-.-  
Simpy committed
39
	if not (E.private.skins.blizzard.enable and E.private.skins.blizzard.misc) then return end
40

41 42
	_G.QueueStatusFrame:StripTextures()

43 44
	-- Blizzard frame we want to reskin
	local skins = {
Simpy's avatar
Simpy committed
45 46 47 48
		'AutoCompleteBox',
		'ReadyCheckFrame',
		'QueueStatusFrame',
		'LFDReadyCheckPopup',
49 50
	}

Simpy's avatar
Simpy committed
51
	for i = 1, #skins do
Merathilis's avatar
Merathilis committed
52
		_G[skins[i]]:StripTextures()
Simpy's avatar
Simpy committed
53
		_G[skins[i]]:CreateBackdrop('Transparent')
54 55
	end

56
	S:HandleButton(_G.StaticPopup1ExtraButton)
57

Simpy's avatar
Simpy committed
58
	hooksecurefunc('QueueStatusEntry_SetFullDisplay', function(entry, _, _, _, isTank, isHealer, isDPS)
Simpy's avatar
boop  
Simpy committed
59 60 61
		if not entry then return end
		local nextRoleIcon = 1
		if isDPS then
Simpy's avatar
Simpy committed
62
			local icon = entry['RoleIcon'..nextRoleIcon]
Simpy's avatar
boop  
Simpy committed
63 64
			if icon then
				icon:SetTexture(LFG_ICONS)
Merathilis's avatar
Merathilis committed
65
				icon:SetTexCoord(_G.LFDQueueFrameRoleButtonDPS.background:GetTexCoord())
Simpy's avatar
boop  
Simpy committed
66 67
				nextRoleIcon = nextRoleIcon + 1
			end
Elv's avatar
Elv committed
68
		end
Simpy's avatar
boop  
Simpy committed
69
		if isHealer then
Simpy's avatar
Simpy committed
70
			local icon = entry['RoleIcon'..nextRoleIcon]
Simpy's avatar
boop  
Simpy committed
71 72
			if icon then
				icon:SetTexture(LFG_ICONS)
Merathilis's avatar
Merathilis committed
73
				icon:SetTexCoord(_G.LFDQueueFrameRoleButtonHealer.background:GetTexCoord())
Simpy's avatar
boop  
Simpy committed
74 75
				nextRoleIcon = nextRoleIcon + 1
			end
Elv's avatar
Elv committed
76
		end
Simpy's avatar
boop  
Simpy committed
77
		if isTank then
Simpy's avatar
Simpy committed
78
			local icon = entry['RoleIcon'..nextRoleIcon]
Simpy's avatar
boop  
Simpy committed
79 80
			if icon then
				icon:SetTexture(LFG_ICONS)
Merathilis's avatar
Merathilis committed
81
				icon:SetTexCoord(_G.LFDQueueFrameRoleButtonTank.background:GetTexCoord())
Simpy's avatar
boop  
Simpy committed
82 83
			end
		end
Elv's avatar
Elv committed
84 85
	end)

Simpy's avatar
Simpy committed
86
	hooksecurefunc('QueueStatusFrame_Update', function()
Simpy's avatar
Simpy committed
87
		for frame in _G.QueueStatusFrame.statusEntriesPool:EnumerateActive() do
Elv's avatar
Elv committed
88 89 90 91 92 93
			frame.HealersFound.Texture:SetTexture(LFG_ICONS)
			frame.TanksFound.Texture:SetTexture(LFG_ICONS)
			frame.DamagersFound.Texture:SetTexture(LFG_ICONS)
			frame.HealersFound.Texture:SetTexCoord(_G.LFDQueueFrameRoleButtonHealer.background:GetTexCoord())
			frame.TanksFound.Texture:SetTexCoord(_G.LFDQueueFrameRoleButtonTank.background:GetTexCoord())
			frame.DamagersFound.Texture:SetTexCoord(_G.LFDQueueFrameRoleButtonDPS.background:GetTexCoord())
Elv's avatar
Elv committed
94
		end
Elv's avatar
Elv committed
95
	end)
96

Simpy's avatar
Simpy committed
97
	if not IsAddOnLoaded('ConsolePortUI_Menu') then
98
		-- reskin all esc/menu buttons
Azilroka's avatar
Azilroka committed
99
		for _, Button in pairs({_G.GameMenuFrame:GetChildren()}) do
Simpy's avatar
Simpy committed
100
			if Button.IsObjectType and Button:IsObjectType('Button') then
Azilroka's avatar
Azilroka committed
101
				S:HandleButton(Button)
102 103 104
			end
		end

105
		_G.GameMenuFrame:StripTextures()
Simpy's avatar
Simpy committed
106
		_G.GameMenuFrame:CreateBackdrop('Transparent')
Merathilis's avatar
Merathilis committed
107 108
		_G.GameMenuFrame.Header:StripTextures()
		_G.GameMenuFrame.Header:ClearAllPoints()
Simpy's avatar
Simpy committed
109
		_G.GameMenuFrame.Header:Point('TOP', _G.GameMenuFrame, 0, 7)
110 111
	end

Simpy's avatar
Simpy committed
112
	if IsAddOnLoaded('OptionHouse') then
113
		S:HandleButton(_G.GameMenuButtonOptionHouse)
114 115
	end

Simpy's avatar
Simpy committed
116 117 118
	-- since we cant hook `CinematicFrame_OnShow` or `CinematicFrame_OnEvent` directly
	-- we can just hook onto this function so that we can get the correct `self`
	-- this is called through `CinematicFrame_OnShow` so the result would still happen where we want
119 120 121
	hooksecurefunc('CinematicFrame_OnDisplaySizeChanged', function(s)
		if s and s.closeDialog and not s.closeDialog.template then
			s.closeDialog:StripTextures()
Simpy's avatar
Simpy committed
122
			s.closeDialog:CreateBackdrop('Transparent')
123 124 125 126
			s:SetScale(_G.UIParent:GetScale())
			local dialogName = s.closeDialog.GetName and s.closeDialog:GetName()
			local closeButton = s.closeDialog.ConfirmButton or (dialogName and _G[dialogName..'ConfirmButton'])
			local resumeButton = s.closeDialog.ResumeButton or (dialogName and _G[dialogName..'ResumeButton'])
127 128
			if closeButton then S:HandleButton(closeButton) end
			if resumeButton then S:HandleButton(resumeButton) end
Simpy's avatar
Simpy committed
129 130 131 132 133 134
		end
	end)

	-- same as above except `MovieFrame_OnEvent` and `MovieFrame_OnShow`
	-- cant be hooked directly so we can just use this
	-- this is called through `MovieFrame_OnEvent` on the event `PLAY_MOVIE`
135 136 137 138
	hooksecurefunc('MovieFrame_PlayMovie', function(s)
		if s and s.CloseDialog and not s.CloseDialog.template then
			s:SetScale(_G.UIParent:GetScale())
			s.CloseDialog:StripTextures()
Simpy's avatar
Simpy committed
139
			s.CloseDialog:CreateBackdrop('Transparent')
140 141
			S:HandleButton(s.CloseDialog.ConfirmButton)
			S:HandleButton(s.CloseDialog.ResumeButton)
142 143
		end
	end)
144

145 146 147 148 149 150 151 152 153 154 155 156 157 158
	do
		local ChatMenus = {
			_G.ChatMenu,
			_G.EmoteMenu,
			_G.LanguageMenu,
			_G.VoiceMacroMenu,
		}

		local menuBackdrop = function(s)
			s:SetTemplate('Transparent')
		end

		local chatMenuBackdrop = function(s)
			s:SetTemplate('Transparent')
159

160 161 162 163 164 165
			s:ClearAllPoints()
			s:Point('BOTTOMLEFT', _G.ChatFrame1, 'TOPLEFT', 0, 30)
		end

		for index, menu in ipairs(ChatMenus) do
			menu:StripTextures()
Merathilis's avatar
Merathilis committed
166

167 168 169 170 171
			if index == 1 then -- ChatMenu
				menu:HookScript('OnShow', chatMenuBackdrop)
			else
				menu:HookScript('OnShow', menuBackdrop)
			end
172 173 174 175
		end
	end

	--LFD Role Picker frame
176
	local roleButtons = {
177 178 179
		_G.LFDRoleCheckPopupRoleButtonTank,
		_G.LFDRoleCheckPopupRoleButtonDPS,
		_G.LFDRoleCheckPopupRoleButtonHealer,
180 181
	}

182
	_G.LFDRoleCheckPopup:StripTextures()
Simpy's avatar
Simpy committed
183
	_G.LFDRoleCheckPopup:CreateBackdrop('Transparent')
184 185
	S:HandleButton(_G.LFDRoleCheckPopupAcceptButton)
	S:HandleButton(_G.LFDRoleCheckPopupDeclineButton)
186

187 188
	for _, roleButton in pairs(roleButtons) do
		S:HandleCheckBox(roleButton.checkButton or roleButton.CheckButton, true)
Simpy's avatar
Simpy committed
189
		roleButton:DisableDrawLayer('OVERLAY')
190 191
	end

192
	-- reskin popup buttons
193
	for i = 1, 4 do
Simpy's avatar
Simpy committed
194 195
		local StaticPopup = _G['StaticPopup'..i]
		StaticPopup:HookScript('OnShow', function() -- UpdateRecapButton is created OnShow
196 197
			if StaticPopup.UpdateRecapButton and (not StaticPopup.UpdateRecapButtonHooked) then
				StaticPopup.UpdateRecapButtonHooked = true -- we should only hook this once
Simpy's avatar
Simpy committed
198
				hooksecurefunc(_G['StaticPopup'..i], 'UpdateRecapButton', S.UpdateRecapButton)
199 200
			end
		end)
Azilroka's avatar
Azilroka committed
201
		StaticPopup:StripTextures()
Simpy's avatar
Simpy committed
202
		StaticPopup:CreateBackdrop('Transparent')
Azilroka's avatar
Azilroka committed
203

204
		for j = 1, 4 do
Simpy's avatar
Simpy committed
205
			local button = StaticPopup['button'..j]
206 207 208 209 210
			S:HandleButton(button)

			button.Flash:Hide()
			button:CreateShadow(5)
			button.shadow:SetAlpha(0)
Simpy's avatar
Simpy committed
211
			button.shadow:SetBackdropBorderColor(unpack(E.media.rgbvaluecolor))
212
			button:SetFrameLevel(button:GetFrameLevel() + 1)
213 214 215 216

			local anim1, anim2 = button.PulseAnim:GetAnimations()
			anim1:SetTarget(button.shadow)
			anim2:SetTarget(button.shadow)
217
		end
Azilroka's avatar
Azilroka committed
218 219 220 221 222 223

		_G['StaticPopup'..i..'EditBox']:SetFrameLevel(_G['StaticPopup'..i..'EditBox']:GetFrameLevel()+1)
		S:HandleEditBox(_G['StaticPopup'..i..'EditBox'])
		S:HandleEditBox(_G['StaticPopup'..i..'MoneyInputFrameGold'])
		S:HandleEditBox(_G['StaticPopup'..i..'MoneyInputFrameSilver'])
		S:HandleEditBox(_G['StaticPopup'..i..'MoneyInputFrameCopper'])
224

Simpy's avatar
Simpy committed
225 226
		_G['StaticPopup'..i..'EditBox'].backdrop:Point('TOPLEFT', -2, -4)
		_G['StaticPopup'..i..'EditBox'].backdrop:Point('BOTTOMRIGHT', 2, 4)
Azilroka's avatar
Azilroka committed
227
		_G['StaticPopup'..i..'ItemFrameNameFrame']:Kill()
Simpy's avatar
Simpy committed
228
		_G['StaticPopup'..i..'ItemFrame']:CreateBackdrop()
Azilroka's avatar
Azilroka committed
229
		_G['StaticPopup'..i..'ItemFrame']:StyleButton()
Simpy's avatar
Simpy committed
230
		S:HandleIconBorder(_G['StaticPopup'..i..'ItemFrame'].IconBorder)
Azilroka's avatar
Azilroka committed
231 232
		_G['StaticPopup'..i..'ItemFrameIconTexture']:SetTexCoord(unpack(E.TexCoords))
		_G['StaticPopup'..i..'ItemFrameIconTexture']:SetInside()
233

Azilroka's avatar
Azilroka committed
234
		local normTex = _G['StaticPopup'..i..'ItemFrame']:GetNormalTexture()
235
		if normTex then
Simpy's avatar
Simpy committed
236
			normTex:SetTexture()
Simpy's avatar
Simpy committed
237
			hooksecurefunc(normTex, 'SetTexture', function(s, tex)
238
				if tex ~= nil then s:SetTexture() end
239
			end)
240 241 242 243 244
		end
	end

	-- skin return to graveyard button
	do
245 246 247 248 249
		_G.GhostFrameMiddle:SetAlpha(0)
		_G.GhostFrameRight:SetAlpha(0)
		_G.GhostFrameLeft:SetAlpha(0)
		_G.GhostFrame:StripTextures()
		_G.GhostFrame:ClearAllPoints()
Simpy's avatar
Simpy committed
250
		_G.GhostFrame:Point('TOP', E.UIParent, 'TOP', 0, -150)
Simpy's avatar
Simpy committed
251
		_G.GhostFrameContentsFrame:CreateBackdrop('Transparent')
Simpy's avatar
Simpy committed
252
		_G.GhostFrameContentsFrameText:Point('TOPLEFT', 53, 0)
253
		_G.GhostFrameContentsFrameIcon:SetTexCoord(unpack(E.TexCoords))
Simpy's avatar
Simpy committed
254
		_G.GhostFrameContentsFrameIcon:Point('RIGHT', _G.GhostFrameContentsFrameText, 'LEFT', -12, 0)
Simpy's avatar
Simpy committed
255
		local b = CreateFrame('Frame', nil, _G.GhostFrameContentsFrameIcon:GetParent(), 'BackdropTemplate')
Simpy's avatar
Simpy committed
256
		local p = E.PixelMode and 1 or 2
Simpy's avatar
Simpy committed
257 258 259
		b:Point('TOPLEFT', _G.GhostFrameContentsFrameIcon, -p, p)
		b:Point('BOTTOMRIGHT', _G.GhostFrameContentsFrameIcon, p, -p)
		_G.GhostFrameContentsFrameIcon:Size(37,38)
260
		_G.GhostFrameContentsFrameIcon:SetParent(b)
Simpy's avatar
Simpy committed
261
		b:CreateBackdrop()
262 263
	end

264
	_G.OpacityFrame:StripTextures()
Simpy's avatar
Simpy committed
265
	_G.OpacityFrame:CreateBackdrop('Transparent')
266

267
	--DropDownMenu
Simpy's avatar
Simpy committed
268 269
	hooksecurefunc('UIDropDownMenu_CreateFrames', function(level, index)
		local listFrame = _G['DropDownList'..level];
Elv's avatar
Elv committed
270
		local listFrameName = listFrame:GetName();
Simpy's avatar
Simpy committed
271
		local expandArrow = _G[listFrameName..'Button'..index..'ExpandArrow'];
Elv's avatar
Elv committed
272
		if expandArrow then
Simpy's avatar
Simpy committed
273
			local normTex = expandArrow:GetNormalTexture()
274
			expandArrow:SetNormalTexture(E.Media.Textures.ArrowUp)
Simpy's avatar
Simpy committed
275 276
			normTex:SetVertexColor(unpack(E.media.rgbvaluecolor))
			normTex:SetRotation(S.ArrowRotation.right)
Simpy's avatar
Simpy committed
277
			expandArrow:Size(12, 12)
Elv's avatar
Elv committed
278
		end
Azilroka's avatar
Azilroka committed
279

Simpy's avatar
Simpy committed
280
		local Backdrop = _G[listFrameName..'Backdrop']
Simpy's avatar
Simpy committed
281
		if not Backdrop.template then Backdrop:StripTextures() end
Simpy's avatar
Simpy committed
282
		Backdrop:CreateBackdrop('Transparent')
Simpy's avatar
Simpy committed
283

Simpy's avatar
Simpy committed
284
		local menuBackdrop = _G[listFrameName..'MenuBackdrop']
Simpy's avatar
Simpy committed
285
		if not menuBackdrop.template then menuBackdrop:StripTextures() end
Simpy's avatar
Simpy committed
286
		menuBackdrop:CreateBackdrop('Transparent')
Elv's avatar
Elv committed
287 288
	end)

Simpy's avatar
Simpy committed
289 290
	hooksecurefunc('UIDropDownMenu_SetIconImage', function(icon, texture)
		if texture:find('Divider') then
Elv's avatar
Elv committed
291 292
			local r, g, b = unpack(E.media.rgbvaluecolor)
			icon:SetColorTexture(r, g, b, 0.45)
Simpy's avatar
Simpy committed
293
			icon:Height(1)
Elv's avatar
Elv committed
294 295 296
		end
	end)

Simpy's avatar
Simpy committed
297
	hooksecurefunc('ToggleDropDownMenu', function(level)
Simpy's avatar
:broom:  
Simpy committed
298
		if not level then
Elv's avatar
Elv committed
299 300 301 302 303
			level = 1;
		end

		local r, g, b = unpack(E.media.rgbvaluecolor)

Simpy's avatar
Simpy committed
304
		for i = 1, _G.UIDROPDOWNMENU_MAXBUTTONS do
Simpy's avatar
Simpy committed
305 306 307 308
			local button = _G['DropDownList'..level..'Button'..i]
			local check = _G['DropDownList'..level..'Button'..i..'Check']
			local uncheck = _G['DropDownList'..level..'Button'..i..'UnCheck']
			local highlight = _G['DropDownList'..level..'Button'..i..'Highlight']
Elv's avatar
Elv committed
309

310 311
			highlight:SetTexture(E.Media.Textures.Highlight)
			highlight:SetBlendMode('BLEND')
Azilroka's avatar
Azilroka committed
312 313 314
			highlight:SetDrawLayer('BACKGROUND')
			highlight:SetVertexColor(r, g, b)

Elv's avatar
Elv committed
315 316 317 318
			if not button.backdrop then
				button:CreateBackdrop()
			end

Elv's avatar
Elv committed
319
			button.backdrop:Hide()
Elv's avatar
Elv committed
320 321

			if not button.notCheckable then
Simpy's avatar
Simpy committed
322
				uncheck:SetTexture()
Elv's avatar
Elv committed
323 324
				local _, co = check:GetTexCoord()
				if co == 0 then
Simpy's avatar
Simpy committed
325
					check:SetTexture([[Interface\Buttons\UI-CheckBox-Check]])
Elv's avatar
Elv committed
326
					check:SetVertexColor(r, g, b, 1)
Simpy's avatar
Simpy committed
327
					check:Size(20, 20)
Elv's avatar
Elv committed
328 329 330 331 332
					check:SetDesaturated(true)
					button.backdrop:SetInside(check, 4, 4)
				else
					check:SetTexture(E.media.normTex)
					check:SetVertexColor(r, g, b, 1)
Simpy's avatar
Simpy committed
333
					check:Size(10, 10)
Elv's avatar
Elv committed
334 335 336 337 338
					check:SetDesaturated(false)
					button.backdrop:SetOutside(check)
				end

				button.backdrop:Show()
339
				check:SetTexCoord(0, 1, 0, 1)
Elv's avatar
Elv committed
340
			else
Simpy's avatar
Simpy committed
341
				check:Size(16, 16)
Elv's avatar
Elv committed
342 343 344 345
			end
		end
	end)

346
	local SideDressUpFrame = _G.SideDressUpFrame
347
	S:HandleCloseButton(_G.SideDressUpFrameCloseButton)
Simpy's avatar
Simpy committed
348 349
	S:HandleButton(_G.SideDressUpFrame.ResetButton)
	_G.SideDressUpFrame.ResetButton:SetFrameLevel(_G.SideDressUpFrame.ResetButton:GetFrameLevel()+1)
350 351 352
	SideDressUpFrame:StripTextures()
	SideDressUpFrame.BGTopLeft:Hide()
	SideDressUpFrame.BGBottomLeft:Hide()
Simpy's avatar
Simpy committed
353
	SideDressUpFrame:CreateBackdrop('Transparent')
354

355
	-- StackSplit
356
	local StackSplitFrame = _G.StackSplitFrame
Merathilis's avatar
Merathilis committed
357
	StackSplitFrame:StripTextures()
Simpy's avatar
Simpy committed
358
	StackSplitFrame:CreateBackdrop('Transparent')
359

Simpy's avatar
Simpy committed
360 361
	StackSplitFrame.bg1 = CreateFrame('Frame', nil, StackSplitFrame, 'BackdropTemplate')
	StackSplitFrame.bg1:SetTemplate('Transparent')
Simpy's avatar
Simpy committed
362 363
	StackSplitFrame.bg1:Point('TOPLEFT', 10, -15)
	StackSplitFrame.bg1:Point('BOTTOMRIGHT', -10, 55)
364 365
	StackSplitFrame.bg1:SetFrameLevel(StackSplitFrame.bg1:GetFrameLevel() - 1)

Merathilis's avatar
Merathilis committed
366 367 368
	S:HandleButton(StackSplitFrame.OkayButton)
	S:HandleButton(StackSplitFrame.CancelButton)

Merathilis's avatar
Merathilis committed
369 370
	local buttons = {StackSplitFrame.LeftButton, StackSplitFrame.RightButton}
	for _, btn in pairs(buttons) do
Simpy's avatar
Simpy committed
371
		btn:Size(14, 18)
Merathilis's avatar
Merathilis committed
372
		btn:ClearAllPoints()
Azilroka's avatar
Azilroka committed
373

Merathilis's avatar
Merathilis committed
374
		if btn == StackSplitFrame.LeftButton then
Simpy's avatar
Simpy committed
375
			btn:Point('LEFT', StackSplitFrame.bg1, 'LEFT', 4, 0)
Merathilis's avatar
Merathilis committed
376
		else
Simpy's avatar
Simpy committed
377
			btn:Point('RIGHT', StackSplitFrame.bg1, 'RIGHT', -4, 0)
Merathilis's avatar
Merathilis committed
378
		end
Simpy's avatar
Simpy committed
379

380
		S:HandleNextPrevButton(btn, nil, nil, true)
Merathilis's avatar
Merathilis committed
381 382
	end

383
	--NavBar Buttons (Used in WorldMapFrame, EncounterJournal and HelpFrame)
Simpy's avatar
Simpy committed
384
	hooksecurefunc('NavBar_AddButton', SkinNavBarButtons)
385 386
end

Simpy's avatar
Simpy committed
387
S:AddCallback('BlizzardMiscFrames')