Util.lua

Util.lua (8.2.5.32722; unchanged since 8.2.5.31960)
  1. function CanAccessObject(obj)
  2. return issecure() or not obj:IsForbidden();
  3. end
  4. function GetTextureInfo(obj)
  5. if obj:GetObjectType() == "Texture" then
  6. local assetName = obj:GetAtlas();
  7. local assetType = "Atlas";
  8. if not assetName then
  9. assetName = obj:GetTextureFilePath();
  10. assetType = "File";
  11. end
  12. if not assetName then
  13. assetName = obj:GetTextureFileID();
  14. assetType = "FileID";
  15. end
  16. if not assetName then
  17. assetName = "UnknownAsset";
  18. assetType = "Unknown";
  19. end
  20. local ulX, ulY, blX, blY, urX, urY, brX, brY = obj:GetTexCoord();
  21. return assetName, assetType, ulX, ulY, blX, blY, urX, urY, brX, brY;
  22. end
  23. end
  24. function CalculateDistanceBetweenRegions(regionA, regionB)
  25. local ax, ay = regionA:GetCenter();
  26. local bx, by = regionB:GetCenter();
  27. if ax and bx then
  28. local dx, dy = bx - ax, by - ay;
  29. return math.sqrt(dx * dx + dy * dy);
  30. else
  31. return 0;
  32. end
  33. end
  34. CLASS_ICON_TCOORDS = {
  35. ["WARRIOR"] = {0, 0.25, 0, 0.25},
  36. ["MAGE"] = {0.25, 0.49609375, 0, 0.25},
  37. ["ROGUE"] = {0.49609375, 0.7421875, 0, 0.25},
  38. ["DRUID"] = {0.7421875, 0.98828125, 0, 0.25},
  39. ["HUNTER"] = {0, 0.25, 0.25, 0.5},
  40. ["SHAMAN"] = {0.25, 0.49609375, 0.25, 0.5},
  41. ["PRIEST"] = {0.49609375, 0.7421875, 0.25, 0.5},
  42. ["WARLOCK"] = {0.7421875, 0.98828125, 0.25, 0.5},
  43. ["PALADIN"] = {0, 0.25, 0.5, 0.75},
  44. ["DEATHKNIGHT"] = {0.25, .5, 0.5, .75},
  45. ["MONK"] = {0.5, 0.73828125, 0.5, .75},
  46. ["DEMONHUNTER"] = {0.7421875, 0.98828125, 0.5, 0.75},
  47. };
  48. function SetClampedTextureRotation(texture, rotationDegrees)
  49. if (rotationDegrees ~= 0 and rotationDegrees ~= 90 and rotationDegrees ~= 180 and rotationDegrees ~= 270) then
  50. error("SetRotation: rotationDegrees must be 0, 90, 180, or 270");
  51. return;
  52. end
  53. if not (texture.rotationDegrees) then
  54. texture.origTexCoords = {texture:GetTexCoord()};
  55. texture.origWidth = texture:GetWidth();
  56. texture.origHeight = texture:GetHeight();
  57. end
  58. if (texture.rotationDegrees == rotationDegrees) then
  59. return;
  60. end
  61. texture.rotationDegrees = rotationDegrees;
  62. if (rotationDegrees == 0 or rotationDegrees == 180) then
  63. texture:SetWidth(texture.origWidth);
  64. texture:SetHeight(texture.origHeight);
  65. else
  66. texture:SetWidth(texture.origHeight);
  67. texture:SetHeight(texture.origWidth);
  68. end
  69. if (rotationDegrees == 0) then
  70. texture:SetTexCoord( texture.origTexCoords[1], texture.origTexCoords[2],
  71. texture.origTexCoords[3], texture.origTexCoords[4],
  72. texture.origTexCoords[5], texture.origTexCoords[6],
  73. texture.origTexCoords[7], texture.origTexCoords[8] );
  74. elseif (rotationDegrees == 90) then
  75. texture:SetTexCoord( texture.origTexCoords[3], texture.origTexCoords[4],
  76. texture.origTexCoords[7], texture.origTexCoords[8],
  77. texture.origTexCoords[1], texture.origTexCoords[2],
  78. texture.origTexCoords[5], texture.origTexCoords[6] );
  79. elseif (rotationDegrees == 180) then
  80. texture:SetTexCoord( texture.origTexCoords[7], texture.origTexCoords[8],
  81. texture.origTexCoords[5], texture.origTexCoords[6],
  82. texture.origTexCoords[3], texture.origTexCoords[4],
  83. texture.origTexCoords[1], texture.origTexCoords[2] );
  84. elseif (rotationDegrees == 270) then
  85. texture:SetTexCoord( texture.origTexCoords[5], texture.origTexCoords[6],
  86. texture.origTexCoords[1], texture.origTexCoords[2],
  87. texture.origTexCoords[7], texture.origTexCoords[8],
  88. texture.origTexCoords[3], texture.origTexCoords[4] );
  89. end
  90. end
  91. function ClearClampedTextureRotation(texture)
  92. if (texture.rotationDegrees) then
  93. SetClampedTextureRotation(0);
  94. texture.origTexCoords = nil;
  95. texture.origWidth = nil;
  96. texture.origHeight = nil;
  97. end
  98. end
  99. function GetTexCoordsByGrid(xOffset, yOffset, textureWidth, textureHeight, gridWidth, gridHeight)
  100. local widthPerGrid = gridWidth/textureWidth;
  101. local heightPerGrid = gridHeight/textureHeight;
  102. return (xOffset-1)*widthPerGrid, (xOffset)*widthPerGrid, (yOffset-1)*heightPerGrid, (yOffset)*heightPerGrid;
  103. end
  104. function GetTexCoordsForRole(role)
  105. local textureHeight, textureWidth = 256, 256;
  106. local roleHeight, roleWidth = 67, 67;
  107. if ( role == "GUIDE" ) then
  108. return GetTexCoordsByGrid(1, 1, textureWidth, textureHeight, roleWidth, roleHeight);
  109. elseif ( role == "TANK" ) then
  110. return GetTexCoordsByGrid(1, 2, textureWidth, textureHeight, roleWidth, roleHeight);
  111. elseif ( role == "HEALER" ) then
  112. return GetTexCoordsByGrid(2, 1, textureWidth, textureHeight, roleWidth, roleHeight);
  113. elseif ( role == "DAMAGER" ) then
  114. return GetTexCoordsByGrid(2, 2, textureWidth, textureHeight, roleWidth, roleHeight);
  115. else
  116. error("Unknown role: "..tostring(role));
  117. end
  118. end
  119. function ConvertPixelsToUI(pixels, frameScale)
  120. local physicalScreenHeight = select(2, GetPhysicalScreenSize());
  121. return (pixels * 768.0)/(physicalScreenHeight * frameScale);
  122. end
  123. function ReloadUI()
  124. C_UI.Reload();
  125. end
  126. function tDeleteItem(tbl, item)
  127. local index = 1;
  128. while tbl[index] do
  129. if ( item == tbl[index] ) then
  130. tremove(tbl, index);
  131. else
  132. index = index + 1;
  133. end
  134. end
  135. end
  136. function tIndexOf(tbl, item)
  137. for i, v in ipairs(tbl) do
  138. if item == v then
  139. return i;
  140. end
  141. end
  142. end
  143. function tContains(tbl, item)
  144. return tIndexOf(tbl, item) ~= nil;
  145. end
  146. function tInvert(tbl)
  147. local inverted = {};
  148. for k, v in pairs(tbl) do
  149. inverted[v] = k;
  150. end
  151. return inverted;
  152. end
  153. function tFilter(tbl, pred, isIndexTable)
  154. local out = {};
  155. if (isIndexTable) then
  156. local currentIndex = 1;
  157. for i, v in ipairs(tbl) do
  158. if (pred(v)) then
  159. out[currentIndex] = v;
  160. currentIndex = currentIndex + 1;
  161. end
  162. end
  163. else
  164. for k, v in pairs(tbl) do
  165. if (pred(v)) then
  166. out[k] = v;
  167. end
  168. end
  169. end
  170. return out;
  171. end
  172. function CopyTable(settings)
  173. local copy = {};
  174. for k, v in pairs(settings) do
  175. if ( type(v) == "table" ) then
  176. copy[k] = CopyTable(v);
  177. else
  178. copy[k] = v;
  179. end
  180. end
  181. return copy;
  182. end
  183. function FindInTableIf(tbl, pred)
  184. for k, v in pairs(tbl) do
  185. if (pred(v)) then
  186. return k, v;
  187. end
  188. end
  189. return nil;
  190. end
  191. function ExtractHyperlinkString(linkString)
  192. local preString, hyperlinkString, postString = linkString:match("^(.*)|H(.+)|h(.*)$");
  193. return preString ~= nil, preString, hyperlinkString, postString;
  194. end
  195. function ExtractLinkData(link)
  196. return string.match(link, "(.-):(.*)");
  197. end
  198. function ExtractQuestRewardID(linkString)
  199. return linkString:match("^questreward:(%d+)$");
  200. end
  201. function SplitTextIntoLines(text, delimiter)
  202. local lines = {};
  203. local startIndex = 1;
  204. local foundIndex = string.find(text, delimiter);
  205. while foundIndex do
  206. table.insert(lines, text:sub(startIndex, foundIndex - 1));
  207. startIndex = foundIndex + 2;
  208. foundIndex = string.find(text, delimiter, startIndex);
  209. end
  210. if startIndex <= #text then
  211. table.insert(lines, text:sub(startIndex));
  212. end
  213. return lines;
  214. end
  215. function SplitTextIntoHeaderAndNonHeader(text)
  216. local foundIndex = string.find(text, "|n");
  217. if not foundIndex then
  218. -- There was no newline...the whole thing is a header
  219. return text;
  220. elseif #text == 2 then
  221. -- There was a newline, but that was all that was in the string.
  222. return nil;
  223. elseif foundIndex == 1 then
  224. -- There was a newline at the very beginning...the whole rest of the string is a header
  225. return text:sub(3);
  226. elseif foundIndex == #text - 1 then
  227. -- There was a newline at the very end...the whole rest of the string is a header
  228. return text:sub(1, foundIndex - 1);
  229. else
  230. -- There was a newline somewhere in the middle...everything before it is the header and everything after it is the non-header
  231. return text:sub(1, foundIndex - 1), text:sub(foundIndex + 2);
  232. end
  233. end
  234. function GetItemInfoFromHyperlink(link)
  235. local strippedItemLink, itemID = link:match("|Hitem:((%d+).-)|h");
  236. if itemID then
  237. return tonumber(itemID), strippedItemLink;
  238. end
  239. end
  240. function GetAchievementInfoFromHyperlink(link)
  241. return tonumber(link:match("|Hachievement:(%d+)"));
  242. end
  243. function FormatValueWithSign(value)
  244. local formatString = value < 0 and SYMBOLIC_NEGATIVE_NUMBER or SYMBOLIC_POSITIVE_NUMBER;
  245. return formatString:format(math.abs(value));
  246. end
  247. function GetPlayerGuid()
  248. return UnitGUID("player");
  249. end
  250. function IsPlayerGuid(guid)
  251. return guid == GetPlayerGuid();
  252. end
  253. function FormatLargeNumber(amount)
  254. amount = tostring(amount);
  255. local newDisplay = "";
  256. local strlen = amount:len();
  257. --Add each thing behind a comma
  258. for i=4, strlen, 3 do
  259. newDisplay = LARGE_NUMBER_SEPERATOR..amount:sub(-(i - 1), -(i - 3))..newDisplay;
  260. end
  261. --Add everything before the first comma
  262. newDisplay = amount:sub(1, (strlen % 3 == 0) and 3 or (strlen % 3))..newDisplay;
  263. return newDisplay;
  264. end
  265. COPPER_PER_SILVER = 100;
  266. SILVER_PER_GOLD = 100;
  267. COPPER_PER_GOLD = COPPER_PER_SILVER * SILVER_PER_GOLD;
  268. function GetMoneyString(money, separateThousands)
  269. local goldString, silverString, copperString;
  270. local gold = floor(money / (COPPER_PER_SILVER * SILVER_PER_GOLD));
  271. local silver = floor((money - (gold * COPPER_PER_SILVER * SILVER_PER_GOLD)) / COPPER_PER_SILVER);
  272. local copper = mod(money, COPPER_PER_SILVER);
  273. if ( ENABLE_COLORBLIND_MODE == "1" ) then
  274. if (separateThousands) then
  275. goldString = FormatLargeNumber(gold)..GOLD_AMOUNT_SYMBOL;
  276. else
  277. goldString = gold..GOLD_AMOUNT_SYMBOL;
  278. end
  279. silverString = silver..SILVER_AMOUNT_SYMBOL;
  280. copperString = copper..COPPER_AMOUNT_SYMBOL;
  281. else
  282. if (separateThousands) then
  283. goldString = GOLD_AMOUNT_TEXTURE_STRING:format(FormatLargeNumber(gold), 0, 0);
  284. else
  285. goldString = GOLD_AMOUNT_TEXTURE:format(gold, 0, 0);
  286. end
  287. silverString = SILVER_AMOUNT_TEXTURE:format(silver, 0, 0);
  288. copperString = COPPER_AMOUNT_TEXTURE:format(copper, 0, 0);
  289. end
  290. local moneyString = "";
  291. local separator = "";
  292. if ( gold > 0 ) then
  293. moneyString = goldString;
  294. separator = " ";
  295. end
  296. if ( silver > 0 ) then
  297. moneyString = moneyString..separator..silverString;
  298. separator = " ";
  299. end
  300. if ( copper > 0 or moneyString == "" ) then
  301. moneyString = moneyString..separator..copperString;
  302. end
  303. return moneyString;
  304. end
  305. function Lerp(startValue, endValue, amount)
  306. return (1 - amount) * startValue + amount * endValue;
  307. end
  308. function Clamp(value, min, max)
  309. if value > max then
  310. return max;
  311. elseif value < min then
  312. return min;
  313. end
  314. return value;
  315. end
  316. function Saturate(value)
  317. return Clamp(value, 0.0, 1.0);
  318. end
  319. function Wrap(value, max)
  320. return (value - 1) % max + 1;
  321. end
  322. function ClampDegrees(value)
  323. return ClampMod(value, 360);
  324. end
  325. function ClampMod(value, mod)
  326. return ((value % mod) + mod) % mod;
  327. end
  328. function NegateIf(value, condition)
  329. return condition and -value or value;
  330. end
  331. function PercentageBetween(value, startValue, endValue)
  332. if startValue == endValue then
  333. return 0.0;
  334. end
  335. return (value - startValue) / (endValue - startValue);
  336. end
  337. function ClampedPercentageBetween(value, startValue, endValue)
  338. return Saturate(PercentageBetween(value, startValue, endValue));
  339. end
  340. local TARGET_FRAME_PER_SEC = 60.0;
  341. function DeltaLerp(startValue, endValue, amount, timeSec)
  342. return Lerp(startValue, endValue, Saturate(amount * timeSec * TARGET_FRAME_PER_SEC));
  343. end
  344. function FrameDeltaLerp(startValue, endValue, amount)
  345. return DeltaLerp(startValue, endValue, amount, GetTickTime());
  346. end
  347. function RandomFloatInRange(minValue, maxValue)
  348. return Lerp(minValue, maxValue, math.random());
  349. end
  350. function GetNavigationButtonEnabledStates(count, index)
  351. -- Returns indicate whether navigation for "previous" and "next" should be enabled, respectively.
  352. if count > 1 then
  353. return index > 1, index < count;
  354. end
  355. return false, false;
  356. end
  357. ----------------------------------
  358. -- TRIAL/VETERAN FUNCTIONS
  359. ----------------------------------
  360. function GameLimitedMode_IsActive()
  361. return IsTrialAccount() or IsVeteranTrialAccount();
  362. end
  363. function TriStateCheckbox_SetState(checked, checkButton)
  364. local checkedTexture = _G[checkButton:GetName().."CheckedTexture"];
  365. if ( not checkedTexture ) then
  366. message("Can't find checked texture");
  367. end
  368. if ( not checked or checked == 0 ) then
  369. -- nil or 0 means not checked
  370. checkButton:SetChecked(false);
  371. checkButton.state = 0;
  372. elseif ( checked == 2 ) then
  373. -- 2 is a normal
  374. checkButton:SetChecked(true);
  375. checkedTexture:SetVertexColor(1, 1, 1);
  376. checkedTexture:SetDesaturated(false);
  377. checkButton.state = 2;
  378. else
  379. -- 1 is a gray check
  380. checkButton:SetChecked(true);
  381. checkedTexture:SetDesaturated(true);
  382. checkButton.state = 1;
  383. end
  384. end
  385. RectangleMixin = {};
  386. function CreateRectangle(left, right, top, bottom)
  387. local rectangle = CreateFromMixins(RectangleMixin);
  388. rectangle:OnLoad(left, right, top, bottom);
  389. return rectangle;
  390. end
  391. function RectangleMixin:OnLoad(left, right, top, bottom)
  392. self:SetSides(left or 0.0, right or 0.0, top or 0.0, bottom or 0.0);
  393. end
  394. function RectangleMixin:SetSides(left, right, top, bottom)
  395. self.left = left;
  396. self.right = right;
  397. self.top = top;
  398. self.bottom = bottom;
  399. end
  400. function RectangleMixin:Reset()
  401. self.left = 0.0;
  402. self.right = 0.0;
  403. self.top = 0.0;
  404. self.bottom = 0.0;
  405. end
  406. function RectangleMixin:Stretch(x, y)
  407. self:Adjust(-x, x, -y, y);
  408. end
  409. function RectangleMixin:Move(x, y)
  410. self:Adjust(x, x, y, y);
  411. end
  412. function RectangleMixin:Adjust(left, right, top, bottom)
  413. self.left = self.left + left;
  414. self.right = self.right + right;
  415. self.top = self.top + top;
  416. self.bottom = self.bottom + bottom;
  417. end
  418. function RectangleMixin:IsEmpty()
  419. return self.left == self.right or self.top == self.bottom;
  420. end
  421. function RectangleMixin:IsInsideOut()
  422. return self.left > self.right or self.top > self.bottom;
  423. end
  424. function RectangleMixin:EnclosesPoint(x, y)
  425. return x >= self.left and x <= self.right and y >= self.top and y <= self.bottom;
  426. end
  427. function RectangleMixin:EnclosesRect(otherRect)
  428. return self:EnclosesPoint(otherRect:GetLeft(), otherRect:GetTop()) and self:EnclosesPoint(otherRect:GetRight(), otherRect:GetBottom());
  429. end
  430. function RectangleMixin:IntersectsRect(otherRect)
  431. return not (
  432. self.left > otherRect.right or
  433. self.right < otherRect.left or
  434. self.top > otherRect.bottom or
  435. self.bottom < otherRect.top
  436. );
  437. end
  438. function RectangleMixin:GetTop()
  439. return self.top;
  440. end
  441. function RectangleMixin:GetBottom()
  442. return self.bottom;
  443. end
  444. function RectangleMixin:GetLeft()
  445. return self.left;
  446. end
  447. function RectangleMixin:GetRight()
  448. return self.right;
  449. end
  450. function RectangleMixin:GetWidth()
  451. return self.right - self.left;
  452. end
  453. function RectangleMixin:GetHeight()
  454. return self.bottom - self.top;
  455. end
  456. function RectangleMixin:GetCenter()
  457. return Lerp(self.left, self.right, .5), Lerp(self.top, self.bottom, .5);
  458. end
  459. function RectangleMixin:SetTop(top)
  460. self.top = top;
  461. end
  462. function RectangleMixin:SetBottom(bottom)
  463. self.bottom = bottom;
  464. end
  465. function RectangleMixin:SetLeft(left)
  466. self.left = left;
  467. end
  468. function RectangleMixin:SetRight(right)
  469. self.right = right;
  470. end
  471. function RectangleMixin:SetWidth(width)
  472. self.right = self.left + width;
  473. end
  474. function RectangleMixin:SetHeight(height)
  475. self.bottom = self.top + height;
  476. end
  477. function RectangleMixin:SetSize(width, height)
  478. self:SetWidth(width);
  479. self:SetHeight(height);
  480. end
  481. function RectangleMixin:SetCenter(x, y)
  482. local width = self:GetWidth();
  483. local height = self:GetHeight();
  484. self.left = x - width * .5;
  485. self.right = x + width * .5;
  486. self.top = y - height * .5;
  487. self.bottom = y + height * .5;
  488. end
  489. local g_updatingBars = {};
  490. local function IsCloseEnough(bar, newValue, targetValue)
  491. local min, max = bar:GetMinMaxValues();
  492. local range = max - min;
  493. if range > 0.0 then
  494. return math.abs((newValue - targetValue) / range) < .00001;
  495. end
  496. return true;
  497. end
  498. local function ProcessSmoothStatusBars()
  499. for bar, targetValue in pairs(g_updatingBars) do
  500. local effectiveTargetValue = Clamp(targetValue, bar:GetMinMaxValues());
  501. local newValue = FrameDeltaLerp(bar:GetValue(), effectiveTargetValue, .25);
  502. if IsCloseEnough(bar, newValue, effectiveTargetValue) then
  503. g_updatingBars[bar] = nil;
  504. bar:SetValue(effectiveTargetValue);
  505. else
  506. bar:SetValue(newValue);
  507. end
  508. end
  509. end
  510. C_Timer.NewTicker(0, ProcessSmoothStatusBars);
  511. SmoothStatusBarMixin = {};
  512. function SmoothStatusBarMixin:ResetSmoothedValue(value) --If nil, tries to set to the last target value
  513. local targetValue = g_updatingBars[self];
  514. if targetValue then
  515. g_updatingBars[self] = nil;
  516. self:SetValue(value or targetValue);
  517. elseif value then
  518. self:SetValue(value);
  519. end
  520. end
  521. function SmoothStatusBarMixin:SetSmoothedValue(value)
  522. g_updatingBars[self] = value;
  523. end
  524. function SmoothStatusBarMixin:SetMinMaxSmoothedValue(min, max)
  525. self:SetMinMaxValues(min, max);
  526. local targetValue = g_updatingBars[self];
  527. if targetValue then
  528. local ratio = 1;
  529. if max ~= 0 and self.lastSmoothedMax and self.lastSmoothedMax ~= 0 then
  530. ratio = max / self.lastSmoothedMax;
  531. end
  532. g_updatingBars[self] = targetValue * ratio;
  533. end
  534. self.lastSmoothedMin = min;
  535. self.lastSmoothedMax = max;
  536. end
  537. function WrapTextInColorCode(text, colorHexString)
  538. return ("|c%s%s|r"):format(colorHexString, text);
  539. end
  540. ColorMixin = {};
  541. function CreateColor(r, g, b, a)
  542. local color = CreateFromMixins(ColorMixin);
  543. color:OnLoad(r, g, b, a);
  544. return color;
  545. end
  546. local function ExtractHexByte(str, index)
  547. return tonumber(str:sub(index, index + 1), 16);
  548. end
  549. function CreateColorFromHexString(hexColor)
  550. if #hexColor == 8 then
  551. local a, r, g, b = ExtractHexByte(hexColor, 1), ExtractHexByte(hexColor, 3), ExtractHexByte(hexColor, 5), ExtractHexByte(hexColor, 7);
  552. return CreateColor(r, g, b, a);
  553. else
  554. GMError("CreateColorFromHexString input must be hexadecimal digits in this format: AARRGGBB.");
  555. end
  556. end
  557. function CreateColorFromBytes(r, g, b, a)
  558. return CreateColor(r / 255, g / 255, b / 255, a / 255);
  559. end
  560. function AreColorsEqual(left, right)
  561. if left and right then
  562. return left:IsEqualTo(right);
  563. end
  564. return left == right;
  565. end
  566. function ColorMixin:OnLoad(r, g, b, a)
  567. self:SetRGBA(r, g, b, a);
  568. end
  569. function ColorMixin:IsEqualTo(otherColor)
  570. return self.r == otherColor.r
  571. and self.g == otherColor.g
  572. and self.b == otherColor.b
  573. and self.a == otherColor.a;
  574. end
  575. function ColorMixin:GetRGB()
  576. return self.r, self.g, self.b;
  577. end
  578. function ColorMixin:GetRGBAsBytes()
  579. return self.r * 255, self.g * 255, self.b * 255;
  580. end
  581. function ColorMixin:GetRGBA()
  582. return self.r, self.g, self.b, self.a;
  583. end
  584. function ColorMixin:GetRGBAAsBytes()
  585. return self.r * 255, self.g * 255, self.b * 255, (self.a or 1) * 255;
  586. end
  587. function ColorMixin:SetRGBA(r, g, b, a)
  588. self.r = r;
  589. self.g = g;
  590. self.b = b;
  591. self.a = a;
  592. end
  593. function ColorMixin:SetRGB(r, g, b)
  594. self:SetRGBA(r, g, b, nil);
  595. end
  596. function ColorMixin:GenerateHexColor()
  597. return ("ff%.2x%.2x%.2x"):format(self:GetRGBAsBytes());
  598. end
  599. function ColorMixin:GenerateHexColorMarkup()
  600. return "|c"..self:GenerateHexColor();
  601. end
  602. function ColorMixin:WrapTextInColorCode(text)
  603. return WrapTextInColorCode(text, self:GenerateHexColor());
  604. end
  605. RAID_CLASS_COLORS = {};
  606. do
  607. local classes = {"HUNTER", "WARLOCK", "PRIEST", "PALADIN", "MAGE", "ROGUE", "DRUID", "SHAMAN", "WARRIOR", "DEATHKNIGHT", "MONK", "DEMONHUNTER"};
  608. for i, className in ipairs(classes) do
  609. RAID_CLASS_COLORS[className] = C_ClassColor.GetClassColor(className);
  610. end
  611. end
  612. for k, v in pairs(RAID_CLASS_COLORS) do
  613. v.colorStr = v:GenerateHexColor();
  614. end
  615. function GetClassColor(classFilename)
  616. local color = RAID_CLASS_COLORS[classFilename];
  617. if color then
  618. return color.r, color.g, color.b, color.colorStr;
  619. end
  620. return 1, 1, 1, "ffffffff";
  621. end
  622. function GetClassColorObj(classFilename)
  623. -- TODO: Remove this, convert everything that's using GetClassColor to use the object instead, then begin using that again
  624. return RAID_CLASS_COLORS[classFilename];
  625. end
  626. function GetClassColoredTextForUnit(unit, text)
  627. local classFilename = select(2, UnitClass(unit));
  628. local color = GetClassColorObj(classFilename);
  629. return color:WrapTextInColorCode(text);
  630. end
  631. function GetFactionColor(factionGroupTag)
  632. return PLAYER_FACTION_COLORS[PLAYER_FACTION_GROUP[factionGroupTag]];
  633. end
  634. -- Time --
  635. function SecondsToClock(seconds, displayZeroHours)
  636. seconds = math.max(seconds, 0);
  637. local hours = math.floor(seconds / 3600);
  638. seconds = seconds - (hours * 3600);
  639. local minutes = math.floor(seconds / 60);
  640. seconds = seconds % 60;
  641. if hours > 0 or displayZeroHours then
  642. return format(HOURS_MINUTES_SECONDS, hours, minutes, seconds);
  643. else
  644. return format(MINUTES_SECONDS, minutes, seconds);
  645. end
  646. end
  647. function SecondsToTime(seconds, noSeconds, notAbbreviated, maxCount, roundUp)
  648. local time = "";
  649. local count = 0;
  650. local tempTime;
  651. seconds = roundUp and ceil(seconds) or floor(seconds);
  652. maxCount = maxCount or 2;
  653. if ( seconds >= 86400 ) then
  654. count = count + 1;
  655. if ( count == maxCount and roundUp ) then
  656. tempTime = ceil(seconds / 86400);
  657. else
  658. tempTime = floor(seconds / 86400);
  659. end
  660. if ( notAbbreviated ) then
  661. time = D_DAYS:format(tempTime);
  662. else
  663. time = DAYS_ABBR:format(tempTime);
  664. end
  665. seconds = mod(seconds, 86400);
  666. end
  667. if ( count < maxCount and seconds >= 3600 ) then
  668. count = count + 1;
  669. if ( time ~= "" ) then
  670. time = time..TIME_UNIT_DELIMITER;
  671. end
  672. if ( count == maxCount and roundUp ) then
  673. tempTime = ceil(seconds / 3600);
  674. else
  675. tempTime = floor(seconds / 3600);
  676. end
  677. if ( notAbbreviated ) then
  678. time = time..D_HOURS:format(tempTime);
  679. else
  680. time = time..HOURS_ABBR:format(tempTime);
  681. end
  682. seconds = mod(seconds, 3600);
  683. end
  684. if ( count < maxCount and seconds >= 60 ) then
  685. count = count + 1;
  686. if ( time ~= "" ) then
  687. time = time..TIME_UNIT_DELIMITER;
  688. end
  689. if ( count == maxCount and roundUp ) then
  690. tempTime = ceil(seconds / 60);
  691. else
  692. tempTime = floor(seconds / 60);
  693. end
  694. if ( notAbbreviated ) then
  695. time = time..D_MINUTES:format(tempTime);
  696. else
  697. time = time..MINUTES_ABBR:format(tempTime);
  698. end
  699. seconds = mod(seconds, 60);
  700. end
  701. if ( count < maxCount and seconds > 0 and not noSeconds ) then
  702. if ( time ~= "" ) then
  703. time = time..TIME_UNIT_DELIMITER;
  704. end
  705. if ( notAbbreviated ) then
  706. time = time..D_SECONDS:format(seconds);
  707. else
  708. time = time..SECONDS_ABBR:format(seconds);
  709. end
  710. end
  711. return time;
  712. end
  713. function SecondsToTimeAbbrev(seconds)
  714. local tempTime;
  715. if ( seconds >= 86400 ) then
  716. tempTime = ceil(seconds / 86400);
  717. return DAY_ONELETTER_ABBR, tempTime;
  718. end
  719. if ( seconds >= 3600 ) then
  720. tempTime = ceil(seconds / 3600);
  721. return HOUR_ONELETTER_ABBR, tempTime;
  722. end
  723. if ( seconds >= 60 ) then
  724. tempTime = ceil(seconds / 60);
  725. return MINUTE_ONELETTER_ABBR, tempTime;
  726. end
  727. return SECOND_ONELETTER_ABBR, seconds;
  728. end
  729. function FormatShortDate(day, month, year)
  730. if (year) then
  731. if (LOCALE_enGB) then
  732. return SHORTDATE_EU:format(day, month, year);
  733. else
  734. return SHORTDATE:format(day, month, year);
  735. end
  736. else
  737. if (LOCALE_enGB) then
  738. return SHORTDATENOYEAR_EU:format(day, month);
  739. else
  740. return SHORTDATENOYEAR:format(day, month);
  741. end
  742. end
  743. end
  744. function Round(value)
  745. if value < 0.0 then
  746. return math.ceil(value - .5);
  747. end
  748. return math.floor(value + .5);
  749. end
  750. function FormatPercentage(percentage, roundToNearestInteger)
  751. if roundToNearestInteger then
  752. percentage = Round(percentage * 100);
  753. else
  754. percentage = percentage * 100;
  755. end
  756. return PERCENTAGE_STRING:format(percentage);
  757. end
  758. function FormatFraction(numerator, denominator)
  759. return GENERIC_FRACTION_STRING:format(numerator, denominator);
  760. end
  761. function CreateTextureMarkup(file, fileWidth, fileHeight, width, height, left, right, top, bottom, xOffset, yOffset)
  762. return ("|T%s:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d|t"):format(
  763. file
  764. , height
  765. , width
  766. , xOffset or 0
  767. , yOffset or 0
  768. , fileWidth
  769. , fileHeight
  770. , left * fileWidth
  771. , right * fileWidth
  772. , top * fileHeight
  773. , bottom * fileHeight
  774. );
  775. end
  776. function CreateAtlasMarkup(atlasName, width, height, offsetX, offsetY)
  777. return ("|A:%s:%d:%d:%d:%d|a"):format(
  778. atlasName
  779. , height or 0
  780. , width or 0
  781. , offsetX or 0
  782. , offsetY or 0
  783. );
  784. end
  785. -- NOTE: Many of the TextureKit functions below use the following parameters
  786. -- If setVisibilityOfRegions is true, the frame will be shown or hidden based on whether the textureKit and atlas element were found
  787. -- If useAtlasSize is true, the frame will be resized to be the same size as the atlas element.
  788. -- Use the constants in TextureKitConstants for both
  789. TextureKitConstants = {
  790. SetVisiblity = true;
  791. DoNotSetVisibility = false;
  792. UseAtlasSize = true;
  793. IgnoreAtlasSize = false;
  794. }
  795. -- Pass in a frame and a table containing parentKeys (on frame) as keys and atlas member names as the values
  796. function SetupAtlasesOnRegions(frame, regionsToAtlases, useAtlasSize)
  797. for region, atlas in pairs(regionsToAtlases) do
  798. if frame[region] then
  799. if frame[region]:GetObjectType() == "StatusBar" then
  800. frame[region]:SetStatusBarAtlas(atlas);
  801. elseif frame[region].SetAtlas then
  802. frame[region]:SetAtlas(atlas, useAtlasSize);
  803. end
  804. end
  805. end
  806. end
  807. function GetFinalNameFromTextureKit(fmt, textureKits)
  808. if type(textureKits) == "table" then
  809. return fmt:format(unpack(textureKits));
  810. else
  811. return fmt:format(textureKits);
  812. end
  813. end
  814. -- Pass in a TextureKit ID, a frame and a formatting string.
  815. -- The TextureKit name will be inserted into fmt (at the first %s). The resulting atlas name will be set on frame
  816. -- Use "%s" for fmt if the TextureKit name is the entire atlas element name
  817. function SetupTextureKitOnFrameByID(textureKitID, frame, fmt, setVisibilityOfRegions, useAtlasSize)
  818. local textureKit = GetUITextureKitInfo(textureKitID);
  819. SetupTextureKitOnFrame(textureKit, frame, fmt, setVisibilityOfRegions, useAtlasSize);
  820. end
  821. -- Pass in a TextureKit name, a frame and a formatting string.
  822. -- The TextureKit name will be inserted into fmt (at the first %s). The resulting atlas name will be set on frame
  823. -- Use "%s" for fmt if the TextureKit name is the entire atlas element name
  824. function SetupTextureKitOnFrame(textureKit, frame, fmt, setVisibility, useAtlasSize)
  825. if not frame then
  826. return;
  827. end
  828. local success = false;
  829. if textureKit then
  830. if frame:GetObjectType() == "StatusBar" then
  831. success = frame:SetStatusBarAtlas(GetFinalNameFromTextureKit(fmt, textureKit));
  832. elseif frame.SetAtlas then
  833. success = frame:SetAtlas(GetFinalNameFromTextureKit(fmt, textureKit), useAtlasSize);
  834. end
  835. end
  836. if setVisibility then
  837. frame:SetShown(success);
  838. end
  839. end
  840. -- Pass in a TextureKit name and a table containing frames as keys and formatting strings as values
  841. -- For each frame key in frames, the TextureKit name will be inserted into fmt (at the first %s). The resulting atlas name will be set on frame
  842. -- Use "%s" for fmt if the TextureKit name is the entire atlas element name
  843. function SetupTextureKitOnFrames(textureKit, frames, setVisibilityOfRegions, useAtlasSize)
  844. if not textureKit and not setVisibilityOfRegions then
  845. return;
  846. end
  847. for frame, fmt in pairs(frames) do
  848. SetupTextureKitOnFrame(textureKit, frame, fmt, setVisibilityOfRegions, useAtlasSize);
  849. end
  850. end
  851. -- Pass in a TextureKit ID and a table containing frames as keys and formatting strings as values
  852. -- For each frame key in frames, the TextureKit name will be inserted into fmt (at the first %s). The resulting atlas name will be set on frame
  853. -- Use "%s" for fmt if the TextureKit name is the entire atlas element name
  854. function SetupTextureKitsOnFrames(textureKitID, frames, setVisibilityOfRegions, useAtlasSize)
  855. local textureKit = GetUITextureKitInfo(textureKitID);
  856. SetupTextureKitOnFrames(textureKit, frames, setVisibilityOfRegions, useAtlasSize);
  857. end
  858. -- Pass in a TextureKit name, a frame and a table containing parentKeys (on frame) as keys and formatting strings as values
  859. -- For each frame key in frames, the TextureKit name will be inserted into fmt (at the first %s). The resulting atlas name will be set on frame
  860. -- Use "%s" for fmt if the TextureKit name is the entire atlas element name
  861. function SetupTextureKitOnRegions(textureKit, frame, regions, setVisibilityOfRegions, useAtlasSize)
  862. if not textureKit and not setVisibilityOfRegions then
  863. return;
  864. end
  865. local frames = {};
  866. for region, fmt in pairs(regions) do
  867. if frame[region] then
  868. frames[frame[region]] = fmt;
  869. end
  870. end
  871. return SetupTextureKitOnFrames(textureKit, frames, setVisibilityOfRegions, useAtlasSize);
  872. end
  873. -- Pass in a TextureKit ID, a frame and a table containing parentKeys (on frame) as keys and formatting strings as values
  874. -- For each frame key in frames, the TextureKit name will be inserted into fmt (at the first %s). The resulting atlas name will be set on frame
  875. -- Use "%s" for fmt if the TextureKit name is the entire atlas element name
  876. function SetupTextureKits(textureKitID, frame, regions, setVisibilityOfRegions, useAtlasSize)
  877. local textureKit = GetUITextureKitInfo(textureKitID);
  878. SetupTextureKitOnRegions(textureKit, frame, regions, setVisibilityOfRegions, useAtlasSize);
  879. end
  880. -- Pass in a TextureKit name, a frame and a table containing parentKeys (on frame) as keys and a table as values
  881. -- The values table should contain formatString as a member (setVisibility and useAtlasSize can also be added if desired)
  882. -- For each frame key in frames, the TextureKit name will be inserted into formatString (at the first %s). The resulting atlas name will be set on frame
  883. -- Use "%s" for formatString if the TextureKit name is the entire atlas element name
  884. function SetupTextureKitsFromRegionInfo(textureKit, frame, regionInfoList)
  885. if not frame or not regionInfoList then
  886. return;
  887. end
  888. for region, regionInfo in pairs(regionInfoList) do
  889. SetupTextureKitOnFrame(textureKit, frame[region], regionInfo.formatString, regionInfo.setVisibility, regionInfo.useAtlasSize);
  890. end
  891. end
  892. -- Pass in a TextureKit ID, a frame and a table containing parentKeys (on frame) as keys and a table as values
  893. -- The values table should contain formatString as a member (setVisibility and useAtlasSize can also be added if desired)
  894. -- For each frame key in frames, the TextureKit name will be inserted into formatString (at the first %s). The resulting atlas name will be set on frame
  895. -- Use "%s" for formatString if the TextureKit name is the entire atlas element name
  896. function SetupTextureKitsFromRegionInfoByID(textureKitID, frame, regionInfoList)
  897. local textureKit = GetUITextureKitInfo(textureKitID);
  898. SetupTextureKitsFromRegionInfo(textureKit, frame, regionInfoList);
  899. end
  900. CallbackRegistryBaseMixin = {};
  901. function CallbackRegistryBaseMixin:OnLoad()
  902. self.callbackRegistry = {};
  903. end
  904. function CallbackRegistryBaseMixin:RegisterCallback(event, callback)
  905. if not self.callbackRegistry[event] then
  906. self.callbackRegistry[event] = {};
  907. end
  908. self.callbackRegistry[event][callback] = true;
  909. end
  910. function CallbackRegistryBaseMixin:UnregisterCallback(event, callback)
  911. if self.callbackRegistry[event] then
  912. self.callbackRegistry[event][callback] = nil;
  913. end
  914. end
  915. function CallbackRegistryBaseMixin:TriggerEvent(event, ...)
  916. local registry = self.callbackRegistry[event];
  917. if registry then
  918. for callback in pairs(registry) do
  919. callback(event, ...);
  920. end
  921. end
  922. end
  923. --[[static]] function CallbackRegistryBaseMixin:GenerateCallbackEvents(events)
  924. self.Event = tInvert(events);
  925. end
  926. EventRegistrationHelper = {};
  927. function EventRegistrationHelper:AddEvent(event)
  928. self.containedEvents = self.containedEvents or {};
  929. self.containedEvents[event] = true;
  930. end
  931. function EventRegistrationHelper:AddEvents(...)
  932. self.containedEvents = self.containedEvents or {};
  933. for i = 1, select("#", ...) do
  934. self.containedEvents[select(i, ...)] = true;
  935. end
  936. end
  937. function EventRegistrationHelper:RemoveEvent(event)
  938. if self.containedEvents then
  939. self.containedEvents[event] = nil;
  940. end
  941. end
  942. function EventRegistrationHelper:ClearEvents()
  943. self.containedEvents = nil;
  944. end
  945. function EventRegistrationHelper:SetEventsRegistered(registered)
  946. local events = self.containedEvents;
  947. if events then
  948. local func = registered and self.RegisterEvent or self.UnregisterEvent;
  949. for event in pairs(self.containedEvents) do
  950. func(self, event);
  951. end
  952. end
  953. end
  954. TabGroupMixin = {};
  955. function TabGroupMixin:OnLoad(...)
  956. self.frames = { ... };
  957. end
  958. function TabGroupMixin:AddFrame(frame)
  959. table.insert(self.frames, frame);
  960. end
  961. function TabGroupMixin:OnTabPressed()
  962. for focusIndex, frame in ipairs(self.frames) do
  963. if frame:HasFocus() then
  964. local nextFocusIndex = IsShiftKeyDown() and (focusIndex - 1) or (focusIndex + 1);
  965. if nextFocusIndex == 0 then
  966. nextFocusIndex = #self.frames;
  967. elseif nextFocusIndex > #self.frames then
  968. nextFocusIndex = 1;
  969. end
  970. self.frames[nextFocusIndex]:SetFocus();
  971. return;
  972. end
  973. end
  974. end
  975. function CreateTabGroup(...)
  976. local tabGroup = CreateFromMixins(TabGroupMixin);
  977. tabGroup:OnLoad(...);
  978. return tabGroup;
  979. end
  980. function ExecuteFrameScript(frame, scriptName, ...)
  981. local script = frame:GetScript(scriptName);
  982. if script then
  983. securecall(script, frame, ...);
  984. end
  985. end
  986. function Flags_CreateMask(...)
  987. local mask = 0;
  988. for i = 1, select("#", ...) do
  989. mask = bit.bor(mask, select(i, ...));
  990. end
  991. return mask;
  992. end
  993. function Flags_CreateMaskFromTable(flagsTable)
  994. local mask = 0;
  995. for flagName, flagValue in pairs(flagsTable) do
  996. mask = bit.bor(mask, flagValue);
  997. end
  998. return mask;
  999. end
  1000. FlagsMixin = {};
  1001. function FlagsMixin:OnLoad()
  1002. self:ClearAll();
  1003. end
  1004. function FlagsMixin:AddNamedFlagsFromTable(flagsTable)
  1005. assert(flagsTable.flags == nil);
  1006. Mixin(self, flagsTable);
  1007. end
  1008. function FlagsMixin:AddNamedMask(flagName, mask)
  1009. assert(self[flagName] == nil);
  1010. self[flagName] = mask;
  1011. end
  1012. function FlagsMixin:Set(flag)
  1013. self.flags = bit.bor(self.flags, flag);
  1014. end
  1015. function FlagsMixin:Clear(flag)
  1016. self.flags = bit.band(self.flags, bit.bnot(flag));
  1017. end
  1018. function FlagsMixin:SetOrClear(flag, isSet)
  1019. if isSet then
  1020. self:Set(flag);
  1021. else
  1022. self:Clear(flag);
  1023. end
  1024. end
  1025. function FlagsMixin:ClearAll()
  1026. self.flags = 0;
  1027. end
  1028. function FlagsMixin:IsAnySet()
  1029. return self.flags ~= 0;
  1030. end
  1031. function FlagsMixin:IsSet(flagOrMask)
  1032. return bit.band(self.flags, flagOrMask) == flagOrMask;
  1033. end
  1034. function FlagsMixin:GetFlags()
  1035. return self.flags;
  1036. end
  1037. DirtyFlagsMixin = CreateFromMixins(FlagsMixin);
  1038. function DirtyFlagsMixin:OnLoad()
  1039. FlagsMixin.OnLoad(self);
  1040. self.isDirty = false;
  1041. end
  1042. function DirtyFlagsMixin:MarkDirty(flag)
  1043. if flag ~= nil then
  1044. self:Set(flag);
  1045. end
  1046. self.isDirty = true;
  1047. end
  1048. function DirtyFlagsMixin:MarkClean()
  1049. self:ClearAll();
  1050. self.isDirty = false;
  1051. end
  1052. function DirtyFlagsMixin:IsDirty(flag)
  1053. if flag ~= nil then
  1054. return self:IsSet(flag);
  1055. else
  1056. return self.isDirty;
  1057. end
  1058. end
  1059. function CallErrorHandler(...)
  1060. return geterrorhandler()(...);
  1061. end
  1062. TabGroupMixin = {};
  1063. function TabGroupMixin:OnLoad(...)
  1064. self.isTabGroup = true;
  1065. self.frames = { ... };
  1066. end
  1067. function TabGroupMixin:AddFrame(frame)
  1068. table.insert(self.frames, frame);
  1069. end
  1070. function TabGroupMixin:HasFocus()
  1071. return self:GetFocusIndex() ~= nil;
  1072. end
  1073. function TabGroupMixin:SetFocus()
  1074. -- focusing the first frame/subgroup for now...actually depends on whether or not we were going backwards or forwards through the groups
  1075. local frame = self.frames[1];
  1076. if frame then
  1077. frame:SetFocus();
  1078. end
  1079. end
  1080. function TabGroupMixin:GetFocusIndex()
  1081. return self.focusIndex or self:DiscoverFocusIndex();
  1082. end
  1083. function TabGroupMixin:DiscoverFocusIndex()
  1084. self.focusIndex = nil;
  1085. for focusIndex, frame in ipairs(self.frames) do
  1086. if frame:HasFocus() then
  1087. self.focusIndex = focusIndex;
  1088. return focusIndex;
  1089. end
  1090. end
  1091. end
  1092. function TabGroupMixin:IsValidFocusIndex(focusIndex)
  1093. return focusIndex > 0 and focusIndex <= #self.frames;
  1094. end
  1095. function TabGroupMixin:WrapFocusIndex(focusIndex)
  1096. if focusIndex == 0 then
  1097. return #self.frames;
  1098. elseif focusIndex > #self.frames then
  1099. return 1;
  1100. end
  1101. return focusIndex;
  1102. end
  1103. function TabGroupMixin:OnTabPressed(preventFocusWrap)
  1104. local focusIndex = self:GetFocusIndex();
  1105. local frameAtIndex = self.frames[focusIndex];
  1106. if frameAtIndex.isTabGroup then
  1107. if frameAtIndex:OnTabPressed(true) then
  1108. return true;
  1109. end
  1110. end
  1111. local nextFocusIndex = IsShiftKeyDown() and (focusIndex - 1) or (focusIndex + 1);
  1112. if preventFocusWrap and not self:IsValidFocusIndex(nextFocusIndex) then
  1113. return false;
  1114. end
  1115. nextFocusIndex = Wrap(nextFocusIndex, #self.frames);
  1116. self.focusIndex = nextFocusIndex;
  1117. self.frames[nextFocusIndex]:SetFocus();
  1118. end
  1119. function CreateTabGroup(...)
  1120. local tabGroup = CreateFromMixins(TabGroupMixin);
  1121. tabGroup:OnLoad(...);
  1122. return tabGroup;
  1123. end
  1124. function ExecuteFrameScript(frame, scriptName, ...)
  1125. local script = frame:GetScript(scriptName);
  1126. if script then
  1127. xpcall(script, CallErrorHandler, frame, ...);
  1128. end
  1129. end
  1130. PredictedSettingBaseMixin = {};
  1131. -- The wrapTable here should have functions to specific keys based on which type of setting you are wrapping.
  1132. -- All tables must have a getFunction key that returns the "real" value.
  1133. -- The PredictedSetting wrapTable should have a setFunction key with a function that takes a value and sets the real value to this value.
  1134. -- This function can return a true/false value noting if the set succeeded or not.
  1135. -- The PredictedToggle wrapTable should have a toggleFunction key that is the function to call to toggle the real value.
  1136. function PredictedSettingBaseMixin:SetUp(wrapTable)
  1137. self.wrapTable = wrapTable;
  1138. end
  1139. function PredictedSettingBaseMixin:Clear()
  1140. self.predictedValue = nil;
  1141. end
  1142. function PredictedSettingBaseMixin:Get()
  1143. if (self.predictedValue ~= nil) then
  1144. return self.predictedValue;
  1145. end
  1146. return self.wrapTable.getFunction();
  1147. end
  1148. PredictedSettingMixin = CreateFromMixins(PredictedSettingBaseMixin);
  1149. function PredictedSettingMixin:Set(value)
  1150. local validated = self.wrapTable.setFunction(value);
  1151. if (validated ~= false) then
  1152. self.predictedValue = value;
  1153. end
  1154. end
  1155. function CreatePredictedSetting(wrapTable)
  1156. local predictedSetting = CreateFromMixins(PredictedSettingMixin);
  1157. predictedSetting:SetUp(wrapTable);
  1158. return predictedSetting;
  1159. end
  1160. PredictedToggleMixin = CreateFromMixins(PredictedSettingBaseMixin)
  1161. function PredictedToggleMixin:SetUp(wrapTable)
  1162. PredictedSettingBaseMixin.SetUp(self, wrapTable);
  1163. self.currentValue = self.wrapTable.getFunction();
  1164. end
  1165. function PredictedToggleMixin:Toggle()
  1166. self.predictedValue = not self.currentValue;
  1167. self.wrapTable.toggleFunction();
  1168. end
  1169. function PredictedToggleMixin:UpdateCurrentValue()
  1170. self.currentValue = self.wrapTable.getFunction();
  1171. end
  1172. function CreatePredictedToggle(wrapTable)
  1173. local predictedToggle = CreateFromMixins(PredictedToggleMixin);
  1174. predictedToggle:SetUp(wrapTable);
  1175. return predictedToggle;
  1176. end
  1177. LayoutIndexManagerMixin = {}
  1178. function LayoutIndexManagerMixin:AddManagedLayoutIndex(key, startingIndex)
  1179. if (not self.managedLayoutIndexes) then
  1180. self.managedLayoutIndexes = {};
  1181. self.startingLayoutIndexes = {};
  1182. end
  1183. self.managedLayoutIndexes[key] = startingIndex;
  1184. self.startingLayoutIndexes[key] = startingIndex;
  1185. end
  1186. function LayoutIndexManagerMixin:GetManagedLayoutIndex(key)
  1187. if (not self.managedLayoutIndexes or not self.managedLayoutIndexes[key]) then
  1188. return 0;
  1189. end
  1190. local layoutIndex = self.managedLayoutIndexes[key];
  1191. self.managedLayoutIndexes[key] = self.managedLayoutIndexes[key] + 1;
  1192. return layoutIndex;
  1193. end
  1194. function LayoutIndexManagerMixin:Reset()
  1195. for k, _ in pairs(self.managedLayoutIndexes) do
  1196. self.managedLayoutIndexes[k] = self.startingLayoutIndexes[k];
  1197. end
  1198. end
  1199. function CreateLayoutIndexManager()
  1200. return CreateFromMixins(LayoutIndexManagerMixin);
  1201. end
  1202. function CallMethodOnNearestAncestor(self, methodName, ...)
  1203. local ancestor = self:GetParent();
  1204. while ancestor and not ancestor[methodName] do
  1205. ancestor = ancestor:GetParent();
  1206. end
  1207. if ancestor then
  1208. ancestor[methodName](ancestor, ...);
  1209. return true;
  1210. end
  1211. return false;
  1212. end
  1213. function GetClampedCurrentExpansionLevel()
  1214. return math.min(GetClientDisplayExpansionLevel(), math.max(GetAccountExpansionLevel(), GetExpansionLevel()));
  1215. end
  1216. function GetHighlightedNumberDifferenceString(baseString, newString)
  1217. local outputString = "";
  1218. -- output string is being built from the new string
  1219. local newStringIndex = 1;
  1220. -- find a stretch of digits (including . and , because of different locales) - but has to end in a digit
  1221. local PATTERN = "([,%.%d]*%d+)";
  1222. local start1, end1, baseNumberString = string.find(baseString, PATTERN);
  1223. local start2, end2, newNumberString = string.find(newString, PATTERN);
  1224. while start1 and start2 do
  1225. -- add from the new string until the matched spot
  1226. outputString = outputString .. string.sub(newString, newStringIndex, start2 - 1);
  1227. newStringIndex = end2 + 1;
  1228. if baseNumberString ~= newNumberString then
  1229. -- need to remove , and . before comparing numbers because of locales
  1230. local scrubbedBaseNumberString = gsub(baseNumberString, "[,%.]", "");
  1231. local scrubbedNewNumberString = gsub(newNumberString, "[,%.]", "");
  1232. local baseNumber = tonumber(scrubbedBaseNumberString);
  1233. local newNumber = tonumber(scrubbedNewNumberString);
  1234. if baseNumber and newNumber then
  1235. local delta = newNumber - baseNumber;
  1236. if delta > 0 then
  1237. newNumberString = GREEN_FONT_COLOR_CODE..string.format(newNumberString)..FONT_COLOR_CODE_CLOSE;
  1238. elseif delta < 0 then
  1239. newNumberString = RED_FONT_COLOR_CODE..string.format(newNumberString)..FONT_COLOR_CODE_CLOSE;
  1240. end
  1241. end
  1242. end
  1243. outputString = outputString..newNumberString;
  1244. start1, end1, baseNumberString = string.find(baseString, PATTERN, end1 + 1);
  1245. start2, end2, newNumberString = string.find(newString, PATTERN, end2 + 1);
  1246. end
  1247. outputString = outputString .. string.sub(newString, newStringIndex, string.len(newString));
  1248. return outputString;
  1249. end
  1250. function GetUnscaledFrameRect(frame, scale)
  1251. local frameLeft, frameBottom, frameWidth, frameHeight = frame:GetScaledRect();
  1252. if frameLeft == nil then
  1253. return 1, 1, 1, 1;
  1254. end
  1255. return frameLeft / scale, frameBottom / scale, frameWidth / scale, frameHeight / scale;
  1256. end
  1257. -- CVar script wrappers
  1258. function RegisterCVar(name, value)
  1259. C_CVar.RegisterCVar(name, value);
  1260. end
  1261. function ResetTestCvars()
  1262. C_CVar.ResetTestCVars();
  1263. end
  1264. function SetCVar(name, value, eventName)
  1265. if type(value) == "boolean" then
  1266. return C_CVar.SetCVar(name, value and "1" or "0", eventName);
  1267. else
  1268. return C_CVar.SetCVar(name, value and tostring(value) or nil, eventName);
  1269. end
  1270. end
  1271. function GetCVar(name)
  1272. return C_CVar.GetCVar(name);
  1273. end
  1274. function SetCVarBitfield(name, index, value, scriptCVar)
  1275. return C_CVar.SetCVarBitfield(name, index, value, scriptCVar);
  1276. end
  1277. function GetCVarBitfield(name, index)
  1278. return C_CVar.GetCVarBitfield(name, index);
  1279. end
  1280. function GetCVarBool(name)
  1281. return C_CVar.GetCVarBool(name);
  1282. end
  1283. function GetCVarDefault(name)
  1284. return C_CVar.GetCVarDefault(name);
  1285. end
  1286. function GetGroupMemberCountsForDisplay()
  1287. local data = GetGroupMemberCounts();
  1288. data.DAMAGER = data.DAMAGER + data.NOROLE; --People without a role count as damage
  1289. data.NOROLE = 0;
  1290. return data;
  1291. end
  1292. function GetURLIndexAndLoadURL(self, link)
  1293. local linkType, index = string.split(":", link);
  1294. if ( linkType == "urlIndex" ) then
  1295. LoadURLIndex(tonumber(index));
  1296. end
  1297. end